Merge m-c to inbound, a=merge

This commit is contained in:
Wes Kocher 2016-08-18 16:32:58 -07:00
commit a58f8b89a0
172 changed files with 2233 additions and 15045 deletions

View File

@ -2,20 +2,9 @@
<!--
Remotes
-->
<!--original fetch url was https://android.googlesource.com/-->
<remote fetch="https://git.mozilla.org/external/aosp" name="aosp"/>
<!--original fetch url was git://github.com/apitrace/-->
<remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
<remote fetch="git://github.com/apitrace/" name="apitrace"/>
<remote fetch="git://github.com/mozilla-b2g/" name="b2g"/>
<!--original fetch url was https://git.mozilla.org/b2g-->
<remote fetch="https://git.mozilla.org/b2g" name="b2gmozilla"/>
<!--original fetch url was git://codeaurora.org/-->
<remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
<!--original fetch url was http://android.git.linaro.org/git-ro/-->
<remote fetch="https://git.mozilla.org/external/linaro" name="linaro"/>
<remote fetch="git://github.com/mozilla/" name="mozilla"/>
<!--original fetch url was https://git.mozilla.org/releases-->
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<remote fetch="git://codeaurora.org/" name="caf"/>
<!--
B2G repositories for all targets
-->

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "a954bd2954c422b7d24d92cfd73000cb455dce44",
"remote": "https://git.mozilla.org/releases/gaia.git",
"git_revision": "90240f7a2e66ae8873e1f1b0e5f1d80a98c2c2db",
"remote": "https://github.com/mozilla-b2g/gaia.git",
"branch": ""
},
"revision": "e9e8c44b43178139fca915cfe97ae3c52a9afe34",
},
"revision": "e9e8c44b43178139fca915cfe97ae3c52a9afe34",
"repo_path": "integration/gaia-central"
}

View File

@ -2,20 +2,9 @@
<!--
Remotes
-->
<!--original fetch url was https://android.googlesource.com/-->
<remote fetch="https://git.mozilla.org/external/aosp" name="aosp"/>
<!--original fetch url was git://github.com/apitrace/-->
<remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
<remote fetch="git://github.com/apitrace/" name="apitrace"/>
<remote fetch="git://github.com/mozilla-b2g/" name="b2g"/>
<!--original fetch url was https://git.mozilla.org/b2g-->
<remote fetch="https://git.mozilla.org/b2g" name="b2gmozilla"/>
<!--original fetch url was git://codeaurora.org/-->
<remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
<!--original fetch url was http://android.git.linaro.org/git-ro/-->
<remote fetch="https://git.mozilla.org/external/linaro" name="linaro"/>
<remote fetch="git://github.com/mozilla/" name="mozilla"/>
<!--original fetch url was https://git.mozilla.org/releases-->
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<remote fetch="git://codeaurora.org/" name="caf"/>
<!--
B2G repositories for all targets
-->

View File

@ -215,6 +215,9 @@ skip-if = true # bug 1057615
[browser_bug565575.js]
[browser_bug567306.js]
subsuite = clipboard
[browser_bug1261299.js]
subsuite = clipboard
skip-if = toolkit != "cocoa" # Because of tests for supporting Service Menu of macOS, bug 1261299
[browser_bug575561.js]
[browser_bug575830.js]
[browser_bug577121.js]

View File

@ -0,0 +1,73 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
/**
* Tests for Bug 1261299
* Test that the service menu code path is called properly and the
* current selection (transferable) is cached properly on the parent process.
*/
add_task(function* test_content_and_chrome_selection()
{
let testPage =
'data:text/html,' +
'<textarea id="textarea">Write something here</textarea>';
let DOMWindowUtils = EventUtils._getDOMWindowUtils(window);
let selectedText;
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, testPage);
yield BrowserTestUtils.synthesizeMouse("#textarea", 0, 0, {}, gBrowser.selectedBrowser);
yield BrowserTestUtils.synthesizeKey("KEY_ArrowRight",
{shiftKey: true, ctrlKey: true, code: "ArrowRight"}, gBrowser.selectedBrowser);
selectedText = DOMWindowUtils.GetSelectionAsPlaintext();
is(selectedText, "Write something here", "The macOS services got the selected content text");
gURLBar.value = "test.mozilla.org";
yield gURLBar.focus();
yield BrowserTestUtils.synthesizeKey("KEY_ArrowRight",
{shiftKey: true, ctrlKey: true, code: "ArrowRight"}, gBrowser.selectedBrowser);
selectedText = DOMWindowUtils.GetSelectionAsPlaintext();
is(selectedText, "test.mozilla.org", "The macOS services got the selected chrome text");
yield BrowserTestUtils.removeTab(tab);
});
// Test switching active selection.
// Each tab has a content selection and when you switch to that tab, its selection becomes
// active aka the current selection.
// Expect: The active selection is what is being sent to OSX service menu.
add_task(function* test_active_selection_switches_properly()
{
let testPage1 =
'data:text/html,' +
'<textarea id="textarea">Write something here</textarea>';
let testPage2 =
'data:text/html,' +
'<textarea id="textarea">Nothing available</textarea>';
let DOMWindowUtils = EventUtils._getDOMWindowUtils(window);
let selectedText;
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, testPage1);
yield BrowserTestUtils.synthesizeMouse("#textarea", 0, 0, {}, gBrowser.selectedBrowser);
yield BrowserTestUtils.synthesizeKey("KEY_ArrowRight",
{shiftKey: true, ctrlKey: true, code: "ArrowRight"}, gBrowser.selectedBrowser);
let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, testPage2);
yield BrowserTestUtils.synthesizeMouse("#textarea", 0, 0, {}, gBrowser.selectedBrowser);
yield BrowserTestUtils.synthesizeKey("KEY_ArrowRight",
{shiftKey: true, ctrlKey: true, code: "ArrowRight"}, gBrowser.selectedBrowser);
yield BrowserTestUtils.switchTab(gBrowser, tab1);
selectedText = DOMWindowUtils.GetSelectionAsPlaintext();
is(selectedText, "Write something here", "The macOS services got the selected content text");
yield BrowserTestUtils.switchTab(gBrowser, tab2);
selectedText = DOMWindowUtils.GetSelectionAsPlaintext();
is(selectedText, "Nothing available", "The macOS services got the selected content text");
yield BrowserTestUtils.removeTab(tab1);
yield BrowserTestUtils.removeTab(tab2);
});

View File

