Bug 1182289: Clean up dispatches in WebrtcGmpVideoEncoder/Decoder. r=jesup, a=abillings

This commit is contained in:
Byron Campen [:bwc] 2015-07-14 09:20:28 -05:00
parent a024b5aa03
commit eeda62f97a
6 changed files with 667 additions and 377 deletions

View File

@ -8,11 +8,11 @@
namespace mozilla {
VideoEncoder* GmpVideoCodec::CreateEncoder() {
return static_cast<VideoEncoder*>(new WebrtcGmpVideoEncoder());
return static_cast<VideoEncoder*>(new WebrtcVideoEncoderProxy());
}
VideoDecoder* GmpVideoCodec::CreateDecoder() {
return static_cast<VideoDecoder*>(new WebrtcGmpVideoDecoder());
return static_cast<VideoDecoder*>(new WebrtcVideoDecoderProxy());
}
}

View File

@ -18,6 +18,7 @@
#include "nsServiceManagerUtils.h"
#include "GMPVideoDecoderProxy.h"
#include "GMPVideoEncoderProxy.h"
#include "MainThreadUtils.h"
#include "gmp-video-host.h"
#include "gmp-video-frame-i420.h"
@ -47,31 +48,61 @@ GetGMPLog()
#define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
#define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
WebrtcGmpPCHandleSetter::WebrtcGmpPCHandleSetter(const std::string& aPCHandle)
{
if (!NS_IsMainThread()) {
MOZ_ASSERT(false, "WebrtcGmpPCHandleSetter can only be used on main");
return;
}
MOZ_ASSERT(sCurrentHandle.empty());
sCurrentHandle = aPCHandle;
}
WebrtcGmpPCHandleSetter::~WebrtcGmpPCHandleSetter()
{
if (!NS_IsMainThread()) {
MOZ_ASSERT(false, "WebrtcGmpPCHandleSetter can only be used on main");
return;
}
sCurrentHandle.clear();
}
/* static */ std::string
WebrtcGmpPCHandleSetter::GetCurrentHandle()
{
if (!NS_IsMainThread()) {
MOZ_ASSERT(false, "WebrtcGmpPCHandleSetter can only be used on main");
return "";
}
return sCurrentHandle;
}
std::string WebrtcGmpPCHandleSetter::sCurrentHandle = "";
// Encoder.
WebrtcGmpVideoEncoder::WebrtcGmpVideoEncoder()
: mGMP(nullptr)
, mInitting(false)
, mHost(nullptr)
, mMaxPayloadSize(0)
, mCallbackMutex("WebrtcGmpVideoEncoder encoded callback mutex")
, mCallback(nullptr)
, mCachedPluginId(0)
{}
static void
Encoder_Close_g(GMPVideoEncoderProxy* aGMP)
{
aGMP->Close();
#ifdef MOZILLA_INTERNAL_API
if (mPCHandle.empty()) {
mPCHandle = WebrtcGmpPCHandleSetter::GetCurrentHandle();
}
MOZ_ASSERT(!mPCHandle.empty());
#endif
}
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;
}
// We should not have been destroyed if we never closed our GMP
MOZ_ASSERT(!mGMP);
}
static int
@ -146,85 +177,135 @@ WebrtcGmpVideoEncoder::InitEncode(const webrtc::VideoCodec* aCodecSettings,
}
}
nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
MOZ_ASSERT(currentThread != mGMPThread);
nsRefPtr<InitDoneRunnable> initDone(new InitDoneRunnable());
mGMPThread->Dispatch(WrapRunnable(this,
&WebrtcGmpVideoEncoder::InitEncode_g,
aCodecSettings,
aNumberOfCores,
aMaxPayloadSize,
initDone),
NS_DISPATCH_NORMAL);
while (!initDone->IsDone()) {
NS_ProcessNextEvent(currentThread, true);
}
return initDone->Result();
}
void
WebrtcGmpVideoEncoder::InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores,
uint32_t aMaxPayloadSize,
InitDoneRunnable* aInitDone)
{
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
UniquePtr<GetGMPVideoEncoderCallback> callback(
new InitDoneCallback(this, aInitDone, aCodecSettings, aMaxPayloadSize));
nsresult rv = mMPS->GetGMPVideoEncoder(&tags,
NS_LITERAL_CSTRING(""),
Move(callback));
if (NS_WARN_IF(NS_FAILED(rv))) {
mMPS = nullptr;
mGMP = nullptr;
mGMPThread = nullptr;
mHost = nullptr;
aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR);
return;
}
}
int32_t
WebrtcGmpVideoEncoder::GmpInitDone(GMPVideoEncoderProxy* aGMP,
GMPVideoHost* aHost,
const webrtc::VideoCodec* aCodecSettings,
uint32_t aMaxPayloadSize)
{
mGMP = aGMP;
mHost = aHost;
if (!mGMP || !mHost) {
return WEBRTC_VIDEO_CODEC_ERROR;
}
// Bug XXXXXX: transfer settings from codecSettings to codec.
memset(&mCodecParams, 0, sizeof(mCodecParams));
GMPVideoCodec codecParams;
memset(&codecParams, 0, sizeof(codecParams));
mCodecParams.mGMPApiVersion = 33;
mCodecParams.mStartBitrate = aCodecSettings->startBitrate;
mCodecParams.mMinBitrate = aCodecSettings->minBitrate;
mCodecParams.mMaxBitrate = aCodecSettings->maxBitrate;
mCodecParams.mMaxFramerate = aCodecSettings->maxFramerate;
codecParams.mGMPApiVersion = 33;
codecParams.mStartBitrate = aCodecSettings->startBitrate;
codecParams.mMinBitrate = aCodecSettings->minBitrate;
codecParams.mMaxBitrate = aCodecSettings->maxBitrate;
codecParams.mMaxFramerate = aCodecSettings->maxFramerate;
mMaxPayloadSize = aMaxPayloadSize;
if (aCodecSettings->codecSpecific.H264.packetizationMode == 1) {
mMaxPayloadSize = 0; // No limit.
}
if (aCodecSettings->mode == webrtc::kScreensharing) {
mCodecParams.mMode = kGMPScreensharing;
codecParams.mMode = kGMPScreensharing;
} else {
mCodecParams.mMode = kGMPRealtimeVideo;
codecParams.mMode = kGMPRealtimeVideo;
}
return InitEncoderForSize(aCodecSettings->width, aCodecSettings->height);
codecParams.mWidth = aCodecSettings->width;
codecParams.mHeight = aCodecSettings->height;
nsRefPtr<GmpInitDoneRunnable> initDone(new GmpInitDoneRunnable(mPCHandle));
mGMPThread->Dispatch(WrapRunnableNM(WebrtcGmpVideoEncoder::InitEncode_g,
nsRefPtr<WebrtcGmpVideoEncoder>(this),
codecParams,
aNumberOfCores,
aMaxPayloadSize,
initDone),
NS_DISPATCH_NORMAL);
// Since init of the GMP encoder is a multi-step async dispatch (including
// dispatches to main), and since this function is invoked on main, there's
// no safe way to block until this init is done. If an error occurs, we'll
// handle it later.
return WEBRTC_VIDEO_CODEC_OK;
}
/* static */
void
WebrtcGmpVideoEncoder::InitEncode_g(
const nsRefPtr<WebrtcGmpVideoEncoder>& aThis,
const GMPVideoCodec& aCodecParams,
int32_t aNumberOfCores,
uint32_t aMaxPayloadSize,
const nsRefPtr<GmpInitDoneRunnable>& aInitDone)
{
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
UniquePtr<GetGMPVideoEncoderCallback> callback(
new InitDoneCallback(aThis, aInitDone, aCodecParams, aMaxPayloadSize));
aThis->mInitting = true;
nsresult rv = aThis->mMPS->GetGMPVideoEncoder(&tags,
NS_LITERAL_CSTRING(""),
Move(callback));
if (NS_WARN_IF(NS_FAILED(rv))) {
LOGD(("GMP Encode: GetGMPVideoEncoder failed"));
aThis->Close_g();
aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR,
"GMP Encode: GetGMPVideoEncoder failed");
}
}
int32_t
WebrtcGmpVideoEncoder::InitEncoderForSize(unsigned short aWidth, unsigned short aHeight)
WebrtcGmpVideoEncoder::GmpInitDone(GMPVideoEncoderProxy* aGMP,
GMPVideoHost* aHost,
std::string* aErrorOut)
{
if (!mInitting || !aGMP || !aHost) {
*aErrorOut = "GMP Encode: Either init was aborted, "
"or init failed to supply either a GMP Encoder or GMP host.";
if (aGMP) {
// This could destroy us, since aGMP may be the last thing holding a ref
// Return immediately.
aGMP->Close();
}
return WEBRTC_VIDEO_CODEC_ERROR;
}
mInitting = false;
if (mGMP && mGMP != aGMP) {
Close_g();
}
mGMP = aGMP;
mHost = aHost;
mCachedPluginId = mGMP->GetPluginId();
return WEBRTC_VIDEO_CODEC_OK;
}
int32_t
WebrtcGmpVideoEncoder::GmpInitDone(GMPVideoEncoderProxy* aGMP,
GMPVideoHost* aHost,
const GMPVideoCodec& aCodecParams,
uint32_t aMaxPayloadSize,
std::string* aErrorOut)
{
int32_t r = GmpInitDone(aGMP, aHost, aErrorOut);
if (r != WEBRTC_VIDEO_CODEC_OK) {
// We might have been destroyed if GmpInitDone failed.
// Return immediately.
return r;
}
mCodecParams = aCodecParams;
return InitEncoderForSize(aCodecParams.mWidth,
aCodecParams.mHeight,
aErrorOut);
}
void
WebrtcGmpVideoEncoder::Close_g()
{
GMPVideoEncoderProxy* gmp(mGMP);
mGMP = nullptr;
mHost = nullptr;
mInitting = false;
if (gmp) {
// Do this last, since this could cause us to be destroyed
gmp->Close();
}
}
int32_t
WebrtcGmpVideoEncoder::InitEncoderForSize(unsigned short aWidth,
unsigned short aHeight,
std::string* aErrorOut)
{
mCodecParams.mWidth = aWidth;
mCodecParams.mHeight = aHeight;
@ -233,6 +314,7 @@ WebrtcGmpVideoEncoder::InitEncoderForSize(unsigned short aWidth, unsigned short
GMPErr err = mGMP->InitEncode(mCodecParams, codecSpecific, this, 1, mMaxPayloadSize);
if (err != GMPNoErr) {
*aErrorOut = "GMP Encode: InitEncode failed";
return WEBRTC_VIDEO_CODEC_ERROR;
}
@ -245,69 +327,45 @@ WebrtcGmpVideoEncoder::Encode(const webrtc::I420VideoFrame& aInputImage,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
const std::vector<webrtc::VideoFrameType>* aFrameTypes)
{
MOZ_ASSERT(mHost);
if (!mGMP) {
// destroyed via Terminate()
return WEBRTC_VIDEO_CODEC_ERROR;
}
MOZ_ASSERT(aInputImage.width() >= 0 && aInputImage.height() >= 0);
if (static_cast<uint32_t>(aInputImage.width()) != mCodecParams.mWidth ||
static_cast<uint32_t>(aInputImage.height()) != mCodecParams.mHeight) {
LOGD(("GMP Encode: resolution change from %ux%u to %dx%d",
mCodecParams.mWidth, mCodecParams.mHeight, aInputImage.width(), aInputImage.height()));
nsRefPtr<InitDoneRunnable> initDone(new InitDoneRunnable());
nsCOMPtr<nsIRunnable> task(
// Would be really nice to avoid this sync dispatch, but it would require a
// copy of the frame, since it doesn't appear to actually have a refcount.
mGMPThread->Dispatch(
WrapRunnable(this,
&WebrtcGmpVideoEncoder::RegetEncoderForResolutionChange,
&WebrtcGmpVideoEncoder::Encode_g,
&aInputImage,
initDone));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
aCodecSpecificInfo,
aFrameTypes),
NS_DISPATCH_SYNC);
nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
while (!initDone->IsDone()) {
NS_ProcessNextEvent(currentThread, true);
}
if (initDone->Result() != WEBRTC_VIDEO_CODEC_OK) {
return initDone->Result();
}
}
int32_t ret;
mozilla::SyncRunnable::DispatchToThread(mGMPThread,
WrapRunnableRet(&ret, this,
&WebrtcGmpVideoEncoder::Encode_g,
&aInputImage,
aCodecSpecificInfo,
aFrameTypes));
return ret;
return WEBRTC_VIDEO_CODEC_OK;
}
void
WebrtcGmpVideoEncoder::RegetEncoderForResolutionChange(const webrtc::I420VideoFrame* aInputImage,
InitDoneRunnable* aInitDone)
WebrtcGmpVideoEncoder::RegetEncoderForResolutionChange(
uint32_t aWidth,
uint32_t aHeight,
const nsRefPtr<GmpInitDoneRunnable>& aInitDone)
{
mGMP->Close();
Close_g();
UniquePtr<GetGMPVideoEncoderCallback> callback(
new InitDoneForResolutionChangeCallback(this, aInitDone,
aInputImage->width(),
aInputImage->height()));
new InitDoneForResolutionChangeCallback(this,
aInitDone,
aWidth,
aHeight));
// OpenH264 codec (at least) can't handle dynamic input resolution changes
// re-init the plugin when the resolution changes
// XXX allow codec to indicate it doesn't need re-init!
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
mInitting = true;
if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoEncoder(&tags,
NS_LITERAL_CSTRING(""),
Move(callback))))) {
mGMP = nullptr;
mHost = nullptr;
aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR);
aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR,
"GMP Encode: GetGMPVideoEncoder failed");
}
}
@ -316,8 +374,27 @@ WebrtcGmpVideoEncoder::Encode_g(const webrtc::I420VideoFrame* aInputImage,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
const std::vector<webrtc::VideoFrameType>* aFrameTypes)
{
if (!mGMP) {
// destroyed via Terminate(), failed to init, or just not initted yet
LOGD(("GMP Encode: not initted yet"));
return WEBRTC_VIDEO_CODEC_ERROR;
}
MOZ_ASSERT(mHost);
MOZ_ASSERT(mGMP);
if (static_cast<uint32_t>(aInputImage->width()) != mCodecParams.mWidth ||
static_cast<uint32_t>(aInputImage->height()) != mCodecParams.mHeight) {
LOGD(("GMP Encode: resolution change from %ux%u to %dx%d",
mCodecParams.mWidth, mCodecParams.mHeight, aInputImage->width(), aInputImage->height()));
nsRefPtr<GmpInitDoneRunnable> initDone(new GmpInitDoneRunnable(mPCHandle));
RegetEncoderForResolutionChange(aInputImage->width(),
aInputImage->height(),
initDone);
if (!mGMP) {
// We needed to go async to re-get the encoder. Bail.
return WEBRTC_VIDEO_CODEC_ERROR;
}
}
GMPVideoFrame* ftmp = nullptr;
GMPErr err = mHost->CreateFrame(kGMPI420VideoFrame, &ftmp);
@ -374,26 +451,28 @@ WebrtcGmpVideoEncoder::Encode_g(const webrtc::I420VideoFrame* aInputImage,
int32_t
WebrtcGmpVideoEncoder::RegisterEncodeCompleteCallback(webrtc::EncodedImageCallback* aCallback)
{
MutexAutoLock lock(mCallbackMutex);
mCallback = aCallback;
return WEBRTC_VIDEO_CODEC_OK;
}
/* static */ void
WebrtcGmpVideoEncoder::ReleaseGmp_g(nsRefPtr<WebrtcGmpVideoEncoder>& aEncoder)
{
aEncoder->Close_g();
}
int32_t
WebrtcGmpVideoEncoder::Release()
WebrtcGmpVideoEncoder::ReleaseGmp()
{
LOGD(("GMP Released:"));
// 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));
if (mGMPThread) {
mGMPThread->Dispatch(
WrapRunnableNM(&WebrtcGmpVideoEncoder::ReleaseGmp_g,
nsRefPtr<WebrtcGmpVideoEncoder>(this)),
NS_DISPATCH_NORMAL);
}
// Now safe to forget things
mMPS = nullptr;
mGMP = nullptr;
mHost = nullptr;
return WEBRTC_VIDEO_CODEC_OK;
}
@ -406,25 +485,28 @@ WebrtcGmpVideoEncoder::SetChannelParameters(uint32_t aPacketLoss, int aRTT)
int32_t
WebrtcGmpVideoEncoder::SetRates(uint32_t aNewBitRate, uint32_t aFrameRate)
{
int32_t ret;
MOZ_ASSERT(mGMPThread);
mozilla::SyncRunnable::DispatchToThread(mGMPThread,
WrapRunnableRet(&ret, this,
&WebrtcGmpVideoEncoder::SetRates_g,
aNewBitRate, aFrameRate));
MOZ_ASSERT(!NS_IsMainThread());
mGMPThread->Dispatch(WrapRunnableNM(&WebrtcGmpVideoEncoder::SetRates_g,
nsRefPtr<WebrtcGmpVideoEncoder>(this),
aNewBitRate,
aFrameRate),
NS_DISPATCH_NORMAL);
return WEBRTC_VIDEO_CODEC_OK;
}
int32_t
WebrtcGmpVideoEncoder::SetRates_g(uint32_t aNewBitRate, uint32_t aFrameRate)
/* static */ int32_t
WebrtcGmpVideoEncoder::SetRates_g(nsRefPtr<WebrtcGmpVideoEncoder> aThis,
uint32_t aNewBitRate,
uint32_t aFrameRate)
{
if (!mGMP) {
if (!aThis->mGMP) {
// destroyed via Terminate()
return WEBRTC_VIDEO_CODEC_ERROR;
}
GMPErr err = mGMP->SetRates(aNewBitRate, aFrameRate);
GMPErr err = aThis->mGMP->SetRates(aNewBitRate, aFrameRate);
if (err != GMPNoErr) {
return WEBRTC_VIDEO_CODEC_ERROR;
}
@ -437,11 +519,11 @@ void
WebrtcGmpVideoEncoder::Terminated()
{
LOGD(("GMP Encoder Terminated: %p", (void *)this));
mCachedPluginId = PluginID();
// We need to drop our reference to this
mGMP->Close();
mGMP = nullptr;
mHost = nullptr;
mInitting = false;
// Could now notify that it's dead
}
@ -449,7 +531,8 @@ void
WebrtcGmpVideoEncoder::Encoded(GMPVideoEncodedFrame* aEncodedFrame,
const nsTArray<uint8_t>& aCodecSpecificInfo)
{
if (mCallback) { // paranoia
MutexAutoLock lock(mCallbackMutex);
if (mCallback) {
webrtc::VideoFrameType ft;
GmpFrameTypeToWebrtcFrameType(aEncodedFrame->FrameType(), &ft);
uint32_t timestamp = (aEncodedFrame->TimeStamp() * 90ll + 999)/1000;
@ -559,7 +642,6 @@ WebrtcGmpVideoEncoder::Encoded(GMPVideoEncodedFrame* aEncodedFrame,
unit._completeFrame = true;
mCallback->Encoded(unit, nullptr, &fragmentation);
}
}
}
@ -567,35 +649,34 @@ WebrtcGmpVideoEncoder::Encoded(GMPVideoEncodedFrame* aEncodedFrame,
// Decoder.
WebrtcGmpVideoDecoder::WebrtcGmpVideoDecoder() :
mGMP(nullptr),
mInitting(false),
mHost(nullptr),
mCallbackMutex("WebrtcGmpVideoDecoder decoded callback mutex"),
mCallback(nullptr),
mCachedPluginId(0),
mDecoderStatus(GMPNoErr){}
static void
Decoder_Close_g(GMPVideoDecoderProxy* aGMP)
mDecoderStatus(GMPNoErr)
{
aGMP->Close();
#ifdef MOZILLA_INTERNAL_API
if (mPCHandle.empty()) {
mPCHandle = WebrtcGmpPCHandleSetter::GetCurrentHandle();
}
MOZ_ASSERT(!mPCHandle.empty());
#endif
}
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;
}
// We should not have been destroyed if we never closed our GMP
MOZ_ASSERT(!mGMP);
}
int32_t
WebrtcGmpVideoDecoder::InitDecode(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores)
{
mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
if (!mMPS) {
mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
}
MOZ_ASSERT(mMPS);
if (!mGMPThread) {
@ -604,54 +685,65 @@ WebrtcGmpVideoDecoder::InitDecode(const webrtc::VideoCodec* aCodecSettings,
}
}
nsRefPtr<InitDoneRunnable> initDone(new InitDoneRunnable());
mGMPThread->Dispatch(WrapRunnable(this,
&WebrtcGmpVideoDecoder::InitDecode_g,
aCodecSettings,
aNumberOfCores,
initDone.get()),
nsRefPtr<GmpInitDoneRunnable> initDone(new GmpInitDoneRunnable(mPCHandle));
mGMPThread->Dispatch(WrapRunnableNM(&WebrtcGmpVideoDecoder::InitDecode_g,
nsRefPtr<WebrtcGmpVideoDecoder>(this),
aCodecSettings,
aNumberOfCores,
initDone),
NS_DISPATCH_NORMAL);
nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
while (!initDone->IsDone()) {
NS_ProcessNextEvent(currentThread, true);
}
return initDone->Result();
return WEBRTC_VIDEO_CODEC_OK;
}
void
WebrtcGmpVideoDecoder::InitDecode_g(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores,
InitDoneRunnable* aInitDone)
/* static */ void
WebrtcGmpVideoDecoder::InitDecode_g(
const nsRefPtr<WebrtcGmpVideoDecoder>& aThis,
const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores,
const nsRefPtr<GmpInitDoneRunnable>& aInitDone)
{
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
UniquePtr<GetGMPVideoDecoderCallback> callback(
new InitDoneCallback(this, aInitDone));
if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoDecoder(&tags,
NS_LITERAL_CSTRING(""),
Move(callback))))) {
mMPS = nullptr;
mGMP = nullptr;
mGMPThread = nullptr;
mHost = nullptr;
aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR);
new InitDoneCallback(aThis, aInitDone));
aThis->mInitting = true;
nsresult rv = aThis->mMPS->GetGMPVideoDecoder(&tags,
NS_LITERAL_CSTRING(""),
Move(callback));
if (NS_WARN_IF(NS_FAILED(rv))) {
LOGD(("GMP Decode: GetGMPVideoDecoder failed"));
aThis->Close_g();
aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR,
"GMP Decode: GetGMPVideoDecoder failed.");
}
}
int32_t
WebrtcGmpVideoDecoder::GmpInitDone(GMPVideoDecoderProxy* aGMP,
GMPVideoHost* aHost)
GMPVideoHost* aHost,
std::string* aErrorOut)
{
mGMP = aGMP;
mHost = aHost;
mMPS = nullptr;
if (!mGMP || !mHost) {
if (!mInitting || !aGMP || !aHost) {
*aErrorOut = "GMP Decode: Either init was aborted, "
"or init failed to supply either a GMP decoder or GMP host.";
if (aGMP) {
// This could destroy us, since aGMP may be the last thing holding a ref
// Return immediately.
aGMP->Close();
}
return WEBRTC_VIDEO_CODEC_ERROR;
}
mInitting = false;
if (mGMP && mGMP != aGMP) {
Close_g();
}
mGMP = aGMP;
mHost = aHost;
mCachedPluginId = mGMP->GetPluginId();
// Bug XXXXXX: transfer settings from codecSettings to codec.
GMPVideoCodec codec;
memset(&codec, 0, sizeof(codec));
@ -663,12 +755,27 @@ WebrtcGmpVideoDecoder::GmpInitDone(GMPVideoDecoderProxy* aGMP,
nsTArray<uint8_t> codecSpecific;
nsresult rv = mGMP->InitDecode(codec, codecSpecific, this, 1);
if (NS_FAILED(rv)) {
*aErrorOut = "GMP Decode: InitDecode failed";
return WEBRTC_VIDEO_CODEC_ERROR;
}
return WEBRTC_VIDEO_CODEC_OK;
}
void
WebrtcGmpVideoDecoder::Close_g()
{
GMPVideoDecoderProxy* gmp(mGMP);
mGMP = nullptr;
mHost = nullptr;
mInitting = false;
if (gmp) {
// Do this last, since this could cause us to be destroyed
gmp->Close();
}
}
int32_t
WebrtcGmpVideoDecoder::Decode(const webrtc::EncodedImage& aInputImage,
bool aMissingFrames,
@ -678,6 +785,9 @@ WebrtcGmpVideoDecoder::Decode(const webrtc::EncodedImage& aInputImage,
{
int32_t ret;
MOZ_ASSERT(mGMPThread);
MOZ_ASSERT(!NS_IsMainThread());
// Would be really nice to avoid this sync dispatch, but it would require a
// copy of the frame, since it doesn't appear to actually have a refcount.
mozilla::SyncRunnable::DispatchToThread(mGMPThread,
WrapRunnableRet(&ret, this,
&WebrtcGmpVideoDecoder::Decode_g,
@ -697,11 +807,12 @@ WebrtcGmpVideoDecoder::Decode_g(const webrtc::EncodedImage& aInputImage,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
int64_t aRenderTimeMs)
{
MOZ_ASSERT(mHost);
if (!mGMP) {
// destroyed via Terminate()
// destroyed via Terminate(), failed to init, or just not initted yet
LOGD(("GMP Decode: not initted yet"));
return WEBRTC_VIDEO_CODEC_ERROR;
}
MOZ_ASSERT(mHost);
if (!aInputImage._length) {
return WEBRTC_VIDEO_CODEC_ERROR;
@ -764,27 +875,29 @@ WebrtcGmpVideoDecoder::Decode_g(const webrtc::EncodedImage& aInputImage,
int32_t
WebrtcGmpVideoDecoder::RegisterDecodeCompleteCallback( webrtc::DecodedImageCallback* aCallback)
{
MutexAutoLock lock(mCallbackMutex);
mCallback = aCallback;
return WEBRTC_VIDEO_CODEC_OK;
}
int32_t
WebrtcGmpVideoDecoder::Release()
/* static */ void
WebrtcGmpVideoDecoder::ReleaseGmp_g(nsRefPtr<WebrtcGmpVideoDecoder>& aDecoder)
{
// 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));
aDecoder->Close_g();
}
int32_t
WebrtcGmpVideoDecoder::ReleaseGmp()
{
LOGD(("GMP Released:"));
if (mGMPThread) {
mGMPThread->Dispatch(
WrapRunnableNM(&WebrtcGmpVideoDecoder::ReleaseGmp_g,
nsRefPtr<WebrtcGmpVideoDecoder>(this)),
NS_DISPATCH_NORMAL);
}
// Now safe to forget things
mMPS = nullptr;
mGMP = nullptr;
mGMPThread = nullptr;
mHost = nullptr;
return WEBRTC_VIDEO_CODEC_OK;
}
@ -799,17 +912,19 @@ void
WebrtcGmpVideoDecoder::Terminated()
{
LOGD(("GMP Decoder Terminated: %p", (void *)this));
mCachedPluginId = PluginID();
mGMP->Close();
mGMP = nullptr;
mHost = nullptr;
mInitting = false;
// Could now notify that it's dead
}
void
WebrtcGmpVideoDecoder::Decoded(GMPVideoi420Frame* aDecodedFrame)
{
if (mCallback) { // paranioa
MutexAutoLock lock(mCallbackMutex);
if (mCallback) {
webrtc::I420VideoFrame image;
int ret = image.CreateFrame(aDecodedFrame->AllocatedSize(kGMPYPlane),
aDecodedFrame->Buffer(kGMPYPlane),

View File

@ -35,6 +35,7 @@
#include <iostream>
#include <queue>
#include <string>
#include "nsThreadUtils.h"
#include "mozilla/Monitor.h"
@ -50,77 +51,67 @@
#include "GMPVideoDecoderProxy.h"
#include "GMPVideoEncoderProxy.h"
#include "PeerConnectionImpl.h"
namespace mozilla {
class WebrtcGmpVideoEncoder : public WebrtcVideoEncoder,
public GMPVideoEncoderCallbackProxy
// Class that allows code on the other side of webrtc.org to tell
// WebrtcGmpVideoEncoder/Decoder what PC they should send errors to.
// This is necessary because webrtc.org gives us no way to plumb the handle
// through, nor does it give us any way to inform it of an error that will
// make it back to the PC that cares (except for errors encountered
// synchronously in functions like InitEncode/Decode, which will not happen
// because GMP init is async).
// Right now, this is used in MediaPipelineFactory.
class WebrtcGmpPCHandleSetter
{
public:
WebrtcGmpVideoEncoder();
virtual ~WebrtcGmpVideoEncoder();
// Implement VideoEncoder interface.
virtual const uint64_t PluginID() override
{
return mGMP ? mGMP->GetPluginId() : mCachedPluginId;
}
virtual void Terminated() override;
virtual int32_t InitEncode(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores,
uint32_t aMaxPayloadSize) override;
virtual int32_t Encode(const webrtc::I420VideoFrame& aInputImage,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
const std::vector<webrtc::VideoFrameType>* aFrameTypes) override;
virtual int32_t RegisterEncodeCompleteCallback(
webrtc::EncodedImageCallback* aCallback) override;
virtual int32_t Release() override;
virtual int32_t SetChannelParameters(uint32_t aPacketLoss,
int aRTT) override;
virtual int32_t SetRates(uint32_t aNewBitRate,
uint32_t aFrameRate) override;
// GMPVideoEncoderCallback virtual functions.
virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
const nsTArray<uint8_t>& aCodecSpecificInfo) override;
virtual void Error(GMPErr aError) override {
}
private:
class InitDoneRunnable : public nsRunnable
{
public:
InitDoneRunnable()
: mInitDone(false),
mResult(WEBRTC_VIDEO_CODEC_OK),
mThread(do_GetCurrentThread())
explicit WebrtcGmpPCHandleSetter(const std::string& aPCHandle);
~WebrtcGmpPCHandleSetter();
static std::string GetCurrentHandle();
private:
static std::string sCurrentHandle;
};
class GmpInitDoneRunnable : public nsRunnable
{
public:
explicit GmpInitDoneRunnable(const std::string& aPCHandle) :
mResult(WEBRTC_VIDEO_CODEC_OK),
mPCHandle(aPCHandle)
{
}
NS_IMETHOD Run()
{
MOZ_ASSERT(mThread == nsCOMPtr<nsIThread>(do_GetCurrentThread()));
mInitDone = true;
if (mResult == WEBRTC_VIDEO_CODEC_OK) {
// Might be useful to notify the PeerConnection about successful init
// someday.
return NS_OK;
}
PeerConnectionWrapper wrapper(mPCHandle);
if (wrapper.impl()) {
wrapper.impl()->OnMediaError(mError);
}
return NS_OK;
}
void Dispatch(int32_t aResult)
void Dispatch(int32_t aResult, const std::string& aError = "")
{
mResult = aResult;
mThread->Dispatch(this, NS_DISPATCH_NORMAL);
}
bool IsDone()
{
MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
return mInitDone;
mError = aError;
nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
if (mainThread) {
// For some reason, the compiler on CI is treating |this| as a const
// pointer, despite the fact that we're in a non-const function. And,
// interestingly enough, correcting this doesn't require a const_cast.
mainThread->Dispatch(do_AddRef(static_cast<nsIRunnable*>(this)),
NS_DISPATCH_NORMAL);
}
}
int32_t Result()
@ -129,69 +120,122 @@ private:
}
private:
bool mInitDone;
int32_t mResult;
nsCOMPtr<nsIThread> mThread;
};
std::string mPCHandle;
std::string mError;
};
void InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores,
uint32_t aMaxPayloadSize,
InitDoneRunnable* aInitDone);
class WebrtcGmpVideoEncoder : public GMPVideoEncoderCallbackProxy
{
public:
WebrtcGmpVideoEncoder();
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcGmpVideoEncoder);
// Implement VideoEncoder interface, sort of.
// (We cannot use |Release|, since that's needed for nsRefPtr)
virtual const uint64_t PluginID()
{
return mCachedPluginId;
}
virtual int32_t InitEncode(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores,
uint32_t aMaxPayloadSize);
virtual int32_t Encode(const webrtc::I420VideoFrame& aInputImage,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
const std::vector<webrtc::VideoFrameType>* aFrameTypes);
virtual int32_t RegisterEncodeCompleteCallback(
webrtc::EncodedImageCallback* aCallback);
virtual int32_t ReleaseGmp();
virtual int32_t SetChannelParameters(uint32_t aPacketLoss,
int aRTT);
virtual int32_t SetRates(uint32_t aNewBitRate,
uint32_t aFrameRate);
// GMPVideoEncoderCallback virtual functions.
virtual void Terminated() override;
virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
const nsTArray<uint8_t>& aCodecSpecificInfo) override;
virtual void Error(GMPErr aError) override {
}
private:
virtual ~WebrtcGmpVideoEncoder();
static void InitEncode_g(const nsRefPtr<WebrtcGmpVideoEncoder>& aThis,
const GMPVideoCodec& aCodecParams,
int32_t aNumberOfCores,
uint32_t aMaxPayloadSize,
const nsRefPtr<GmpInitDoneRunnable>& aInitDone);
int32_t GmpInitDone(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost,
const webrtc::VideoCodec* aCodecSettings,
uint32_t aMaxPayloadSize);
int32_t InitEncoderForSize(unsigned short aWidth, unsigned short aHeight);
const GMPVideoCodec& aCodecParams,
uint32_t aMaxPayloadSize,
std::string* aErrorOut);
int32_t GmpInitDone(GMPVideoEncoderProxy* aGMP,
GMPVideoHost* aHost,
std::string* aErrorOut);
int32_t InitEncoderForSize(unsigned short aWidth,
unsigned short aHeight,
std::string* aErrorOut);
static void ReleaseGmp_g(nsRefPtr<WebrtcGmpVideoEncoder>& aEncoder);
void Close_g();
class InitDoneCallback : public GetGMPVideoEncoderCallback
{
public:
InitDoneCallback(WebrtcGmpVideoEncoder* aEncoder,
InitDoneRunnable* aInitDone,
const webrtc::VideoCodec* aCodecSettings,
InitDoneCallback(const nsRefPtr<WebrtcGmpVideoEncoder>& aEncoder,
const nsRefPtr<GmpInitDoneRunnable>& aInitDone,
const GMPVideoCodec& aCodecParams,
uint32_t aMaxPayloadSize)
: mEncoder(aEncoder),
mInitDone(aInitDone),
mCodecSettings(aCodecSettings),
mCodecParams(aCodecParams),
mMaxPayloadSize(aMaxPayloadSize)
{
}
virtual void Done(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost) override
{
mEncoder->mGMP = aGMP;
mEncoder->mHost = aHost;
int32_t result;
if (aGMP || aHost) {
result = mEncoder->GmpInitDone(aGMP, aHost, mCodecSettings,
mMaxPayloadSize);
} else {
result = WEBRTC_VIDEO_CODEC_ERROR;
}
std::string errorOut;
int32_t result = mEncoder->GmpInitDone(aGMP,
aHost,
mCodecParams,
mMaxPayloadSize,
&errorOut);
mInitDone->Dispatch(result);
mInitDone->Dispatch(result, errorOut);
}
private:
WebrtcGmpVideoEncoder* mEncoder;
nsRefPtr<InitDoneRunnable> mInitDone;
const webrtc::VideoCodec* mCodecSettings;
nsRefPtr<WebrtcGmpVideoEncoder> mEncoder;
nsRefPtr<GmpInitDoneRunnable> mInitDone;
GMPVideoCodec mCodecParams;
uint32_t mMaxPayloadSize;
};
int32_t Encode_g(const webrtc::I420VideoFrame* aInputImage,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
const std::vector<webrtc::VideoFrameType>* aFrameTypes);
void RegetEncoderForResolutionChange(const webrtc::I420VideoFrame* aInputImage,
InitDoneRunnable* aInitDone);
void RegetEncoderForResolutionChange(
uint32_t aWidth,
uint32_t aHeight,
const nsRefPtr<GmpInitDoneRunnable>& aInitDone);
class InitDoneForResolutionChangeCallback : public GetGMPVideoEncoderCallback
{
public:
InitDoneForResolutionChangeCallback(WebrtcGmpVideoEncoder* aEncoder,
InitDoneRunnable* aInitDone,
uint32_t aWidth,
uint32_t aHeight)
InitDoneForResolutionChangeCallback(
const nsRefPtr<WebrtcGmpVideoEncoder>& aEncoder,
const nsRefPtr<GmpInitDoneRunnable>& aInitDone,
uint32_t aWidth,
uint32_t aHeight)
: mEncoder(aEncoder),
mInitDone(aInitDone),
mWidth(aWidth),
@ -201,66 +245,140 @@ private:
virtual void Done(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost) override
{
mEncoder->mGMP = aGMP;
mEncoder->mHost = aHost;
int32_t result;
if (aGMP && aHost) {
result = mEncoder->InitEncoderForSize(mWidth, mHeight);
} else {
result = WEBRTC_VIDEO_CODEC_ERROR;
std::string errorOut;
int32_t result = mEncoder->GmpInitDone(aGMP, aHost, &errorOut);
if (result != WEBRTC_VIDEO_CODEC_OK) {
mInitDone->Dispatch(result, errorOut);
return;
}
mInitDone->Dispatch(result);
result = mEncoder->InitEncoderForSize(mWidth, mHeight, &errorOut);
mInitDone->Dispatch(result, errorOut);
}
private:
WebrtcGmpVideoEncoder* mEncoder;
nsRefPtr<InitDoneRunnable> mInitDone;
nsRefPtr<WebrtcGmpVideoEncoder> mEncoder;
nsRefPtr<GmpInitDoneRunnable> mInitDone;
uint32_t mWidth;
uint32_t mHeight;
};
virtual int32_t SetRates_g(uint32_t aNewBitRate,
static int32_t SetRates_g(nsRefPtr<WebrtcGmpVideoEncoder> aThis,
uint32_t aNewBitRate,
uint32_t aFrameRate);
nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
nsCOMPtr<nsIThread> mGMPThread;
GMPVideoEncoderProxy* mGMP;
// Used to handle a race where Release() is called while init is in progress
bool mInitting;
GMPVideoHost* mHost;
GMPVideoCodec mCodecParams;
uint32_t mMaxPayloadSize;
// Protects mCallback
Mutex mCallbackMutex;
webrtc::EncodedImageCallback* mCallback;
uint64_t mCachedPluginId;
std::string mPCHandle;
};
class WebrtcGmpVideoDecoder : public WebrtcVideoDecoder,
public GMPVideoDecoderCallbackProxy
// Basically a strong ref to a WebrtcGmpVideoEncoder, that also translates
// from Release() to WebrtcGmpVideoEncoder::ReleaseGmp(), since we need
// WebrtcGmpVideoEncoder::Release() for managing the refcount.
// The webrtc.org code gets one of these, so it doesn't unilaterally delete
// the "real" encoder.
class WebrtcVideoEncoderProxy : public WebrtcVideoEncoder
{
public:
WebrtcVideoEncoderProxy() :
mEncoderImpl(new WebrtcGmpVideoEncoder)
{}
virtual ~WebrtcVideoEncoderProxy()
{
RegisterEncodeCompleteCallback(nullptr);
}
const uint64_t PluginID() override
{
return mEncoderImpl->PluginID();
}
int32_t InitEncode(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores,
uint32_t aMaxPayloadSize) override
{
return mEncoderImpl->InitEncode(aCodecSettings,
aNumberOfCores,
aMaxPayloadSize);
}
int32_t Encode(
const webrtc::I420VideoFrame& aInputImage,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
const std::vector<webrtc::VideoFrameType>* aFrameTypes) override
{
return mEncoderImpl->Encode(aInputImage,
aCodecSpecificInfo,
aFrameTypes);
}
int32_t RegisterEncodeCompleteCallback(
webrtc::EncodedImageCallback* aCallback) override
{
return mEncoderImpl->RegisterEncodeCompleteCallback(aCallback);
}
int32_t Release() override
{
return mEncoderImpl->ReleaseGmp();
}
int32_t SetChannelParameters(uint32_t aPacketLoss,
int aRTT) override
{
return mEncoderImpl->SetChannelParameters(aPacketLoss, aRTT);
}
int32_t SetRates(uint32_t aNewBitRate,
uint32_t aFrameRate) override
{
return mEncoderImpl->SetRates(aNewBitRate, aFrameRate);
}
private:
nsRefPtr<WebrtcGmpVideoEncoder> mEncoderImpl;
};
class WebrtcGmpVideoDecoder : public GMPVideoDecoderCallbackProxy
{
public:
WebrtcGmpVideoDecoder();
virtual ~WebrtcGmpVideoDecoder();
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcGmpVideoDecoder);
// Implement VideoDecoder interface.
virtual const uint64_t PluginID() override
// Implement VideoEncoder interface, sort of.
// (We cannot use |Release|, since that's needed for nsRefPtr)
virtual const uint64_t PluginID()
{
return mGMP ? mGMP->GetPluginId() : mCachedPluginId;
return mCachedPluginId;
}
virtual void Terminated() override;
virtual int32_t InitDecode(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores) override;
int32_t aNumberOfCores);
virtual int32_t Decode(const webrtc::EncodedImage& aInputImage,
bool aMissingFrames,
const webrtc::RTPFragmentationHeader* aFragmentation,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo = nullptr,
int64_t aRenderTimeMs = -1) override;
virtual int32_t RegisterDecodeCompleteCallback(webrtc::DecodedImageCallback* aCallback) override;
const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
int64_t aRenderTimeMs);
virtual int32_t RegisterDecodeCompleteCallback(webrtc::DecodedImageCallback* aCallback);
virtual int32_t Release() override;
virtual int32_t ReleaseGmp();
virtual int32_t Reset() override;
virtual int32_t Reset();
// GMPVideoDecoderCallbackProxy
virtual void Terminated() override;
virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) override;
@ -286,56 +404,24 @@ public:
}
private:
class InitDoneRunnable : public nsRunnable
{
public:
InitDoneRunnable()
: mInitDone(false),
mResult(WEBRTC_VIDEO_CODEC_OK),
mThread(do_GetCurrentThread())
{
}
virtual ~WebrtcGmpVideoDecoder();
NS_IMETHOD Run()
{
MOZ_ASSERT(mThread == nsCOMPtr<nsIThread>(do_GetCurrentThread()));
mInitDone = true;
return NS_OK;
}
void Dispatch(int32_t aResult)
{
mResult = aResult;
mThread->Dispatch(this, NS_DISPATCH_NORMAL);
}
bool IsDone()
{
MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
return mInitDone;
}
int32_t Result()
{
return mResult;
}
private:
bool mInitDone;
int32_t mResult;
nsCOMPtr<nsIThread> mThread;
};
void InitDecode_g(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores,
InitDoneRunnable* aInitDone);
int32_t GmpInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost);
static void InitDecode_g(
const nsRefPtr<WebrtcGmpVideoDecoder>& aThis,
const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores,
const nsRefPtr<GmpInitDoneRunnable>& aInitDone);
int32_t GmpInitDone(GMPVideoDecoderProxy* aGMP,
GMPVideoHost* aHost,
std::string* aErrorOut);
static void ReleaseGmp_g(nsRefPtr<WebrtcGmpVideoDecoder>& aDecoder);
void Close_g();
class InitDoneCallback : public GetGMPVideoDecoderCallback
{
public:
explicit InitDoneCallback(WebrtcGmpVideoDecoder* aDecoder,
InitDoneRunnable* aInitDone)
const nsRefPtr<GmpInitDoneRunnable>& aInitDone)
: mDecoder(aDecoder),
mInitDone(aInitDone)
{
@ -343,15 +429,16 @@ private:
virtual void Done(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
{
int32_t result = mDecoder->GmpInitDone(aGMP, aHost);
std::string errorOut;
int32_t result = mDecoder->GmpInitDone(aGMP, aHost, &errorOut);
mInitDone->Dispatch(result);
mInitDone->Dispatch(result, errorOut);
}
private:
WebrtcGmpVideoDecoder* mDecoder;
nsRefPtr<InitDoneRunnable> mInitDone;
};
nsRefPtr<GmpInitDoneRunnable> mInitDone;
};
virtual int32_t Decode_g(const webrtc::EncodedImage& aInputImage,
bool aMissingFrames,
@ -362,10 +449,77 @@ private:
nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
nsCOMPtr<nsIThread> mGMPThread;
GMPVideoDecoderProxy* mGMP; // Addref is held for us
// Used to handle a race where Release() is called while init is in progress
bool mInitting;
GMPVideoHost* mHost;
// Protects mCallback
Mutex mCallbackMutex;
webrtc::DecodedImageCallback* mCallback;
uint64_t mCachedPluginId;
Atomic<uint64_t> mCachedPluginId;
GMPErr mDecoderStatus;
std::string mPCHandle;
};
// Basically a strong ref to a WebrtcGmpVideoDecoder, that also translates
// from Release() to WebrtcGmpVideoDecoder::ReleaseGmp(), since we need
// WebrtcGmpVideoDecoder::Release() for managing the refcount.
// The webrtc.org code gets one of these, so it doesn't unilaterally delete
// the "real" encoder.
class WebrtcVideoDecoderProxy : public WebrtcVideoDecoder
{
public:
WebrtcVideoDecoderProxy() :
mDecoderImpl(new WebrtcGmpVideoDecoder)
{}
virtual ~WebrtcVideoDecoderProxy()
{
RegisterDecodeCompleteCallback(nullptr);
}
const uint64_t PluginID() override
{
return mDecoderImpl->PluginID();
}
int32_t InitDecode(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores) override
{
return mDecoderImpl->InitDecode(aCodecSettings, aNumberOfCores);
}
int32_t Decode(
const webrtc::EncodedImage& aInputImage,
bool aMissingFrames,
const webrtc::RTPFragmentationHeader* aFragmentation,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
int64_t aRenderTimeMs) override
{
return mDecoderImpl->Decode(aInputImage,
aMissingFrames,
aFragmentation,
aCodecSpecificInfo,
aRenderTimeMs);
}
int32_t RegisterDecodeCompleteCallback(
webrtc::DecodedImageCallback* aCallback) override
{
return mDecoderImpl->RegisterDecodeCompleteCallback(aCallback);
}
int32_t Release() override
{
return mDecoderImpl->ReleaseGmp();
}
int32_t Reset() override
{
return mDecoderImpl->Reset();
}
private:
nsRefPtr<WebrtcGmpVideoDecoder> mDecoderImpl;
};
}

View File

@ -40,6 +40,10 @@
#include "mozilla/Preferences.h"
#endif
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
#include "WebrtcGmpVideoCodec.h"
#endif
#include <stdlib.h>
namespace mozilla {
@ -315,6 +319,14 @@ MediaPipelineFactory::CreateOrUpdateMediaPipeline(
const JsepTrackPair& aTrackPair,
const JsepTrack& aTrack)
{
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
// The GMP code is all the way on the other side of webrtc.org, and it is not
// feasible to plumb this information all the way through. So, we set it (for
// the duration of this call) in a global variable. This allows the GMP code
// to report errors to the PC.
WebrtcGmpPCHandleSetter setter(mPC->GetHandle());
#endif
MOZ_ASSERT(aTrackPair.mRtpTransport);
bool receiving =

View File

@ -2030,6 +2030,13 @@ PeerConnectionImpl::GetStreamId(const DOMMediaStream& aStream)
#endif
}
void
PeerConnectionImpl::OnMediaError(const std::string& aError)
{
CSFLogError(logTag, "Encountered media error! %s", aError.c_str());
// TODO: Let content know about this somehow.
}
nsresult
PeerConnectionImpl::AddTrack(MediaStreamTrack& aTrack,
const Sequence<OwningNonNull<DOMMediaStream>>& aStreams)

View File

@ -618,6 +618,8 @@ public:
static std::string GetStreamId(const DOMMediaStream& aStream);
static std::string GetTrackId(const dom::MediaStreamTrack& track);
void OnMediaError(const std::string& aError);
private:
virtual ~PeerConnectionImpl();
PeerConnectionImpl(const PeerConnectionImpl&rhs);