import changes from `dev' branch of rmottola/Arctic-Fox:

- As suggested in PR 101, use OpenBSD assembler files. Update the NetBSD on them and use .S instead of .s, to indicate files to process (or preprocessor would fail on comments). (6a17dbacc3)
- Bug 1229769 - Expose Promise interface to WorkerDebugger #ifdef SPIDERMONKEY_PROMISE;r=bz (da9e838c23)
- Bug 1155969 - Make xpt.py flake8 compliant. r=ted (84f8eab5a3)
- Bug 977464 - Always relink XPT files for all changed XPIDL interfaces without requiring the IID to be revved; r=khuey (9b22512c41)
- Bug 977464 follow-up: Fix the indentation to use 4 spaces (bd68a8ebc3)
- Bug 1240053 - Consider the order of methods, their params, and constant important when comparing XPT interfaces to decide whether to relink XPT files; r=khuey (b9253dd183)
- Bug 1264377. Get rid of some unnecessary custom JSClass hook functions in xpconnect sandboxes and DOM simple globals. r=bholley (60950b416b)
- Bug 1258496 - Purge message manager cached scripts on 'message-manager-flush-caches' notification. r=smaug (028b229d02)
- Bug 1251298 - Null out |*idp| when necessary in DoInterfaceDescriptor. r=khuey. (dbdd15dae8)
- Bug 659625 - part1: implement Console::clear in dom/base/Console.cpp;r=baku (17c4b33789)
- Bug 659625 - part2: implement console.clear in devtools webconsole;r=bgrins (b72c6173ee)
- Bug 1248507 - p5. DecoderDoctorDiagnostics implementation - r=jya,bz (22f68130af)
- Bug 1248507 - p6. Minimal notification definition - r=bz (02f3eeb2f9)
- Bug 1248507 - p7. Notify decoder-doctor-notification listeners - r=jya,bz (2c2eb33388)
- Bug 1248507 - p8. FFMpeg checks: Console message - r=bz (50a993c143)
- Bug 1248507 - p9. FFMpeg checks: Notification definition - r=bz (0bcdcc090c)
- Bug 1248507 - p10. Detect and report when FFMpeg/Linux fails to load - r=jya (28137efda0)
- Bug 1190939: Decode VP9 4:4:4 properly. r=jya (98508bb48b)
- Bug 1232911 - [1.2] Allow to test for specific VPX MIME type version. r=cpearce (1b53e02981)
- Bug 1251887 - Add break to unintentional switch fallthrough in GfxInfoBase.cpp to fix -Wimplicit-fallthrough warning. r=milan (9969a7bec7)
- Bug 1232911 - [2.2] Add VPX decoding blocking support. r=snorp (fa860a9d4d)
- Bug 1249777: Added support for 10.11 in the blocklisting code as well. r=mstange (479f629083)
- Bug 1242084 - Fix GfxInfoBase nsStringBuffer leak. r=dvander (87b38ee72d)
- Bug 1222201: Only use container calculated dimensions. r=cpearce (693ebdf450)
- Bug 1190240 - Cannot compile WMFVideoMFTManager.cpp using Windows 10 SDK. r=cpearce (8ee2e315f5)
- Bug 1248496 - Enable D3D11 DXVA. r=ajones (a79df0baf2)
- Bug 1248496 - Show which DXVA API is being used in about:support. r=jya (1f6b1f0c8e)
- Bug 1257028 - Fallback to d3d9 decoding if d3d11 fails. r=cpearce (5ad7c159f1)
- Bug 1232045 - WebMDemuxer handles resolution changes. r=jya (18bdc79b1c)
- Bug 1243538: P1. Make MediaInfo::mImage an nsIntSize again and introduce a mImageRect member. r=mattwoodrow (a446cca01e)
- Bug 1243538: P2. Add convenience VideoInfo::ScaledImageRect. r=mattwoodrow (657e675b72)
- Bug 1243538: P3. Adjust libvpx decoder to allow different decoding size from metadata. r=mattwoodrow (50949ce02d)
- Bug 1243538: P4. Adjust ffvpx decoder to allow different decoding size from metadata. r=mattwoodrow (392c8939f5)
- Bug 1243538: P5. Adjust wmf decoder to allow different decoding size from metadata. r=cpearce (f50940564f)
- Bug 1239611 - Remove GonkNativeWindowClient r=nical (2c7ccb54a4)
- Bug 1170589 - Force decoder to use all allocated buffers. r=bwu (7e5c02e48a)
- Bug 1222923 - Enable MOZ_FMP4 on gonk L r=jolin (c04ad6ff55)
- Bug 1178214 - Return INIT_ERROR when video resolution exceeds hw codec capability. r=sotaro (bf3c45cde1)
- Bug 1147304 - Send codec specific data for MPEG4 codec type only. r=jya (ca48d110f4)
- Bug 1243538: P6. Adjust gonk decoder to allow different decoding size from metadata. r=alfredo (257e017762)
- Bug 1243538: [webm] P7. Let the decoder handle picture resizing. r=SingingTree (32dc4a5aac)
- Bug 1262727: [webm] Ensure first frame returned after seek is a keyframe. r=kinetik (f16140852a)
- Bug 1246536: [webm] Only use discard padding information on last packet. r=kinetik (0bac4f8855)
- Bug 1266013: Fix Firefox OS compile errors. r=gerald (f021717287)
- cleanup (390cdec6ee)
- Bug 1264991: Don't construct invalid channel configuration. r=gerald (661828e8b8)
- Bug 1265093: Fix CID 1358648. r=gerald (55468c1261)
- Bug 1262659 - Report HTTP Live Streaming playback requests. r=cpearce,bsmedberg (96b8cd2810)
- Bug 1265400 - Use unsigned long for AudioBuffer length and numberOfChannels; r=smaug (f74f27ea4e)
This commit is contained in:
2024-05-18 23:12:44 +08:00
parent 76c2cfb906
commit faafb5fd9d
63 changed files with 1250 additions and 4012 deletions
+6
View File
@@ -1038,6 +1038,7 @@ METHOD(Error, "error")
METHOD(Exception, "exception")
METHOD(Debug, "debug")
METHOD(Table, "table")
METHOD(Clear, "clear")
void
Console::Trace(JSContext* aCx)
@@ -1533,6 +1534,11 @@ Console::ProcessCallData(JSContext* aCx, ConsoleCallData* aData,
innerID.AppendInt(aData->mInnerIDNumber);
}
if (aData->mMethodName == MethodClear) {
nsresult rv = mStorage->ClearEvents(innerID);
NS_WARN_IF(NS_FAILED(rv));
}
if (NS_FAILED(mStorage->RecordEvent(innerID, outerID, eventValue))) {
NS_WARNING("Failed to record a console event.");
}
+5 -1
View File
@@ -115,6 +115,9 @@ public:
void
Count(JSContext* aCx, const Sequence<JS::Value>& aData);
void
Clear(JSContext* aCx, const Sequence<JS::Value>& aData);
void
NoopMethod();
@@ -156,7 +159,8 @@ private:
MethodTimeEnd,
MethodTimeStamp,
MethodAssert,
MethodCount
MethodCount,
MethodClear
};
void
+10 -1
View File
@@ -1657,7 +1657,7 @@ nsMessageManagerScriptExecutor::DidCreateGlobal()
// static
void
nsMessageManagerScriptExecutor::Shutdown()
nsMessageManagerScriptExecutor::PurgeCache()
{
if (sCachedScripts) {
NS_ASSERTION(sCachedScripts != nullptr, "Need cached scripts");
@@ -1665,6 +1665,15 @@ nsMessageManagerScriptExecutor::Shutdown()
delete iter.Data();
iter.Remove();
}
}
}
// static
void
nsMessageManagerScriptExecutor::Shutdown()
{
if (sCachedScripts) {
PurgeCache();
delete sCachedScripts;
sCachedScripts = nullptr;
+9 -2
View File
@@ -377,6 +377,7 @@ struct nsMessageManagerScriptHolder
class nsMessageManagerScriptExecutor
{
public:
static void PurgeCache();
static void Shutdown();
already_AddRefed<nsIXPConnectJSObjectHolder> GetGlobal()
{
@@ -417,15 +418,21 @@ class nsScriptCacheCleaner final : public nsIObserver
nsScriptCacheCleaner()
{
nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
if (obsSvc)
if (obsSvc) {
obsSvc->AddObserver(this, "message-manager-flush-caches", false);
obsSvc->AddObserver(this, "xpcom-shutdown", false);
}
}
NS_IMETHODIMP Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *aData) override
{
nsMessageManagerScriptExecutor::Shutdown();
if (strcmp("message-manager-flush-caches", aTopic) == 0) {
nsMessageManagerScriptExecutor::PurgeCache();
} else if (strcmp("xpcom-shutdown", aTopic) == 0) {
nsMessageManagerScriptExecutor::Shutdown();
}
return NS_OK;
}
};
+1
View File
@@ -275,6 +275,7 @@ GK_ATOM(decimalFormat, "decimal-format")
GK_ATOM(decimalSeparator, "decimal-separator")
GK_ATOM(deck, "deck")
GK_ATOM(declare, "declare")
GK_ATOM(decoderDoctor, "decoder-doctor")
GK_ATOM(decrement, "decrement")
GK_ATOM(_default, "default")
GK_ATOM(headerDefaultStyle, "default-style")
+1
View File
@@ -621,6 +621,7 @@ skip-if = toolkit == 'android' #bug 687032
[test_bug647518.html]
[test_bug650001.html]
[test_bug656283.html]
[test_bug659625.html]
[test_bug664916.html]
[test_bug666604.html]
skip-if = buildapp == 'b2g' # b2g(dom.disable_open_during_load not implemented in b2g) b2g-debug(dom.disable_open_during_load not implemented in b2g) b2g-desktop(dom.disable_open_during_load not implemented in b2g)
+92
View File
@@ -0,0 +1,92 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=659625
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 659625</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.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=659625">Mozilla Bug 659625</a>
<script type="application/javascript">
const { Cc, Ci } = SpecialPowers;
let consoleStorage = Cc["@mozilla.org/consoleAPI-storage;1"];
let storage = consoleStorage.getService(Ci.nsIConsoleAPIStorage);
let clearAndCheckStorage = () => {
console.clear();
ok(storage.getEvents().length === 1,
"Only one event remains in consoleAPIStorage");
ok(storage.getEvents()[0].level === "clear",
"Remaining event has level 'clear'");
}
storage.clearEvents();
ok(storage.getEvents().length === 0,
"Console is empty when test is starting");
clearAndCheckStorage();
console.log("log");
console.debug("debug");
console.warn("warn");
console.error("error");
console.exception("exception");
ok(storage.getEvents().length === 6,
"5 new console events have been registered for logging variants");
clearAndCheckStorage();
console.trace();
ok(storage.getEvents().length === 2,
"1 new console event registered for trace");
clearAndCheckStorage();
console.dir({});
ok(storage.getEvents().length === 2,
"1 new console event registered for dir");
clearAndCheckStorage();
console.count("count-label");
console.count("count-label");
ok(storage.getEvents().length === 3,
"2 new console events registered for 2 count calls");
clearAndCheckStorage();
console.group("group-label")
console.log("group-log");
ok(storage.getEvents().length === 3,
"2 new console events registered for group + log");
clearAndCheckStorage();
console.groupCollapsed("group-collapsed")
console.log("group-collapsed-log");
ok(storage.getEvents().length === 3,
"2 new console events registered for groupCollapsed + log");
clearAndCheckStorage();
console.group("closed-group-label")
console.log("group-log");
console.groupEnd()
ok(storage.getEvents().length === 4,
"3 new console events registered for group/groupEnd");
clearAndCheckStorage();
console.time("time-label");
console.timeEnd();
ok(storage.getEvents().length === 3,
"2 new console events registered for time/timeEnd");
clearAndCheckStorage();
console.timeStamp("timestamp-label");
ok(storage.getEvents().length === 2,
"1 new console event registered for timeStamp");
clearAndCheckStorage();
// Check that console.clear() clears previous clear messages
clearAndCheckStorage();
</script>
</body>
</html>
+3 -16
View File
@@ -41,19 +41,6 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SimpleGlobalObject)
NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
NS_INTERFACE_MAP_END
static bool
SimpleGlobal_enumerate(JSContext *cx, JS::Handle<JSObject *> obj)
{
return JS_EnumerateStandardClasses(cx, obj);
}
static bool
SimpleGlobal_resolve(JSContext *cx, JS::Handle<JSObject *> obj,
JS::Handle<jsid> id, bool *resolvedp)
{
return JS_ResolveStandardClass(cx, obj, id, resolvedp);
}
static void
SimpleGlobal_finalize(js::FreeOp *fop, JSObject *obj)
{
@@ -75,9 +62,9 @@ static const js::ClassOps SimpleGlobalClassOps = {
nullptr,
nullptr,
nullptr,
SimpleGlobal_enumerate,
SimpleGlobal_resolve,
nullptr,
JS_EnumerateStandardClasses,
JS_ResolveStandardClass,
JS_MayResolveStandardClass,
SimpleGlobal_finalize,
nullptr,
nullptr,
@@ -112,6 +112,8 @@ MediaLoadDecodeError=Media resource %S could not be decoded.
# LOCALIZATION NOTE: %S is a comma-separated list of codecs (e.g. 'video/mp4, video/webm')
MediaCannotPlayNoDecoders=Cannot play media. No decoders for requested formats: %S
# LOCALIZATION NOTE: %S is a comma-separated list of codecs (e.g. 'video/mp4, video/webm')
MediaPlatformDecoderNotFound=The video on this page can't be played. Your system may not have the required video codecs for: %S
# LOCALIZATION NOTE: %S is a comma-separated list of codecs (e.g. 'video/mp4, video/webm')
MediaNoDecoders=No decoders for some of the requested formats: %S
# LOCALIZATION NOTE: Do not translate "MediaRecorder".
MediaRecorderMultiTracksNotSupported=MediaRecorder does not support recording multiple tracks of the same type at this time.
+1 -1
View File
@@ -35,7 +35,7 @@ ADTSDecoder::IsEnabled()
PDMFactory::Init();
RefPtr<PDMFactory> platform = new PDMFactory();
return (platform && platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm"),
/* DecoderDoctorDiagnostics* */ nullptr));
/* DecoderDoctorDiagnostics* */ nullptr));
}
/* static */ bool
+403 -3
View File
@@ -6,30 +6,430 @@
#include "DecoderDoctorDiagnostics.h"
#include "mozilla/dom/DecoderDoctorNotificationBinding.h"
#include "mozilla/Logging.h"
#include "nsGkAtoms.h"
#include "nsIDocument.h"
#include "nsIObserverService.h"
#include "nsITimer.h"
#include "nsIWeakReference.h"
static mozilla::LazyLogModule sDecoderDoctorLog("DecoderDoctor");
#define DD_LOG(level, arg, ...) MOZ_LOG(sDecoderDoctorLog, level, (arg, ##__VA_ARGS__))
#define DD_DEBUG(arg, ...) DD_LOG(mozilla::LogLevel::Debug, arg, ##__VA_ARGS__)
#define DD_INFO(arg, ...) DD_LOG(mozilla::LogLevel::Info, arg, ##__VA_ARGS__)
#define DD_WARN(arg, ...) DD_LOG(mozilla::LogLevel::Warning, arg, ##__VA_ARGS__)
namespace mozilla
{
// Class that collects a sequence of diagnostics from the same document over a
// small period of time, in order to provide a synthesized analysis.
//
// Referenced by the document through a nsINode property, mTimer, and
// inter-task captures.
// When notified that the document is dead, or when the timer expires but
// nothing new happened, StopWatching() will remove the document property and
// timer (if present), so no more work will happen and the watcher will be
// destroyed once all references are gone.
class DecoderDoctorDocumentWatcher : public nsITimerCallback
{
public:
static RefPtr<DecoderDoctorDocumentWatcher>
RetrieveOrCreate(nsIDocument* aDocument);
NS_DECL_ISUPPORTS
NS_DECL_NSITIMERCALLBACK
void AddDiagnostics(const nsAString& aFormat,
const char* aCallSite,
DecoderDoctorDiagnostics&& aDiagnostics);
private:
explicit DecoderDoctorDocumentWatcher(nsIDocument* aDocument);
virtual ~DecoderDoctorDocumentWatcher();
// This will prevent further work from happening, watcher will deregister
// itself from document (if requested) and cancel any timer, and soon die.
void StopWatching(bool aRemoveProperty);
// Remove property from document; will call DestroyPropertyCallback.
void RemovePropertyFromDocument();
// Callback for property destructor, will be automatically called when the
// document (in aObject) is being destroyed.
static void DestroyPropertyCallback(void* aObject,
nsIAtom* aPropertyName,
void* aPropertyValue,
void* aData);
static const uint32_t sAnalysisPeriod_ms = 1000;
void EnsureTimerIsStarted();
void ReportAnalysis(dom::DecoderDoctorNotificationType aNotificationType,
const char* aReportStringId,
const nsAString& aFormats);
void SynthesizeAnalysis();
// Raw pointer to an nsIDocument.
// Must be non-null during construction.
// Nulled when we want to stop watching, because either:
// 1. The document has been destroyed (notified through
// DestroyPropertyCallback).
// 2. We have not received new diagnostic information within a short time
// period, so we just stop watching.
// Once nulled, no more actual work will happen, and the watcher will be
// destroyed soon.
nsIDocument* mDocument;
struct Diagnostics
{
Diagnostics(DecoderDoctorDiagnostics&& aDiagnostics,
const nsAString& aFormat,
const char* aCallSite)
: mDecoderDoctorDiagnostics(Move(aDiagnostics))
, mFormat(aFormat)
, mCallSite(aCallSite)
{}
Diagnostics(const Diagnostics&) = delete;
Diagnostics(Diagnostics&& aOther)
: mDecoderDoctorDiagnostics(Move(aOther.mDecoderDoctorDiagnostics))
, mFormat(Move(aOther.mFormat))
, mCallSite(Move(aOther.mCallSite))
{}
const DecoderDoctorDiagnostics mDecoderDoctorDiagnostics;
const nsString mFormat;
const nsCString mCallSite;
};
typedef nsTArray<Diagnostics> DiagnosticsSequence;
DiagnosticsSequence mDiagnosticsSequence;
nsCOMPtr<nsITimer> mTimer; // Keep timer alive until we run.
DiagnosticsSequence::size_type mDiagnosticsHandled = 0;
};
NS_IMPL_ISUPPORTS(DecoderDoctorDocumentWatcher, nsITimerCallback)
// static
RefPtr<DecoderDoctorDocumentWatcher>
DecoderDoctorDocumentWatcher::RetrieveOrCreate(nsIDocument* aDocument)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aDocument);
RefPtr<DecoderDoctorDocumentWatcher> watcher =
static_cast<DecoderDoctorDocumentWatcher*>(
aDocument->GetProperty(nsGkAtoms::decoderDoctor));
if (!watcher) {
watcher = new DecoderDoctorDocumentWatcher(aDocument);
if (NS_WARN_IF(NS_FAILED(
aDocument->SetProperty(nsGkAtoms::decoderDoctor,
watcher.get(),
DestroyPropertyCallback,
/*transfer*/ false)))) {
DD_WARN("DecoderDoctorDocumentWatcher::RetrieveOrCreate(doc=%p) - Could not set property in document, will destroy new watcher[%p]",
aDocument, watcher.get());
return nullptr;
}
// Document owns watcher through this property.
// Released in DestroyPropertyCallback().
NS_ADDREF(watcher.get());
}
return watcher;
}
DecoderDoctorDocumentWatcher::DecoderDoctorDocumentWatcher(nsIDocument* aDocument)
: mDocument(aDocument)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mDocument);
DD_DEBUG("DecoderDoctorDocumentWatcher[%p]::DecoderDoctorDocumentWatcher(doc=%p)",
this, mDocument);
}
DecoderDoctorDocumentWatcher::~DecoderDoctorDocumentWatcher()
{
MOZ_ASSERT(NS_IsMainThread());
DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p <- expect 0]::~DecoderDoctorDocumentWatcher()",
this, mDocument);
// mDocument should have been reset through StopWatching()!
MOZ_ASSERT(!mDocument);
}
void
DecoderDoctorDocumentWatcher::RemovePropertyFromDocument()
{
MOZ_ASSERT(NS_IsMainThread());
DecoderDoctorDocumentWatcher* watcher =
static_cast<DecoderDoctorDocumentWatcher*>(
mDocument->GetProperty(nsGkAtoms::decoderDoctor));
if (!watcher) {
return;
}
DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::RemovePropertyFromDocument()\n",
watcher, watcher->mDocument);
// This will remove the property and call our DestroyPropertyCallback.
mDocument->DeleteProperty(nsGkAtoms::decoderDoctor);
}
// Callback for property destructors. |aObject| is the object
// the property is being removed for, |aPropertyName| is the property
// being removed, |aPropertyValue| is the value of the property, and |aData|
// is the opaque destructor data that was passed to SetProperty().
// static
void
DecoderDoctorDocumentWatcher::DestroyPropertyCallback(void* aObject,
nsIAtom* aPropertyName,
void* aPropertyValue,
void*)
{
MOZ_ASSERT(NS_IsMainThread());
#ifdef DEBUG
nsIDocument* document = static_cast<nsIDocument*>(aObject);
#endif
MOZ_ASSERT(aPropertyName == nsGkAtoms::decoderDoctor);
DecoderDoctorDocumentWatcher* watcher =
static_cast<DecoderDoctorDocumentWatcher*>(aPropertyValue);
MOZ_ASSERT(watcher);
MOZ_ASSERT(watcher->mDocument == document);
DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::DestroyPropertyCallback()\n",
watcher, watcher->mDocument);
// 'false': StopWatching should not try and remove the property.
watcher->StopWatching(false);
NS_RELEASE(watcher);
}
void
DecoderDoctorDocumentWatcher::StopWatching(bool aRemoveProperty)
{
MOZ_ASSERT(NS_IsMainThread());
// StopWatching() shouldn't be called twice.
MOZ_ASSERT(mDocument);
if (aRemoveProperty) {
RemovePropertyFromDocument();
}
// Forget document now, this will prevent more work from being started.
mDocument = nullptr;
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
}
void
DecoderDoctorDocumentWatcher::EnsureTimerIsStarted()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mTimer) {
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
if (NS_WARN_IF(!mTimer)) {
return;
}
if (NS_WARN_IF(NS_FAILED(
mTimer->InitWithCallback(
this, sAnalysisPeriod_ms, nsITimer::TYPE_ONE_SHOT)))) {
mTimer = nullptr;
}
}
}
static void
DispatchNotification(nsISupports* aSubject,
dom::DecoderDoctorNotificationType aNotificationType,
const nsAString& aFormats)
{
if (!aSubject) {
return;
}
dom::DecoderDoctorNotification data;
data.mType = aNotificationType;
if (!aFormats.IsEmpty()) {
data.mFormats.Construct(aFormats);
}
nsAutoString json;
data.ToJSON(json);
if (json.IsEmpty()) {
DD_WARN("DecoderDoctorDiagnostics/DispatchEvent() - Could not create json for dispatch");
// No point in dispatching this notification without data, the front-end
// wouldn't know what to display.
return;
}
DD_DEBUG("DecoderDoctorDiagnostics/DispatchEvent() %s", NS_ConvertUTF16toUTF8(json).get());
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
obs->NotifyObservers(aSubject, "decoder-doctor-notification", json.get());
}
}
void
DecoderDoctorDocumentWatcher::ReportAnalysis(
dom::DecoderDoctorNotificationType aNotificationType,
const char* aReportStringId,
const nsAString& aFormats)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mDocument) {
return;
}
const char16_t* params[] = { aFormats.Data() };
DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::ReportAnalysis() ReportToConsole - aMsg='%s' params[0]='%s'",
this, mDocument, aReportStringId,
NS_ConvertUTF16toUTF8(params[0]).get());
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("Media"),
mDocument,
nsContentUtils::eDOM_PROPERTIES,
aReportStringId,
params,
ArrayLength(params));
// For now, disable all front-end notifications by default.
// TODO: Future bugs will use finer-grained filtering instead.
if (Preferences::GetBool("media.decoderdoctor.enable-notification-bar", false)) {
DispatchNotification(
mDocument->GetInnerWindow(), aNotificationType, aFormats);
}
}
void
DecoderDoctorDocumentWatcher::SynthesizeAnalysis()
{
MOZ_ASSERT(NS_IsMainThread());
bool canPlay = false;
#if defined(MOZ_FFMPEG)
bool PlatformDecoderNeeded = false;
#endif
nsAutoString formats;
for (auto& diag : mDiagnosticsSequence) {
if (diag.mDecoderDoctorDiagnostics.CanPlay()) {
canPlay = true;
} else {
#if defined(MOZ_FFMPEG)
if (diag.mDecoderDoctorDiagnostics.DidFFmpegFailToLoad()) {
PlatformDecoderNeeded = true;
}
#endif
if (!formats.IsEmpty()) {
formats += NS_LITERAL_STRING(", ");
}
formats += diag.mFormat;
}
}
if (!canPlay) {
#if defined(MOZ_FFMPEG)
if (PlatformDecoderNeeded) {
DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::Notify() - formats: %s -> Cannot play media because platform decoder was not found",
this, mDocument, NS_ConvertUTF16toUTF8(formats).get());
ReportAnalysis(dom::DecoderDoctorNotificationType::Platform_decoder_not_found,
"MediaPlatformDecoderNotFound", formats);
} else
#endif
{
DD_WARN("DecoderDoctorDocumentWatcher[%p, doc=%p]::Notify() - Cannot play media, formats: %s",
this, mDocument, NS_ConvertUTF16toUTF8(formats).get());
ReportAnalysis(dom::DecoderDoctorNotificationType::Cannot_play,
"MediaCannotPlayNoDecoders", formats);
}
} else if (!formats.IsEmpty()) {
DD_INFO("DecoderDoctorDocumentWatcher[%p, doc=%p]::Notify() - Can play media, but no decoders for some requested formats: %s",
this, mDocument, NS_ConvertUTF16toUTF8(formats).get());
if (Preferences::GetBool("media.decoderdoctor.verbose", false)) {
ReportAnalysis(
dom::DecoderDoctorNotificationType::Can_play_but_some_missing_decoders,
"MediaNoDecoders", formats);
}
} else {
DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::Notify() - Can play media, decoders available for all requested formats",
this, mDocument);
}
}
void
DecoderDoctorDocumentWatcher::AddDiagnostics(const nsAString& aFormat,
const char* aCallSite,
DecoderDoctorDiagnostics&& aDiagnostics)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mDocument) {
return;
}
DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::AddDiagnostics(format='%s', call site '%s', can play=%d, platform lib failed to load=%d)",
this, mDocument, NS_ConvertUTF16toUTF8(aFormat).get(),
aCallSite, aDiagnostics.CanPlay(), aDiagnostics.DidFFmpegFailToLoad());
mDiagnosticsSequence.AppendElement(
Diagnostics(Move(aDiagnostics), aFormat, aCallSite));
EnsureTimerIsStarted();
}
NS_IMETHODIMP
DecoderDoctorDocumentWatcher::Notify(nsITimer* timer)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(timer == mTimer);
// Forget timer. (Assuming timer keeps itself and us alive during this call.)
mTimer = nullptr;
if (!mDocument) {
return NS_OK;
}
if (mDiagnosticsSequence.Length() > mDiagnosticsHandled) {
// We have new diagnostic data.
mDiagnosticsHandled = mDiagnosticsSequence.Length();
SynthesizeAnalysis();
// Restart timer, to redo analysis or stop watching this document,
// depending on whether anything new happens.
EnsureTimerIsStarted();
} else {
DD_DEBUG("DecoderDoctorDocumentWatcher[%p, doc=%p]::Notify() - No new diagnostics to analyze -> Stop watching",
this, mDocument);
// Stop watching this document, we don't expect more diagnostics for now.
// If more diagnostics come in, we'll treat them as another burst, separately.
// 'true' to remove the property from the document.
StopWatching(true);
}
return NS_OK;
}
void
DecoderDoctorDiagnostics::StoreDiagnostics(nsIDocument* aDocument,
const nsAString& aFormat,
const char* aCallSite)
{
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!aDocument)) {
DD_WARN("DecoderDoctorDiagnostics[%p]::StoreDiagnostics(nsIDocument* aDocument=nullptr, format='%s', call site '%s')",
this, NS_ConvertUTF16toUTF8(aFormat).get(), aCallSite);
return;
}
// TODO: Actually analyze data.
DD_DEBUG("DecoderDoctorDiagnostics[%p]::StoreDiagnostics(nsIDocument* aDocument=%p, format='%s', call site '%s')",
this, aDocument, NS_ConvertUTF16toUTF8(aFormat).get(), aCallSite);
RefPtr<DecoderDoctorDocumentWatcher> watcher =
DecoderDoctorDocumentWatcher::RetrieveOrCreate(aDocument);
if (NS_WARN_IF(!watcher)) {
DD_WARN("DecoderDoctorDiagnostics[%p]::StoreDiagnostics(nsIDocument* aDocument=nullptr, format='%s', call site '%s') - Could not create document watcher",
this, NS_ConvertUTF16toUTF8(aFormat).get(), aCallSite);
return;
}
// StoreDiagnostics should only be called once, after all data is available,
// so it is safe to Move() from this object.
watcher->AddDiagnostics(aFormat, aCallSite, Move(*this));
}
} // namespace mozilla
+5
View File
@@ -44,9 +44,14 @@ public:
void SetCanPlay() { mCanPlay = true; }
bool CanPlay() const { return mCanPlay; }
void SetFFmpegFailedToLoad() { mFFmpegFailedToLoad = true; }
bool DidFFmpegFailToLoad() const { return mFFmpegFailedToLoad; }
private:
// True if there is at least one decoder that can play the media.
bool mCanPlay = false;
bool mFFmpegFailedToLoad = false;
};
} // namespace mozilla
+25
View File
@@ -9,6 +9,7 @@
#include "nsCharSeparatedTokenizer.h"
#include "nsMimeTypes.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "OggDecoder.h"
#include "OggReader.h"
@@ -176,6 +177,21 @@ DecoderTraits::IsWebMAudioType(const nsACString& aType)
return aType.EqualsASCII("audio/webm");
}
static char const *const gHttpLiveStreamingTypes[] = {
// For m3u8.
// https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-10
"application/vnd.apple.mpegurl",
// Some sites serve these as the informal m3u type.
"audio/x-mpegurl",
nullptr
};
static bool
IsHttpLiveStreamingType(const nsACString& aType)
{
return CodecListContains(gHttpLiveStreamingTypes, aType);
}
#ifdef MOZ_OMX_DECODER
static const char* const gOmxTypes[] = {
"audio/mpeg",
@@ -481,6 +497,10 @@ DecoderTraits::CanHandleMediaType(const char* aMIMEType,
{
MOZ_ASSERT(NS_IsMainThread());
if (IsHttpLiveStreamingType(nsDependentCString(aMIMEType))) {
Telemetry::Accumulate(Telemetry::MEDIA_HLS_CANPLAY_REQUESTED, true);
}
if (aHaveRequestedCodecs) {
CanPlayStatus result = CanHandleCodecsType(aMIMEType,
aRequestedCodecs,
@@ -625,6 +645,11 @@ InstantiateDecoder(const nsACString& aType,
}
#endif
if (IsHttpLiveStreamingType(aType)) {
// We don't have an HLS decoder.
Telemetry::Accumulate(Telemetry::MEDIA_HLS_DECODER_SUCCESS, false);
}
return nullptr;
}
+50 -3
View File
@@ -190,10 +190,11 @@ public:
EmptyString(), EmptyString(), true, 2)
, mDisplay(nsIntSize(aWidth, aHeight))
, mStereoMode(StereoMode::MONO)
, mImage(nsIntRect(0, 0, aWidth, aHeight))
, mImage(nsIntSize(aWidth, aHeight))
, mCodecSpecificConfig(new MediaByteBuffer)
, mExtraData(new MediaByteBuffer)
, mRotation(kDegree_0)
, mImageRect(nsIntRect(0, 0, aWidth, aHeight))
{
}
@@ -205,6 +206,7 @@ public:
, mCodecSpecificConfig(aOther.mCodecSpecificConfig)
, mExtraData(aOther.mExtraData)
, mRotation(aOther.mRotation)
, mImageRect(aOther.mImageRect)
{
}
@@ -228,6 +230,40 @@ public:
return MakeUnique<VideoInfo>(*this);
}
nsIntRect ImageRect() const
{
if (mImageRect.width < 0 || mImageRect.height < 0) {
return nsIntRect(0, 0, mImage.width, mImage.height);
}
return mImageRect;
}
void SetImageRect(const nsIntRect& aRect)
{
mImageRect = aRect;
}
// Returned the crop rectangle scaled to aWidth/aHeight size relative to
// mImage size.
// If aWidth and aHeight are identical to the original mImage.width/mImage.height
// then the scaling ratio will be 1.
// This is used for when the frame size is different from what the container
// reports. This is legal in WebM, and we will preserve the ratio of the crop
// rectangle as it was reported relative to the picture size reported by the
// container.
nsIntRect ScaledImageRect(int64_t aWidth, int64_t aHeight) const
{
if (aWidth == mImage.width && aHeight == mImage.height) {
return ImageRect();
}
nsIntRect imageRect = ImageRect();
imageRect.x = (imageRect.x * aWidth) / mImage.width;
imageRect.y = (imageRect.y * aHeight) / mImage.height;
imageRect.width = (aWidth * imageRect.width) / mImage.width;
imageRect.height = (aHeight * imageRect.height) / mImage.height;
return imageRect;
}
Rotation ToSupportedRotation(int32_t aDegree)
{
switch (aDegree) {
@@ -250,14 +286,20 @@ public:
// Indicates the frame layout for single track stereo videos.
StereoMode mStereoMode;
// Visible area of the decoded video's image.
nsIntRect mImage;
// Size of the decoded video's image.
nsIntSize mImage;
RefPtr<MediaByteBuffer> mCodecSpecificConfig;
RefPtr<MediaByteBuffer> mExtraData;
// Describing how many degrees video frames should be rotated in clock-wise to
// get correct view.
Rotation mRotation;
private:
// mImage may be cropped; currently only used with the WebM container.
// A negative width or height indicate that no cropping is to occur.
nsIntRect mImageRect;
};
class AudioInfo : public TrackInfo {
@@ -530,7 +572,12 @@ public:
: ChannelLayout(aChannels, SMPTEDefault(aChannels))
{}
ChannelLayout(uint32_t aChannels, const Channel* aConfig)
: ChannelLayout()
{
if (!aConfig) {
mValid = false;
return;
}
mChannels.AppendElements(aConfig, aChannels);
UpdateChannelMap();
}
+5 -6
View File
@@ -227,8 +227,7 @@ CreateTestH264Decoder(layers::LayersBackend aBackend,
aConfig.mId = 1;
aConfig.mDuration = 40000;
aConfig.mMediaTime = 0;
aConfig.mDisplay = nsIntSize(640, 360);
aConfig.mImage = nsIntRect(0, 0, 640, 360);
aConfig.mImage = aConfig.mDisplay = nsIntSize(640, 360);
aConfig.mExtraData = new MediaByteBuffer();
aConfig.mExtraData->AppendElements(sTestH264ExtraData,
MOZ_ARRAY_LENGTH(sTestH264ExtraData));
@@ -278,10 +277,10 @@ MP4Decoder::IsVideoAccelerated(layers::LayersBackend aBackend, nsIGlobalObject*
result.AssignLiteral("Yes");
} else {
result.AssignLiteral("No");
if (failureReason.Length()) {
result.AppendLiteral("; ");
AppendUTF8toUTF16(failureReason, result);
}
}
if (failureReason.Length()) {
result.AppendLiteral("; ");
AppendUTF8toUTF16(failureReason, result);
}
decoder->Shutdown();
taskQueue->BeginShutdown();
+1 -2
View File
@@ -14,9 +14,8 @@
#include "AudioSegment.h"
#include "GonkNativeWindow.h"
#include "GonkNativeWindowClient.h"
#include "mozilla/media/MediaSystemResourceClient.h"
#include "RefPtr.h"
#include "mozilla/RefPtr.h"
#include <speex/speex_resampler.h>
-1
View File
@@ -29,7 +29,6 @@
#include "mozilla/Logging.h"
#include "GonkNativeWindow.h"
#include "GonkNativeWindowClient.h"
#include "OMXCodecProxy.h"
#include "OmxDecoder.h"
+21 -1
View File
@@ -39,6 +39,8 @@
#include "mozilla/CDMProxy.h"
#endif
#include "DecoderDoctorDiagnostics.h"
namespace mozilla {
extern already_AddRefed<PlatformDecoderModule> CreateAgnosticDecoderModule();
@@ -147,6 +149,14 @@ PDMFactory::CreateDecoder(const TrackInfo& aConfig,
aImageContainer);
}
if (aDiagnostics) {
// If libraries failed to load, the following loop over mCurrentPDMs
// will not even try to use them. So we record failures now.
if (mFFmpegFailedToLoad) {
aDiagnostics->SetFFmpegFailedToLoad();
}
}
for (auto& current : mCurrentPDMs) {
if (!current->SupportsMimeType(aConfig.mMimeType, aDiagnostics)) {
continue;
@@ -273,7 +283,9 @@ PDMFactory::CreatePDMs()
#ifdef MOZ_FFMPEG
if (sFFmpegDecoderEnabled) {
m = FFmpegRuntimeLinker::CreateDecoderModule();
StartupPDM(m);
if (!StartupPDM(m)) {
mFFmpegFailedToLoad = true;
}
}
#endif
#ifdef MOZ_APPLEMEDIA
@@ -316,6 +328,14 @@ already_AddRefed<PlatformDecoderModule>
PDMFactory::GetDecoder(const nsACString& aMimeType,
DecoderDoctorDiagnostics* aDiagnostics) const
{
if (aDiagnostics) {
// If libraries failed to load, the following loop over mCurrentPDMs
// will not even try to use them. So we record failures now.
if (mFFmpegFailedToLoad) {
aDiagnostics->SetFFmpegFailedToLoad();
}
}
RefPtr<PlatformDecoderModule> pdm;
for (auto& current : mCurrentPDMs) {
if (current->SupportsMimeType(aMimeType, aDiagnostics)) {
+2
View File
@@ -91,6 +91,8 @@ private:
nsTArray<RefPtr<PlatformDecoderModule>> mCurrentPDMs;
RefPtr<PlatformDecoderModule> mEMEPDM;
bool mFFmpegFailedToLoad = false;
};
} // namespace mozilla
+35 -19
View File
@@ -114,7 +114,9 @@ VPXDecoder::DoDecodeFrame(MediaRawData* aSample)
vpx_image_t *img;
while ((img = vpx_codec_get_frame(&mVPX, &iter))) {
NS_ASSERTION(img->fmt == VPX_IMG_FMT_I420, "WebM image format not I420");
NS_ASSERTION(img->fmt == VPX_IMG_FMT_I420 ||
img->fmt == VPX_IMG_FMT_I444,
"WebM image format not I420 or I444");
// Chroma shifts are rounded down as per the decoding examples in the SDK
VideoData::YCbCrBuffer b;
@@ -126,27 +128,39 @@ VPXDecoder::DoDecodeFrame(MediaRawData* aSample)
b.mPlanes[1].mData = img->planes[1];
b.mPlanes[1].mStride = img->stride[1];
b.mPlanes[1].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
b.mPlanes[1].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
b.mPlanes[1].mOffset = b.mPlanes[1].mSkip = 0;
b.mPlanes[2].mData = img->planes[2];
b.mPlanes[2].mStride = img->stride[2];
b.mPlanes[2].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
b.mPlanes[2].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0;
VideoInfo info;
info.mDisplay = mInfo.mDisplay;
RefPtr<VideoData> v = VideoData::Create(info,
mImageContainer,
aSample->mOffset,
aSample->mTime,
aSample->mDuration,
b,
aSample->mKeyframe,
aSample->mTimecode,
mInfo.mImage);
if (img->fmt == VPX_IMG_FMT_I420) {
b.mPlanes[1].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
b.mPlanes[1].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
b.mPlanes[2].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
b.mPlanes[2].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
} else if (img->fmt == VPX_IMG_FMT_I444) {
b.mPlanes[1].mHeight = img->d_h;
b.mPlanes[1].mWidth = img->d_w;
b.mPlanes[2].mHeight = img->d_h;
b.mPlanes[2].mWidth = img->d_w;
} else {
LOG("VPX Unknown image format");
return -1;
}
RefPtr<VideoData> v = VideoData::Create(mInfo,
mImageContainer,
aSample->mOffset,
aSample->mTime,
aSample->mDuration,
b,
aSample->mKeyframe,
aSample->mTimecode,
mInfo.ScaledImageRect(img->d_w,
img->d_h));
if (!v) {
LOG("Image allocation error source %ldx%ld display %ldx%ld picture %ldx%ld",
@@ -199,10 +213,12 @@ VPXDecoder::Drain()
/* static */
bool
VPXDecoder::IsVPX(const nsACString& aMimeType)
VPXDecoder::IsVPX(const nsACString& aMimeType, uint8_t aCodecMask)
{
return aMimeType.EqualsLiteral("video/webm; codecs=vp8") ||
aMimeType.EqualsLiteral("video/webm; codecs=vp9");
return ((aCodecMask & VPXDecoder::VP8) &&
aMimeType.EqualsLiteral("video/webm; codecs=vp8")) ||
((aCodecMask & VPXDecoder::VP9) &&
aMimeType.EqualsLiteral("video/webm; codecs=vp9"));
}
} // namespace mozilla
+6 -6
View File
@@ -38,14 +38,14 @@ public:
return "libvpx video decoder";
}
// Return true if mimetype is a VPX codec
static bool IsVPX(const nsACString& aMimeType);
enum Codec {
VP8,
VP9
enum Codec: uint8_t {
VP8 = 1 << 0,
VP9 = 1 << 1
};
// Return true if mimetype is a VPX codec of given types.
static bool IsVPX(const nsACString& aMimeType, uint8_t aCodecMask=VP8|VP9);
private:
void DecodeFrame (MediaRawData* aSample);
int DoDecodeFrame (MediaRawData* aSample);
@@ -115,8 +115,7 @@ FFmpegH264Decoder<LIBAV_VER>::FFmpegH264Decoder(
ImageContainer* aImageContainer)
: FFmpegDataDecoder(aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType))
, mImageContainer(aImageContainer)
, mDisplay(aConfig.mDisplay)
, mImage(aConfig.mImage)
, mInfo(aConfig)
, mCodecParser(nullptr)
{
MOZ_COUNT_CTOR(FFmpegH264Decoder);
@@ -141,18 +140,18 @@ FFmpegH264Decoder<LIBAV_VER>::Init()
void
FFmpegH264Decoder<LIBAV_VER>::InitCodecContext()
{
mCodecContext->width = mImage.width;
mCodecContext->height = mImage.height;
mCodecContext->width = mInfo.mImage.width;
mCodecContext->height = mInfo.mImage.height;
// We use the same logic as libvpx in determining the number of threads to use
// so that we end up behaving in the same fashion when using ffmpeg as
// we would otherwise cause various crashes (see bug 1236167)
int decode_threads = 1;
if (mDisplay.width >= 2048) {
if (mInfo.mDisplay.width >= 2048) {
decode_threads = 8;
} else if (mDisplay.width >= 1024) {
} else if (mInfo.mDisplay.width >= 1024) {
decode_threads = 4;
} else if (mDisplay.width >= 320) {
} else if (mInfo.mDisplay.width >= 320) {
decode_threads = 2;
}
@@ -308,9 +307,6 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
mDurationMap.Clear();
}
VideoInfo info;
info.mDisplay = mDisplay;
VideoData::YCbCrBuffer b;
b.mPlanes[0].mData = mFrame->data[0];
b.mPlanes[1].mData = mFrame->data[1];
@@ -334,15 +330,17 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
b.mPlanes[1].mHeight = b.mPlanes[2].mHeight = (mFrame->height + 1) >> 1;
}
RefPtr<VideoData> v = VideoData::Create(info,
mImageContainer,
aSample->mOffset,
pts,
duration,
b,
!!mFrame->key_frame,
-1,
mImage);
RefPtr<VideoData> v = VideoData::Create(mInfo,
mImageContainer,
aSample->mOffset,
pts,
duration,
b,
!!mFrame->key_frame,
-1,
mInfo.ScaledImageRect(mFrame->width,
mFrame->height));
if (!v) {
NS_WARNING("image allocation error.");
mCallback->Error();
@@ -69,8 +69,7 @@ private:
AVFrame* aFrame);
RefPtr<ImageContainer> mImageContainer;
nsIntSize mDisplay;
nsIntRect mImage;
VideoInfo mInfo;
// Parser used for VP8 and VP9 decoding.
AVCodecParserContext* mCodecParser;
@@ -8,6 +8,7 @@
#include <gui/Surface.h>
#include <ICrypto.h>
#include "GonkVideoDecoderManager.h"
#include "GrallocImages.h"
#include "MediaDecoderReader.h"
#include "ImageContainer.h"
#include "VideoUtils.h"
@@ -19,7 +20,6 @@
#include <stagefright/MediaErrors.h>
#include <stagefright/foundation/AString.h>
#include "GonkNativeWindow.h"
#include "GonkNativeWindowClient.h"
#include "mozilla/layers/GrallocTextureClient.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/layers/TextureClient.h"
@@ -43,25 +43,13 @@ namespace mozilla {
GonkVideoDecoderManager::GonkVideoDecoderManager(
mozilla::layers::ImageContainer* aImageContainer,
const VideoInfo& aConfig)
: mImageContainer(aImageContainer)
: mConfig(aConfig)
, mImageContainer(aImageContainer)
, mColorConverterBufferSize(0)
, mNativeWindow(nullptr)
, mPendingReleaseItemsLock("GonkVideoDecoderManager::mPendingReleaseItemsLock")
, mNeedsCopyBuffer(false)
{
MOZ_COUNT_CTOR(GonkVideoDecoderManager);
mMimeType = aConfig.mMimeType;
mVideoWidth = aConfig.mDisplay.width;
mVideoHeight = aConfig.mDisplay.height;
mDisplayWidth = aConfig.mDisplay.width;
mDisplayHeight = aConfig.mDisplay.height;
mInfo.mVideo = aConfig;
mCodecSpecificData = aConfig.mCodecSpecificConfig;
nsIntRect pictureRect(0, 0, mVideoWidth, mVideoHeight);
nsIntSize frameSize(mVideoWidth, mVideoHeight);
mPicture = pictureRect;
mInitialFrame = frameSize;
}
GonkVideoDecoderManager::~GonkVideoDecoderManager()
@@ -81,12 +69,21 @@ GonkVideoDecoderManager::Init()
{
mNeedsCopyBuffer = false;
nsIntSize displaySize(mDisplayWidth, mDisplayHeight);
nsIntRect pictureRect(0, 0, mVideoWidth, mVideoHeight);
uint32_t maxWidth, maxHeight;
char propValue[PROPERTY_VALUE_MAX];
property_get("ro.moz.omx.hw.max_width", propValue, "-1");
maxWidth = -1 == atoi(propValue) ? MAX_VIDEO_WIDTH : atoi(propValue);
property_get("ro.moz.omx.hw.max_height", propValue, "-1");
maxHeight = -1 == atoi(propValue) ? MAX_VIDEO_HEIGHT : atoi(propValue) ;
if (uint32_t(mConfig.mImage.width * mConfig.mImage.height) > maxWidth * maxHeight) {
GVDM_LOG("Video resolution exceeds hw codec capability");
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
// Validate the container-reported frame and pictureRect sizes. This ensures
// that our video frame creation code doesn't overflow.
nsIntSize frameSize(mVideoWidth, mVideoHeight);
if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
if (!IsValidVideoRegion(mConfig.mImage, mConfig.ImageRect(), mConfig.mDisplay)) {
GVDM_LOG("It is not a valid region");
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
@@ -104,12 +101,20 @@ GonkVideoDecoderManager::Init()
RefPtr<InitPromise> p = mInitPromise.Ensure(__func__);
android::sp<GonkVideoDecoderManager> self = this;
mDecoder = MediaCodecProxy::CreateByType(mDecodeLooper, mMimeType.get(), false);
mDecoder = MediaCodecProxy::CreateByType(mDecodeLooper,
mConfig.mMimeType.get(),
false);
uint32_t capability = MediaCodecProxy::kEmptyCapability;
if (mDecoder->getCapability(&capability) == OK && (capability &
MediaCodecProxy::kCanExposeGraphicBuffer)) {
#if ANDROID_VERSION >= 21
sp<IGonkGraphicBufferConsumer> consumer;
GonkBufferQueue::createBufferQueue(&mGraphicBufferProducer, &consumer);
mNativeWindow = new GonkNativeWindow(consumer);
#else
mNativeWindow = new GonkNativeWindow();
#endif
}
mVideoCodecRequest.Begin(mDecoder->AsyncAllocateVideoMediaCodec()
@@ -163,19 +168,8 @@ GonkVideoDecoderManager::CreateVideoData(MediaBuffer* aBuffer,
keyFrame = 0;
}
gfx::IntRect picture = mPicture;
if (mFrameInfo.mWidth != mInitialFrame.width ||
mFrameInfo.mHeight != mInitialFrame.height) {
// Frame size is different from what the container reports. This is legal,
// and we will preserve the ratio of the crop rectangle as it
// was reported relative to the picture size reported by the container.
picture.x = (mPicture.x * mFrameInfo.mWidth) / mInitialFrame.width;
picture.y = (mPicture.y * mFrameInfo.mHeight) / mInitialFrame.height;
picture.width = (mFrameInfo.mWidth * mPicture.width) / mInitialFrame.width;
picture.height = (mFrameInfo.mHeight * mPicture.height) / mInitialFrame.height;
}
gfx::IntRect picture =
mConfig.ScaledImageRect(mFrameInfo.mWidth, mFrameInfo.mHeight);
if (aBuffer->graphicBuffer().get()) {
data = CreateVideoDataFromGraphicBuffer(aBuffer, picture);
if (data && !mNeedsCopyBuffer) {
@@ -364,7 +358,7 @@ GonkVideoDecoderManager::CreateVideoDataFromGraphicBuffer(MediaBuffer* aSource,
static_cast<GrallocTextureData*>(textureClient->GetInternalData())->SetMediaBuffer(aSource);
}
RefPtr<VideoData> data = VideoData::Create(mInfo.mVideo,
RefPtr<VideoData> data = VideoData::Create(mConfig,
mImageContainer,
0, // Filled later by caller.
0, // Filled later by caller.
@@ -433,7 +427,7 @@ GonkVideoDecoderManager::CreateVideoDataFromDataBuffer(MediaBuffer* aSource, gfx
b.mPlanes[2].mOffset = 0;
b.mPlanes[2].mSkip = 0;
RefPtr<VideoData> data = VideoData::Create(mInfo.mVideo,
RefPtr<VideoData> data = VideoData::Create(mConfig,
mImageContainer,
0, // Filled later by caller.
0, // Filled later by caller.
@@ -479,7 +473,9 @@ GonkVideoDecoderManager::SetVideoFormat()
mFrameInfo.mColorFormat = color_format;
nsIntSize displaySize(width, height);
if (!IsValidVideoRegion(mInitialFrame, mPicture, displaySize)) {
if (!IsValidVideoRegion(mConfig.mDisplay,
mConfig.ScaledImageRect(width, height),
displaySize)) {
GVDM_LOG("It is not a valid region");
return false;
}
@@ -537,7 +533,7 @@ GonkVideoDecoderManager::Output(int64_t aStreamOffset,
}
case -EAGAIN:
{
GVDM_LOG("Need to try again!");
// GVDM_LOG("Need to try again!");
return NS_ERROR_NOT_AVAILABLE;
}
case android::ERROR_END_OF_STREAM:
@@ -580,21 +576,28 @@ GonkVideoDecoderManager::codecReserved()
GVDM_LOG("codecReserved");
sp<AMessage> format = new AMessage;
sp<Surface> surface;
status_t rv = OK;
// Fixed values
GVDM_LOG("Configure video mime type: %s, widht:%d, height:%d", mMimeType.get(), mVideoWidth, mVideoHeight);
format->setString("mime", mMimeType.get());
format->setInt32("width", mVideoWidth);
format->setInt32("height", mVideoHeight);
GVDM_LOG("Configure video mime type: %s, width:%d, height:%d", mConfig.mMimeType.get(), mConfig.mImage.width, mConfig.mImage.height);
format->setString("mime", mConfig.mMimeType.get());
format->setInt32("width", mConfig.mImage.width);
format->setInt32("height", mConfig.mImage.height);
// Set the "moz-use-undequeued-bufs" to use the undeque buffers to accelerate
// the video decoding.
format->setInt32("moz-use-undequeued-bufs", 1);
if (mNativeWindow != nullptr) {
#if ANDROID_VERSION >= 21
surface = new Surface(mGraphicBufferProducer);
#else
surface = new Surface(mNativeWindow->getBufferQueue());
#endif
}
mDecoder->configure(format, surface, nullptr, 0);
mDecoder->Prepare();
if (mMimeType.EqualsLiteral("video/mp4v-es")) {
rv = mDecoder->Input(mCodecSpecificData->Elements(),
mCodecSpecificData->Length(), 0,
if (mConfig.mMimeType.EqualsLiteral("video/mp4v-es")) {
rv = mDecoder->Input(mConfig.mCodecSpecificConfig->Elements(),
mConfig.mCodecSpecificConfig->Length(), 0,
android::MediaCodec::BUFFER_FLAG_CODECCONFIG,
CODECCONFIG_TIMEOUT_US);
}
@@ -689,7 +692,7 @@ void GonkVideoDecoderManager::ReleaseAllPendingVideoBuffers()
// Free all pending video buffers without holding mPendingReleaseItemsLock.
size_t size = releasingItems.Length();
for (size_t i = 0; i < size; i++) {
nsRefPtr<FenceHandle::FdObj> fdObj = releasingItems[i].mReleaseFence.GetAndResetFdObj();
RefPtr<FenceHandle::FdObj> fdObj = releasingItems[i].mReleaseFence.GetAndResetFdObj();
sp<Fence> fence = new Fence(fdObj->GetAndResetFd());
fence->waitForever("GonkVideoDecoderManager");
mDecoder->ReleaseMediaBuffer(releasingItems[i].mBuffer);
@@ -13,7 +13,6 @@
#include "I420ColorConverterHelper.h"
#include "MediaCodecProxy.h"
#include "GonkNativeWindow.h"
#include "GonkNativeWindowClient.h"
#include "mozilla/layers/FenceUtils.h"
#include "mozilla/UniquePtr.h"
#include <ui/Fence.h>
@@ -101,17 +100,11 @@ private:
void PostReleaseVideoBuffer(android::MediaBuffer *aBuffer,
layers::FenceHandle mReleaseFence);
uint32_t mVideoWidth;
uint32_t mVideoHeight;
uint32_t mDisplayWidth;
uint32_t mDisplayHeight;
nsIntRect mPicture;
nsIntSize mInitialFrame;
VideoInfo mConfig;
RefPtr<layers::ImageContainer> mImageContainer;
RefPtr<layers::TextureClientRecycleAllocator> mCopyAllocator;
MediaInfo mInfo;
MozPromiseRequestHolder<android::MediaCodecProxy::CodecPromise> mVideoCodecRequest;
FrameInfo mFrameInfo;
@@ -121,6 +114,10 @@ private:
size_t mColorConverterBufferSize;
android::sp<android::GonkNativeWindow> mNativeWindow;
#if ANDROID_VERSION >= 21
android::sp<android::IGraphicBufferProducer> mGraphicBufferProducer;
#endif
enum {
kNotifyPostReleaseBuffer = 'nprb',
};
@@ -1,212 +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 "OmxPlatformLayer.h"
#if defined(MOZ_WIDGET_GONK) && (ANDROID_VERSION == 20 || ANDROID_VERSION == 19)
#define OMX_PLATFORM_GONK
#include "GonkOmxPlatformLayer.h"
#endif
extern mozilla::LogModule* GetPDMLog();
#ifdef LOG
#undef LOG
#endif
#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("OmxPlatformLayer -- %s: " arg, __func__, ##__VA_ARGS__))
#define RETURN_IF_ERR(err) \
if (err != OMX_ErrorNone) { \
LOG("error: 0x%08x", err); \
return err; \
} \
// Common OMX decoder configuration code.
namespace mozilla {
// This helper class encapsulates the details of component parameters setting
// for different OMX audio & video codecs.
template<typename ParamType>
class OmxConfig
{
public:
virtual ~OmxConfig() {}
// Subclasses should implement this method to configure the codec.
virtual OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const ParamType& aParam) = 0;
};
typedef OmxConfig<AudioInfo> OmxAudioConfig;
typedef OmxConfig<VideoInfo> OmxVideoConfig;
template<typename ConfigType>
UniquePtr<ConfigType> ConfigForMime(const nsACString&);
class OmxAacConfig : public OmxAudioConfig
{
public:
OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const AudioInfo& aInfo) override
{
OMX_ERRORTYPE err;
OMX_AUDIO_PARAM_AACPROFILETYPE aacProfile;
InitOmxParameter(&aacProfile);
err = aOmx.GetParameter(OMX_IndexParamAudioAac, &aacProfile, sizeof(aacProfile));
RETURN_IF_ERR(err);
aacProfile.nChannels = aInfo.mChannels;
aacProfile.nSampleRate = aInfo.mRate;
aacProfile.eAACProfile = static_cast<OMX_AUDIO_AACPROFILETYPE>(aInfo.mProfile);
err = aOmx.SetParameter(OMX_IndexParamAudioAac, &aacProfile, sizeof(aacProfile));
RETURN_IF_ERR(err);
LOG("Config OMX_IndexParamAudioAac, channel %d, sample rate %d, profile %d",
aacProfile.nChannels, aacProfile.nSampleRate, aacProfile.eAACProfile);
return OMX_ErrorNone;
}
};
template<>
UniquePtr<OmxAudioConfig>
ConfigForMime(const nsACString& aMimeType)
{
UniquePtr<OmxAudioConfig> conf;
if (OmxPlatformLayer::SupportsMimeType(aMimeType)) {
if (aMimeType.EqualsLiteral("audio/mp4a-latm")) {
conf.reset(new OmxAacConfig());
}
}
return Move(conf);
}
// There should be a better way to calculate it.
#define MIN_VIDEO_INPUT_BUFFER_SIZE 64 * 1024
class OmxCommonVideoConfig : public OmxVideoConfig
{
public:
explicit OmxCommonVideoConfig(OMX_VIDEO_CODINGTYPE aCodec)
: OmxVideoConfig()
, mCodec(aCodec)
{}
OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const VideoInfo& aInfo) override
{
OMX_ERRORTYPE err;
OMX_PARAM_PORTDEFINITIONTYPE def;
// Set up in/out port definition.
nsTArray<uint32_t> ports;
GetOmxPortIndex(ports);
for (auto idx : ports) {
InitOmxParameter(&def);
def.nPortIndex = idx;
err = aOmx.GetParameter(OMX_IndexParamPortDefinition, &def, sizeof(def));
RETURN_IF_ERR(err);
def.format.video.nFrameWidth = aInfo.mDisplay.width;
def.format.video.nFrameHeight = aInfo.mDisplay.height;
def.format.video.nStride = aInfo.mImage.width;
def.format.video.nSliceHeight = aInfo.mImage.height;
if (def.eDir == OMX_DirInput) {
def.format.video.eCompressionFormat = mCodec;
def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
if (def.nBufferSize < MIN_VIDEO_INPUT_BUFFER_SIZE) {
def.nBufferSize = aInfo.mImage.width * aInfo.mImage.height;
LOG("Change input buffer size to %d", def.nBufferSize);
}
} else {
def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
}
err = aOmx.SetParameter(OMX_IndexParamPortDefinition, &def, sizeof(def));
}
return err;
}
private:
const OMX_VIDEO_CODINGTYPE mCodec;
};
template<>
UniquePtr<OmxVideoConfig>
ConfigForMime(const nsACString& aMimeType)
{
UniquePtr<OmxVideoConfig> conf;
if (OmxPlatformLayer::SupportsMimeType(aMimeType)) {
if (aMimeType.EqualsLiteral("video/avc")) {
conf.reset(new OmxCommonVideoConfig(OMX_VIDEO_CodingAVC));
} else if (aMimeType.EqualsLiteral("video/mp4v-es") ||
aMimeType.EqualsLiteral("video/mp4")) {
conf.reset(new OmxCommonVideoConfig(OMX_VIDEO_CodingMPEG4));
} else if (aMimeType.EqualsLiteral("video/3gpp")) {
conf.reset(new OmxCommonVideoConfig(OMX_VIDEO_CodingH263));
}
}
return Move(conf);
}
OMX_ERRORTYPE
OmxPlatformLayer::Config()
{
MOZ_ASSERT(mInfo);
if (mInfo->IsAudio()) {
UniquePtr<OmxAudioConfig> conf(ConfigForMime<OmxAudioConfig>(mInfo->mMimeType));
MOZ_ASSERT(conf.get());
return conf->Apply(*this, *(mInfo->GetAsAudioInfo()));
} else if (mInfo->IsVideo()) {
UniquePtr<OmxVideoConfig> conf(ConfigForMime<OmxVideoConfig>(mInfo->mMimeType));
MOZ_ASSERT(conf.get());
return conf->Apply(*this, *(mInfo->GetAsVideoInfo()));
} else {
MOZ_ASSERT_UNREACHABLE("non-AV data (text?) is not supported.");
return OMX_ErrorNotImplemented;
}
}
// Implementations for different platforms will be defined in their own files.
#ifdef OMX_PLATFORM_GONK
bool
OmxPlatformLayer::SupportsMimeType(const nsACString& aMimeType)
{
return GonkOmxPlatformLayer::FindComponents(aMimeType);
}
OmxPlatformLayer*
OmxPlatformLayer::Create(OmxDataDecoder* aDataDecoder,
OmxPromiseLayer* aPromiseLayer,
TaskQueue* aTaskQueue,
layers::ImageContainer* aImageContainer)
{
return new GonkOmxPlatformLayer(aDataDecoder, aPromiseLayer, aTaskQueue, aImageContainer);
}
#else // For platforms without OMX IL support.
bool
OmxPlatformLayer::SupportsMimeType(const nsACString& aMimeType)
{
return false;
}
OmxPlatformLayer*
OmxPlatformLayer::Create(OmxDataDecoder* aDataDecoder,
OmxPromiseLayer* aPromiseLayer,
TaskQueue* aTaskQueue,
layers::ImageContainer* aImageContainer)
{
return nullptr;
}
#endif
}
+50 -33
View File
@@ -33,6 +33,8 @@ using mozilla::layers::IMFYCbCrImage;
using mozilla::layers::LayerManager;
using mozilla::layers::LayersBackend;
#if MOZ_WINSDK_MAXVER < 0x0A000000
// Windows 10+ SDK has VP80 and VP90 defines
const GUID MFVideoFormat_VP80 =
{
0x30385056,
@@ -48,6 +50,7 @@ const GUID MFVideoFormat_VP90 =
0x0010,
{0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
};
#endif
const CLSID CLSID_WebmMfVp8Dec =
{
@@ -74,6 +77,7 @@ WMFVideoMFTManager::WMFVideoMFTManager(
bool aDXVAEnabled)
: mVideoInfo(aConfig)
, mVideoStride(0)
, mImageSize(aConfig.mImage)
, mImageContainer(aImageContainer)
, mDXVAEnabled(aDXVAEnabled)
, mLayersBackend(aLayersBackend)
@@ -154,13 +158,23 @@ public:
NS_IMETHOD Run() {
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
nsACString* failureReason = &mFailureReason;
nsCString secondFailureReason;
if (mBackend == LayersBackend::LAYERS_D3D11 &&
Preferences::GetBool("media.windows-media-foundation.allow-d3d11-dxva", false) &&
Preferences::GetBool("media.windows-media-foundation.allow-d3d11-dxva", true) &&
IsWin8OrLater()) {
mDXVA2Manager = DXVA2Manager::CreateD3D11DXVA(mFailureReason);
} else {
mDXVA2Manager = DXVA2Manager::CreateD3D9DXVA(mFailureReason);
mDXVA2Manager = DXVA2Manager::CreateD3D11DXVA(*failureReason);
if (mDXVA2Manager) {
return NS_OK;
}
// Try again with d3d9, but record the failure reason
// into a new var to avoid overwriting the d3d11 failure.
failureReason = &secondFailureReason;
mFailureReason.Append(NS_LITERAL_CSTRING("; "));
}
mDXVA2Manager = DXVA2Manager::CreateD3D9DXVA(*failureReason);
// Make sure we include the messages from both attempts (if applicable).
mFailureReason.Append(secondFailureReason);
return NS_OK;
}
nsAutoPtr<DXVA2Manager> mDXVA2Manager;
@@ -204,14 +218,14 @@ WMFVideoMFTManager::Init()
{
bool success = InitInternal(/* aForceD3D9 = */ false);
// If initialization failed with d3d11 DXVA then try falling back
// to d3d9.
if (!success && mDXVA2Manager && mDXVA2Manager->IsD3D11()) {
mDXVA2Manager = nullptr;
nsCString d3d11Failure = mDXVAFailureReason;
success = InitInternal(true);
mDXVAFailureReason.Append(NS_LITERAL_CSTRING("; "));
mDXVAFailureReason.Append(d3d11Failure);
if (success && mDXVA2Manager) {
// If we had some failures but eventually made it work,
// make sure we preserve the messages.
if (mDXVA2Manager->IsD3D11()) {
mDXVAFailureReason.Append(NS_LITERAL_CSTRING("Using D3D11 API"));
} else {
mDXVAFailureReason.Append(NS_LITERAL_CSTRING("Using D3D9 API"));
}
}
return success;
@@ -383,18 +397,17 @@ WMFVideoMFTManager::ConfigureVideoFrameGeometry()
NS_ENSURE_TRUE(videoFormat == MFVideoFormat_NV12 || !mUseHwAccel, E_FAIL);
NS_ENSURE_TRUE(videoFormat == MFVideoFormat_YV12 || mUseHwAccel, E_FAIL);
UINT32 width = 0, height = 0;
hr = MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &width, &height);
nsIntRect pictureRegion;
hr = GetPictureRegion(mediaType, pictureRegion);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
mVideoInfo.mImage.width = width;
mVideoInfo.mImage.height = height;
nsIntRect pictureRegion = mVideoInfo.mImage;
UINT32 width = pictureRegion.width;
UINT32 height = pictureRegion.height;
mImageSize = nsIntSize(width, height);
// Calculate and validate the picture region and frame dimensions after
// scaling by the pixel aspect ratio.
nsIntSize frameSize = nsIntSize(width, height);
nsIntSize displaySize = nsIntSize(mVideoInfo.mDisplay.width, mVideoInfo.mDisplay.height);
if (!IsValidVideoRegion(frameSize, pictureRegion, displaySize)) {
pictureRegion = mVideoInfo.ScaledImageRect(width, height);
if (!IsValidVideoRegion(mImageSize, pictureRegion, mVideoInfo.mDisplay)) {
// Video track's frame sizes will overflow. Ignore the video track.
return E_FAIL;
}
@@ -453,8 +466,8 @@ WMFVideoMFTManager::CreateBasicVideoFrame(IMFSample* aSample,
// i.e., Y, then V, then U.
VideoData::YCbCrBuffer b;
uint32_t videoWidth = mVideoInfo.mImage.width;
uint32_t videoHeight = mVideoInfo.mImage.height;
uint32_t videoWidth = mImageSize.width;
uint32_t videoHeight = mImageSize.height;
// Y (Y') plane
b.mPlanes[0].mData = data;
@@ -500,10 +513,11 @@ WMFVideoMFTManager::CreateBasicVideoFrame(IMFSample* aSample,
RefPtr<layers::PlanarYCbCrImage> image =
new IMFYCbCrImage(buffer, twoDBuffer);
nsIntRect pictureRegion = mVideoInfo.ScaledImageRect(videoWidth, videoHeight);
VideoData::SetVideoDataToImage(image,
mVideoInfo,
b,
mVideoInfo.mImage,
pictureRegion,
false);
RefPtr<VideoData> v =
@@ -515,7 +529,7 @@ WMFVideoMFTManager::CreateBasicVideoFrame(IMFSample* aSample,
image.forget(),
false,
-1,
mVideoInfo.mImage);
pictureRegion);
v.forget(aOutVideoData);
return S_OK;
@@ -534,9 +548,11 @@ WMFVideoMFTManager::CreateD3DVideoFrame(IMFSample* aSample,
*aOutVideoData = nullptr;
HRESULT hr;
nsIntRect pictureRegion =
mVideoInfo.ScaledImageRect(mImageSize.width, mImageSize.height);
RefPtr<Image> image;
hr = mDXVA2Manager->CopyToImage(aSample,
mVideoInfo.mImage,
pictureRegion,
mImageContainer,
getter_AddRefs(image));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
@@ -547,14 +563,14 @@ WMFVideoMFTManager::CreateD3DVideoFrame(IMFSample* aSample,
media::TimeUnit duration = GetSampleDuration(aSample);
NS_ENSURE_TRUE(duration.IsValid(), E_FAIL);
RefPtr<VideoData> v = VideoData::CreateFromImage(mVideoInfo,
mImageContainer,
aStreamOffset,
pts.ToMicroseconds(),
duration.ToMicroseconds(),
image.forget(),
false,
-1,
mVideoInfo.mImage);
mImageContainer,
aStreamOffset,
pts.ToMicroseconds(),
duration.ToMicroseconds(),
image.forget(),
false,
-1,
pictureRegion);
NS_ENSURE_TRUE(v, E_FAIL);
v.forget(aOutVideoData);
@@ -669,6 +685,7 @@ WMFVideoMFTManager::ConfigurationChanged(const TrackInfo& aConfig)
{
MOZ_ASSERT(aConfig.GetAsVideoInfo());
mVideoInfo = *aConfig.GetAsVideoInfo();
mImageSize = mVideoInfo.mImage;
}
} // namespace mozilla
@@ -66,6 +66,7 @@ private:
// Video frame geometry.
VideoInfo mVideoInfo;
uint32_t mVideoStride;
nsIntSize mImageSize;
RefPtr<layers::ImageContainer> mImageContainer;
nsAutoPtr<DXVA2Manager> mDXVA2Manager;
+1 -1
View File
@@ -68,7 +68,7 @@ public:
return mSampleRate;
}
int32_t Length() const
uint32_t Length() const
{
return mLength;
}
+28 -2
View File
@@ -11,6 +11,7 @@
#include "WebMDemuxer.h"
#include "WebMBufferedParser.h"
#include "gfx2DGlue.h"
#include "mozilla/Atomics.h"
#include "mozilla/Endian.h"
#include "mozilla/Preferences.h"
#include "mozilla/SharedThreadPool.h"
@@ -42,6 +43,8 @@ LazyLogModule gNesteggLog("Nestegg");
// files encountered appear to have keyframes located < 4s.
#define MAX_LOOK_AHEAD 10000000
static Atomic<uint32_t> sStreamSourceID(0u);
// Functions for reading and seeking using WebMDemuxer required for
// nestegg_io. The 'user data' passed to these functions is the
// demuxer.
@@ -337,7 +340,8 @@ WebMDemuxer::ReadMetadata()
mHasVideo = true;
mInfo.mVideo.mDisplay = displaySize;
mInfo.mVideo.mImage = pictureRect;
mInfo.mVideo.mImage = frameSize;
mInfo.mVideo.SetImageRect(pictureRect);
switch (params.stereo_mode) {
case NESTEGG_VIDEO_MONO:
@@ -572,6 +576,18 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl
break;
}
isKeyframe = si.is_kf;
if (isKeyframe) {
// We only look for resolution changes on keyframes for both VP8 and
// VP9. Other resolution changes are invalid.
if (mLastSeenFrameWidth.isSome() && mLastSeenFrameHeight.isSome() &&
(si.w != mLastSeenFrameWidth.value() ||
si.h != mLastSeenFrameHeight.value())) {
mInfo.mVideo.mDisplay = nsIntSize(si.w, si.h);
mSharedVideoTrackInfo = new SharedTrackInfo(mInfo.mVideo, ++sStreamSourceID);
}
mLastSeenFrameWidth = Some(si.w);
mLastSeenFrameHeight = Some(si.h);
}
}
WEBM_DEBUG("push sample tstamp: %ld next_tstamp: %ld length: %ld kf: %d",
@@ -582,12 +598,15 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl
sample->mDuration = next_tstamp - tstamp;
sample->mOffset = holder->Offset();
sample->mKeyframe = isKeyframe;
if (discardPadding) {
if (discardPadding && i == count - 1) {
uint8_t c[8];
BigEndian::writeInt64(&c[0], discardPadding);
sample->mExtraData = new MediaByteBuffer;
sample->mExtraData->AppendElements(&c[0], 8);
}
if (aType == TrackInfo::kVideoTrack) {
sample->mTrackInfo = mSharedVideoTrackInfo;
}
aSamples->Push(sample);
}
return true;
@@ -792,6 +811,7 @@ WebMTrackDemuxer::WebMTrackDemuxer(WebMDemuxer* aParent,
uint32_t aTrackNumber)
: mParent(aParent)
, mType(aType)
, mNeedKeyframe(true)
{
mInfo = mParent->GetTrackInfo(aType, aTrackNumber);
MOZ_ASSERT(mInfo);
@@ -818,6 +838,7 @@ WebMTrackDemuxer::Seek(media::TimeUnit aTime)
mSamples.Reset();
mParent->SeekInternal(aTime);
mParent->GetNextPacket(mType, &mSamples);
mNeedKeyframe = true;
// Check what time we actually seeked to.
if (mSamples.GetSize() > 0) {
@@ -853,6 +874,10 @@ WebMTrackDemuxer::GetSamples(int32_t aNumSamples)
if (!sample) {
break;
}
if (mNeedKeyframe && !sample->mKeyframe) {
continue;
}
mNeedKeyframe = false;
samples->mSamples.AppendElement(sample);
aNumSamples--;
}
@@ -929,6 +954,7 @@ WebMTrackDemuxer::Reset()
{
mSamples.Reset();
media::TimeIntervals buffered = GetBuffered();
mNeedKeyframe = true;
if (buffered.Length()) {
WEBM_DEBUG("Seek to start point: %f", buffered.Start(0).ToSeconds());
mParent->SeekInternal(buffered.Start(0));
+7
View File
@@ -201,6 +201,12 @@ private:
// as nestegg only performs 1-byte read at a time.
int64_t mLastWebMBlockOffset;
const bool mIsMediaSource;
Maybe<uint32_t> mLastSeenFrameWidth;
Maybe<uint32_t> mLastSeenFrameHeight;
// This will be populated only if a resolution change occurs, otherwise it
// will be left as null so the original metadata is used
RefPtr<SharedTrackInfo> mSharedVideoTrackInfo;
};
class WebMTrackDemuxer : public MediaTrackDemuxer
@@ -236,6 +242,7 @@ private:
TrackInfo::TrackType mType;
UniquePtr<TrackInfo> mInfo;
Maybe<media::TimeUnit> mNextKeyframeTime;
bool mNeedKeyframe;
// Queued samples extracted by the demuxer, but not yet returned.
MediaRawDataQueue mSamples;
+2 -2
View File
@@ -13,12 +13,12 @@
interface AudioBuffer {
readonly attribute float sampleRate;
readonly attribute long length;
readonly attribute unsigned long length;
// in seconds
readonly attribute double duration;
readonly attribute long numberOfChannels;
readonly attribute unsigned long numberOfChannels;
[Throws]
Float32Array getChannelData(unsigned long channel);
+1 -2
View File
@@ -23,6 +23,7 @@ interface Console {
void time(optional any time);
void timeEnd(optional any time);
void timeStamp(optional any data);
void clear(any... data);
void profile(any... data);
void profileEnd(any... data);
@@ -32,8 +33,6 @@ interface Console {
// No-op methods for compatibility with other browsers.
[BinaryName="noopMethod"]
void clear();
[BinaryName="noopMethod"]
void markTimeline();
[BinaryName="noopMethod"]
void timeline();
@@ -6,6 +6,7 @@
enum DecoderDoctorNotificationType {
"cannot-play",
"platform-decoder-not-found",
"can-play-but-some-missing-decoders"
};
+1 -1
View File
@@ -63,7 +63,7 @@ interface _Promise {
};
#else // SPIDERMONKEY_PROMISE
[NoInterfaceObject,
Exposed=(Window,Worker,System)]
Exposed=(Window,Worker,WorkerDebugger,System)]
// Need to escape "Promise" so it's treated as an identifier.
interface _Promise {
};
+1
View File
@@ -111,6 +111,7 @@ WEBIDL_FILES = [
'DataStore.webidl',
'DataStoreImpl.webidl',
'DataTransfer.webidl',
'DecoderDoctorNotification.webidl',
'DedicatedWorkerGlobalScope.webidl',
'DelayNode.webidl',
'DesktopNotification.webidl',
+7 -19
View File
@@ -400,18 +400,6 @@ SandboxCloneInto(JSContext* cx, unsigned argc, Value* vp)
return xpc::CloneInto(cx, args[0], args[1], options, args.rval());
}
static bool
sandbox_enumerate(JSContext* cx, HandleObject obj)
{
return JS_EnumerateStandardClasses(cx, obj);
}
static bool
sandbox_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
{
return JS_ResolveStandardClass(cx, obj, id, resolvedp);
}
static void
sandbox_finalize(js::FreeOp* fop, JSObject* obj)
{
@@ -491,9 +479,9 @@ sandbox_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
CompartmentPrivate* priv = CompartmentPrivate::Get(obj);
MOZ_ASSERT(priv->writeToGlobalPrototype);
// Whenever JS_EnumerateStandardClasses is called (by sandbox_enumerate for
// example), it defines the "undefined" property, even if it's already
// defined. We don't want to do anything in that case.
// Whenever JS_EnumerateStandardClasses is called, it defines the
// "undefined" property, even if it's already defined. We don't want to do
// anything in that case.
if (id == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_UNDEFINED))
return true;
@@ -553,8 +541,8 @@ sandbox_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
static const js::ClassOps SandboxClassOps = {
nullptr, nullptr, nullptr, nullptr,
sandbox_enumerate, sandbox_resolve,
nullptr, /* mayResolve */
JS_EnumerateStandardClasses, JS_ResolveStandardClass,
JS_MayResolveStandardClass,
sandbox_finalize,
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
};
@@ -577,8 +565,8 @@ static const js::Class SandboxClass = {
// to do the work for this class.
static const js::ClassOps SandboxWriteToProtoClassOps = {
sandbox_addProperty, nullptr, nullptr, nullptr,
sandbox_enumerate, sandbox_resolve,
nullptr, /* mayResolve */
JS_EnumerateStandardClasses, JS_ResolveStandardClass,
JS_MayResolveStandardClass,
sandbox_finalize,
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
};
+1
View File
@@ -349,6 +349,7 @@ pref("media.wmf.enabled", true);
pref("media.wmf.decoder.thread-count", -1);
pref("media.wmf.low-latency.enabled", false);
pref("media.wmf.skip-blacklist", false);
pref("media.windows-media-foundation.allow-d3d11-dxva", true);
#endif
#if defined(MOZ_FFMPEG)
pref("media.ffmpeg.enabled", true);
@@ -6101,6 +6101,21 @@
"description": "Whether Ogg audio/video encountered are chained or not.",
"bug_numbers": [1230295]
},
"MEDIA_HLS_CANPLAY_REQUESTED": {
"alert_emails": ["ajones@mozilla.com", "giles@mozilla.com"],
"expires_in_version": "50",
"kind": "boolean",
"description": "Reports a true value when a page requests canPlayType for an HTTP Live Streaming media type (or generic m3u playlist).",
"bug_numbers": [1262659]
},
"MEDIA_HLS_DECODER_SUCCESS": {
"alert_emails": ["ajones@mozilla.com", "giles@mozilla.com"],
"expires_in_version": "50",
"kind": "boolean",
"description": "Reports whether a decoder for an HTTP Live Streaming media type was created when requested.",
"bug_numbers": [1262659]
},
"VIDEO_MFT_OUTPUT_NULL_SAMPLES": {
"alert_emails": ["cpearce@mozilla.com"],
"expires_in_version": "53",
+1
View File
@@ -300,6 +300,7 @@ const LOG_LEVELS = {
"debug": 2,
"log": 3,
"info": 3,
"clear": 3,
"trace": 3,
"timeEnd": 3,
"time": 3,
@@ -89,6 +89,7 @@ const CONSOLE_API_LEVELS_TO_SEVERITIES = {
warn: "warning",
info: "info",
log: "log",
clear: "log",
trace: "log",
table: "log",
debug: "log",
@@ -71,6 +71,7 @@ support-files =
test-closure-optimized-out.html
test-closures.html
test-console-assert.html
test-console-clear.html
test-console-count.html
test-console-count-external-file.js
test-console-extras.html
@@ -149,6 +150,7 @@ skip-if = e10s # Bug 1042253 - webconsole e10s tests (intermittent Linux debug)
skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests (expectUncaughtException)
[browser_console.js]
[browser_console_addonsdk_loader_exception.js]
[browser_console_clear_method.js]
[browser_console_clear_on_reload.js]
[browser_console_click_focus.js]
[browser_console_consolejsm_output.js]
@@ -0,0 +1,131 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Check that calls to console.clear from a script delete the messages
// previously logged.
"use strict";
add_task(function* () {
const TEST_URI = "http://example.com/browser/toolkit/devtools/webconsole/" +
"test/test-console-clear.html";
yield loadTab(TEST_URI);
let hud = yield openConsole();
ok(hud, "Web Console opened");
info("Check the console.clear() done on page load has been processed.");
yield waitForLog("Console was cleared", hud);
ok(hud.outputNode.textContent.includes("Console was cleared"),
"console.clear() message is displayed");
ok(!hud.outputNode.textContent.includes("log1"), "log1 not displayed");
ok(!hud.outputNode.textContent.includes("log2"), "log2 not displayed");
info("Logging two messages log3, log4");
ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
content.wrappedJSObject.console.log("log3");
content.wrappedJSObject.console.log("log4");
});
yield waitForLog("log3", hud);
yield waitForLog("log4", hud);
ok(hud.outputNode.textContent.includes("Console was cleared"),
"console.clear() message is still displayed");
ok(hud.outputNode.textContent.includes("log3"), "log3 is displayed");
ok(hud.outputNode.textContent.includes("log4"), "log4 is displayed");
info("Open the variables view sidebar for 'objFromPage'");
yield openSidebar("objFromPage", { a: 1 }, hud);
let sidebarClosed = hud.jsterm.once("sidebar-closed");
info("Call console.clear from the page");
ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
content.wrappedJSObject.console.clear();
});
// Cannot wait for "Console was cleared" here because such a message is
// already present and would yield immediately.
info("Wait for variables view sidebar to be closed after console.clear()");
yield sidebarClosed;
ok(!hud.outputNode.textContent.includes("log3"), "log3 not displayed");
ok(!hud.outputNode.textContent.includes("log4"), "log4 not displayed");
ok(hud.outputNode.textContent.includes("Console was cleared"),
"console.clear() message is still displayed");
is(hud.outputNode.textContent.split("Console was cleared").length, 2,
"console.clear() message is only displayed once");
info("Logging one messages log5");
ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
content.wrappedJSObject.console.log("log5");
});
yield waitForLog("log5", hud);
info("Close and reopen the webconsole.");
yield closeConsole(gBrowser.selectedTab);
hud = yield openConsole();
yield waitForLog("Console was cleared", hud);
ok(hud.outputNode.textContent.includes("Console was cleared"),
"console.clear() message is still displayed");
ok(!hud.outputNode.textContent.includes("log1"), "log1 not displayed");
ok(!hud.outputNode.textContent.includes("log2"), "log1 not displayed");
ok(!hud.outputNode.textContent.includes("log3"), "log3 not displayed");
ok(!hud.outputNode.textContent.includes("log4"), "log4 not displayed");
ok(hud.outputNode.textContent.includes("log5"), "log5 still displayed");
});
/**
* Wait for a single message to be logged in the provided webconsole instance
* with the category CATEGORY_WEBDEV and the SEVERITY_LOG severity.
*
* @param {String} message
* The expected messaged.
* @param {WebConsole} webconsole
* WebConsole instance in which the message should be logged.
*/
function* waitForLog(message, webconsole, options) {
yield waitForMessages({
webconsole: webconsole,
messages: [{
text: message,
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
}],
});
}
/**
* Open the variables view sidebar for the object with the provided name objName
* and wait for the expected object is displayed in the variables view.
*
* @param {String} objName
* The name of the object to open in the sidebar.
* @param {Object} expectedObj
* The properties that should be displayed in the variables view.
* @param {WebConsole} webconsole
* WebConsole instance in which the message should be logged.
*
*/
function* openSidebar(objName, expectedObj, webconsole) {
let msg = yield webconsole.jsterm.execute(objName);
ok(msg, "output message found");
let anchor = msg.querySelector("a");
let body = msg.querySelector(".message-body");
ok(anchor, "object anchor");
ok(body, "message body");
yield EventUtils.synthesizeMouse(anchor, 2, 2, {}, webconsole.iframeWindow);
let vviewVar = yield webconsole.jsterm.once("variablesview-fetched");
let vview = vviewVar._variablesView;
ok(vview, "variables view object exists");
yield findVariableViewProperties(vviewVar, [
expectedObj,
], { webconsole: webconsole });
}
@@ -0,0 +1,16 @@
<!DOCTYPE HTML>
<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
<meta charset="utf-8">
<title>Console.clear() tests</title>
<script type="text/javascript">
console.log("log1");
console.log("log2");
console.clear();
window.objFromPage = { a: 1 };
</script>
</head>
<body>
<h1 id="header">Clear Demo</h1>
</body>
</html>
+14
View File
@@ -126,6 +126,7 @@ const LEVELS = {
warn: SEVERITY_WARNING,
info: SEVERITY_INFO,
log: SEVERITY_LOG,
clear: SEVERITY_LOG,
trace: SEVERITY_LOG,
table: SEVERITY_LOG,
debug: SEVERITY_LOG,
@@ -1271,6 +1272,11 @@ WebConsoleFrame.prototype = {
node = msg.init(this.output).render().element;
break;
}
case "clear": {
body = l10n.getStr("consoleCleared");
clipboardText = body;
break;
}
case "dir": {
body = { arguments: args };
let clipboardArray = [];
@@ -2279,6 +2285,14 @@ WebConsoleFrame.prototype = {
let isRepeated = this._filterRepeatedMessage(node);
// If a clear message is processed while the webconsole is opened, the UI
// should be cleared.
if (message && message.level == "clear") {
// Do not clear the consoleStorage here as it has been cleared already
// by the clear method, only clear the UI.
this.jsterm.clearOutput(false);
}
let visible = !isRepeated && !isFiltered;
if (!isRepeated) {
this.outputNode.insertBefore(node,
@@ -129,6 +129,11 @@ timerStarted=%S: timer started
# is the number of milliseconds.
timeEnd=%1$S: %2$Sms
# LOCALIZATION NOTE (consoleCleared): this string is displayed when receiving a
# call to console.clear() to let the user know the previous messages of the
# console have been removed programmatically.
consoleCleared=Console was cleared.
# LOCALIZATION NOTE (noCounterLabel): this string is used to display
# count-messages with no label provided.
noCounterLabel=<no label>
+1
View File
@@ -54,6 +54,7 @@ enum OperatingSystem {
DRIVER_OS_OS_X_10_8,
DRIVER_OS_OS_X_10_9,
DRIVER_OS_OS_X_10_10,
DRIVER_OS_OS_X_10_11,
DRIVER_OS_ANDROID,
DRIVER_OS_IOS,
DRIVER_OS_ALL
+12 -3
View File
@@ -154,7 +154,13 @@ GetPrefNameForFeature(int32_t aFeature)
break;
case nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION:
name = BLACKLIST_PREF_BRANCH "canvas2d.acceleration";
break;
case nsIGfxInfo::FEATURE_VP8_HW_DECODE:
case nsIGfxInfo::FEATURE_VP9_HW_DECODE:
// We don't provide prefs for this features.
break;
default:
MOZ_ASSERT_UNREACHABLE("Unexpected nsIGfxInfo feature?!");
break;
}
@@ -299,6 +305,8 @@ BlacklistOSToOperatingSystem(const nsAString& os)
return DRIVER_OS_OS_X_10_9;
else if (os.EqualsLiteral("Darwin 14"))
return DRIVER_OS_OS_X_10_10;
else if (os.EqualsLiteral("Darwin 15"))
return DRIVER_OS_OS_X_10_11;
else if (os.EqualsLiteral("Android"))
return DRIVER_OS_ANDROID;
else if (os.EqualsLiteral("All"))
@@ -1190,10 +1198,11 @@ nsresult GfxInfoBase::GetInfo(JSContext* aCx, JS::MutableHandle<JS::Value> aResu
return NS_OK;
}
nsAutoCString gBaseAppVersion;
const nsCString&
GfxInfoBase::GetApplicationVersion()
{
static nsCString version;
static bool versionInitialized = false;
if (!versionInitialized) {
// If we fail to get the version, we will not try again.
@@ -1202,10 +1211,10 @@ GfxInfoBase::GetApplicationVersion()
// Get the version from xpcom/system/nsIXULAppInfo.idl
nsCOMPtr<nsIXULAppInfo> app = do_GetService("@mozilla.org/xre/app-info;1");
if (app) {
app->GetVersion(version);
app->GetVersion(gBaseAppVersion);
}
}
return version;
return gBaseAppVersion;
}
void
+2
View File
@@ -50,6 +50,8 @@ OSXVersionToOperatingSystem(uint32_t aOSXVersion)
return DRIVER_OS_OS_X_10_9;
case 10:
return DRIVER_OS_OS_X_10_10;
case 11:
return DRIVER_OS_OS_X_10_11;
}
}
@@ -1,24 +0,0 @@
/* Copyright 2013 Mozilla Foundation and Mozilla contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
# include "GonkNativeWindowClientLL.h"
#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19
# include "GonkNativeWindowClientKK.h"
#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
# include "GonkNativeWindowClientJB.h"
#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION == 15
# include "GonkNativeWindowClientICS.h"
#endif
@@ -1,446 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
* Copyright (C) 2012 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 "CameraCommon.h"
#include "GonkNativeWindow.h"
#include "GonkNativeWindowClient.h"
#include "nsDebug.h"
/**
* DOM_CAMERA_LOGI() is enabled in debug builds, and turned on by setting
* NSPR_LOG_MODULES=Camera:N environment variable, where N >= 3.
*
* CNW_LOGE() is always enabled.
*/
#define CNW_LOGD(...) DOM_CAMERA_LOGI(__VA_ARGS__)
#define CNW_LOGE(...) {(void)printf_stderr(__VA_ARGS__);}
using namespace android;
using namespace mozilla::layers;
GonkNativeWindowClient::GonkNativeWindowClient(const sp<GonkNativeWindow>& window)
: mNativeWindow(window) {
GonkNativeWindowClient::init();
}
GonkNativeWindowClient::~GonkNativeWindowClient() {
if (mConnectedToCpu) {
GonkNativeWindowClient::disconnect(NATIVE_WINDOW_API_CPU);
}
}
void GonkNativeWindowClient::init() {
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = hook_setSwapInterval;
ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
ANativeWindow::cancelBuffer = hook_cancelBuffer;
ANativeWindow::lockBuffer = hook_lockBuffer;
ANativeWindow::queueBuffer = hook_queueBuffer;
ANativeWindow::query = hook_query;
ANativeWindow::perform = hook_perform;
mReqWidth = 0;
mReqHeight = 0;
mReqFormat = 0;
mReqUsage = 0;
mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
mDefaultWidth = 0;
mDefaultHeight = 0;
mTransformHint = 0;
mConnectedToCpu = false;
}
int GonkNativeWindowClient::hook_setSwapInterval(ANativeWindow* window, int interval) {
GonkNativeWindowClient* c = getSelf(window);
return c->setSwapInterval(interval);
}
int GonkNativeWindowClient::hook_dequeueBuffer(ANativeWindow* window,
ANativeWindowBuffer** buffer) {
GonkNativeWindowClient* c = getSelf(window);
return c->dequeueBuffer(buffer);
}
int GonkNativeWindowClient::hook_cancelBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer) {
GonkNativeWindowClient* c = getSelf(window);
return c->cancelBuffer(buffer);
}
int GonkNativeWindowClient::hook_lockBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer) {
GonkNativeWindowClient* c = getSelf(window);
return c->lockBuffer(buffer);
}
int GonkNativeWindowClient::hook_queueBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer) {
GonkNativeWindowClient* c = getSelf(window);
return c->queueBuffer(buffer);
}
int GonkNativeWindowClient::hook_query(const ANativeWindow* window,
int what, int* value) {
const GonkNativeWindowClient* c = getSelf(window);
return c->query(what, value);
}
int GonkNativeWindowClient::hook_perform(ANativeWindow* window, int operation, ...) {
va_list args;
va_start(args, operation);
GonkNativeWindowClient* c = getSelf(window);
return c->perform(operation, args);
}
int GonkNativeWindowClient::setSwapInterval(int interval) {
return NO_ERROR;
}
int GonkNativeWindowClient::dequeueBuffer(android_native_buffer_t** buffer) {
CNW_LOGD("GonkNativeWindowClient::dequeueBuffer");
Mutex::Autolock lock(mMutex);
int buf = -1;
status_t result = mNativeWindow->dequeueBuffer(&buf, mReqWidth, mReqHeight,
mReqFormat, mReqUsage);
if (result < 0) {
CNW_LOGD("dequeueBuffer: ISurfaceTexture::dequeueBuffer(%d, %d, %d, %d)"
"failed: %d", mReqWidth, mReqHeight, mReqFormat, mReqUsage,
result);
return result;
}
sp<GraphicBuffer>& gbuf(mSlots[buf]);
if (result & ISurfaceTexture::RELEASE_ALL_BUFFERS) {
freeAllBuffers();
}
if ((result & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
result = mNativeWindow->requestBuffer(buf, &gbuf);
if (result != NO_ERROR) {
CNW_LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed: %d",
result);
return result;
}
}
*buffer = gbuf.get();
return OK;
}
int GonkNativeWindowClient::cancelBuffer(ANativeWindowBuffer* buffer) {
CNW_LOGD("GonkNativeWindowClient::cancelBuffer");
Mutex::Autolock lock(mMutex);
int i = getSlotFromBufferLocked(buffer);
if (i < 0) {
return i;
}
mNativeWindow->cancelBuffer(i);
return OK;
}
int GonkNativeWindowClient::getSlotFromBufferLocked(
android_native_buffer_t* buffer) const {
bool dumpedState = false;
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
// XXX: Dump the slots whenever we hit a NULL entry while searching for
// a buffer.
if (mSlots[i] == NULL) {
if (!dumpedState) {
CNW_LOGD("getSlotFromBufferLocked: encountered NULL buffer in slot %d "
"looking for buffer %p", i, buffer->handle);
for (int j = 0; j < NUM_BUFFER_SLOTS; j++) {
if (mSlots[j] == NULL) {
CNW_LOGD("getSlotFromBufferLocked: %02d: NULL", j);
} else {
CNW_LOGD("getSlotFromBufferLocked: %02d: %p", j, mSlots[j]->handle);
}
}
dumpedState = true;
}
}
if (mSlots[i] != NULL && mSlots[i]->handle == buffer->handle) {
return i;
}
}
CNW_LOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
return BAD_VALUE;
}
int GonkNativeWindowClient::lockBuffer(android_native_buffer_t* buffer) {
CNW_LOGD("GonkNativeWindowClient::lockBuffer");
Mutex::Autolock lock(mMutex);
return OK;
}
int GonkNativeWindowClient::queueBuffer(android_native_buffer_t* buffer) {
CNW_LOGD("GonkNativeWindowClient::queueBuffer");
Mutex::Autolock lock(mMutex);
int64_t timestamp;
if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
CNW_LOGD("GonkNativeWindowClient::queueBuffer making up timestamp: %.2f ms",
timestamp / 1000000.f);
} else {
timestamp = mTimestamp;
}
int i = getSlotFromBufferLocked(buffer);
if (i < 0) {
return i;
}
status_t err = mNativeWindow->queueBuffer(i, timestamp,
&mDefaultWidth, &mDefaultHeight, &mTransformHint);
if (err != OK) {
CNW_LOGE("queueBuffer: error queuing buffer to GonkNativeWindow, %d", err);
}
return err;
}
int GonkNativeWindowClient::query(int what, int* value) const {
CNW_LOGD("query");
{ // scope for the lock
Mutex::Autolock lock(mMutex);
switch (what) {
case NATIVE_WINDOW_FORMAT:
if (mReqFormat) {
*value = mReqFormat;
return NO_ERROR;
}
break;
case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
*value = 0;
return NO_ERROR;
case NATIVE_WINDOW_CONCRETE_TYPE:
*value = NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT;
return NO_ERROR;
case NATIVE_WINDOW_DEFAULT_WIDTH:
*value = mDefaultWidth;
return NO_ERROR;
case NATIVE_WINDOW_DEFAULT_HEIGHT:
*value = mDefaultHeight;
return NO_ERROR;
case NATIVE_WINDOW_TRANSFORM_HINT:
*value = mTransformHint;
return NO_ERROR;
}
}
return mNativeWindow->query(what, value);
}
int GonkNativeWindowClient::perform(int operation, va_list args)
{
int res = NO_ERROR;
switch (operation) {
case NATIVE_WINDOW_CONNECT:
// deprecated. must return NO_ERROR.
break;
case NATIVE_WINDOW_DISCONNECT:
// deprecated. must return NO_ERROR.
break;
case NATIVE_WINDOW_SET_SCALING_MODE:
return NO_ERROR;
case NATIVE_WINDOW_SET_USAGE:
res = dispatchSetUsage(args);
break;
case NATIVE_WINDOW_SET_CROP:
//not implemented
break;
case NATIVE_WINDOW_SET_BUFFER_COUNT:
res = dispatchSetBufferCount(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
res = dispatchSetBuffersGeometry(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
//not implemented
break;
case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
res = dispatchSetBuffersTimestamp(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS:
res = dispatchSetBuffersDimensions(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
res = dispatchSetBuffersFormat(args);
break;
case NATIVE_WINDOW_LOCK:
res = INVALID_OPERATION;// not supported
break;
case NATIVE_WINDOW_UNLOCK_AND_POST:
res = INVALID_OPERATION;// not supported
break;
case NATIVE_WINDOW_API_CONNECT:
res = dispatchConnect(args);
break;
case NATIVE_WINDOW_API_DISCONNECT:
res = dispatchDisconnect(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_SIZE:
//not implemented
break;
default:
res = NAME_NOT_FOUND;
break;
}
return res;
}
int GonkNativeWindowClient::dispatchConnect(va_list args) {
int api = va_arg(args, int);
return connect(api);
}
int GonkNativeWindowClient::dispatchDisconnect(va_list args) {
int api = va_arg(args, int);
return disconnect(api);
}
int GonkNativeWindowClient::dispatchSetUsage(va_list args) {
int usage = va_arg(args, int);
return setUsage(usage);
}
int GonkNativeWindowClient::dispatchSetBufferCount(va_list args) {
size_t bufferCount = va_arg(args, size_t);
return setBufferCount(bufferCount);
}
int GonkNativeWindowClient::dispatchSetBuffersGeometry(va_list args) {
int w = va_arg(args, int);
int h = va_arg(args, int);
int f = va_arg(args, int);
int err = setBuffersDimensions(w, h);
if (err != 0) {
return err;
}
return setBuffersFormat(f);
}
int GonkNativeWindowClient::dispatchSetBuffersDimensions(va_list args) {
int w = va_arg(args, int);
int h = va_arg(args, int);
return setBuffersDimensions(w, h);
}
int GonkNativeWindowClient::dispatchSetBuffersFormat(va_list args) {
int f = va_arg(args, int);
return setBuffersFormat(f);
}
int GonkNativeWindowClient::dispatchSetBuffersTimestamp(va_list args) {
int64_t timestamp = va_arg(args, int64_t);
return setBuffersTimestamp(timestamp);
}
int GonkNativeWindowClient::connect(int api) {
CNW_LOGD("GonkNativeWindowClient::connect");
Mutex::Autolock lock(mMutex);
int err = mNativeWindow->connect(api,
&mDefaultWidth, &mDefaultHeight, &mTransformHint);
if (!err && api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = true;
}
return err;
}
int GonkNativeWindowClient::disconnect(int api) {
CNW_LOGD("GonkNativeWindowClient::disconnect");
Mutex::Autolock lock(mMutex);
freeAllBuffers();
int err = mNativeWindow->disconnect(api);
if (!err) {
mReqFormat = 0;
mReqWidth = 0;
mReqHeight = 0;
mReqUsage = 0;
if (api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = false;
}
}
return err;
}
int GonkNativeWindowClient::setUsage(uint32_t reqUsage)
{
CNW_LOGD("GonkNativeWindowClient::setUsage");
Mutex::Autolock lock(mMutex);
mReqUsage = reqUsage;
return OK;
}
int GonkNativeWindowClient::setBufferCount(int bufferCount)
{
CNW_LOGD("GonkNativeWindowClient::setBufferCount");
Mutex::Autolock lock(mMutex);
status_t err = mNativeWindow->setBufferCount(bufferCount);
if (err == NO_ERROR) {
freeAllBuffers();
}
return err;
}
int GonkNativeWindowClient::setBuffersDimensions(int w, int h)
{
CNW_LOGD("GonkNativeWindowClient::setBuffersDimensions");
Mutex::Autolock lock(mMutex);
if (w<0 || h<0)
return BAD_VALUE;
if ((w && !h) || (!w && h))
return BAD_VALUE;
mReqWidth = w;
mReqHeight = h;
status_t err = OK;
return err;
}
int GonkNativeWindowClient::setBuffersFormat(int format)
{
CNW_LOGD("GonkNativeWindowClient::setBuffersFormat");
Mutex::Autolock lock(mMutex);
if (format<0)
return BAD_VALUE;
mReqFormat = format;
return NO_ERROR;
}
int GonkNativeWindowClient::setBuffersTimestamp(int64_t timestamp)
{
CNW_LOGD("GonkNativeWindowClient::setBuffersTimestamp");
Mutex::Autolock lock(mMutex);
mTimestamp = timestamp;
return NO_ERROR;
}
void GonkNativeWindowClient::freeAllBuffers() {
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
mSlots[i] = 0;
}
}
@@ -1,136 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
* Copyright (C) 2012 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 NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_ICS_H
#define NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_ICS_H
#include <ui/egl/android_natives.h>
#include "GonkNativeWindow.h"
namespace android {
class GonkNativeWindowClient : public EGLNativeBase<ANativeWindow, GonkNativeWindowClient, RefBase>
{
public:
GonkNativeWindowClient(const sp<GonkNativeWindow>& window);
~GonkNativeWindowClient(); // this class cannot be overloaded
private:
void init();
// ANativeWindow hooks
static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer);
static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer);
static int hook_lockBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer);
static int hook_perform(ANativeWindow* window, int operation, ...);
static int hook_query(const ANativeWindow* window, int what, int* value);
static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer);
static int hook_setSwapInterval(ANativeWindow* window, int interval);
int dispatchConnect(va_list args);
int dispatchDisconnect(va_list args);
int dispatchSetBufferCount(va_list args);
int dispatchSetBuffersGeometry(va_list args);
int dispatchSetBuffersDimensions(va_list args);
int dispatchSetBuffersFormat(va_list args);
int dispatchSetBuffersTimestamp(va_list args);
int dispatchSetUsage(va_list args);
protected:
virtual int cancelBuffer(ANativeWindowBuffer* buffer);
virtual int dequeueBuffer(ANativeWindowBuffer** buffer);
virtual int lockBuffer(ANativeWindowBuffer* buffer);
virtual int perform(int operation, va_list args);
virtual int query(int what, int* value) const;
virtual int queueBuffer(ANativeWindowBuffer* buffer);
virtual int setSwapInterval(int interval);
virtual int connect(int api);
virtual int disconnect(int api);
virtual int setBufferCount(int bufferCount);
virtual int setBuffersDimensions(int w, int h);
virtual int setBuffersFormat(int format);
virtual int setBuffersTimestamp(int64_t timestamp);
virtual int setUsage(uint32_t reqUsage);
int getNumberOfArgsForOperation(int operation);
enum { MIN_UNDEQUEUED_BUFFERS = GonkNativeWindow::MIN_UNDEQUEUED_BUFFERS };
enum { NUM_BUFFER_SLOTS = GonkNativeWindow::NUM_BUFFER_SLOTS };
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
enum { NATIVE_WINDOW_SET_BUFFERS_SIZE = 0x10000000 };
private:
void freeAllBuffers();
int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
sp<GonkNativeWindow> mNativeWindow;
// mSlots stores the buffers that have been allocated for each buffer slot.
// It is initialized to null pointers, and gets filled in with the result of
// ISurfaceTexture::requestBuffer when the client dequeues a buffer from a
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
sp<GraphicBuffer> mSlots[NUM_BUFFER_SLOTS];
// mReqWidth is the buffer width that will be requested at the next dequeue
// operation. It is initialized to 1.
uint32_t mReqWidth;
// mReqHeight is the buffer height that will be requested at the next deuque
// operation. It is initialized to 1.
uint32_t mReqHeight;
// mReqFormat is the buffer pixel format that will be requested at the next
// deuque operation. It is initialized to PIXEL_FORMAT_RGBA_8888.
uint32_t mReqFormat;
// mReqUsage is the set of buffer usage flags that will be requested
// at the next deuque operation. It is initialized to 0.
uint32_t mReqUsage;
// mTimestamp is the timestamp that will be used for the next buffer queue
// operation. It defaults to NATIVE_WINDOW_TIMESTAMP_AUTO, which means that
// a timestamp is auto-generated when queueBuffer is called.
int64_t mTimestamp;
// mDefaultWidth is default width of the window, regardless of the
// native_window_set_buffers_dimensions call
uint32_t mDefaultWidth;
// mDefaultHeight is default width of the window, regardless of the
// native_window_set_buffers_dimensions call
uint32_t mDefaultHeight;
// mTransformHint is the transform probably applied to buffers of this
// window. this is only a hint, actual transform may differ.
uint32_t mTransformHint;
// mMutex is the mutex used to prevent concurrent access to the member
// variables of GonkNativeWindow objects. It must be locked whenever the
// member variables are accessed.
mutable Mutex mMutex;
bool mConnectedToCpu;
};
}; // namespace android
#endif // NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_ICS_H
@@ -1,679 +0,0 @@
/*
* Copyright (C) 2010 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.
*/
#define LOG_TAG "GonkNativeWindowClient"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
#include <android/native_window.h>
#if ANDROID_VERSION == 17
#include <utils/Trace.h>
#else
#include <cutils/trace.h>
#endif
#include <binder/Parcel.h>
#include <utils/Log.h>
#include <ui/Fence.h>
#include "GonkNativeWindowClientJB.h"
namespace android {
GonkNativeWindowClient::GonkNativeWindowClient(
const sp<IGraphicBufferProducer>& bufferProducer)
: mBufferProducer(bufferProducer)
{
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = hook_setSwapInterval;
ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
ANativeWindow::cancelBuffer = hook_cancelBuffer;
ANativeWindow::queueBuffer = hook_queueBuffer;
ANativeWindow::query = hook_query;
ANativeWindow::perform = hook_perform;
ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;
ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;
ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
const_cast<int&>(ANativeWindow::minSwapInterval) = 0;
const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
mReqWidth = 0;
mReqHeight = 0;
mReqFormat = 0;
mReqUsage = 0;
mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
mCrop.clear();
mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
mTransform = 0;
mDefaultWidth = 0;
mDefaultHeight = 0;
mUserWidth = 0;
mUserHeight = 0;
mTransformHint = 0;
mConsumerRunningBehind = false;
mConnectedToCpu = false;
}
GonkNativeWindowClient::~GonkNativeWindowClient() {
if (mConnectedToCpu) {
GonkNativeWindowClient::disconnect(NATIVE_WINDOW_API_CPU);
}
}
#if ANDROID_VERSION == 17
sp<IGraphicBufferProducer> GonkNativeWindowClient::getISurfaceTexture() const {
#else
sp<IGraphicBufferProducer> GonkNativeWindowClient::getIGraphicBufferProducer() const {
#endif
return mBufferProducer;
}
int GonkNativeWindowClient::hook_setSwapInterval(ANativeWindow* window, int interval) {
GonkNativeWindowClient* c = getSelf(window);
return c->setSwapInterval(interval);
}
int GonkNativeWindowClient::hook_dequeueBuffer(ANativeWindow* window,
ANativeWindowBuffer** buffer, int* fenceFd) {
GonkNativeWindowClient* c = getSelf(window);
return c->dequeueBuffer(buffer, fenceFd);
}
int GonkNativeWindowClient::hook_cancelBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd) {
GonkNativeWindowClient* c = getSelf(window);
return c->cancelBuffer(buffer, fenceFd);
}
int GonkNativeWindowClient::hook_queueBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd) {
GonkNativeWindowClient* c = getSelf(window);
return c->queueBuffer(buffer, fenceFd);
}
int GonkNativeWindowClient::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer** buffer) {
GonkNativeWindowClient* c = getSelf(window);
ANativeWindowBuffer* buf;
int fenceFd = -1;
int result = c->dequeueBuffer(&buf, &fenceFd);
sp<Fence> fence(new Fence(fenceFd));
#if ANDROID_VERSION == 17
int waitResult = fence->waitForever(1000, "dequeueBuffer_DEPRECATED");
#else
int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
#endif
if (waitResult != OK) {
ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d",
waitResult);
c->cancelBuffer(buf, -1);
return waitResult;
}
*buffer = buf;
return result;
}
int GonkNativeWindowClient::hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer) {
GonkNativeWindowClient* c = getSelf(window);
return c->cancelBuffer(buffer, -1);
}
int GonkNativeWindowClient::hook_lockBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer) {
GonkNativeWindowClient* c = getSelf(window);
return c->lockBuffer_DEPRECATED(buffer);
}
int GonkNativeWindowClient::hook_queueBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer) {
GonkNativeWindowClient* c = getSelf(window);
return c->queueBuffer(buffer, -1);
}
int GonkNativeWindowClient::hook_query(const ANativeWindow* window,
int what, int* value) {
const GonkNativeWindowClient* c = getSelf(window);
return c->query(what, value);
}
int GonkNativeWindowClient::hook_perform(ANativeWindow* window, int operation, ...) {
va_list args;
va_start(args, operation);
GonkNativeWindowClient* c = getSelf(window);
return c->perform(operation, args);
}
int GonkNativeWindowClient::setSwapInterval(int interval) {
// EGL specification states:
// interval is silently clamped to minimum and maximum implementation
// dependent values before being stored.
// Although we don't have to, we apply the same logic here.
if (interval < minSwapInterval)
interval = minSwapInterval;
if (interval > maxSwapInterval)
interval = maxSwapInterval;
status_t res = mBufferProducer->setSynchronousMode(interval ? true : false);
return res;
}
int GonkNativeWindowClient::dequeueBuffer(android_native_buffer_t** buffer,
int* fenceFd) {
ALOGV("GonkNativeWindowClient::dequeueBuffer");
Mutex::Autolock lock(mMutex);
int buf = -1;
int reqW = mReqWidth ? mReqWidth : mUserWidth;
int reqH = mReqHeight ? mReqHeight : mUserHeight;
sp<Fence> fence;
#if ANDROID_VERSION == 17
status_t result = mBufferProducer->dequeueBuffer(&buf, fence,
reqW, reqH, mReqFormat, mReqUsage);
#else
status_t result = mBufferProducer->dequeueBuffer(&buf, &fence,
reqW, reqH, mReqFormat, mReqUsage);
#endif
if (result < 0) {
ALOGV("dequeueBuffer: dequeueBuffer(%d, %d, %d, %d)"
"failed: %d", mReqWidth, mReqHeight, mReqFormat, mReqUsage,
result);
return result;
}
sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
freeAllBuffers();
}
if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
result = mBufferProducer->requestBuffer(buf, &gbuf);
if (result != NO_ERROR) {
ALOGE("dequeueBuffer: requestBuffer failed: %d",
result);
return result;
}
}
if (fence.get() && fence->isValid()) {
*fenceFd = fence->dup();
if (*fenceFd == -1) {
ALOGE("dequeueBuffer: error duping fence: %d", errno);
// dup() should never fail; something is badly wrong. Soldier on
// and hope for the best; the worst that should happen is some
// visible corruption that lasts until the next frame.
}
} else {
*fenceFd = -1;
}
*buffer = gbuf.get();
return OK;
}
int GonkNativeWindowClient::cancelBuffer(android_native_buffer_t* buffer,
int fenceFd) {
ALOGV("GonkNativeWindowClient::cancelBuffer");
Mutex::Autolock lock(mMutex);
int i = getSlotFromBufferLocked(buffer);
if (i < 0) {
return i;
}
sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
mBufferProducer->cancelBuffer(i, fence);
return OK;
}
int GonkNativeWindowClient::getSlotFromBufferLocked(
android_native_buffer_t* buffer) const {
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
if (mSlots[i].buffer != NULL &&
mSlots[i].buffer->handle == buffer->handle) {
return i;
}
}
ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
return BAD_VALUE;
}
int GonkNativeWindowClient::lockBuffer_DEPRECATED(android_native_buffer_t* buffer) {
ALOGV("GonkNativeWindowClient::lockBuffer");
Mutex::Autolock lock(mMutex);
return OK;
}
int GonkNativeWindowClient::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
ALOGV("GonkNativeWindowClient::queueBuffer");
Mutex::Autolock lock(mMutex);
int64_t timestamp;
if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
ALOGV("GonkNativeWindowClient::queueBuffer making up timestamp: %.2f ms",
timestamp / 1000000.f);
} else {
timestamp = mTimestamp;
}
int i = getSlotFromBufferLocked(buffer);
if (i < 0) {
return i;
}
// Make sure the crop rectangle is entirely inside the buffer.
Rect crop;
mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
IGraphicBufferProducer::QueueBufferOutput output;
IGraphicBufferProducer::QueueBufferInput input(timestamp, crop, mScalingMode,
mTransform, fence);
status_t err = mBufferProducer->queueBuffer(i, input, &output);
if (err != OK) {
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
}
uint32_t numPendingBuffers = 0;
output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint,
&numPendingBuffers);
mConsumerRunningBehind = (numPendingBuffers >= 2);
return err;
}
int GonkNativeWindowClient::query(int what, int* value) const {
ALOGV("GonkNativeWindowClient::query");
{ // scope for the lock
Mutex::Autolock lock(mMutex);
switch (what) {
case NATIVE_WINDOW_FORMAT:
if (mReqFormat) {
*value = mReqFormat;
return NO_ERROR;
}
break;
case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
//sp<ISurfaceComposer> composer(
// ComposerService::getComposerService());
//if (composer->authenticateSurfaceTexture(mBufferProducer)) {
// *value = 1;
//} else {
*value = 0;
//}
return NO_ERROR;
}
case NATIVE_WINDOW_CONCRETE_TYPE:
#if ANDROID_VERSION == 17
*value = NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT;
#else
*value = NATIVE_WINDOW_SURFACE;
#endif
return NO_ERROR;
case NATIVE_WINDOW_DEFAULT_WIDTH:
*value = mUserWidth ? mUserWidth : mDefaultWidth;
return NO_ERROR;
case NATIVE_WINDOW_DEFAULT_HEIGHT:
*value = mUserHeight ? mUserHeight : mDefaultHeight;
return NO_ERROR;
case NATIVE_WINDOW_TRANSFORM_HINT:
*value = mTransformHint;
return NO_ERROR;
case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: {
status_t err = NO_ERROR;
if (!mConsumerRunningBehind) {
*value = 0;
} else {
err = mBufferProducer->query(what, value);
if (err == NO_ERROR) {
mConsumerRunningBehind = *value;
}
}
return err;
}
}
}
return mBufferProducer->query(what, value);
}
int GonkNativeWindowClient::perform(int operation, va_list args)
{
int res = NO_ERROR;
switch (operation) {
case NATIVE_WINDOW_CONNECT:
// deprecated. must return NO_ERROR.
break;
case NATIVE_WINDOW_DISCONNECT:
// deprecated. must return NO_ERROR.
break;
case NATIVE_WINDOW_SET_USAGE:
res = dispatchSetUsage(args);
break;
case NATIVE_WINDOW_SET_CROP:
res = dispatchSetCrop(args);
break;
case NATIVE_WINDOW_SET_BUFFER_COUNT:
res = dispatchSetBufferCount(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
res = dispatchSetBuffersGeometry(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
res = dispatchSetBuffersTransform(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
res = dispatchSetBuffersTimestamp(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS:
res = dispatchSetBuffersDimensions(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS:
res = dispatchSetBuffersUserDimensions(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
res = dispatchSetBuffersFormat(args);
break;
case NATIVE_WINDOW_LOCK:
res = dispatchLock(args);
break;
case NATIVE_WINDOW_UNLOCK_AND_POST:
res = dispatchUnlockAndPost(args);
break;
case NATIVE_WINDOW_SET_SCALING_MODE:
res = dispatchSetScalingMode(args);
break;
case NATIVE_WINDOW_API_CONNECT:
res = dispatchConnect(args);
break;
case NATIVE_WINDOW_API_DISCONNECT:
res = dispatchDisconnect(args);
break;
default:
res = NAME_NOT_FOUND;
break;
}
return res;
}
int GonkNativeWindowClient::dispatchConnect(va_list args) {
int api = va_arg(args, int);
return connect(api);
}
int GonkNativeWindowClient::dispatchDisconnect(va_list args) {
int api = va_arg(args, int);
return disconnect(api);
}
int GonkNativeWindowClient::dispatchSetUsage(va_list args) {
int usage = va_arg(args, int);
return setUsage(usage);
}
int GonkNativeWindowClient::dispatchSetCrop(va_list args) {
android_native_rect_t const* rect = va_arg(args, android_native_rect_t*);
return setCrop(reinterpret_cast<Rect const*>(rect));
}
int GonkNativeWindowClient::dispatchSetBufferCount(va_list args) {
size_t bufferCount = va_arg(args, size_t);
return setBufferCount(bufferCount);
}
int GonkNativeWindowClient::dispatchSetBuffersGeometry(va_list args) {
int w = va_arg(args, int);
int h = va_arg(args, int);
int f = va_arg(args, int);
int err = setBuffersDimensions(w, h);
if (err != 0) {
return err;
}
return setBuffersFormat(f);
}
int GonkNativeWindowClient::dispatchSetBuffersDimensions(va_list args) {
int w = va_arg(args, int);
int h = va_arg(args, int);
return setBuffersDimensions(w, h);
}
int GonkNativeWindowClient::dispatchSetBuffersUserDimensions(va_list args) {
int w = va_arg(args, int);
int h = va_arg(args, int);
return setBuffersUserDimensions(w, h);
}
int GonkNativeWindowClient::dispatchSetBuffersFormat(va_list args) {
int f = va_arg(args, int);
return setBuffersFormat(f);
}
int GonkNativeWindowClient::dispatchSetScalingMode(va_list args) {
int m = va_arg(args, int);
return setScalingMode(m);
}
int GonkNativeWindowClient::dispatchSetBuffersTransform(va_list args) {
int transform = va_arg(args, int);
return setBuffersTransform(transform);
}
int GonkNativeWindowClient::dispatchSetBuffersTimestamp(va_list args) {
int64_t timestamp = va_arg(args, int64_t);
return setBuffersTimestamp(timestamp);
}
int GonkNativeWindowClient::dispatchLock(va_list args) {
ANativeWindow_Buffer* outBuffer = va_arg(args, ANativeWindow_Buffer*);
ARect* inOutDirtyBounds = va_arg(args, ARect*);
return lock(outBuffer, inOutDirtyBounds);
}
int GonkNativeWindowClient::dispatchUnlockAndPost(va_list args) {
return unlockAndPost();
}
int GonkNativeWindowClient::connect(int api) {
ALOGV("GonkNativeWindowClient::connect");
Mutex::Autolock lock(mMutex);
IGraphicBufferProducer::QueueBufferOutput output;
int err = mBufferProducer->connect(api, &output);
if (err == NO_ERROR) {
uint32_t numPendingBuffers = 0;
output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint,
&numPendingBuffers);
mConsumerRunningBehind = (numPendingBuffers >= 2);
}
if (!err && api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = true;
}
return err;
}
int GonkNativeWindowClient::disconnect(int api) {
ALOGV("GonkNativeWindowClient::disconnect");
Mutex::Autolock lock(mMutex);
freeAllBuffers();
int err = mBufferProducer->disconnect(api);
if (!err) {
mReqFormat = 0;
mReqWidth = 0;
mReqHeight = 0;
mReqUsage = 0;
mCrop.clear();
mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
mTransform = 0;
if (api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = false;
}
}
return err;
}
int GonkNativeWindowClient::setUsage(uint32_t reqUsage)
{
ALOGV("GonkNativeWindowClient::setUsage");
Mutex::Autolock lock(mMutex);
mReqUsage = reqUsage;
return OK;
}
int GonkNativeWindowClient::setCrop(Rect const* rect)
{
Rect realRect;
if (rect == NULL || rect->isEmpty()) {
realRect.clear();
} else {
realRect = *rect;
}
ALOGV("GonkNativeWindowClient::setCrop rect=[%d %d %d %d]",
realRect.left, realRect.top, realRect.right, realRect.bottom);
Mutex::Autolock lock(mMutex);
mCrop = realRect;
return NO_ERROR;
}
int GonkNativeWindowClient::setBufferCount(int bufferCount)
{
ALOGV("GonkNativeWindowClient::setBufferCount");
Mutex::Autolock lock(mMutex);
status_t err = mBufferProducer->setBufferCount(bufferCount);
ALOGE_IF(err, "IGraphicBufferProducer::setBufferCount(%d) returned %s",
bufferCount, strerror(-err));
if (err == NO_ERROR) {
freeAllBuffers();
}
return err;
}
int GonkNativeWindowClient::setBuffersDimensions(int w, int h)
{
ALOGV("GonkNativeWindowClient::setBuffersDimensions");
if (w<0 || h<0)
return BAD_VALUE;
if ((w && !h) || (!w && h))
return BAD_VALUE;
Mutex::Autolock lock(mMutex);
mReqWidth = w;
mReqHeight = h;
return NO_ERROR;
}
int GonkNativeWindowClient::setBuffersUserDimensions(int w, int h)
{
ALOGV("GonkNativeWindowClient::setBuffersUserDimensions");
if (w<0 || h<0)
return BAD_VALUE;
if ((w && !h) || (!w && h))
return BAD_VALUE;
Mutex::Autolock lock(mMutex);
mUserWidth = w;
mUserHeight = h;
return NO_ERROR;
}
int GonkNativeWindowClient::setBuffersFormat(int format)
{
ALOGV("GonkNativeWindowClient::setBuffersFormat");
if (format<0)
return BAD_VALUE;
Mutex::Autolock lock(mMutex);
mReqFormat = format;
return NO_ERROR;
}
int GonkNativeWindowClient::setScalingMode(int mode)
{
ALOGV("GonkNativeWindowClient::setScalingMode(%d)", mode);
switch (mode) {
case NATIVE_WINDOW_SCALING_MODE_FREEZE:
case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
break;
default:
ALOGE("unknown scaling mode: %d", mode);
return BAD_VALUE;
}
Mutex::Autolock lock(mMutex);
mScalingMode = mode;
return NO_ERROR;
}
int GonkNativeWindowClient::setBuffersTransform(int transform)
{
ALOGV("GonkNativeWindowClient::setBuffersTransform");
Mutex::Autolock lock(mMutex);
mTransform = transform;
return NO_ERROR;
}
int GonkNativeWindowClient::setBuffersTimestamp(int64_t timestamp)
{
ALOGV("GonkNativeWindowClient::setBuffersTimestamp");
Mutex::Autolock lock(mMutex);
mTimestamp = timestamp;
return NO_ERROR;
}
void GonkNativeWindowClient::freeAllBuffers() {
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
mSlots[i].buffer = 0;
}
}
// ----------------------------------------------------------------------
// the lock/unlock APIs must be used from the same thread
// ----------------------------------------------------------------------------
status_t GonkNativeWindowClient::lock(
ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{
return INVALID_OPERATION;
}
status_t GonkNativeWindowClient::unlockAndPost()
{
return INVALID_OPERATION;
}
}; // namespace android
@@ -1,262 +0,0 @@
/*
* Copyright (C) 2010 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.
*/
#ifndef NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_JB_H
#define NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_JB_H
#if ANDROID_VERSION == 17
#include <gui/ISurfaceTexture.h>
#else
#include <gui/IGraphicBufferProducer.h>
#endif
#include <ui/ANativeObjectBase.h>
#include <ui/Region.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
#include <utils/KeyedVector.h>
#include "mozilla/Types.h"
#include "GonkBufferQueue.h"
struct ANativeWindow_Buffer;
namespace android {
/*
* An implementation of ANativeWindow that feeds graphics buffers into a
* BufferQueue.
*
* This is typically used by programs that want to render frames through
* some means (maybe OpenGL, a software renderer, or a hardware decoder)
* and have the frames they create forwarded to SurfaceFlinger for
* compositing. For example, a video decoder could render a frame and call
* eglSwapBuffers(), which invokes ANativeWindow callbacks defined by
* GonkNativeWindowClient. GonkNativeWindowClient then forwards the buffers through Binder IPC
* to the BufferQueue's producer interface, providing the new frame to a
* consumer such as GLConsumer.
*/
class GonkNativeWindowClient
: public ANativeObjectBase<ANativeWindow, GonkNativeWindowClient, RefBase>
{
public:
/*
* creates a GonkNativeWindowClient from the given IGraphicBufferProducer (which concrete
* implementation is a BufferQueue).
*
* GonkNativeWindowClient is mainly state-less while it's disconnected, it can be
* viewed as a glorified IGraphicBufferProducer holder. It's therefore
* safe to create other GonkNativeWindowClients from the same IGraphicBufferProducer.
*
* However, once a GonkNativeWindowClient is connected, it'll prevent other GonkNativeWindowClients
* referring to the same IGraphicBufferProducer to become connected and
* therefore prevent them to be used as actual producers of buffers.
*/
GonkNativeWindowClient(const sp<IGraphicBufferProducer>& bufferProducer);
/* getIGraphicBufferProducer() returns the IGraphicBufferProducer this
* GonkNativeWindowClient was created with. Usually it's an error to use the
* IGraphicBufferProducer while the GonkNativeWindowClient is connected.
*/
#if ANDROID_VERSION == 17
sp<IGraphicBufferProducer> getISurfaceTexture() const;
#else
sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
#endif
/* convenience function to check that the given surface is non NULL as
* well as its IGraphicBufferProducer */
#if ANDROID_VERSION >= 18
static bool isValid(const sp<GonkNativeWindowClient>& surface) {
return surface != NULL && surface->getIGraphicBufferProducer() != NULL;
}
#endif
protected:
virtual ~GonkNativeWindowClient();
private:
// can't be copied
GonkNativeWindowClient& operator = (const GonkNativeWindowClient& rhs);
GonkNativeWindowClient(const GonkNativeWindowClient& rhs);
// ANativeWindow hooks
static int hook_cancelBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd);
static int hook_dequeueBuffer(ANativeWindow* window,
ANativeWindowBuffer** buffer, int* fenceFd);
static int hook_perform(ANativeWindow* window, int operation, ...);
static int hook_query(const ANativeWindow* window, int what, int* value);
static int hook_queueBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd);
static int hook_setSwapInterval(ANativeWindow* window, int interval);
static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer);
static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer** buffer);
static int hook_lockBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer);
static int hook_queueBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer);
int dispatchConnect(va_list args);
int dispatchDisconnect(va_list args);
int dispatchSetBufferCount(va_list args);
int dispatchSetBuffersGeometry(va_list args);
int dispatchSetBuffersDimensions(va_list args);
int dispatchSetBuffersUserDimensions(va_list args);
int dispatchSetBuffersFormat(va_list args);
int dispatchSetScalingMode(va_list args);
int dispatchSetBuffersTransform(va_list args);
int dispatchSetBuffersTimestamp(va_list args);
int dispatchSetCrop(va_list args);
int dispatchSetPostTransformCrop(va_list args);
int dispatchSetUsage(va_list args);
int dispatchLock(va_list args);
int dispatchUnlockAndPost(va_list args);
protected:
virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
virtual int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd);
virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd);
virtual int perform(int operation, va_list args);
virtual int query(int what, int* value) const;
virtual int setSwapInterval(int interval);
virtual int lockBuffer_DEPRECATED(ANativeWindowBuffer* buffer);
virtual int connect(int api);
virtual int disconnect(int api);
virtual int setBufferCount(int bufferCount);
virtual int setBuffersDimensions(int w, int h);
virtual int setBuffersUserDimensions(int w, int h);
virtual int setBuffersFormat(int format);
virtual int setScalingMode(int mode);
virtual int setBuffersTransform(int transform);
virtual int setBuffersTimestamp(int64_t timestamp);
virtual int setCrop(Rect const* rect);
virtual int setUsage(uint32_t reqUsage);
public:
virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
virtual int unlockAndPost();
protected:
enum { NUM_BUFFER_SLOTS = GonkBufferQueue::NUM_BUFFER_SLOTS };
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
private:
void freeAllBuffers();
int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
struct BufferSlot {
sp<GraphicBuffer> buffer;
Region dirtyRegion;
};
// mSurfaceTexture is the interface to the surface texture server. All
// operations on the surface texture client ultimately translate into
// interactions with the server using this interface.
sp<IGraphicBufferProducer> mBufferProducer;
// mSlots stores the buffers that have been allocated for each buffer slot.
// It is initialized to null pointers, and gets filled in with the result of
// IGraphicBufferProducer::requestBuffer when the client dequeues a buffer from a
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
BufferSlot mSlots[NUM_BUFFER_SLOTS];
// mReqWidth is the buffer width that will be requested at the next dequeue
// operation. It is initialized to 1.
uint32_t mReqWidth;
// mReqHeight is the buffer height that will be requested at the next
// dequeue operation. It is initialized to 1.
uint32_t mReqHeight;
// mReqFormat is the buffer pixel format that will be requested at the next
// deuque operation. It is initialized to PIXEL_FORMAT_RGBA_8888.
uint32_t mReqFormat;
// mReqUsage is the set of buffer usage flags that will be requested
// at the next deuque operation. It is initialized to 0.
uint32_t mReqUsage;
// mTimestamp is the timestamp that will be used for the next buffer queue
// operation. It defaults to NATIVE_WINDOW_TIMESTAMP_AUTO, which means that
// a timestamp is auto-generated when queueBuffer is called.
int64_t mTimestamp;
// mCrop is the crop rectangle that will be used for the next buffer
// that gets queued. It is set by calling setCrop.
Rect mCrop;
// mScalingMode is the scaling mode that will be used for the next
// buffers that get queued. It is set by calling setScalingMode.
int mScalingMode;
// mTransform is the transform identifier that will be used for the next
// buffer that gets queued. It is set by calling setTransform.
uint32_t mTransform;
// mDefaultWidth is default width of the buffers, regardless of the
// native_window_set_buffers_dimensions call.
uint32_t mDefaultWidth;
// mDefaultHeight is default height of the buffers, regardless of the
// native_window_set_buffers_dimensions call.
uint32_t mDefaultHeight;
// mUserWidth, if non-zero, is an application-specified override
// of mDefaultWidth. This is lower priority than the width set by
// native_window_set_buffers_dimensions.
uint32_t mUserWidth;
// mUserHeight, if non-zero, is an application-specified override
// of mDefaultHeight. This is lower priority than the height set
// by native_window_set_buffers_dimensions.
uint32_t mUserHeight;
// mTransformHint is the transform probably applied to buffers of this
// window. this is only a hint, actual transform may differ.
uint32_t mTransformHint;
// mConsumerRunningBehind whether the consumer is running more than
// one buffer behind the producer.
mutable bool mConsumerRunningBehind;
// mMutex is the mutex used to prevent concurrent access to the member
// variables of GonkNativeWindowClient objects. It must be locked whenever the
// member variables are accessed.
mutable Mutex mMutex;
// must be used from the lock/unlock thread
sp<GraphicBuffer> mLockedBuffer;
sp<GraphicBuffer> mPostedBuffer;
bool mConnectedToCpu;
// must be accessed from lock/unlock thread only
Region mDirtyRegion;
};
}; // namespace android
#endif // NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_JB_H
@@ -1,673 +0,0 @@
/*
* Copyright (C) 2010 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.
*/
#define LOG_TAG "GonkNativeWindowClient"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
#include <android/native_window.h>
#include <binder/Parcel.h>
#include <utils/Log.h>
#include <utils/Trace.h>
#include <ui/Fence.h>
#include "GonkNativeWindowClientKK.h"
namespace android {
GonkNativeWindowClient::GonkNativeWindowClient(
const sp<IGraphicBufferProducer>& bufferProducer,
bool controlledByApp)
: mGraphicBufferProducer(bufferProducer)
{
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = hook_setSwapInterval;
ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
ANativeWindow::cancelBuffer = hook_cancelBuffer;
ANativeWindow::queueBuffer = hook_queueBuffer;
ANativeWindow::query = hook_query;
ANativeWindow::perform = hook_perform;
ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;
ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;
ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
const_cast<int&>(ANativeWindow::minSwapInterval) = 0;
const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
mReqWidth = 0;
mReqHeight = 0;
mReqFormat = 0;
mReqUsage = 0;
mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
mCrop.clear();
mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
mTransform = 0;
mDefaultWidth = 0;
mDefaultHeight = 0;
mUserWidth = 0;
mUserHeight = 0;
mTransformHint = 0;
mConsumerRunningBehind = false;
mConnectedToCpu = false;
mProducerControlledByApp = controlledByApp;
mSwapIntervalZero = false;
}
GonkNativeWindowClient::~GonkNativeWindowClient() {
if (mConnectedToCpu) {
GonkNativeWindowClient::disconnect(NATIVE_WINDOW_API_CPU);
}
}
sp<IGraphicBufferProducer> GonkNativeWindowClient::getIGraphicBufferProducer() const {
return mGraphicBufferProducer;
}
int GonkNativeWindowClient::hook_setSwapInterval(ANativeWindow* window, int interval) {
GonkNativeWindowClient* c = getSelf(window);
return c->setSwapInterval(interval);
}
int GonkNativeWindowClient::hook_dequeueBuffer(ANativeWindow* window,
ANativeWindowBuffer** buffer, int* fenceFd) {
GonkNativeWindowClient* c = getSelf(window);
return c->dequeueBuffer(buffer, fenceFd);
}
int GonkNativeWindowClient::hook_cancelBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd) {
GonkNativeWindowClient* c = getSelf(window);
return c->cancelBuffer(buffer, fenceFd);
}
int GonkNativeWindowClient::hook_queueBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd) {
GonkNativeWindowClient* c = getSelf(window);
return c->queueBuffer(buffer, fenceFd);
}
int GonkNativeWindowClient::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer** buffer) {
GonkNativeWindowClient* c = getSelf(window);
ANativeWindowBuffer* buf;
int fenceFd = -1;
int result = c->dequeueBuffer(&buf, &fenceFd);
sp<Fence> fence(new Fence(fenceFd));
int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
if (waitResult != OK) {
ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d",
waitResult);
c->cancelBuffer(buf, -1);
return waitResult;
}
*buffer = buf;
return result;
}
int GonkNativeWindowClient::hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer) {
GonkNativeWindowClient* c = getSelf(window);
return c->cancelBuffer(buffer, -1);
}
int GonkNativeWindowClient::hook_lockBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer) {
GonkNativeWindowClient* c = getSelf(window);
return c->lockBuffer_DEPRECATED(buffer);
}
int GonkNativeWindowClient::hook_queueBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer) {
GonkNativeWindowClient* c = getSelf(window);
return c->queueBuffer(buffer, -1);
}
int GonkNativeWindowClient::hook_query(const ANativeWindow* window,
int what, int* value) {
const GonkNativeWindowClient* c = getSelf(window);
return c->query(what, value);
}
int GonkNativeWindowClient::hook_perform(ANativeWindow* window, int operation, ...) {
va_list args;
va_start(args, operation);
GonkNativeWindowClient* c = getSelf(window);
return c->perform(operation, args);
}
int GonkNativeWindowClient::setSwapInterval(int interval) {
ATRACE_CALL();
// EGL specification states:
// interval is silently clamped to minimum and maximum implementation
// dependent values before being stored.
if (interval < minSwapInterval)
interval = minSwapInterval;
if (interval > maxSwapInterval)
interval = maxSwapInterval;
return NO_ERROR;
}
int GonkNativeWindowClient::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::dequeueBuffer");
Mutex::Autolock lock(mMutex);
int buf = -1;
int reqW = mReqWidth ? mReqWidth : mUserWidth;
int reqH = mReqHeight ? mReqHeight : mUserHeight;
sp<Fence> fence;
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, mSwapIntervalZero,
reqW, reqH, mReqFormat, mReqUsage);
if (result < 0) {
ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer(%d, %d, %d, %d)"
"failed: %d", mReqWidth, mReqHeight, mReqFormat, mReqUsage,
result);
return result;
}
sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
// this should never happen
ALOGE_IF(fence == NULL, "Surface::dequeueBuffer: received null Fence! buf=%d", buf);
if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
freeAllBuffers();
}
if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
if (result != NO_ERROR) {
ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result);
return result;
}
}
if (fence->isValid()) {
*fenceFd = fence->dup();
if (*fenceFd == -1) {
ALOGE("dequeueBuffer: error duping fence: %d", errno);
// dup() should never fail; something is badly wrong. Soldier on
// and hope for the best; the worst that should happen is some
// visible corruption that lasts until the next frame.
}
} else {
*fenceFd = -1;
}
*buffer = gbuf.get();
return OK;
}
int GonkNativeWindowClient::cancelBuffer(android_native_buffer_t* buffer,
int fenceFd) {
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::cancelBuffer");
Mutex::Autolock lock(mMutex);
int i = getSlotFromBufferLocked(buffer);
if (i < 0) {
return i;
}
sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
mGraphicBufferProducer->cancelBuffer(i, fence);
return OK;
}
int GonkNativeWindowClient::getSlotFromBufferLocked(
android_native_buffer_t* buffer) const {
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
if (mSlots[i].buffer != NULL &&
mSlots[i].buffer->handle == buffer->handle) {
return i;
}
}
ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
return BAD_VALUE;
}
int GonkNativeWindowClient::lockBuffer_DEPRECATED(android_native_buffer_t* buffer) {
ALOGV("GonkNativeWindowClient::lockBuffer");
Mutex::Autolock lock(mMutex);
return OK;
}
int GonkNativeWindowClient::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::queueBuffer");
Mutex::Autolock lock(mMutex);
int64_t timestamp;
bool isAutoTimestamp = false;
if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
isAutoTimestamp = true;
ALOGV("GonkNativeWindowClient::queueBuffer making up timestamp: %.2f ms",
timestamp / 1000000.f);
} else {
timestamp = mTimestamp;
}
int i = getSlotFromBufferLocked(buffer);
if (i < 0) {
return i;
}
// Make sure the crop rectangle is entirely inside the buffer.
Rect crop;
mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
IGraphicBufferProducer::QueueBufferOutput output;
IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
crop, mScalingMode, mTransform, mSwapIntervalZero, fence);
status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
if (err != OK) {
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
}
uint32_t numPendingBuffers = 0;
output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint,
&numPendingBuffers);
mConsumerRunningBehind = (numPendingBuffers >= 2);
return err;
}
int GonkNativeWindowClient::query(int what, int* value) const {
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::query");
{ // scope for the lock
Mutex::Autolock lock(mMutex);
switch (what) {
case NATIVE_WINDOW_FORMAT:
if (mReqFormat) {
*value = mReqFormat;
return NO_ERROR;
}
break;
case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
//sp<ISurfaceComposer> composer(
// ComposerService::getComposerService());
//if (composer->authenticateSurfaceTexture(mGraphicBufferProducer)) {
// *value = 1;
//} else {
*value = 0;
//}
return NO_ERROR;
}
case NATIVE_WINDOW_CONCRETE_TYPE:
*value = NATIVE_WINDOW_SURFACE;
return NO_ERROR;
case NATIVE_WINDOW_DEFAULT_WIDTH:
*value = mUserWidth ? mUserWidth : mDefaultWidth;
return NO_ERROR;
case NATIVE_WINDOW_DEFAULT_HEIGHT:
*value = mUserHeight ? mUserHeight : mDefaultHeight;
return NO_ERROR;
case NATIVE_WINDOW_TRANSFORM_HINT:
*value = mTransformHint;
return NO_ERROR;
case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: {
status_t err = NO_ERROR;
if (!mConsumerRunningBehind) {
*value = 0;
} else {
err = mGraphicBufferProducer->query(what, value);
if (err == NO_ERROR) {
mConsumerRunningBehind = *value;
}
}
return err;
}
}
}
return mGraphicBufferProducer->query(what, value);
}
int GonkNativeWindowClient::perform(int operation, va_list args)
{
int res = NO_ERROR;
switch (operation) {
case NATIVE_WINDOW_CONNECT:
// deprecated. must return NO_ERROR.
break;
case NATIVE_WINDOW_DISCONNECT:
// deprecated. must return NO_ERROR.
break;
case NATIVE_WINDOW_SET_USAGE:
res = dispatchSetUsage(args);
break;
case NATIVE_WINDOW_SET_CROP:
res = dispatchSetCrop(args);
break;
case NATIVE_WINDOW_SET_BUFFER_COUNT:
res = dispatchSetBufferCount(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
res = dispatchSetBuffersGeometry(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
res = dispatchSetBuffersTransform(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
res = dispatchSetBuffersTimestamp(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS:
res = dispatchSetBuffersDimensions(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS:
res = dispatchSetBuffersUserDimensions(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
res = dispatchSetBuffersFormat(args);
break;
case NATIVE_WINDOW_LOCK:
res = dispatchLock(args);
break;
case NATIVE_WINDOW_UNLOCK_AND_POST:
res = dispatchUnlockAndPost(args);
break;
case NATIVE_WINDOW_SET_SCALING_MODE:
res = dispatchSetScalingMode(args);
break;
case NATIVE_WINDOW_API_CONNECT:
res = dispatchConnect(args);
break;
case NATIVE_WINDOW_API_DISCONNECT:
res = dispatchDisconnect(args);
break;
default:
res = NAME_NOT_FOUND;
break;
}
return res;
}
int GonkNativeWindowClient::dispatchConnect(va_list args) {
int api = va_arg(args, int);
return connect(api);
}
int GonkNativeWindowClient::dispatchDisconnect(va_list args) {
int api = va_arg(args, int);
return disconnect(api);
}
int GonkNativeWindowClient::dispatchSetUsage(va_list args) {
int usage = va_arg(args, int);
return setUsage(usage);
}
int GonkNativeWindowClient::dispatchSetCrop(va_list args) {
android_native_rect_t const* rect = va_arg(args, android_native_rect_t*);
return setCrop(reinterpret_cast<Rect const*>(rect));
}
int GonkNativeWindowClient::dispatchSetBufferCount(va_list args) {
size_t bufferCount = va_arg(args, size_t);
return setBufferCount(bufferCount);
}
int GonkNativeWindowClient::dispatchSetBuffersGeometry(va_list args) {
int w = va_arg(args, int);
int h = va_arg(args, int);
int f = va_arg(args, int);
int err = setBuffersDimensions(w, h);
if (err != 0) {
return err;
}
return setBuffersFormat(f);
}
int GonkNativeWindowClient::dispatchSetBuffersDimensions(va_list args) {
int w = va_arg(args, int);
int h = va_arg(args, int);
return setBuffersDimensions(w, h);
}
int GonkNativeWindowClient::dispatchSetBuffersUserDimensions(va_list args) {
int w = va_arg(args, int);
int h = va_arg(args, int);
return setBuffersUserDimensions(w, h);
}
int GonkNativeWindowClient::dispatchSetBuffersFormat(va_list args) {
int f = va_arg(args, int);
return setBuffersFormat(f);
}
int GonkNativeWindowClient::dispatchSetScalingMode(va_list args) {
int m = va_arg(args, int);
return setScalingMode(m);
}
int GonkNativeWindowClient::dispatchSetBuffersTransform(va_list args) {
int transform = va_arg(args, int);
return setBuffersTransform(transform);
}
int GonkNativeWindowClient::dispatchSetBuffersTimestamp(va_list args) {
int64_t timestamp = va_arg(args, int64_t);
return setBuffersTimestamp(timestamp);
}
int GonkNativeWindowClient::dispatchLock(va_list args) {
ANativeWindow_Buffer* outBuffer = va_arg(args, ANativeWindow_Buffer*);
ARect* inOutDirtyBounds = va_arg(args, ARect*);
return lock(outBuffer, inOutDirtyBounds);
}
int GonkNativeWindowClient::dispatchUnlockAndPost(va_list args) {
return unlockAndPost();
}
int GonkNativeWindowClient::connect(int api) {
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::connect");
static sp<BBinder> sLife = new BBinder();
Mutex::Autolock lock(mMutex);
IGraphicBufferProducer::QueueBufferOutput output;
int err = mGraphicBufferProducer->connect(sLife, api, true, &output);
if (err == NO_ERROR) {
uint32_t numPendingBuffers = 0;
output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint,
&numPendingBuffers);
mConsumerRunningBehind = (numPendingBuffers >= 2);
}
if (!err && api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = true;
}
return err;
}
int GonkNativeWindowClient::disconnect(int api) {
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::disconnect");
Mutex::Autolock lock(mMutex);
freeAllBuffers();
int err = mGraphicBufferProducer->disconnect(api);
if (!err) {
mReqFormat = 0;
mReqWidth = 0;
mReqHeight = 0;
mReqUsage = 0;
mCrop.clear();
mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
mTransform = 0;
if (api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = false;
}
}
return err;
}
int GonkNativeWindowClient::setUsage(uint32_t reqUsage)
{
ALOGV("GonkNativeWindowClient::setUsage");
Mutex::Autolock lock(mMutex);
mReqUsage = reqUsage;
return OK;
}
int GonkNativeWindowClient::setCrop(Rect const* rect)
{
ATRACE_CALL();
Rect realRect;
if (rect == NULL || rect->isEmpty()) {
realRect.clear();
} else {
realRect = *rect;
}
ALOGV("GonkNativeWindowClient::setCrop rect=[%d %d %d %d]",
realRect.left, realRect.top, realRect.right, realRect.bottom);
Mutex::Autolock lock(mMutex);
mCrop = realRect;
return NO_ERROR;
}
int GonkNativeWindowClient::setBufferCount(int bufferCount)
{
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::setBufferCount");
Mutex::Autolock lock(mMutex);
status_t err = mGraphicBufferProducer->setBufferCount(bufferCount);
ALOGE_IF(err, "IGraphicBufferProducer::setBufferCount(%d) returned %s",
bufferCount, strerror(-err));
if (err == NO_ERROR) {
freeAllBuffers();
}
return err;
}
int GonkNativeWindowClient::setBuffersDimensions(int w, int h)
{
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::setBuffersDimensions");
if (w<0 || h<0)
return BAD_VALUE;
if ((w && !h) || (!w && h))
return BAD_VALUE;
Mutex::Autolock lock(mMutex);
mReqWidth = w;
mReqHeight = h;
return NO_ERROR;
}
int GonkNativeWindowClient::setBuffersUserDimensions(int w, int h)
{
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::setBuffersUserDimensions");
if (w<0 || h<0)
return BAD_VALUE;
if ((w && !h) || (!w && h))
return BAD_VALUE;
Mutex::Autolock lock(mMutex);
mUserWidth = w;
mUserHeight = h;
return NO_ERROR;
}
int GonkNativeWindowClient::setBuffersFormat(int format)
{
ALOGV("GonkNativeWindowClient::setBuffersFormat");
if (format<0)
return BAD_VALUE;
Mutex::Autolock lock(mMutex);
mReqFormat = format;
return NO_ERROR;
}
int GonkNativeWindowClient::setScalingMode(int mode)
{
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::setScalingMode(%d)", mode);
switch (mode) {
case NATIVE_WINDOW_SCALING_MODE_FREEZE:
case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
break;
default:
ALOGE("unknown scaling mode: %d", mode);
return BAD_VALUE;
}
Mutex::Autolock lock(mMutex);
mScalingMode = mode;
return NO_ERROR;
}
int GonkNativeWindowClient::setBuffersTransform(int transform)
{
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::setBuffersTransform");
Mutex::Autolock lock(mMutex);
mTransform = transform;
return NO_ERROR;
}
int GonkNativeWindowClient::setBuffersTimestamp(int64_t timestamp)
{
ALOGV("GonkNativeWindowClient::setBuffersTimestamp");
Mutex::Autolock lock(mMutex);
mTimestamp = timestamp;
return NO_ERROR;
}
void GonkNativeWindowClient::freeAllBuffers() {
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
mSlots[i].buffer = 0;
}
}
// ----------------------------------------------------------------------
// the lock/unlock APIs must be used from the same thread
// ----------------------------------------------------------------------------
status_t GonkNativeWindowClient::lock(
ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{
return INVALID_OPERATION;
}
status_t GonkNativeWindowClient::unlockAndPost()
{
return INVALID_OPERATION;
}
}; // namespace android
@@ -1,264 +0,0 @@
/*
* Copyright (C) 2010 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.
*/
#ifndef NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_KK_H
#define NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_KK_H
#include <gui/IGraphicBufferProducer.h>
#include <ui/ANativeObjectBase.h>
#include <ui/Region.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
#include <utils/KeyedVector.h>
#include "mozilla/Types.h"
#include "GonkBufferQueue.h"
struct MOZ_EXPORT ANativeWindow_Buffer;
namespace android {
/*
* An implementation of ANativeWindow that feeds graphics buffers into a
* BufferQueue.
*
* This is typically used by programs that want to render frames through
* some means (maybe OpenGL, a software renderer, or a hardware decoder)
* and have the frames they create forwarded to SurfaceFlinger for
* compositing. For example, a video decoder could render a frame and call
* eglSwapBuffers(), which invokes ANativeWindow callbacks defined by
* GonkNativeWindowClient. GonkNativeWindowClient then forwards the buffers through Binder IPC
* to the BufferQueue's producer interface, providing the new frame to a
* consumer such as GLConsumer.
*/
class GonkNativeWindowClient
: public ANativeObjectBase<ANativeWindow, GonkNativeWindowClient, RefBase>
{
public:
/*
* creates a GonkNativeWindowClient from the given IGraphicBufferProducer (which concrete
* implementation is a BufferQueue).
*
* GonkNativeWindowClient is mainly state-less while it's disconnected, it can be
* viewed as a glorified IGraphicBufferProducer holder. It's therefore
* safe to create other GonkNativeWindowClients from the same IGraphicBufferProducer.
*
* However, once a GonkNativeWindowClient is connected, it'll prevent other GonkNativeWindowClients
* referring to the same IGraphicBufferProducer to become connected and
* therefore prevent them to be used as actual producers of buffers.
*
* the controlledByApp flag indicates that this Surface (producer) is
* controlled by the application. This flag is used at connect time.
*/
GonkNativeWindowClient(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp = false);
/* getIGraphicBufferProducer() returns the IGraphicBufferProducer this
* GonkNativeWindowClient was created with. Usually it's an error to use the
* IGraphicBufferProducer while the GonkNativeWindowClient is connected.
*/
sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
/* convenience function to check that the given surface is non NULL as
* well as its IGraphicBufferProducer */
static bool isValid(const sp<GonkNativeWindowClient>& surface) {
return surface != NULL && surface->getIGraphicBufferProducer() != NULL;
}
protected:
virtual ~GonkNativeWindowClient();
private:
// can't be copied
GonkNativeWindowClient& operator = (const GonkNativeWindowClient& rhs);
GonkNativeWindowClient(const GonkNativeWindowClient& rhs);
// ANativeWindow hooks
static int hook_cancelBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd);
static int hook_dequeueBuffer(ANativeWindow* window,
ANativeWindowBuffer** buffer, int* fenceFd);
static int hook_perform(ANativeWindow* window, int operation, ...);
static int hook_query(const ANativeWindow* window, int what, int* value);
static int hook_queueBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd);
static int hook_setSwapInterval(ANativeWindow* window, int interval);
static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer);
static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer** buffer);
static int hook_lockBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer);
static int hook_queueBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer);
int dispatchConnect(va_list args);
int dispatchDisconnect(va_list args);
int dispatchSetBufferCount(va_list args);
int dispatchSetBuffersGeometry(va_list args);
int dispatchSetBuffersDimensions(va_list args);
int dispatchSetBuffersUserDimensions(va_list args);
int dispatchSetBuffersFormat(va_list args);
int dispatchSetScalingMode(va_list args);
int dispatchSetBuffersTransform(va_list args);
int dispatchSetBuffersTimestamp(va_list args);
int dispatchSetCrop(va_list args);
int dispatchSetPostTransformCrop(va_list args);
int dispatchSetUsage(va_list args);
int dispatchLock(va_list args);
int dispatchUnlockAndPost(va_list args);
protected:
virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
virtual int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd);
virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd);
virtual int perform(int operation, va_list args);
virtual int query(int what, int* value) const;
virtual int setSwapInterval(int interval);
virtual int lockBuffer_DEPRECATED(ANativeWindowBuffer* buffer);
virtual int connect(int api);
virtual int disconnect(int api);
virtual int setBufferCount(int bufferCount);
virtual int setBuffersDimensions(int w, int h);
virtual int setBuffersUserDimensions(int w, int h);
virtual int setBuffersFormat(int format);
virtual int setScalingMode(int mode);
virtual int setBuffersTransform(int transform);
virtual int setBuffersTimestamp(int64_t timestamp);
virtual int setCrop(Rect const* rect);
virtual int setUsage(uint32_t reqUsage);
public:
virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
virtual int unlockAndPost();
protected:
enum { NUM_BUFFER_SLOTS = GonkBufferQueue::NUM_BUFFER_SLOTS };
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
private:
void freeAllBuffers();
int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
struct BufferSlot {
sp<GraphicBuffer> buffer;
Region dirtyRegion;
};
// mSurfaceTexture is the interface to the surface texture server. All
// operations on the surface texture client ultimately translate into
// interactions with the server using this interface.
// TODO: rename to mBufferProducer
sp<IGraphicBufferProducer> mGraphicBufferProducer;
// mSlots stores the buffers that have been allocated for each buffer slot.
// It is initialized to null pointers, and gets filled in with the result of
// IGraphicBufferProducer::requestBuffer when the client dequeues a buffer from a
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
BufferSlot mSlots[NUM_BUFFER_SLOTS];
// mReqWidth is the buffer width that will be requested at the next dequeue
// operation. It is initialized to 1.
uint32_t mReqWidth;
// mReqHeight is the buffer height that will be requested at the next
// dequeue operation. It is initialized to 1.
uint32_t mReqHeight;
// mReqFormat is the buffer pixel format that will be requested at the next
// deuque operation. It is initialized to PIXEL_FORMAT_RGBA_8888.
uint32_t mReqFormat;
// mReqUsage is the set of buffer usage flags that will be requested
// at the next deuque operation. It is initialized to 0.
uint32_t mReqUsage;
// mTimestamp is the timestamp that will be used for the next buffer queue
// operation. It defaults to NATIVE_WINDOW_TIMESTAMP_AUTO, which means that
// a timestamp is auto-generated when queueBuffer is called.
int64_t mTimestamp;
// mCrop is the crop rectangle that will be used for the next buffer
// that gets queued. It is set by calling setCrop.
Rect mCrop;
// mScalingMode is the scaling mode that will be used for the next
// buffers that get queued. It is set by calling setScalingMode.
int mScalingMode;
// mTransform is the transform identifier that will be used for the next
// buffer that gets queued. It is set by calling setTransform.
uint32_t mTransform;
// mDefaultWidth is default width of the buffers, regardless of the
// native_window_set_buffers_dimensions call.
uint32_t mDefaultWidth;
// mDefaultHeight is default height of the buffers, regardless of the
// native_window_set_buffers_dimensions call.
uint32_t mDefaultHeight;
// mUserWidth, if non-zero, is an application-specified override
// of mDefaultWidth. This is lower priority than the width set by
// native_window_set_buffers_dimensions.
uint32_t mUserWidth;
// mUserHeight, if non-zero, is an application-specified override
// of mDefaultHeight. This is lower priority than the height set
// by native_window_set_buffers_dimensions.
uint32_t mUserHeight;
// mTransformHint is the transform probably applied to buffers of this
// window. this is only a hint, actual transform may differ.
uint32_t mTransformHint;
// mProducerControlledByApp whether this buffer producer is controlled
// by the application
bool mProducerControlledByApp;
// mSwapIntervalZero set if we should drop buffers at queue() time to
// achieve an asynchronous swap interval
bool mSwapIntervalZero;
// mConsumerRunningBehind whether the consumer is running more than
// one buffer behind the producer.
mutable bool mConsumerRunningBehind;
// mMutex is the mutex used to prevent concurrent access to the member
// variables of GonkNativeWindowClient objects. It must be locked whenever the
// member variables are accessed.
mutable Mutex mMutex;
// must be used from the lock/unlock thread
sp<GraphicBuffer> mLockedBuffer;
sp<GraphicBuffer> mPostedBuffer;
bool mConnectedToCpu;
// must be accessed from lock/unlock thread only
Region mDirtyRegion;
};
}; // namespace android
#endif // NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_JB_H
@@ -1,743 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
* 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.
*/
#define LOG_TAG "GonkNativeWindowClient"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
#include <android/native_window.h>
#include <binder/Parcel.h>
#include <utils/Log.h>
#include <utils/Trace.h>
#include <utils/NativeHandle.h>
#include <ui/Fence.h>
#include <gui/IProducerListener.h>
#include "GonkNativeWindowClientLL.h"
namespace android {
GonkNativeWindowClient::GonkNativeWindowClient(
const sp<IGraphicBufferProducer>& bufferProducer,
bool controlledByApp)
: mGraphicBufferProducer(bufferProducer)
{
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = hook_setSwapInterval;
ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
ANativeWindow::cancelBuffer = hook_cancelBuffer;
ANativeWindow::queueBuffer = hook_queueBuffer;
ANativeWindow::query = hook_query;
ANativeWindow::perform = hook_perform;
ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;
ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;
ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
const_cast<int&>(ANativeWindow::minSwapInterval) = 0;
const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
mReqWidth = 0;
mReqHeight = 0;
mReqFormat = 0;
mReqUsage = 0;
mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
mCrop.clear();
mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
mTransform = 0;
mStickyTransform = 0;
mDefaultWidth = 0;
mDefaultHeight = 0;
mUserWidth = 0;
mUserHeight = 0;
mTransformHint = 0;
mConsumerRunningBehind = false;
mConnectedToCpu = false;
mProducerControlledByApp = controlledByApp;
mSwapIntervalZero = false;
}
GonkNativeWindowClient::~GonkNativeWindowClient() {
if (mConnectedToCpu) {
GonkNativeWindowClient::disconnect(NATIVE_WINDOW_API_CPU);
}
}
sp<IGraphicBufferProducer> GonkNativeWindowClient::getIGraphicBufferProducer() const {
return mGraphicBufferProducer;
}
void GonkNativeWindowClient::setSidebandStream(const sp<NativeHandle>& stream) {
mGraphicBufferProducer->setSidebandStream(stream);
}
void GonkNativeWindowClient::allocateBuffers() {
uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth;
uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight;
mGraphicBufferProducer->allocateBuffers(mSwapIntervalZero, reqWidth,
reqHeight, mReqFormat, mReqUsage);
}
int GonkNativeWindowClient::hook_setSwapInterval(ANativeWindow* window, int interval) {
GonkNativeWindowClient* c = getSelf(window);
return c->setSwapInterval(interval);
}
int GonkNativeWindowClient::hook_dequeueBuffer(ANativeWindow* window,
ANativeWindowBuffer** buffer, int* fenceFd) {
GonkNativeWindowClient* c = getSelf(window);
return c->dequeueBuffer(buffer, fenceFd);
}
int GonkNativeWindowClient::hook_cancelBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd) {
GonkNativeWindowClient* c = getSelf(window);
return c->cancelBuffer(buffer, fenceFd);
}
int GonkNativeWindowClient::hook_queueBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd) {
GonkNativeWindowClient* c = getSelf(window);
return c->queueBuffer(buffer, fenceFd);
}
int GonkNativeWindowClient::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer** buffer) {
GonkNativeWindowClient* c = getSelf(window);
ANativeWindowBuffer* buf;
int fenceFd = -1;
int result = c->dequeueBuffer(&buf, &fenceFd);
sp<Fence> fence(new Fence(fenceFd));
int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
if (waitResult != OK) {
ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d",
waitResult);
c->cancelBuffer(buf, -1);
return waitResult;
}
*buffer = buf;
return result;
}
int GonkNativeWindowClient::hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer) {
GonkNativeWindowClient* c = getSelf(window);
return c->cancelBuffer(buffer, -1);
}
int GonkNativeWindowClient::hook_lockBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer) {
GonkNativeWindowClient* c = getSelf(window);
return c->lockBuffer_DEPRECATED(buffer);
}
int GonkNativeWindowClient::hook_queueBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer) {
GonkNativeWindowClient* c = getSelf(window);
return c->queueBuffer(buffer, -1);
}
int GonkNativeWindowClient::hook_query(const ANativeWindow* window,
int what, int* value) {
const GonkNativeWindowClient* c = getSelf(window);
return c->query(what, value);
}
int GonkNativeWindowClient::hook_perform(ANativeWindow* window, int operation, ...) {
va_list args;
va_start(args, operation);
GonkNativeWindowClient* c = getSelf(window);
return c->perform(operation, args);
}
int GonkNativeWindowClient::setSwapInterval(int interval) {
ATRACE_CALL();
// EGL specification states:
// interval is silently clamped to minimum and maximum implementation
// dependent values before being stored.
if (interval < minSwapInterval)
interval = minSwapInterval;
if (interval > maxSwapInterval)
interval = maxSwapInterval;
return NO_ERROR;
}
int GonkNativeWindowClient::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::dequeueBuffer");
int reqW;
int reqH;
bool swapIntervalZero;
uint32_t reqFormat;
uint32_t reqUsage;
{
Mutex::Autolock lock(mMutex);
reqW = mReqWidth ? mReqWidth : mUserWidth;
reqH = mReqHeight ? mReqHeight : mUserHeight;
swapIntervalZero = mSwapIntervalZero;
reqFormat = mReqFormat;
reqUsage = mReqUsage;
} // Drop the lock so that we can still touch the GonkNativeWindowClient while blocking in IGBP::dequeueBuffer
int buf = -1;
sp<Fence> fence;
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, swapIntervalZero,
reqW, reqH, reqFormat, reqUsage);
if (result < 0) {
ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer(%d, %d, %d, %d, %d)"
"failed: %d", swapIntervalZero, reqW, reqH, reqFormat, reqUsage,
result);
return result;
}
Mutex::Autolock lock(mMutex);
sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
// this should never happen
ALOGE_IF(fence == NULL, "GonkNativeWindowClient::dequeueBuffer: received null Fence! buf=%d", buf);
if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
freeAllBuffers();
}
if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
if (result != NO_ERROR) {
ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result);
mGraphicBufferProducer->cancelBuffer(buf, fence);
return result;
}
}
if (fence->isValid()) {
*fenceFd = fence->dup();
if (*fenceFd == -1) {
ALOGE("dequeueBuffer: error duping fence: %d", errno);
// dup() should never fail; something is badly wrong. Soldier on
// and hope for the best; the worst that should happen is some
// visible corruption that lasts until the next frame.
}
} else {
*fenceFd = -1;
}
*buffer = gbuf.get();
return OK;
}
int GonkNativeWindowClient::cancelBuffer(android_native_buffer_t* buffer,
int fenceFd) {
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::cancelBuffer");
Mutex::Autolock lock(mMutex);
int i = getSlotFromBufferLocked(buffer);
if (i < 0) {
return i;
}
sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
mGraphicBufferProducer->cancelBuffer(i, fence);
return OK;
}
int GonkNativeWindowClient::getSlotFromBufferLocked(
android_native_buffer_t* buffer) const {
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
if (mSlots[i].buffer != NULL &&
mSlots[i].buffer->handle == buffer->handle) {
return i;
}
}
ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
return BAD_VALUE;
}
int GonkNativeWindowClient::lockBuffer_DEPRECATED(android_native_buffer_t* buffer __attribute__((unused))) {
ALOGV("GonkNativeWindowClient::lockBuffer");
Mutex::Autolock lock(mMutex);
return OK;
}
int GonkNativeWindowClient::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::queueBuffer");
Mutex::Autolock lock(mMutex);
int64_t timestamp;
bool isAutoTimestamp = false;
if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
isAutoTimestamp = true;
ALOGV("GonkNativeWindowClient::queueBuffer making up timestamp: %.2f ms",
timestamp / 1000000.f);
} else {
timestamp = mTimestamp;
}
int i = getSlotFromBufferLocked(buffer);
if (i < 0) {
return i;
}
// Make sure the crop rectangle is entirely inside the buffer.
Rect crop;
mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
IGraphicBufferProducer::QueueBufferOutput output;
IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
crop, mScalingMode, mTransform ^ mStickyTransform, mSwapIntervalZero,
fence, mStickyTransform);
status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
if (err != OK) {
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
}
uint32_t numPendingBuffers = 0;
uint32_t hint = 0;
output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
&numPendingBuffers);
// Disable transform hint if sticky transform is set.
if (mStickyTransform == 0) {
mTransformHint = hint;
}
mConsumerRunningBehind = (numPendingBuffers >= 2);
return err;
}
int GonkNativeWindowClient::query(int what, int* value) const {
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::query");
{ // scope for the lock
Mutex::Autolock lock(mMutex);
switch (what) {
case NATIVE_WINDOW_FORMAT:
if (mReqFormat) {
*value = mReqFormat;
return NO_ERROR;
}
break;
case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
*value = 0;
return NO_ERROR;
}
case NATIVE_WINDOW_CONCRETE_TYPE:
*value = NATIVE_WINDOW_SURFACE;
return NO_ERROR;
case NATIVE_WINDOW_DEFAULT_WIDTH:
*value = mUserWidth ? mUserWidth : mDefaultWidth;
return NO_ERROR;
case NATIVE_WINDOW_DEFAULT_HEIGHT:
*value = mUserHeight ? mUserHeight : mDefaultHeight;
return NO_ERROR;
case NATIVE_WINDOW_TRANSFORM_HINT:
*value = mTransformHint;
return NO_ERROR;
case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: {
status_t err = NO_ERROR;
if (!mConsumerRunningBehind) {
*value = 0;
} else {
err = mGraphicBufferProducer->query(what, value);
if (err == NO_ERROR) {
mConsumerRunningBehind = *value;
}
}
return err;
}
}
}
return mGraphicBufferProducer->query(what, value);
}
int GonkNativeWindowClient::perform(int operation, va_list args)
{
int res = NO_ERROR;
switch (operation) {
case NATIVE_WINDOW_CONNECT:
// deprecated. must return NO_ERROR.
break;
case NATIVE_WINDOW_DISCONNECT:
// deprecated. must return NO_ERROR.
break;
case NATIVE_WINDOW_SET_USAGE:
res = dispatchSetUsage(args);
break;
case NATIVE_WINDOW_SET_CROP:
res = dispatchSetCrop(args);
break;
case NATIVE_WINDOW_SET_BUFFER_COUNT:
res = dispatchSetBufferCount(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
res = dispatchSetBuffersGeometry(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
res = dispatchSetBuffersTransform(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM:
res = dispatchSetBuffersStickyTransform(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
res = dispatchSetBuffersTimestamp(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS:
res = dispatchSetBuffersDimensions(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS:
res = dispatchSetBuffersUserDimensions(args);
break;
case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
res = dispatchSetBuffersFormat(args);
break;
case NATIVE_WINDOW_LOCK:
res = dispatchLock(args);
break;
case NATIVE_WINDOW_UNLOCK_AND_POST:
res = dispatchUnlockAndPost(args);
break;
case NATIVE_WINDOW_SET_SCALING_MODE:
res = dispatchSetScalingMode(args);
break;
case NATIVE_WINDOW_API_CONNECT:
res = dispatchConnect(args);
break;
case NATIVE_WINDOW_API_DISCONNECT:
res = dispatchDisconnect(args);
break;
case NATIVE_WINDOW_SET_SIDEBAND_STREAM:
res = dispatchSetSidebandStream(args);
break;
default:
res = NAME_NOT_FOUND;
break;
}
return res;
}
int GonkNativeWindowClient::dispatchConnect(va_list args) {
int api = va_arg(args, int);
return connect(api);
}
int GonkNativeWindowClient::dispatchDisconnect(va_list args) {
int api = va_arg(args, int);
return disconnect(api);
}
int GonkNativeWindowClient::dispatchSetUsage(va_list args) {
int usage = va_arg(args, int);
return setUsage(usage);
}
int GonkNativeWindowClient::dispatchSetCrop(va_list args) {
android_native_rect_t const* rect = va_arg(args, android_native_rect_t*);
return setCrop(reinterpret_cast<Rect const*>(rect));
}
int GonkNativeWindowClient::dispatchSetBufferCount(va_list args) {
size_t bufferCount = va_arg(args, size_t);
return setBufferCount(bufferCount);
}
int GonkNativeWindowClient::dispatchSetBuffersGeometry(va_list args) {
int w = va_arg(args, int);
int h = va_arg(args, int);
int f = va_arg(args, int);
int err = setBuffersDimensions(w, h);
if (err != 0) {
return err;
}
return setBuffersFormat(f);
}
int GonkNativeWindowClient::dispatchSetBuffersDimensions(va_list args) {
int w = va_arg(args, int);
int h = va_arg(args, int);
return setBuffersDimensions(w, h);
}
int GonkNativeWindowClient::dispatchSetBuffersUserDimensions(va_list args) {
int w = va_arg(args, int);
int h = va_arg(args, int);
return setBuffersUserDimensions(w, h);
}
int GonkNativeWindowClient::dispatchSetBuffersFormat(va_list args) {
int f = va_arg(args, int);
return setBuffersFormat(f);
}
int GonkNativeWindowClient::dispatchSetScalingMode(va_list args) {
int m = va_arg(args, int);
return setScalingMode(m);
}
int GonkNativeWindowClient::dispatchSetBuffersTransform(va_list args) {
int transform = va_arg(args, int);
return setBuffersTransform(transform);
}
int GonkNativeWindowClient::dispatchSetBuffersStickyTransform(va_list args) {
int transform = va_arg(args, int);
return setBuffersStickyTransform(transform);
}
int GonkNativeWindowClient::dispatchSetBuffersTimestamp(va_list args) {
int64_t timestamp = va_arg(args, int64_t);
return setBuffersTimestamp(timestamp);
}
int GonkNativeWindowClient::dispatchLock(va_list args) {
ANativeWindow_Buffer* outBuffer = va_arg(args, ANativeWindow_Buffer*);
ARect* inOutDirtyBounds = va_arg(args, ARect*);
return lock(outBuffer, inOutDirtyBounds);
}
int GonkNativeWindowClient::dispatchUnlockAndPost(va_list args __attribute__((unused))) {
return unlockAndPost();
}
int GonkNativeWindowClient::dispatchSetSidebandStream(va_list args) {
native_handle_t* sH = va_arg(args, native_handle_t*);
sp<NativeHandle> sidebandHandle = NativeHandle::create(sH, false);
setSidebandStream(sidebandHandle);
return OK;
}
int GonkNativeWindowClient::connect(int api) {
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::connect");
static sp<IProducerListener> listener = new DummyProducerListener();
Mutex::Autolock lock(mMutex);
IGraphicBufferProducer::QueueBufferOutput output;
int err = mGraphicBufferProducer->connect(listener, api, true, &output);
if (err == NO_ERROR) {
uint32_t numPendingBuffers = 0;
uint32_t hint = 0;
output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
&numPendingBuffers);
// Disable transform hint if sticky transform is set.
if (mStickyTransform == 0) {
mTransformHint = hint;
}
mConsumerRunningBehind = (numPendingBuffers >= 2);
}
if (!err && api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = true;
}
return err;
}
int GonkNativeWindowClient::disconnect(int api) {
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::disconnect");
Mutex::Autolock lock(mMutex);
freeAllBuffers();
int err = mGraphicBufferProducer->disconnect(api);
if (!err) {
mReqFormat = 0;
mReqWidth = 0;
mReqHeight = 0;
mReqUsage = 0;
mCrop.clear();
mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
mTransform = 0;
mStickyTransform = 0;
if (api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = false;
}
}
return err;
}
int GonkNativeWindowClient::setUsage(uint32_t reqUsage)
{
ALOGV("GonkNativeWindowClient::setUsage");
Mutex::Autolock lock(mMutex);
mReqUsage = reqUsage;
return OK;
}
int GonkNativeWindowClient::setCrop(Rect const* rect)
{
ATRACE_CALL();
Rect realRect;
if (rect == NULL || rect->isEmpty()) {
realRect.clear();
} else {
realRect = *rect;
}
ALOGV("GonkNativeWindowClient::setCrop rect=[%d %d %d %d]",
realRect.left, realRect.top, realRect.right, realRect.bottom);
Mutex::Autolock lock(mMutex);
mCrop = realRect;
return NO_ERROR;
}
int GonkNativeWindowClient::setBufferCount(int bufferCount)
{
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::setBufferCount");
Mutex::Autolock lock(mMutex);
status_t err = mGraphicBufferProducer->setBufferCount(bufferCount);
ALOGE_IF(err, "IGraphicBufferProducer::setBufferCount(%d) returned %s",
bufferCount, strerror(-err));
if (err == NO_ERROR) {
freeAllBuffers();
}
return err;
}
int GonkNativeWindowClient::setBuffersDimensions(int w, int h)
{
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::setBuffersDimensions");
if (w<0 || h<0)
return BAD_VALUE;
if ((w && !h) || (!w && h))
return BAD_VALUE;
Mutex::Autolock lock(mMutex);
mReqWidth = w;
mReqHeight = h;
return NO_ERROR;
}
int GonkNativeWindowClient::setBuffersUserDimensions(int w, int h)
{
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::setBuffersUserDimensions");
if (w<0 || h<0)
return BAD_VALUE;
if ((w && !h) || (!w && h))
return BAD_VALUE;
Mutex::Autolock lock(mMutex);
mUserWidth = w;
mUserHeight = h;
return NO_ERROR;
}
int GonkNativeWindowClient::setBuffersFormat(int format)
{
ALOGV("GonkNativeWindowClient::setBuffersFormat");
if (format<0)
return BAD_VALUE;
Mutex::Autolock lock(mMutex);
mReqFormat = format;
return NO_ERROR;
}
int GonkNativeWindowClient::setScalingMode(int mode)
{
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::setScalingMode(%d)", mode);
switch (mode) {
case NATIVE_WINDOW_SCALING_MODE_FREEZE:
case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
break;
default:
ALOGE("unknown scaling mode: %d", mode);
return BAD_VALUE;
}
Mutex::Autolock lock(mMutex);
mScalingMode = mode;
return NO_ERROR;
}
int GonkNativeWindowClient::setBuffersTransform(int transform)
{
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::setBuffersTransform");
Mutex::Autolock lock(mMutex);
mTransform = transform;
return NO_ERROR;
}
int GonkNativeWindowClient::setBuffersStickyTransform(int transform)
{
ATRACE_CALL();
ALOGV("GonkNativeWindowClient::setBuffersStickyTransform");
Mutex::Autolock lock(mMutex);
mStickyTransform = transform;
return NO_ERROR;
}
int GonkNativeWindowClient::setBuffersTimestamp(int64_t timestamp)
{
ALOGV("GonkNativeWindowClient::setBuffersTimestamp");
Mutex::Autolock lock(mMutex);
mTimestamp = timestamp;
return NO_ERROR;
}
void GonkNativeWindowClient::freeAllBuffers() {
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
mSlots[i].buffer = 0;
}
}
// ----------------------------------------------------------------------
// the lock/unlock APIs must be used from the same thread
// ----------------------------------------------------------------------------
status_t GonkNativeWindowClient::lock(
ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{
return INVALID_OPERATION;
}
status_t GonkNativeWindowClient::unlockAndPost()
{
return INVALID_OPERATION;
}
}; // namespace android
@@ -1,295 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
* 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 NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_LL_H
#define NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_LL_H
#include <gui/IGraphicBufferProducer.h>
#include <ui/ANativeObjectBase.h>
#include <ui/Region.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
#include <utils/KeyedVector.h>
#include "GonkBufferQueueLL.h"
struct ANativeWindow_Buffer;
namespace android {
/*
* An implementation of ANativeWindow that feeds graphics buffers into a
* GonkBufferQueue.
*
* This is typically used by programs that want to render frames through
* some means (maybe OpenGL, a software renderer, or a hardware decoder)
* and have the frames they create forwarded to SurfaceFlinger for
* compositing. For example, a video decoder could render a frame and call
* eglSwapBuffers(), which invokes ANativeWindow callbacks defined by
* GonkNativeWindowClient. GonkNativeWindowClient then forwards the buffers through Binder IPC
* to the GonkBufferQueue's producer interface, providing the new frame to a
* consumer such as GLConsumer.
*/
class GonkNativeWindowClient
: public ANativeObjectBase<ANativeWindow, GonkNativeWindowClient, RefBase>
{
public:
/*
* creates a GonkNativeWindowClient from the given IGraphicBufferProducer (which concrete
* implementation is a GonkBufferQueue).
*
* GonkNativeWindowClient is mainly state-less while it's disconnected, it can be
* viewed as a glorified IGraphicBufferProducer holder. It's therefore
* safe to create other GonkNativeWindowClients from the same IGraphicBufferProducer.
*
* However, once a GonkNativeWindowClient is connected, it'll prevent other GonkNativeWindowClients
* referring to the same IGraphicBufferProducer to become connected and
* therefore prevent them to be used as actual producers of buffers.
*
* the controlledByApp flag indicates that this GonkNativeWindowClient (producer) is
* controlled by the application. This flag is used at connect time.
*/
GonkNativeWindowClient(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp = false);
/* getIGraphicBufferProducer() returns the IGraphicBufferProducer this
* GonkNativeWindowClient was created with. Usually it's an error to use the
* IGraphicBufferProducer while the GonkNativeWindowClient is connected.
*/
sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
/* convenience function to check that the given surface is non NULL as
* well as its IGraphicBufferProducer */
static bool isValid(const sp<GonkNativeWindowClient>& surface) {
return surface != NULL && surface->getIGraphicBufferProducer() != NULL;
}
/* Attaches a sideband buffer stream to the GonkNativeWindowClient's IGraphicBufferProducer.
*
* A sideband stream is a device-specific mechanism for passing buffers
* from the producer to the consumer without using dequeueBuffer/
* queueBuffer. If a sideband stream is present, the consumer can choose
* whether to acquire buffers from the sideband stream or from the queued
* buffers.
*
* Passing NULL or a different stream handle will detach the previous
* handle if any.
*/
void setSidebandStream(const sp<NativeHandle>& stream);
/* Allocates buffers based on the current dimensions/format.
*
* This function will allocate up to the maximum number of buffers
* permitted by the current GonkBufferQueue configuration. It will use the
* default format and dimensions. This is most useful to avoid an allocation
* delay during dequeueBuffer. If there are already the maximum number of
* buffers allocated, this function has no effect.
*/
void allocateBuffers();
protected:
virtual ~GonkNativeWindowClient();
private:
// can't be copied
GonkNativeWindowClient& operator = (const GonkNativeWindowClient& rhs);
GonkNativeWindowClient(const GonkNativeWindowClient& rhs);
// ANativeWindow hooks
static int hook_cancelBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd);
static int hook_dequeueBuffer(ANativeWindow* window,
ANativeWindowBuffer** buffer, int* fenceFd);
static int hook_perform(ANativeWindow* window, int operation, ...);
static int hook_query(const ANativeWindow* window, int what, int* value);
static int hook_queueBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd);
static int hook_setSwapInterval(ANativeWindow* window, int interval);
static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer);
static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer** buffer);
static int hook_lockBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer);
static int hook_queueBuffer_DEPRECATED(ANativeWindow* window,
ANativeWindowBuffer* buffer);
int dispatchConnect(va_list args);
int dispatchDisconnect(va_list args);
int dispatchSetBufferCount(va_list args);
int dispatchSetBuffersGeometry(va_list args);
int dispatchSetBuffersDimensions(va_list args);
int dispatchSetBuffersUserDimensions(va_list args);
int dispatchSetBuffersFormat(va_list args);
int dispatchSetScalingMode(va_list args);
int dispatchSetBuffersTransform(va_list args);
int dispatchSetBuffersStickyTransform(va_list args);
int dispatchSetBuffersTimestamp(va_list args);
int dispatchSetCrop(va_list args);
int dispatchSetPostTransformCrop(va_list args);
int dispatchSetUsage(va_list args);
int dispatchLock(va_list args);
int dispatchUnlockAndPost(va_list args);
int dispatchSetSidebandStream(va_list args);
protected:
virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
virtual int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd);
virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd);
virtual int perform(int operation, va_list args);
virtual int query(int what, int* value) const;
virtual int setSwapInterval(int interval);
virtual int lockBuffer_DEPRECATED(ANativeWindowBuffer* buffer);
virtual int connect(int api);
virtual int disconnect(int api);
virtual int setBufferCount(int bufferCount);
virtual int setBuffersDimensions(int w, int h);
virtual int setBuffersUserDimensions(int w, int h);
virtual int setBuffersFormat(int format);
virtual int setScalingMode(int mode);
virtual int setBuffersTransform(int transform);
virtual int setBuffersStickyTransform(int transform);
virtual int setBuffersTimestamp(int64_t timestamp);
virtual int setCrop(Rect const* rect);
virtual int setUsage(uint32_t reqUsage);
public:
virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
virtual int unlockAndPost();
protected:
enum { NUM_BUFFER_SLOTS = GonkBufferQueue::NUM_BUFFER_SLOTS };
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
private:
void freeAllBuffers();
int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
struct BufferSlot {
sp<GraphicBuffer> buffer;
Region dirtyRegion;
};
// mSurfaceTexture is the interface to the surface texture server. All
// operations on the surface texture client ultimately translate into
// interactions with the server using this interface.
// TODO: rename to mBufferProducer
sp<IGraphicBufferProducer> mGraphicBufferProducer;
// mSlots stores the buffers that have been allocated for each buffer slot.
// It is initialized to null pointers, and gets filled in with the result of
// IGraphicBufferProducer::requestBuffer when the client dequeues a buffer from a
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
BufferSlot mSlots[NUM_BUFFER_SLOTS];
// mReqWidth is the buffer width that will be requested at the next dequeue
// operation. It is initialized to 1.
uint32_t mReqWidth;
// mReqHeight is the buffer height that will be requested at the next
// dequeue operation. It is initialized to 1.
uint32_t mReqHeight;
// mReqFormat is the buffer pixel format that will be requested at the next
// deuque operation. It is initialized to PIXEL_FORMAT_RGBA_8888.
uint32_t mReqFormat;
// mReqUsage is the set of buffer usage flags that will be requested
// at the next deuque operation. It is initialized to 0.
uint32_t mReqUsage;
// mTimestamp is the timestamp that will be used for the next buffer queue
// operation. It defaults to NATIVE_WINDOW_TIMESTAMP_AUTO, which means that
// a timestamp is auto-generated when queueBuffer is called.
int64_t mTimestamp;
// mCrop is the crop rectangle that will be used for the next buffer
// that gets queued. It is set by calling setCrop.
Rect mCrop;
// mScalingMode is the scaling mode that will be used for the next
// buffers that get queued. It is set by calling setScalingMode.
int mScalingMode;
// mTransform is the transform identifier that will be used for the next
// buffer that gets queued. It is set by calling setTransform.
uint32_t mTransform;
// mStickyTransform is a transform that is applied on top of mTransform
// in each buffer that is queued. This is typically used to force the
// compositor to apply a transform, and will prevent the transform hint
// from being set by the compositor.
uint32_t mStickyTransform;
// mDefaultWidth is default width of the buffers, regardless of the
// native_window_set_buffers_dimensions call.
uint32_t mDefaultWidth;
// mDefaultHeight is default height of the buffers, regardless of the
// native_window_set_buffers_dimensions call.
uint32_t mDefaultHeight;
// mUserWidth, if non-zero, is an application-specified override
// of mDefaultWidth. This is lower priority than the width set by
// native_window_set_buffers_dimensions.
uint32_t mUserWidth;
// mUserHeight, if non-zero, is an application-specified override
// of mDefaultHeight. This is lower priority than the height set
// by native_window_set_buffers_dimensions.
uint32_t mUserHeight;
// mTransformHint is the transform probably applied to buffers of this
// window. this is only a hint, actual transform may differ.
uint32_t mTransformHint;
// mProducerControlledByApp whether this buffer producer is controlled
// by the application
bool mProducerControlledByApp;
// mSwapIntervalZero set if we should drop buffers at queue() time to
// achieve an asynchronous swap interval
bool mSwapIntervalZero;
// mConsumerRunningBehind whether the consumer is running more than
// one buffer behind the producer.
mutable bool mConsumerRunningBehind;
// mMutex is the mutex used to prevent concurrent access to the member
// variables of GonkNativeWindowClient objects. It must be locked whenever the
// member variables are accessed.
mutable Mutex mMutex;
// must be used from the lock/unlock thread
sp<GraphicBuffer> mLockedBuffer;
sp<GraphicBuffer> mPostedBuffer;
bool mConnectedToCpu;
// must be accessed from lock/unlock thread only
Region mDirtyRegion;
};
}; // namespace android
#endif // NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_LL_H
-9
View File
@@ -17,7 +17,6 @@
EXPORTS += [
'GonkBufferQueue.h',
'GonkNativeWindow.h',
'GonkNativeWindowClient.h',
]
if CONFIG['ANDROID_VERSION'] >= '19':
@@ -32,7 +31,6 @@ if CONFIG['ANDROID_VERSION'] >= '21':
'GonkBufferQueueLL/GonkBufferQueueProducer.h',
'GonkBufferQueueLL/GonkBufferSlot.h',
'GonkConsumerBaseLL.h',
'GonkNativeWindowClientLL.h',
'GonkNativeWindowLL.h',
'IGonkGraphicBufferConsumerLL.h',
]
@@ -40,7 +38,6 @@ elif CONFIG['ANDROID_VERSION'] >= '19':
EXPORTS += [
'GonkBufferQueueKK.h',
'GonkConsumerBaseKK.h',
'GonkNativeWindowClientKK.h',
'GonkNativeWindowKK.h',
'IGonkGraphicBufferConsumerKK.h',
]
@@ -48,12 +45,10 @@ elif CONFIG['ANDROID_VERSION'] in ('17', '18'):
EXPORTS += [
'GonkBufferQueueJB.h',
'GonkConsumerBaseJB.h',
'GonkNativeWindowClientJB.h',
'GonkNativeWindowJB.h',
]
elif CONFIG['ANDROID_VERSION'] == '15':
EXPORTS += [
'GonkNativeWindowClientICS.h',
'GonkNativeWindowICS.h',
]
@@ -67,7 +62,6 @@ if CONFIG['MOZ_B2G_CAMERA'] or CONFIG['MOZ_OMX_DECODER'] or CONFIG['MOZ_WEBRTC']
'GonkBufferQueueLL/GonkBufferQueueProducer.cpp',
'GonkBufferQueueLL/GonkBufferSlot.cpp',
'GonkConsumerBaseLL.cpp',
'GonkNativeWindowClientLL.cpp',
'GonkNativeWindowLL.cpp',
'IGonkGraphicBufferConsumerLL.cpp',
]
@@ -75,7 +69,6 @@ if CONFIG['MOZ_B2G_CAMERA'] or CONFIG['MOZ_OMX_DECODER'] or CONFIG['MOZ_WEBRTC']
SOURCES += [
'GonkBufferQueueKK.cpp',
'GonkConsumerBaseKK.cpp',
'GonkNativeWindowClientKK.cpp',
'GonkNativeWindowKK.cpp',
'IGonkGraphicBufferConsumerKK.cpp',
]
@@ -83,12 +76,10 @@ if CONFIG['MOZ_B2G_CAMERA'] or CONFIG['MOZ_OMX_DECODER'] or CONFIG['MOZ_WEBRTC']
SOURCES += [
'GonkBufferQueueJB.cpp',
'GonkConsumerBaseJB.cpp',
'GonkNativeWindowClientJB.cpp',
'GonkNativeWindowJB.cpp',
]
elif CONFIG['ANDROID_VERSION'] == '15':
SOURCES += [
'GonkNativeWindowClientICS.cpp',
'GonkNativeWindowICS.cpp',
]
+5 -2
View File
@@ -7,8 +7,7 @@
#include "nsISupports.idl"
/* NOTE: this interface is completely undesigned, not stable and likely to change */
[scriptable, uuid(4b5ea59e-af89-44f7-8c1c-2dea47a170d1)]
[scriptable, uuid(1accd618-4c80-4703-9d29-ecf257d397c8)]
interface nsIGfxInfo : nsISupports
{
/*
@@ -108,6 +107,10 @@ interface nsIGfxInfo : nsISupports
const long FEATURE_WEBRTC_HW_ACCELERATION_DECODE = 15;
/* Whether Canvas acceleration is supported, starting in 45 */
const long FEATURE_CANVAS2D_ACCELERATION = 16;
/* Whether hardware VP8 decoding is supported. */
const long FEATURE_VP8_HW_DECODE = 17;
/* Whether hardware VP9 decoding is supported. */
const long FEATURE_VP9_HW_DECODE = 18;
/*
* A set of return values from GetFeatureStatus
+3 -3
View File
@@ -211,12 +211,12 @@ if CONFIG['OS_TEST'] in ('powerpc64', 'powerpc64le'):
'xptcstubs_ppc64_linux.cpp',
]
if CONFIG['OS_TEST'] in ('macppc', 'bebox', 'ofppc', 'prep', 'amigappc'):
if CONFIG['OS_TEST'] in ('powerpc', 'macppc', 'bebox', 'ofppc', 'prep', 'amigappc'):
if CONFIG['OS_ARCH'] == 'NetBSD':
SOURCES += [
'xptcinvoke_asm_ppc_netbsd.s',
'xptcinvoke_asm_ppc_netbsd.S',
'xptcinvoke_ppc_netbsd.cpp',
'xptcstubs_asm_ppc_netbsd.s',
'xptcstubs_asm_ppc_netbsd.S',
'xptcstubs_ppc_netbsd.cpp',
]
+185 -64
View File
@@ -66,21 +66,24 @@ InterfaceType() - construct a new object representing a type that
"""
from __future__ import with_statement
import os, sys
import os
import sys
import struct
import operator
import itertools
# header magic
XPT_MAGIC = "XPCOM\nTypeLib\r\n\x1a"
TYPELIB_VERSION = (1, 2)
class FileFormatError(Exception):
pass
class DataError(Exception):
pass
# Magic for creating enums
def M_add_class_attribs(attribs):
def foo(name, bases, dict_):
@@ -89,19 +92,22 @@ def M_add_class_attribs(attribs):
return type(name, bases, dict_)
return foo
def enum(*names):
class Foo(object):
__metaclass__ = M_add_class_attribs(enumerate(names))
def __setattr__(self, name, value): # this makes it read-only
raise NotImplementedError
return Foo()
# Descriptor types as described in the spec
class Type(object):
"""
Data type of a method parameter or return value. Do not instantiate
this class directly. Rather, use one of its subclasses.
"""
_prefixdescriptor = struct.Struct(">B")
Tags = enum(
@@ -135,7 +141,7 @@ class Type(object):
'StringWithSize',
# WideStringWithSizeTypeDescriptor
'WideStringWithSize',
#XXX: These are also SimpleTypes (but not in the spec)
# XXX: These are also SimpleTypes (but not in the spec)
# http://hg.mozilla.org/mozilla-central/annotate/0e0e2516f04e/xpcom/typelib/xpt/tools/xpt_dump.c#l69
'UTF8String',
'CString',
@@ -149,6 +155,14 @@ class Type(object):
if reference and not pointer:
raise Exception("If reference is True pointer must be True too")
def __cmp__(self, other):
return (
# First make sure we have two Types of the same type (no pun intended!)
cmp(type(self), type(other)) or
cmp(self.pointer, other.pointer) or
cmp(self.reference, other.reference)
)
@staticmethod
def decodeflags(byte):
"""
@@ -157,7 +171,7 @@ class Type(object):
http://www.mozilla.org/scriptable/typelib_file.html#TypeDescriptor
and return a dict of flagname: (True|False) suitable
for passing to Type.__init__ as **kwargs.
"""
return {'pointer': bool(byte & 0x80),
'reference': bool(byte & 0x20),
@@ -182,7 +196,7 @@ class Type(object):
data pool offset |data_pool|. Returns (Type, next offset),
where |next offset| is an offset suitable for reading the data
following this TypeDescriptor.
"""
start = data_pool + offset - 1
(data,) = Type._prefixdescriptor.unpack_from(map, start)
@@ -217,6 +231,7 @@ class Type(object):
"""
file.write(Type._prefixdescriptor.pack(self.encodeflags() | self.tag))
class SimpleType(Type):
"""
A simple data type. (SimpleTypeDescriptor from the typelib specification.)
@@ -228,13 +243,19 @@ class SimpleType(Type):
Type.__init__(self, **kwargs)
self.tag = tag
def __cmp__(self, other):
return (
Type.__cmp__(self, other) or
cmp(self.tag, other.tag)
)
@staticmethod
def get(data, tag, flags):
"""
Get a SimpleType object representing |data| (a TypeDescriptorPrefix).
May return an already-created object. If no cached object is found,
construct one with |tag| and |flags|.
"""
if data not in SimpleType._cache:
SimpleType._cache[data] = SimpleType(tag, **flags)
@@ -258,6 +279,7 @@ class SimpleType(Type):
s += " *"
return s
class InterfaceType(Type):
"""
A type representing a pointer to an IDL-defined interface.
@@ -268,11 +290,19 @@ class InterfaceType(Type):
def __init__(self, iface, pointer=True, **kwargs):
if not pointer:
raise DataError, "InterfaceType is not valid with pointer=False"
raise DataError("InterfaceType is not valid with pointer=False")
Type.__init__(self, pointer=pointer, **kwargs)
self.iface = iface
self.tag = Type.Tags.Interface
def __cmp__(self, other):
return (
Type.__cmp__(self, other) or
# When comparing interface types, only look at the name.
cmp(self.iface.name, other.iface.name) or
cmp(self.tag, other.tag)
)
@staticmethod
def read(typelib, map, data_pool, offset, flags):
"""
@@ -281,7 +311,7 @@ class InterfaceType(Type):
Returns (InterfaceType, next offset),
where |next offset| is an offset suitable for reading the data
following this InterfaceTypeDescriptor.
"""
if not flags['pointer']:
return None, offset
@@ -309,23 +339,31 @@ class InterfaceType(Type):
return self.iface.name
return "unknown interface"
class InterfaceIsType(Type):
"""
A type representing an interface described by one of the other
arguments to the method. (InterfaceIsTypeDescriptor from the
typelib specification.)
"""
_descriptor = struct.Struct(">B")
_cache = {}
def __init__(self, param_index, pointer=True, **kwargs):
if not pointer:
raise DataError, "InterfaceIsType is not valid with pointer=False"
raise DataError("InterfaceIsType is not valid with pointer=False")
Type.__init__(self, pointer=pointer, **kwargs)
self.param_index = param_index
self.tag = Type.Tags.InterfaceIs
def __cmp__(self, other):
return (
Type.__cmp__(self, other) or
cmp(self.param_index, other.param_index) or
cmp(self.tag, other.tag)
)
@staticmethod
def read(typelib, map, data_pool, offset, flags):
"""
@@ -335,7 +373,7 @@ class InterfaceIsType(Type):
where |next offset| is an offset suitable for reading the data
following this InterfaceIsTypeDescriptor.
May return a cached value.
"""
if not flags['pointer']:
return None, offset
@@ -358,25 +396,35 @@ class InterfaceIsType(Type):
def __str__(self):
return "InterfaceIs *"
class ArrayType(Type):
"""
A type representing an Array of elements of another type, whose
size and length are passed as separate parameters to a method.
(ArrayTypeDescriptor from the typelib specification.)
"""
_descriptor = struct.Struct(">BB")
def __init__(self, element_type, size_is_arg_num, length_is_arg_num,
pointer=True, **kwargs):
if not pointer:
raise DataError, "ArrayType is not valid with pointer=False"
raise DataError("ArrayType is not valid with pointer=False")
Type.__init__(self, pointer=pointer, **kwargs)
self.element_type = element_type
self.size_is_arg_num = size_is_arg_num
self.length_is_arg_num = length_is_arg_num
self.tag = Type.Tags.Array
def __cmp__(self, other):
return (
Type.__cmp__(self, other) or
cmp(self.element_type, other.element_type) or
cmp(self.size_is_arg_num, other.size_is_arg_num) or
cmp(self.length_is_arg_num, other.length_is_arg_num) or
cmp(self.tag, other.tag)
)
@staticmethod
def read(typelib, map, data_pool, offset, flags):
"""
@@ -408,6 +456,7 @@ class ArrayType(Type):
def __str__(self):
return "%s []" % str(self.element_type)
class StringWithSizeType(Type):
"""
A type representing a UTF-8 encoded string whose size and length
@@ -420,12 +469,20 @@ class StringWithSizeType(Type):
def __init__(self, size_is_arg_num, length_is_arg_num,
pointer=True, **kwargs):
if not pointer:
raise DataError, "StringWithSizeType is not valid with pointer=False"
raise DataError("StringWithSizeType is not valid with pointer=False")
Type.__init__(self, pointer=pointer, **kwargs)
self.size_is_arg_num = size_is_arg_num
self.length_is_arg_num = length_is_arg_num
self.tag = Type.Tags.StringWithSize
def __cmp__(self, other):
return (
Type.__cmp__(self, other) or
cmp(self.size_is_arg_num, other.size_is_arg_num) or
cmp(self.length_is_arg_num, other.length_is_arg_num) or
cmp(self.tag, other.tag)
)
@staticmethod
def read(typelib, map, data_pool, offset, flags):
"""
@@ -455,6 +512,7 @@ class StringWithSizeType(Type):
def __str__(self):
return "string_s"
class WideStringWithSizeType(Type):
"""
A type representing a UTF-16 encoded string whose size and length
@@ -467,12 +525,20 @@ class WideStringWithSizeType(Type):
def __init__(self, size_is_arg_num, length_is_arg_num,
pointer=True, **kwargs):
if not pointer:
raise DataError, "WideStringWithSizeType is not valid with pointer=False"
raise DataError("WideStringWithSizeType is not valid with pointer=False")
Type.__init__(self, pointer=pointer, **kwargs)
self.size_is_arg_num = size_is_arg_num
self.length_is_arg_num = length_is_arg_num
self.tag = Type.Tags.WideStringWithSize
def __cmp__(self, other):
return (
Type.__cmp__(self, other) or
cmp(self.size_is_arg_num, other.size_is_arg_num) or
cmp(self.length_is_arg_num, other.length_is_arg_num) or
cmp(self.tag, other.tag)
)
@staticmethod
def read(typelib, map, data_pool, offset, flags):
"""
@@ -502,6 +568,7 @@ class WideStringWithSizeType(Type):
def __str__(self):
return "wstring_s"
class Param(object):
"""
A parameter to a method, or the return value of a method.
@@ -526,6 +593,17 @@ class Param(object):
self.dipper = dipper
self.optional = optional
def __cmp__(self, other):
return (
cmp(self.type, other.type) or
cmp(self.in_, other.in_) or
cmp(self.out, other.out) or
cmp(self.retval, other.retval) or
cmp(self.shared, other.shared) or
cmp(self.dipper, other.dipper) or
cmp(self.optional, other.optional)
)
@staticmethod
def decodeflags(byte):
"""
@@ -540,7 +618,7 @@ class Param(object):
'retval': bool(byte & 0x20),
'shared': bool(byte & 0x10),
'dipper': bool(byte & 0x08),
#XXX: Not in the spec, see:
# XXX: Not in the spec, see:
# http://hg.mozilla.org/mozilla-central/annotate/0e0e2516f04e/xpcom/typelib/xpt/public/xpt_struct.h#l456
'optional': bool(byte & 0x04),
}
@@ -620,12 +698,13 @@ class Param(object):
def __str__(self):
return self.prefix() + str(self.type)
class Method(object):
"""
A method of an interface, defining its associated parameters
and return value.
(MethodDescriptor from the typelib specification.)
"""
_descriptorstart = struct.Struct(">BIB")
@@ -647,13 +726,27 @@ class Method(object):
raise Exception("result must be a Param!")
self.result = result
def __cmp__(self, other):
return (
cmp(self.name, other.name) or
cmp(self.getter, other.getter) or
cmp(self.setter, other.setter) or
cmp(self.notxpcom, other.notxpcom) or
cmp(self.constructor, other.constructor) or
cmp(self.hidden, other.hidden) or
cmp(self.optargc, other.optargc) or
cmp(self.implicit_jscontext, other.implicit_jscontext) or
cmp(self.params, other.params) or
cmp(self.result, other.result)
)
def read_params(self, typelib, map, data_pool, offset, num_args):
"""
Read |num_args| ParamDescriptors representing this Method's arguments
from the mmaped file |map| with data pool at the offset |data_pool|,
starting at |offset| into self.params. Returns the offset
suitable for reading the data following the ParamDescriptor array.
"""
for i in range(num_args):
p, offset = Param.read(typelib, map, data_pool, offset)
@@ -666,7 +759,7 @@ class Method(object):
from the mmaped file |map| with data pool at the offset |data_pool|,
starting at |offset| into self.result. Returns the offset
suitable for reading the data following the ParamDescriptor.
"""
self.result, offset = Param.read(typelib, map, data_pool, offset)
return offset
@@ -679,7 +772,7 @@ class Method(object):
http://www.mozilla.org/scriptable/typelib_file.html#MethodDescriptor
and return a dict of flagname: (True|False) suitable
for passing to Method.__init__ as **kwargs
"""
return {'getter': bool(byte & 0x80),
'setter': bool(byte & 0x40),
@@ -722,7 +815,7 @@ class Method(object):
data pool offset |data_pool|. Returns (Method, next offset),
where |next offset| is an offset suitable for reading the data
following this MethodDescriptor.
"""
start = data_pool + offset - 1
flags, name_offset, num_args = Method._descriptorstart.unpack_from(map, start)
@@ -762,6 +855,7 @@ class Method(object):
else:
self._name_offset = 0
class Constant(object):
"""
A constant value of a specific type defined on an interface.
@@ -770,7 +864,7 @@ class Constant(object):
"""
_descriptorstart = struct.Struct(">I")
# Actual value is restricted to this set of types
#XXX: the spec lies, the source allows a bunch more
# XXX: the spec lies, the source allows a bunch more
# http://hg.mozilla.org/mozilla-central/annotate/9c85f9aaec8c/xpcom/typelib/xpt/src/xpt_struct.c#l689
typemap = {Type.Tags.int16: '>h',
Type.Tags.uint16: '>H',
@@ -783,6 +877,13 @@ class Constant(object):
self.type = type
self.value = value
def __cmp__(self, other):
return (
cmp(self.name, other.name) or
cmp(self.type, other.type) or
cmp(self.value, other.value)
)
@staticmethod
def read(typelib, map, data_pool, offset):
"""
@@ -790,7 +891,7 @@ class Constant(object):
data pool offset |data_pool|. Returns (Constant, next offset),
where |next offset| is an offset suitable for reading the data
following this ConstDescriptor.
"""
start = data_pool + offset - 1
(name_offset,) = Constant._descriptorstart.unpack_from(map, start)
@@ -834,12 +935,13 @@ class Constant(object):
def __repr__(self):
return "Constant(%s, %s, %d)" % (self.name, str(self.type), self.value)
class Interface(object):
"""
An Interface represents an object, with its associated methods
and constant values.
(InterfaceDescriptor from the typelib specification.)
"""
_direntry = struct.Struct(">16sIII")
_descriptorstart = struct.Struct(">HH")
@@ -851,7 +953,7 @@ class Interface(object):
scriptable=False, function=False, builtinclass=False,
main_process_scriptable_only=False):
self.resolved = resolved
#TODO: should validate IIDs!
# TODO: should validate IIDs!
self.iid = iid
self.name = name
self.namespace = namespace
@@ -868,7 +970,7 @@ class Interface(object):
if self.methods or self.constants:
# make sure it has a valid IID
if self.iid == Interface.UNRESOLVED_IID:
raise DataError, "Cannot instantiate Interface %s containing methods or constants with an unresolved IID" % self.name
raise DataError("Cannot instantiate Interface %s containing methods or constants with an unresolved IID" % self.name)
self.resolved = True
# These are only used for writing out the interface
self._descriptor_offset = 0
@@ -902,10 +1004,27 @@ class Interface(object):
else:
return 1
else:
# both unresolved, but names and IIDs are the same, so equal
return 0
#TODO: actually compare methods etc
return 0
if not self.resolved:
# both unresolved, but names and IIDs are the same, so equal
return 0
# When comparing parents, only look at the name.
if (self.parent is None) != (other.parent is None):
if self.parent is None:
return -1
else:
return 1
elif self.parent is not None:
c = cmp(self.parent.name, other.parent.name)
if c != 0:
return c
return (
cmp(self.methods, other.methods) or
cmp(self.constants, other.constants) or
cmp(self.scriptable, other.scriptable) or
cmp(self.function, other.function) or
cmp(self.builtinclass, other.builtinclass) or
cmp(self.main_process_scriptable_only, other.main_process_scriptable_only)
)
def read_descriptor(self, typelib, map, data_pool):
offset = self._descriptor_offset
@@ -1008,6 +1127,7 @@ class Interface(object):
for c in self.constants:
c.write_name(file, data_pool_offset)
class Typelib(object):
"""
A typelib represents one entire typelib file and all the interfaces
@@ -1049,7 +1169,7 @@ class Typelib(object):
Convert a UUID string into a 16-byte IID.
"""
s = iid_str.replace('-','')
s = iid_str.replace('-', '')
return ''.join([chr(int(s[i:i+2], 16)) for i in range(0, len(s), 2)])
@staticmethod
@@ -1089,12 +1209,12 @@ class Typelib(object):
interface_directory_offset,
data_pool_offset) = Typelib._header.unpack_from(data)
if magic != XPT_MAGIC:
raise FileFormatError, "Bad magic: %s" % magic
raise FileFormatError("Bad magic: %s" % magic)
xpt = Typelib((major_ver, minor_ver))
xpt.filename = filename
if expected_size and file_length != expected_size:
raise FileFormatError, "File is of wrong length, got %d bytes, expected %d" % (expected_size, file_length)
#XXX: by spec this is a zero-based file offset. however,
raise FileFormatError("File is of wrong length, got %d bytes, expected %d" % (expected_size, file_length))
# XXX: by spec this is a zero-based file offset. however,
# the xpt_xdr code always subtracts 1 from data offsets
# (because that's what you do in the data pool) so it
# winds up accidentally treating this as 1-based.
@@ -1104,9 +1224,8 @@ class Typelib(object):
# since XPIDL doesn't produce any anyway.
start = Typelib._header.size
(anno, ) = struct.unpack_from(">B", data, start)
islast = anno & 0x80
tag = anno & 0x7F
if tag == 0: # EmptyAnnotation
if tag == 0: # EmptyAnnotation
xpt.annotations.append(None)
# We don't bother handling PrivateAnnotations or anything
@@ -1138,14 +1257,14 @@ class Typelib(object):
self.interfaces.sort()
for i in self.interfaces:
if i.parent and i.parent not in self.interfaces:
raise DataError, "Interface %s has parent %s not present in typelib!" % (i.name, i.parent.name)
raise DataError("Interface %s has parent %s not present in typelib!" % (i.name, i.parent.name))
for m in i.methods:
for n, p in enumerate(m.params):
if isinstance(p, InterfaceType) and \
p.iface not in self.interfaces:
raise DataError, "Interface method %s::%s, parameter %d references interface %s not present in typelib!" % (i.name, m.name, n, p.iface.name)
p.iface not in self.interfaces:
raise DataError("Interface method %s::%s, parameter %d references interface %s not present in typelib!" % (i.name, m.name, n, p.iface.name))
if isinstance(m.result, InterfaceType) and m.result.iface not in self.interfaces:
raise DataError, "Interface method %s::%s, result references interface %s not present in typelib!" % (i.name, m.name, m.result.iface.name)
raise DataError("Interface method %s::%s, result references interface %s not present in typelib!" % (i.name, m.name, m.result.iface.name))
def writefd(self, fd):
# write out space for a header + one empty annotation,
@@ -1177,7 +1296,7 @@ class Typelib(object):
# write an empty annotation
fd.write(struct.pack(">B", 0x80))
# now write the interface directory
#XXX: bug-compatible with existing xpt lib, put it one byte
# XXX: bug-compatible with existing xpt lib, put it one byte
# ahead of where it's supposed to be.
fd.seek(interface_directory_offset - 1)
for i in self.interfaces:
@@ -1200,7 +1319,7 @@ class Typelib(object):
"""
Print a human-readable listing of the contents of this typelib
to |out|, in the format of xpt_dump.
"""
out.write("""Header:
Major version: %d
@@ -1218,7 +1337,7 @@ class Typelib(object):
else:
if i.parent:
out.write(" Parent: %s::%s\n" % (i.parent.namespace,
i.parent.name))
i.parent.name))
out.write(""" Flags:
Scriptable: %s
BuiltinClass: %s
@@ -1249,6 +1368,7 @@ class Typelib(object):
for c in i.constants:
out.write(" %s %s = %d;\n" % (c.type, c.name, c.value))
def xpt_dump(file):
"""
Dump the contents of |file| to stdout in the format of xpt_dump.
@@ -1257,6 +1377,7 @@ def xpt_dump(file):
t = Typelib.read(file)
t.dump(sys.stdout)
def xpt_link(inputs):
"""
Link all of the xpt files in |inputs| together and return the result
@@ -1287,9 +1408,9 @@ def xpt_link(inputs):
Result = enum('Equal', # Interfaces the same, doesn't matter
'NotEqual', # Interfaces differ, keep both
'KeepFirst', # Replace second interface with first
'KeepSecond')# Replace first interface with second
'KeepFirst', # Replace second interface with first
'KeepSecond') # Replace first interface with second
def compare(i, j):
"""
Compare two interfaces, determine if they're equal or
@@ -1303,10 +1424,10 @@ def xpt_link(inputs):
if i.name != j.name:
if i.iid == j.iid and i.iid != Interface.UNRESOLVED_IID:
# Same IID but different names: raise an exception.
raise DataError, \
"Typelibs contain definitions of interface %s" \
" with different names (%s (%s) vs %s (%s))!" % \
(i.iid, i.name, i.xpt_filename, j.name, j.xpt_filename)
raise DataError(
"Typelibs contain definitions of interface %s"
" with different names (%s (%s) vs %s (%s))!" %
(i.iid, i.name, i.xpt_filename, j.name, j.xpt_filename))
# Otherwise just different interfaces.
return Result.NotEqual
# Interfaces have the same name, so either they need to be merged
@@ -1333,14 +1454,14 @@ def xpt_link(inputs):
return Result.KeepSecond
else:
# Same name but different IIDs: raise an exception.
raise DataError, \
"Typelibs contain definitions of interface %s" \
" with different IIDs (%s (%s) vs %s (%s))!" % \
(i.name, i.iid, i.xpt_filename, \
j.iid, j.xpt_filename)
raise DataError, "No idea what happened here: %s:%s (%s), %s:%s (%s)" % \
(i.name, i.iid, i.xpt_filename, j.name, j.iid, j.xpt_filename)
raise DataError(
"Typelibs contain definitions of interface %s"
" with different IIDs (%s (%s) vs %s (%s))!" %
(i.name, i.iid, i.xpt_filename,
j.iid, j.xpt_filename))
raise DataError("No idea what happened here: %s:%s (%s), %s:%s (%s)" %
(i.name, i.iid, i.xpt_filename, j.name, j.iid, j.xpt_filename))
# Compare interfaces pairwise to find duplicates that should be merged.
i = 1
while i < len(interfaces):
@@ -1356,15 +1477,15 @@ def xpt_link(inputs):
elif res == Result.KeepSecond:
merged_interfaces[interfaces[i-1]] = interfaces[i]
del interfaces[i-1]
# Now fixup any merged interfaces
def checkType(t):
if isinstance(t, InterfaceType) and t.iface in merged_interfaces:
t.iface = merged_interfaces[t.iface]
elif isinstance(t, ArrayType) and \
isinstance(t.element_type, InterfaceType) and \
t.element_type.iface in merged_interfaces:
t.element_type.iface = merged_interfaces[t.element_type.iface]
isinstance(t.element_type, InterfaceType) and \
t.element_type.iface in merged_interfaces:
t.element_type.iface = merged_interfaces[t.element_type.iface]
for i in interfaces:
# Replace parent references
@@ -1383,6 +1504,7 @@ def xpt_link(inputs):
# scriptable interfaces.
worklist = set(i for i in interfaces if i.scriptable)
required_interfaces = set()
def maybe_add_to_worklist(iface):
if iface in required_interfaces or iface in worklist:
return
@@ -1416,4 +1538,3 @@ if __name__ == '__main__':
xpt_dump(sys.argv[2])
elif sys.argv[1] == 'link':
xpt_link(sys.argv[3:]).write(sys.argv[2])
+1
View File
@@ -208,6 +208,7 @@ DoInterfaceDescriptor(XPTArena *arena, XPTCursor *outer,
if (!XPT_Do32(outer, &cursor->offset))
return PR_FALSE;
if (!cursor->offset) {
*idp = NULL;
return PR_TRUE;
}
if(!XPT_Do16(cursor, &id->parent_interface) ||