blob: 6f818953d2389e8ecc2c6d8e88d2eb9a15a75c56 [file] [log] [blame]
/*
* 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.
*/
#include "codec_database.h"
#include <assert.h>
#include "../../../../engine_configurations.h"
#include "internal_defines.h"
#include "trace.h"
#if defined(_WIN32)
// VS 2005: Don't warn for default initialized arrays. See help for more info.
// Don't warn for strncpy being unsecure.
// switch statement contains 'default' but no 'case' labels
#pragma warning(disable:4351; disable:4996; disable:4065)
#endif
// Supported codecs
#ifdef VIDEOCODEC_VP8
#include "vp8.h"
#endif
#ifdef VIDEOCODEC_I420
#include "i420.h"
#endif
namespace webrtc
{
VCMDecoderMapItem::VCMDecoderMapItem(VideoCodec* settings,
WebRtc_UWord32 numberOfCores,
bool requireKeyFrame)
:
_settings(settings),
_numberOfCores(numberOfCores),
_requireKeyFrame(requireKeyFrame)
{
}
VCMExtDecoderMapItem::VCMExtDecoderMapItem(VideoDecoder* externalDecoderInstance,
WebRtc_UWord8 payloadType,
bool internalRenderTiming)
:
_payloadType(payloadType),
_externalDecoderInstance(externalDecoderInstance),
_internalRenderTiming(internalRenderTiming)
{
}
VCMCodecDataBase::VCMCodecDataBase(WebRtc_Word32 id):
_id(id),
_numberOfCores(0),
_maxPayloadSize(kDefaultPayloadSize),
_periodicKeyFrames(false),
_currentEncIsExternal(false),
_sendCodec(),
_receiveCodec(),
_externalPayloadType(0),
_externalEncoder(NULL),
_internalSource(false),
_ptrEncoder(NULL),
_ptrDecoder(NULL),
_currentDecIsExternal(false),
_decMap(),
_decExternalMap()
{
//
}
VCMCodecDataBase::~VCMCodecDataBase()
{
Reset();
}
WebRtc_Word32
VCMCodecDataBase::Reset()
{
WebRtc_Word32 ret = ResetReceiver();
if (ret < 0)
{
return ret;
}
ret = ResetSender();
if (ret < 0)
{
return ret;
}
return VCM_OK;
}
WebRtc_Word32
VCMCodecDataBase::ResetSender()
{
DeleteEncoder();
_periodicKeyFrames = false;
return VCM_OK;
}
VCMGenericEncoder* VCMCodecDataBase::CreateEncoder(
const VideoCodecType type) const {
switch(type)
{
#ifdef VIDEOCODEC_VP8
case kVideoCodecVP8:
return new VCMGenericEncoder(*(VP8Encoder::Create()));
#endif
#ifdef VIDEOCODEC_I420
case kVideoCodecI420:
return new VCMGenericEncoder(*(new I420Encoder));
#endif
default:
return NULL;
}
}
void
VCMCodecDataBase::DeleteEncoder()
{
if (_ptrEncoder)
{
_ptrEncoder->Release();
if (!_currentEncIsExternal)
{
delete &_ptrEncoder->_encoder;
}
delete _ptrEncoder;
_ptrEncoder = NULL;
}
}
WebRtc_UWord8
VCMCodecDataBase::NumberOfCodecs()
{
return VCM_NUM_VIDEO_CODECS_AVAILABLE;
}
WebRtc_Word32
VCMCodecDataBase::Codec(WebRtc_UWord8 listId, VideoCodec *settings)
{
if (settings == NULL)
{
return VCM_PARAMETER_ERROR;
}
if (listId >= VCM_NUM_VIDEO_CODECS_AVAILABLE)
{
return VCM_PARAMETER_ERROR;
}
memset(settings, 0, sizeof(VideoCodec));
switch (listId)
{
#ifdef VIDEOCODEC_VP8
case VCM_VP8_IDX:
{
strncpy(settings->plName, "VP8", 4);
settings->codecType = kVideoCodecVP8;
// 96 to 127 dynamic payload types for video codecs
settings->plType = VCM_VP8_PAYLOAD_TYPE;
settings->startBitrate = 100;
settings->minBitrate = VCM_MIN_BITRATE;
settings->maxBitrate = 0;
settings->maxFramerate = VCM_DEFAULT_FRAME_RATE;
settings->width = VCM_DEFAULT_CODEC_WIDTH;
settings->height = VCM_DEFAULT_CODEC_HEIGHT;
settings->numberOfSimulcastStreams = 0;
settings->codecSpecific.VP8.resilience = kResilientStream;
settings->codecSpecific.VP8.numberOfTemporalLayers = 1;
break;
}
#endif
#ifdef VIDEOCODEC_I420
case VCM_I420_IDX:
{
strncpy(settings->plName, "I420", 5);
settings->codecType = kVideoCodecI420;
// 96 to 127 dynamic payload types for video codecs
settings->plType = VCM_I420_PAYLOAD_TYPE;
// Bitrate needed for this size and framerate
settings->startBitrate = 3*VCM_DEFAULT_CODEC_WIDTH*
VCM_DEFAULT_CODEC_HEIGHT*8*
VCM_DEFAULT_FRAME_RATE/1000/2;
settings->maxBitrate = settings->startBitrate;
settings->maxFramerate = VCM_DEFAULT_FRAME_RATE;
settings->width = VCM_DEFAULT_CODEC_WIDTH;
settings->height = VCM_DEFAULT_CODEC_HEIGHT;
settings->minBitrate = VCM_MIN_BITRATE;
settings->numberOfSimulcastStreams = 0;
break;
}
#endif
default:
{
return VCM_PARAMETER_ERROR;
}
}
return VCM_OK;
}
WebRtc_Word32
VCMCodecDataBase::Codec(VideoCodecType codecType, VideoCodec* settings)
{
for (int i = 0; i < VCMCodecDataBase::NumberOfCodecs(); i++)
{
const WebRtc_Word32 ret = VCMCodecDataBase::Codec(i, settings);
if (ret != VCM_OK)
{
return ret;
}
if (codecType == settings->codecType)
{
return VCM_OK;
}
}
return VCM_PARAMETER_ERROR;
}
// assuming only one registered encoder - since only one used, no need for more
WebRtc_Word32
VCMCodecDataBase::RegisterSendCodec(const VideoCodec* sendCodec,
WebRtc_UWord32 numberOfCores,
WebRtc_UWord32 maxPayloadSize)
{
if (sendCodec == NULL)
{
return VCM_UNINITIALIZED;
}
if (maxPayloadSize == 0)
{
maxPayloadSize = kDefaultPayloadSize;
}
if (numberOfCores > 32)
{
return VCM_PARAMETER_ERROR;
}
if (sendCodec->plType <= 0)
{
return VCM_PARAMETER_ERROR;
}
// Make sure the start bit rate is sane...
if (sendCodec->startBitrate > 1000000)
{
return VCM_PARAMETER_ERROR;
}
if (sendCodec->codecType == kVideoCodecUnknown)
{
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
}
_numberOfCores = numberOfCores;
_maxPayloadSize = maxPayloadSize;
memcpy(&_sendCodec, sendCodec, sizeof(VideoCodec));
if (_sendCodec.maxBitrate == 0)
{
// max is one bit per pixel
_sendCodec.maxBitrate = ((WebRtc_Word32)_sendCodec.height *
(WebRtc_Word32)_sendCodec.width *
(WebRtc_Word32)_sendCodec.maxFramerate) / 1000;
if (_sendCodec.startBitrate > _sendCodec.maxBitrate)
{
// but if the customer tries to set a higher start bit rate we will increase
// the max accordingly
_sendCodec.maxBitrate = _sendCodec.startBitrate;
}
}
return VCM_OK;
}
WebRtc_Word32
VCMCodecDataBase::SendCodec(VideoCodec* currentSendCodec) const
{
WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, VCMId(_id), "SendCodec");
if(_ptrEncoder == NULL)
{
return VCM_UNINITIALIZED;
}
memcpy(currentSendCodec, &_sendCodec, sizeof(VideoCodec));
return VCM_OK;
}
VideoCodecType
VCMCodecDataBase::SendCodec() const
{
WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, VCMId(_id),
"SendCodec type");
if (_ptrEncoder == NULL)
{
return kVideoCodecUnknown;
}
return _sendCodec.codecType;
}
WebRtc_Word32
VCMCodecDataBase::DeRegisterExternalEncoder(WebRtc_UWord8 payloadType, bool& wasSendCodec)
{
wasSendCodec = false;
if (_externalPayloadType != payloadType)
{
return VCM_PARAMETER_ERROR;
}
if (_sendCodec.plType == payloadType)
{
//De-register as send codec if needed
DeleteEncoder();
memset(&_sendCodec, 0, sizeof(VideoCodec));
_currentEncIsExternal = false;
wasSendCodec = true;
}
_externalPayloadType = 0;
_externalEncoder = NULL;
_internalSource = false;
return VCM_OK;
}
WebRtc_Word32
VCMCodecDataBase::RegisterExternalEncoder(VideoEncoder* externalEncoder,
WebRtc_UWord8 payloadType,
bool internalSource)
{
// since only one encoder can be used at a given time,
// only one external encoder can be registered/used
_externalEncoder = externalEncoder;
_externalPayloadType = payloadType;
_internalSource = internalSource;
return VCM_OK;
}
VCMGenericEncoder*
VCMCodecDataBase::SetEncoder(const VideoCodec* settings,
VCMEncodedFrameCallback* VCMencodedFrameCallback)
{
// if encoder exists, will destroy it and create new one
DeleteEncoder();
if (settings->plType == _externalPayloadType)
{
// External encoder
_ptrEncoder = new VCMGenericEncoder(*_externalEncoder, _internalSource);
_currentEncIsExternal = true;
}
else
{
_ptrEncoder = CreateEncoder(settings->codecType);
_currentEncIsExternal = false;
}
VCMencodedFrameCallback->SetPayloadType(settings->plType);
if (_ptrEncoder == NULL)
{
WEBRTC_TRACE(webrtc::kTraceError,
webrtc::kTraceVideoCoding,
VCMId(_id),
"Failed to create encoder: %s.",
settings->plName);
return NULL;
}
if (_ptrEncoder->InitEncode(settings, _numberOfCores, _maxPayloadSize) < 0)
{
WEBRTC_TRACE(webrtc::kTraceError,
webrtc::kTraceVideoCoding,
VCMId(_id),
"Failed to initialize encoder: %s.",
settings->plName);
DeleteEncoder();
return NULL;
}
else if (_ptrEncoder->RegisterEncodeCallback(VCMencodedFrameCallback) < 0)
{
DeleteEncoder();
return NULL;
}
// Intentionally don't check return value since the encoder registration
// shouldn't fail because the codec doesn't support changing the
// periodic key frame setting.
_ptrEncoder->SetPeriodicKeyFrames(_periodicKeyFrames);
return _ptrEncoder;
}
WebRtc_Word32
VCMCodecDataBase::SetPeriodicKeyFrames(bool enable)
{
_periodicKeyFrames = enable;
if (_ptrEncoder != NULL)
{
return _ptrEncoder->SetPeriodicKeyFrames(_periodicKeyFrames);
}
return VCM_OK;
}
WebRtc_Word32
VCMCodecDataBase::RegisterReceiveCodec(const VideoCodec* receiveCodec,
WebRtc_UWord32 numberOfCores,
bool requireKeyFrame)
{
WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCoding, VCMId(_id),
"Codec: %s, Payload type %d, Height %d, Width %d, Bitrate %d, Framerate %d.",
receiveCodec->plName, receiveCodec->plType,
receiveCodec->height, receiveCodec->width,
receiveCodec->startBitrate, receiveCodec->maxFramerate);
// check if payload value already exists, if so - erase old and insert new
DeRegisterReceiveCodec(receiveCodec->plType);
if (receiveCodec->codecType == kVideoCodecUnknown)
{
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
}
VideoCodec* newReceiveCodec = new VideoCodec(*receiveCodec);
_decMap[receiveCodec->plType] =
new VCMDecoderMapItem(newReceiveCodec, numberOfCores, requireKeyFrame);
return VCM_OK;
}
WebRtc_Word32 VCMCodecDataBase::DeRegisterReceiveCodec(
WebRtc_UWord8 payloadType)
{
DecoderMap::iterator it = _decMap.find(payloadType);
if (it == _decMap.end())
{
return VCM_PARAMETER_ERROR;
}
VCMDecoderMapItem* decItem = (*it).second;
delete decItem->_settings;
delete decItem;
_decMap.erase(it);
if (_receiveCodec.plType == payloadType)
{
// This codec is currently in use.
memset(&_receiveCodec, 0, sizeof(VideoCodec));
_currentDecIsExternal = false;
}
return VCM_OK;
}
WebRtc_Word32
VCMCodecDataBase::ResetReceiver()
{
ReleaseDecoder(_ptrDecoder);
_ptrDecoder = NULL;
memset(&_receiveCodec, 0, sizeof(VideoCodec));
DecoderMap::iterator it = _decMap.begin();
while (it != _decMap.end()) {
if ((*it).second->_settings != NULL)
{
delete (*it).second->_settings;
}
delete (*it).second;
_decMap.erase(it);
it = _decMap.begin();
}
ExternalDecoderMap::iterator exterit = _decExternalMap.begin();
while (exterit != _decExternalMap.end()) {
delete (*exterit).second;
_decExternalMap.erase(exterit);
exterit = _decExternalMap.begin();
}
_currentDecIsExternal = false;
return VCM_OK;
}
WebRtc_Word32
VCMCodecDataBase::DeRegisterExternalDecoder(WebRtc_UWord8 payloadType)
{
ExternalDecoderMap::iterator it = _decExternalMap.find(payloadType);
if (it == _decExternalMap.end())
{
// Not found
return VCM_PARAMETER_ERROR;
}
if (_receiveCodec.plType == payloadType)
{
// Release it if it was registered and in use
ReleaseDecoder(_ptrDecoder);
_ptrDecoder = NULL;
}
DeRegisterReceiveCodec(payloadType);
delete (*it).second;
_decExternalMap.erase(it);
return VCM_OK;
}
// Add the external encoder object to the list of external decoders.
// Won't be registered as a receive codec until RegisterReceiveCodec is called.
WebRtc_Word32
VCMCodecDataBase::RegisterExternalDecoder(VideoDecoder* externalDecoder,
WebRtc_UWord8 payloadType,
bool internalRenderTiming)
{
// check if payload value already exists, if so - erase old and insert new
VCMExtDecoderMapItem* extDecoder = new VCMExtDecoderMapItem(externalDecoder,
payloadType,
internalRenderTiming);
if (extDecoder == NULL)
{
return VCM_MEMORY;
}
DeRegisterExternalDecoder(payloadType);
_decExternalMap[payloadType] = extDecoder;
return VCM_OK;
}
bool
VCMCodecDataBase::DecoderRegistered() const
{
return !_decMap.empty();
}
WebRtc_Word32
VCMCodecDataBase::ReceiveCodec(VideoCodec* currentReceiveCodec) const
{
if (_ptrDecoder == NULL)
{
return VCM_NO_FRAME_DECODED;
}
memcpy(currentReceiveCodec, &_receiveCodec, sizeof(VideoCodec));
return VCM_OK;
}
VideoCodecType
VCMCodecDataBase::ReceiveCodec() const
{
if (_ptrDecoder == NULL)
{
return kVideoCodecUnknown;
}
return _receiveCodec.codecType;
}
VCMGenericDecoder*
VCMCodecDataBase::SetDecoder(WebRtc_UWord8 payloadType,
VCMDecodedFrameCallback& callback)
{
if (payloadType == _receiveCodec.plType || payloadType == 0)
{
return _ptrDecoder;
}
// check for exisitng decoder, if exists - delete
if (_ptrDecoder)
{
ReleaseDecoder(_ptrDecoder);
_ptrDecoder = NULL;
memset(&_receiveCodec, 0, sizeof(VideoCodec));
}
_ptrDecoder = CreateAndInitDecoder(payloadType, _receiveCodec,
_currentDecIsExternal);
if (_ptrDecoder == NULL)
{
return NULL;
}
if (_ptrDecoder->RegisterDecodeCompleteCallback(&callback) < 0)
{
ReleaseDecoder(_ptrDecoder);
_ptrDecoder = NULL;
memset(&_receiveCodec, 0, sizeof(VideoCodec));
return NULL;
}
return _ptrDecoder;
}
VCMGenericDecoder*
VCMCodecDataBase::CreateAndInitDecoder(WebRtc_UWord8 payloadType,
VideoCodec& newCodec,
bool &external) const
{
VCMDecoderMapItem* decoderItem = FindDecoderItem(payloadType);
if (decoderItem == NULL)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id),
"Unknown payload type: %u", payloadType);
return NULL;
}
VCMGenericDecoder* ptrDecoder = NULL;
VCMExtDecoderMapItem* externalDecItem = FindExternalDecoderItem(
payloadType);
if (externalDecItem != NULL)
{
// External codec
ptrDecoder = new VCMGenericDecoder(
*externalDecItem->_externalDecoderInstance,
_id,
true);
external = true;
}
else
{
// create decoder
ptrDecoder = CreateDecoder(decoderItem->_settings->codecType);
external = false;
}
if (ptrDecoder == NULL)
{
return NULL;
}
if (ptrDecoder->InitDecode(decoderItem->_settings,
decoderItem->_numberOfCores,
decoderItem->_requireKeyFrame) < 0)
{
ReleaseDecoder(ptrDecoder);
return NULL;
}
memcpy(&newCodec, decoderItem->_settings, sizeof(VideoCodec));
return ptrDecoder;
}
VCMGenericDecoder*
VCMCodecDataBase::CreateDecoderCopy() const
{
if (_ptrDecoder == NULL)
{
return NULL;
}
VideoDecoder* decoderCopy = _ptrDecoder->_decoder.Copy();
if (decoderCopy == NULL)
{
return NULL;
}
return new VCMGenericDecoder(*decoderCopy, _id, _ptrDecoder->External());
}
void
VCMCodecDataBase::CopyDecoder(const VCMGenericDecoder& decoder)
{
VideoDecoder* decoderCopy = decoder._decoder.Copy();
if (decoderCopy != NULL)
{
VCMDecodedFrameCallback* cb = _ptrDecoder->_callback;
ReleaseDecoder(_ptrDecoder);
_ptrDecoder = new VCMGenericDecoder(*decoderCopy, _id,
decoder.External());
if (cb && _ptrDecoder->RegisterDecodeCompleteCallback(cb))
{
assert(false);
}
}
}
bool
VCMCodecDataBase::RenderTiming() const
{
bool renderTiming = true;
if (_currentDecIsExternal)
{
VCMExtDecoderMapItem* extItem = FindExternalDecoderItem(_receiveCodec.plType);
renderTiming = extItem->_internalRenderTiming;
}
return renderTiming;
}
void
VCMCodecDataBase::ReleaseDecoder(VCMGenericDecoder* decoder) const
{
if (decoder != NULL)
{
assert(&decoder->_decoder != NULL);
decoder->Release();
if (!decoder->External())
{
delete &decoder->_decoder;
}
delete decoder;
}
}
VCMDecoderMapItem*
VCMCodecDataBase::FindDecoderItem(WebRtc_UWord8 payloadType) const
{
DecoderMap::const_iterator it = _decMap.find(payloadType);
if (it != _decMap.end())
{
return (*it).second;
}
return NULL;
}
VCMExtDecoderMapItem*
VCMCodecDataBase::FindExternalDecoderItem(WebRtc_UWord8 payloadType) const
{
ExternalDecoderMap::const_iterator it = _decExternalMap.find(payloadType);
if (it != _decExternalMap.end())
{
return (*it).second;
}
return NULL;
}
VCMGenericDecoder*
VCMCodecDataBase::CreateDecoder(VideoCodecType type) const
{
switch(type)
{
#ifdef VIDEOCODEC_VP8
case kVideoCodecVP8:
return new VCMGenericDecoder(*(VP8Decoder::Create()), _id);
#endif
#ifdef VIDEOCODEC_I420
case kVideoCodecI420:
return new VCMGenericDecoder(*(new I420Decoder), _id);
#endif
default:
return NULL;
}
}
}