diff --git a/content/media/gmp/gmp-api/gmp-video-codec.h b/content/media/gmp/gmp-api/gmp-video-codec.h index 31601d5c0642..92dbe88ea71e 100644 --- a/content/media/gmp/gmp-api/gmp-video-codec.h +++ b/content/media/gmp/gmp-api/gmp-video-codec.h @@ -75,15 +75,33 @@ struct GMPVideoCodecVP8 int32_t mKeyFrameInterval; }; +// H264 specific +struct GMPVideoCodecH264 +{ + uint8_t mProfile; + uint8_t mConstraints; + uint8_t mLevel; + uint8_t mPacketizationMode; // 0 or 1 + bool mFrameDroppingOn; + int32_t mKeyFrameInterval; + // These are null/0 if not externally negotiated + const uint8_t* mSPSData; + size_t mSPSLen; + const uint8_t* mPPSData; + size_t mPPSLen; +}; + enum GMPVideoCodecType { kGMPVideoCodecVP8, + kGMPVideoCodecH264, kGMPVideoCodecInvalid // Should always be last. }; union GMPVideoCodecUnion { GMPVideoCodecVP8 mVP8; + GMPVideoCodecH264 mH264; }; // Simulcast is when the same stream is encoded multiple times with different @@ -128,10 +146,26 @@ struct GMPVideoCodec GMPVideoCodecMode mMode; }; +// Either single encoded unit, or multiple units separated by 8/16/24/32 +// bit lengths, all with the same timestamp. Note there is no final 0-length +// entry; one should check the overall end-of-buffer against where the next +// length would be. +enum GMPBufferType { + GMP_BufferSingle = 0, + GMP_BufferLength8, + GMP_BufferLength16, + GMP_BufferLength24, + GMP_BufferLength32, +}; + struct GMPCodecSpecificInfoGeneric { uint8_t mSimulcastIdx; }; +struct GMPCodecSpecificInfoH264 { + uint8_t mSimulcastIdx; +}; + // Note: if any pointers are added to this struct, it must be fitted // with a copy-constructor. See below. struct GMPCodecSpecificInfoVP8 @@ -161,6 +195,7 @@ union GMPCodecSpecificInfoUnion struct GMPCodecSpecificInfo { GMPVideoCodecType mCodecType; + GMPBufferType mBufferType; GMPCodecSpecificInfoUnion mCodecSpecific; }; diff --git a/media/webrtc/signaling/signaling.gyp b/media/webrtc/signaling/signaling.gyp index debcee505fdd..362fa4f99f65 100644 --- a/media/webrtc/signaling/signaling.gyp +++ b/media/webrtc/signaling/signaling.gyp @@ -87,6 +87,8 @@ './src/media-conduit/CodecStatistics.h', './src/media-conduit/CodecStatistics.cpp', './src/media-conduit/RunningStat.h', + './src/media-conduit/GmpVideoCodec.cpp', + './src/media-conduit/WebrtcGmpVideoCodec.cpp', # Common './src/common/CommonTypes.h', './src/common/csf_common.h', diff --git a/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.cpp b/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.cpp new file mode 100644 index 000000000000..7cb81fa86766 --- /dev/null +++ b/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.cpp @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "WebrtcGmpVideoCodec.h" +#include "GmpVideoCodec.h" + +namespace mozilla { + +VideoEncoder* GmpVideoCodec::CreateEncoder() { + return static_cast(new WebrtcGmpVideoEncoder()); +} + +VideoDecoder* GmpVideoCodec::CreateDecoder() { + return static_cast(new WebrtcGmpVideoDecoder()); +} + +} diff --git a/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.h b/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.h new file mode 100644 index 000000000000..340150409331 --- /dev/null +++ b/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.h @@ -0,0 +1,19 @@ +/* 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 GMPVIDEOCODEC_H_ +#define GMPVIDEOCODEC_H_ + +#include "MediaConduitInterface.h" + +namespace mozilla { +class GmpVideoCodec { + public: + static VideoEncoder* CreateEncoder(); + static VideoDecoder* CreateDecoder(); +}; + +} + +#endif diff --git a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp index 76322664e073..4c77f3af124f 100644 --- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp +++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp @@ -80,6 +80,11 @@ WebrtcVideoConduit::~WebrtcVideoConduit() } } + if (mPtrExtCodec) { + mPtrExtCodec->Release(); + mPtrExtCodec = NULL; + } + //Deal with External Renderer if(mPtrViERender) { @@ -346,6 +351,13 @@ MediaConduitErrorCode WebrtcVideoConduit::Init(WebrtcVideoConduit *other) return kMediaConduitSessionNotInited; } + mPtrExtCodec = webrtc::ViEExternalCodec::GetInterface(mVideoEngine); + if (!mPtrExtCodec) { + CSFLogError(logTag, "%s Unable to get external codec interface: %d ", + __FUNCTION__,mPtrViEBase->LastError()); + return kMediaConduitSessionNotInited; + } + if( !(mPtrRTP = webrtc::ViERTP_RTCP::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video RTCP interface ", __FUNCTION__); @@ -518,7 +530,7 @@ WebrtcVideoConduit::AttachTransport(mozilla::RefPtr aTranspo MediaConduitErrorCode WebrtcVideoConduit::ConfigureSendMediaCodec(const VideoCodecConfig* codecConfig) { - CSFLogDebug(logTag, "%s for %s", __FUNCTION__, codecConfig->mName.c_str()); + CSFLogDebug(logTag, "%s for %s", __FUNCTION__, codecConfig ? codecConfig->mName.c_str() : ""); bool codecFound = false; MediaConduitErrorCode condError = kMediaConduitNoError; int error = 0; //webrtc engine errors diff --git a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp new file mode 100644 index 000000000000..6b6c222092f0 --- /dev/null +++ b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp @@ -0,0 +1,601 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "WebrtcGmpVideoCodec.h" + +#include +#include + +#include "mozilla/Scoped.h" +#include "VideoConduit.h" +#include "AudioConduit.h" +#include "runnable_utils.h" + +#include "mozIGeckoMediaPluginService.h" +#include "nsServiceManagerUtils.h" + +#include "gmp-video-host.h" +#include "gmp-video-frame-i420.h" +#include "gmp-video-frame-encoded.h" + +#include "webrtc/video_engine/include/vie_external_codec.h" + +namespace mozilla { + +// Encoder. +WebrtcGmpVideoEncoder::WebrtcGmpVideoEncoder() + : mGMPThread(nullptr) + , mGMP(nullptr) + , mHost(nullptr) + , mCallback(nullptr) +{} + +static int +WebrtcFrameTypeToGmpFrameType(webrtc::VideoFrameType aIn, + GMPVideoFrameType *aOut) +{ + MOZ_ASSERT(aOut); + switch(aIn) { + case webrtc::kKeyFrame: + *aOut = kGMPKeyFrame; + break; + case webrtc::kDeltaFrame: + *aOut = kGMPDeltaFrame; + break; + case webrtc::kGoldenFrame: + *aOut = kGMPGoldenFrame; + break; + case webrtc::kAltRefFrame: + *aOut = kGMPAltRefFrame; + break; + case webrtc::kSkipFrame: + *aOut = kGMPSkipFrame; + break; + default: + MOZ_CRASH(); + return WEBRTC_VIDEO_CODEC_ERROR; + } + + return WEBRTC_VIDEO_CODEC_OK; +} + +static int +GmpFrameTypeToWebrtcFrameType(GMPVideoFrameType aIn, + webrtc::VideoFrameType *aOut) +{ + MOZ_ASSERT(aOut); + switch(aIn) { + case kGMPKeyFrame: + *aOut = webrtc::kKeyFrame; + break; + case kGMPDeltaFrame: + *aOut = webrtc::kDeltaFrame; + break; + case kGMPGoldenFrame: + *aOut = webrtc::kGoldenFrame; + break; + case kGMPAltRefFrame: + *aOut = webrtc::kAltRefFrame; + break; + case kGMPSkipFrame: + *aOut = webrtc::kSkipFrame; + break; + default: + MOZ_CRASH(); + return WEBRTC_VIDEO_CODEC_ERROR; + } + + return WEBRTC_VIDEO_CODEC_OK; +} + + +int32_t +WebrtcGmpVideoEncoder::InitEncode(const webrtc::VideoCodec* aCodecSettings, + int32_t aNumberOfCores, + uint32_t aMaxPayloadSize) +{ + mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1"); + MOZ_ASSERT(mMPS); + + nsresult rv = mMPS->GetThread(&mGMPThread); + if (NS_FAILED(rv)) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + + int32_t ret; + RUN_ON_THREAD(mGMPThread, + WrapRunnableRet(this, + &WebrtcGmpVideoEncoder::InitEncode_g, + aCodecSettings, + aNumberOfCores, + aMaxPayloadSize, + &ret)); + + return ret; +} + +int32_t +WebrtcGmpVideoEncoder::InitEncode_g(const webrtc::VideoCodec* aCodecSettings, + int32_t aNumberOfCores, + uint32_t aMaxPayloadSize) +{ + GMPVideoHost* host = nullptr; + GMPVideoEncoder* gmp = nullptr; + + nsresult rv = mMPS->GetGMPVideoEncoderVP8(&host, &gmp); + if (NS_FAILED(rv)) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + + mGMP = gmp; + mHost = host; + + if (!gmp || !host) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + + // Bug XXXXXX: transfer settings from codecSettings to codec. + GMPVideoCodec codec; + memset(&codec, 0, sizeof(codec)); + + codec.mWidth = aCodecSettings->width; + codec.mHeight = aCodecSettings->height; + codec.mStartBitrate = aCodecSettings->startBitrate; + codec.mMinBitrate = aCodecSettings->minBitrate; + codec.mMaxBitrate = aCodecSettings->maxBitrate; + codec.mMaxFramerate = aCodecSettings->maxFramerate; + + GMPVideoErr err = mGMP->InitEncode(codec, this, 1, aMaxPayloadSize); + if (err != GMPVideoNoErr) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + + return WEBRTC_VIDEO_CODEC_OK; +} + + +int32_t +WebrtcGmpVideoEncoder::Encode(const webrtc::I420VideoFrame& aInputImage, + const webrtc::CodecSpecificInfo* aCodecSpecificInfo, + const std::vector* aFrameTypes) +{ + int32_t ret; + MOZ_ASSERT(mGMPThread); + RUN_ON_THREAD(mGMPThread, + WrapRunnableRet(this, + &WebrtcGmpVideoEncoder::Encode_g, + &aInputImage, + aCodecSpecificInfo, + aFrameTypes, + &ret)); + + return ret; +} + + +int32_t +WebrtcGmpVideoEncoder::Encode_g(const webrtc::I420VideoFrame* aInputImage, + const webrtc::CodecSpecificInfo* aCodecSpecificInfo, + const std::vector* aFrameTypes) +{ + MOZ_ASSERT(mHost); + MOZ_ASSERT(mGMP); + + GMPVideoFrame* ftmp = nullptr; + GMPVideoErr err = mHost->CreateFrame(kGMPI420VideoFrame, &ftmp); + if (err != GMPVideoNoErr) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + GMPVideoi420Frame* frame = static_cast(ftmp); + + err = frame->CreateFrame(aInputImage->allocated_size(webrtc::kYPlane), + aInputImage->buffer(webrtc::kYPlane), + aInputImage->allocated_size(webrtc::kUPlane), + aInputImage->buffer(webrtc::kUPlane), + aInputImage->allocated_size(webrtc::kVPlane), + aInputImage->buffer(webrtc::kVPlane), + aInputImage->width(), + aInputImage->height(), + aInputImage->stride(webrtc::kYPlane), + aInputImage->stride(webrtc::kUPlane), + aInputImage->stride(webrtc::kVPlane)); + if (err != GMPVideoNoErr) { + return err; + } + frame->SetTimestamp(aInputImage->timestamp()); + frame->SetRenderTime_ms(aInputImage->render_time_ms()); + + // Bug XXXXXX: Set codecSpecific info + GMPCodecSpecificInfo info; + memset(&info, 0, sizeof(info)); + + std::vector gmp_frame_types; + for (auto it = aFrameTypes->begin(); it != aFrameTypes->end(); ++it) { + GMPVideoFrameType ft; + + int32_t ret = WebrtcFrameTypeToGmpFrameType(*it, &ft); + if (ret != WEBRTC_VIDEO_CODEC_OK) { + return ret; + } + + gmp_frame_types.push_back(ft); + } + + err = mGMP->Encode(frame, info, gmp_frame_types); + if (err != GMPVideoNoErr) { + return err; + } + + return WEBRTC_VIDEO_CODEC_OK; +} + + + +int32_t +WebrtcGmpVideoEncoder::RegisterEncodeCompleteCallback(webrtc::EncodedImageCallback* aCallback) +{ + mCallback = aCallback; + + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t +WebrtcGmpVideoEncoder::Release() +{ + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t +WebrtcGmpVideoEncoder::SetChannelParameters(uint32_t aPacketLoss, int aRTT) +{ + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t +WebrtcGmpVideoEncoder::SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) +{ + int32_t ret; + MOZ_ASSERT(mGMPThread); + RUN_ON_THREAD(mGMPThread, + WrapRunnableRet(this, + &WebrtcGmpVideoEncoder::SetRates_g, + aNewBitRate, aFrameRate, + &ret)); + + return ret; +} + +int32_t +WebrtcGmpVideoEncoder::SetRates_g(uint32_t aNewBitRate, uint32_t aFrameRate) +{ + MOZ_ASSERT(mGMP); + GMPVideoErr err = mGMP->SetRates(aNewBitRate, aFrameRate); + if (err != GMPVideoNoErr) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + + return WEBRTC_VIDEO_CODEC_OK; +} + +#define GMP_ENCODE_HAS_START_CODES 1 +#ifdef GMP_ENCODE_HAS_START_CODES +// Temporary until inside-sandbox-code switches from start codes to the API here +static int GetNextNALUnit(const uint8_t **aData, + const uint8_t *aEnd, // at first byte past end + size_t *aNalSize) +{ + const uint8_t *data = *aData; + uint8_t zeros = 0; + + MOZ_ASSERT(data); + // Don't assume we start with a start code (paranoia) + while (data < aEnd) { + if (*data == 0) { + zeros++; + if (zeros > 3) { + // internal format error; keep going anyways + zeros = 3; + } + } else { + if (*data == 0x01) { + if (zeros >= 2) { + // Found start code 0x000001 or 0x00000001 + MOZ_ASSERT(zeros == 3); // current temp code only handles 4-byte codes + // now find the length of the NAL + *aData = ++data; // start of actual data + + while (data < aEnd) { + if (*data == 0) { + zeros++; + if (zeros > 3) { + // internal format error; keep going anyways + zeros = 3; + } + } else { + if (*data == 0x01) { + if (zeros >= 2) { + // Found start code 0x000001 or 0x00000001 + *aNalSize = (data - *aData) - zeros; + return 0; + } + } + zeros = 0; + } + data++; + } + // NAL ends at the end of the buffer + *aNalSize = (data - *aData); + return 0; + } + } + zeros = 0; + } + data++; + } + return -1; // no nals +} + +#endif + +// GMPEncoderCallback virtual functions. +void +WebrtcGmpVideoEncoder::Encoded(GMPVideoEncodedFrame* aEncodedFrame, + const GMPCodecSpecificInfo& aCodecSpecificInfo) +{ + if (mCallback) { // paranoia + webrtc::VideoFrameType ft; + GmpFrameTypeToWebrtcFrameType(aEncodedFrame->FrameType(), &ft); + GMPBufferType type = aCodecSpecificInfo.mBufferType; + +#ifdef GMP_ENCODE_HAS_START_CODES + { + // This code will be removed when the code inside the plugin is updated + // Break input encoded data into NALUs and convert to length+data format + const uint8_t* data = aEncodedFrame->Buffer(); + const uint8_t* end = data + aEncodedFrame->Size(); // at first byte past end + size_t nalSize = 0; + while (GetNextNALUnit(&data, end, &nalSize) == 0) { + // Assumes 4-byte start codes (0x00000001) + MOZ_ASSERT(data >= aEncodedFrame->Buffer() + 4); + uint8_t *start_code = const_cast(data-sizeof(uint32_t)); + if (*start_code == 0x00 && *(start_code+1) == 0x00 && + *(start_code+2) == 0x00 && *(start_code+3) == 0x01) { + *(reinterpret_cast(start_code)) = nalSize; + } + data += nalSize; + } + type = GMP_BufferLength32; + } +#endif + + // Right now makes one Encoded() callback per unit + // XXX convert to FragmentationHeader format (array of offsets and sizes plus a buffer) in + // combination with H264 packetization changes in webrtc/trunk code + uint8_t *buffer = aEncodedFrame->Buffer(); + uint8_t *end = aEncodedFrame->Buffer() + aEncodedFrame->Size(); + uint32_t size; + while (buffer < end) { + switch (type) { + case GMP_BufferSingle: + size = aEncodedFrame->Size(); + break; + case GMP_BufferLength8: + size = *buffer++; + break; + case GMP_BufferLength16: + // presumes we can do unaligned loads + size = *(reinterpret_cast(buffer)); + buffer += 2; + break; + case GMP_BufferLength24: + // 24-bits is a pain, since byte-order issues make things painful + // I'm going to define 24-bit as little-endian always; big-endian must convert + size = ((uint32_t) *buffer) | + (((uint32_t) *(buffer+1)) << 8) | + (((uint32_t) *(buffer+2)) << 16); + buffer += 3; + break; + case GMP_BufferLength32: + // presumes we can do unaligned loads + size = *(reinterpret_cast(buffer)); + buffer += 4; + break; + default: + // really that it's not in the enum; gives more readable error + MOZ_ASSERT(aCodecSpecificInfo.mBufferType != GMP_BufferSingle); + aEncodedFrame->Destroy(); + return; + } + webrtc::EncodedImage unit(buffer, size, size); + unit._frameType = ft; + unit._timeStamp = aEncodedFrame->TimeStamp(); + unit._completeFrame = true; + + mCallback->Encoded(unit, nullptr, nullptr); + + buffer += size; + } + } + aEncodedFrame->Destroy(); +} + + +// Decoder. +WebrtcGmpVideoDecoder::WebrtcGmpVideoDecoder() : + mGMPThread(nullptr), + mGMP(nullptr), + mHost(nullptr), + mCallback(nullptr) {} + +int32_t +WebrtcGmpVideoDecoder::InitDecode(const webrtc::VideoCodec* aCodecSettings, + int32_t aNumberOfCores) +{ + mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1"); + MOZ_ASSERT(mMPS); + + if (NS_WARN_IF(NS_FAILED(mMPS->GetThread(&mGMPThread)))) + { + return WEBRTC_VIDEO_CODEC_ERROR; + } + + int32_t ret; + RUN_ON_THREAD(mGMPThread, + WrapRunnableRet(this, + &WebrtcGmpVideoDecoder::InitDecode_g, + aCodecSettings, + aNumberOfCores, + &ret)); + + return ret; +} + +int32_t +WebrtcGmpVideoDecoder::InitDecode_g(const webrtc::VideoCodec* aCodecSettings, + int32_t aNumberOfCores) +{ + GMPVideoHost* host = nullptr; + GMPVideoDecoder* gmp = nullptr; + + if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoDecoderVP8(&host, &gmp)))) + { + return WEBRTC_VIDEO_CODEC_ERROR; + } + + mGMP = gmp; + mHost = host; + if (!gmp || !host) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + + // Bug XXXXXX: transfer settings from codecSettings to codec. + GMPVideoCodec codec; + memset(&codec, 0, sizeof(codec)); + + GMPVideoErr err = mGMP->InitDecode(codec, this, 1); + if (err != GMPVideoNoErr) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t +WebrtcGmpVideoDecoder::Decode(const webrtc::EncodedImage& aInputImage, + bool aMissingFrames, + const webrtc::RTPFragmentationHeader* aFragmentation, + const webrtc::CodecSpecificInfo* aCodecSpecificInfo, + int64_t aRenderTimeMs) +{ + int32_t ret; + MOZ_ASSERT(mGMPThread); + RUN_ON_THREAD(mGMPThread, + WrapRunnableRet(this, + &WebrtcGmpVideoDecoder::Decode_g, + aInputImage, + aMissingFrames, + aFragmentation, + aCodecSpecificInfo, + aRenderTimeMs, + &ret)); + + return ret; +} + +int32_t +WebrtcGmpVideoDecoder::Decode_g(const webrtc::EncodedImage& aInputImage, + bool aMissingFrames, + const webrtc::RTPFragmentationHeader* aFragmentation, + const webrtc::CodecSpecificInfo* aCodecSpecificInfo, + int64_t aRenderTimeMs) +{ + MOZ_ASSERT(mHost); + MOZ_ASSERT(mGMP); + + GMPVideoFrame* ftmp = nullptr; + GMPVideoErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp); + if (err != GMPVideoNoErr) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + + GMPVideoEncodedFrame* frame = static_cast(ftmp); + err = frame->CreateEmptyFrame(aInputImage._length); + if (err != GMPVideoNoErr) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + // XXX It'd be wonderful not to have to memcpy the encoded data! + memcpy(frame->Buffer(), aInputImage._buffer, frame->Size()); + + frame->SetEncodedWidth(aInputImage._encodedWidth); + frame->SetEncodedHeight(aInputImage._encodedHeight); + frame->SetTimeStamp(aInputImage._timeStamp); + frame->SetCompleteFrame(aInputImage._completeFrame); + + GMPVideoFrameType ft; + int32_t ret = WebrtcFrameTypeToGmpFrameType(aInputImage._frameType, &ft); + if (ret != WEBRTC_VIDEO_CODEC_OK) { + return ret; + } + + // Bug XXXXXX: Set codecSpecific info + GMPCodecSpecificInfo info; + memset(&info, 0, sizeof(info)); + + err = mGMP->Decode(frame, aMissingFrames, info, aRenderTimeMs); + if (err != GMPVideoNoErr) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t +WebrtcGmpVideoDecoder::RegisterDecodeCompleteCallback( webrtc::DecodedImageCallback* aCallback) +{ + mCallback = aCallback; + + return WEBRTC_VIDEO_CODEC_OK; +} + + +int32_t +WebrtcGmpVideoDecoder::Release() +{ + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t +WebrtcGmpVideoDecoder::Reset() +{ + return WEBRTC_VIDEO_CODEC_OK; +} + +void +WebrtcGmpVideoDecoder::Decoded(GMPVideoi420Frame* aDecodedFrame) +{ + if (mCallback) { // paranioa + webrtc::I420VideoFrame image; + int ret = image.CreateFrame(aDecodedFrame->AllocatedSize(kGMPYPlane), + aDecodedFrame->Buffer(kGMPYPlane), + aDecodedFrame->AllocatedSize(kGMPUPlane), + aDecodedFrame->Buffer(kGMPUPlane), + aDecodedFrame->AllocatedSize(kGMPVPlane), + aDecodedFrame->Buffer(kGMPVPlane), + aDecodedFrame->Width(), + aDecodedFrame->Height(), + aDecodedFrame->Stride(kGMPYPlane), + aDecodedFrame->Stride(kGMPUPlane), + aDecodedFrame->Stride(kGMPVPlane)); + if (ret != 0) { + return; + } + image.set_timestamp(aDecodedFrame->Timestamp()); + image.set_render_time_ms(0); + + mCallback->Decoded(image); + } + aDecodedFrame->Destroy(); +} + +} diff --git a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h new file mode 100644 index 000000000000..27ab495466e9 --- /dev/null +++ b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h @@ -0,0 +1,150 @@ +/* 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/. */ + +// Class templates copied from WebRTC: +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + +#ifndef WEBRTCGMPVIDEOCODEC_H_ +#define WEBRTCGMPVIDEOCODEC_H_ + +#include + +#include +#include + +#include "nsThreadUtils.h" +#include "mozilla/Mutex.h" + +#include "mozIGeckoMediaPluginService.h" +#include "MediaConduitInterface.h" +#include "AudioConduit.h" +#include "VideoConduit.h" +#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h" + +#include "gmp-video-host.h" +#include "gmp-video-encode.h" +#include "gmp-video-decode.h" +#include "gmp-video-frame-i420.h" +#include "gmp-video-frame-encoded.h" + +#include "WebrtcGmpVideoCodec.h" + +namespace mozilla { + +class WebrtcGmpVideoEncoder : public WebrtcVideoEncoder, + public GMPEncoderCallback +{ +public: + WebrtcGmpVideoEncoder(); + virtual ~WebrtcGmpVideoEncoder() {} + + // Implement VideoEncoder interface. + 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* aFrameTypes); + + virtual int32_t RegisterEncodeCompleteCallback( + webrtc::EncodedImageCallback* aCallback) MOZ_OVERRIDE; + + virtual int32_t Release(); + + virtual int32_t SetChannelParameters(uint32_t aPacketLoss, + int aRTT) MOZ_OVERRIDE; + + virtual int32_t SetRates(uint32_t aNewBitRate, + uint32_t aFrameRate) MOZ_OVERRIDE; + + // GMPEncoderCallback virtual functions. + virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame, + const GMPCodecSpecificInfo& aCodecSpecificInfo) MOZ_OVERRIDE; + + +private: + virtual int32_t InitEncode_g(const webrtc::VideoCodec* aCodecSettings, + int32_t aNumberOfCores, + uint32_t aMaxPayloadSize); + + virtual int32_t Encode_g(const webrtc::I420VideoFrame* aInputImage, + const webrtc::CodecSpecificInfo* aCodecSpecificInfo, + const std::vector* aFrameTypes); + + virtual int32_t SetRates_g(uint32_t aNewBitRate, + uint32_t aFrameRate); + + nsCOMPtr mMPS; + nsIThread* mGMPThread; + GMPVideoEncoder* mGMP; + GMPVideoHost* mHost; + webrtc::EncodedImageCallback* mCallback; +}; + + +class WebrtcGmpVideoDecoder : public WebrtcVideoDecoder, + public GMPDecoderCallback +{ +public: + WebrtcGmpVideoDecoder(); + virtual ~WebrtcGmpVideoDecoder() {} + + // Implement VideoDecoder interface. + virtual int32_t InitDecode(const webrtc::VideoCodec* aCodecSettings, + 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) MOZ_OVERRIDE; + virtual int32_t RegisterDecodeCompleteCallback(webrtc::DecodedImageCallback* aCallback) MOZ_OVERRIDE; + + virtual int32_t Release(); + + virtual int32_t Reset() MOZ_OVERRIDE; + + virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) MOZ_OVERRIDE; + + virtual void ReceivedDecodedReferenceFrame(const uint64_t aPictureId) MOZ_OVERRIDE { + MOZ_CRASH(); + } + + virtual void ReceivedDecodedFrame(const uint64_t aPictureId) MOZ_OVERRIDE { + MOZ_CRASH(); + } + + virtual void InputDataExhausted() MOZ_OVERRIDE { + MOZ_CRASH(); + } + +private: + virtual int32_t InitDecode_g(const webrtc::VideoCodec* aCodecSettings, + int32_t aNumberOfCores); + + virtual int32_t Decode_g(const webrtc::EncodedImage& aInputImage, + bool aMissingFrames, + const webrtc::RTPFragmentationHeader* aFragmentation, + const webrtc::CodecSpecificInfo* aCodecSpecificInfo, + int64_t aRenderTimeMs); + + nsCOMPtr mMPS; + nsIThread* mGMPThread; + GMPVideoDecoder* mGMP; + GMPVideoHost* mHost; + webrtc::DecodedImageCallback* mCallback; +}; + +} + +#endif diff --git a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp index cd8ebd688255..ce67752f0e60 100644 --- a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp +++ b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp @@ -11,6 +11,7 @@ #include "CSFVideoTermination.h" #include "MediaConduitErrors.h" #include "MediaConduitInterface.h" +#include "GmpVideoCodec.h" #include "MediaPipeline.h" #include "MediaPipelineFilter.h" #include "VcmSIPCCBinding.h" @@ -2172,7 +2173,6 @@ static int vcmEnsureExternalCodec( if (config->mName == "VP8") { // whitelist internal codecs; I420 will be here once we resolve bug 995884 return 0; -#ifdef MOZ_WEBRTC_OMX } else if (config->mName == "H264_P0" || config->mName == "H264_P1") { // Here we use "I420" to register H.264 because WebRTC.org code has a // whitelist of supported video codec in |webrtc::ViECodecImpl::CodecValid()| @@ -2181,14 +2181,25 @@ static int vcmEnsureExternalCodec( // Register H.264 codec. if (send) { - VideoEncoder* encoder = OMXVideoCodec::CreateEncoder(OMXVideoCodec::CodecType::CODEC_H264); + VideoEncoder* encoder = nullptr; +#ifdef MOZ_WEBRTC_OMX + encoder = OMXVideoCodec::CreateEncoder( + OMXVideoCodec::CodecType::CODEC_H264); +#else + encoder = mozilla::GmpVideoCodec::CreateEncoder(); +#endif if (encoder) { return conduit->SetExternalSendCodec(config, encoder); } else { return kMediaConduitInvalidSendCodec; } } else { - VideoDecoder* decoder = OMXVideoCodec::CreateDecoder(OMXVideoCodec::CodecType::CODEC_H264); + VideoDecoder* decoder; +#ifdef MOZ_WEBRTC_OMX + decoder = OMXVideoCodec::CreateDecoder(OMXVideoCodec::CodecType::CODEC_H264); +#else + decoder = mozilla::GmpVideoCodec::CreateDecoder(); +#endif if (decoder) { return conduit->SetExternalRecvCodec(config, decoder); } else { @@ -2196,10 +2207,6 @@ static int vcmEnsureExternalCodec( } } NS_NOTREACHED("Shouldn't get here!"); -#else - } else if (config->mName == "I420") { - return 0; -#endif } else { CSFLogError( logTag, "%s: Invalid video codec configured: %s", __FUNCTION__, config->mName.c_str()); return send ? kMediaConduitInvalidSendCodec : kMediaConduitInvalidReceiveCodec; diff --git a/media/webrtc/signaling/test/mediaconduit_unittests.cpp b/media/webrtc/signaling/test/mediaconduit_unittests.cpp index 9a1278517a96..9102f8346bb5 100644 --- a/media/webrtc/signaling/test/mediaconduit_unittests.cpp +++ b/media/webrtc/signaling/test/mediaconduit_unittests.cpp @@ -12,14 +12,22 @@ using namespace std; #include "mozilla/Scoped.h" +#include "mozilla/SyncRunnable.h" #include #include "nsIEventTarget.h" #include "FakeMediaStreamsImpl.h" +#include "GmpVideoCodec.h" +#include "nsThreadUtils.h" +#include "runnable_utils.h" #define GTEST_HAS_RTTI 0 #include "gtest/gtest.h" #include "gtest_utils.h" +nsCOMPtr gMainThread; +nsCOMPtr gGtestThread; +bool gTestsComplete = false; + #include "mtransport_test_utils.h" MtransportTestUtils *test_utils; @@ -48,7 +56,6 @@ struct VideoTestStats VideoTestStats vidStatsGlobal={0,0,0}; - /** * A Dummy Video Conduit Tester. * The test-case inserts a 640*480 grey imagerevery 33 milliseconds @@ -59,7 +66,8 @@ class VideoSendAndReceive { public: VideoSendAndReceive():width(640), - height(480) + height(480), + rate(30) { } @@ -67,36 +75,46 @@ public: { } + void SetDimensions(int w, int h) + { + width = w; + height = h; + } + void SetRate(int r) { + rate = r; + } void Init(mozilla::RefPtr aSession) { mSession = aSession; + mLen = ((width * height) * 3 / 2); + mFrame = (uint8_t*) PR_MALLOC(mLen); + memset(mFrame, COLOR, mLen); + numFrames = 121; } + void GenerateAndReadSamples() { - - int len = ((width * height) * 3 / 2); - uint8_t* frame = (uint8_t*) PR_MALLOC(len); - int numFrames = 121; - memset(frame, COLOR, len); - do { - mSession->SendVideoFrame((unsigned char*)frame, - len, + mSession->SendVideoFrame((unsigned char*)mFrame, + mLen, width, height, mozilla::kVideoI420, 0); - PR_Sleep(PR_MillisecondsToInterval(33)); + PR_Sleep(PR_MillisecondsToInterval(1000/rate)); vidStatsGlobal.numRawFramesInserted++; numFrames--; } while(numFrames >= 0); - PR_Free(frame); } private: mozilla::RefPtr mSession; +mozilla::ScopedDeletePtr mFrame; +int mLen; int width, height; +int rate; +int numFrames; }; @@ -267,17 +285,17 @@ void AudioSendAndReceive::GenerateMusic(short* buf, int len) //Hardcoded for 16 bit samples for now void AudioSendAndReceive::GenerateAndReadSamples() { - int16_t audioInput[PLAYOUT_SAMPLE_LENGTH]; - int16_t audioOutput[PLAYOUT_SAMPLE_LENGTH]; + mozilla::ScopedDeletePtr audioInput(new int16_t [PLAYOUT_SAMPLE_LENGTH]); + mozilla::ScopedDeletePtr audioOutput(new int16_t [PLAYOUT_SAMPLE_LENGTH]); short* inbuf; int sampleLengthDecoded = 0; unsigned int SAMPLES = (PLAYOUT_SAMPLE_FREQUENCY * 10); //10 seconds int CHANNELS = 1; //mono audio - int sampleLengthInBytes = sizeof(audioInput); + int sampleLengthInBytes = sizeof(int16_t) * PLAYOUT_SAMPLE_LENGTH; //generated audio buffer inbuf = (short *)moz_xmalloc(sizeof(short)*SAMPLES*CHANNELS); - memset(audioInput,0,sampleLengthInBytes); - memset(audioOutput,0,sampleLengthInBytes); + memset(audioInput.get(),0,sampleLengthInBytes); + memset(audioOutput.get(),0,sampleLengthInBytes); MOZ_ASSERT(SAMPLES <= PLAYOUT_SAMPLE_LENGTH); FILE* inFile = fopen( iFile.c_str(), "wb+"); @@ -303,7 +321,7 @@ void AudioSendAndReceive::GenerateAndReadSamples() unsigned int numSamplesReadFromInput = 0; do { - if(!memcpy(audioInput, inbuf, sampleLengthInBytes)) + if(!memcpy(audioInput.get(), inbuf, sampleLengthInBytes)) { return; } @@ -311,19 +329,19 @@ void AudioSendAndReceive::GenerateAndReadSamples() numSamplesReadFromInput += PLAYOUT_SAMPLE_LENGTH; inbuf += PLAYOUT_SAMPLE_LENGTH; - mSession->SendAudioFrame(audioInput, + mSession->SendAudioFrame(audioInput.get(), PLAYOUT_SAMPLE_LENGTH, PLAYOUT_SAMPLE_FREQUENCY,10); PR_Sleep(PR_MillisecondsToInterval(10)); - mOtherSession->GetAudioFrame(audioOutput, PLAYOUT_SAMPLE_FREQUENCY, + mOtherSession->GetAudioFrame(audioOutput.get(), PLAYOUT_SAMPLE_FREQUENCY, 10, sampleLengthDecoded); if(sampleLengthDecoded == 0) { cerr << " Zero length Sample " << endl; } - int wrote_ = fwrite (audioOutput, 1 , sampleLengthInBytes, outFile); + int wrote_ = fwrite (audioOutput.get(), 1 , sampleLengthInBytes, outFile); if(wrote_ != sampleLengthInBytes) { cerr << "Couldn't Write " << sampleLengthInBytes << "bytes" << endl; @@ -396,22 +414,22 @@ public: }; /** - * Fake Audio and Video External Transport Class + * Webrtc Audio and Video External Transport Class * The functions in this class will be invoked by the conduit * when it has RTP/RTCP frame to transmit. * For everty RTP/RTCP frame we receive, we pass it back * to the conduit for eventual decoding and rendering. */ -class FakeMediaTransport : public mozilla::TransportInterface +class WebrtcMediaTransport : public mozilla::TransportInterface { public: - FakeMediaTransport():numPkts(0), + WebrtcMediaTransport():numPkts(0), mAudio(false), mVideo(false) { } - ~FakeMediaTransport() + ~WebrtcMediaTransport() { } @@ -475,6 +493,7 @@ namespace { class TransportConduitTest : public ::testing::Test { public: + TransportConduitTest() { //input and output file names @@ -484,6 +503,21 @@ class TransportConduitTest : public ::testing::Test ~TransportConduitTest() { + mozilla::SyncRunnable::DispatchToThread(gMainThread, + mozilla::WrapRunnable( + this, + &TransportConduitTest::SelfDestruct)); + } + + void SelfDestruct() { + mAudioSession = nullptr; + mAudioSession2 = nullptr; + mAudioTransport = nullptr; + + mVideoSession = nullptr; + mVideoSession2 = nullptr; + mVideoRenderer = nullptr; + mVideoTransport = nullptr; } //1. Dump audio samples to dummy external transport @@ -491,15 +525,23 @@ class TransportConduitTest : public ::testing::Test { //get pointer to AudioSessionConduit int err=0; - mAudioSession = mozilla::AudioSessionConduit::Create(nullptr); + mozilla::SyncRunnable::DispatchToThread(gMainThread, + WrapRunnableNMRet( + &mozilla::AudioSessionConduit::Create, + nullptr, + &mAudioSession)); if( !mAudioSession ) ASSERT_NE(mAudioSession, (void*)nullptr); - mAudioSession2 = mozilla::AudioSessionConduit::Create(nullptr); + mozilla::SyncRunnable::DispatchToThread(gMainThread, + WrapRunnableNMRet( + &mozilla::AudioSessionConduit::Create, + nullptr, + &mAudioSession2)); if( !mAudioSession2 ) ASSERT_NE(mAudioSession2, (void*)nullptr); - FakeMediaTransport* xport = new FakeMediaTransport(); + WebrtcMediaTransport* xport = new WebrtcMediaTransport(); ASSERT_NE(xport, (void*)nullptr); xport->SetAudioSession(mAudioSession, mAudioSession2); mAudioTransport = xport; @@ -545,23 +587,35 @@ class TransportConduitTest : public ::testing::Test } //2. Dump audio samples to dummy external transport - void TestDummyVideoAndTransport() + void TestDummyVideoAndTransport(bool send_vp8 = true, const char *source_file = nullptr) { int err = 0; //get pointer to VideoSessionConduit - mVideoSession = mozilla::VideoSessionConduit::Create(nullptr); + mozilla::SyncRunnable::DispatchToThread(gMainThread, + WrapRunnableNMRet( + &mozilla::VideoSessionConduit::Create, + nullptr, + &mVideoSession)); if( !mVideoSession ) ASSERT_NE(mVideoSession, (void*)nullptr); // This session is for other one - mVideoSession2 = mozilla::VideoSessionConduit::Create(nullptr); + mozilla::SyncRunnable::DispatchToThread(gMainThread, + WrapRunnableNMRet( + &mozilla::VideoSessionConduit::Create, + nullptr, + &mVideoSession2)); if( !mVideoSession2 ) ASSERT_NE(mVideoSession2,(void*)nullptr); + if (!send_vp8) { + SetGmpCodecs(); + } + mVideoRenderer = new DummyVideoTarget(); ASSERT_NE(mVideoRenderer, (void*)nullptr); - FakeMediaTransport* xport = new FakeMediaTransport(); + WebrtcMediaTransport* xport = new WebrtcMediaTransport(); ASSERT_NE(xport, (void*)nullptr); xport->SetVideoSession(mVideoSession,mVideoSession2); mVideoTransport = xport; @@ -583,9 +637,15 @@ class TransportConduitTest : public ::testing::Test rcvCodecList.push_back(&cinst1); rcvCodecList.push_back(&cinst2); - err = mVideoSession->ConfigureSendMediaCodec(&cinst1); + err = mVideoSession->ConfigureSendMediaCodec( + send_vp8 ? &cinst1 : &cinst2); + ASSERT_EQ(mozilla::kMediaConduitNoError, err); + err = mVideoSession2->ConfigureSendMediaCodec( + send_vp8 ? &cinst1 : &cinst2); + + ASSERT_EQ(mozilla::kMediaConduitNoError, err); err = mVideoSession2->ConfigureRecvMediaCodecs(rcvCodecList); ASSERT_EQ(mozilla::kMediaConduitNoError, err); @@ -597,6 +657,7 @@ class TransportConduitTest : public ::testing::Test videoTester.Init(mVideoSession); videoTester.GenerateAndReadSamples(); PR_Sleep(PR_SecondsToInterval(2)); + cerr << " **************************************************" << endl; cerr << " Done With The Testing " << endl; cerr << " VIDEO TEST STATS " << endl; @@ -611,18 +672,32 @@ class TransportConduitTest : public ::testing::Test cerr << " **************************************************" << endl; ASSERT_EQ(0, vidStatsGlobal.numFramesRenderedWrongly); - ASSERT_EQ(vidStatsGlobal.numRawFramesInserted, - vidStatsGlobal.numFramesRenderedSuccessfully); + if (send_vp8) { + ASSERT_EQ(vidStatsGlobal.numRawFramesInserted, + vidStatsGlobal.numFramesRenderedSuccessfully); + } + else { + // Allow some fudge because there seems to be some buffering. + // TODO(ekr@rtfm.com): Fix this. + ASSERT_GE(vidStatsGlobal.numRawFramesInserted, + vidStatsGlobal.numFramesRenderedSuccessfully); + ASSERT_LE(vidStatsGlobal.numRawFramesInserted, + vidStatsGlobal.numFramesRenderedSuccessfully + 2); + } } void TestVideoConduitCodecAPI() { int err = 0; - mozilla::RefPtr mVideoSession; + mozilla::RefPtr videoSession; //get pointer to VideoSessionConduit - mVideoSession = mozilla::VideoSessionConduit::Create(nullptr); - if( !mVideoSession ) - ASSERT_NE(mVideoSession, (void*)nullptr); + mozilla::SyncRunnable::DispatchToThread(gMainThread, + WrapRunnableNMRet( + &mozilla::VideoSessionConduit::Create, + nullptr, + &videoSession)); + if( !videoSession ) + ASSERT_NE(videoSession, (void*)nullptr); //Test Configure Recv Codec APIS cerr << " *************************************************" << endl; @@ -640,7 +715,7 @@ class TransportConduitTest : public ::testing::Test mozilla::VideoCodecConfig cinst2(120, "VP8", 0); rcvCodecList.push_back(&cinst1); rcvCodecList.push_back(&cinst2); - err = mVideoSession->ConfigureRecvMediaCodecs(rcvCodecList); + err = videoSession->ConfigureRecvMediaCodecs(rcvCodecList); EXPECT_NE(err,mozilla::kMediaConduitNoError); rcvCodecList.pop_back(); rcvCodecList.pop_back(); @@ -659,7 +734,7 @@ class TransportConduitTest : public ::testing::Test rcvCodecList.push_back(&cinst3); rcvCodecList.push_back(&cinst4); - err = mVideoSession->ConfigureRecvMediaCodecs(rcvCodecList); + err = videoSession->ConfigureRecvMediaCodecs(rcvCodecList); EXPECT_TRUE(err != mozilla::kMediaConduitNoError); rcvCodecList.pop_back(); rcvCodecList.pop_back(); @@ -672,7 +747,7 @@ class TransportConduitTest : public ::testing::Test rcvCodecList.push_back(0); - err = mVideoSession->ConfigureRecvMediaCodecs(rcvCodecList); + err = videoSession->ConfigureRecvMediaCodecs(rcvCodecList); EXPECT_TRUE(err != mozilla::kMediaConduitNoError); rcvCodecList.pop_back(); @@ -685,9 +760,9 @@ class TransportConduitTest : public ::testing::Test cerr << " *************************************************" << endl; - err = mVideoSession->ConfigureSendMediaCodec(&cinst1); + err = videoSession->ConfigureSendMediaCodec(&cinst1); EXPECT_EQ(mozilla::kMediaConduitNoError, err); - err = mVideoSession->ConfigureSendMediaCodec(&cinst1); + err = videoSession->ConfigureSendMediaCodec(&cinst1); EXPECT_EQ(mozilla::kMediaConduitCodecInUse, err); @@ -696,16 +771,20 @@ class TransportConduitTest : public ::testing::Test cerr << " *************************************************" << endl; cerr << " Setting payload with name: I4201234tttttthhhyyyy89087987y76t567r7756765rr6u6676" << endl; - err = mVideoSession->ConfigureSendMediaCodec(&cinst3); + err = videoSession->ConfigureSendMediaCodec(&cinst3); EXPECT_TRUE(err != mozilla::kMediaConduitNoError); cerr << " *************************************************" << endl; cerr << " 3. Null Codec Parameter " << endl; cerr << " *************************************************" << endl; - err = mVideoSession->ConfigureSendMediaCodec(nullptr); + err = videoSession->ConfigureSendMediaCodec(nullptr); EXPECT_TRUE(err != mozilla::kMediaConduitNoError); + mozilla::SyncRunnable::DispatchToThread(gMainThread, + WrapRunnable( + videoSession.forget().drop(), + &mozilla::VideoSessionConduit::Release)); } void DumpMaxFs(int orig_width, int orig_height, int max_fs, @@ -724,7 +803,11 @@ class TransportConduitTest : public ::testing::Test int err = 0; // Get pointer to VideoSessionConduit. - mVideoSession = mozilla::VideoSessionConduit::Create(nullptr); + mozilla::SyncRunnable::DispatchToThread(gMainThread, + WrapRunnableNMRet( + &mozilla::VideoSessionConduit::Create, + nullptr, + &mVideoSession)); if( !mVideoSession ) ASSERT_NE(mVideoSession, (void*)nullptr); @@ -861,7 +944,15 @@ class TransportConduitTest : public ::testing::Test cerr << endl; } -private: + void SetGmpCodecs() { + mExternalEncoder = mozilla::GmpVideoCodec::CreateEncoder(); + mExternalDecoder = mozilla::GmpVideoCodec::CreateDecoder(); + mozilla::VideoCodecConfig config(124, "H264", 0); + mVideoSession->SetExternalSendCodec(&config, mExternalEncoder); + mVideoSession2->SetExternalRecvCodec(&config, mExternalDecoder); + } + + private: //Audio Conduit Test Objects mozilla::RefPtr mAudioSession; mozilla::RefPtr mAudioSession2; @@ -875,6 +966,9 @@ private: mozilla::RefPtr mVideoTransport; VideoSendAndReceive videoTester; + mozilla::VideoEncoder* mExternalEncoder; + mozilla::VideoDecoder* mExternalDecoder; + std::string fileToPlay; std::string fileToRecord; std::string iAudiofilename; @@ -892,6 +986,10 @@ TEST_F(TransportConduitTest, TestDummyVideoWithTransport) { TestDummyVideoAndTransport(); } +TEST_F(TransportConduitTest, TestVideoConduitExternalCodec) { + TestDummyVideoAndTransport(false); +} + TEST_F(TransportConduitTest, TestVideoConduitCodecAPI) { TestVideoConduitCodecAPI(); } @@ -902,16 +1000,64 @@ TEST_F(TransportConduitTest, TestVideoConduitMaxFs) { } // end namespace +static int test_result; +bool test_finished = false; + + + +// This exists to send as an event to trigger shutdown. +static void tests_complete() { + gTestsComplete = true; +} + +// The GTest thread runs this instead of the main thread so it can +// do things like ASSERT_TRUE_WAIT which you could not do on the main thread. +static int gtest_main(int argc, char **argv) { + MOZ_ASSERT(!NS_IsMainThread()); + + ::testing::InitGoogleTest(&argc, argv); + + int result = RUN_ALL_TESTS(); + + // Set the global shutdown flag and tickle the main thread + // The main thread did not go through Init() so calling Shutdown() + // on it will not work. + gMainThread->Dispatch(mozilla::WrapRunnableNM(tests_complete), NS_DISPATCH_SYNC); + + return result; +} + int main(int argc, char **argv) { // This test can cause intermittent oranges on the builders CHECK_ENVIRONMENT_FLAG("MOZ_WEBRTC_MEDIACONDUIT_TESTS") test_utils = new MtransportTestUtils(); - ::testing::InitGoogleTest(&argc, argv); - int rv = RUN_ALL_TESTS(); + + // Set the main thread global which is this thread. + nsIThread *thread; + NS_GetMainThread(&thread); + gMainThread = thread; + + // Now create the GTest thread and run all of the tests on it + // When it is complete it will set gTestsComplete + NS_NewNamedThread("gtest_thread", &thread); + gGtestThread = thread; + + int result; + gGtestThread->Dispatch( + mozilla::WrapRunnableNMRet(gtest_main, argc, argv, &result), NS_DISPATCH_NORMAL); + + // Here we handle the event queue for dispatches to the main thread + // When the GTest thread is complete it will send one more dispatch + // with gTestsComplete == true. + while (!gTestsComplete && NS_ProcessNextEvent()); + + gGtestThread->Shutdown(); + delete test_utils; - return rv; + return test_result; } +