mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
Bug 999704: Implement GMP codec interface to webrtc (not enabled yet) r=joshmoz,ehugg,jesup
This commit is contained in:
parent
a85d1d1f3c
commit
11db644e91
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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',
|
||||
|
18
media/webrtc/signaling/src/media-conduit/GmpVideoCodec.cpp
Normal file
18
media/webrtc/signaling/src/media-conduit/GmpVideoCodec.cpp
Normal 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());
|
||||
}
|
||||
|
||||
}
|
19
media/webrtc/signaling/src/media-conduit/GmpVideoCodec.h
Normal file
19
media/webrtc/signaling/src/media-conduit/GmpVideoCodec.h
Normal 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
|
@ -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
|
||||
|
601
media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp
Normal file
601
media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
150
media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h
Normal file
150
media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h
Normal file
@ -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 <sys/time.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
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user