diff --git a/content/media/gmp/GMPChild.cpp b/content/media/gmp/GMPChild.cpp index 0adf7034e7e2..f24dcd7144ff 100644 --- a/content/media/gmp/GMPChild.cpp +++ b/content/media/gmp/GMPChild.cpp @@ -110,7 +110,7 @@ GMPChild::LoadPluginLibrary(const std::string& aPluginPath) } auto platformAPI = new GMPPlatformAPI(); - InitPlatformAPI(*platformAPI); + InitPlatformAPI(*platformAPI, this); if (initFunc(platformAPI) != GMPNoErr) { return false; @@ -301,6 +301,33 @@ GMPChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor) return true; } +PGMPTimerChild* +GMPChild::AllocPGMPTimerChild() +{ + return new GMPTimerChild(this); +} + +bool +GMPChild::DeallocPGMPTimerChild(PGMPTimerChild* aActor) +{ + MOZ_ASSERT(mTimerChild == static_cast(aActor)); + mTimerChild = nullptr; + return true; +} + +GMPTimerChild* +GMPChild::GetGMPTimers() +{ + if (!mTimerChild) { + PGMPTimerChild* sc = SendPGMPTimerConstructor(); + if (!sc) { + return nullptr; + } + mTimerChild = static_cast(sc); + } + return mTimerChild; +} + bool GMPChild::RecvCrashPluginNow() { diff --git a/content/media/gmp/GMPChild.h b/content/media/gmp/GMPChild.h index 93c054d7bcdf..dd453cde4050 100644 --- a/content/media/gmp/GMPChild.h +++ b/content/media/gmp/GMPChild.h @@ -8,6 +8,7 @@ #include "mozilla/gmp/PGMPChild.h" #include "GMPSharedMemManager.h" +#include "GMPTimerChild.h" #include "gmp-entrypoints.h" #include "prlink.h" @@ -28,6 +29,9 @@ public: bool LoadPluginLibrary(const std::string& aPluginPath); MessageLoop* GMPMessageLoop(); + // Main thread only. + GMPTimerChild* GetGMPTimers(); + // GMPSharedMem virtual void CheckThread() MOZ_OVERRIDE; @@ -51,11 +55,16 @@ private: virtual bool DeallocPGMPAudioDecoderChild(PGMPAudioDecoderChild* aActor) MOZ_OVERRIDE; virtual bool RecvPGMPAudioDecoderConstructor(PGMPAudioDecoderChild* aActor) MOZ_OVERRIDE; + virtual PGMPTimerChild* AllocPGMPTimerChild() MOZ_OVERRIDE; + virtual bool DeallocPGMPTimerChild(PGMPTimerChild* aActor) MOZ_OVERRIDE; + virtual bool RecvCrashPluginNow() MOZ_OVERRIDE; virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; virtual void ProcessingError(Result aWhat) MOZ_OVERRIDE; + nsRefPtr mTimerChild; + PRLibrary* mLib; GMPGetAPIFunc mGetAPIFunc; MessageLoop* mGMPMessageLoop; diff --git a/content/media/gmp/GMPDecryptorChild.h b/content/media/gmp/GMPDecryptorChild.h index ecc6229f50e4..6e9a9eb4a886 100644 --- a/content/media/gmp/GMPDecryptorChild.h +++ b/content/media/gmp/GMPDecryptorChild.h @@ -120,7 +120,7 @@ private: #ifdef DEBUG GMPChild* mPlugin; -#endif +#endif }; } // namespace gmp diff --git a/content/media/gmp/GMPParent.cpp b/content/media/gmp/GMPParent.cpp index 791a333b71f6..b3a6e149e4ce 100644 --- a/content/media/gmp/GMPParent.cpp +++ b/content/media/gmp/GMPParent.cpp @@ -16,6 +16,7 @@ #include "mozIGeckoMediaPluginService.h" #include "mozilla/unused.h" #include "nsIObserverService.h" +#include "GMPTimerParent.h" #include "runnable_utils.h" #include "mozilla/dom/CrashReporterParent.h" @@ -92,7 +93,7 @@ GMPParent::Init(GeckoMediaPluginService *aService, nsIFile* aPluginDir) if (NS_FAILED(rv)) { return rv; } - LOGD(("%s::%s: %p for %s", __CLASS__, __FUNCTION__, this, + LOGD(("%s::%s: %p for %s", __CLASS__, __FUNCTION__, this, NS_LossyConvertUTF16toASCII(leafname).get())); MOZ_ASSERT(leafname.Length() > 4); @@ -618,6 +619,28 @@ GMPParent::DeallocPGMPAudioDecoderParent(PGMPAudioDecoderParent* aActor) return true; } +bool +GMPParent::RecvPGMPTimerConstructor(PGMPTimerParent* actor) +{ + return true; +} + +PGMPTimerParent* +GMPParent::AllocPGMPTimerParent() +{ + GMPTimerParent* p = new GMPTimerParent(GMPThread()); + NS_ADDREF(p); // Released in DeallocPGMPTimerParent. + return p; +} + +bool +GMPParent::DeallocPGMPTimerParent(PGMPTimerParent* aActor) +{ + GMPTimerParent* p = static_cast(aActor); + NS_RELEASE(p); + return true; +} + nsresult ParseNextRecord(nsILineInputStream* aLineInputStream, const nsCString& aPrefix, diff --git a/content/media/gmp/GMPParent.h b/content/media/gmp/GMPParent.h index d513b82aebfa..f215f899e059 100644 --- a/content/media/gmp/GMPParent.h +++ b/content/media/gmp/GMPParent.h @@ -12,6 +12,7 @@ #include "GMPDecryptorParent.h" #include "GMPVideoDecoderParent.h" #include "GMPVideoEncoderParent.h" +#include "GMPTimerParent.h" #include "mozilla/gmp/PGMPParent.h" #include "nsCOMPtr.h" #include "nscore.h" @@ -143,7 +144,7 @@ private: virtual PGMPVideoDecoderParent* AllocPGMPVideoDecoderParent() MOZ_OVERRIDE; virtual bool DeallocPGMPVideoDecoderParent(PGMPVideoDecoderParent* aActor) MOZ_OVERRIDE; - + virtual PGMPVideoEncoderParent* AllocPGMPVideoEncoderParent() MOZ_OVERRIDE; virtual bool DeallocPGMPVideoEncoderParent(PGMPVideoEncoderParent* aActor) MOZ_OVERRIDE; @@ -153,6 +154,10 @@ private: virtual PGMPAudioDecoderParent* AllocPGMPAudioDecoderParent() MOZ_OVERRIDE; virtual bool DeallocPGMPAudioDecoderParent(PGMPAudioDecoderParent* aActor) MOZ_OVERRIDE; + virtual bool RecvPGMPTimerConstructor(PGMPTimerParent* actor) MOZ_OVERRIDE; + virtual PGMPTimerParent* AllocPGMPTimerParent() MOZ_OVERRIDE; + virtual bool DeallocPGMPTimerParent(PGMPTimerParent* aActor) MOZ_OVERRIDE; + GMPState mState; nsCOMPtr mDirectory; // plugin directory on disk nsString mName; // base name of plugin on disk, UTF-16 because used for paths diff --git a/content/media/gmp/GMPPlatform.cpp b/content/media/gmp/GMPPlatform.cpp index 82ee879d4601..e58b7bd3eb49 100644 --- a/content/media/gmp/GMPPlatform.cpp +++ b/content/media/gmp/GMPPlatform.cpp @@ -4,13 +4,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GMPPlatform.h" +#include "GMPTimerChild.h" #include "mozilla/Monitor.h" #include "nsAutoPtr.h" +#include "GMPChild.h" +#include namespace mozilla { namespace gmp { static MessageLoop* sMainLoop = nullptr; +static GMPChild* sChild = nullptr; // We just need a refcounted wrapper for GMPTask objects. class Runnable MOZ_FINAL @@ -141,12 +145,30 @@ CreateMutex(GMPMutex** aMutex) return GMPNoErr; } +GMPErr +SetTimerOnMainThread(GMPTask* aTask, int64_t aTimeoutMS) +{ + GMPTimerChild* timers = sChild->GetGMPTimers(); + NS_ENSURE_TRUE(timers, GMPGenericErr); + return timers->SetTimer(aTask, aTimeoutMS); +} + +GMPErr +GetClock(GMPTimestamp* aOutTime) +{ + *aOutTime = time(0) * 1000; + return GMPNoErr; +} + void -InitPlatformAPI(GMPPlatformAPI& aPlatformAPI) +InitPlatformAPI(GMPPlatformAPI& aPlatformAPI, GMPChild* aChild) { if (!sMainLoop) { sMainLoop = MessageLoop::current(); } + if (!sChild) { + sChild = aChild; + } aPlatformAPI.version = 0; aPlatformAPI.createthread = &CreateThread; @@ -154,8 +176,8 @@ InitPlatformAPI(GMPPlatformAPI& aPlatformAPI) aPlatformAPI.syncrunonmainthread = &SyncRunOnMainThread; aPlatformAPI.createmutex = &CreateMutex; aPlatformAPI.createrecord = nullptr; - aPlatformAPI.settimer = nullptr; - aPlatformAPI.getcurrenttime = nullptr; + aPlatformAPI.settimer = &SetTimerOnMainThread; + aPlatformAPI.getcurrenttime = &GetClock; } GMPThreadImpl::GMPThreadImpl() diff --git a/content/media/gmp/GMPPlatform.h b/content/media/gmp/GMPPlatform.h index 5d0051f6fce2..9323d53d91dc 100644 --- a/content/media/gmp/GMPPlatform.h +++ b/content/media/gmp/GMPPlatform.h @@ -15,7 +15,9 @@ namespace gmp { class GMPChild; -void InitPlatformAPI(GMPPlatformAPI& aPlatformAPI); +void InitPlatformAPI(GMPPlatformAPI& aPlatformAPI, GMPChild* aChild); + +GMPErr RunOnMainThread(GMPTask* aTask); class GMPThreadImpl : public GMPThread { diff --git a/content/media/gmp/GMPTimerChild.cpp b/content/media/gmp/GMPTimerChild.cpp new file mode 100644 index 000000000000..0b2b55c79e14 --- /dev/null +++ b/content/media/gmp/GMPTimerChild.cpp @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GMPTimerChild.h" +#include "GMPPlatform.h" +#include "GMPChild.h" + +#define MAX_NUM_TIMERS 1000 + +namespace mozilla { +namespace gmp { + +GMPTimerChild::GMPTimerChild(GMPChild* aPlugin) + : mTimerCount(1) + , mPlugin(aPlugin) +{ + MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); +} + +GMPTimerChild::~GMPTimerChild() +{ + MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); +} + +GMPErr +GMPTimerChild::SetTimer(GMPTask* aTask, int64_t aTimeoutMS) +{ + if (!aTask) { + NS_WARNING("Tried to set timer with null task!"); + return GMPGenericErr; + } + + if (mPlugin->GMPMessageLoop() != MessageLoop::current()) { + NS_WARNING("Tried to set GMP timer on non-main thread."); + return GMPGenericErr; + } + + if (mTimers.Count() > MAX_NUM_TIMERS) { + return GMPQuotaExceededErr; + } + uint32_t timerId = mTimerCount; + mTimers.Put(timerId, aTask); + mTimerCount++; + + if (!SendSetTimer(timerId, aTimeoutMS)) { + return GMPGenericErr; + } + return GMPNoErr; +} + +bool +GMPTimerChild::RecvTimerExpired(const uint32_t& aTimerId) +{ + MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); + + GMPTask* task = mTimers.Get(aTimerId); + mTimers.Remove(aTimerId); + if (task) { + RunOnMainThread(task); + } + return true; +} + +} // namespace gmp +} // namespace mozilla diff --git a/content/media/gmp/GMPTimerChild.h b/content/media/gmp/GMPTimerChild.h new file mode 100644 index 000000000000..eb153dbd9f70 --- /dev/null +++ b/content/media/gmp/GMPTimerChild.h @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GMPTimerChild_h_ +#define GMPTimerChild_h_ + +#include "mozilla/gmp/PGMPTimerChild.h" +#include "mozilla/Monitor.h" +#include "nsDataHashtable.h" +#include "nsHashKeys.h" +#include "gmp-errors.h" +#include "gmp-platform.h" + +namespace mozilla { +namespace gmp { + +class GMPChild; + +class GMPTimerChild : public PGMPTimerChild +{ +public: + NS_INLINE_DECL_REFCOUNTING(GMPTimerChild) + + GMPTimerChild(GMPChild* aPlugin); + + GMPErr SetTimer(GMPTask* aTask, int64_t aTimeoutMS); + +protected: + // GMPTimerChild + virtual bool RecvTimerExpired(const uint32_t& aTimerId) MOZ_OVERRIDE; + +private: + ~GMPTimerChild(); + + nsDataHashtable mTimers; + uint32_t mTimerCount; + + GMPChild* mPlugin; +}; + +} // namespace gmp +} // namespace mozilla + +#endif // GMPTimerChild_h_ diff --git a/content/media/gmp/GMPTimerParent.cpp b/content/media/gmp/GMPTimerParent.cpp new file mode 100644 index 000000000000..23307bfc892a --- /dev/null +++ b/content/media/gmp/GMPTimerParent.cpp @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GMPTimerParent.h" +#include "nsComponentManagerUtils.h" +#include "mozilla/unused.h" + +namespace mozilla { +namespace gmp { + +GMPTimerParent::GMPTimerParent(nsIThread* aGMPThread) + : mGMPThread(aGMPThread) +{ +} + +bool +GMPTimerParent::RecvSetTimer(const uint32_t& aTimerId, + const uint32_t& aTimeoutMs) +{ + MOZ_ASSERT(mGMPThread == NS_GetCurrentThread()); + + nsresult rv; + nsAutoPtr ctx(new Context()); + ctx->mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); + NS_ENSURE_SUCCESS(rv, true); + + ctx->mId = aTimerId; + rv = ctx->mTimer->SetTarget(mGMPThread); + NS_ENSURE_SUCCESS(rv, true); + ctx->mParent = this; + + rv = ctx->mTimer->InitWithFuncCallback(&GMPTimerParent::GMPTimerExpired, + ctx, + aTimeoutMs, + nsITimer::TYPE_ONE_SHOT); + NS_ENSURE_SUCCESS(rv, true); + + mTimers.PutEntry(ctx.forget()); + + return true; +} + +/*static */ +PLDHashOperator +GMPTimerParent::CancelTimers(nsPtrHashKey* aContext, void* aClosure) +{ + auto context = aContext->GetKey(); + context->mTimer->Cancel(); + delete context; + return PL_DHASH_NEXT; +} + +void +GMPTimerParent::ActorDestroy(ActorDestroyReason aWhy) +{ + MOZ_ASSERT(mGMPThread == NS_GetCurrentThread()); + mTimers.EnumerateEntries(GMPTimerParent::CancelTimers, nullptr); + mTimers.Clear(); +} + +/* static */ +void +GMPTimerParent::GMPTimerExpired(nsITimer *aTimer, void *aClosure) +{ + MOZ_ASSERT(aClosure); + nsAutoPtr ctx = static_cast(aClosure); + MOZ_ASSERT(ctx->mParent); + if (ctx->mParent) { + ctx->mParent->TimerExpired(ctx); + } +} + +void +GMPTimerParent::TimerExpired(Context* aContext) +{ + MOZ_ASSERT(mGMPThread == NS_GetCurrentThread()); + + uint32_t id = aContext->mId; + mTimers.RemoveEntry(aContext); + if (id) { + unused << SendTimerExpired(id); + } +} + +} // namespace gmp +} // namespace mozilla diff --git a/content/media/gmp/GMPTimerParent.h b/content/media/gmp/GMPTimerParent.h new file mode 100644 index 000000000000..06385a6d1d24 --- /dev/null +++ b/content/media/gmp/GMPTimerParent.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GMPTimerParent_h_ +#define GMPTimerParent_h_ + +#include "mozilla/gmp/PGMPTimerParent.h" +#include "nsITimer.h" +#include "nsCOMPtr.h" +#include "nsClassHashtable.h" +#include "nsHashKeys.h" +#include "nsAutoPtr.h" +#include "mozilla/Monitor.h" +#include "nsIThread.h" + +namespace mozilla { +namespace gmp { + +class GMPTimerParent : public PGMPTimerParent { +public: + NS_INLINE_DECL_REFCOUNTING(GMPTimerParent) + GMPTimerParent(nsIThread* aGMPThread); + +protected: + virtual bool RecvSetTimer(const uint32_t& aTimerId, + const uint32_t& aTimeoutMs) MOZ_OVERRIDE; + virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + +private: + ~GMPTimerParent() {} + + static void GMPTimerExpired(nsITimer *aTimer, void *aClosure); + + struct Context { + Context() { + MOZ_COUNT_CTOR(Context); + } + ~Context() { + MOZ_COUNT_DTOR(Context); + } + nsCOMPtr mTimer; + nsRefPtr mParent; // Note: live timers keep the GMPTimerParent alive. + uint32_t mId; + }; + + static PLDHashOperator + CancelTimers(nsPtrHashKey* aContext, void* aClosure); + + void TimerExpired(Context* aContext); + + nsTHashtable> mTimers; + + nsCOMPtr mGMPThread; +}; + +} // namespace gmp +} // namespace mozilla + +#endif // GMPTimerParent_h_ diff --git a/content/media/gmp/PGMP.ipdl b/content/media/gmp/PGMP.ipdl index 80bebacdeb64..86dfe0bf2ca9 100644 --- a/content/media/gmp/PGMP.ipdl +++ b/content/media/gmp/PGMP.ipdl @@ -8,6 +8,7 @@ include protocol PGMPVideoEncoder; include protocol PCrashReporter; include protocol PGMPDecryptor; include protocol PGMPAudioDecoder; +include protocol PGMPTimer; using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h"; @@ -21,9 +22,11 @@ intr protocol PGMP manages PGMPVideoDecoder; manages PGMPVideoEncoder; manages PCrashReporter; + manages PGMPTimer; parent: async PCrashReporter(NativeThreadId tid); + async PGMPTimer(); child: async PGMPAudioDecoder(); diff --git a/content/media/gmp/PGMPTimer.ipdl b/content/media/gmp/PGMPTimer.ipdl new file mode 100644 index 000000000000..fdcf694c0c75 --- /dev/null +++ b/content/media/gmp/PGMPTimer.ipdl @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PGMP; + +namespace mozilla { +namespace gmp { + +async protocol PGMPTimer +{ + manager PGMP; +child: + TimerExpired(uint32_t aTimerId); +parent: + SetTimer(uint32_t aTimerId, uint32_t aTimeoutMs); + __delete__(); +}; + +} // namespace gmp +} // namespace mozilla diff --git a/content/media/gmp/gmp-api/gmp-platform.h b/content/media/gmp/gmp-api/gmp-platform.h index d35cf888fd6b..feffe0133daf 100644 --- a/content/media/gmp/gmp-api/gmp-platform.h +++ b/content/media/gmp/gmp-api/gmp-platform.h @@ -73,6 +73,8 @@ typedef GMPErr (*GMPCreateRecordPtr)(const char* aRecordName, uint32_t aRecordNameSize, GMPRecord** aOutRecord, GMPRecordClient* aClient); + +// Call on main thread only. typedef GMPErr (*GMPSetTimerOnMainThreadPtr)(GMPTask* aTask, int64_t aTimeoutMS); typedef GMPErr (*GMPGetCurrentTimePtr)(GMPTimestamp* aOutTime); diff --git a/content/media/gmp/moz.build b/content/media/gmp/moz.build index c93fc8d6f45e..2bf2083d1ee6 100644 --- a/content/media/gmp/moz.build +++ b/content/media/gmp/moz.build @@ -46,6 +46,8 @@ EXPORTS += [ 'GMPProcessParent.h', 'GMPService.h', 'GMPSharedMemManager.h', + 'GMPTimerChild.h', + 'GMPTimerParent.h', 'GMPVideoDecoderChild.h', 'GMPVideoDecoderParent.h', 'GMPVideoDecoderProxy.h', @@ -72,6 +74,8 @@ UNIFIED_SOURCES += [ 'GMPProcessParent.cpp', 'GMPService.cpp', 'GMPSharedMemManager.cpp', + 'GMPTimerChild.cpp', + 'GMPTimerParent.cpp', 'GMPVideoDecoderChild.cpp', 'GMPVideoDecoderParent.cpp', 'GMPVideoEncodedFrameImpl.cpp', @@ -87,6 +91,7 @@ IPDL_SOURCES += [ 'PGMP.ipdl', 'PGMPAudioDecoder.ipdl', 'PGMPDecryptor.ipdl', + 'PGMPTimer.ipdl', 'PGMPVideoDecoder.ipdl', 'PGMPVideoEncoder.ipdl', ]