Bug 1041232: Resolve GMP API lifetime issues and allow mid-call shutdown, etc r=cpearce

This commit is contained in:
Randell Jesup 2014-07-24 21:47:40 -04:00
parent 721e6c9846
commit 1965298721
11 changed files with 299 additions and 91 deletions

View File

@ -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

View File

@ -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<GMPVideoDecoderParent*>(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<GMPVideoEncoderParent*>(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);

View File

@ -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<GMPVideoDecoderParent> kungfudeathgrip(this);
NS_RELEASE(kungfudeathgrip);
Shutdown();
}
nsresult
GMPVideoDecoderParent::InitDecode(const GMPVideoCodec& aCodecSettings,
const nsTArray<uint8_t>& 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<GMPVideoEncodedFrame> 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<GMPVideoEncodedFrameImpl*>(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();
}

View File

@ -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<uint8_t>& 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<uint64_t>(mPlugin.get()); }
// GMPSharedMemManager
@ -76,9 +77,9 @@ private:
Shmem* aMem) MOZ_OVERRIDE;
virtual bool Recv__delete__() MOZ_OVERRIDE;
bool mCanSendMessages;
bool mIsOpen;
nsRefPtr<GMPParent> mPlugin;
GMPVideoDecoderCallback* mCallback;
GMPVideoDecoderCallbackProxy* mCallback;
GMPVideoHostImpl mVideoHost;
};

View File

@ -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<uint8_t>& 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

View File

@ -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<GMPVideoEncoderParent> kungfudeathgrip(this);
NS_RELEASE(kungfudeathgrip);
Shutdown();
}
GMPErr
GMPVideoEncoderParent::InitEncode(const GMPVideoCodec& aCodecSettings,
const nsTArray<uint8_t>& 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<GMPVideoi420Frame> 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<GMPVideoi420FrameImpl*>(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();
}

View File

@ -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<uint8_t>& 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<uint64_t>(mPlugin.get()); }
// GMPSharedMemManager
@ -73,7 +74,7 @@ private:
Shmem* aMem) MOZ_OVERRIDE;
virtual bool Recv__delete__() MOZ_OVERRIDE;
bool mCanSendMessages;
bool mIsOpen;
nsRefPtr<GMPParent> mPlugin;
GMPVideoEncoderCallbackProxy* mCallback;
GMPVideoHostImpl mVideoHost;

View File

@ -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<uint8_t>& 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_

View File

@ -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',

View File

@ -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<uint8_t> 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<webrtc::VideoFrameType>* 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<uint8_t>& 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)
{

View File

@ -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<webrtc::VideoFrameType>* aFrameTypes);
const std::vector<webrtc::VideoFrameType>* 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<mozIGeckoMediaPluginService> mMPS;
nsCOMPtr<nsIThread> mGMPThread;
GMPVideoDecoderProxy* mGMP;
GMPVideoDecoderProxy* mGMP; // Addref is held for us
GMPVideoHost* mHost;
webrtc::DecodedImageCallback* mCallback;
};