mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 1041232: Resolve GMP API lifetime issues and allow mid-call shutdown, etc r=cpearce
This commit is contained in:
parent
721e6c9846
commit
1965298721
21
content/media/gmp/GMPCallbackBase.h
Normal file
21
content/media/gmp/GMPCallbackBase.h
Normal 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
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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_
|
||||
|
@ -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',
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user