WebRtcVoiceEngine: Use AudioDecoderFactory to generate recv codecs.
Changed WebRtcVoiceEngine to present receive codecs from the formats provided by its decoder factory. Added supported formats to BuiltinAudioDecoderFactory. Added helper functions for creating some simple decoder factories for mocking. Created a PayloadTypeMapper for assigning payload types to formats. I think we'll eventually want to use this further up, or possibly in both the audio and video sides. It would be best if the engines didn't have to talk payload types at all, but it might be more difficult to get around when payload types depend on each-other, like the RTX codecs for video. This CL also includes some changes to rtc::Optional. I've put them in a separate CL that should (or should not) land first, making these changes void. See: https://codereview.webrtc.org/2072713002/ BUG=webrtc:5805 Review-Url: https://codereview.webrtc.org/2072753002 Cr-Commit-Position: refs/heads/master@{#13459}
This commit is contained in:
parent
7470eb7977
commit
95eb1ba0db
@ -92,6 +92,8 @@ source_set("rtc_media") {
|
||||
"base/videosourcebase.h",
|
||||
"devices/videorendererfactory.h",
|
||||
"engine/nullwebrtcvideoengine.h",
|
||||
"engine/payload_type_mapper.cc",
|
||||
"engine/payload_type_mapper.h",
|
||||
"engine/simulcast.cc",
|
||||
"engine/simulcast.h",
|
||||
"engine/webrtccommon.h",
|
||||
@ -301,6 +303,7 @@ if (rtc_include_tests) {
|
||||
"base/videoengine_unittest.h",
|
||||
"base/videoframe_unittest.h",
|
||||
"engine/nullwebrtcvideoengine_unittest.cc",
|
||||
"engine/payload_type_mapper_unittest.cc",
|
||||
"engine/simulcast_unittest.cc",
|
||||
"engine/webrtcmediaengine_unittest.cc",
|
||||
"engine/webrtcvideocapturer_unittest.cc",
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
|
||||
#include "webrtc/media/engine/nullwebrtcvideoengine.h"
|
||||
#include "webrtc/media/engine/webrtcvoiceengine.h"
|
||||
#include "webrtc/modules/audio_coding/codecs/mock/mock_audio_decoder_factory.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
@ -34,7 +35,9 @@ class WebRtcMediaEngineNullVideo
|
||||
// Simple test to check if NullWebRtcVideoEngine implements the methods
|
||||
// required by CompositeMediaEngine.
|
||||
TEST(NullWebRtcVideoEngineTest, CheckInterface) {
|
||||
WebRtcMediaEngineNullVideo engine(nullptr, nullptr, nullptr, nullptr);
|
||||
WebRtcMediaEngineNullVideo engine(
|
||||
nullptr, webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr,
|
||||
nullptr);
|
||||
EXPECT_TRUE(engine.Init());
|
||||
}
|
||||
|
||||
|
||||
159
webrtc/media/engine/payload_type_mapper.cc
Normal file
159
webrtc/media/engine/payload_type_mapper.cc
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (c) 2016 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 "webrtc/media/engine/payload_type_mapper.h"
|
||||
|
||||
#include "webrtc/common_types.h"
|
||||
#include "webrtc/media/base/mediaconstants.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
PayloadTypeMapper::PayloadTypeMapper()
|
||||
// RFC 3551 reserves payload type numbers in the range 96-127 exclusively
|
||||
// for dynamic assignment. Once those are used up, it is recommended that
|
||||
// payload types unassigned by the RFC are used for dynamic payload type
|
||||
// mapping, before any static payload ids. At this point, we only support
|
||||
// mapping within the exclusive range.
|
||||
: next_unused_payload_type_(96),
|
||||
max_payload_type_(127),
|
||||
mappings_({
|
||||
// Static payload type assignments according to RFC 3551.
|
||||
{{"PCMU", 8000, 1}, 0},
|
||||
{{"GSM", 8000, 1}, 3},
|
||||
{{"G723", 8000, 1}, 4},
|
||||
{{"DVI4", 8000, 1}, 5},
|
||||
{{"DVI4", 16000, 1}, 6},
|
||||
{{"LPC", 8000, 1}, 7},
|
||||
{{"PCMA", 8000, 1}, 8},
|
||||
{{"G722", 8000, 1}, 9},
|
||||
{{"L16", 44100, 2}, 10},
|
||||
{{"L16", 44100, 1}, 11},
|
||||
{{"QCELP", 8000, 1}, 12},
|
||||
{{"CN", 8000, 1}, 13},
|
||||
// RFC 4566 is a bit ambiguous on the contents of the "encoding
|
||||
// parameters" field, which, for audio, encodes the number of
|
||||
// channels. It is "optional and may be omitted if the number of
|
||||
// channels is one". Does that necessarily imply that an omitted
|
||||
// encoding parameter means one channel? Since RFC 3551 doesn't
|
||||
// specify a value for this parameter for MPA, I've included both 0
|
||||
// and 1 here, to increase the chances it will be correctly used if
|
||||
// someone implements an MPEG audio encoder/decoder.
|
||||
{{"MPA", 90000, 0}, 14},
|
||||
{{"MPA", 90000, 1}, 14},
|
||||
{{"G728", 8000, 1}, 15},
|
||||
{{"DVI4", 11025, 1}, 16},
|
||||
{{"DVI4", 22050, 1}, 17},
|
||||
{{"G729", 8000, 1}, 18},
|
||||
|
||||
// Payload type assignments currently used by WebRTC.
|
||||
// Includes video, to reduce collisions (and thus reassignments)
|
||||
// RTX codecs mapping to specific video payload types
|
||||
{{kRtxCodecName, 90000, 0,
|
||||
{{kCodecParamAssociatedPayloadType,
|
||||
std::to_string(kDefaultVp8PlType)}}},
|
||||
kDefaultRtxVp8PlType},
|
||||
{{kRtxCodecName, 90000, 0,
|
||||
{{kCodecParamAssociatedPayloadType,
|
||||
std::to_string(kDefaultVp9PlType)}}},
|
||||
kDefaultRtxVp9PlType},
|
||||
{{kRtxCodecName, 90000, 0,
|
||||
{{kCodecParamAssociatedPayloadType,
|
||||
std::to_string(kDefaultRedPlType)}}},
|
||||
kDefaultRtxRedPlType},
|
||||
{{kRtxCodecName, 90000, 0,
|
||||
{{kCodecParamAssociatedPayloadType,
|
||||
std::to_string(kDefaultH264PlType)}}},
|
||||
kDefaultRtxH264PlType},
|
||||
// Other codecs
|
||||
{{kVp8CodecName, 90000, 0}, kDefaultVp8PlType},
|
||||
{{kVp9CodecName, 90000, 0}, kDefaultVp9PlType},
|
||||
{{kIlbcCodecName, 8000, 1}, 102},
|
||||
{{kIsacCodecName, 16000, 1}, 103},
|
||||
{{kIsacCodecName, 32000, 1}, 104},
|
||||
{{kCnCodecName, 16000, 1}, 105},
|
||||
{{kCnCodecName, 32000, 1}, 106},
|
||||
{{kH264CodecName, 90000, 0}, kDefaultH264PlType},
|
||||
{{kOpusCodecName, 48000, 2,
|
||||
{{"minptime", "10"}, {"useinbandfec", "1"}}}, 111},
|
||||
{{kRedCodecName, 90000, 0}, kDefaultRedPlType},
|
||||
{{kUlpfecCodecName, 90000, 0}, kDefaultUlpfecType},
|
||||
{{kDtmfCodecName, 8000, 1}, 126}}) {
|
||||
// TODO(ossu): Try to keep this as change-proof as possible until we're able
|
||||
// to remove the payload type constants from everywhere in the code.
|
||||
for (const auto& mapping : mappings_) {
|
||||
used_payload_types_.insert(mapping.second);
|
||||
}
|
||||
}
|
||||
|
||||
PayloadTypeMapper::~PayloadTypeMapper() = default;
|
||||
|
||||
rtc::Optional<int> PayloadTypeMapper::GetMappingFor(
|
||||
const webrtc::SdpAudioFormat& format) {
|
||||
auto iter = mappings_.find(format);
|
||||
if (iter != mappings_.end())
|
||||
return rtc::Optional<int>(iter->second);
|
||||
|
||||
for (; next_unused_payload_type_ <= max_payload_type_;
|
||||
++next_unused_payload_type_) {
|
||||
int payload_type = next_unused_payload_type_;
|
||||
if (used_payload_types_.find(payload_type) == used_payload_types_.end()) {
|
||||
used_payload_types_.insert(payload_type);
|
||||
mappings_[format] = payload_type;
|
||||
++next_unused_payload_type_;
|
||||
return rtc::Optional<int>(payload_type);
|
||||
}
|
||||
}
|
||||
|
||||
return rtc::Optional<int>();
|
||||
}
|
||||
|
||||
rtc::Optional<int> PayloadTypeMapper::FindMappingFor(
|
||||
const webrtc::SdpAudioFormat& format) const {
|
||||
auto iter = mappings_.find(format);
|
||||
if (iter != mappings_.end())
|
||||
return rtc::Optional<int>(iter->second);
|
||||
|
||||
return rtc::Optional<int>();
|
||||
}
|
||||
|
||||
rtc::Optional<AudioCodec> PayloadTypeMapper::ToAudioCodec(
|
||||
const webrtc::SdpAudioFormat& format) {
|
||||
// TODO(ossu): We can safely set bitrate to zero here, since that field is
|
||||
// not presented in the SDP. It is used to ferry around some target bitrate
|
||||
// values for certain codecs (ISAC and Opus) and in ways it really
|
||||
// shouldn't. It should be removed once we no longer use CodecInsts in the
|
||||
// ACM or NetEq.
|
||||
auto opt_payload_type = GetMappingFor(format);
|
||||
if (opt_payload_type) {
|
||||
AudioCodec codec(*opt_payload_type, format.name, format.clockrate_hz, 0,
|
||||
format.num_channels);
|
||||
codec.params = format.parameters;
|
||||
return rtc::Optional<AudioCodec>(std::move(codec));
|
||||
}
|
||||
|
||||
return rtc::Optional<AudioCodec>();
|
||||
}
|
||||
|
||||
bool PayloadTypeMapper::SdpAudioFormatOrdering::operator()(
|
||||
const webrtc::SdpAudioFormat& a,
|
||||
const webrtc::SdpAudioFormat& b) const {
|
||||
if (a.clockrate_hz == b.clockrate_hz) {
|
||||
if (a.num_channels == b.num_channels) {
|
||||
int name_cmp = STR_CASE_CMP(a.name.c_str(), b.name.c_str());
|
||||
if (name_cmp == 0)
|
||||
return a.parameters < b.parameters;
|
||||
return name_cmp < 0;
|
||||
}
|
||||
return a.num_channels < b.num_channels;
|
||||
}
|
||||
return a.clockrate_hz < b.clockrate_hz;
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
54
webrtc/media/engine/payload_type_mapper.h
Normal file
54
webrtc/media/engine/payload_type_mapper.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2016 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 WEBRTC_MEDIA_ENGINE_PAYLOAD_TYPE_MAPPER_H_
|
||||
#define WEBRTC_MEDIA_ENGINE_PAYLOAD_TYPE_MAPPER_H_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "webrtc/base/optional.h"
|
||||
#include "webrtc/media/base/codec.h"
|
||||
#include "webrtc/modules/audio_coding/codecs/audio_format.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class PayloadTypeMapper {
|
||||
public:
|
||||
PayloadTypeMapper();
|
||||
~PayloadTypeMapper();
|
||||
|
||||
// Finds the current payload type for |format| or assigns a new one, if no
|
||||
// current mapping exists. Will return an empty value if it was unable to
|
||||
// create a mapping, i.e. if all dynamic payload type ids have been used up.
|
||||
rtc::Optional<int> GetMappingFor(const webrtc::SdpAudioFormat& format);
|
||||
|
||||
// Finds the current payload type for |format|, if any. Returns an empty value
|
||||
// if no payload type mapping exists for the format.
|
||||
rtc::Optional<int> FindMappingFor(const webrtc::SdpAudioFormat& format) const;
|
||||
|
||||
// Like GetMappingFor, but fills in an AudioCodec structure with the necessary
|
||||
// information instead.
|
||||
rtc::Optional<AudioCodec> ToAudioCodec(const webrtc::SdpAudioFormat& format);
|
||||
|
||||
private:
|
||||
struct SdpAudioFormatOrdering {
|
||||
bool operator()(const webrtc::SdpAudioFormat& a,
|
||||
const webrtc::SdpAudioFormat& b) const;
|
||||
};
|
||||
|
||||
int next_unused_payload_type_;
|
||||
int max_payload_type_;
|
||||
std::map<webrtc::SdpAudioFormat, int, SdpAudioFormatOrdering> mappings_;
|
||||
std::set<int> used_payload_types_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
#endif // WEBRTC_MEDIA_ENGINE_PAYLOAD_TYPE_MAPPER_H_
|
||||
162
webrtc/media/engine/payload_type_mapper_unittest.cc
Normal file
162
webrtc/media/engine/payload_type_mapper_unittest.cc
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (c) 2016 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 <set>
|
||||
#include <string>
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "webrtc/media/engine/payload_type_mapper.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class PayloadTypeMapperTest : public testing::Test {
|
||||
public:
|
||||
// TODO(ossu): These are to work around missing comparison operators in
|
||||
// rtc::Optional. They should be removed once Optional has been updated.
|
||||
int FindMapping(const webrtc::SdpAudioFormat& format) {
|
||||
auto opt_mapping = mapper_.FindMappingFor(format);
|
||||
if (opt_mapping)
|
||||
return *opt_mapping;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int GetMapping(const webrtc::SdpAudioFormat& format) {
|
||||
auto opt_mapping = mapper_.GetMappingFor(format);
|
||||
if (opt_mapping)
|
||||
return *opt_mapping;
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected:
|
||||
PayloadTypeMapper mapper_;
|
||||
};
|
||||
|
||||
TEST_F(PayloadTypeMapperTest, StaticPayloadTypes) {
|
||||
EXPECT_EQ(0, FindMapping({"pcmu", 8000, 1}));
|
||||
EXPECT_EQ(3, FindMapping({"gsm", 8000, 1}));
|
||||
EXPECT_EQ(4, FindMapping({"g723", 8000, 1}));
|
||||
EXPECT_EQ(5, FindMapping({"dvi4", 8000, 1}));
|
||||
EXPECT_EQ(6, FindMapping({"dvi4", 16000, 1}));
|
||||
EXPECT_EQ(7, FindMapping({"lpc", 8000, 1}));
|
||||
EXPECT_EQ(8, FindMapping({"pcma", 8000, 1}));
|
||||
EXPECT_EQ(9, FindMapping({"g722", 8000, 1}));
|
||||
EXPECT_EQ(10, FindMapping({"l16", 44100, 2}));
|
||||
EXPECT_EQ(11, FindMapping({"l16", 44100, 1}));
|
||||
EXPECT_EQ(12, FindMapping({"qcelp", 8000, 1}));
|
||||
EXPECT_EQ(13, FindMapping({"cn", 8000, 1}));
|
||||
EXPECT_EQ(14, FindMapping({"mpa", 90000, 0}));
|
||||
EXPECT_EQ(14, FindMapping({"mpa", 90000, 1}));
|
||||
EXPECT_EQ(15, FindMapping({"g728", 8000, 1}));
|
||||
EXPECT_EQ(16, FindMapping({"dvi4", 11025, 1}));
|
||||
EXPECT_EQ(17, FindMapping({"dvi4", 22050, 1}));
|
||||
EXPECT_EQ(18, FindMapping({"g729", 8000, 1}));
|
||||
}
|
||||
|
||||
TEST_F(PayloadTypeMapperTest, WebRTCPayloadTypes) {
|
||||
// Tests that the payload mapper knows about the formats we've been using in
|
||||
// WebRTC, with their hard coded values.
|
||||
auto video_mapping = [this] (const char *name) {
|
||||
return FindMapping({name, kVideoCodecClockrate, 0});
|
||||
};
|
||||
EXPECT_EQ(kDefaultVp8PlType, video_mapping(kVp8CodecName));
|
||||
EXPECT_EQ(kDefaultVp9PlType, video_mapping(kVp9CodecName));
|
||||
EXPECT_EQ(kDefaultH264PlType, video_mapping(kH264CodecName));
|
||||
EXPECT_EQ(kDefaultRedPlType, video_mapping(kRedCodecName));
|
||||
EXPECT_EQ(kDefaultUlpfecType, video_mapping(kUlpfecCodecName));
|
||||
|
||||
auto rtx_mapping = [this] (int payload_type) {
|
||||
return FindMapping({kRtxCodecName, kVideoCodecClockrate, 0,
|
||||
{{ kCodecParamAssociatedPayloadType, std::to_string(payload_type)}}});
|
||||
};
|
||||
EXPECT_EQ(kDefaultRtxVp8PlType, rtx_mapping(kDefaultVp8PlType));
|
||||
EXPECT_EQ(kDefaultRtxVp9PlType, rtx_mapping(kDefaultVp9PlType));
|
||||
EXPECT_EQ(kDefaultRtxH264PlType, rtx_mapping(kDefaultH264PlType));
|
||||
EXPECT_EQ(kDefaultRtxRedPlType, rtx_mapping(kDefaultRedPlType));
|
||||
|
||||
EXPECT_EQ(102, FindMapping({kIlbcCodecName, 8000, 1}));
|
||||
EXPECT_EQ(103, FindMapping({kIsacCodecName, 16000, 1}));
|
||||
EXPECT_EQ(104, FindMapping({kIsacCodecName, 32000, 1}));
|
||||
EXPECT_EQ(105, FindMapping({kCnCodecName, 16000, 1}));
|
||||
EXPECT_EQ(106, FindMapping({kCnCodecName, 32000, 1}));
|
||||
EXPECT_EQ(111, FindMapping({kOpusCodecName, 48000, 2,
|
||||
{{"minptime", "10"}, {"useinbandfec", "1"}}}));
|
||||
EXPECT_EQ(126, FindMapping({kDtmfCodecName, 8000, 1}));
|
||||
}
|
||||
|
||||
TEST_F(PayloadTypeMapperTest, ValidDynamicPayloadTypes) {
|
||||
// RFC 3551 says:
|
||||
// "This profile reserves payload type numbers in the range 96-127
|
||||
// exclusively for dynamic assignment. Applications SHOULD first use
|
||||
// values in this range for dynamic payload types. Those applications
|
||||
// which need to define more than 32 dynamic payload types MAY bind
|
||||
// codes below 96, in which case it is RECOMMENDED that unassigned
|
||||
// payload type numbers be used first. However, the statically assigned
|
||||
// payload types are default bindings and MAY be dynamically bound to
|
||||
// new encodings if needed."
|
||||
|
||||
// Tests that the payload mapper uses values in the dynamic payload type range
|
||||
// (96 - 127) before any others and that the values returned are all valid.
|
||||
bool has_been_below_96 = false;
|
||||
std::set<int> used_payload_types;
|
||||
for (int i = 0; i != 256; ++i) {
|
||||
std::string format_name = "unknown_format_" + std::to_string(i);
|
||||
webrtc::SdpAudioFormat format(format_name.c_str(), i*100, (i % 2) + 1);
|
||||
auto opt_payload_type = mapper_.GetMappingFor(format);
|
||||
bool mapper_is_full = false;
|
||||
|
||||
// There's a limited number of slots for payload types. We're fine with not
|
||||
// being able to map them all.
|
||||
if (opt_payload_type) {
|
||||
int payload_type = *opt_payload_type;
|
||||
EXPECT_FALSE(mapper_is_full) << "Mapping should not fail sporadically";
|
||||
EXPECT_EQ(used_payload_types.find(payload_type), used_payload_types.end())
|
||||
<< "Payload types must not be reused";
|
||||
used_payload_types.insert(payload_type);
|
||||
EXPECT_GE(payload_type, 0) << "Negative payload types are invalid";
|
||||
EXPECT_LE(payload_type, 127) << "Payload types above 127 are invalid";
|
||||
EXPECT_FALSE(payload_type >= 96 && has_been_below_96);
|
||||
if (payload_type < 96)
|
||||
has_been_below_96 = true;
|
||||
|
||||
EXPECT_EQ(payload_type, FindMapping(format))
|
||||
<< "Mapping must be permanent after successful call to "
|
||||
"GetMappingFor";
|
||||
EXPECT_EQ(payload_type, GetMapping(format))
|
||||
<< "Subsequent calls to GetMappingFor must return the same value";
|
||||
} else {
|
||||
mapper_is_full = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Also, we must've been able to map at least one dynamic payload type.
|
||||
EXPECT_FALSE(used_payload_types.empty())
|
||||
<< "Mapper must support at least one user-defined payload type";
|
||||
}
|
||||
|
||||
TEST_F(PayloadTypeMapperTest, ToAudioCodec) {
|
||||
webrtc::SdpAudioFormat format("unknown_format", 4711, 17);
|
||||
auto opt_payload_type = mapper_.GetMappingFor(format);
|
||||
EXPECT_TRUE(opt_payload_type);
|
||||
auto opt_audio_codec = mapper_.ToAudioCodec(format);
|
||||
EXPECT_TRUE(opt_audio_codec);
|
||||
|
||||
if (opt_payload_type && opt_audio_codec) {
|
||||
int payload_type = *opt_payload_type;
|
||||
const AudioCodec& codec = *opt_audio_codec;
|
||||
|
||||
EXPECT_EQ(codec.id, payload_type);
|
||||
EXPECT_EQ(codec.name, format.name);
|
||||
EXPECT_EQ(codec.clockrate, format.clockrate_hz);
|
||||
EXPECT_EQ(codec.channels, format.num_channels);
|
||||
EXPECT_EQ(codec.params, format.parameters);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
@ -18,6 +18,7 @@
|
||||
#include "webrtc/media/base/mediaengine.h"
|
||||
|
||||
namespace webrtc {
|
||||
class AudioDecoderFactory;
|
||||
class AudioDeviceModule;
|
||||
}
|
||||
namespace cricket {
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -32,6 +33,7 @@
|
||||
#include "webrtc/media/base/audiosource.h"
|
||||
#include "webrtc/media/base/mediaconstants.h"
|
||||
#include "webrtc/media/base/streamparams.h"
|
||||
#include "webrtc/media/engine/payload_type_mapper.h"
|
||||
#include "webrtc/media/engine/webrtcmediaengine.h"
|
||||
#include "webrtc/media/engine/webrtcvoe.h"
|
||||
#include "webrtc/modules/audio_coding/acm2/rent_a_codec.h"
|
||||
@ -248,7 +250,7 @@ class WebRtcVoiceCodecs final {
|
||||
public:
|
||||
// TODO(solenberg): Do this filtering once off-line, add a simple AudioCodec
|
||||
// list and add a test which verifies VoE supports the listed codecs.
|
||||
static std::vector<AudioCodec> SupportedCodecs() {
|
||||
static std::vector<AudioCodec> SupportedSendCodecs() {
|
||||
std::vector<AudioCodec> result;
|
||||
// Iterate first over our preferred codecs list, so that the results are
|
||||
// added in order of preference.
|
||||
@ -511,13 +513,20 @@ WebRtcVoiceEngine::WebRtcVoiceEngine(
|
||||
RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
|
||||
LOG(LS_INFO) << "WebRtcVoiceEngine::WebRtcVoiceEngine";
|
||||
RTC_DCHECK(voe_wrapper);
|
||||
RTC_DCHECK(decoder_factory);
|
||||
|
||||
signal_thread_checker_.DetachFromThread();
|
||||
|
||||
// Load our audio codec list.
|
||||
LOG(LS_INFO) << "Supported codecs in order of preference:";
|
||||
codecs_ = WebRtcVoiceCodecs::SupportedCodecs();
|
||||
for (const AudioCodec& codec : codecs_) {
|
||||
LOG(LS_INFO) << "Supported send codecs in order of preference:";
|
||||
send_codecs_ = WebRtcVoiceCodecs::SupportedSendCodecs();
|
||||
for (const AudioCodec& codec : send_codecs_) {
|
||||
LOG(LS_INFO) << ToString(codec);
|
||||
}
|
||||
|
||||
LOG(LS_INFO) << "Supported recv codecs in order of preference:";
|
||||
recv_codecs_ = CollectRecvCodecs();
|
||||
for (const AudioCodec& codec : recv_codecs_) {
|
||||
LOG(LS_INFO) << ToString(codec);
|
||||
}
|
||||
|
||||
@ -936,12 +945,12 @@ int WebRtcVoiceEngine::GetInputLevel() {
|
||||
|
||||
const std::vector<AudioCodec>& WebRtcVoiceEngine::send_codecs() const {
|
||||
RTC_DCHECK(signal_thread_checker_.CalledOnValidThread());
|
||||
return codecs_;
|
||||
return send_codecs_;
|
||||
}
|
||||
|
||||
const std::vector<AudioCodec>& WebRtcVoiceEngine::recv_codecs() const {
|
||||
RTC_DCHECK(signal_thread_checker_.CalledOnValidThread());
|
||||
return codecs_;
|
||||
return recv_codecs_;
|
||||
}
|
||||
|
||||
RtpCapabilities WebRtcVoiceEngine::GetCapabilities() const {
|
||||
@ -1081,6 +1090,61 @@ webrtc::AudioDeviceModule* WebRtcVoiceEngine::adm() {
|
||||
return adm_;
|
||||
}
|
||||
|
||||
AudioCodecs WebRtcVoiceEngine::CollectRecvCodecs() const {
|
||||
PayloadTypeMapper mapper;
|
||||
AudioCodecs out;
|
||||
const std::vector<webrtc::SdpAudioFormat>& formats =
|
||||
decoder_factory_->GetSupportedFormats();
|
||||
|
||||
// Only generate CN payload types for these clockrates
|
||||
std::map<int, bool, std::greater<int>> generate_cn = {{ 8000, false },
|
||||
{ 16000, false },
|
||||
{ 32000, false }};
|
||||
|
||||
auto map_format = [&mapper, &out] (const webrtc::SdpAudioFormat& format) {
|
||||
rtc::Optional<AudioCodec> opt_codec = mapper.ToAudioCodec(format);
|
||||
if (!opt_codec) {
|
||||
LOG(LS_ERROR) << "Unable to assign payload type to format: " << format;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& codec = *opt_codec;
|
||||
if (IsCodec(codec, kOpusCodecName)) {
|
||||
// TODO(ossu): Set this specifically for Opus for now, until we have a
|
||||
// better way of dealing with rtcp-fb parameters.
|
||||
codec.AddFeedbackParam(
|
||||
FeedbackParam(kRtcpFbParamTransportCc, kParamValueEmpty));
|
||||
}
|
||||
out.push_back(codec);
|
||||
return true;
|
||||
};
|
||||
|
||||
for (const auto& format : formats) {
|
||||
if (map_format(format)) {
|
||||
// TODO(ossu): We should get more than just a format from the factory, so
|
||||
// we can determine if a format should be used with CN or not. For now,
|
||||
// generate a CN entry for each supported clock rate also used by a format
|
||||
// supported by the factory.
|
||||
auto cn = generate_cn.find(format.clockrate_hz);
|
||||
if (cn != generate_cn.end() /* && format.allow_comfort_noise */) {
|
||||
cn->second = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add CN codecs after "proper" audio codecs
|
||||
for (const auto& cn : generate_cn) {
|
||||
if (cn.second) {
|
||||
map_format({kCnCodecName, cn.first, 1});
|
||||
}
|
||||
}
|
||||
|
||||
// Add telephone-event codec last
|
||||
map_format({kDtmfCodecName, 8000, 1});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
class WebRtcVoiceMediaChannel::WebRtcAudioSendStream
|
||||
: public AudioSource::Sink {
|
||||
public:
|
||||
|
||||
@ -123,6 +123,8 @@ class WebRtcVoiceEngine final : public webrtc::TraceCallback {
|
||||
int CreateVoEChannel();
|
||||
webrtc::AudioDeviceModule* adm();
|
||||
|
||||
AudioCodecs CollectRecvCodecs() const;
|
||||
|
||||
rtc::ThreadChecker signal_thread_checker_;
|
||||
rtc::ThreadChecker worker_thread_checker_;
|
||||
|
||||
@ -132,7 +134,8 @@ class WebRtcVoiceEngine final : public webrtc::TraceCallback {
|
||||
// The primary instance of WebRtc VoiceEngine.
|
||||
std::unique_ptr<VoEWrapper> voe_wrapper_;
|
||||
rtc::scoped_refptr<webrtc::AudioState> audio_state_;
|
||||
std::vector<AudioCodec> codecs_;
|
||||
std::vector<AudioCodec> send_codecs_;
|
||||
std::vector<AudioCodec> recv_codecs_;
|
||||
std::vector<WebRtcVoiceMediaChannel*> channels_;
|
||||
webrtc::Config voe_config_;
|
||||
bool is_dumping_aec_ = false;
|
||||
|
||||
@ -75,7 +75,9 @@ TEST(WebRtcVoiceEngineTestStubLibrary, StartupShutdown) {
|
||||
cricket::FakeWebRtcVoiceEngine voe;
|
||||
EXPECT_FALSE(voe.IsInited());
|
||||
{
|
||||
cricket::WebRtcVoiceEngine engine(&adm, nullptr, new FakeVoEWrapper(&voe));
|
||||
cricket::WebRtcVoiceEngine engine(
|
||||
&adm, webrtc::MockAudioDecoderFactory::CreateUnusedFactory(),
|
||||
new FakeVoEWrapper(&voe));
|
||||
EXPECT_TRUE(voe.IsInited());
|
||||
}
|
||||
EXPECT_FALSE(voe.IsInited());
|
||||
@ -96,12 +98,13 @@ class WebRtcVoiceEngineTestFake : public testing::Test {
|
||||
|
||||
explicit WebRtcVoiceEngineTestFake(const char* field_trials)
|
||||
: call_(webrtc::Call::Config()), override_field_trials_(field_trials) {
|
||||
auto factory = webrtc::MockAudioDecoderFactory::CreateUnusedFactory();
|
||||
EXPECT_CALL(adm_, AddRef()).WillOnce(Return(0));
|
||||
EXPECT_CALL(adm_, Release()).WillOnce(Return(0));
|
||||
EXPECT_CALL(adm_, BuiltInAECIsAvailable()).WillOnce(Return(false));
|
||||
EXPECT_CALL(adm_, BuiltInAGCIsAvailable()).WillOnce(Return(false));
|
||||
EXPECT_CALL(adm_, BuiltInNSIsAvailable()).WillOnce(Return(false));
|
||||
engine_.reset(new cricket::WebRtcVoiceEngine(&adm_, nullptr,
|
||||
engine_.reset(new cricket::WebRtcVoiceEngine(&adm_, factory,
|
||||
new FakeVoEWrapper(&voe_)));
|
||||
send_parameters_.codecs.push_back(kPcmuCodec);
|
||||
recv_parameters_.codecs.push_back(kPcmuCodec);
|
||||
@ -3361,20 +3364,10 @@ TEST_F(WebRtcVoiceEngineTestFake, OnReadyToSendSignalsNetworkState) {
|
||||
|
||||
// Tests that the library initializes and shuts down properly.
|
||||
TEST(WebRtcVoiceEngineTest, StartupShutdown) {
|
||||
using testing::_;
|
||||
using testing::AnyNumber;
|
||||
|
||||
// If the VoiceEngine wants to gather available codecs early, that's fine but
|
||||
// we never want it to create a decoder at this stage.
|
||||
rtc::scoped_refptr<webrtc::MockAudioDecoderFactory> factory =
|
||||
new rtc::RefCountedObject<webrtc::MockAudioDecoderFactory>;
|
||||
ON_CALL(*factory.get(), GetSupportedFormats())
|
||||
.WillByDefault(Return(std::vector<webrtc::SdpAudioFormat>()));
|
||||
EXPECT_CALL(*factory.get(), GetSupportedFormats())
|
||||
.Times(AnyNumber());
|
||||
EXPECT_CALL(*factory.get(), MakeAudioDecoderMock(_, _)).Times(0);
|
||||
|
||||
cricket::WebRtcVoiceEngine engine(nullptr, factory);
|
||||
cricket::WebRtcVoiceEngine engine(
|
||||
nullptr, webrtc::MockAudioDecoderFactory::CreateUnusedFactory());
|
||||
std::unique_ptr<webrtc::Call> call(
|
||||
webrtc::Call::Create(webrtc::Call::Config()));
|
||||
cricket::VoiceMediaChannel* channel = engine.CreateChannel(
|
||||
@ -3389,7 +3382,8 @@ TEST(WebRtcVoiceEngineTest, StartupShutdownWithExternalADM) {
|
||||
EXPECT_CALL(adm, AddRef()).Times(3).WillRepeatedly(Return(0));
|
||||
EXPECT_CALL(adm, Release()).Times(3).WillRepeatedly(Return(0));
|
||||
{
|
||||
cricket::WebRtcVoiceEngine engine(&adm, nullptr);
|
||||
cricket::WebRtcVoiceEngine engine(
|
||||
&adm, webrtc::MockAudioDecoderFactory::CreateUnusedFactory());
|
||||
std::unique_ptr<webrtc::Call> call(
|
||||
webrtc::Call::Create(webrtc::Call::Config()));
|
||||
cricket::VoiceMediaChannel* channel = engine.CreateChannel(
|
||||
@ -3400,8 +3394,6 @@ TEST(WebRtcVoiceEngineTest, StartupShutdownWithExternalADM) {
|
||||
}
|
||||
|
||||
// Tests that the library is configured with the codecs we want.
|
||||
// TODO(ossu): This test should move into the builtin audio codecs module
|
||||
// eventually.
|
||||
TEST(WebRtcVoiceEngineTest, HasCorrectCodecs) {
|
||||
// TODO(ossu): These tests should move into a future "builtin audio codecs"
|
||||
// module.
|
||||
@ -3457,10 +3449,13 @@ TEST(WebRtcVoiceEngineTest, HasCorrectCodecs) {
|
||||
cricket::AudioCodec(0, "", 0, 5000, 1), nullptr));
|
||||
|
||||
// Verify the payload id of common audio codecs, including CN, ISAC, and G722.
|
||||
cricket::WebRtcVoiceEngine engine(nullptr,
|
||||
webrtc::CreateBuiltinAudioDecoderFactory());
|
||||
// TODO(ossu): Why are the payload types of codecs with non-static payload
|
||||
// type assignments checked here? It shouldn't really matter.
|
||||
cricket::WebRtcVoiceEngine engine(
|
||||
nullptr, webrtc::MockAudioDecoderFactory::CreateUnusedFactory());
|
||||
for (std::vector<cricket::AudioCodec>::const_iterator it =
|
||||
engine.send_codecs().begin(); it != engine.send_codecs().end(); ++it) {
|
||||
engine.send_codecs().begin();
|
||||
it != engine.send_codecs().end(); ++it) {
|
||||
if (it->name == "CN" && it->clockrate == 16000) {
|
||||
EXPECT_EQ(105, it->id);
|
||||
} else if (it->name == "CN" && it->clockrate == 32000) {
|
||||
@ -3485,7 +3480,8 @@ TEST(WebRtcVoiceEngineTest, HasCorrectCodecs) {
|
||||
|
||||
// Tests that VoE supports at least 32 channels
|
||||
TEST(WebRtcVoiceEngineTest, Has32Channels) {
|
||||
cricket::WebRtcVoiceEngine engine(nullptr, nullptr);
|
||||
cricket::WebRtcVoiceEngine engine(
|
||||
nullptr, webrtc::MockAudioDecoderFactory::CreateUnusedFactory());
|
||||
std::unique_ptr<webrtc::Call> call(
|
||||
webrtc::Call::Create(webrtc::Call::Config()));
|
||||
|
||||
|
||||
@ -69,6 +69,8 @@
|
||||
'base/videosourcebase.h',
|
||||
'devices/videorendererfactory.h',
|
||||
'engine/nullwebrtcvideoengine.h',
|
||||
'engine/payload_type_mapper.cc',
|
||||
'engine/payload_type_mapper.h',
|
||||
'engine/simulcast.cc',
|
||||
'engine/simulcast.h',
|
||||
'engine/webrtccommon.h',
|
||||
@ -267,6 +269,7 @@
|
||||
'base/videoengine_unittest.h',
|
||||
'base/videoframe_unittest.h',
|
||||
'engine/nullwebrtcvideoengine_unittest.cc',
|
||||
'engine/payload_type_mapper_unittest.cc',
|
||||
'engine/simulcast_unittest.cc',
|
||||
'engine/webrtcmediaengine_unittest.cc',
|
||||
'engine/webrtcvideocapturer_unittest.cc',
|
||||
|
||||
@ -130,7 +130,31 @@ NamedDecoderConstructor decoder_constructors[] = {
|
||||
class BuiltinAudioDecoderFactory : public AudioDecoderFactory {
|
||||
public:
|
||||
std::vector<SdpAudioFormat> GetSupportedFormats() override {
|
||||
FATAL() << "Not implemented yet!";
|
||||
static std::vector<SdpAudioFormat> formats = {
|
||||
#ifdef WEBRTC_CODEC_OPUS
|
||||
{ "opus", 48000, 2, {
|
||||
{"minptime", "10" },
|
||||
{"useinbandfec", "1" }
|
||||
}
|
||||
},
|
||||
#endif
|
||||
#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX))
|
||||
{ "isac", 16000, 1 },
|
||||
#endif
|
||||
#if (defined(WEBRTC_CODEC_ISAC))
|
||||
{ "isac", 32000, 1 },
|
||||
#endif
|
||||
#ifdef WEBRTC_CODEC_G722
|
||||
{ "G722", 8000, 1 },
|
||||
#endif
|
||||
#ifdef WEBRTC_CODEC_ILBC
|
||||
{ "iLBC", 8000, 1 },
|
||||
#endif
|
||||
{ "PCMU", 8000, 1 },
|
||||
{ "PCMA", 8000, 1 }
|
||||
};
|
||||
|
||||
return formats;
|
||||
}
|
||||
|
||||
std::unique_ptr<AudioDecoder> MakeAudioDecoder(
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "webrtc/base/scoped_ref_ptr.h"
|
||||
#include "webrtc/modules/audio_coding/codecs/audio_decoder_factory.h"
|
||||
|
||||
namespace webrtc {
|
||||
@ -30,6 +31,45 @@ class MockAudioDecoderFactory : public AudioDecoderFactory {
|
||||
MOCK_METHOD2(MakeAudioDecoderMock,
|
||||
void(const SdpAudioFormat& format,
|
||||
std::unique_ptr<AudioDecoder>* return_value));
|
||||
|
||||
// Creates a MockAudioDecoderFactory with no formats and that may not be
|
||||
// invoked to create a codec - useful for initializing a voice engine, for
|
||||
// example.
|
||||
static rtc::scoped_refptr<webrtc::MockAudioDecoderFactory>
|
||||
CreateUnusedFactory() {
|
||||
using testing::_;
|
||||
using testing::AnyNumber;
|
||||
using testing::Return;
|
||||
|
||||
rtc::scoped_refptr<webrtc::MockAudioDecoderFactory> factory =
|
||||
new rtc::RefCountedObject<webrtc::MockAudioDecoderFactory>;
|
||||
ON_CALL(*factory.get(), GetSupportedFormats())
|
||||
.WillByDefault(Return(std::vector<webrtc::SdpAudioFormat>()));
|
||||
EXPECT_CALL(*factory.get(), GetSupportedFormats()).Times(AnyNumber());
|
||||
EXPECT_CALL(*factory.get(), MakeAudioDecoderMock(_, _)).Times(0);
|
||||
return factory;
|
||||
}
|
||||
|
||||
// Creates a MockAudioDecoderFactory with no formats that may be invoked to
|
||||
// create a codec any number of times. It will, though, return nullptr on each
|
||||
// call, since it supports no codecs.
|
||||
static rtc::scoped_refptr<webrtc::MockAudioDecoderFactory>
|
||||
CreateEmptyFactory() {
|
||||
using testing::_;
|
||||
using testing::AnyNumber;
|
||||
using testing::Return;
|
||||
using testing::SetArgPointee;
|
||||
|
||||
rtc::scoped_refptr<webrtc::MockAudioDecoderFactory> factory =
|
||||
new rtc::RefCountedObject<webrtc::MockAudioDecoderFactory>;
|
||||
ON_CALL(*factory.get(), GetSupportedFormats())
|
||||
.WillByDefault(Return(std::vector<webrtc::SdpAudioFormat>()));
|
||||
EXPECT_CALL(*factory.get(), GetSupportedFormats()).Times(AnyNumber());
|
||||
ON_CALL(*factory.get(), MakeAudioDecoderMock(_, _))
|
||||
.WillByDefault(SetArgPointee<1>(nullptr));
|
||||
EXPECT_CALL(*factory.get(), MakeAudioDecoderMock(_, _)).Times(AnyNumber());
|
||||
return factory;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user