@ -115,7 +115,7 @@ function getObserver() {
onEndUpdateBatch: function() {},
onTitleChanged: function() {},
onClearHistory: function() {
this.emit("visitRemoved", {allHistory: true});
this.emit("visitRemoved", {allHistory: true, urls: []});
},
onPageChanged: function() {},
onFrecencyChanged: function() {},

View File

@ -316,7 +316,6 @@
},
"urls": {
"type": "array",
"optional": true,
"items": {
"type": "string"
}

View File

@ -255,6 +255,7 @@
"name": "sendMessage",
"type": "function",
"description": "Sends a single message to the content script(s) in the specified tab, with an optional callback to run when a response is sent back. The $(ref:runtime.onMessage) event is fired in each content script running in the specified tab for the current extension.",
"async": "sendResponse",
"parameters": [
{
"type": "integer",

View File

@ -17,6 +17,7 @@ add_task(function* test_delete() {
browser.history.onVisitRemoved.addListener(data => {
if (data.allHistory) {
historyClearedCount++;
browser.test.assertEq(0, data.urls.length, "onVisitRemoved received an empty urls array");
} else {
browser.test.assertEq(1, data.urls.length, "onVisitRemoved received one URL");
removedUrls.push(data.urls[0]);

View File

@ -747,11 +747,7 @@ var gAdvancedPane = {
*/
showUpdates: function ()
{
if (AppConstants.MOZ_UPDATER) {
var prompter = Components.classes["@mozilla.org/updates/update-prompt;1"]
.createInstance(Components.interfaces.nsIUpdatePrompt);
prompter.showUpdateHistory(window);
}
gSubDialog.open("chrome://mozapps/content/update/history.xul");
},
// ENCRYPTION TAB

View File

@ -3,26 +3,110 @@
"use strict";
function test() {
waitForExplicitFinish();
resetPreferences();
const { classes: Cc, interfaces: Ci, manager: Cm, utils: Cu, results: Cr } = Components;
registerCleanupFunction(resetPreferences);
Services.prefs.setBoolPref("browser.search.update", false);
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
open_preferences(runTest);
const uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
const dateFormat = Cc["@mozilla.org/intl/scriptabledateformat;1"]
.getService(Components.interfaces.nsIScriptableDateFormat);
const mockUpdateManager = {
contractId: "@mozilla.org/updates/update-manager;1",
_mockClassId: uuidGenerator.generateUUID(),
_originalClassId: "",
_originalFactory: null,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdateManager]),
createInstance: function(outer, iiD) {
if (outer) {
throw Cr.NS_ERROR_NO_AGGREGATION;
}
return this.QueryInterface(iiD);
},
register: function () {
let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
if (!registrar.isCIDRegistered(this._mockClassId)) {
this._originalClassId = registrar.contractIDToCID(this.contractId);
this._originalFactory = Cm.getClassObject(Cc[this.contractId], Ci.nsIFactory);
registrar.unregisterFactory(this._originalClassId, this._originalFactory);
registrar.registerFactory(this._mockClassId, "Unregister after testing", this.contractId, this);
}
},
unregister: function () {
let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
registrar.unregisterFactory(this._mockClassId, this);
registrar.registerFactory(this._originalClassId, "", this.contractId, this._originalFactory);
},
get updateCount() {
return this._updates.length;
},
getUpdateAt: function (index) {
return this._updates[index];
},
_updates: [
{
name: "Firefox Developer Edition 49.0a2",
statusText: "The Update was successfully installed",
buildID: "20160728004010",
type: "minor",
installDate: 1469763105156,
detailsURL: "https://www.mozilla.org/firefox/aurora/"
},
{
name: "Firefox Developer Edition 43.0a2",
statusText: "The Update was successfully installed",
buildID: "20150929004011",
type: "minor",
installDate: 1443585886224,
detailsURL: "https://www.mozilla.org/firefox/aurora/"
},
{
name: "Firefox Developer Edition 42.0a2",
statusText: "The Update was successfully installed",
buildID: "20150920004018",
type: "major",
installDate: 1442818147544,
detailsURL: "https://www.mozilla.org/firefox/aurora/"
}
]
};
function resetPreferences() {
Services.prefs.clearUserPref("browser.search.update");
}
function runTest(win) {
let doc = win.document;
function formatInstallDate(sec) {
var date = new Date(sec);
return dateFormat.FormatDateTime("",
dateFormat.dateFormatLong,
dateFormat.timeFormatSeconds,
date.getFullYear(),
date.getMonth() + 1,
date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds());
}
registerCleanupFunction(resetPreferences);
add_task(function*() {
yield openPreferencesViaOpenPreferencesAPI("advanced", "updateTab", { leaveOpen: true });
resetPreferences();
Services.prefs.setBoolPref("browser.search.update", false);
let doc = gBrowser.selectedBrowser.contentDocument;
let enableSearchUpdate = doc.getElementById("enableSearchUpdate");
win.gotoPref("paneAdvanced");
let advancedPrefs = doc.getElementById("advancedPrefs");
let updateTab = doc.getElementById("updateTab");
advancedPrefs.selectedTab = updateTab;
is_element_visible(enableSearchUpdate, "Check search update preference is visible");
// Ensure that the update pref dialog reflects the actual pref value.
@ -31,10 +115,49 @@ function runTest(win) {
ok(enableSearchUpdate.checked, "Ensure search updates are enabled");
gBrowser.removeCurrentTab();
win.close();
finish();
}
});
function resetPreferences() {
Services.prefs.clearUserPref("browser.search.update");
}
add_task(function*() {
mockUpdateManager.register();
yield openPreferencesViaOpenPreferencesAPI("advanced", "updateTab", { leaveOpen: true });
let doc = gBrowser.selectedBrowser.contentDocument;
let showBtn = doc.getElementById("showUpdateHistory");
let dialogOverlay = doc.getElementById("dialogOverlay");
// Test the dialog window opens
is(dialogOverlay.style.visibility, "", "The dialog should be invisible");
showBtn.doCommand();
yield promiseLoadSubDialog("chrome://mozapps/content/update/history.xul");
is(dialogOverlay.style.visibility, "visible", "The dialog should be visible");
let dialogFrame = doc.getElementById("dialogFrame");
let frameDoc = dialogFrame.contentDocument;
let updates = frameDoc.querySelectorAll("update");
// Test the update history numbers are correct
is(updates.length, mockUpdateManager.updateCount, "The update count is incorrect.");
// Test the updates are displayed correctly
let update = null;
let updateData = null;
for (let i = 0; i < updates.length; ++i) {
update = updates[i];
updateData = mockUpdateManager.getUpdateAt(i);
is(update.name, updateData.name + " (" + updateData.buildID + ")", "Wrong update name");
is(update.type, updateData.type == "major" ? "New Version" : "Security Update", "Wrong update type");
is(update.installDate, formatInstallDate(updateData.installDate), "Wrong update installDate");
is(update.detailsURL, updateData.detailsURL, "Wrong update detailsURL");
is(update.status, updateData.statusText, "Wrong update status");
}
// Test the dialog window closes
let closeBtn = doc.getElementById("dialogClose");
closeBtn.doCommand();
is(dialogOverlay.style.visibility, "", "The dialog should be invisible");
mockUpdateManager.unregister();
gBrowser.removeCurrentTab();
});

View File

@ -957,10 +957,13 @@ toolbaritem[cui-areatype="menu-panel"] > :-moz-any(@nestedButtons@) > .toolbarbu
/* ::::: URL Bar Zoom Reset Button ::::: */
@keyframes urlbar-zoom-reset-pulse {
0% {
transform: scale(0);
transform: scale(0);
}
75% {
transform: scale(1.5);
}
100% {
transform: scale(1.5);
transform: scale(1.0);
}
}

View File

@ -1662,9 +1662,12 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
0% {
transform: scale(0);
}
100% {
75% {
transform: scale(1.5);
}
100% {
transform: scale(1.0);
}
}
#urlbar-zoom-button {

View File

@ -1391,10 +1391,13 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder,
/* ::::: URL Bar Zoom Reset Button ::::: */
@keyframes urlbar-zoom-reset-pulse {
0% {
transform: scale(0);
transform: scale(0);
}
75% {
transform: scale(1.5);
}
100% {
transform: scale(1.5);
transform: scale(1.0);
}
}

View File

@ -13,6 +13,7 @@ Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://testing-common/BrowserTestUtils.jsm");
Cu.import("resource:///modules/SitePermissions.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
let {UrlClassifierTestUtils} = Cu.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
@ -39,11 +40,15 @@ this.ControlCenter = {
localFile: {
applyConfig: Task.async(function* () {
let filePath = "file:///";
if (Services.appinfo.OS === "WINNT") {
filePath += "C:/";
}
yield loadPage(filePath);
let channel = NetUtil.newChannel({
uri: "chrome://mozscreenshots/content/lib/mozscreenshots.html",
loadUsingSystemPrincipal: true
});
channel = channel.QueryInterface(Ci.nsIFileChannel);
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
let gBrowser = browserWindow.gBrowser;
BrowserTestUtils.loadURI(gBrowser.selectedBrowser, channel.file.path);
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
yield openIdentityPopup();
}),
},

View File

@ -19,6 +19,7 @@ const { LocationStore, serialize, deserialize } = require("./location-store");
function SourceMapService(target) {
this._target = target;
this._locationStore = new LocationStore();
this._isNotSourceMapped = new Map();
EventEmitter.decorate(this);
@ -37,10 +38,11 @@ function SourceMapService(target) {
}
/**
* Clears the store containing the cached resolved locations and promises
* Clears the store containing the cached promised locations
*/
SourceMapService.prototype.reset = function () {
this._locationStore.clear();
this._isNotSourceMapped.clear();
};
SourceMapService.prototype.destroy = function () {
@ -49,15 +51,24 @@ SourceMapService.prototype.destroy = function () {
this._target.off("navigate", this.reset);
this._target.off("will-navigate", this.reset);
this._target.off("close", this.destroy);
this._target = this._locationStore = null;
this._target = this._locationStore = this._isNotSourceMapped = null;
};
/**
* Sets up listener for the callback to update the FrameView and tries to resolve location
* Sets up listener for the callback to update the FrameView
* and tries to resolve location, if it is source-mappable
* @param location
* @param callback
*/
SourceMapService.prototype.subscribe = function (location, callback) {
// A valid candidate location for source-mapping should have a url and line.
// Abort if there's no `url`, which means it's unsourcemappable anyway,
// like an eval script.
// From previous attempts to source-map locations, we also determine if a location
// is not source-mapped.
if (!location.url || !location.line || this._isNotSourceMapped.get(location.url)) {
return;
}
this.on(serialize(location), callback);
this._locationStore.set(location);
this._resolveAndUpdate(location);
@ -80,7 +91,7 @@ SourceMapService.prototype.unsubscribe = function (location, callback) {
/**
* Tries to resolve the location and if successful,
* emits the resolved location and caches it
* emits the resolved location
* @param location
* @private
*/
@ -96,46 +107,48 @@ SourceMapService.prototype._resolveAndUpdate = function (location) {
};
/**
* Validates the location model,
* checks if there is existing promise to resolve location, if so returns cached promise
* if not promised to resolve,
* tries to resolve location and returns a promised location
* Checks if there is existing promise to resolve location, if so returns cached promise
* if not, tries to resolve location and returns a promised location
* @param location
* @return Promise<Object>
* @private
*/
SourceMapService.prototype._resolveLocation = Task.async(function* (location) {
// Location must have a url and a line
if (!location.url || !location.line) {
return null;
}
let resolvedLocation;
const cachedLocation = this._locationStore.get(location);
if (cachedLocation) {
return cachedLocation;
resolvedLocation = cachedLocation;
} else {
const promisedLocation = resolveLocation(this._target, location);
if (promisedLocation) {
this._locationStore.set(location, promisedLocation);
return promisedLocation;
resolvedLocation = promisedLocation;
}
}
return resolvedLocation;
});
/**
* Checks if the `source-updated` event is fired from the target.
* Checks to see if location store has the source url in its cache,
* if so, tries to update each stale location in the store.
* Determines if the source should be source-mapped or not.
* @param _
* @param sourceEvent
* @private
*/
SourceMapService.prototype._onSourceUpdated = function (_, sourceEvent) {
let { type, source } = sourceEvent;
// If we get a new source, and it's not a source map, abort;
// we can have no actionable updates as this is just a new normal source.
// Also abort if there's no `url`, which means it's unsourcemappable anyway,
// like an eval script.
if (!source.url || type === "newSource" && !source.isSourceMapped) {
// Check Source Actor for sourceMapURL property (after Firefox 48)
// If not present, utilize isSourceMapped and isPrettyPrinted properties
// to estimate if a source is not source-mapped.
const isNotSourceMapped = !(source.sourceMapURL ||
source.isSourceMapped || source.isPrettyPrinted);
if (type === "newSource" && isNotSourceMapped) {
this._isNotSourceMapped.set(source.url, true);
return;
}
let sourceUrl = null;
@ -180,7 +193,7 @@ function resolveLocation(target, location) {
}
/**
* Returns if the original location and resolved location are the same
* Returns true if the original location and resolved location are the same
* @param location
* @param resolvedLocation
* @returns {boolean}

View File

@ -42,8 +42,12 @@ function InspectorSearch(inspector, input, clearBtn) {
this._onKeyDown = this._onKeyDown.bind(this);
this._onInput = this._onInput.bind(this);
this._onClearSearch = this._onClearSearch.bind(this);
this._onFilterTextboxContextMenu =
this._onFilterTextboxContextMenu.bind(this);
this.searchBox.addEventListener("keydown", this._onKeyDown, true);
this.searchBox.addEventListener("input", this._onInput, true);
this.searchBox.addEventListener("contextmenu",
this._onFilterTextboxContextMenu);
this.searchClearButton.addEventListener("click", this._onClearSearch);
// For testing, we need to be able to wait for the most recent node request
@ -64,6 +68,8 @@ InspectorSearch.prototype = {
destroy: function () {
this.searchBox.removeEventListener("keydown", this._onKeyDown, true);
this.searchBox.removeEventListener("input", this._onInput, true);
this.searchBox.removeEventListener("contextmenu",
this._onFilterTextboxContextMenu);
this.searchClearButton.removeEventListener("click", this._onClearSearch);
this.searchBox = null;
this.searchClearButton = null;
@ -132,6 +138,18 @@ InspectorSearch.prototype = {
}
},
/**
* Context menu handler for filter search box.
*/
_onFilterTextboxContextMenu: function (event) {
try {
let contextmenu = this.inspector.toolbox.textboxContextMenuPopup;
contextmenu.openPopupAtScreen(event.screenX, event.screenY, true);
} catch (e) {
console.error(e);
}
},
_onClearSearch: function () {
this.searchBox.classList.remove("devtools-style-searchbox-no-match");
this.searchBox.value = "";

View File

@ -139,6 +139,7 @@ skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keybo
[browser_inspector_search-06.js]
[browser_inspector_search-07.js]
[browser_inspector_search-08.js]
[browser_inspector_search-filter_context-menu.js]
[browser_inspector_search_keyboard_trap.js]
[browser_inspector_search-reserved.js]
[browser_inspector_search-selection.js]

View File

@ -0,0 +1,68 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test inspector's markup view search filter context menu works properly.
const TEST_INPUT = "h1";
const TEST_URI = "<h1>test filter context menu</h1>";
add_task(function* () {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let {toolbox, inspector} = yield openInspector();
let {searchBox} = inspector;
yield selectNode("h1", inspector);
let win = inspector.panelWin;
let searchContextMenu = toolbox.textboxContextMenuPopup;
ok(searchContextMenu,
"The search filter context menu is loaded in the inspector");
let cmdUndo = searchContextMenu.querySelector("[command=cmd_undo]");
let cmdDelete = searchContextMenu.querySelector("[command=cmd_delete]");
let cmdSelectAll = searchContextMenu.querySelector("[command=cmd_selectAll]");
let cmdCut = searchContextMenu.querySelector("[command=cmd_cut]");
let cmdCopy = searchContextMenu.querySelector("[command=cmd_copy]");
let cmdPaste = searchContextMenu.querySelector("[command=cmd_paste]");
info("Opening context menu");
let onContextMenuPopup = once(searchContextMenu, "popupshowing");
EventUtils.synthesizeMouse(searchBox, 2, 2,
{type: "contextmenu", button: 2}, win);
yield onContextMenuPopup;
is(cmdUndo.getAttribute("disabled"), "true", "cmdUndo is disabled");
is(cmdDelete.getAttribute("disabled"), "true", "cmdDelete is disabled");
is(cmdSelectAll.getAttribute("disabled"), "", "cmdSelectAll is enabled");
is(cmdCut.getAttribute("disabled"), "true", "cmdCut is disabled");
is(cmdCopy.getAttribute("disabled"), "true", "cmdCopy is disabled");
is(cmdPaste.getAttribute("disabled"), "true", "cmdPaste is disabled");
info("Closing context menu");
let onContextMenuHidden = once(searchContextMenu, "popuphidden");
searchContextMenu.hidePopup();
yield onContextMenuHidden;
info("Copy text in search field using the context menu");
searchBox.value = TEST_INPUT;
searchBox.select();
EventUtils.synthesizeMouse(searchBox, 2, 2,
{type: "contextmenu", button: 2}, win);
yield onContextMenuPopup;
yield waitForClipboard(() => cmdCopy.click(), TEST_INPUT);
searchContextMenu.hidePopup();
yield onContextMenuHidden;
info("Reopen context menu and check command properties");
EventUtils.synthesizeMouse(searchBox, 2, 2,
{type: "contextmenu", button: 2}, win);
yield onContextMenuPopup;
is(cmdUndo.getAttribute("disabled"), "", "cmdUndo is enabled");
is(cmdDelete.getAttribute("disabled"), "", "cmdDelete is enabled");
is(cmdSelectAll.getAttribute("disabled"), "", "cmdSelectAll is enabled");
is(cmdCut.getAttribute("disabled"), "", "cmdCut is enabled");
is(cmdCopy.getAttribute("disabled"), "", "cmdCopy is enabled");
is(cmdPaste.getAttribute("disabled"), "", "cmdPaste is enabled");
});

View File

@ -245,6 +245,11 @@ html, body {
.browser {
display: block;
border: 0;
-moz-user-select: none;
}
.browser:-moz-focusring {
outline: none;
}
/**

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-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/. -->
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" fill="#0A0805" width="16" height="16">
<path d="M 0.00,160.00 L 512.00,160.00 L 480.00,480.00 L 32.00,480.00 L 0.00,160.00 Z M 464.00,96.00 L 480.00,128.00 L 32.00,128.00 L 64.00,64.00 L 240.00,64.00 L 256.00,96.00 L 464.00,96.00 Z"/>
</svg>
<!-- 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/. -->
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" stroke="#0b0b0b" fill="none" fill-rule="evenodd">
<path d="M.5 5.5v-2c0-.553.448-1 1-1H4c.553 0 1.268.358 1.6.802L6.5 4.5h5.997c.554 0 1.003.446 1.003.998v7.004c0 .55-.447.998-1 .998h-11c-.553 0-1-.453-1-.997V5.5zM1.5 6.5h11"/>
</svg>

Before

Width:  |  Height:  |  Size: 564 B

After

Width:  |  Height:  |  Size: 532 B

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-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/. -->
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" fill="#0A0805" width="16" height="16">
<path d="M 416.00,480.00L 512.00,224.00L 96.00,224.00L0.00,480.00 zM 64.00,192.00 L 0.00,480.00 L 0.00,64.00 L 144.00,64.00 L 208.00,128.00 L 416.00,128.00 L 416.00,192.00 Z"/>
</svg>
<!-- 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/. -->
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" stroke="#0b0b0b" fill="none" fill-rule="evenodd">
<path d="M.5 5.5v-2c0-.553.448-1 1-1H4c.553 0 1.268.358 1.6.802L6.5 4.5h5.997c.554 0 1.003.443 1.003 1v2H4.495c-.55 0-1.192.394-1.444.898l-2.1 4.204c-.25.496-.45.445-.45-.1V5.5z"/>
<path d="M.5 12v.508c0 .548.456.992 1.002.992h9.996c.553 0 1.2-.394 1.45-.898l2.103-4.204c.25-.496.004-.898-.55-.898H13"/>
</svg>

Before

Width:  |  Height:  |  Size: 545 B

After

Width:  |  Height:  |  Size: 660 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,8 +1,7 @@
<!-- 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/. -->
<svg width="16" xmlns="http://www.w3.org/2000/svg" height="16" viewBox="0 0 16 16" fill="#0A0805">
<path d="m1.3,12.5v-2.4c0,0 0,2.5 6.7,2.5 6.7,0 6.7-2.5 6.7-2.5v2.4c0,0 0,2.7-6.8,2.7-6.6,0-6.6-2.7-6.6-2.7z"/>
<path d="m14.7,3.4c0-1.4-3-2.5-6.7-2.5s-6.7,1.1-6.7,2.5c0,.2 0,.3 .1,.5-.1-.3-.1-.4-.1-.4v1.5c0,0 0,2.7 6.7,2.7 6.7,0 6.8-2.7 6.8-2.7v-1.6c0,.1 0,.2-.1,.5-0-.2-0-.3-0-.5z"/>
<path d="m1.3,8.7v-2.4c0,0 0,2.5 6.7,2.5 6.7,0 6.7-2.5 6.7-2.5v2.4c0,0 0,2.7-6.8,2.7-6.6-0-6.6-2.7-6.6-2.7z"/>
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#0b0b0b">
<path d="M7.5 7.556c3.006 0 5.5-1.136 5.5-2.778C13 3.136 10.506 2 7.5 2S2 3.136 2 4.778C2 6.42 4.494 7.556 7.5 7.556zm0-1c-2.517 0-4.5-.903-4.5-1.778S4.983 3 7.5 3s4.5.903 4.5 1.778-1.983 1.778-4.5 1.778zM7.5 14.445c3.006 0 5.5-1.137 5.5-2.778 0-.878-.595-1.606-1.657-2.081-.244-.11-.473-.107-.778-.033-.056.014-.565.158-.765.205-.626.148-1.342.231-2.3.231-.973 0-1.683-.082-2.273-.225a18.574 18.574 0 0 1-.673-.193c-.277-.076-.479-.089-.707-.005l-.035.014C2.638 10.064 2 10.756 2 11.667c0 1.641 2.494 2.778 5.5 2.778zm0-1c-2.517 0-4.5-.904-4.5-1.778 0-.432.354-.816 1.194-1.163h-.002c-.012.005.003.006.097.032-.056-.016.474.144.702.2.669.162 1.458.253 2.509.253 1.035 0 1.828-.092 2.53-.257.228-.054.74-.2.77-.207a.756.756 0 0 1 .134-.027c.734.329 1.066.735 1.066 1.169 0 .874-1.983 1.778-4.5 1.778z"/>
<path d="M7.5 10.945c3.006 0 5.5-1.137 5.5-2.778 0-.873-.62-1.601-1.693-2.082-.244-.109-.472-.106-.773-.032-.051.013-.551.158-.75.206-.615.147-1.326.23-2.284.23-.973 0-1.68-.082-2.265-.225a17.077 17.077 0 0 1-.66-.19c-.27-.076-.467-.092-.692-.015l-.054.02C2.65 6.568 2 7.259 2 8.168c0 1.641 2.494 2.778 5.5 2.778zm0-1C4.983 9.945 3 9.04 3 8.167c0-.426.364-.813 1.21-1.163l-.003.001c-.011.004.005.005.099.032-.079-.022.465.143.69.198.665.163 1.452.254 2.504.254 1.036 0 1.825-.092 2.517-.258.228-.054.733-.2.758-.207a.766.766 0 0 1 .124-.026c.748.335 1.101.75 1.101 1.169 0 .874-1.983 1.778-4.5 1.778z"/>
</svg>

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -124,7 +124,7 @@
is fixed and the all-tabs-menu is available again. */
#inspector-sidebar-container {
overflow: hidden;
min-width: 450px;
min-width: 300px;
position: relative;
}

View File

@ -307,6 +307,15 @@ nsCopySupport::HTMLCopy(nsISelection* aSel, nsIDocument* aDoc,
return SelectionCopyHelper(aSel, aDoc, true, aClipboardID, flags, nullptr);
}
nsresult
nsCopySupport::ClearSelectionCache()
{
nsresult rv;
nsCOMPtr<nsIClipboard> clipboard = do_GetService(kCClipboardCID, &rv);
clipboard->EmptyClipboard(nsIClipboard::kSelectionCache);
return rv;
}
nsresult
nsCopySupport::GetTransferableForSelection(nsISelection* aSel,
nsIDocument* aDoc,

View File

@ -25,6 +25,7 @@ class nsCopySupport
{
// class of static helper functions for copy support
public:
static nsresult ClearSelectionCache();
static nsresult HTMLCopy(nsISelection *aSel, nsIDocument *aDoc,
int16_t aClipboardID, bool aWithRubyAnnotation);
static nsresult DoHooks(nsIDocument *aDoc, nsITransferable *aTrans,

View File

@ -1247,6 +1247,18 @@ nsDOMWindowUtils::ForceUpdateNativeMenuAt(const nsAString& indexString)
return widget->ForceUpdateNativeMenuAt(indexString);
}
NS_IMETHODIMP
nsDOMWindowUtils::GetSelectionAsPlaintext(nsAString& aResult)
{
// Get the widget to send the event to.
nsCOMPtr<nsIWidget> widget = GetWidget();
if (!widget) {
return NS_ERROR_FAILURE;
}
return widget->GetSelectionAsPlaintext(aResult);
}
nsIWidget*
nsDOMWindowUtils::GetWidget(nsPoint* aOffset)
{

View File

@ -1957,6 +1957,9 @@ GK_ATOM(ondevicelight, "ondevicelight")
GK_ATOM(onmozinterruptbegin, "onmozinterruptbegin")
GK_ATOM(onmozinterruptend, "onmozinterruptend")
// MediaDevices device change event
GK_ATOM(ondevicechange, "ondevicechange")
//---------------------------------------------------------------------------
// Special atoms
//---------------------------------------------------------------------------

View File

@ -5101,6 +5101,7 @@ nsIContent*
EventStateManager::GetFocusedContent()
{
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
EnsureDocument(mPresContext);
if (!fm || !mDocument)
return nullptr;

View File

@ -1722,7 +1722,7 @@ HTMLMediaElement::Seek(double aTime,
if (mSrcStream) {
// do nothing since media streams have an empty Seekable range.
promise->MaybeRejectWithUndefined();
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
return promise.forget();
}
@ -1740,14 +1740,14 @@ HTMLMediaElement::Seek(double aTime,
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
mDefaultPlaybackStartPosition = aTime;
promise->MaybeRejectWithUndefined();
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
return promise.forget();
}
if (!mDecoder) {
// mDecoder must always be set in order to reach this point.
NS_ASSERTION(mDecoder, "SetCurrentTime failed: no decoder");
promise->MaybeRejectWithUndefined();
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
return promise.forget();
}
@ -1762,7 +1762,7 @@ HTMLMediaElement::Seek(double aTime,
uint32_t length = 0;
seekable->GetLength(&length);
if (!length) {
promise->MaybeRejectWithUndefined();
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
return promise.forget();
}

View File

@ -782,6 +782,15 @@ interface nsIDOMWindowUtils : nsISupports {
*/
void forceUpdateNativeMenuAt(in AString indexString);
/**
* Returns the current selection as plaintext. Note that the result may be
* different from the result of sendQueryContentEvent(QUERY_SELECTED_TEXT).
* This result is computed by native API with transferable data. In other
* words, when the OS treats the selection as plaintext, it treats current
* selection as this result.
*/
AString GetSelectionAsPlaintext();
/**
* Focus the element aElement. The element should be in the same document
* that the window is displaying. Pass null to blur the element, if any,

View File

@ -832,7 +832,7 @@ MediaDecoder::AsyncRejectSeekDOMPromiseIfExists()
if (mSeekDOMPromise) {
RefPtr<dom::Promise> promise = mSeekDOMPromise;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
promise->MaybeRejectWithUndefined();
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
});
AbstractThread::MainThread()->Dispatch(r.forget());
mSeekDOMPromise = nullptr;

View File

@ -179,6 +179,72 @@ NS_INTERFACE_MAP_BEGIN(MediaDevices)
NS_INTERFACE_MAP_ENTRY(MediaDevices)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
void
MediaDevices::OnDeviceChange()
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv = CheckInnerWindowCorrectness();
if (NS_FAILED(rv))
return;
if (!(MediaManager::Get()->IsActivelyCapturingOrHasAPermission(GetOwner()->WindowID()) ||
Preferences::GetBool("media.navigator.permission.disabled", false))) {
return;
}
DispatchTrustedEvent(NS_LITERAL_STRING("devicechange"));
}
mozilla::dom::EventHandlerNonNull*
MediaDevices::GetOndevicechange()
{
if (NS_IsMainThread()) {
return GetEventHandler(nsGkAtoms::ondevicechange, EmptyString());
}
return GetEventHandler(nullptr, NS_LITERAL_STRING("devicechange"));
}
void
MediaDevices::SetOndevicechange(mozilla::dom::EventHandlerNonNull* aCallback)
{
if (NS_IsMainThread()) {
SetEventHandler(nsGkAtoms::ondevicechange, EmptyString(), aCallback);
} else {
SetEventHandler(nullptr, NS_LITERAL_STRING("devicechange"), aCallback);
}
MediaManager::Get()->AddDeviceChangeCallback(this);
}
nsresult
MediaDevices::AddEventListener(const nsAString& aType,
nsIDOMEventListener* aListener,
bool aUseCapture, bool aWantsUntrusted,
uint8_t optional_argc)
{
MediaManager::Get()->AddDeviceChangeCallback(this);
return mozilla::DOMEventTargetHelper::AddEventListener(aType, aListener,
aUseCapture,
aWantsUntrusted,
optional_argc);
}
void
MediaDevices::AddEventListener(const nsAString& aType,
dom::EventListener* aListener,
const dom::AddEventListenerOptionsOrBoolean& aOptions,
const dom::Nullable<bool>& aWantsUntrusted,
ErrorResult& aRv)
{
MediaManager::Get()->AddDeviceChangeCallback(this);
return mozilla::DOMEventTargetHelper::AddEventListener(aType, aListener,
aOptions,
aWantsUntrusted,
aRv);
}
JSObject*
MediaDevices::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{

View File

@ -10,6 +10,7 @@
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/BindingUtils.h"
#include "nsPIDOMWindow.h"
#include "DeviceChangeCallback.h"
namespace mozilla {
namespace dom {
@ -23,6 +24,7 @@ struct MediaTrackSupportedConstraints;
{ 0x9a, 0x36, 0x74, 0xa4, 0xd6, 0x71, 0xa6, 0xc8 } }
class MediaDevices final : public DOMEventTargetHelper
,public DeviceChangeCallback
{
public:
explicit MediaDevices(nsPIDOMWindowInner* aWindow) :
@ -42,6 +44,23 @@ public:
already_AddRefed<Promise>
EnumerateDevices(ErrorResult &aRv);
virtual void OnDeviceChange() override;
mozilla::dom::EventHandlerNonNull* GetOndevicechange();
void SetOndevicechange(mozilla::dom::EventHandlerNonNull* aCallback);
NS_IMETHOD AddEventListener(const nsAString& aType,
nsIDOMEventListener* aListener,
bool aUseCapture, bool aWantsUntrusted,
uint8_t optional_argc) override;
virtual void AddEventListener(const nsAString& aType,
dom::EventListener* aListener,
const dom::AddEventListenerOptionsOrBoolean& aOptions,
const dom::Nullable<bool>& aWantsUntrusted,
ErrorResult& aRv) override;
private:
class GumResolver;
class EnumDevResolver;

View File

@ -44,6 +44,7 @@
#include "mozilla/dom/MediaStreamTrackBinding.h"
#include "mozilla/dom/GetUserMediaRequestBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/MediaDevices.h"
#include "mozilla/Base64.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/media/MediaChild.h"
@ -1708,6 +1709,7 @@ MediaManager::MediaManager()
mPrefs.mNoiseOn = false;
mPrefs.mExtendedFilter = true;
mPrefs.mDelayAgnostic = true;
mPrefs.mFakeDeviceChangeEventOn = false;
#ifdef MOZ_WEBRTC
mPrefs.mAec = webrtc::kEcUnchanged;
mPrefs.mAgc = webrtc::kAgcUnchanged;
@ -1806,6 +1808,7 @@ MediaManager::Get() {
prefs->AddObserver("media.getusermedia.noise_enabled", sSingleton, false);
prefs->AddObserver("media.getusermedia.noise", sSingleton, false);
prefs->AddObserver("media.getusermedia.playout_delay", sSingleton, false);
prefs->AddObserver("media.ondevicechange.fakeDeviceChangeEvent.enabled", sSingleton, false);
#endif
}
@ -1996,6 +1999,28 @@ bool MediaManager::IsPrivateBrowsing(nsPIDOMWindowInner* window)
return loadContext && loadContext->UsePrivateBrowsing();
}
int MediaManager::AddDeviceChangeCallback(DeviceChangeCallback* aCallback)
{
bool fakeDeviceChangeEventOn = mPrefs.mFakeDeviceChangeEventOn;
MediaManager::PostTask(NewTaskFrom([fakeDeviceChangeEventOn]() {
RefPtr<MediaManager> manager = MediaManager_GetInstance();
manager->GetBackend(0)->AddDeviceChangeCallback(manager);
if (fakeDeviceChangeEventOn)
manager->GetBackend(0)->SetFakeDeviceChangeEvents();
}));
return DeviceChangeCallback::AddDeviceChangeCallback(aCallback);
}
void MediaManager::OnDeviceChange() {
RefPtr<MediaManager> self(this);
NS_DispatchToMainThread(media::NewRunnableFrom([self,this]() mutable {
MOZ_ASSERT(NS_IsMainThread());
DeviceChangeCallback::OnDeviceChange();
return NS_OK;
}));
}
nsresult MediaManager::GenerateUUID(nsAString& aResult)
{
nsresult rv;
@ -2725,6 +2750,25 @@ MediaManager::OnNavigation(uint64_t aWindowID)
} else {
RemoveWindowID(aWindowID);
}
RemoveMediaDevicesCallback(aWindowID);
}
void
MediaManager::RemoveMediaDevicesCallback(uint64_t aWindowID)
{
for (DeviceChangeCallback* observer : mDeviceChangeCallbackList)
{
dom::MediaDevices* mediadevices = static_cast<dom::MediaDevices *>(observer);
MOZ_ASSERT(mediadevices);
if (mediadevices) {
nsPIDOMWindowInner* window = mediadevices->GetOwner();
MOZ_ASSERT(window);
if (window && window->WindowID() == aWindowID)
DeviceChangeCallback::RemoveDeviceChangeCallback(observer);
return;
}
}
}
StreamListeners*
@ -2836,6 +2880,7 @@ MediaManager::GetPrefs(nsIPrefBranch *aBranch, const char *aData)
GetPref(aBranch, "media.getusermedia.playout_delay", aData, &mPrefs.mPlayoutDelay);
GetPrefBool(aBranch, "media.getusermedia.aec_extended_filter", aData, &mPrefs.mExtendedFilter);
GetPrefBool(aBranch, "media.getusermedia.aec_aec_delay_agnostic", aData, &mPrefs.mDelayAgnostic);
GetPrefBool(aBranch, "media.ondevicechange.fakeDeviceChangeEvent.enabled", aData, &mPrefs.mFakeDeviceChangeEventOn);
#endif
GetPrefBool(aBranch, "media.navigator.audio.full_duplex", aData, &mPrefs.mFullDuplex);
}
@ -2872,6 +2917,7 @@ MediaManager::Shutdown()
prefs->RemoveObserver("media.getusermedia.noise_enabled", this);
prefs->RemoveObserver("media.getusermedia.noise", this);
prefs->RemoveObserver("media.getusermedia.playout_delay", this);
prefs->RemoveObserver("media.ondevicechange.fakeDeviceChangeEvent.enabled", this);
#endif
prefs->RemoveObserver("media.navigator.audio.full_duplex", this);
}
@ -2904,6 +2950,7 @@ MediaManager::Shutdown()
{
if (mManager->mBackend) {
mManager->mBackend->Shutdown(); // ok to invoke multiple times
mManager->mBackend->RemoveDeviceChangeCallback(mManager);
}
}
mozilla::ipc::BackgroundChild::CloseForCurrentThread();

View File

@ -6,6 +6,7 @@
#define MOZILLA_MEDIAMANAGER_H
#include "MediaEngine.h"
#include "DeviceChangeCallback.h"
#include "mozilla/Services.h"
#include "mozilla/unused.h"
#include "nsAutoPtr.h"
@ -187,6 +188,7 @@ typedef void (*WindowListenerCallback)(MediaManager *aThis,
class MediaManager final : public nsIMediaManagerService,
public nsIObserver
,public DeviceChangeCallback
{
friend GetUserMediaCallbackMediaStreamListener;
public:
@ -256,6 +258,9 @@ public:
typedef nsTArray<RefPtr<MediaDevice>> SourceSet;
static bool IsPrivateBrowsing(nsPIDOMWindowInner* window);
virtual int AddDeviceChangeCallback(DeviceChangeCallback* aCallback) override;
virtual void OnDeviceChange() override;
private:
typedef media::Pledge<SourceSet*, dom::MediaStreamError*> PledgeSourceSet;
typedef media::Pledge<const char*, dom::MediaStreamError*> PledgeChar;
@ -308,6 +313,7 @@ private:
void *aData);
void StopMediaStreams();
void RemoveMediaDevicesCallback(uint64_t aWindowID);
// ONLY access from MainThread so we don't need to lock
WindowTable mActiveWindows;

View File

@ -145,7 +145,8 @@ FFmpegVideoDecoder<LIBAV_VER>::InitCodecContext()
decode_threads = 2;
}
decode_threads = std::min(decode_threads, PR_GetNumberOfProcessors());
decode_threads = std::min(decode_threads, PR_GetNumberOfProcessors() - 1);
decode_threads = std::max(decode_threads, 1);
mCodecContext->thread_count = decode_threads;
if (decode_threads > 1) {
mCodecContext->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME;

View File

@ -25,6 +25,9 @@ mozilla::LazyLogModule gCamerasChildLog("CamerasChild");
#define LOG(args) MOZ_LOG(gCamerasChildLog, mozilla::LogLevel::Debug, args)
#define LOG_ENABLED() MOZ_LOG_TEST(gCamerasChildLog, mozilla::LogLevel::Debug)
#define FAKE_ONDEVICECHANGE_EVENT_PERIOD_IN_MS 5000
#define FAKE_ONDEVICECHANGE_EVENT_REPEAT_COUNT 30
namespace mozilla {
namespace camera {
@ -39,6 +42,34 @@ CamerasSingleton::~CamerasSingleton() {
LOG(("~CamerasSingleton: %p", this));
}
class FakeOnDeviceChangeEventRunnable : public Runnable
{
public:
explicit FakeOnDeviceChangeEventRunnable(uint8_t counter)
: mCounter(counter) {}
NS_IMETHOD Run() override
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = CamerasSingleton::Child();
if (child) {
child->OnDeviceChange();
if (mCounter++ < FAKE_ONDEVICECHANGE_EVENT_REPEAT_COUNT) {
RefPtr<FakeOnDeviceChangeEventRunnable> evt = new FakeOnDeviceChangeEventRunnable(mCounter);
CamerasSingleton::Thread()->DelayedDispatch(evt.forget(),
FAKE_ONDEVICECHANGE_EVENT_PERIOD_IN_MS);
}
}
return NS_OK;
}
private:
uint8_t mCounter;
};
class InitializeIPCThread : public Runnable
{
public:
@ -108,6 +139,12 @@ GetCamerasChild() {
return CamerasSingleton::Child();
}
CamerasChild*
GetCamerasChildIfExists() {
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
return CamerasSingleton::Child();
}
bool
CamerasChild::RecvReplyFailure(void)
{
@ -579,6 +616,26 @@ CamerasChild::RecvDeliverFrame(const int& capEngine,
return true;
}
bool
CamerasChild::RecvDeviceChange()
{
this->OnDeviceChange();
return true;
}
int
CamerasChild::SetFakeDeviceChangeEvents()
{
CamerasSingleton::Mutex().AssertCurrentThreadOwns();
// To simulate the devicechange event in mochitest,
// we fire a fake devicechange event in Camera IPC thread periodically
RefPtr<FakeOnDeviceChangeEventRunnable> evt = new FakeOnDeviceChangeEventRunnable(0);
CamerasSingleton::Thread()->Dispatch(evt.forget(), NS_DISPATCH_NORMAL);
return 0;
}
bool
CamerasChild::RecvFrameSizeChange(const int& capEngine,
const int& capId,

View File

@ -12,6 +12,7 @@
#include "mozilla/dom/ContentChild.h"
#include "mozilla/camera/PCamerasChild.h"
#include "mozilla/camera/PCamerasParent.h"
#include "DeviceChangeCallback.h"
#include "mozilla/Mutex.h"
#include "base/singleton.h"
#include "nsCOMPtr.h"
@ -118,6 +119,8 @@ private:
// it will set up the CamerasSingleton.
CamerasChild* GetCamerasChild();
CamerasChild* GetCamerasChildIfExists();
// Shut down the IPC channel and everything associated, like WebRTC.
// This is a static call because the CamerasChild object may not even
// be alive when we're called.
@ -138,6 +141,7 @@ int GetChildAndCall(MEM_FUN&& f, ARGS&&... args)
}
class CamerasChild final : public PCamerasChild
,public DeviceChangeCallback
{
friend class mozilla::ipc::BackgroundChildImpl;
template <class T> friend class mozilla::camera::LockAndDispatch;
@ -155,6 +159,9 @@ public:
virtual bool RecvFrameSizeChange(const int&, const int&,
const int& w, const int& h) override;
virtual bool RecvDeviceChange() override;
int SetFakeDeviceChangeEvents();
// these are response messages to our outgoing requests
virtual bool RecvReplyNumberOfCaptureDevices(const int&) override;
virtual bool RecvReplyNumberOfCapabilities(const int&) override;

View File

@ -39,6 +39,24 @@ namespace camera {
// called "VideoCapture". On Windows this is a thread with an event loop
// suitable for UI access.
void InputObserver::DeviceChange() {
LOG((__PRETTY_FUNCTION__));
MOZ_ASSERT(mParent);
RefPtr<nsIRunnable> ipc_runnable =
media::NewRunnableFrom([this]() -> nsresult {
if (mParent->IsShuttingDown()) {
return NS_ERROR_FAILURE;
}
Unused << mParent->SendDeviceChange();
return NS_OK;
});
nsIThread* thread = mParent->GetBackgroundThread();
MOZ_ASSERT(thread != nullptr);
thread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
};
class FrameSizeChangeRunnable : public Runnable {
public:
FrameSizeChangeRunnable(CamerasParent *aParent, CaptureEngine capEngine,
@ -399,6 +417,15 @@ CamerasParent::SetupEngine(CaptureEngine aCapEngine)
return false;
}
InputObserver** observer = mObservers.AppendElement(
new InputObserver(this));
#ifdef DEBUG
MOZ_ASSERT(0 == helper->mPtrViECapture->RegisterInputObserver(*observer));
#else
helper->mPtrViECapture->RegisterInputObserver(*observer);
#endif
helper->mPtrViERender = webrtc::ViERender::GetInterface(helper->mEngine);
if (!helper->mPtrViERender) {
LOG(("ViERender::GetInterface failed"));
@ -435,6 +462,12 @@ CamerasParent::CloseEngines()
mEngines[i].mPtrViERender = nullptr;
}
if (mEngines[i].mPtrViECapture) {
#ifdef DEBUG
MOZ_ASSERT(0 == mEngines[i].mPtrViECapture->DeregisterInputObserver());
#else
mEngines[i].mPtrViECapture->DeregisterInputObserver();
#endif
mEngines[i].mPtrViECapture->Release();
mEngines[i].mPtrViECapture = nullptr;
}
@ -449,6 +482,11 @@ CamerasParent::CloseEngines()
}
}
for (InputObserver* observer : mObservers) {
delete observer;
}
mObservers.Clear();
mWebRTCAlive = false;
}

View File

@ -76,6 +76,19 @@ public:
bool mEngineIsRunning;
};
class InputObserver : public webrtc::ViEInputObserver
{
public:
explicit InputObserver(CamerasParent* aParent)
: mParent(aParent) {};
virtual void DeviceChange();
friend CamerasParent;
private:
RefPtr<CamerasParent> mParent;
};
class CamerasParent : public PCamerasParent,
public nsIObserver
{
@ -153,6 +166,7 @@ protected:
// Above 2 are PBackground only, but this is potentially
// read cross-thread.
mozilla::Atomic<bool> mWebRTCAlive;
nsTArray<InputObserver*> mObservers;
};
PCamerasParent* CreateCamerasParent();

View File

@ -0,0 +1,57 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et ft=cpp : */
/* 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_DeviceChangeCallback_h
#define mozilla_DeviceChangeCallback_h
namespace mozilla {
class DeviceChangeCallback
{
public:
virtual void OnDeviceChange()
{
MutexAutoLock lock(mCallbackMutex);
for (DeviceChangeCallback* observer : mDeviceChangeCallbackList)
{
observer->OnDeviceChange();
}
}
virtual int AddDeviceChangeCallback(DeviceChangeCallback* aCallback)
{
MutexAutoLock lock(mCallbackMutex);
if (mDeviceChangeCallbackList.IndexOf(aCallback) == mDeviceChangeCallbackList.NoIndex)
mDeviceChangeCallbackList.AppendElement(aCallback);
return 0;
}
virtual int RemoveDeviceChangeCallback(DeviceChangeCallback* aCallback)
{
MutexAutoLock lock(mCallbackMutex);
if (mDeviceChangeCallbackList.IndexOf(aCallback) != mDeviceChangeCallbackList.NoIndex)
mDeviceChangeCallbackList.RemoveElement(aCallback);
return 0;
}
DeviceChangeCallback() : mCallbackMutex("mozilla::media::DeviceChangeCallback::mCallbackMutex")
{
mDeviceChangeCallbackList.Clear();
}
virtual ~DeviceChangeCallback()
{
mDeviceChangeCallbackList.Clear();
}
protected:
nsTArray<DeviceChangeCallback*> mDeviceChangeCallbackList;
Mutex mCallbackMutex;
};
} // namespace mozilla
#endif // mozilla_DeviceChangeCallback_h

View File

@ -29,6 +29,7 @@ child:
async DeliverFrame(int capEngine, int cap_id,
Shmem buffer, size_t size, uint32_t time_stamp,
int64_t ntp_time, int64_t render_time);
async DeviceChange();
async ReplyNumberOfCaptureDevices(int numdev);
async ReplyNumberOfCapabilities(int numdev);
async ReplyAllocateCaptureDevice(int numdev);

View File

@ -8,6 +8,7 @@ if CONFIG['MOZ_WEBRTC']:
EXPORTS += [
'CamerasChild.h',
'CamerasParent.h',
'DeviceChangeCallback.h',
'LoadManager.h',
'LoadManagerFactory.h',
'LoadMonitor.h',

View File

@ -509,11 +509,9 @@ var gInfoLeakTests = [
];
// These are files that must fire an error during load or playback, and do not
// cause a crash. Put files of the same type together in this list so if
// something crashes we have some idea of which backend is responsible. Used
// by test_playback_errors, which expects one error event and no ended event.
// Put files of the same type together in this list so if something crashes
// we have some idea of which backend is responsible.
// cause a crash. Used by test_playback_errors, which expects one error event
// and no ended event. Put files of the same type together in this list so if
// something crashes we have some idea of which backend is responsible.
var gErrorTests = [
{ name:"bogus.wav", type:"audio/x-wav" },
{ name:"bogus.ogv", type:"video/ogg" },

View File

@ -670,9 +670,7 @@ skip-if = toolkit == 'android' || toolkit == 'gonk' # android: bug 1149374; gonk
tags=msg capturestream
skip-if = toolkit == 'android' || toolkit == 'gonk' # android: bug 1149374; gonk: bug 1193351
[test_empty_resource.html]
skip-if = os == 'win' && debug #win debug : Bug 1202683
[test_error_in_video_document.html]
skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634
[test_error_on_404.html]
[test_fastSeek.html]
[test_fastSeek-forwards.html]

View File

@ -56,6 +56,7 @@ if (!t) {
info("Error occured by the time we got |load| - checking directly.");
check();
} else {
//TODO: Fix this todo in Bug 1295923.
todo(false, "Error hasn't occurred yet - adding |error| event listener. This shouldn't happen, see bug 608634.");
documentVideo().addEventListener("error", check);
}

View File

@ -63,7 +63,6 @@ function startTest(test, token) {
is(test.name, v.name, test.name + ": Name should match #2");
checkMetadata(test.name, v, test);
is(v.readyState, v.HAVE_CURRENT_DATA, test.name + " checking readyState");
is(v.networkState, v.NETWORK_IDLE, test.name + " checking networkState");
ok(v.ended, test.name + " checking playback has ended");
ok(!v.finished, test.name + " shouldn't be finished");
ok(!v.seenEnded, test.name + " shouldn't be ended");

View File

@ -63,7 +63,6 @@ function startTest(test, token) {
is(test.name, v.name, test.name + ": Name should match #2");
checkMetadata(test.name, v, test);
is(v.readyState, v.HAVE_CURRENT_DATA, test.name + " checking readyState");
is(v.networkState, v.NETWORK_IDLE, test.name + " checking networkState");
ok(v.ended, test.name + " checking playback has ended");
finish();

View File

@ -40,6 +40,8 @@ skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g emulator seems to be to
[test_dataChannel_noOffer.html]
[test_enumerateDevices.html]
skip-if = buildapp == 'mulet'
[test_ondevicechange.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || os == 'linux' || os == 'win' || os == 'android'
[test_getUserMedia_audioCapture.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version == '18' # b2g emulator seems to be too slow (Bug 1016498 and 1008080), android(Bug 1189784, timeouts on 4.3 emulator)
[test_getUserMedia_addTrackRemoveTrack.html]

View File

@ -0,0 +1,80 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1041393
-->
<head>
<meta charset="utf-8">
<title>onndevicechange tests</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1152383">ondevicechange tests</a>
<script type="application/javascript">
const RESPONSE_WAIT_TIME_MS = 10000;
function wait(time, message) {
return new Promise(r => setTimeout(() => r(message), time));
}
function OnDeviceChangeEvent() {
return new Promise(resolve => navigator.mediaDevices.ondevicechange = resolve);
}
function OnDeviceChangeEventReceived() {
return Promise.race([
OnDeviceChangeEvent(),
wait(RESPONSE_WAIT_TIME_MS).then(() => Promise.reject("Timed out while waiting for devicechange event"))
]);
}
function OnDeviceChangeEventNotReceived() {
return Promise.race([
OnDeviceChangeEvent().then(() => Promise.reject("ondevicechange event is fired unexpectedly.")),
wait(RESPONSE_WAIT_TIME_MS)
]);
}
var pushPrefs = (...p) => new Promise(r => SpecialPowers.pushPrefEnv({set: p}, r));
var videoTracks;
SimpleTest.requestCompleteLog();
SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout("Fake devicechange event is fired periodically, \
so we need to wait a while to make sure the event is fired or not as we expect.");
var videoTracks;
function wait(time, message) {
return new Promise(r => setTimeout(() => r(message), time));
}
pushPrefs(["media.ondevicechange.fakeDeviceChangeEvent.enabled", true])
.then(() => pushPrefs(["media.navigator.permission.disabled", false]))
.then(() => pushPrefs(["media.ondevicechange.enabled", true]))
.then(() => info("assure devicechange event is NOT fired when gUM is NOT in use and permanent permission is NOT granted"))
.then(() => OnDeviceChangeEventNotReceived())
.then(() => ok(true, "devicechange event is NOT fired when gUM is NOT in use and permanent permission is NOT granted"))
.then(() => pushPrefs(['media.navigator.permission.disabled', true]))
.then(() => info("assure devicechange event is fired when gUM is NOT in use and permanent permission is granted"))
.then(() => OnDeviceChangeEventReceived())
.then(() => ok(true, "devicechange event is fired when gUM is NOT in use and permanent permission is granted"))
.then(() => navigator.mediaDevices.getUserMedia({video: true, fake: true}))
.then(st => {videoTracks = st.getVideoTracks();})
.then(() => info("assure devicechange event is fired when gUM is in use"))
.then(() => OnDeviceChangeEventReceived())
.then(() => ok(true, "devicechange event is fired when gUM is in use"))
.catch(e => ok(false, "Error: " + e))
.then(() => {
if(videoTracks)
videoTracks.forEach(track => track.stop());
})
.then(() => SimpleTest.finish());
</script>
</pre>
</body>
</html>

View File

@ -152,13 +152,18 @@ AudioNodeExternalInputStream::ProcessInput(GraphTime aFrom, GraphTime aTo,
MediaStream* source = mInputs[0]->GetSource();
AutoTArray<AudioSegment,1> audioSegments;
uint32_t inputChannels = 0;
for (StreamTracks::TrackIter tracks(source->mTracks, MediaSegment::AUDIO);
for (StreamTracks::TrackIter tracks(source->mTracks);
!tracks.IsEnded(); tracks.Next()) {
const StreamTracks::Track& inputTrack = *tracks;
if (!mInputs[0]->PassTrackThrough(tracks->GetID())) {
continue;
}
if (inputTrack.GetSegment()->GetType() == MediaSegment::VIDEO) {
MOZ_ASSERT(false, "AudioNodeExternalInputStream shouldn't have video tracks");
continue;
}
const AudioSegment& inputSegment =
*static_cast<AudioSegment*>(inputTrack.GetSegment());
if (inputSegment.IsNull()) {

View File

@ -96,6 +96,8 @@ void
MediaStreamAudioSourceNode::AttachToTrack(const RefPtr<MediaStreamTrack>& aTrack)
{
MOZ_ASSERT(!mInputTrack);
MOZ_ASSERT(aTrack->AsAudioStreamTrack());
if (!mStream) {
return;
}
@ -148,6 +150,10 @@ MediaStreamAudioSourceNode::NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTr
return;
}
if (!aTrack->AsAudioStreamTrack()) {
return;
}
AttachToTrack(aTrack);
}

View File

@ -11,6 +11,7 @@
#include "MediaTrackConstraints.h"
#include "mozilla/dom/MediaStreamTrackBinding.h"
#include "mozilla/dom/VideoStreamTrack.h"
#include "DeviceChangeCallback.h"
namespace mozilla {
@ -41,7 +42,7 @@ enum MediaEngineState {
kReleased
};
class MediaEngine
class MediaEngine : public DeviceChangeCallback
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEngine)
@ -75,6 +76,8 @@ public:
virtual void Shutdown() = 0;
virtual void SetFakeDeviceChangeEvents() {}
protected:
virtual ~MediaEngine() {}
};
@ -100,6 +103,7 @@ public:
, mFullDuplex(false)
, mExtendedFilter(false)
, mDelayAgnostic(false)
, mFakeDeviceChangeEventOn(false)
{}
int32_t mWidth;
@ -117,6 +121,7 @@ public:
bool mFullDuplex;
bool mExtendedFilter;
bool mDelayAgnostic;
bool mFakeDeviceChangeEventOn;
// mWidth and/or mHeight may be zero (=adaptive default), so use functions.

View File

@ -126,6 +126,17 @@ MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
#endif
// XXX
gFarendObserver = new AudioOutputObserver();
camera::GetChildAndCall(
&camera::CamerasChild::AddDeviceChangeCallback,
this);
}
void
MediaEngineWebRTC::SetFakeDeviceChangeEvents()
{
camera::GetChildAndCall(
&camera::CamerasChild::SetFakeDeviceChangeEvents);
}
void
@ -421,6 +432,11 @@ MediaEngineWebRTC::Shutdown()
// This is likely paranoia
MutexAutoLock lock(mMutex);
if (camera::GetCamerasChildIfExists()) {
camera::GetChildAndCall(
&camera::CamerasChild::RemoveDeviceChangeCallback, this);
}
LOG(("%s", __FUNCTION__));
// Shutdown all the sources, since we may have dangling references to the
// sources in nsDOMUserMediaStreams waiting for GC/CC

View File

@ -572,6 +572,8 @@ class MediaEngineWebRTC : public MediaEngine
public:
explicit MediaEngineWebRTC(MediaEnginePrefs& aPrefs);
virtual void SetFakeDeviceChangeEvents() override;
// Clients should ensure to clean-up sources video/audio sources
// before invoking Shutdown on this class.
void Shutdown() override;

View File

@ -12,7 +12,8 @@
[Func="Navigator::HasUserMediaSupport"]
interface MediaDevices : EventTarget {
// attribute EventHandler ondevicechange;
[Pref="media.ondevicechange.enabled"]
attribute EventHandler ondevicechange;
MediaTrackSupportedConstraints getSupportedConstraints();
[Throws]

View File

@ -144,6 +144,13 @@ GPUParent::RecvNewContentVRManager(Endpoint<PVRManagerParent>&& aEndpoint)
return VRManagerParent::CreateForContent(Move(aEndpoint));
}
bool
GPUParent::RecvDeallocateLayerTreeId(const uint64_t& aLayersId)
{
CompositorBridgeParent::DeallocateLayerTreeId(aLayersId);
return true;
}
void
GPUParent::ActorDestroy(ActorDestroyReason aWhy)
{

View File

@ -40,6 +40,7 @@ public:
bool RecvNewContentCompositorBridge(Endpoint<PCompositorBridgeParent>&& aEndpoint) override;
bool RecvNewContentImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override;
bool RecvNewContentVRManager(Endpoint<PVRManagerParent>&& aEndpoint) override;
bool RecvDeallocateLayerTreeId(const uint64_t& aLayersId) override;
void ActorDestroy(ActorDestroyReason aWhy) override;

View File

@ -504,6 +504,10 @@ GPUProcessManager::AllocateLayerTreeId()
void
GPUProcessManager::DeallocateLayerTreeId(uint64_t aLayersId)
{
if (mGPUChild) {
mGPUChild->SendDeallocateLayerTreeId(aLayersId);
return;
}
CompositorBridgeParent::DeallocateLayerTreeId(aLayersId);
}

View File

@ -53,6 +53,8 @@ parent:
async NewContentCompositorBridge(Endpoint<PCompositorBridgeParent> endpoint);
async NewContentImageBridge(Endpoint<PImageBridgeParent> endpoint);
async NewContentVRManager(Endpoint<PVRManagerParent> endpoint);
async DeallocateLayerTreeId(uint64_t layersId);
};
} // namespace gfx

View File

@ -48,6 +48,7 @@ class CancelableRunnable;
namespace gfx {
class DrawTarget;
class GPUProcessManager;
class GPUParent;
} // namespace gfx
namespace ipc {
@ -210,6 +211,7 @@ class CompositorBridgeParent final : public PCompositorBridgeParent,
friend class CompositorThreadHolder;
friend class InProcessCompositorSession;
friend class gfx::GPUProcessManager;
friend class gfx::GPUParent;
public:
explicit CompositorBridgeParent(CSSToLayoutDeviceScale aScale,

View File

@ -0,0 +1,167 @@
/* -*- 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 "mozilla/Move.h"
#include "mozilla/mscom/DispatchForwarder.h"
#include "mozilla/mscom/MainThreadInvoker.h"
#include <oleauto.h>
namespace mozilla {
namespace mscom {
/* static */ HRESULT
DispatchForwarder::Create(IInterceptor* aInterceptor,
STAUniquePtr<IDispatch>& aTarget, IUnknown** aOutput)
{
MOZ_ASSERT(aInterceptor && aOutput);
if (!aOutput) {
return E_INVALIDARG;
}
*aOutput = nullptr;
if (!aInterceptor) {
return E_INVALIDARG;
}
DispatchForwarder* forwarder = new DispatchForwarder(aInterceptor, aTarget);
HRESULT hr = forwarder->QueryInterface(IID_IDispatch, (void**) aOutput);
forwarder->Release();
return hr;
}
DispatchForwarder::DispatchForwarder(IInterceptor* aInterceptor,
STAUniquePtr<IDispatch>& aTarget)
: mRefCnt(1)
, mInterceptor(aInterceptor)
, mTarget(Move(aTarget))
{
}
DispatchForwarder::~DispatchForwarder()
{
}
HRESULT
DispatchForwarder::QueryInterface(REFIID riid, void** ppv)
{
if (!ppv) {
return E_INVALIDARG;
}
// Since this class implements a tearoff, any interfaces that are not
// IDispatch must be routed to the original object's QueryInterface.
// This is especially important for IUnknown since COM uses that interface
// to determine object identity.
if (riid != IID_IDispatch) {
return mInterceptor->QueryInterface(riid, ppv);
}
IUnknown* punk = static_cast<IDispatch*>(this);
*ppv = punk;
if (!punk) {
return E_NOINTERFACE;
}
punk->AddRef();
return S_OK;
}
ULONG
DispatchForwarder::AddRef()
{
return (ULONG) InterlockedIncrement((LONG*)&mRefCnt);
}
ULONG
DispatchForwarder::Release()
{
ULONG newRefCnt = (ULONG) InterlockedDecrement((LONG*)&mRefCnt);
if (newRefCnt == 0) {
delete this;
}
return newRefCnt;
}
HRESULT
DispatchForwarder::GetTypeInfoCount(UINT *pctinfo)
{
if (!pctinfo) {
return E_INVALIDARG;
}
*pctinfo = 1;
return S_OK;
}
HRESULT
DispatchForwarder::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
{
// ITypeInfo as implemented by COM is apartment-neutral, so we don't need
// to wrap it (yay!)
if (mTypeInfo) {
*ppTInfo = mTypeInfo.get();
mTypeInfo->AddRef();
return S_OK;
}
HRESULT hr = E_UNEXPECTED;
auto fn = [&]() -> void {
hr = mTarget->GetTypeInfo(iTInfo, lcid, ppTInfo);
};
MainThreadInvoker invoker;
if (!invoker.Invoke(NS_NewRunnableFunction(fn))) {
return E_UNEXPECTED;
}
if (FAILED(hr)) {
return hr;
}
mTypeInfo = *ppTInfo;
return hr;
}
HRESULT
DispatchForwarder::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames,
LCID lcid, DISPID *rgDispId)
{
HRESULT hr = E_UNEXPECTED;
auto fn = [&]() -> void {
hr = mTarget->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
};
MainThreadInvoker invoker;
if (!invoker.Invoke(NS_NewRunnableFunction(fn))) {
return E_UNEXPECTED;
}
return hr;
}
HRESULT
DispatchForwarder::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
WORD wFlags, DISPPARAMS *pDispParams,
VARIANT *pVarResult, EXCEPINFO *pExcepInfo,
UINT *puArgErr)
{
HRESULT hr;
if (!mInterface) {
if (!mTypeInfo) {
return E_UNEXPECTED;
}
TYPEATTR* typeAttr = nullptr;
hr = mTypeInfo->GetTypeAttr(&typeAttr);
if (FAILED(hr)) {
return hr;
}
hr = mInterceptor->QueryInterface(typeAttr->guid,
(void**)getter_AddRefs(mInterface));
mTypeInfo->ReleaseTypeAttr(typeAttr);
if (FAILED(hr)) {
return hr;
}
}
// We don't invoke IDispatch on the target, but rather on the interceptor!
hr = ::DispInvoke(mInterface.get(), mTypeInfo, dispIdMember, wFlags,
pDispParams, pVarResult, pExcepInfo, puArgErr);
return hr;
}
} // namespace mscom
} // namespace mozilla

View File

@ -0,0 +1,81 @@
/* -*- 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_mscom_DispatchForwarder_h
#define mozilla_mscom_DispatchForwarder_h
#include <oaidl.h>
#include "mozilla/mscom/Interceptor.h"
#include "mozilla/mscom/Ptr.h"
namespace mozilla {
namespace mscom {
class DispatchForwarder : public IDispatch
{
public:
static HRESULT Create(IInterceptor* aInterceptor,
STAUniquePtr<IDispatch>& aTarget, IUnknown** aOutput);
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
STDMETHODIMP_(ULONG) AddRef() override;
STDMETHODIMP_(ULONG) Release() override;
// IDispatch
STDMETHODIMP GetTypeInfoCount(
/* [out] */ __RPC__out UINT *pctinfo) override;
STDMETHODIMP GetTypeInfo(
/* [in] */ UINT iTInfo,
/* [in] */ LCID lcid,
/* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo) override;
STDMETHODIMP GetIDsOfNames(
/* [in] */ __RPC__in REFIID riid,
/* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
/* [range][in] */ __RPC__in_range(0,16384) UINT cNames,
/* [in] */ LCID lcid,
/* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId)
override;
STDMETHODIMP Invoke(
/* [annotation][in] */
_In_ DISPID dispIdMember,
/* [annotation][in] */
_In_ REFIID riid,
/* [annotation][in] */
_In_ LCID lcid,
/* [annotation][in] */
_In_ WORD wFlags,
/* [annotation][out][in] */
_In_ DISPPARAMS *pDispParams,
/* [annotation][out] */
_Out_opt_ VARIANT *pVarResult,
/* [annotation][out] */
_Out_opt_ EXCEPINFO *pExcepInfo,
/* [annotation][out] */
_Out_opt_ UINT *puArgErr) override;
private:
DispatchForwarder(IInterceptor* aInterceptor,
STAUniquePtr<IDispatch>& aTarget);
~DispatchForwarder();
private:
ULONG mRefCnt;
RefPtr<IInterceptor> mInterceptor;
STAUniquePtr<IDispatch> mTarget;
RefPtr<ITypeInfo> mTypeInfo;
RefPtr<IUnknown> mInterface;
};
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_DispatchForwarder_h

View File

@ -8,6 +8,7 @@
#include "mozilla/mscom/Interceptor.h"
#include "mozilla/mscom/InterceptorLog.h"
#include "mozilla/mscom/DispatchForwarder.h"
#include "mozilla/mscom/MainThreadInvoker.h"
#include "mozilla/mscom/Registration.h"
#include "mozilla/mscom/utils.h"
@ -282,6 +283,17 @@ Interceptor::ThreadSafeQueryInterface(REFIID aIid, IUnknown** aOutInterface)
return S_OK;
}
if (aIid == IID_IDispatch) {
STAUniquePtr<IDispatch> disp;
IDispatch* rawDisp = nullptr;
HRESULT hr = QueryInterfaceTarget(aIid, (void**)&rawDisp);
if (FAILED(hr)) {
return hr;
}
disp.reset(rawDisp);
return DispatchForwarder::Create(this, disp, aOutInterface);
}
return GetInterceptorForIID(aIid, (void**)aOutInterface);
}

View File

@ -7,6 +7,7 @@
EXPORTS.mozilla.mscom += [
'COMApartmentRegion.h',
'COMPtrHolder.h',
'DispatchForwarder.h',
'EnsureMTA.h',
'Interceptor.h',
'InterceptorLog.h',
@ -28,6 +29,7 @@ SOURCES += [
]
UNIFIED_SOURCES += [
'DispatchForwarder.cpp',
'EnsureMTA.cpp',
'InterceptorLog.cpp',
'MainThreadHandoff.cpp',

View File

@ -16,16 +16,20 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSISELECTIONLISTENER
explicit nsAutoCopyListener(int16_t aClipboardID)
: mCachedClipboard(aClipboardID)
{}
void Listen(nsISelectionPrivate *aSelection)
{
NS_ASSERTION(aSelection, "Null selection passed to Listen()");
aSelection->AddSelectionListener(this);
}
static nsAutoCopyListener* GetInstance()
static nsAutoCopyListener* GetInstance(int16_t aClipboardID)
{
if (!sInstance) {
sInstance = new nsAutoCopyListener();
sInstance = new nsAutoCopyListener(aClipboardID);
NS_ADDREF(sInstance);
}
@ -42,6 +46,7 @@ private:
~nsAutoCopyListener() {}
static nsAutoCopyListener* sInstance;
int16_t mCachedClipboard;
};
#endif

View File

@ -373,7 +373,7 @@ public:
* specified by aSelectionType.
* @param aSelectionType The selection type what you want to repaint.
*/
nsresult RepaintSelection(mozilla::SelectionType aSelectionType) const;
nsresult RepaintSelection(mozilla::SelectionType aSelectionType);
/** GetFrameForNodeOffset given a node and its child offset, return the nsIFrame and
* the offset into that frame.
@ -681,6 +681,10 @@ private:
// nsFrameSelection may get deleted when calling this,
// so remember to use nsCOMPtr when needed.
nsresult NotifySelectionListeners(mozilla::SelectionType aSelectionType);
// Update the selection cache on repaint when the
// selection being repainted is not empty.
nsresult UpdateSelectionCacheOnRepaintSelection(mozilla::dom::
Selection* aSel);
RefPtr<mozilla::dom::Selection>
mDomSelections[mozilla::kPresentSelectionTypeCount];

View File

@ -84,6 +84,7 @@ static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
#include "nsIEditor.h"
#include "nsIHTMLEditor.h"
#include "nsFocusManager.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -549,16 +550,22 @@ nsFrameSelection::nsFrameSelection()
mSelectingTableCellMode = 0;
mSelectedCellIndex = 0;
nsAutoCopyListener *autoCopy = nullptr;
// On macOS, cache the current selection to send to osx service menu.
#ifdef XP_MACOSX
autoCopy = nsAutoCopyListener::GetInstance(nsIClipboard::kSelectionCache);
#endif
// Check to see if the autocopy pref is enabled
// and add the autocopy listener if it is
if (Preferences::GetBool("clipboard.autocopy")) {
nsAutoCopyListener *autoCopy = nsAutoCopyListener::GetInstance();
autoCopy = nsAutoCopyListener::GetInstance(nsIClipboard::kSelectionClipboard);
}
if (autoCopy) {
int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
if (mDomSelections[index]) {
autoCopy->Listen(mDomSelections[index]);
}
if (autoCopy) {
int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
if (mDomSelections[index]) {
autoCopy->Listen(mDomSelections[index]);
}
}
@ -1958,7 +1965,7 @@ nsFrameSelection::ScrollSelectionIntoView(SelectionType aSelectionType,
}
nsresult
nsFrameSelection::RepaintSelection(SelectionType aSelectionType) const
nsFrameSelection::RepaintSelection(SelectionType aSelectionType)
{
int8_t index = GetIndexFromSelectionType(aSelectionType);
if (index < 0)
@ -1966,6 +1973,17 @@ nsFrameSelection::RepaintSelection(SelectionType aSelectionType) const
if (!mDomSelections[index])
return NS_ERROR_NULL_POINTER;
NS_ENSURE_STATE(mShell);
// On macOS, update the selection cache to the new active selection
// aka the current selection.
#ifdef XP_MACOSX
nsFocusManager* fm = nsFocusManager::GetFocusManager();
// Check an active window exists otherwise there cannot be a current selection
// and that it's a normal selection.
if (fm->GetActiveWindow() && aSelectionType == SelectionType::eNormal) {
UpdateSelectionCacheOnRepaintSelection(mDomSelections[index]);
}
#endif
return mDomSelections[index]->Repaint(mShell->GetPresContext());
}
@ -6467,12 +6485,26 @@ NS_IMPL_ISUPPORTS(nsAutoCopyListener, nsISelectionListener)
* selections?
* - maybe we should just never clear the X clipboard? That would make this
* problem just go away, which is very tempting.
*
* On macOS,
* nsIClipboard::kSelectionCache is the flag for current selection cache.
* Set the current selection cache on the parent process in
* widget cocoa nsClipboard whenever selection changes.
*/
NS_IMETHODIMP
nsAutoCopyListener::NotifySelectionChanged(nsIDOMDocument *aDoc,
nsISelection *aSel, int16_t aReason)
{
if (mCachedClipboard == nsIClipboard::kSelectionCache) {
nsFocusManager* fm = nsFocusManager::GetFocusManager();
// If no active window, do nothing because a current selection changed
// cannot occur unless it is in the active window.
if (!fm->GetActiveWindow()) {
return NS_OK;
}
}
if (!(aReason & nsISelectionListener::MOUSEUP_REASON ||
aReason & nsISelectionListener::SELECTALL_REASON ||
aReason & nsISelectionListener::KEYPRESS_REASON))
@ -6484,6 +6516,11 @@ nsAutoCopyListener::NotifySelectionChanged(nsIDOMDocument *aDoc,
#ifdef DEBUG_CLIPBOARD
fprintf(stderr, "CLIPBOARD: no selection/collapsed selection\n");
#endif
// If on macOS, clear the current selection transferable cached
// on the parent process (nsClipboard) when the selection is empty.
if (mCachedClipboard == nsIClipboard::kSelectionCache) {
return nsCopySupport::ClearSelectionCache();
}
/* clear X clipboard? */
return NS_OK;
}
@ -6493,7 +6530,45 @@ nsAutoCopyListener::NotifySelectionChanged(nsIDOMDocument *aDoc,
// call the copy code
return nsCopySupport::HTMLCopy(aSel, doc,
nsIClipboard::kSelectionClipboard, false);
mCachedClipboard, false);
}
/**
* See Bug 1288453.
*
* Update the selection cache on repaint to handle when a pre-existing
* selection becomes active aka the current selection.
*
* 1. Change the current selection by click n dragging another selection.
* - Make a selection on content page. Make a selection in a text editor.
* - You can click n drag the content selection to make it active again.
* 2. Change the current selection when switching to a tab with a selection.
* - Make selection in tab.
* - Switching tabs will make its respective selection active.
*
* Therefore, we only update the selection cache on a repaint
* if the current selection being repainted is not an empty selection.
*
* If the current selection is empty. The current selection cache
* would be cleared by nsAutoCopyListener::NotifySelectionChanged.
*/
nsresult
nsFrameSelection::UpdateSelectionCacheOnRepaintSelection(Selection* aSel)
{
nsIPresShell* ps = aSel->GetPresShell();
if (!ps) {
return NS_OK;
}
nsCOMPtr<nsIDocument> aDoc = ps->GetDocument();
bool collapsed;
if (aDoc && aSel &&
NS_SUCCEEDED(aSel->GetIsCollapsed(&collapsed)) && !collapsed) {
return nsCopySupport::HTMLCopy(aSel, aDoc,
nsIClipboard::kSelectionCache, false);
}
return NS_OK;
}
// SelectionChangeListener

View File

@ -21,6 +21,14 @@
namespace webrtc {
class VideoInputFeedBack
{
public:
virtual void OnDeviceChange() = 0;
protected:
virtual ~VideoInputFeedBack(){}
};
#if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) && !defined(WEBRTC_GONK)
int32_t SetCaptureAndroidVM(JavaVM* javaVM);
#endif
@ -32,6 +40,16 @@ class VideoCaptureModule: public RefCountedModule {
public:
virtual uint32_t NumberOfDevices() = 0;
virtual int32_t Refresh() = 0;
virtual void DeviceChange() {
if (_inputCallBack)
_inputCallBack->OnDeviceChange();
}
virtual void RegisterVideoInputFeedBack(VideoInputFeedBack& callBack) {
_inputCallBack = &callBack;
}
virtual void DeRegisterVideoInputFeedBack() {
_inputCallBack = NULL;
}
// Returns the available capture devices.
// deviceNumber - Index of capture device.
@ -82,6 +100,8 @@ class VideoCaptureModule: public RefCountedModule {
uint32_t positionY) = 0;
virtual ~DeviceInfo() {}
private:
VideoInputFeedBack* _inputCallBack = NULL;
};
class VideoCaptureEncodeInterface {

View File

@ -24,11 +24,13 @@ VideoCaptureMacAVFoundationInfo::VideoCaptureMacAVFoundationInfo(const int32_t i
{
nsAutoreleasePool localPool;
_captureInfo = [[VideoCaptureMacAVFoundationInfoObjC alloc] init];
[_captureInfo registerOwner:this];
}
VideoCaptureMacAVFoundationInfo::~VideoCaptureMacAVFoundationInfo()
{
nsAutoreleasePool localPool;
[_captureInfo registerOwner:nil];
[_captureInfo release];
}

View File

@ -26,6 +26,9 @@
bool _OSSupportedInfo;
NSArray* _captureDevicesInfo;
int _captureDeviceCountInfo;
NSArray* _observers;
NSLock* _lock;
webrtc::videocapturemodule::VideoCaptureMacAVFoundationInfo* _owner;
}
@ -46,6 +49,8 @@
*
***************************************************************************/
- (void)registerOwner:(webrtc::videocapturemodule::VideoCaptureMacAVFoundationInfo*)owner;
- (NSNumber*)getCaptureDeviceCount;
- (NSNumber*)getCaptureCapabilityCount:(const char*)uniqueId;

View File

@ -15,6 +15,7 @@
#include "webrtc/system_wrappers/interface/trace.h"
using namespace webrtc;
using namespace videocapturemodule;
#pragma mark **** hidden class interface
@ -38,12 +39,25 @@ using namespace webrtc;
return self;
}
- (void)registerOwner:(VideoCaptureMacAVFoundationInfo*)owner {
[_lock lock];
_owner = owner;
[_lock unlock];
}
/// ***** Objective-C. Similar to C++ destructor
/// ***** Returns nothing
- (void)dealloc {
[_captureDevicesInfo release];
// Remove Observers
NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
for (id observer in _observers)
[notificationCenter removeObserver:observer];
[_observers release];
[_lock release];
[super dealloc];
}
@ -223,6 +237,33 @@ using namespace webrtc;
_captureDeviceCountInfo = 0;
[self getCaptureDevices];
_lock = [[NSLock alloc] init];
//register device connected / disconnected event
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
id deviceWasConnectedObserver = [notificationCenter addObserverForName:AVCaptureDeviceWasConnectedNotification
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
[_lock lock];
if(_owner)
_owner->DeviceChange();
[_lock unlock];
}];
id deviceWasDisconnectedObserver = [notificationCenter addObserverForName:AVCaptureDeviceWasDisconnectedNotification
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
[_lock lock];
if(_owner)
_owner->DeviceChange();
[_lock unlock];
}];
_observers = [[NSArray alloc] initWithObjects:deviceWasConnectedObserver, deviceWasDisconnectedObserver, nil];
return [NSNumber numberWithInt:0];
}

View File

@ -27,6 +27,17 @@ namespace webrtc {
class VideoEngine;
class VideoCaptureModule;
// The observer is registered using RegisterInputObserver() and
// deregistered using DeregisterInputObserver().
class WEBRTC_DLLEXPORT ViEInputObserver {
public:
// This method is called if an input device is connected or disconnected .
virtual void DeviceChange() = 0;
protected:
virtual ~ViEInputObserver() {}
};
// This structure describes one set of the supported capabilities for a capture
// device.
struct CaptureCapability {
@ -216,9 +227,13 @@ class WEBRTC_DLLEXPORT ViECapture {
virtual int RegisterObserver(const int capture_id,
ViECaptureObserver& observer) = 0;
virtual int RegisterInputObserver(ViEInputObserver* observer) = 0;
// Removes an already registered instance of ViECaptureObserver.
virtual int DeregisterObserver(const int capture_id) = 0;
virtual int DeregisterInputObserver() = 0;
protected:
ViECapture() {}
virtual ~ViECapture() {}

View File

@ -373,6 +373,14 @@ int ViECaptureImpl::RegisterObserver(const int capture_id,
return 0;
}
int ViECaptureImpl::RegisterInputObserver(ViEInputObserver* observer) {
if (shared_data_->input_manager()->RegisterObserver(observer) != 0) {
shared_data_->SetLastError(kViECaptureDeviceUnknownError);
return -1;
}
return 0;
}
int ViECaptureImpl::DeregisterObserver(const int capture_id) {
ViEInputManagerScoped is(*(shared_data_->input_manager()));
ViECapturer* vie_capture = is.Capture(capture_id);
@ -392,4 +400,12 @@ int ViECaptureImpl::DeregisterObserver(const int capture_id) {
return 0;
}
int ViECaptureImpl::DeregisterInputObserver() {
if (shared_data_->input_manager()->DeRegisterObserver() != 0) {
shared_data_->SetLastError(kViECaptureDeviceUnknownError);
return -1;
}
return 0;
}
} // namespace webrtc

View File

@ -66,7 +66,9 @@ class ViECaptureImpl
virtual int EnableBrightnessAlarm(const int capture_id, const bool enable);
virtual int RegisterObserver(const int capture_id,
ViECaptureObserver& observer);
virtual int RegisterInputObserver(ViEInputObserver* observer);
virtual int DeregisterObserver(const int capture_id);
virtual int DeregisterInputObserver();
protected:
explicit ViECaptureImpl(ViESharedData* shared_data);

View File

@ -32,6 +32,8 @@ ViEInputManager::ViEInputManager(const int engine_id, const Config& config)
engine_id_(engine_id),
map_cs_(CriticalSectionWrapper::CreateCriticalSection()),
device_info_cs_(CriticalSectionWrapper::CreateCriticalSection()),
observer_cs_(CriticalSectionWrapper::CreateCriticalSection()),
observer_(NULL),
vie_frame_provider_map_(),
capture_device_info_(NULL),
module_process_thread_(NULL) {
@ -334,6 +336,13 @@ ViECapturer* ViEInputManager::ViECapturePtr(int capture_id) const {
return static_cast<ViECapturer*>(ViEFrameProvider(capture_id));
}
void ViEInputManager::OnDeviceChange() {
CriticalSectionScoped cs(observer_cs_.get());
if (observer_) {
observer_->DeviceChange();
}
}
// Create different DeviceInfo by _config;
VideoCaptureModule::DeviceInfo* ViEInputManager::GetDeviceInfo() {
CaptureDeviceType type = config_.Get<CaptureDeviceInfo>().type;
@ -361,6 +370,35 @@ VideoCaptureModule::DeviceInfo* ViEInputManager::GetDeviceInfo() {
}
return capture_device_info_;
}
int32_t ViEInputManager::RegisterObserver(ViEInputObserver* observer) {
{
CriticalSectionScoped cs(observer_cs_.get());
if (observer_) {
LOG_F(LS_ERROR) << "Observer already registered.";
return -1;
}
observer_ = observer;
}
if (!GetDeviceInfo())
return -1;
if (capture_device_info_ != NULL)
capture_device_info_->RegisterVideoInputFeedBack(*this);
return 0;
}
int32_t ViEInputManager::DeRegisterObserver() {
if (capture_device_info_ != NULL)
capture_device_info_->DeRegisterVideoInputFeedBack();
CriticalSectionScoped cs(observer_cs_.get());
observer_ = NULL;
return 0;
}
ViEInputManagerScoped::ViEInputManagerScoped(
const ViEInputManager& vie_input_manager)
: ViEManagerScopedBase(vie_input_manager) {

View File

@ -32,7 +32,8 @@ class ViECapturer;
class ViEExternalCapture;
class VoiceEngine;
class ViEInputManager : private ViEManagerBase {
class ViEInputManager : private ViEManagerBase,
protected VideoInputFeedBack {
friend class ViEInputManagerScoped;
public:
ViEInputManager(int engine_id, const Config& config);
@ -78,8 +79,12 @@ class ViEInputManager : private ViEManagerBase {
int CreateExternalCaptureDevice(ViEExternalCapture*& external_capture,
int& capture_id);
int DestroyCaptureDevice(int capture_id);
int32_t RegisterObserver(ViEInputObserver* observer);
int32_t DeRegisterObserver();
protected:
VideoCaptureModule::DeviceInfo* GetDeviceInfo();
// Implements VideoInputFeedBack.
virtual void OnDeviceChange();
private:
// Gets and allocates a free capture device id. Assumed protected by caller.
bool GetFreeCaptureId(int* freecapture_id);
@ -101,6 +106,8 @@ class ViEInputManager : private ViEManagerBase {
int engine_id_;
rtc::scoped_ptr<CriticalSectionWrapper> map_cs_;
rtc::scoped_ptr<CriticalSectionWrapper> device_info_cs_;
rtc::scoped_ptr<CriticalSectionWrapper> observer_cs_;
ViEInputObserver* observer_ GUARDED_BY(observer_cs_.get());
typedef std::map<int, ViEFrameProviderBase*> FrameProviderMap;
FrameProviderMap vie_frame_provider_map_;

View File

@ -4790,6 +4790,12 @@ pref("dom.w3c_pointer_events.enabled", false);
// W3C draft ImageCapture API
pref("dom.imagecapture.enabled", false);
// W3C MediaDevices devicechange event
pref("media.ondevicechange.enabled", false);
// W3C MediaDevices devicechange fake event
pref("media.ondevicechange.fakeDeviceChangeEvent.enabled", false);
// W3C touch-action css property (related to touch and pointer events)
// Note that we turn this on even on platforms/configurations where touch
// events are not supported (e.g. OS X, or Windows with e10s disabled). For

View File

@ -1,295 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 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/. */
#include "nsContentSecurityManager.h"
#include "nsContentUtils.h"
#include "RtspChannelChild.h"
#include "mozilla/ipc/URIUtils.h"
#include "nsServiceManagerUtils.h"
using namespace mozilla::ipc;
namespace mozilla {
namespace net {
//-----------------------------------------------------------------------------
// RtspChannelChild
//-----------------------------------------------------------------------------
RtspChannelChild::RtspChannelChild(nsIURI *aUri)
: mIPCOpen(false)
, mCanceled(false)
{
nsBaseChannel::SetURI(aUri);
DisallowThreadRetargeting();
}
RtspChannelChild::~RtspChannelChild()
{
}
nsIStreamingProtocolController*
RtspChannelChild::GetController()
{
return mMediaStreamController;
}
void
RtspChannelChild::ReleaseController()
{
if (mMediaStreamController) {
mMediaStreamController = nullptr;
}
}
//-----------------------------------------------------------------------------
// IPDL
//-----------------------------------------------------------------------------
void
RtspChannelChild::AddIPDLReference()
{
MOZ_ASSERT(!mIPCOpen,
"Attempt to retain more than one IPDL reference");
mIPCOpen = true;
AddRef();
}
void
RtspChannelChild::ReleaseIPDLReference()
{
MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference");
mIPCOpen = false;
Release();
}
//-----------------------------------------------------------------------------
// nsISupports
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS_INHERITED(RtspChannelChild,
nsBaseChannel,
nsIChannel,
nsIChildChannel)
//-----------------------------------------------------------------------------
// nsBaseChannel::nsIChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspChannelChild::GetContentType(nsACString& aContentType)
{
aContentType.AssignLiteral("RTSP");
return NS_OK;
}
class CallListenerOnStartRequestEvent : public Runnable
{
public:
CallListenerOnStartRequestEvent(nsIStreamListener *aListener,
nsIRequest *aRequest, nsISupports *aContext)
: mListener(aListener)
, mRequest(aRequest)
, mContext(aContext)
{
MOZ_RELEASE_ASSERT(aListener);
}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
mListener->OnStartRequest(mRequest, mContext);
return NS_OK;
}
private:
RefPtr<nsIStreamListener> mListener;
RefPtr<nsIRequest> mRequest;
RefPtr<nsISupports> mContext;
};
NS_IMETHODIMP
RtspChannelChild::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
{
MOZ_ASSERT(!mLoadInfo ||
mLoadInfo->GetSecurityMode() == 0 ||
mLoadInfo->GetInitialSecurityCheckDone() ||
(mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
"security flags in loadInfo but asyncOpen2() not called");
// Precondition checks.
MOZ_ASSERT(aListener);
nsCOMPtr<nsIURI> uri = nsBaseChannel::URI();
NS_ENSURE_TRUE(uri, NS_ERROR_ILLEGAL_VALUE);
// Create RtspController.
nsCOMPtr<nsIStreamingProtocolControllerService> mediaControllerService =
do_GetService(MEDIASTREAMCONTROLLERSERVICE_CONTRACTID);
MOZ_RELEASE_ASSERT(mediaControllerService,
"Cannot proceed if media controller service is unavailable!");
mediaControllerService->Create(this, getter_AddRefs(mMediaStreamController));
MOZ_ASSERT(mMediaStreamController);
// Add ourselves to the load group.
if (mLoadGroup) {
mLoadGroup->AddRequest(this, nullptr);
}
// Dispatch mListener's OnStartRequest directly. mListener is expected to
// create an RtspMediaResource and use the RtspController we just created to
// manage the control and data streams to and from the network.
mListener = aListener;
mListenerContext = aContext;
NS_DispatchToMainThread(
new CallListenerOnStartRequestEvent(mListener, this, mListenerContext));
return NS_OK;
}
NS_IMETHODIMP
RtspChannelChild::AsyncOpen2(nsIStreamListener *aListener)
{
nsCOMPtr<nsIStreamListener> listener = aListener;
nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
NS_ENSURE_SUCCESS(rv, rv);
return AsyncOpen(listener, nullptr);
}
//-----------------------------------------------------------------------------
// nsBaseChannel::nsIStreamListener::nsIRequestObserver
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspChannelChild::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
{
MOZ_CRASH("Should never be called");
}
NS_IMETHODIMP
RtspChannelChild::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
nsresult aStatusCode)
{
MOZ_CRASH("Should never be called");
}
//-----------------------------------------------------------------------------
// nsBaseChannel::nsIStreamListener
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspChannelChild::OnDataAvailable(nsIRequest *aRequest,
nsISupports *aContext,
nsIInputStream *aInputStream,
uint64_t aOffset,
uint32_t aCount)
{
MOZ_CRASH("Should never be called");
}
//-----------------------------------------------------------------------------
// nsBaseChannel::nsIChannel::nsIRequest
//-----------------------------------------------------------------------------
class CallListenerOnStopRequestEvent : public Runnable
{
public:
CallListenerOnStopRequestEvent(nsIStreamListener *aListener,
nsIRequest *aRequest,
nsISupports *aContext, nsresult aStatus)
: mListener(aListener)
, mRequest(aRequest)
, mContext(aContext)
, mStatus(aStatus)
{
MOZ_RELEASE_ASSERT(aListener);
}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
mListener->OnStopRequest(mRequest, mContext, mStatus);
return NS_OK;
}
private:
RefPtr<nsIStreamListener> mListener;
RefPtr<nsIRequest> mRequest;
RefPtr<nsISupports> mContext;
nsresult mStatus;
};
NS_IMETHODIMP
RtspChannelChild::Cancel(nsresult status)
{
if (mCanceled) {
return NS_OK;
}
mCanceled = true;
// Stop RtspController.
if (mMediaStreamController) {
mMediaStreamController->Stop();
}
// Call mListener's OnStopRequest to do clean up.
NS_DispatchToMainThread(
new CallListenerOnStopRequestEvent(mListener, this,
mListenerContext, status));
mListener = nullptr;
mListenerContext = nullptr;
// Remove ourselves from the load group.
if (mLoadGroup) {
mLoadGroup->RemoveRequest(this, nullptr, status);
}
return NS_OK;
}
NS_IMETHODIMP
RtspChannelChild::Suspend()
{
MOZ_CRASH("Should never be called");
}
NS_IMETHODIMP
RtspChannelChild::Resume()
{
MOZ_CRASH("Should never be called");
}
//-----------------------------------------------------------------------------
// nsBaseChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspChannelChild::OpenContentStream(bool aAsync,
nsIInputStream **aStream,
nsIChannel **aChannel)
{
MOZ_CRASH("Should never be called");
}
//-----------------------------------------------------------------------------
// nsIChildChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspChannelChild::ConnectParent(uint32_t id)
{
// Create RtspChannelParent for redirection.
AddIPDLReference();
RtspChannelConnectArgs connectArgs;
SerializeURI(nsBaseChannel::URI(), connectArgs.uri());
connectArgs.channelId() = id;
if (!gNeckoChild->SendPRtspChannelConstructor(this, connectArgs)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
RtspChannelChild::CompleteRedirectSetup(nsIStreamListener *aListener,
nsISupports *aContext)
{
if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
MOZ_ASSERT(!aContext, "aContext should be null!");
return AsyncOpen2(aListener);
}
return AsyncOpen(aListener, aContext);
}
} // namespace net
} // namespace mozilla

View File

@ -1,91 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 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 RtspChannelChild_h
#define RtspChannelChild_h
#include "mozilla/net/PRtspChannelChild.h"
#include "mozilla/net/NeckoChild.h"
#include "nsBaseChannel.h"
#include "nsIChildChannel.h"
#include "nsIStreamingProtocolController.h"
#include "nsIStreamingProtocolService.h"
namespace mozilla {
namespace net {
//-----------------------------------------------------------------------------
// RtspChannelChild is a dummy channel used to aid MediaResource creation in
// HTMLMediaElement. Network control and data flows are managed by an
// RtspController object, which is created by us and manipulated by
// RtspMediaResource. This object is also responsible for inter-process
// communication with the parent process.
// When RtspChannelChild::AsyncOpen is called, it should create an
// RtspController object, dispatch an OnStartRequest and immediately return.
// We expect an RtspMediaResource object will be created in the calling context
// and it will use the RtpController we create.
class RtspChannelChild : public PRtspChannelChild
, public nsBaseChannel
, public nsIChildChannel
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICHILDCHANNEL
RtspChannelChild(nsIURI *aUri);
// nsBaseChannel::nsIChannel
NS_IMETHOD GetContentType(nsACString & aContentType) override final;
NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext)
override final;
NS_IMETHOD AsyncOpen2(nsIStreamListener *listener) override final;
// nsBaseChannel::nsIStreamListener::nsIRequestObserver
NS_IMETHOD OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
override final;
NS_IMETHOD OnStopRequest(nsIRequest *aRequest,
nsISupports *aContext,
nsresult aStatusCode) override final;
// nsBaseChannel::nsIStreamListener
NS_IMETHOD OnDataAvailable(nsIRequest *aRequest,
nsISupports *aContext,
nsIInputStream *aInputStream,
uint64_t aOffset,
uint32_t aCount) override final;
// nsBaseChannel::nsIChannel::nsIRequest
NS_IMETHOD Cancel(nsresult status) override final;
NS_IMETHOD Suspend() override final;
NS_IMETHOD Resume() override final;
// nsBaseChannel
NS_IMETHOD OpenContentStream(bool aAsync,
nsIInputStream **aStream,
nsIChannel **aChannel) override final;
// IPDL
void AddIPDLReference();
void ReleaseIPDLReference();
// RtspChannelChild
nsIStreamingProtocolController* GetController();
void ReleaseController();
protected:
~RtspChannelChild();
private:
bool mIPCOpen;
bool mCanceled;
nsCOMPtr<nsIStreamingProtocolController> mMediaStreamController;
};
} // namespace net
} // namespace mozilla
#endif // RtspChannelChild_h

View File

@ -1,173 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 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/. */
#include "RtspChannelParent.h"
#include "nsContentSecurityManager.h"
using namespace mozilla::ipc;
namespace mozilla {
namespace net {
//-----------------------------------------------------------------------------
// RtspChannelParent
//-----------------------------------------------------------------------------
RtspChannelParent::RtspChannelParent(nsIURI *aUri)
: mIPCClosed(false)
{
nsBaseChannel::SetURI(aUri);
DisallowThreadRetargeting();
}
RtspChannelParent::~RtspChannelParent()
{
}
void
RtspChannelParent::ActorDestroy(ActorDestroyReason why)
{
mIPCClosed = true;
}
//-----------------------------------------------------------------------------
// nsISupports
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS_INHERITED(RtspChannelParent,
nsBaseChannel,
nsIParentChannel)
//-----------------------------------------------------------------------------
// RtspChannelParent methods
//-----------------------------------------------------------------------------
bool
RtspChannelParent::Init(const RtspChannelConnectArgs& aArgs)
{
return ConnectChannel(aArgs.channelId());
}
bool
RtspChannelParent::ConnectChannel(const uint32_t& channelId)
{
nsCOMPtr<nsIChannel> channel;
NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel));
return true;
}
//-----------------------------------------------------------------------------
// nsBaseChannel::nsIChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspChannelParent::GetContentType(nsACString& aContentType)
{
aContentType.AssignLiteral("RTSP");
return NS_OK;
}
NS_IMETHODIMP
RtspChannelParent::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
{
return NS_OK;
}
NS_IMETHODIMP
RtspChannelParent::AsyncOpen2(nsIStreamListener *aListener)
{
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsBaseChannel::nsIStreamListener::nsIRequestObserver
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspChannelParent::OnStartRequest(nsIRequest *aRequest,
nsISupports *aContext)
{
MOZ_CRASH("Should never be called");
}
NS_IMETHODIMP
RtspChannelParent::OnStopRequest(nsIRequest *aRequest,
nsISupports *aContext,
nsresult aStatusCode)
{
MOZ_CRASH("Should never be called");
}
//-----------------------------------------------------------------------------
// nsBaseChannel::nsIStreamListener
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspChannelParent::OnDataAvailable(nsIRequest *aRequest,
nsISupports *aContext,
nsIInputStream *aInputStream,
uint64_t aOffset,
uint32_t aCount)
{
MOZ_CRASH("Should never be called");
}
//-----------------------------------------------------------------------------
// nsBaseChannel::nsIChannel::nsIRequeset
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspChannelParent::Cancel(nsresult status)
{
// FIXME: This method will be called by
// nsXMLHttpRequest::CloseRequestWithError while closing the browser app.
// However, the root cause is RtspChannelParent will be created by
// nsXMLHttpRequest::Open when we navigate away from an RTSP web page.
// We should find out why it happens and decide how to fix it.
return NS_OK;
}
NS_IMETHODIMP
RtspChannelParent::Suspend()
{
MOZ_CRASH("Should never be called");
}
NS_IMETHODIMP
RtspChannelParent::Resume()
{
MOZ_CRASH("Should never be called");
}
//-----------------------------------------------------------------------------
// nsBaseChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspChannelParent::OpenContentStream(bool aAsync,
nsIInputStream **aStream,
nsIChannel **aChannel)
{
MOZ_CRASH("Should never be called");
}
//-----------------------------------------------------------------------------
// nsIParentChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspChannelParent::SetParentListener(HttpChannelParentListener *aListener)
{
return NS_OK;
}
NS_IMETHODIMP
RtspChannelParent::NotifyTrackingProtectionDisabled()
{
// One day, this should probably be filled in.
return NS_OK;
}
NS_IMETHODIMP
RtspChannelParent::Delete()
{
return NS_OK;
}
} // namespace net
} // namespace mozilla

View File

@ -1,86 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 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 RtspChannelParent_h
#define RtspChannelParent_h
#include "mozilla/net/PRtspChannelParent.h"
#include "mozilla/net/NeckoParent.h"
#include "nsBaseChannel.h"
#include "nsIParentChannel.h"
namespace mozilla {
namespace net {
//-----------------------------------------------------------------------------
// Note: RtspChannel doesn't transport streams as normal channel does.
// (See RtspChannelChild.h for detail).
// The reason for the existence of RtspChannelParent is to support HTTP->RTSP
// redirection.
// When redirection happens, two instances of RtspChannelParent will be created:
// - One will be created when HTTP creates the new channel for redirects, and
// will be registered as an nsIChannel.
// - The other will be created via IPDL by RtspChannelChild, and will be
// registered as an nsIParentChannel.
class RtspChannelParent : public PRtspChannelParent
, public nsBaseChannel
, public nsIParentChannel
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPARENTCHANNEL
RtspChannelParent(nsIURI *aUri);
// nsBaseChannel::nsIChannel
NS_IMETHOD GetContentType(nsACString & aContentType) override final;
NS_IMETHOD AsyncOpen(nsIStreamListener *listener,
nsISupports *aContext) override final;
NS_IMETHOD AsyncOpen2(nsIStreamListener *listener) override final;
// nsBaseChannel::nsIStreamListener::nsIRequestObserver
NS_IMETHOD OnStartRequest(nsIRequest *aRequest,
nsISupports *aContext) override final;
NS_IMETHOD OnStopRequest(nsIRequest *aRequest,
nsISupports *aContext,
nsresult aStatusCode) override final;
// nsBaseChannel::nsIStreamListener
NS_IMETHOD OnDataAvailable(nsIRequest *aRequest,
nsISupports *aContext,
nsIInputStream *aInputStream,
uint64_t aOffset,
uint32_t aCount) override final;
// nsBaseChannel::nsIChannel::nsIRequest
NS_IMETHOD Cancel(nsresult status) override final;
NS_IMETHOD Suspend() override final;
NS_IMETHOD Resume() override final;
// nsBaseChannel
NS_IMETHOD OpenContentStream(bool aAsync,
nsIInputStream **aStream,
nsIChannel **aChannel) override final;
// RtspChannelParent
bool Init(const RtspChannelConnectArgs& aArgs);
protected:
~RtspChannelParent();
// Used to connect redirected-to channel in parent with just created
// ChildChannel. Used during HTTP->RTSP redirection.
bool ConnectChannel(const uint32_t& channelId);
private:
bool mIPCClosed;
virtual void ActorDestroy(ActorDestroyReason why) override;
};
} // namespace net
} // namespace mozilla
#endif // RtspChannelParent_h

View File

@ -1,112 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 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/. */
#include "RtspChannelChild.h"
#include "RtspChannelParent.h"
#include "RtspHandler.h"
#include "nsILoadGroup.h"
#include "nsIInterfaceRequestor.h"
#include "nsIURI.h"
#include "nsAutoPtr.h"
#include "nsStandardURL.h"
#include "mozilla/net/NeckoChild.h"
namespace mozilla {
namespace net {
NS_IMPL_ISUPPORTS(RtspHandler, nsIProtocolHandler)
//-----------------------------------------------------------------------------
// RtspHandler::nsIProtocolHandler
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspHandler::GetScheme(nsACString &aScheme)
{
aScheme.AssignLiteral("rtsp");
return NS_OK;
}
NS_IMETHODIMP
RtspHandler::GetDefaultPort(int32_t *aDefaultPort)
{
*aDefaultPort = kDefaultRtspPort;
return NS_OK;
}
NS_IMETHODIMP
RtspHandler::GetProtocolFlags(uint32_t *aProtocolFlags)
{
*aProtocolFlags = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE |
URI_NON_PERSISTABLE | URI_SYNC_LOAD_IS_OK;
return NS_OK;
}
NS_IMETHODIMP
RtspHandler::NewURI(const nsACString & aSpec,
const char *aOriginCharset,
nsIURI *aBaseURI, nsIURI **aResult)
{
int32_t port;
nsresult rv = GetDefaultPort(&port);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<nsStandardURL> url = new nsStandardURL();
rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY, port, aSpec,
aOriginCharset, aBaseURI);
NS_ENSURE_SUCCESS(rv, rv);
url.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
RtspHandler::NewChannel2(nsIURI* aURI,
nsILoadInfo* aLoadInfo,
nsIChannel** aResult)
{
bool isRtsp = false;
RefPtr<nsBaseChannel> rtspChannel;
nsresult rv = aURI->SchemeIs("rtsp", &isRtsp);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(isRtsp, NS_ERROR_UNEXPECTED);
if (IsNeckoChild()) {
rtspChannel = new RtspChannelChild(aURI);
} else {
rtspChannel = new RtspChannelParent(aURI);
}
rv = rtspChannel->Init();
NS_ENSURE_SUCCESS(rv, rv);
// set the loadInfo on the new channel
rv = rtspChannel->SetLoadInfo(aLoadInfo);
NS_ENSURE_SUCCESS(rv, rv);
rtspChannel.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
RtspHandler::NewChannel(nsIURI *aURI, nsIChannel **aResult)
{
return NewChannel2(aURI, nullptr, aResult);
}
NS_IMETHODIMP
RtspHandler::AllowPort(int32_t port, const char *scheme, bool *aResult)
{
// Do not override any blacklisted ports.
*aResult = false;
return NS_OK;
}
} // namespace net
} // namespace mozilla

View File

@ -1,34 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 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 RtspHandler_h
#define RtspHandler_h
#include "nsIProtocolHandler.h"
#include "nsCOMPtr.h"
#include "nsString.h"
namespace mozilla {
namespace net {
class RtspHandler final : public nsIProtocolHandler
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPROTOCOLHANDLER
RtspHandler() { }
const static int32_t kDefaultRtspPort = 554;
protected:
~RtspHandler() { }
};
} // namespace net
} // namespace mozilla
#endif // RtspHandler_h

View File

@ -1,382 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 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/. */
#include "RtspController.h"
#include "RtspMetaData.h"
#include "nsIURI.h"
#include "nsICryptoHash.h"
#include "nsIRunnable.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
#include "nsICancelable.h"
#include "nsIStreamConverterService.h"
#include "nsIIOService2.h"
#include "nsIProtocolProxyService.h"
#include "nsIProxyInfo.h"
#include "nsIProxiedChannel.h"
#include "nsIHttpProtocolHandler.h"
#include "nsAutoPtr.h"
#include "nsStandardURL.h"
#include "nsNetCID.h"
#include "nsServiceManagerUtils.h"
#include "nsXPIDLString.h"
#include "nsCRT.h"
#include "nsThreadUtils.h"
#include "nsError.h"
#include "nsStringStream.h"
#include "nsAlgorithm.h"
#include "nsProxyRelease.h"
#include "mozilla/Attributes.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Logging.h"
#include "plbase64.h"
#include "prmem.h"
#include "prnetdb.h"
#include "zlib.h"
#include <algorithm>
#include "nsDebug.h"
namespace mozilla {
namespace net {
extern LazyLogModule gRtspLog;
#undef LOG
#define LOG(args) MOZ_LOG(mozilla::net::gRtspLog, mozilla::LogLevel::Debug, args)
//-----------------------------------------------------------------------------
// RtspController
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS(RtspController,
nsIStreamingProtocolController)
RtspController::RtspController(nsIChannel *channel)
: mState(INIT)
{
LOG(("RtspController::RtspController()"));
}
RtspController::~RtspController()
{
LOG(("RtspController::~RtspController()"));
if (mRtspSource.get()) {
mRtspSource.clear();
}
}
//-----------------------------------------------------------------------------
// nsIStreamingProtocolController
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspController::GetTrackMetaData(uint8_t index,
nsIStreamingProtocolMetaData * *_retval)
{
LOG(("RtspController::GetTrackMetaData()"));
return NS_OK;
}
NS_IMETHODIMP
RtspController::Play(void)
{
LOG(("RtspController::Play()"));
if (!mRtspSource.get()) {
MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
return NS_ERROR_NOT_INITIALIZED;
}
if (mState != CONNECTED) {
return NS_ERROR_NOT_CONNECTED;
}
mRtspSource->play();
return NS_OK;
}
NS_IMETHODIMP
RtspController::Pause(void)
{
LOG(("RtspController::Pause()"));
if (!mRtspSource.get()) {
MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
return NS_ERROR_NOT_INITIALIZED;
}
if (mState != CONNECTED) {
return NS_ERROR_NOT_CONNECTED;
}
mRtspSource->pause();
return NS_OK;
}
NS_IMETHODIMP
RtspController::Resume(void)
{
return Play();
}
NS_IMETHODIMP
RtspController::Suspend(void)
{
return Pause();
}
NS_IMETHODIMP
RtspController::Seek(uint64_t seekTimeUs)
{
LOG(("RtspController::Seek() %llu", seekTimeUs));
if (!mRtspSource.get()) {
MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
return NS_ERROR_NOT_INITIALIZED;
}
if (mState != CONNECTED) {
return NS_ERROR_NOT_CONNECTED;
}
mRtspSource->seek(seekTimeUs);
return NS_OK;
}
NS_IMETHODIMP
RtspController::Stop()
{
LOG(("RtspController::Stop()"));
mState = INIT;
if (!mRtspSource.get()) {
MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
return NS_ERROR_NOT_INITIALIZED;
}
mRtspSource->stop();
return NS_OK;
}
NS_IMETHODIMP
RtspController::GetTotalTracks(uint8_t *aTracks)
{
LOG(("RtspController::GetTotalTracks()"));
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
RtspController::AsyncOpen(nsIStreamingProtocolListener *aListener)
{
if (!aListener) {
LOG(("RtspController::AsyncOpen() illegal listener"));
return NS_ERROR_NOT_INITIALIZED;
}
mListener = aListener;
if (!mURI) {
LOG(("RtspController::AsyncOpen() illegal URI"));
return NS_ERROR_ILLEGAL_VALUE;
}
nsAutoCString uriSpec;
mURI->GetSpec(uriSpec);
LOG(("RtspController AsyncOpen uri=%s", uriSpec.get()));
if (!mRtspSource.get()) {
mRtspSource = new android::RTSPSource(this, uriSpec.get(),
mUserAgent.get(), false, 0);
}
// Connect to Rtsp Server.
mRtspSource->start();
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsIStreamingProtocolListener
//-----------------------------------------------------------------------------
class SendMediaDataTask : public Runnable
{
public:
SendMediaDataTask(nsIStreamingProtocolListener *listener,
uint8_t index,
const nsACString & data,
uint32_t length,
uint32_t offset,
nsIStreamingProtocolMetaData *meta)
: mIndex(index)
, mLength(length)
, mOffset(offset)
, mMetaData(meta)
, mListener(listener)
{
mData.Assign(data);
}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
mListener->OnMediaDataAvailable(mIndex, mData, mLength,
mOffset, mMetaData);
return NS_OK;
}
private:
uint8_t mIndex;
nsCString mData;
uint32_t mLength;
uint32_t mOffset;
RefPtr<nsIStreamingProtocolMetaData> mMetaData;
nsCOMPtr<nsIStreamingProtocolListener> mListener;
};
NS_IMETHODIMP
RtspController::OnMediaDataAvailable(uint8_t index,
const nsACString & data,
uint32_t length,
uint32_t offset,
nsIStreamingProtocolMetaData *meta)
{
if (mListener && mState == CONNECTED) {
RefPtr<SendMediaDataTask> task =
new SendMediaDataTask(mListener, index, data, length, offset, meta);
return NS_DispatchToMainThread(task);
}
return NS_ERROR_NOT_AVAILABLE;
}
class SendOnConnectedTask : public Runnable
{
public:
SendOnConnectedTask(nsIStreamingProtocolListener *listener,
uint8_t index,
nsIStreamingProtocolMetaData *meta)
: mListener(listener)
, mIndex(index)
, mMetaData(meta)
{ }
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
mListener->OnConnected(mIndex, mMetaData);
return NS_OK;
}
private:
nsCOMPtr<nsIStreamingProtocolListener> mListener;
uint8_t mIndex;
RefPtr<nsIStreamingProtocolMetaData> mMetaData;
};
NS_IMETHODIMP
RtspController::OnConnected(uint8_t index,
nsIStreamingProtocolMetaData *meta)
{
LOG(("RtspController::OnConnected()"));
mState = CONNECTED;
if (mListener) {
RefPtr<SendOnConnectedTask> task =
new SendOnConnectedTask(mListener, index, meta);
return NS_DispatchToMainThread(task);
}
return NS_ERROR_NOT_AVAILABLE;
}
class SendOnDisconnectedTask : public Runnable
{
public:
SendOnDisconnectedTask(nsIStreamingProtocolListener *listener,
uint8_t index,
nsresult reason)
: mListener(listener)
, mIndex(index)
, mReason(reason)
{ }
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
mListener->OnDisconnected(mIndex, mReason);
return NS_OK;
}
private:
nsCOMPtr<nsIStreamingProtocolListener> mListener;
uint8_t mIndex;
nsresult mReason;
};
NS_IMETHODIMP
RtspController::OnDisconnected(uint8_t index,
nsresult reason)
{
LOG(("RtspController::OnDisconnected() for track %d reason = 0x%x", index, reason));
mState = DISCONNECTED;
if (mListener) {
RefPtr<SendOnDisconnectedTask> task =
new SendOnDisconnectedTask(mListener, index, reason);
// Break the cycle reference between the Listener (RtspControllerParent) and
// us.
mListener = nullptr;
return NS_DispatchToMainThread(task);
}
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
RtspController::Init(nsIURI *aURI)
{
nsresult rv;
if (!aURI) {
LOG(("RtspController::Init() - invalid URI"));
return NS_ERROR_NOT_INITIALIZED;
}
nsAutoCString host;
int32_t port = -1;
rv = aURI->GetAsciiHost(host);
if (NS_FAILED(rv)) return rv;
// Reject the URL if it doesn't specify a host
if (host.IsEmpty())
return NS_ERROR_MALFORMED_URI;
rv = aURI->GetPort(&port);
if (NS_FAILED(rv)) return rv;
rv = aURI->GetAsciiSpec(mSpec);
if (NS_FAILED(rv)) return rv;
mURI = aURI;
// Get User-Agent.
nsCOMPtr<nsIHttpProtocolHandler>
service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
rv = service->GetUserAgent(mUserAgent);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
RtspController::PlaybackEnded()
{
LOG(("RtspController::PlaybackEnded()"));
if (!mRtspSource.get()) {
MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
return NS_ERROR_NOT_INITIALIZED;
}
mRtspSource->playbackEnded();
return NS_OK;
}
} // namespace mozilla::net
} // namespace mozilla

View File

@ -1,58 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 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 RtspController_h
#define RtspController_h
#include "nsIStreamingProtocolController.h"
#include "nsIChannel.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "RTSPSource.h"
namespace mozilla {
namespace net {
class RtspController final : public nsIStreamingProtocolController
, public nsIStreamingProtocolListener
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISTREAMINGPROTOCOLCONTROLLER
NS_DECL_NSISTREAMINGPROTOCOLLISTENER
RtspController(nsIChannel *channel);
protected:
~RtspController();
private:
enum State {
INIT,
CONNECTED,
DISCONNECTED
};
// RTSP URL refer to a stream or an aggregate of streams.
nsCOMPtr<nsIURI> mURI;
// The nsIStreamingProtocolListener implementation.
nsCOMPtr<nsIStreamingProtocolListener> mListener;
// ASCII encoded URL spec.
nsCString mSpec;
// UserAgent string.
nsCString mUserAgent;
// Indicate the connection state between the
// media streaming server and the Rtsp client.
State mState;
// Rtsp Streaming source.
android::sp<android::RTSPSource> mRtspSource;
};
}
} // namespace mozilla::net
#endif

View File

@ -1,613 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 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/. */
#include "RtspControllerChild.h"
#include "RtspMetaData.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/net/NeckoChild.h"
#include "nsITabChild.h"
#include "nsILoadContext.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "nsStringStream.h"
#include "mozilla/Logging.h"
const uint32_t kRtspTotalTracks = 2;
const unsigned long kRtspCommandDelayMs = 200;
using namespace mozilla::ipc;
namespace mozilla {
namespace net {
static LazyLogModule gRtspChildLog("nsRtspChild");
#undef LOG
#define LOG(args) MOZ_LOG(mozilla::net::gRtspChildLog, mozilla::LogLevel::Debug, args)
NS_IMPL_ADDREF(RtspControllerChild)
NS_IMETHODIMP_(nsrefcnt) RtspControllerChild::Release()
{
NS_PRECONDITION(0 != mRefCnt, "dup release");
// Enable this to find non-threadsafe destructors:
// NS_ASSERT_OWNINGTHREAD(RtspControllerChild);
--mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "RtspControllerChild");
if (mRefCnt == 1 && mIPCOpen) {
Send__delete__(this);
return mRefCnt;
}
if (mRefCnt == 0) {
mRefCnt = 1; /* stabilize */
delete this;
return 0;
}
return mRefCnt;
}
NS_INTERFACE_MAP_BEGIN(RtspControllerChild)
NS_INTERFACE_MAP_ENTRY(nsIStreamingProtocolController)
NS_INTERFACE_MAP_ENTRY(nsIStreamingProtocolListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamingProtocolController)
NS_INTERFACE_MAP_END
//-----------------------------------------------------------------------------
// RtspControllerChild methods
//-----------------------------------------------------------------------------
RtspControllerChild::RtspControllerChild(nsIChannel *channel)
: mIPCOpen(false)
, mIPCAllowed(false)
, mChannel(channel)
, mTotalTracks(0)
, mSuspendCount(0)
, mTimerLock("RtspControllerChild.mTimerLock")
, mPlayTimer(nullptr)
, mPauseTimer(nullptr)
{
AddIPDLReference();
gNeckoChild->SendPRtspControllerConstructor(this);
}
RtspControllerChild::~RtspControllerChild()
{
LOG(("RtspControllerChild::~RtspControllerChild()"));
}
void
RtspControllerChild::ReleaseChannel()
{
static_cast<RtspChannelChild*>(mChannel.get())->ReleaseController();
}
bool
RtspControllerChild::OKToSendIPC()
{
MOZ_ASSERT(NS_IsMainThread());
if (mIPCOpen == false) {
return false;
}
return mIPCAllowed;
}
void
RtspControllerChild::AllowIPC()
{
MOZ_ASSERT(NS_IsMainThread());
mIPCAllowed = true;
}
void
RtspControllerChild::DisallowIPC()
{
MOZ_ASSERT(NS_IsMainThread());
mIPCAllowed = false;
}
void
RtspControllerChild::StopPlayAndPauseTimer()
{
MutexAutoLock lock(mTimerLock);
if (mPlayTimer) {
mPlayTimer->Cancel();
mPlayTimer = nullptr;
}
if (mPauseTimer) {
mPauseTimer->Cancel();
mPauseTimer = nullptr;
}
}
//-----------------------------------------------------------------------------
// RtspControllerChild::PRtspControllerChild
//-----------------------------------------------------------------------------
bool
RtspControllerChild::RecvOnMediaDataAvailable(
const uint8_t& index,
const nsCString& data,
const uint32_t& length,
const uint32_t& offset,
InfallibleTArray<RtspMetadataParam>&& metaArray)
{
RefPtr<RtspMetaData> meta = new RtspMetaData();
nsresult rv = meta->DeserializeRtspMetaData(metaArray);
NS_ENSURE_SUCCESS(rv, true);
if (mListener) {
mListener->OnMediaDataAvailable(index, data, length, offset, meta.get());
}
return true;
}
void
RtspControllerChild::AddMetaData(
already_AddRefed<nsIStreamingProtocolMetaData>&& meta)
{
mMetaArray.AppendElement(mozilla::Move(meta));
}
int
RtspControllerChild::GetMetaDataLength()
{
return mMetaArray.Length();
}
bool
RtspControllerChild::RecvOnConnected(
const uint8_t& index,
InfallibleTArray<RtspMetadataParam>&& metaArray)
{
// Deserialize meta data.
RefPtr<RtspMetaData> meta = new RtspMetaData();
nsresult rv = meta->DeserializeRtspMetaData(metaArray);
NS_ENSURE_SUCCESS(rv, true);
meta->GetTotalTracks(&mTotalTracks);
if (mTotalTracks <= 0) {
LOG(("RtspControllerChild::RecvOnConnected invalid tracks %d", mTotalTracks));
// Set the default value.
mTotalTracks = kRtspTotalTracks;
}
AddMetaData(meta.forget().downcast<nsIStreamingProtocolMetaData>());
// Notify the listener when meta data of tracks are available.
if ((static_cast<uint32_t>(index) + 1) == mTotalTracks) {
// The controller provide |GetTrackMetaData| method for his client.
if (mListener) {
mListener->OnConnected(index, nullptr);
}
}
return true;
}
bool
RtspControllerChild::RecvOnDisconnected(
const uint8_t& index,
const nsresult& reason)
{
StopPlayAndPauseTimer();
DisallowIPC();
LOG(("RtspControllerChild::RecvOnDisconnected for track %d reason = 0x%x", index, reason));
if (mListener) {
mListener->OnDisconnected(index, reason);
}
ReleaseChannel();
return true;
}
bool
RtspControllerChild::RecvAsyncOpenFailed(const nsresult& reason)
{
StopPlayAndPauseTimer();
DisallowIPC();
LOG(("RtspControllerChild::RecvAsyncOpenFailed reason = 0x%x", reason));
if (mListener) {
mListener->OnDisconnected(0, NS_ERROR_CONNECTION_REFUSED);
}
ReleaseChannel();
return true;
}
void
RtspControllerChild::AddIPDLReference()
{
MOZ_ASSERT(!mIPCOpen,
"Attempt to retain more than one IPDL reference");
mIPCOpen = true;
AllowIPC();
AddRef();
}
void
RtspControllerChild::ReleaseIPDLReference()
{
MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference");
mIPCOpen = false;
DisallowIPC();
Release();
}
NS_IMETHODIMP
RtspControllerChild::GetTrackMetaData(
uint8_t index,
nsIStreamingProtocolMetaData **result)
{
if (GetMetaDataLength() <= 0 || index >= GetMetaDataLength()) {
LOG(("RtspControllerChild:: meta data is not available"));
return NS_ERROR_NOT_INITIALIZED;
}
LOG(("RtspControllerChild::GetTrackMetaData() %d", index));
NS_IF_ADDREF(*result = mMetaArray[index]);
return NS_OK;
}
enum IPCEvent
{
SendNoneEvent = 0,
SendPlayEvent,
SendPauseEvent,
SendSeekEvent,
SendStopEvent,
SendPlaybackEndedEvent
};
class SendIPCEvent : public Runnable
{
public:
SendIPCEvent(RtspControllerChild *aController, IPCEvent aEvent)
: mController(aController)
, mEvent(aEvent)
, mSeekTime(0)
{
}
SendIPCEvent(RtspControllerChild *aController,
IPCEvent aEvent,
uint64_t aSeekTime)
: mController(aController)
, mEvent(aEvent)
, mSeekTime(aSeekTime)
{
}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
if (mController->OKToSendIPC() == false) {
// Don't send any more IPC events; no guarantee that parent objects are
// still alive.
return NS_ERROR_FAILURE;
}
bool rv = true;
if (mEvent == SendPlayEvent) {
rv = mController->SendPlay();
} else if (mEvent == SendPauseEvent) {
rv = mController->SendPause();
} else if (mEvent == SendSeekEvent) {
rv = mController->SendSeek(mSeekTime);
} else if (mEvent == SendStopEvent) {
rv = mController->SendStop();
} else if (mEvent == SendPlaybackEndedEvent) {
rv = mController->SendPlaybackEnded();
} else {
LOG(("RtspControllerChild::SendIPCEvent"));
}
if (!rv) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
private:
RefPtr<RtspControllerChild> mController;
IPCEvent mEvent;
uint64_t mSeekTime;
};
//-----------------------------------------------------------------------------
// RtspControllerChild::nsIStreamingProtocolController
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspControllerChild::Play(void)
{
LOG(("RtspControllerChild::Play()"));
MutexAutoLock lock(mTimerLock);
// Cancel the pause timer if it is active because successive pause-play in a
// short duration is unncessary but could impair playback smoothing.
if (mPauseTimer) {
mPauseTimer->Cancel();
mPauseTimer = nullptr;
}
// Start a timer to delay the play operation for a short duration.
if (!mPlayTimer) {
mPlayTimer = do_CreateInstance("@mozilla.org/timer;1");
if (!mPlayTimer) {
return NS_ERROR_NOT_INITIALIZED;
}
// We have to dispatch the timer callback to the main thread because the
// decoder thread is a thread from nsIThreadPool and cannot be the timer
// target. Furthermore, IPC send functions should only be called from the
// main thread.
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
mPlayTimer->SetTarget(mainThread);
mPlayTimer->InitWithFuncCallback(
RtspControllerChild::PlayTimerCallback,
this, kRtspCommandDelayMs,
nsITimer::TYPE_ONE_SHOT);
}
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::Pause(void)
{
LOG(("RtspControllerChild::Pause()"));
MutexAutoLock lock(mTimerLock);
// Cancel the play timer if it is active because successive play-pause in a
// shrot duration is unnecessary but could impair playback smoothing.
if (mPlayTimer) {
mPlayTimer->Cancel();
mPlayTimer = nullptr;
}
// Start a timer to delay the pause operation for a short duration.
if (!mPauseTimer) {
mPauseTimer = do_CreateInstance("@mozilla.org/timer;1");
if (!mPauseTimer) {
return NS_ERROR_NOT_INITIALIZED;
}
// We have to dispatch the timer callback to the main thread because the
// decoder thread is a thread from nsIThreadPool and cannot be the timer
// target.
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
mPauseTimer->SetTarget(mainThread);
mPauseTimer->InitWithFuncCallback(
RtspControllerChild::PauseTimerCallback,
this, kRtspCommandDelayMs,
nsITimer::TYPE_ONE_SHOT);
}
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::Resume(void)
{
LOG(("RtspControllerChild::Resume()"));
NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
if (!--mSuspendCount) {
return Play();
}
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::Suspend(void)
{
LOG(("RtspControllerChild::Suspend()"));
if (!mSuspendCount++) {
return Pause();
}
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::Seek(uint64_t seekTimeUs)
{
LOG(("RtspControllerChild::Seek() %llu", seekTimeUs));
if (NS_IsMainThread()) {
if (!OKToSendIPC() || !SendSeek(seekTimeUs)) {
return NS_ERROR_FAILURE;
}
} else {
nsresult rv = NS_DispatchToMainThread(
new SendIPCEvent(this, SendSeekEvent, seekTimeUs));
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::Stop()
{
LOG(("RtspControllerChild::Stop()"));
StopPlayAndPauseTimer();
if (NS_IsMainThread()) {
if (!OKToSendIPC() || !SendStop()) {
return NS_ERROR_FAILURE;
}
DisallowIPC();
} else {
nsresult rv = NS_DispatchToMainThread(
new SendIPCEvent(this, SendStopEvent));
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::GetTotalTracks(uint8_t *aTracks)
{
NS_ENSURE_ARG_POINTER(aTracks);
*aTracks = kRtspTotalTracks;
if (mTotalTracks) {
*aTracks = mTotalTracks;
}
LOG(("RtspControllerChild::GetTracks() %d", *aTracks));
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::PlaybackEnded()
{
LOG(("RtspControllerChild::PlaybackEnded"));
StopPlayAndPauseTimer();
if (NS_IsMainThread()) {
if (!OKToSendIPC() || !SendPlaybackEnded()) {
return NS_ERROR_FAILURE;
}
} else {
nsresult rv = NS_DispatchToMainThread(
new SendIPCEvent(this, SendPlaybackEndedEvent));
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// RtspControllerChild::nsIStreamingProtocolListener
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspControllerChild::OnMediaDataAvailable(uint8_t index,
const nsACString & data,
uint32_t length,
uint32_t offset,
nsIStreamingProtocolMetaData *meta)
{
LOG(("RtspControllerChild::OnMediaDataAvailable()"));
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
RtspControllerChild::OnConnected(uint8_t index,
nsIStreamingProtocolMetaData *meta)
{
LOG(("RtspControllerChild::OnConnected()"));
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
RtspControllerChild::OnDisconnected(uint8_t index,
nsresult reason)
{
LOG(("RtspControllerChild::OnDisconnected() reason = 0x%x", reason));
return NS_ERROR_NOT_IMPLEMENTED;
}
//-----------------------------------------------------------------------------
// RtspControllerChild::nsIStreamingProtocoController
//-----------------------------------------------------------------------------
NS_IMETHODIMP
RtspControllerChild::Init(nsIURI *aURI)
{
nsresult rv;
if (!aURI) {
LOG(("RtspControllerChild::Init() - invalid URI"));
return NS_ERROR_NOT_INITIALIZED;
}
nsAutoCString host;
int32_t port = -1;
rv = aURI->GetAsciiHost(host);
if (NS_FAILED(rv)) return rv;
// Reject the URL if it doesn't specify a host
if (host.IsEmpty())
return NS_ERROR_MALFORMED_URI;
rv = aURI->GetPort(&port);
if (NS_FAILED(rv)) return rv;
rv = aURI->GetAsciiSpec(mSpec);
if (NS_FAILED(rv)) return rv;
if (!strncmp(mSpec.get(), "rtsp:", 5) == 0)
return NS_ERROR_UNEXPECTED;
mURI = aURI;
return NS_OK;
}
NS_IMETHODIMP
RtspControllerChild::AsyncOpen(nsIStreamingProtocolListener *aListener)
{
LOG(("RtspControllerChild::AsyncOpen()"));
if (!aListener) {
LOG(("RtspControllerChild::AsyncOpen() - invalid listener"));
return NS_ERROR_NOT_INITIALIZED;
}
mListener = aListener;
if (!mChannel) {
LOG(("RtspControllerChild::AsyncOpen() - invalid URI"));
return NS_ERROR_NOT_INITIALIZED;
}
nsCOMPtr<nsIURI> uri;
URIParams uriParams;
mChannel->GetURI(getter_AddRefs(uri));
if (!uri) {
LOG(("RtspControllerChild::AsyncOpen() - invalid URI"));
return NS_ERROR_NOT_INITIALIZED;
}
SerializeURI(uri, uriParams);
if (!OKToSendIPC() || !SendAsyncOpen(uriParams)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// RtspControllerChild static member methods
//-----------------------------------------------------------------------------
//static
void
RtspControllerChild::PlayTimerCallback(nsITimer *aTimer, void *aClosure)
{
MOZ_ASSERT(aTimer);
MOZ_ASSERT(aClosure);
MOZ_ASSERT(NS_IsMainThread());
RtspControllerChild *self = static_cast<RtspControllerChild*>(aClosure);
MutexAutoLock lock(self->mTimerLock);
if (!self->mPlayTimer || !self->OKToSendIPC()) {
return;
}
self->SendPlay();
self->mPlayTimer = nullptr;
}
//static
void
RtspControllerChild::PauseTimerCallback(nsITimer *aTimer, void *aClosure)
{
MOZ_ASSERT(aTimer);
MOZ_ASSERT(aClosure);
MOZ_ASSERT(NS_IsMainThread());
RtspControllerChild *self = static_cast<RtspControllerChild*>(aClosure);
MutexAutoLock lock(self->mTimerLock);
if (!self->mPauseTimer || !self->OKToSendIPC()) {
return;
}
self->SendPause();
self->mPauseTimer = nullptr;
}
} // namespace net
} // namespace mozilla

View File

@ -1,99 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 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 RtspControllerChild_h
#define RtspControllerChild_h
#include "mozilla/net/PRtspControllerChild.h"
#include "nsIStreamingProtocolController.h"
#include "nsIChannel.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsTArray.h"
#include "mozilla/net/RtspChannelChild.h"
#include "mozilla/Mutex.h"
#include "nsITimer.h"
namespace mozilla {
namespace net {
class RtspControllerChild : public nsIStreamingProtocolController
, public nsIStreamingProtocolListener
, public PRtspControllerChild
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISTREAMINGPROTOCOLCONTROLLER
NS_DECL_NSISTREAMINGPROTOCOLLISTENER
RtspControllerChild(nsIChannel *channel);
bool RecvOnConnected(const uint8_t& index,
InfallibleTArray<RtspMetadataParam>&& meta);
bool RecvOnMediaDataAvailable(
const uint8_t& index,
const nsCString& data,
const uint32_t& length,
const uint32_t& offset,
InfallibleTArray<RtspMetadataParam>&& meta);
bool RecvOnDisconnected(const uint8_t& index,
const nsresult& reason);
bool RecvAsyncOpenFailed(const nsresult& reason);
void AddIPDLReference();
void ReleaseIPDLReference();
void AddMetaData(already_AddRefed<nsIStreamingProtocolMetaData>&& meta);
int GetMetaDataLength();
bool OKToSendIPC();
void AllowIPC();
void DisallowIPC();
// These callbacks will be called when mPlayTimer/mPauseTimer fires.
static void PlayTimerCallback(nsITimer *aTimer, void *aClosure);
static void PauseTimerCallback(nsITimer *aTimer, void *aClosure);
protected:
~RtspControllerChild();
private:
bool mIPCOpen;
// The intention of this variable is just to avoid any IPC message to be sent
// when this flag is set as false. Nothing more.
bool mIPCAllowed;
// Dummy channel used to aid MediaResource creation in HTMLMediaElement.
nsCOMPtr<nsIChannel> mChannel;
// The nsIStreamingProtocolListener implementation.
nsCOMPtr<nsIStreamingProtocolListener> mListener;
// RTSP URL refer to a stream or an aggregate of streams.
nsCOMPtr<nsIURI> mURI;
// Array refer to metadata of the media stream.
nsTArray<nsCOMPtr<nsIStreamingProtocolMetaData>> mMetaArray;
// ASCII encoded URL spec
nsCString mSpec;
// The total tracks for the given media stream session.
uint32_t mTotalTracks;
// Current suspension depth for this channel object
uint32_t mSuspendCount;
// Detach channel-controller relationship.
void ReleaseChannel();
// This lock protects mPlayTimer and mPauseTimer.
Mutex mTimerLock;
// Timers to delay the play and pause operations.
// They are used for optimization and to avoid sending unnecessary requests to
// the server.
nsCOMPtr<nsITimer> mPlayTimer;
nsCOMPtr<nsITimer> mPauseTimer;
// Timers should be stopped if we are going to terminate, such as when
// receiving Stop command or OnDisconnected event.
void StopPlayAndPauseTimer();
};
} // namespace net
} // namespace mozilla
#endif // RtspControllerChild_h

View File

@ -1,295 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 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/. */
#include "RtspControllerParent.h"
#include "RtspController.h"
#include "nsIAuthPromptProvider.h"
#include "nsThreadUtils.h"
#include "nsProxyRelease.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "mozilla/unused.h"
#include "mozilla/Logging.h"
#include <sys/types.h>
#define SEND_DISCONNECT_IF_ERROR(rv) \
if (NS_FAILED(rv) && mIPCOpen && mTotalTracks > 0ul) { \
for (uint32_t i = 0; i < mTotalTracks; i++) { \
Unused << SendOnDisconnected(i, rv); \
} \
}
using namespace mozilla::ipc;
namespace mozilla {
namespace net {
LazyLogModule gRtspLog("nsRtsp");
#undef LOG
#define LOG(args) MOZ_LOG(mozilla::net::gRtspLog, mozilla::LogLevel::Debug, args)
void
RtspControllerParent::Destroy()
{
// If we're being destroyed on a non-main thread, we AddRef again and use a
// proxy to release the RtspControllerParent on the main thread, where the
// RtspControllerParent is deleted. This ensures we only delete the
// RtspControllerParent on the main thread.
if (!NS_IsMainThread()) {
RefPtr<RtspControllerParent> doomed(this);
NS_ReleaseOnMainThread(doomed.forget(), true);
} else {
delete this;
}
}
NS_IMPL_ADDREF(RtspControllerParent)
NS_IMPL_RELEASE_WITH_DESTROY(RtspControllerParent, Destroy())
NS_IMPL_QUERY_INTERFACE(RtspControllerParent,
nsIStreamingProtocolListener)
RtspControllerParent::RtspControllerParent()
: mIPCOpen(true)
, mTotalTracks(0)
{
}
RtspControllerParent::~RtspControllerParent()
{
}
void
RtspControllerParent::ActorDestroy(ActorDestroyReason why)
{
LOG(("RtspControllerParent::ActorDestroy()"));
mIPCOpen = false;
NS_ENSURE_TRUE_VOID(mController);
if (mController) {
mController->Stop();
mController = nullptr;
}
}
bool
RtspControllerParent::RecvAsyncOpen(const URIParams& aURI)
{
LOG(("RtspControllerParent::RecvAsyncOpen()"));
mURI = DeserializeURI(aURI);
mController = new RtspController(nullptr);
mController->Init(mURI);
nsresult rv = mController->AsyncOpen(this);
if (NS_SUCCEEDED(rv)) return true;
mController = nullptr;
return SendAsyncOpenFailed(rv);
}
bool
RtspControllerParent::RecvPlay()
{
LOG(("RtspControllerParent::RecvPlay()"));
NS_ENSURE_TRUE(mController, true);
nsresult rv = mController->Play();
SEND_DISCONNECT_IF_ERROR(rv)
return true;
}
bool
RtspControllerParent::RecvPause()
{
LOG(("RtspControllerParent::RecvPause()"));
NS_ENSURE_TRUE(mController, true);
nsresult rv = mController->Pause();
SEND_DISCONNECT_IF_ERROR(rv)
return true;
}
bool
RtspControllerParent::RecvResume()
{
LOG(("RtspControllerParent::RecvResume()"));
NS_ENSURE_TRUE(mController, true);
nsresult rv = mController->Resume();
SEND_DISCONNECT_IF_ERROR(rv)
return true;
}
bool
RtspControllerParent::RecvSuspend()
{
LOG(("RtspControllerParent::RecvSuspend()"));
NS_ENSURE_TRUE(mController, true);
nsresult rv = mController->Suspend();
SEND_DISCONNECT_IF_ERROR(rv)
return true;
}
bool
RtspControllerParent::RecvSeek(const uint64_t& offset)
{
LOG(("RtspControllerParent::RecvSeek()"));
NS_ENSURE_TRUE(mController, true);
nsresult rv = mController->Seek(offset);
SEND_DISCONNECT_IF_ERROR(rv)
return true;
}
bool
RtspControllerParent::RecvStop()
{
LOG(("RtspControllerParent::RecvStop()"));
NS_ENSURE_TRUE(mController, true);
nsresult rv = mController->Stop();
NS_ENSURE_SUCCESS(rv, true);
return true;
}
bool
RtspControllerParent::RecvPlaybackEnded()
{
LOG(("RtspControllerParent::RecvPlaybackEnded()"));
NS_ENSURE_TRUE(mController, true);
nsresult rv = mController->PlaybackEnded();
SEND_DISCONNECT_IF_ERROR(rv)
return true;
}
NS_IMETHODIMP
RtspControllerParent::OnMediaDataAvailable(uint8_t index,
const nsACString & data,
uint32_t length,
uint32_t offset,
nsIStreamingProtocolMetaData *meta)
{
NS_ENSURE_ARG_POINTER(meta);
uint32_t int32Value;
uint64_t int64Value;
nsresult rv = meta->GetTimeStamp(&int64Value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
LOG(("RtspControllerParent:: OnMediaDataAvailable %d:%d time %lld",
index, length, int64Value));
// Serialize meta data.
nsCString name;
name.AssignLiteral("TIMESTAMP");
InfallibleTArray<RtspMetadataParam> metaData;
metaData.AppendElement(RtspMetadataParam(name, int64Value));
name.AssignLiteral("FRAMETYPE");
rv = meta->GetFrameType(&int32Value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
metaData.AppendElement(RtspMetadataParam(name, int32Value));
nsCString stream;
stream.Assign(data);
if (!mIPCOpen ||
!SendOnMediaDataAvailable(index, stream, length, offset, metaData)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
RtspControllerParent::OnConnected(uint8_t index,
nsIStreamingProtocolMetaData *meta)
{
NS_ENSURE_ARG_POINTER(meta);
uint32_t int32Value;
uint64_t int64Value;
LOG(("RtspControllerParent:: OnConnected"));
// Serialize meta data.
InfallibleTArray<RtspMetadataParam> metaData;
nsCString name;
name.AssignLiteral("TRACKS");
nsresult rv = meta->GetTotalTracks(&mTotalTracks);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
metaData.AppendElement(RtspMetadataParam(name, mTotalTracks));
name.AssignLiteral("MIMETYPE");
nsCString mimeType;
rv = meta->GetMimeType(mimeType);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
metaData.AppendElement(RtspMetadataParam(name, mimeType));
name.AssignLiteral("WIDTH");
rv = meta->GetWidth(&int32Value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
metaData.AppendElement(RtspMetadataParam(name, int32Value));
name.AssignLiteral("HEIGHT");
rv = meta->GetHeight(&int32Value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
metaData.AppendElement(RtspMetadataParam(name, int32Value));
name.AssignLiteral("DURATION");
rv = meta->GetDuration(&int64Value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
metaData.AppendElement(RtspMetadataParam(name, int64Value));
name.AssignLiteral("SAMPLERATE");
rv = meta->GetSampleRate(&int32Value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
metaData.AppendElement(RtspMetadataParam(name, int32Value));
name.AssignLiteral("TIMESTAMP");
rv = meta->GetTimeStamp(&int64Value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
metaData.AppendElement(RtspMetadataParam(name, int64Value));
name.AssignLiteral("CHANNELCOUNT");
rv = meta->GetChannelCount(&int32Value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
metaData.AppendElement(RtspMetadataParam(name, int32Value));
nsCString esds;
rv = meta->GetEsdsData(esds);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
name.AssignLiteral("ESDS");
metaData.AppendElement(RtspMetadataParam(name, esds));
nsCString avcc;
rv = meta->GetAvccData(avcc);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
name.AssignLiteral("AVCC");
metaData.AppendElement(RtspMetadataParam(name, avcc));
if (!mIPCOpen || !SendOnConnected(index, metaData)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
RtspControllerParent::OnDisconnected(uint8_t index,
nsresult reason)
{
LOG(("RtspControllerParent::OnDisconnected() for track %d reason = 0x%x", index, reason));
if (!mIPCOpen || !SendOnDisconnected(index, reason)) {
return NS_ERROR_FAILURE;
}
if (mController) {
mController = nullptr;
}
return NS_OK;
}
} // namespace net
} // namespace mozilla

View File

@ -1,57 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 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 RtspControllerParent_h
#define RtspControllerParent_h
#include "mozilla/net/PRtspControllerParent.h"
#include "mozilla/net/NeckoParent.h"
#include "nsIStreamingProtocolController.h"
#include "nsILoadContext.h"
#include "nsIURI.h"
#include "nsCOMPtr.h"
#include "nsString.h"
namespace mozilla {
namespace net {
class RtspControllerParent : public PRtspControllerParent
, public nsIStreamingProtocolListener
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISTREAMINGPROTOCOLLISTENER
RtspControllerParent();
bool RecvAsyncOpen(const URIParams& aURI);
bool RecvPlay();
bool RecvPause();
bool RecvResume();
bool RecvSuspend();
bool RecvSeek(const uint64_t& offset);
bool RecvStop();
bool RecvPlaybackEnded();
protected:
~RtspControllerParent();
private:
bool mIPCOpen;
void ActorDestroy(ActorDestroyReason why);
// RTSP URL refer to a stream or an aggregate of streams.
nsCOMPtr<nsIURI> mURI;
// The nsIStreamingProtocolController implementation.
nsCOMPtr<nsIStreamingProtocolController> mController;
uint32_t mTotalTracks;
// Ensure we are destroyed on the main thread.
void Destroy();
};
} // namespace net
} // namespace mozilla
#endif // RtspControllerParent_h

View File

@ -1,246 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 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/. */
#include "RtspMetaData.h"
#include "mozilla/Logging.h"
using namespace mozilla;
namespace mozilla {
namespace net {
NS_IMPL_ISUPPORTS(RtspMetaData, nsIStreamingProtocolMetaData)
RtspMetaData::RtspMetaData()
: mIndex(0)
, mWidth(0)
, mHeight(0)
, mDuration(0)
, mSampleRate(0)
, mCurrentTimeStamp(0)
, mChannelCount(0)
{
mMimeType.AssignLiteral("NONE");
}
RtspMetaData::~RtspMetaData()
{
}
nsresult
RtspMetaData::DeserializeRtspMetaData(const InfallibleTArray<RtspMetadataParam>& metaArray)
{
nsresult rv;
// Deserialize meta data.
for (uint32_t i = 0; i < metaArray.Length(); i++) {
const RtspMetaValue& value = metaArray[i].value();
const nsCString& name = metaArray[i].name();
if (name.EqualsLiteral("FRAMETYPE")) {
rv = SetFrameType(value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
} else if (name.EqualsLiteral("TIMESTAMP")) {
rv = SetTimeStamp(value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
} else if (name.EqualsLiteral("TRACKS")) {
rv = SetTotalTracks(value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
} else if(name.EqualsLiteral("MIMETYPE")) {
rv = SetMimeType(value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
} else if (name.EqualsLiteral("WIDTH")) {
rv = SetWidth(value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
} else if (name.EqualsLiteral("HEIGHT")) {
rv = SetHeight(value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
} else if (name.EqualsLiteral("SAMPLERATE")) {
rv = SetSampleRate(value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
} else if(name.EqualsLiteral("DURATION")) {
rv = SetDuration(value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
} else if (name.EqualsLiteral("CHANNELCOUNT")) {
rv = SetChannelCount(value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
} else if (name.EqualsLiteral("ESDS")) {
rv = SetEsdsData(value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
} else if (name.EqualsLiteral("AVCC")) {
rv = SetAvccData(value);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
}
}
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::GetFrameType(uint32_t *aFrameType)
{
NS_ENSURE_ARG_POINTER(aFrameType);
*aFrameType = mFrameType;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::SetFrameType(uint32_t aFrameType)
{
mFrameType = aFrameType;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::GetTotalTracks(uint32_t *aTracks)
{
NS_ENSURE_ARG_POINTER(aTracks);
*aTracks = mTotalTracks;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::SetTotalTracks(uint32_t aTracks)
{
mTotalTracks = aTracks;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::GetMimeType(nsACString & aMimeType)
{
aMimeType.Assign(mMimeType);
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::SetMimeType(const nsACString & aMimeType)
{
mMimeType.Assign(aMimeType);
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::GetWidth(uint32_t *aWidth)
{
NS_ENSURE_ARG_POINTER(aWidth);
*aWidth = mWidth;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::SetWidth(uint32_t aWidth)
{
mWidth = aWidth;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::GetHeight(uint32_t *aHeight)
{
NS_ENSURE_ARG_POINTER(aHeight);
*aHeight = mHeight;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::SetHeight(uint32_t aHeight)
{
mHeight = aHeight;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::GetDuration(uint64_t *aDuration)
{
NS_ENSURE_ARG_POINTER(aDuration);
*aDuration = mDuration;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::SetDuration(uint64_t aDuration)
{
mDuration = aDuration;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::GetSampleRate(uint32_t *aSampleRate)
{
NS_ENSURE_ARG_POINTER(aSampleRate);
*aSampleRate = mSampleRate;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::SetSampleRate(uint32_t aSampleRate)
{
mSampleRate = aSampleRate;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::GetTimeStamp(uint64_t *aTimeStamp)
{
NS_ENSURE_ARG_POINTER(aTimeStamp);
*aTimeStamp = mCurrentTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::SetTimeStamp(uint64_t aTimeStamp)
{
mCurrentTimeStamp = aTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::GetChannelCount(uint32_t *aChannelCount)
{
NS_ENSURE_ARG_POINTER(aChannelCount);
*aChannelCount = mChannelCount;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::SetChannelCount(uint32_t aChannelCount)
{
mChannelCount = aChannelCount;
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::GetEsdsData(nsACString & aESDS)
{
aESDS.Assign(mESDS);
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::SetEsdsData(const nsACString & aESDS)
{
mESDS.Assign(aESDS);
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::GetAvccData(nsACString & aAVCC)
{
aAVCC.Assign(mAVCC);
return NS_OK;
}
NS_IMETHODIMP
RtspMetaData::SetAvccData(const nsACString & aAVCC)
{
mAVCC.Assign(aAVCC);
return NS_OK;
}
} // namespace mozilla::net
} // namespace mozilla

View File

@ -1,49 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 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 RtspMetaData_h
#define RtspMetaData_h
#include "mozilla/net/PRtspController.h"
#include "nsIStreamingProtocolController.h"
#include "nsCOMPtr.h"
#include "nsString.h"
namespace mozilla {
namespace net {
class RtspMetaData final : public nsIStreamingProtocolMetaData
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISTREAMINGPROTOCOLMETADATA
RtspMetaData();
nsresult DeserializeRtspMetaData(const InfallibleTArray<RtspMetadataParam>& metaArray);
protected:
~RtspMetaData();
private:
uint32_t mFrameType;
uint32_t mIndex;
uint32_t mTotalTracks;
uint32_t mWidth;
uint32_t mHeight;
uint64_t mDuration;
uint32_t mSampleRate;
uint64_t mCurrentTimeStamp;
uint32_t mChannelCount;
nsCString mMimeType;
nsCString mESDS;
nsCString mAVCC;
};
} // namespace net
} // namespace mozilla
#endif // RtspMetaData_h

View File

@ -1,73 +0,0 @@
# 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.net += [
'controller/RtspController.h',
'controller/RtspControllerChild.h',
'controller/RtspControllerParent.h',
'controller/RtspMetaData.h',
'rtsp/RTSPSource.h',
'RtspChannelChild.h',
'RtspChannelParent.h',
'RtspHandler.h',
]
UNIFIED_SOURCES += [
'controller/RtspController.cpp',
'controller/RtspControllerChild.cpp',
'controller/RtspControllerParent.cpp',
'controller/RtspMetaData.cpp',
'RtspChannelChild.cpp',
'RtspChannelParent.cpp',
'RtspHandler.cpp',
]
# Android sources
SOURCES += [
'rtsp/AAMRAssembler.cpp',
'rtsp/AAVCAssembler.cpp',
'rtsp/AH263Assembler.cpp',
'rtsp/AMPEG4AudioAssembler.cpp',
'rtsp/AMPEG4ElementaryAssembler.cpp',
'rtsp/APacketSource.cpp',
'rtsp/ARawAudioAssembler.cpp',
'rtsp/ARTPAssembler.cpp',
'rtsp/ARTPConnection.cpp',
'rtsp/ARTPSource.cpp',
'rtsp/ARTPWriter.cpp',
'rtsp/ARTSPConnection.cpp',
'rtsp/ASessionDescription.cpp',
'rtsp/RTSPSource.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
# Suppress some GCC warnings being treated as errors:
# - about attributes on forward declarations for types that are already
# defined, which complains about an important MOZ_EXPORT for android::AString
if CONFIG['GNU_CC']:
CXXFLAGS += ['-Wno-error=attributes']
FINAL_LIBRARY = 'xul'
DEFINES['IMPL_NS_NET'] = True
DEFINES['FORCE_PR_LOG'] = True
LOCAL_INCLUDES += [
'/dom/base',
'/netwerk/base',
'controller',
'rtsp',
]
for var in ('IMPL_NS_NET', 'FORCE_PR_LOG'):
DEFINES[var] = True
if CONFIG['ANDROID_VERSION'] == '15':
LOCAL_INCLUDES += ['%' + '%s/frameworks/base/media/libstagefright/mpeg2ts' % CONFIG['ANDROID_SOURCE']]
else:
LOCAL_INCLUDES += ['%' + '%s/frameworks/av/media/libstagefright/mpeg2ts' % CONFIG['ANDROID_SOURCE']]
CXXFLAGS += ['-Wno-multichar']

View File

@ -1,244 +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.
*/
#define LOG_TAG "AAMRAssembler"
#include "RtspPrlog.h"
#include "AAMRAssembler.h"
#include "ARTPSource.h"
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/Utils.h>
namespace android {
static bool GetAttribute(const char *s, const char *key, AString *value) {
value->clear();
size_t keyLen = strlen(key);
for (;;) {
const char *colonPos = strchr(s, ';');
size_t len =
(colonPos == NULL) ? strlen(s) : colonPos - s;
if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
value->setTo(&s[keyLen + 1], len - keyLen - 1);
return true;
}
if (len == keyLen && !strncmp(s, key, keyLen)) {
value->setTo("1");
return true;
}
if (colonPos == NULL) {
return false;
}
s = colonPos + 1;
}
}
AAMRAssembler::AAMRAssembler(
const sp<AMessage> &notify, bool isWide, const AString &params)
: mIsWide(isWide),
mNotifyMsg(notify),
mNextExpectedSeqNoValid(false),
mNextExpectedSeqNo(0) {
AString value;
CHECK(GetAttribute(params.c_str(), "octet-align", &value) && value == "1");
CHECK(!GetAttribute(params.c_str(), "crc", &value) || value == "0");
CHECK(!GetAttribute(params.c_str(), "interleaving", &value));
}
AAMRAssembler::~AAMRAssembler() {
}
ARTPAssembler::AssemblyStatus AAMRAssembler::assembleMore(
const sp<ARTPSource> &source) {
return addPacket(source);
}
static size_t getFrameSize(bool isWide, unsigned FT) {
static const size_t kFrameSizeNB[9] = {
95, 103, 118, 134, 148, 159, 204, 244, 39
};
static const size_t kFrameSizeWB[10] = {
132, 177, 253, 285, 317, 365, 397, 461, 477, 40
};
if (FT == 15) {
return 1;
}
size_t frameSize = isWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
// Round up bits to bytes and add 1 for the header byte.
frameSize = (frameSize + 7) / 8 + 1;
return frameSize;
}
ARTPAssembler::AssemblyStatus AAMRAssembler::addPacket(
const sp<ARTPSource> &source) {
List<sp<ABuffer> > *queue = source->queue();
if (queue->empty()) {
return NOT_ENOUGH_DATA;
}
if (mNextExpectedSeqNoValid) {
List<sp<ABuffer> >::iterator it = queue->begin();
while (it != queue->end()) {
if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
break;
}
it = queue->erase(it);
}
if (queue->empty()) {
return NOT_ENOUGH_DATA;
}
}
sp<ABuffer> buffer = *queue->begin();
if (!mNextExpectedSeqNoValid) {
mNextExpectedSeqNoValid = true;
mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
} else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
LOGV("Not the sequence number I expected");
return WRONG_SEQUENCE_NUMBER;
}
// hexdump(buffer->data(), buffer->size());
if (buffer->size() < 1) {
queue->erase(queue->begin());
++mNextExpectedSeqNo;
LOGW("AMR packet too short.");
return MALFORMED_PACKET;
}
unsigned payloadHeader = buffer->data()[0];
if ((payloadHeader & 0x0f) != 0u) {
queue->erase(queue->begin());
++mNextExpectedSeqNo;
LOGW("Wrong payload header");
return MALFORMED_PACKET;
}
Vector<uint8_t> tableOfContents;
size_t offset = 1;
size_t totalSize = 0;
for (;;) {
if (offset >= buffer->size()) {
queue->erase(queue->begin());
++mNextExpectedSeqNo;
LOGW("Unable to parse TOC.");
return MALFORMED_PACKET;
}
uint8_t toc = buffer->data()[offset++];
unsigned FT = (toc >> 3) & 0x0f;
if ((toc & 3) != 0
|| (mIsWide && FT > 9 && FT != 15)
|| (!mIsWide && FT > 8 && FT != 15)) {
queue->erase(queue->begin());
++mNextExpectedSeqNo;
LOGW("Illegal TOC entry.");
return MALFORMED_PACKET;
}
totalSize += getFrameSize(mIsWide, (toc >> 3) & 0x0f);
tableOfContents.push(toc);
if (0 == (toc & 0x80)) {
break;
}
}
sp<ABuffer> accessUnit = new ABuffer(totalSize);
if (!CopyTimes(accessUnit, buffer)) {
queue->erase(queue->begin());
++mNextExpectedSeqNo;
return MALFORMED_PACKET;
}
size_t dstOffset = 0;
for (size_t i = 0; i < tableOfContents.size(); ++i) {
uint8_t toc = tableOfContents[i];
size_t frameSize = getFrameSize(mIsWide, (toc >> 3) & 0x0f);
if (offset + frameSize - 1 > buffer->size()) {
queue->erase(queue->begin());
++mNextExpectedSeqNo;
LOGW("AMR packet too short.");
return MALFORMED_PACKET;
}
accessUnit->data()[dstOffset++] = toc;
memcpy(accessUnit->data() + dstOffset,
buffer->data() + offset, frameSize - 1);
offset += frameSize - 1;
dstOffset += frameSize - 1;
}
sp<AMessage> msg = mNotifyMsg->dup();
msg->setObject("access-unit", accessUnit);
msg->post();
queue->erase(queue->begin());
++mNextExpectedSeqNo;
return OK;
}
void AAMRAssembler::packetLost() {
CHECK(mNextExpectedSeqNoValid);
++mNextExpectedSeqNo;
}
void AAMRAssembler::onByeReceived() {
sp<AMessage> msg = mNotifyMsg->dup();
msg->setInt32("eos", true);
msg->post();
}
} // namespace android

View File

@ -1,60 +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.
*/
#ifndef A_AMR_ASSEMBLER_H_
#define A_AMR_ASSEMBLER_H_
#include "mozilla/Types.h"
#include "ARTPAssembler.h"
#include <utils/List.h>
#include <stdint.h>
namespace android {
struct MOZ_EXPORT AMessage;
struct MOZ_EXPORT AString;
struct AAMRAssembler : public ARTPAssembler {
AAMRAssembler(
const sp<AMessage> &notify, bool isWide,
const AString &params);
protected:
virtual ~AAMRAssembler();
virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
virtual void onByeReceived();
virtual void packetLost();
private:
bool mIsWide;
sp<AMessage> mNotifyMsg;
bool mNextExpectedSeqNoValid;
uint32_t mNextExpectedSeqNo;
AssemblyStatus addPacket(const sp<ARTPSource> &source);
DISALLOW_EVIL_CONSTRUCTORS(AAMRAssembler);
};
} // namespace android
#endif // A_AMR_ASSEMBLER_H_

View File

@ -1,410 +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.
*/
#define LOG_TAG "AAVCAssembler"
#include "RtspPrlog.h"
#include "AAVCAssembler.h"
#include "ARTPSource.h"
#include "mozilla/Assertions.h"
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/hexdump.h>
#include <stdint.h>
namespace android {
// static
AAVCAssembler::AAVCAssembler(const sp<AMessage> &notify)
: mNotifyMsg(notify),
mAccessUnitRTPTime(0),
mNextExpectedSeqNoValid(false),
mNextExpectedSeqNo(0),
mAccessUnitDamaged(false) {
}
AAVCAssembler::~AAVCAssembler() {
}
ARTPAssembler::AssemblyStatus AAVCAssembler::addNALUnit(
const sp<ARTPSource> &source) {
List<sp<ABuffer> > *queue = source->queue();
if (queue->empty()) {
return NOT_ENOUGH_DATA;
}
if (mNextExpectedSeqNoValid) {
List<sp<ABuffer> >::iterator it = queue->begin();
while (it != queue->end()) {
if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
break;
}
it = queue->erase(it);
}
if (queue->empty()) {
return NOT_ENOUGH_DATA;
}
}
sp<ABuffer> buffer = *queue->begin();
if (!mNextExpectedSeqNoValid) {
mNextExpectedSeqNoValid = true;
mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
} else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
LOGV("Not the sequence number I expected");
return WRONG_SEQUENCE_NUMBER;
}
const uint8_t *data = buffer->data();
size_t size = buffer->size();
if (size < 1 || (data[0] & 0x80)) {
// Corrupt.
LOGW("Ignoring corrupt buffer.");
queue->erase(queue->begin());
++mNextExpectedSeqNo;
return MALFORMED_PACKET;
}
unsigned nalType = data[0] & 0x1f;
if (nalType >= 1 && nalType <= 23) {
bool success = addSingleNALUnit(buffer);
queue->erase(queue->begin());
++mNextExpectedSeqNo;
return success ? OK : MALFORMED_PACKET;
} else if (nalType == 28) {
// FU-A
return addFragmentedNALUnit(queue);
} else if (nalType == 24) {
// STAP-A
bool success = addSingleTimeAggregationPacket(buffer);
queue->erase(queue->begin());
++mNextExpectedSeqNo;
return success ? OK : MALFORMED_PACKET;
} else {
LOGV("Ignoring unsupported buffer (nalType=%d)", nalType);
queue->erase(queue->begin());
++mNextExpectedSeqNo;
return MALFORMED_PACKET;
}
}
bool AAVCAssembler::addSingleNALUnit(const sp<ABuffer> &buffer) {
LOGV("addSingleNALUnit of size %d", buffer->size());
#if !LOG_NDEBUG
hexdump(buffer->data(), buffer->size());
#endif
uint32_t rtpTime;
if (!buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)) {
LOGW("Cannot find rtp-time");
return false;
}
if (!mNALUnits.empty() && rtpTime != mAccessUnitRTPTime) {
if (!submitAccessUnit()) {
LOGW("Cannot find rtp-time. Malformed packet.");
return false;
}
}
mAccessUnitRTPTime = rtpTime;
mNALUnits.push_back(buffer);
return true;
}
bool AAVCAssembler::addSingleTimeAggregationPacket(const sp<ABuffer> &buffer) {
const uint8_t *data = buffer->data();
size_t size = buffer->size();
if (size < 3) {
LOGV("Discarding too small STAP-A packet.");
return false;
}
++data;
--size;
while (size >= 2) {
size_t nalSize = (data[0] << 8) | data[1];
if (size < nalSize + 2) {
LOGV("Discarding malformed STAP-A packet.");
return false;
}
sp<ABuffer> unit = new ABuffer(nalSize);
memcpy(unit->data(), &data[2], nalSize);
if (!CopyTimes(unit, buffer)) {
return false;
}
if (!addSingleNALUnit(unit)) {
LOGW("addSingleNALUnit() failed");
return false;
}
data += 2 + nalSize;
size -= 2 + nalSize;
}
if (size != 0) {
LOGV("Unexpected padding at end of STAP-A packet.");
}
return true;
}
ARTPAssembler::AssemblyStatus AAVCAssembler::addFragmentedNALUnit(
List<sp<ABuffer> > *queue) {
MOZ_ASSERT(!queue->empty());
sp<ABuffer> buffer = *queue->begin();
const uint8_t *data = buffer->data();
size_t size = buffer->size();
if (size <= 0) {
LOGW("Buffer is empty");
queue->erase(queue->begin());
++mNextExpectedSeqNo;
return MALFORMED_PACKET;
}
unsigned indicator = data[0];
if ((indicator & 0x1f) != 28) {
LOGW("Indicator is wrong");
queue->erase(queue->begin());
++mNextExpectedSeqNo;
return MALFORMED_PACKET;
}
if (size < 2) {
LOGW("Ignoring malformed FU buffer (size = %d)", size);
queue->erase(queue->begin());
++mNextExpectedSeqNo;
return MALFORMED_PACKET;
}
if (!(data[1] & 0x80)) {
// Start bit not set on the first buffer.
LOGW("Start bit not set on first buffer");
queue->erase(queue->begin());
++mNextExpectedSeqNo;
return MALFORMED_PACKET;
}
uint32_t nalType = data[1] & 0x1f;
uint32_t nri = (data[0] >> 5) & 3;
uint32_t expectedSeqNo = (uint32_t)buffer->int32Data() + 1;
size_t totalSize = size - 2;
size_t totalCount = 1;
bool complete = false;
if (data[1] & 0x40) {
// Huh? End bit also set on the first buffer.
LOGV("Grrr. This isn't fragmented at all.");
complete = true;
} else {
List<sp<ABuffer> >::iterator it = ++queue->begin();
while (it != queue->end()) {
LOGV("sequence length %d", totalCount);
const sp<ABuffer> &buffer = *it;
const uint8_t *data = buffer->data();
size_t size = buffer->size();
if ((uint32_t)buffer->int32Data() != expectedSeqNo) {
LOGV("sequence not complete, expected seqNo %d, got %d",
expectedSeqNo, (uint32_t)buffer->int32Data());
return WRONG_SEQUENCE_NUMBER;
}
if (size < 2
|| data[0] != indicator
|| (data[1] & 0x1f) != nalType
|| (data[1] & 0x80)) {
LOGW("Ignoring malformed FU buffer.");
// Delete the whole start of the FU.
it = queue->begin();
for (size_t i = 0; i <= totalCount; ++i) {
it = queue->erase(it);
}
mNextExpectedSeqNo = expectedSeqNo + 1;
return MALFORMED_PACKET;
}
totalSize += size - 2;
++totalCount;
expectedSeqNo = expectedSeqNo + 1;
if (data[1] & 0x40) {
// This is the last fragment.
complete = true;
break;
}
++it;
}
}
if (!complete) {
return NOT_ENOUGH_DATA;
}
mNextExpectedSeqNo = expectedSeqNo;
// We found all the fragments that make up the complete NAL unit.
// Leave room for the header. So far totalSize did not include the
// header byte.
++totalSize;
sp<ABuffer> unit = new ABuffer(totalSize);
if (!CopyTimes(unit, *queue->begin())) {
return MALFORMED_PACKET;
}
unit->data()[0] = (nri << 5) | nalType;
size_t offset = 1;
List<sp<ABuffer> >::iterator it = queue->begin();
for (size_t i = 0; i < totalCount; ++i) {
const sp<ABuffer> &buffer = *it;
LOGV("piece #%d/%d", i + 1, totalCount);
#if !LOG_NDEBUG
hexdump(buffer->data(), buffer->size());
#endif
memcpy(unit->data() + offset, buffer->data() + 2, buffer->size() - 2);
offset += buffer->size() - 2;
it = queue->erase(it);
}
unit->setRange(0, totalSize);
if (!addSingleNALUnit(unit)) {
return MALFORMED_PACKET;
}
LOGV("successfully assembled a NAL unit from fragments.");
return OK;
}
bool AAVCAssembler::submitAccessUnit() {
MOZ_ASSERT(!mNALUnits.empty());
LOGV("Access unit complete (%d nal units)", mNALUnits.size());
size_t totalSize = 0;
for (List<sp<ABuffer> >::iterator it = mNALUnits.begin();
it != mNALUnits.end(); ++it) {
totalSize += 4 + (*it)->size();
}
sp<ABuffer> accessUnit = new ABuffer(totalSize);
size_t offset = 0;
for (List<sp<ABuffer> >::iterator it = mNALUnits.begin();
it != mNALUnits.end(); ++it) {
memcpy(accessUnit->data() + offset, "\x00\x00\x00\x01", 4);
offset += 4;
sp<ABuffer> nal = *it;
memcpy(accessUnit->data() + offset, nal->data(), nal->size());
offset += nal->size();
}
if (!CopyTimes(accessUnit, *mNALUnits.begin())) {
return false;
}
#if 0
printf(mAccessUnitDamaged ? "X" : ".");
fflush(stdout);
#endif
if (mAccessUnitDamaged) {
accessUnit->meta()->setInt32("damaged", true);
}
mNALUnits.clear();
mAccessUnitDamaged = false;
sp<AMessage> msg = mNotifyMsg->dup();
msg->setObject("access-unit", accessUnit);
msg->post();
return true;
}
ARTPAssembler::AssemblyStatus AAVCAssembler::assembleMore(
const sp<ARTPSource> &source) {
AssemblyStatus status = addNALUnit(source);
if (status == MALFORMED_PACKET) {
mAccessUnitDamaged = true;
}
return status;
}
void AAVCAssembler::packetLost() {
CHECK(mNextExpectedSeqNoValid);
LOGV("packetLost (expected %d)", mNextExpectedSeqNo);
++mNextExpectedSeqNo;
mAccessUnitDamaged = true;
}
void AAVCAssembler::onByeReceived() {
sp<AMessage> msg = mNotifyMsg->dup();
msg->setInt32("eos", true);
msg->post();
}
} // namespace android

View File

@ -1,63 +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.
*/
#ifndef A_AVC_ASSEMBLER_H_
#define A_AVC_ASSEMBLER_H_
#include "mozilla/Types.h"
#include "ARTPAssembler.h"
#include <utils/List.h>
#include <utils/RefBase.h>
namespace android {
struct MOZ_EXPORT ABuffer;
struct MOZ_EXPORT AMessage;
struct AAVCAssembler : public ARTPAssembler {
AAVCAssembler(const sp<AMessage> &notify);
protected:
virtual ~AAVCAssembler();
virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
virtual void onByeReceived();
virtual void packetLost();
private:
sp<AMessage> mNotifyMsg;
uint32_t mAccessUnitRTPTime;
bool mNextExpectedSeqNoValid;
uint32_t mNextExpectedSeqNo;
bool mAccessUnitDamaged;
List<sp<ABuffer> > mNALUnits;
AssemblyStatus addNALUnit(const sp<ARTPSource> &source);
bool addSingleNALUnit(const sp<ABuffer> &buffer);
AssemblyStatus addFragmentedNALUnit(List<sp<ABuffer> > *queue);
bool addSingleTimeAggregationPacket(const sp<ABuffer> &buffer);
bool submitAccessUnit();
DISALLOW_EVIL_CONSTRUCTORS(AAVCAssembler);
};
} // namespace android
#endif // A_AVC_ASSEMBLER_H_

View File

@ -1,260 +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.
*/
#include "AH263Assembler.h"
#include "ARTPSource.h"
#include "RtspPrlog.h"
#include "mozilla/Assertions.h"
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/Utils.h>
namespace android {
AH263Assembler::AH263Assembler(const sp<AMessage> &notify)
: mNotifyMsg(notify),
mAccessUnitRTPTime(0),
mNextExpectedSeqNoValid(false),
mNextExpectedSeqNo(0),
mAccessUnitDamaged(false) {
}
AH263Assembler::~AH263Assembler() {
}
ARTPAssembler::AssemblyStatus AH263Assembler::assembleMore(
const sp<ARTPSource> &source) {
AssemblyStatus status = addPacket(source);
if (status == MALFORMED_PACKET) {
mAccessUnitDamaged = true;
}
return status;
}
ARTPAssembler::AssemblyStatus AH263Assembler::addPacket(
const sp<ARTPSource> &source) {
List<sp<ABuffer> > *queue = source->queue();
if (queue->empty()) {
return NOT_ENOUGH_DATA;
}
if (mNextExpectedSeqNoValid) {
List<sp<ABuffer> >::iterator it = queue->begin();
while (it != queue->end()) {
if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
break;
}
it = queue->erase(it);
}
if (queue->empty()) {
return NOT_ENOUGH_DATA;
}
}
sp<ABuffer> buffer = *queue->begin();
if (!mNextExpectedSeqNoValid) {
mNextExpectedSeqNoValid = true;
mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
} else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
#if VERBOSE
LOG(VERBOSE) << "Not the sequence number I expected";
#endif
return WRONG_SEQUENCE_NUMBER;
}
uint32_t rtpTime;
if (!buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)) {
queue->erase(queue->begin());
++mNextExpectedSeqNo;
LOGW("Cannot find rtp-time. Malformed packet.");
return MALFORMED_PACKET;
}
if (mPackets.size() > 0 && rtpTime != mAccessUnitRTPTime) {
if (!submitAccessUnit()) {
queue->erase(queue->begin());
++mNextExpectedSeqNo;
LOGW("Cannot find rtp-time. Malformed packet.");
return MALFORMED_PACKET;
}
}
mAccessUnitRTPTime = rtpTime;
// hexdump(buffer->data(), buffer->size());
if (buffer->size() < 2) {
queue->erase(queue->begin());
++mNextExpectedSeqNo;
return MALFORMED_PACKET;
}
// RFC 4629, Sec. 5.1 General H.263+ Payload Header.
// The H.263+ payload header is structured as follows:
// 0 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | RR |P|V| PLEN |PEBIT|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// RR: 5 bits
// Reserved bits. It SHALL be zero and MUST be ignored by receivers.
// P: 1 bit
// Indicates the picture start or a picture segment (GOB/Slice) start or
// a video sequence end (EOS or EOSBS). Two bytes of zero bits then have
// to be prefixed to the payload of such a packet to compose a complete
// picture/GOB/slice/EOS/EOSBS start code. This bit allows the omission
// of the two first bytes of the start code, thus improving the
// compression ratio.
// V: 1 bit
// Indicates the presence of an 8-bit field containing information for
// Video Redundancy Coding (VRC), which follows immediately after the
// initial 16 bits of the payload header.
// PLEN: 6 bits
// Length, in bytes, of the extra picture header. If no extra picture
// header is attached, PLEN is 0. If PLEN>0, the extra picture header is
// attached immediately following the rest of the payload header. Note
// that the length reflects the omission of the first two bytes of the
// picture start code (PSC).
// PEBIT: 3 bits
// Indicates the number of bits that shall be ignored in the last byte
// of the picture header. If PLEN is not zero, the ignored bits shall be
// the least significant bits of the byte. If PLEN is zero, then PEBIT
// shall also be zero.
unsigned payloadHeader = U16_AT(buffer->data());
unsigned P = (payloadHeader >> 10) & 1;
unsigned V = (payloadHeader >> 9) & 1;
unsigned PLEN = (payloadHeader >> 3) & 0x3f;
unsigned PEBIT = payloadHeader & 7;
// V = 0
// We do not support VRC header extension for now, so just discard it if
// present.
if (V != 0u) {
queue->erase(queue->begin());
++mNextExpectedSeqNo;
LOGW("Packet discarded due to VRC (V != 0)");
return MALFORMED_PACKET;
}
// If PLEN is zero, then PEBIT shall also be zero.
if (PLEN == 0u && PEBIT != 0u) {
queue->erase(queue->begin());
++mNextExpectedSeqNo;
LOGW("Packet discarded (PEBIT != 0)");
return MALFORMED_PACKET;
}
size_t skip = PLEN + (P ? 0: 2);
buffer->setRange(buffer->offset() + skip, buffer->size() - skip);
if (P) {
buffer->data()[0] = 0x00;
buffer->data()[1] = 0x00;
}
mPackets.push_back(buffer);
queue->erase(queue->begin());
++mNextExpectedSeqNo;
return OK;
}
bool AH263Assembler::submitAccessUnit() {
MOZ_ASSERT(!mPackets.empty());
#if VERBOSE
LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " packets)";
#endif
size_t totalSize = 0;
List<sp<ABuffer> >::iterator it = mPackets.begin();
while (it != mPackets.end()) {
const sp<ABuffer> &unit = *it;
totalSize += unit->size();
++it;
}
sp<ABuffer> accessUnit = new ABuffer(totalSize);
size_t offset = 0;
it = mPackets.begin();
while (it != mPackets.end()) {
const sp<ABuffer> &unit = *it;
memcpy((uint8_t *)accessUnit->data() + offset,
unit->data(), unit->size());
offset += unit->size();
++it;
}
if (!CopyTimes(accessUnit, *mPackets.begin())) {
return false;
}
#if 0
printf(mAccessUnitDamaged ? "X" : ".");
fflush(stdout);
#endif
if (mAccessUnitDamaged) {
accessUnit->meta()->setInt32("damaged", true);
}
mPackets.clear();
mAccessUnitDamaged = false;
sp<AMessage> msg = mNotifyMsg->dup();
msg->setObject("access-unit", accessUnit);
msg->post();
return true;
}
void AH263Assembler::packetLost() {
CHECK(mNextExpectedSeqNoValid);
++mNextExpectedSeqNo;
mAccessUnitDamaged = true;
}
void AH263Assembler::onByeReceived() {
sp<AMessage> msg = mNotifyMsg->dup();
msg->setInt32("eos", true);
msg->post();
}
} // namespace android

Some files were not shown because too many files have changed in this diff Show More