diff --git a/content/media/gmp/GMPCallbackBase.h b/content/media/gmp/GMPCallbackBase.h new file mode 100644 index 000000000000..3d96629ef3a2 --- /dev/null +++ b/content/media/gmp/GMPCallbackBase.h @@ -0,0 +1,21 @@ +/* -*- 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 GMPCallbackBase_h_ +#define GMPCallbackBase_h_ + +class GMPCallbackBase +{ +public: + virtual ~GMPCallbackBase() {} + + // The GMP code will call this if the codec crashes or shuts down. It's + // expected that the consumer (destination of this callback) will respond + // by dropping their reference to the proxy, allowing the proxy/parent to + // be destroyed. + virtual void Terminated() = 0; +}; + +#endif diff --git a/content/media/gmp/GMPParent.cpp b/content/media/gmp/GMPParent.cpp index 7ed5fd56ba3a..e36096d72d59 100644 --- a/content/media/gmp/GMPParent.cpp +++ b/content/media/gmp/GMPParent.cpp @@ -111,12 +111,12 @@ GMPParent::CloseActive() // Invalidate and remove any remaining API objects. for (uint32_t i = mVideoDecoders.Length(); i > 0; i--) { - mVideoDecoders[i - 1]->DecodingComplete(); + mVideoDecoders[i - 1]->Shutdown(); } // Invalidate and remove any remaining API objects. for (uint32_t i = mVideoEncoders.Length(); i > 0; i--) { - mVideoEncoders[i - 1]->EncodingComplete(); + mVideoEncoders[i - 1]->Shutdown(); } // Note: the shutdown of the codecs is async! don't kill @@ -261,6 +261,9 @@ GMPParent::GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD) return NS_ERROR_FAILURE; } GMPVideoDecoderParent *vdp = static_cast(pvdp); + // This addref corresponds to the Proxy pointer the consumer is returned. + // It's dropped by calling Close() on the interface. + NS_ADDREF(vdp); *aGMPVD = vdp; mVideoDecoders.AppendElement(vdp); @@ -282,6 +285,9 @@ GMPParent::GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE) return NS_ERROR_FAILURE; } GMPVideoEncoderParent *vep = static_cast(pvep); + // This addref corresponds to the Proxy pointer the consumer is returned. + // It's dropped by calling Close() on the interface. + NS_ADDREF(vep); *aGMPVE = vep; mVideoEncoders.AppendElement(vep); diff --git a/content/media/gmp/GMPVideoDecoderParent.cpp b/content/media/gmp/GMPVideoDecoderParent.cpp index 626c99f900e2..d940e15fa686 100644 --- a/content/media/gmp/GMPVideoDecoderParent.cpp +++ b/content/media/gmp/GMPVideoDecoderParent.cpp @@ -24,8 +24,18 @@ public: namespace mozilla { namespace gmp { +// States: +// Initial: mIsOpen == false +// on InitDecode success -> Open +// on Shutdown -> Dead +// Open: mIsOpen == true +// on Close -> Dead +// on ActorDestroy -> Dead +// on Shutdown -> Dead +// Dead: mIsOpen == false + GMPVideoDecoderParent::GMPVideoDecoderParent(GMPParent* aPlugin) - : mCanSendMessages(true) + : mIsOpen(false) , mPlugin(aPlugin) , mCallback(nullptr) , mVideoHost(MOZ_THIS_IN_INITIALIZER_LIST()) @@ -43,14 +53,30 @@ GMPVideoDecoderParent::Host() return mVideoHost; } +// Note: may be called via Terminated() +void +GMPVideoDecoderParent::Close() +{ + MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); + // Consumer is done with us; we can shut down. No more callbacks should + // be made to mCallback. Note: do this before Shutdown()! + mCallback = nullptr; + // Let Shutdown mark us as dead so it knows if we had been alive + + // In case this is the last reference + nsRefPtr kungfudeathgrip(this); + NS_RELEASE(kungfudeathgrip); + Shutdown(); +} + nsresult GMPVideoDecoderParent::InitDecode(const GMPVideoCodec& aCodecSettings, const nsTArray& aCodecSpecific, - GMPVideoDecoderCallback* aCallback, + GMPVideoDecoderCallbackProxy* aCallback, int32_t aCoreCount) { - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video decoder!"); + if (mIsOpen) { + NS_WARNING("Trying to re-init an in-use GMP video decoder!"); return NS_ERROR_FAILURE; } @@ -64,6 +90,7 @@ GMPVideoDecoderParent::InitDecode(const GMPVideoCodec& aCodecSettings, if (!SendInitDecode(aCodecSettings, aCodecSpecific, aCoreCount)) { return NS_ERROR_FAILURE; } + mIsOpen = true; // Async IPC, we don't have access to a return value. return NS_OK; @@ -77,8 +104,8 @@ GMPVideoDecoderParent::Decode(GMPVideoEncodedFrame* aInputFrame, { nsAutoRef autoDestroy(aInputFrame); - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video decoder!"); + if (!mIsOpen) { + NS_WARNING("Trying to use an dead GMP video decoder"); return NS_ERROR_FAILURE; } @@ -86,9 +113,6 @@ GMPVideoDecoderParent::Decode(GMPVideoEncodedFrame* aInputFrame, auto inputFrameImpl = static_cast(aInputFrame); - GMPVideoEncodedFrameData frameData; - inputFrameImpl->RelinquishFrameData(frameData); - // Very rough kill-switch if the plugin stops processing. If it's merely // hung and continues, we'll come back to life eventually. // 3* is because we're using 3 buffers per frame for i420 data for now. @@ -97,6 +121,9 @@ GMPVideoDecoderParent::Decode(GMPVideoEncodedFrame* aInputFrame, return NS_ERROR_FAILURE; } + GMPVideoEncodedFrameData frameData; + inputFrameImpl->RelinquishFrameData(frameData); + if (!SendDecode(frameData, aMissingFrames, aCodecSpecificInfo, @@ -111,8 +138,8 @@ GMPVideoDecoderParent::Decode(GMPVideoEncodedFrame* aInputFrame, nsresult GMPVideoDecoderParent::Reset() { - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video decoder!"); + if (!mIsOpen) { + NS_WARNING("Trying to use an dead GMP video decoder"); return NS_ERROR_FAILURE; } @@ -129,8 +156,8 @@ GMPVideoDecoderParent::Reset() nsresult GMPVideoDecoderParent::Drain() { - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video decoder!"); + if (!mIsOpen) { + NS_WARNING("Trying to use an dead GMP video decoder"); return NS_ERROR_FAILURE; } @@ -146,37 +173,41 @@ GMPVideoDecoderParent::Drain() // Note: Consider keeping ActorDestroy sync'd up when making changes here. nsresult -GMPVideoDecoderParent::DecodingComplete() +GMPVideoDecoderParent::Shutdown() { - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video decoder!"); - return NS_ERROR_FAILURE; - } - MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); - mCanSendMessages = false; - - mCallback = nullptr; - + // Notify client we're gone! Won't occur after Close() + if (mCallback) { + mCallback->Terminated(); + mCallback = nullptr; + } mVideoHost.DoneWithAPI(); - unused << SendDecodingComplete(); + if (mIsOpen) { + // Don't send DecodingComplete if we died + mIsOpen = false; + unused << SendDecodingComplete(); + } return NS_OK; } -// Note: Keep this sync'd up with DecodingComplete +// Note: Keep this sync'd up with Shutdown void GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy) { + mIsOpen = false; + if (mCallback) { + // May call Close() (and Shutdown()) immediately or with a delay + mCallback->Terminated(); + mCallback = nullptr; + } if (mPlugin) { // Ignore any return code. It is OK for this to fail without killing the process. mPlugin->VideoDecoderDestroyed(this); mPlugin = nullptr; } - mCanSendMessages = false; - mCallback = nullptr; mVideoHost.ActorDestroyed(); } diff --git a/content/media/gmp/GMPVideoDecoderParent.h b/content/media/gmp/GMPVideoDecoderParent.h index a104dba46eed..ac8a9fc5ff6b 100644 --- a/content/media/gmp/GMPVideoDecoderParent.h +++ b/content/media/gmp/GMPVideoDecoderParent.h @@ -29,11 +29,13 @@ public: GMPVideoDecoderParent(GMPParent *aPlugin); GMPVideoHostImpl& Host(); + nsresult Shutdown(); // GMPVideoDecoder + virtual void Close() MOZ_OVERRIDE; virtual nsresult InitDecode(const GMPVideoCodec& aCodecSettings, const nsTArray& aCodecSpecific, - GMPVideoDecoderCallback* aCallback, + GMPVideoDecoderCallbackProxy* aCallback, int32_t aCoreCount) MOZ_OVERRIDE; virtual nsresult Decode(GMPVideoEncodedFrame* aInputFrame, bool aMissingFrames, @@ -41,7 +43,6 @@ public: int64_t aRenderTimeMs = -1) MOZ_OVERRIDE; virtual nsresult Reset() MOZ_OVERRIDE; virtual nsresult Drain() MOZ_OVERRIDE; - virtual nsresult DecodingComplete() MOZ_OVERRIDE; virtual const uint64_t ParentID() MOZ_OVERRIDE { return reinterpret_cast(mPlugin.get()); } // GMPSharedMemManager @@ -76,9 +77,9 @@ private: Shmem* aMem) MOZ_OVERRIDE; virtual bool Recv__delete__() MOZ_OVERRIDE; - bool mCanSendMessages; + bool mIsOpen; nsRefPtr mPlugin; - GMPVideoDecoderCallback* mCallback; + GMPVideoDecoderCallbackProxy* mCallback; GMPVideoHostImpl mVideoHost; }; diff --git a/content/media/gmp/GMPVideoDecoderProxy.h b/content/media/gmp/GMPVideoDecoderProxy.h index 5e8f4d587eb3..e3b381edcb73 100644 --- a/content/media/gmp/GMPVideoDecoderProxy.h +++ b/content/media/gmp/GMPVideoDecoderProxy.h @@ -11,15 +11,31 @@ #include "gmp-video-frame-i420.h" #include "gmp-video-frame-encoded.h" +#include "GMPCallbackBase.h" + +class GMPVideoDecoderCallbackProxy : public GMPCallbackBase, + public GMPVideoDecoderCallback +{ +public: + virtual ~GMPVideoDecoderCallbackProxy() {} +}; + // A proxy to GMPVideoDecoder in the child process. // GMPVideoDecoderParent exposes this to users the GMP. // This enables Gecko to pass nsTArrays to the child GMP and avoid // an extra copy when doing so. + +// The consumer must call Close() when done with the codec, or when +// Terminated() is called by the GMP plugin indicating an abnormal shutdown +// of the underlying plugin. After calling Close(), the consumer must +// not access this again. + +// This interface is not thread-safe and must only be used from GMPThread. class GMPVideoDecoderProxy { public: virtual nsresult InitDecode(const GMPVideoCodec& aCodecSettings, const nsTArray& aCodecSpecific, - GMPVideoDecoderCallback* aCallback, + GMPVideoDecoderCallbackProxy* aCallback, int32_t aCoreCount) = 0; virtual nsresult Decode(GMPVideoEncodedFrame* aInputFrame, bool aMissingFrames, @@ -27,8 +43,11 @@ public: int64_t aRenderTimeMs = -1) = 0; virtual nsresult Reset() = 0; virtual nsresult Drain() = 0; - virtual nsresult DecodingComplete() = 0; virtual const uint64_t ParentID() = 0; + + // Call to tell GMP/plugin the consumer will no longer use this + // interface/codec. + virtual void Close() = 0; }; #endif diff --git a/content/media/gmp/GMPVideoEncoderParent.cpp b/content/media/gmp/GMPVideoEncoderParent.cpp index b2e411be58ae..88ec3a08dc89 100644 --- a/content/media/gmp/GMPVideoEncoderParent.cpp +++ b/content/media/gmp/GMPVideoEncoderParent.cpp @@ -24,8 +24,18 @@ public: namespace mozilla { namespace gmp { +// States: +// Initial: mIsOpen == false +// on InitDecode success -> Open +// on Shutdown -> Dead +// Open: mIsOpen == true +// on Close -> Dead +// on ActorDestroy -> Dead +// on Shutdown -> Dead +// Dead: mIsOpen == false + GMPVideoEncoderParent::GMPVideoEncoderParent(GMPParent *aPlugin) -: mCanSendMessages(true), +: mIsOpen(false), mPlugin(aPlugin), mCallback(nullptr), mVideoHost(MOZ_THIS_IN_INITIALIZER_LIST()) @@ -43,6 +53,22 @@ GMPVideoEncoderParent::Host() return mVideoHost; } +// Note: may be called via Terminated() +void +GMPVideoEncoderParent::Close() +{ + MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); + // Consumer is done with us; we can shut down. No more callbacks should + // be made to mCallback. Note: do this before Shutdown()! + mCallback = nullptr; + // Let Shutdown mark us as dead so it knows if we had been alive + + // In case this is the last reference + nsRefPtr kungfudeathgrip(this); + NS_RELEASE(kungfudeathgrip); + Shutdown(); +} + GMPErr GMPVideoEncoderParent::InitEncode(const GMPVideoCodec& aCodecSettings, const nsTArray& aCodecSpecific, @@ -50,9 +76,9 @@ GMPVideoEncoderParent::InitEncode(const GMPVideoCodec& aCodecSettings, int32_t aNumberOfCores, uint32_t aMaxPayloadSize) { - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video encoder!"); - return GMPGenericErr; + if (mIsOpen) { + NS_WARNING("Trying to re-init an in-use GMP video encoder!"); + return GMPGenericErr;; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); @@ -65,6 +91,7 @@ GMPVideoEncoderParent::InitEncode(const GMPVideoCodec& aCodecSettings, if (!SendInitEncode(aCodecSettings, aCodecSpecific, aNumberOfCores, aMaxPayloadSize)) { return GMPGenericErr; } + mIsOpen = true; // Async IPC, we don't have access to a return value. return GMPNoErr; @@ -77,8 +104,8 @@ GMPVideoEncoderParent::Encode(GMPVideoi420Frame* aInputFrame, { nsAutoRef frameRef(aInputFrame); - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video encoder!"); + if (!mIsOpen) { + NS_WARNING("Trying to use an dead GMP video encoder"); return GMPGenericErr; } @@ -86,9 +113,6 @@ GMPVideoEncoderParent::Encode(GMPVideoi420Frame* aInputFrame, auto inputFrameImpl = static_cast(aInputFrame); - GMPVideoi420FrameData frameData; - inputFrameImpl->InitFrameData(frameData); - // Very rough kill-switch if the plugin stops processing. If it's merely // hung and continues, we'll come back to life eventually. // 3* is because we're using 3 buffers per frame for i420 data for now. @@ -97,6 +121,9 @@ GMPVideoEncoderParent::Encode(GMPVideoi420Frame* aInputFrame, return GMPGenericErr; } + GMPVideoi420FrameData frameData; + inputFrameImpl->InitFrameData(frameData); + if (!SendEncode(frameData, aCodecSpecificInfo, aFrameTypes)) { @@ -110,7 +137,7 @@ GMPVideoEncoderParent::Encode(GMPVideoi420Frame* aInputFrame, GMPErr GMPVideoEncoderParent::SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) { - if (!mCanSendMessages) { + if (!mIsOpen) { NS_WARNING("Trying to use an invalid GMP video encoder!"); return GMPGenericErr; } @@ -128,8 +155,8 @@ GMPVideoEncoderParent::SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) GMPErr GMPVideoEncoderParent::SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) { - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video encoder!"); + if (!mIsOpen) { + NS_WARNING("Trying to use an dead GMP video decoder"); return GMPGenericErr; } @@ -146,7 +173,7 @@ GMPVideoEncoderParent::SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) GMPErr GMPVideoEncoderParent::SetPeriodicKeyFrames(bool aEnable) { - if (!mCanSendMessages) { + if (!mIsOpen) { NS_WARNING("Trying to use an invalid GMP video encoder!"); return GMPGenericErr; } @@ -163,35 +190,38 @@ GMPVideoEncoderParent::SetPeriodicKeyFrames(bool aEnable) // Note: Consider keeping ActorDestroy sync'd up when making changes here. void -GMPVideoEncoderParent::EncodingComplete() +GMPVideoEncoderParent::Shutdown() { - if (!mCanSendMessages) { - NS_WARNING("Trying to use an invalid GMP video encoder!"); - return; - } - MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); - mCanSendMessages = false; - - mCallback = nullptr; - + // Notify client we're gone! Won't occur after Close() + if (mCallback) { + mCallback->Terminated(); + mCallback = nullptr; + } mVideoHost.DoneWithAPI(); - - unused << SendEncodingComplete(); + if (mIsOpen) { + // Don't send EncodingComplete if we died + mIsOpen = false; + unused << SendEncodingComplete(); + } } -// Note: Keep this sync'd up with DecodingComplete +// Note: Keep this sync'd up with Shutdown void GMPVideoEncoderParent::ActorDestroy(ActorDestroyReason aWhy) { + mIsOpen = false; + if (mCallback) { + // May call Close() (and Shutdown()) immediately or with a delay + mCallback->Terminated(); + mCallback = nullptr; + } if (mPlugin) { // Ignore any return code. It is OK for this to fail without killing the process. mPlugin->VideoEncoderDestroyed(this); mPlugin = nullptr; } - mCanSendMessages = false; - mCallback = nullptr; mVideoHost.ActorDestroyed(); } diff --git a/content/media/gmp/GMPVideoEncoderParent.h b/content/media/gmp/GMPVideoEncoderParent.h index 5482e9ac0c5f..82943876d41e 100644 --- a/content/media/gmp/GMPVideoEncoderParent.h +++ b/content/media/gmp/GMPVideoEncoderParent.h @@ -29,8 +29,10 @@ public: GMPVideoEncoderParent(GMPParent *aPlugin); GMPVideoHostImpl& Host(); + void Shutdown(); // GMPVideoEncoderProxy + virtual void Close() MOZ_OVERRIDE; virtual GMPErr InitEncode(const GMPVideoCodec& aCodecSettings, const nsTArray& aCodecSpecific, GMPVideoEncoderCallbackProxy* aCallback, @@ -42,7 +44,6 @@ public: virtual GMPErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) MOZ_OVERRIDE; virtual GMPErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) MOZ_OVERRIDE; virtual GMPErr SetPeriodicKeyFrames(bool aEnable) MOZ_OVERRIDE; - virtual void EncodingComplete() MOZ_OVERRIDE; virtual const uint64_t ParentID() MOZ_OVERRIDE { return reinterpret_cast(mPlugin.get()); } // GMPSharedMemManager @@ -73,7 +74,7 @@ private: Shmem* aMem) MOZ_OVERRIDE; virtual bool Recv__delete__() MOZ_OVERRIDE; - bool mCanSendMessages; + bool mIsOpen; nsRefPtr mPlugin; GMPVideoEncoderCallbackProxy* mCallback; GMPVideoHostImpl mVideoHost; diff --git a/content/media/gmp/GMPVideoEncoderProxy.h b/content/media/gmp/GMPVideoEncoderProxy.h index 40e95155d60d..ec77b079cdc8 100644 --- a/content/media/gmp/GMPVideoEncoderProxy.h +++ b/content/media/gmp/GMPVideoEncoderProxy.h @@ -11,8 +11,11 @@ #include "gmp-video-frame-i420.h" #include "gmp-video-frame-encoded.h" -class GMPVideoEncoderCallbackProxy { +#include "GMPCallbackBase.h" + +class GMPVideoEncoderCallbackProxy : public GMPCallbackBase { public: + virtual ~GMPVideoEncoderCallbackProxy() {} virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame, const nsTArray& aCodecSpecificInfo) = 0; virtual void Error(GMPErr aError) = 0; @@ -22,6 +25,13 @@ public: // GMPVideoEncoderParent exposes this to users the GMP. // This enables Gecko to pass nsTArrays to the child GMP and avoid // an extra copy when doing so. + +// The consumer must call Close() when done with the codec, or when +// Terminated() is called by the GMP plugin indicating an abnormal shutdown +// of the underlying plugin. After calling Close(), the consumer must +// not access this again. + +// This interface is not thread-safe and must only be used from GMPThread. class GMPVideoEncoderProxy { public: virtual GMPErr InitEncode(const GMPVideoCodec& aCodecSettings, @@ -35,8 +45,11 @@ public: virtual GMPErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) = 0; virtual GMPErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) = 0; virtual GMPErr SetPeriodicKeyFrames(bool aEnable) = 0; - virtual void EncodingComplete() = 0; virtual const uint64_t ParentID() = 0; + + // Call to tell GMP/plugin the consumer will no longer use this + // interface/codec. + virtual void Close() = 0; }; #endif // GMPVideoEncoderProxy_h_ diff --git a/content/media/gmp/moz.build b/content/media/gmp/moz.build index 9cc330da3351..1beee3227d5f 100644 --- a/content/media/gmp/moz.build +++ b/content/media/gmp/moz.build @@ -29,6 +29,7 @@ EXPORTS += [ 'gmp-api/gmp-video-frame.h', 'gmp-api/gmp-video-host.h', 'gmp-api/gmp-video-plane.h', + 'GMPCallbackBase.h', 'GMPChild.h', 'GMPMessageUtils.h', 'GMPParent.h', diff --git a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp index a188acc1b62d..3f3d5d767358 100644 --- a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp +++ b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp @@ -53,6 +53,25 @@ WebrtcGmpVideoEncoder::WebrtcGmpVideoEncoder() , mCallback(nullptr) {} +static void +Encoder_Close_g(GMPVideoEncoderProxy* aGMP) +{ + aGMP->Close(); +} + +WebrtcGmpVideoEncoder::~WebrtcGmpVideoEncoder() +{ + // Note: we only use SyncRunnables to access mGMP + // Callbacks may occur at any time until we call Close (or receive + // Terminated()), so call Close here synchronously. + // Do NOT touch the refcount of 'this'! + if (mGMPThread && mGMP) { + mozilla::SyncRunnable::DispatchToThread(mGMPThread, + WrapRunnableNM(&Encoder_Close_g, mGMP)); + mGMP = nullptr; + } +} + static int WebrtcFrameTypeToGmpFrameType(webrtc::VideoFrameType aIn, GMPVideoFrameType *aOut) @@ -128,13 +147,13 @@ WebrtcGmpVideoEncoder::InitEncode(const webrtc::VideoCodec* aCodecSettings, } int32_t ret; - mozilla::SyncRunnable::DispatchToThread(mGMPThread, - WrapRunnableRet(this, - &WebrtcGmpVideoEncoder::InitEncode_g, - aCodecSettings, - aNumberOfCores, - aMaxPayloadSize, - &ret)); + mGMPThread->Dispatch(WrapRunnableRet(this, + &WebrtcGmpVideoEncoder::InitEncode_g, + aCodecSettings, + aNumberOfCores, + aMaxPayloadSize, + &ret), + NS_DISPATCH_SYNC); return ret; } @@ -176,7 +195,7 @@ WebrtcGmpVideoEncoder::InitEncode_g(const webrtc::VideoCodec* aCodecSettings, // Pass dummy codecSpecific data for now... nsTArray codecSpecific; - + // H.264 mode 1 only supported so far GMPErr err = mGMP->InitEncode(codec, codecSpecific, this, 1, 1024*1024 /*aMaxPayloadSize*/); if (err != GMPNoErr) { @@ -212,7 +231,10 @@ WebrtcGmpVideoEncoder::Encode_g(const webrtc::I420VideoFrame* aInputImage, const std::vector* aFrameTypes) { MOZ_ASSERT(mHost); - MOZ_ASSERT(mGMP); + if (!mGMP) { + // destroyed via Terminate() + return WEBRTC_VIDEO_CODEC_ERROR; + } GMPVideoFrame* ftmp = nullptr; GMPErr err = mHost->CreateFrame(kGMPI420VideoFrame, &ftmp); @@ -279,6 +301,14 @@ WebrtcGmpVideoEncoder::RegisterEncodeCompleteCallback(webrtc::EncodedImageCallba int32_t WebrtcGmpVideoEncoder::Release() { + // Note: we only use SyncRunnables to access mGMP + // Callbacks may occur at any time until we call Close (or receive + // Terminated()), so call Close here synchronously. + if (mGMPThread && mGMP) { + mozilla::SyncRunnable::DispatchToThread(mGMPThread, + WrapRunnableNM(&Encoder_Close_g, mGMP)); + } + // Now safe to forget things mMPS = nullptr; mGMP = nullptr; mHost = nullptr; @@ -308,7 +338,11 @@ WebrtcGmpVideoEncoder::SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) int32_t WebrtcGmpVideoEncoder::SetRates_g(uint32_t aNewBitRate, uint32_t aFrameRate) { - MOZ_ASSERT(mGMP); + if (!mGMP) { + // destroyed via Terminate() + return WEBRTC_VIDEO_CODEC_ERROR; + } + GMPErr err = mGMP->SetRates(aNewBitRate, aFrameRate); if (err != GMPNoErr) { return WEBRTC_VIDEO_CODEC_ERROR; @@ -318,6 +352,15 @@ WebrtcGmpVideoEncoder::SetRates_g(uint32_t aNewBitRate, uint32_t aFrameRate) } // GMPVideoEncoderCallback virtual functions. +void +WebrtcGmpVideoEncoder::Terminated() +{ + // We need to drop our reference to this + mGMP->Close(); + mGMP = nullptr; + // Could now notify that it's dead +} + void WebrtcGmpVideoEncoder::Encoded(GMPVideoEncodedFrame* aEncodedFrame, const nsTArray& aCodecSpecificInfo) @@ -389,6 +432,25 @@ WebrtcGmpVideoDecoder::WebrtcGmpVideoDecoder() : mHost(nullptr), mCallback(nullptr) {} +static void +Decoder_Close_g(GMPVideoDecoderProxy* aGMP) +{ + aGMP->Close(); +} + +WebrtcGmpVideoDecoder::~WebrtcGmpVideoDecoder() +{ + // Note: we only use SyncRunnables to access mGMP + // Callbacks may occur at any time until we call Close (or receive + // Terminated()), so call Close here synchronously. + // Do NOT touch the refcount of 'this'! + if (mGMPThread && mGMP) { + mozilla::SyncRunnable::DispatchToThread(mGMPThread, + WrapRunnableNM(&Decoder_Close_g, mGMP)); + mGMP = nullptr; + } +} + int32_t WebrtcGmpVideoDecoder::InitDecode(const webrtc::VideoCodec* aCodecSettings, int32_t aNumberOfCores) @@ -403,12 +465,12 @@ WebrtcGmpVideoDecoder::InitDecode(const webrtc::VideoCodec* aCodecSettings, } int32_t ret; - mozilla::SyncRunnable::DispatchToThread(mGMPThread, - WrapRunnableRet(this, - &WebrtcGmpVideoDecoder::InitDecode_g, - aCodecSettings, - aNumberOfCores, - &ret)); + mGMPThread->Dispatch(WrapRunnableRet(this, + &WebrtcGmpVideoDecoder::InitDecode_g, + aCodecSettings, + aNumberOfCores, + &ret), + NS_DISPATCH_SYNC); return ret; } @@ -482,7 +544,10 @@ WebrtcGmpVideoDecoder::Decode_g(const webrtc::EncodedImage& aInputImage, int64_t aRenderTimeMs) { MOZ_ASSERT(mHost); - MOZ_ASSERT(mGMP); + if (!mGMP) { + // destroyed via Terminate() + return WEBRTC_VIDEO_CODEC_ERROR; + } GMPVideoFrame* ftmp = nullptr; GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp); @@ -547,6 +612,14 @@ WebrtcGmpVideoDecoder::RegisterDecodeCompleteCallback( webrtc::DecodedImageCallb int32_t WebrtcGmpVideoDecoder::Release() { + // Note: we only use SyncRunnables to access mGMP + // Callbacks may occur at any time until we call Close (or receive + // Terminated()), so call Close here synchronously. + if (mGMPThread && mGMP) { + mozilla::SyncRunnable::DispatchToThread(mGMPThread, + WrapRunnableNM(&Decoder_Close_g, mGMP)); + } + // Now safe to forget things mMPS = nullptr; mGMP = nullptr; mGMPThread = nullptr; @@ -561,6 +634,14 @@ WebrtcGmpVideoDecoder::Reset() return WEBRTC_VIDEO_CODEC_OK; } +void +WebrtcGmpVideoDecoder::Terminated() +{ + mGMP->Close(); + mGMP = nullptr; + // Could now notify that it's dead +} + void WebrtcGmpVideoDecoder::Decoded(GMPVideoi420Frame* aDecodedFrame) { diff --git a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h index b31b886804aa..653d12b2be46 100644 --- a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h +++ b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h @@ -40,7 +40,7 @@ class WebrtcGmpVideoEncoder : public WebrtcVideoEncoder, { public: WebrtcGmpVideoEncoder(); - virtual ~WebrtcGmpVideoEncoder() {} + virtual ~WebrtcGmpVideoEncoder(); // Implement VideoEncoder interface. virtual const uint64_t PluginID() MOZ_OVERRIDE @@ -48,18 +48,20 @@ public: return mGMP ? mGMP->ParentID() : 0; } + virtual void Terminated() MOZ_OVERRIDE; + virtual int32_t InitEncode(const webrtc::VideoCodec* aCodecSettings, int32_t aNumberOfCores, - uint32_t aMaxPayloadSize); + uint32_t aMaxPayloadSize) MOZ_OVERRIDE; virtual int32_t Encode(const webrtc::I420VideoFrame& aInputImage, const webrtc::CodecSpecificInfo* aCodecSpecificInfo, - const std::vector* aFrameTypes); + const std::vector* aFrameTypes) MOZ_OVERRIDE; virtual int32_t RegisterEncodeCompleteCallback( webrtc::EncodedImageCallback* aCallback) MOZ_OVERRIDE; - virtual int32_t Release(); + virtual int32_t Release() MOZ_OVERRIDE; virtual int32_t SetChannelParameters(uint32_t aPacketLoss, int aRTT) MOZ_OVERRIDE; @@ -95,11 +97,11 @@ private: class WebrtcGmpVideoDecoder : public WebrtcVideoDecoder, - public GMPVideoDecoderCallback + public GMPVideoDecoderCallbackProxy { public: WebrtcGmpVideoDecoder(); - virtual ~WebrtcGmpVideoDecoder() {} + virtual ~WebrtcGmpVideoDecoder(); // Implement VideoDecoder interface. virtual const uint64_t PluginID() MOZ_OVERRIDE @@ -107,6 +109,8 @@ public: return mGMP ? mGMP->ParentID() : 0; } + virtual void Terminated(); + virtual int32_t InitDecode(const webrtc::VideoCodec* aCodecSettings, int32_t aNumberOfCores); virtual int32_t Decode(const webrtc::EncodedImage& aInputImage, @@ -157,7 +161,7 @@ private: nsCOMPtr mMPS; nsCOMPtr mGMPThread; - GMPVideoDecoderProxy* mGMP; + GMPVideoDecoderProxy* mGMP; // Addref is held for us GMPVideoHost* mHost; webrtc::DecodedImageCallback* mCallback; };