mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-12 21:30:45 +01:00
Expose AudioLevel as an absl::optional struct in api/rtp_headers.h
Start migrating away from `hasAudioLevel`, `voiceActivity`, `audioLevel` fields in RTPHeaderExtension and switch usages to a more modern absl::optional<AudioLevel> accessor instead. The old fields are preserved for compatibility with downstream projects, but will be removed in the future. Bug: webrtc:15788 Change-Id: I76599124fd68dd4d449f850df3b9814d6a002f5d Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/336303 Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> Commit-Queue: Danil Chapovalov <danilchap@webrtc.org> Cr-Commit-Position: refs/heads/main@{#41947}
This commit is contained in:
parent
28efb5acb4
commit
5075cb4a60
21 changed files with 212 additions and 141 deletions
|
@ -12,6 +12,14 @@
|
|||
|
||||
namespace webrtc {
|
||||
|
||||
AudioLevel::AudioLevel() : voice_activity_(false), audio_level_(0) {}
|
||||
|
||||
AudioLevel::AudioLevel(bool voice_activity, int audio_level)
|
||||
: voice_activity_(voice_activity), audio_level_(audio_level) {
|
||||
RTC_CHECK_GE(audio_level, 0);
|
||||
RTC_CHECK_LE(audio_level, 127);
|
||||
}
|
||||
|
||||
RTPHeaderExtension::RTPHeaderExtension()
|
||||
: hasTransmissionTimeOffset(false),
|
||||
transmissionTimeOffset(0),
|
||||
|
@ -34,6 +42,24 @@ RTPHeaderExtension::RTPHeaderExtension(const RTPHeaderExtension& other) =
|
|||
RTPHeaderExtension& RTPHeaderExtension::operator=(
|
||||
const RTPHeaderExtension& other) = default;
|
||||
|
||||
absl::optional<AudioLevel> RTPHeaderExtension::audio_level() const {
|
||||
if (!hasAudioLevel) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
return AudioLevel(voiceActivity, audioLevel);
|
||||
}
|
||||
|
||||
void RTPHeaderExtension::set_audio_level(
|
||||
absl::optional<AudioLevel> audio_level) {
|
||||
if (audio_level) {
|
||||
hasAudioLevel = true;
|
||||
voiceActivity = audio_level->voice_activity();
|
||||
audioLevel = audio_level->level();
|
||||
} else {
|
||||
hasAudioLevel = false;
|
||||
}
|
||||
}
|
||||
|
||||
RTPHeader::RTPHeader()
|
||||
: markerBit(false),
|
||||
payloadType(0),
|
||||
|
|
|
@ -77,6 +77,29 @@ struct AbsoluteCaptureTime {
|
|||
absl::optional<int64_t> estimated_capture_clock_offset;
|
||||
};
|
||||
|
||||
// The audio level extension is used to indicate the voice activity and the
|
||||
// audio level of the payload in the RTP stream. See:
|
||||
// https://tools.ietf.org/html/rfc6464#section-3.
|
||||
class AudioLevel {
|
||||
public:
|
||||
AudioLevel();
|
||||
AudioLevel(bool voice_activity, int audio_level);
|
||||
AudioLevel(const AudioLevel& other) = default;
|
||||
AudioLevel& operator=(const AudioLevel& other) = default;
|
||||
|
||||
// Flag indicating whether the encoder believes the audio packet contains
|
||||
// voice activity.
|
||||
bool voice_activity() const { return voice_activity_; }
|
||||
|
||||
// Audio level in -dBov. Values range from 0 to 127, representing 0 to -127
|
||||
// dBov. 127 represents digital silence.
|
||||
int level() const { return audio_level_; }
|
||||
|
||||
private:
|
||||
bool voice_activity_;
|
||||
int audio_level_;
|
||||
};
|
||||
|
||||
inline bool operator==(const AbsoluteCaptureTime& lhs,
|
||||
const AbsoluteCaptureTime& rhs) {
|
||||
return (lhs.absolute_capture_timestamp == rhs.absolute_capture_timestamp) &&
|
||||
|
@ -114,6 +137,12 @@ struct RTPHeaderExtension {
|
|||
|
||||
// Audio Level includes both level in dBov and voiced/unvoiced bit. See:
|
||||
// https://tools.ietf.org/html/rfc6464#section-3
|
||||
absl::optional<AudioLevel> audio_level() const;
|
||||
|
||||
void set_audio_level(absl::optional<AudioLevel> audio_level);
|
||||
|
||||
// Direct use of the following members is discouraged and will be removed
|
||||
// once downstream projects have been updated.
|
||||
bool hasAudioLevel;
|
||||
bool voiceActivity;
|
||||
uint8_t audioLevel;
|
||||
|
|
|
@ -37,8 +37,8 @@ RtpPacketInfo::RtpPacketInfo(const RTPHeader& rtp_header,
|
|||
|
||||
csrcs_.assign(&rtp_header.arrOfCSRCs[0], &rtp_header.arrOfCSRCs[csrcs_count]);
|
||||
|
||||
if (extension.hasAudioLevel) {
|
||||
audio_level_ = extension.audioLevel;
|
||||
if (extension.audio_level()) {
|
||||
audio_level_ = extension.audio_level()->level();
|
||||
}
|
||||
|
||||
absolute_capture_time_ = extension.absolute_capture_time;
|
||||
|
|
|
@ -116,11 +116,9 @@ TEST_F(AudioSendStreamCallTest, SupportsAudioLevel) {
|
|||
RtpPacket rtp_packet(&extensions_);
|
||||
EXPECT_TRUE(rtp_packet.Parse(packet));
|
||||
|
||||
uint8_t audio_level = 0;
|
||||
bool voice = false;
|
||||
EXPECT_TRUE(
|
||||
rtp_packet.GetExtension<AudioLevelExtension>(&voice, &audio_level));
|
||||
if (audio_level != 0) {
|
||||
AudioLevel audio_level;
|
||||
EXPECT_TRUE(rtp_packet.GetExtension<AudioLevelExtension>(&audio_level));
|
||||
if (audio_level.level() != 0) {
|
||||
// Wait for at least one packet with a non-zero level.
|
||||
observation_complete_.Set();
|
||||
} else {
|
||||
|
|
|
@ -61,8 +61,13 @@ class TransformableIncomingAudioFrame
|
|||
const RTPHeader& Header() const { return header_; }
|
||||
|
||||
FrameType Type() const override {
|
||||
return header_.extension.voiceActivity ? FrameType::kAudioFrameSpeech
|
||||
: FrameType::kAudioFrameCN;
|
||||
if (!header_.extension.audio_level()) {
|
||||
// Audio level extension not set.
|
||||
return FrameType::kAudioFrameCN;
|
||||
}
|
||||
return header_.extension.audio_level()->voice_activity()
|
||||
? FrameType::kAudioFrameSpeech
|
||||
: FrameType::kAudioFrameCN;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -444,16 +444,14 @@ void RtcEventLogEncoderNewFormat::EncodeRtpPacket(const Batch& batch,
|
|||
absl::optional<uint64_t> base_audio_level;
|
||||
absl::optional<uint64_t> base_voice_activity;
|
||||
{
|
||||
bool voice_activity;
|
||||
uint8_t audio_level;
|
||||
if (base_event->template GetExtension<AudioLevelExtension>(&voice_activity,
|
||||
&audio_level)) {
|
||||
RTC_DCHECK_LE(audio_level, 0x7Fu);
|
||||
base_audio_level = audio_level;
|
||||
proto_batch->set_audio_level(audio_level);
|
||||
AudioLevel audio_level;
|
||||
if (base_event->template GetExtension<AudioLevelExtension>(&audio_level)) {
|
||||
RTC_DCHECK_LE(audio_level.level(), 0x7Fu);
|
||||
base_audio_level = audio_level.level();
|
||||
proto_batch->set_audio_level(audio_level.level());
|
||||
|
||||
base_voice_activity = voice_activity;
|
||||
proto_batch->set_voice_activity(voice_activity);
|
||||
base_voice_activity = audio_level.voice_activity();
|
||||
proto_batch->set_voice_activity(audio_level.voice_activity());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -641,12 +639,10 @@ void RtcEventLogEncoderNewFormat::EncodeRtpPacket(const Batch& batch,
|
|||
// audio_level (RTP extension)
|
||||
for (size_t i = 0; i < values.size(); ++i) {
|
||||
const EventType* event = batch[i + 1];
|
||||
bool voice_activity;
|
||||
uint8_t audio_level;
|
||||
if (event->template GetExtension<AudioLevelExtension>(&voice_activity,
|
||||
&audio_level)) {
|
||||
RTC_DCHECK_LE(audio_level, 0x7Fu);
|
||||
values[i] = audio_level;
|
||||
AudioLevel audio_level;
|
||||
if (event->template GetExtension<AudioLevelExtension>(&audio_level)) {
|
||||
RTC_DCHECK_LE(audio_level.level(), 0x7F);
|
||||
values[i] = audio_level.level();
|
||||
} else {
|
||||
values[i].reset();
|
||||
}
|
||||
|
@ -659,12 +655,10 @@ void RtcEventLogEncoderNewFormat::EncodeRtpPacket(const Batch& batch,
|
|||
// voice_activity (RTP extension)
|
||||
for (size_t i = 0; i < values.size(); ++i) {
|
||||
const EventType* event = batch[i + 1];
|
||||
bool voice_activity;
|
||||
uint8_t audio_level;
|
||||
if (event->template GetExtension<AudioLevelExtension>(&voice_activity,
|
||||
&audio_level)) {
|
||||
RTC_DCHECK_LE(audio_level, 0x7Fu);
|
||||
values[i] = voice_activity;
|
||||
AudioLevel audio_level;
|
||||
if (event->template GetExtension<AudioLevelExtension>(&audio_level)) {
|
||||
RTC_DCHECK_LE(audio_level.level(), 0x7F);
|
||||
values[i] = audio_level.voice_activity();
|
||||
} else {
|
||||
values[i].reset();
|
||||
}
|
||||
|
|
|
@ -135,10 +135,9 @@ void ConvertRtpPacket(
|
|||
if (incoming.rtp.header.extension.hasTransportSequenceNumber)
|
||||
reconstructed_packet.SetExtension<webrtc::TransportSequenceNumber>(
|
||||
incoming.rtp.header.extension.transportSequenceNumber);
|
||||
if (incoming.rtp.header.extension.hasAudioLevel)
|
||||
if (incoming.rtp.header.extension.audio_level())
|
||||
reconstructed_packet.SetExtension<webrtc::AudioLevelExtension>(
|
||||
incoming.rtp.header.extension.voiceActivity,
|
||||
incoming.rtp.header.extension.audioLevel);
|
||||
*incoming.rtp.header.extension.audio_level());
|
||||
if (incoming.rtp.header.extension.hasVideoRotation)
|
||||
reconstructed_packet.SetExtension<webrtc::VideoOrientation>(
|
||||
incoming.rtp.header.extension.videoRotation);
|
||||
|
|
|
@ -358,13 +358,10 @@ ParsedRtcEventLog::ParseStatus StoreRtpPackets(
|
|||
}
|
||||
if (proto.has_audio_level()) {
|
||||
RTC_PARSE_CHECK_OR_RETURN(proto.has_voice_activity());
|
||||
header.extension.hasAudioLevel = true;
|
||||
header.extension.voiceActivity =
|
||||
rtc::checked_cast<bool>(proto.voice_activity());
|
||||
const uint8_t audio_level =
|
||||
rtc::checked_cast<uint8_t>(proto.audio_level());
|
||||
RTC_PARSE_CHECK_OR_RETURN_LE(audio_level, 0x7Fu);
|
||||
header.extension.audioLevel = audio_level;
|
||||
bool voice_activity = rtc::checked_cast<bool>(proto.voice_activity());
|
||||
int audio_level = rtc::checked_cast<int>(proto.audio_level());
|
||||
RTC_PARSE_CHECK_OR_RETURN_LE(audio_level, 0x7F);
|
||||
header.extension.set_audio_level(AudioLevel(voice_activity, audio_level));
|
||||
} else {
|
||||
RTC_PARSE_CHECK_OR_RETURN(!proto.has_voice_activity());
|
||||
}
|
||||
|
@ -562,13 +559,11 @@ ParsedRtcEventLog::ParseStatus StoreRtpPackets(
|
|||
if (audio_level_values.size() > i && audio_level_values[i].has_value()) {
|
||||
RTC_PARSE_CHECK_OR_RETURN(voice_activity_values.size() > i &&
|
||||
voice_activity_values[i].has_value());
|
||||
header.extension.hasAudioLevel = true;
|
||||
header.extension.voiceActivity =
|
||||
bool voice_activity =
|
||||
rtc::checked_cast<bool>(voice_activity_values[i].value());
|
||||
const uint8_t audio_level =
|
||||
rtc::checked_cast<uint8_t>(audio_level_values[i].value());
|
||||
RTC_PARSE_CHECK_OR_RETURN_LE(audio_level, 0x7Fu);
|
||||
header.extension.audioLevel = audio_level;
|
||||
int audio_level = rtc::checked_cast<int>(audio_level_values[i].value());
|
||||
RTC_PARSE_CHECK_OR_RETURN_LE(audio_level, 0x7F);
|
||||
header.extension.set_audio_level(AudioLevel(voice_activity, audio_level));
|
||||
} else {
|
||||
RTC_PARSE_CHECK_OR_RETURN(voice_activity_values.size() <= i ||
|
||||
!voice_activity_values[i].has_value());
|
||||
|
|
|
@ -618,8 +618,8 @@ void EventGenerator::RandomizeRtpPacket(
|
|||
|
||||
if (extension_map.IsRegistered(AudioLevelExtension::kId) &&
|
||||
(all_configured_exts || prng_.Rand<bool>())) {
|
||||
rtp_packet->SetExtension<AudioLevelExtension>(prng_.Rand<bool>(),
|
||||
prng_.Rand(127));
|
||||
rtp_packet->SetExtension<AudioLevelExtension>(
|
||||
AudioLevel(prng_.Rand<bool>(), prng_.Rand(127)));
|
||||
}
|
||||
|
||||
if (extension_map.IsRegistered(AbsoluteSendTime::kId) &&
|
||||
|
@ -1029,14 +1029,15 @@ void VerifyLoggedRtpHeader(const Event& original_header,
|
|||
|
||||
// AudioLevel header extension.
|
||||
ASSERT_EQ(original_header.template HasExtension<AudioLevelExtension>(),
|
||||
logged_header.extension.hasAudioLevel);
|
||||
if (logged_header.extension.hasAudioLevel) {
|
||||
bool voice_activity;
|
||||
uint8_t audio_level;
|
||||
logged_header.extension.audio_level().has_value());
|
||||
if (logged_header.extension.audio_level()) {
|
||||
AudioLevel audio_level;
|
||||
ASSERT_TRUE(original_header.template GetExtension<AudioLevelExtension>(
|
||||
&voice_activity, &audio_level));
|
||||
EXPECT_EQ(voice_activity, logged_header.extension.voiceActivity);
|
||||
EXPECT_EQ(audio_level, logged_header.extension.audioLevel);
|
||||
&audio_level));
|
||||
EXPECT_EQ(audio_level.voice_activity(),
|
||||
logged_header.extension.audio_level()->voice_activity());
|
||||
EXPECT_EQ(audio_level.level(),
|
||||
logged_header.extension.audio_level()->level());
|
||||
}
|
||||
|
||||
// VideoOrientation header extension.
|
||||
|
|
|
@ -1561,7 +1561,7 @@ TEST_P(WebRtcVoiceEngineTestFake, OnPacketReceivedIdentifiesExtensions) {
|
|||
webrtc::RtpPacketReceived reference_packet(&extension_map);
|
||||
constexpr uint8_t kAudioLevel = 123;
|
||||
reference_packet.SetExtension<webrtc::AudioLevelExtension>(
|
||||
/*voice_activity=*/true, kAudioLevel);
|
||||
webrtc::AudioLevel(/*voice_activity=*/true, kAudioLevel));
|
||||
// Create a packet without the extension map but with the same content.
|
||||
webrtc::RtpPacketReceived received_packet;
|
||||
ASSERT_TRUE(received_packet.Parse(reference_packet.Buffer()));
|
||||
|
@ -1569,12 +1569,10 @@ TEST_P(WebRtcVoiceEngineTestFake, OnPacketReceivedIdentifiesExtensions) {
|
|||
receive_channel_->OnPacketReceived(received_packet);
|
||||
rtc::Thread::Current()->ProcessMessages(0);
|
||||
|
||||
bool voice_activity;
|
||||
uint8_t audio_level;
|
||||
webrtc::AudioLevel audio_level;
|
||||
EXPECT_TRUE(call_.last_received_rtp_packet()
|
||||
.GetExtension<webrtc::AudioLevelExtension>(&voice_activity,
|
||||
&audio_level));
|
||||
EXPECT_EQ(audio_level, kAudioLevel);
|
||||
.GetExtension<webrtc::AudioLevelExtension>(&audio_level));
|
||||
EXPECT_EQ(audio_level.level(), kAudioLevel);
|
||||
}
|
||||
|
||||
// Test that we apply codecs properly.
|
||||
|
|
|
@ -564,8 +564,8 @@ TEST_F(NetEqImplTest, ReorderedPacket) {
|
|||
rtp_header.sequenceNumber = 0x1234;
|
||||
rtp_header.timestamp = 0x12345678;
|
||||
rtp_header.ssrc = 0x87654321;
|
||||
rtp_header.extension.hasAudioLevel = true;
|
||||
rtp_header.extension.audioLevel = 42;
|
||||
rtp_header.extension.set_audio_level(
|
||||
AudioLevel(/*voice_activity=*/false, 42));
|
||||
|
||||
EXPECT_CALL(mock_decoder, Reset()).WillRepeatedly(Return());
|
||||
EXPECT_CALL(mock_decoder, SampleRateHz())
|
||||
|
@ -606,7 +606,8 @@ TEST_F(NetEqImplTest, ReorderedPacket) {
|
|||
EXPECT_EQ(packet_info.ssrc(), rtp_header.ssrc);
|
||||
EXPECT_THAT(packet_info.csrcs(), IsEmpty());
|
||||
EXPECT_EQ(packet_info.rtp_timestamp(), rtp_header.timestamp);
|
||||
EXPECT_EQ(packet_info.audio_level(), rtp_header.extension.audioLevel);
|
||||
EXPECT_EQ(packet_info.audio_level(),
|
||||
rtp_header.extension.audio_level()->level());
|
||||
EXPECT_EQ(packet_info.receive_time(), expected_receive_time);
|
||||
}
|
||||
|
||||
|
@ -614,13 +615,13 @@ TEST_F(NetEqImplTest, ReorderedPacket) {
|
|||
// old, the second one is the expected next packet.
|
||||
rtp_header.sequenceNumber -= 1;
|
||||
rtp_header.timestamp -= kPayloadLengthSamples;
|
||||
rtp_header.extension.audioLevel = 1;
|
||||
rtp_header.extension.set_audio_level(AudioLevel(/*voice_activity=*/false, 1));
|
||||
payload[0] = 1;
|
||||
clock_.AdvanceTimeMilliseconds(1000);
|
||||
EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
|
||||
rtp_header.sequenceNumber += 2;
|
||||
rtp_header.timestamp += 2 * kPayloadLengthSamples;
|
||||
rtp_header.extension.audioLevel = 2;
|
||||
rtp_header.extension.set_audio_level(AudioLevel(/*voice_activity=*/false, 2));
|
||||
payload[0] = 2;
|
||||
clock_.AdvanceTimeMilliseconds(2000);
|
||||
expected_receive_time = clock_.CurrentTime();
|
||||
|
@ -655,7 +656,8 @@ TEST_F(NetEqImplTest, ReorderedPacket) {
|
|||
EXPECT_EQ(packet_info.ssrc(), rtp_header.ssrc);
|
||||
EXPECT_THAT(packet_info.csrcs(), IsEmpty());
|
||||
EXPECT_EQ(packet_info.rtp_timestamp(), rtp_header.timestamp);
|
||||
EXPECT_EQ(packet_info.audio_level(), rtp_header.extension.audioLevel);
|
||||
EXPECT_EQ(packet_info.audio_level(),
|
||||
rtp_header.extension.audio_level()->level());
|
||||
EXPECT_EQ(packet_info.receive_time(), expected_receive_time);
|
||||
}
|
||||
|
||||
|
@ -772,8 +774,7 @@ TEST_F(NetEqImplTest, InsertRedPayload) {
|
|||
AbsoluteCaptureTime capture_time;
|
||||
capture_time.absolute_capture_timestamp = 1234;
|
||||
header.extension.absolute_capture_time = capture_time;
|
||||
header.extension.audioLevel = 12;
|
||||
header.extension.hasAudioLevel = true;
|
||||
header.extension.set_audio_level(AudioLevel(/*voice_activity=*/false, 12));
|
||||
header.numCSRCs = 1;
|
||||
header.arrOfCSRCs[0] = 123;
|
||||
neteq_->InsertPacket(header, payload);
|
||||
|
@ -795,7 +796,7 @@ TEST_F(NetEqImplTest, InsertRedPayload) {
|
|||
EXPECT_EQ(frame.packet_infos_.size(), 1u);
|
||||
EXPECT_EQ(frame.packet_infos_.front().absolute_capture_time(), capture_time);
|
||||
EXPECT_EQ(frame.packet_infos_.front().audio_level(),
|
||||
header.extension.audioLevel);
|
||||
header.extension.audio_level()->level());
|
||||
EXPECT_EQ(frame.packet_infos_.front().csrcs()[0], header.arrOfCSRCs[0]);
|
||||
}
|
||||
|
||||
|
|
|
@ -110,9 +110,10 @@ int main(int argc, char* argv[]) {
|
|||
static_cast<int>(packet->virtual_packet_length_bytes()),
|
||||
packet->header().payloadType, packet->header().markerBit,
|
||||
packet->header().ssrc);
|
||||
if (print_audio_level && packet->header().extension.hasAudioLevel) {
|
||||
fprintf(out_file, " %5u (%1i)", packet->header().extension.audioLevel,
|
||||
packet->header().extension.voiceActivity);
|
||||
if (print_audio_level && packet->header().extension.audio_level()) {
|
||||
fprintf(out_file, " %5d (%1i)",
|
||||
packet->header().extension.audio_level()->level(),
|
||||
packet->header().extension.audio_level()->voice_activity());
|
||||
}
|
||||
if (print_abs_send_time && packet->header().extension.hasAbsoluteSendTime) {
|
||||
if (cycles == -1) {
|
||||
|
|
|
@ -161,24 +161,41 @@ bool AbsoluteCaptureTimeExtension::Write(rtc::ArrayView<uint8_t> data,
|
|||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// Sample Audio Level Encoding Using the Two-Byte Header Format
|
||||
bool AudioLevelExtension::Parse(rtc::ArrayView<const uint8_t> data,
|
||||
bool* voice_activity,
|
||||
uint8_t* audio_level) {
|
||||
AudioLevel* extension) {
|
||||
// One-byte and two-byte format share the same data definition.
|
||||
if (data.size() != 1)
|
||||
return false;
|
||||
*voice_activity = (data[0] & 0x80) != 0;
|
||||
*audio_level = data[0] & 0x7F;
|
||||
bool voice_activity = (data[0] & 0x80) != 0;
|
||||
int audio_level = data[0] & 0x7F;
|
||||
*extension = AudioLevel(voice_activity, audio_level);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioLevelExtension::Write(rtc::ArrayView<uint8_t> data,
|
||||
const AudioLevel& extension) {
|
||||
// One-byte and two-byte format share the same data definition.
|
||||
RTC_DCHECK_EQ(data.size(), 1);
|
||||
RTC_CHECK_GE(extension.level(), 0);
|
||||
RTC_CHECK_LE(extension.level(), 0x7f);
|
||||
data[0] = (extension.voice_activity() ? 0x80 : 0x00) | extension.level();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioLevelExtension::Parse(rtc::ArrayView<const uint8_t> data,
|
||||
bool* voice_activity,
|
||||
uint8_t* audio_level) {
|
||||
AudioLevel extension;
|
||||
Parse(data, &extension);
|
||||
*voice_activity = extension.voice_activity();
|
||||
*audio_level = extension.level();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioLevelExtension::Write(rtc::ArrayView<uint8_t> data,
|
||||
bool voice_activity,
|
||||
uint8_t audio_level) {
|
||||
// One-byte and two-byte format share the same data definition.
|
||||
RTC_DCHECK_EQ(data.size(), 1);
|
||||
RTC_CHECK_LE(audio_level, 0x7f);
|
||||
data[0] = (voice_activity ? 0x80 : 0x00) | audio_level;
|
||||
return true;
|
||||
AudioLevel extension(voice_activity, audio_level);
|
||||
return Write(data, extension);
|
||||
}
|
||||
|
||||
// An RTP Header Extension for Mixer-to-Client Audio Level Indication
|
||||
|
|
|
@ -84,21 +84,30 @@ class AbsoluteCaptureTimeExtension {
|
|||
|
||||
class AudioLevelExtension {
|
||||
public:
|
||||
using value_type = AudioLevel;
|
||||
static constexpr RTPExtensionType kId = kRtpExtensionAudioLevel;
|
||||
static constexpr uint8_t kValueSizeBytes = 1;
|
||||
static constexpr absl::string_view Uri() {
|
||||
return RtpExtension::kAudioLevelUri;
|
||||
}
|
||||
|
||||
static bool Parse(rtc::ArrayView<const uint8_t> data,
|
||||
bool* voice_activity,
|
||||
uint8_t* audio_level);
|
||||
static size_t ValueSize(bool voice_activity, uint8_t audio_level) {
|
||||
static bool Parse(rtc::ArrayView<const uint8_t> data, AudioLevel* extension);
|
||||
static size_t ValueSize(const AudioLevel& extension) {
|
||||
return kValueSizeBytes;
|
||||
}
|
||||
static bool Write(rtc::ArrayView<uint8_t> data,
|
||||
bool voice_activity,
|
||||
uint8_t audio_level);
|
||||
static bool Write(rtc::ArrayView<uint8_t> data, const AudioLevel& extension);
|
||||
|
||||
[[deprecated("Use AudioLevel struct")]] static bool Parse(
|
||||
rtc::ArrayView<const uint8_t> data,
|
||||
bool* voice_activity,
|
||||
uint8_t* audio_level);
|
||||
[[deprecated("Use AudioLevel struct")]] static size_t ValueSize(
|
||||
bool voice_activity,
|
||||
uint8_t audio_level) {
|
||||
return kValueSizeBytes;
|
||||
}
|
||||
[[deprecated("Use AudioLevel struct")]] static bool
|
||||
Write(rtc::ArrayView<uint8_t> data, bool voice_activity, uint8_t audio_level);
|
||||
};
|
||||
|
||||
class CsrcAudioLevel {
|
||||
|
|
|
@ -61,8 +61,7 @@ void RtpPacketReceived::GetHeader(RTPHeader* header) const {
|
|||
&header->extension.feedback_request) ||
|
||||
GetExtension<TransportSequenceNumber>(
|
||||
&header->extension.transportSequenceNumber);
|
||||
header->extension.hasAudioLevel = GetExtension<AudioLevelExtension>(
|
||||
&header->extension.voiceActivity, &header->extension.audioLevel);
|
||||
header->extension.set_audio_level(GetExtension<AudioLevelExtension>());
|
||||
header->extension.hasVideoRotation =
|
||||
GetExtension<VideoOrientation>(&header->extension.videoRotation);
|
||||
header->extension.hasVideoContentType =
|
||||
|
|
|
@ -237,7 +237,8 @@ TEST(RtpPacketTest, CreateWith2Extensions) {
|
|||
packet.SetTimestamp(kTimestamp);
|
||||
packet.SetSsrc(kSsrc);
|
||||
packet.SetExtension<TransmissionOffset>(kTimeOffset);
|
||||
packet.SetExtension<AudioLevelExtension>(kVoiceActive, kAudioLevel);
|
||||
packet.SetExtension<AudioLevelExtension>(
|
||||
AudioLevel(kVoiceActive, kAudioLevel));
|
||||
EXPECT_THAT(kPacketWithTOAndAL,
|
||||
ElementsAreArray(packet.data(), packet.size()));
|
||||
}
|
||||
|
@ -257,7 +258,8 @@ TEST(RtpPacketTest, CreateWithTwoByteHeaderExtensionFirst) {
|
|||
TimeDelta::Millis(340));
|
||||
ASSERT_TRUE(packet.SetExtension<PlayoutDelayLimits>(playout_delay));
|
||||
packet.SetExtension<TransmissionOffset>(kTimeOffset);
|
||||
packet.SetExtension<AudioLevelExtension>(kVoiceActive, kAudioLevel);
|
||||
packet.SetExtension<AudioLevelExtension>(
|
||||
AudioLevel(kVoiceActive, kAudioLevel));
|
||||
EXPECT_THAT(kPacketWithTwoByteExtensionIdFirst,
|
||||
ElementsAreArray(packet.data(), packet.size()));
|
||||
}
|
||||
|
@ -274,7 +276,8 @@ TEST(RtpPacketTest, CreateWithTwoByteHeaderExtensionLast) {
|
|||
packet.SetTimestamp(kTimestamp);
|
||||
packet.SetSsrc(kSsrc);
|
||||
packet.SetExtension<TransmissionOffset>(kTimeOffset);
|
||||
packet.SetExtension<AudioLevelExtension>(kVoiceActive, kAudioLevel);
|
||||
packet.SetExtension<AudioLevelExtension>(
|
||||
AudioLevel(kVoiceActive, kAudioLevel));
|
||||
EXPECT_THAT(kPacketWithTOAndAL,
|
||||
ElementsAreArray(packet.data(), packet.size()));
|
||||
// Set extension that requires two-byte header.
|
||||
|
@ -334,8 +337,8 @@ TEST(RtpPacketTest, TryToCreateTwoByteHeaderNotSupported) {
|
|||
extensions.Register<AudioLevelExtension>(kTwoByteExtensionId);
|
||||
RtpPacketToSend packet(&extensions);
|
||||
// Set extension that requires two-byte header.
|
||||
EXPECT_FALSE(
|
||||
packet.SetExtension<AudioLevelExtension>(kVoiceActive, kAudioLevel));
|
||||
EXPECT_FALSE(packet.SetExtension<AudioLevelExtension>(
|
||||
AudioLevel(kVoiceActive, kAudioLevel)));
|
||||
}
|
||||
|
||||
TEST(RtpPacketTest, CreateTwoByteHeaderSupportedIfExtmapAllowMixed) {
|
||||
|
@ -343,8 +346,8 @@ TEST(RtpPacketTest, CreateTwoByteHeaderSupportedIfExtmapAllowMixed) {
|
|||
extensions.Register<AudioLevelExtension>(kTwoByteExtensionId);
|
||||
RtpPacketToSend packet(&extensions);
|
||||
// Set extension that requires two-byte header.
|
||||
EXPECT_TRUE(
|
||||
packet.SetExtension<AudioLevelExtension>(kVoiceActive, kAudioLevel));
|
||||
EXPECT_TRUE(packet.SetExtension<AudioLevelExtension>(
|
||||
AudioLevel(kVoiceActive, kAudioLevel)));
|
||||
}
|
||||
|
||||
TEST(RtpPacketTest, CreateWithMaxSizeHeaderExtension) {
|
||||
|
@ -409,8 +412,8 @@ TEST(RtpPacketTest, SetReservedExtensionsAfterPayload) {
|
|||
EXPECT_TRUE(packet.ReserveExtension<TransmissionOffset>());
|
||||
packet.SetPayloadSize(kPayloadSize);
|
||||
// Can't set extension after payload.
|
||||
EXPECT_FALSE(
|
||||
packet.SetExtension<AudioLevelExtension>(kVoiceActive, kAudioLevel));
|
||||
EXPECT_FALSE(packet.SetExtension<AudioLevelExtension>(
|
||||
AudioLevel(kVoiceActive, kAudioLevel)));
|
||||
// Unless reserved.
|
||||
EXPECT_TRUE(packet.SetExtension<TransmissionOffset>(kTimeOffset));
|
||||
}
|
||||
|
@ -688,12 +691,10 @@ TEST(RtpPacketTest, ParseWith2Extensions) {
|
|||
int32_t time_offset;
|
||||
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
|
||||
EXPECT_EQ(kTimeOffset, time_offset);
|
||||
bool voice_active;
|
||||
uint8_t audio_level;
|
||||
EXPECT_TRUE(
|
||||
packet.GetExtension<AudioLevelExtension>(&voice_active, &audio_level));
|
||||
EXPECT_EQ(kVoiceActive, voice_active);
|
||||
EXPECT_EQ(kAudioLevel, audio_level);
|
||||
AudioLevel audio_level;
|
||||
EXPECT_TRUE(packet.GetExtension<AudioLevelExtension>(&audio_level));
|
||||
EXPECT_EQ(kVoiceActive, audio_level.voice_activity());
|
||||
EXPECT_EQ(kAudioLevel, audio_level.level());
|
||||
}
|
||||
|
||||
TEST(RtpPacketTest, ParseSecondPacketWithFewerExtensions) {
|
||||
|
@ -721,10 +722,8 @@ TEST(RtpPacketTest, ParseWith2ExtensionsInvalidPadding) {
|
|||
int32_t time_offset;
|
||||
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
|
||||
EXPECT_EQ(kTimeOffset, time_offset);
|
||||
bool voice_active;
|
||||
uint8_t audio_level;
|
||||
EXPECT_FALSE(
|
||||
packet.GetExtension<AudioLevelExtension>(&voice_active, &audio_level));
|
||||
AudioLevel audio_level;
|
||||
EXPECT_FALSE(packet.GetExtension<AudioLevelExtension>(&audio_level));
|
||||
}
|
||||
|
||||
TEST(RtpPacketTest, ParseWith2ExtensionsReservedExtensionId) {
|
||||
|
@ -737,10 +736,8 @@ TEST(RtpPacketTest, ParseWith2ExtensionsReservedExtensionId) {
|
|||
int32_t time_offset;
|
||||
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
|
||||
EXPECT_EQ(kTimeOffset, time_offset);
|
||||
bool voice_active;
|
||||
uint8_t audio_level;
|
||||
EXPECT_FALSE(
|
||||
packet.GetExtension<AudioLevelExtension>(&voice_active, &audio_level));
|
||||
AudioLevel audio_level;
|
||||
EXPECT_FALSE(packet.GetExtension<AudioLevelExtension>(&audio_level));
|
||||
}
|
||||
|
||||
TEST(RtpPacketTest, ParseWithAllFeatures) {
|
||||
|
@ -792,12 +789,10 @@ TEST(RtpPacketTest, ParseTwoByteHeaderExtensionWithPadding) {
|
|||
int32_t time_offset;
|
||||
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
|
||||
EXPECT_EQ(kTimeOffset, time_offset);
|
||||
bool voice_active;
|
||||
uint8_t audio_level;
|
||||
EXPECT_TRUE(
|
||||
packet.GetExtension<AudioLevelExtension>(&voice_active, &audio_level));
|
||||
EXPECT_EQ(kVoiceActive, voice_active);
|
||||
EXPECT_EQ(kAudioLevel, audio_level);
|
||||
AudioLevel audio_level;
|
||||
EXPECT_TRUE(packet.GetExtension<AudioLevelExtension>(&audio_level));
|
||||
EXPECT_EQ(kVoiceActive, audio_level.voice_activity());
|
||||
EXPECT_EQ(kAudioLevel, audio_level.level());
|
||||
}
|
||||
|
||||
TEST(RtpPacketTest, ParseWithExtensionDelayed) {
|
||||
|
@ -1221,7 +1216,8 @@ TEST(RtpPacketTest, RemoveMultipleExtensions) {
|
|||
packet.SetTimestamp(kTimestamp);
|
||||
packet.SetSsrc(kSsrc);
|
||||
packet.SetExtension<TransmissionOffset>(kTimeOffset);
|
||||
packet.SetExtension<AudioLevelExtension>(kVoiceActive, kAudioLevel);
|
||||
packet.SetExtension<AudioLevelExtension>(
|
||||
AudioLevel(kVoiceActive, kAudioLevel));
|
||||
|
||||
EXPECT_THAT(kPacketWithTOAndAL,
|
||||
ElementsAreArray(packet.data(), packet.size()));
|
||||
|
@ -1249,7 +1245,8 @@ TEST(RtpPacketTest, RemoveExtensionPreservesOtherUnregisteredExtensions) {
|
|||
packet.SetTimestamp(kTimestamp);
|
||||
packet.SetSsrc(kSsrc);
|
||||
packet.SetExtension<TransmissionOffset>(kTimeOffset);
|
||||
packet.SetExtension<AudioLevelExtension>(kVoiceActive, kAudioLevel);
|
||||
packet.SetExtension<AudioLevelExtension>(
|
||||
AudioLevel(kVoiceActive, kAudioLevel));
|
||||
|
||||
EXPECT_THAT(kPacketWithTOAndAL,
|
||||
ElementsAreArray(packet.data(), packet.size()));
|
||||
|
|
|
@ -244,8 +244,8 @@ bool RTPSenderAudio::SendAudio(const RtpAudioFrame& frame) {
|
|||
packet->set_capture_time(clock_->CurrentTime());
|
||||
// Set audio level extension, if included.
|
||||
packet->SetExtension<AudioLevelExtension>(
|
||||
frame.type == AudioFrameType::kAudioFrameSpeech,
|
||||
frame.audio_level_dbov.value_or(127));
|
||||
AudioLevel(frame.type == AudioFrameType::kAudioFrameSpeech,
|
||||
frame.audio_level_dbov.value_or(127)));
|
||||
|
||||
if (absolute_capture_time.has_value()) {
|
||||
// It also checks that extension was registered during SDP negotiation. If
|
||||
|
|
|
@ -121,12 +121,11 @@ TEST_F(RtpSenderAudioTest, SendAudioWithAudioLevelExtension) {
|
|||
auto sent_payload = transport_.last_sent_packet().payload();
|
||||
EXPECT_THAT(sent_payload, ElementsAreArray(payload));
|
||||
// Verify AudioLevel extension.
|
||||
bool voice_activity;
|
||||
uint8_t audio_level;
|
||||
AudioLevel audio_level;
|
||||
EXPECT_TRUE(transport_.last_sent_packet().GetExtension<AudioLevelExtension>(
|
||||
&voice_activity, &audio_level));
|
||||
EXPECT_EQ(kAudioLevel, audio_level);
|
||||
EXPECT_FALSE(voice_activity);
|
||||
&audio_level));
|
||||
EXPECT_EQ(kAudioLevel, audio_level.level());
|
||||
EXPECT_FALSE(audio_level.voice_activity());
|
||||
}
|
||||
|
||||
TEST_F(RtpSenderAudioTest, SendAudioWithoutAbsoluteCaptureTime) {
|
||||
|
|
|
@ -333,10 +333,11 @@ bool Convert(std::string inputfile,
|
|||
fprintf(output, " transmission_offset=%d",
|
||||
event.rtp.header.extension.transmissionTimeOffset);
|
||||
}
|
||||
if (event.rtp.header.extension.hasAudioLevel) {
|
||||
if (event.rtp.header.extension.audio_level()) {
|
||||
fprintf(output, " voice_activity=%d",
|
||||
event.rtp.header.extension.voiceActivity);
|
||||
fprintf(output, " audio_level=%u", event.rtp.header.extension.audioLevel);
|
||||
event.rtp.header.extension.audio_level()->voice_activity());
|
||||
fprintf(output, " audio_level=%u",
|
||||
event.rtp.header.extension.audio_level()->level());
|
||||
}
|
||||
if (event.rtp.header.extension.hasVideoRotation) {
|
||||
fprintf(output, " video_rotation=%d",
|
||||
|
@ -367,10 +368,11 @@ bool Convert(std::string inputfile,
|
|||
fprintf(output, " transmission_offset=%d",
|
||||
event.rtp.header.extension.transmissionTimeOffset);
|
||||
}
|
||||
if (event.rtp.header.extension.hasAudioLevel) {
|
||||
if (event.rtp.header.extension.audio_level()) {
|
||||
fprintf(output, " voice_activity=%d",
|
||||
event.rtp.header.extension.voiceActivity);
|
||||
fprintf(output, " audio_level=%u", event.rtp.header.extension.audioLevel);
|
||||
event.rtp.header.extension.audio_level()->voice_activity());
|
||||
fprintf(output, " audio_level=%u",
|
||||
event.rtp.header.extension.audio_level()->level());
|
||||
}
|
||||
if (event.rtp.header.extension.hasVideoRotation) {
|
||||
fprintf(output, " video_rotation=%d",
|
||||
|
|
|
@ -740,11 +740,12 @@ void EventLogAnalyzer::CreateAudioLevelGraph(PacketDirection direction,
|
|||
TimeSeries time_series(GetStreamName(parsed_log_, direction, stream.ssrc),
|
||||
LineStyle::kLine);
|
||||
for (auto& packet : stream.packet_view) {
|
||||
if (packet.header.extension.hasAudioLevel) {
|
||||
if (packet.header.extension.audio_level()) {
|
||||
float x = config_.GetCallTimeSec(packet.log_time());
|
||||
// The audio level is stored in -dBov (so e.g. -10 dBov is stored as 10)
|
||||
// Here we convert it to dBov.
|
||||
float y = static_cast<float>(-packet.header.extension.audioLevel);
|
||||
float y =
|
||||
static_cast<float>(-packet.header.extension.audio_level()->level());
|
||||
time_series.points.emplace_back(TimeSeriesPoint(x, y));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,11 +72,11 @@ void FuzzOneInput(const uint8_t* data, size_t size) {
|
|||
int32_t offset;
|
||||
packet.GetExtension<TransmissionOffset>(&offset);
|
||||
break;
|
||||
case kRtpExtensionAudioLevel:
|
||||
bool voice_activity;
|
||||
uint8_t audio_level;
|
||||
packet.GetExtension<AudioLevelExtension>(&voice_activity, &audio_level);
|
||||
case kRtpExtensionAudioLevel: {
|
||||
AudioLevel audio_level;
|
||||
packet.GetExtension<AudioLevelExtension>(&audio_level);
|
||||
break;
|
||||
}
|
||||
case kRtpExtensionCsrcAudioLevel: {
|
||||
std::vector<uint8_t> audio_levels;
|
||||
packet.GetExtension<CsrcAudioLevel>(&audio_levels);
|
||||
|
|
Loading…
Reference in a new issue