blob: 4583a0bb67be317c65a46409fb1e4c7ab21e4b9b [file] [log] [blame]
/*
* Copyright (c) 2011 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 "../../../../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"
#include "vp8_simulcast.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::Version(WebRtc_Word8* version,
WebRtc_UWord32& remainingBufferInBytes,
WebRtc_UWord32& position) const
{
VCMGenericEncoder* encoder = NULL;
VideoCodec settings;
WebRtc_Word32 ret;
for (int i = 0; i < VCMCodecDataBase::NumberOfCodecs(); i++)
{
ret = VCMCodecDataBase::Codec(i, &settings);
if (ret < 0)
{
return ret;
}
encoder = CreateEncoder(settings.codecType, false);
if (encoder == NULL)
{
return VCM_MEMORY;
}
ret = encoder->_encoder.Version(&version[position], remainingBufferInBytes);
if (ret < 0)
{
return ret;
}
remainingBufferInBytes -= ret;
position += ret;
delete &encoder->_encoder;
delete encoder;
}
return VCM_OK;
}
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 bool simulcast) const {
switch(type)
{
#ifdef VIDEOCODEC_VP8
case kVideoCodecVP8:
if (simulcast) {
return new VCMGenericEncoder(*(new VP8SimulcastEncoder));
} else {
return new VCMGenericEncoder(*(new VP8Encoder));
}
#endif
#ifdef VIDEOCODEC_I420
case kVideoCodecI420:
if (!simulcast) {
return new VCMGenericEncoder(*(new I420Encoder));
}
return NULL;
#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", 3);
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.numberOfTemporalLayers = 1;
break;
}
#endif
#ifdef VIDEOCODEC_I420
case VCM_I420_IDX:
{
strncpy(settings->plName, "I420", 4);
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 (strcmp(sendCodec->plName, "H263") == 0 &&
(sendCodec->plType != 34))
{
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
{
bool simulcast = false;
if (settings->numberOfSimulcastStreams > 1)
{
simulcast = true;
}
_ptrEncoder = CreateEncoder(settings->codecType, simulcast);
_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.Insert(receiveCodec->plType,
new VCMDecoderMapItem(newReceiveCodec, numberOfCores, requireKeyFrame));
return VCM_OK;
}
WebRtc_Word32 VCMCodecDataBase::DeRegisterReceiveCodec(WebRtc_UWord8 payloadType)
{
MapItem* item = _decMap.Find(payloadType);
if (item == NULL)
{
return VCM_PARAMETER_ERROR;
}
VCMDecoderMapItem* decItem = static_cast<VCMDecoderMapItem*>(item->GetItem());
delete decItem->_settings;
delete decItem;
_decMap.Erase(item);
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));
MapItem* item = _decMap.First();
while (item != NULL)
{
VCMDecoderMapItem* decItem = static_cast<VCMDecoderMapItem*>(item->GetItem());
if (decItem != NULL)
{
if (decItem->_settings != NULL)
{
delete decItem->_settings;
}
delete decItem;
}
_decMap.Erase(item);
item = _decMap.First();
}
item = _decExternalMap.First();
while (item != NULL)
{
VCMExtDecoderMapItem* decItem = static_cast<VCMExtDecoderMapItem*>(item->GetItem());
if (decItem != NULL)
{
delete decItem;
}
_decExternalMap.Erase(item);
item = _decExternalMap.First();
}
_currentDecIsExternal = false;
return VCM_OK;
}
WebRtc_Word32
VCMCodecDataBase::DeRegisterExternalDecoder(WebRtc_UWord8 payloadType)
{
MapItem* item = _decExternalMap.Find(payloadType);
if (item == NULL)
{
// 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);
VCMExtDecoderMapItem* decItem = static_cast<VCMExtDecoderMapItem*>(item->GetItem());
delete decItem;
_decExternalMap.Erase(item);
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.Insert(payloadType, extDecoder);
return VCM_OK;
}
bool
VCMCodecDataBase::DecoderRegistered() const
{
return (_decMap.Size() > 0);
}
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;
}
SetCodecConfigParameters(*ptrDecoder, *decoderItem->_settings);
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)
{
ReleaseDecoder(_ptrDecoder);
_ptrDecoder = new VCMGenericDecoder(*decoderCopy, _id, decoder.External());
}
}
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)
{
decoder->Release();
if (!decoder->External() && &decoder->_decoder != NULL)
{
delete &decoder->_decoder;
}
delete decoder;
}
}
WebRtc_Word32
VCMCodecDataBase::SetCodecConfigParameters(WebRtc_UWord8 payloadType,
const WebRtc_UWord8* buffer,
WebRtc_Word32 length)
{
VCMDecoderMapItem* decItem = FindDecoderItem(payloadType);
if (decItem == NULL)
{
return VCM_PARAMETER_ERROR;
}
switch (decItem->_settings->codecType)
{
case kVideoCodecMPEG4:
{
memcpy(decItem->_settings->codecSpecific.MPEG4.configParameters, buffer, length);
decItem->_settings->codecSpecific.MPEG4.configParametersSize =
static_cast<WebRtc_UWord8>(length);
break;
}
default:
// This codec doesn't have codec config parameters
return VCM_GENERAL_ERROR;
}
if (_ptrDecoder != NULL && _receiveCodec.plType == decItem->_settings->plType)
{
return _ptrDecoder->SetCodecConfigParameters(buffer, length);
}
return VCM_OK;
}
VCMDecoderMapItem*
VCMCodecDataBase::FindDecoderItem(WebRtc_UWord8 payloadType) const
{
MapItem* item = _decMap.Find(payloadType);
if (item != NULL)
{
return static_cast<VCMDecoderMapItem*>(item->GetItem());
}
return NULL;
}
VCMExtDecoderMapItem*
VCMCodecDataBase::FindExternalDecoderItem(WebRtc_UWord8 payloadType) const
{
MapItem* item = _decExternalMap.Find(payloadType);
if (item != NULL)
{
return static_cast<VCMExtDecoderMapItem*>(item->GetItem());
}
return NULL;
}
VCMGenericDecoder*
VCMCodecDataBase::CreateDecoder(VideoCodecType type) const
{
switch(type)
{
#ifdef VIDEOCODEC_VP8
case kVideoCodecVP8:
return new VCMGenericDecoder(*(new VP8Decoder), _id);
#endif
#ifdef VIDEOCODEC_I420
case kVideoCodecI420:
return new VCMGenericDecoder(*(new I420Decoder), _id);
#endif
default:
return NULL;
}
}
void
VCMCodecDataBase::SetCodecConfigParameters(VCMGenericDecoder& decoder,
const VideoCodec& settings)
{
switch (settings.codecType)
{
case kVideoCodecMPEG4:
{
if (settings.codecSpecific.MPEG4.configParametersSize > 0)
{
decoder.SetCodecConfigParameters(
settings.codecSpecific.MPEG4.configParameters,
settings.codecSpecific.MPEG4.configParametersSize);
}
break;
}
default:
// No codec config parameters for this codec
return;
}
return;
}
}