Merge m-c to inbound, a=merge
@ -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
|
||||
-->
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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
|
||||
-->
|
||||
|
@ -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]
|
||||
|
73
browser/base/content/test/general/browser_bug1261299.js
Normal 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);
|
||||
});
|
@ -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() {},
|
||||
|
@ -316,7 +316,6 @@
|
||||
},
|
||||
"urls": {
|
||||
"type": "array",
|
||||
"optional": true,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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]);
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}),
|
||||
},
|
||||
|
@ -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}
|
||||
|
@ -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 = "";
|
||||
|
@ -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]
|
||||
|
@ -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");
|
||||
});
|
@ -245,6 +245,11 @@ html, body {
|
||||
.browser {
|
||||
display: block;
|
||||
border: 0;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.browser:-moz-focusring {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 |
@ -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 |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 1.5 KiB |
@ -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 |
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -5101,6 +5101,7 @@ nsIContent*
|
||||
EventStateManager::GetFocusedContent()
|
||||
{
|
||||
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
EnsureDocument(mPresContext);
|
||||
if (!fm || !mDocument)
|
||||
return nullptr;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
57
dom/media/systemservices/DeviceChangeCallback.h
Normal 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
|
@ -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);
|
||||
|
@ -8,6 +8,7 @@ if CONFIG['MOZ_WEBRTC']:
|
||||
EXPORTS += [
|
||||
'CamerasChild.h',
|
||||
'CamerasParent.h',
|
||||
'DeviceChangeCallback.h',
|
||||
'LoadManager.h',
|
||||
'LoadManagerFactory.h',
|
||||
'LoadMonitor.h',
|
||||
|
@ -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" },
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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();
|
||||
|
@ -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]
|
||||
|
80
dom/media/tests/mochitest/test_ondevicechange.html
Normal 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>
|
@ -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()) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -12,7 +12,8 @@
|
||||
|
||||
[Func="Navigator::HasUserMediaSupport"]
|
||||
interface MediaDevices : EventTarget {
|
||||
// attribute EventHandler ondevicechange;
|
||||
[Pref="media.ondevicechange.enabled"]
|
||||
attribute EventHandler ondevicechange;
|
||||
MediaTrackSupportedConstraints getSupportedConstraints();
|
||||
|
||||
[Throws]
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
|
@ -504,6 +504,10 @@ GPUProcessManager::AllocateLayerTreeId()
|
||||
void
|
||||
GPUProcessManager::DeallocateLayerTreeId(uint64_t aLayersId)
|
||||
{
|
||||
if (mGPUChild) {
|
||||
mGPUChild->SendDeallocateLayerTreeId(aLayersId);
|
||||
return;
|
||||
}
|
||||
CompositorBridgeParent::DeallocateLayerTreeId(aLayersId);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
167
ipc/mscom/DispatchForwarder.cpp
Normal 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
|
81
ipc/mscom/DispatchForwarder.h
Normal 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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
@ -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() {}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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_;
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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']
|
@ -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> ¬ify, bool isWide, const AString ¶ms)
|
||||
: 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
|
@ -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> ¬ify, bool isWide,
|
||||
const AString ¶ms);
|
||||
|
||||
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_
|
||||
|
@ -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> ¬ify)
|
||||
: 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
|
@ -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> ¬ify);
|
||||
|
||||
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_
|
@ -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> ¬ify)
|
||||
: 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
|
||||
|