Bug 999704: Implement GMP codec interface to webrtc (not enabled yet) r=joshmoz,ehugg,jesup,pkerr

This commit is contained in:
Randell Jesup 2014-06-08 17:25:18 -04:00
parent a566302cbc
commit 2f70ec93bb
9 changed files with 1048 additions and 59 deletions

View File

@ -35,6 +35,7 @@
#define GMP_VIDEO_CODEC_h_
#include <stdint.h>
#include <stddef.h>
enum { kGMPPayloadNameSize = 32};
enum { kGMPMaxSimulcastStreams = 4};
@ -75,15 +76,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 +147,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 +196,7 @@ union GMPCodecSpecificInfoUnion
struct GMPCodecSpecificInfo
{
GMPVideoCodecType mCodecType;
GMPBufferType mBufferType;
GMPCodecSpecificInfoUnion mCodecSpecific;
};

View File

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

View File

@ -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<VideoEncoder*>(new WebrtcGmpVideoEncoder());
}
VideoDecoder* GmpVideoCodec::CreateDecoder() {
return static_cast<VideoDecoder*>(new WebrtcGmpVideoDecoder());
}
}

View File

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

View File

@ -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<TransportInterface> 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() : "<null>");
bool codecFound = false;
MediaConduitErrorCode condError = kMediaConduitNoError;
int error = 0; //webrtc engine errors

View File

@ -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 <iostream>
#include <vector>
#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<webrtc::VideoFrameType>* 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<webrtc::VideoFrameType>* 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<GMPVideoi420Frame*>(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<GMPVideoFrameType> 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<uint8_t*>(data-sizeof(uint32_t));
if (*start_code == 0x00 && *(start_code+1) == 0x00 &&
*(start_code+2) == 0x00 && *(start_code+3) == 0x01) {
*(reinterpret_cast<uint32_t*>(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<uint16_t*>(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<uint32_t*>(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<GMPVideoEncodedFrame*>(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();
}
}

View File

@ -0,0 +1,148 @@
/* 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 <iostream>
#include <queue>
#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<webrtc::VideoFrameType>* 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<webrtc::VideoFrameType>* aFrameTypes);
virtual int32_t SetRates_g(uint32_t aNewBitRate,
uint32_t aFrameRate);
nsCOMPtr<mozIGeckoMediaPluginService> 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<mozIGeckoMediaPluginService> mMPS;
nsIThread* mGMPThread;
GMPVideoDecoder* mGMP;
GMPVideoHost* mHost;
webrtc::DecodedImageCallback* mCallback;
};
}
#endif

View File

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

View File

@ -12,14 +12,22 @@
using namespace std;
#include "mozilla/Scoped.h"
#include "mozilla/SyncRunnable.h"
#include <MediaConduitInterface.h>
#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<nsIThread> gMainThread;
nsCOMPtr<nsIThread> 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<mozilla::VideoSessionConduit> 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<mozilla::VideoSessionConduit> mSession;
mozilla::ScopedDeletePtr<uint8_t> 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<int16_t> audioInput(new int16_t [PLAYOUT_SAMPLE_LENGTH]);
mozilla::ScopedDeletePtr<int16_t> 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<mozilla::VideoSessionConduit> mVideoSession;
mozilla::RefPtr<mozilla::VideoSessionConduit> 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<mozilla::AudioSessionConduit> mAudioSession;
mozilla::RefPtr<mozilla::AudioSessionConduit> mAudioSession2;
@ -875,6 +966,9 @@ private:
mozilla::RefPtr<mozilla::TransportInterface> 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;
}