Wire up external encoders.
R=pthatcher@webrtc.org BUG=1788 Review URL: https://webrtc-codereview.appspot.com/30649005 git-svn-id: http://webrtc.googlecode.com/svn/trunk@7440 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
f68cc0b0c3
commit
7fe1e03dd6
@ -33,6 +33,7 @@ extern const int kVideoMtu;
|
||||
extern const int kVideoRtpBufferSize;
|
||||
|
||||
extern const char kVp8CodecName[];
|
||||
extern const char kH264CodecName[];
|
||||
|
||||
extern const int kDefaultFramerate;
|
||||
extern const int kMinVideoBitrate;
|
||||
|
@ -38,8 +38,10 @@
|
||||
#include "talk/media/webrtc/webrtcvideoencoderfactory.h"
|
||||
#include "talk/media/webrtc/webrtcvie.h"
|
||||
#include "webrtc/base/basictypes.h"
|
||||
#include "webrtc/base/criticalsection.h"
|
||||
#include "webrtc/base/gunit.h"
|
||||
#include "webrtc/base/stringutils.h"
|
||||
#include "webrtc/base/thread_annotations.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
@ -146,7 +148,7 @@ class FakeWebRtcVideoDecoderFactory : public WebRtcVideoDecoderFactory {
|
||||
// Fake class for mocking out webrtc::VideoEnoder
|
||||
class FakeWebRtcVideoEncoder : public webrtc::VideoEncoder {
|
||||
public:
|
||||
FakeWebRtcVideoEncoder() {}
|
||||
FakeWebRtcVideoEncoder() : num_frames_encoded_(0) {}
|
||||
|
||||
virtual int32 InitEncode(const webrtc::VideoCodec* codecSettings,
|
||||
int32 numberOfCores,
|
||||
@ -158,6 +160,8 @@ class FakeWebRtcVideoEncoder : public webrtc::VideoEncoder {
|
||||
const webrtc::I420VideoFrame& inputImage,
|
||||
const webrtc::CodecSpecificInfo* codecSpecificInfo,
|
||||
const std::vector<webrtc::VideoFrameType>* frame_types) {
|
||||
rtc::CritScope lock(&crit_);
|
||||
++num_frames_encoded_;
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
@ -179,6 +183,15 @@ class FakeWebRtcVideoEncoder : public webrtc::VideoEncoder {
|
||||
uint32 frameRate) {
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
int GetNumEncodedFrames() {
|
||||
rtc::CritScope lock(&crit_);
|
||||
return num_frames_encoded_;
|
||||
}
|
||||
|
||||
private:
|
||||
rtc::CriticalSection crit_;
|
||||
int num_frames_encoded_ GUARDED_BY(crit_);
|
||||
};
|
||||
|
||||
// Fake class for mocking out WebRtcVideoEncoderFactory.
|
||||
|
@ -49,6 +49,65 @@
|
||||
ASSERT(false)
|
||||
|
||||
namespace cricket {
|
||||
namespace {
|
||||
|
||||
static bool CodecNameMatches(const std::string& name1,
|
||||
const std::string& name2) {
|
||||
return _stricmp(name1.c_str(), name2.c_str()) == 0;
|
||||
}
|
||||
|
||||
// True if codec is supported by a software implementation that's always
|
||||
// available.
|
||||
static bool CodecIsInternallySupported(const std::string& codec_name) {
|
||||
return CodecNameMatches(codec_name, kVp8CodecName);
|
||||
}
|
||||
|
||||
static std::string CodecVectorToString(const std::vector<VideoCodec>& codecs) {
|
||||
std::stringstream out;
|
||||
out << '{';
|
||||
for (size_t i = 0; i < codecs.size(); ++i) {
|
||||
out << codecs[i].ToString();
|
||||
if (i != codecs.size() - 1) {
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
out << '}';
|
||||
return out.str();
|
||||
}
|
||||
|
||||
static bool ValidateCodecFormats(const std::vector<VideoCodec>& codecs) {
|
||||
bool has_video = false;
|
||||
for (size_t i = 0; i < codecs.size(); ++i) {
|
||||
if (!codecs[i].ValidateCodecFormat()) {
|
||||
return false;
|
||||
}
|
||||
if (codecs[i].GetCodecType() == VideoCodec::CODEC_VIDEO) {
|
||||
has_video = true;
|
||||
}
|
||||
}
|
||||
if (!has_video) {
|
||||
LOG(LS_ERROR) << "Setting codecs without a video codec is invalid: "
|
||||
<< CodecVectorToString(codecs);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string RtpExtensionsToString(
|
||||
const std::vector<RtpHeaderExtension>& extensions) {
|
||||
std::stringstream out;
|
||||
out << '{';
|
||||
for (size_t i = 0; i < extensions.size(); ++i) {
|
||||
out << "{" << extensions[i].uri << ": " << extensions[i].id << "}";
|
||||
if (i != extensions.size() - 1) {
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
out << '}';
|
||||
return out.str();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// This constant is really an on/off, lower-level configurable NACK history
|
||||
// duration hasn't been implemented.
|
||||
@ -58,6 +117,13 @@ static const int kDefaultQpMax = 56;
|
||||
|
||||
static const int kDefaultRtcpReceiverReportSsrc = 1;
|
||||
|
||||
// External video encoders are given payloads 120-127. This also means that we
|
||||
// only support up to 8 external payload types.
|
||||
static const int kExternalVideoPayloadTypeBase = 120;
|
||||
#ifndef NDEBUG
|
||||
static const size_t kMaxExternalVideoCodecs = 8;
|
||||
#endif
|
||||
|
||||
struct VideoCodecPref {
|
||||
int payload_type;
|
||||
int width;
|
||||
@ -66,6 +132,8 @@ struct VideoCodecPref {
|
||||
int rtx_payload_type;
|
||||
} kDefaultVideoCodecPref = {100, 640, 400, kVp8CodecName, 96};
|
||||
|
||||
const char kH264CodecName[] = "H264";
|
||||
|
||||
VideoCodecPref kRedPref = {116, -1, -1, kRedCodecName, -1};
|
||||
VideoCodecPref kUlpfecPref = {117, -1, -1, kUlpfecCodecName, -1};
|
||||
|
||||
@ -169,7 +237,6 @@ std::vector<webrtc::VideoStream> WebRtcVideoEncoderFactory2::CreateVideoStreams(
|
||||
const VideoCodec& codec,
|
||||
const VideoOptions& options,
|
||||
size_t num_streams) {
|
||||
assert(SupportsCodec(codec));
|
||||
if (num_streams != 1) {
|
||||
LOG(LS_ERROR) << "Unsupported number of streams: " << num_streams;
|
||||
return std::vector<webrtc::VideoStream>();
|
||||
@ -196,24 +263,10 @@ std::vector<webrtc::VideoStream> WebRtcVideoEncoderFactory2::CreateVideoStreams(
|
||||
return streams;
|
||||
}
|
||||
|
||||
webrtc::VideoEncoder* WebRtcVideoEncoderFactory2::CreateVideoEncoder(
|
||||
const VideoCodec& codec,
|
||||
const VideoOptions& options) {
|
||||
assert(SupportsCodec(codec));
|
||||
if (_stricmp(codec.name.c_str(), kVp8CodecName) == 0) {
|
||||
return webrtc::VideoEncoder::Create(webrtc::VideoEncoder::kVp8);
|
||||
}
|
||||
// This shouldn't happen, we should be able to create encoders for all codecs
|
||||
// we support.
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* WebRtcVideoEncoderFactory2::CreateVideoEncoderSettings(
|
||||
const VideoCodec& codec,
|
||||
const VideoOptions& options) {
|
||||
assert(SupportsCodec(codec));
|
||||
if (_stricmp(codec.name.c_str(), kVp8CodecName) == 0) {
|
||||
if (CodecNameMatches(codec.name, kVp8CodecName)) {
|
||||
webrtc::VideoCodecVP8* settings = new webrtc::VideoCodecVP8(
|
||||
webrtc::VideoEncoder::GetDefaultVp8Settings());
|
||||
options.video_noise_reduction.Get(&settings->denoisingOn);
|
||||
@ -225,19 +278,14 @@ void* WebRtcVideoEncoderFactory2::CreateVideoEncoderSettings(
|
||||
void WebRtcVideoEncoderFactory2::DestroyVideoEncoderSettings(
|
||||
const VideoCodec& codec,
|
||||
void* encoder_settings) {
|
||||
assert(SupportsCodec(codec));
|
||||
if (encoder_settings == NULL) {
|
||||
return;
|
||||
}
|
||||
if (_stricmp(codec.name.c_str(), kVp8CodecName) == 0) {
|
||||
if (CodecNameMatches(codec.name, kVp8CodecName)) {
|
||||
delete reinterpret_cast<webrtc::VideoCodecVP8*>(encoder_settings);
|
||||
}
|
||||
}
|
||||
|
||||
bool WebRtcVideoEncoderFactory2::SupportsCodec(const VideoCodec& codec) {
|
||||
return _stricmp(codec.name.c_str(), kVp8CodecName) == 0;
|
||||
}
|
||||
|
||||
DefaultUnsignalledSsrcHandler::DefaultUnsignalledSsrcHandler()
|
||||
: default_recv_ssrc_(0), default_renderer_(NULL) {}
|
||||
|
||||
@ -284,7 +332,6 @@ void DefaultUnsignalledSsrcHandler::SetDefaultRenderer(
|
||||
WebRtcVideoEngine2::WebRtcVideoEngine2()
|
||||
: worker_thread_(NULL),
|
||||
voice_engine_(NULL),
|
||||
video_codecs_(DefaultVideoCodecs()),
|
||||
default_codec_format_(kDefaultVideoCodecPref.width,
|
||||
kDefaultVideoCodecPref.height,
|
||||
FPS_TO_INTERVAL(kDefaultFramerate),
|
||||
@ -295,6 +342,7 @@ WebRtcVideoEngine2::WebRtcVideoEngine2()
|
||||
external_decoder_factory_(NULL),
|
||||
external_encoder_factory_(NULL) {
|
||||
LOG(LS_INFO) << "WebRtcVideoEngine2::WebRtcVideoEngine2()";
|
||||
video_codecs_ = GetSupportedCodecs();
|
||||
rtp_header_extensions_.push_back(
|
||||
RtpHeaderExtension(kRtpTimestampOffsetHeaderExtension,
|
||||
kRtpTimestampOffsetHeaderExtensionDefaultId));
|
||||
@ -312,6 +360,7 @@ WebRtcVideoEngine2::~WebRtcVideoEngine2() {
|
||||
}
|
||||
|
||||
void WebRtcVideoEngine2::SetCallFactory(WebRtcCallFactory* call_factory) {
|
||||
assert(!initialized_);
|
||||
call_factory_ = call_factory;
|
||||
}
|
||||
|
||||
@ -344,7 +393,7 @@ bool WebRtcVideoEngine2::SetDefaultEncoderConfig(
|
||||
const VideoEncoderConfig& config) {
|
||||
const VideoCodec& codec = config.max_codec;
|
||||
// TODO(pbos): Make use of external encoder factory.
|
||||
if (!GetVideoEncoderFactory()->SupportsCodec(codec)) {
|
||||
if (!CodecIsInternallySupported(codec.name)) {
|
||||
LOG(LS_ERROR) << "SetDefaultEncoderConfig, codec not supported:"
|
||||
<< codec.ToString();
|
||||
return false;
|
||||
@ -366,11 +415,16 @@ VideoEncoderConfig WebRtcVideoEngine2::GetDefaultEncoderConfig() const {
|
||||
|
||||
WebRtcVideoChannel2* WebRtcVideoEngine2::CreateChannel(
|
||||
VoiceMediaChannel* voice_channel) {
|
||||
assert(initialized_);
|
||||
LOG(LS_INFO) << "CreateChannel: "
|
||||
<< (voice_channel != NULL ? "With" : "Without")
|
||||
<< " voice channel.";
|
||||
WebRtcVideoChannel2* channel = new WebRtcVideoChannel2(
|
||||
call_factory_, voice_channel, GetVideoEncoderFactory());
|
||||
WebRtcVideoChannel2* channel =
|
||||
new WebRtcVideoChannel2(call_factory_,
|
||||
voice_channel,
|
||||
external_encoder_factory_,
|
||||
external_decoder_factory_,
|
||||
GetVideoEncoderFactory());
|
||||
if (!channel->Init()) {
|
||||
delete channel;
|
||||
return NULL;
|
||||
@ -400,12 +454,16 @@ void WebRtcVideoEngine2::SetLogging(int min_sev, const char* filter) {
|
||||
|
||||
void WebRtcVideoEngine2::SetExternalDecoderFactory(
|
||||
WebRtcVideoDecoderFactory* decoder_factory) {
|
||||
assert(!initialized_);
|
||||
external_decoder_factory_ = decoder_factory;
|
||||
}
|
||||
|
||||
void WebRtcVideoEngine2::SetExternalEncoderFactory(
|
||||
WebRtcVideoEncoderFactory* encoder_factory) {
|
||||
assert(!initialized_);
|
||||
external_encoder_factory_ = encoder_factory;
|
||||
|
||||
video_codecs_ = GetSupportedCodecs();
|
||||
}
|
||||
|
||||
bool WebRtcVideoEngine2::EnableTimedRender() {
|
||||
@ -495,6 +553,35 @@ WebRtcVideoEncoderFactory2* WebRtcVideoEngine2::GetVideoEncoderFactory() {
|
||||
return &default_video_encoder_factory_;
|
||||
}
|
||||
|
||||
std::vector<VideoCodec> WebRtcVideoEngine2::GetSupportedCodecs() const {
|
||||
std::vector<VideoCodec> supported_codecs = DefaultVideoCodecs();
|
||||
|
||||
if (external_encoder_factory_ == NULL) {
|
||||
return supported_codecs;
|
||||
}
|
||||
|
||||
assert(external_encoder_factory_->codecs().size() <= kMaxExternalVideoCodecs);
|
||||
const std::vector<WebRtcVideoEncoderFactory::VideoCodec>& codecs =
|
||||
external_encoder_factory_->codecs();
|
||||
for (size_t i = 0; i < codecs.size(); ++i) {
|
||||
// Don't add internally-supported codecs twice.
|
||||
if (CodecIsInternallySupported(codecs[i].name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
VideoCodec codec(kExternalVideoPayloadTypeBase + static_cast<int>(i),
|
||||
codecs[i].name,
|
||||
codecs[i].max_width,
|
||||
codecs[i].max_height,
|
||||
codecs[i].max_fps,
|
||||
0);
|
||||
|
||||
AddDefaultFeedbackParams(&codec);
|
||||
supported_codecs.push_back(codec);
|
||||
}
|
||||
return supported_codecs;
|
||||
}
|
||||
|
||||
// Thin map between VideoFrame and an existing webrtc::I420VideoFrame
|
||||
// to avoid having to copy the rendered VideoFrame prematurely.
|
||||
// This implementation is only safe to use in a const context and should never
|
||||
@ -656,8 +743,12 @@ class WebRtcVideoRenderFrame : public VideoFrame {
|
||||
WebRtcVideoChannel2::WebRtcVideoChannel2(
|
||||
WebRtcCallFactory* call_factory,
|
||||
VoiceMediaChannel* voice_channel,
|
||||
WebRtcVideoEncoderFactory* external_encoder_factory,
|
||||
WebRtcVideoDecoderFactory* external_decoder_factory,
|
||||
WebRtcVideoEncoderFactory2* encoder_factory)
|
||||
: unsignalled_ssrc_handler_(&default_unsignalled_ssrc_handler_),
|
||||
external_encoder_factory_(external_encoder_factory),
|
||||
external_decoder_factory_(external_decoder_factory),
|
||||
encoder_factory_(encoder_factory) {
|
||||
// TODO(pbos): Connect the video and audio with |voice_channel|.
|
||||
webrtc::Call::Config config(this);
|
||||
@ -696,55 +787,6 @@ WebRtcVideoChannel2::~WebRtcVideoChannel2() {
|
||||
|
||||
bool WebRtcVideoChannel2::Init() { return true; }
|
||||
|
||||
namespace {
|
||||
|
||||
static std::string CodecVectorToString(const std::vector<VideoCodec>& codecs) {
|
||||
std::stringstream out;
|
||||
out << '{';
|
||||
for (size_t i = 0; i < codecs.size(); ++i) {
|
||||
out << codecs[i].ToString();
|
||||
if (i != codecs.size() - 1) {
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
out << '}';
|
||||
return out.str();
|
||||
}
|
||||
|
||||
static bool ValidateCodecFormats(const std::vector<VideoCodec>& codecs) {
|
||||
bool has_video = false;
|
||||
for (size_t i = 0; i < codecs.size(); ++i) {
|
||||
if (!codecs[i].ValidateCodecFormat()) {
|
||||
return false;
|
||||
}
|
||||
if (codecs[i].GetCodecType() == VideoCodec::CODEC_VIDEO) {
|
||||
has_video = true;
|
||||
}
|
||||
}
|
||||
if (!has_video) {
|
||||
LOG(LS_ERROR) << "Setting codecs without a video codec is invalid: "
|
||||
<< CodecVectorToString(codecs);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string RtpExtensionsToString(
|
||||
const std::vector<RtpHeaderExtension>& extensions) {
|
||||
std::stringstream out;
|
||||
out << '{';
|
||||
for (size_t i = 0; i < extensions.size(); ++i) {
|
||||
out << "{" << extensions[i].uri << ": " << extensions[i].id << "}";
|
||||
if (i != extensions.size() - 1) {
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
out << '}';
|
||||
return out.str();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool WebRtcVideoChannel2::SetRecvCodecs(const std::vector<VideoCodec>& codecs) {
|
||||
LOG(LS_INFO) << "SetRecvCodecs: " << CodecVectorToString(codecs);
|
||||
if (!ValidateCodecFormats(codecs)) {
|
||||
@ -760,7 +802,7 @@ bool WebRtcVideoChannel2::SetRecvCodecs(const std::vector<VideoCodec>& codecs) {
|
||||
// TODO(pbos): Add a decoder factory which controls supported codecs.
|
||||
// Blocked on webrtc:2854.
|
||||
for (size_t i = 0; i < mapped_codecs.size(); ++i) {
|
||||
if (_stricmp(mapped_codecs[i].codec.name.c_str(), kVp8CodecName) != 0) {
|
||||
if (!CodecNameMatches(mapped_codecs[i].codec.name, kVp8CodecName)) {
|
||||
LOG(LS_ERROR) << "SetRecvCodecs called with unsupported codec: '"
|
||||
<< mapped_codecs[i].codec.name << "'";
|
||||
return false;
|
||||
@ -881,6 +923,7 @@ bool WebRtcVideoChannel2::AddSendStream(const StreamParams& sp) {
|
||||
|
||||
WebRtcVideoSendStream* stream =
|
||||
new WebRtcVideoSendStream(call_.get(),
|
||||
external_encoder_factory_,
|
||||
encoder_factory_,
|
||||
options_,
|
||||
send_codec_,
|
||||
@ -1307,15 +1350,18 @@ WebRtcVideoChannel2::WebRtcVideoSendStream::VideoSendStreamParameters::
|
||||
|
||||
WebRtcVideoChannel2::WebRtcVideoSendStream::WebRtcVideoSendStream(
|
||||
webrtc::Call* call,
|
||||
WebRtcVideoEncoderFactory* external_encoder_factory,
|
||||
WebRtcVideoEncoderFactory2* encoder_factory,
|
||||
const VideoOptions& options,
|
||||
const Settable<VideoCodecSettings>& codec_settings,
|
||||
const StreamParams& sp,
|
||||
const std::vector<webrtc::RtpExtension>& rtp_extensions)
|
||||
: call_(call),
|
||||
external_encoder_factory_(external_encoder_factory),
|
||||
encoder_factory_(encoder_factory),
|
||||
stream_(NULL),
|
||||
parameters_(webrtc::VideoSendStream::Config(), options, codec_settings),
|
||||
allocated_encoder_(NULL, webrtc::kVideoCodecUnknown, false),
|
||||
capturer_(NULL),
|
||||
sending_(false),
|
||||
muted_(false) {
|
||||
@ -1338,7 +1384,7 @@ WebRtcVideoChannel2::WebRtcVideoSendStream::~WebRtcVideoSendStream() {
|
||||
if (stream_ != NULL) {
|
||||
call_->DestroyVideoSendStream(stream_);
|
||||
}
|
||||
delete parameters_.config.encoder_settings.encoder;
|
||||
DestroyVideoEncoder(&allocated_encoder_);
|
||||
}
|
||||
|
||||
static void SetWebRtcFrameToBlack(webrtc::I420VideoFrame* video_frame) {
|
||||
@ -1505,11 +1551,60 @@ void WebRtcVideoChannel2::WebRtcVideoSendStream::SetOptions(
|
||||
parameters_.options = options;
|
||||
}
|
||||
}
|
||||
|
||||
void WebRtcVideoChannel2::WebRtcVideoSendStream::SetCodec(
|
||||
const VideoCodecSettings& codec_settings) {
|
||||
rtc::CritScope cs(&lock_);
|
||||
SetCodecAndOptions(codec_settings, parameters_.options);
|
||||
}
|
||||
|
||||
webrtc::VideoCodecType CodecTypeFromName(const std::string& name) {
|
||||
if (CodecNameMatches(name, kVp8CodecName)) {
|
||||
return webrtc::kVideoCodecVP8;
|
||||
} else if (CodecNameMatches(name, kH264CodecName)) {
|
||||
return webrtc::kVideoCodecH264;
|
||||
}
|
||||
return webrtc::kVideoCodecUnknown;
|
||||
}
|
||||
|
||||
WebRtcVideoChannel2::WebRtcVideoSendStream::AllocatedEncoder
|
||||
WebRtcVideoChannel2::WebRtcVideoSendStream::CreateVideoEncoder(
|
||||
const VideoCodec& codec) {
|
||||
webrtc::VideoCodecType type = CodecTypeFromName(codec.name);
|
||||
|
||||
// Do not re-create encoders of the same type.
|
||||
if (type == allocated_encoder_.type && allocated_encoder_.encoder != NULL) {
|
||||
return allocated_encoder_;
|
||||
}
|
||||
|
||||
if (external_encoder_factory_ != NULL) {
|
||||
webrtc::VideoEncoder* encoder =
|
||||
external_encoder_factory_->CreateVideoEncoder(type);
|
||||
if (encoder != NULL) {
|
||||
return AllocatedEncoder(encoder, type, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (type == webrtc::kVideoCodecVP8) {
|
||||
return AllocatedEncoder(
|
||||
webrtc::VideoEncoder::Create(webrtc::VideoEncoder::kVp8), type, false);
|
||||
}
|
||||
|
||||
// This shouldn't happen, we should not be trying to create something we don't
|
||||
// support.
|
||||
assert(false);
|
||||
return AllocatedEncoder(NULL, webrtc::kVideoCodecUnknown, false);
|
||||
}
|
||||
|
||||
void WebRtcVideoChannel2::WebRtcVideoSendStream::DestroyVideoEncoder(
|
||||
AllocatedEncoder* encoder) {
|
||||
if (encoder->external) {
|
||||
external_encoder_factory_->DestroyVideoEncoder(encoder->encoder);
|
||||
} else {
|
||||
delete encoder->encoder;
|
||||
}
|
||||
}
|
||||
|
||||
void WebRtcVideoChannel2::WebRtcVideoSendStream::SetCodecAndOptions(
|
||||
const VideoCodecSettings& codec_settings,
|
||||
const VideoOptions& options) {
|
||||
@ -1525,10 +1620,8 @@ void WebRtcVideoChannel2::WebRtcVideoSendStream::SetCodecAndOptions(
|
||||
VideoFormat::FpsToInterval(30),
|
||||
FOURCC_I420);
|
||||
|
||||
webrtc::VideoEncoder* old_encoder =
|
||||
parameters_.config.encoder_settings.encoder;
|
||||
parameters_.config.encoder_settings.encoder =
|
||||
encoder_factory_->CreateVideoEncoder(codec_settings.codec, options);
|
||||
AllocatedEncoder new_encoder = CreateVideoEncoder(codec_settings.codec);
|
||||
parameters_.config.encoder_settings.encoder = new_encoder.encoder;
|
||||
parameters_.config.encoder_settings.payload_name = codec_settings.codec.name;
|
||||
parameters_.config.encoder_settings.payload_type = codec_settings.codec.id;
|
||||
parameters_.config.rtp.fec = codec_settings.fec;
|
||||
@ -1552,7 +1645,10 @@ void WebRtcVideoChannel2::WebRtcVideoSendStream::SetCodecAndOptions(
|
||||
parameters_.options = options;
|
||||
|
||||
RecreateWebRtcStream();
|
||||
delete old_encoder;
|
||||
if (allocated_encoder_.encoder != new_encoder.encoder) {
|
||||
DestroyVideoEncoder(&allocated_encoder_);
|
||||
allocated_encoder_ = new_encoder;
|
||||
}
|
||||
}
|
||||
|
||||
void WebRtcVideoChannel2::WebRtcVideoSendStream::SetRtpExtensions(
|
||||
@ -1957,8 +2053,21 @@ WebRtcVideoChannel2::FilterSupportedCodecs(
|
||||
const std::vector<WebRtcVideoChannel2::VideoCodecSettings>& mapped_codecs) {
|
||||
std::vector<VideoCodecSettings> supported_codecs;
|
||||
for (size_t i = 0; i < mapped_codecs.size(); ++i) {
|
||||
if (encoder_factory_->SupportsCodec(mapped_codecs[i].codec)) {
|
||||
supported_codecs.push_back(mapped_codecs[i]);
|
||||
const VideoCodecSettings& codec = mapped_codecs[i];
|
||||
if (CodecIsInternallySupported(codec.codec.name)) {
|
||||
supported_codecs.push_back(codec);
|
||||
}
|
||||
|
||||
if (external_encoder_factory_ == NULL) {
|
||||
continue;
|
||||
}
|
||||
const std::vector<WebRtcVideoEncoderFactory::VideoCodec> external_codecs =
|
||||
external_encoder_factory_->codecs();
|
||||
for (size_t c = 0; c < external_codecs.size(); ++c) {
|
||||
if (CodecNameMatches(codec.codec.name, external_codecs[c].name)) {
|
||||
supported_codecs.push_back(codec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return supported_codecs;
|
||||
|
@ -72,7 +72,6 @@ class WebRtcDecoderObserver;
|
||||
class WebRtcEncoderObserver;
|
||||
class WebRtcLocalStreamInfo;
|
||||
class WebRtcRenderAdapter;
|
||||
class WebRtcVideoChannel2;
|
||||
class WebRtcVideoChannelRecvInfo;
|
||||
class WebRtcVideoChannelSendInfo;
|
||||
class WebRtcVoiceEngine;
|
||||
@ -80,7 +79,6 @@ class WebRtcVoiceEngine;
|
||||
struct CapturedFrame;
|
||||
struct Device;
|
||||
|
||||
class WebRtcVideoChannel2;
|
||||
class WebRtcVideoRenderer;
|
||||
|
||||
class UnsignalledSsrcHandler {
|
||||
@ -116,17 +114,11 @@ class WebRtcVideoEncoderFactory2 {
|
||||
const VideoOptions& options,
|
||||
size_t num_streams);
|
||||
|
||||
virtual webrtc::VideoEncoder* CreateVideoEncoder(
|
||||
const VideoCodec& codec,
|
||||
const VideoOptions& options);
|
||||
|
||||
virtual void* CreateVideoEncoderSettings(const VideoCodec& codec,
|
||||
const VideoOptions& options);
|
||||
|
||||
virtual void DestroyVideoEncoderSettings(const VideoCodec& codec,
|
||||
void* encoder_settings);
|
||||
|
||||
virtual bool SupportsCodec(const cricket::VideoCodec& codec);
|
||||
};
|
||||
|
||||
// CallFactory, overridden for testing to verify that webrtc::Call is configured
|
||||
@ -192,6 +184,8 @@ class WebRtcVideoEngine2 : public sigslot::has_slots<> {
|
||||
virtual WebRtcVideoEncoderFactory2* GetVideoEncoderFactory();
|
||||
|
||||
private:
|
||||
std::vector<VideoCodec> GetSupportedCodecs() const;
|
||||
|
||||
rtc::Thread* worker_thread_;
|
||||
WebRtcVoiceEngine* voice_engine_;
|
||||
std::vector<VideoCodec> video_codecs_;
|
||||
@ -217,6 +211,8 @@ class WebRtcVideoChannel2 : public rtc::MessageHandler,
|
||||
public:
|
||||
WebRtcVideoChannel2(WebRtcCallFactory* call_factory,
|
||||
VoiceMediaChannel* voice_channel,
|
||||
WebRtcVideoEncoderFactory* external_encoder_factory,
|
||||
WebRtcVideoDecoderFactory* external_decoder_factory,
|
||||
WebRtcVideoEncoderFactory2* encoder_factory);
|
||||
~WebRtcVideoChannel2();
|
||||
bool Init();
|
||||
@ -292,6 +288,7 @@ class WebRtcVideoChannel2 : public rtc::MessageHandler,
|
||||
public:
|
||||
WebRtcVideoSendStream(
|
||||
webrtc::Call* call,
|
||||
WebRtcVideoEncoderFactory* external_encoder_factory,
|
||||
WebRtcVideoEncoderFactory2* encoder_factory,
|
||||
const VideoOptions& options,
|
||||
const Settable<VideoCodecSettings>& codec_settings,
|
||||
@ -337,6 +334,19 @@ class WebRtcVideoChannel2 : public rtc::MessageHandler,
|
||||
webrtc::VideoEncoderConfig encoder_config;
|
||||
};
|
||||
|
||||
struct AllocatedEncoder {
|
||||
AllocatedEncoder(webrtc::VideoEncoder* encoder,
|
||||
webrtc::VideoCodecType type,
|
||||
bool external)
|
||||
: encoder(encoder), type(type), external(external) {}
|
||||
webrtc::VideoEncoder* encoder;
|
||||
webrtc::VideoCodecType type;
|
||||
bool external;
|
||||
};
|
||||
|
||||
AllocatedEncoder CreateVideoEncoder(const VideoCodec& codec)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(lock_);
|
||||
void DestroyVideoEncoder(AllocatedEncoder* encoder);
|
||||
void SetCodecAndOptions(const VideoCodecSettings& codec,
|
||||
const VideoOptions& options)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(lock_);
|
||||
@ -346,11 +356,13 @@ class WebRtcVideoChannel2 : public rtc::MessageHandler,
|
||||
EXCLUSIVE_LOCKS_REQUIRED(lock_);
|
||||
|
||||
webrtc::Call* const call_;
|
||||
WebRtcVideoEncoderFactory* const external_encoder_factory_;
|
||||
WebRtcVideoEncoderFactory2* const encoder_factory_;
|
||||
|
||||
rtc::CriticalSection lock_;
|
||||
webrtc::VideoSendStream* stream_ GUARDED_BY(lock_);
|
||||
VideoSendStreamParameters parameters_ GUARDED_BY(lock_);
|
||||
AllocatedEncoder allocated_encoder_ GUARDED_BY(lock_);
|
||||
|
||||
VideoCapturer* capturer_ GUARDED_BY(lock_);
|
||||
bool sending_ GUARDED_BY(lock_);
|
||||
@ -436,6 +448,8 @@ class WebRtcVideoChannel2 : public rtc::MessageHandler,
|
||||
Settable<VideoCodecSettings> send_codec_;
|
||||
std::vector<webrtc::RtpExtension> send_rtp_extensions_;
|
||||
|
||||
WebRtcVideoEncoderFactory* const external_encoder_factory_;
|
||||
WebRtcVideoDecoderFactory* const external_decoder_factory_;
|
||||
WebRtcVideoEncoderFactory2* const encoder_factory_;
|
||||
std::vector<VideoCodecSettings> recv_codecs_;
|
||||
std::vector<webrtc::RtpExtension> recv_rtp_extensions_;
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#include "talk/media/base/testutils.h"
|
||||
#include "talk/media/base/videoengine_unittest.h"
|
||||
#include "talk/media/webrtc/fakewebrtcvideoengine.h"
|
||||
#include "talk/media/webrtc/webrtcvideochannelfactory.h"
|
||||
#include "talk/media/webrtc/webrtcvideoengine2.h"
|
||||
#include "talk/media/webrtc/webrtcvideoengine2_unittest.h"
|
||||
@ -40,11 +41,11 @@
|
||||
namespace {
|
||||
static const cricket::VideoCodec kVp8Codec720p(100, "VP8", 1280, 720, 30, 0);
|
||||
static const cricket::VideoCodec kVp8Codec360p(100, "VP8", 640, 360, 30, 0);
|
||||
static const cricket::VideoCodec kVp8Codec270p(100, "VP8", 480, 270, 30, 0);
|
||||
static const cricket::VideoCodec kVp8Codec180p(100, "VP8", 320, 180, 30, 0);
|
||||
|
||||
static const cricket::VideoCodec kVp8Codec(100, "VP8", 640, 400, 30, 0);
|
||||
static const cricket::VideoCodec kVp9Codec(101, "VP9", 640, 400, 30, 0);
|
||||
static const cricket::VideoCodec kH264Codec(102, "H264", 640, 400, 30, 0);
|
||||
|
||||
static const cricket::VideoCodec kRedCodec(116, "red", 0, 0, 0, 0);
|
||||
static const cricket::VideoCodec kUlpfecCodec(117, "ulpfec", 0, 0, 0, 0);
|
||||
|
||||
@ -303,7 +304,7 @@ void FakeCall::SignalNetworkState(webrtc::Call::NetworkState state) {
|
||||
network_state_ = state;
|
||||
}
|
||||
|
||||
class WebRtcVideoEngine2Test : public testing::Test {
|
||||
class WebRtcVideoEngine2Test : public ::testing::Test {
|
||||
public:
|
||||
WebRtcVideoEngine2Test() {
|
||||
std::vector<VideoCodec> engine_codecs = engine_.codecs();
|
||||
@ -326,6 +327,9 @@ class WebRtcVideoEngine2Test : public testing::Test {
|
||||
}
|
||||
|
||||
protected:
|
||||
VideoMediaChannel* SetUpForExternalEncoderFactory(
|
||||
cricket::WebRtcVideoEncoderFactory* encoder_factory,
|
||||
const std::vector<VideoCodec>& codecs);
|
||||
WebRtcVideoEngine2 engine_;
|
||||
VideoCodec default_codec_;
|
||||
VideoCodec default_red_codec_;
|
||||
@ -422,6 +426,7 @@ TEST_F(WebRtcVideoEngine2Test, SupportsAbsoluteSenderTimeHeaderExtension) {
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoEngine2Test, SetSendFailsBeforeSettingCodecs) {
|
||||
engine_.Init(rtc::Thread::Current());
|
||||
rtc::scoped_ptr<VideoMediaChannel> channel(engine_.CreateChannel(NULL));
|
||||
|
||||
EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(123)));
|
||||
@ -433,12 +438,110 @@ TEST_F(WebRtcVideoEngine2Test, SetSendFailsBeforeSettingCodecs) {
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoEngine2Test, GetStatsWithoutSendCodecsSetDoesNotCrash) {
|
||||
engine_.Init(rtc::Thread::Current());
|
||||
rtc::scoped_ptr<VideoMediaChannel> channel(engine_.CreateChannel(NULL));
|
||||
EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(123)));
|
||||
VideoMediaInfo info;
|
||||
channel->GetStats(&info);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoEngine2Test, UseExternalFactoryForVp8WhenSupported) {
|
||||
cricket::FakeWebRtcVideoEncoderFactory encoder_factory;
|
||||
encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecVP8, "VP8");
|
||||
std::vector<cricket::VideoCodec> codecs;
|
||||
codecs.push_back(kVp8Codec);
|
||||
|
||||
rtc::scoped_ptr<VideoMediaChannel> channel(
|
||||
SetUpForExternalEncoderFactory(&encoder_factory, codecs));
|
||||
|
||||
EXPECT_TRUE(
|
||||
channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
|
||||
ASSERT_EQ(1u, encoder_factory.encoders().size());
|
||||
EXPECT_TRUE(channel->SetSend(true));
|
||||
|
||||
cricket::FakeVideoCapturer capturer;
|
||||
EXPECT_TRUE(channel->SetCapturer(kSsrc, &capturer));
|
||||
EXPECT_EQ(cricket::CS_RUNNING,
|
||||
capturer.Start(capturer.GetSupportedFormats()->front()));
|
||||
EXPECT_TRUE(capturer.CaptureFrame());
|
||||
|
||||
EXPECT_TRUE_WAIT(encoder_factory.encoders()[0]->GetNumEncodedFrames() > 0,
|
||||
kTimeout);
|
||||
|
||||
// Setting codecs of the same type should not reallocate the encoder.
|
||||
EXPECT_TRUE(channel->SetSendCodecs(codecs));
|
||||
EXPECT_EQ(1, encoder_factory.GetNumCreatedEncoders());
|
||||
|
||||
// Remove stream previously added to free the external encoder instance.
|
||||
EXPECT_TRUE(channel->RemoveSendStream(kSsrc));
|
||||
EXPECT_EQ(0u, encoder_factory.encoders().size());
|
||||
}
|
||||
|
||||
VideoMediaChannel* WebRtcVideoEngine2Test::SetUpForExternalEncoderFactory(
|
||||
cricket::WebRtcVideoEncoderFactory* encoder_factory,
|
||||
const std::vector<VideoCodec>& codecs) {
|
||||
engine_.SetExternalEncoderFactory(encoder_factory);
|
||||
engine_.Init(rtc::Thread::Current());
|
||||
|
||||
VideoMediaChannel* channel = engine_.CreateChannel(NULL);
|
||||
EXPECT_TRUE(channel->SetSendCodecs(codecs));
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoEngine2Test, ChannelWithExternalH264CanChangeToInternalVp8) {
|
||||
cricket::FakeWebRtcVideoEncoderFactory encoder_factory;
|
||||
encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecH264, "H264");
|
||||
std::vector<cricket::VideoCodec> codecs;
|
||||
codecs.push_back(kH264Codec);
|
||||
|
||||
rtc::scoped_ptr<VideoMediaChannel> channel(
|
||||
SetUpForExternalEncoderFactory(&encoder_factory, codecs));
|
||||
|
||||
EXPECT_TRUE(
|
||||
channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
|
||||
ASSERT_EQ(1u, encoder_factory.encoders().size());
|
||||
|
||||
codecs.clear();
|
||||
codecs.push_back(kVp8Codec);
|
||||
EXPECT_TRUE(channel->SetSendCodecs(codecs));
|
||||
|
||||
ASSERT_EQ(0u, encoder_factory.encoders().size());
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoEngine2Test,
|
||||
DontUseExternalEncoderFactoryForUnsupportedCodecs) {
|
||||
cricket::FakeWebRtcVideoEncoderFactory encoder_factory;
|
||||
encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecH264, "H264");
|
||||
std::vector<cricket::VideoCodec> codecs;
|
||||
codecs.push_back(kVp8Codec);
|
||||
|
||||
rtc::scoped_ptr<VideoMediaChannel> channel(
|
||||
SetUpForExternalEncoderFactory(&encoder_factory, codecs));
|
||||
|
||||
EXPECT_TRUE(
|
||||
channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
|
||||
ASSERT_EQ(0u, encoder_factory.encoders().size());
|
||||
}
|
||||
|
||||
// Test external codec with be added to the end of the supported codec list.
|
||||
TEST_F(WebRtcVideoEngine2Test, ReportSupportedExternalCodecs) {
|
||||
cricket::FakeWebRtcVideoEncoderFactory encoder_factory;
|
||||
encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecH264, "H264");
|
||||
engine_.SetExternalEncoderFactory(&encoder_factory);
|
||||
|
||||
engine_.Init(rtc::Thread::Current());
|
||||
|
||||
std::vector<cricket::VideoCodec> codecs(engine_.codecs());
|
||||
ASSERT_GE(codecs.size(), 2u);
|
||||
cricket::VideoCodec internal_codec = codecs.front();
|
||||
cricket::VideoCodec external_codec = codecs.back();
|
||||
|
||||
// The external codec will appear at last.
|
||||
EXPECT_EQ("VP8", internal_codec.name);
|
||||
EXPECT_EQ("H264", external_codec.name);
|
||||
}
|
||||
|
||||
class WebRtcVideoEngine2BaseTest
|
||||
: public VideoEngineTest<cricket::WebRtcVideoEngine2> {
|
||||
protected:
|
||||
@ -581,6 +684,7 @@ class WebRtcVideoChannel2Test : public WebRtcVideoEngine2Test,
|
||||
WebRtcVideoChannel2Test() : fake_call_(NULL) {}
|
||||
virtual void SetUp() OVERRIDE {
|
||||
engine_.SetCallFactory(this);
|
||||
engine_.Init(rtc::Thread::Current());
|
||||
channel_.reset(engine_.CreateChannel(NULL));
|
||||
ASSERT_TRUE(fake_call_ != NULL) << "Call not created through factory.";
|
||||
last_ssrc_ = 123;
|
||||
@ -1604,28 +1708,6 @@ TEST_F(WebRtcVideoChannel2Test, DISABLED_DontRegisterDecoderForNonVP8) {
|
||||
FAIL() << "Not implemented."; // TODO(pbos): Implement.
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoChannel2Test,
|
||||
DISABLED_DontRegisterEncoderIfFactoryIsNotGiven) {
|
||||
FAIL() << "Not implemented."; // TODO(pbos): Implement.
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoChannel2Test, DISABLED_RegisterEncoderIfFactoryIsGiven) {
|
||||
FAIL() << "Not implemented."; // TODO(pbos): Implement.
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoChannel2Test, DISABLED_DontRegisterEncoderMultipleTimes) {
|
||||
FAIL() << "Not implemented."; // TODO(pbos): Implement.
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoChannel2Test,
|
||||
DISABLED_RegisterEncoderWithMultipleSendStreams) {
|
||||
FAIL() << "Not implemented."; // TODO(pbos): Implement.
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoChannel2Test, DISABLED_DontRegisterEncoderForNonVP8) {
|
||||
FAIL() << "Not implemented."; // TODO(pbos): Implement.
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoChannel2Test, DISABLED_FeedbackParamsForNonVP8) {
|
||||
FAIL() << "Not implemented."; // TODO(pbos): Implement.
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user