зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound
This commit is contained in:
Коммит
c481713ead
|
@ -130,7 +130,7 @@ SettingsListener.observe('language.current', 'en-US', function(value) {
|
|||
// =================== RIL ====================
|
||||
(function RILSettingsToPrefs() {
|
||||
// DSDS default service IDs
|
||||
['mms', 'sms', 'telephony', 'voicemail'].forEach(function(key) {
|
||||
['mms', 'sms', 'telephony'].forEach(function(key) {
|
||||
SettingsListener.observe('ril.' + key + '.defaultServiceId', 0,
|
||||
function(value) {
|
||||
if (value != null) {
|
||||
|
|
|
@ -165,9 +165,6 @@
|
|||
#ifdef MOZ_B2G_BT
|
||||
@RESPATH@/components/dom_bluetooth.xpt
|
||||
#endif
|
||||
#ifdef MOZ_B2G_CAMERA
|
||||
@RESPATH@/components/dom_camera.xpt
|
||||
#endif
|
||||
@RESPATH@/components/dom_canvas.xpt
|
||||
@RESPATH@/components/dom_contacts.xpt
|
||||
@RESPATH@/components/dom_core.xpt
|
||||
|
@ -206,7 +203,6 @@
|
|||
@RESPATH@/components/dom_tv.xpt
|
||||
@RESPATH@/components/dom_inputport.xpt
|
||||
@RESPATH@/components/dom_views.xpt
|
||||
@RESPATH@/components/dom_voicemail.xpt
|
||||
#ifdef MOZ_WEBSPEECH
|
||||
@RESPATH@/components/dom_webspeechrecognition.xpt
|
||||
#endif
|
||||
|
@ -446,12 +442,6 @@
|
|||
@RESPATH@/components/WifiWorker.manifest
|
||||
#endif // MOZ_WIDGET_GONK
|
||||
|
||||
; Camera
|
||||
#ifdef MOZ_B2G_CAMERA
|
||||
@RESPATH@/components/CameraTestHardware.js
|
||||
@RESPATH@/components/CameraTestHardware.manifest
|
||||
#endif // MOZ_B2G_CAMERA
|
||||
|
||||
; Tethering
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
@RESPATH@/components/TetheringManager.js
|
||||
|
@ -499,8 +489,6 @@
|
|||
#ifndef DISABLE_MOZ_RIL_GEOLOC
|
||||
@RESPATH@/components/TelephonyService.js
|
||||
@RESPATH@/components/TelephonyService.manifest
|
||||
@RESPATH@/components/VoicemailService.js
|
||||
@RESPATH@/components/VoicemailService.manifest
|
||||
#endif
|
||||
#endif // MOZ_WIDGET_GONK && MOZ_B2G_RIL
|
||||
|
||||
|
|
|
@ -1659,13 +1659,17 @@
|
|||
if (isRemote == aShouldBeRemote)
|
||||
return false;
|
||||
|
||||
let tab = this.getTabForBrowser(aBrowser);
|
||||
let evt = document.createEvent("Events");
|
||||
evt.initEvent("BeforeTabRemotenessChange", true, false);
|
||||
tab.dispatchEvent(evt);
|
||||
|
||||
let wasActive = document.activeElement == aBrowser;
|
||||
|
||||
// Unmap the old outerWindowID.
|
||||
this._outerWindowIDBrowserMap.delete(aBrowser.outerWindowID);
|
||||
|
||||
// Unhook our progress listener.
|
||||
let tab = this.getTabForBrowser(aBrowser);
|
||||
let filter = this._tabFilters.get(tab);
|
||||
let listener = this._tabListeners.get(tab);
|
||||
aBrowser.webProgress.removeProgressListener(filter);
|
||||
|
@ -1749,7 +1753,7 @@
|
|||
this.getFindBar(tab).browser = aBrowser;
|
||||
}
|
||||
|
||||
let evt = document.createEvent("Events");
|
||||
evt = document.createEvent("Events");
|
||||
evt.initEvent("TabRemotenessChange", true, false);
|
||||
tab.dispatchEvent(evt);
|
||||
|
||||
|
|
|
@ -1,81 +1,49 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This test ensures that autoFilled values are not trimmed, unless the user
|
||||
// selects from the autocomplete popup.
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
add_task(function* setup() {
|
||||
const PREF_TRIMURL = "browser.urlbar.trimURLs";
|
||||
const PREF_AUTOFILL = "browser.urlbar.autoFill";
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
registerCleanupFunction(function* () {
|
||||
Services.prefs.clearUserPref(PREF_TRIMURL);
|
||||
Services.prefs.clearUserPref(PREF_AUTOFILL);
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
gURLBar.handleRevert();
|
||||
});
|
||||
Services.prefs.setBoolPref(PREF_TRIMURL, true);
|
||||
Services.prefs.setBoolPref(PREF_AUTOFILL, true);
|
||||
|
||||
// Adding a tab would hit switch-to-tab, so it's safer to just add a visit.
|
||||
let callback = {
|
||||
handleError: function () {},
|
||||
handleResult: function () {},
|
||||
handleCompletion: continue_test
|
||||
};
|
||||
let history = Cc["@mozilla.org/browser/history;1"]
|
||||
.getService(Ci.mozIAsyncHistory);
|
||||
history.updatePlaces({ uri: NetUtil.newURI("http://www.autofilltrimurl.com/whatever")
|
||||
, visits: [ { transitionType: Ci.nsINavHistoryService.TRANSITION_TYPED
|
||||
, visitDate: Date.now() * 1000
|
||||
} ]
|
||||
}, callback);
|
||||
}
|
||||
|
||||
function continue_test() {
|
||||
function test_autoFill(aTyped, aExpected, aCallback) {
|
||||
info(`Testing with input: ${aTyped}`);
|
||||
gURLBar.inputField.value = aTyped.substr(0, aTyped.length - 1);
|
||||
gURLBar.focus();
|
||||
gURLBar.selectionStart = aTyped.length - 1;
|
||||
gURLBar.selectionEnd = aTyped.length - 1;
|
||||
|
||||
EventUtils.synthesizeKey(aTyped.substr(-1), {});
|
||||
waitForSearchComplete(function () {
|
||||
info(`Got value: ${gURLBar.textValue}`);
|
||||
is(gURLBar.textValue, aExpected, "Autofilled value is as expected");
|
||||
aCallback();
|
||||
});
|
||||
}
|
||||
|
||||
test_autoFill("http://", "http://", function () {
|
||||
test_autoFill("http://au", "http://autofilltrimurl.com/", function () {
|
||||
test_autoFill("http://www.autofilltrimurl.com", "http://www.autofilltrimurl.com/", function () {
|
||||
// Now ensure selecting from the popup correctly trims.
|
||||
is(gURLBar.controller.matchCount, 2, "Found the expected number of matches");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
is(gURLBar.textValue, "www.autofilltrimurl.com/whatever", "trim was applied correctly");
|
||||
gURLBar.closePopup();
|
||||
PlacesTestUtils.clearHistory().then(finish);
|
||||
});
|
||||
});
|
||||
yield PlacesTestUtils.addVisits({
|
||||
uri: "http://www.autofilltrimurl.com/whatever",
|
||||
transition: Ci.nsINavHistoryService.TRANSITION_TYPED,
|
||||
});
|
||||
});
|
||||
|
||||
function* promiseSearch(searchtext) {
|
||||
gURLBar.focus();
|
||||
gURLBar.inputField.value = searchtext.substr(0, searchtext.length -1);
|
||||
EventUtils.synthesizeKey(searchtext.substr(-1, 1), {});
|
||||
yield promiseSearchComplete();
|
||||
}
|
||||
|
||||
var gOnSearchComplete = null;
|
||||
function waitForSearchComplete(aCallback) {
|
||||
info("Waiting for onSearchComplete");
|
||||
if (!gOnSearchComplete) {
|
||||
gOnSearchComplete = gURLBar.onSearchComplete;
|
||||
registerCleanupFunction(() => {
|
||||
gURLBar.onSearchComplete = gOnSearchComplete;
|
||||
});
|
||||
}
|
||||
gURLBar.onSearchComplete = function () {
|
||||
ok(gURLBar.popupOpen, "The autocomplete popup is correctly open");
|
||||
gOnSearchComplete.apply(gURLBar);
|
||||
aCallback();
|
||||
}
|
||||
}
|
||||
add_task(function* () {
|
||||
yield promiseSearch("http://");
|
||||
is(gURLBar.inputField.value, "http://", "Autofilled value is as expected");
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
yield promiseSearch("http://au");
|
||||
is(gURLBar.inputField.value, "http://autofilltrimurl.com/", "Autofilled value is as expected");
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
yield promiseSearch("http://www.autofilltrimurl.com");
|
||||
is(gURLBar.inputField.value, "http://www.autofilltrimurl.com/", "Autofilled value is as expected");
|
||||
|
||||
// Now ensure selecting from the popup correctly trims.
|
||||
is(gURLBar.controller.matchCount, 2, "Found the expected number of matches");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
is(gURLBar.inputField.value, "www.autofilltrimurl.com/whatever", "trim was applied correctly");
|
||||
});
|
||||
|
|
|
@ -25,9 +25,11 @@ add_task(function* injectJSON() {
|
|||
});
|
||||
|
||||
add_task(function losslessDecode() {
|
||||
let url = "http://example.com/\u30a2\u30a4\u30a6\u30a8\u30aa";
|
||||
let urlNoScheme = "example.com/\u30a2\u30a4\u30a6\u30a8\u30aa";
|
||||
let url = "http://" + urlNoScheme;
|
||||
gURLBar.textValue = url;
|
||||
Assert.equal(gURLBar.inputField.value, url,
|
||||
// Since this is directly setting textValue, it is expected to be trimmed.
|
||||
Assert.equal(gURLBar.inputField.value, urlNoScheme,
|
||||
"The string displayed in the textbox should not be escaped");
|
||||
gURLBar.value = "";
|
||||
gURLBar.handleRevert();
|
||||
|
|
|
@ -147,7 +147,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||
-->
|
||||
<method name="onBeforeValueGet">
|
||||
<body><![CDATA[
|
||||
return {value: this._value};
|
||||
return { value: this._value };
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
|
@ -919,12 +919,24 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||
]]></body>
|
||||
</method>
|
||||
|
||||
<property name="textValue">
|
||||
<getter><![CDATA[
|
||||
return this.inputField.value;
|
||||
]]></getter>
|
||||
<setter>
|
||||
<![CDATA[
|
||||
<!--
|
||||
onBeforeTextValueSet is called by the base-binding's .textValue getter.
|
||||
It should return the value that the getter should use.
|
||||
-->
|
||||
<method name="onBeforeTextValueGet">
|
||||
<body><![CDATA[
|
||||
return { value: this.inputField.value };
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
onBeforeTextValueSet is called by the base-binding's .textValue setter.
|
||||
It should return the value that the setter should use.
|
||||
-->
|
||||
<method name="onBeforeTextValueSet">
|
||||
<parameter name="aValue"/>
|
||||
<body><![CDATA[
|
||||
let val = aValue;
|
||||
let uri;
|
||||
try {
|
||||
uri = makeURI(val);
|
||||
|
@ -939,33 +951,9 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||
}
|
||||
}
|
||||
|
||||
// Trim popup selected values, but never trim results coming from
|
||||
// autofill.
|
||||
let styles = new Set(
|
||||
this.popup.selectedIndex == -1 ? [] :
|
||||
this.mController.getStyleAt(this.popup.selectedIndex).split(/\s+/)
|
||||
);
|
||||
if (this.popup.selectedIndex == -1 ||
|
||||
this.mController
|
||||
.getStyleAt(this.popup.selectedIndex)
|
||||
.split(/\s+/).indexOf("autofill") >= 0) {
|
||||
this._disableTrim = true;
|
||||
}
|
||||
this.value = val;
|
||||
this._disableTrim = false;
|
||||
|
||||
// Completing a result should simulate the user typing the result, so
|
||||
// fire an input event.
|
||||
let evt = document.createEvent("UIEvents");
|
||||
evt.initUIEvent("input", true, false, window, 0);
|
||||
this.mIgnoreInput = true;
|
||||
this.dispatchEvent(evt);
|
||||
this.mIgnoreInput = false;
|
||||
|
||||
return this.value;
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
return val;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_parseActionUrl">
|
||||
<parameter name="aUrl"/>
|
||||
|
|
|
@ -40,3 +40,58 @@ add_task(function* () {
|
|||
}
|
||||
yield BrowserTestUtils.closeWindow(window1);
|
||||
});
|
||||
|
||||
add_task(function* test_currentWindowAfterTabMoved() {
|
||||
const files = {
|
||||
"current.html": "<meta charset=utf-8><script src=current.js></script>",
|
||||
"current.js": function() {
|
||||
browser.test.onMessage.addListener(msg => {
|
||||
if (msg === "current") {
|
||||
browser.windows.getCurrent(win => {
|
||||
browser.test.sendMessage("id", win.id);
|
||||
});
|
||||
}
|
||||
});
|
||||
browser.test.sendMessage("ready");
|
||||
},
|
||||
};
|
||||
|
||||
function background() {
|
||||
let tabId;
|
||||
const url = browser.extension.getURL("current.html");
|
||||
browser.tabs.create({url}).then(tab => {
|
||||
tabId = tab.id;
|
||||
});
|
||||
browser.test.onMessage.addListener(msg => {
|
||||
if (msg === "move") {
|
||||
browser.windows.create({tabId}).then(() => {
|
||||
browser.test.sendMessage("moved");
|
||||
});
|
||||
} else if (msg === "close") {
|
||||
browser.tabs.remove(tabId).then(() => {
|
||||
browser.test.sendMessage("done");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const extension = ExtensionTestUtils.loadExtension({files, background});
|
||||
|
||||
yield extension.startup();
|
||||
yield extension.awaitMessage("ready");
|
||||
|
||||
extension.sendMessage("current");
|
||||
const first = yield extension.awaitMessage("id");
|
||||
|
||||
extension.sendMessage("move");
|
||||
yield extension.awaitMessage("moved");
|
||||
|
||||
extension.sendMessage("current");
|
||||
const second = yield extension.awaitMessage("id");
|
||||
|
||||
isnot(first, second, "current window id is different after moving the tab");
|
||||
|
||||
extension.sendMessage("close");
|
||||
yield extension.awaitMessage("done");
|
||||
yield extension.unload();
|
||||
});
|
||||
|
|
|
@ -17,9 +17,10 @@ Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
|
||||
"resource://gre/modules/PluralForm.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
|
||||
"resource:///modules/sessionstore/SessionStore.jsm");
|
||||
|
||||
var navigatorBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
|
||||
var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||
|
||||
this.RecentlyClosedTabsAndWindowsMenuUtils = {
|
||||
|
||||
|
@ -40,8 +41,8 @@ this.RecentlyClosedTabsAndWindowsMenuUtils = {
|
|||
aRestoreAllLabel="menuRestoreAllTabs.label") {
|
||||
let doc = aWindow.document;
|
||||
let fragment = doc.createDocumentFragment();
|
||||
if (ss.getClosedTabCount(aWindow) != 0) {
|
||||
let closedTabs = JSON.parse(ss.getClosedTabData(aWindow));
|
||||
if (SessionStore.getClosedTabCount(aWindow) != 0) {
|
||||
let closedTabs = SessionStore.getClosedTabData(aWindow, false);
|
||||
for (let i = 0; i < closedTabs.length; i++) {
|
||||
let element = doc.createElementNS(kNSXUL, aTagName);
|
||||
element.setAttribute("label", closedTabs[i].title);
|
||||
|
@ -99,7 +100,7 @@ this.RecentlyClosedTabsAndWindowsMenuUtils = {
|
|||
*/
|
||||
getWindowsFragment: function(aWindow, aTagName, aPrefixRestoreAll=false,
|
||||
aRestoreAllLabel="menuRestoreAllWindows.label") {
|
||||
let closedWindowData = JSON.parse(ss.getClosedWindowData());
|
||||
let closedWindowData = SessionStore.getClosedWindowData(false);
|
||||
let fragment = aWindow.document.createDocumentFragment();
|
||||
if (closedWindowData.length != 0) {
|
||||
let menuLabelString = navigatorBundle.GetStringFromName("menuUndoCloseWindowLabel");
|
||||
|
|
|
@ -246,8 +246,8 @@ this.SessionStore = {
|
|||
return SessionStoreInternal.getClosedTabCount(aWindow);
|
||||
},
|
||||
|
||||
getClosedTabData: function ss_getClosedTabDataAt(aWindow) {
|
||||
return SessionStoreInternal.getClosedTabData(aWindow);
|
||||
getClosedTabData: function ss_getClosedTabData(aWindow, aAsString = true) {
|
||||
return SessionStoreInternal.getClosedTabData(aWindow, aAsString);
|
||||
},
|
||||
|
||||
undoCloseTab: function ss_undoCloseTab(aWindow, aIndex) {
|
||||
|
@ -262,8 +262,8 @@ this.SessionStore = {
|
|||
return SessionStoreInternal.getClosedWindowCount();
|
||||
},
|
||||
|
||||
getClosedWindowData: function ss_getClosedWindowData() {
|
||||
return SessionStoreInternal.getClosedWindowData();
|
||||
getClosedWindowData: function ss_getClosedWindowData(aAsString = true) {
|
||||
return SessionStoreInternal.getClosedWindowData(aAsString);
|
||||
},
|
||||
|
||||
undoCloseWindow: function ss_undoCloseWindow(aIndex) {
|
||||
|
@ -2184,9 +2184,11 @@ var SessionStoreInternal = {
|
|||
return DyingWindowCache.get(aWindow)._closedTabs.length;
|
||||
},
|
||||
|
||||
getClosedTabData: function ssi_getClosedTabDataAt(aWindow) {
|
||||
getClosedTabData: function ssi_getClosedTabData(aWindow, aAsString = true) {
|
||||
if ("__SSi" in aWindow) {
|
||||
return JSON.stringify(this._windows[aWindow.__SSi]._closedTabs);
|
||||
return aAsString ?
|
||||
JSON.stringify(this._windows[aWindow.__SSi]._closedTabs) :
|
||||
Cu.cloneInto(this._windows[aWindow.__SSi]._closedTabs, {});
|
||||
}
|
||||
|
||||
if (!DyingWindowCache.has(aWindow)) {
|
||||
|
@ -2194,7 +2196,7 @@ var SessionStoreInternal = {
|
|||
}
|
||||
|
||||
let data = DyingWindowCache.get(aWindow);
|
||||
return JSON.stringify(data._closedTabs);
|
||||
return aAsString ? JSON.stringify(data._closedTabs) : Cu.cloneInto(data._closedTabs, {});
|
||||
},
|
||||
|
||||
undoCloseTab: function ssi_undoCloseTab(aWindow, aIndex) {
|
||||
|
@ -2250,8 +2252,8 @@ var SessionStoreInternal = {
|
|||
return this._closedWindows.length;
|
||||
},
|
||||
|
||||
getClosedWindowData: function ssi_getClosedWindowData() {
|
||||
return JSON.stringify(this._closedWindows);
|
||||
getClosedWindowData: function ssi_getClosedWindowData(aAsString = true) {
|
||||
return aAsString ? JSON.stringify(this._closedWindows) : Cu.cloneInto(this._closedWindows, {});
|
||||
},
|
||||
|
||||
undoCloseWindow: function ssi_undoCloseWindow(aIndex) {
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
const TEST_URL = "data:text/html;charset=utf-8,<input%20id=txt>" +
|
||||
"<input%20type=checkbox%20id=chk>";
|
||||
|
||||
Cu.import("resource:///modules/sessionstore/SessionStore.jsm");
|
||||
|
||||
/**
|
||||
* This test ensures that closing a window is a reversible action. We will
|
||||
* close the the window, restore it and check that all data has been restored.
|
||||
|
@ -25,7 +27,7 @@ function test() {
|
|||
provideWindow(function onTestURLLoaded(newWin) {
|
||||
newWin.gBrowser.addTab().linkedBrowser.stop();
|
||||
|
||||
// mark the window with some unique data to be restored later on
|
||||
// Mark the window with some unique data to be restored later on.
|
||||
ss.setWindowValue(newWin, uniqueKey, uniqueValue);
|
||||
let [txt, chk] = newWin.content.document.querySelectorAll("#txt, #chk");
|
||||
txt.value = uniqueText;
|
||||
|
@ -35,11 +37,17 @@ function test() {
|
|||
BrowserTestUtils.closeWindow(newWin).then(() => {
|
||||
is(ss.getClosedWindowCount(), 1,
|
||||
"The closed window was added to Recently Closed Windows");
|
||||
let data = JSON.parse(ss.getClosedWindowData())[0];
|
||||
ok(data.title == TEST_URL && JSON.stringify(data).indexOf(uniqueText) > -1,
|
||||
|
||||
let data = SessionStore.getClosedWindowData(false);
|
||||
|
||||
// Verify that non JSON serialized data is the same as JSON serialized data.
|
||||
is(JSON.stringify(data), ss.getClosedWindowData(),
|
||||
"Non-serialized data is the same as serialized data")
|
||||
|
||||
ok(data[0].title == TEST_URL && JSON.stringify(data[0]).indexOf(uniqueText) > -1,
|
||||
"The closed window data was stored correctly");
|
||||
|
||||
// reopen the closed window and ensure its integrity
|
||||
// Reopen the closed window and ensure its integrity.
|
||||
let newWin2 = ss.undoCloseWindow(0);
|
||||
|
||||
ok(newWin2 instanceof ChromeWindow,
|
||||
|
@ -47,9 +55,9 @@ function test() {
|
|||
is(ss.getClosedWindowCount(), 0,
|
||||
"The reopened window was removed from Recently Closed Windows");
|
||||
|
||||
// SSTabRestored will fire more than once, so we need to make sure we count them
|
||||
// SSTabRestored will fire more than once, so we need to make sure we count them.
|
||||
let restoredTabs = 0;
|
||||
let expectedTabs = data.tabs.length;
|
||||
let expectedTabs = data[0].tabs.length;
|
||||
newWin2.addEventListener("SSTabRestored", function sstabrestoredListener(aEvent) {
|
||||
++restoredTabs;
|
||||
info("Restored tab " + restoredTabs + "/" + expectedTabs);
|
||||
|
@ -57,7 +65,7 @@ function test() {
|
|||
return;
|
||||
}
|
||||
|
||||
is(restoredTabs, expectedTabs, "correct number of tabs restored");
|
||||
is(restoredTabs, expectedTabs, "Correct number of tabs restored");
|
||||
newWin2.removeEventListener("SSTabRestored", sstabrestoredListener, true);
|
||||
|
||||
is(newWin2.gBrowser.tabs.length, 2,
|
||||
|
@ -71,7 +79,7 @@ function test() {
|
|||
is(ss.getWindowValue(newWin2, uniqueKey), uniqueValue,
|
||||
"The window correctly restored the data associated with it");
|
||||
|
||||
// clean up
|
||||
// Clean up.
|
||||
BrowserTestUtils.closeWindow(newWin2).then(finish);
|
||||
}, true);
|
||||
});
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Cu.import("resource:///modules/sessionstore/SessionStore.jsm");
|
||||
|
||||
function test() {
|
||||
/** Test for Bug 461634 **/
|
||||
|
||||
|
@ -30,23 +32,28 @@ function test() {
|
|||
}
|
||||
}
|
||||
|
||||
// open a window and add the above closed tab list
|
||||
// Open a window and add the above closed tab list.
|
||||
let newWin = openDialog(location, "", "chrome,all,dialog=no");
|
||||
promiseWindowLoaded(newWin).then(() => {
|
||||
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo",
|
||||
test_state.windows[0]._closedTabs.length);
|
||||
ss.setWindowState(newWin, JSON.stringify(test_state), true);
|
||||
|
||||
let closedTabs = JSON.parse(ss.getClosedTabData(newWin));
|
||||
let closedTabs = SessionStore.getClosedTabData(newWin, false);
|
||||
|
||||
// Verify that non JSON serialized data is the same as JSON serialized data.
|
||||
is(JSON.stringify(closedTabs), SessionStore.getClosedTabData(newWin),
|
||||
"Non-serialized data is the same as serialized data")
|
||||
|
||||
is(closedTabs.length, test_state.windows[0]._closedTabs.length,
|
||||
"Closed tab list has the expected length");
|
||||
is(countByTitle(closedTabs, FORGET),
|
||||
test_state.windows[0]._closedTabs.length - remember_count,
|
||||
"The correct amout of tabs are to be forgotten");
|
||||
is(countByTitle(closedTabs, REMEMBER), remember_count,
|
||||
"Everything is set up.");
|
||||
"Everything is set up");
|
||||
|
||||
// all of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE
|
||||
// All of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE.
|
||||
ok(testForError(() => ss.forgetClosedTab({}, 0)),
|
||||
"Invalid window for forgetClosedTab throws");
|
||||
ok(testForError(() => ss.forgetClosedTab(newWin, -1)),
|
||||
|
@ -54,19 +61,24 @@ function test() {
|
|||
ok(testForError(() => ss.forgetClosedTab(newWin, test_state.windows[0]._closedTabs.length + 1)),
|
||||
"Invalid tab for forgetClosedTab throws");
|
||||
|
||||
// Remove third tab, then first tab
|
||||
// Remove third tab, then first tab.
|
||||
ss.forgetClosedTab(newWin, 2);
|
||||
ss.forgetClosedTab(newWin, null);
|
||||
|
||||
closedTabs = JSON.parse(ss.getClosedTabData(newWin));
|
||||
closedTabs = SessionStore.getClosedTabData(newWin, false);
|
||||
|
||||
// Verify that non JSON serialized data is the same as JSON serialized data.
|
||||
is(JSON.stringify(closedTabs), SessionStore.getClosedTabData(newWin),
|
||||
"Non-serialized data is the same as serialized data")
|
||||
|
||||
is(closedTabs.length, remember_count,
|
||||
"The correct amout of tabs was removed");
|
||||
is(countByTitle(closedTabs, FORGET), 0,
|
||||
"All tabs specifically forgotten were indeed removed");
|
||||
is(countByTitle(closedTabs, REMEMBER), remember_count,
|
||||
"... and tabs not specifically forgetten weren't.");
|
||||
"... and tabs not specifically forgetten weren't");
|
||||
|
||||
// clean up
|
||||
// Clean up.
|
||||
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
|
||||
BrowserTestUtils.closeWindow(newWin).then(finish);
|
||||
});
|
||||
|
|
|
@ -144,17 +144,30 @@ function* runScenarios(scenarios) {
|
|||
// Hook up an event listener to make sure that the right
|
||||
// tabs flip remoteness, and only once.
|
||||
let flipListener = {
|
||||
seenTabs: new Set(),
|
||||
seenBeforeTabs: new Set(),
|
||||
seenAfterTabs: new Set(),
|
||||
handleEvent(e) {
|
||||
let index = Array.from(tabbrowser.tabs).indexOf(e.target);
|
||||
info(`Saw a tab at index ${index} flip remoteness`);
|
||||
if (this.seenTabs.has(e.target)) {
|
||||
Assert.ok(false, "Saw a tab flip remoteness more than once");
|
||||
switch (e.type) {
|
||||
case "BeforeTabRemotenessChange":
|
||||
info(`Saw tab at index ${index} before remoteness flip`);
|
||||
if (this.seenBeforeTabs.has(e.target)) {
|
||||
Assert.ok(false, "Saw tab before remoteness flip more than once");
|
||||
}
|
||||
this.seenBeforeTabs.add(e.target);
|
||||
break;
|
||||
case "TabRemotenessChange":
|
||||
info(`Saw tab at index ${index} after remoteness flip`);
|
||||
if (this.seenAfterTabs.has(e.target)) {
|
||||
Assert.ok(false, "Saw tab after remoteness flip more than once");
|
||||
}
|
||||
this.seenAfterTabs.add(e.target);
|
||||
break;
|
||||
}
|
||||
this.seenTabs.add(e.target);
|
||||
},
|
||||
};
|
||||
|
||||
win.addEventListener("BeforeTabRemotenessChange", flipListener);
|
||||
win.addEventListener("TabRemotenessChange", flipListener);
|
||||
|
||||
// Okay, time to test!
|
||||
|
@ -163,6 +176,7 @@ function* runScenarios(scenarios) {
|
|||
|
||||
SessionStore.setWindowState(win, state, true);
|
||||
|
||||
win.removeEventListener("BeforeTabRemotenessChange", flipListener);
|
||||
win.removeEventListener("TabRemotenessChange", flipListener);
|
||||
|
||||
// Because we know that scenario.expectedFlips and
|
||||
|
@ -173,11 +187,15 @@ function* runScenarios(scenarios) {
|
|||
let expectedRemoteness = scenario.expectedRemoteness[i];
|
||||
let tab = tabbrowser.tabs[i];
|
||||
if (expectedToFlip) {
|
||||
Assert.ok(flipListener.seenTabs.has(tab),
|
||||
`We should have seen tab at index ${i} flip remoteness`);
|
||||
Assert.ok(flipListener.seenBeforeTabs.has(tab),
|
||||
`We should have seen tab at index ${i} before remoteness flip`);
|
||||
Assert.ok(flipListener.seenAfterTabs.has(tab),
|
||||
`We should have seen tab at index ${i} after remoteness flip`);
|
||||
} else {
|
||||
Assert.ok(!flipListener.seenTabs.has(tab),
|
||||
`We should not have seen tab at index ${i} flip remoteness`);
|
||||
Assert.ok(!flipListener.seenBeforeTabs.has(tab),
|
||||
`We should not have seen tab at index ${i} before remoteness flip`);
|
||||
Assert.ok(!flipListener.seenAfterTabs.has(tab),
|
||||
`We should not have seen tab at index ${i} after remoteness flip`);
|
||||
}
|
||||
|
||||
Assert.equal(tab.linkedBrowser.isRemoteBrowser, expectedRemoteness,
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
This is the pdf.js project output, https://github.com/mozilla/pdf.js
|
||||
|
||||
Current extension version is: 1.6.221
|
||||
Current extension version is: 1.6.234
|
||||
|
|
|
@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdf = {}));
|
|||
// Use strict in our context only - users might not want it
|
||||
'use strict';
|
||||
|
||||
var pdfjsVersion = '1.6.221';
|
||||
var pdfjsBuild = 'f8bd3d4';
|
||||
var pdfjsVersion = '1.6.234';
|
||||
var pdfjsBuild = 'bc3bceb';
|
||||
|
||||
var pdfjsFilePath =
|
||||
typeof document !== 'undefined' && document.currentScript ?
|
||||
|
|
|
@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdfWorker = {}));
|
|||
// Use strict in our context only - users might not want it
|
||||
'use strict';
|
||||
|
||||
var pdfjsVersion = '1.6.221';
|
||||
var pdfjsBuild = 'f8bd3d4';
|
||||
var pdfjsVersion = '1.6.234';
|
||||
var pdfjsBuild = 'bc3bceb';
|
||||
|
||||
var pdfjsFilePath =
|
||||
typeof document !== 'undefined' && document.currentScript ?
|
||||
|
@ -37498,7 +37498,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||
for (var j = 0, jj = items.length; j < jj; j++) {
|
||||
if (typeof items[j] === 'string') {
|
||||
buildTextContentItem(items[j]);
|
||||
} else {
|
||||
} else if (isNum(items[j])) {
|
||||
ensureTextContentItem();
|
||||
|
||||
// PDF Specification 5.3.2 states:
|
||||
|
|
|
@ -1925,11 +1925,11 @@ html[dir='rtl'] #documentPropertiesOverlay .row > * {
|
|||
display: none;
|
||||
}
|
||||
|
||||
/* Rules for browsers that support mozPrintCallback */
|
||||
body[data-mozPrintCallback] #outerContainer {
|
||||
/* Rules for browsers that support PDF.js printing */
|
||||
body[data-pdfjsprinting] #outerContainer {
|
||||
display: none;
|
||||
}
|
||||
body[data-mozPrintCallback] #printContainer {
|
||||
body[data-pdfjsprinting] #printContainer {
|
||||
display: block;
|
||||
}
|
||||
#printContainer {
|
||||
|
@ -1946,7 +1946,8 @@ html[dir='rtl'] #documentPropertiesOverlay .row > * {
|
|||
page-break-after: always;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
#printContainer canvas {
|
||||
#printContainer canvas,
|
||||
#printContainer img {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,7 +148,6 @@ var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
|
|||
this.element.addEventListener('scroll', this._endPan, true);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.document.documentElement.classList.add(this.CSS_CLASS_GRABBING);
|
||||
|
||||
var focusedElement = document.activeElement;
|
||||
if (focusedElement && !focusedElement.contains(event.target)) {
|
||||
|
@ -167,8 +166,18 @@ var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
|
|||
}
|
||||
var xDiff = event.clientX - this.clientXStart;
|
||||
var yDiff = event.clientY - this.clientYStart;
|
||||
this.element.scrollTop = this.scrollTopStart - yDiff;
|
||||
this.element.scrollLeft = this.scrollLeftStart - xDiff;
|
||||
var scrollTop = this.scrollTopStart - yDiff;
|
||||
var scrollLeft = this.scrollLeftStart - xDiff;
|
||||
if (this.element.scrollTo) {
|
||||
this.element.scrollTo({
|
||||
top: scrollTop,
|
||||
left: scrollLeft,
|
||||
behavior: 'instant',
|
||||
});
|
||||
} else {
|
||||
this.element.scrollTop = scrollTop;
|
||||
this.element.scrollLeft = scrollLeft;
|
||||
}
|
||||
if (!this.overlay.parentNode) {
|
||||
document.body.appendChild(this.overlay);
|
||||
}
|
||||
|
@ -3644,8 +3653,9 @@ var PDFThumbnailView = (function PDFThumbnailViewClosure() {
|
|||
this.linkService = linkService;
|
||||
this.renderingQueue = renderingQueue;
|
||||
|
||||
this.resume = null;
|
||||
this.renderTask = null;
|
||||
this.renderingState = RenderingStates.INITIAL;
|
||||
this.resume = null;
|
||||
this.disableCanvasToImageConversion = disableCanvasToImageConversion;
|
||||
|
||||
this.pageWidth = this.viewport.width;
|
||||
|
@ -3697,11 +3707,7 @@ var PDFThumbnailView = (function PDFThumbnailViewClosure() {
|
|||
},
|
||||
|
||||
reset: function PDFThumbnailView_reset() {
|
||||
if (this.renderTask) {
|
||||
this.renderTask.cancel();
|
||||
}
|
||||
this.resume = null;
|
||||
this.renderingState = RenderingStates.INITIAL;
|
||||
this.cancelRendering();
|
||||
|
||||
this.pageWidth = this.viewport.width;
|
||||
this.pageHeight = this.viewport.height;
|
||||
|
@ -3745,6 +3751,15 @@ var PDFThumbnailView = (function PDFThumbnailViewClosure() {
|
|||
this.reset();
|
||||
},
|
||||
|
||||
cancelRendering: function PDFThumbnailView_cancelRendering() {
|
||||
if (this.renderTask) {
|
||||
this.renderTask.cancel();
|
||||
this.renderTask = null;
|
||||
}
|
||||
this.renderingState = RenderingStates.INITIAL;
|
||||
this.resume = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
@ -5216,6 +5231,7 @@ var PDFPageView = (function PDFPageViewClosure() {
|
|||
this.textLayerFactory = textLayerFactory;
|
||||
this.annotationLayerFactory = annotationLayerFactory;
|
||||
|
||||
this.renderTask = null;
|
||||
this.renderingState = RenderingStates.INITIAL;
|
||||
this.resume = null;
|
||||
|
||||
|
@ -5259,11 +5275,7 @@ var PDFPageView = (function PDFPageViewClosure() {
|
|||
},
|
||||
|
||||
reset: function PDFPageView_reset(keepZoomLayer, keepAnnotations) {
|
||||
if (this.renderTask) {
|
||||
this.renderTask.cancel();
|
||||
}
|
||||
this.resume = null;
|
||||
this.renderingState = RenderingStates.INITIAL;
|
||||
this.cancelRendering();
|
||||
|
||||
var div = this.div;
|
||||
div.style.width = Math.floor(this.viewport.width) + 'px';
|
||||
|
@ -5349,6 +5361,20 @@ var PDFPageView = (function PDFPageViewClosure() {
|
|||
this.reset(/* keepZoomLayer = */ true, /* keepAnnotations = */ true);
|
||||
},
|
||||
|
||||
cancelRendering: function PDFPageView_cancelRendering() {
|
||||
if (this.renderTask) {
|
||||
this.renderTask.cancel();
|
||||
this.renderTask = null;
|
||||
}
|
||||
this.renderingState = RenderingStates.INITIAL;
|
||||
this.resume = null;
|
||||
|
||||
if (this.textLayer) {
|
||||
this.textLayer.cancel();
|
||||
this.textLayer = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when moved in the parent's container.
|
||||
*/
|
||||
|
@ -5444,6 +5470,7 @@ var PDFPageView = (function PDFPageViewClosure() {
|
|||
draw: function PDFPageView_draw() {
|
||||
if (this.renderingState !== RenderingStates.INITIAL) {
|
||||
console.error('Must be in new state before drawing');
|
||||
this.reset(); // Ensure that we reset all state to prevent issues.
|
||||
}
|
||||
|
||||
this.renderingState = RenderingStates.RUNNING;
|
||||
|
@ -5662,59 +5689,6 @@ var PDFPageView = (function PDFPageViewClosure() {
|
|||
}
|
||||
return promise;
|
||||
},
|
||||
|
||||
beforePrint: function PDFPageView_beforePrint(printContainer) {
|
||||
var CustomStyle = pdfjsLib.CustomStyle;
|
||||
var pdfPage = this.pdfPage;
|
||||
|
||||
var viewport = pdfPage.getViewport(1);
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
|
||||
// The size of the canvas in pixels for printing.
|
||||
var PRINT_RESOLUTION = 150;
|
||||
var PRINT_UNITS = PRINT_RESOLUTION / 72.0;
|
||||
canvas.width = Math.floor(viewport.width * PRINT_UNITS);
|
||||
canvas.height = Math.floor(viewport.height * PRINT_UNITS);
|
||||
|
||||
// The physical size of the canvas as specified by the PDF document.
|
||||
canvas.style.width = Math.floor(viewport.width * CSS_UNITS) + 'px';
|
||||
canvas.style.height = Math.floor(viewport.height * CSS_UNITS) + 'px';
|
||||
|
||||
var canvasWrapper = document.createElement('div');
|
||||
canvasWrapper.appendChild(canvas);
|
||||
printContainer.appendChild(canvasWrapper);
|
||||
|
||||
canvas.mozPrintCallback = function(obj) {
|
||||
var ctx = obj.context;
|
||||
|
||||
ctx.save();
|
||||
ctx.fillStyle = 'rgb(255, 255, 255)';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.restore();
|
||||
|
||||
var renderContext = {
|
||||
canvasContext: ctx,
|
||||
transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
|
||||
viewport: viewport,
|
||||
intent: 'print'
|
||||
};
|
||||
|
||||
pdfPage.render(renderContext).promise.then(function() {
|
||||
// Tell the printEngine that rendering this canvas/page has finished.
|
||||
obj.done();
|
||||
}, function(error) {
|
||||
console.error(error);
|
||||
// Tell the printEngine that rendering this canvas/page has failed.
|
||||
// This will make the print process stop.
|
||||
if ('abort' in obj) {
|
||||
obj.abort();
|
||||
} else {
|
||||
obj.done();
|
||||
}
|
||||
});
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
return PDFPageView;
|
||||
|
@ -5838,15 +5812,14 @@ var PDFThumbnailViewer = (function PDFThumbnailViewerClosure() {
|
|||
this.thumbnails = [];
|
||||
this._pagesRotation = 0;
|
||||
this._pagesRequests = [];
|
||||
|
||||
// Remove the thumbnails from the DOM.
|
||||
this.container.textContent = '';
|
||||
},
|
||||
|
||||
setDocument: function PDFThumbnailViewer_setDocument(pdfDocument) {
|
||||
if (this.pdfDocument) {
|
||||
// cleanup of the elements and views
|
||||
var thumbsView = this.container;
|
||||
while (thumbsView.hasChildNodes()) {
|
||||
thumbsView.removeChild(thumbsView.lastChild);
|
||||
}
|
||||
this._cancelRendering();
|
||||
this._resetView();
|
||||
}
|
||||
|
||||
|
@ -5872,6 +5845,17 @@ var PDFThumbnailViewer = (function PDFThumbnailViewerClosure() {
|
|||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_cancelRendering: function PDFThumbnailViewer_cancelRendering() {
|
||||
for (var i = 0, ii = this.thumbnails.length; i < ii; i++) {
|
||||
if (this.thumbnails[i]) {
|
||||
this.thumbnails[i].cancelRendering();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {PDFThumbnailView} thumbView
|
||||
* @returns {PDFPage}
|
||||
|
@ -5949,8 +5933,8 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
|||
function TextLayerBuilder(options) {
|
||||
this.textLayerDiv = options.textLayerDiv;
|
||||
this.eventBus = options.eventBus || domEvents.getGlobalEventBus();
|
||||
this.textContent = null;
|
||||
this.renderingDone = false;
|
||||
this.divContentDone = false;
|
||||
this.pageIdx = options.pageIndex;
|
||||
this.pageNumber = this.pageIdx + 1;
|
||||
this.matches = [];
|
||||
|
@ -5963,6 +5947,9 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
|||
}
|
||||
|
||||
TextLayerBuilder.prototype = {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_finishRendering: function TextLayerBuilder_finishRendering() {
|
||||
this.renderingDone = true;
|
||||
|
||||
|
@ -5974,7 +5961,8 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
|||
|
||||
this.eventBus.dispatch('textlayerrendered', {
|
||||
source: this,
|
||||
pageNumber: this.pageNumber
|
||||
pageNumber: this.pageNumber,
|
||||
numTextDivs: this.textDivs.length,
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -5984,14 +5972,10 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
|||
* for specified amount of ms.
|
||||
*/
|
||||
render: function TextLayerBuilder_render(timeout) {
|
||||
if (!this.divContentDone || this.renderingDone) {
|
||||
if (!this.textContent || this.renderingDone) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.textLayerRenderTask) {
|
||||
this.textLayerRenderTask.cancel();
|
||||
this.textLayerRenderTask = null;
|
||||
}
|
||||
this.cancel();
|
||||
|
||||
this.textDivs = [];
|
||||
var textLayerFrag = document.createDocumentFragment();
|
||||
|
@ -6008,17 +5992,23 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
|||
this._finishRendering();
|
||||
this.updateMatches();
|
||||
}.bind(this), function (reason) {
|
||||
// canceled or failed to render text layer -- skipping errors
|
||||
// cancelled or failed to render text layer -- skipping errors
|
||||
});
|
||||
},
|
||||
|
||||
setTextContent: function TextLayerBuilder_setTextContent(textContent) {
|
||||
/**
|
||||
* Cancels rendering of the text layer.
|
||||
*/
|
||||
cancel: function TextLayerBuilder_cancel() {
|
||||
if (this.textLayerRenderTask) {
|
||||
this.textLayerRenderTask.cancel();
|
||||
this.textLayerRenderTask = null;
|
||||
}
|
||||
},
|
||||
|
||||
setTextContent: function TextLayerBuilder_setTextContent(textContent) {
|
||||
this.cancel();
|
||||
this.textContent = textContent;
|
||||
this.divContentDone = true;
|
||||
},
|
||||
|
||||
convertMatches: function TextLayerBuilder_convertMatches(matches,
|
||||
|
@ -6221,6 +6211,7 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
|||
var div = this.textLayerDiv;
|
||||
var self = this;
|
||||
var expandDivsTimer = null;
|
||||
|
||||
div.addEventListener('mousedown', function (e) {
|
||||
if (self.enhanceTextSelection && self.textLayerRenderTask) {
|
||||
self.textLayerRenderTask.expandTextDivs(true);
|
||||
|
@ -6232,6 +6223,7 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
|||
}
|
||||
end.classList.add('active');
|
||||
});
|
||||
|
||||
div.addEventListener('mouseup', function (e) {
|
||||
if (self.enhanceTextSelection && self.textLayerRenderTask) {
|
||||
self.textLayerRenderTask.expandTextDivs(false);
|
||||
|
@ -6536,6 +6528,13 @@ var PDFViewer = (function pdfViewer() {
|
|||
return this._pages[index];
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {boolean} true if all {PDFPageView} objects are initialized.
|
||||
*/
|
||||
get pageViewsReady() {
|
||||
return this._pageViewsReady;
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
|
@ -6667,6 +6666,7 @@ var PDFViewer = (function pdfViewer() {
|
|||
*/
|
||||
setDocument: function (pdfDocument) {
|
||||
if (this.pdfDocument) {
|
||||
this._cancelRendering();
|
||||
this._resetView();
|
||||
}
|
||||
|
||||
|
@ -6684,6 +6684,7 @@ var PDFViewer = (function pdfViewer() {
|
|||
});
|
||||
this.pagesPromise = pagesPromise;
|
||||
pagesPromise.then(function () {
|
||||
self._pageViewsReady = true;
|
||||
self.eventBus.dispatch('pagesloaded', {
|
||||
source: self,
|
||||
pagesCount: pagesCount
|
||||
|
@ -6789,11 +6790,10 @@ var PDFViewer = (function pdfViewer() {
|
|||
this._location = null;
|
||||
this._pagesRotation = 0;
|
||||
this._pagesRequests = [];
|
||||
this._pageViewsReady = false;
|
||||
|
||||
var container = this.viewer;
|
||||
while (container.hasChildNodes()) {
|
||||
container.removeChild(container.lastChild);
|
||||
}
|
||||
// Remove the pages from the DOM.
|
||||
this.viewer.textContent = '';
|
||||
},
|
||||
|
||||
_scrollUpdate: function PDFViewer_scrollUpdate() {
|
||||
|
@ -7155,6 +7155,17 @@ var PDFViewer = (function pdfViewer() {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_cancelRendering: function PDFViewer_cancelRendering() {
|
||||
for (var i = 0, ii = this._pages.length; i < ii; i++) {
|
||||
if (this._pages[i]) {
|
||||
this._pages[i].cancelRendering();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {PDFPageView} pageView
|
||||
* @returns {PDFPage}
|
||||
|
@ -7239,6 +7250,17 @@ var PDFViewer = (function pdfViewer() {
|
|||
setFindController: function (findController) {
|
||||
this.findController = findController;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns sizes of the pages.
|
||||
* @returns {Array} Array of objects with width/height fields.
|
||||
*/
|
||||
getPagesOverview: function () {
|
||||
return this._pages.map(function (pageView) {
|
||||
var viewport = pageView.pdfPage.getViewport(1);
|
||||
return {width: viewport.width, height: viewport.height};
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
return PDFViewer;
|
||||
|
@ -7343,7 +7365,7 @@ var PDFViewerApplication = {
|
|||
appConfig: null,
|
||||
pdfDocument: null,
|
||||
pdfLoadingTask: null,
|
||||
printing: false,
|
||||
printService: null,
|
||||
/** @type {PDFViewer} */
|
||||
pdfViewer: null,
|
||||
/** @type {PDFThumbnailViewer} */
|
||||
|
@ -7628,11 +7650,12 @@ var PDFViewerApplication = {
|
|||
return this.pdfViewer.currentPageNumber;
|
||||
},
|
||||
|
||||
get supportsPrinting() {
|
||||
var canvas = document.createElement('canvas');
|
||||
var value = 'mozPrintCallback' in canvas;
|
||||
get printing() {
|
||||
return !!this.printService;
|
||||
},
|
||||
|
||||
return pdfjsLib.shadow(this, 'supportsPrinting', value);
|
||||
get supportsPrinting() {
|
||||
return PDFPrintServiceFactory.instance.supportsPrinting;
|
||||
},
|
||||
|
||||
get supportsFullscreen() {
|
||||
|
@ -8221,6 +8244,13 @@ var PDFViewerApplication = {
|
|||
},
|
||||
|
||||
beforePrint: function pdfViewSetupBeforePrint() {
|
||||
if (this.printService) {
|
||||
// There is no way to suppress beforePrint/afterPrint events,
|
||||
// but PDFPrintService may generate double events -- this will ignore
|
||||
// the second event that will be coming from native window.print().
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.supportsPrinting) {
|
||||
var printMessage = mozL10n.get('printing_not_supported', null,
|
||||
'Warning: Printing is not fully supported by this browser.');
|
||||
|
@ -8228,59 +8258,23 @@ var PDFViewerApplication = {
|
|||
return;
|
||||
}
|
||||
|
||||
var alertNotReady = false;
|
||||
var i, ii;
|
||||
if (!this.pdfDocument || !this.pagesCount) {
|
||||
alertNotReady = true;
|
||||
} else {
|
||||
for (i = 0, ii = this.pagesCount; i < ii; ++i) {
|
||||
if (!this.pdfViewer.getPageView(i).pdfPage) {
|
||||
alertNotReady = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (alertNotReady) {
|
||||
// The beforePrint is a sync method and we need to know layout before
|
||||
// returning from this method. Ensure that we can get sizes of the pages.
|
||||
if (!this.pdfViewer.pageViewsReady) {
|
||||
var notReadyMessage = mozL10n.get('printing_not_ready', null,
|
||||
'Warning: The PDF is not fully loaded for printing.');
|
||||
window.alert(notReadyMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
this.printing = true;
|
||||
var pagesOverview = this.pdfViewer.getPagesOverview();
|
||||
var printContainer = this.appConfig.printContainer;
|
||||
var printService = PDFPrintServiceFactory.instance.createPrintService(
|
||||
this.pdfDocument, pagesOverview, printContainer);
|
||||
this.printService = printService;
|
||||
this.forceRendering();
|
||||
|
||||
var printContainer = this.appConfig.printContainer;
|
||||
var body = document.querySelector('body');
|
||||
body.setAttribute('data-mozPrintCallback', true);
|
||||
|
||||
if (!this.hasEqualPageSizes) {
|
||||
console.warn('Not all pages have the same size. The printed result ' +
|
||||
'may be incorrect!');
|
||||
}
|
||||
|
||||
// Insert a @page + size rule to make sure that the page size is correctly
|
||||
// set. Note that we assume that all pages have the same size, because
|
||||
// variable-size pages are not supported yet (at least in Chrome & Firefox).
|
||||
// TODO(robwu): Use named pages when size calculation bugs get resolved
|
||||
// (e.g. https://crbug.com/355116) AND when support for named pages is
|
||||
// added (http://www.w3.org/TR/css3-page/#using-named-pages).
|
||||
// In browsers where @page + size is not supported (such as Firefox,
|
||||
// https://bugzil.la/851441), the next stylesheet will be ignored and the
|
||||
// user has to select the correct paper size in the UI if wanted.
|
||||
this.pageStyleSheet = document.createElement('style');
|
||||
var pageSize = this.pdfViewer.getPageView(0).pdfPage.getViewport(1);
|
||||
this.pageStyleSheet.textContent =
|
||||
// "size:<width> <height>" is what we need. But also add "A4" because
|
||||
// Firefox incorrectly reports support for the other value.
|
||||
'@supports ((size:A4) and (size:1pt 1pt)) {' +
|
||||
'@page { size: ' + pageSize.width + 'pt ' + pageSize.height + 'pt;}' +
|
||||
'}';
|
||||
body.appendChild(this.pageStyleSheet);
|
||||
|
||||
for (i = 0, ii = this.pagesCount; i < ii; ++i) {
|
||||
this.pdfViewer.getPageView(i).beforePrint(printContainer);
|
||||
}
|
||||
printService.layout();
|
||||
|
||||
this.externalServices.reportTelemetry({
|
||||
type: 'print'
|
||||
|
@ -8301,17 +8295,10 @@ var PDFViewerApplication = {
|
|||
},
|
||||
|
||||
afterPrint: function pdfViewSetupAfterPrint() {
|
||||
var div = this.appConfig.printContainer;
|
||||
while (div.hasChildNodes()) {
|
||||
div.removeChild(div.lastChild);
|
||||
if (this.printService) {
|
||||
this.printService.destroy();
|
||||
this.printService = null;
|
||||
}
|
||||
|
||||
if (this.pageStyleSheet && this.pageStyleSheet.parentNode) {
|
||||
this.pageStyleSheet.parentNode.removeChild(this.pageStyleSheet);
|
||||
this.pageStyleSheet = null;
|
||||
}
|
||||
|
||||
this.printing = false;
|
||||
this.forceRendering();
|
||||
},
|
||||
|
||||
|
@ -8618,6 +8605,19 @@ function webViewerPageRendered(e) {
|
|||
var pageIndex = pageNumber - 1;
|
||||
var pageView = PDFViewerApplication.pdfViewer.getPageView(pageIndex);
|
||||
|
||||
// If the page is still visible when it has finished rendering,
|
||||
// ensure that the page number input loading indicator is hidden.
|
||||
if (pageNumber === PDFViewerApplication.page) {
|
||||
var pageNumberInput = PDFViewerApplication.appConfig.toolbar.pageNumber;
|
||||
pageNumberInput.classList.remove(PAGE_NUMBER_LOADING_INDICATOR);
|
||||
}
|
||||
|
||||
// Prevent errors in the edge-case where the PDF document is removed *before*
|
||||
// the 'pagerendered' event handler is invoked.
|
||||
if (!pageView) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the rendered page to set the corresponding thumbnail image.
|
||||
if (PDFViewerApplication.pdfSidebar.isThumbnailViewVisible) {
|
||||
var thumbnailView = PDFViewerApplication.pdfThumbnailViewer.
|
||||
|
@ -8634,13 +8634,6 @@ function webViewerPageRendered(e) {
|
|||
'An error occurred while rendering the page.'), pageView.error);
|
||||
}
|
||||
|
||||
// If the page is still visible when it has finished rendering,
|
||||
// ensure that the page number input loading indicator is hidden.
|
||||
if (pageNumber === PDFViewerApplication.page) {
|
||||
var pageNumberInput = PDFViewerApplication.appConfig.toolbar.pageNumber;
|
||||
pageNumberInput.classList.remove(PAGE_NUMBER_LOADING_INDICATOR);
|
||||
}
|
||||
|
||||
PDFViewerApplication.externalServices.reportTelemetry({
|
||||
type: 'pageInfo'
|
||||
});
|
||||
|
@ -8654,12 +8647,7 @@ function webViewerPageRendered(e) {
|
|||
}
|
||||
|
||||
function webViewerTextLayerRendered(e) {
|
||||
var pageIndex = e.pageNumber - 1;
|
||||
var pageView = PDFViewerApplication.pdfViewer.getPageView(pageIndex);
|
||||
|
||||
if (pageView.textLayer && pageView.textLayer.textDivs &&
|
||||
pageView.textLayer.textDivs.length > 0 &&
|
||||
!PDFViewerApplication.supportsDocumentColors) {
|
||||
if (e.numTextDivs > 0 && !PDFViewerApplication.supportsDocumentColors) {
|
||||
console.error(mozL10n.get('document_colors_not_allowed', null,
|
||||
'PDF documents are not allowed to use their own colors: ' +
|
||||
'\'Allow pages to choose their own colors\' ' +
|
||||
|
@ -9268,8 +9256,120 @@ window.addEventListener('afterprint', function afterPrint(evt) {
|
|||
});
|
||||
})();
|
||||
|
||||
/* Abstract factory for the print service. */
|
||||
var PDFPrintServiceFactory = {
|
||||
instance: {
|
||||
supportsPrinting: false,
|
||||
createPrintService: function () {
|
||||
throw new Error('Not implemented: createPrintService');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.PDFViewerApplication = PDFViewerApplication;
|
||||
exports.DefaultExernalServices = DefaultExernalServices;
|
||||
exports.PDFPrintServiceFactory = PDFPrintServiceFactory;
|
||||
}));
|
||||
|
||||
|
||||
(function (root, factory) {
|
||||
{
|
||||
factory((root.pdfjsWebFirefoxPrintService = {}), root.pdfjsWebUIUtils,
|
||||
root.pdfjsWebApp, root.pdfjsWebPDFJS);
|
||||
}
|
||||
}(this, function (exports, uiUtils, app, pdfjsLib) {
|
||||
var CSS_UNITS = uiUtils.CSS_UNITS;
|
||||
var PDFPrintServiceFactory = app.PDFPrintServiceFactory;
|
||||
|
||||
// Creates a placeholder with div and canvas with right size for the page.
|
||||
function composePage(pdfDocument, pageNumber, size, printContainer) {
|
||||
var canvas = document.createElement('canvas');
|
||||
|
||||
// The size of the canvas in pixels for printing.
|
||||
var PRINT_RESOLUTION = 150;
|
||||
var PRINT_UNITS = PRINT_RESOLUTION / 72.0;
|
||||
canvas.width = Math.floor(size.width * PRINT_UNITS);
|
||||
canvas.height = Math.floor(size.height * PRINT_UNITS);
|
||||
|
||||
// The physical size of the canvas as specified by the PDF document.
|
||||
canvas.style.width = Math.floor(size.width * CSS_UNITS) + 'px';
|
||||
canvas.style.height = Math.floor(size.height * CSS_UNITS) + 'px';
|
||||
|
||||
var canvasWrapper = document.createElement('div');
|
||||
canvasWrapper.appendChild(canvas);
|
||||
printContainer.appendChild(canvasWrapper);
|
||||
|
||||
canvas.mozPrintCallback = function(obj) {
|
||||
// Printing/rendering the page.
|
||||
var ctx = obj.context;
|
||||
|
||||
ctx.save();
|
||||
ctx.fillStyle = 'rgb(255, 255, 255)';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.restore();
|
||||
|
||||
pdfDocument.getPage(pageNumber).then(function (pdfPage) {
|
||||
var renderContext = {
|
||||
canvasContext: ctx,
|
||||
transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
|
||||
viewport: pdfPage.getViewport(1),
|
||||
intent: 'print'
|
||||
};
|
||||
return pdfPage.render(renderContext).promise;
|
||||
}).then(function() {
|
||||
// Tell the printEngine that rendering this canvas/page has finished.
|
||||
obj.done();
|
||||
}, function(error) {
|
||||
console.error(error);
|
||||
// Tell the printEngine that rendering this canvas/page has failed.
|
||||
// This will make the print process stop.
|
||||
if ('abort' in obj) {
|
||||
obj.abort();
|
||||
} else {
|
||||
obj.done();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function FirefoxPrintService(pdfDocument, pagesOverview, printContainer) {
|
||||
this.pdfDocument = pdfDocument;
|
||||
this.pagesOverview = pagesOverview;
|
||||
this.printContainer = printContainer;
|
||||
}
|
||||
|
||||
FirefoxPrintService.prototype = {
|
||||
layout: function () {
|
||||
var pdfDocument = this.pdfDocument;
|
||||
var printContainer = this.printContainer;
|
||||
var body = document.querySelector('body');
|
||||
body.setAttribute('data-pdfjsprinting', true);
|
||||
|
||||
for (var i = 0, ii = this.pagesOverview.length; i < ii; ++i) {
|
||||
composePage(pdfDocument, i + 1, this.pagesOverview[i], printContainer);
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.printContainer.textContent = '';
|
||||
}
|
||||
};
|
||||
|
||||
PDFPrintServiceFactory.instance = {
|
||||
get supportsPrinting() {
|
||||
var canvas = document.createElement('canvas');
|
||||
var value = 'mozPrintCallback' in canvas;
|
||||
|
||||
return pdfjsLib.shadow(this, 'supportsPrinting', value);
|
||||
},
|
||||
|
||||
createPrintService: function (pdfDocument, pagesOverview, printContainer) {
|
||||
return new FirefoxPrintService(pdfDocument, pagesOverview,
|
||||
printContainer);
|
||||
}
|
||||
};
|
||||
|
||||
exports.FirefoxPrintService = FirefoxPrintService;
|
||||
}));
|
||||
|
||||
|
||||
|
|
|
@ -213,7 +213,6 @@
|
|||
@RESPATH@/components/dom_stylesheets.xpt
|
||||
@RESPATH@/components/dom_telephony.xpt
|
||||
@RESPATH@/components/dom_traversal.xpt
|
||||
@RESPATH@/components/dom_voicemail.xpt
|
||||
#ifdef MOZ_WEBSPEECH
|
||||
@RESPATH@/components/dom_webspeechrecognition.xpt
|
||||
#endif
|
||||
|
|
|
@ -88,6 +88,12 @@ document_properties_version=PDF Version:
|
|||
document_properties_page_count=Page Count:
|
||||
document_properties_close=Close
|
||||
|
||||
print_progress_message=Preparing document for printing…
|
||||
# LOCALIZATION NOTE (print_progress_percent): "{{progress}}" will be replaced by
|
||||
# a numerical per cent value.
|
||||
print_progress_percent={{progress}}%
|
||||
print_progress_close=Cancel
|
||||
|
||||
# Tooltips and alt text for side panel toolbar buttons
|
||||
# (the _label strings are alt text for the buttons, the .title strings are
|
||||
# tooltips)
|
||||
|
|
|
@ -133,10 +133,14 @@ var AnimationsController = {
|
|||
|
||||
initialize: Task.async(function* () {
|
||||
if (this.initialized) {
|
||||
yield this.initialized.promise;
|
||||
yield this.initialized;
|
||||
return;
|
||||
}
|
||||
this.initialized = promise.defer();
|
||||
|
||||
let resolver;
|
||||
this.initialized = new Promise(resolve => {
|
||||
resolver = resolve;
|
||||
});
|
||||
|
||||
this.onPanelVisibilityChange = this.onPanelVisibilityChange.bind(this);
|
||||
this.onNewNodeFront = this.onNewNodeFront.bind(this);
|
||||
|
@ -162,7 +166,7 @@ var AnimationsController = {
|
|||
this.startListeners();
|
||||
yield this.onNewNodeFront();
|
||||
|
||||
this.initialized.resolve();
|
||||
resolver();
|
||||
}),
|
||||
|
||||
destroy: Task.async(function* () {
|
||||
|
@ -171,10 +175,14 @@ var AnimationsController = {
|
|||
}
|
||||
|
||||
if (this.destroyed) {
|
||||
yield this.destroyed.promise;
|
||||
yield this.destroyed;
|
||||
return;
|
||||
}
|
||||
this.destroyed = promise.defer();
|
||||
|
||||
let resolver;
|
||||
this.destroyed = new Promise(resolve => {
|
||||
resolver = resolve;
|
||||
});
|
||||
|
||||
this.stopListeners();
|
||||
this.destroyAnimationPlayers();
|
||||
|
@ -184,8 +192,7 @@ var AnimationsController = {
|
|||
this.animationsFront.destroy();
|
||||
this.animationsFront = null;
|
||||
}
|
||||
|
||||
this.destroyed.resolve();
|
||||
resolver();
|
||||
}),
|
||||
|
||||
startListeners: function () {
|
||||
|
|
|
@ -30,10 +30,14 @@ var AnimationsPanel = {
|
|||
return;
|
||||
}
|
||||
if (this.initialized) {
|
||||
yield this.initialized.promise;
|
||||
yield this.initialized;
|
||||
return;
|
||||
}
|
||||
this.initialized = promise.defer();
|
||||
|
||||
let resolver;
|
||||
this.initialized = new Promise(resolve => {
|
||||
resolver = resolve;
|
||||
});
|
||||
|
||||
this.playersEl = $("#players");
|
||||
this.errorMessageEl = $("#error-message");
|
||||
|
@ -78,7 +82,7 @@ var AnimationsPanel = {
|
|||
|
||||
yield this.refreshAnimationsUI();
|
||||
|
||||
this.initialized.resolve();
|
||||
resolver();
|
||||
this.emit(this.PANEL_INITIALIZED);
|
||||
}),
|
||||
|
||||
|
@ -88,10 +92,14 @@ var AnimationsPanel = {
|
|||
}
|
||||
|
||||
if (this.destroyed) {
|
||||
yield this.destroyed.promise;
|
||||
yield this.destroyed;
|
||||
return;
|
||||
}
|
||||
this.destroyed = promise.defer();
|
||||
|
||||
let resolver;
|
||||
this.destroyed = new Promise(resolve => {
|
||||
resolver = resolve;
|
||||
});
|
||||
|
||||
this.stopListeners();
|
||||
|
||||
|
@ -108,7 +116,7 @@ var AnimationsPanel = {
|
|||
this.playTimelineButtonEl = this.rewindTimelineButtonEl = null;
|
||||
this.timelineCurrentTimeEl = this.rateSelectorEl = null;
|
||||
|
||||
this.destroyed.resolve();
|
||||
resolver();
|
||||
}),
|
||||
|
||||
startListeners: function () {
|
||||
|
|
|
@ -189,12 +189,12 @@ function waitForContentMessage(name) {
|
|||
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
|
||||
let def = promise.defer();
|
||||
mm.addMessageListener(name, function onMessage(msg) {
|
||||
mm.removeMessageListener(name, onMessage);
|
||||
def.resolve(msg.data);
|
||||
return new Promise(resolve => {
|
||||
mm.addMessageListener(name, function onMessage(msg) {
|
||||
mm.removeMessageListener(name, onMessage);
|
||||
resolve(msg.data);
|
||||
});
|
||||
});
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,27 +2,28 @@
|
|||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://devtools/skin/inspector.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://devtools/skin/rules.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://devtools/skin/computed.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://devtools/skin/fonts.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://devtools/skin/boxmodel.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://devtools/skin/animationinspector.css" type="text/css"?>
|
||||
<?xml-stylesheet href="resource://devtools/client/shared/components/sidebar-toggle.css" type="text/css"?>
|
||||
<?xml-stylesheet href="resource://devtools/client/shared/components/tabs/tabs.css" type="text/css"?>
|
||||
<?xml-stylesheet href="resource://devtools/client/shared/components/tabs/tabbar.css" type="text/css"?>
|
||||
<?xml-stylesheet href="resource://devtools/client/inspector/components/side-panel.css" type="text/css"?>
|
||||
<?xml-stylesheet href="resource://devtools/client/inspector/components/inspector-tab-panel.css" type="text/css"?>
|
||||
<?xml-stylesheet href="resource://devtools/client/shared/components/splitter/split-box.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" dir="">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
|
||||
<link rel="stylesheet" href="chrome://global/skin/"/>
|
||||
<link rel="stylesheet" href="resource://devtools/client/shared/widgets/widgets.css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/widgets.css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/inspector.css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/rules.css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/computed.css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/fonts.css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/boxmodel.css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/animationinspector.css"/>
|
||||
<link rel="stylesheet" href="resource://devtools/client/shared/components/sidebar-toggle.css"/>
|
||||
<link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabs.css"/>
|
||||
<link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabbar.css"/>
|
||||
<link rel="stylesheet" href="resource://devtools/client/inspector/components/side-panel.css"/>
|
||||
<link rel="stylesheet" href="resource://devtools/client/inspector/components/inspector-tab-panel.css"/>
|
||||
<link rel="stylesheet" href="resource://devtools/client/shared/components/splitter/split-box.css"/>
|
||||
|
||||
<script type="application/javascript;version=1.8"
|
||||
src="chrome://devtools/content/shared/theme-switching.js"></script>
|
||||
<script type="application/javascript;version=1.8" src="inspector.js" defer="true"></script>
|
||||
|
@ -35,9 +36,9 @@
|
|||
<div id="inspector-toolbar" class="devtools-toolbar" nowindowdrag="true"
|
||||
data-localization-bundle="devtools/locale/inspector.properties">
|
||||
<button id="inspector-element-add-button" class="devtools-button"
|
||||
data-localization="title=inspectorAddNode.label"/>
|
||||
<div class="devtools-toolbar-spacer" />
|
||||
<span id="inspector-searchlabel" />
|
||||
data-localization="title=inspectorAddNode.label"></button>
|
||||
<div class="devtools-toolbar-spacer"></div>
|
||||
<span id="inspector-searchlabel"></span>
|
||||
<div id="inspector-search" class="devtools-searchbox has-clear-btn">
|
||||
<input id="inspector-searchbox" class="devtools-searchinput"
|
||||
type="search"
|
||||
|
@ -46,13 +47,13 @@
|
|||
</div>
|
||||
<button id="inspector-eyedropper-toggle"
|
||||
data-localization="title=inspector.eyedropper.label"
|
||||
class="devtools-button command-button-invertable" />
|
||||
<div id="inspector-sidebar-toggle-box" />
|
||||
class="devtools-button command-button-invertable"></button>
|
||||
<div id="inspector-sidebar-toggle-box"></div>
|
||||
</div>
|
||||
<div id="markup-box" />
|
||||
<div id="markup-box"></div>
|
||||
<div id="inspector-breadcrumbs-toolbar" class="devtools-toolbar">
|
||||
<div id="inspector-breadcrumbs" class="breadcrumbs-widget-container"
|
||||
role="group" data-localization="aria-label=inspector.breadcrumbs.label" tabindex="0" />
|
||||
role="group" data-localization="aria-label=inspector.breadcrumbs.label" tabindex="0"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -67,7 +68,7 @@
|
|||
<div
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
id="inspector-sidebar"
|
||||
hidden="true" />
|
||||
hidden="true"></div>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar panel definitions -->
|
||||
|
@ -115,7 +116,7 @@
|
|||
type="checkbox"
|
||||
class="includebrowserstyles"/>
|
||||
<label id="browser-style-checkbox-label" for="browser-style-checkbox"
|
||||
data-localization="content=inspector.browserStyles.label"/>
|
||||
data-localization="content=inspector.browserStyles.label"></label>
|
||||
</div>
|
||||
|
||||
<div id="computedview-container">
|
||||
|
@ -124,16 +125,16 @@
|
|||
data-localization-bundle="devtools/locale/boxmodel.properties">
|
||||
<div id="boxmodel-header">
|
||||
<div id="boxmodel-expander" class="expander theme-twisty expandable" open=""></div>
|
||||
<span data-localization="content=boxmodel.title"/>
|
||||
<span data-localization="content=boxmodel.title"></span>
|
||||
</div>
|
||||
|
||||
<div id="boxmodel-container">
|
||||
<div id="boxmodel-main">
|
||||
<span class="boxmodel-legend" data-box="margin" data-localization="content=boxmodel.margin;title=boxmodel.margin"/>
|
||||
<span class="boxmodel-legend" data-box="margin" data-localization="content=boxmodel.margin;title=boxmodel.margin"></span>
|
||||
<div id="boxmodel-margins" data-box="margin" data-localization="title=boxmodel.margin">
|
||||
<span class="boxmodel-legend" data-box="border" data-localization="content=boxmodel.border;title=boxmodel.border"/>
|
||||
<span class="boxmodel-legend" data-box="border" data-localization="content=boxmodel.border;title=boxmodel.border"></span>
|
||||
<div id="boxmodel-borders" data-box="border" data-localization="title=boxmodel.border">
|
||||
<span class="boxmodel-legend" data-box="padding" data-localization="content=boxmodel.padding;title=boxmodel.padding"/>
|
||||
<span class="boxmodel-legend" data-box="padding" data-localization="content=boxmodel.padding;title=boxmodel.padding"></span>
|
||||
<div id="boxmodel-padding" data-box="padding" data-localization="title=boxmodel.padding">
|
||||
<div id="boxmodel-content" data-box="content" data-localization="title=boxmodel.content">
|
||||
</div>
|
||||
|
@ -179,7 +180,7 @@
|
|||
<div id="propertyContainer" class="theme-separator" tabindex="0">
|
||||
</div>
|
||||
|
||||
<div id="computedview-no-results" hidden="" data-localization="content=inspector.noProperties"/>
|
||||
<div id="computedview-no-results" hidden="" data-localization="content=inspector.noProperties"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -198,7 +199,7 @@
|
|||
</div>
|
||||
<label id="font-showall" class="theme-link"
|
||||
data-localization="content=fontinspector.seeAll;
|
||||
title=fontinspector.seeAll.tooltip"/>
|
||||
title=fontinspector.seeAll.tooltip"></label>
|
||||
</div>
|
||||
|
||||
<div id="font-container">
|
||||
|
@ -212,14 +213,14 @@
|
|||
</div>
|
||||
<div class="font-info">
|
||||
<h1 class="font-name"></h1>
|
||||
<span class="font-is-local" data-localization="content=fontinspector.system"/>
|
||||
<span class="font-is-remote" data-localization="content=fontinspector.remote"/>
|
||||
<span class="font-is-local" data-localization="content=fontinspector.system"></span>
|
||||
<span class="font-is-remote" data-localization="content=fontinspector.remote"></span>
|
||||
<p class="font-format-url">
|
||||
<input readonly="readonly" class="font-url"></input>
|
||||
<span class="font-format"></span>
|
||||
</p>
|
||||
<p class="font-css">
|
||||
<span data-localization="content=fontinspector.usedAs"/> "<span class="font-css-name"></span>"
|
||||
<span data-localization="content=fontinspector.usedAs"></span> "<span class="font-css-name"></span>"
|
||||
</p>
|
||||
<pre class="font-css-code"></pre>
|
||||
</div>
|
||||
|
@ -228,7 +229,7 @@
|
|||
</div>
|
||||
|
||||
<div id="sidebar-panel-animationinspector" class="devtools-monospace theme-sidebar inspector-tabpanel">
|
||||
<iframe class="devtools-inspector-tab-frame" />
|
||||
<iframe class="devtools-inspector-tab-frame"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -453,6 +453,7 @@ TextPropertyEditor.prototype = {
|
|||
|
||||
_onStartEditing: function () {
|
||||
this.element.classList.remove("ruleview-overridden");
|
||||
this.filterProperty.hidden = true;
|
||||
this.enable.style.visibility = "hidden";
|
||||
},
|
||||
|
||||
|
|
|
@ -164,7 +164,7 @@ const JITOptimizationsItem = createClass({
|
|||
return dom.div(
|
||||
{
|
||||
className: `optimization-tree-item optimization-tree-item-${type}`,
|
||||
style: { marginLeft: depth * TREE_ROW_HEIGHT }
|
||||
style: { marginInlineStart: depth * TREE_ROW_HEIGHT }
|
||||
},
|
||||
arrow,
|
||||
content
|
||||
|
|
|
@ -246,7 +246,7 @@ PrefBranch.prototype = {
|
|||
userValue: this._userValue,
|
||||
};
|
||||
|
||||
localStorage.setItem(PREFIX + this.fullName, JSON.stringify(store));
|
||||
localStorage.setItem(PREFIX + this._fullName, JSON.stringify(store));
|
||||
this._parent._notify(this._name);
|
||||
},
|
||||
|
||||
|
@ -341,8 +341,9 @@ PrefBranch.prototype = {
|
|||
let parent = this;
|
||||
for (let branch of branchList) {
|
||||
if (!parent._children[branch]) {
|
||||
parent._children[branch] = new PrefBranch(parent, branch,
|
||||
parent.root + "." + branch);
|
||||
let isParentRoot = !parent.parent;
|
||||
let branchName = (isParentRoot ? "" : parent.root + ".") + branch;
|
||||
parent._children[branch] = new PrefBranch(parent, branch, branchName);
|
||||
}
|
||||
parent = parent._children[branch];
|
||||
}
|
||||
|
@ -356,12 +357,15 @@ PrefBranch.prototype = {
|
|||
* @param {String} keyName the full-qualified name of the preference.
|
||||
* This is also the name of the key in local storage.
|
||||
* @param {Any} userValue the user value to use if the pref does not exist
|
||||
* @param {Any} defaultValue the default value to use if the pref
|
||||
* does not exist
|
||||
* @param {Boolean} hasUserValue if a new pref is created, whether
|
||||
* the default value is also a user value
|
||||
* @param {Any} defaultValue the default value to use if the pref
|
||||
* does not exist
|
||||
* @param {Boolean} init if true, then this call is initialization
|
||||
* from local storage and should override the default prefs
|
||||
*/
|
||||
_findOrCreatePref: function (keyName, userValue, hasUserValue, defaultValue) {
|
||||
_findOrCreatePref: function (keyName, userValue, hasUserValue, defaultValue,
|
||||
init = false) {
|
||||
let branch = this._createBranch(keyName.split("."));
|
||||
|
||||
if (hasUserValue && typeof (userValue) !== typeof (defaultValue)) {
|
||||
|
@ -383,7 +387,7 @@ PrefBranch.prototype = {
|
|||
throw new Error("unhandled argument type: " + typeof (defaultValue));
|
||||
}
|
||||
|
||||
if (branch._type === PREF_INVALID) {
|
||||
if (init || branch._type === PREF_INVALID) {
|
||||
branch._storageUpdated(type, userValue, hasUserValue, defaultValue);
|
||||
} else if (branch._type !== type) {
|
||||
throw new Error("attempt to change type of pref " + keyName);
|
||||
|
@ -422,7 +426,7 @@ PrefBranch.prototype = {
|
|||
* Helper function to initialize the root PrefBranch.
|
||||
*/
|
||||
_initializeRoot: function () {
|
||||
if (localStorage.length === 0 && Services._defaultPrefsEnabled) {
|
||||
if (Services._defaultPrefsEnabled) {
|
||||
/* eslint-disable no-eval */
|
||||
let devtools = require("raw!prefs!devtools/client/preferences/devtools");
|
||||
eval(devtools);
|
||||
|
@ -439,7 +443,7 @@ PrefBranch.prototype = {
|
|||
let {userValue, hasUserValue, defaultValue} =
|
||||
JSON.parse(localStorage.getItem(keyName));
|
||||
this._findOrCreatePref(keyName.slice(PREFIX.length), userValue,
|
||||
hasUserValue, defaultValue);
|
||||
hasUserValue, defaultValue, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,3 +5,4 @@ support-files =
|
|||
[test_service_appinfo.html]
|
||||
[test_service_focus.html]
|
||||
[test_service_prefs.html]
|
||||
[test_service_prefs_defaults.html]
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1309384
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 1309384 - Services.prefs replacement defaults handling</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
|
||||
<script type="application/javascript;version=1.8">
|
||||
"use strict";
|
||||
var exports = {}
|
||||
var module = {exports};
|
||||
|
||||
// Allow one require("raw!prefs...") to return some defaults, with the
|
||||
// others being ignored.
|
||||
var firstTime = true;
|
||||
function require(something) {
|
||||
if (!something.startsWith("raw!prefs!")) {
|
||||
throw new Error("whoops");
|
||||
}
|
||||
if (!firstTime) {
|
||||
return "";
|
||||
}
|
||||
firstTime = false;
|
||||
return "pref('pref1', 'pref1default');\n" +
|
||||
"pref('pref2', 'pref2default');\n" +
|
||||
"pref('pref3', 'pref3default');\n";
|
||||
}
|
||||
|
||||
// Pretend that one of the prefs was modifed by the user in an earlier session.
|
||||
localStorage.setItem("Services.prefs:pref3", JSON.stringify({
|
||||
// string
|
||||
type: 32,
|
||||
defaultValue: "pref3default",
|
||||
hasUserValue: true,
|
||||
userValue: "glass winged butterfly"
|
||||
}));
|
||||
|
||||
</script>
|
||||
|
||||
<script type="application/javascript;version=1.8"
|
||||
src="resource://devtools/client/shared/shim/Services.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.8">
|
||||
"use strict";
|
||||
|
||||
is(Services.prefs.getCharPref("pref1"), "pref1default", "pref1 value");
|
||||
is(Services.prefs.getCharPref("pref2"), "pref2default", "pref2 value");
|
||||
is(Services.prefs.getCharPref("pref3"), "glass winged butterfly", "pref3 value");
|
||||
|
||||
// Only pref3 should be in local storage at this point.
|
||||
is(localStorage.length, 1, "local storage is correct");
|
||||
|
||||
Services.prefs.setCharPref("pref2", "pref2override");
|
||||
|
||||
// Check that a default pref can be overridden properly
|
||||
|
||||
// Workaround to reset the prefs helper and force it to read defaults & overrides again.
|
||||
Services._prefs = null;
|
||||
is(Services.prefs.getCharPref("pref2"), "pref2override", "pref2 value overridden");
|
||||
|
||||
// Clean up.
|
||||
localStorage.clear();
|
||||
|
||||
</script>
|
||||
</body>
|
|
@ -43,7 +43,14 @@ add_task(function* () {
|
|||
|
||||
info("Run tests for a Tooltip with a XUL panel");
|
||||
useXulWrapper = true;
|
||||
yield runTests(doc);
|
||||
|
||||
let isLinux = Services.appinfo.OS === "Linux";
|
||||
if (!isLinux) {
|
||||
// Skip these tests on linux when using a XUL Panel wrapper because some linux window
|
||||
// manager don't support nicely XUL Panels with noautohide _and_ noautofocus.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1301342#c11
|
||||
yield runTests(doc);
|
||||
}
|
||||
});
|
||||
|
||||
function* runTests(doc) {
|
||||
|
|
|
@ -572,6 +572,7 @@ HTMLTooltip.prototype = {
|
|||
panel.setAttribute("animate", false);
|
||||
panel.setAttribute("consumeoutsideclicks", false);
|
||||
panel.setAttribute("noautofocus", true);
|
||||
panel.setAttribute("noautohide", true);
|
||||
panel.setAttribute("ignorekeys", true);
|
||||
|
||||
// Use type="arrow" to prevent side effects (see Bug 1285206)
|
||||
|
|
|
@ -746,10 +746,18 @@ checkbox:-moz-focusring {
|
|||
border: 0;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
.theme-dark .theme-checkbox {
|
||||
background-position: -28px 0;
|
||||
}
|
||||
|
||||
.theme-checkbox[checked] {
|
||||
background-position: -14px 0;
|
||||
}
|
||||
|
||||
.theme-dark .theme-checkbox[checked] {
|
||||
background-position: -42px 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -253,11 +253,6 @@ this.PermissionsTable = { geolocation: {
|
|||
privileged: ALLOW_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
"voicemail": {
|
||||
app: DENY_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
"idle": {
|
||||
app: DENY_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
#include "mozilla/dom/StorageManager.h"
|
||||
#include "mozilla/dom/TCPSocket.h"
|
||||
#include "mozilla/dom/Telephony.h"
|
||||
#include "mozilla/dom/Voicemail.h"
|
||||
#include "mozilla/dom/VRDisplay.h"
|
||||
#include "mozilla/dom/workers/RuntimeService.h"
|
||||
#include "mozilla/Hal.h"
|
||||
|
@ -84,7 +83,6 @@
|
|||
#ifdef MOZ_B2G_BT
|
||||
#include "BluetoothManager.h"
|
||||
#endif
|
||||
#include "DOMCameraManager.h"
|
||||
|
||||
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
|
||||
#include "AudioChannelManager.h"
|
||||
|
@ -220,7 +218,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIccManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMobileMessageManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTelephony)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVoicemail)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputPortManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorageManager)
|
||||
|
@ -233,7 +230,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
|
|||
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelManager)
|
||||
#endif
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCameraManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaDevices)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimeManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerContainer)
|
||||
|
@ -305,11 +301,6 @@ Navigator::Invalidate()
|
|||
mTelephony = nullptr;
|
||||
}
|
||||
|
||||
if (mVoicemail) {
|
||||
mVoicemail->Shutdown();
|
||||
mVoicemail = nullptr;
|
||||
}
|
||||
|
||||
if (mInputPortManager) {
|
||||
mInputPortManager = nullptr;
|
||||
}
|
||||
|
@ -331,7 +322,6 @@ Navigator::Invalidate()
|
|||
}
|
||||
#endif
|
||||
|
||||
mCameraManager = nullptr;
|
||||
mMediaDevices = nullptr;
|
||||
|
||||
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
|
||||
|
@ -1710,21 +1700,6 @@ Navigator::GetMozMobileConnections(ErrorResult& aRv)
|
|||
|
||||
#endif // MOZ_B2G_RIL
|
||||
|
||||
Voicemail*
|
||||
Navigator::GetMozVoicemail(ErrorResult& aRv)
|
||||
{
|
||||
if (!mVoicemail) {
|
||||
if (!mWindow) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mVoicemail = Voicemail::Create(mWindow, aRv);
|
||||
}
|
||||
|
||||
return mVoicemail;
|
||||
}
|
||||
|
||||
IccManager*
|
||||
Navigator::GetMozIccManager(ErrorResult& aRv)
|
||||
{
|
||||
|
@ -1902,23 +1877,6 @@ Navigator::GetMozTime(ErrorResult& aRv)
|
|||
}
|
||||
#endif
|
||||
|
||||
nsDOMCameraManager*
|
||||
Navigator::GetMozCameras(ErrorResult& aRv)
|
||||
{
|
||||
if (!mCameraManager) {
|
||||
if (!mWindow ||
|
||||
!mWindow->GetOuterWindow() ||
|
||||
mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
|
||||
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mCameraManager = nsDOMCameraManager::CreateInstance(mWindow);
|
||||
}
|
||||
|
||||
return mCameraManager;
|
||||
}
|
||||
|
||||
already_AddRefed<ServiceWorkerContainer>
|
||||
Navigator::ServiceWorker()
|
||||
{
|
||||
|
@ -1965,9 +1923,6 @@ Navigator::OnNavigation()
|
|||
if (manager) {
|
||||
manager->OnNavigation(mWindow->WindowID());
|
||||
}
|
||||
if (mCameraManager) {
|
||||
mCameraManager->OnNavigation(mWindow->WindowID());
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -2021,14 +1976,6 @@ Navigator::HasWakeLockSupport(JSContext* /* unused*/, JSObject* /*unused */)
|
|||
return !!pmService;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
Navigator::HasCameraSupport(JSContext* /* unused */, JSObject* aGlobal)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindowInner> win = GetWindowFromGlobal(aGlobal);
|
||||
return win && nsDOMCameraManager::CheckPermission(win);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
Navigator::HasWifiManagerSupport(JSContext* /* unused */,
|
||||
|
|
|
@ -24,7 +24,6 @@ class nsPluginArray;
|
|||
class nsMimeTypeArray;
|
||||
class nsPIDOMWindowInner;
|
||||
class nsIDOMNavigatorSystemMessages;
|
||||
class nsDOMCameraManager;
|
||||
class nsDOMDeviceStorage;
|
||||
class nsIPrincipal;
|
||||
class nsIURI;
|
||||
|
@ -87,7 +86,6 @@ class MobileConnectionArray;
|
|||
class PowerManager;
|
||||
class IccManager;
|
||||
class Telephony;
|
||||
class Voicemail;
|
||||
class InputPortManager;
|
||||
class DeviceStorageAreaListener;
|
||||
class Presentation;
|
||||
|
@ -226,11 +224,9 @@ public:
|
|||
IccManager* GetMozIccManager(ErrorResult& aRv);
|
||||
MobileMessageManager* GetMozMobileMessage();
|
||||
Telephony* GetMozTelephony(ErrorResult& aRv);
|
||||
Voicemail* GetMozVoicemail(ErrorResult& aRv);
|
||||
InputPortManager* GetInputPortManager(ErrorResult& aRv);
|
||||
already_AddRefed<LegacyMozTCPSocket> MozTCPSocket();
|
||||
network::Connection* GetConnection(ErrorResult& aRv);
|
||||
nsDOMCameraManager* GetMozCameras(ErrorResult& aRv);
|
||||
MediaDevices* GetMediaDevices(ErrorResult& aRv);
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
|
@ -281,8 +277,6 @@ public:
|
|||
|
||||
// WebIDL helper methods
|
||||
static bool HasWakeLockSupport(JSContext* /* unused*/, JSObject* /*unused */);
|
||||
static bool HasCameraSupport(JSContext* /* unused */,
|
||||
JSObject* aGlobal);
|
||||
static bool HasWifiManagerSupport(JSContext* /* unused */,
|
||||
JSObject* aGlobal);
|
||||
#ifdef MOZ_NFC
|
||||
|
@ -335,7 +329,6 @@ private:
|
|||
RefPtr<IccManager> mIccManager;
|
||||
RefPtr<MobileMessageManager> mMobileMessageManager;
|
||||
RefPtr<Telephony> mTelephony;
|
||||
RefPtr<Voicemail> mVoicemail;
|
||||
RefPtr<InputPortManager> mInputPortManager;
|
||||
RefPtr<network::Connection> mConnection;
|
||||
#ifdef MOZ_B2G_RIL
|
||||
|
@ -347,7 +340,6 @@ private:
|
|||
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
|
||||
RefPtr<system::AudioChannelManager> mAudioChannelManager;
|
||||
#endif
|
||||
RefPtr<nsDOMCameraManager> mCameraManager;
|
||||
RefPtr<MediaDevices> mMediaDevices;
|
||||
nsTArray<nsWeakPtr> mDeviceStorageStores;
|
||||
RefPtr<time::TimeManager> mTimeManager;
|
||||
|
|
|
@ -686,22 +686,6 @@ nsContentPermissionRequestProxy::Allow(JS::HandleValue aChoices)
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
uint32_t len = mPermissionRequests.Length();
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
if (mPermissionRequests[i].type().EqualsLiteral("audio-capture")) {
|
||||
GonkPermissionService::GetInstance()->addGrantInfo(
|
||||
"android.permission.RECORD_AUDIO",
|
||||
static_cast<ContentParent*>(mParent->Manager())->Pid());
|
||||
}
|
||||
if (mPermissionRequests[i].type().EqualsLiteral("video-capture")) {
|
||||
GonkPermissionService::GetInstance()->addGrantInfo(
|
||||
"android.permission.CAMERA",
|
||||
static_cast<ContentParent*>(mParent->Manager())->Pid());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
nsTArray<PermissionChoice> choices;
|
||||
if (aChoices.isNullOrUndefined()) {
|
||||
// No choice is specified.
|
||||
|
|
|
@ -1307,6 +1307,7 @@ nsIDocument::nsIDocument()
|
|||
mFontFaceSetDirty(true),
|
||||
mGetUserFontSetCalled(false),
|
||||
mPostedFlushUserFontSet(false),
|
||||
mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS),
|
||||
mPartID(0),
|
||||
mDidFireDOMContentLoaded(true),
|
||||
mHasScrollLinkedEffect(false),
|
||||
|
|
|
@ -734,7 +734,6 @@ GK_ATOM(oncomplete, "oncomplete")
|
|||
GK_ATOM(oncompositionend, "oncompositionend")
|
||||
GK_ATOM(oncompositionstart, "oncompositionstart")
|
||||
GK_ATOM(oncompositionupdate, "oncompositionupdate")
|
||||
GK_ATOM(onconfigurationchange, "onconfigurationchange")
|
||||
GK_ATOM(onconnect, "onconnect")
|
||||
GK_ATOM(onconnected, "onconnected")
|
||||
GK_ATOM(onconnecting, "onconnecting")
|
||||
|
@ -791,7 +790,6 @@ GK_ATOM(onenterpincodereq, "onenterpincodereq")
|
|||
GK_ATOM(onemergencycbmodechange, "onemergencycbmodechange")
|
||||
GK_ATOM(onerror, "onerror")
|
||||
GK_ATOM(onevicted, "onevicted")
|
||||
GK_ATOM(onfacesdetected, "onfacesdetected")
|
||||
GK_ATOM(onfailed, "onfailed")
|
||||
GK_ATOM(onfetch, "onfetch")
|
||||
GK_ATOM(onfinish, "onfinish")
|
||||
|
@ -882,7 +880,6 @@ GK_ATOM(onpairingconsentreq, "onpairingconsentreq")
|
|||
GK_ATOM(onpaste, "onpaste")
|
||||
GK_ATOM(onpendingchange, "onpendingchange")
|
||||
GK_ATOM(onpichange, "onpichange")
|
||||
GK_ATOM(onpicture, "onpicture")
|
||||
GK_ATOM(onpointerlockchange, "onpointerlockchange")
|
||||
GK_ATOM(onpointerlockerror, "onpointerlockerror")
|
||||
GK_ATOM(onpopuphidden, "onpopuphidden")
|
||||
|
@ -890,8 +887,6 @@ GK_ATOM(onpopuphiding, "onpopuphiding")
|
|||
GK_ATOM(onpopuppositioned, "onpopuppositioned")
|
||||
GK_ATOM(onpopupshowing, "onpopupshowing")
|
||||
GK_ATOM(onpopupshown, "onpopupshown")
|
||||
GK_ATOM(onposter, "onposter")
|
||||
GK_ATOM(onpreviewstatechange, "onpreviewstatechange")
|
||||
GK_ATOM(onpullphonebookreq, "onpullphonebookreq")
|
||||
GK_ATOM(onpullvcardentryreq, "onpullvcardentryreq")
|
||||
GK_ATOM(onpullvcardlistingreq, "onpullvcardlistingreq")
|
||||
|
@ -907,7 +902,6 @@ GK_ATOM(onreadsuccess, "onreadsuccess")
|
|||
GK_ATOM(onready, "onready")
|
||||
GK_ATOM(onreadystatechange, "onreadystatechange")
|
||||
GK_ATOM(onreceived, "onreceived")
|
||||
GK_ATOM(onrecorderstatechange, "onrecorderstatechange")
|
||||
GK_ATOM(onremoteheld, "onremoteheld")
|
||||
GK_ATOM(onremoteresumed, "onremoteresumed")
|
||||
GK_ATOM(onresourcetimingbufferfull, "onresourcetimingbufferfull")
|
||||
|
@ -928,7 +922,6 @@ GK_ATOM(onsending, "onsending")
|
|||
GK_ATOM(onsent, "onsent")
|
||||
GK_ATOM(onset, "onset")
|
||||
GK_ATOM(onshow, "onshow")
|
||||
GK_ATOM(onshutter, "onshutter")
|
||||
GK_ATOM(onstatechange, "onstatechange")
|
||||
GK_ATOM(onstatuschanged, "onstatuschanged")
|
||||
GK_ATOM(onstkcommand, "onstkcommand")
|
||||
|
|
|
@ -189,45 +189,6 @@ DOMInterfaces = {
|
|||
'nativeType': 'mozilla::dom::cache::CacheStorage',
|
||||
},
|
||||
|
||||
'CameraCapabilities': {
|
||||
'nativeType': 'mozilla::dom::CameraCapabilities',
|
||||
'headerFile': 'DOMCameraCapabilities.h'
|
||||
},
|
||||
|
||||
'CameraControl': {
|
||||
'nativeType': 'mozilla::nsDOMCameraControl',
|
||||
'headerFile': 'DOMCameraControl.h',
|
||||
'binaryNames': {
|
||||
"release": "ReleaseHardware"
|
||||
}
|
||||
},
|
||||
|
||||
'CameraDetectedFace': {
|
||||
'nativeType': 'mozilla::dom::DOMCameraDetectedFace',
|
||||
'headerFile': 'DOMCameraDetectedFace.h'
|
||||
},
|
||||
|
||||
'CameraManager': {
|
||||
'nativeType': 'nsDOMCameraManager',
|
||||
'headerFile': 'DOMCameraManager.h'
|
||||
},
|
||||
|
||||
'CameraRecorderAudioProfile': {
|
||||
'headerFile': 'DOMCameraCapabilities.h'
|
||||
},
|
||||
|
||||
'CameraRecorderProfile': {
|
||||
'headerFile': 'DOMCameraCapabilities.h'
|
||||
},
|
||||
|
||||
'CameraRecorderProfiles': {
|
||||
'headerFile': 'DOMCameraCapabilities.h'
|
||||
},
|
||||
|
||||
'CameraRecorderVideoProfile': {
|
||||
'headerFile': 'DOMCameraCapabilities.h'
|
||||
},
|
||||
|
||||
'CanvasRenderingContext2D': {
|
||||
'implicitJSContext': [
|
||||
'createImageData', 'getImageData'
|
||||
|
@ -750,14 +711,6 @@ DOMInterfaces = {
|
|||
'nativeType': 'mozilla::dom::time::TimeManager',
|
||||
},
|
||||
|
||||
'MozVoicemail': {
|
||||
'nativeType': 'mozilla::dom::Voicemail',
|
||||
},
|
||||
|
||||
'MozVoicemailStatus': {
|
||||
'nativeType': 'mozilla::dom::VoicemailStatus',
|
||||
},
|
||||
|
||||
'MutationObserver': {
|
||||
'nativeType': 'nsDOMMutationObserver',
|
||||
},
|
||||
|
|
|
@ -60,7 +60,6 @@ LOCAL_INCLUDES += [
|
|||
'/dom/base',
|
||||
'/dom/battery',
|
||||
'/dom/bluetooth/common/webapi',
|
||||
'/dom/camera',
|
||||
'/dom/canvas',
|
||||
'/dom/geolocation',
|
||||
'/dom/html',
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef RWLOCK_AUTO_ENTER_H
|
||||
#define RWLOCK_AUTO_ENTER_H
|
||||
|
||||
#include "prrwlock.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
class RwLockAutoEnterRead
|
||||
{
|
||||
public:
|
||||
explicit RwLockAutoEnterRead(PRRWLock* aRwLock)
|
||||
: mRwLock(aRwLock)
|
||||
{
|
||||
MOZ_ASSERT(mRwLock);
|
||||
PR_RWLock_Rlock(mRwLock);
|
||||
}
|
||||
|
||||
~RwLockAutoEnterRead()
|
||||
{
|
||||
PR_RWLock_Unlock(mRwLock);
|
||||
}
|
||||
|
||||
protected:
|
||||
PRRWLock* mRwLock;
|
||||
};
|
||||
|
||||
class RwLockAutoEnterWrite
|
||||
{
|
||||
public:
|
||||
explicit RwLockAutoEnterWrite(PRRWLock* aRwLock)
|
||||
: mRwLock(aRwLock)
|
||||
{
|
||||
MOZ_ASSERT(mRwLock);
|
||||
PR_RWLock_Wlock(mRwLock);
|
||||
}
|
||||
|
||||
~RwLockAutoEnterWrite()
|
||||
{
|
||||
PR_RWLock_Unlock(mRwLock);
|
||||
}
|
||||
|
||||
protected:
|
||||
PRRWLock* mRwLock;
|
||||
};
|
||||
|
||||
#endif // RWLOCK_AUTO_ENTER_H
|
|
@ -1,44 +0,0 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=40: */
|
||||
/* 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 DOM_CAMERA_CAMERACOMMON_H
|
||||
#define DOM_CAMERA_CAMERACOMMON_H
|
||||
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
extern mozilla::LogModule* GetCameraLog();
|
||||
#define DOM_CAMERA_LOG( type, ... ) MOZ_LOG(GetCameraLog(), (mozilla::LogLevel)type, ( __VA_ARGS__ ))
|
||||
|
||||
#define DOM_CAMERA_LOGA( ... ) DOM_CAMERA_LOG( mozilla::LogLevel::Error, __VA_ARGS__ )
|
||||
|
||||
/**
|
||||
* From the least to the most output.
|
||||
*/
|
||||
enum {
|
||||
DOM_CAMERA_LOG_NOTHING,
|
||||
DOM_CAMERA_LOG_ERROR,
|
||||
DOM_CAMERA_LOG_WARNING,
|
||||
DOM_CAMERA_LOG_INFO,
|
||||
DOM_CAMERA_LOG_TRACE,
|
||||
DOM_CAMERA_LOG_REFERENCES
|
||||
};
|
||||
|
||||
/**
|
||||
* DOM_CAMERA_LOGR() can be called before 'gCameraLog' is set, so
|
||||
* we need to handle this one a little differently.
|
||||
*/
|
||||
#define DOM_CAMERA_LOGR( ... ) \
|
||||
do { \
|
||||
if (GetCameraLog()) { \
|
||||
DOM_CAMERA_LOG( DOM_CAMERA_LOG_REFERENCES, __VA_ARGS__ ); \
|
||||
} \
|
||||
} while (0)
|
||||
#define DOM_CAMERA_LOGT( ... ) DOM_CAMERA_LOG( DOM_CAMERA_LOG_TRACE, __VA_ARGS__ )
|
||||
#define DOM_CAMERA_LOGI( ... ) DOM_CAMERA_LOG( DOM_CAMERA_LOG_INFO, __VA_ARGS__ )
|
||||
#define DOM_CAMERA_LOGW( ... ) DOM_CAMERA_LOG( DOM_CAMERA_LOG_WARNING, __VA_ARGS__ )
|
||||
#define DOM_CAMERA_LOGE( ... ) DOM_CAMERA_LOG( DOM_CAMERA_LOG_ERROR, __VA_ARGS__ )
|
||||
|
||||
#endif // DOM_CAMERA_CAMERACOMMON_H
|
|
@ -1,795 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CameraControlImpl.h"
|
||||
#include "base/basictypes.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsIWeakReferenceUtils.h"
|
||||
#include "CameraCommon.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "DeviceStorageFileDescriptor.h"
|
||||
#include "CameraControlListener.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
/* static */ StaticRefPtr<nsIThread> CameraControlImpl::sCameraThread;
|
||||
|
||||
CameraControlImpl::CameraControlImpl()
|
||||
: mListenerLock("mozilla::camera::CameraControlImpl.Listeners")
|
||||
, mPreviewState(CameraControlListener::kPreviewStopped)
|
||||
, mHardwareState(CameraControlListener::kHardwareUninitialized)
|
||||
, mHardwareStateChangeReason(NS_OK)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
mCurrentConfiguration.mMode = ICameraControl::kUnspecifiedMode;
|
||||
|
||||
class Delegate : public Runnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
char stackBaseGuess;
|
||||
profiler_register_thread("CameraThread", &stackBaseGuess);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
// reuse the same camera thread to conserve resources
|
||||
nsCOMPtr<nsIThread> ct = do_QueryInterface(sCameraThread);
|
||||
if (ct) {
|
||||
mCameraThread = ct.forget();
|
||||
} else {
|
||||
nsresult rv = NS_NewNamedThread("CameraThread", getter_AddRefs(mCameraThread));
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_CRASH("Failed to create new Camera Thread");
|
||||
}
|
||||
mCameraThread->Dispatch(new Delegate(), NS_DISPATCH_NORMAL);
|
||||
sCameraThread = mCameraThread;
|
||||
}
|
||||
}
|
||||
|
||||
CameraControlImpl::~CameraControlImpl()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::OnHardwareStateChange(CameraControlListener::HardwareState aNewState,
|
||||
nsresult aReason)
|
||||
{
|
||||
// This callback can run on threads other than the Main Thread and
|
||||
// the Camera Thread. On Gonk, it may be called from the camera's
|
||||
// local binder thread, should the mediaserver process die.
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
if (aNewState == mHardwareState) {
|
||||
DOM_CAMERA_LOGI("OnHardwareStateChange: state did not change from %d\n", mHardwareState);
|
||||
return;
|
||||
}
|
||||
|
||||
const char* state[] = { "uninitialized", "closed", "open", "failed" };
|
||||
MOZ_ASSERT(aNewState >= 0);
|
||||
if (static_cast<unsigned int>(aNewState) < sizeof(state) / sizeof(state[0])) {
|
||||
DOM_CAMERA_LOGI("New hardware state is '%s' (reason=0x%x)\n",
|
||||
state[aNewState], aReason);
|
||||
} else {
|
||||
DOM_CAMERA_LOGE("OnHardwareStateChange: got invalid HardwareState value %d\n", aNewState);
|
||||
}
|
||||
|
||||
mHardwareState = aNewState;
|
||||
mHardwareStateChangeReason = aReason;
|
||||
|
||||
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
|
||||
CameraControlListener* l = mListeners[i];
|
||||
l->OnHardwareStateChange(mHardwareState, mHardwareStateChangeReason);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::OnConfigurationChange()
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
DOM_CAMERA_LOGI("OnConfigurationChange : %zu listeners\n", mListeners.Length());
|
||||
|
||||
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
|
||||
CameraControlListener* l = mListeners[i];
|
||||
l->OnConfigurationChange(mCurrentConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::OnAutoFocusComplete(bool aAutoFocusSucceeded)
|
||||
{
|
||||
// This callback can run on threads other than the Main Thread and
|
||||
// the Camera Thread. On Gonk, it is called from the camera
|
||||
// library's auto focus thread.
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
|
||||
CameraControlListener* l = mListeners[i];
|
||||
l->OnAutoFocusComplete(aAutoFocusSucceeded);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::OnAutoFocusMoving(bool aIsMoving)
|
||||
{
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
|
||||
CameraControlListener* l = mListeners[i];
|
||||
l->OnAutoFocusMoving(aIsMoving);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::OnFacesDetected(const nsTArray<Face>& aFaces)
|
||||
{
|
||||
// This callback can run on threads other than the Main Thread and
|
||||
// the Camera Thread. On Gonk, it is called from the camera
|
||||
// library's face detection thread.
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
|
||||
CameraControlListener* l = mListeners[i];
|
||||
l->OnFacesDetected(aFaces);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::OnTakePictureComplete(const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
|
||||
{
|
||||
// This callback can run on threads other than the Main Thread and
|
||||
// the Camera Thread. On Gonk, it is called from the camera
|
||||
// library's snapshot thread.
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
|
||||
CameraControlListener* l = mListeners[i];
|
||||
l->OnTakePictureComplete(aData, aLength, aMimeType);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::OnPoster(dom::BlobImpl* aBlobImpl)
|
||||
{
|
||||
// This callback can run on threads other than the Main Thread and
|
||||
// the Camera Thread.
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
|
||||
CameraControlListener* l = mListeners[i];
|
||||
l->OnPoster(aBlobImpl);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::OnShutter()
|
||||
{
|
||||
// This callback can run on threads other than the Main Thread and
|
||||
// the Camera Thread. On Gonk, it is called from the camera driver's
|
||||
// preview thread.
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
|
||||
CameraControlListener* l = mListeners[i];
|
||||
l->OnShutter();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::OnRecorderStateChange(CameraControlListener::RecorderState aState,
|
||||
int32_t aStatus, int32_t aTrackNumber)
|
||||
{
|
||||
// This callback can run on threads other than the Main Thread and
|
||||
// the Camera Thread. On Gonk, it is called from the media encoder
|
||||
// thread.
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
|
||||
CameraControlListener* l = mListeners[i];
|
||||
l->OnRecorderStateChange(aState, aStatus, aTrackNumber);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::OnPreviewStateChange(CameraControlListener::PreviewState aNewState)
|
||||
{
|
||||
// This callback runs on the Main Thread and the Camera Thread, and
|
||||
// may run on the local binder thread, should the mediaserver
|
||||
// process die.
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
if (aNewState == mPreviewState) {
|
||||
DOM_CAMERA_LOGI("OnPreviewStateChange: state did not change from %d\n", mPreviewState);
|
||||
return;
|
||||
}
|
||||
|
||||
const char* state[] = { "stopped", "paused", "started" };
|
||||
MOZ_ASSERT(aNewState >= 0);
|
||||
if (static_cast<unsigned int>(aNewState) < sizeof(state) / sizeof(state[0])) {
|
||||
DOM_CAMERA_LOGI("New preview state is '%s'\n", state[aNewState]);
|
||||
} else {
|
||||
DOM_CAMERA_LOGE("OnPreviewStateChange: got unknown PreviewState value %d\n", aNewState);
|
||||
}
|
||||
|
||||
mPreviewState = aNewState;
|
||||
|
||||
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
|
||||
CameraControlListener* l = mListeners[i];
|
||||
l->OnPreviewStateChange(mPreviewState);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::OnRateLimitPreview(bool aLimit)
|
||||
{
|
||||
// This function runs on neither the Main Thread nor the Camera Thread.
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
DOM_CAMERA_LOGI("OnRateLimitPreview: %d\n", aLimit);
|
||||
|
||||
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
|
||||
CameraControlListener* l = mListeners[i];
|
||||
l->OnRateLimitPreview(aLimit);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CameraControlImpl::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight)
|
||||
{
|
||||
// This function runs on neither the Main Thread nor the Camera Thread.
|
||||
// On Gonk, it is called from the camera driver's preview thread.
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
DOM_CAMERA_LOGI("OnNewPreviewFrame: we have %zu preview frame listener(s)\n",
|
||||
mListeners.Length());
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
|
||||
CameraControlListener* l = mListeners[i];
|
||||
consumed = l->OnNewPreviewFrame(aImage, aWidth, aHeight) || consumed;
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::OnUserError(CameraControlListener::UserContext aContext,
|
||||
nsresult aError)
|
||||
{
|
||||
// This callback can run on threads other than the Main Thread and
|
||||
// the Camera Thread.
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
const char* context[] = {
|
||||
"StartCamera",
|
||||
"StopCamera",
|
||||
"AutoFocus",
|
||||
"StartFaceDetection",
|
||||
"StopFaceDetection",
|
||||
"TakePicture",
|
||||
"StartRecording",
|
||||
"StopRecording",
|
||||
"PauseRecording",
|
||||
"ResumeRecording",
|
||||
"SetConfiguration",
|
||||
"StartPreview",
|
||||
"StopPreview",
|
||||
"SetPictureSize",
|
||||
"SetThumbnailSize",
|
||||
"ResumeContinuousFocus",
|
||||
"Unspecified"
|
||||
};
|
||||
if (static_cast<size_t>(aContext) < sizeof(context) / sizeof(context[0])) {
|
||||
DOM_CAMERA_LOGW("CameraControlImpl::OnUserError : aContext='%s' (%d), aError=0x%x\n",
|
||||
context[aContext], aContext, aError);
|
||||
} else {
|
||||
DOM_CAMERA_LOGE("CameraControlImpl::OnUserError : aContext=%d, aError=0x%x\n",
|
||||
aContext, aError);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
|
||||
CameraControlListener* l = mListeners[i];
|
||||
l->OnUserError(aContext, aError);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::OnSystemError(CameraControlListener::SystemContext aContext,
|
||||
nsresult aError)
|
||||
{
|
||||
// This callback can run on threads other than the Main Thread and
|
||||
// the Camera Thread.
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
const char* context[] = {
|
||||
"Camera Service"
|
||||
};
|
||||
if (static_cast<size_t>(aContext) < sizeof(context) / sizeof(context[0])) {
|
||||
DOM_CAMERA_LOGW("CameraControlImpl::OnSystemError : aContext='%s' (%d), aError=0x%x\n",
|
||||
context[aContext], aContext, aError);
|
||||
} else {
|
||||
DOM_CAMERA_LOGE("CameraControlImpl::OnSystemError : aContext=%d, aError=0x%x\n",
|
||||
aContext, aError);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
|
||||
CameraControlListener* l = mListeners[i];
|
||||
l->OnSystemError(aContext, aError);
|
||||
}
|
||||
}
|
||||
|
||||
// Camera control asynchronous message; these are dispatched from
|
||||
// the Main Thread to the Camera Thread, where they are consumed.
|
||||
|
||||
class CameraControlImpl::ControlMessage : public Runnable
|
||||
{
|
||||
public:
|
||||
ControlMessage(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext)
|
||||
: mCameraControl(aCameraControl)
|
||||
, mContext(aContext)
|
||||
{ }
|
||||
|
||||
virtual nsresult RunImpl() = 0;
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
MOZ_ASSERT(mCameraControl);
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mCameraControl->mCameraThread);
|
||||
|
||||
nsresult rv = RunImpl();
|
||||
if (NS_FAILED(rv)) {
|
||||
nsPrintfCString msg("Camera control API(%d) failed with 0x%x", mContext, rv);
|
||||
NS_WARNING(msg.get());
|
||||
mCameraControl->OnUserError(mContext, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~ControlMessage() { }
|
||||
|
||||
RefPtr<CameraControlImpl> mCameraControl;
|
||||
CameraControlListener::UserContext mContext;
|
||||
};
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::Dispatch(ControlMessage* aMessage)
|
||||
{
|
||||
nsresult rv = mCameraThread->Dispatch(aMessage, NS_DISPATCH_NORMAL);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsPrintfCString msg("Failed to dispatch camera control message (0x%x)", rv);
|
||||
NS_WARNING(msg.get());
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::Start(const Configuration* aConfig)
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext,
|
||||
const Configuration* aConfig)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
, mHaveInitialConfig(false)
|
||||
{
|
||||
if (aConfig) {
|
||||
mConfig = *aConfig;
|
||||
mHaveInitialConfig = true;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
if (mHaveInitialConfig) {
|
||||
return mCameraControl->StartImpl(&mConfig);
|
||||
}
|
||||
return mCameraControl->StartImpl();
|
||||
}
|
||||
|
||||
protected:
|
||||
bool mHaveInitialConfig;
|
||||
Configuration mConfig;
|
||||
};
|
||||
|
||||
return Dispatch(new Message(this, CameraControlListener::kInStartCamera, aConfig));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::SetConfiguration(const Configuration& aConfig)
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext,
|
||||
const Configuration& aConfig)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
, mConfig(aConfig)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
return mCameraControl->SetConfigurationImpl(mConfig);
|
||||
}
|
||||
|
||||
protected:
|
||||
Configuration mConfig;
|
||||
};
|
||||
|
||||
return Dispatch(new Message(this, CameraControlListener::kInSetConfiguration, aConfig));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::AutoFocus()
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
return mCameraControl->AutoFocusImpl();
|
||||
}
|
||||
};
|
||||
|
||||
return Dispatch(new Message(this, CameraControlListener::kInAutoFocus));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::StartFaceDetection()
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
return mCameraControl->StartFaceDetectionImpl();
|
||||
}
|
||||
};
|
||||
|
||||
return Dispatch(new Message(this, CameraControlListener::kInStartFaceDetection));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::StopFaceDetection()
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
return mCameraControl->StopFaceDetectionImpl();
|
||||
}
|
||||
};
|
||||
|
||||
return Dispatch(new Message(this, CameraControlListener::kInStopFaceDetection));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::TakePicture()
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
return mCameraControl->TakePictureImpl();
|
||||
}
|
||||
};
|
||||
|
||||
return Dispatch(new Message(this, CameraControlListener::kInTakePicture));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::StartRecording(DeviceStorageFileDescriptor* aFileDescriptor,
|
||||
const StartRecordingOptions* aOptions)
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext,
|
||||
const StartRecordingOptions* aOptions,
|
||||
DeviceStorageFileDescriptor* aFileDescriptor)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
, mOptionsPassed(false)
|
||||
, mFileDescriptor(aFileDescriptor)
|
||||
{
|
||||
if (aOptions) {
|
||||
mOptions = *aOptions;
|
||||
mOptionsPassed = true;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
return mCameraControl->StartRecordingImpl(mFileDescriptor,
|
||||
mOptionsPassed ? &mOptions : nullptr);
|
||||
}
|
||||
|
||||
protected:
|
||||
StartRecordingOptions mOptions;
|
||||
bool mOptionsPassed;
|
||||
RefPtr<DeviceStorageFileDescriptor> mFileDescriptor;
|
||||
};
|
||||
|
||||
if (!aFileDescriptor) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
return Dispatch(new Message(this, CameraControlListener::kInStartRecording,
|
||||
aOptions, aFileDescriptor));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::StopRecording()
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
return mCameraControl->StopRecordingImpl();
|
||||
}
|
||||
};
|
||||
|
||||
return Dispatch(new Message(this, CameraControlListener::kInStopRecording));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::PauseRecording()
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
return mCameraControl->PauseRecordingImpl();
|
||||
}
|
||||
};
|
||||
|
||||
return Dispatch(new Message(this, CameraControlListener::kInPauseRecording));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::ResumeRecording()
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
return mCameraControl->ResumeRecordingImpl();
|
||||
}
|
||||
};
|
||||
|
||||
return Dispatch(new Message(this, CameraControlListener::kInResumeRecording));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::StartPreview()
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
return mCameraControl->StartPreviewImpl();
|
||||
}
|
||||
};
|
||||
|
||||
return Dispatch(new Message(this, CameraControlListener::kInStartPreview));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::StopPreview()
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
return mCameraControl->StopPreviewImpl();
|
||||
}
|
||||
};
|
||||
|
||||
return Dispatch(new Message(this, CameraControlListener::kInStopPreview));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::ResumeContinuousFocus()
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
return mCameraControl->ResumeContinuousFocusImpl();
|
||||
}
|
||||
};
|
||||
|
||||
return Dispatch(new Message(this, CameraControlListener::kInResumeContinuousFocus));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::Stop()
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
return mCameraControl->StopImpl();
|
||||
}
|
||||
};
|
||||
|
||||
return Dispatch(new Message(this, CameraControlListener::kInStopCamera));
|
||||
}
|
||||
|
||||
class CameraControlImpl::ListenerMessage : public CameraControlImpl::ControlMessage
|
||||
{
|
||||
public:
|
||||
ListenerMessage(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener* aListener)
|
||||
: ControlMessage(aCameraControl, CameraControlListener::kInUnspecified)
|
||||
, mListener(aListener)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
RefPtr<CameraControlListener> mListener;
|
||||
};
|
||||
|
||||
void
|
||||
CameraControlImpl::AddListenerImpl(already_AddRefed<CameraControlListener> aListener)
|
||||
{
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
CameraControlListener* l = *mListeners.AppendElement() = aListener;
|
||||
DOM_CAMERA_LOGI("Added camera control listener %p\n", l);
|
||||
|
||||
// Update the newly-added listener's state
|
||||
l->OnConfigurationChange(mCurrentConfiguration);
|
||||
l->OnHardwareStateChange(mHardwareState, mHardwareStateChangeReason);
|
||||
l->OnPreviewStateChange(mPreviewState);
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::AddListener(CameraControlListener* aListener)
|
||||
{
|
||||
class Message : public ListenerMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener* aListener)
|
||||
: ListenerMessage(aCameraControl, aListener)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
mCameraControl->AddListenerImpl(mListener.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
if (aListener) {
|
||||
Dispatch(new Message(this, aListener));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::RemoveListenerImpl(CameraControlListener* aListener)
|
||||
{
|
||||
MutexAutoLock lock(mListenerLock);
|
||||
|
||||
RefPtr<CameraControlListener> l(aListener);
|
||||
mListeners.RemoveElement(l);
|
||||
DOM_CAMERA_LOGI("Removed camera control listener %p\n", l.get());
|
||||
// XXXmikeh - do we want to notify the listener that it has been removed?
|
||||
}
|
||||
|
||||
void
|
||||
CameraControlImpl::RemoveListener(CameraControlListener* aListener)
|
||||
{
|
||||
class Message : public ListenerMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl, CameraControlListener* aListener)
|
||||
: ListenerMessage(aCameraControl, aListener)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
mCameraControl->RemoveListenerImpl(mListener);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
if (aListener) {
|
||||
Dispatch(new Message(this, aListener));
|
||||
}
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DOM_CAMERA_CAMERACONTROLIMPL_H
|
||||
#define DOM_CAMERA_CAMERACONTROLIMPL_H
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsWeakPtr.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "ICameraControl.h"
|
||||
#include "CameraCommon.h"
|
||||
#include "DeviceStorage.h"
|
||||
#include "DeviceStorageFileDescriptor.h"
|
||||
#include "CameraControlListener.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class BlobImpl;
|
||||
} // namespace dom
|
||||
|
||||
namespace layers {
|
||||
class Image;
|
||||
} // namespace layers
|
||||
|
||||
class CameraControlImpl : public ICameraControl
|
||||
{
|
||||
public:
|
||||
explicit CameraControlImpl();
|
||||
virtual void AddListener(CameraControlListener* aListener) override;
|
||||
virtual void RemoveListener(CameraControlListener* aListener) override;
|
||||
|
||||
// See ICameraControl.h for these methods' return values.
|
||||
virtual nsresult Start(const Configuration* aConfig = nullptr) override;
|
||||
virtual nsresult Stop() override;
|
||||
virtual nsresult SetConfiguration(const Configuration& aConfig) override;
|
||||
virtual nsresult StartPreview() override;
|
||||
virtual nsresult StopPreview() override;
|
||||
virtual nsresult AutoFocus() override;
|
||||
virtual nsresult StartFaceDetection() override;
|
||||
virtual nsresult StopFaceDetection() override;
|
||||
virtual nsresult TakePicture() override;
|
||||
virtual nsresult StartRecording(DeviceStorageFileDescriptor* aFileDescriptor,
|
||||
const StartRecordingOptions* aOptions) override;
|
||||
virtual nsresult StopRecording() override;
|
||||
virtual nsresult PauseRecording() override;
|
||||
virtual nsresult ResumeRecording() override;
|
||||
virtual nsresult ResumeContinuousFocus() override;
|
||||
|
||||
// Event handlers called directly from outside this class.
|
||||
void OnShutter();
|
||||
void OnUserError(CameraControlListener::UserContext aContext, nsresult aError);
|
||||
void OnSystemError(CameraControlListener::SystemContext aContext, nsresult aError);
|
||||
void OnAutoFocusMoving(bool aIsMoving);
|
||||
|
||||
protected:
|
||||
// Event handlers.
|
||||
void OnAutoFocusComplete(bool aAutoFocusSucceeded);
|
||||
void OnFacesDetected(const nsTArray<Face>& aFaces);
|
||||
void OnTakePictureComplete(const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType);
|
||||
void OnPoster(dom::BlobImpl* aBlobImpl);
|
||||
|
||||
void OnRateLimitPreview(bool aLimit);
|
||||
bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight);
|
||||
void OnRecorderStateChange(CameraControlListener::RecorderState aState,
|
||||
int32_t aStatus = -1, int32_t aTrackNumber = -1);
|
||||
void OnPreviewStateChange(CameraControlListener::PreviewState aState);
|
||||
void OnHardwareStateChange(CameraControlListener::HardwareState aState,
|
||||
nsresult aReason);
|
||||
void OnConfigurationChange();
|
||||
|
||||
// When we create a new CameraThread, we keep a static reference to it so
|
||||
// that multiple CameraControl instances can find and reuse it; but we
|
||||
// don't want that reference to keep the thread object around unnecessarily,
|
||||
// so we make it a weak reference. The strong dynamic references will keep
|
||||
// the thread object alive as needed.
|
||||
static StaticRefPtr<nsIThread> sCameraThread;
|
||||
nsCOMPtr<nsIThread> mCameraThread;
|
||||
|
||||
virtual ~CameraControlImpl();
|
||||
|
||||
virtual void BeginBatchParameterSet() override { }
|
||||
virtual void EndBatchParameterSet() override { }
|
||||
|
||||
// Manage camera event listeners.
|
||||
void AddListenerImpl(already_AddRefed<CameraControlListener> aListener);
|
||||
void RemoveListenerImpl(CameraControlListener* aListener);
|
||||
nsTArray<RefPtr<CameraControlListener> > mListeners;
|
||||
mutable Mutex mListenerLock;
|
||||
|
||||
class ControlMessage;
|
||||
class ListenerMessage;
|
||||
|
||||
nsresult Dispatch(ControlMessage* aMessage);
|
||||
|
||||
// Asynchronous method implementations, invoked on the Camera Thread.
|
||||
//
|
||||
// Return values:
|
||||
// - NS_OK on success;
|
||||
// - NS_ERROR_INVALID_ARG if one or more arguments is invalid;
|
||||
// - NS_ERROR_NOT_INITIALIZED if the underlying hardware is not initialized,
|
||||
// failed to initialize (in the case of StartImpl()), or has been stopped;
|
||||
// for StartRecordingImpl(), this indicates that no recorder has been
|
||||
// configured (either by calling StartImpl() or SetConfigurationImpl());
|
||||
// - NS_ERROR_ALREADY_INITIALIZED if the underlying hardware is already
|
||||
// initialized;
|
||||
// - NS_ERROR_NOT_IMPLEMENTED if the method is not implemented;
|
||||
// - NS_ERROR_FAILURE on general failures.
|
||||
virtual nsresult StartImpl(const Configuration* aConfig = nullptr) = 0;
|
||||
virtual nsresult StopImpl() = 0;
|
||||
virtual nsresult SetConfigurationImpl(const Configuration& aConfig) = 0;
|
||||
virtual nsresult StartPreviewImpl() = 0;
|
||||
virtual nsresult StopPreviewImpl() = 0;
|
||||
virtual nsresult AutoFocusImpl() = 0;
|
||||
virtual nsresult StartFaceDetectionImpl() = 0;
|
||||
virtual nsresult StopFaceDetectionImpl() = 0;
|
||||
virtual nsresult TakePictureImpl() = 0;
|
||||
virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
|
||||
const StartRecordingOptions* aOptions) = 0;
|
||||
virtual nsresult StopRecordingImpl() = 0;
|
||||
virtual nsresult PauseRecordingImpl() = 0;
|
||||
virtual nsresult ResumeRecordingImpl() = 0;
|
||||
virtual nsresult ResumeContinuousFocusImpl() = 0;
|
||||
virtual nsresult PushParametersImpl() = 0;
|
||||
virtual nsresult PullParametersImpl() = 0;
|
||||
|
||||
void OnShutterInternal();
|
||||
void OnClosedInternal();
|
||||
|
||||
CameraControlListener::CameraListenerConfiguration mCurrentConfiguration;
|
||||
|
||||
CameraControlListener::PreviewState mPreviewState;
|
||||
CameraControlListener::HardwareState mHardwareState;
|
||||
nsresult mHardwareStateChangeReason;
|
||||
|
||||
private:
|
||||
CameraControlImpl(const CameraControlImpl&) = delete;
|
||||
CameraControlImpl& operator=(const CameraControlImpl&) = delete;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_CAMERA_CAMERACONTROLIMPL_H
|
|
@ -1,138 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DOM_CAMERA_CAMERACONTROLLISTENER_H
|
||||
#define DOM_CAMERA_CAMERACONTROLLISTENER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "ICameraControl.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class BlobImpl;
|
||||
} // namespace dom
|
||||
|
||||
namespace layers {
|
||||
class Image;
|
||||
} // namespace layers
|
||||
|
||||
class CameraControlListener
|
||||
{
|
||||
public:
|
||||
CameraControlListener()
|
||||
{
|
||||
MOZ_COUNT_CTOR(CameraControlListener);
|
||||
}
|
||||
|
||||
protected:
|
||||
// Protected destructor, to discourage deletion outside of Release():
|
||||
virtual ~CameraControlListener()
|
||||
{
|
||||
MOZ_COUNT_DTOR(CameraControlListener);
|
||||
}
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CameraControlListener);
|
||||
|
||||
enum HardwareState
|
||||
{
|
||||
kHardwareUninitialized,
|
||||
kHardwareClosed,
|
||||
kHardwareOpen,
|
||||
kHardwareOpenFailed
|
||||
};
|
||||
// aReason:
|
||||
// NS_OK : state change was expected and normal;
|
||||
// NS_ERROR_FAILURE : one or more system-level components failed and
|
||||
// the camera was closed;
|
||||
// NS_ERROR_NOT_AVAILABLE : the hardware is in use by another process
|
||||
// and cannot be acquired, or another process
|
||||
// was given access to the camera hardware.
|
||||
virtual void OnHardwareStateChange(HardwareState aState, nsresult aReason) { }
|
||||
|
||||
enum PreviewState
|
||||
{
|
||||
kPreviewStopped,
|
||||
kPreviewPaused,
|
||||
kPreviewStarted
|
||||
};
|
||||
virtual void OnPreviewStateChange(PreviewState aState) { }
|
||||
|
||||
enum RecorderState
|
||||
{
|
||||
kRecorderStopped,
|
||||
kRecorderStarted,
|
||||
kRecorderPaused,
|
||||
kRecorderResumed,
|
||||
kPosterCreated,
|
||||
kPosterFailed,
|
||||
#ifdef MOZ_B2G_CAMERA
|
||||
kFileSizeLimitReached,
|
||||
kVideoLengthLimitReached,
|
||||
kTrackCompleted,
|
||||
kTrackFailed,
|
||||
kMediaRecorderFailed,
|
||||
kMediaServerFailed
|
||||
#endif
|
||||
};
|
||||
enum { kNoTrackNumber = -1 };
|
||||
virtual void OnRecorderStateChange(RecorderState aState, int32_t aStatus, int32_t aTrackNum) { }
|
||||
|
||||
virtual void OnShutter() { }
|
||||
virtual void OnRateLimitPreview(bool aLimit) { }
|
||||
virtual bool OnNewPreviewFrame(layers::Image* aFrame, uint32_t aWidth, uint32_t aHeight)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
class CameraListenerConfiguration : public ICameraControl::Configuration
|
||||
{
|
||||
public:
|
||||
uint32_t mMaxMeteringAreas;
|
||||
uint32_t mMaxFocusAreas;
|
||||
};
|
||||
virtual void OnConfigurationChange(const CameraListenerConfiguration& aConfiguration) { }
|
||||
|
||||
virtual void OnAutoFocusComplete(bool aAutoFocusSucceeded) { }
|
||||
virtual void OnAutoFocusMoving(bool aIsMoving) { }
|
||||
virtual void OnTakePictureComplete(const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) { }
|
||||
virtual void OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces) { }
|
||||
virtual void OnPoster(dom::BlobImpl* aBlobImpl) { }
|
||||
|
||||
enum UserContext
|
||||
{
|
||||
kInStartCamera,
|
||||
kInStopCamera,
|
||||
kInAutoFocus,
|
||||
kInStartFaceDetection,
|
||||
kInStopFaceDetection,
|
||||
kInTakePicture,
|
||||
kInStartRecording,
|
||||
kInStopRecording,
|
||||
kInPauseRecording,
|
||||
kInResumeRecording,
|
||||
kInSetConfiguration,
|
||||
kInStartPreview,
|
||||
kInStopPreview,
|
||||
kInSetPictureSize,
|
||||
kInSetThumbnailSize,
|
||||
kInResumeContinuousFocus,
|
||||
kInUnspecified
|
||||
};
|
||||
// Error handler for problems arising due to user-initiated actions.
|
||||
virtual void OnUserError(UserContext aContext, nsresult aError) { }
|
||||
|
||||
enum SystemContext
|
||||
{
|
||||
kSystemService
|
||||
};
|
||||
// Error handler for problems arising due to system failures, not triggered
|
||||
// by something the CameraControl API user did.
|
||||
virtual void OnSystemError(SystemContext aContext, nsresult aError) { }
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_CAMERA_CAMERACONTROLLISTENER_H
|
|
@ -1,425 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CameraPreferences.h"
|
||||
#include "CameraCommon.h"
|
||||
#include "DOMCameraManager.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsIObserverService.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
/* statics */
|
||||
static StaticAutoPtr<Monitor> sPrefMonitor;
|
||||
|
||||
StaticAutoPtr<nsCString> CameraPreferences::sPrefTestEnabled;
|
||||
StaticAutoPtr<nsCString> CameraPreferences::sPrefHardwareTest;
|
||||
StaticAutoPtr<nsCString> CameraPreferences::sPrefGonkParameters;
|
||||
|
||||
nsresult CameraPreferences::sPrefCameraControlMethodErrorOverride = NS_OK;
|
||||
nsresult CameraPreferences::sPrefCameraControlAsyncErrorOverride = NS_OK;
|
||||
|
||||
uint32_t CameraPreferences::sPrefCameraControlLowMemoryThresholdMB = 0;
|
||||
|
||||
bool CameraPreferences::sPrefCameraParametersIsLowMemory = false;
|
||||
|
||||
bool CameraPreferences::sPrefCameraParametersPermission = false;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
StaticRefPtr<CameraPreferences> CameraPreferences::sObserver;
|
||||
|
||||
NS_IMPL_ISUPPORTS(CameraPreferences, nsIObserver);
|
||||
#endif
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
CameraPreferences::UpdatePref(const char* aPref, nsresult& aVal)
|
||||
{
|
||||
uint32_t val;
|
||||
nsresult rv = Preferences::GetUint(aPref, &val);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
aVal = static_cast<nsresult>(val);
|
||||
} else if(rv == NS_ERROR_UNEXPECTED) {
|
||||
// Preference does not exist
|
||||
rv = NS_OK;
|
||||
aVal = NS_OK;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
CameraPreferences::UpdatePref(const char* aPref, uint32_t& aVal)
|
||||
{
|
||||
uint32_t val;
|
||||
nsresult rv = Preferences::GetUint(aPref, &val);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
aVal = val;
|
||||
} else if(rv == NS_ERROR_UNEXPECTED) {
|
||||
// Preference does not exist
|
||||
rv = NS_OK;
|
||||
aVal = 0;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
CameraPreferences::UpdatePref(const char* aPref, nsACString& aVal)
|
||||
{
|
||||
nsCString val;
|
||||
nsresult rv = Preferences::GetCString(aPref, &val);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
aVal = val;
|
||||
} else if(rv == NS_ERROR_UNEXPECTED) {
|
||||
// Preference does not exist
|
||||
rv = NS_OK;
|
||||
aVal.Truncate();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
CameraPreferences::UpdatePref(const char* aPref, bool& aVal)
|
||||
{
|
||||
bool val;
|
||||
nsresult rv = Preferences::GetBool(aPref, &val);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
aVal = val;
|
||||
} else if(rv == NS_ERROR_UNEXPECTED) {
|
||||
// Preference does not exist
|
||||
rv = NS_OK;
|
||||
aVal = false;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* static */
|
||||
CameraPreferences::Pref CameraPreferences::sPrefs[] = {
|
||||
{
|
||||
"camera.control.test.enabled",
|
||||
kPrefValueIsCString,
|
||||
{ &sPrefTestEnabled }
|
||||
},
|
||||
{
|
||||
"camera.control.test.hardware",
|
||||
kPrefValueIsCString,
|
||||
{ &sPrefHardwareTest }
|
||||
},
|
||||
{
|
||||
"camera.control.test.permission",
|
||||
kPrefValueIsBoolean,
|
||||
{ &sPrefCameraParametersPermission }
|
||||
},
|
||||
#ifdef MOZ_B2G
|
||||
{
|
||||
"camera.control.test.hardware.gonk.parameters",
|
||||
kPrefValueIsCString,
|
||||
{ &sPrefGonkParameters }
|
||||
},
|
||||
#endif
|
||||
{
|
||||
"camera.control.test.method.error",
|
||||
kPrefValueIsNsResult,
|
||||
{ &sPrefCameraControlMethodErrorOverride }
|
||||
},
|
||||
{
|
||||
"camera.control.test.async.error",
|
||||
kPrefValueIsNsResult,
|
||||
{ &sPrefCameraControlAsyncErrorOverride }
|
||||
},
|
||||
{
|
||||
"camera.control.test.is_low_memory",
|
||||
kPrefValueIsBoolean,
|
||||
{ &sPrefCameraParametersIsLowMemory }
|
||||
},
|
||||
{
|
||||
"camera.control.low_memory_thresholdMB",
|
||||
kPrefValueIsUint32,
|
||||
{ &sPrefCameraControlLowMemoryThresholdMB }
|
||||
},
|
||||
};
|
||||
|
||||
/* static */
|
||||
uint32_t
|
||||
CameraPreferences::PrefToIndex(const char* aPref)
|
||||
{
|
||||
for (uint32_t i = 0; i < ArrayLength(sPrefs); ++i) {
|
||||
if (strcmp(aPref, sPrefs[i].mPref) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return kPrefNotFound;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
CameraPreferences::PreferenceChanged(const char* aPref, void* aClosure)
|
||||
{
|
||||
MonitorAutoLock mon(*sPrefMonitor);
|
||||
|
||||
uint32_t i = PrefToIndex(aPref);
|
||||
if (i == kPrefNotFound) {
|
||||
DOM_CAMERA_LOGE("Preference '%s' is not tracked by CameraPreferences\n", aPref);
|
||||
return;
|
||||
}
|
||||
|
||||
Pref& p = sPrefs[i];
|
||||
nsresult rv;
|
||||
switch (p.mValueType) {
|
||||
case kPrefValueIsNsResult:
|
||||
{
|
||||
nsresult& v = *p.mValue.mAsNsResult;
|
||||
rv = UpdatePref(aPref, v);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
DOM_CAMERA_LOGI("Preference '%s' has changed, 0x%x\n", aPref, v);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kPrefValueIsUint32:
|
||||
{
|
||||
uint32_t& v = *p.mValue.mAsUint32;
|
||||
rv = UpdatePref(aPref, v);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
DOM_CAMERA_LOGI("Preference '%s' has changed, %u\n", aPref, v);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kPrefValueIsCString:
|
||||
{
|
||||
nsCString& v = **p.mValue.mAsCString;
|
||||
rv = UpdatePref(aPref, v);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
DOM_CAMERA_LOGI("Preference '%s' has changed, '%s'\n", aPref, v.get());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kPrefValueIsBoolean:
|
||||
{
|
||||
bool& v = *p.mValue.mAsBoolean;
|
||||
rv = UpdatePref(aPref, v);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
DOM_CAMERA_LOGI("Preference '%s' has changed, %s\n",
|
||||
aPref, v ? "true" : "false");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unhandled preference value type!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Failed to get pref '%s' (0x%x)\n", aPref, rv);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
CameraPreferences::Initialize()
|
||||
{
|
||||
DOM_CAMERA_LOGI("Initializing camera preference callbacks\n");
|
||||
|
||||
nsresult rv;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (obs) {
|
||||
sObserver = new CameraPreferences();
|
||||
rv = obs->AddObserver(sObserver, "init-camera-hw", false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
sObserver = nullptr;
|
||||
}
|
||||
} else {
|
||||
DOM_CAMERA_LOGE("Could not get observer service\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
sPrefMonitor = new Monitor("CameraPreferences.sPrefMonitor");
|
||||
|
||||
sPrefTestEnabled = new nsCString();
|
||||
sPrefHardwareTest = new nsCString();
|
||||
sPrefGonkParameters = new nsCString();
|
||||
|
||||
for (uint32_t i = 0; i < ArrayLength(sPrefs); ++i) {
|
||||
rv = Preferences::RegisterCallbackAndCall(CameraPreferences::PreferenceChanged,
|
||||
sPrefs[i].mPref);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DOM_CAMERA_LOGI("Camera preferences initialized\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
CameraPreferences::Shutdown()
|
||||
{
|
||||
DOM_CAMERA_LOGI("Shutting down camera preference callbacks\n");
|
||||
|
||||
for (uint32_t i = 0; i < ArrayLength(sPrefs); ++i) {
|
||||
Preferences::UnregisterCallback(CameraPreferences::PreferenceChanged,
|
||||
sPrefs[i].mPref);
|
||||
}
|
||||
|
||||
sPrefTestEnabled = nullptr;
|
||||
sPrefHardwareTest = nullptr;
|
||||
sPrefGonkParameters = nullptr;
|
||||
sPrefMonitor = nullptr;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (obs) {
|
||||
nsresult rv = obs->RemoveObserver(sObserver , "init-camera-hw");
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Failed to remove CameraPreferences observer (0x%x)\n", rv);
|
||||
}
|
||||
sObserver = nullptr;
|
||||
} else {
|
||||
DOM_CAMERA_LOGE("Could not get observer service\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
DOM_CAMERA_LOGI("Camera preferences shut down\n");
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsresult
|
||||
CameraPreferences::PreinitCameraHardware()
|
||||
{
|
||||
nsDOMCameraManager::PreinitCameraHardware();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CameraPreferences::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
|
||||
{
|
||||
if (strcmp(aTopic, "init-camera-hw") == 0) {
|
||||
return PreinitCameraHardware();
|
||||
}
|
||||
|
||||
DOM_CAMERA_LOGE("Got unhandled topic '%s'\n", aTopic);
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* static */
|
||||
bool
|
||||
CameraPreferences::GetPref(const char* aPref, nsACString& aVal)
|
||||
{
|
||||
MOZ_ASSERT(sPrefMonitor, "sPrefMonitor missing in CameraPreferences::GetPref()");
|
||||
MonitorAutoLock mon(*sPrefMonitor);
|
||||
|
||||
uint32_t i = PrefToIndex(aPref);
|
||||
if (i == kPrefNotFound || i >= ArrayLength(sPrefs)) {
|
||||
DOM_CAMERA_LOGW("Preference '%s' is not tracked by CameraPreferences\n", aPref);
|
||||
return false;
|
||||
}
|
||||
if (sPrefs[i].mValueType != kPrefValueIsCString) {
|
||||
DOM_CAMERA_LOGW("Preference '%s' is not a string type\n", aPref);
|
||||
return false;
|
||||
}
|
||||
|
||||
StaticAutoPtr<nsCString>* s = sPrefs[i].mValue.mAsCString;
|
||||
if (!*s) {
|
||||
DOM_CAMERA_LOGE("Preference '%s' cache is not initialized\n", aPref);
|
||||
return false;
|
||||
}
|
||||
if ((*s)->IsEmpty()) {
|
||||
DOM_CAMERA_LOGI("Preference '%s' is not set\n", aPref);
|
||||
return false;
|
||||
}
|
||||
|
||||
DOM_CAMERA_LOGI("Preference '%s', got '%s'\n", aPref, (*s)->get());
|
||||
aVal = **s;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
CameraPreferences::GetPref(const char* aPref, nsresult& aVal)
|
||||
{
|
||||
MOZ_ASSERT(sPrefMonitor, "sPrefMonitor missing in CameraPreferences::GetPref()");
|
||||
MonitorAutoLock mon(*sPrefMonitor);
|
||||
|
||||
uint32_t i = PrefToIndex(aPref);
|
||||
if (i == kPrefNotFound || i >= ArrayLength(sPrefs)) {
|
||||
DOM_CAMERA_LOGW("Preference '%s' is not tracked by CameraPreferences\n", aPref);
|
||||
return false;
|
||||
}
|
||||
if (sPrefs[i].mValueType != kPrefValueIsNsResult) {
|
||||
DOM_CAMERA_LOGW("Preference '%s' is not an nsresult type\n", aPref);
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult v = *sPrefs[i].mValue.mAsNsResult;
|
||||
if (v == NS_OK) {
|
||||
DOM_CAMERA_LOGW("Preference '%s' is not set\n", aPref);
|
||||
return false;
|
||||
}
|
||||
|
||||
DOM_CAMERA_LOGI("Preference '%s', got 0x%x\n", aPref, v);
|
||||
aVal = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
CameraPreferences::GetPref(const char* aPref, uint32_t& aVal)
|
||||
{
|
||||
MOZ_ASSERT(sPrefMonitor, "sPrefMonitor missing in CameraPreferences::GetPref()");
|
||||
MonitorAutoLock mon(*sPrefMonitor);
|
||||
|
||||
uint32_t i = PrefToIndex(aPref);
|
||||
if (i == kPrefNotFound || i >= ArrayLength(sPrefs)) {
|
||||
DOM_CAMERA_LOGW("Preference '%s' is not tracked by CameraPreferences\n", aPref);
|
||||
return false;
|
||||
}
|
||||
if (sPrefs[i].mValueType != kPrefValueIsUint32) {
|
||||
DOM_CAMERA_LOGW("Preference '%s' is not a uint32_t type\n", aPref);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t v = *sPrefs[i].mValue.mAsUint32;
|
||||
DOM_CAMERA_LOGI("Preference '%s', got %u\n", aPref, v);
|
||||
aVal = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
CameraPreferences::GetPref(const char* aPref, bool& aVal)
|
||||
{
|
||||
MOZ_ASSERT(sPrefMonitor, "sPrefMonitor missing in CameraPreferences::GetPref()");
|
||||
MonitorAutoLock mon(*sPrefMonitor);
|
||||
|
||||
uint32_t i = PrefToIndex(aPref);
|
||||
if (i == kPrefNotFound || i >= ArrayLength(sPrefs)) {
|
||||
DOM_CAMERA_LOGW("Preference '%s' is not tracked by CameraPreferences\n", aPref);
|
||||
return false;
|
||||
}
|
||||
if (sPrefs[i].mValueType != kPrefValueIsBoolean) {
|
||||
DOM_CAMERA_LOGW("Preference '%s' is not a boolean type\n", aPref);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool v = *sPrefs[i].mValue.mAsBoolean;
|
||||
DOM_CAMERA_LOGI("Preference '%s', got %s\n", aPref, v ? "true" : "false");
|
||||
aVal = v;
|
||||
return true;
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DOM_CAMERA_CAMERAPREFERENCES_H
|
||||
#define DOM_CAMERA_CAMERAPREFERENCES_H
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsIObserver.h"
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
template<class T> class StaticAutoPtr;
|
||||
|
||||
class CameraPreferences
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
: public nsIObserver
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
#endif
|
||||
|
||||
static bool Initialize();
|
||||
static void Shutdown();
|
||||
|
||||
static bool GetPref(const char* aPref, nsACString& aVal);
|
||||
static bool GetPref(const char* aPref, nsresult& aVal);
|
||||
static bool GetPref(const char* aPref, uint32_t& aVal);
|
||||
static bool GetPref(const char* aPref, bool& aVal);
|
||||
|
||||
protected:
|
||||
static const uint32_t kPrefNotFound = UINT32_MAX;
|
||||
static uint32_t PrefToIndex(const char* aPref);
|
||||
|
||||
static void PreferenceChanged(const char* aPref, void* aClosure);
|
||||
static nsresult UpdatePref(const char* aPref, nsresult& aVar);
|
||||
static nsresult UpdatePref(const char* aPref, uint32_t& aVar);
|
||||
static nsresult UpdatePref(const char* aPref, nsACString& aVar);
|
||||
static nsresult UpdatePref(const char* aPref, bool& aVar);
|
||||
|
||||
enum PrefValueType {
|
||||
kPrefValueIsNsResult,
|
||||
kPrefValueIsUint32,
|
||||
kPrefValueIsCString,
|
||||
kPrefValueIsBoolean
|
||||
};
|
||||
struct Pref {
|
||||
const char* const mPref;
|
||||
PrefValueType mValueType;
|
||||
union {
|
||||
// The 'mAsVoid' member must be first and is required to allow 'mValue'
|
||||
// to be initialized with any pointer type, as not all of our platforms
|
||||
// support the use of designated initializers; in their absence, only
|
||||
// the first element of a union can be statically initialized, and
|
||||
// 'void*' lets us stuff any pointer type into it.
|
||||
void* mAsVoid;
|
||||
StaticAutoPtr<nsCString>* mAsCString;
|
||||
nsresult* mAsNsResult;
|
||||
uint32_t* mAsUint32;
|
||||
bool* mAsBoolean;
|
||||
} mValue;
|
||||
};
|
||||
static Pref sPrefs[];
|
||||
|
||||
static StaticAutoPtr<nsCString> sPrefTestEnabled;
|
||||
static StaticAutoPtr<nsCString> sPrefHardwareTest;
|
||||
static StaticAutoPtr<nsCString> sPrefGonkParameters;
|
||||
|
||||
static nsresult sPrefCameraControlMethodErrorOverride;
|
||||
static nsresult sPrefCameraControlAsyncErrorOverride;
|
||||
|
||||
static uint32_t sPrefCameraControlLowMemoryThresholdMB;
|
||||
|
||||
static bool sPrefCameraParametersIsLowMemory;
|
||||
|
||||
static bool sPrefCameraParametersPermission;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
static StaticRefPtr<CameraPreferences> sObserver;
|
||||
|
||||
nsresult PreinitCameraHardware();
|
||||
|
||||
protected:
|
||||
// Objects may be instantiated for use as observers.
|
||||
CameraPreferences() { }
|
||||
virtual ~CameraPreferences() { }
|
||||
#else
|
||||
private:
|
||||
// Static class only.
|
||||
CameraPreferences();
|
||||
~CameraPreferences();
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_CAMERA_CAMERAPREFERENCES_H
|
|
@ -1,200 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CameraPreviewMediaStream.h"
|
||||
#include "CameraCommon.h"
|
||||
#include "MediaStreamListener.h"
|
||||
#include "VideoFrameContainer.h"
|
||||
|
||||
/**
|
||||
* Maximum number of outstanding invalidates before we start to drop frames;
|
||||
* if we hit this threshold, it is an indicator that the main thread is
|
||||
* either very busy or the device is busy elsewhere (e.g. encoding or
|
||||
* persisting video data).
|
||||
*/
|
||||
#define MAX_INVALIDATE_PENDING 4
|
||||
|
||||
using namespace mozilla::layers;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
static const TrackID TRACK_VIDEO = 2;
|
||||
|
||||
void
|
||||
FakeMediaStreamGraph::DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed<nsIRunnable> aRunnable)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> task = aRunnable;
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
CameraPreviewMediaStream::CameraPreviewMediaStream()
|
||||
: ProcessedMediaStream()
|
||||
, mMutex("mozilla::camera::CameraPreviewMediaStream")
|
||||
, mInvalidatePending(0)
|
||||
, mDiscardedFrames(0)
|
||||
, mRateLimit(false)
|
||||
, mTrackCreated(false)
|
||||
{
|
||||
SetGraphImpl(
|
||||
MediaStreamGraph::GetInstance(
|
||||
MediaStreamGraph::SYSTEM_THREAD_DRIVER, AudioChannel::Normal));
|
||||
mFakeMediaStreamGraph = new FakeMediaStreamGraph();
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::AddAudioOutput(void* aKey)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::SetAudioOutputVolume(void* aKey, float aVolume)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::RemoveAudioOutput(void* aKey)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::AddVideoOutput(MediaStreamVideoSink* aSink, TrackID aID)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
RefPtr<MediaStreamVideoSink> sink = aSink;
|
||||
AddVideoOutputImpl(sink.forget(), aID);
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::RemoveVideoOutput(MediaStreamVideoSink* aSink, TrackID aID)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
RemoveVideoOutputImpl(aSink, aID);
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::AddListener(MediaStreamListener* aListener)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
MediaStreamListener* listener = *mListeners.AppendElement() = aListener;
|
||||
listener->NotifyBlockingChanged(mFakeMediaStreamGraph, MediaStreamListener::UNBLOCKED);
|
||||
listener->NotifyHasCurrentData(mFakeMediaStreamGraph);
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::RemoveListener(MediaStreamListener* aListener)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
RefPtr<MediaStreamListener> listener(aListener);
|
||||
mListeners.RemoveElement(aListener);
|
||||
listener->NotifyEvent(mFakeMediaStreamGraph, MediaStreamGraphEvent::EVENT_REMOVED);
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::OnPreviewStateChange(bool aActive)
|
||||
{
|
||||
if (aActive) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (!mTrackCreated) {
|
||||
mTrackCreated = true;
|
||||
VideoSegment tmpSegment;
|
||||
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
|
||||
MediaStreamListener* l = mListeners[j];
|
||||
l->NotifyQueuedTrackChanges(mFakeMediaStreamGraph, TRACK_VIDEO, 0,
|
||||
TrackEventCommand::TRACK_EVENT_CREATED,
|
||||
tmpSegment);
|
||||
l->NotifyFinishedTrackCreation(mFakeMediaStreamGraph);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::Destroy()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mMainThreadDestroyed = true;
|
||||
DestroyImpl();
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::Invalidate()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
--mInvalidatePending;
|
||||
for (const TrackBound<MediaStreamVideoSink>& sink : mVideoOutputs) {
|
||||
VideoFrameContainer* output = sink.mListener->AsVideoFrameContainer();
|
||||
if (!output) {
|
||||
continue;
|
||||
}
|
||||
output->Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::ProcessInput(GraphTime aFrom, GraphTime aTo,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::RateLimit(bool aLimit)
|
||||
{
|
||||
mRateLimit = aLimit;
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::SetCurrentFrame(const gfx::IntSize& aIntrinsicSize, Image* aImage)
|
||||
{
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (mInvalidatePending > 0) {
|
||||
if (mRateLimit || mInvalidatePending > MAX_INVALIDATE_PENDING) {
|
||||
++mDiscardedFrames;
|
||||
DOM_CAMERA_LOGW("Discard preview frame %d, %d invalidation(s) pending",
|
||||
mDiscardedFrames, mInvalidatePending);
|
||||
return;
|
||||
}
|
||||
|
||||
DOM_CAMERA_LOGI("Update preview frame, %d invalidation(s) pending",
|
||||
mInvalidatePending);
|
||||
}
|
||||
mDiscardedFrames = 0;
|
||||
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
for (const TrackBound<MediaStreamVideoSink>& sink : mVideoOutputs) {
|
||||
VideoFrameContainer* output = sink.mListener->AsVideoFrameContainer();
|
||||
if (!output) {
|
||||
continue;
|
||||
}
|
||||
output->SetCurrentFrame(aIntrinsicSize, aImage, now);
|
||||
}
|
||||
|
||||
++mInvalidatePending;
|
||||
}
|
||||
|
||||
NS_DispatchToMainThread(NewRunnableMethod(this, &CameraPreviewMediaStream::Invalidate));
|
||||
}
|
||||
|
||||
void
|
||||
CameraPreviewMediaStream::ClearCurrentFrame()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
for (const TrackBound<MediaStreamVideoSink>& sink : mVideoOutputs) {
|
||||
VideoFrameContainer* output = sink.mListener->AsVideoFrameContainer();
|
||||
if (!output) {
|
||||
continue;
|
||||
}
|
||||
output->ClearCurrentFrame();
|
||||
NS_DispatchToMainThread(NewRunnableMethod(output, &VideoFrameContainer::Invalidate));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,81 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DOM_CAMERA_CAMERAPREVIEWMEDIASTREAM_H
|
||||
#define DOM_CAMERA_CAMERAPREVIEWMEDIASTREAM_H
|
||||
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MediaStreamVideoSink;
|
||||
|
||||
class FakeMediaStreamGraph : public MediaStreamGraph
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FakeMediaStreamGraph)
|
||||
public:
|
||||
FakeMediaStreamGraph()
|
||||
: MediaStreamGraph(16000)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void
|
||||
DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed<nsIRunnable> aRunnable) override;
|
||||
|
||||
protected:
|
||||
~FakeMediaStreamGraph()
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a stream for camera preview.
|
||||
*
|
||||
* XXX It is a temporary fix of SourceMediaStream.
|
||||
* A camera preview requests no delay and no buffering stream,
|
||||
* but the SourceMediaStream does not support it.
|
||||
*/
|
||||
class CameraPreviewMediaStream : public ProcessedMediaStream
|
||||
{
|
||||
typedef mozilla::layers::Image Image;
|
||||
|
||||
public:
|
||||
CameraPreviewMediaStream();
|
||||
|
||||
void AddAudioOutput(void* aKey) override;
|
||||
void SetAudioOutputVolume(void* aKey, float aVolume) override;
|
||||
void RemoveAudioOutput(void* aKey) override;
|
||||
void AddVideoOutput(MediaStreamVideoSink* aSink, TrackID aID) override;
|
||||
void RemoveVideoOutput(MediaStreamVideoSink* aSink, TrackID aID) override;
|
||||
void Suspend() override {}
|
||||
void Resume() override {}
|
||||
void AddListener(MediaStreamListener* aListener) override;
|
||||
void RemoveListener(MediaStreamListener* aListener) override;
|
||||
void Destroy() override;
|
||||
void OnPreviewStateChange(bool aActive);
|
||||
|
||||
void Invalidate();
|
||||
|
||||
void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override;
|
||||
|
||||
// Call these on any thread.
|
||||
void SetCurrentFrame(const gfx::IntSize& aIntrinsicSize, Image* aImage);
|
||||
void ClearCurrentFrame();
|
||||
void RateLimit(bool aLimit);
|
||||
|
||||
protected:
|
||||
// mMutex protects all the class' fields.
|
||||
// This class is not registered to MediaStreamGraph.
|
||||
// It needs to protect all the fields.
|
||||
Mutex mMutex;
|
||||
int32_t mInvalidatePending;
|
||||
uint32_t mDiscardedFrames;
|
||||
bool mRateLimit;
|
||||
bool mTrackCreated;
|
||||
RefPtr<FakeMediaStreamGraph> mFakeMediaStreamGraph;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_CAMERA_CAMERAPREVIEWMEDIASTREAM_H
|
|
@ -1,220 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
||||
|
||||
const MOZ_CAMERATESTHW_CONTRACTID = "@mozilla.org/cameratesthardware;1";
|
||||
const MOZ_CAMERATESTHW_CID = Components.ID("{fcb7b4cd-689e-453c-8a2c-611a45fa09ac}");
|
||||
const DEBUG = false;
|
||||
|
||||
function debug(msg) {
|
||||
if (DEBUG) {
|
||||
dump('-*- MozCameraTestHardware: ' + msg + '\n');
|
||||
}
|
||||
}
|
||||
|
||||
function MozCameraTestHardware() {
|
||||
this._params = {};
|
||||
}
|
||||
|
||||
MozCameraTestHardware.prototype = {
|
||||
classID: MOZ_CAMERATESTHW_CID,
|
||||
contractID: MOZ_CAMERATESTHW_CONTRACTID,
|
||||
|
||||
classInfo: XPCOMUtils.generateCI({classID: MOZ_CAMERATESTHW_CID,
|
||||
contractID: MOZ_CAMERATESTHW_CONTRACTID,
|
||||
flags: Ci.nsIClassInfo.SINGLETON,
|
||||
interfaces: [Ci.nsICameraTestHardware]}),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICameraTestHardware]),
|
||||
|
||||
_params: null,
|
||||
_window: null,
|
||||
_mock: null,
|
||||
_handler: null,
|
||||
|
||||
attach: function(mock) {
|
||||
/* Waive xrays permits us to call functions provided to us
|
||||
in the mock */
|
||||
this._mock = Components.utils.waiveXrays(mock);
|
||||
},
|
||||
|
||||
detach: function() {
|
||||
this._mock = null;
|
||||
},
|
||||
|
||||
/* Trigger a delegate handler attached to the test hardware
|
||||
if given via attach. If there is no delegate attached, or
|
||||
it does not provide a handler for this specific operation,
|
||||
or the handler returns true, it will execute the default
|
||||
behaviour. The handler may throw an exception in order to
|
||||
return an error code from the driver call. */
|
||||
_delegate: function(prop) {
|
||||
return (this._mock && this._mock[prop] && !this._mock[prop]());
|
||||
},
|
||||
|
||||
get params() {
|
||||
return this._params;
|
||||
},
|
||||
|
||||
set params(aParams) {
|
||||
this._params = aParams;
|
||||
},
|
||||
|
||||
setHandler: function(handler) {
|
||||
this._handler = handler;
|
||||
},
|
||||
|
||||
dispatchEvent: function(evt) {
|
||||
var self = this;
|
||||
/* We should not dispatch the event in the current thread
|
||||
context because it may be directly from a driver call
|
||||
and we could hit a deadlock situation. */
|
||||
this._window.setTimeout(function() {
|
||||
if (self._handler) {
|
||||
self._handler.handleEvent(evt);
|
||||
}
|
||||
}, 0);
|
||||
},
|
||||
|
||||
reset: function(aWindow) {
|
||||
this._window = aWindow;
|
||||
this._mock = null;
|
||||
this._params = {};
|
||||
},
|
||||
|
||||
initCamera: function() {
|
||||
this._delegate('init');
|
||||
},
|
||||
|
||||
pushParameters: function(params) {
|
||||
let oldParams = this._params;
|
||||
this._params = {};
|
||||
let s = params.split(';');
|
||||
for(let i = 0; i < s.length; ++i) {
|
||||
let parts = s[i].split('=');
|
||||
if (parts.length == 2) {
|
||||
this._params[parts[0]] = parts[1];
|
||||
}
|
||||
}
|
||||
try {
|
||||
this._delegate('pushParameters');
|
||||
} catch(e) {
|
||||
this._params = oldParams;
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
|
||||
pullParameters: function() {
|
||||
this._delegate('pullParameters');
|
||||
let ret = "";
|
||||
for(let p in this._params) {
|
||||
ret += p + "=" + this._params[p] + ";";
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
autoFocus: function() {
|
||||
if (!this._delegate('autoFocus')) {
|
||||
this.fireAutoFocusComplete(true);
|
||||
}
|
||||
},
|
||||
|
||||
fireAutoFocusMoving: function(moving) {
|
||||
let evt = new this._window.CameraStateChangeEvent('focus', { 'newState': moving ? 'focusing' : 'not_focusing' } );
|
||||
this.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
fireAutoFocusComplete: function(state) {
|
||||
let evt = new this._window.CameraStateChangeEvent('focus', { 'newState': state ? 'focused' : 'unfocused' } );
|
||||
this.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
cancelAutoFocus: function() {
|
||||
this._delegate('cancelAutoFocus');
|
||||
},
|
||||
|
||||
fireShutter: function() {
|
||||
let evt = new this._window.Event('shutter');
|
||||
this.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
takePicture: function() {
|
||||
if (!this._delegate('takePicture')) {
|
||||
this.fireTakePictureComplete(new this._window.Blob(['foobar'], {'type': 'jpeg'}));
|
||||
}
|
||||
},
|
||||
|
||||
fireTakePictureComplete: function(blob) {
|
||||
let evt = new this._window.BlobEvent('picture', {'data': blob});
|
||||
this.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
fireTakePictureError: function() {
|
||||
let evt = new this._window.ErrorEvent('error', {'message': 'picture'});
|
||||
this.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
cancelTakePicture: function() {
|
||||
this._delegate('cancelTakePicture');
|
||||
},
|
||||
|
||||
startPreview: function() {
|
||||
this._delegate('startPreview');
|
||||
},
|
||||
|
||||
stopPreview: function() {
|
||||
this._delegate('stopPreview');
|
||||
},
|
||||
|
||||
startFaceDetection: function() {
|
||||
this._delegate('startFaceDetection');
|
||||
},
|
||||
|
||||
stopFaceDetection: function() {
|
||||
this._delegate('stopFaceDetection');
|
||||
},
|
||||
|
||||
fireFacesDetected: function(faces) {
|
||||
/* This works around the fact that we can't have references to
|
||||
dictionaries in a dictionary in WebIDL; we provide a boolean
|
||||
to indicate whether or not the values for those features are
|
||||
actually valid. */
|
||||
let facesIf = [];
|
||||
if (typeof(faces) === 'object' && typeof(faces.faces) === 'object') {
|
||||
let self = this;
|
||||
faces.faces.forEach(function(face) {
|
||||
face.hasLeftEye = face.hasOwnProperty('leftEye') && face.leftEye != null;
|
||||
face.hasRightEye = face.hasOwnProperty('rightEye') && face.rightEye != null;
|
||||
face.hasMouth = face.hasOwnProperty('mouth') && face.mouth != null;
|
||||
facesIf.push(new self._window.CameraDetectedFace(face));
|
||||
});
|
||||
}
|
||||
|
||||
let evt = new this._window.CameraFacesDetectedEvent('facesdetected', {'faces': facesIf});
|
||||
this.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
startRecording: function() {
|
||||
this._delegate('startRecording');
|
||||
},
|
||||
|
||||
stopRecording: function() {
|
||||
this._delegate('stopRecording');
|
||||
},
|
||||
|
||||
fireSystemError: function() {
|
||||
let evt = new this._window.ErrorEvent('error', {'message': 'system'});
|
||||
this.dispatchEvent(evt);
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MozCameraTestHardware]);
|
|
@ -1,2 +0,0 @@
|
|||
component {fcb7b4cd-689e-453c-8a2c-611a45fa09ac} CameraTestHardware.js
|
||||
contract @mozilla.org/cameratesthardware;1 {fcb7b4cd-689e-453c-8a2c-611a45fa09ac}
|
|
@ -1,604 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DOMCameraCapabilities.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "mozilla/dom/CameraManagerBinding.h"
|
||||
#include "mozilla/dom/CameraCapabilitiesBinding.h"
|
||||
#include "Navigator.h"
|
||||
#include "CameraCommon.h"
|
||||
#include "ICameraControl.h"
|
||||
#include "CameraControlListener.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/**
|
||||
* CameraClosedListenerProxy and CameraClosedMessage
|
||||
*/
|
||||
template<class T>
|
||||
class CameraClosedMessage : public Runnable
|
||||
{
|
||||
public:
|
||||
explicit CameraClosedMessage(nsMainThreadPtrHandle<T> aListener)
|
||||
: mListener(aListener)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
RefPtr<T> listener = mListener.get();
|
||||
if (listener) {
|
||||
listener->OnHardwareClosed();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~CameraClosedMessage()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
}
|
||||
|
||||
nsMainThreadPtrHandle<T> mListener;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class CameraClosedListenerProxy : public CameraControlListener
|
||||
{
|
||||
public:
|
||||
explicit CameraClosedListenerProxy(T* aListener)
|
||||
: mListener(new nsMainThreadPtrHolder<T>(aListener))
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
}
|
||||
|
||||
virtual void
|
||||
OnHardwareStateChange(HardwareState aState, nsresult aReason) override
|
||||
{
|
||||
if (aState != kHardwareClosed) {
|
||||
return;
|
||||
}
|
||||
NS_DispatchToMainThread(new CameraClosedMessage<T>(mListener));
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~CameraClosedListenerProxy()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
}
|
||||
|
||||
nsMainThreadPtrHandle<T> mListener;
|
||||
};
|
||||
|
||||
/**
|
||||
* CameraRecorderVideoProfile
|
||||
*/
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderVideoProfile, mParent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderVideoProfile)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderVideoProfile)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderVideoProfile)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
JSObject*
|
||||
CameraRecorderVideoProfile::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return CameraRecorderVideoProfileBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
CameraRecorderVideoProfile::CameraRecorderVideoProfile(nsISupports* aParent,
|
||||
const ICameraControl::RecorderProfile::Video& aProfile)
|
||||
: mParent(aParent)
|
||||
, mCodec(aProfile.GetCodec())
|
||||
, mBitrate(aProfile.GetBitsPerSecond())
|
||||
, mFramerate(aProfile.GetFramesPerSecond())
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
|
||||
mSize.mWidth = aProfile.GetSize().width;
|
||||
mSize.mHeight = aProfile.GetSize().height;
|
||||
|
||||
DOM_CAMERA_LOGI(" video: '%s' %ux%u bps=%u fps=%u\n",
|
||||
NS_ConvertUTF16toUTF8(mCodec).get(), mSize.mWidth, mSize.mHeight, mBitrate, mFramerate);
|
||||
}
|
||||
|
||||
CameraRecorderVideoProfile::~CameraRecorderVideoProfile()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* CameraRecorderAudioProfile
|
||||
*/
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderAudioProfile, mParent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderAudioProfile)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderAudioProfile)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderAudioProfile)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
JSObject*
|
||||
CameraRecorderAudioProfile::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return CameraRecorderAudioProfileBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
CameraRecorderAudioProfile::CameraRecorderAudioProfile(nsISupports* aParent,
|
||||
const ICameraControl::RecorderProfile::Audio& aProfile)
|
||||
: mParent(aParent)
|
||||
, mCodec(aProfile.GetCodec())
|
||||
, mBitrate(aProfile.GetBitsPerSecond())
|
||||
, mSamplerate(aProfile.GetSamplesPerSecond())
|
||||
, mChannels(aProfile.GetChannels())
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
DOM_CAMERA_LOGI(" audio: '%s' bps=%u samples/s=%u channels=%u\n",
|
||||
NS_ConvertUTF16toUTF8(mCodec).get(), mBitrate, mSamplerate, mChannels);
|
||||
}
|
||||
|
||||
CameraRecorderAudioProfile::~CameraRecorderAudioProfile()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* CameraRecorderProfile
|
||||
*/
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderProfile,
|
||||
mParent,
|
||||
mVideo,
|
||||
mAudio)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderProfile)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderProfile)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderProfile)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
JSObject*
|
||||
CameraRecorderProfile::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return CameraRecorderProfileBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
CameraRecorderProfile::CameraRecorderProfile(nsISupports* aParent,
|
||||
const ICameraControl::RecorderProfile& aProfile)
|
||||
: mParent(aParent)
|
||||
, mName(aProfile.GetName())
|
||||
, mContainerFormat(aProfile.GetContainer())
|
||||
, mMimeType(aProfile.GetMimeType())
|
||||
, mVideo(new CameraRecorderVideoProfile(this, aProfile.GetVideo()))
|
||||
, mAudio(new CameraRecorderAudioProfile(this, aProfile.GetAudio()))
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
DOM_CAMERA_LOGI("profile: '%s' container=%s mime-type=%s\n",
|
||||
NS_ConvertUTF16toUTF8(mName).get(),
|
||||
NS_ConvertUTF16toUTF8(mContainerFormat).get(),
|
||||
NS_ConvertUTF16toUTF8(mMimeType).get());
|
||||
}
|
||||
|
||||
CameraRecorderProfile::~CameraRecorderProfile()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* CameraRecorderProfiles
|
||||
*/
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderProfiles,
|
||||
mParent,
|
||||
mProfiles)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderProfiles)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderProfiles)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderProfiles)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
JSObject*
|
||||
CameraRecorderProfiles::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return CameraRecorderProfilesBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
CameraRecorderProfiles::CameraRecorderProfiles(nsISupports* aParent,
|
||||
ICameraControl* aCameraControl)
|
||||
: mParent(aParent)
|
||||
, mCameraControl(aCameraControl)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
if (mCameraControl) {
|
||||
mListener = new CameraClosedListenerProxy<CameraRecorderProfiles>(this);
|
||||
mCameraControl->AddListener(mListener);
|
||||
}
|
||||
}
|
||||
|
||||
CameraRecorderProfiles::~CameraRecorderProfiles()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
}
|
||||
|
||||
void
|
||||
CameraRecorderProfiles::GetSupportedNames(nsTArray<nsString>& aNames)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n",
|
||||
__func__, __LINE__, this);
|
||||
if (!mCameraControl) {
|
||||
aNames.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = mCameraControl->GetRecorderProfiles(aNames);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aNames.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
CameraRecorderProfile*
|
||||
CameraRecorderProfiles::NamedGetter(const nsAString& aName, bool& aFound)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p, name='%s'\n", __func__, __LINE__, this,
|
||||
NS_ConvertUTF16toUTF8(aName).get());
|
||||
if (!mCameraControl) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CameraRecorderProfile* profile = mProfiles.GetWeak(aName, &aFound);
|
||||
if (!aFound || !profile) {
|
||||
RefPtr<ICameraControl::RecorderProfile> p = mCameraControl->GetProfileInfo(aName);
|
||||
if (p) {
|
||||
profile = new CameraRecorderProfile(this, *p);
|
||||
mProfiles.Put(aName, profile);
|
||||
aFound = true;
|
||||
}
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
void
|
||||
CameraRecorderProfiles::OnHardwareClosed()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mCameraControl) {
|
||||
mCameraControl->RemoveListener(mListener);
|
||||
mCameraControl = nullptr;
|
||||
}
|
||||
mListener = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* CameraCapabilities
|
||||
*/
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraCapabilities, mWindow)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraCapabilities)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraCapabilities)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraCapabilities)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
/* static */
|
||||
bool
|
||||
CameraCapabilities::HasSupport(JSContext* aCx, JSObject* aGlobal)
|
||||
{
|
||||
return Navigator::HasCameraSupport(aCx, aGlobal);
|
||||
}
|
||||
|
||||
CameraCapabilities::CameraCapabilities(nsPIDOMWindowInner* aWindow,
|
||||
ICameraControl* aCameraControl)
|
||||
: mWindow(aWindow)
|
||||
, mCameraControl(aCameraControl)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
MOZ_COUNT_CTOR(CameraCapabilities);
|
||||
if (mCameraControl) {
|
||||
mListener = new CameraClosedListenerProxy<CameraCapabilities>(this);
|
||||
mCameraControl->AddListener(mListener);
|
||||
}
|
||||
}
|
||||
|
||||
CameraCapabilities::~CameraCapabilities()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
MOZ_COUNT_DTOR(CameraCapabilities);
|
||||
}
|
||||
|
||||
void
|
||||
CameraCapabilities::OnHardwareClosed()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mCameraControl) {
|
||||
mCameraControl->RemoveListener(mListener);
|
||||
mCameraControl = nullptr;
|
||||
}
|
||||
mListener = nullptr;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
CameraCapabilities::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return CameraCapabilitiesBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
#define LOG_IF_ERROR(rv, param) \
|
||||
do { \
|
||||
if (NS_FAILED(rv)) { \
|
||||
DOM_CAMERA_LOGW("Error %x trying to get " #param "\n", \
|
||||
(rv)); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
nsresult
|
||||
CameraCapabilities::TranslateToDictionary(uint32_t aKey, nsTArray<CameraSize>& aSizes)
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsTArray<ICameraControl::Size> sizes;
|
||||
|
||||
rv = mCameraControl->Get(aKey, sizes);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
aSizes.Clear();
|
||||
aSizes.SetCapacity(sizes.Length());
|
||||
for (uint32_t i = 0; i < sizes.Length(); ++i) {
|
||||
CameraSize* s = aSizes.AppendElement();
|
||||
s->mWidth = sizes[i].width;
|
||||
s->mHeight = sizes[i].height;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// The following attributes are tagged [Cached, Constant] in the WebIDL, so
|
||||
// the framework will handle caching them for us.
|
||||
|
||||
void
|
||||
CameraCapabilities::GetPreviewSizes(nsTArray<dom::CameraSize>& aRetVal)
|
||||
{
|
||||
nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, aRetVal);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES);
|
||||
}
|
||||
|
||||
void
|
||||
CameraCapabilities::GetPictureSizes(nsTArray<dom::CameraSize>& aRetVal)
|
||||
{
|
||||
nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_PICTURESIZES, aRetVal);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTURESIZES);
|
||||
}
|
||||
|
||||
void
|
||||
CameraCapabilities::GetThumbnailSizes(nsTArray<dom::CameraSize>& aRetVal)
|
||||
{
|
||||
nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES, aRetVal);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES);
|
||||
}
|
||||
|
||||
void
|
||||
CameraCapabilities::GetVideoSizes(nsTArray<dom::CameraSize>& aRetVal)
|
||||
{
|
||||
nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, aRetVal);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_VIDEOSIZES);
|
||||
}
|
||||
|
||||
void
|
||||
CameraCapabilities::GetFileFormats(nsTArray<nsString>& aRetVal)
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_PICTUREFORMATS, aRetVal);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTUREFORMATS);
|
||||
}
|
||||
|
||||
void
|
||||
CameraCapabilities::GetWhiteBalanceModes(nsTArray<nsString>& aRetVal)
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_WHITEBALANCES, aRetVal);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_WHITEBALANCES);
|
||||
}
|
||||
|
||||
void
|
||||
CameraCapabilities::GetSceneModes(nsTArray<nsString>& aRetVal)
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_SCENEMODES, aRetVal);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_SCENEMODES);
|
||||
}
|
||||
|
||||
void
|
||||
CameraCapabilities::GetEffects(nsTArray<nsString>& aRetVal)
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_EFFECTS, aRetVal);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EFFECTS);
|
||||
}
|
||||
|
||||
void
|
||||
CameraCapabilities::GetFlashModes(nsTArray<nsString>& aRetVal)
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_FLASHMODES, aRetVal);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FLASHMODES);
|
||||
}
|
||||
|
||||
void
|
||||
CameraCapabilities::GetFocusModes(nsTArray<nsString>& aRetVal)
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_FOCUSMODES, aRetVal);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FOCUSMODES);
|
||||
}
|
||||
|
||||
void
|
||||
CameraCapabilities::GetZoomRatios(nsTArray<double>& aRetVal)
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, aRetVal);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ZOOMRATIOS);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CameraCapabilities::MaxFocusAreas()
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t areas = 0;
|
||||
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, areas);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS);
|
||||
return areas < 0 ? 0 : areas;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CameraCapabilities::MaxMeteringAreas()
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t areas = 0;
|
||||
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS, areas);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS);
|
||||
return areas < 0 ? 0 : areas;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CameraCapabilities::MaxDetectedFaces()
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t faces = 0;
|
||||
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES, faces);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES);
|
||||
return faces < 0 ? 0 : faces;
|
||||
}
|
||||
|
||||
double
|
||||
CameraCapabilities::MinExposureCompensation()
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double minEv = 0.0;
|
||||
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION, minEv);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION);
|
||||
return minEv;
|
||||
}
|
||||
|
||||
double
|
||||
CameraCapabilities::MaxExposureCompensation()
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double maxEv = 0.0;
|
||||
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION, maxEv);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION);
|
||||
return maxEv;
|
||||
}
|
||||
|
||||
double
|
||||
CameraCapabilities::ExposureCompensationStep()
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double evStep = 0.0;
|
||||
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP, evStep);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP);
|
||||
return evStep;
|
||||
}
|
||||
|
||||
CameraRecorderProfiles*
|
||||
CameraCapabilities::RecorderProfiles()
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<CameraRecorderProfiles> profiles =
|
||||
new CameraRecorderProfiles(this, mCameraControl);
|
||||
return profiles;
|
||||
}
|
||||
|
||||
void
|
||||
CameraCapabilities::GetIsoModes(nsTArray<nsString>& aRetVal)
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_ISOMODES, aRetVal);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ISOMODES);
|
||||
}
|
||||
|
||||
void
|
||||
CameraCapabilities::GetMeteringModes(nsTArray<nsString>& aRetVal)
|
||||
{
|
||||
if (NS_WARN_IF(!mCameraControl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_METERINGMODES, aRetVal);
|
||||
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_METERINGMODES);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -1,243 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_CameraCapabilities_h__
|
||||
#define mozilla_dom_CameraCapabilities_h__
|
||||
|
||||
#include "nsString.h"
|
||||
#include "base/basictypes.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/CameraManagerBinding.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "ICameraControl.h"
|
||||
|
||||
struct JSContext;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/**
|
||||
* CameraRecorderVideoProfile
|
||||
*/
|
||||
class CameraRecorderVideoProfile final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderVideoProfile)
|
||||
|
||||
explicit CameraRecorderVideoProfile(nsISupports* aParent,
|
||||
const ICameraControl::RecorderProfile::Video& aProfile);
|
||||
nsISupports* GetParentObject() const { return mParent; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
uint32_t BitsPerSecond() const { return mBitrate; }
|
||||
uint32_t FramesPerSecond() const { return mFramerate; }
|
||||
void GetCodec(nsAString& aCodec) const { aCodec = mCodec; }
|
||||
|
||||
void GetSize(dom::CameraSize& aSize) const { aSize = mSize; }
|
||||
|
||||
// XXXmikeh - legacy, remove these when the Camera app is updated
|
||||
uint32_t Width() const { return mSize.mWidth; }
|
||||
uint32_t Height() const { return mSize.mHeight; }
|
||||
|
||||
protected:
|
||||
virtual ~CameraRecorderVideoProfile();
|
||||
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
|
||||
const nsString mCodec;
|
||||
uint32_t mBitrate;
|
||||
uint32_t mFramerate;
|
||||
dom::CameraSize mSize;
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderVideoProfile);
|
||||
};
|
||||
|
||||
/**
|
||||
* CameraRecorderAudioProfile
|
||||
*/
|
||||
class CameraRecorderAudioProfile final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderAudioProfile)
|
||||
|
||||
explicit CameraRecorderAudioProfile(nsISupports* aParent,
|
||||
const ICameraControl::RecorderProfile::Audio& aProfile);
|
||||
nsISupports* GetParentObject() const { return mParent; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
uint32_t BitsPerSecond() const { return mBitrate; }
|
||||
uint32_t SamplesPerSecond() const { return mSamplerate; }
|
||||
uint32_t Channels() const { return mChannels; }
|
||||
void GetCodec(nsAString& aCodec) const { aCodec = mCodec; }
|
||||
|
||||
protected:
|
||||
virtual ~CameraRecorderAudioProfile();
|
||||
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
|
||||
const nsString mCodec;
|
||||
uint32_t mBitrate;
|
||||
uint32_t mSamplerate;
|
||||
uint32_t mChannels;
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderAudioProfile);
|
||||
};
|
||||
|
||||
/**
|
||||
* CameraRecorderProfile
|
||||
*/
|
||||
class CameraRecorderProfile final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderProfile)
|
||||
|
||||
explicit CameraRecorderProfile(nsISupports* aParent,
|
||||
const ICameraControl::RecorderProfile& aProfile);
|
||||
nsISupports* GetParentObject() const { return mParent; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void GetMimeType(nsAString& aMimeType) const { aMimeType = mMimeType; }
|
||||
|
||||
CameraRecorderVideoProfile* Video() { return mVideo; }
|
||||
CameraRecorderAudioProfile* Audio() { return mAudio; }
|
||||
|
||||
void GetName(nsAString& aName) const { aName = mName; }
|
||||
|
||||
void
|
||||
GetContainerFormat(nsAString& aContainerFormat) const
|
||||
{
|
||||
aContainerFormat = mContainerFormat;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~CameraRecorderProfile();
|
||||
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
|
||||
const nsString mName;
|
||||
const nsString mContainerFormat;
|
||||
const nsString mMimeType;
|
||||
|
||||
RefPtr<CameraRecorderVideoProfile> mVideo;
|
||||
RefPtr<CameraRecorderAudioProfile> mAudio;
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderProfile);
|
||||
};
|
||||
|
||||
/**
|
||||
* CameraRecorderProfiles
|
||||
*/
|
||||
template<class T> class CameraClosedListenerProxy;
|
||||
|
||||
class CameraRecorderProfiles final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderProfiles)
|
||||
|
||||
explicit CameraRecorderProfiles(nsISupports* aParent,
|
||||
ICameraControl* aCameraControl);
|
||||
nsISupports* GetParentObject() const { return mParent; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
CameraRecorderProfile* NamedGetter(const nsAString& aName, bool& aFound);
|
||||
void GetSupportedNames(nsTArray<nsString>& aNames);
|
||||
|
||||
virtual void OnHardwareClosed();
|
||||
|
||||
protected:
|
||||
virtual ~CameraRecorderProfiles();
|
||||
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
RefPtr<ICameraControl> mCameraControl;
|
||||
nsRefPtrHashtable<nsStringHashKey, CameraRecorderProfile> mProfiles;
|
||||
RefPtr<CameraClosedListenerProxy<CameraRecorderProfiles>> mListener;
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderProfiles);
|
||||
};
|
||||
|
||||
/**
|
||||
* CameraCapabilities
|
||||
*/
|
||||
class CameraCapabilities final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraCapabilities)
|
||||
|
||||
// Because this header's filename doesn't match its C++ or DOM-facing
|
||||
// classname, we can't rely on the [Func="..."] WebIDL tag to implicitly
|
||||
// include the right header for us; instead we must explicitly include a
|
||||
// HasSupport() method in each header. We can get rid of these with the
|
||||
// Great Renaming proposed in bug 983177.
|
||||
static bool HasSupport(JSContext* aCx, JSObject* aGlobal);
|
||||
|
||||
explicit CameraCapabilities(nsPIDOMWindowInner* aWindow,
|
||||
ICameraControl* aCameraControl);
|
||||
|
||||
nsPIDOMWindowInner* GetParentObject() const { return mWindow; }
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void GetPreviewSizes(nsTArray<CameraSize>& aRetVal);
|
||||
void GetPictureSizes(nsTArray<CameraSize>& aRetVal);
|
||||
void GetThumbnailSizes(nsTArray<CameraSize>& aRetVal);
|
||||
void GetVideoSizes(nsTArray<CameraSize>& aRetVal);
|
||||
void GetFileFormats(nsTArray<nsString>& aRetVal);
|
||||
void GetWhiteBalanceModes(nsTArray<nsString>& aRetVal);
|
||||
void GetSceneModes(nsTArray<nsString>& aRetVal);
|
||||
void GetEffects(nsTArray<nsString>& aRetVal);
|
||||
void GetFlashModes(nsTArray<nsString>& aRetVal);
|
||||
void GetFocusModes(nsTArray<nsString>& aRetVal);
|
||||
void GetZoomRatios(nsTArray<double>& aRetVal);
|
||||
uint32_t MaxFocusAreas();
|
||||
uint32_t MaxMeteringAreas();
|
||||
uint32_t MaxDetectedFaces();
|
||||
double MinExposureCompensation();
|
||||
double MaxExposureCompensation();
|
||||
double ExposureCompensationStep();
|
||||
void GetIsoModes(nsTArray<nsString>& aRetVal);
|
||||
void GetMeteringModes(nsTArray<nsString>& aRetVal);
|
||||
|
||||
CameraRecorderProfiles* RecorderProfiles();
|
||||
|
||||
virtual void OnHardwareClosed();
|
||||
|
||||
protected:
|
||||
~CameraCapabilities();
|
||||
|
||||
nsresult TranslateToDictionary(uint32_t aKey, nsTArray<CameraSize>& aSizes);
|
||||
|
||||
RefPtr<nsPIDOMWindowInner> mWindow;
|
||||
RefPtr<ICameraControl> mCameraControl;
|
||||
RefPtr<CameraClosedListenerProxy<CameraCapabilities>> mListener;
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(CameraCapabilities);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_CameraCapabilities_h__
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,262 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DOM_CAMERA_DOMCAMERACONTROL_H
|
||||
#define DOM_CAMERA_DOMCAMERACONTROL_H
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "mozilla/dom/CameraControlBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "ICameraControl.h"
|
||||
#include "CameraCommon.h"
|
||||
#include "DOMMediaStream.h"
|
||||
#include "AudioChannelAgent.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsHashPropertyBag.h"
|
||||
#include "DeviceStorage.h"
|
||||
#include "DOMCameraControlListener.h"
|
||||
#include "nsWeakReference.h"
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "nsITimer.h"
|
||||
#endif
|
||||
|
||||
class nsDOMDeviceStorage;
|
||||
class nsPIDOMWindowInner;
|
||||
class nsIDOMBlob;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class CameraCapabilities;
|
||||
struct CameraPictureOptions;
|
||||
struct CameraStartRecordingOptions;
|
||||
struct CameraRegion;
|
||||
struct CameraSize;
|
||||
template<typename T> class Optional;
|
||||
} // namespace dom
|
||||
class ErrorResult;
|
||||
class StartRecordingHelper;
|
||||
class RecorderPosterHelper;
|
||||
class TrackCreatedListener;
|
||||
|
||||
#define NS_DOM_CAMERA_CONTROL_CID \
|
||||
{ 0x3700c096, 0xf920, 0x438d, \
|
||||
{ 0x8b, 0x3f, 0x15, 0xb3, 0xc9, 0x96, 0x23, 0x62 } }
|
||||
|
||||
// Main camera control.
|
||||
class nsDOMCameraControl final : public DOMMediaStream
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_CAMERA_CONTROL_CID)
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMCameraControl, DOMMediaStream)
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// Because this header's filename doesn't match its C++ or DOM-facing
|
||||
// classname, we can't rely on the [Func="..."] WebIDL tag to implicitly
|
||||
// include the right header for us; instead we must explicitly include a
|
||||
// HasSupport() method in each header. We can get rid of these with the
|
||||
// Great Renaming proposed in bug 983177.
|
||||
static bool HasSupport(JSContext* aCx, JSObject* aGlobal);
|
||||
|
||||
nsDOMCameraControl(uint32_t aCameraId,
|
||||
const dom::CameraConfiguration& aInitialConfig,
|
||||
dom::Promise* aPromise,
|
||||
nsPIDOMWindowInner* aWindow);
|
||||
|
||||
void Shutdown();
|
||||
|
||||
nsPIDOMWindowInner* GetParentObject() const { return mWindow; }
|
||||
|
||||
MediaStream* GetCameraStream() const override;
|
||||
|
||||
// Called by TrackCreatedListener when the underlying track has been created.
|
||||
// XXX Bug 1124630. This can be removed with CameraPreviewMediaStream.
|
||||
void TrackCreated(TrackID aTrackID);
|
||||
|
||||
// Attributes.
|
||||
void GetEffect(nsString& aEffect, ErrorResult& aRv);
|
||||
void SetEffect(const nsAString& aEffect, ErrorResult& aRv);
|
||||
void GetWhiteBalanceMode(nsString& aMode, ErrorResult& aRv);
|
||||
void SetWhiteBalanceMode(const nsAString& aMode, ErrorResult& aRv);
|
||||
void GetSceneMode(nsString& aMode, ErrorResult& aRv);
|
||||
void SetSceneMode(const nsAString& aMode, ErrorResult& aRv);
|
||||
void GetFlashMode(nsString& aMode, ErrorResult& aRv);
|
||||
void SetFlashMode(const nsAString& aMode, ErrorResult& aRv);
|
||||
void GetFocusMode(nsString& aMode, ErrorResult& aRv);
|
||||
void SetFocusMode(const nsAString& aMode, ErrorResult& aRv);
|
||||
double GetZoom(ErrorResult& aRv);
|
||||
void SetZoom(double aZoom, ErrorResult& aRv);
|
||||
double GetFocalLength(ErrorResult& aRv);
|
||||
double GetFocusDistanceNear(ErrorResult& aRv);
|
||||
double GetFocusDistanceOptimum(ErrorResult& aRv);
|
||||
double GetFocusDistanceFar(ErrorResult& aRv);
|
||||
void SetExposureCompensation(double aCompensation, ErrorResult& aRv);
|
||||
double GetExposureCompensation(ErrorResult& aRv);
|
||||
int32_t SensorAngle();
|
||||
already_AddRefed<dom::CameraCapabilities> Capabilities();
|
||||
void GetIsoMode(nsString& aMode, ErrorResult& aRv);
|
||||
void SetIsoMode(const nsAString& aMode, ErrorResult& aRv);
|
||||
double GetPictureQuality(ErrorResult& aRv);
|
||||
void SetPictureQuality(double aQuality, ErrorResult& aRv);
|
||||
void GetMeteringMode(nsString& aMode, ErrorResult& aRv);
|
||||
void SetMeteringMode(const nsAString& aMode, ErrorResult& aRv);
|
||||
|
||||
// Methods.
|
||||
already_AddRefed<dom::Promise> SetConfiguration(const dom::CameraConfiguration& aConfiguration,
|
||||
ErrorResult& aRv);
|
||||
void GetMeteringAreas(nsTArray<dom::CameraRegion>& aAreas, ErrorResult& aRv);
|
||||
void SetMeteringAreas(const dom::Optional<dom::Sequence<dom::CameraRegion> >& aAreas, ErrorResult& aRv);
|
||||
void GetFocusAreas(nsTArray<dom::CameraRegion>& aAreas, ErrorResult& aRv);
|
||||
void SetFocusAreas(const dom::Optional<dom::Sequence<dom::CameraRegion> >& aAreas, ErrorResult& aRv);
|
||||
void GetPictureSize(dom::CameraSize& aSize, ErrorResult& aRv);
|
||||
void SetPictureSize(const dom::CameraSize& aSize, ErrorResult& aRv);
|
||||
void GetThumbnailSize(dom::CameraSize& aSize, ErrorResult& aRv);
|
||||
void SetThumbnailSize(const dom::CameraSize& aSize, ErrorResult& aRv);
|
||||
already_AddRefed<dom::Promise> AutoFocus(ErrorResult& aRv);
|
||||
void StartFaceDetection(ErrorResult& aRv);
|
||||
void StopFaceDetection(ErrorResult& aRv);
|
||||
already_AddRefed<dom::Promise> TakePicture(const dom::CameraPictureOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<dom::Promise> StartRecording(const dom::CameraStartRecordingOptions& aOptions,
|
||||
nsDOMDeviceStorage& storageArea,
|
||||
const nsAString& filename,
|
||||
ErrorResult& aRv);
|
||||
void StopRecording(ErrorResult& aRv);
|
||||
void PauseRecording(ErrorResult& aRv);
|
||||
void ResumeRecording(ErrorResult& aRv);
|
||||
void ResumePreview(ErrorResult& aRv);
|
||||
already_AddRefed<dom::Promise> ReleaseHardware(ErrorResult& aRv);
|
||||
void ResumeContinuousFocus(ErrorResult& aRv);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
operator nsISupports*() { return static_cast<DOMMediaStream*>(this); }
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
static void PreinitCameraHardware();
|
||||
static void DiscardCachedCameraInstance(nsITimer* aTimer, void* aClosure);
|
||||
#endif
|
||||
|
||||
IMPL_EVENT_HANDLER(facesdetected)
|
||||
IMPL_EVENT_HANDLER(shutter)
|
||||
IMPL_EVENT_HANDLER(close)
|
||||
IMPL_EVENT_HANDLER(recorderstatechange)
|
||||
IMPL_EVENT_HANDLER(previewstatechange)
|
||||
IMPL_EVENT_HANDLER(focus)
|
||||
IMPL_EVENT_HANDLER(picture)
|
||||
IMPL_EVENT_HANDLER(configurationchange)
|
||||
IMPL_EVENT_HANDLER(poster)
|
||||
|
||||
protected:
|
||||
virtual ~nsDOMCameraControl();
|
||||
|
||||
class DOMCameraConfiguration final : public dom::CameraConfiguration
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(DOMCameraConfiguration)
|
||||
|
||||
DOMCameraConfiguration();
|
||||
explicit DOMCameraConfiguration(const dom::CameraConfiguration& aConfiguration);
|
||||
|
||||
// Additional configuration options that aren't exposed to the DOM
|
||||
uint32_t mMaxFocusAreas;
|
||||
uint32_t mMaxMeteringAreas;
|
||||
|
||||
private:
|
||||
// Private destructor, to discourage deletion outside of Release():
|
||||
~DOMCameraConfiguration();
|
||||
};
|
||||
|
||||
friend class DOMCameraControlListener;
|
||||
friend class mozilla::StartRecordingHelper;
|
||||
friend class mozilla::RecorderPosterHelper;
|
||||
|
||||
void OnCreatedFileDescriptor(bool aSucceeded);
|
||||
|
||||
void OnAutoFocusComplete(bool aAutoFocusSucceeded);
|
||||
void OnAutoFocusMoving(bool aIsMoving);
|
||||
void OnTakePictureComplete(nsIDOMBlob* aPicture);
|
||||
void OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces);
|
||||
void OnPoster(dom::BlobImpl* aPoster);
|
||||
|
||||
void OnGetCameraComplete();
|
||||
void OnHardwareStateChange(DOMCameraControlListener::HardwareState aState, nsresult aReason);
|
||||
void OnPreviewStateChange(DOMCameraControlListener::PreviewState aState);
|
||||
void OnRecorderStateChange(CameraControlListener::RecorderState aState, int32_t aStatus, int32_t aTrackNum);
|
||||
void OnConfigurationChange(DOMCameraConfiguration* aConfiguration);
|
||||
void OnShutter();
|
||||
void OnUserError(CameraControlListener::UserContext aContext, nsresult aError);
|
||||
|
||||
bool IsWindowStillActive();
|
||||
nsresult SelectPreviewSize(const dom::CameraSize& aRequestedPreviewSize, ICameraControl::Size& aSelectedPreviewSize);
|
||||
|
||||
void ReleaseAudioChannelAgent();
|
||||
nsresult NotifyRecordingStatusChange(const nsString& aMsg);
|
||||
|
||||
already_AddRefed<dom::Promise> CreatePromise(ErrorResult& aRv);
|
||||
void AbortPromise(RefPtr<dom::Promise>& aPromise);
|
||||
virtual void EventListenerAdded(nsIAtom* aType) override;
|
||||
void DispatchPreviewStateEvent(DOMCameraControlListener::PreviewState aState);
|
||||
void DispatchStateEvent(const nsString& aType, const nsString& aState);
|
||||
|
||||
RefPtr<ICameraControl> mCameraControl; // non-DOM camera control
|
||||
|
||||
// An agent used to join audio channel service.
|
||||
nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
|
||||
|
||||
nsresult Set(uint32_t aKey, const dom::Optional<dom::Sequence<dom::CameraRegion> >& aValue, uint32_t aLimit);
|
||||
nsresult Get(uint32_t aKey, nsTArray<dom::CameraRegion>& aValue);
|
||||
|
||||
RefPtr<DOMCameraConfiguration> mCurrentConfiguration;
|
||||
RefPtr<dom::CameraCapabilities> mCapabilities;
|
||||
|
||||
// camera control pending promises
|
||||
RefPtr<dom::Promise> mGetCameraPromise;
|
||||
RefPtr<dom::Promise> mAutoFocusPromise;
|
||||
RefPtr<dom::Promise> mTakePicturePromise;
|
||||
RefPtr<dom::Promise> mStartRecordingPromise;
|
||||
RefPtr<dom::Promise> mReleasePromise;
|
||||
RefPtr<dom::Promise> mSetConfigurationPromise;
|
||||
|
||||
// Camera event listener; we only need this weak reference so that
|
||||
// we can remove the listener from the camera when we're done
|
||||
// with it.
|
||||
DOMCameraControlListener* mListener;
|
||||
|
||||
// our viewfinder stream
|
||||
RefPtr<CameraPreviewMediaStream> mInput;
|
||||
|
||||
// A listener on mInput for adding tracks to the DOM side.
|
||||
RefPtr<TrackCreatedListener> mTrackCreatedListener;
|
||||
|
||||
// set once when this object is created
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
|
||||
dom::CameraStartRecordingOptions mOptions;
|
||||
RefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
|
||||
DOMCameraControlListener::PreviewState mPreviewState;
|
||||
bool mRecording;
|
||||
bool mRecordingStoppedDeferred;
|
||||
bool mSetInitialConfig;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// cached camera control, to improve start-up time
|
||||
static StaticRefPtr<ICameraControl> sCachedCameraControl;
|
||||
static nsresult sCachedCameraControlStartResult;
|
||||
static nsCOMPtr<nsITimer> sDiscardCachedCameraControlTimer;
|
||||
#endif
|
||||
|
||||
private:
|
||||
nsDOMCameraControl(const nsDOMCameraControl&) = delete;
|
||||
nsDOMCameraControl& operator=(const nsDOMCameraControl&) = delete;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsDOMCameraControl, NS_DOM_CAMERA_CONTROL_CID)
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_CAMERA_DOMCAMERACONTROL_H
|
|
@ -1,436 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DOMCameraControlListener.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "CameraCommon.h"
|
||||
#include "DOMCameraControl.h"
|
||||
#include "CameraPreviewMediaStream.h"
|
||||
#include "mozilla/dom/CameraManagerBinding.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "nsQueryObject.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
DOMCameraControlListener::DOMCameraControlListener(nsDOMCameraControl* aDOMCameraControl,
|
||||
CameraPreviewMediaStream* aStream)
|
||||
: mDOMCameraControl(
|
||||
new nsMainThreadPtrHolder<nsISupports>(static_cast<DOMMediaStream*>(aDOMCameraControl)))
|
||||
, mStream(aStream)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p, camera=%p, stream=%p\n",
|
||||
__func__, __LINE__, this, aDOMCameraControl, aStream);
|
||||
}
|
||||
|
||||
DOMCameraControlListener::~DOMCameraControlListener()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
}
|
||||
|
||||
// Boilerplate callback runnable
|
||||
class DOMCameraControlListener::DOMCallback : public Runnable
|
||||
{
|
||||
public:
|
||||
explicit DOMCallback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl)
|
||||
: mDOMCameraControl(aDOMCameraControl)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DOMCameraControlListener::DOMCallback);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~DOMCallback()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DOMCameraControlListener::DOMCallback);
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void RunCallback(nsDOMCameraControl* aDOMCameraControl) = 0;
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
RefPtr<nsDOMCameraControl> camera = do_QueryObject(mDOMCameraControl.get());
|
||||
if (!camera) {
|
||||
DOM_CAMERA_LOGE("do_QueryObject failed to get an nsDOMCameraControl\n");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
RunCallback(camera);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
nsMainThreadPtrHandle<nsISupports> mDOMCameraControl;
|
||||
};
|
||||
|
||||
// Specific callback handlers
|
||||
void
|
||||
DOMCameraControlListener::OnHardwareStateChange(HardwareState aState,
|
||||
nsresult aReason)
|
||||
{
|
||||
class Callback : public DOMCallback
|
||||
{
|
||||
public:
|
||||
Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
|
||||
HardwareState aState, nsresult aReason)
|
||||
: DOMCallback(aDOMCameraControl)
|
||||
, mState(aState)
|
||||
, mReason(aReason)
|
||||
{ }
|
||||
|
||||
void
|
||||
RunCallback(nsDOMCameraControl* aDOMCameraControl) override
|
||||
{
|
||||
aDOMCameraControl->OnHardwareStateChange(mState, mReason);
|
||||
}
|
||||
|
||||
protected:
|
||||
HardwareState mState;
|
||||
nsresult mReason;
|
||||
};
|
||||
|
||||
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState, aReason));
|
||||
}
|
||||
|
||||
void
|
||||
DOMCameraControlListener::OnPreviewStateChange(PreviewState aState)
|
||||
{
|
||||
class Callback : public DOMCallback
|
||||
{
|
||||
public:
|
||||
Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
|
||||
PreviewState aState)
|
||||
: DOMCallback(aDOMCameraControl)
|
||||
, mState(aState)
|
||||
{ }
|
||||
|
||||
void
|
||||
RunCallback(nsDOMCameraControl* aDOMCameraControl) override
|
||||
{
|
||||
aDOMCameraControl->OnPreviewStateChange(mState);
|
||||
}
|
||||
|
||||
protected:
|
||||
PreviewState mState;
|
||||
};
|
||||
|
||||
switch (aState) {
|
||||
case kPreviewStopped:
|
||||
// Clear the current frame right away, without dispatching a
|
||||
// runnable. This is an ugly coupling between the camera's
|
||||
// SurfaceTextureClient and the MediaStream/ImageContainer,
|
||||
// but without it, the preview can fail to start.
|
||||
DOM_CAMERA_LOGI("Preview stopped, clearing current frame\n");
|
||||
mStream->ClearCurrentFrame();
|
||||
break;
|
||||
|
||||
case kPreviewPaused:
|
||||
// In the paused state, we still want to reflect the change
|
||||
// in preview state, but we don't want to clear the current
|
||||
// frame as above, since doing so seems to cause genlock
|
||||
// problems when we restart the preview. See bug 957749.
|
||||
DOM_CAMERA_LOGI("Preview paused\n");
|
||||
break;
|
||||
|
||||
case kPreviewStarted:
|
||||
DOM_CAMERA_LOGI("Preview started\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
DOM_CAMERA_LOGE("Unknown preview state %d\n", aState);
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid preview state");
|
||||
return;
|
||||
}
|
||||
mStream->OnPreviewStateChange(aState == kPreviewStarted);
|
||||
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState));
|
||||
}
|
||||
|
||||
void
|
||||
DOMCameraControlListener::OnRecorderStateChange(RecorderState aState,
|
||||
int32_t aStatus, int32_t aTrackNum)
|
||||
{
|
||||
class Callback : public DOMCallback
|
||||
{
|
||||
public:
|
||||
Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
|
||||
RecorderState aState,
|
||||
int32_t aStatus,
|
||||
int32_t aTrackNum)
|
||||
: DOMCallback(aDOMCameraControl)
|
||||
, mState(aState)
|
||||
, mStatus(aStatus)
|
||||
, mTrackNum(aTrackNum)
|
||||
{ }
|
||||
|
||||
void
|
||||
RunCallback(nsDOMCameraControl* aDOMCameraControl) override
|
||||
{
|
||||
aDOMCameraControl->OnRecorderStateChange(mState, mStatus, mTrackNum);
|
||||
}
|
||||
|
||||
protected:
|
||||
RecorderState mState;
|
||||
int32_t mStatus;
|
||||
int32_t mTrackNum;
|
||||
};
|
||||
|
||||
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState, aStatus, aTrackNum));
|
||||
}
|
||||
|
||||
void
|
||||
DOMCameraControlListener::OnConfigurationChange(const CameraListenerConfiguration& aConfiguration)
|
||||
{
|
||||
class Callback : public DOMCallback
|
||||
{
|
||||
public:
|
||||
Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
|
||||
const CameraListenerConfiguration& aConfiguration)
|
||||
: DOMCallback(aDOMCameraControl)
|
||||
, mConfiguration(aConfiguration)
|
||||
{ }
|
||||
|
||||
void
|
||||
RunCallback(nsDOMCameraControl* aDOMCameraControl) override
|
||||
{
|
||||
RefPtr<nsDOMCameraControl::DOMCameraConfiguration> config =
|
||||
new nsDOMCameraControl::DOMCameraConfiguration();
|
||||
|
||||
switch (mConfiguration.mMode) {
|
||||
case ICameraControl::kVideoMode:
|
||||
config->mMode = CameraMode::Video;
|
||||
break;
|
||||
|
||||
case ICameraControl::kPictureMode:
|
||||
config->mMode = CameraMode::Picture;
|
||||
break;
|
||||
|
||||
default:
|
||||
DOM_CAMERA_LOGI("Camera mode still unspecified, nothing to do\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Map CameraControl parameters to their DOM-facing equivalents
|
||||
config->mRecorderProfile = mConfiguration.mRecorderProfile;
|
||||
config->mPreviewSize.mWidth = mConfiguration.mPreviewSize.width;
|
||||
config->mPreviewSize.mHeight = mConfiguration.mPreviewSize.height;
|
||||
config->mPictureSize.mWidth = mConfiguration.mPictureSize.width;
|
||||
config->mPictureSize.mHeight = mConfiguration.mPictureSize.height;
|
||||
config->mMaxMeteringAreas = mConfiguration.mMaxMeteringAreas;
|
||||
config->mMaxFocusAreas = mConfiguration.mMaxFocusAreas;
|
||||
|
||||
aDOMCameraControl->OnConfigurationChange(config);
|
||||
}
|
||||
|
||||
protected:
|
||||
const CameraListenerConfiguration mConfiguration;
|
||||
};
|
||||
|
||||
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aConfiguration));
|
||||
}
|
||||
|
||||
void
|
||||
DOMCameraControlListener::OnAutoFocusMoving(bool aIsMoving)
|
||||
{
|
||||
class Callback : public DOMCallback
|
||||
{
|
||||
public:
|
||||
Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl, bool aIsMoving)
|
||||
: DOMCallback(aDOMCameraControl)
|
||||
, mIsMoving(aIsMoving)
|
||||
{ }
|
||||
|
||||
void
|
||||
RunCallback(nsDOMCameraControl* aDOMCameraControl) override
|
||||
{
|
||||
aDOMCameraControl->OnAutoFocusMoving(mIsMoving);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool mIsMoving;
|
||||
};
|
||||
|
||||
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aIsMoving));
|
||||
}
|
||||
|
||||
void
|
||||
DOMCameraControlListener::OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces)
|
||||
{
|
||||
class Callback : public DOMCallback
|
||||
{
|
||||
public:
|
||||
Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
|
||||
const nsTArray<ICameraControl::Face>& aFaces)
|
||||
: DOMCallback(aDOMCameraControl)
|
||||
, mFaces(aFaces)
|
||||
{ }
|
||||
|
||||
void
|
||||
RunCallback(nsDOMCameraControl* aDOMCameraControl) override
|
||||
{
|
||||
aDOMCameraControl->OnFacesDetected(mFaces);
|
||||
}
|
||||
|
||||
protected:
|
||||
const nsTArray<ICameraControl::Face> mFaces;
|
||||
};
|
||||
|
||||
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aFaces));
|
||||
}
|
||||
|
||||
void
|
||||
DOMCameraControlListener::OnShutter()
|
||||
{
|
||||
class Callback : public DOMCallback
|
||||
{
|
||||
public:
|
||||
explicit Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl)
|
||||
: DOMCallback(aDOMCameraControl)
|
||||
{ }
|
||||
|
||||
void
|
||||
RunCallback(nsDOMCameraControl* aDOMCameraControl) override
|
||||
{
|
||||
aDOMCameraControl->OnShutter();
|
||||
}
|
||||
};
|
||||
|
||||
NS_DispatchToMainThread(new Callback(mDOMCameraControl));
|
||||
}
|
||||
|
||||
void
|
||||
DOMCameraControlListener::OnRateLimitPreview(bool aLimit)
|
||||
{
|
||||
mStream->RateLimit(aLimit);
|
||||
}
|
||||
|
||||
bool
|
||||
DOMCameraControlListener::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight)
|
||||
{
|
||||
DOM_CAMERA_LOGI("OnNewPreviewFrame: got %d x %d frame\n", aWidth, aHeight);
|
||||
|
||||
mStream->SetCurrentFrame(gfx::IntSize(aWidth, aHeight), aImage);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DOMCameraControlListener::OnAutoFocusComplete(bool aAutoFocusSucceeded)
|
||||
{
|
||||
class Callback : public DOMCallback
|
||||
{
|
||||
public:
|
||||
Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
|
||||
bool aAutoFocusSucceeded)
|
||||
: DOMCallback(aDOMCameraControl)
|
||||
, mAutoFocusSucceeded(aAutoFocusSucceeded)
|
||||
{ }
|
||||
|
||||
void
|
||||
RunCallback(nsDOMCameraControl* aDOMCameraControl) override
|
||||
{
|
||||
aDOMCameraControl->OnAutoFocusComplete(mAutoFocusSucceeded);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool mAutoFocusSucceeded;
|
||||
};
|
||||
|
||||
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aAutoFocusSucceeded));
|
||||
}
|
||||
|
||||
void
|
||||
DOMCameraControlListener::OnTakePictureComplete(const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
|
||||
{
|
||||
class Callback : public DOMCallback
|
||||
{
|
||||
public:
|
||||
Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
|
||||
const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
|
||||
: DOMCallback(aDOMCameraControl)
|
||||
, mLength(aLength)
|
||||
, mMimeType(aMimeType)
|
||||
{
|
||||
mData = (uint8_t*) malloc(aLength);
|
||||
memcpy(mData, aData, aLength);
|
||||
}
|
||||
|
||||
void
|
||||
RunCallback(nsDOMCameraControl* aDOMCameraControl) override
|
||||
{
|
||||
nsCOMPtr<nsIDOMBlob> picture =
|
||||
Blob::CreateMemoryBlob(mDOMCameraControl.get(),
|
||||
static_cast<void*>(mData),
|
||||
static_cast<uint64_t>(mLength),
|
||||
mMimeType);
|
||||
aDOMCameraControl->OnTakePictureComplete(picture);
|
||||
mData = NULL;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual
|
||||
~Callback()
|
||||
{
|
||||
free(mData);
|
||||
}
|
||||
|
||||
uint8_t* mData;
|
||||
uint32_t mLength;
|
||||
nsString mMimeType;
|
||||
};
|
||||
|
||||
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aData, aLength, aMimeType));
|
||||
}
|
||||
|
||||
void
|
||||
DOMCameraControlListener::OnUserError(UserContext aContext, nsresult aError)
|
||||
{
|
||||
class Callback : public DOMCallback
|
||||
{
|
||||
public:
|
||||
Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
|
||||
UserContext aContext,
|
||||
nsresult aError)
|
||||
: DOMCallback(aDOMCameraControl)
|
||||
, mContext(aContext)
|
||||
, mError(aError)
|
||||
{ }
|
||||
|
||||
virtual void
|
||||
RunCallback(nsDOMCameraControl* aDOMCameraControl) override
|
||||
{
|
||||
aDOMCameraControl->OnUserError(mContext, mError);
|
||||
}
|
||||
|
||||
protected:
|
||||
UserContext mContext;
|
||||
nsresult mError;
|
||||
};
|
||||
|
||||
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aContext, aError));
|
||||
}
|
||||
|
||||
void
|
||||
DOMCameraControlListener::OnPoster(BlobImpl* aBlobImpl)
|
||||
{
|
||||
class Callback : public DOMCallback
|
||||
{
|
||||
public:
|
||||
Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl, BlobImpl* aBlobImpl)
|
||||
: DOMCallback(aDOMCameraControl)
|
||||
, mBlobImpl(aBlobImpl)
|
||||
{ }
|
||||
|
||||
void
|
||||
RunCallback(nsDOMCameraControl* aDOMCameraControl) override
|
||||
{
|
||||
aDOMCameraControl->OnPoster(mBlobImpl);
|
||||
}
|
||||
|
||||
protected:
|
||||
RefPtr<BlobImpl> mBlobImpl;
|
||||
};
|
||||
|
||||
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aBlobImpl));
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DOM_CAMERA_DOMCAMERACONTROLLISTENER_H
|
||||
#define DOM_CAMERA_DOMCAMERACONTROLLISTENER_H
|
||||
|
||||
#include "nsProxyRelease.h"
|
||||
#include "CameraControlListener.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class nsDOMCameraControl;
|
||||
class CameraPreviewMediaStream;
|
||||
|
||||
class DOMCameraControlListener : public CameraControlListener
|
||||
{
|
||||
public:
|
||||
DOMCameraControlListener(nsDOMCameraControl* aDOMCameraControl, CameraPreviewMediaStream* aStream);
|
||||
|
||||
virtual void OnAutoFocusComplete(bool aAutoFocusSucceeded) override;
|
||||
virtual void OnAutoFocusMoving(bool aIsMoving) override;
|
||||
virtual void OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces) override;
|
||||
virtual void OnTakePictureComplete(const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) override;
|
||||
|
||||
virtual void OnHardwareStateChange(HardwareState aState, nsresult aReason) override;
|
||||
virtual void OnPreviewStateChange(PreviewState aState) override;
|
||||
virtual void OnRecorderStateChange(RecorderState aState, int32_t aStatus, int32_t aTrackNum) override;
|
||||
virtual void OnConfigurationChange(const CameraListenerConfiguration& aConfiguration) override;
|
||||
virtual void OnShutter() override;
|
||||
virtual void OnRateLimitPreview(bool aLimit) override;
|
||||
virtual bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight) override;
|
||||
virtual void OnUserError(UserContext aContext, nsresult aError) override;
|
||||
virtual void OnPoster(dom::BlobImpl* aBlobImpl) override;
|
||||
|
||||
protected:
|
||||
virtual ~DOMCameraControlListener();
|
||||
|
||||
nsMainThreadPtrHandle<nsISupports> mDOMCameraControl;
|
||||
CameraPreviewMediaStream* mStream;
|
||||
|
||||
class DOMCallback;
|
||||
|
||||
private:
|
||||
DOMCameraControlListener(const DOMCameraControlListener&) = delete;
|
||||
DOMCameraControlListener& operator=(const DOMCameraControlListener&) = delete;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_CAMERA_DOMCAMERACONTROLLISTENER_H
|
|
@ -1,89 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DOMCameraDetectedFace.h"
|
||||
#include "Navigator.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMCameraDetectedFace, mParent,
|
||||
mBounds, mLeftEye, mRightEye, mMouth)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMCameraDetectedFace)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMCameraDetectedFace)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMCameraDetectedFace)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
/* static */
|
||||
bool
|
||||
DOMCameraDetectedFace::HasSupport(JSContext* aCx, JSObject* aGlobal)
|
||||
{
|
||||
return Navigator::HasCameraSupport(aCx, aGlobal);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
DOMCameraDetectedFace::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return CameraDetectedFaceBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<DOMCameraDetectedFace>
|
||||
DOMCameraDetectedFace::Constructor(const GlobalObject& aGlobal,
|
||||
const dom::CameraDetectedFaceInit& aFace,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<DOMCameraDetectedFace> face =
|
||||
new DOMCameraDetectedFace(aGlobal.GetAsSupports(), aFace);
|
||||
return face.forget();
|
||||
}
|
||||
|
||||
DOMCameraDetectedFace::DOMCameraDetectedFace(nsISupports* aParent,
|
||||
const dom::CameraDetectedFaceInit& aFace)
|
||||
: mParent(aParent)
|
||||
, mId(aFace.mId)
|
||||
, mScore(aFace.mScore)
|
||||
, mBounds(new DOMRect(this))
|
||||
{
|
||||
mBounds->SetRect(aFace.mBounds.mLeft,
|
||||
aFace.mBounds.mTop,
|
||||
aFace.mBounds.mRight - aFace.mBounds.mLeft,
|
||||
aFace.mBounds.mBottom - aFace.mBounds.mTop);
|
||||
|
||||
if (aFace.mHasLeftEye) {
|
||||
mLeftEye = new DOMPoint(this, aFace.mLeftEye.mX, aFace.mLeftEye.mY);
|
||||
}
|
||||
if (aFace.mHasRightEye) {
|
||||
mRightEye = new DOMPoint(this, aFace.mRightEye.mX, aFace.mRightEye.mY);
|
||||
}
|
||||
if (aFace.mHasMouth) {
|
||||
mMouth = new DOMPoint(this, aFace.mMouth.mX, aFace.mMouth.mY);
|
||||
}
|
||||
}
|
||||
|
||||
DOMCameraDetectedFace::DOMCameraDetectedFace(nsISupports* aParent,
|
||||
const ICameraControl::Face& aFace)
|
||||
: mParent(aParent)
|
||||
, mId(aFace.id)
|
||||
, mScore(aFace.score)
|
||||
, mBounds(new DOMRect(this))
|
||||
{
|
||||
mBounds->SetRect(aFace.bound.left,
|
||||
aFace.bound.top,
|
||||
aFace.bound.right - aFace.bound.left,
|
||||
aFace.bound.bottom - aFace.bound.top);
|
||||
|
||||
if (aFace.hasLeftEye) {
|
||||
mLeftEye = new DOMPoint(this, aFace.leftEye.x, aFace.leftEye.y);
|
||||
}
|
||||
if (aFace.hasRightEye) {
|
||||
mRightEye = new DOMPoint(this, aFace.rightEye.x, aFace.rightEye.y);
|
||||
}
|
||||
if (aFace.hasMouth) {
|
||||
mMouth = new DOMPoint(this, aFace.mouth.x, aFace.mouth.y);
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DOM_CAMERA_DOMCAMERADETECTEDFACE_H
|
||||
#define DOM_CAMERA_DOMCAMERADETECTEDFACE_H
|
||||
|
||||
#include "mozilla/dom/CameraControlBinding.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "mozilla/dom/DOMRect.h"
|
||||
#include "mozilla/dom/DOMPoint.h"
|
||||
#include "ICameraControl.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
|
||||
class DOMCameraDetectedFace final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMCameraDetectedFace)
|
||||
|
||||
// Because this header's filename doesn't match its C++ or DOM-facing
|
||||
// classname, we can't rely on the [Func="..."] WebIDL tag to implicitly
|
||||
// include the right header for us; instead we must explicitly include a
|
||||
// HasSupport() method in each header. We can get rid of these with the
|
||||
// Great Renaming proposed in bug 983177.
|
||||
static bool HasSupport(JSContext* aCx, JSObject* aGlobal);
|
||||
|
||||
static already_AddRefed<DOMCameraDetectedFace> Constructor(const GlobalObject& aGlobal,
|
||||
const dom::CameraDetectedFaceInit& aFace,
|
||||
ErrorResult& aRv);
|
||||
|
||||
DOMCameraDetectedFace(nsISupports* aParent, const ICameraControl::Face& aFace);
|
||||
|
||||
uint32_t Id() { return mId; }
|
||||
uint32_t Score() { return mScore; }
|
||||
bool HasLeftEye() { return mLeftEye; }
|
||||
bool HasRightEye() { return mRightEye; }
|
||||
bool HasMouth() { return mMouth; }
|
||||
|
||||
dom::DOMRect* Bounds() { return mBounds; }
|
||||
|
||||
dom::DOMPoint* GetLeftEye() { return mLeftEye; }
|
||||
dom::DOMPoint* GetRightEye() { return mRightEye; }
|
||||
dom::DOMPoint* GetMouth() { return mMouth; }
|
||||
|
||||
nsISupports*
|
||||
GetParentObject() const
|
||||
{
|
||||
MOZ_ASSERT(mParent);
|
||||
return mParent;
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
protected:
|
||||
DOMCameraDetectedFace(nsISupports* aParent, const dom::CameraDetectedFaceInit& aFace);
|
||||
virtual ~DOMCameraDetectedFace() { }
|
||||
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
|
||||
uint32_t mId;
|
||||
uint32_t mScore;
|
||||
|
||||
RefPtr<dom::DOMRect> mBounds;
|
||||
|
||||
RefPtr<dom::DOMPoint> mLeftEye;
|
||||
RefPtr<dom::DOMPoint> mRightEye;
|
||||
RefPtr<dom::DOMPoint> mMouth;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_CAMERA_DOMCAMERADETECTEDFACE_H
|
|
@ -1,451 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DOMCameraManager.h"
|
||||
#include "nsDebug.h"
|
||||
#include "jsapi.h"
|
||||
#include "Navigator.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
#include "nsIContentPermissionPrompt.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
#include "DOMCameraControl.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "CameraCommon.h"
|
||||
#include "CameraPreferences.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
#include "nsQueryObject.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMCameraManager, mWindow)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCameraManager)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCameraManager)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCameraManager)
|
||||
|
||||
/**
|
||||
* Global camera logging object
|
||||
*
|
||||
* Set the MOZ_LOG environment variable to enable logging
|
||||
* in a debug build, e.g. MOZ_LOG=Camera:5
|
||||
*/
|
||||
LogModule*
|
||||
GetCameraLog()
|
||||
{
|
||||
static LazyLogModule sLog("Camera");
|
||||
return sLog;
|
||||
}
|
||||
|
||||
::WindowTable* nsDOMCameraManager::sActiveWindows = nullptr;
|
||||
|
||||
nsDOMCameraManager::nsDOMCameraManager(nsPIDOMWindowInner* aWindow)
|
||||
: mWindowId(aWindow->WindowID())
|
||||
, mPermission(nsIPermissionManager::DENY_ACTION)
|
||||
, mWindow(aWindow)
|
||||
{
|
||||
/* member initializers and constructor code */
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%" PRIx64 "\n", __func__, __LINE__, this, mWindowId);
|
||||
MOZ_COUNT_CTOR(nsDOMCameraManager);
|
||||
}
|
||||
|
||||
nsDOMCameraManager::~nsDOMCameraManager()
|
||||
{
|
||||
/* destructor code */
|
||||
MOZ_COUNT_DTOR(nsDOMCameraManager);
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsDOMCameraManager::GetListOfCameras(nsTArray<nsString>& aList, ErrorResult& aRv)
|
||||
{
|
||||
aRv = ICameraControl::GetListOfCameras(aList);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
nsDOMCameraManager::HasSupport(JSContext* aCx, JSObject* aGlobal)
|
||||
{
|
||||
return Navigator::HasCameraSupport(aCx, aGlobal);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
nsDOMCameraManager::CheckPermission(nsPIDOMWindowInner* aWindow)
|
||||
{
|
||||
nsCOMPtr<nsIPermissionManager> permMgr =
|
||||
services::GetPermissionManager();
|
||||
NS_ENSURE_TRUE(permMgr, false);
|
||||
|
||||
uint32_t permission = nsIPermissionManager::DENY_ACTION;
|
||||
permMgr->TestPermissionFromWindow(aWindow, "camera", &permission);
|
||||
if (permission != nsIPermissionManager::ALLOW_ACTION &&
|
||||
permission != nsIPermissionManager::PROMPT_ACTION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<nsDOMCameraManager>
|
||||
nsDOMCameraManager::CreateInstance(nsPIDOMWindowInner* aWindow)
|
||||
{
|
||||
// Initialize the shared active window tracker
|
||||
if (!sActiveWindows) {
|
||||
sActiveWindows = new ::WindowTable();
|
||||
}
|
||||
|
||||
RefPtr<nsDOMCameraManager> cameraManager =
|
||||
new nsDOMCameraManager(aWindow);
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (!obs) {
|
||||
DOM_CAMERA_LOGE("Camera manager failed to get observer service\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsresult rv = obs->AddObserver(cameraManager, "xpcom-shutdown", true);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Camera manager failed to add 'xpcom-shutdown' observer (0x%x)\n", rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return cameraManager.forget();
|
||||
}
|
||||
|
||||
class CameraPermissionRequest : public nsIContentPermissionRequest
|
||||
, public nsIRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_NSICONTENTPERMISSIONREQUEST
|
||||
NS_DECL_NSIRUNNABLE
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(CameraPermissionRequest,
|
||||
nsIContentPermissionRequest)
|
||||
|
||||
CameraPermissionRequest(nsIPrincipal* aPrincipal,
|
||||
nsPIDOMWindowInner* aWindow,
|
||||
RefPtr<nsDOMCameraManager> aManager,
|
||||
uint32_t aCameraId,
|
||||
const CameraConfiguration& aInitialConfig,
|
||||
RefPtr<Promise> aPromise)
|
||||
: mPrincipal(aPrincipal)
|
||||
, mWindow(aWindow)
|
||||
, mCameraManager(aManager)
|
||||
, mCameraId(aCameraId)
|
||||
, mInitialConfig(aInitialConfig)
|
||||
, mPromise(aPromise)
|
||||
, mRequester(new nsContentPermissionRequester(mWindow))
|
||||
{ }
|
||||
|
||||
protected:
|
||||
virtual ~CameraPermissionRequest() { }
|
||||
|
||||
nsresult DispatchCallback(uint32_t aPermission);
|
||||
void CallAllow();
|
||||
void CallCancel();
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
RefPtr<nsDOMCameraManager> mCameraManager;
|
||||
uint32_t mCameraId;
|
||||
CameraConfiguration mInitialConfig;
|
||||
RefPtr<Promise> mPromise;
|
||||
nsCOMPtr<nsIContentPermissionRequester> mRequester;
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(CameraPermissionRequest, mWindow, mPromise)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraPermissionRequest)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraPermissionRequest)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraPermissionRequest)
|
||||
|
||||
NS_IMETHODIMP
|
||||
CameraPermissionRequest::Run()
|
||||
{
|
||||
return nsContentPermissionUtils::AskPermission(this, mWindow);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CameraPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
|
||||
{
|
||||
NS_ADDREF(*aRequestingPrincipal = mPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CameraPermissionRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
|
||||
{
|
||||
NS_ADDREF(*aRequestingWindow = mWindow);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CameraPermissionRequest::GetElement(nsIDOMElement** aElement)
|
||||
{
|
||||
*aElement = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CameraPermissionRequest::Cancel()
|
||||
{
|
||||
return DispatchCallback(nsIPermissionManager::DENY_ACTION);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CameraPermissionRequest::Allow(JS::HandleValue aChoices)
|
||||
{
|
||||
MOZ_ASSERT(aChoices.isUndefined());
|
||||
return DispatchCallback(nsIPermissionManager::ALLOW_ACTION);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CameraPermissionRequest::GetRequester(nsIContentPermissionRequester** aRequester)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aRequester);
|
||||
|
||||
nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
|
||||
requester.forget(aRequester);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraPermissionRequest::DispatchCallback(uint32_t aPermission)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> callbackRunnable;
|
||||
if (aPermission == nsIPermissionManager::ALLOW_ACTION) {
|
||||
callbackRunnable = NewRunnableMethod(this, &CameraPermissionRequest::CallAllow);
|
||||
} else {
|
||||
callbackRunnable = NewRunnableMethod(this, &CameraPermissionRequest::CallCancel);
|
||||
}
|
||||
return NS_DispatchToMainThread(callbackRunnable.forget());
|
||||
}
|
||||
|
||||
void
|
||||
CameraPermissionRequest::CallAllow()
|
||||
{
|
||||
mCameraManager->PermissionAllowed(mCameraId, mInitialConfig, mPromise);
|
||||
}
|
||||
|
||||
void
|
||||
CameraPermissionRequest::CallCancel()
|
||||
{
|
||||
mCameraManager->PermissionCancelled(mCameraId, mInitialConfig, mPromise);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CameraPermissionRequest::GetTypes(nsIArray** aTypes)
|
||||
{
|
||||
nsTArray<nsString> emptyOptions;
|
||||
return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("camera"),
|
||||
NS_LITERAL_CSTRING("unused"),
|
||||
emptyOptions,
|
||||
aTypes);
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
/* static */ void
|
||||
nsDOMCameraManager::PreinitCameraHardware()
|
||||
{
|
||||
nsDOMCameraControl::PreinitCameraHardware();
|
||||
}
|
||||
#endif
|
||||
|
||||
already_AddRefed<Promise>
|
||||
nsDOMCameraManager::GetCamera(const nsAString& aCamera,
|
||||
const CameraConfiguration& aInitialConfig,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
|
||||
uint32_t cameraId = 0; // back (or forward-facing) camera by default
|
||||
if (aCamera.EqualsLiteral("front")) {
|
||||
cameraId = 1;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mPermission == nsIPermissionManager::ALLOW_ACTION) {
|
||||
PermissionAllowed(cameraId, aInitialConfig, promise);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
|
||||
if (!sop) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
|
||||
// If we are a CERTIFIED app, we can short-circuit the permission check,
|
||||
// which gets us a performance win.
|
||||
// Unprivileged mochitests always fail the dispatched permission check,
|
||||
// even if permission to the camera has been granted.
|
||||
bool immediateCheck = false;
|
||||
CameraPreferences::GetPref("camera.control.test.permission", immediateCheck);
|
||||
if ((principal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED || immediateCheck) &&
|
||||
CheckPermission(mWindow)) {
|
||||
PermissionAllowed(cameraId, aInitialConfig, promise);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> permissionRequest =
|
||||
new CameraPermissionRequest(principal, mWindow, this, cameraId,
|
||||
aInitialConfig, promise);
|
||||
|
||||
NS_DispatchToMainThread(permissionRequest);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMCameraManager::PermissionAllowed(uint32_t aCameraId,
|
||||
const CameraConfiguration& aInitialConfig,
|
||||
Promise* aPromise)
|
||||
{
|
||||
mPermission = nsIPermissionManager::ALLOW_ACTION;
|
||||
|
||||
// Creating this object will trigger the aOnSuccess callback
|
||||
// (or the aOnError one, if it fails).
|
||||
RefPtr<nsDOMCameraControl> cameraControl =
|
||||
new nsDOMCameraControl(aCameraId, aInitialConfig, aPromise, mWindow);
|
||||
|
||||
Register(cameraControl);
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMCameraManager::PermissionCancelled(uint32_t aCameraId,
|
||||
const CameraConfiguration& aInitialConfig,
|
||||
Promise* aPromise)
|
||||
{
|
||||
mPermission = nsIPermissionManager::DENY_ACTION;
|
||||
aPromise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMCameraManager::Register(nsDOMCameraControl* aDOMCameraControl)
|
||||
{
|
||||
DOM_CAMERA_LOGI(">>> Register( aDOMCameraControl = %p ) mWindowId = 0x%" PRIx64 "\n", aDOMCameraControl, mWindowId);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
CameraControls* controls = sActiveWindows->Get(mWindowId);
|
||||
if (!controls) {
|
||||
controls = new CameraControls();
|
||||
sActiveWindows->Put(mWindowId, controls);
|
||||
}
|
||||
|
||||
// Remove any stale CameraControl objects to limit our memory usage
|
||||
uint32_t i = controls->Length();
|
||||
while (i > 0) {
|
||||
--i;
|
||||
RefPtr<nsDOMCameraControl> cameraControl =
|
||||
do_QueryObject(controls->ElementAt(i));
|
||||
if (!cameraControl) {
|
||||
controls->RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Put the camera control into the hash table
|
||||
nsWeakPtr cameraControl =
|
||||
do_GetWeakReference(static_cast<DOMMediaStream*>(aDOMCameraControl));
|
||||
controls->AppendElement(cameraControl);
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMCameraManager::Shutdown(uint64_t aWindowId)
|
||||
{
|
||||
DOM_CAMERA_LOGI(">>> Shutdown( aWindowId = 0x%" PRIx64 " )\n", aWindowId);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
CameraControls* controls = sActiveWindows->Get(aWindowId);
|
||||
if (!controls) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t i = controls->Length();
|
||||
while (i > 0) {
|
||||
--i;
|
||||
RefPtr<nsDOMCameraControl> cameraControl =
|
||||
do_QueryObject(controls->ElementAt(i));
|
||||
if (cameraControl) {
|
||||
cameraControl->Shutdown();
|
||||
}
|
||||
}
|
||||
controls->Clear();
|
||||
|
||||
sActiveWindows->Remove(aWindowId);
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMCameraManager::XpComShutdown()
|
||||
{
|
||||
DOM_CAMERA_LOGI(">>> XPCOM Shutdown\n");
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
obs->RemoveObserver(this, "xpcom-shutdown");
|
||||
|
||||
delete sActiveWindows;
|
||||
sActiveWindows = nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMCameraManager::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
|
||||
{
|
||||
if (strcmp(aTopic, "xpcom-shutdown") == 0) {
|
||||
XpComShutdown();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMCameraManager::OnNavigation(uint64_t aWindowId)
|
||||
{
|
||||
DOM_CAMERA_LOGI(">>> OnNavigation event\n");
|
||||
Shutdown(aWindowId);
|
||||
}
|
||||
|
||||
bool
|
||||
nsDOMCameraManager::IsWindowStillActive(uint64_t aWindowId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!sActiveWindows) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!sActiveWindows->Get(aWindowId);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
nsDOMCameraManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return CameraManagerBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=40: */
|
||||
/* 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 DOM_CAMERA_DOMCAMERAMANAGER_H
|
||||
#define DOM_CAMERA_DOMCAMERAMANAGER_H
|
||||
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
namespace mozilla {
|
||||
class ErrorResult;
|
||||
class nsDOMCameraControl;
|
||||
namespace dom {
|
||||
struct CameraConfiguration;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
typedef nsTArray<nsWeakPtr> CameraControls;
|
||||
typedef nsClassHashtable<nsUint64HashKey, CameraControls> WindowTable;
|
||||
|
||||
class nsDOMCameraManager final
|
||||
: public nsIObserver
|
||||
, public nsSupportsWeakReference
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDOMCameraManager,
|
||||
nsIObserver)
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
// Because this header's filename doesn't match its C++ or DOM-facing
|
||||
// classname, we can't rely on the [Func="..."] WebIDL tag to implicitly
|
||||
// include the right header for us; instead we must explicitly include a
|
||||
// HasSupport() method in each header. We can get rid of these with the
|
||||
// Great Renaming proposed in bug 983177.
|
||||
static bool HasSupport(JSContext* aCx, JSObject* aGlobal);
|
||||
|
||||
static bool CheckPermission(nsPIDOMWindowInner* aWindow);
|
||||
static already_AddRefed<nsDOMCameraManager>
|
||||
CreateInstance(nsPIDOMWindowInner* aWindow);
|
||||
static bool IsWindowStillActive(uint64_t aWindowId);
|
||||
|
||||
void Register(mozilla::nsDOMCameraControl* aDOMCameraControl);
|
||||
void OnNavigation(uint64_t aWindowId);
|
||||
|
||||
void PermissionAllowed(uint32_t aCameraId,
|
||||
const mozilla::dom::CameraConfiguration& aOptions,
|
||||
mozilla::dom::Promise* aPromise);
|
||||
|
||||
void PermissionCancelled(uint32_t aCameraId,
|
||||
const mozilla::dom::CameraConfiguration& aOptions,
|
||||
mozilla::dom::Promise* aPromise);
|
||||
|
||||
// WebIDL
|
||||
already_AddRefed<mozilla::dom::Promise>
|
||||
GetCamera(const nsAString& aCamera,
|
||||
const mozilla::dom::CameraConfiguration& aOptions,
|
||||
mozilla::ErrorResult& aRv);
|
||||
void GetListOfCameras(nsTArray<nsString>& aList, mozilla::ErrorResult& aRv);
|
||||
|
||||
nsPIDOMWindowInner* GetParentObject() const { return mWindow; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
static void PreinitCameraHardware();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void XpComShutdown();
|
||||
void Shutdown(uint64_t aWindowId);
|
||||
~nsDOMCameraManager();
|
||||
|
||||
private:
|
||||
nsDOMCameraManager() = delete;
|
||||
explicit nsDOMCameraManager(nsPIDOMWindowInner* aWindow);
|
||||
nsDOMCameraManager(const nsDOMCameraManager&) = delete;
|
||||
nsDOMCameraManager& operator=(const nsDOMCameraManager&) = delete;
|
||||
|
||||
protected:
|
||||
uint64_t mWindowId;
|
||||
uint32_t mPermission;
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
/**
|
||||
* 'sActiveWindows' is only ever accessed while in the Main Thread,
|
||||
* so it is not otherwise protected.
|
||||
*/
|
||||
static ::WindowTable* sActiveWindows;
|
||||
};
|
||||
|
||||
#endif // DOM_CAMERA_DOMCAMERAMANAGER_H
|
|
@ -1,73 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CameraControlImpl.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
namespace mozilla {
|
||||
class RecorderProfileManager;
|
||||
|
||||
namespace layers {
|
||||
class GraphicBufferLocked;
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
/**
|
||||
* Fallback camera control subclass. Can be used as a template for the
|
||||
* definition of new camera support classes.
|
||||
*/
|
||||
class FallbackCameraControl : public CameraControlImpl
|
||||
{
|
||||
public:
|
||||
explicit FallbackCameraControl() : CameraControlImpl() { }
|
||||
|
||||
virtual nsresult Set(uint32_t aKey, const nsAString& aValue) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Get(uint32_t aKey, nsAString& aValue) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Set(uint32_t aKey, double aValue) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Get(uint32_t aKey, double& aValue) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Set(uint32_t aKey, int32_t aValue) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Get(uint32_t aKey, int32_t& aValue) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Set(uint32_t aKey, int64_t aValue) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Get(uint32_t aKey, int64_t& aValue) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Set(uint32_t aKey, bool aValue) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Get(uint32_t aKey, bool& aValue) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Set(uint32_t aKey, const Size& aValue) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Get(uint32_t aKey, Size& aValue) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Set(uint32_t aKey, const nsTArray<Region>& aRegions) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Get(uint32_t aKey, nsTArray<Region>& aRegions) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
|
||||
virtual nsresult SetLocation(const Position& aLocation) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
|
||||
virtual nsresult Get(uint32_t aKey, nsTArray<Size>& aSizes) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Get(uint32_t aKey, nsTArray<nsString>& aValues) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Get(uint32_t aKey, nsTArray<double>& aValues) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
|
||||
virtual nsresult GetRecorderProfiles(nsTArray<nsString>& aProfiles) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual RecorderProfile* GetProfileInfo(const nsAString& aProfile) override { return nullptr; }
|
||||
|
||||
nsresult PushParameters() { return NS_ERROR_NOT_INITIALIZED; }
|
||||
nsresult PullParameters() { return NS_ERROR_NOT_INITIALIZED; }
|
||||
|
||||
protected:
|
||||
~FallbackCameraControl();
|
||||
|
||||
virtual nsresult StartPreviewImpl() override { return NS_ERROR_NOT_INITIALIZED; }
|
||||
virtual nsresult StopPreviewImpl() override { return NS_ERROR_NOT_INITIALIZED; }
|
||||
virtual nsresult AutoFocusImpl() override { return NS_ERROR_NOT_INITIALIZED; }
|
||||
virtual nsresult StartFaceDetectionImpl() override { return NS_ERROR_NOT_INITIALIZED; }
|
||||
virtual nsresult StopFaceDetectionImpl() override { return NS_ERROR_NOT_INITIALIZED; }
|
||||
virtual nsresult TakePictureImpl() override { return NS_ERROR_NOT_INITIALIZED; }
|
||||
virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
|
||||
const StartRecordingOptions* aOptions = nullptr) override
|
||||
{ return NS_ERROR_NOT_INITIALIZED; }
|
||||
virtual nsresult StopRecordingImpl() override { return NS_ERROR_NOT_INITIALIZED; }
|
||||
virtual nsresult PushParametersImpl() override { return NS_ERROR_NOT_INITIALIZED; }
|
||||
virtual nsresult PullParametersImpl() override { return NS_ERROR_NOT_INITIALIZED; }
|
||||
|
||||
private:
|
||||
FallbackCameraControl(const FallbackCameraControl&) = delete;
|
||||
FallbackCameraControl& operator=(const FallbackCameraControl&) = delete;
|
||||
};
|
|
@ -1,32 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ICameraControl.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
// From ICameraControl.
|
||||
nsresult
|
||||
ICameraControl::GetNumberOfCameras(int32_t& aDeviceCount)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
};
|
||||
|
||||
nsresult
|
||||
ICameraControl::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ICameraControl::GetListOfCameras(nsTArray<nsString>& aList)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
already_AddRefed<ICameraControl>
|
||||
ICameraControl::Create(uint32_t aCameraId)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* Copyright (C) 2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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 "FallbackCameraPlatform.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
MediaProfiles* MediaProfiles::sMediaProfiles = nullptr;
|
||||
|
||||
const char CameraParameters::KEY_PREVIEW_SIZE[] = "preview-size";
|
||||
const char CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES[] = "preview-size-values";
|
||||
const char CameraParameters::KEY_PREVIEW_FORMAT[] = "preview-format";
|
||||
const char CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS[] = "preview-format-values";
|
||||
const char CameraParameters::KEY_PREVIEW_FRAME_RATE[] = "preview-frame-rate";
|
||||
const char CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES[] = "preview-frame-rate-values";
|
||||
const char CameraParameters::KEY_PREVIEW_FPS_RANGE[] = "preview-fps-range";
|
||||
const char CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE[] = "preview-fps-range-values";
|
||||
const char CameraParameters::KEY_PICTURE_SIZE[] = "picture-size";
|
||||
const char CameraParameters::KEY_SUPPORTED_PICTURE_SIZES[] = "picture-size-values";
|
||||
const char CameraParameters::KEY_PICTURE_FORMAT[] = "picture-format";
|
||||
const char CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS[] = "picture-format-values";
|
||||
const char CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH[] = "jpeg-thumbnail-width";
|
||||
const char CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT[] = "jpeg-thumbnail-height";
|
||||
const char CameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES[] = "jpeg-thumbnail-size-values";
|
||||
const char CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY[] = "jpeg-thumbnail-quality";
|
||||
const char CameraParameters::KEY_JPEG_QUALITY[] = "jpeg-quality";
|
||||
const char CameraParameters::KEY_ROTATION[] = "rotation";
|
||||
const char CameraParameters::KEY_GPS_LATITUDE[] = "gps-latitude";
|
||||
const char CameraParameters::KEY_GPS_LONGITUDE[] = "gps-longitude";
|
||||
const char CameraParameters::KEY_GPS_ALTITUDE[] = "gps-altitude";
|
||||
const char CameraParameters::KEY_GPS_TIMESTAMP[] = "gps-timestamp";
|
||||
const char CameraParameters::KEY_GPS_PROCESSING_METHOD[] = "gps-processing-method";
|
||||
const char CameraParameters::KEY_WHITE_BALANCE[] = "whitebalance";
|
||||
const char CameraParameters::KEY_SUPPORTED_WHITE_BALANCE[] = "whitebalance-values";
|
||||
const char CameraParameters::KEY_EFFECT[] = "effect";
|
||||
const char CameraParameters::KEY_SUPPORTED_EFFECTS[] = "effect-values";
|
||||
const char CameraParameters::KEY_ANTIBANDING[] = "antibanding";
|
||||
const char CameraParameters::KEY_SUPPORTED_ANTIBANDING[] = "antibanding-values";
|
||||
const char CameraParameters::KEY_SCENE_MODE[] = "scene-mode";
|
||||
const char CameraParameters::KEY_SUPPORTED_SCENE_MODES[] = "scene-mode-values";
|
||||
const char CameraParameters::KEY_FLASH_MODE[] = "flash-mode";
|
||||
const char CameraParameters::KEY_SUPPORTED_FLASH_MODES[] = "flash-mode-values";
|
||||
const char CameraParameters::KEY_FOCUS_MODE[] = "focus-mode";
|
||||
const char CameraParameters::KEY_SUPPORTED_FOCUS_MODES[] = "focus-mode-values";
|
||||
const char CameraParameters::KEY_MAX_NUM_FOCUS_AREAS[] = "max-num-focus-areas";
|
||||
const char CameraParameters::KEY_FOCUS_AREAS[] = "focus-areas";
|
||||
const char CameraParameters::KEY_FOCAL_LENGTH[] = "focal-length";
|
||||
const char CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE[] = "horizontal-view-angle";
|
||||
const char CameraParameters::KEY_VERTICAL_VIEW_ANGLE[] = "vertical-view-angle";
|
||||
const char CameraParameters::KEY_EXPOSURE_COMPENSATION[] = "exposure-compensation";
|
||||
const char CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION[] = "max-exposure-compensation";
|
||||
const char CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION[] = "min-exposure-compensation";
|
||||
const char CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP[] = "exposure-compensation-step";
|
||||
const char CameraParameters::KEY_AUTO_EXPOSURE_LOCK[] = "auto-exposure-lock";
|
||||
const char CameraParameters::KEY_AUTO_EXPOSURE_LOCK_SUPPORTED[] = "auto-exposure-lock-supported";
|
||||
const char CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK[] = "auto-whitebalance-lock";
|
||||
const char CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED[] = "auto-whitebalance-lock-supported";
|
||||
const char CameraParameters::KEY_MAX_NUM_METERING_AREAS[] = "max-num-metering-areas";
|
||||
const char CameraParameters::KEY_METERING_AREAS[] = "metering-areas";
|
||||
const char CameraParameters::KEY_ZOOM[] = "zoom";
|
||||
const char CameraParameters::KEY_MAX_ZOOM[] = "max-zoom";
|
||||
const char CameraParameters::KEY_ZOOM_RATIOS[] = "zoom-ratios";
|
||||
const char CameraParameters::KEY_ZOOM_SUPPORTED[] = "zoom-supported";
|
||||
const char CameraParameters::KEY_SMOOTH_ZOOM_SUPPORTED[] = "smooth-zoom-supported";
|
||||
const char CameraParameters::KEY_FOCUS_DISTANCES[] = "focus-distances";
|
||||
const char CameraParameters::KEY_VIDEO_FRAME_FORMAT[] = "video-frame-format";
|
||||
const char CameraParameters::KEY_VIDEO_SIZE[] = "video-size";
|
||||
const char CameraParameters::KEY_SUPPORTED_VIDEO_SIZES[] = "video-size-values";
|
||||
const char CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[] = "preferred-preview-size-for-video";
|
||||
const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW[] = "max-num-detected-faces-hw";
|
||||
const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW[] = "max-num-detected-faces-sw";
|
||||
const char CameraParameters::KEY_RECORDING_HINT[] = "recording-hint";
|
||||
const char CameraParameters::KEY_VIDEO_SNAPSHOT_SUPPORTED[] = "video-snapshot-supported";
|
||||
const char CameraParameters::KEY_VIDEO_STABILIZATION[] = "video-stabilization";
|
||||
const char CameraParameters::KEY_VIDEO_STABILIZATION_SUPPORTED[] = "video-stabilization-supported";
|
||||
const char CameraParameters::KEY_LIGHTFX[] = "light-fx";
|
||||
|
|
@ -1,304 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* Copyright (C) 2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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 DOM_CAMERA_FALLBACKCAMERAPLATFORM_H
|
||||
#define DOM_CAMERA_FALLBACKCAMERAPLATFORM_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
int32_t id;
|
||||
int32_t score;
|
||||
int32_t rect[4];
|
||||
int32_t left_eye[2];
|
||||
int32_t right_eye[2];
|
||||
int32_t mouth[2];
|
||||
} camera_face_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t number_of_faces;
|
||||
camera_face_t* faces;
|
||||
} camera_frame_metadata_t;
|
||||
|
||||
namespace android {
|
||||
enum camcorder_quality {
|
||||
CAMCORDER_QUALITY_LOW,
|
||||
CAMCORDER_QUALITY_HIGH,
|
||||
CAMCORDER_QUALITY_QCIF,
|
||||
CAMCORDER_QUALITY_CIF,
|
||||
CAMCORDER_QUALITY_480P,
|
||||
CAMCORDER_QUALITY_720P,
|
||||
CAMCORDER_QUALITY_1080P,
|
||||
CAMCORDER_QUALITY_QVGA,
|
||||
CAMCORDER_QUALITY_VGA,
|
||||
CAMCORDER_QUALITY_LIST_START = CAMCORDER_QUALITY_LOW,
|
||||
CAMCORDER_QUALITY_LIST_END = CAMCORDER_QUALITY_VGA
|
||||
};
|
||||
|
||||
enum output_format {
|
||||
OUTPUT_FORMAT_THREE_GPP,
|
||||
OUTPUT_FORMAT_MPEG_4
|
||||
};
|
||||
|
||||
enum video_encoder {
|
||||
VIDEO_ENCODER_H263,
|
||||
VIDEO_ENCODER_H264,
|
||||
VIDEO_ENCODER_MPEG_4_SP
|
||||
};
|
||||
|
||||
enum audio_encoder {
|
||||
AUDIO_ENCODER_AMR_WB,
|
||||
AUDIO_ENCODER_AMR_NB,
|
||||
AUDIO_ENCODER_AAC
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class sp final
|
||||
{
|
||||
public:
|
||||
sp()
|
||||
: mPtr(nullptr)
|
||||
{ }
|
||||
|
||||
sp(T *aPtr)
|
||||
: mPtr(aPtr)
|
||||
{ }
|
||||
|
||||
virtual ~sp() { }
|
||||
T* get() const { return mPtr; }
|
||||
void clear() { mPtr = nullptr; }
|
||||
T* operator->() const { return get(); }
|
||||
|
||||
private:
|
||||
RefPtr<T> mPtr;
|
||||
};
|
||||
|
||||
typedef uint64_t nsecs_t;
|
||||
|
||||
enum error_t {
|
||||
OK = 0,
|
||||
UNKNOWN_ERROR,
|
||||
INVALID_OPERATION
|
||||
};
|
||||
|
||||
enum camera_msg_t {
|
||||
CAMERA_MSG_SHUTTER,
|
||||
CAMERA_MSG_COMPRESSED_IMAGE
|
||||
};
|
||||
|
||||
class String8 final
|
||||
{
|
||||
public:
|
||||
String8() { }
|
||||
String8(const char* aData) { mData.AssignASCII(aData); }
|
||||
virtual ~String8() { }
|
||||
const char* string() const { return mData.Data(); }
|
||||
|
||||
private:
|
||||
nsCString mData;
|
||||
};
|
||||
|
||||
enum camera_facing_t {
|
||||
CAMERA_FACING_BACK,
|
||||
CAMERA_FACING_FRONT
|
||||
};
|
||||
|
||||
struct CameraInfo {
|
||||
camera_facing_t facing;
|
||||
};
|
||||
|
||||
class Camera final : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS;
|
||||
|
||||
void disconnect() { }
|
||||
String8 getParameters() { return String8(); }
|
||||
int setParameters(const String8& aParams) { return UNKNOWN_ERROR; }
|
||||
int storeMetaDataInBuffers(bool aEnabled) { return UNKNOWN_ERROR; }
|
||||
int autoFocus() { return UNKNOWN_ERROR; }
|
||||
int cancelAutoFocus() { return UNKNOWN_ERROR; }
|
||||
int takePicture(uint32_t flags) { return UNKNOWN_ERROR; }
|
||||
int startPreview() { return UNKNOWN_ERROR; }
|
||||
int stopPreview() { return UNKNOWN_ERROR; }
|
||||
int startRecording() { return UNKNOWN_ERROR; }
|
||||
int stopRecording() { return UNKNOWN_ERROR; }
|
||||
int startFaceDetection() { return UNKNOWN_ERROR; }
|
||||
int stopFaceDetection() { return UNKNOWN_ERROR; }
|
||||
static int32_t getNumberOfCameras() { return 2; }
|
||||
|
||||
static int getCameraInfo(int32_t aDevice, CameraInfo* aInfo)
|
||||
{
|
||||
switch (aDevice) {
|
||||
case 0:
|
||||
aInfo->facing = CAMERA_FACING_BACK;
|
||||
break;
|
||||
case 1:
|
||||
aInfo->facing = CAMERA_FACING_FRONT;
|
||||
break;
|
||||
default:
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
Camera() { }
|
||||
virtual ~Camera() { }
|
||||
|
||||
private:
|
||||
Camera(const Camera&) = delete;
|
||||
Camera& operator=(const Camera&) = delete;
|
||||
};
|
||||
|
||||
class CameraParameters final
|
||||
{
|
||||
public:
|
||||
static const char KEY_PREVIEW_SIZE[];
|
||||
static const char KEY_SUPPORTED_PREVIEW_SIZES[];
|
||||
static const char KEY_PREVIEW_FPS_RANGE[];
|
||||
static const char KEY_SUPPORTED_PREVIEW_FPS_RANGE[];
|
||||
static const char KEY_PREVIEW_FORMAT[];
|
||||
static const char KEY_SUPPORTED_PREVIEW_FORMATS[];
|
||||
static const char KEY_PREVIEW_FRAME_RATE[];
|
||||
static const char KEY_SUPPORTED_PREVIEW_FRAME_RATES[];
|
||||
static const char KEY_PICTURE_SIZE[];
|
||||
static const char KEY_SUPPORTED_PICTURE_SIZES[];
|
||||
static const char KEY_PICTURE_FORMAT[];
|
||||
static const char KEY_SUPPORTED_PICTURE_FORMATS[];
|
||||
static const char KEY_JPEG_THUMBNAIL_WIDTH[];
|
||||
static const char KEY_JPEG_THUMBNAIL_HEIGHT[];
|
||||
static const char KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES[];
|
||||
static const char KEY_JPEG_THUMBNAIL_QUALITY[];
|
||||
static const char KEY_JPEG_QUALITY[];
|
||||
static const char KEY_ROTATION[];
|
||||
static const char KEY_GPS_LATITUDE[];
|
||||
static const char KEY_GPS_LONGITUDE[];
|
||||
static const char KEY_GPS_ALTITUDE[];
|
||||
static const char KEY_GPS_TIMESTAMP[];
|
||||
static const char KEY_GPS_PROCESSING_METHOD[];
|
||||
static const char KEY_WHITE_BALANCE[];
|
||||
static const char KEY_SUPPORTED_WHITE_BALANCE[];
|
||||
static const char KEY_EFFECT[];
|
||||
static const char KEY_SUPPORTED_EFFECTS[];
|
||||
static const char KEY_ANTIBANDING[];
|
||||
static const char KEY_SUPPORTED_ANTIBANDING[];
|
||||
static const char KEY_SCENE_MODE[];
|
||||
static const char KEY_SUPPORTED_SCENE_MODES[];
|
||||
static const char KEY_FLASH_MODE[];
|
||||
static const char KEY_SUPPORTED_FLASH_MODES[];
|
||||
static const char KEY_FOCUS_MODE[];
|
||||
static const char KEY_SUPPORTED_FOCUS_MODES[];
|
||||
static const char KEY_MAX_NUM_FOCUS_AREAS[];
|
||||
static const char KEY_FOCUS_AREAS[];
|
||||
static const char KEY_FOCAL_LENGTH[];
|
||||
static const char KEY_HORIZONTAL_VIEW_ANGLE[];
|
||||
static const char KEY_VERTICAL_VIEW_ANGLE[];
|
||||
static const char KEY_EXPOSURE_COMPENSATION[];
|
||||
static const char KEY_MAX_EXPOSURE_COMPENSATION[];
|
||||
static const char KEY_MIN_EXPOSURE_COMPENSATION[];
|
||||
static const char KEY_EXPOSURE_COMPENSATION_STEP[];
|
||||
static const char KEY_AUTO_EXPOSURE_LOCK[];
|
||||
static const char KEY_AUTO_EXPOSURE_LOCK_SUPPORTED[];
|
||||
static const char KEY_AUTO_WHITEBALANCE_LOCK[];
|
||||
static const char KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED[];
|
||||
static const char KEY_MAX_NUM_METERING_AREAS[];
|
||||
static const char KEY_METERING_AREAS[];
|
||||
static const char KEY_ZOOM[];
|
||||
static const char KEY_MAX_ZOOM[];
|
||||
static const char KEY_ZOOM_RATIOS[];
|
||||
static const char KEY_ZOOM_SUPPORTED[];
|
||||
static const char KEY_SMOOTH_ZOOM_SUPPORTED[];
|
||||
static const char KEY_FOCUS_DISTANCES[];
|
||||
static const char KEY_VIDEO_SIZE[];
|
||||
static const char KEY_SUPPORTED_VIDEO_SIZES[];
|
||||
static const char KEY_MAX_NUM_DETECTED_FACES_HW[];
|
||||
static const char KEY_MAX_NUM_DETECTED_FACES_SW[];
|
||||
static const char KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[];
|
||||
static const char KEY_VIDEO_FRAME_FORMAT[];
|
||||
static const char KEY_RECORDING_HINT[];
|
||||
static const char KEY_VIDEO_SNAPSHOT_SUPPORTED[];
|
||||
static const char KEY_VIDEO_STABILIZATION[];
|
||||
static const char KEY_VIDEO_STABILIZATION_SUPPORTED[];
|
||||
static const char KEY_LIGHTFX[];
|
||||
};
|
||||
|
||||
class MediaProfiles final
|
||||
{
|
||||
public:
|
||||
static MediaProfiles* getInstance() {
|
||||
if (!sMediaProfiles) {
|
||||
sMediaProfiles = new MediaProfiles();
|
||||
}
|
||||
return sMediaProfiles;
|
||||
}
|
||||
|
||||
bool hasCamcorderProfile(int aCameraId, camcorder_quality aQuality) const {
|
||||
switch (aQuality) {
|
||||
case CAMCORDER_QUALITY_LOW:
|
||||
case CAMCORDER_QUALITY_HIGH:
|
||||
case CAMCORDER_QUALITY_QVGA:
|
||||
case CAMCORDER_QUALITY_VGA:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int getCamcorderProfileParamByName(const char* aParameter, int aCameraId, camcorder_quality aQuality) const {
|
||||
switch (aQuality) {
|
||||
case CAMCORDER_QUALITY_LOW:
|
||||
case CAMCORDER_QUALITY_QVGA:
|
||||
if (strcmp(aParameter, "vid.width") == 0) {
|
||||
return 320;
|
||||
} else if (strcmp(aParameter, "vid.height") == 0) {
|
||||
return 240;
|
||||
} else if (strcmp(aParameter, "vid.fps") == 0) {
|
||||
return 30;
|
||||
}
|
||||
return 0;
|
||||
case CAMCORDER_QUALITY_HIGH:
|
||||
case CAMCORDER_QUALITY_VGA:
|
||||
if (strcmp(aParameter, "vid.width") == 0) {
|
||||
return 640;
|
||||
} else if (strcmp(aParameter, "vid.height") == 0) {
|
||||
return 480;
|
||||
} else if (strcmp(aParameter, "vid.fps") == 0) {
|
||||
return 30;
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected:
|
||||
MediaProfiles() { }
|
||||
virtual ~MediaProfiles() { }
|
||||
|
||||
private:
|
||||
MediaProfiles(const MediaProfiles&) = delete;
|
||||
MediaProfiles& operator=(const MediaProfiles&) = delete;
|
||||
|
||||
static MediaProfiles* sMediaProfiles;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,237 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef DOM_CAMERA_GONKCAMERACONTROL_H
|
||||
#define DOM_CAMERA_GONKCAMERACONTROL_H
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "DeviceStorage.h"
|
||||
#include "CameraControlImpl.h"
|
||||
#include "CameraCommon.h"
|
||||
#include "GonkCameraHwMgr.h"
|
||||
#include "GonkCameraParameters.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include <media/MediaProfiles.h>
|
||||
#include <camera/Camera.h>
|
||||
#include "GonkRecorder.h"
|
||||
#else
|
||||
#include "FallbackCameraPlatform.h"
|
||||
#endif
|
||||
|
||||
class nsITimer;
|
||||
|
||||
namespace android {
|
||||
class GonkCameraHardware;
|
||||
class GonkRecorder;
|
||||
class GonkCameraSource;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace layers {
|
||||
class TextureClient;
|
||||
class ImageContainer;
|
||||
class Image;
|
||||
}
|
||||
|
||||
class nsGonkCameraControl : public CameraControlImpl
|
||||
{
|
||||
public:
|
||||
nsGonkCameraControl(uint32_t aCameraId);
|
||||
|
||||
void OnAutoFocusMoving(bool aIsMoving);
|
||||
void OnAutoFocusComplete(bool aSuccess, bool aExpired);
|
||||
void OnFacesDetected(camera_frame_metadata_t* aMetaData);
|
||||
void OnTakePictureComplete(uint8_t* aData, uint32_t aLength);
|
||||
void OnTakePictureError();
|
||||
void OnRateLimitPreview(bool aLimit);
|
||||
void OnPoster(void* aData, uint32_t aLength);
|
||||
void OnNewPreviewFrame(layers::TextureClient* aBuffer);
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
void OnRecorderEvent(int msg, int ext1, int ext2);
|
||||
#endif
|
||||
void OnSystemError(CameraControlListener::SystemContext aWhere, nsresult aError);
|
||||
|
||||
// See ICameraControl.h for getter/setter return values.
|
||||
virtual nsresult Set(uint32_t aKey, const nsAString& aValue) override;
|
||||
virtual nsresult Get(uint32_t aKey, nsAString& aValue) override;
|
||||
virtual nsresult Set(uint32_t aKey, double aValue) override;
|
||||
virtual nsresult Get(uint32_t aKey, double& aValue) override;
|
||||
virtual nsresult Set(uint32_t aKey, int32_t aValue) override;
|
||||
virtual nsresult Get(uint32_t aKey, int32_t& aValue) override;
|
||||
virtual nsresult Set(uint32_t aKey, int64_t aValue) override;
|
||||
virtual nsresult Get(uint32_t aKey, int64_t& aValue) override;
|
||||
virtual nsresult Set(uint32_t aKey, bool aValue) override;
|
||||
virtual nsresult Get(uint32_t aKey, bool& aValue) override;
|
||||
virtual nsresult Set(uint32_t aKey, const Size& aValue) override;
|
||||
virtual nsresult Get(uint32_t aKey, Size& aValue) override;
|
||||
virtual nsresult Set(uint32_t aKey, const nsTArray<Region>& aRegions) override;
|
||||
virtual nsresult Get(uint32_t aKey, nsTArray<Region>& aRegions) override;
|
||||
|
||||
virtual nsresult SetLocation(const Position& aLocation) override;
|
||||
|
||||
virtual nsresult Get(uint32_t aKey, nsTArray<Size>& aSizes) override;
|
||||
virtual nsresult Get(uint32_t aKey, nsTArray<nsString>& aValues) override;
|
||||
virtual nsresult Get(uint32_t aKey, nsTArray<double>& aValues) override;
|
||||
|
||||
virtual nsresult GetRecorderProfiles(nsTArray<nsString>& aProfiles) override;
|
||||
virtual ICameraControl::RecorderProfile*
|
||||
GetProfileInfo(const nsAString& aProfile) override;
|
||||
|
||||
nsresult PushParameters();
|
||||
nsresult PullParameters();
|
||||
|
||||
protected:
|
||||
~nsGonkCameraControl();
|
||||
|
||||
using CameraControlImpl::OnRateLimitPreview;
|
||||
using CameraControlImpl::OnNewPreviewFrame;
|
||||
using CameraControlImpl::OnAutoFocusComplete;
|
||||
using CameraControlImpl::OnFacesDetected;
|
||||
using CameraControlImpl::OnTakePictureComplete;
|
||||
using CameraControlImpl::OnConfigurationChange;
|
||||
using CameraControlImpl::OnUserError;
|
||||
|
||||
typedef nsTArray<Size>::index_type SizeIndex;
|
||||
|
||||
virtual void BeginBatchParameterSet() override;
|
||||
virtual void EndBatchParameterSet() override;
|
||||
|
||||
nsresult Initialize();
|
||||
|
||||
nsresult ValidateConfiguration(const Configuration& aConfig, Configuration& aValidatedConfig);
|
||||
nsresult SetConfigurationInternal(const Configuration& aConfig);
|
||||
nsresult SetPictureConfiguration(const Configuration& aConfig);
|
||||
nsresult SetVideoConfiguration(const Configuration& aConfig);
|
||||
nsresult StartInternal(const Configuration* aInitialConfig);
|
||||
nsresult StartPreviewInternal();
|
||||
nsresult StopInternal();
|
||||
|
||||
template<class T> nsresult SetAndPush(uint32_t aKey, const T& aValue);
|
||||
|
||||
// See CameraControlImpl.h for these methods' return values.
|
||||
virtual nsresult StartImpl(const Configuration* aInitialConfig = nullptr) override;
|
||||
virtual nsresult SetConfigurationImpl(const Configuration& aConfig) override;
|
||||
virtual nsresult StopImpl() override;
|
||||
virtual nsresult StartPreviewImpl() override;
|
||||
virtual nsresult StopPreviewImpl() override;
|
||||
virtual nsresult AutoFocusImpl() override;
|
||||
virtual nsresult StartFaceDetectionImpl() override;
|
||||
virtual nsresult StopFaceDetectionImpl() override;
|
||||
virtual nsresult TakePictureImpl() override;
|
||||
virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
|
||||
const StartRecordingOptions* aOptions = nullptr) override;
|
||||
virtual nsresult StopRecordingImpl() override;
|
||||
virtual nsresult PauseRecordingImpl() override;
|
||||
virtual nsresult ResumeRecordingImpl() override;
|
||||
virtual nsresult ResumeContinuousFocusImpl() override;
|
||||
virtual nsresult PushParametersImpl() override;
|
||||
virtual nsresult PullParametersImpl() override;
|
||||
|
||||
nsresult SetupRecording(int aFd, int aRotation, uint64_t aMaxFileSizeBytes,
|
||||
uint64_t aMaxVideoLengthMs);
|
||||
nsresult SetupRecordingFlash(bool aAutoEnableLowLightTorch);
|
||||
nsresult SelectCaptureAndPreviewSize(const Size& aPreviewSize, const Size& aCaptureSize,
|
||||
const Size& aMaxSize, uint32_t aCaptureSizeKey);
|
||||
nsresult MaybeAdjustVideoSize();
|
||||
nsresult PausePreview();
|
||||
nsresult GetSupportedSize(const Size& aSize, const nsTArray<Size>& supportedSizes, Size& best);
|
||||
|
||||
void CreatePoster(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight, int32_t aRotation);
|
||||
|
||||
nsresult LoadRecorderProfiles();
|
||||
|
||||
friend class SetPictureSize;
|
||||
friend class SetThumbnailSize;
|
||||
nsresult SetPictureSize(const Size& aSize);
|
||||
nsresult SetPictureSizeImpl(const Size& aSize);
|
||||
nsresult SetThumbnailSize(const Size& aSize);
|
||||
nsresult UpdateThumbnailSize();
|
||||
nsresult SetThumbnailSizeImpl(const Size& aSize);
|
||||
|
||||
friend class android::GonkCameraSource;
|
||||
android::sp<android::GonkCameraHardware> GetCameraHw();
|
||||
|
||||
int32_t RationalizeRotation(int32_t aRotation);
|
||||
|
||||
uint32_t mCameraId;
|
||||
|
||||
android::sp<android::GonkCameraHardware> mCameraHw;
|
||||
|
||||
Size mLastThumbnailSize;
|
||||
Size mLastRecorderSize;
|
||||
Size mRequestedPreviewSize;
|
||||
uint32_t mPreviewFps;
|
||||
bool mResumePreviewAfterTakingPicture;
|
||||
bool mFlashSupported;
|
||||
bool mLuminanceSupported;
|
||||
bool mAutoFlashModeOverridden;
|
||||
bool mSeparateVideoAndPreviewSizesSupported;
|
||||
Atomic<uint32_t> mDeferConfigUpdate;
|
||||
GonkCameraParameters mParams;
|
||||
|
||||
RefPtr<mozilla::layers::ImageContainer> mImageContainer;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
RefPtr<android::GonkRecorder> mRecorder;
|
||||
#endif
|
||||
// Touching mRecorder happens inside this monitor because the destructor
|
||||
// can run on any thread, and we need to be able to clean up properly if
|
||||
// GonkCameraControl goes away.
|
||||
ReentrantMonitor mRecorderMonitor;
|
||||
|
||||
// Supported recorder profiles
|
||||
nsRefPtrHashtable<nsStringHashKey, RecorderProfile> mRecorderProfiles;
|
||||
|
||||
RefPtr<DeviceStorageFile> mVideoFile;
|
||||
nsString mFileFormat;
|
||||
|
||||
Atomic<bool> mCapturePoster;
|
||||
int32_t mVideoRotation;
|
||||
|
||||
bool mAutoFocusPending;
|
||||
nsCOMPtr<nsITimer> mAutoFocusCompleteTimer;
|
||||
int32_t mAutoFocusCompleteExpired;
|
||||
|
||||
uint32_t mPrevFacesDetected;
|
||||
|
||||
// Guards against calling StartPreviewImpl() while in OnTakePictureComplete().
|
||||
ReentrantMonitor mReentrantMonitor;
|
||||
|
||||
private:
|
||||
nsGonkCameraControl(const nsGonkCameraControl&) = delete;
|
||||
nsGonkCameraControl& operator=(const nsGonkCameraControl&) = delete;
|
||||
};
|
||||
|
||||
// camera driver callbacks
|
||||
void OnRateLimitPreview(nsGonkCameraControl* gc, bool aLimit);
|
||||
void OnTakePictureComplete(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength);
|
||||
void OnTakePictureError(nsGonkCameraControl* gc);
|
||||
void OnAutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess);
|
||||
void OnAutoFocusMoving(nsGonkCameraControl* gc, bool aIsMoving);
|
||||
void OnFacesDetected(nsGonkCameraControl* gc, camera_frame_metadata_t* aMetaData);
|
||||
void OnNewPreviewFrame(nsGonkCameraControl* gc, layers::TextureClient* aBuffer);
|
||||
void OnShutter(nsGonkCameraControl* gc);
|
||||
void OnSystemError(nsGonkCameraControl* gc,
|
||||
CameraControlListener::SystemContext aWhere,
|
||||
int32_t aArg1, int32_t aArg2);
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_CAMERA_GONKCAMERACONTROL_H
|
|
@ -1,520 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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 "GonkCameraHwMgr.h"
|
||||
#include "TestGonkCameraHardware.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <sys/system_properties.h>
|
||||
#include "GonkNativeWindow.h"
|
||||
#endif
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "nsDebug.h"
|
||||
#include "mozilla/layers/TextureClient.h"
|
||||
#include "CameraPreferences.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
|
||||
#include "GonkBufferQueueProducer.h"
|
||||
#endif
|
||||
#include "GonkCameraControl.h"
|
||||
#include "CameraCommon.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::layers;
|
||||
using namespace android;
|
||||
|
||||
#ifndef MOZ_WIDGET_GONK
|
||||
NS_IMPL_ISUPPORTS0(GonkCameraHardware);
|
||||
NS_IMPL_ISUPPORTS0(android::Camera);
|
||||
#endif
|
||||
|
||||
GonkCameraHardware::GonkCameraHardware(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId, const sp<Camera>& aCamera)
|
||||
: mCameraId(aCameraId)
|
||||
, mClosing(false)
|
||||
, mNumFrames(0)
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
, mCamera(aCamera)
|
||||
#endif
|
||||
, mTarget(aTarget)
|
||||
, mRawSensorOrientation(0)
|
||||
, mSensorOrientation(0)
|
||||
, mEmulated(false)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p (aTarget=%p)\n", __func__, __LINE__, (void*)this, (void*)aTarget);
|
||||
}
|
||||
|
||||
void
|
||||
GonkCameraHardware::OnRateLimitPreview(bool aLimit)
|
||||
{
|
||||
::OnRateLimitPreview(mTarget, aLimit);
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
void
|
||||
GonkCameraHardware::OnNewFrame()
|
||||
{
|
||||
if (mClosing) {
|
||||
return;
|
||||
}
|
||||
RefPtr<TextureClient> buffer = mNativeWindow->getCurrentBuffer();
|
||||
if (!buffer) {
|
||||
DOM_CAMERA_LOGE("received null frame");
|
||||
return;
|
||||
}
|
||||
OnNewPreviewFrame(mTarget, buffer);
|
||||
}
|
||||
|
||||
// Android data callback
|
||||
void
|
||||
GonkCameraHardware::postData(int32_t aMsgType, const sp<IMemory>& aDataPtr, camera_frame_metadata_t* metadata)
|
||||
{
|
||||
if (mClosing) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aMsgType) {
|
||||
case CAMERA_MSG_PREVIEW_FRAME:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case CAMERA_MSG_COMPRESSED_IMAGE:
|
||||
if (aDataPtr != nullptr) {
|
||||
OnTakePictureComplete(mTarget, static_cast<uint8_t*>(aDataPtr->pointer()), aDataPtr->size());
|
||||
} else {
|
||||
OnTakePictureError(mTarget);
|
||||
}
|
||||
break;
|
||||
|
||||
case CAMERA_MSG_PREVIEW_METADATA:
|
||||
OnFacesDetected(mTarget, metadata);
|
||||
break;
|
||||
|
||||
default:
|
||||
DOM_CAMERA_LOGE("Unhandled data callback event %d\n", aMsgType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Android notify callback
|
||||
void
|
||||
GonkCameraHardware::notify(int32_t aMsgType, int32_t ext1, int32_t ext2)
|
||||
{
|
||||
if (mClosing) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aMsgType) {
|
||||
case CAMERA_MSG_FOCUS:
|
||||
OnAutoFocusComplete(mTarget, !!ext1);
|
||||
break;
|
||||
|
||||
#if ANDROID_VERSION >= 16
|
||||
case CAMERA_MSG_FOCUS_MOVE:
|
||||
OnAutoFocusMoving(mTarget, !!ext1);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case CAMERA_MSG_SHUTTER:
|
||||
OnShutter(mTarget);
|
||||
break;
|
||||
|
||||
case CAMERA_MSG_ERROR:
|
||||
OnSystemError(mTarget, CameraControlListener::kSystemService, ext1, ext2);
|
||||
break;
|
||||
|
||||
default:
|
||||
DOM_CAMERA_LOGE("Unhandled notify callback event %d\n", aMsgType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GonkCameraHardware::postDataTimestamp(nsecs_t aTimestamp, int32_t aMsgType, const sp<IMemory>& aDataPtr)
|
||||
{
|
||||
DOM_CAMERA_LOGI("%s",__func__);
|
||||
if (mClosing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mListener.get()) {
|
||||
DOM_CAMERA_LOGI("Listener registered, posting recording frame!");
|
||||
if (!mListener->postDataTimestamp(aTimestamp, aMsgType, aDataPtr)) {
|
||||
DOM_CAMERA_LOGW("Listener unable to process. Drop a recording frame.");
|
||||
mCamera->releaseRecordingFrame(aDataPtr);
|
||||
}
|
||||
} else {
|
||||
DOM_CAMERA_LOGW("No listener was set. Drop a recording frame.");
|
||||
mCamera->releaseRecordingFrame(aDataPtr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
GonkCameraHardware::Init()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s: this=%p\n", __func__, (void* )this);
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
CameraInfo info;
|
||||
int rv = Camera::getCameraInfo(mCameraId, &info);
|
||||
if (rv != 0) {
|
||||
DOM_CAMERA_LOGE("%s: failed to get CameraInfo mCameraId %d\n", __func__, mCameraId);
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
mRawSensorOrientation = info.orientation;
|
||||
mSensorOrientation = mRawSensorOrientation;
|
||||
|
||||
/**
|
||||
* Non-V4L2-based camera driver adds extra offset onto picture orientation
|
||||
* set by gecko, so we have to adjust it back.
|
||||
*/
|
||||
char propname[PROP_NAME_MAX];
|
||||
char prop[PROP_VALUE_MAX];
|
||||
int offset = 0;
|
||||
snprintf(propname, sizeof(propname), "ro.moz.cam.%d.sensor_offset", mCameraId);
|
||||
if (__system_property_get(propname, prop) > 0) {
|
||||
offset = clamped(atoi(prop), 0, 270);
|
||||
mSensorOrientation += offset;
|
||||
mSensorOrientation %= 360;
|
||||
}
|
||||
DOM_CAMERA_LOGI("Sensor orientation: base=%d, offset=%d, final=%d\n", info.orientation, offset, mSensorOrientation);
|
||||
|
||||
if (__system_property_get("ro.kernel.qemu", prop) > 0 && atoi(prop)) {
|
||||
DOM_CAMERA_LOGI("Using emulated camera\n");
|
||||
mEmulated = true;
|
||||
}
|
||||
|
||||
// Disable shutter sound in android CameraService because gaia camera app will play it
|
||||
mCamera->sendCommand(CAMERA_CMD_ENABLE_SHUTTER_SOUND, 0, 0);
|
||||
|
||||
#if ANDROID_VERSION >= 21
|
||||
sp<IGraphicBufferProducer> producer;
|
||||
sp<IGonkGraphicBufferConsumer> consumer;
|
||||
GonkBufferQueue::createBufferQueue(&producer, &consumer);
|
||||
static_cast<GonkBufferQueueProducer*>(producer.get())->setSynchronousMode(false);
|
||||
mNativeWindow = new GonkNativeWindow(consumer, GonkCameraHardware::MIN_UNDEQUEUED_BUFFERS);
|
||||
mCamera->setPreviewTarget(producer);
|
||||
#elif ANDROID_VERSION >= 19
|
||||
mNativeWindow = new GonkNativeWindow(GonkCameraHardware::MIN_UNDEQUEUED_BUFFERS);
|
||||
sp<GonkBufferQueue> bq = mNativeWindow->getBufferQueue();
|
||||
bq->setSynchronousMode(false);
|
||||
mCamera->setPreviewTarget(mNativeWindow->getBufferQueue());
|
||||
#elif ANDROID_VERSION >= 17
|
||||
mNativeWindow = new GonkNativeWindow(GonkCameraHardware::MIN_UNDEQUEUED_BUFFERS);
|
||||
sp<GonkBufferQueue> bq = mNativeWindow->getBufferQueue();
|
||||
bq->setSynchronousMode(false);
|
||||
mCamera->setPreviewTexture(mNativeWindow->getBufferQueue());
|
||||
#else
|
||||
mNativeWindow = new GonkNativeWindow();
|
||||
mCamera->setPreviewTexture(mNativeWindow);
|
||||
#endif
|
||||
mNativeWindow->setNewFrameCallback(this);
|
||||
mCamera->setListener(this);
|
||||
|
||||
#if ANDROID_VERSION >= 16
|
||||
rv = mCamera->sendCommand(CAMERA_CMD_ENABLE_FOCUS_MOVE_MSG, 1, 0);
|
||||
if (rv != OK) {
|
||||
NS_WARNING("Failed to send command CAMERA_CMD_ENABLE_FOCUS_MOVE_MSG");
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
sp<GonkCameraHardware>
|
||||
GonkCameraHardware::Connect(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId)
|
||||
{
|
||||
sp<Camera> camera;
|
||||
|
||||
nsCString test;
|
||||
CameraPreferences::GetPref("camera.control.test.enabled", test);
|
||||
|
||||
if (!test.EqualsASCII("hardware")) {
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#if ANDROID_VERSION >= 18
|
||||
camera = Camera::connect(aCameraId, /* clientPackageName */String16("gonk.camera"), Camera::USE_CALLING_UID);
|
||||
#else
|
||||
camera = Camera::connect(aCameraId);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (camera.get() == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
sp<GonkCameraHardware> cameraHardware;
|
||||
if (test.EqualsASCII("hardware")) {
|
||||
NS_WARNING("Using test Gonk hardware layer");
|
||||
cameraHardware = new TestGonkCameraHardware(aTarget, aCameraId, camera);
|
||||
} else {
|
||||
cameraHardware = new GonkCameraHardware(aTarget, aCameraId, camera);
|
||||
}
|
||||
|
||||
nsresult rv = cameraHardware->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Failed to initialize camera hardware (0x%X)\n", rv);
|
||||
cameraHardware->Close();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return cameraHardware;
|
||||
}
|
||||
|
||||
void
|
||||
GonkCameraHardware::Close()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, (void*)this);
|
||||
|
||||
mClosing = true;
|
||||
if (mCamera.get()) {
|
||||
mCamera->stopPreview();
|
||||
mCamera->disconnect();
|
||||
}
|
||||
mCamera.clear();
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
if (mNativeWindow.get()) {
|
||||
mNativeWindow->abandon();
|
||||
}
|
||||
mNativeWindow.clear();
|
||||
|
||||
// Ensure that ICamera's destructor is actually executed
|
||||
IPCThreadState::self()->flushCommands();
|
||||
#endif
|
||||
}
|
||||
|
||||
GonkCameraHardware::~GonkCameraHardware()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, (void*)this);
|
||||
mCamera.clear();
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
mNativeWindow.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
GonkCameraHardware::GetSensorOrientation(uint32_t aType)
|
||||
{
|
||||
DOM_CAMERA_LOGI("%s\n", __func__);
|
||||
|
||||
switch (aType) {
|
||||
case OFFSET_SENSOR_ORIENTATION:
|
||||
return mSensorOrientation;
|
||||
|
||||
case RAW_SENSOR_ORIENTATION:
|
||||
return mRawSensorOrientation;
|
||||
|
||||
default:
|
||||
DOM_CAMERA_LOGE("%s:%d : unknown aType=%d\n", __func__, __LINE__, aType);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
GonkCameraHardware::IsEmulated()
|
||||
{
|
||||
return mEmulated;
|
||||
}
|
||||
|
||||
int
|
||||
GonkCameraHardware::AutoFocus()
|
||||
{
|
||||
DOM_CAMERA_LOGI("%s\n", __func__);
|
||||
if (NS_WARN_IF(mClosing)) {
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
return mCamera->autoFocus();
|
||||
}
|
||||
|
||||
int
|
||||
GonkCameraHardware::CancelAutoFocus()
|
||||
{
|
||||
DOM_CAMERA_LOGI("%s\n", __func__);
|
||||
if (NS_WARN_IF(mClosing)) {
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
return mCamera->cancelAutoFocus();
|
||||
}
|
||||
|
||||
int
|
||||
GonkCameraHardware::StartFaceDetection()
|
||||
{
|
||||
DOM_CAMERA_LOGI("%s\n", __func__);
|
||||
if (NS_WARN_IF(mClosing)) {
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
|
||||
int rv = INVALID_OPERATION;
|
||||
#if ANDROID_VERSION >= 15
|
||||
rv = mCamera->sendCommand(CAMERA_CMD_START_FACE_DETECTION, CAMERA_FACE_DETECTION_HW, 0);
|
||||
#endif
|
||||
if (rv != OK) {
|
||||
DOM_CAMERA_LOGE("Start face detection failed with status %d", rv);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
GonkCameraHardware::StopFaceDetection()
|
||||
{
|
||||
DOM_CAMERA_LOGI("%s\n", __func__);
|
||||
if (mClosing) {
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
|
||||
int rv = INVALID_OPERATION;
|
||||
#if ANDROID_VERSION >= 15
|
||||
rv = mCamera->sendCommand(CAMERA_CMD_STOP_FACE_DETECTION, 0, 0);
|
||||
#endif
|
||||
if (rv != OK) {
|
||||
DOM_CAMERA_LOGE("Stop face detection failed with status %d", rv);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
GonkCameraHardware::TakePicture()
|
||||
{
|
||||
if (NS_WARN_IF(mClosing)) {
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
return mCamera->takePicture(CAMERA_MSG_SHUTTER | CAMERA_MSG_COMPRESSED_IMAGE);
|
||||
}
|
||||
|
||||
void
|
||||
GonkCameraHardware::CancelTakePicture()
|
||||
{
|
||||
DOM_CAMERA_LOGW("%s: android::Camera do not provide this capability\n", __func__);
|
||||
}
|
||||
|
||||
int
|
||||
GonkCameraHardware::PushParameters(const GonkCameraParameters& aParams)
|
||||
{
|
||||
if (NS_WARN_IF(mClosing)) {
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
const String8 s = aParams.Flatten();
|
||||
return mCamera->setParameters(s);
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkCameraHardware::PullParameters(GonkCameraParameters& aParams)
|
||||
{
|
||||
if (NS_WARN_IF(mClosing)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
const String8 s = mCamera->getParameters();
|
||||
return aParams.Unflatten(s);
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
int
|
||||
GonkCameraHardware::PushParameters(const CameraParameters& aParams)
|
||||
{
|
||||
if (NS_WARN_IF(mClosing)) {
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
String8 s = aParams.flatten();
|
||||
return mCamera->setParameters(s);
|
||||
}
|
||||
|
||||
void
|
||||
GonkCameraHardware::PullParameters(CameraParameters& aParams)
|
||||
{
|
||||
if (!NS_WARN_IF(mClosing)) {
|
||||
const String8 s = mCamera->getParameters();
|
||||
aParams.unflatten(s);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
GonkCameraHardware::StartPreview()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
if (NS_WARN_IF(mClosing)) {
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
return mCamera->startPreview();
|
||||
}
|
||||
|
||||
void
|
||||
GonkCameraHardware::StopPreview()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
if (!mClosing) {
|
||||
mCamera->stopPreview();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
GonkCameraHardware::StartRecording()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
if (NS_WARN_IF(mClosing)) {
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
|
||||
int rv = mCamera->startRecording();
|
||||
if (rv != OK) {
|
||||
DOM_CAMERA_LOGE("mHardware->startRecording() failed with status %d", rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
GonkCameraHardware::StopRecording()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
if (mClosing) {
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
mCamera->stopRecording();
|
||||
return OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
int
|
||||
GonkCameraHardware::SetListener(const sp<GonkCameraListener>& aListener)
|
||||
{
|
||||
mListener = aListener;
|
||||
return OK;
|
||||
}
|
||||
|
||||
void
|
||||
GonkCameraHardware::ReleaseRecordingFrame(const sp<IMemory>& aFrame)
|
||||
{
|
||||
if (!NS_WARN_IF(mClosing)) {
|
||||
mCamera->releaseRecordingFrame(aFrame);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
GonkCameraHardware::StoreMetaDataInBuffers(bool aEnabled)
|
||||
{
|
||||
if (NS_WARN_IF(mClosing)) {
|
||||
return DEAD_OBJECT;
|
||||
}
|
||||
return mCamera->storeMetaDataInBuffers(aEnabled);
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef DOM_CAMERA_GONKCAMERAHWMGR_H
|
||||
#define DOM_CAMERA_GONKCAMERAHWMGR_H
|
||||
|
||||
#include "GonkCameraControl.h"
|
||||
#include "CameraCommon.h"
|
||||
#include "GonkCameraParameters.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include <binder/IMemory.h>
|
||||
#include <camera/Camera.h>
|
||||
#include <camera/CameraParameters.h>
|
||||
#include <utils/threads.h>
|
||||
#include "GonkCameraListener.h"
|
||||
#include "GonkNativeWindow.h"
|
||||
#else
|
||||
#include "FallbackCameraPlatform.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
class nsGonkCameraControl;
|
||||
class GonkCameraParameters;
|
||||
}
|
||||
|
||||
namespace android {
|
||||
|
||||
class GonkCameraHardware
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
: public GonkNativeWindowNewFrameCallback
|
||||
, public CameraListener
|
||||
#else
|
||||
: public nsISupports
|
||||
#endif
|
||||
{
|
||||
#ifndef MOZ_WIDGET_GONK
|
||||
NS_DECL_ISUPPORTS
|
||||
#endif
|
||||
|
||||
protected:
|
||||
GonkCameraHardware(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId, const sp<Camera>& aCamera);
|
||||
virtual ~GonkCameraHardware();
|
||||
|
||||
// Initialize the AOSP camera interface.
|
||||
//
|
||||
// Return values:
|
||||
// - NS_OK on success;
|
||||
// - NS_ERROR_NOT_INITIALIZED if the interface could not be initialized.
|
||||
virtual nsresult Init();
|
||||
|
||||
public:
|
||||
static sp<GonkCameraHardware> Connect(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId);
|
||||
virtual void Close();
|
||||
|
||||
virtual void OnRateLimitPreview(bool aLimit);
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// derived from GonkNativeWindowNewFrameCallback
|
||||
virtual void OnNewFrame() override;
|
||||
|
||||
// derived from CameraListener
|
||||
virtual void notify(int32_t aMsgType, int32_t ext1, int32_t ext2);
|
||||
virtual void postData(int32_t aMsgType, const sp<IMemory>& aDataPtr, camera_frame_metadata_t* metadata);
|
||||
virtual void postDataTimestamp(nsecs_t aTimestamp, int32_t aMsgType, const sp<IMemory>& aDataPtr);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The physical orientation of the camera sensor: 0, 90, 180, or 270.
|
||||
*
|
||||
* For example, suppose a device has a naturally tall screen. The
|
||||
* back-facing camera sensor is mounted in landscape. You are looking at
|
||||
* the screen. If the top side of the camera sensor is aligned with the
|
||||
* right edge of the screen in natural orientation, the value should be
|
||||
* 90. If the top side of a front-facing camera sensor is aligned with the
|
||||
* right of the screen, the value should be 270.
|
||||
*
|
||||
* RAW_SENSOR_ORIENTATION is the uncorrected orientation returned directly
|
||||
* by get_camera_info(); OFFSET_SENSOR_ORIENTATION is the offset adjusted
|
||||
* orientation.
|
||||
*/
|
||||
enum {
|
||||
RAW_SENSOR_ORIENTATION,
|
||||
OFFSET_SENSOR_ORIENTATION
|
||||
};
|
||||
virtual int GetSensorOrientation(uint32_t aType = RAW_SENSOR_ORIENTATION);
|
||||
|
||||
virtual bool IsEmulated();
|
||||
|
||||
/**
|
||||
* MIN_UNDEQUEUED_BUFFERS has increased to 4 since Android JB. For FFOS, more
|
||||
* than 3 gralloc buffers are necessary between ImageHost and GonkBufferQueue
|
||||
* for consuming preview stream. To keep the stability for older platform, we
|
||||
* set MIN_UNDEQUEUED_BUFFERS to 4 only in Android KK base.
|
||||
* See also bug 988704.
|
||||
*/
|
||||
enum { MIN_UNDEQUEUED_BUFFERS = 4};
|
||||
|
||||
virtual int AutoFocus();
|
||||
virtual int CancelAutoFocus();
|
||||
virtual int StartFaceDetection();
|
||||
virtual int StopFaceDetection();
|
||||
virtual int TakePicture();
|
||||
virtual void CancelTakePicture();
|
||||
virtual int StartPreview();
|
||||
virtual void StopPreview();
|
||||
virtual int PushParameters(const mozilla::GonkCameraParameters& aParams);
|
||||
virtual nsresult PullParameters(mozilla::GonkCameraParameters& aParams);
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
virtual int PushParameters(const CameraParameters& aParams);
|
||||
virtual void PullParameters(CameraParameters& aParams);
|
||||
virtual int SetListener(const sp<GonkCameraListener>& aListener);
|
||||
virtual void ReleaseRecordingFrame(const sp<IMemory>& aFrame);
|
||||
#endif
|
||||
virtual int StartRecording();
|
||||
virtual int StopRecording();
|
||||
virtual int StoreMetaDataInBuffers(bool aEnabled);
|
||||
|
||||
protected:
|
||||
uint32_t mCameraId;
|
||||
bool mClosing;
|
||||
uint32_t mNumFrames;
|
||||
sp<Camera> mCamera;
|
||||
mozilla::nsGonkCameraControl* mTarget;
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
sp<GonkNativeWindow> mNativeWindow;
|
||||
sp<GonkCameraListener> mListener;
|
||||
#endif
|
||||
int mRawSensorOrientation;
|
||||
int mSensorOrientation;
|
||||
bool mEmulated;
|
||||
|
||||
private:
|
||||
GonkCameraHardware(const GonkCameraHardware&) = delete;
|
||||
GonkCameraHardware& operator=(const GonkCameraHardware&) = delete;
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // GONK_IMPL_HW_MGR_H
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2008 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 GONK_CAMERA_LISTENER_H
|
||||
#define GONK_CAMERA_LISTENER_H
|
||||
|
||||
#include <utils/Timers.h>
|
||||
#include <camera/Camera.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
// ref-counted object for callbacks
|
||||
class GonkCameraListener: virtual public RefBase
|
||||
{
|
||||
public:
|
||||
virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) = 0;
|
||||
virtual bool postData(int32_t msgType, const sp<IMemory>& dataPtr,
|
||||
camera_frame_metadata_t *metadata) = 0;
|
||||
virtual bool postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) = 0;
|
||||
};
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif
|
|
@ -1,137 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ICameraControl.h"
|
||||
#include "CameraCommon.h"
|
||||
#include "GonkCameraControl.h"
|
||||
#include "CameraPreferences.h"
|
||||
#include "TestGonkCameraControl.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include <camera/Camera.h>
|
||||
#else
|
||||
#include "FallbackCameraPlatform.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
// From ICameraControl, gonk-specific management functions
|
||||
nsresult
|
||||
ICameraControl::GetNumberOfCameras(int32_t& aDeviceCount)
|
||||
{
|
||||
aDeviceCount = android::Camera::getNumberOfCameras();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ICameraControl::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName)
|
||||
{
|
||||
int32_t count = android::Camera::getNumberOfCameras();
|
||||
int32_t deviceNum = static_cast<int32_t>(aDeviceNum);
|
||||
|
||||
DOM_CAMERA_LOGI("GetCameraName : getNumberOfCameras() returned %d\n", count);
|
||||
if (deviceNum < 0 || deviceNum > count) {
|
||||
DOM_CAMERA_LOGE("GetCameraName : invalid device number (%u)\n", aDeviceNum);
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
android::CameraInfo info;
|
||||
int rv = android::Camera::getCameraInfo(deviceNum, &info);
|
||||
if (rv != 0) {
|
||||
DOM_CAMERA_LOGE("GetCameraName : get_camera_info(%d) failed: %d\n", deviceNum, rv);
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
switch (info.facing) {
|
||||
case CAMERA_FACING_BACK:
|
||||
aDeviceName.AssignLiteral("back");
|
||||
break;
|
||||
|
||||
case CAMERA_FACING_FRONT:
|
||||
aDeviceName.AssignLiteral("front");
|
||||
break;
|
||||
|
||||
default:
|
||||
aDeviceName.AssignLiteral("extra-camera-");
|
||||
aDeviceName.AppendInt(deviceNum);
|
||||
break;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ICameraControl::GetListOfCameras(nsTArray<nsString>& aList)
|
||||
{
|
||||
int32_t count = android::Camera::getNumberOfCameras();
|
||||
DOM_CAMERA_LOGI("getListOfCameras : getNumberOfCameras() returned %d\n", count);
|
||||
if (count <= 0) {
|
||||
aList.Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Allocate 2 extra slots to reserve space for 'front' and 'back' cameras
|
||||
// at the front of the array--we will collapse any empty slots below.
|
||||
aList.SetLength(2);
|
||||
uint32_t extraIdx = 2;
|
||||
bool gotFront = false;
|
||||
bool gotBack = false;
|
||||
while (count--) {
|
||||
nsCString cameraName;
|
||||
nsresult result = GetCameraName(count, cameraName);
|
||||
if (result != NS_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The first camera we find named 'back' gets slot 0; and the first
|
||||
// we find named 'front' gets slot 1. All others appear after these.
|
||||
if (cameraName.EqualsLiteral("back")) {
|
||||
CopyUTF8toUTF16(cameraName, aList[0]);
|
||||
gotBack = true;
|
||||
} else if (cameraName.EqualsLiteral("front")) {
|
||||
CopyUTF8toUTF16(cameraName, aList[1]);
|
||||
gotFront = true;
|
||||
} else {
|
||||
CopyUTF8toUTF16(cameraName, *aList.InsertElementAt(extraIdx));
|
||||
extraIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gotFront) {
|
||||
aList.RemoveElementAt(1);
|
||||
}
|
||||
|
||||
if (!gotBack) {
|
||||
aList.RemoveElementAt(0);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// implementation-specific camera factory
|
||||
already_AddRefed<ICameraControl>
|
||||
ICameraControl::Create(uint32_t aCameraId)
|
||||
{
|
||||
nsCString test;
|
||||
CameraPreferences::GetPref("camera.control.test.enabled", test);
|
||||
RefPtr<nsGonkCameraControl> control;
|
||||
if (test.EqualsASCII("control")) {
|
||||
NS_WARNING("Using test CameraControl layer");
|
||||
control = new TestGonkCameraControl(aCameraId);
|
||||
} else {
|
||||
control = new nsGonkCameraControl(aCameraId);
|
||||
}
|
||||
return control.forget();
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,301 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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 DOM_CAMERA_GONKCAMERAPARAMETERS_H
|
||||
#define DOM_CAMERA_GONKCAMERAPARAMETERS_H
|
||||
|
||||
#include <math.h>
|
||||
#include "nsTArray.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "ICameraControl.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include <camera/CameraParameters.h>
|
||||
#else
|
||||
#include "FallbackCameraPlatform.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class GonkCameraParameters
|
||||
{
|
||||
public:
|
||||
GonkCameraParameters();
|
||||
virtual ~GonkCameraParameters();
|
||||
|
||||
// IMPORTANT: This class is read and written by multiple threads --
|
||||
// ALL public methods must hold mLock, for either reading or writing,
|
||||
// for the life of their operation. Not doing so was the cause of
|
||||
// bug 928856, which was -painful- to track down.
|
||||
//
|
||||
// Return values:
|
||||
// - see return values for GetTranslated() and SetTranslated() below.
|
||||
template<class T> nsresult
|
||||
Set(uint32_t aKey, const T& aValue)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
nsresult rv = SetTranslated(aKey, aValue);
|
||||
mDirty = mDirty || NS_SUCCEEDED(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
template<class T> nsresult
|
||||
Get(uint32_t aKey, T& aValue)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
return GetTranslated(aKey, aValue);
|
||||
}
|
||||
|
||||
bool
|
||||
TestAndClearDirtyFlag()
|
||||
{
|
||||
bool dirty;
|
||||
|
||||
MutexAutoLock lock(mLock);
|
||||
dirty = mDirty;
|
||||
mDirty = false;
|
||||
return dirty;
|
||||
}
|
||||
|
||||
android::String8 Flatten() const;
|
||||
nsresult Unflatten(const android::String8& aFlatParameters);
|
||||
|
||||
protected:
|
||||
mutable Mutex mLock;
|
||||
bool mDirty;
|
||||
bool mInitialized;
|
||||
|
||||
// Required internal properties
|
||||
double mExposureCompensationStep;
|
||||
int32_t mExposureCompensationMinIndex;
|
||||
int32_t mExposureCompensationMaxIndex;
|
||||
const char* mVendorSpecificKeyIsoMode;
|
||||
const char* mVendorSpecificKeySupportedIsoModes;
|
||||
nsTArray<int> mZoomRatios;
|
||||
nsTArray<nsString> mIsoModes;
|
||||
nsTArray<nsString> mSceneModes;
|
||||
nsTArray<nsString> mMeteringModes;
|
||||
nsClassHashtable<nsStringHashKey, nsCString> mIsoModeMap;
|
||||
nsClassHashtable<nsCStringHashKey, nsCString> mParams;
|
||||
|
||||
nsresult SetImpl(const char* aKey, const char* aValue)
|
||||
{
|
||||
if (!aValue || strchr(aValue, ';') || strchr(aValue, '=')) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
nsDependentCString key(aKey);
|
||||
mParams.Put(key, new nsCString(aValue));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult SetImpl(const char* aKey, int aValue)
|
||||
{
|
||||
nsDependentCString key(aKey);
|
||||
nsCString* value = new nsCString();
|
||||
value->AppendInt(aValue);
|
||||
mParams.Put(key, value);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult SetImpl(const char* aKey, double aValue)
|
||||
{
|
||||
nsDependentCString key(aKey);
|
||||
nsCString* value = new nsCString();
|
||||
value->AppendFloat(aValue);
|
||||
mParams.Put(key, value);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult SetImpl(const char* aKey, float aValue)
|
||||
{
|
||||
nsDependentCString key(aKey);
|
||||
nsCString* value = new nsCString();
|
||||
value->AppendFloat(aValue);
|
||||
mParams.Put(key, value);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult SetImpl(const char* aKey, bool aValue)
|
||||
{
|
||||
nsDependentCString key(aKey);
|
||||
mParams.Put(key, new nsCString(aValue ? "true" : "false"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult GetImpl(const char* aKey, const char*& aRet)
|
||||
{
|
||||
nsDependentCString key(aKey);
|
||||
nsCString* value;
|
||||
if (!mParams.Get(key, &value)) {
|
||||
aRet = nullptr;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
aRet = value->Data();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult GetImpl(const char* aKey, float& aRet)
|
||||
{
|
||||
nsDependentCString key(aKey);
|
||||
nsCString* value;
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
if (mParams.Get(key, &value)) {
|
||||
aRet = value->ToFloat(&rv);
|
||||
} else {
|
||||
aRet = 0.0;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult GetImpl(const char* aKey, double& aRet)
|
||||
{
|
||||
nsDependentCString key(aKey);
|
||||
nsCString* value;
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
if (mParams.Get(key, &value)) {
|
||||
aRet = value->ToFloat(&rv);
|
||||
} else {
|
||||
aRet = 0.0;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult GetImpl(const char* aKey, int& aRet)
|
||||
{
|
||||
nsDependentCString key(aKey);
|
||||
nsCString* value;
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
if (mParams.Get(key, &value)) {
|
||||
aRet = value->ToInteger(&rv);
|
||||
} else {
|
||||
aRet = 0.0;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult GetImpl(const char* aKey, bool& aRet)
|
||||
{
|
||||
nsDependentCString key(aKey);
|
||||
nsCString* value;
|
||||
if (!mParams.Get(key, &value)) {
|
||||
aRet = false;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
aRet = value->EqualsLiteral("true");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const char* GetTextKey(uint32_t aKey);
|
||||
const char* FindVendorSpecificKey(const char* aPotentialKeys[], size_t aPotentialKeyCount);
|
||||
|
||||
// The *Impl() templates handle converting the parameter keys from
|
||||
// their enum values to string types, if necessary. These are the
|
||||
// bottom layer accessors to mParams.
|
||||
//
|
||||
// Return values:
|
||||
// - NS_OK on success;
|
||||
// - NS_ERROR_NOT_IMPLEMENTED if the numeric 'aKey' value is invalid.
|
||||
template<typename T> nsresult
|
||||
SetImpl(uint32_t aKey, const T& aValue)
|
||||
{
|
||||
const char* key = GetTextKey(aKey);
|
||||
NS_ENSURE_TRUE(key, NS_ERROR_NOT_IMPLEMENTED);
|
||||
return SetImpl(key, aValue);
|
||||
}
|
||||
|
||||
template<typename T> nsresult
|
||||
GetImpl(uint32_t aKey, T& aValue)
|
||||
{
|
||||
const char* key = GetTextKey(aKey);
|
||||
NS_ENSURE_TRUE(key, NS_ERROR_NOT_IMPLEMENTED);
|
||||
return GetImpl(key, aValue);
|
||||
}
|
||||
|
||||
nsresult
|
||||
ClearImpl(const char* aKey)
|
||||
{
|
||||
nsDependentCString key(aKey);
|
||||
mParams.Remove(key);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// The *Translated() functions allow us to handle special cases;
|
||||
// for example, where the thumbnail size setting is exposed as an
|
||||
// ICameraControl::Size object, but is handled by the AOSP layer
|
||||
// as two separate parameters.
|
||||
//
|
||||
// Return values:
|
||||
// - NS_OK on success;
|
||||
// - NS_ERROR_INVALID_ARG if 'aValue' contains an invalid value;
|
||||
// - NS_ERROR_NOT_IMPLEMENTED if 'aKey' is invalid;
|
||||
// - NS_ERROR_NOT_AVAILABLE if the getter fails to retrieve a valid value,
|
||||
// or if a setter fails because it requires one or more values that
|
||||
// could not be retrieved;
|
||||
// - NS_ERROR_FAILURE on unexpected internal failures.
|
||||
nsresult SetTranslated(uint32_t aKey, const nsAString& aValue);
|
||||
nsresult GetTranslated(uint32_t aKey, nsAString& aValue);
|
||||
nsresult SetTranslated(uint32_t aKey, const ICameraControl::Size& aSize);
|
||||
nsresult GetTranslated(uint32_t aKey, ICameraControl::Size& aSize);
|
||||
nsresult GetTranslated(uint32_t aKey, nsTArray<ICameraControl::Size>& aSizes);
|
||||
nsresult SetTranslated(uint32_t aKey, const nsTArray<ICameraControl::Region>& aRegions);
|
||||
nsresult GetTranslated(uint32_t aKey, nsTArray<ICameraControl::Region>& aRegions);
|
||||
nsresult SetTranslated(uint32_t aKey, const ICameraControl::Position& aPosition);
|
||||
nsresult SetTranslated(uint32_t aKey, const int64_t& aValue);
|
||||
nsresult GetTranslated(uint32_t aKey, int64_t& aValue);
|
||||
nsresult SetTranslated(uint32_t aKey, const double& aValue);
|
||||
nsresult GetTranslated(uint32_t aKey, double& aValue);
|
||||
nsresult SetTranslated(uint32_t aKey, const int& aValue);
|
||||
nsresult GetTranslated(uint32_t aKey, int& aValue);
|
||||
nsresult SetTranslated(uint32_t aKey, const uint32_t& aValue);
|
||||
nsresult GetTranslated(uint32_t aKey, uint32_t& aValue);
|
||||
nsresult SetTranslated(uint32_t aKey, const bool& aValue);
|
||||
nsresult GetTranslated(uint32_t aKey, bool& aValue);
|
||||
nsresult GetTranslated(uint32_t aKey, nsTArray<nsString>& aValues);
|
||||
nsresult GetTranslated(uint32_t aKey, nsTArray<double>& aValues);
|
||||
|
||||
// Converts a string of multiple, comma-separated values into an array
|
||||
// of the appropriate type.
|
||||
//
|
||||
// Return values:
|
||||
// - NS_OK on success;
|
||||
// - NS_ERROR_NOT_IMPLEMENTED if 'aKey' is invalid;
|
||||
// - NS_ERROR_NOT_AVAILABLE if a valid value could not be returned.
|
||||
template<class T> nsresult GetListAsArray(uint32_t aKey, nsTArray<T>& aArray);
|
||||
|
||||
// Converts ISO values (e.g., "auto", "hjr", "100", "200", etc.) to and from
|
||||
// values understood by Gonk (e.g., "auto", "ISO_HJR", "ISO100", "ISO200",
|
||||
// respectively).
|
||||
//
|
||||
// Return values:
|
||||
// - NS_OK on success;
|
||||
// - NS_ERROR_INVALID_ARG if the 'aIso' argument is not a valid form.
|
||||
nsresult MapIsoToGonk(const nsAString& aIso, nsACString& aIsoOut);
|
||||
nsresult MapIsoFromGonk(const char* aIso, nsAString& aIsoOut);
|
||||
|
||||
// Call once to initialize local cached values used in translating other
|
||||
// arguments between Gecko and Gonk. Always returns NS_OK.
|
||||
nsresult Initialize();
|
||||
|
||||
// Returns true if we're a memory-constrained platform that requires
|
||||
// certain features to be disabled; returns false otherwise.
|
||||
static bool IsLowMemoryPlatform();
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_CAMERA_GONKCAMERAPARAMETERS_H
|
|
@ -1,803 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
* Copyright (C) 2013 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <base/basictypes.h>
|
||||
#include "nsDebug.h"
|
||||
#define DOM_CAMERA_LOG_LEVEL 3
|
||||
#include "CameraCommon.h"
|
||||
/*
|
||||
#define CS_LOGD(...) DOM_CAMERA_LOGA(__VA_ARGS__)
|
||||
#define CS_LOGV(...) DOM_CAMERA_LOGI(__VA_ARGS__)
|
||||
#define CS_LOGI(...) DOM_CAMERA_LOGI(__VA_ARGS__)
|
||||
#define CS_LOGW(...) DOM_CAMERA_LOGW(__VA_ARGS__)
|
||||
#define CS_LOGE(...) DOM_CAMERA_LOGE(__VA_ARGS__)
|
||||
*/
|
||||
|
||||
#define CS_LOGD(fmt, ...) DOM_CAMERA_LOGA("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
|
||||
#define CS_LOGV(fmt, ...) DOM_CAMERA_LOGI("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
|
||||
#define CS_LOGI(fmt, ...) DOM_CAMERA_LOGI("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
|
||||
#define CS_LOGW(fmt, ...) DOM_CAMERA_LOGW("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
|
||||
#define CS_LOGE(fmt, ...) DOM_CAMERA_LOGE("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
|
||||
|
||||
#include <OMX_Component.h>
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <media/stagefright/foundation/ADebug.h>
|
||||
#include <media/stagefright/MediaDefs.h>
|
||||
#include <media/stagefright/MediaErrors.h>
|
||||
#include <media/stagefright/MetaData.h>
|
||||
#include <camera/CameraParameters.h>
|
||||
#include <utils/String8.h>
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#include "GonkCameraSource.h"
|
||||
#include "GonkCameraListener.h"
|
||||
#include "GonkCameraHwMgr.h"
|
||||
#include "ICameraControl.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
namespace android {
|
||||
|
||||
static const int64_t CAMERA_SOURCE_TIMEOUT_NS = 3000000000LL;
|
||||
|
||||
struct GonkCameraSourceListener : public GonkCameraListener {
|
||||
GonkCameraSourceListener(const sp<GonkCameraSource> &source);
|
||||
|
||||
virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
|
||||
virtual bool postData(int32_t msgType, const sp<IMemory> &dataPtr,
|
||||
camera_frame_metadata_t *metadata);
|
||||
|
||||
virtual bool postDataTimestamp(
|
||||
nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
|
||||
|
||||
protected:
|
||||
virtual ~GonkCameraSourceListener();
|
||||
|
||||
private:
|
||||
wp<GonkCameraSource> mSource;
|
||||
|
||||
GonkCameraSourceListener(const GonkCameraSourceListener &);
|
||||
GonkCameraSourceListener &operator=(const GonkCameraSourceListener &);
|
||||
};
|
||||
|
||||
GonkCameraSourceListener::GonkCameraSourceListener(const sp<GonkCameraSource> &source)
|
||||
: mSource(source) {
|
||||
}
|
||||
|
||||
GonkCameraSourceListener::~GonkCameraSourceListener() {
|
||||
}
|
||||
|
||||
void GonkCameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) {
|
||||
CS_LOGV("notify(%d, %d, %d)", msgType, ext1, ext2);
|
||||
}
|
||||
|
||||
bool GonkCameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr,
|
||||
camera_frame_metadata_t *metadata) {
|
||||
CS_LOGV("postData(%d, ptr:%p, size:%d)",
|
||||
msgType, dataPtr->pointer(), dataPtr->size());
|
||||
|
||||
sp<GonkCameraSource> source = mSource.promote();
|
||||
if (source.get() != NULL) {
|
||||
source->dataCallback(msgType, dataPtr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GonkCameraSourceListener::postDataTimestamp(
|
||||
nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) {
|
||||
|
||||
sp<GonkCameraSource> source = mSource.promote();
|
||||
if (source.get() != NULL) {
|
||||
source->dataCallbackTimestamp(timestamp/1000, msgType, dataPtr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int32_t getColorFormat(const char* colorFormat) {
|
||||
return OMX_COLOR_FormatYUV420SemiPlanar; //XXX nsGonkCameraControl uses only YUV420SemiPlanar
|
||||
|
||||
if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420P)) {
|
||||
return OMX_COLOR_FormatYUV420Planar;
|
||||
}
|
||||
|
||||
if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422SP)) {
|
||||
return OMX_COLOR_FormatYUV422SemiPlanar;
|
||||
}
|
||||
|
||||
if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420SP)) {
|
||||
return OMX_COLOR_FormatYUV420SemiPlanar;
|
||||
}
|
||||
|
||||
if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422I)) {
|
||||
return OMX_COLOR_FormatYCbYCr;
|
||||
}
|
||||
|
||||
if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_RGB565)) {
|
||||
return OMX_COLOR_Format16bitRGB565;
|
||||
}
|
||||
|
||||
if (!strcmp(colorFormat, "OMX_TI_COLOR_FormatYUV420PackedSemiPlanar")) {
|
||||
return OMX_TI_COLOR_FormatYUV420PackedSemiPlanar;
|
||||
}
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
|
||||
if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_ANDROID_OPAQUE)) {
|
||||
return OMX_COLOR_FormatAndroidOpaque;
|
||||
}
|
||||
#endif
|
||||
CS_LOGE("Uknown color format (%s), please add it to "
|
||||
"GonkCameraSource::getColorFormat", colorFormat);
|
||||
|
||||
CHECK(!"Unknown color format");
|
||||
}
|
||||
|
||||
GonkCameraSource *GonkCameraSource::Create(
|
||||
const sp<GonkCameraHardware>& aCameraHw,
|
||||
Size videoSize,
|
||||
int32_t frameRate,
|
||||
bool storeMetaDataInVideoBuffers) {
|
||||
|
||||
GonkCameraSource *source = new GonkCameraSource(aCameraHw,
|
||||
videoSize, frameRate,
|
||||
storeMetaDataInVideoBuffers);
|
||||
return source;
|
||||
}
|
||||
|
||||
GonkCameraSource *GonkCameraSource::Create(
|
||||
ICameraControl* aControl,
|
||||
Size videoSize,
|
||||
int32_t frameRate)
|
||||
{
|
||||
mozilla::nsGonkCameraControl* control =
|
||||
static_cast<mozilla::nsGonkCameraControl*>(aControl);
|
||||
return Create(control->GetCameraHw(), videoSize, frameRate, false);
|
||||
}
|
||||
|
||||
GonkCameraSource::GonkCameraSource(
|
||||
const sp<GonkCameraHardware>& aCameraHw,
|
||||
Size videoSize,
|
||||
int32_t frameRate,
|
||||
bool storeMetaDataInVideoBuffers)
|
||||
: mCameraFlags(0),
|
||||
mNumInputBuffers(0),
|
||||
mVideoFrameRate(-1),
|
||||
mNumFramesReceived(0),
|
||||
mLastFrameTimestampUs(0),
|
||||
mStarted(false),
|
||||
mNumFramesEncoded(0),
|
||||
mTimeBetweenFrameCaptureUs(0),
|
||||
mRateLimit(false),
|
||||
mFirstFrameTimeUs(0),
|
||||
mNumFramesDropped(0),
|
||||
mNumGlitches(0),
|
||||
mGlitchDurationThresholdUs(200000),
|
||||
mCollectStats(false),
|
||||
mCameraHw(aCameraHw) {
|
||||
mVideoSize.width = -1;
|
||||
mVideoSize.height = -1;
|
||||
|
||||
mInitCheck = init(
|
||||
videoSize, frameRate,
|
||||
storeMetaDataInVideoBuffers);
|
||||
if (mInitCheck != OK) releaseCamera();
|
||||
}
|
||||
|
||||
status_t GonkCameraSource::initCheck() const {
|
||||
return mInitCheck;
|
||||
}
|
||||
|
||||
//TODO: Do we need to reimplement isCameraAvailable?
|
||||
|
||||
/*
|
||||
* Check to see whether the requested video width and height is one
|
||||
* of the supported sizes.
|
||||
* @param width the video frame width in pixels
|
||||
* @param height the video frame height in pixels
|
||||
* @param suppportedSizes the vector of sizes that we check against
|
||||
* @return true if the dimension (width and height) is supported.
|
||||
*/
|
||||
static bool isVideoSizeSupported(
|
||||
int32_t width, int32_t height,
|
||||
const Vector<Size>& supportedSizes) {
|
||||
|
||||
CS_LOGV("isVideoSizeSupported");
|
||||
for (size_t i = 0; i < supportedSizes.size(); ++i) {
|
||||
if (width == supportedSizes[i].width &&
|
||||
height == supportedSizes[i].height) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the preview and video output is separate, we only set the
|
||||
* the video size, and applications should set the preview size
|
||||
* to some proper value, and the recording framework will not
|
||||
* change the preview size; otherwise, if the video and preview
|
||||
* output is the same, we need to set the preview to be the same
|
||||
* as the requested video size.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* Query the camera to retrieve the supported video frame sizes
|
||||
* and also to see whether CameraParameters::setVideoSize()
|
||||
* is supported or not.
|
||||
* @param params CameraParameters to retrieve the information
|
||||
* @@param isSetVideoSizeSupported retunrs whether method
|
||||
* CameraParameters::setVideoSize() is supported or not.
|
||||
* @param sizes returns the vector of Size objects for the
|
||||
* supported video frame sizes advertised by the camera.
|
||||
*/
|
||||
static void getSupportedVideoSizes(
|
||||
const CameraParameters& params,
|
||||
bool *isSetVideoSizeSupported,
|
||||
Vector<Size>& sizes) {
|
||||
|
||||
*isSetVideoSizeSupported = true;
|
||||
params.getSupportedVideoSizes(sizes);
|
||||
if (sizes.size() == 0) {
|
||||
CS_LOGD("Camera does not support setVideoSize()");
|
||||
params.getSupportedPreviewSizes(sizes);
|
||||
*isSetVideoSizeSupported = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the camera has the supported color format
|
||||
* @param params CameraParameters to retrieve the information
|
||||
* @return OK if no error.
|
||||
*/
|
||||
status_t GonkCameraSource::isCameraColorFormatSupported(
|
||||
const CameraParameters& params) {
|
||||
mColorFormat = getColorFormat(params.get(
|
||||
CameraParameters::KEY_VIDEO_FRAME_FORMAT));
|
||||
if (mColorFormat == -1) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the camera to use the requested video size
|
||||
* (width and height) and/or frame rate. If both width and
|
||||
* height are -1, configuration on the video size is skipped.
|
||||
* if frameRate is -1, configuration on the frame rate
|
||||
* is skipped. Skipping the configuration allows one to
|
||||
* use the current camera setting without the need to
|
||||
* actually know the specific values (see Create() method).
|
||||
*
|
||||
* @param params the CameraParameters to be configured
|
||||
* @param width the target video frame width in pixels
|
||||
* @param height the target video frame height in pixels
|
||||
* @param frameRate the target frame rate in frames per second.
|
||||
* @return OK if no error.
|
||||
*/
|
||||
status_t GonkCameraSource::configureCamera(
|
||||
CameraParameters* params,
|
||||
int32_t width, int32_t height,
|
||||
int32_t frameRate) {
|
||||
CS_LOGV("configureCamera");
|
||||
Vector<Size> sizes;
|
||||
bool isSetVideoSizeSupportedByCamera = true;
|
||||
getSupportedVideoSizes(*params, &isSetVideoSizeSupportedByCamera, sizes);
|
||||
bool isCameraParamChanged = false;
|
||||
if (width != -1 && height != -1) {
|
||||
if (!isVideoSizeSupported(width, height, sizes)) {
|
||||
CS_LOGE("Video dimension (%dx%d) is unsupported", width, height);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
if (isSetVideoSizeSupportedByCamera) {
|
||||
params->setVideoSize(width, height);
|
||||
} else {
|
||||
params->setPreviewSize(width, height);
|
||||
}
|
||||
isCameraParamChanged = true;
|
||||
} else if ((width == -1 && height != -1) ||
|
||||
(width != -1 && height == -1)) {
|
||||
// If one and only one of the width and height is -1
|
||||
// we reject such a request.
|
||||
CS_LOGE("Requested video size (%dx%d) is not supported", width, height);
|
||||
return BAD_VALUE;
|
||||
} else { // width == -1 && height == -1
|
||||
// Do not configure the camera.
|
||||
// Use the current width and height value setting from the camera.
|
||||
}
|
||||
|
||||
if (frameRate != -1) {
|
||||
CHECK(frameRate > 0 && frameRate <= 120);
|
||||
const char* supportedFrameRates =
|
||||
params->get(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES);
|
||||
CHECK(supportedFrameRates != NULL);
|
||||
CS_LOGV("Supported frame rates: %s", supportedFrameRates);
|
||||
char buf[4];
|
||||
snprintf(buf, 4, "%d", frameRate);
|
||||
if (strstr(supportedFrameRates, buf) == NULL) {
|
||||
CS_LOGE("Requested frame rate (%d) is not supported: %s",
|
||||
frameRate, supportedFrameRates);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
// The frame rate is supported, set the camera to the requested value.
|
||||
params->setPreviewFrameRate(frameRate);
|
||||
isCameraParamChanged = true;
|
||||
} else { // frameRate == -1
|
||||
// Do not configure the camera.
|
||||
// Use the current frame rate value setting from the camera
|
||||
}
|
||||
|
||||
if (isCameraParamChanged) {
|
||||
// Either frame rate or frame size needs to be changed.
|
||||
if (OK != mCameraHw->PushParameters(*params)) {
|
||||
CS_LOGE("Could not change settings."
|
||||
" Someone else is using camera?");
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the requested video frame size
|
||||
* has been successfully configured or not. If both width and height
|
||||
* are -1, check on the current width and height value setting
|
||||
* is performed.
|
||||
*
|
||||
* @param params CameraParameters to retrieve the information
|
||||
* @param the target video frame width in pixels to check against
|
||||
* @param the target video frame height in pixels to check against
|
||||
* @return OK if no error
|
||||
*/
|
||||
status_t GonkCameraSource::checkVideoSize(
|
||||
const CameraParameters& params,
|
||||
int32_t width, int32_t height) {
|
||||
|
||||
CS_LOGV("checkVideoSize");
|
||||
// The actual video size is the same as the preview size
|
||||
// if the camera hal does not support separate video and
|
||||
// preview output. In this case, we retrieve the video
|
||||
// size from preview.
|
||||
int32_t frameWidthActual = -1;
|
||||
int32_t frameHeightActual = -1;
|
||||
Vector<Size> sizes;
|
||||
params.getSupportedVideoSizes(sizes);
|
||||
if (sizes.size() == 0) {
|
||||
// video size is the same as preview size
|
||||
params.getPreviewSize(&frameWidthActual, &frameHeightActual);
|
||||
} else {
|
||||
// video size may not be the same as preview
|
||||
params.getVideoSize(&frameWidthActual, &frameHeightActual);
|
||||
}
|
||||
if (frameWidthActual < 0 || frameHeightActual < 0) {
|
||||
CS_LOGE("Failed to retrieve video frame size (%dx%d)",
|
||||
frameWidthActual, frameHeightActual);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
// Check the actual video frame size against the target/requested
|
||||
// video frame size.
|
||||
if (width != -1 && height != -1) {
|
||||
if (frameWidthActual != width || frameHeightActual != height) {
|
||||
CS_LOGE("Failed to set video frame size to %dx%d. "
|
||||
"The actual video size is %dx%d ", width, height,
|
||||
frameWidthActual, frameHeightActual);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// Good now.
|
||||
mVideoSize.width = frameWidthActual;
|
||||
mVideoSize.height = frameHeightActual;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the requested frame rate has been successfully configured or not.
|
||||
* If the target frameRate is -1, check on the current frame rate value
|
||||
* setting is performed.
|
||||
*
|
||||
* @param params CameraParameters to retrieve the information
|
||||
* @param the target video frame rate to check against
|
||||
* @return OK if no error.
|
||||
*/
|
||||
status_t GonkCameraSource::checkFrameRate(
|
||||
const CameraParameters& params,
|
||||
int32_t frameRate) {
|
||||
|
||||
CS_LOGV("checkFrameRate");
|
||||
int32_t frameRateActual = params.getPreviewFrameRate();
|
||||
if (frameRateActual < 0) {
|
||||
CS_LOGE("Failed to retrieve preview frame rate (%d)", frameRateActual);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
// Check the actual video frame rate against the target/requested
|
||||
// video frame rate.
|
||||
if (frameRate != -1 && (frameRateActual - frameRate) != 0) {
|
||||
CS_LOGE("Failed to set preview frame rate to %d fps. The actual "
|
||||
"frame rate is %d", frameRate, frameRateActual);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
// Good now.
|
||||
mVideoFrameRate = frameRateActual;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the GonkCameraSource so that it becomes
|
||||
* ready for providing the video input streams as requested.
|
||||
* @param camera the camera object used for the video source
|
||||
* @param cameraId if camera == 0, use camera with this id
|
||||
* as the video source
|
||||
* @param videoSize the target video frame size. If both
|
||||
* width and height in videoSize is -1, use the current
|
||||
* width and heigth settings by the camera
|
||||
* @param frameRate the target frame rate in frames per second.
|
||||
* if it is -1, use the current camera frame rate setting.
|
||||
* @param storeMetaDataInVideoBuffers request to store meta
|
||||
* data or real YUV data in video buffers. Request to
|
||||
* store meta data in video buffers may not be honored
|
||||
* if the source does not support this feature.
|
||||
*
|
||||
* @return OK if no error.
|
||||
*/
|
||||
status_t GonkCameraSource::init(
|
||||
Size videoSize,
|
||||
int32_t frameRate,
|
||||
bool storeMetaDataInVideoBuffers) {
|
||||
|
||||
CS_LOGV("init");
|
||||
status_t err = OK;
|
||||
//TODO: need to do something here to check the sanity of camera
|
||||
|
||||
CameraParameters params;
|
||||
mCameraHw->PullParameters(params);
|
||||
if ((err = isCameraColorFormatSupported(params)) != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// Set the camera to use the requested video frame size
|
||||
// and/or frame rate.
|
||||
if ((err = configureCamera(¶ms,
|
||||
videoSize.width, videoSize.height,
|
||||
frameRate))) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// Check on video frame size and frame rate.
|
||||
CameraParameters newCameraParams;
|
||||
mCameraHw->PullParameters(newCameraParams);
|
||||
if ((err = checkVideoSize(newCameraParams,
|
||||
videoSize.width, videoSize.height)) != OK) {
|
||||
return err;
|
||||
}
|
||||
if ((err = checkFrameRate(newCameraParams, frameRate)) != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// By default, do not store metadata in video buffers
|
||||
mIsMetaDataStoredInVideoBuffers = false;
|
||||
mCameraHw->StoreMetaDataInBuffers(false);
|
||||
if (storeMetaDataInVideoBuffers) {
|
||||
if (OK == mCameraHw->StoreMetaDataInBuffers(true)) {
|
||||
mIsMetaDataStoredInVideoBuffers = true;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t glitchDurationUs = (1000000LL / mVideoFrameRate);
|
||||
if (glitchDurationUs > mGlitchDurationThresholdUs) {
|
||||
mGlitchDurationThresholdUs = glitchDurationUs;
|
||||
}
|
||||
|
||||
// XXX: query camera for the stride and slice height
|
||||
// when the capability becomes available.
|
||||
mMeta = new MetaData;
|
||||
mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
|
||||
mMeta->setInt32(kKeyColorFormat, mColorFormat);
|
||||
mMeta->setInt32(kKeyWidth, mVideoSize.width);
|
||||
mMeta->setInt32(kKeyHeight, mVideoSize.height);
|
||||
mMeta->setInt32(kKeyStride, mVideoSize.width);
|
||||
mMeta->setInt32(kKeySliceHeight, mVideoSize.height);
|
||||
mMeta->setInt32(kKeyFrameRate, mVideoFrameRate);
|
||||
return OK;
|
||||
}
|
||||
|
||||
GonkCameraSource::~GonkCameraSource() {
|
||||
if (mStarted) {
|
||||
reset();
|
||||
} else if (mInitCheck == OK) {
|
||||
// Camera is initialized but because start() is never called,
|
||||
// the lock on Camera is never released(). This makes sure
|
||||
// Camera's lock is released in this case.
|
||||
// TODO: Don't think I need to do this
|
||||
releaseCamera();
|
||||
}
|
||||
}
|
||||
|
||||
int GonkCameraSource::startCameraRecording() {
|
||||
CS_LOGV("startCameraRecording");
|
||||
return mCameraHw->StartRecording();
|
||||
}
|
||||
|
||||
status_t GonkCameraSource::start(MetaData *meta) {
|
||||
int rv;
|
||||
|
||||
CS_LOGV("start");
|
||||
CHECK(!mStarted);
|
||||
if (mInitCheck != OK) {
|
||||
CS_LOGE("GonkCameraSource is not initialized yet");
|
||||
return mInitCheck;
|
||||
}
|
||||
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
if (property_get("media.stagefright.record-stats", value, NULL)
|
||||
&& (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
|
||||
mCollectStats = true;
|
||||
}
|
||||
|
||||
mStartTimeUs = 0;
|
||||
mNumInputBuffers = 0;
|
||||
if (meta) {
|
||||
int64_t startTimeUs;
|
||||
if (meta->findInt64(kKeyTime, &startTimeUs)) {
|
||||
mStartTimeUs = startTimeUs;
|
||||
}
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
|
||||
int32_t nBuffers;
|
||||
if (meta->findInt32(kKeyNumBuffers, &nBuffers)) {
|
||||
CHECK_GT(nBuffers, 0);
|
||||
mNumInputBuffers = nBuffers;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Register a listener with GonkCameraHardware so that we can get callbacks
|
||||
mCameraHw->SetListener(new GonkCameraSourceListener(this));
|
||||
|
||||
rv = startCameraRecording();
|
||||
|
||||
mStarted = (rv == OK);
|
||||
return rv;
|
||||
}
|
||||
|
||||
void GonkCameraSource::stopCameraRecording() {
|
||||
CS_LOGV("stopCameraRecording");
|
||||
mCameraHw->StopRecording();
|
||||
}
|
||||
|
||||
void GonkCameraSource::releaseCamera() {
|
||||
CS_LOGV("releaseCamera");
|
||||
}
|
||||
|
||||
status_t GonkCameraSource::reset() {
|
||||
CS_LOGD("reset: E");
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
mStarted = false;
|
||||
mFrameAvailableCondition.signal();
|
||||
|
||||
releaseQueuedFrames();
|
||||
while (!mFramesBeingEncoded.empty()) {
|
||||
if (NO_ERROR !=
|
||||
mFrameCompleteCondition.waitRelative(mLock,
|
||||
mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) {
|
||||
CS_LOGW("Timed out waiting for outstanding frames being encoded: %d",
|
||||
mFramesBeingEncoded.size());
|
||||
}
|
||||
}
|
||||
stopCameraRecording();
|
||||
if (mRateLimit) {
|
||||
mRateLimit = false;
|
||||
mCameraHw->OnRateLimitPreview(false);
|
||||
}
|
||||
releaseCamera();
|
||||
|
||||
if (mDirectBufferListener.get()) {
|
||||
mDirectBufferListener = nullptr;
|
||||
}
|
||||
|
||||
if (mCollectStats) {
|
||||
CS_LOGI("Frames received/encoded/dropped: %d/%d/%d in %lld us",
|
||||
mNumFramesReceived, mNumFramesEncoded, mNumFramesDropped,
|
||||
mLastFrameTimestampUs - mFirstFrameTimeUs);
|
||||
}
|
||||
|
||||
if (mNumGlitches > 0) {
|
||||
CS_LOGW("%d long delays between neighboring video frames", mNumGlitches);
|
||||
}
|
||||
|
||||
CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped);
|
||||
CS_LOGD("reset: X");
|
||||
return OK;
|
||||
}
|
||||
|
||||
void GonkCameraSource::releaseRecordingFrame(const sp<IMemory>& frame) {
|
||||
CS_LOGV("releaseRecordingFrame");
|
||||
mCameraHw->ReleaseRecordingFrame(frame);
|
||||
}
|
||||
|
||||
void GonkCameraSource::releaseQueuedFrames() {
|
||||
List<sp<IMemory> >::iterator it;
|
||||
while (!mFramesReceived.empty()) {
|
||||
it = mFramesReceived.begin();
|
||||
releaseRecordingFrame(*it);
|
||||
mFramesReceived.erase(it);
|
||||
++mNumFramesDropped;
|
||||
}
|
||||
}
|
||||
|
||||
sp<MetaData> GonkCameraSource::getFormat() {
|
||||
return mMeta;
|
||||
}
|
||||
|
||||
void GonkCameraSource::releaseOneRecordingFrame(const sp<IMemory>& frame) {
|
||||
releaseRecordingFrame(frame);
|
||||
}
|
||||
|
||||
void GonkCameraSource::signalBufferReturned(MediaBuffer *buffer) {
|
||||
CS_LOGV("signalBufferReturned: %p", buffer->data());
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
for (List<sp<IMemory> >::iterator it = mFramesBeingEncoded.begin();
|
||||
it != mFramesBeingEncoded.end(); ++it) {
|
||||
if ((*it)->pointer() == buffer->data()) {
|
||||
releaseOneRecordingFrame((*it));
|
||||
mFramesBeingEncoded.erase(it);
|
||||
++mNumFramesEncoded;
|
||||
buffer->setObserver(0);
|
||||
buffer->release();
|
||||
mFrameCompleteCondition.signal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
CHECK(!"signalBufferReturned: bogus buffer");
|
||||
}
|
||||
|
||||
status_t GonkCameraSource::AddDirectBufferListener(DirectBufferListener* aListener) {
|
||||
if (mDirectBufferListener.get()) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
mDirectBufferListener = aListener;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t GonkCameraSource::read(
|
||||
MediaBuffer **buffer, const ReadOptions *options) {
|
||||
CS_LOGV("read");
|
||||
|
||||
*buffer = NULL;
|
||||
|
||||
int64_t seekTimeUs;
|
||||
ReadOptions::SeekMode mode;
|
||||
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
|
||||
return ERROR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
sp<IMemory> frame;
|
||||
int64_t frameTime;
|
||||
|
||||
{
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
while (mStarted && mFramesReceived.empty()) {
|
||||
if (NO_ERROR !=
|
||||
mFrameAvailableCondition.waitRelative(mLock,
|
||||
mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) {
|
||||
//TODO: check sanity of camera?
|
||||
CS_LOGW("Timed out waiting for incoming camera video frames: %lld us",
|
||||
mLastFrameTimestampUs);
|
||||
}
|
||||
}
|
||||
if (!mStarted) {
|
||||
return OK;
|
||||
}
|
||||
frame = *mFramesReceived.begin();
|
||||
mFramesReceived.erase(mFramesReceived.begin());
|
||||
|
||||
frameTime = *mFrameTimes.begin();
|
||||
mFrameTimes.erase(mFrameTimes.begin());
|
||||
mFramesBeingEncoded.push_back(frame);
|
||||
*buffer = new MediaBuffer(frame->pointer(), frame->size());
|
||||
(*buffer)->setObserver(this);
|
||||
(*buffer)->add_ref();
|
||||
(*buffer)->meta_data()->setInt64(kKeyTime, frameTime);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void GonkCameraSource::dataCallbackTimestamp(int64_t timestampUs,
|
||||
int32_t msgType, const sp<IMemory> &data) {
|
||||
bool rateLimit;
|
||||
bool prevRateLimit;
|
||||
CS_LOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs);
|
||||
{
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
if (!mStarted || (mNumFramesReceived == 0 && timestampUs < mStartTimeUs)) {
|
||||
CS_LOGV("Drop frame at %lld/%lld us", timestampUs, mStartTimeUs);
|
||||
releaseOneRecordingFrame(data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mNumFramesReceived > 0) {
|
||||
if (timestampUs <= mLastFrameTimestampUs) {
|
||||
CS_LOGE("Drop frame at %lld us, before last at %lld us",
|
||||
timestampUs, mLastFrameTimestampUs);
|
||||
releaseOneRecordingFrame(data);
|
||||
return;
|
||||
}
|
||||
if (timestampUs - mLastFrameTimestampUs > mGlitchDurationThresholdUs) {
|
||||
++mNumGlitches;
|
||||
}
|
||||
}
|
||||
|
||||
// May need to skip frame or modify timestamp. Currently implemented
|
||||
// by the subclass CameraSourceTimeLapse.
|
||||
if (skipCurrentFrame(timestampUs)) {
|
||||
releaseOneRecordingFrame(data);
|
||||
return;
|
||||
}
|
||||
|
||||
mLastFrameTimestampUs = timestampUs;
|
||||
if (mNumFramesReceived == 0) {
|
||||
mFirstFrameTimeUs = timestampUs;
|
||||
// Initial delay
|
||||
if (mStartTimeUs > 0) {
|
||||
if (timestampUs < mStartTimeUs) {
|
||||
// Frame was captured before recording was started
|
||||
// Drop it without updating the statistical data.
|
||||
releaseOneRecordingFrame(data);
|
||||
return;
|
||||
}
|
||||
mStartTimeUs = timestampUs - mStartTimeUs;
|
||||
}
|
||||
}
|
||||
++mNumFramesReceived;
|
||||
|
||||
// If a backlog is building up in the receive queue, we are likely
|
||||
// resource constrained and we need to throttle
|
||||
prevRateLimit = mRateLimit;
|
||||
rateLimit = mFramesReceived.empty();
|
||||
mRateLimit = rateLimit;
|
||||
|
||||
CHECK(data != NULL && data->size() > 0);
|
||||
mFramesReceived.push_back(data);
|
||||
int64_t timeUs = mStartTimeUs + (timestampUs - mFirstFrameTimeUs);
|
||||
mFrameTimes.push_back(timeUs);
|
||||
CS_LOGV("initial delay: %lld, current time stamp: %lld",
|
||||
mStartTimeUs, timeUs);
|
||||
mFrameAvailableCondition.signal();
|
||||
}
|
||||
|
||||
if(prevRateLimit != rateLimit) {
|
||||
mCameraHw->OnRateLimitPreview(rateLimit);
|
||||
}
|
||||
|
||||
if (mDirectBufferListener.get()) {
|
||||
MediaBuffer* mediaBuffer;
|
||||
if (read(&mediaBuffer) == OK) {
|
||||
mDirectBufferListener->BufferAvailable(mediaBuffer);
|
||||
// read() calls MediaBuffer->add_ref() so it needs to be released here.
|
||||
mediaBuffer->release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GonkCameraSource::isMetaDataStoredInVideoBuffers() const {
|
||||
CS_LOGV("isMetaDataStoredInVideoBuffers");
|
||||
return mIsMetaDataStoredInVideoBuffers;
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -1,192 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2009 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 GONK_CAMERA_SOURCE_H_
|
||||
|
||||
#define GONK_CAMERA_SOURCE_H_
|
||||
|
||||
#include <media/stagefright/MediaBuffer.h>
|
||||
#include <media/stagefright/MediaSource.h>
|
||||
#include <camera/CameraParameters.h>
|
||||
#include <utils/List.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/String16.h>
|
||||
|
||||
#include "GonkCameraHwMgr.h"
|
||||
|
||||
namespace mozilla {
|
||||
class ICameraControl;
|
||||
}
|
||||
|
||||
namespace android {
|
||||
|
||||
class IMemory;
|
||||
|
||||
class GonkCameraSource : public MediaSource, public MediaBufferObserver {
|
||||
public:
|
||||
|
||||
static GonkCameraSource *Create(const sp<GonkCameraHardware>& aCameraHw,
|
||||
Size videoSize,
|
||||
int32_t frameRate,
|
||||
bool storeMetaDataInVideoBuffers = false);
|
||||
|
||||
static GonkCameraSource *Create(mozilla::ICameraControl* aControl,
|
||||
Size videoSize,
|
||||
int32_t frameRate);
|
||||
|
||||
virtual ~GonkCameraSource();
|
||||
|
||||
virtual status_t start(MetaData *params = NULL);
|
||||
virtual status_t stop() { return reset(); }
|
||||
virtual status_t read(
|
||||
MediaBuffer **buffer, const ReadOptions *options = NULL);
|
||||
|
||||
/**
|
||||
* Check whether a GonkCameraSource object is properly initialized.
|
||||
* Must call this method before stop().
|
||||
* @return OK if initialization has successfully completed.
|
||||
*/
|
||||
virtual status_t initCheck() const;
|
||||
|
||||
/**
|
||||
* Returns the MetaData associated with the GonkCameraSource,
|
||||
* including:
|
||||
* kKeyColorFormat: YUV color format of the video frames
|
||||
* kKeyWidth, kKeyHeight: dimension (in pixels) of the video frames
|
||||
* kKeySampleRate: frame rate in frames per second
|
||||
* kKeyMIMEType: always fixed to be MEDIA_MIMETYPE_VIDEO_RAW
|
||||
*/
|
||||
virtual sp<MetaData> getFormat();
|
||||
|
||||
/**
|
||||
* Tell whether this camera source stores meta data or real YUV
|
||||
* frame data in video buffers.
|
||||
*
|
||||
* @return true if meta data is stored in the video
|
||||
* buffers; false if real YUV data is stored in
|
||||
* the video buffers.
|
||||
*/
|
||||
bool isMetaDataStoredInVideoBuffers() const;
|
||||
|
||||
virtual void signalBufferReturned(MediaBuffer* buffer);
|
||||
|
||||
/**
|
||||
* It sends recording frames to listener directly in the same thread.
|
||||
* Because recording frame is critical resource and it should not be
|
||||
* propagated to other thread as much as possible or there could be frame
|
||||
* rate jitter due to camera HAL waiting for resource.
|
||||
*/
|
||||
class DirectBufferListener : public RefBase {
|
||||
public:
|
||||
DirectBufferListener() {};
|
||||
|
||||
virtual status_t BufferAvailable(MediaBuffer* aBuffer) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~DirectBufferListener() {}
|
||||
};
|
||||
|
||||
status_t AddDirectBufferListener(DirectBufferListener* aListener);
|
||||
|
||||
protected:
|
||||
|
||||
enum CameraFlags {
|
||||
FLAGS_SET_CAMERA = 1L << 0,
|
||||
FLAGS_HOT_CAMERA = 1L << 1,
|
||||
};
|
||||
|
||||
int32_t mCameraFlags;
|
||||
Size mVideoSize;
|
||||
int32_t mNumInputBuffers;
|
||||
int32_t mVideoFrameRate;
|
||||
int32_t mColorFormat;
|
||||
status_t mInitCheck;
|
||||
|
||||
sp<MetaData> mMeta;
|
||||
|
||||
int64_t mStartTimeUs;
|
||||
int32_t mNumFramesReceived;
|
||||
int64_t mLastFrameTimestampUs;
|
||||
bool mStarted;
|
||||
int32_t mNumFramesEncoded;
|
||||
|
||||
// Time between capture of two frames.
|
||||
int64_t mTimeBetweenFrameCaptureUs;
|
||||
|
||||
GonkCameraSource(const sp<GonkCameraHardware>& aCameraHw,
|
||||
Size videoSize, int32_t frameRate,
|
||||
bool storeMetaDataInVideoBuffers = false);
|
||||
|
||||
virtual int startCameraRecording();
|
||||
virtual void stopCameraRecording();
|
||||
virtual void releaseRecordingFrame(const sp<IMemory>& frame);
|
||||
|
||||
// Returns true if need to skip the current frame.
|
||||
// Called from dataCallbackTimestamp.
|
||||
virtual bool skipCurrentFrame(int64_t timestampUs) {return false;}
|
||||
|
||||
friend class GonkCameraSourceListener;
|
||||
// Callback called when still camera raw data is available.
|
||||
virtual void dataCallback(int32_t msgType, const sp<IMemory> &data) {}
|
||||
|
||||
virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
|
||||
const sp<IMemory> &data);
|
||||
|
||||
private:
|
||||
|
||||
Mutex mLock;
|
||||
Condition mFrameAvailableCondition;
|
||||
Condition mFrameCompleteCondition;
|
||||
List<sp<IMemory> > mFramesReceived;
|
||||
List<sp<IMemory> > mFramesBeingEncoded;
|
||||
List<int64_t> mFrameTimes;
|
||||
bool mRateLimit;
|
||||
|
||||
int64_t mFirstFrameTimeUs;
|
||||
int32_t mNumFramesDropped;
|
||||
int32_t mNumGlitches;
|
||||
int64_t mGlitchDurationThresholdUs;
|
||||
bool mCollectStats;
|
||||
bool mIsMetaDataStoredInVideoBuffers;
|
||||
sp<GonkCameraHardware> mCameraHw;
|
||||
sp<DirectBufferListener> mDirectBufferListener;
|
||||
|
||||
void releaseQueuedFrames();
|
||||
void releaseOneRecordingFrame(const sp<IMemory>& frame);
|
||||
|
||||
status_t init(Size videoSize, int32_t frameRate,
|
||||
bool storeMetaDataInVideoBuffers);
|
||||
status_t isCameraColorFormatSupported(const CameraParameters& params);
|
||||
status_t configureCamera(CameraParameters* params,
|
||||
int32_t width, int32_t height,
|
||||
int32_t frameRate);
|
||||
|
||||
status_t checkVideoSize(const CameraParameters& params,
|
||||
int32_t width, int32_t height);
|
||||
|
||||
status_t checkFrameRate(const CameraParameters& params,
|
||||
int32_t frameRate);
|
||||
|
||||
void releaseCamera();
|
||||
status_t reset();
|
||||
|
||||
GonkCameraSource(const GonkCameraSource &);
|
||||
GonkCameraSource &operator=(const GonkCameraSource &);
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // GONK_CAMERA_SOURCE_H_
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,198 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2009 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 GONK_RECORDER_H_
|
||||
#define GONK_RECORDER_H_
|
||||
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "GonkCameraHwMgr.h"
|
||||
|
||||
#include <media/MediaRecorderBase.h>
|
||||
#include <camera/CameraParameters.h>
|
||||
#include <utils/String8.h>
|
||||
#include <system/audio.h>
|
||||
#if ANDROID_VERSION >= 21
|
||||
#include <media/stagefright/foundation/ALooper.h>
|
||||
#include <media/stagefright/foundation/AMessage.h>
|
||||
#endif
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "GonkCameraHwMgr.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
class GonkCameraSource;
|
||||
struct MOZ_EXPORT MediaSource;
|
||||
struct MediaWriter;
|
||||
class MOZ_EXPORT MetaData;
|
||||
struct MOZ_EXPORT AudioSource;
|
||||
class GonkCameraHardware;
|
||||
|
||||
struct GonkRecorder {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GonkRecorder)
|
||||
|
||||
GonkRecorder();
|
||||
|
||||
virtual status_t init();
|
||||
virtual status_t setAudioSource(audio_source_t as);
|
||||
virtual status_t setVideoSource(video_source vs);
|
||||
virtual status_t setOutputFormat(output_format of);
|
||||
virtual status_t setAudioEncoder(audio_encoder ae);
|
||||
virtual status_t setVideoEncoder(video_encoder ve);
|
||||
virtual status_t setVideoSize(int width, int height);
|
||||
virtual status_t setVideoFrameRate(int frames_per_second);
|
||||
virtual status_t setOutputFile(const char *path);
|
||||
virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
|
||||
virtual status_t setParameters(const String8& params);
|
||||
virtual status_t setCamera(const sp<GonkCameraHardware>& aCameraHw);
|
||||
virtual status_t setListener(const sp<IMediaRecorderClient>& listener);
|
||||
virtual status_t setClientName(const String16& clientName);
|
||||
virtual status_t prepare();
|
||||
virtual status_t start();
|
||||
virtual status_t pause();
|
||||
virtual status_t resume();
|
||||
virtual status_t stop();
|
||||
virtual status_t close();
|
||||
virtual status_t reset();
|
||||
virtual status_t getMaxAmplitude(int *max);
|
||||
virtual status_t dump(int fd, const Vector<String16>& args) const;
|
||||
// Querying a SurfaceMediaSourcer
|
||||
|
||||
protected:
|
||||
virtual ~GonkRecorder();
|
||||
|
||||
private:
|
||||
struct WrappedMediaSource;
|
||||
|
||||
sp<IMediaRecorderClient> mListener;
|
||||
String16 mClientName;
|
||||
uid_t mClientUid;
|
||||
sp<MediaWriter> mWriter;
|
||||
sp<WrappedMediaSource> mWriterSource;
|
||||
int mOutputFd;
|
||||
sp<AudioSource> mAudioSourceNode;
|
||||
|
||||
audio_source_t mAudioSource;
|
||||
video_source mVideoSource;
|
||||
output_format mOutputFormat;
|
||||
audio_encoder mAudioEncoder;
|
||||
video_encoder mVideoEncoder;
|
||||
bool mUse64BitFileOffset;
|
||||
int32_t mVideoWidth, mVideoHeight;
|
||||
int32_t mFrameRate;
|
||||
int32_t mVideoBitRate;
|
||||
int32_t mAudioBitRate;
|
||||
int32_t mAudioChannels;
|
||||
int32_t mSampleRate;
|
||||
int32_t mInterleaveDurationUs;
|
||||
int32_t mIFramesIntervalSec;
|
||||
int32_t mCameraId;
|
||||
int32_t mVideoEncoderProfile;
|
||||
int32_t mVideoEncoderLevel;
|
||||
int32_t mMovieTimeScale;
|
||||
int32_t mVideoTimeScale;
|
||||
int32_t mAudioTimeScale;
|
||||
int64_t mMaxFileSizeBytes;
|
||||
int64_t mMaxFileDurationUs;
|
||||
int64_t mTrackEveryTimeDurationUs;
|
||||
int32_t mRotationDegrees; // Clockwise
|
||||
int32_t mLatitudex10000;
|
||||
int32_t mLongitudex10000;
|
||||
int32_t mStartTimeOffsetMs;
|
||||
|
||||
String8 mParams;
|
||||
|
||||
bool mIsMetaDataStoredInVideoBuffers;
|
||||
MediaProfiles *mEncoderProfiles;
|
||||
|
||||
bool mStarted;
|
||||
|
||||
#if ANDROID_VERSION >= 21
|
||||
sp<ALooper> mLooper;
|
||||
#endif
|
||||
|
||||
sp<GonkCameraHardware> mCameraHw;
|
||||
|
||||
status_t setupMPEG4Recording(
|
||||
int outputFd,
|
||||
int32_t videoWidth, int32_t videoHeight,
|
||||
int32_t videoBitRate,
|
||||
int32_t *totalBitRate,
|
||||
sp<MediaWriter> *mediaWriter,
|
||||
sp<WrappedMediaSource> *mediaSource);
|
||||
void setupMPEG4MetaData(int64_t startTimeUs, int32_t totalBitRate,
|
||||
sp<MetaData> *meta);
|
||||
status_t startMPEG4Recording();
|
||||
status_t startAMRRecording();
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
|
||||
status_t startAACRecording();
|
||||
#endif
|
||||
status_t startRawAudioRecording();
|
||||
status_t startRTPRecording();
|
||||
status_t startMPEG2TSRecording();
|
||||
sp<MediaSource> createAudioSource();
|
||||
status_t checkVideoEncoderCapabilities();
|
||||
status_t checkAudioEncoderCapabilities();
|
||||
// Generic MediaSource set-up. Returns the appropriate
|
||||
// source (CameraSource or SurfaceMediaSource)
|
||||
// depending on the videosource type
|
||||
status_t setupMediaSource(sp<MediaSource> *mediaSource);
|
||||
status_t setupCameraSource(sp<GonkCameraSource> *cameraSource);
|
||||
// setup the surfacemediasource for the encoder
|
||||
|
||||
status_t setupAudioEncoder(const sp<MediaWriter>& writer);
|
||||
status_t setupVideoEncoder(
|
||||
sp<MediaSource> cameraSource,
|
||||
int32_t videoBitRate,
|
||||
sp<MediaSource> *source);
|
||||
|
||||
// Encoding parameter handling utilities
|
||||
status_t setParameter(const String8 &key, const String8 &value);
|
||||
status_t setParamAudioEncodingBitRate(int32_t bitRate);
|
||||
status_t setParamAudioNumberOfChannels(int32_t channles);
|
||||
status_t setParamAudioSamplingRate(int32_t sampleRate);
|
||||
status_t setParamAudioTimeScale(int32_t timeScale);
|
||||
status_t setParamVideoEncodingBitRate(int32_t bitRate);
|
||||
status_t setParamVideoIFramesInterval(int32_t seconds);
|
||||
status_t setParamVideoEncoderProfile(int32_t profile);
|
||||
status_t setParamVideoEncoderLevel(int32_t level);
|
||||
status_t setParamVideoCameraId(int32_t cameraId);
|
||||
status_t setParamVideoTimeScale(int32_t timeScale);
|
||||
status_t setParamVideoRotation(int32_t degrees);
|
||||
status_t setParamTrackTimeStatus(int64_t timeDurationUs);
|
||||
status_t setParamInterleaveDuration(int32_t durationUs);
|
||||
status_t setParam64BitFileOffset(bool use64BitFileOffset);
|
||||
status_t setParamMaxFileDurationUs(int64_t timeUs);
|
||||
status_t setParamMaxFileSizeBytes(int64_t bytes);
|
||||
status_t setParamMovieTimeScale(int32_t timeScale);
|
||||
status_t setParamGeoDataLongitude(int64_t longitudex10000);
|
||||
status_t setParamGeoDataLatitude(int64_t latitudex10000);
|
||||
void clipVideoBitRate();
|
||||
void clipVideoFrameRate();
|
||||
void clipVideoFrameWidth();
|
||||
void clipVideoFrameHeight();
|
||||
void clipAudioBitRate();
|
||||
void clipAudioSampleRate();
|
||||
void clipNumberOfAudioChannels();
|
||||
void setDefaultProfileIfNecessary();
|
||||
|
||||
GonkRecorder(const GonkRecorder &);
|
||||
GonkRecorder &operator=(const GonkRecorder &);
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // GONK_RECORDER_H_
|
|
@ -1,425 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "GonkRecorderProfiles.h"
|
||||
#include "nsMimeTypes.h"
|
||||
#include "CameraControlImpl.h"
|
||||
#include "CameraCommon.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "GonkRecorder.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace android;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct ProfileConfig {
|
||||
const char* name;
|
||||
int quality;
|
||||
uint32_t priority;
|
||||
};
|
||||
|
||||
#define DEF_GONK_RECORDER_PROFILE(e, n, p) { n, e, p },
|
||||
static const ProfileConfig ProfileList[] = {
|
||||
#include "GonkRecorderProfiles.def"
|
||||
};
|
||||
|
||||
static const size_t ProfileListSize = MOZ_ARRAY_LENGTH(ProfileList);
|
||||
|
||||
struct ProfileConfigDetect {
|
||||
const char* name;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t priority;
|
||||
};
|
||||
|
||||
#define DEF_GONK_RECORDER_PROFILE_DETECT(n, w, h, p) { n, w, h, p },
|
||||
static const ProfileConfigDetect ProfileListDetect[] = {
|
||||
#include "GonkRecorderProfiles.def"
|
||||
};
|
||||
|
||||
static const size_t ProfileListDetectSize = MOZ_ARRAY_LENGTH(ProfileListDetect);
|
||||
|
||||
};
|
||||
|
||||
/* static */ nsClassHashtable<nsUint32HashKey, ProfileHashtable> GonkRecorderProfile::sProfiles;
|
||||
/* static */ android::MediaProfiles* sMediaProfiles = nullptr;
|
||||
|
||||
static MediaProfiles*
|
||||
GetMediaProfiles()
|
||||
{
|
||||
if (!sMediaProfiles) {
|
||||
sMediaProfiles = MediaProfiles::getInstance();
|
||||
}
|
||||
MOZ_ASSERT(sMediaProfiles);
|
||||
return sMediaProfiles;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsProfileSupported(uint32_t aCameraId, int aQuality)
|
||||
{
|
||||
MediaProfiles* profiles = GetMediaProfiles();
|
||||
return profiles->hasCamcorderProfile(static_cast<int>(aCameraId),
|
||||
static_cast<camcorder_quality>(aQuality));
|
||||
}
|
||||
|
||||
static int
|
||||
GetProfileParameter(uint32_t aCameraId, int aQuality, const char* aParameter)
|
||||
{
|
||||
MediaProfiles* profiles = GetMediaProfiles();
|
||||
return profiles->getCamcorderProfileParamByName(aParameter, static_cast<int>(aCameraId),
|
||||
static_cast<camcorder_quality>(aQuality));
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
GonkRecorderVideo::Translate(video_encoder aCodec, nsAString& aCodecName)
|
||||
{
|
||||
switch (aCodec) {
|
||||
case VIDEO_ENCODER_H263:
|
||||
aCodecName.AssignASCII("h263");
|
||||
break;
|
||||
|
||||
case VIDEO_ENCODER_H264:
|
||||
aCodecName.AssignASCII("h264");
|
||||
break;
|
||||
|
||||
case VIDEO_ENCODER_MPEG_4_SP:
|
||||
aCodecName.AssignASCII("mpeg4sp");
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
GonkRecorderVideo::GetProfileParameter(const char* aParameter)
|
||||
{
|
||||
return ::GetProfileParameter(mCameraId, mQuality, aParameter);
|
||||
}
|
||||
|
||||
GonkRecorderVideo::GonkRecorderVideo(uint32_t aCameraId, int aQuality)
|
||||
: mCameraId(aCameraId)
|
||||
, mQuality(aQuality)
|
||||
, mIsValid(false)
|
||||
{
|
||||
mPlatformEncoder = static_cast<video_encoder>(GetProfileParameter("vid.codec"));
|
||||
bool isValid = Translate(mPlatformEncoder, mCodec);
|
||||
|
||||
int v = GetProfileParameter("vid.width");
|
||||
if (v >= 0) {
|
||||
mSize.width = v;
|
||||
} else {
|
||||
isValid = false;
|
||||
}
|
||||
v = GetProfileParameter("vid.height");
|
||||
if (v >= 0) {
|
||||
mSize.height = v;
|
||||
} else {
|
||||
isValid = false;
|
||||
}
|
||||
v = GetProfileParameter("vid.bps");
|
||||
if (v >= 0) {
|
||||
mBitsPerSecond = v;
|
||||
} else {
|
||||
isValid = false;
|
||||
}
|
||||
v = GetProfileParameter("vid.fps");
|
||||
if (v >= 0) {
|
||||
mFramesPerSecond = v;
|
||||
} else {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
mIsValid = isValid;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
GonkRecorderAudio::Translate(audio_encoder aCodec, nsAString& aCodecName)
|
||||
{
|
||||
switch (aCodec) {
|
||||
case AUDIO_ENCODER_AMR_NB:
|
||||
aCodecName.AssignASCII("amrnb");
|
||||
break;
|
||||
|
||||
case AUDIO_ENCODER_AMR_WB:
|
||||
aCodecName.AssignASCII("amrwb");
|
||||
break;
|
||||
|
||||
case AUDIO_ENCODER_AAC:
|
||||
aCodecName.AssignASCII("aac");
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
GonkRecorderAudio::GetProfileParameter(const char* aParameter)
|
||||
{
|
||||
return ::GetProfileParameter(mCameraId, mQuality, aParameter);
|
||||
}
|
||||
|
||||
GonkRecorderAudio::GonkRecorderAudio(uint32_t aCameraId, int aQuality)
|
||||
: mCameraId(aCameraId)
|
||||
, mQuality(aQuality)
|
||||
, mIsValid(false)
|
||||
{
|
||||
mPlatformEncoder = static_cast<audio_encoder>(GetProfileParameter("aud.codec"));
|
||||
bool isValid = Translate(mPlatformEncoder, mCodec);
|
||||
|
||||
int v = GetProfileParameter("aud.ch");
|
||||
if (v >= 0) {
|
||||
mChannels = v;
|
||||
} else {
|
||||
isValid = false;
|
||||
}
|
||||
v = GetProfileParameter("aud.bps");
|
||||
if (v >= 0) {
|
||||
mBitsPerSecond = v;
|
||||
} else {
|
||||
isValid = false;
|
||||
}
|
||||
v = GetProfileParameter("aud.hz");
|
||||
if (v >= 0) {
|
||||
mSamplesPerSecond = v;
|
||||
} else {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
mIsValid = isValid;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
GonkRecorderProfile::Translate(output_format aContainer, nsAString& aContainerName)
|
||||
{
|
||||
switch (aContainer) {
|
||||
case OUTPUT_FORMAT_THREE_GPP:
|
||||
aContainerName.AssignASCII("3gp");
|
||||
break;
|
||||
|
||||
case OUTPUT_FORMAT_MPEG_4:
|
||||
aContainerName.AssignASCII("mp4");
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
GonkRecorderProfile::GetMimeType(output_format aContainer, nsAString& aMimeType)
|
||||
{
|
||||
switch (aContainer) {
|
||||
case OUTPUT_FORMAT_THREE_GPP:
|
||||
aMimeType.AssignASCII(VIDEO_3GPP);
|
||||
break;
|
||||
|
||||
case OUTPUT_FORMAT_MPEG_4:
|
||||
aMimeType.AssignASCII(VIDEO_MP4);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
GonkRecorderProfile::GetProfileParameter(const char* aParameter)
|
||||
{
|
||||
return ::GetProfileParameter(mCameraId, mQuality, aParameter);
|
||||
}
|
||||
|
||||
GonkRecorderProfile::GonkRecorderProfile(uint32_t aCameraId,
|
||||
int aQuality)
|
||||
: GonkRecorderProfileBase<GonkRecorderAudio, GonkRecorderVideo>(aCameraId,
|
||||
aQuality)
|
||||
, mCameraId(aCameraId)
|
||||
, mQuality(aQuality)
|
||||
, mIsValid(false)
|
||||
{
|
||||
mOutputFormat = static_cast<output_format>(GetProfileParameter("file.format"));
|
||||
bool isValid = Translate(mOutputFormat, mContainer);
|
||||
isValid = GetMimeType(mOutputFormat, mMimeType) ? isValid : false;
|
||||
|
||||
mIsValid = isValid && mAudio.IsValid() && mVideo.IsValid();
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<GonkRecorderProfile>
|
||||
GonkRecorderProfile::CreateProfile(uint32_t aCameraId, int aQuality)
|
||||
{
|
||||
if (!IsProfileSupported(aCameraId, aQuality)) {
|
||||
DOM_CAMERA_LOGI("Profile %d not supported by platform\n", aQuality);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<GonkRecorderProfile> profile = new GonkRecorderProfile(aCameraId, aQuality);
|
||||
if (!profile->IsValid()) {
|
||||
DOM_CAMERA_LOGE("Profile %d is not valid\n", aQuality);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return profile.forget();
|
||||
}
|
||||
|
||||
/* static */
|
||||
ProfileHashtable*
|
||||
GonkRecorderProfile::GetProfileHashtable(uint32_t aCameraId)
|
||||
{
|
||||
ProfileHashtable* profiles = sProfiles.Get(aCameraId);
|
||||
if (!profiles) {
|
||||
profiles = new ProfileHashtable();
|
||||
sProfiles.Put(aCameraId, profiles);
|
||||
|
||||
/* First handle the profiles with a known enum. We can process those
|
||||
efficently because MediaProfiles indexes their profiles that way. */
|
||||
int highestKnownQuality = CAMCORDER_QUALITY_LIST_START - 1;
|
||||
for (size_t i = 0; i < ProfileListSize; ++i) {
|
||||
const ProfileConfig& p = ProfileList[i];
|
||||
if (p.quality > highestKnownQuality) {
|
||||
highestKnownQuality = p.quality;
|
||||
}
|
||||
|
||||
RefPtr<GonkRecorderProfile> profile = CreateProfile(aCameraId, p.quality);
|
||||
if (!profile) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DOM_CAMERA_LOGI("Profile %d '%s' supported by platform\n", p.quality, p.name);
|
||||
profile->mName.AssignASCII(p.name);
|
||||
profile->mPriority = p.priority;
|
||||
profiles->Put(profile->GetName(), profile);
|
||||
}
|
||||
|
||||
/* However not all of the potentially supported profiles have a known
|
||||
enum on all of our supported platforms because some entries may
|
||||
be missing from MediaProfiles.h. As such, we can't rely upon
|
||||
having the CAMCORDER_QUALITY_* enums for those profiles. We need
|
||||
to map the profiles to a name by matching the width and height of
|
||||
the video resolution to our configured values.
|
||||
|
||||
In theory there may be collisions given that there can be multiple
|
||||
resolutions sharing the same name (e.g. 800x480 and 768x480 are both
|
||||
wvga). In practice this should not happen because there should be
|
||||
only one WVGA profile given there is only one enum for it. In the
|
||||
situation there is a collision, it will merely select the last
|
||||
detected profile. */
|
||||
for (int q = highestKnownQuality + 1; q <= CAMCORDER_QUALITY_LIST_END; ++q) {
|
||||
RefPtr<GonkRecorderProfile> profile = CreateProfile(aCameraId, q);
|
||||
if (!profile) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ICameraControl::Size& s = profile->GetVideo().GetSize();
|
||||
size_t match;
|
||||
for (match = 0; match < ProfileListDetectSize; ++match) {
|
||||
const ProfileConfigDetect& p = ProfileListDetect[match];
|
||||
if (s.width == p.width && s.height == p.height) {
|
||||
DOM_CAMERA_LOGI("Profile %d '%s' supported by platform\n", q, p.name);
|
||||
profile->mName.AssignASCII(p.name);
|
||||
profile->mPriority = p.priority;
|
||||
profiles->Put(profile->GetName(), profile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match == ProfileListDetectSize) {
|
||||
DOM_CAMERA_LOGW("Profile %d size %u x %u is not recognized\n",
|
||||
q, s.width, s.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
return profiles;
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
GonkRecorderProfile::GetAll(uint32_t aCameraId,
|
||||
nsTArray<RefPtr<ICameraControl::RecorderProfile>>& aProfiles)
|
||||
{
|
||||
ProfileHashtable* profiles = GetProfileHashtable(aCameraId);
|
||||
if (!profiles) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
aProfiles.Clear();
|
||||
for (auto iter = profiles->Iter(); !iter.Done(); iter.Next()) {
|
||||
aProfiles.AppendElement(iter.UserData());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsresult
|
||||
GonkRecorderProfile::ConfigureRecorder(GonkRecorder& aRecorder)
|
||||
{
|
||||
static const size_t SIZE = 256;
|
||||
char buffer[SIZE];
|
||||
|
||||
// set all the params
|
||||
CHECK_SETARG(aRecorder.setAudioSource(AUDIO_SOURCE_CAMCORDER));
|
||||
CHECK_SETARG(aRecorder.setVideoSource(VIDEO_SOURCE_CAMERA));
|
||||
CHECK_SETARG(aRecorder.setOutputFormat(mOutputFormat));
|
||||
CHECK_SETARG(aRecorder.setVideoFrameRate(mVideo.GetFramesPerSecond()));
|
||||
CHECK_SETARG(aRecorder.setVideoSize(mVideo.GetSize().width, mVideo.GetSize().height));
|
||||
CHECK_SETARG(aRecorder.setVideoEncoder(mVideo.GetPlatformEncoder()));
|
||||
CHECK_SETARG(aRecorder.setAudioEncoder(mAudio.GetPlatformEncoder()));
|
||||
|
||||
snprintf(buffer, SIZE, "video-param-encoding-bitrate=%d", mVideo.GetBitsPerSecond());
|
||||
CHECK_SETARG(aRecorder.setParameters(String8(buffer)));
|
||||
|
||||
snprintf(buffer, SIZE, "audio-param-encoding-bitrate=%d", mAudio.GetBitsPerSecond());
|
||||
CHECK_SETARG(aRecorder.setParameters(String8(buffer)));
|
||||
|
||||
snprintf(buffer, SIZE, "audio-param-number-of-channels=%d", mAudio.GetChannels());
|
||||
CHECK_SETARG(aRecorder.setParameters(String8(buffer)));
|
||||
|
||||
snprintf(buffer, SIZE, "audio-param-sampling-rate=%d", mAudio.GetSamplesPerSecond());
|
||||
CHECK_SETARG(aRecorder.setParameters(String8(buffer)));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
GonkRecorderProfile::ConfigureRecorder(android::GonkRecorder& aRecorder,
|
||||
uint32_t aCameraId,
|
||||
const nsAString& aProfileName)
|
||||
{
|
||||
ProfileHashtable* profiles = GetProfileHashtable(aCameraId);
|
||||
if (!profiles) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
GonkRecorderProfile* profile;
|
||||
if (!profiles->Get(aProfileName, &profile)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return profile->ConfigureRecorder(aRecorder);
|
||||
}
|
||||
#endif
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DO NOT PUT RE-INCLUSION GUARD MACROS AROUND THIS HEADER!!!
|
||||
*/
|
||||
|
||||
#ifndef DEF_GONK_RECORDER_PROFILE
|
||||
#define DEF_GONK_RECORDER_PROFILE(e, n, p)
|
||||
#endif
|
||||
|
||||
#ifndef DEF_GONK_RECORDER_PROFILE_DETECT
|
||||
#define DEF_GONK_RECORDER_PROFILE_DETECT(n, w, h, p)
|
||||
#endif
|
||||
|
||||
/* Enum value, name, and priority. The supported profile with the
|
||||
* lowest priority and then largest area is selected as the default.
|
||||
*/
|
||||
|
||||
DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_LOW, "low", 900)
|
||||
DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_HIGH, "high", 900)
|
||||
DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_QCIF, "qcif", 300)
|
||||
DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_CIF, "cif", 300)
|
||||
DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_480P, "480p", 200)
|
||||
DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_720P, "720p", 100)
|
||||
DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_1080P, "1080p", 0)
|
||||
|
||||
/**
|
||||
* The following profiles do not appear in all versions of the
|
||||
* MediaProfiles.h and must be detected at runtime. Additionally some
|
||||
* profiles may have more than one resolution, depending on the camera.
|
||||
*/
|
||||
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("4kuhd", 3840, 2160, 800)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("fwvga", 864, 480, 200)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("fwvga", 854, 480, 200)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("wvga", 800, 480, 200)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("wvga", 768, 480, 200)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("vga", 640, 480, 200)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("hvga", 480, 320, 300)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("wqvga", 400, 240, 300)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("qvga", 320, 240, 300)
|
||||
|
||||
#undef DEF_GONK_RECORDER_PROFILE
|
||||
#undef DEF_GONK_RECORDER_PROFILE_DETECT
|
|
@ -1,159 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DOM_CAMERA_GONK_RECORDER_PROFILES_H
|
||||
#define DOM_CAMERA_GONK_RECORDER_PROFILES_H
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include <media/MediaProfiles.h>
|
||||
#else
|
||||
#include "FallbackCameraPlatform.h"
|
||||
#endif
|
||||
|
||||
#include "ICameraControl.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
|
||||
#ifndef CHECK_SETARG_RETURN
|
||||
#define CHECK_SETARG_RETURN(x, rv) \
|
||||
do { \
|
||||
if (x) { \
|
||||
DOM_CAMERA_LOGE(#x " failed\n"); \
|
||||
return rv; \
|
||||
} \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
#ifndef CHECK_SETARG
|
||||
#define CHECK_SETARG(x) CHECK_SETARG_RETURN(x, NS_ERROR_NOT_AVAILABLE)
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
class GonkRecorder;
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* class GonkRecorderProfileBase
|
||||
*/
|
||||
template<class A, class V>
|
||||
class GonkRecorderProfileBase : public ICameraControl::RecorderProfile
|
||||
{
|
||||
public:
|
||||
GonkRecorderProfileBase(uint32_t aCameraId, int aQuality)
|
||||
: RecorderProfile()
|
||||
, mAudio(aCameraId, aQuality)
|
||||
, mVideo(aCameraId, aQuality)
|
||||
{ }
|
||||
|
||||
virtual const Audio& GetAudio() const override { return mAudio; }
|
||||
virtual const Video& GetVideo() const override { return mVideo; }
|
||||
|
||||
protected:
|
||||
virtual ~GonkRecorderProfileBase() { }
|
||||
A mAudio;
|
||||
V mVideo;
|
||||
};
|
||||
|
||||
/**
|
||||
* class GonkRecorderVideo
|
||||
*/
|
||||
class GonkRecorderVideo : public ICameraControl::RecorderProfile::Video
|
||||
{
|
||||
public:
|
||||
GonkRecorderVideo(uint32_t aCameraId, int aQuality);
|
||||
virtual ~GonkRecorderVideo() { }
|
||||
|
||||
android::video_encoder GetPlatformEncoder() const { return mPlatformEncoder; }
|
||||
bool IsValid() const { return mIsValid; }
|
||||
|
||||
protected:
|
||||
int GetProfileParameter(const char* aParameter);
|
||||
static bool Translate(android::video_encoder aCodec, nsAString& aCodecName);
|
||||
|
||||
uint32_t mCameraId;
|
||||
int mQuality;
|
||||
bool mIsValid;
|
||||
android::video_encoder mPlatformEncoder;
|
||||
};
|
||||
|
||||
/**
|
||||
* class GonkRecorderAudio
|
||||
*/
|
||||
class GonkRecorderAudio : public ICameraControl::RecorderProfile::Audio
|
||||
{
|
||||
public:
|
||||
GonkRecorderAudio(uint32_t aCameraId, int aQuality);
|
||||
virtual ~GonkRecorderAudio() { }
|
||||
|
||||
android::audio_encoder GetPlatformEncoder() const { return mPlatformEncoder; }
|
||||
bool IsValid() const { return mIsValid; }
|
||||
|
||||
protected:
|
||||
int GetProfileParameter(const char* aParameter);
|
||||
static bool Translate(android::audio_encoder aCodec, nsAString& aCodecName);
|
||||
|
||||
uint32_t mCameraId;
|
||||
int mQuality;
|
||||
bool mIsValid;
|
||||
android::audio_encoder mPlatformEncoder;
|
||||
};
|
||||
|
||||
/**
|
||||
* class GonkRecorderProfile
|
||||
*/
|
||||
class GonkRecorderProfile;
|
||||
typedef nsRefPtrHashtable<nsStringHashKey, GonkRecorderProfile> ProfileHashtable;
|
||||
|
||||
class GonkRecorderProfile
|
||||
: public GonkRecorderProfileBase<GonkRecorderAudio, GonkRecorderVideo>
|
||||
{
|
||||
public:
|
||||
static nsresult GetAll(uint32_t aCameraId,
|
||||
nsTArray<RefPtr<ICameraControl::RecorderProfile>>& aProfiles);
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// Configures the specified recorder using the specified profile.
|
||||
//
|
||||
// Return values:
|
||||
// - NS_OK on success;
|
||||
// - NS_ERROR_INVALID_ARG if the profile isn't supported;
|
||||
// - NS_ERROR_NOT_AVAILABLE if the recorder rejected the profile.
|
||||
static nsresult ConfigureRecorder(android::GonkRecorder& aRecorder,
|
||||
uint32_t aCameraId,
|
||||
const nsAString& aProfileName);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
GonkRecorderProfile(uint32_t aCameraId,
|
||||
int aQuality);
|
||||
|
||||
int GetProfileParameter(const char* aParameter);
|
||||
|
||||
bool Translate(android::output_format aContainer, nsAString& aContainerName);
|
||||
bool GetMimeType(android::output_format aContainer, nsAString& aMimeType);
|
||||
bool IsValid() const { return mIsValid; };
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsresult ConfigureRecorder(android::GonkRecorder& aRecorder);
|
||||
#endif
|
||||
static already_AddRefed<GonkRecorderProfile> CreateProfile(uint32_t aCameraId,
|
||||
int aQuality);
|
||||
static ProfileHashtable* GetProfileHashtable(uint32_t aCameraId);
|
||||
|
||||
uint32_t mCameraId;
|
||||
int mQuality;
|
||||
bool mIsValid;
|
||||
android::output_format mOutputFormat;
|
||||
|
||||
static nsClassHashtable<nsUint32HashKey, ProfileHashtable> sProfiles;
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(GonkRecorderProfile);
|
||||
};
|
||||
|
||||
}; // namespace mozilla
|
||||
|
||||
#endif // DOM_CAMERA_GONK_RECORDER_PROFILES_H
|
|
@ -1,337 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DOM_CAMERA_ICAMERACONTROL_H
|
||||
#define DOM_CAMERA_ICAMERACONTROL_H
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "base/basictypes.h"
|
||||
|
||||
struct DeviceStorageFileDescriptor;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class CameraControlListener;
|
||||
|
||||
// XXXmikeh - In some future patch this should move into the ICameraControl
|
||||
// class as well, and the names updated to the 'k'-style;
|
||||
// e.g. kParamPreviewSize, etc.
|
||||
enum {
|
||||
// current settings
|
||||
CAMERA_PARAM_PREVIEWSIZE,
|
||||
CAMERA_PARAM_PREVIEWFORMAT,
|
||||
CAMERA_PARAM_PREVIEWFRAMERATE,
|
||||
CAMERA_PARAM_VIDEOSIZE,
|
||||
CAMERA_PARAM_PICTURE_SIZE,
|
||||
CAMERA_PARAM_PICTURE_FILEFORMAT,
|
||||
CAMERA_PARAM_PICTURE_ROTATION,
|
||||
CAMERA_PARAM_PICTURE_LOCATION,
|
||||
CAMERA_PARAM_PICTURE_DATETIME,
|
||||
CAMERA_PARAM_PICTURE_QUALITY,
|
||||
CAMERA_PARAM_EFFECT,
|
||||
CAMERA_PARAM_WHITEBALANCE,
|
||||
CAMERA_PARAM_SCENEMODE,
|
||||
CAMERA_PARAM_FLASHMODE,
|
||||
CAMERA_PARAM_FOCUSMODE,
|
||||
CAMERA_PARAM_ZOOM,
|
||||
CAMERA_PARAM_METERINGAREAS,
|
||||
CAMERA_PARAM_FOCUSAREAS,
|
||||
CAMERA_PARAM_FOCALLENGTH,
|
||||
CAMERA_PARAM_FOCUSDISTANCENEAR,
|
||||
CAMERA_PARAM_FOCUSDISTANCEOPTIMUM,
|
||||
CAMERA_PARAM_FOCUSDISTANCEFAR,
|
||||
CAMERA_PARAM_EXPOSURECOMPENSATION,
|
||||
CAMERA_PARAM_THUMBNAILSIZE,
|
||||
CAMERA_PARAM_THUMBNAILQUALITY,
|
||||
CAMERA_PARAM_SENSORANGLE,
|
||||
CAMERA_PARAM_ISOMODE,
|
||||
CAMERA_PARAM_LUMINANCE,
|
||||
CAMERA_PARAM_SCENEMODE_HDR_RETURNNORMALPICTURE,
|
||||
CAMERA_PARAM_RECORDINGHINT,
|
||||
CAMERA_PARAM_PREFERRED_PREVIEWSIZE_FOR_VIDEO,
|
||||
CAMERA_PARAM_METERINGMODE,
|
||||
|
||||
// supported features
|
||||
CAMERA_PARAM_SUPPORTED_PREVIEWSIZES,
|
||||
CAMERA_PARAM_SUPPORTED_PICTURESIZES,
|
||||
CAMERA_PARAM_SUPPORTED_VIDEOSIZES,
|
||||
CAMERA_PARAM_SUPPORTED_PICTUREFORMATS,
|
||||
CAMERA_PARAM_SUPPORTED_WHITEBALANCES,
|
||||
CAMERA_PARAM_SUPPORTED_SCENEMODES,
|
||||
CAMERA_PARAM_SUPPORTED_EFFECTS,
|
||||
CAMERA_PARAM_SUPPORTED_FLASHMODES,
|
||||
CAMERA_PARAM_SUPPORTED_FOCUSMODES,
|
||||
CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS,
|
||||
CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS,
|
||||
CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION,
|
||||
CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION,
|
||||
CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP,
|
||||
CAMERA_PARAM_SUPPORTED_ZOOM,
|
||||
CAMERA_PARAM_SUPPORTED_ZOOMRATIOS,
|
||||
CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES,
|
||||
CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES,
|
||||
CAMERA_PARAM_SUPPORTED_ISOMODES,
|
||||
CAMERA_PARAM_SUPPORTED_METERINGMODES
|
||||
};
|
||||
|
||||
class ICameraControl
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ICameraControl)
|
||||
|
||||
// Returns the number of cameras supported by the system.
|
||||
//
|
||||
// Return values:
|
||||
// - NS_OK on success;
|
||||
// - NS_ERROR_FAILURE if the camera count cannot be retrieved.
|
||||
static nsresult GetNumberOfCameras(int32_t& aDeviceCount);
|
||||
|
||||
// Gets the (possibly-meaningful) name of a particular camera.
|
||||
//
|
||||
// Return values:
|
||||
// - NS_OK on success;
|
||||
// - NS_ERROR_INVALID_ARG if 'aDeviceNum' is not a valid camera number;
|
||||
// - NS_ERROR_NOT_AVAILABLE if 'aDeviceNum' is valid but the camera name
|
||||
// could still not be retrieved.
|
||||
static nsresult GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName);
|
||||
|
||||
// Returns a list of names of all cameras supported by the system.
|
||||
//
|
||||
// Return values:
|
||||
// - NS_OK on success, even if no camera names are returned (in which
|
||||
// case 'aList' will be empty);
|
||||
// - NS_ERROR_NOT_AVAILABLE if the list of cameras cannot be retrieved.
|
||||
static nsresult GetListOfCameras(nsTArray<nsString>& aList);
|
||||
|
||||
enum Mode {
|
||||
kUnspecifiedMode,
|
||||
kPictureMode,
|
||||
kVideoMode,
|
||||
};
|
||||
|
||||
struct Size {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
|
||||
bool Equals(const Size& aSize) const
|
||||
{
|
||||
return width == aSize.width && height == aSize.height;
|
||||
}
|
||||
};
|
||||
|
||||
struct Region {
|
||||
int32_t top;
|
||||
int32_t left;
|
||||
int32_t bottom;
|
||||
int32_t right;
|
||||
uint32_t weight;
|
||||
};
|
||||
|
||||
struct Position {
|
||||
double latitude;
|
||||
double longitude;
|
||||
double altitude;
|
||||
double timestamp;
|
||||
};
|
||||
|
||||
struct StartRecordingOptions {
|
||||
uint32_t rotation;
|
||||
uint64_t maxFileSizeBytes;
|
||||
uint64_t maxVideoLengthMs;
|
||||
bool autoEnableLowLightTorch;
|
||||
bool createPoster;
|
||||
};
|
||||
|
||||
struct Configuration {
|
||||
Mode mMode;
|
||||
Size mPreviewSize;
|
||||
Size mPictureSize;
|
||||
nsString mRecorderProfile;
|
||||
};
|
||||
|
||||
struct Point {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
};
|
||||
|
||||
struct Face {
|
||||
uint32_t id;
|
||||
uint32_t score;
|
||||
Region bound;
|
||||
bool hasLeftEye;
|
||||
Point leftEye;
|
||||
bool hasRightEye;
|
||||
Point rightEye;
|
||||
bool hasMouth;
|
||||
Point mouth;
|
||||
};
|
||||
|
||||
class RecorderProfile
|
||||
{
|
||||
public:
|
||||
class Video
|
||||
{
|
||||
public:
|
||||
Video() { }
|
||||
virtual ~Video() { }
|
||||
|
||||
const nsString& GetCodec() const { return mCodec; }
|
||||
const Size& GetSize() const { return mSize; }
|
||||
uint32_t GetBitsPerSecond() const { return mBitsPerSecond; }
|
||||
uint32_t GetFramesPerSecond() const { return mFramesPerSecond; }
|
||||
|
||||
protected:
|
||||
nsString mCodec;
|
||||
Size mSize;
|
||||
uint32_t mBitsPerSecond;
|
||||
uint32_t mFramesPerSecond;
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(Video);
|
||||
};
|
||||
|
||||
class Audio
|
||||
{
|
||||
public:
|
||||
Audio() { }
|
||||
virtual ~Audio() { }
|
||||
|
||||
const nsString& GetCodec() const { return mCodec; }
|
||||
|
||||
uint32_t GetChannels() const { return mChannels; }
|
||||
uint32_t GetBitsPerSecond() const { return mBitsPerSecond; }
|
||||
uint32_t GetSamplesPerSecond() const { return mSamplesPerSecond; }
|
||||
|
||||
protected:
|
||||
nsString mCodec;
|
||||
uint32_t mChannels;
|
||||
uint32_t mBitsPerSecond;
|
||||
uint32_t mSamplesPerSecond;
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(Audio);
|
||||
};
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecorderProfile)
|
||||
|
||||
RecorderProfile()
|
||||
{ }
|
||||
|
||||
const nsString& GetName() const { return mName; }
|
||||
const nsString& GetContainer() const { return mContainer; }
|
||||
const nsString& GetMimeType() const { return mMimeType; }
|
||||
uint32_t GetPriority() const { return mPriority; }
|
||||
|
||||
virtual const Video& GetVideo() const = 0;
|
||||
virtual const Audio& GetAudio() const = 0;
|
||||
|
||||
protected:
|
||||
virtual ~RecorderProfile() { }
|
||||
|
||||
nsString mName;
|
||||
nsString mContainer;
|
||||
nsString mMimeType;
|
||||
uint32_t mPriority;
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(RecorderProfile);
|
||||
};
|
||||
|
||||
static already_AddRefed<ICameraControl> Create(uint32_t aCameraId);
|
||||
|
||||
virtual void AddListener(CameraControlListener* aListener) = 0;
|
||||
virtual void RemoveListener(CameraControlListener* aListener) = 0;
|
||||
|
||||
// Camera control methods.
|
||||
//
|
||||
// Return values:
|
||||
// - NS_OK on success (if the method requires an asynchronous process,
|
||||
// this value indicates that the process has begun successfully);
|
||||
// - NS_ERROR_INVALID_ARG if one or more arguments is invalid;
|
||||
// - NS_ERROR_FAILURE if an asynchronous method could not be dispatched.
|
||||
virtual nsresult Start(const Configuration* aInitialConfig = nullptr) = 0;
|
||||
virtual nsresult Stop() = 0;
|
||||
virtual nsresult SetConfiguration(const Configuration& aConfig) = 0;
|
||||
virtual nsresult StartPreview() = 0;
|
||||
virtual nsresult StopPreview() = 0;
|
||||
virtual nsresult AutoFocus() = 0;
|
||||
virtual nsresult TakePicture() = 0;
|
||||
virtual nsresult StartRecording(DeviceStorageFileDescriptor* aFileDescriptor,
|
||||
const StartRecordingOptions* aOptions = nullptr) = 0;
|
||||
virtual nsresult StopRecording() = 0;
|
||||
virtual nsresult PauseRecording() = 0;
|
||||
virtual nsresult ResumeRecording() = 0;
|
||||
virtual nsresult StartFaceDetection() = 0;
|
||||
virtual nsresult StopFaceDetection() = 0;
|
||||
virtual nsresult ResumeContinuousFocus() = 0;
|
||||
|
||||
// Camera parameter getters and setters. 'aKey' must be one of the
|
||||
// CAMERA_PARAM_* values enumerated above.
|
||||
//
|
||||
// Return values:
|
||||
// - NS_OK on success;
|
||||
// - NS_ERROR_INVALID_ARG if 'aValue' contains an invalid value;
|
||||
// - NS_ERROR_NOT_IMPLEMENTED if 'aKey' is invalid;
|
||||
// - NS_ERROR_NOT_AVAILABLE if the getter fails to retrieve a valid value,
|
||||
// or if a setter fails because it requires one or more values that
|
||||
// could not be retrieved;
|
||||
// - NS_ERROR_FAILURE on unexpected internal failures.
|
||||
virtual nsresult Set(uint32_t aKey, const nsAString& aValue) = 0;
|
||||
virtual nsresult Get(uint32_t aKey, nsAString& aValue) = 0;
|
||||
virtual nsresult Set(uint32_t aKey, double aValue) = 0;
|
||||
virtual nsresult Get(uint32_t aKey, double& aValue) = 0;
|
||||
virtual nsresult Set(uint32_t aKey, int32_t aValue) = 0;
|
||||
virtual nsresult Get(uint32_t aKey, int32_t& aValue) = 0;
|
||||
virtual nsresult Set(uint32_t aKey, int64_t aValue) = 0;
|
||||
virtual nsresult Get(uint32_t aKey, int64_t& aValue) = 0;
|
||||
virtual nsresult Set(uint32_t aKey, bool aValue) = 0;
|
||||
virtual nsresult Get(uint32_t aKey, bool& aValue) = 0;
|
||||
virtual nsresult Set(uint32_t aKey, const Size& aValue) = 0;
|
||||
virtual nsresult Get(uint32_t aKey, Size& aValue) = 0;
|
||||
virtual nsresult Set(uint32_t aKey, const nsTArray<Region>& aRegions) = 0;
|
||||
virtual nsresult Get(uint32_t aKey, nsTArray<Region>& aRegions) = 0;
|
||||
|
||||
virtual nsresult SetLocation(const Position& aLocation) = 0;
|
||||
|
||||
virtual nsresult Get(uint32_t aKey, nsTArray<Size>& aSizes) = 0;
|
||||
virtual nsresult Get(uint32_t aKey, nsTArray<nsString>& aValues) = 0;
|
||||
virtual nsresult Get(uint32_t aKey, nsTArray<double>& aValues) = 0;
|
||||
|
||||
virtual nsresult GetRecorderProfiles(nsTArray<nsString>& aProfiles) = 0;
|
||||
virtual RecorderProfile* GetProfileInfo(const nsAString& aProfile) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~ICameraControl() { }
|
||||
|
||||
friend class ICameraControlParameterSetAutoEnter;
|
||||
|
||||
virtual void BeginBatchParameterSet() = 0;
|
||||
virtual void EndBatchParameterSet() = 0;
|
||||
};
|
||||
|
||||
// Helper class to make it easy to update a batch of camera parameters;
|
||||
// the parameters are applied atomically when this object goes out of
|
||||
// scope.
|
||||
class ICameraControlParameterSetAutoEnter
|
||||
{
|
||||
public:
|
||||
explicit ICameraControlParameterSetAutoEnter(ICameraControl* aCameraControl)
|
||||
: mCameraControl(aCameraControl)
|
||||
{
|
||||
mCameraControl->BeginBatchParameterSet();
|
||||
}
|
||||
virtual ~ICameraControlParameterSetAutoEnter()
|
||||
{
|
||||
mCameraControl->EndBatchParameterSet();
|
||||
}
|
||||
|
||||
protected:
|
||||
RefPtr<ICameraControl> mCameraControl;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_CAMERA_ICAMERACONTROL_H
|
|
@ -1,28 +0,0 @@
|
|||
This README file details from where some of the camcorder source files were derived from and how to apply the provided patch file to get the updated files for B2G.
|
||||
---------------------------------
|
||||
|
||||
Following is the list of B2G files which were derived from an android ics_chocolate build. It also shows the corresponding locations where the original source files can be found:
|
||||
|
||||
GonkRecoder.cpp:
|
||||
https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=media/libmediaplayerservice/StagefrightRecorder.cpp;hb=ef1672482a9c2b88d8017927df68144fee42626c
|
||||
|
||||
GonkRecorder.h:
|
||||
https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=media/libmediaplayerservice/StagefrightRecorder.h;hb=e3682213bcd3fe43b059e00f0fe4dbebc3f3c35d
|
||||
|
||||
GonkCameraSource.cpp:
|
||||
https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=media/libstagefright/CameraSource.cpp;hb=7fa677babfee9c241a131b22c9c1c5ab512ef2d2
|
||||
|
||||
GonkCameraSource.h:
|
||||
https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=include/media/stagefright/CameraSource.h;hb=96af14d9b013496accf40a85a66fefcba3ac0111
|
||||
|
||||
AudioParameter.cpp:
|
||||
https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=media/libmedia/AudioParameter.cpp;hb=4dc22e77cfd2a1c3671e5646ee87c5e4c15596a0
|
||||
|
||||
GonkCameraListener.h:
|
||||
https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=include/camera/Camera.h;hb=796f35e408d9dca386f90d8fbde80471ac011fa6
|
||||
|
||||
There were quite a few changes done to the above listed sources to support camcorder on B2G platform.
|
||||
update.patch lists the changes on top of the original files.
|
||||
update.sh shell script copies the files from an android tree and applies the patch to get updated files for B2G.
|
||||
|
||||
|
|
@ -1,278 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "TestGonkCameraControl.h"
|
||||
#include "CameraPreferences.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
TestGonkCameraControl::TestGonkCameraControl(uint32_t aCameraId)
|
||||
: nsGonkCameraControl(aCameraId)
|
||||
{
|
||||
DOM_CAMERA_LOGA("v===== Created TestGonkCameraControl =====v\n");
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
}
|
||||
|
||||
TestGonkCameraControl::~TestGonkCameraControl()
|
||||
{
|
||||
DOM_CAMERA_LOGA("^===== Destroyed TestGonkCameraControl =====^\n");
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::ForceMethodFailWithCodeInternal(const char* aFile, int aLine)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
CameraPreferences::GetPref("camera.control.test.method.error", rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGI("[%s:%d] CameraControl method error override: 0x%x\n",
|
||||
aFile, aLine, rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::ForceAsyncFailWithCodeInternal(const char* aFile, int aLine)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
CameraPreferences::GetPref("camera.control.test.async.error", rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGI("[%s:%d] CameraControl async error override: 0x%x\n",
|
||||
aFile, aLine, rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::Start(const Configuration* aConfig)
|
||||
{
|
||||
nsresult rv = ForceMethodFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::Start(aConfig);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::StartImpl(const Configuration* aInitialConfig)
|
||||
{
|
||||
nsresult rv = ForceAsyncFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::StartImpl(aInitialConfig);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::Stop()
|
||||
{
|
||||
nsresult rv = ForceMethodFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::Stop();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::StopImpl()
|
||||
{
|
||||
nsresult rv = ForceAsyncFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::StopImpl();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::SetConfiguration(const Configuration& aConfig)
|
||||
{
|
||||
nsresult rv = ForceMethodFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::SetConfiguration(aConfig);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::SetConfigurationImpl(const Configuration& aConfig)
|
||||
{
|
||||
nsresult rv = ForceAsyncFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::SetConfigurationImpl(aConfig);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::StartPreview()
|
||||
{
|
||||
nsresult rv = ForceMethodFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::StartPreview();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::StartPreviewImpl()
|
||||
{
|
||||
nsresult rv = ForceAsyncFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::StartImpl();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::StopPreview()
|
||||
{
|
||||
nsresult rv = ForceMethodFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::StopPreview();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::StopPreviewImpl()
|
||||
{
|
||||
nsresult rv = ForceAsyncFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::StartImpl();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::AutoFocus()
|
||||
{
|
||||
nsresult rv = ForceMethodFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::AutoFocus();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::AutoFocusImpl()
|
||||
{
|
||||
nsresult rv = ForceAsyncFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::AutoFocusImpl();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::StartFaceDetection()
|
||||
{
|
||||
nsresult rv = ForceMethodFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::StartFaceDetection();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::StartFaceDetectionImpl()
|
||||
{
|
||||
nsresult rv = ForceAsyncFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::StartFaceDetectionImpl();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::StopFaceDetection()
|
||||
{
|
||||
nsresult rv = ForceMethodFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::StopFaceDetection();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::StopFaceDetectionImpl()
|
||||
{
|
||||
nsresult rv = ForceAsyncFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::StopFaceDetectionImpl();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::TakePicture()
|
||||
{
|
||||
nsresult rv = ForceMethodFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::TakePicture();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::TakePictureImpl()
|
||||
{
|
||||
nsresult rv = ForceAsyncFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::TakePictureImpl();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::StartRecording(DeviceStorageFileDescriptor* aFileDescriptor,
|
||||
const StartRecordingOptions* aOptions)
|
||||
{
|
||||
nsresult rv = ForceMethodFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::StartRecording(aFileDescriptor, aOptions);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
|
||||
const StartRecordingOptions* aOptions)
|
||||
{
|
||||
nsresult rv = ForceAsyncFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::StartRecordingImpl(aFileDescriptor, aOptions);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::StopRecording()
|
||||
{
|
||||
nsresult rv = ForceMethodFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::StopRecording();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraControl::StopRecordingImpl()
|
||||
{
|
||||
nsresult rv = ForceAsyncFailWithCode();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return nsGonkCameraControl::StopRecordingImpl();
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef DOM_CAMERA_TESTGONKCAMERACONTROL_H
|
||||
#define DOM_CAMERA_TESTGONKCAMERACONTROL_H
|
||||
|
||||
#include "GonkCameraControl.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class TestGonkCameraControl : public nsGonkCameraControl
|
||||
{
|
||||
public:
|
||||
TestGonkCameraControl(uint32_t aCameraId);
|
||||
|
||||
virtual nsresult Start(const Configuration* aConfig = nullptr) override;
|
||||
virtual nsresult Stop() override;
|
||||
virtual nsresult SetConfiguration(const Configuration& aConfig) override;
|
||||
virtual nsresult StartPreview() override;
|
||||
virtual nsresult StopPreview() override;
|
||||
virtual nsresult AutoFocus() override;
|
||||
virtual nsresult StartFaceDetection() override;
|
||||
virtual nsresult StopFaceDetection() override;
|
||||
virtual nsresult TakePicture() override;
|
||||
virtual nsresult StartRecording(DeviceStorageFileDescriptor* aFileDescriptor,
|
||||
const StartRecordingOptions* aOptions) override;
|
||||
virtual nsresult StopRecording() override;
|
||||
|
||||
protected:
|
||||
virtual ~TestGonkCameraControl();
|
||||
|
||||
virtual nsresult StartImpl(const Configuration* aInitialConfig = nullptr) override;
|
||||
virtual nsresult StopImpl() override;
|
||||
virtual nsresult SetConfigurationImpl(const Configuration& aConfig) override;
|
||||
virtual nsresult StartPreviewImpl() override;
|
||||
virtual nsresult StopPreviewImpl() override;
|
||||
virtual nsresult AutoFocusImpl() override;
|
||||
virtual nsresult StartFaceDetectionImpl() override;
|
||||
virtual nsresult StopFaceDetectionImpl() override;
|
||||
virtual nsresult TakePictureImpl() override;
|
||||
virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
|
||||
const StartRecordingOptions* aOptions = nullptr) override;
|
||||
virtual nsresult StopRecordingImpl() override;
|
||||
|
||||
nsresult ForceMethodFailWithCodeInternal(const char* aFile, int aLine);
|
||||
nsresult ForceAsyncFailWithCodeInternal(const char* aFile, int aLine);
|
||||
|
||||
private:
|
||||
TestGonkCameraControl(const TestGonkCameraControl&) = delete;
|
||||
TestGonkCameraControl& operator=(const TestGonkCameraControl&) = delete;
|
||||
};
|
||||
|
||||
#define ForceMethodFailWithCode() ForceMethodFailWithCodeInternal(__FILE__, __LINE__)
|
||||
#define ForceAsyncFailWithCode() ForceAsyncFailWithCodeInternal(__FILE__, __LINE__)
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_CAMERA_TESTGONKCAMERACONTROL_H
|
|
@ -1,757 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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 "TestGonkCameraHardware.h"
|
||||
#include "CameraPreferences.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/dom/EventListenerBinding.h"
|
||||
#include "mozilla/dom/BlobEvent.h"
|
||||
#include "mozilla/dom/ErrorEvent.h"
|
||||
#include "mozilla/dom/CameraFacesDetectedEvent.h"
|
||||
#include "mozilla/dom/CameraStateChangeEvent.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "DOMCameraDetectedFace.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsICameraTestHardware.h"
|
||||
|
||||
using namespace android;
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
#ifndef MOZ_WIDGET_GONK
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(TestGonkCameraHardware, GonkCameraHardware);
|
||||
#endif
|
||||
|
||||
static void
|
||||
CopyFaceFeature(int32_t (&aDst)[2], bool aExists, const DOMPoint* aSrc)
|
||||
{
|
||||
if (aExists && aSrc) {
|
||||
aDst[0] = static_cast<int32_t>(aSrc->X());
|
||||
aDst[1] = static_cast<int32_t>(aSrc->Y());
|
||||
} else {
|
||||
aDst[0] = -2000;
|
||||
aDst[1] = -2000;
|
||||
}
|
||||
}
|
||||
|
||||
class TestGonkCameraHardwareListener : public nsIDOMEventListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
TestGonkCameraHardwareListener(nsGonkCameraControl* aTarget, nsIThread* aCameraThread)
|
||||
: mTarget(aTarget)
|
||||
, mCameraThread(aCameraThread)
|
||||
{
|
||||
MOZ_COUNT_CTOR(TestGonkCameraHardwareListener);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~TestGonkCameraHardwareListener()
|
||||
{
|
||||
MOZ_COUNT_DTOR(TestGonkCameraHardwareListener);
|
||||
}
|
||||
|
||||
RefPtr<nsGonkCameraControl> mTarget;
|
||||
nsCOMPtr<nsIThread> mCameraThread;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
TestGonkCameraHardwareListener::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
nsString eventType;
|
||||
aEvent->GetType(eventType);
|
||||
|
||||
DOM_CAMERA_LOGI("Inject '%s' event",
|
||||
NS_ConvertUTF16toUTF8(eventType).get());
|
||||
|
||||
if (eventType.EqualsLiteral("focus")) {
|
||||
CameraStateChangeEvent* event = aEvent->InternalDOMEvent()->AsCameraStateChangeEvent();
|
||||
|
||||
if (!NS_WARN_IF(!event)) {
|
||||
nsString state;
|
||||
|
||||
event->GetNewState(state);
|
||||
if (state.EqualsLiteral("focused")) {
|
||||
OnAutoFocusComplete(mTarget, true);
|
||||
} else if (state.EqualsLiteral("unfocused")) {
|
||||
OnAutoFocusComplete(mTarget, false);
|
||||
} else if (state.EqualsLiteral("focusing")) {
|
||||
OnAutoFocusMoving(mTarget, true);
|
||||
} else if (state.EqualsLiteral("not_focusing")) {
|
||||
OnAutoFocusMoving(mTarget, false);
|
||||
} else {
|
||||
DOM_CAMERA_LOGE("Unhandled focus state '%s'\n",
|
||||
NS_ConvertUTF16toUTF8(state).get());
|
||||
}
|
||||
}
|
||||
} else if (eventType.EqualsLiteral("shutter")) {
|
||||
DOM_CAMERA_LOGI("Inject shutter event");
|
||||
OnShutter(mTarget);
|
||||
} else if (eventType.EqualsLiteral("picture")) {
|
||||
BlobEvent* event = aEvent->InternalDOMEvent()->AsBlobEvent();
|
||||
|
||||
if (!NS_WARN_IF(!event)) {
|
||||
Blob* blob = event->GetData();
|
||||
|
||||
if (blob) {
|
||||
static const uint64_t MAX_FILE_SIZE = 2147483647;
|
||||
|
||||
ErrorResult rv;
|
||||
uint64_t dataLength = blob->GetSize(rv);
|
||||
|
||||
if (NS_WARN_IF(rv.Failed()) || NS_WARN_IF(dataLength > MAX_FILE_SIZE)) {
|
||||
rv.SuppressException();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
blob->GetInternalStream(getter_AddRefs(inputStream), rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint8_t* data = new uint8_t[dataLength];
|
||||
rv = NS_ReadInputStreamToBuffer(inputStream,
|
||||
reinterpret_cast<void**>(&data),
|
||||
static_cast<uint32_t>(dataLength));
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
delete [] data;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
OnTakePictureComplete(mTarget, data, dataLength);
|
||||
delete [] data;
|
||||
} else {
|
||||
OnTakePictureComplete(mTarget, nullptr, 0);
|
||||
}
|
||||
}
|
||||
} else if(eventType.EqualsLiteral("error")) {
|
||||
ErrorEvent* event = aEvent->InternalDOMEvent()->AsErrorEvent();
|
||||
|
||||
if (!NS_WARN_IF(!event)) {
|
||||
nsString errorType;
|
||||
|
||||
event->GetMessage(errorType);
|
||||
if (errorType.EqualsLiteral("picture")) {
|
||||
OnTakePictureError(mTarget);
|
||||
} else if (errorType.EqualsLiteral("system")) {
|
||||
if (!NS_WARN_IF(!mCameraThread)) {
|
||||
class DeferredSystemFailure : public Runnable
|
||||
{
|
||||
public:
|
||||
DeferredSystemFailure(nsGonkCameraControl* aTarget)
|
||||
: mTarget(aTarget)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
OnSystemError(mTarget, CameraControlListener::kSystemService, 100, 0);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
RefPtr<nsGonkCameraControl> mTarget;
|
||||
};
|
||||
|
||||
mCameraThread->Dispatch(new DeferredSystemFailure(mTarget), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
} else {
|
||||
DOM_CAMERA_LOGE("Unhandled error event type '%s'\n",
|
||||
NS_ConvertUTF16toUTF8(errorType).get());
|
||||
}
|
||||
}
|
||||
} else if(eventType.EqualsLiteral("facesdetected")) {
|
||||
CameraFacesDetectedEvent* event = aEvent->InternalDOMEvent()->AsCameraFacesDetectedEvent();
|
||||
|
||||
if (!NS_WARN_IF(!event)) {
|
||||
Nullable<nsTArray<RefPtr<DOMCameraDetectedFace>>> faces;
|
||||
event->GetFaces(faces);
|
||||
|
||||
camera_frame_metadata_t metadata;
|
||||
memset(&metadata, 0, sizeof(metadata));
|
||||
|
||||
if (faces.IsNull()) {
|
||||
OnFacesDetected(mTarget, &metadata);
|
||||
} else {
|
||||
const nsTArray<RefPtr<DOMCameraDetectedFace>>& facesData = faces.Value();
|
||||
uint32_t i = facesData.Length();
|
||||
|
||||
metadata.number_of_faces = i;
|
||||
metadata.faces = new camera_face_t[i];
|
||||
memset(metadata.faces, 0, sizeof(camera_face_t) * i);
|
||||
|
||||
while (i > 0) {
|
||||
--i;
|
||||
const RefPtr<DOMCameraDetectedFace>& face = facesData[i];
|
||||
camera_face_t& f = metadata.faces[i];
|
||||
const DOMRect& bounds = *face->Bounds();
|
||||
f.rect[0] = static_cast<int32_t>(bounds.Left());
|
||||
f.rect[1] = static_cast<int32_t>(bounds.Top());
|
||||
f.rect[2] = static_cast<int32_t>(bounds.Right());
|
||||
f.rect[3] = static_cast<int32_t>(bounds.Bottom());
|
||||
CopyFaceFeature(f.left_eye, face->HasLeftEye(), face->GetLeftEye());
|
||||
CopyFaceFeature(f.right_eye, face->HasRightEye(), face->GetRightEye());
|
||||
CopyFaceFeature(f.mouth, face->HasMouth(), face->GetMouth());
|
||||
f.id = face->Id();
|
||||
f.score = face->Score();
|
||||
}
|
||||
|
||||
OnFacesDetected(mTarget, &metadata);
|
||||
delete [] metadata.faces;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DOM_CAMERA_LOGE("Unhandled injected event '%s'",
|
||||
NS_ConvertUTF16toUTF8(eventType).get());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(TestGonkCameraHardwareListener, nsIDOMEventListener)
|
||||
|
||||
class TestGonkCameraHardware::ControlMessage : public Runnable
|
||||
{
|
||||
public:
|
||||
ControlMessage(TestGonkCameraHardware* aTestHw)
|
||||
: mTestHw(aTestHw)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
if (NS_WARN_IF(!mTestHw)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
MutexAutoLock lock(mTestHw->mMutex);
|
||||
|
||||
mTestHw->mStatus = RunInline();
|
||||
DebugOnly<nsresult> rv = mTestHw->mCondVar.Notify();
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Notify failed");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
RunInline()
|
||||
{
|
||||
if (NS_WARN_IF(!mTestHw)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
mJSTestWrapper = do_GetService("@mozilla.org/cameratesthardware;1", &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
DOM_CAMERA_LOGE("Cannot get camera test service\n");
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = RunImpl();
|
||||
mJSTestWrapper = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
protected:
|
||||
NS_IMETHOD RunImpl() = 0;
|
||||
virtual ~ControlMessage() { }
|
||||
|
||||
nsCOMPtr<nsICameraTestHardware> mJSTestWrapper;
|
||||
|
||||
/* Since we block the control thread until we have finished
|
||||
processing the request on the main thread, we know that this
|
||||
pointer will not go out of scope because the control thread
|
||||
and calling class is the owner. */
|
||||
TestGonkCameraHardware* mTestHw;
|
||||
};
|
||||
|
||||
TestGonkCameraHardware::TestGonkCameraHardware(nsGonkCameraControl* aTarget,
|
||||
uint32_t aCameraId,
|
||||
const sp<Camera>& aCamera)
|
||||
: GonkCameraHardware(aTarget, aCameraId, aCamera)
|
||||
, mMutex("TestGonkCameraHardware::mMutex")
|
||||
, mCondVar(mMutex, "TestGonkCameraHardware::mCondVar")
|
||||
{
|
||||
DOM_CAMERA_LOGA("v===== Created TestGonkCameraHardware =====v\n");
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p (aTarget=%p)\n",
|
||||
__func__, __LINE__, this, aTarget);
|
||||
MOZ_COUNT_CTOR(TestGonkCameraHardware);
|
||||
mCameraThread = NS_GetCurrentThread();
|
||||
}
|
||||
|
||||
TestGonkCameraHardware::~TestGonkCameraHardware()
|
||||
{
|
||||
MOZ_COUNT_DTOR(TestGonkCameraHardware);
|
||||
|
||||
class Delegate : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Delegate(TestGonkCameraHardware* aTestHw)
|
||||
: ControlMessage(aTestHw)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
RunImpl() override
|
||||
{
|
||||
if (mTestHw->mDomListener) {
|
||||
mTestHw->mDomListener = nullptr;
|
||||
DebugOnly<nsresult> rv = mJSTestWrapper->SetHandler(nullptr);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetHandler failed");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
DebugOnly<nsresult> rv = WaitWhileRunningOnMainThread(new Delegate(this));
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WaitWhileRunningOnMainThread failed");
|
||||
DOM_CAMERA_LOGA("^===== Destroyed TestGonkCameraHardware =====^\n");
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraHardware::WaitWhileRunningOnMainThread(RefPtr<ControlMessage> aRunnable)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (NS_WARN_IF(!aRunnable)) {
|
||||
mStatus = NS_ERROR_INVALID_ARG;
|
||||
} else if (!NS_IsMainThread()) {
|
||||
nsresult rv = NS_DispatchToMainThread(aRunnable);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = mCondVar.Wait();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
/* Cannot dispatch to main thread since we would block on
|
||||
the condvar, so we need to run inline. */
|
||||
mStatus = aRunnable->RunInline();
|
||||
}
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraHardware::Init()
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
|
||||
class Delegate : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Delegate(TestGonkCameraHardware* aTestHw)
|
||||
: ControlMessage(aTestHw)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
RunImpl() override
|
||||
{
|
||||
nsresult rv = mJSTestWrapper->InitCamera();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mTestHw->mDomListener = new TestGonkCameraHardwareListener(mTestHw->mTarget, mTestHw->mCameraThread);
|
||||
if (NS_WARN_IF(!mTestHw->mDomListener)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
rv = mJSTestWrapper->SetHandler(mTestHw->mDomListener);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
DebugOnly<nsresult> rv = WaitWhileRunningOnMainThread(new Delegate(this));
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WaitWhileRunningOnMainThread failed");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int
|
||||
TestGonkCameraHardware::AutoFocus()
|
||||
{
|
||||
class Delegate : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Delegate(TestGonkCameraHardware* aTestHw)
|
||||
: ControlMessage(aTestHw)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
RunImpl() override
|
||||
{
|
||||
return mJSTestWrapper->AutoFocus();
|
||||
}
|
||||
};
|
||||
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
nsresult rv = WaitWhileRunningOnMainThread(new Delegate(this));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
TestGonkCameraHardware::CancelAutoFocus()
|
||||
{
|
||||
class Delegate : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Delegate(TestGonkCameraHardware* aTestHw)
|
||||
: ControlMessage(aTestHw)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
RunImpl() override
|
||||
{
|
||||
return mJSTestWrapper->CancelAutoFocus();
|
||||
}
|
||||
};
|
||||
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
nsresult rv = WaitWhileRunningOnMainThread(new Delegate(this));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
TestGonkCameraHardware::StartFaceDetection()
|
||||
{
|
||||
class Delegate : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Delegate(TestGonkCameraHardware* aTestHw)
|
||||
: ControlMessage(aTestHw)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
RunImpl() override
|
||||
{
|
||||
return mJSTestWrapper->StartFaceDetection();
|
||||
}
|
||||
};
|
||||
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
nsresult rv = WaitWhileRunningOnMainThread(new Delegate(this));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
TestGonkCameraHardware::StopFaceDetection()
|
||||
{
|
||||
class Delegate : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Delegate(TestGonkCameraHardware* aTestHw)
|
||||
: ControlMessage(aTestHw)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
RunImpl() override
|
||||
{
|
||||
return mJSTestWrapper->StopFaceDetection();
|
||||
}
|
||||
};
|
||||
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
nsresult rv = WaitWhileRunningOnMainThread(new Delegate(this));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
TestGonkCameraHardware::TakePicture()
|
||||
{
|
||||
class Delegate : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Delegate(TestGonkCameraHardware* aTestHw)
|
||||
: ControlMessage(aTestHw)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
RunImpl() override
|
||||
{
|
||||
return mJSTestWrapper->TakePicture();
|
||||
}
|
||||
};
|
||||
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
nsresult rv = WaitWhileRunningOnMainThread(new Delegate(this));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void
|
||||
TestGonkCameraHardware::CancelTakePicture()
|
||||
{
|
||||
class Delegate : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Delegate(TestGonkCameraHardware* aTestHw)
|
||||
: ControlMessage(aTestHw)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
RunImpl() override
|
||||
{
|
||||
return mJSTestWrapper->CancelTakePicture();
|
||||
}
|
||||
};
|
||||
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
DebugOnly<nsresult> rv = WaitWhileRunningOnMainThread(new Delegate(this));
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WaitWhileRunningOnMainThread failed");
|
||||
}
|
||||
|
||||
int
|
||||
TestGonkCameraHardware::StartPreview()
|
||||
{
|
||||
class Delegate : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Delegate(TestGonkCameraHardware* aTestHw)
|
||||
: ControlMessage(aTestHw)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
RunImpl() override
|
||||
{
|
||||
return mJSTestWrapper->StartPreview();
|
||||
}
|
||||
};
|
||||
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
nsresult rv = WaitWhileRunningOnMainThread(new Delegate(this));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void
|
||||
TestGonkCameraHardware::StopPreview()
|
||||
{
|
||||
class Delegate : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Delegate(TestGonkCameraHardware* aTestHw)
|
||||
: ControlMessage(aTestHw)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
RunImpl() override
|
||||
{
|
||||
return mJSTestWrapper->StopPreview();
|
||||
}
|
||||
};
|
||||
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
DebugOnly<nsresult> rv = WaitWhileRunningOnMainThread(new Delegate(this));
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WaitWhileRunningOnMainThread failed");
|
||||
}
|
||||
|
||||
class TestGonkCameraHardware::PushParametersDelegate : public ControlMessage
|
||||
{
|
||||
public:
|
||||
PushParametersDelegate(TestGonkCameraHardware* aTestHw, String8* aParams)
|
||||
: ControlMessage(aTestHw)
|
||||
, mParams(aParams)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
RunImpl() override
|
||||
{
|
||||
if (NS_WARN_IF(!mParams)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
DOM_CAMERA_LOGI("Push test parameters: %s\n", mParams->string());
|
||||
return mJSTestWrapper->PushParameters(NS_ConvertASCIItoUTF16(mParams->string()));
|
||||
}
|
||||
|
||||
String8* mParams;
|
||||
};
|
||||
|
||||
class TestGonkCameraHardware::PullParametersDelegate : public ControlMessage
|
||||
{
|
||||
public:
|
||||
PullParametersDelegate(TestGonkCameraHardware* aTestHw, nsString* aParams)
|
||||
: ControlMessage(aTestHw)
|
||||
, mParams(aParams)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
RunImpl() override
|
||||
{
|
||||
if (NS_WARN_IF(!mParams)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsresult rv = mJSTestWrapper->PullParameters(*mParams);
|
||||
DOM_CAMERA_LOGI("Pull test parameters: %s\n",
|
||||
NS_LossyConvertUTF16toASCII(*mParams).get());
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsString* mParams;
|
||||
};
|
||||
|
||||
int
|
||||
TestGonkCameraHardware::PushParameters(const GonkCameraParameters& aParams)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
String8 s = aParams.Flatten();
|
||||
nsresult rv = WaitWhileRunningOnMainThread(new PushParametersDelegate(this, &s));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TestGonkCameraHardware::PullParameters(GonkCameraParameters& aParams)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
nsString as;
|
||||
nsresult rv = WaitWhileRunningOnMainThread(new PullParametersDelegate(this, &as));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
String8 s(NS_LossyConvertUTF16toASCII(as).get());
|
||||
aParams.Unflatten(s);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
int
|
||||
TestGonkCameraHardware::PushParameters(const CameraParameters& aParams)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
String8 s = aParams.flatten();
|
||||
nsresult rv = WaitWhileRunningOnMainThread(new PushParametersDelegate(this, &s));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void
|
||||
TestGonkCameraHardware::PullParameters(CameraParameters& aParams)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
nsString as;
|
||||
nsresult rv = WaitWhileRunningOnMainThread(new PullParametersDelegate(this, &as));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
as.Truncate();
|
||||
}
|
||||
|
||||
String8 s(NS_LossyConvertUTF16toASCII(as).get());
|
||||
aParams.unflatten(s);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
TestGonkCameraHardware::StartRecording()
|
||||
{
|
||||
class Delegate : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Delegate(TestGonkCameraHardware* aTestHw)
|
||||
: ControlMessage(aTestHw)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
RunImpl() override
|
||||
{
|
||||
return mJSTestWrapper->StartRecording();
|
||||
}
|
||||
};
|
||||
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
nsresult rv = WaitWhileRunningOnMainThread(new Delegate(this));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
TestGonkCameraHardware::StopRecording()
|
||||
{
|
||||
class Delegate : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Delegate(TestGonkCameraHardware* aTestHw)
|
||||
: ControlMessage(aTestHw)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
NS_IMETHOD
|
||||
RunImpl() override
|
||||
{
|
||||
return mJSTestWrapper->StopRecording();
|
||||
}
|
||||
};
|
||||
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
nsresult rv = WaitWhileRunningOnMainThread(new Delegate(this));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
int
|
||||
TestGonkCameraHardware::StoreMetaDataInBuffers(bool aEnabled)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
return OK;
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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 DOM_CAMERA_TESTGONKCAMERAHARDWARE_H
|
||||
#define DOM_CAMERA_TESTGONKCAMERAHARDWARE_H
|
||||
|
||||
#include "GonkCameraHwMgr.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "mozilla/CondVar.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class TestGonkCameraHardware : public android::GonkCameraHardware
|
||||
{
|
||||
#ifndef MOZ_WIDGET_GONK
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
#endif
|
||||
|
||||
public:
|
||||
virtual nsresult Init() override;
|
||||
virtual int AutoFocus() override;
|
||||
virtual int CancelAutoFocus() override;
|
||||
virtual int StartFaceDetection() override;
|
||||
virtual int StopFaceDetection() override;
|
||||
virtual int TakePicture() override;
|
||||
virtual void CancelTakePicture() override;
|
||||
virtual int StartPreview() override;
|
||||
virtual void StopPreview() override;
|
||||
virtual int PushParameters(const mozilla::GonkCameraParameters& aParams) override;
|
||||
virtual nsresult PullParameters(mozilla::GonkCameraParameters& aParams) override;
|
||||
virtual int StartRecording() override;
|
||||
virtual int StopRecording() override;
|
||||
virtual int StoreMetaDataInBuffers(bool aEnabled) override;
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
virtual int PushParameters(const android::CameraParameters& aParams) override;
|
||||
virtual void PullParameters(android::CameraParameters& aParams) override;
|
||||
#endif
|
||||
|
||||
TestGonkCameraHardware(mozilla::nsGonkCameraControl* aTarget,
|
||||
uint32_t aCameraId,
|
||||
const android::sp<android::Camera>& aCamera);
|
||||
|
||||
protected:
|
||||
virtual ~TestGonkCameraHardware();
|
||||
|
||||
class ControlMessage;
|
||||
class PushParametersDelegate;
|
||||
class PullParametersDelegate;
|
||||
|
||||
nsresult WaitWhileRunningOnMainThread(RefPtr<ControlMessage> aRunnable);
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> mDomListener;
|
||||
nsCOMPtr<nsIThread> mCameraThread;
|
||||
mozilla::Mutex mMutex;
|
||||
mozilla::CondVar mCondVar;
|
||||
nsresult mStatus;
|
||||
|
||||
private:
|
||||
TestGonkCameraHardware(const TestGonkCameraHardware&) = delete;
|
||||
TestGonkCameraHardware& operator=(const TestGonkCameraHardware&) = delete;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_CAMERA_TESTGONKCAMERAHARDWARE_H
|
|
@ -1,80 +0,0 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
if CONFIG['MOZ_B2G_CAMERA']:
|
||||
MOCHITEST_MANIFESTS += ['test/mochitest.ini']
|
||||
|
||||
EXPORTS += [
|
||||
'CameraCommon.h',
|
||||
'CameraPreferences.h',
|
||||
'DOMCameraManager.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'CameraControlImpl.cpp',
|
||||
'CameraPreferences.cpp',
|
||||
'CameraPreviewMediaStream.cpp',
|
||||
'DOMCameraCapabilities.cpp',
|
||||
'DOMCameraControl.cpp',
|
||||
'DOMCameraControlListener.cpp',
|
||||
'DOMCameraDetectedFace.cpp',
|
||||
'DOMCameraManager.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_B2G_CAMERA']:
|
||||
XPIDL_SOURCES += [
|
||||
'nsICameraTestHardware.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'dom_camera'
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'GonkCameraControl.cpp',
|
||||
'GonkCameraHwMgr.cpp',
|
||||
'GonkCameraManager.cpp',
|
||||
'GonkCameraParameters.cpp',
|
||||
'GonkRecorderProfiles.cpp',
|
||||
'TestGonkCameraControl.cpp',
|
||||
'TestGonkCameraHardware.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
UNIFIED_SOURCES += [
|
||||
'GonkCameraSource.cpp',
|
||||
'GonkRecorder.cpp',
|
||||
]
|
||||
else:
|
||||
UNIFIED_SOURCES += [
|
||||
'FallbackCameraPlatform.cpp',
|
||||
]
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'CameraTestHardware.js',
|
||||
'CameraTestHardware.manifest',
|
||||
]
|
||||
else:
|
||||
UNIFIED_SOURCES += [
|
||||
'FallbackCameraControl.cpp',
|
||||
'FallbackCameraManager.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../base',
|
||||
'/media/libyuv/include',
|
||||
]
|
||||
|
||||
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',
|
||||
'-Wno-error=shadow',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
|
@ -1,191 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIDOMBlob;
|
||||
interface nsIDOMEventListener;
|
||||
|
||||
[scriptable, uuid(2e567730-f164-49d7-b975-862caa4425a5)]
|
||||
interface nsICameraTestHardware : nsISupports
|
||||
{
|
||||
/* The following methods are intended to be used by the test cases
|
||||
written in JavaScript to define the behaviour of the hardware: */
|
||||
|
||||
/* Attach a delegate handler object such that the test hardware
|
||||
will call the given handlers for the given operations to decide
|
||||
what to do. This allows a test case to define specific behaviours
|
||||
on a fine grained basis.
|
||||
|
||||
The following handlers may be supplied as properties of the
|
||||
given delagate handler object:
|
||||
autoFocus
|
||||
cancelAutoFocus
|
||||
cancelTakePicture
|
||||
init
|
||||
pushParameters
|
||||
pullParameters
|
||||
startFaceDetection
|
||||
startPreview
|
||||
startRecording
|
||||
stopFaceDetection
|
||||
stopPreview
|
||||
stopRecording
|
||||
takePicture
|
||||
|
||||
Implementation notes for handlers:
|
||||
|
||||
- If the handler throws an error, we will the return code
|
||||
of the driver operation.
|
||||
|
||||
- If the handler returns true, we will perform the default
|
||||
action (if any) for the operation. */
|
||||
void attach(in jsval mock);
|
||||
|
||||
/* Detach a delegate handler object such that the test hardware
|
||||
will revert to default behaviour when a function is called. */
|
||||
void detach();
|
||||
|
||||
/* Reset the state of the test hardware back to the initial state.
|
||||
This is useful when one test case has been completed and we need
|
||||
a clean slate for the next. */
|
||||
void reset(in jsval window);
|
||||
|
||||
/* Trigger an OnAutoFocusMoving callback at the Gonk layer.
|
||||
|
||||
state is a boolean indicating where or not the camera focus
|
||||
is moving. */
|
||||
void fireAutoFocusComplete(in boolean state);
|
||||
|
||||
/* Trigger an OnAutoFocusComplete callback at the Gonk layer.
|
||||
|
||||
state is a boolean indicating where or not the camera is focused. */
|
||||
void fireAutoFocusMoving(in boolean moving);
|
||||
|
||||
/* Trigger an OnTakePictureComplete callback at the Gonk layer.
|
||||
|
||||
blob should be a Blob object. The actual content of the blob
|
||||
is unimportant since nothing processes it as an image internally. */
|
||||
void fireTakePictureComplete(in nsIDOMBlob picture);
|
||||
|
||||
/* Trigger an OnTakePictureError callback at the Gonk layer. */
|
||||
void fireTakePictureError();
|
||||
|
||||
/* Trigger an OnSystemError callback at the Gonk layer. */
|
||||
void fireSystemError();
|
||||
|
||||
/* Trigger an OnShutter callback at the Gonk layer. */
|
||||
void fireShutter();
|
||||
|
||||
/* Trigger an OnFacesDetected callback at the Gonk layer.
|
||||
|
||||
faces is an array of CameraDetectedFaceInit dictionaries although
|
||||
hasLeftEye, hasRightEye and hasMouth may be omitted and will be
|
||||
implied by the presence/absence of leftEye, rightEye and mouth. */
|
||||
void fireFacesDetected(in jsval faces);
|
||||
|
||||
/* Object which stores the camera parameters read/written by the
|
||||
camera control layer from the hardware. The test case may set
|
||||
its own values to control the behaviour of the camera middleware.
|
||||
|
||||
E.g. params['preview-sizes'] = '320x240,640x480'; */
|
||||
attribute jsval params;
|
||||
|
||||
/* The following methods are intended to be used by the Gonk layer
|
||||
in order to call back into JavaScript to get test case defined
|
||||
behaviour: */
|
||||
|
||||
/* Set a handler to capture asynchronous events triggered by the
|
||||
test case via the fireXXX methods. E.g.:
|
||||
|
||||
nsCOMPtr<nsICameraHardware> wrapper =
|
||||
do_GetService("@mozilla.org/cameratesthardware;1");
|
||||
|
||||
nsCOMPtr<nsIDOMEventListener> listener = new HwListener();
|
||||
|
||||
wrapper->setHander(listener);
|
||||
|
||||
where
|
||||
|
||||
class HwListener : public nsIDOMEventListener {
|
||||
NS_IMETHODIMP HandleEvent(nsIDOMEvent *aEvent) {
|
||||
nsString type;
|
||||
aEvent->GetType(&type);
|
||||
if (aEvent.EqualsLiteral("focus")) {
|
||||
...
|
||||
} else {
|
||||
...
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
The following event types may be generated:
|
||||
focus: CameraStateChangeEvent where newState should map
|
||||
to the OnAutoFocusComplete and OnAutoFocusMoving callbacks:
|
||||
-- focused: OnAutoFocusComplete(false)
|
||||
-- unfocused: OnAutoFocusComplete(true)
|
||||
-- focusing: OnAutoFocusMoving(true)
|
||||
-- not_focusing: OnAutoFocusMoving(false)
|
||||
|
||||
picture: BlobEvent which contains the picture type and
|
||||
data corresponding to the OnTakePictureComplete callback.
|
||||
|
||||
error: ErrorEvent corresponding to the various error callbacks,
|
||||
where the message is:
|
||||
-- picture: OnTakePictureError()
|
||||
-- system: OnSystemError(100, 0)
|
||||
|
||||
facesdetected: CameraFacesDetectedEvent which contains the
|
||||
faces data corresponding to OnFacesDetected callback.
|
||||
|
||||
shutter: Event which corresponds to the OnShutter callback. */
|
||||
void setHandler(in nsIDOMEventListener handler);
|
||||
|
||||
/* Execute an intercepted Init() driver call. */
|
||||
void initCamera();
|
||||
|
||||
/* Execute an intercepted AutoFocus() driver call. Default behaviour is
|
||||
to trigger OnAutoFocusComplete where the camera is focused. */
|
||||
void autoFocus();
|
||||
|
||||
/* Execute an intercepted CancelAutoFocus() driver call. */
|
||||
void cancelAutoFocus();
|
||||
|
||||
/* Execute an intercepted StartFaceDetection() driver call. */
|
||||
void startFaceDetection();
|
||||
|
||||
/* Execute an intercepted StopFaceDetection() driver call. */
|
||||
void stopFaceDetection();
|
||||
|
||||
/* Execute an intercepted TakePicture() driver call. Default behaviour is
|
||||
to trigger OnTakePictureComplete with a fake jpeg blob. */
|
||||
void takePicture();
|
||||
|
||||
/* Execute an intercepted CancelTakePicture() driver call. */
|
||||
void cancelTakePicture();
|
||||
|
||||
/* Execute an intercepted StartPreview() driver call. */
|
||||
void startPreview();
|
||||
|
||||
/* Execute an intercepted StopPreview() driver call. */
|
||||
void stopPreview();
|
||||
|
||||
/* Execute an intercepted StartRecording() driver call. */
|
||||
void startRecording();
|
||||
|
||||
/* Execute an intercepted StopRecording() driver call. */
|
||||
void stopRecording();
|
||||
|
||||
/* Execute an intercepted PushParameters() driver call. If the delegate
|
||||
handler throws an error, it will restore the old parameters.
|
||||
When the delegate is called, the new proposed parameters are
|
||||
placed in this.params. */
|
||||
void pushParameters(in DOMString params);
|
||||
|
||||
/* Execute an intercepted PullParameters() driver call. Unless the delegate
|
||||
handler throws an error, it will return an assembled parameter
|
||||
list derived from the this.params hash table. */
|
||||
DOMString pullParameters();
|
||||
};
|
||||
|
|
@ -1,467 +0,0 @@
|
|||
function isDefinedObj(obj) {
|
||||
return typeof(obj) !== 'undefined' && obj != null;
|
||||
}
|
||||
|
||||
function isDefined(obj) {
|
||||
return typeof(obj) !== 'undefined';
|
||||
}
|
||||
|
||||
/* This is a simple test suite class removing the need to
|
||||
write a lot of boilerplate for camera tests. It can
|
||||
manage the platform configurations for testing, any
|
||||
cleanup required, and common actions such as fetching
|
||||
the camera or waiting for the preview to be completed.
|
||||
|
||||
To create the suite:
|
||||
var suite = new CameraTestSuite();
|
||||
|
||||
To add a test case to the suite:
|
||||
suite.test('test-name', function() {
|
||||
function startAutoFocus(p) {
|
||||
return suite.camera.autoFocus();
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(startAutoFocus, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
Finally, to execute the test cases:
|
||||
suite.setup()
|
||||
.then(suite.run);
|
||||
|
||||
Behind the scenes, suite configured the native camera
|
||||
to use the JS hardware, setup that hardware such that
|
||||
the getCamera would succeed, got a camera control
|
||||
reference and saved it to suite.camera, and after the
|
||||
tests were finished, it reset any modified state,
|
||||
released the camera object, and concluded the mochitest
|
||||
appropriately.
|
||||
*/
|
||||
function CameraTestSuite() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
this._window = window;
|
||||
this._document = document;
|
||||
this.viewfinder = document.getElementById('viewfinder');
|
||||
this._tests = [];
|
||||
this.hwType = '';
|
||||
|
||||
/* Ensure that the this pointer is bound to all functions so that
|
||||
they may be used as promise resolve/reject handlers without any
|
||||
special effort, permitting code like this:
|
||||
|
||||
getCamera().catch(suite.rejectGetCamera);
|
||||
|
||||
instead of:
|
||||
|
||||
getCamera().catch(suite.rejectGetCamera.bind(suite));
|
||||
*/
|
||||
this.setup = this._setup.bind(this);
|
||||
this.teardown = this._teardown.bind(this);
|
||||
this.test = this._test.bind(this);
|
||||
this.run = this._run.bind(this);
|
||||
this.waitPreviewStarted = this._waitPreviewStarted.bind(this);
|
||||
this.waitParameterPush = this._waitParameterPush.bind(this);
|
||||
this.initJsHw = this._initJsHw.bind(this);
|
||||
this.getCamera = this._getCamera.bind(this);
|
||||
this.setLowMemoryPlatform = this._setLowMemoryPlatform.bind(this);
|
||||
this.logError = this._logError.bind(this);
|
||||
this.expectedError = this._expectedError.bind(this);
|
||||
this.expectedRejectGetCamera = this._expectedRejectGetCamera.bind(this);
|
||||
this.expectedRejectConfigure = this._expectedRejectConfigure.bind(this);
|
||||
this.expectedRejectAutoFocus = this._expectedRejectAutoFocus.bind(this);
|
||||
this.expectedRejectTakePicture = this._expectedRejectTakePicture.bind(this);
|
||||
this.expectedRejectStartRecording = this._expectedRejectStartRecording.bind(this);
|
||||
this.expectedRejectStopRecording = this._expectedRejectStopRecording.bind(this);
|
||||
this.rejectGetCamera = this._rejectGetCamera.bind(this);
|
||||
this.rejectConfigure = this._rejectConfigure.bind(this);
|
||||
this.rejectRelease = this._rejectRelease.bind(this);
|
||||
this.rejectAutoFocus = this._rejectAutoFocus.bind(this);
|
||||
this.rejectTakePicture = this._rejectTakePicture.bind(this);
|
||||
this.rejectStartRecording = this._rejectStartRecording.bind(this);
|
||||
this.rejectStopRecording = this._rejectStopRecording.bind(this);
|
||||
this.rejectPauseRecording = this._rejectPauseRecording.bind(this);
|
||||
this.rejectResumeRecording = this._rejectResumeRecording.bind(this);
|
||||
this.rejectPreviewStarted = this._rejectPreviewStarted.bind(this);
|
||||
|
||||
var self = this;
|
||||
this._window.addEventListener('beforeunload', function() {
|
||||
if (isDefinedObj(self.viewfinder)) {
|
||||
self.viewfinder.srcObject = null;
|
||||
}
|
||||
|
||||
self.hw = null;
|
||||
if (isDefinedObj(self.camera)) {
|
||||
ok(false, 'window unload triggered camera release instead of test completion');
|
||||
self.camera.release();
|
||||
self.camera = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
CameraTestSuite.prototype = {
|
||||
camera: null,
|
||||
hw: null,
|
||||
_lowMemSet: false,
|
||||
_reloading: false,
|
||||
|
||||
_setupPermission: function(permission) {
|
||||
if (!SpecialPowers.hasPermission(permission, document)) {
|
||||
info("requesting " + permission + " permission");
|
||||
SpecialPowers.addPermission(permission, true, document);
|
||||
this._reloading = true;
|
||||
}
|
||||
},
|
||||
|
||||
/* Returns a promise which is resolved when the test suite is ready
|
||||
to be executing individual test cases. One may provide the expected
|
||||
hardware type here if desired; the default is to use the JS test
|
||||
hardware. Use '' for the native emulated camera hardware. */
|
||||
_setup: function(hwType) {
|
||||
/* Depending on how we run the mochitest, we may not have the necessary
|
||||
permissions yet. If we do need to request them, then we have to reload
|
||||
the window to ensure the reconfiguration propogated properly. */
|
||||
this._setupPermission("camera");
|
||||
this._setupPermission("device-storage:videos");
|
||||
this._setupPermission("device-storage:videos-create");
|
||||
this._setupPermission("device-storage:videos-write");
|
||||
|
||||
if (this._reloading) {
|
||||
window.location.reload();
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
info("has necessary permissions");
|
||||
if (!isDefined(hwType)) {
|
||||
hwType = 'hardware';
|
||||
}
|
||||
|
||||
this._hwType = hwType;
|
||||
return new Promise(function(resolve, reject) {
|
||||
SpecialPowers.pushPrefEnv({'set': [['device.storage.prompt.testing', true]]}, function() {
|
||||
SpecialPowers.pushPrefEnv({'set': [['camera.control.test.permission', true]]}, function() {
|
||||
SpecialPowers.pushPrefEnv({'set': [['camera.control.test.enabled', hwType]]}, function() {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/* Returns a promise which is resolved when all of the SpecialPowers
|
||||
parameters that were set while testing are flushed. This includes
|
||||
camera.control.test.enabled and camera.control.test.is_low_memory. */
|
||||
_teardown: function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
SpecialPowers.flushPrefEnv(function() {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/* Returns a promise which is resolved when the set low memory
|
||||
parameter is set. If no value is given, it defaults to true.
|
||||
This is intended to be used inside a test case at the beginning
|
||||
of its promise chain to configure the platform as desired. */
|
||||
_setLowMemoryPlatform: function(val) {
|
||||
if (typeof(val) === 'undefined') {
|
||||
val = true;
|
||||
}
|
||||
|
||||
if (this._lowMemSet === val) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
var self = this;
|
||||
return new Promise(function(resolve, reject) {
|
||||
SpecialPowers.pushPrefEnv({'set': [['camera.control.test.is_low_memory', val]]}, function() {
|
||||
self._lowMemSet = val;
|
||||
resolve();
|
||||
});
|
||||
}).catch(function(e) {
|
||||
return self.logError('set low memory ' + val + ' failed', e);
|
||||
});
|
||||
},
|
||||
|
||||
/* Add a test case to the test suite to be executed later. */
|
||||
_test: function(aName, aCb) {
|
||||
this._tests.push({
|
||||
name: aName,
|
||||
cb: aCb
|
||||
});
|
||||
},
|
||||
|
||||
/* Execute all test cases (after setup is called). */
|
||||
_run: function() {
|
||||
if (this._reloading) {
|
||||
return;
|
||||
}
|
||||
|
||||
var test = this._tests.shift();
|
||||
var self = this;
|
||||
if (test) {
|
||||
info(test.name + ' started');
|
||||
|
||||
function runNextTest() {
|
||||
self.run();
|
||||
}
|
||||
|
||||
function resetLowMem() {
|
||||
return self.setLowMemoryPlatform(false);
|
||||
}
|
||||
|
||||
function postTest(pass) {
|
||||
ok(pass, test.name + ' finished');
|
||||
var camera = self.camera;
|
||||
self.viewfinder.srcObject = null;
|
||||
self.camera = null;
|
||||
|
||||
if (!isDefinedObj(camera)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function handler(e) {
|
||||
ok(typeof(e) === 'undefined', 'camera released');
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return camera.release().then(handler).catch(handler);
|
||||
}
|
||||
|
||||
this.initJsHw();
|
||||
|
||||
var testPromise;
|
||||
try {
|
||||
testPromise = test.cb();
|
||||
if (!isDefinedObj(testPromise)) {
|
||||
testPromise = Promise.resolve();
|
||||
}
|
||||
} catch(e) {
|
||||
ok(false, 'caught exception while running test: ' + e);
|
||||
testPromise = Promise.reject(e);
|
||||
}
|
||||
|
||||
testPromise
|
||||
.then(function(p) {
|
||||
return postTest(true);
|
||||
}, function(e) {
|
||||
self.logError('unhandled error', e);
|
||||
return postTest(false);
|
||||
})
|
||||
.then(resetLowMem, resetLowMem)
|
||||
.then(runNextTest, runNextTest);
|
||||
} else {
|
||||
ok(true, 'all tests completed');
|
||||
var finish = SimpleTest.finish.bind(SimpleTest);
|
||||
this.teardown().then(finish, finish);
|
||||
}
|
||||
},
|
||||
|
||||
/* If the JS hardware is in use, get (and possibly initialize)
|
||||
the service XPCOM object. The native Gonk layers are able
|
||||
to get it via the same mechanism. Save a reference to it
|
||||
so that the test case may manipulate it as it sees fit in
|
||||
this.hw. Minimal setup is done for the test hardware such
|
||||
that the camera is able to be brought up without issue.
|
||||
|
||||
This function has no effect if the JS hardware is not used. */
|
||||
_initJsHw: function() {
|
||||
if (this._hwType === 'hardware') {
|
||||
this.hw = SpecialPowers.Cc['@mozilla.org/cameratesthardware;1']
|
||||
.getService(SpecialPowers.Ci.nsICameraTestHardware);
|
||||
this.hw.reset(this._window);
|
||||
|
||||
/* Minimum parameters required to get camera started */
|
||||
this.hw.params['preview-size'] = '320x240';
|
||||
this.hw.params['preview-size-values'] = '320x240';
|
||||
this.hw.params['picture-size-values'] = '320x240';
|
||||
} else {
|
||||
this.hw = null;
|
||||
}
|
||||
},
|
||||
|
||||
/* Returns a promise which resolves when the camera has
|
||||
been successfully opened with the given name and
|
||||
configuration. If no name is given, it uses the first
|
||||
camera in the list from the camera manager. */
|
||||
_getCamera: function(name, config) {
|
||||
var cameraManager = navigator.mozCameras;
|
||||
if (!isDefined(name)) {
|
||||
name = cameraManager.getListOfCameras()[0];
|
||||
}
|
||||
|
||||
var self = this;
|
||||
return cameraManager.getCamera(name, config).then(
|
||||
function(p) {
|
||||
ok(isDefinedObj(p) && isDefinedObj(p.camera), 'got camera');
|
||||
self.camera = p.camera;
|
||||
/* Ensure a followup promise can verify config by
|
||||
returning the same parameter again. */
|
||||
return Promise.resolve(p);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/* Returns a promise which resolves when the camera has
|
||||
successfully started the preview and is bound to the
|
||||
given viewfinder object. Note that this requires that
|
||||
a video element be present with the ID 'viewfinder'. */
|
||||
_waitPreviewStarted: function() {
|
||||
var self = this;
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
function onPreviewStateChange(e) {
|
||||
try {
|
||||
if (e.newState === 'started') {
|
||||
ok(true, 'viewfinder is ready and playing');
|
||||
self.camera.removeEventListener('previewstatechange', onPreviewStateChange);
|
||||
resolve();
|
||||
}
|
||||
} catch(e) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isDefinedObj(self.viewfinder)) {
|
||||
reject(new Error('no viewfinder object'));
|
||||
return;
|
||||
}
|
||||
|
||||
self.viewfinder.srcObject = self.camera;
|
||||
self.viewfinder.play();
|
||||
self.camera.addEventListener('previewstatechange', onPreviewStateChange);
|
||||
});
|
||||
},
|
||||
|
||||
/* Returns a promise which resolves when the camera hardware
|
||||
has received a push parameters request. This is useful
|
||||
when setting camera parameters from the application and
|
||||
you want confirmation when the operation is complete if
|
||||
there is no asynchronous notification provided. */
|
||||
_waitParameterPush: function() {
|
||||
var self = this;
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
self.hw.attach({
|
||||
'pushParameters': function() {
|
||||
self._window.setTimeout(resolve);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/* When an error occurs in the promise chain, all of the relevant rejection
|
||||
functions will be triggered. Most of the time however we only want the
|
||||
first rejection to be handled and then let the failure trickle down the
|
||||
chain to terminate the test. There is no way to exit a promise chain
|
||||
early so the convention is to handle the error in the first reject and
|
||||
then give an empty error for subsequent reject handlers so they know
|
||||
it is not for them.
|
||||
|
||||
For example:
|
||||
function rejectSomething(e) {
|
||||
return suite.logError('something call failed');
|
||||
}
|
||||
|
||||
getCamera()
|
||||
.then(, suite.rejectGetCamera)
|
||||
.then(something)
|
||||
.then(, rejectSomething)
|
||||
|
||||
If the getCamera promise is rejected, suite.rejectGetCamera reports an
|
||||
error, but rejectSomething remains silent. */
|
||||
_logError: function(msg, e) {
|
||||
if (isDefined(e)) {
|
||||
ok(false, msg + ': ' + e);
|
||||
}
|
||||
// Make sure the error is undefined for later handlers
|
||||
return Promise.reject();
|
||||
},
|
||||
|
||||
/* The reject handlers below are intended to be used
|
||||
when a test case does not expect a particular call
|
||||
to fail but otherwise does not require any special
|
||||
handling of that situation beyond failing the test
|
||||
case and logging why.*/
|
||||
_rejectGetCamera: function(e) {
|
||||
return this.logError('get camera failed', e);
|
||||
},
|
||||
|
||||
_rejectConfigure: function(e) {
|
||||
return this.logError('set configuration failed', e);
|
||||
},
|
||||
|
||||
_rejectRelease: function(e) {
|
||||
return this.logError('release camera failed', e);
|
||||
},
|
||||
|
||||
_rejectAutoFocus: function(e) {
|
||||
return this.logError('auto focus failed', e);
|
||||
},
|
||||
|
||||
_rejectTakePicture: function(e) {
|
||||
return this.logError('take picture failed', e);
|
||||
},
|
||||
|
||||
_rejectStartRecording: function(e) {
|
||||
return this.logError('start recording failed', e);
|
||||
},
|
||||
|
||||
_rejectStopRecording: function(e) {
|
||||
return this.logError('stop recording failed', e);
|
||||
},
|
||||
|
||||
_rejectPauseRecording: function(e) {
|
||||
return this.logError('pause recording failed', e);
|
||||
},
|
||||
|
||||
_rejectResumeRecording: function(e) {
|
||||
return this.logError('resume recording failed', e);
|
||||
},
|
||||
|
||||
_rejectPreviewStarted: function(e) {
|
||||
return this.logError('preview start failed', e);
|
||||
},
|
||||
|
||||
/* The success handlers below are intended to be used
|
||||
when a test case does not expect a particular call
|
||||
to succed but otherwise does not require any special
|
||||
handling of that situation beyond failing the test
|
||||
case and logging why.*/
|
||||
_expectedError: function(msg) {
|
||||
ok(false, msg);
|
||||
/* Since the original promise was technically resolved
|
||||
we actually want to pass up a rejection to try and
|
||||
end the test case sooner */
|
||||
return Promise.reject();
|
||||
},
|
||||
|
||||
_expectedRejectGetCamera: function(p) {
|
||||
/* Copy handle to ensure it gets released at the end
|
||||
of the test case */
|
||||
self.camera = p.camera;
|
||||
return this.expectedError('expected get camera to fail');
|
||||
},
|
||||
|
||||
_expectedRejectConfigure: function(p) {
|
||||
return this.expectedError('expected set configuration to fail');
|
||||
},
|
||||
|
||||
_expectedRejectAutoFocus: function(p) {
|
||||
return this.expectedError('expected auto focus to fail');
|
||||
},
|
||||
|
||||
_expectedRejectTakePicture: function(p) {
|
||||
return this.expectedError('expected take picture to fail');
|
||||
},
|
||||
|
||||
_expectedRejectStartRecording: function(p) {
|
||||
return this.expectedError('expected start recording to fail');
|
||||
},
|
||||
|
||||
_expectedRejectStopRecording: function(p) {
|
||||
return this.expectedError('expected stop recording to fail');
|
||||
},
|
||||
};
|
||||
|
||||
is(SpecialPowers.sanityCheck(), "foo", "SpecialPowers passed sanity check");
|
|
@ -1,20 +0,0 @@
|
|||
[DEFAULT]
|
||||
support-files = camera_common.js
|
||||
|
||||
[test_camera_configuration.html]
|
||||
[test_camera_release.html]
|
||||
[test_camera_auto_focus.html]
|
||||
[test_camera_take_picture.html]
|
||||
[test_camera_record.html]
|
||||
skip-if = toolkit == 'gonk'
|
||||
[test_camera_face_detection.html]
|
||||
[test_camera_fake_parameters.html]
|
||||
[test_camera_hardware_init_failure.html]
|
||||
[test_camera.html]
|
||||
skip-if = toolkit != 'gonk'
|
||||
[test_camera_2.html]
|
||||
skip-if = toolkit != 'gonk'
|
||||
[test_camera_3.html]
|
||||
skip-if = toolkit != 'gonk'
|
||||
[test_bug1104913.html]
|
||||
skip-if = toolkit != 'gonk'
|
|
@ -1,81 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for bug 1104913</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="camera_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<video id="viewfinder" width="200" height="200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var config = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'qvga',
|
||||
pictureSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
},
|
||||
previewSize: {
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
|
||||
function onError(e) {
|
||||
ok(false, "Error: " + JSON.stringify(e));
|
||||
}
|
||||
|
||||
var Camera = {
|
||||
cameraObj: null,
|
||||
|
||||
get viewfinder() {
|
||||
return document.getElementById('viewfinder');
|
||||
},
|
||||
|
||||
start: function test_start() {
|
||||
function getCamera_onSuccess(d) {
|
||||
var camera = d.camera;
|
||||
var cfg = d.configuration;
|
||||
Camera.cameraObj = camera;
|
||||
Camera.viewfinder.srcObject = camera;
|
||||
Camera.viewfinder.play();
|
||||
|
||||
// Check the default configuration
|
||||
ok(cfg.mode === config.mode, "Initial mode = " + cfg.mode);
|
||||
ok(cfg.previewSize.width === config.previewSize.width &&
|
||||
cfg.previewSize.height === config.previewSize.height,
|
||||
"Initial preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height);
|
||||
ok(cfg.pictureSize.width === config.pictureSize.width &&
|
||||
cfg.pictureSize.height === config.pictureSize.height,
|
||||
"Initial picture size = " + cfg.pictureSize.width + "x" + cfg.pictureSize.height);
|
||||
ok(cfg.recorderProfile === config.recorderProfile,
|
||||
"Initial recorder profile = '" + cfg.recorderProfile + "'");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
navigator.mozCameras.getCamera(whichCamera, {}).then(getCamera_onSuccess, onError);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
window.addEventListener('beforeunload', function() {
|
||||
Camera.viewfinder.srcObject = null;
|
||||
if (Camera.cameraObj) {
|
||||
Camera.cameraObj.release();
|
||||
Camera.cameraObj = null;
|
||||
}
|
||||
});
|
||||
|
||||
Camera.start();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,258 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for mozCameras.getCamera() with separate .setConfiguration() call</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<video id="viewfinder" width="200" height="200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var options = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
|
||||
var config = {
|
||||
dateTime: Date.now() / 1000,
|
||||
pictureSize: null,
|
||||
fileFormat: 'jpeg',
|
||||
rotation: 90
|
||||
};
|
||||
|
||||
function onError(e) {
|
||||
ok(false, "Error " + e);
|
||||
}
|
||||
|
||||
var capabilities = [ 'previewSizes', 'pictureSizes', 'fileFormats', 'maxFocusAreas', 'minExposureCompensation',
|
||||
'maxExposureCompensation', 'stepExposureCompensation', 'maxMeteringAreas', 'videoSizes',
|
||||
'recorderProfiles', 'zoomRatios', 'isoModes'];
|
||||
|
||||
var Camera = {
|
||||
cameraObj: null,
|
||||
_recording: false,
|
||||
_currentTest: null,
|
||||
_autoFocusSupported: 0,
|
||||
_manuallyFocused: false,
|
||||
_flashmodes: null,
|
||||
_pictureSizes: null,
|
||||
_previewSizes: null,
|
||||
_whiteBalanceModes: null,
|
||||
_zoomRatios: null,
|
||||
_sceneModes: null,
|
||||
_focusModes: null,
|
||||
_zoomRatios: null,
|
||||
_testsCompleted: 0,
|
||||
_shutter: 0,
|
||||
_config: {
|
||||
dateTime: Date.now() / 1000,
|
||||
pictureSize: null,
|
||||
fileFormat: 'jpeg',
|
||||
rotation: 90
|
||||
},
|
||||
_tests: null,
|
||||
get viewfinder() {
|
||||
return document.getElementById('viewfinder');
|
||||
},
|
||||
setFlashMode: function camera_setFlash(mode) {
|
||||
this.cameraObj.flashMode = mode;
|
||||
},
|
||||
setFocus: function camera_setfocus(mode) {
|
||||
this.cameraObj.focus = mode;
|
||||
},
|
||||
setZoom: function camera_setZoom(zoom) {
|
||||
this.cameraObj.zoom = zoom;
|
||||
},
|
||||
getZoom: function camera_getZoom() {
|
||||
return this.cameraObj.zoom;
|
||||
},
|
||||
getFileFormats: function camera_formats() {
|
||||
this._fileFormats = this.cameraObj.capabilities.fileFormats;
|
||||
},
|
||||
getFlashModes: function camera_getFlash() {
|
||||
this._flashmodes = this.cameraObj.capabilities.flashModes;
|
||||
},
|
||||
getFocusModes: function camera_getFocus() {
|
||||
this._focusModes = this.cameraObj.capabilities.focusModes;
|
||||
},
|
||||
getSceneModes: function camera_getScene() {
|
||||
this._sceneModes = this.cameraObj.capabilities.sceneModes;
|
||||
},
|
||||
getZoomRatios: function camera_getZoom() {
|
||||
this._zoomRatios = this.cameraObj.capabilities.zoomRatios;
|
||||
},
|
||||
getWhiteBalance: function camera_white() {
|
||||
this._whitebalanceModes = this.cameraObj.capabilities.whiteBalanceModes;
|
||||
},
|
||||
getPictureSizes: function camera_sizes() {
|
||||
this._pictureSizes = this.cameraObj.capabilities.pictureSizes;
|
||||
},
|
||||
getPreviewSizes: function camera_preview() {
|
||||
this._previewSizes = this.cameraObj.capabilities.previewSizes;
|
||||
},
|
||||
getZoomRatios: function camera_preview() {
|
||||
this._zoomRatios = this.cameraObj.capabilities.zoomRatios;
|
||||
},
|
||||
takePictureSuccess: function taken_foto(blob) {
|
||||
ok(blob.size > 100 , "Blob Size Gathered = " + blob.size);
|
||||
ok("image/" + this._currentTest.fileFormat === blob.type, "Blob Type = " + blob.type);
|
||||
},
|
||||
takePictureEvent: function taken_foto_evt(e) {
|
||||
var blob = e.data;
|
||||
var img = new Image();
|
||||
var test = this._currentTest;
|
||||
var onPreviewStateChange = function(e) {
|
||||
if (e.newState === 'started') {
|
||||
ok(true, "viewfinder is ready and playing after resume");
|
||||
Camera.cameraObj.removeEventListener('previewstatechange', onPreviewStateChange);
|
||||
Camera._testsCompleted++;
|
||||
if(Camera._testsCompleted == Camera._tests.length) {
|
||||
ok(true, "test finishing");
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
Camera.runTests();
|
||||
}
|
||||
}
|
||||
}
|
||||
Camera.cameraObj.addEventListener('previewstatechange', onPreviewStateChange);
|
||||
img.onload = function Imgsize() {
|
||||
ok(this.width == test.pictureSize.width, "The image taken has the width " +
|
||||
this.width + " pictureSize width = " + test.pictureSize.width);
|
||||
ok(this.height == test.pictureSize.height, "The image taken has the height " +
|
||||
this.height + " picturesize height = " + test.pictureSize.height);
|
||||
Camera.cameraObj.resumePreview();
|
||||
}
|
||||
ok(blob.size > 100 , "Blob Size Gathered = " + blob.size);
|
||||
ok("image/" + test.fileFormat == blob.type, "Blob Type = " + blob.type);
|
||||
img.src = window.URL.createObjectURL(blob);
|
||||
},
|
||||
shutter: function onShutter () {
|
||||
Camera._shutter++;
|
||||
|
||||
ok(Camera._shutter == (Camera._testsCompleted + 1), "on Shutter has been called " +
|
||||
Camera._shutter + " times");
|
||||
|
||||
},
|
||||
onReady: function onReady() {
|
||||
var camcap = Camera.cameraObj.capabilities;
|
||||
var tests = {};
|
||||
for (var prop in capabilities) {
|
||||
prop = capabilities[prop];
|
||||
ok(camcap[prop] || isFinite(camcap[prop]) || camcap[prop] == null, "Camera Capability: " +
|
||||
prop + " is exposed, value = " + JSON.stringify(camcap[prop]));
|
||||
}
|
||||
ok(camcap.maxMeteringAreas >= 0, "maxMeteringAreas = " + camcap.maxMeteringAreas);
|
||||
ok(camcap.maxFocusAreas >= 0, "maxFocusAreas = " + camcap.maxFocusAreas);
|
||||
for (var prop in camcap) {
|
||||
if(camcap[prop] && camcap[prop].length > 1) {
|
||||
tests[prop] = camcap[prop];
|
||||
}
|
||||
}
|
||||
Camera.getPictureSizes();
|
||||
Camera.getPreviewSizes();
|
||||
Camera.getFileFormats();
|
||||
Camera.getFocusModes();
|
||||
Camera.getZoomRatios();
|
||||
ok(Camera._previewSizes.length > 0, "previewSizes length = " + Camera._previewSizes.length);
|
||||
ok(Camera._pictureSizes.length > 0, "picturesizes length = " + Camera._pictureSizes.length);
|
||||
ok(Camera._fileFormats.length > 0, "file formats length = " + Camera._fileFormats.length);
|
||||
ok(camcap.isoModes.length == 0, "ISO modes length = " + camcap.isoModes.length);
|
||||
|
||||
// The emulator doesn't support zoom, so these parameters will be very constrained
|
||||
// For more ambitious tests, see test_camera_fake_parameters.html
|
||||
ok(Camera._zoomRatios.length == 1, "zoom ratios length = " + Camera._zoomRatios.length);
|
||||
ok(Camera.cameraObj.zoom == 1.0, "zoom = " + Camera.cameraObj.zoom);
|
||||
// Test snapping to supported values
|
||||
Camera.cameraObj.zoom = 0.9;
|
||||
ok(Camera.cameraObj.zoom == 1.0, "zoom (lower limit) = " + Camera.cameraObj.zoom);
|
||||
Camera.cameraObj.zoom = 1.1;
|
||||
ok(Camera.cameraObj.zoom == 1.0, "zoom (upper limit) = " + Camera.cameraObj.zoom);
|
||||
|
||||
// Check image quality handling
|
||||
Camera.cameraObj.pictureQuality = 0.0;
|
||||
ok(Camera.cameraObj.pictureQuality == 0.0, "picture quality = " + Camera.cameraObj.pictureQuality);
|
||||
Camera.cameraObj.pictureQuality = -0.1;
|
||||
ok(Camera.cameraObj.pictureQuality == 0.0, "picture quality (minimum limit) = " + Camera.cameraObj.pictureQuality);
|
||||
Camera.cameraObj.pictureQuality = -Math.pow(2, 80);
|
||||
ok(Camera.cameraObj.pictureQuality == 0.0, "picture quality (BIG negative) = " + Camera.cameraObj.pictureQuality);
|
||||
Camera.cameraObj.pictureQuality = 1.0;
|
||||
ok(Camera.cameraObj.pictureQuality == 1.0, "picture quality = " + Camera.cameraObj.pictureQuality);
|
||||
Camera.cameraObj.pictureQuality = 1.1;
|
||||
ok(Camera.cameraObj.pictureQuality == 1.0, "picture quality (maximum limit) = " + Camera.cameraObj.pictureQuality);
|
||||
Camera.cameraObj.pictureQuality = Math.pow(2, 80);
|
||||
ok(Camera.cameraObj.pictureQuality == 1.0, "picture quality (BIG positive) = " + Camera.cameraObj.pictureQuality);
|
||||
|
||||
Camera._tests = new Array();
|
||||
for (var i in Camera._pictureSizes) {
|
||||
for (var l in Camera._fileFormats) {
|
||||
var config = {
|
||||
pictureSize: Camera._pictureSizes[i],
|
||||
fileFormat: Camera._fileFormats[l]
|
||||
};
|
||||
Camera._tests.push(config);
|
||||
}
|
||||
}
|
||||
Camera.runTests();
|
||||
},
|
||||
runTests: function run_tests() {
|
||||
var test = this._tests[this._testsCompleted];
|
||||
this._currentTest = test;
|
||||
Camera.setFlashMode(test.flashMode);
|
||||
config.fileFormat = test.fileFormat;
|
||||
config.pictureSize = test.pictureSize;
|
||||
ok(true, "testing picture size " + JSON.stringify(config.pictureSize));
|
||||
Camera.cameraObj.takePicture(config).then(this.takePictureSuccess.bind(this), onError);
|
||||
},
|
||||
onConfigChange: function onConfigChange(config) {
|
||||
ok(config.mode === options.mode, "configuration mode = " + config.mode);
|
||||
ok(config.recorderProfile === options.recorderProfile, "recorder profile = " + config.recorderProfile);
|
||||
ok(config.previewSize.width === options.previewSize.width &&
|
||||
config.previewSize.height === options.previewSize.height,
|
||||
"preview size (w x h) = " + config.previewSize.width + " x " + config.previewSize.height);
|
||||
},
|
||||
onPreviewStateChange: function onPreviewStateChange(e) {
|
||||
if (e.newState === 'started') {
|
||||
ok(true, "viewfinder is ready and playing");
|
||||
Camera.cameraObj.removeEventListener('previewstatechange', Camera.onPreviewStateChange);
|
||||
Camera.onReady();
|
||||
}
|
||||
},
|
||||
setUp: function setup_tests() {
|
||||
function onSuccess(d) {
|
||||
Camera.cameraObj = d.camera;
|
||||
Camera.cameraObj.addEventListener('previewstatechange', Camera.onPreviewStateChange);
|
||||
Camera.cameraObj.addEventListener('configurationchanged', Camera.onConfigChange);
|
||||
Camera.cameraObj.addEventListener('shutter', Camera.shutter);
|
||||
Camera.cameraObj.addEventListener('picture', Camera.takePictureEvent.bind(Camera));
|
||||
Camera.viewfinder.srcObject = d.camera;
|
||||
Camera.viewfinder.play();
|
||||
SimpleTest.expectAssertions(0);
|
||||
ok(true, "Camera Control object has been successfully initialized");
|
||||
Camera.cameraObj.setConfiguration(options).then(Camera.onConfigChange, onError);
|
||||
};
|
||||
navigator.mozCameras.getCamera(whichCamera, null).then(onSuccess, onError);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
window.addEventListener('beforeunload', function() {
|
||||
Camera.viewfinder.srcObject = null;
|
||||
Camera.cameraObj.release();
|
||||
Camera.cameraObj = null;
|
||||
});
|
||||
|
||||
Camera.setUp();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,203 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for mozCameras.getCamera() using an initial configuration</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<video id="viewfinder" width="200" height="200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var options = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
|
||||
var config = {
|
||||
dateTime: Date.now() / 1000,
|
||||
pictureSize: null,
|
||||
fileFormat: 'jpeg',
|
||||
rotation: 90
|
||||
};
|
||||
|
||||
function onError(e) {
|
||||
ok(false, "Error " + e);
|
||||
}
|
||||
|
||||
var capabilities = [ 'previewSizes', 'pictureSizes', 'fileFormats', 'maxFocusAreas', 'minExposureCompensation',
|
||||
'maxExposureCompensation', 'stepExposureCompensation', 'maxMeteringAreas', 'videoSizes',
|
||||
'recorderProfiles'];
|
||||
|
||||
var Camera = {
|
||||
cameraObj: null,
|
||||
_recording: false,
|
||||
_currentTest: null,
|
||||
_autoFocusSupported: 0,
|
||||
_manuallyFocused: false,
|
||||
_flashmodes: null,
|
||||
_pictureSizes: null,
|
||||
_previewSizes: null,
|
||||
_whiteBalanceModes: null,
|
||||
_zoomRatios: null,
|
||||
_sceneModes: null,
|
||||
_focusModes: null,
|
||||
_testsCompleted: 0,
|
||||
_shutter: 0,
|
||||
_config: {
|
||||
dateTime: Date.now() / 1000,
|
||||
pictureSize: null,
|
||||
fileFormat: 'jpeg',
|
||||
rotation: 90
|
||||
},
|
||||
_tests: null,
|
||||
get viewfinder() {
|
||||
return document.getElementById('viewfinder');
|
||||
},
|
||||
setFlashMode: function camera_setFlash(mode) {
|
||||
this.cameraObj.flashMode = mode;
|
||||
},
|
||||
setFocus: function camera_setfocus(mode) {
|
||||
this.cameraObj.focus = mode;
|
||||
},
|
||||
getFileFormats: function camera_formats() {
|
||||
this._fileFormats = this.cameraObj.capabilities.fileFormats;
|
||||
},
|
||||
getFlashModes: function camera_getFlash() {
|
||||
this._flashmodes = this.cameraObj.capabilities.flashModes;
|
||||
},
|
||||
getFocusModes: function camera_getFocus() {
|
||||
this._focusModes = this.cameraObj.capabilities.focusModes;
|
||||
},
|
||||
getSceneModes: function camera_getScene() {
|
||||
this._sceneModes = this.cameraObj.capabilities.sceneModes;
|
||||
},
|
||||
getZoomRatios: function camera_getZoom() {
|
||||
this._zoomRatios = this.cameraObj.capabilities.zoomRatios;
|
||||
},
|
||||
getWhiteBalance: function camera_white() {
|
||||
this._whitebalanceModes = this.cameraObj.capabilities.whiteBalanceModes;
|
||||
},
|
||||
getPictureSizes: function camera_sizes() {
|
||||
this._pictureSizes = this.cameraObj.capabilities.pictureSizes;
|
||||
},
|
||||
getPreviewSizes: function camera_preview() {
|
||||
this._previewSizes = this.cameraObj.capabilities.previewSizes;
|
||||
},
|
||||
takePictureSuccess: function taken_foto(blob) {
|
||||
var img = new Image();
|
||||
var test = this._currentTest;
|
||||
img.onload = function Imgsize() {
|
||||
ok(this.width == test.pictureSize.width, "The image taken has the width " +
|
||||
this.width + " pictureSize width = " + test.pictureSize.width);
|
||||
ok(this.height == test.pictureSize.height, "The image taken has the height " +
|
||||
this.height + " picturesize height = " + test.pictureSize.height);
|
||||
Camera._testsCompleted++;
|
||||
if(Camera._testsCompleted == Camera._tests.length) {
|
||||
ok(true, "test finishing");
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
Camera.runTests();
|
||||
}
|
||||
}
|
||||
ok(blob.size > 100 , "Blob Size Gathered = " + blob.size);
|
||||
ok("image/" + test.fileFormat == blob.type, "Blob Type = " + blob.type);
|
||||
img.src = window.URL.createObjectURL(blob);
|
||||
},
|
||||
shutter: function onShutter () {
|
||||
Camera._shutter++;
|
||||
|
||||
ok(Camera._shutter == (Camera._testsCompleted + 1), "on Shutter has been called " +
|
||||
Camera._shutter + " times");
|
||||
|
||||
},
|
||||
onPreviewStateChange: function onPreviewStateChange(e) {
|
||||
ok(true, "viewfinder state change " + e);
|
||||
if (e.newState === 'started') {
|
||||
ok(true, "viewfinder is ready and playing");
|
||||
Camera.cameraObj.removeEventListener('previewstatechange', Camera.onPreviewStateChange);
|
||||
Camera.onReady();
|
||||
}
|
||||
},
|
||||
onReady: function onReady() {
|
||||
var camcap = Camera.cameraObj.capabilities;
|
||||
var tests = {};
|
||||
for (var prop in capabilities) {
|
||||
prop = capabilities[prop];
|
||||
ok(camcap[prop] || isFinite(camcap[prop]) || camcap[prop] == null, "Camera Capability: " +
|
||||
prop + " is exposed, value = " + JSON.stringify(camcap[prop]));
|
||||
}
|
||||
for (var prop in camcap) {
|
||||
if(camcap[prop] && camcap[prop].length > 1) {
|
||||
tests[prop] = camcap[prop];
|
||||
}
|
||||
}
|
||||
Camera.getPictureSizes();
|
||||
Camera.getPreviewSizes();
|
||||
Camera.getFileFormats();
|
||||
Camera.getFocusModes();
|
||||
ok(Camera._previewSizes.length > 0, "previewSizes length = " + Camera._previewSizes.length);
|
||||
ok(Camera._pictureSizes.length > 0, "picturesizes length = " + Camera._pictureSizes.length);
|
||||
ok(Camera._fileFormats.length > 0, "file formats length = " + Camera._fileFormats.length);
|
||||
Camera._tests = new Array();
|
||||
for (var i in Camera._pictureSizes) {
|
||||
for (var l in Camera._fileFormats) {
|
||||
var config = {
|
||||
pictureSize: Camera._pictureSizes[i],
|
||||
fileFormat: Camera._fileFormats[l]
|
||||
};
|
||||
Camera._tests.push(config);
|
||||
}
|
||||
}
|
||||
Camera.runTests();
|
||||
},
|
||||
runTests: function run_tests() {
|
||||
var test = this._tests[this._testsCompleted];
|
||||
this._currentTest = test;
|
||||
Camera.setFlashMode(test.flashMode);
|
||||
config.fileFormat = test.fileFormat;
|
||||
config.pictureSize = test.pictureSize;
|
||||
ok(true, "testing picture size " + JSON.stringify(config.pictureSize));
|
||||
Camera.cameraObj.takePicture(config).then(this.takePictureSuccess.bind(this), onError);
|
||||
},
|
||||
setUp: function setup_tests() {
|
||||
function onSuccess(d) {
|
||||
var config = d.configuration;
|
||||
ok(true, "Camera Control object has been successfully initialized");
|
||||
ok(config.mode === options.mode, "configuration mode = " + config.mode);
|
||||
ok(config.recorderProfile === options.recorderProfile, "recorder profile = " + config.recorderProfile);
|
||||
ok(config.previewSize.width === options.previewSize.width &&
|
||||
config.previewSize.height === options.previewSize.height,
|
||||
"preview size (w x h) = " + config.previewSize.width + " x " + config.previewSize.height);
|
||||
Camera.cameraObj = d.camera;
|
||||
Camera.cameraObj.addEventListener('previewstatechange', Camera.onPreviewStateChange);
|
||||
Camera.cameraObj.addEventListener('shutter', Camera.shutter);
|
||||
Camera.viewfinder.srcObject = d.camera;
|
||||
Camera.viewfinder.play();
|
||||
SimpleTest.expectAssertions(0);
|
||||
};
|
||||
navigator.mozCameras.getCamera(whichCamera, options).then(onSuccess, onError);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
window.addEventListener('beforeunload', function() {
|
||||
Camera.viewfinder.srcObject = null;
|
||||
Camera.cameraObj.release();
|
||||
Camera.cameraObj = null;
|
||||
});
|
||||
|
||||
Camera.setUp();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,77 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for multiple calls to mozCameras.getCamera()</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<video id="viewfinder" width="200" height="200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var options = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
}
|
||||
};
|
||||
|
||||
function onError(e) {
|
||||
ok(false, "Error " + e);
|
||||
}
|
||||
|
||||
var Camera = {
|
||||
cameraObj: null,
|
||||
get viewfinder() {
|
||||
return document.getElementById('viewfinder');
|
||||
},
|
||||
onReady: function take_two() {
|
||||
function onSuccess(d) {
|
||||
ok(false, "Unexpectedly got second camera instance: " + d.config.toSource);
|
||||
}
|
||||
function onFailure(error) {
|
||||
ok(true, "Correctly failed to get camera again");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
navigator.mozCameras.getCamera(whichCamera, options).then(onSuccess, onFailure);
|
||||
},
|
||||
onPreviewStateChange: function onPreviewStateChange(e) {
|
||||
if (e.newState === 'started') {
|
||||
ok(true, "viewfinder is ready and playing");
|
||||
Camera.cameraObj.removeEventListener('previewstatechange', Camera.onPreviewStateChange);
|
||||
Camera.onReady();
|
||||
}
|
||||
},
|
||||
release: function release() {
|
||||
cameraObj = null;
|
||||
},
|
||||
start: function run_test() {
|
||||
function onSuccess(d) {
|
||||
Camera.cameraObj = d.camera;
|
||||
Camera.cameraObj.addEventListener('previewstatechange', Camera.onPreviewStateChange);
|
||||
Camera.viewfinder.srcObject = d.camera;
|
||||
Camera.viewfinder.play();
|
||||
};
|
||||
navigator.mozCameras.getCamera(whichCamera, options).then(onSuccess, onError);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
window.addEventListener('beforeunload', function() {
|
||||
Camera.viewfinder.srcObject = null;
|
||||
Camera.cameraObj.release();
|
||||
Camera.cameraObj = null;
|
||||
});
|
||||
|
||||
Camera.start();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,238 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for auto focus</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="camera_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<video id="viewfinder" width="200" height="200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var suite = new CameraTestSuite();
|
||||
|
||||
/* Each discrete test case can be added to the test queue here.
|
||||
They won't be run until you call suite setup and run. */
|
||||
suite.test('auto-focus-failures', function() {
|
||||
function startAutoFocusFail(p) {
|
||||
suite.hw.attach({
|
||||
autoFocus: function() {
|
||||
suite.hw.fireAutoFocusComplete(false);
|
||||
}
|
||||
});
|
||||
return suite.camera.autoFocus();
|
||||
}
|
||||
|
||||
function resolveAutoFocusFail(focused) {
|
||||
ok(!focused, 'autoFocus() should be unfocused: ' + focused);
|
||||
}
|
||||
|
||||
function startAutoFocusError(p) {
|
||||
suite.hw.attach({
|
||||
autoFocus: function() {
|
||||
throw SpecialPowers.Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
});
|
||||
return suite.camera.autoFocus();
|
||||
}
|
||||
|
||||
function rejectAutoFocusError(e) {
|
||||
ok(e.name === 'NS_ERROR_FAILURE', 'autoFocus() should fail: ' + e);
|
||||
}
|
||||
|
||||
/* This is the promise chain which drives the test execution.
|
||||
|
||||
suite.getCamera
|
||||
This returns a promise which is resolved when the suite
|
||||
acquires the camera object (i.e. returns as if one
|
||||
called cameraManager.getCamera() directly). It saves
|
||||
the CameraControl reference in suite.camera and will
|
||||
automatically release it for you when the test is
|
||||
completed.
|
||||
|
||||
.then(startAutoFocusFail, suite.rejectGetCamera)
|
||||
If the getCamera promise is resolved, startAutoFocusFail
|
||||
will be called. That function attaches a handler to
|
||||
the camera test hardware to intercept the auto focus
|
||||
driver call so that we can change the behaviour to
|
||||
trigger an OnAutoFocusComplete(false) event. It then
|
||||
calls camera.autoFocus() and returns the promise for
|
||||
that camera call so that the next promise in the chain
|
||||
can block waiting for that promise to be fulfilled.
|
||||
|
||||
If the getCamera promise is rejected,
|
||||
suite.rejectGetCamera will be called. This is a helper
|
||||
handler provided by the test suite; it will log
|
||||
a get camera failure and fail the test via ok, and
|
||||
return a new promise rejecting *without* an error
|
||||
object (i.e. promise parameter is undefined). This is
|
||||
important because all reject handlers further down
|
||||
the promise chain will still be called due to the
|
||||
failure. However since we replaced the promise with
|
||||
an error object, with a promise without one, we can
|
||||
use this to identify errors that have already been
|
||||
handled.
|
||||
|
||||
.then(resolveAutoFocusFail, suite.rejectAutoFocus)
|
||||
If the suite.camera.autoFocus() promise is resolved,
|
||||
resolveAutoFocusFail will be called. That function
|
||||
simply verifies the result from the autoFocus() call.
|
||||
It should be false, given the modified behaviour we
|
||||
gave the driver. Since it doesn't return a new promise
|
||||
explicitly, it will generate a new promise which is
|
||||
already resolved (without the focused state parameter,
|
||||
will now be undefined to the next .then handler).
|
||||
|
||||
If the suite.camera.autoFocus() promise is rejected,
|
||||
we want to fail the test case again, just like when
|
||||
suite.getCamera() failed.
|
||||
|
||||
.then(startAutoFocusError)
|
||||
Assuming the first suite.camera.autoFocus() promise
|
||||
was resolved, startAutoFocusError will be called.
|
||||
That function is similar to startAutoFocusFail but
|
||||
now it throws an error in the intercepted auto focus
|
||||
driver call in order to trigger an OnUserError(kAutoFocus)
|
||||
event. It then calls and returns the promise from
|
||||
suite.camera.autoFocus().
|
||||
|
||||
.then(suite.expectedRejectAutoFocus, rejectAutoFocusError)
|
||||
Now we are expecting the previous suite.camera.autoFocus()
|
||||
promise to be rejected, which would call
|
||||
rejectAutoFocusError. This simply verifies that we
|
||||
got the error we expected.
|
||||
|
||||
If, on the other hand, it somehow succeeded, we
|
||||
let suite.expectedRejectAutoFocus handle it, which
|
||||
similar to the suite.rejectAutoFocus method, will
|
||||
fail and return an empty rejected promise.
|
||||
|
||||
Note that this method itself returns the promise chain.
|
||||
This allows the test suite to 1) capture any unhandled
|
||||
errors in the promise chain and ensure the test case
|
||||
fails as a result, and 2) perform any cleanup operations
|
||||
such as resetting low memory state, releasing the camera,
|
||||
etc, before starting the next test.
|
||||
*/
|
||||
return suite.getCamera()
|
||||
.then(startAutoFocusFail, suite.rejectGetCamera)
|
||||
.then(resolveAutoFocusFail, suite.rejectAutoFocus)
|
||||
.then(startAutoFocusError)
|
||||
.then(suite.expectedRejectAutoFocus, rejectAutoFocusError)
|
||||
});
|
||||
|
||||
suite.test('auto-focus-moving', function() {
|
||||
function triggerAutoFocusMoving(p) {
|
||||
var sync = new Promise(function(resolve, reject) {
|
||||
function onEvent(e) {
|
||||
suite.camera.removeEventListener('focus', onEvent);
|
||||
ok(e.newState === 'focusing', 'autofocus event state focusing == ' + e.newState);
|
||||
resolve();
|
||||
}
|
||||
suite.camera.addEventListener('focus', onEvent);
|
||||
});
|
||||
|
||||
suite.hw.fireAutoFocusMoving(true);
|
||||
return sync;
|
||||
}
|
||||
|
||||
function waitAutoFocusComplete(p) {
|
||||
var sync = new Promise(function(resolve, reject) {
|
||||
function onEvent(e) {
|
||||
suite.camera.removeEventListener('focus', onEvent);
|
||||
ok(e.newState === 'focused', 'autofocus event state focused == ' + e.newState);
|
||||
resolve();
|
||||
}
|
||||
suite.camera.addEventListener('focus', onEvent);
|
||||
});
|
||||
|
||||
// Missing the fireAutoFocusComplete but it should timeout on its own
|
||||
suite.hw.fireAutoFocusMoving(false);
|
||||
return sync;
|
||||
}
|
||||
|
||||
function runAutoFocusCycle(p) {
|
||||
return triggerAutoFocusMoving(p)
|
||||
.then(waitAutoFocusComplete);
|
||||
}
|
||||
|
||||
/* If the driver doesn't supply an onAutoFocusComplete notification,
|
||||
gecko will timeout and provide it. After three times, it will no
|
||||
longer rely upon a timeout and fire it immediately. */
|
||||
return suite.getCamera()
|
||||
.then(runAutoFocusCycle)
|
||||
.then(runAutoFocusCycle)
|
||||
.then(runAutoFocusCycle)
|
||||
.then(runAutoFocusCycle);
|
||||
});
|
||||
|
||||
suite.test('auto-focus-interrupted', function() {
|
||||
// bug 1022766
|
||||
function triggerAutoFocus(p) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var firstCall = false;
|
||||
var secondCall = false;
|
||||
|
||||
function end() {
|
||||
if (firstCall && secondCall) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
// It doesn't matter if the emulator supports focus or not;
|
||||
// this is just testing the sequencing.
|
||||
suite.camera.autoFocus().then(function(p) {
|
||||
ok(false, "First call to autoFocus() succeeded unexpectedly");
|
||||
firstCall = true;
|
||||
end();
|
||||
}, function(e) {
|
||||
ok(e.name === 'NS_ERROR_IN_PROGRESS', 'First call to autoFocus() failed with: ' + e);
|
||||
firstCall = true;
|
||||
end();
|
||||
});
|
||||
|
||||
suite.camera.autoFocus().then(function(p) {
|
||||
ok(true, "Second call to autoFocus() succeeded");
|
||||
secondCall = true;
|
||||
end();
|
||||
}, function(e) {
|
||||
ok(false, "Second call to autoFocus() failed unexpectedly with: " + e);
|
||||
secondCall = true;
|
||||
end();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(triggerAutoFocus, suite.rejectGetCamera)
|
||||
});
|
||||
|
||||
suite.test('cancel-auto-focus', function() {
|
||||
function cancelAutoFocus(p) {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
suite.hw.attach({
|
||||
cancelAutoFocus: function() {
|
||||
ok(true, 'got cancel auto focus');
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
suite.camera.resumeContinuousFocus();
|
||||
return promise;
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(cancelAutoFocus, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.setup()
|
||||
.then(suite.run);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,598 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for camera configuration</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="camera_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<video id="viewfinder" width="200" height="200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var suite = new CameraTestSuite();
|
||||
|
||||
function verifyConfig(cfg, expConfig)
|
||||
{
|
||||
ok(cfg.mode === expConfig.mode, "Configured mode = " + cfg.mode +
|
||||
", expected = " + expConfig.mode);
|
||||
ok(cfg.previewSize.width === expConfig.previewSize.width &&
|
||||
cfg.previewSize.height === expConfig.previewSize.height,
|
||||
"Configured preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height +
|
||||
", expected = " + expConfig.previewSize.width + "x" + expConfig.previewSize.height);
|
||||
ok(cfg.pictureSize.width === expConfig.pictureSize.width &&
|
||||
cfg.pictureSize.height === expConfig.pictureSize.height,
|
||||
"Configured picture size = " + cfg.pictureSize.width + "x" + cfg.pictureSize.height +
|
||||
", expected = " + expConfig.pictureSize.width + "x" + expConfig.pictureSize.height);
|
||||
ok(cfg.recorderProfile === expConfig.recorderProfile,
|
||||
"Configured recorder profile = '" + cfg.recorderProfile + "'" +
|
||||
", expected = '" + expConfig.recorderProfile + "'");
|
||||
}
|
||||
|
||||
function setAndVerifyConfig(setConfig, expConfig)
|
||||
{
|
||||
return suite.getCamera(undefined, setConfig)
|
||||
.catch(suite.rejectGetCamera)
|
||||
.then(function(p) {
|
||||
verifyConfig(p.configuration, expConfig);
|
||||
});
|
||||
}
|
||||
|
||||
suite.test('bad-initial-config', function() {
|
||||
function getCamera() {
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var config = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'foobar',
|
||||
};
|
||||
|
||||
return navigator.mozCameras.getCamera(whichCamera, config);
|
||||
}
|
||||
|
||||
function rejectGetCamera(error) {
|
||||
ok(error.name === "NS_ERROR_NOT_AVAILABLE",
|
||||
"getCamera() failed with: " + error.name);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return getCamera()
|
||||
.then(suite.expectedRejectGetCamera, rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('start-unspecified', function() {
|
||||
// bug 1037322
|
||||
var cameraManager = navigator.mozCameras;
|
||||
var whichCamera = cameraManager.getListOfCameras()[0];
|
||||
|
||||
var postConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'low',
|
||||
previewSize: {
|
||||
width: 320,
|
||||
height: 240
|
||||
},
|
||||
pictureSize: {
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
|
||||
function resolveGetCamera(p) {
|
||||
suite.camera = p.camera;
|
||||
|
||||
// Check the default configuration
|
||||
var cfg = p.configuration;
|
||||
ok(cfg.mode === "unspecified", "Initial mode = " + cfg.mode);
|
||||
ok(cfg.previewSize.width === 0 && cfg.previewSize.height === 0,
|
||||
"Initial preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height);
|
||||
ok(cfg.recorderProfile === "default",
|
||||
"Initial recorder profile = '" + cfg.recorderProfile + "'");
|
||||
}
|
||||
|
||||
function configure(p) {
|
||||
// Apply our specific configuration
|
||||
return suite.camera.setConfiguration(postConfig);
|
||||
}
|
||||
|
||||
function resolveConfigure(cfg) {
|
||||
// Check our specific configuration
|
||||
verifyConfig(cfg, postConfig);
|
||||
}
|
||||
|
||||
return cameraManager.getCamera(whichCamera, {mode: 'unspecified'})
|
||||
.then(resolveGetCamera, suite.rejectGetCamera)
|
||||
.then(configure)
|
||||
.then(resolveConfigure, suite.rejectConfigure);
|
||||
});
|
||||
|
||||
suite.test('picture-mode', function() {
|
||||
suite.hw.params['preview-size'] = '1x1';
|
||||
suite.hw.params['picture-size'] = '1x1';
|
||||
suite.hw.params['preview-size-values'] = '640x480,320x240,1x1';
|
||||
suite.hw.params['picture-size-values'] = '1280x960,640x480,320x240,1x1';
|
||||
suite.hw.params['video-size-values'] = '320x240';
|
||||
|
||||
var setConfig = {
|
||||
mode: 'picture',
|
||||
previewSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
}
|
||||
};
|
||||
|
||||
var expConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
},
|
||||
pictureSize: {
|
||||
width: 1280,
|
||||
height: 960
|
||||
}
|
||||
};
|
||||
|
||||
var postConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
},
|
||||
pictureSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
}
|
||||
};
|
||||
|
||||
function syncPreview() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
function onEvent(e) {
|
||||
if (e.newState === 'started') {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
suite.camera.addEventListener('previewstatechange', onEvent);
|
||||
});
|
||||
}
|
||||
|
||||
function reconfigure(p) {
|
||||
// The preview restarting confirms the configuration actually happened
|
||||
var sync = new Promise(function(resolve, reject) {
|
||||
var i = 0;
|
||||
var expState = ['started', 'stopped', 'started'];
|
||||
|
||||
function onEvent(e) {
|
||||
ok(e.newState === expState[i], 'preview event state ' + expState[i] +
|
||||
' === ' + e.newState);
|
||||
++i;
|
||||
if (i === expState.length) { return; }
|
||||
suite.camera.removeEventListener('previewstatechange', onEvent);
|
||||
resolve();
|
||||
}
|
||||
|
||||
suite.camera.addEventListener('previewstatechange', onEvent);
|
||||
});
|
||||
|
||||
var configure = suite.camera.setConfiguration(postConfig)
|
||||
.then(resolveReconfigure);
|
||||
return Promise.all([sync, configure]);
|
||||
}
|
||||
|
||||
function resolveReconfigure(cfg) {
|
||||
verifyConfig(cfg, postConfig);
|
||||
}
|
||||
|
||||
return setAndVerifyConfig(setConfig, expConfig)
|
||||
.then(syncPreview)
|
||||
.then(reconfigure);
|
||||
});
|
||||
|
||||
suite.test('picture-mode-larger-picture-size', function() {
|
||||
suite.hw.params['preview-size'] = '1x1';
|
||||
suite.hw.params['picture-size'] = '1x1';
|
||||
suite.hw.params['preview-size-values'] = '640x480,320x240,1x1';
|
||||
suite.hw.params['picture-size-values'] = '1280x960,640x480,320x240,1x1';
|
||||
suite.hw.params['video-size-values'] = '320x240';
|
||||
|
||||
var setConfig = {
|
||||
mode: 'picture',
|
||||
previewSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
}
|
||||
};
|
||||
|
||||
var expConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
},
|
||||
pictureSize: {
|
||||
width: 1280,
|
||||
height: 960
|
||||
}
|
||||
};
|
||||
|
||||
return setAndVerifyConfig(setConfig, expConfig);
|
||||
});
|
||||
|
||||
suite.test('picture-mode-size-unsupported-big', function() {
|
||||
suite.hw.params['preview-size'] = '1x1';
|
||||
suite.hw.params['picture-size'] = '1x1';
|
||||
suite.hw.params['preview-size-values'] = '640x480,320x240,1x1';
|
||||
suite.hw.params['picture-size-values'] = '1280x960,640x480,320x240,1x1';
|
||||
suite.hw.params['video-size-values'] = '320x240';
|
||||
|
||||
var setConfig = {
|
||||
mode: 'picture',
|
||||
previewSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
},
|
||||
pictureSize: {
|
||||
width: 2000,
|
||||
height: 2000
|
||||
},
|
||||
};
|
||||
|
||||
var expConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
},
|
||||
pictureSize: {
|
||||
width: 1280,
|
||||
height: 960
|
||||
},
|
||||
};
|
||||
|
||||
return setAndVerifyConfig(setConfig, expConfig);
|
||||
});
|
||||
|
||||
suite.test('picture-mode-size-unsupported', function() {
|
||||
suite.hw.params['preview-size'] = '1x1';
|
||||
suite.hw.params['picture-size'] = '1x1';
|
||||
suite.hw.params['preview-size-values'] = '640x480,320x240,1x1';
|
||||
suite.hw.params['picture-size-values'] = '1280x960,640x480,320x240,100x100,1x1';
|
||||
suite.hw.params['video-size-values'] = '320x240';
|
||||
|
||||
var setConfig = {
|
||||
mode: 'picture',
|
||||
previewSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
},
|
||||
pictureSize: {
|
||||
width: 641,
|
||||
height: 481,
|
||||
},
|
||||
};
|
||||
|
||||
var expConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
},
|
||||
pictureSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
},
|
||||
};
|
||||
|
||||
return setAndVerifyConfig(setConfig, expConfig);
|
||||
});
|
||||
|
||||
suite.test('picture-mode-asr-mismatch', function() {
|
||||
suite.hw.params['preview-size'] = '320x240';
|
||||
suite.hw.params['picture-size'] = '640x480';
|
||||
suite.hw.params['preview-size-values'] = '640x480,320x240,50x50';
|
||||
suite.hw.params['picture-size-values'] = '1280x960,640x480,320x240,100x100';
|
||||
suite.hw.params['video-size-values'] = '320x240';
|
||||
|
||||
var setConfig = {
|
||||
mode: 'picture',
|
||||
previewSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
},
|
||||
pictureSize: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
},
|
||||
};
|
||||
|
||||
var expConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 50,
|
||||
height: 50
|
||||
},
|
||||
pictureSize: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
},
|
||||
};
|
||||
|
||||
return setAndVerifyConfig(setConfig, expConfig);
|
||||
});
|
||||
|
||||
suite.test('picture-mode-update-video-size', function() {
|
||||
suite.hw.params['preview-size'] = '320x240';
|
||||
suite.hw.params['picture-size'] = '640x480';
|
||||
suite.hw.params['video-size'] = '50x50';
|
||||
suite.hw.params['preview-size-values'] = '640x480,320x240,50x50';
|
||||
suite.hw.params['video-size-values'] = '1280x960,640x480,320x240,50x50';
|
||||
suite.hw.params['picture-size-values'] = '1280x960,640x480,320x240,100x100';
|
||||
|
||||
var setConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 320,
|
||||
height: 240
|
||||
},
|
||||
pictureSize: {
|
||||
width: 320,
|
||||
height: 240,
|
||||
},
|
||||
};
|
||||
|
||||
var expConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 320,
|
||||
height: 240
|
||||
},
|
||||
pictureSize: {
|
||||
width: 320,
|
||||
height: 240,
|
||||
},
|
||||
};
|
||||
|
||||
function checkVideoSize(p) {
|
||||
ok(suite.hw.params['video-size'] === '320x240', 'video size reset with picture mode switch');
|
||||
}
|
||||
|
||||
return setAndVerifyConfig(setConfig, expConfig)
|
||||
.then(checkVideoSize);
|
||||
});
|
||||
|
||||
suite.test('picture-mode-no-update-video-size', function() {
|
||||
suite.hw.params['preview-size'] = '320x240';
|
||||
suite.hw.params['picture-size'] = '640x480';
|
||||
suite.hw.params['video-size'] = '1280x960';
|
||||
suite.hw.params['preview-size-values'] = '640x480,320x240,50x50';
|
||||
suite.hw.params['video-size-values'] = '1280x960,640x480,320x240,50x50';
|
||||
suite.hw.params['picture-size-values'] = '1280x960,640x480,320x240,100x100';
|
||||
|
||||
var setConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
},
|
||||
pictureSize: {
|
||||
width: 640,
|
||||
height: 480,
|
||||
},
|
||||
};
|
||||
|
||||
var expConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
},
|
||||
pictureSize: {
|
||||
width: 640,
|
||||
height: 480,
|
||||
},
|
||||
};
|
||||
|
||||
function checkVideoSize(p) {
|
||||
ok(suite.hw.params['video-size'] === '1280x960', 'video size retained with picture mode switch');
|
||||
}
|
||||
|
||||
return setAndVerifyConfig(setConfig, expConfig)
|
||||
.then(checkVideoSize);
|
||||
});
|
||||
|
||||
suite.test('video-mode-preview-size', function() {
|
||||
suite.hw.params['preview-size'] = '1x1';
|
||||
suite.hw.params['picture-size'] = '1x1';
|
||||
suite.hw.params['recording-hint'] = 'false';
|
||||
suite.hw.params['preview-size-values'] = '640x480,320x240,1x1';
|
||||
suite.hw.params['picture-size-values'] = '700x700,640x480,320x240,1x1';
|
||||
|
||||
var setConfig = {
|
||||
mode: 'video',
|
||||
recorderProfile: 'qvga'
|
||||
};
|
||||
|
||||
var expConfig = {
|
||||
mode: 'video',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 320,
|
||||
height: 240
|
||||
},
|
||||
pictureSize: {
|
||||
width: 700,
|
||||
height: 700
|
||||
}
|
||||
};
|
||||
|
||||
function checkRecordingHint(p) {
|
||||
ok(suite.hw.params['recording-hint'] === 'true', 'recording hint enabled');
|
||||
}
|
||||
|
||||
return setAndVerifyConfig(setConfig, expConfig)
|
||||
.then(checkRecordingHint);
|
||||
});
|
||||
|
||||
suite.test('video-mode', function() {
|
||||
suite.hw.params['preview-size'] = '1x1';
|
||||
suite.hw.params['picture-size'] = '1x1';
|
||||
suite.hw.params['video-size'] = '1x1';
|
||||
suite.hw.params['recording-hint'] = 'false';
|
||||
suite.hw.params['preferred-preview-size-for-video'] = '640x480';
|
||||
suite.hw.params['preview-size-values'] = '640x480,320x240,1x1';
|
||||
suite.hw.params['picture-size-values'] = '700x700,640x480,320x240,1x1';
|
||||
suite.hw.params['video-size-values'] = '700x700,640x480,320x240,1x1';
|
||||
|
||||
var setConfig = {
|
||||
mode: 'video',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
|
||||
var expConfig = {
|
||||
mode: 'video',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 320,
|
||||
height: 240
|
||||
},
|
||||
pictureSize: {
|
||||
width: 700,
|
||||
height: 700
|
||||
}
|
||||
};
|
||||
|
||||
function checkRecordingHint(p) {
|
||||
ok(suite.hw.params['recording-hint'] === 'true', 'recording hint enabled');
|
||||
}
|
||||
|
||||
return setAndVerifyConfig(setConfig, expConfig)
|
||||
.then(checkRecordingHint);
|
||||
});
|
||||
|
||||
suite.test('video-mode-larger-preview-size', function() {
|
||||
suite.hw.params['preview-size'] = '1x1';
|
||||
suite.hw.params['picture-size'] = '1x1';
|
||||
suite.hw.params['video-size'] = '1x1';
|
||||
suite.hw.params['recording-hint'] = 'false';
|
||||
suite.hw.params['preferred-preview-size-for-video'] = '640x480';
|
||||
suite.hw.params['preview-size-values'] = '640x480,320x240,1x1';
|
||||
suite.hw.params['picture-size-values'] = '700x700,640x480,320x240,1x1';
|
||||
suite.hw.params['video-size-values'] = '700x700,640x480,320x240,1x1';
|
||||
|
||||
var setConfig = {
|
||||
mode: 'video',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
}
|
||||
};
|
||||
|
||||
var expConfig = {
|
||||
mode: 'video',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 320,
|
||||
height: 240
|
||||
},
|
||||
pictureSize: {
|
||||
width: 700,
|
||||
height: 700
|
||||
}
|
||||
};
|
||||
|
||||
return setAndVerifyConfig(setConfig, expConfig);
|
||||
});
|
||||
|
||||
suite.test('video-mode-smaller-preview-size', function() {
|
||||
suite.hw.params['preview-size'] = '1x1';
|
||||
suite.hw.params['picture-size'] = '1x1';
|
||||
suite.hw.params['video-size'] = '1x1';
|
||||
suite.hw.params['recording-hint'] = 'false';
|
||||
suite.hw.params['preferred-preview-size-for-video'] = '640x480';
|
||||
suite.hw.params['preview-size-values'] = '640x480,320x240,160x120,1x1';
|
||||
suite.hw.params['picture-size-values'] = '700x700,640x480,320x240,1x1';
|
||||
suite.hw.params['video-size-values'] = '700x700,640x480,320x240,1x1';
|
||||
|
||||
var setConfig = {
|
||||
mode: 'video',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 160,
|
||||
height: 120
|
||||
}
|
||||
};
|
||||
|
||||
var expConfig = {
|
||||
mode: 'video',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 160,
|
||||
height: 120
|
||||
},
|
||||
pictureSize: {
|
||||
width: 700,
|
||||
height: 700
|
||||
}
|
||||
};
|
||||
|
||||
return setAndVerifyConfig(setConfig, expConfig);
|
||||
});
|
||||
|
||||
suite.test('video-mode-larger-preview-size-than-preferred', function() {
|
||||
suite.hw.params['preview-size'] = '1x1';
|
||||
suite.hw.params['picture-size'] = '1x1';
|
||||
suite.hw.params['video-size'] = '1x1';
|
||||
suite.hw.params['recording-hint'] = 'false';
|
||||
suite.hw.params['preferred-preview-size-for-video'] = '200x200';
|
||||
suite.hw.params['preview-size-values'] = '640x480,320x240,160x120,1x1';
|
||||
suite.hw.params['picture-size-values'] = '700x700,640x480,320x240,1x1';
|
||||
suite.hw.params['video-size-values'] = '700x700,640x480,400x400,320x240,1x1';
|
||||
|
||||
var setConfig = {
|
||||
mode: 'video',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
|
||||
var expConfig = {
|
||||
mode: 'video',
|
||||
recorderProfile: 'qvga',
|
||||
previewSize: {
|
||||
width: 160,
|
||||
height: 120
|
||||
},
|
||||
pictureSize: {
|
||||
width: 700,
|
||||
height: 700
|
||||
}
|
||||
};
|
||||
|
||||
return setAndVerifyConfig(setConfig, expConfig);
|
||||
});
|
||||
|
||||
suite.setup()
|
||||
.then(suite.run);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,357 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=965420
|
||||
-->
|
||||
<head>
|
||||
<title>Bug 965420 - Test camera hardware API for face detection</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="camera_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=965420">Mozilla Bug 965420</a>
|
||||
<video id="viewfinder" width = "200" height = "200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
function compareFaces(aFaces, expected)
|
||||
{
|
||||
ok(aFaces, "have detected faces object");
|
||||
ok(aFaces.length == expected.faces.length,
|
||||
"expected=" + expected.faces.length + ", got=" + aFaces.length);
|
||||
aFaces.forEach(function (face, index) {
|
||||
let result = compareFace(face, expected.faces[index]);
|
||||
ok(result === "ok", "face check: " + result);
|
||||
if (result !== "ok") {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
function compareFace(aFace, expected)
|
||||
{
|
||||
if (aFace.id != expected.id) {
|
||||
return "expected face.id=" + expected.id + ", got=" + aFace.id;
|
||||
}
|
||||
if (aFace.score != expected.score) {
|
||||
return "expected face.score=" + expected.score + ", got=" + aFace.score;
|
||||
}
|
||||
if (!aFace.bounds) {
|
||||
return "face.bounds is missing";
|
||||
}
|
||||
if (aFace.bounds.left != expected.bounds.left ||
|
||||
aFace.bounds.top != expected.bounds.top ||
|
||||
aFace.bounds.right != expected.bounds.right ||
|
||||
aFace.bounds.bottom != expected.bounds.bottom) {
|
||||
return "expected face.bounds=" + expected.bounds.toSource() +
|
||||
", got=({left:" + aFace.bounds.left + ", top:" + aFace.bounds.top + ", right:" + aFace.bounds.right + ", bottom:" + aFace.bounds.bottom + "})";
|
||||
}
|
||||
|
||||
if (aFace.leftEye && !expected.leftEye) {
|
||||
return "expected null face.leftEye, got=({x:" + aFace.leftEye.x + ", y:" + aFace.leftEye.y + "})";
|
||||
}
|
||||
if (!aFace.leftEye && expected.leftEye) {
|
||||
return "expected face.leftEye=" + expected.leftEye.toSource() + ", got null leftEye";
|
||||
}
|
||||
if (aFace.leftEye && expected.leftEye &&
|
||||
(aFace.leftEye.x != expected.leftEye.x || aFace.leftEye.y != expected.leftEye.y)) {
|
||||
return "expected face.leftEye=" + expected.leftEye.toSource() +
|
||||
", got=({x:" + aFace.leftEye.x + ", y:" + aFace.leftEye.y + "})";
|
||||
}
|
||||
|
||||
if (aFace.rightEye && !expected.rightEye) {
|
||||
return "expected null face.rightEye, got=({x:" + aFace.rightEye.x + ", y:" + aFace.rightEye.y + "})";
|
||||
}
|
||||
if (!aFace.rightEye && expected.rightEye) {
|
||||
return "expected face.rightEye=" + expected.rightEye.toSource() + ", got null rightEye";
|
||||
}
|
||||
if (aFace.rightEye && expected.rightEye &&
|
||||
(aFace.rightEye.x != expected.rightEye.x || aFace.rightEye.y != expected.rightEye.y)) {
|
||||
return "expected face.rightEye=" + expected.rightEye.toSource() +
|
||||
", got=({x:" + aFace.rightEye.x + ", y:" + aFace.rightEye.y + "})";
|
||||
}
|
||||
|
||||
if (aFace.mouth && !expected.mouth) {
|
||||
return "expected null face.mouth, got=({x:" + aFace.mouth.x + ", y:" + aFace.mouth.y + "})";
|
||||
}
|
||||
if (!aFace.mouth && expected.mouth) {
|
||||
return "expected face.mouth=" + expected.mouth.toSource() + ", got null mouth";
|
||||
}
|
||||
if (aFace.mouth && expected.mouth &&
|
||||
(aFace.mouth.x != expected.mouth.x || aFace.mouth.y != expected.mouth.y)) {
|
||||
return "expected face.mouth=" + expected.mouth.toSource() +
|
||||
", got=({x:" + aFace.mouth.x + ", y:" + aFace.mouth.y + "})";
|
||||
}
|
||||
|
||||
return "ok";
|
||||
}
|
||||
|
||||
var suite = new CameraTestSuite();
|
||||
|
||||
suite.test('face-detection-op', function() {
|
||||
function start(p) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
suite.hw.attach({
|
||||
startFaceDetection: function() {
|
||||
ok(true, "startFaceDetection() requested");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
try {
|
||||
suite.camera.startFaceDetection();
|
||||
ok(true, "startFaceDetection() succeeded");
|
||||
} catch(e) {
|
||||
ok(false, "startFaceDetection() failed with: " + e.name);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function stop(p) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
suite.hw.attach({
|
||||
stopFaceDetection: function() {
|
||||
ok(true, "stopFaceDetection() requested");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
try {
|
||||
suite.camera.stopFaceDetection();
|
||||
ok(true, "stopFaceDetection() succeeded");
|
||||
} catch(e) {
|
||||
ok(false, "stopFaceDetection() failed with: " + e.name);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function startFailure(p) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
suite.hw.attach({
|
||||
startFaceDetection: function() {
|
||||
ok(true, "startFaceDetection() requested and failed");
|
||||
resolve();
|
||||
throw SpecialPowers.Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
});
|
||||
try {
|
||||
suite.camera.startFaceDetection();
|
||||
ok(true, "startFaceDetection() succeeded and error swallowed");
|
||||
} catch(e) {
|
||||
ok(false, "startFaceDetection() failed with: " + e.name);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function stopFailure(p) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
suite.hw.attach({
|
||||
stopFaceDetection: function() {
|
||||
ok(true, "stopFaceDetection() requested and failed");
|
||||
resolve();
|
||||
throw SpecialPowers.Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
});
|
||||
try {
|
||||
suite.camera.stopFaceDetection();
|
||||
ok(true, "stopFaceDetection() succeeded and error swallowed");
|
||||
} catch(e) {
|
||||
ok(false, "stopFaceDetection() failed with: " + e.name);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(start, suite.rejectGetCamera)
|
||||
.then(stop)
|
||||
.then(startFailure)
|
||||
.then(stopFailure);
|
||||
});
|
||||
|
||||
suite.test('face-detection', function() {
|
||||
function detectFace(msg, given, expected) {
|
||||
if (expected === undefined) {
|
||||
expected = given;
|
||||
}
|
||||
var sync = new Promise(function(resolve, reject) {
|
||||
function onEvent(evt) {
|
||||
try {
|
||||
suite.camera.removeEventListener('facesdetected', onEvent);
|
||||
ok(compareFaces(evt.faces, expected),
|
||||
"facedetected event received " + msg + " correctly");
|
||||
resolve();
|
||||
} catch(e) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
suite.camera.addEventListener('facesdetected', onEvent);
|
||||
});
|
||||
|
||||
suite.hw.fireFacesDetected(given);
|
||||
return sync;
|
||||
}
|
||||
|
||||
function detectOneFace(p) {
|
||||
return detectFace('one-face',
|
||||
{
|
||||
faces: [ {
|
||||
id: 1,
|
||||
score: 2,
|
||||
bounds: {
|
||||
left: 3,
|
||||
top: 4,
|
||||
right: 5,
|
||||
bottom: 6
|
||||
},
|
||||
leftEye: {
|
||||
x: 7,
|
||||
y: 8
|
||||
},
|
||||
rightEye: {
|
||||
x: 9,
|
||||
y: 10
|
||||
},
|
||||
mouth: {
|
||||
x: 11,
|
||||
y: 12
|
||||
}
|
||||
} ]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function detectTwoFaces(p) {
|
||||
return detectFace('two-faces',
|
||||
{
|
||||
faces: [ {
|
||||
id: 1,
|
||||
score: 2,
|
||||
bounds: {
|
||||
left: 3,
|
||||
top: 4,
|
||||
right: 5,
|
||||
bottom: 6
|
||||
},
|
||||
leftEye: {
|
||||
x: 7,
|
||||
y: 8
|
||||
},
|
||||
rightEye: {
|
||||
x: 9,
|
||||
y: 10
|
||||
},
|
||||
mouth: {
|
||||
x: 11,
|
||||
y: 12
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
score: 14,
|
||||
bounds: {
|
||||
left: 15,
|
||||
top: 16,
|
||||
right: 17,
|
||||
bottom: 18
|
||||
},
|
||||
leftEye: {
|
||||
x: 19,
|
||||
y: 20
|
||||
},
|
||||
rightEye: {
|
||||
x: 21,
|
||||
y: 22
|
||||
},
|
||||
mouth: {
|
||||
x: 23,
|
||||
y: 24
|
||||
}
|
||||
} ]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function detectOneFaceNoFeatures() {
|
||||
return detectFace('one-face-no-features',
|
||||
{
|
||||
faces: [ {
|
||||
id: 1,
|
||||
score: 100,
|
||||
bounds: {
|
||||
left: 3,
|
||||
top: 4,
|
||||
right: 5,
|
||||
bottom: 6
|
||||
},
|
||||
leftEye: null,
|
||||
rightEye: null,
|
||||
mouth: null
|
||||
} ]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function detectNoFaces() {
|
||||
return detectFace('no-faces',
|
||||
{
|
||||
faces: []
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function detectOneFaceExcessScore() {
|
||||
return detectFace('one-face-excess-score',
|
||||
{
|
||||
faces: [ {
|
||||
id: 1,
|
||||
score: 120,
|
||||
bounds: {
|
||||
left: 3,
|
||||
top: 4,
|
||||
right: 5,
|
||||
bottom: 6
|
||||
},
|
||||
leftEye: null,
|
||||
rightEye: null,
|
||||
mouth: null
|
||||
} ]
|
||||
},
|
||||
{
|
||||
faces: [ {
|
||||
id: 1,
|
||||
score: 100,
|
||||
bounds: {
|
||||
left: 3,
|
||||
top: 4,
|
||||
right: 5,
|
||||
bottom: 6
|
||||
},
|
||||
leftEye: null,
|
||||
rightEye: null,
|
||||
mouth: null
|
||||
} ]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(detectOneFace, suite.rejectGetCamera)
|
||||
.then(detectTwoFaces)
|
||||
.then(detectOneFaceNoFeatures)
|
||||
.then(detectNoFaces)
|
||||
.then(detectOneFaceExcessScore);
|
||||
});
|
||||
|
||||
suite.setup()
|
||||
.then(suite.run);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,559 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for CameraParameters we need to fake</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="camera_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=976802">Mozilla Bug 976802</a>
|
||||
<video id="viewfinder" width="200" height="200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var suite = new CameraTestSuite();
|
||||
|
||||
suite.test('fake-zoom', function() {
|
||||
suite.hw.params['zoom-ratios'] = '100,150,200,300,400';
|
||||
suite.hw.params['max-zoom'] = '4';
|
||||
|
||||
function resolve(p) {
|
||||
var cam = suite.camera;
|
||||
var cap = cam.capabilities;
|
||||
|
||||
ok(cap.zoomRatios.length == 5, "zoom ratios length = " + cap.zoomRatios.length);
|
||||
|
||||
// test individual zoom ratios
|
||||
cap.zoomRatios.forEach(function(zoom, index) {
|
||||
cam.zoom = zoom;
|
||||
ok(cam.zoom === zoom,
|
||||
"zoom[" + index + "] = " + zoom + "x, cam.zoom = " + cam.zoom + "x");
|
||||
});
|
||||
|
||||
// test below-lower-bound zoom ratio
|
||||
var zoom = cap.zoomRatios[0] - 0.1;
|
||||
cam.zoom = zoom;
|
||||
ok(cam.zoom === cap.zoomRatios[0],
|
||||
zoom + "x zoom clamps to minimum: " +
|
||||
cap.zoomRatios[0] + "x, cam.zoom = " + cam.zoom + "x");
|
||||
|
||||
// test above-upper-bound zoom ratio
|
||||
zoom = cap.zoomRatios.slice(-1)[0] + 1.0;
|
||||
cam.zoom = zoom;
|
||||
ok(cam.zoom === cap.zoomRatios.slice(-1)[0],
|
||||
zoom + "x zoom clamps to maximum: " + cap.zoomRatios.slice(-1)[0] +
|
||||
"x, cam.zoom = " + cam.zoom + "x");
|
||||
|
||||
// test snapping to supported zoom ratio
|
||||
if (cap.zoomRatios.length > 1) {
|
||||
zoom = (cap.zoomRatios[0] + cap.zoomRatios[1]) / 2;
|
||||
cam.zoom = zoom;
|
||||
ok(cam.zoom === cap.zoomRatios[0],
|
||||
zoom + "x zoom rounded down to: " + cap.zoomRatios[0] +
|
||||
"x, cam.zoom = " + cam.zoom + "x");
|
||||
}
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(resolve, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
|
||||
suite.test('fake-zoom-out-of-order', function() {
|
||||
// We expect the camera library to give us zoom ratios in order; if
|
||||
// it doesn't we ignore the list and just return 1x support.
|
||||
suite.hw.params['zoom-ratios'] = '100,150,200,400,300';
|
||||
suite.hw.params['max-zoom'] = '4';
|
||||
|
||||
function resolve(p) {
|
||||
var cap = suite.camera.capabilities;
|
||||
ok(cap.zoomRatios.length == 1, "zoom ratios length = " + cap.zoomRatios.length);
|
||||
ok(cap.zoomRatios[0] == 1.0, "only supported zoom = " + cap.zoomRatios[0] + "x");
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(resolve, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('fake-high-memory-platform', function() {
|
||||
suite.hw.params['scene-mode-values'] = 'none,snow,beach,hdr,nothdr';
|
||||
|
||||
function resolve(p) {
|
||||
var cam = suite.camera;
|
||||
var cap = cam.capabilities;
|
||||
ok(cap.sceneModes.length == 5, "scene modes length = " + cap.zoomRatios.length);
|
||||
|
||||
// make sure expected values are present and can be set
|
||||
[ "none", "snow", "beach", "hdr", "nothdr" ].forEach(function(mode) {
|
||||
ok(cap.sceneModes.indexOf(mode) != -1, "Scene mode '" + mode + "' is present");
|
||||
cam.sceneMode = mode;
|
||||
ok(cam.sceneMode == mode, "Scene mode '" + cam.sceneMode + "' is set");
|
||||
});
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(resolve, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('fake-low-memory-platform', function() {
|
||||
suite.hw.params['scene-mode-values'] = 'none,hdr,snow,beach,hdr,nothdr';
|
||||
|
||||
function resolve(p) {
|
||||
var cam = suite.camera;
|
||||
var cap = cam.capabilities;
|
||||
ok(cap.sceneModes.length == 4, "scene modes length = " + cap.zoomRatios.length);
|
||||
|
||||
// make sure expected values are present and can be set
|
||||
[ "none", "snow", "beach", "nothdr" ].forEach(function(mode) {
|
||||
ok(cap.sceneModes.indexOf(mode) != -1, "Scene mode '" + mode + "' is present");
|
||||
cam.sceneMode = mode;
|
||||
ok(cam.sceneMode == mode, "Scene mode '" + cam.sceneMode + "' is set");
|
||||
});
|
||||
|
||||
// make sure unsupported values have been removed, and can't be set
|
||||
var sceneMode = cam.sceneMode;
|
||||
[ "hdr" ].forEach(function(mode) {
|
||||
ok(cap.sceneModes.indexOf(mode) == -1, "Scene mode '" + mode + "' is not present");
|
||||
try {
|
||||
cam.sceneMode = mode;
|
||||
} catch(e) {
|
||||
}
|
||||
ok(cam.sceneMode != mode, "Scene mode '" + cam.sceneMode + "' is still set, '"
|
||||
+ mode + "' rejected");
|
||||
});
|
||||
ok(cam.sceneMode == sceneMode, "Scene mode '" + cam.sceneMode + "' is still set");
|
||||
}
|
||||
|
||||
return suite.setLowMemoryPlatform()
|
||||
.then(suite.getCamera)
|
||||
.then(resolve, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('fake-effects', function() {
|
||||
var supportedValues = ['none', 'mono', 'negative', 'solarize', 'sepia', 'posterize', 'whiteboard', 'blackboard', 'aqua'];
|
||||
suite.hw.params['effect'] = 'none';
|
||||
suite.hw.params['effect-values'] = supportedValues.join(',');
|
||||
|
||||
function resolve(p) {
|
||||
var cap = suite.camera.capabilities;
|
||||
ok(cap.effects.length == supportedValues.length, "Effects length = " + cap.effects.length);
|
||||
|
||||
// make sure expected values are present
|
||||
supportedValues.forEach(function(val) {
|
||||
ok(cap.effects.indexOf(val) != -1, "Effect '" + val + "' is present");
|
||||
});
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(resolve, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('fake-flash-modes', function() {
|
||||
var supportedValues = ['off', 'auto', 'on', 'red-eye', 'torch'];
|
||||
suite.hw.params['flash-mode'] = 'auto';
|
||||
suite.hw.params['flash-mode-values'] = supportedValues.join(',');
|
||||
|
||||
function resolve(p) {
|
||||
var cam = suite.camera;
|
||||
var cap = cam.capabilities;
|
||||
ok(cap.flashModes.length == supportedValues.length, "Flash modes length = " + cap.flashModes.length);
|
||||
|
||||
// make sure expected values are present
|
||||
supportedValues.forEach(function(val) {
|
||||
ok(cap.flashModes.indexOf(val) != -1, "Flash mode '" + val + "' is present");
|
||||
});
|
||||
|
||||
// test setters/getters
|
||||
cap.flashModes.forEach(function(val, index) {
|
||||
cam.flashMode = val;
|
||||
ok(cam.flashMode === val,
|
||||
"Flash Mode [" + index + "] = " + val + ", cam.flashMode = " + cam.flashMode);
|
||||
});
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(resolve, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('fake-focus-modes', function() {
|
||||
var supportedValues = ['auto', 'infinity', 'macro', 'fixed', 'edof', 'continuous-video'];
|
||||
suite.hw.params['focus-mode'] = 'auto';
|
||||
suite.hw.params['focus-mode-values'] = supportedValues.join(',');
|
||||
|
||||
function resolve(p) {
|
||||
var cam = suite.camera;
|
||||
var cap = cam.capabilities;
|
||||
ok(cap.focusModes.length == supportedValues.length, "Focus modes length = " + cap.focusModes.length);
|
||||
|
||||
// make sure expected values are present
|
||||
supportedValues.forEach(function(val) {
|
||||
ok(cap.focusModes.indexOf(val) != -1, "Focus mode '" + val + "' is present");
|
||||
});
|
||||
|
||||
// test setters/getters
|
||||
cap.focusModes.forEach(function(val, index) {
|
||||
cam.focusMode = val;
|
||||
ok(cam.focusMode === val,
|
||||
"Focus Mode [" + index + "] = " + val + ", cam.focusMode = " + cam.focusMode);
|
||||
});
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(resolve, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('fake-white-balance-modes', function() {
|
||||
var supportedValues = ['auto', 'incandescent', 'fluorescent', 'warm-fluorescent', 'daylight', 'cloudy-daylight', 'twilight', 'shade'];
|
||||
suite.hw.params['whitebalance'] = 'auto';
|
||||
suite.hw.params['whitebalance-values'] = supportedValues.join(',');
|
||||
|
||||
function resolve(p) {
|
||||
var cam = suite.camera;
|
||||
var cap = cam.capabilities;
|
||||
ok(cap.whiteBalanceModes.length == supportedValues.length, "White balance modes length = " + cap.whiteBalanceModes.length);
|
||||
|
||||
// make sure expected values are present
|
||||
supportedValues.forEach(function(val) {
|
||||
ok(cap.whiteBalanceModes.indexOf(val) != -1, "White balance mode '" + val + "' is present");
|
||||
});
|
||||
|
||||
// test setters/getters
|
||||
cap.whiteBalanceModes.forEach(function(val, index) {
|
||||
cam.whiteBalanceMode = val;
|
||||
ok(cam.whiteBalanceMode === val,
|
||||
"White balance mode [" + index + "] = " + val + ", cam.whiteBalanceMode = " + cam.whiteBalanceMode);
|
||||
});
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(resolve, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('fake-video-sizes', function() {
|
||||
var supportedValues = ['auto', 'incandescent', 'fluorescent', 'warm-fluorescent', 'daylight', 'cloudy-daylight', 'twilight', 'shade'];
|
||||
suite.hw.params['whitebalance'] = 'auto';
|
||||
suite.hw.params['whitebalance-values'] = supportedValues.join(',');
|
||||
|
||||
function resolve(p) {
|
||||
var cam = suite.camera;
|
||||
var cap = cam.capabilities;
|
||||
ok(cap.whiteBalanceModes.length == supportedValues.length, "White balance modes length = " + cap.whiteBalanceModes.length);
|
||||
|
||||
// make sure expected values are present
|
||||
supportedValues.forEach(function(val) {
|
||||
ok(cap.whiteBalanceModes.indexOf(val) != -1, "White balance mode '" + val + "' is present");
|
||||
});
|
||||
|
||||
// test setters/getters
|
||||
cap.whiteBalanceModes.forEach(function(val, index) {
|
||||
cam.whiteBalanceMode = val;
|
||||
ok(cam.whiteBalanceMode === val,
|
||||
"White balance mode [" + index + "] = " + val + ", cam.whiteBalanceMode = " + cam.whiteBalanceMode);
|
||||
});
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(resolve, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('fake-iso', function() {
|
||||
suite.hw.params['iso'] = 'auto';
|
||||
suite.hw.params['iso-values'] = 'auto,ISO_HJR,ISO100,foo,ISObar,ISO150moz,ISO200,400,ISO800,1600';
|
||||
|
||||
function resolve(p) {
|
||||
var cam = suite.camera;
|
||||
var cap = cam.capabilities;
|
||||
ok(cap.isoModes.length == 7, "ISO modes length = " + cap.isoModes.length);
|
||||
|
||||
// make sure we're not leaking any unexpected values formats
|
||||
[ "ISO_HJR", "_HJR", "HJR", "ISO100", "ISO200", "ISO800" ].forEach(function(iso) {
|
||||
ok(cap.isoModes.indexOf(iso) == -1, "ISO mode '" + iso + "' does not appear");
|
||||
});
|
||||
|
||||
// make sure any weird values are dropped entirely
|
||||
[ "foo", "ISObar", "bar", "ISO150moz", "150moz", "150" ].forEach(function(iso) {
|
||||
ok(cap.isoModes.indexOf(iso) == -1, "Unknown ISO mode '" + iso + "' is ignored");
|
||||
});
|
||||
|
||||
// make sure expected values are present
|
||||
[ "auto", "hjr", "100", "200", "400", "800", "1600" ].forEach(function(iso) {
|
||||
ok(cap.isoModes.indexOf(iso) != -1, "ISO mode '" + iso + "' is present");
|
||||
});
|
||||
|
||||
// test setters/getters for individual ISO modes
|
||||
cap.isoModes.forEach(function(iso, index) {
|
||||
cam.isoMode = iso;
|
||||
ok(cam.isoMode === iso,
|
||||
"ISO[" + index + "] = " + iso + ", cam.iso = " + cam.isoMode);
|
||||
});
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(resolve, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('fake-faces-detected', function() {
|
||||
suite.hw.params['max-num-detected-faces-hw'] = '5';
|
||||
|
||||
function resolve(p) {
|
||||
var cap = suite.camera.capabilities;
|
||||
|
||||
ok(cap.maxDetectedFaces == 5, "maxDetectedFaces = " + cap.maxDetectedFaces);
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(resolve, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('fake-metering-areas', function() {
|
||||
suite.hw.params['max-num-metering-areas'] = '1';
|
||||
|
||||
function resolve(p) {
|
||||
var cam = suite.camera;
|
||||
var cap = cam.capabilities;
|
||||
|
||||
ok(cap.maxMeteringAreas == 1, "maxMeteringAreas = " + cap.maxMeteringAreas);
|
||||
cam.setMeteringAreas([
|
||||
{top: -500, bottom: 500, left: -500, right: 500, weight: 100}
|
||||
]);
|
||||
areas = cam.getMeteringAreas();
|
||||
ok(areas.length == 1, "areas length = " + areas.length);
|
||||
ok(areas[0].top == -500, "area[0] top = " + areas[0].top);
|
||||
ok(areas[0].bottom == 500, "area[0] bottom = " + areas[0].bottom);
|
||||
ok(areas[0].left == -500, "area[0] left = " + areas[0].left);
|
||||
ok(areas[0].right == 500, "area[0] right = " + areas[0].right);
|
||||
ok(areas[0].weight == 100, "area[0] weight = " + areas[0].weight);
|
||||
cam.setMeteringAreas([
|
||||
{top: -501, bottom: 502, left: -503, right: 504, weight: 105},
|
||||
{top: -500, bottom: 500, left: -500, right: 500, weight: 100}
|
||||
]);
|
||||
areas = cam.getMeteringAreas();
|
||||
ok(areas.length == 1, "areas length = " + areas.length);
|
||||
ok(areas[0].top == -501, "area[0] top = " + areas[0].top);
|
||||
ok(areas[0].bottom == 502, "area[0] bottom = " + areas[0].bottom);
|
||||
ok(areas[0].left == -503, "area[0] left = " + areas[0].left);
|
||||
ok(areas[0].right == 504, "area[0] right = " + areas[0].right);
|
||||
ok(areas[0].weight == 105, "area[0] weight = " + areas[0].weight);
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(resolve, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('fake-focus-areas', function() {
|
||||
suite.hw.params['max-num-focus-areas'] = '1';
|
||||
|
||||
function resolve(p) {
|
||||
var cam = suite.camera;
|
||||
var cap = cam.capabilities;
|
||||
|
||||
ok(cap.maxFocusAreas == 1, "maxFocusAreas = " + cap.maxFocusAreas);
|
||||
cam.setFocusAreas([
|
||||
{top: -500, bottom: 500, left: -500, right: 500, weight: 100}
|
||||
]);
|
||||
areas = cam.getFocusAreas();
|
||||
ok(areas.length == 1, "areas length = " + areas.length);
|
||||
ok(areas[0].top == -500, "area[0] top = " + areas[0].top);
|
||||
ok(areas[0].bottom == 500, "area[0] bottom = " + areas[0].bottom);
|
||||
ok(areas[0].left == -500, "area[0] left = " + areas[0].left);
|
||||
ok(areas[0].right == 500, "area[0] right = " + areas[0].right);
|
||||
ok(areas[0].weight == 100, "area[0] weight = " + areas[0].weight);
|
||||
cam.setFocusAreas([
|
||||
{top: -501, bottom: 502, left: -503, right: 504, weight: 105},
|
||||
{top: -500, bottom: 500, left: -500, right: 500, weight: 100}
|
||||
]);
|
||||
areas = cam.getFocusAreas();
|
||||
ok(areas.length == 1, "areas length = " + areas.length);
|
||||
ok(areas[0].top == -501, "area[0] top = " + areas[0].top);
|
||||
ok(areas[0].bottom == 502, "area[0] bottom = " + areas[0].bottom);
|
||||
ok(areas[0].left == -503, "area[0] left = " + areas[0].left);
|
||||
ok(areas[0].right == 504, "area[0] right = " + areas[0].right);
|
||||
ok(areas[0].weight == 105, "area[0] weight = " + areas[0].weight);
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(resolve, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('fake-exposure-compensation', function() {
|
||||
suite.hw.params['max-num-focus-areas'] = '1';
|
||||
suite.hw.params['exposure-compensation'] = '-1';
|
||||
suite.hw.params['max-exposure-compensation'] = '6';
|
||||
suite.hw.params['min-exposure-compensation'] = '-6';
|
||||
suite.hw.params['exposure-compensation-step'] = '0.5'
|
||||
|
||||
function resolve(p) {
|
||||
var cam = suite.camera;
|
||||
var cap = cam.capabilities;
|
||||
|
||||
ok(cap.exposureCompensationStep == 0.5,
|
||||
"exposureCompensationStep = " + cap.exposureCompensationStep);
|
||||
ok(cap.minExposureCompensation == -3.0,
|
||||
"minExposureCompensation = " + cap.minExposureCompensation);
|
||||
ok(cap.maxExposureCompensation == 3.0,
|
||||
"maxExposureCompensation = " + cap.maxExposureCompensation);
|
||||
ok(cam.exposureCompensation == -0.5,
|
||||
"exposureCompensation = " + cam.exposureCompensation);
|
||||
|
||||
// Check normal values
|
||||
cam.exposureCompensation = 0.0;
|
||||
ok(cam.exposureCompensation == 0.0,
|
||||
"exposureCompensation = " + cam.exposureCompensation);
|
||||
cam.exposureCompensation = cap.minExposureCompensation;
|
||||
ok(cam.exposureCompensation == cap.minExposureCompensation,
|
||||
"exposureCompensation(min) = " + cam.exposureCompensation);
|
||||
cam.exposureCompensation = cap.maxExposureCompensation;
|
||||
ok(cam.exposureCompensation == cap.maxExposureCompensation,
|
||||
"exposureCompensation(max) = " + cam.exposureCompensation);
|
||||
|
||||
// Rounding
|
||||
cam.exposureCompensation = 1.24;
|
||||
ok(cam.exposureCompensation == 1.0,
|
||||
"exposureCompensation(1.24) = " + cam.exposureCompensation);
|
||||
cam.exposureCompensation = 1.25;
|
||||
ok(cam.exposureCompensation == 1.5,
|
||||
"exposureCompensation(1.25) = " + cam.exposureCompensation);
|
||||
cam.exposureCompensation = -1.24;
|
||||
ok(cam.exposureCompensation == -1.0,
|
||||
"exposureCompensation(-1.24) = " + cam.exposureCompensation);
|
||||
cam.exposureCompensation = -1.25;
|
||||
ok(cam.exposureCompensation == -1.5,
|
||||
"exposureCompensation(-1.25) = " + cam.exposureCompensation);
|
||||
// Check out-of-bounds values
|
||||
cam.exposureCompensation = cap.minExposureCompensation - 1.0;
|
||||
ok(cam.exposureCompensation == cap.minExposureCompensation,
|
||||
"exposureCompensation(min - 1.0) = " + cam.exposureCompensation);
|
||||
cam.exposureCompensation = cap.maxExposureCompensation + 1.0;
|
||||
ok(cam.exposureCompensation == cap.maxExposureCompensation,
|
||||
"exposureCompensation(max + 1.0) = " + cam.exposureCompensation);
|
||||
|
||||
// Check extreme values
|
||||
cam.exposureCompensation = -1 * Math.pow(2, 32);
|
||||
ok(cam.exposureCompensation == cap.minExposureCompensation,
|
||||
"exposureCompensation(-2^32) = " + cam.exposureCompensation);
|
||||
cam.exposureCompensation = Math.pow(2, 32);
|
||||
ok(cam.exposureCompensation == cap.maxExposureCompensation,
|
||||
"exposureCompensation(2^32) = " + cam.exposureCompensation);
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(resolve, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('bug-1054803', function() {
|
||||
// The important part of this test is that 3264 * 1836 = 5,992,704 = 2448 * 2448,
|
||||
// so we need to make sure that the size-matching algorithm picks the right size.
|
||||
suite.hw.params['picture-size-values'] = '3264x1836,2448x2448,1836x3264';
|
||||
|
||||
function verify(p) {
|
||||
var cam = suite.camera;
|
||||
var cap = cam.capabilities;
|
||||
|
||||
var expSizes = [ { height: 3264, width: 1836 },
|
||||
{ height: 1836, width: 3264 },
|
||||
{ height: 2448, width: 2448 } ];
|
||||
|
||||
// validate the capability attribute
|
||||
ok(cap.pictureSizes.length == expSizes.length, "pictureSizes.length = " + cap.pictureSizes.length);
|
||||
var found = 0;
|
||||
expSizes.forEach(function(size) {
|
||||
found = 0;
|
||||
cap.pictureSizes.forEach(function(capSize) {
|
||||
if (capSize.height == size.height && capSize.width == size.width) {
|
||||
++found;
|
||||
}
|
||||
});
|
||||
ok(found == 1, "found size " + size.toSource() + " in pictureSizes");
|
||||
});
|
||||
|
||||
var sizeIterator = expSizes.values();
|
||||
return new Promise(function(resolve, reject) {
|
||||
function nextSize() {
|
||||
var {value:size, done} = sizeIterator.next();
|
||||
if (done) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
var sync = suite.waitParameterPush();
|
||||
cam.setPictureSize(size);
|
||||
sync.then(function() {
|
||||
var got = cam.getPictureSize();
|
||||
ok(got.width == size.width && got.height == size.height,
|
||||
"Set size " + size.toSource() + ", got size " + got.toSource());
|
||||
nextSize();
|
||||
}, reject);
|
||||
}
|
||||
|
||||
nextSize();
|
||||
});
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(verify, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('bug-1052851', function() {
|
||||
// We should reject duplicate values.
|
||||
suite.hw.params['auto-exposure'] = 'frame-average';
|
||||
suite.hw.params['auto-exposure-values'] = 'spot,frame-average,center-weighted,spot,center-weighted';
|
||||
|
||||
function resolve(p) {
|
||||
var cam = suite.camera;
|
||||
var cap = cam.capabilities;
|
||||
|
||||
ok(cap.meteringModes.length == 3, "Metering modes length = " + cap.meteringModes.length);
|
||||
|
||||
// make sure expected values are present
|
||||
[ "spot", "frame-average", "center-weighted" ].forEach(function(mode) {
|
||||
ok(cap.meteringModes.indexOf(mode) != -1, "Metering mode '" + mode + "' is present");
|
||||
});
|
||||
|
||||
// test setters/getters for individual metering modes
|
||||
cap.meteringModes.forEach(function(mode, index) {
|
||||
cam.meteringMode = mode;
|
||||
ok(cam.meteringMode === mode,
|
||||
"Metering Mode[" + index + "] = " + mode + ", cam.meteringMode = " + cam.meteringMode);
|
||||
});
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(resolve, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('bug-1124338', function() {
|
||||
function triggerAutoFocus(p) {
|
||||
var sync = new Promise(function(resolve, reject) {
|
||||
function onEvent(e) {
|
||||
suite.camera.removeEventListener('focus', onEvent);
|
||||
var thumbnailSize = suite.camera.getThumbnailSize();
|
||||
ok(thumbnailSize.width == 640 && thumbnailSize.height == 480, 'thumbnail size reset with auto focus');
|
||||
resolve();
|
||||
}
|
||||
suite.camera.addEventListener('focus', onEvent);
|
||||
});
|
||||
|
||||
var initThumbnailSize = suite.camera.getThumbnailSize();
|
||||
ok(initThumbnailSize.width == 320 && initThumbnailSize.height == 240, 'initial thumbnail size incorrect');
|
||||
suite.hw.params['jpeg-thumbnail-width'] = '640';
|
||||
suite.hw.params['jpeg-thumbnail-height'] = '480';
|
||||
suite.hw.fireAutoFocusComplete(false);
|
||||
return sync;
|
||||
}
|
||||
|
||||
suite.hw.params['jpeg-thumbnail-size-values'] = '320x240,640x480';
|
||||
suite.hw.params['jpeg-thumbnail-width'] = '320';
|
||||
suite.hw.params['jpeg-thumbnail-height'] = '240';
|
||||
return suite.getCamera()
|
||||
.then(triggerAutoFocus)
|
||||
});
|
||||
|
||||
suite.setup()
|
||||
.then(suite.run);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,88 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=940424
|
||||
-->
|
||||
<head>
|
||||
<title>Bug 940424 - Test camera hardware init failure handling</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="camera_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=940424">Mozilla Bug 940424</a>
|
||||
<video id="viewfinder" width="200" height="200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var suite = new CameraTestSuite();
|
||||
|
||||
suite.test('init-failure', function() {
|
||||
var cameraManager = navigator.mozCameras;
|
||||
var whichCamera = cameraManager.getListOfCameras()[0];
|
||||
|
||||
suite.hw.attach({
|
||||
init: function() {
|
||||
throw SpecialPowers.Cr.NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
});
|
||||
|
||||
function rejectGetCamera(e) {
|
||||
ok(e.name === 'NS_ERROR_NOT_AVAILABLE', 'onError called correctly on getCamera init failure: ' + e);
|
||||
// Let the next getCamera attempt succeed
|
||||
suite.initJsHw();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function resolveGetCamera(p) {
|
||||
ok(true, 'onSuccess called correctly for second getCamera request');
|
||||
}
|
||||
|
||||
return cameraManager.getCamera(whichCamera)
|
||||
.then(suite.expectedRejectGetCamera, rejectGetCamera)
|
||||
.then(suite.getCamera)
|
||||
.then(resolveGetCamera, suite.rejectGetCamera)
|
||||
});
|
||||
|
||||
/* Test for bug 1099390 to make sure events related to the underlying
|
||||
platform failing are generated and handled properly. */
|
||||
suite.test('post-init-system-failure', function() {
|
||||
function triggerSystemFailure(p) {
|
||||
var sync = new Promise(function(resolve, reject) {
|
||||
function onClosedEvent(e) {
|
||||
suite.camera.removeEventListener('close', onClosedEvent);
|
||||
ok(e.reason === 'SystemFailure', 'reason is: ' + e.reason);
|
||||
resolve();
|
||||
}
|
||||
suite.camera.addEventListener('close', onClosedEvent);
|
||||
});
|
||||
|
||||
suite.hw.fireSystemError();
|
||||
return sync;
|
||||
}
|
||||
|
||||
function releaseCamera(p) {
|
||||
var camera = suite.camera;
|
||||
suite.camera = null;
|
||||
return camera.release();
|
||||
}
|
||||
|
||||
function releasedCamera(p) {
|
||||
ok(true, 'camera released after system failure');
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(triggerSystemFailure, suite.rejectGetCamera)
|
||||
.then(releaseCamera)
|
||||
.then(releasedCamera, suite.rejectRelease);
|
||||
});
|
||||
|
||||
suite.setup()
|
||||
.then(suite.run);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,208 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Camera Recording</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="camera_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<video id="viewfinder" width = "200" height = "200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var suite = new CameraTestSuite();
|
||||
|
||||
var baseConfig = {
|
||||
mode: 'video',
|
||||
};
|
||||
|
||||
var testFilePath = 'test.3gp';
|
||||
var storage = navigator.getDeviceStorage("videos");
|
||||
|
||||
function cleanup()
|
||||
{
|
||||
return storage.delete(testFilePath).then(function(p) {
|
||||
}, function(e) {
|
||||
Promise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
suite.test('recording', function() {
|
||||
function startRecording(p) {
|
||||
var eventPromise = new Promise(function(resolve, reject) {
|
||||
function onEvent(evt) {
|
||||
ok(evt.newState === 'Started', 'recorder state change event = ' + evt.newState);
|
||||
suite.camera.removeEventListener('recorderstatechange', onEvent);
|
||||
resolve();
|
||||
}
|
||||
suite.camera.addEventListener('recorderstatechange', onEvent);
|
||||
});
|
||||
|
||||
var domPromise = suite.camera.startRecording({}, storage, testFilePath);
|
||||
return Promise.all([domPromise, eventPromise]);
|
||||
}
|
||||
|
||||
function pauseRecording(p) {
|
||||
var eventPromise = new Promise(function(resolve, reject) {
|
||||
function onEvent(evt) {
|
||||
ok(evt.newState === 'Paused', 'recorder state change event = ' + evt.newState);
|
||||
suite.camera.removeEventListener('recorderstatechange', onEvent);
|
||||
resolve();
|
||||
}
|
||||
suite.camera.addEventListener('recorderstatechange', onEvent);
|
||||
});
|
||||
|
||||
var domPromise = new Promise(function(resolve, reject) {
|
||||
try {
|
||||
suite.camera.pauseRecording();
|
||||
resolve();
|
||||
} catch(e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all([domPromise, eventPromise]);
|
||||
}
|
||||
|
||||
function resumeRecording(p) {
|
||||
var eventPromise = new Promise(function(resolve, reject) {
|
||||
function onEvent(evt) {
|
||||
ok(evt.newState === 'Resumed', 'recorder state change event = ' + evt.newState);
|
||||
suite.camera.removeEventListener('recorderstatechange', onEvent);
|
||||
resolve();
|
||||
}
|
||||
suite.camera.addEventListener('recorderstatechange', onEvent);
|
||||
});
|
||||
|
||||
var domPromise = new Promise(function(resolve, reject) {
|
||||
try {
|
||||
suite.camera.resumeRecording();
|
||||
resolve();
|
||||
} catch(e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all([domPromise, eventPromise]);
|
||||
}
|
||||
|
||||
function stopRecording(p) {
|
||||
var eventPromise = new Promise(function(resolve, reject) {
|
||||
function onEvent(evt) {
|
||||
ok(evt.newState === 'Stopped', 'recorder state change event = ' + evt.newState);
|
||||
suite.camera.removeEventListener('recorderstatechange', onEvent);
|
||||
resolve();
|
||||
}
|
||||
suite.camera.addEventListener('recorderstatechange', onEvent);
|
||||
});
|
||||
|
||||
var domPromise = new Promise(function(resolve, reject) {
|
||||
try {
|
||||
suite.camera.stopRecording();
|
||||
resolve();
|
||||
} catch(e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all([domPromise, eventPromise]);
|
||||
}
|
||||
|
||||
return suite.getCamera(undefined, baseConfig)
|
||||
.then(cleanup, suite.rejectGetCamera)
|
||||
.then(startRecording)
|
||||
.then(pauseRecording, suite.rejectStartRecording)
|
||||
.then(resumeRecording, suite.rejectPauseRecording)
|
||||
.then(stopRecording, suite.rejectResumeRecording)
|
||||
.catch(suite.rejectStopRecording);
|
||||
});
|
||||
|
||||
// bug 1152500
|
||||
suite.test('interrupt-record', function() {
|
||||
function startRecording(p) {
|
||||
var startPromise = suite.camera.startRecording({}, storage, testFilePath);
|
||||
suite.camera.stopRecording();
|
||||
return startPromise;
|
||||
}
|
||||
|
||||
function rejectStartRecording(e) {
|
||||
ok(e.name === 'NS_ERROR_ABORT', 'onError called correctly on startRecording interrupted: ' + e);
|
||||
}
|
||||
|
||||
return suite.getCamera(undefined, baseConfig)
|
||||
.then(cleanup, suite.rejectGetCamera)
|
||||
.then(startRecording)
|
||||
.then(suite.expectedRejectStartRecording, rejectStartRecording);
|
||||
});
|
||||
|
||||
// bug 1152500
|
||||
suite.test('already-initiated-recording', function() {
|
||||
function startRecording(p) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var firstCall = false;
|
||||
var secondCall = false;
|
||||
|
||||
function end() {
|
||||
if (firstCall && secondCall) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
suite.camera.startRecording({}, storage, testFilePath).then(function(p) {
|
||||
ok(true, "First call to startRecording() succeeded");
|
||||
firstCall = true;
|
||||
end();
|
||||
}, function(e) {
|
||||
ok(false, "First call to startRecording() failed unexpectedly with: " + e);
|
||||
firstCall = true;
|
||||
end();
|
||||
});
|
||||
|
||||
suite.camera.startRecording({}, storage, testFilePath).then(function(p) {
|
||||
ok(false, "Second call to startRecording() succeeded unexpectedly");
|
||||
secondCall = true;
|
||||
end();
|
||||
}, function(e) {
|
||||
ok(e.name === 'NS_ERROR_IN_PROGRESS', "Second call to startRecording() failed expectedly with: " + e);
|
||||
secondCall = true;
|
||||
end();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return suite.getCamera(undefined, baseConfig)
|
||||
.then(cleanup, suite.rejectGetCamera)
|
||||
.then(startRecording);
|
||||
});
|
||||
|
||||
// bug 1152500
|
||||
suite.test('already-started-recording', function() {
|
||||
function startRecording(p) {
|
||||
return suite.camera.startRecording({}, storage, testFilePath);
|
||||
}
|
||||
|
||||
function startRecordingAgain(p) {
|
||||
return suite.camera.startRecording({}, storage, testFilePath);
|
||||
}
|
||||
|
||||
function rejectStartRecordingAgain(e) {
|
||||
ok(e.name === 'NS_ERROR_IN_PROGRESS', "Second call to startRecording() failed expectedly with: " + e);
|
||||
}
|
||||
|
||||
return suite.getCamera(undefined, baseConfig)
|
||||
.then(cleanup, suite.rejectGetCamera)
|
||||
.then(startRecording)
|
||||
.then(startRecordingAgain, suite.rejectStartRecording)
|
||||
.then(suite.expectedRejectStartRecording, rejectStartRecordingAgain)
|
||||
});
|
||||
|
||||
suite.setup()
|
||||
.then(suite.run);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,243 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for bug 975472</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="camera_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<video id="viewfinder" width="200" height="200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var suite = new CameraTestSuite();
|
||||
|
||||
function cameraRelease(p) {
|
||||
return suite.camera.release();
|
||||
}
|
||||
|
||||
suite.test('release-close-event', function() {
|
||||
// bug 1099390
|
||||
function release(p) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var gotCloseEvent = false;
|
||||
var gotReleasePromise = false;
|
||||
|
||||
var onClosed = function(e) {
|
||||
suite.camera.removeEventListener('close', onClosed);
|
||||
ok(!gotCloseEvent, "gotCloseEvent was " + gotCloseEvent);
|
||||
ok(e.reason === "HardwareReleased", "'close' event reason is: " + e.reason);
|
||||
gotCloseEvent = true;
|
||||
if (gotReleasePromise) {
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
|
||||
suite.camera.addEventListener('close', onClosed);
|
||||
|
||||
suite.camera.release().then(function(p) {
|
||||
ok(true, "released camera");
|
||||
gotReleasePromise = true;
|
||||
if (gotCloseEvent) {
|
||||
resolve();
|
||||
}
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(release, suite.rejectGetCamera);
|
||||
});
|
||||
|
||||
suite.test('release-after-release', function() {
|
||||
return suite.getCamera()
|
||||
.then(cameraRelease, suite.rejectGetCamera)
|
||||
.then(cameraRelease, suite.rejectRelease)
|
||||
.catch(suite.rejectRelease);
|
||||
});
|
||||
|
||||
suite.test('set-picture-size-after-release', function() {
|
||||
function setPictureSize(p) {
|
||||
try {
|
||||
suite.camera.setPictureSize({ width: 0, height: 0 });
|
||||
ok(false, "SetPictureSize() should have failed");
|
||||
} catch(e) {
|
||||
ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
|
||||
"setPictureSize() failed with: " + e.name);
|
||||
}
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(cameraRelease, suite.rejectGetCamera)
|
||||
.then(setPictureSize, suite.rejectRelease);
|
||||
});
|
||||
|
||||
suite.test('set-thumbnail-size-after-release', function() {
|
||||
function setThumbnailSize(p) {
|
||||
try {
|
||||
suite.camera.setThumbnailSize({ width: 0, height: 0 });
|
||||
ok(false, "SetThumbnailSize() should have failed");
|
||||
} catch(e) {
|
||||
ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
|
||||
"setThumbnailSize() failed with: " + e.name);
|
||||
}
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(cameraRelease, suite.rejectGetCamera)
|
||||
.then(setThumbnailSize, suite.rejectRelease);
|
||||
});
|
||||
|
||||
suite.test('get-sensor-angle-after-release', function() {
|
||||
function getSensorAngle(p) {
|
||||
ok(suite.camera.sensorAngle == 0, "camera.sensorAngle = " + suite.camera.sensorAngle);
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(cameraRelease, suite.rejectGetCamera)
|
||||
.then(getSensorAngle, suite.rejectRelease);
|
||||
});
|
||||
|
||||
suite.test('resume-preview-after-release', function() {
|
||||
function resumePreview(p) {
|
||||
try {
|
||||
suite.camera.resumePreview();
|
||||
ok(false, "resumePreview() should have failed");
|
||||
} catch(e) {
|
||||
ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
|
||||
"resumePreview() failed with: " + e.name);
|
||||
}
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(cameraRelease, suite.rejectGetCamera)
|
||||
.then(resumePreview, suite.rejectRelease);
|
||||
});
|
||||
|
||||
suite.test('auto-focus-after-release', function() {
|
||||
function autoFocus(p) {
|
||||
return suite.camera.autoFocus();
|
||||
}
|
||||
|
||||
function rejectAutoFocus(error) {
|
||||
ok(error.name === "NS_ERROR_NOT_AVAILABLE",
|
||||
"autoFocus() failed with: " + error.name);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(cameraRelease, suite.rejectGetCamera)
|
||||
.then(autoFocus, suite.rejectRelease)
|
||||
.then(suite.expectedRejectAutoFocus, rejectAutoFocus);
|
||||
});
|
||||
|
||||
suite.test('take-picture-after-release', function() {
|
||||
function takePicture(p) {
|
||||
return suite.camera.takePicture(null);
|
||||
}
|
||||
|
||||
function rejectTakePicture(error) {
|
||||
ok(error.name === "NS_ERROR_NOT_AVAILABLE",
|
||||
"takePicture() failed with: " + error.name);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(cameraRelease, suite.rejectGetCamera)
|
||||
.then(takePicture, suite.rejectRelease)
|
||||
.then(suite.expectedRejectTakePicture, rejectTakePicture);
|
||||
});
|
||||
|
||||
suite.test('start-recording-after-release', function() {
|
||||
function startRecording(p) {
|
||||
return suite.camera.startRecording(
|
||||
{
|
||||
profile: 'high',
|
||||
rotation: 0
|
||||
},
|
||||
navigator.getDeviceStorage('videos'),
|
||||
'bug975472.mp4'
|
||||
);
|
||||
}
|
||||
|
||||
function rejectStartRecording(error) {
|
||||
ok(error.name === "NS_ERROR_NOT_AVAILABLE",
|
||||
"takePicture() failed with: " + error.name);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(cameraRelease, suite.rejectGetCamera)
|
||||
.then(startRecording, suite.rejectRelease)
|
||||
.then(suite.expectedRejectStartRecording, rejectStartRecording);
|
||||
});
|
||||
|
||||
suite.test('stop-recording-after-release', function() {
|
||||
function stopRecording(p) {
|
||||
try {
|
||||
suite.camera.stopRecording();
|
||||
ok(false, "stopRecording() should have failed");
|
||||
} catch(e) {
|
||||
ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
|
||||
"stopRecording() failed with: " + e.name);
|
||||
}
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(cameraRelease, suite.rejectGetCamera)
|
||||
.then(stopRecording, suite.rejectRelease);
|
||||
});
|
||||
|
||||
suite.test('face-detection-after-release', function() {
|
||||
function startFaceDetection(p) {
|
||||
try {
|
||||
suite.camera.startFaceDetection();
|
||||
ok(false, "startFaceDetection() should have failed");
|
||||
} catch(e) {
|
||||
ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
|
||||
"startFaceDetection() failed with: " + e.name);
|
||||
}
|
||||
}
|
||||
|
||||
function stopFaceDetection(p) {
|
||||
try {
|
||||
suite.camera.stopFaceDetection();
|
||||
ok(false, "stopFaceDetection() should have failed");
|
||||
} catch(e) {
|
||||
ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
|
||||
"stopFaceDetection() failed with: " + e.name);
|
||||
}
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(cameraRelease, suite.rejectGetCamera)
|
||||
.then(startFaceDetection, suite.rejectRelease)
|
||||
.then(stopFaceDetection);
|
||||
});
|
||||
|
||||
suite.test('set-configuration-after-release', function() {
|
||||
function configure(p) {
|
||||
return suite.camera.setConfiguration(null);
|
||||
}
|
||||
|
||||
function rejectConfigure(error) {
|
||||
ok(error.name === "NS_ERROR_NOT_AVAILABLE",
|
||||
"takePicture() failed with: " + error.name);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return suite.getCamera()
|
||||
.then(cameraRelease, suite.rejectGetCamera)
|
||||
.then(configure, suite.rejectRelease)
|
||||
.then(suite.expectedRejectConfigure, rejectConfigure);
|
||||
});
|
||||
|
||||
suite.setup()
|
||||
.then(suite.run);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче