Allowing RED decoding for Opus.

BUG=4247
TEST=reproduced and fixed the bug
R=henrik.lundin@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/41809004

Cr-Commit-Position: refs/heads/master@{#8364}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8364 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
minyue@webrtc.org 2015-02-13 14:01:54 +00:00
parent 96e4db9bea
commit a8cc3440b1
8 changed files with 130 additions and 48 deletions

View File

@ -37,7 +37,8 @@ int AudioDecoder::IncomingPacket(const uint8_t* payload,
int AudioDecoder::ErrorCode() { return 0; } int AudioDecoder::ErrorCode() { return 0; }
int AudioDecoder::PacketDuration(const uint8_t* encoded, size_t encoded_len) { int AudioDecoder::PacketDuration(const uint8_t* encoded,
size_t encoded_len) const {
return kNotImplemented; return kNotImplemented;
} }

View File

@ -69,7 +69,7 @@ class AudioDecoder {
// Returns the duration in samples of the payload in |encoded| which is // Returns the duration in samples of the payload in |encoded| which is
// |encoded_len| bytes long. Returns kNotImplemented if no duration estimate // |encoded_len| bytes long. Returns kNotImplemented if no duration estimate
// is available, or -1 in case of an error. // is available, or -1 in case of an error.
virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len) const;
// Returns the duration in samples of the redandant payload in |encoded| which // Returns the duration in samples of the redandant payload in |encoded| which
// is |encoded_len| bytes long. Returns kNotImplemented if no duration // is |encoded_len| bytes long. Returns kNotImplemented if no duration

View File

@ -1134,7 +1134,7 @@ int AudioDecoderProxy::ErrorCode() {
} }
int AudioDecoderProxy::PacketDuration(const uint8_t* encoded, int AudioDecoderProxy::PacketDuration(const uint8_t* encoded,
size_t encoded_len) { size_t encoded_len) const {
CriticalSectionScoped decoder_lock(decoder_lock_.get()); CriticalSectionScoped decoder_lock(decoder_lock_.get());
return decoder_->PacketDuration(encoded, encoded_len); return decoder_->PacketDuration(encoded, encoded_len);
} }

View File

@ -1004,7 +1004,7 @@ class AudioDecoderProxy final : public AudioDecoder {
uint32_t rtp_timestamp, uint32_t rtp_timestamp,
uint32_t arrival_timestamp) override; uint32_t arrival_timestamp) override;
int ErrorCode() override; int ErrorCode() override;
int PacketDuration(const uint8_t* encoded, size_t encoded_len) override; int PacketDuration(const uint8_t* encoded, size_t encoded_len) const override;
int PacketDurationRedundant(const uint8_t* encoded, int PacketDurationRedundant(const uint8_t* encoded,
size_t encoded_len) const override; size_t encoded_len) const override;
bool PacketHasFec(const uint8_t* encoded, size_t encoded_len) const override; bool PacketHasFec(const uint8_t* encoded, size_t encoded_len) const override;

View File

@ -48,7 +48,7 @@ int AudioDecoderPcmU::Decode(const uint8_t* encoded, size_t encoded_len,
} }
int AudioDecoderPcmU::PacketDuration(const uint8_t* encoded, int AudioDecoderPcmU::PacketDuration(const uint8_t* encoded,
size_t encoded_len) { size_t encoded_len) const {
// One encoded byte per sample per channel. // One encoded byte per sample per channel.
return static_cast<int>(encoded_len / channels_); return static_cast<int>(encoded_len / channels_);
} }
@ -64,7 +64,7 @@ int AudioDecoderPcmA::Decode(const uint8_t* encoded, size_t encoded_len,
} }
int AudioDecoderPcmA::PacketDuration(const uint8_t* encoded, int AudioDecoderPcmA::PacketDuration(const uint8_t* encoded,
size_t encoded_len) { size_t encoded_len) const {
// One encoded byte per sample per channel. // One encoded byte per sample per channel.
return static_cast<int>(encoded_len / channels_); return static_cast<int>(encoded_len / channels_);
} }
@ -82,7 +82,7 @@ int AudioDecoderPcm16B::Decode(const uint8_t* encoded, size_t encoded_len,
} }
int AudioDecoderPcm16B::PacketDuration(const uint8_t* encoded, int AudioDecoderPcm16B::PacketDuration(const uint8_t* encoded,
size_t encoded_len) { size_t encoded_len) const {
// Two encoded byte per sample per channel. // Two encoded byte per sample per channel.
return static_cast<int>(encoded_len / (2 * channels_)); return static_cast<int>(encoded_len / (2 * channels_));
} }
@ -148,7 +148,7 @@ int AudioDecoderG722::Init() {
} }
int AudioDecoderG722::PacketDuration(const uint8_t* encoded, int AudioDecoderG722::PacketDuration(const uint8_t* encoded,
size_t encoded_len) { size_t encoded_len) const {
// 1/2 encoded byte per sample per channel. // 1/2 encoded byte per sample per channel.
return static_cast<int>(2 * encoded_len / channels_); return static_cast<int>(2 * encoded_len / channels_);
} }
@ -260,6 +260,11 @@ int AudioDecoderOpus::Decode(const uint8_t* encoded, size_t encoded_len,
int AudioDecoderOpus::DecodeRedundant(const uint8_t* encoded, int AudioDecoderOpus::DecodeRedundant(const uint8_t* encoded,
size_t encoded_len, int16_t* decoded, size_t encoded_len, int16_t* decoded,
SpeechType* speech_type) { SpeechType* speech_type) {
if (!PacketHasFec(encoded, encoded_len)) {
// This packet is a RED packet.
return Decode(encoded, encoded_len, decoded, speech_type);
}
int16_t temp_type = 1; // Default is speech. int16_t temp_type = 1; // Default is speech.
int16_t ret = WebRtcOpus_DecodeFec(dec_state_, encoded, int16_t ret = WebRtcOpus_DecodeFec(dec_state_, encoded,
static_cast<int16_t>(encoded_len), decoded, static_cast<int16_t>(encoded_len), decoded,
@ -275,13 +280,18 @@ int AudioDecoderOpus::Init() {
} }
int AudioDecoderOpus::PacketDuration(const uint8_t* encoded, int AudioDecoderOpus::PacketDuration(const uint8_t* encoded,
size_t encoded_len) { size_t encoded_len) const {
return WebRtcOpus_DurationEst(dec_state_, return WebRtcOpus_DurationEst(dec_state_,
encoded, static_cast<int>(encoded_len)); encoded, static_cast<int>(encoded_len));
} }
int AudioDecoderOpus::PacketDurationRedundant(const uint8_t* encoded, int AudioDecoderOpus::PacketDurationRedundant(const uint8_t* encoded,
size_t encoded_len) const { size_t encoded_len) const {
if (!PacketHasFec(encoded, encoded_len)) {
// This packet is a RED packet.
return PacketDuration(encoded, encoded_len);
}
return WebRtcOpus_FecDurationEst(encoded, static_cast<int>(encoded_len)); return WebRtcOpus_FecDurationEst(encoded, static_cast<int>(encoded_len));
} }

View File

@ -40,7 +40,7 @@ class AudioDecoderPcmU : public AudioDecoder {
virtual int Decode(const uint8_t* encoded, size_t encoded_len, virtual int Decode(const uint8_t* encoded, size_t encoded_len,
int16_t* decoded, SpeechType* speech_type); int16_t* decoded, SpeechType* speech_type);
virtual int Init() { return 0; } virtual int Init() { return 0; }
virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len) const;
private: private:
DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmU); DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmU);
@ -52,7 +52,7 @@ class AudioDecoderPcmA : public AudioDecoder {
virtual int Decode(const uint8_t* encoded, size_t encoded_len, virtual int Decode(const uint8_t* encoded, size_t encoded_len,
int16_t* decoded, SpeechType* speech_type); int16_t* decoded, SpeechType* speech_type);
virtual int Init() { return 0; } virtual int Init() { return 0; }
virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len) const;
private: private:
DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmA); DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcmA);
@ -89,7 +89,7 @@ class AudioDecoderPcm16B : public AudioDecoder {
virtual int Decode(const uint8_t* encoded, size_t encoded_len, virtual int Decode(const uint8_t* encoded, size_t encoded_len,
int16_t* decoded, SpeechType* speech_type); int16_t* decoded, SpeechType* speech_type);
virtual int Init() { return 0; } virtual int Init() { return 0; }
virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len) const;
private: private:
DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcm16B); DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcm16B);
@ -133,7 +133,7 @@ class AudioDecoderG722 : public AudioDecoder {
int16_t* decoded, SpeechType* speech_type); int16_t* decoded, SpeechType* speech_type);
virtual bool HasDecodePlc() const { return false; } virtual bool HasDecodePlc() const { return false; }
virtual int Init(); virtual int Init();
virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len) const;
private: private:
G722DecInst* dec_state_; G722DecInst* dec_state_;
@ -174,7 +174,7 @@ class AudioDecoderOpus : public AudioDecoder {
virtual int DecodeRedundant(const uint8_t* encoded, size_t encoded_len, virtual int DecodeRedundant(const uint8_t* encoded, size_t encoded_len,
int16_t* decoded, SpeechType* speech_type); int16_t* decoded, SpeechType* speech_type);
virtual int Init(); virtual int Init();
virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len); virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len) const;
virtual int PacketDurationRedundant(const uint8_t* encoded, virtual int PacketDurationRedundant(const uint8_t* encoded,
size_t encoded_len) const; size_t encoded_len) const;
virtual bool PacketHasFec(const uint8_t* encoded, size_t encoded_len) const; virtual bool PacketHasFec(const uint8_t* encoded, size_t encoded_len) const;

View File

@ -151,8 +151,11 @@ int PayloadSplitter::SplitFec(PacketList* packet_list,
switch (info->codec_type) { switch (info->codec_type) {
case kDecoderOpus: case kDecoderOpus:
case kDecoderOpus_2ch: { case kDecoderOpus_2ch: {
Packet* new_packet = new Packet; // The main payload of this packet should be decoded as a primary
// payload, even if it comes as a secondary payload in a RED packet.
packet->primary = true;
Packet* new_packet = new Packet;
new_packet->header = packet->header; new_packet->header = packet->header;
int duration = decoder-> int duration = decoder->
PacketDurationRedundant(packet->payload, packet->payload_length); PacketDurationRedundant(packet->payload, packet->payload_length);

View File

@ -32,6 +32,28 @@ static const size_t kRedHeaderLength = 4; // 4 bytes RED header.
static const uint16_t kSequenceNumber = 0; static const uint16_t kSequenceNumber = 0;
static const uint32_t kBaseTimestamp = 0x12345678; static const uint32_t kBaseTimestamp = 0x12345678;
// A possible Opus packet that contains FEC is the following.
// The frame is 20 ms in duration.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |0|0|0|0|1|0|0|0|x|1|x|x|x|x|x|x|x| |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
// | Compressed frame 1 (N-2 bytes)... :
// : |
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
void CreateOpusFecPayload(uint8_t* payload, size_t payload_length,
uint8_t payload_value) {
if (payload_length < 2) {
return;
}
payload[0] = 0x08;
payload[1] = 0x40;
memset(&payload[2], payload_value, payload_length - 2);
}
// RED headers (according to RFC 2198): // RED headers (according to RFC 2198):
// //
// 0 1 2 3 // 0 1 2 3
@ -52,7 +74,8 @@ static const uint32_t kBaseTimestamp = 0x12345678;
// "behind" the the previous payload. // "behind" the the previous payload.
Packet* CreateRedPayload(size_t num_payloads, Packet* CreateRedPayload(size_t num_payloads,
uint8_t* payload_types, uint8_t* payload_types,
int timestamp_offset) { int timestamp_offset,
bool embed_opus_fec = false) {
Packet* packet = new Packet; Packet* packet = new Packet;
packet->header.payloadType = kRedPayloadType; packet->header.payloadType = kRedPayloadType;
packet->header.timestamp = kBaseTimestamp; packet->header.timestamp = kBaseTimestamp;
@ -84,52 +107,34 @@ Packet* CreateRedPayload(size_t num_payloads,
} }
for (size_t i = 0; i < num_payloads; ++i) { for (size_t i = 0; i < num_payloads; ++i) {
// Write |i| to all bytes in each payload. // Write |i| to all bytes in each payload.
memset(payload_ptr, static_cast<int>(i), kPayloadLength); if (embed_opus_fec) {
CreateOpusFecPayload(payload_ptr, kPayloadLength,
static_cast<uint8_t>(i));
} else {
memset(payload_ptr, static_cast<int>(i), kPayloadLength);
}
payload_ptr += kPayloadLength; payload_ptr += kPayloadLength;
} }
packet->payload = payload; packet->payload = payload;
return packet; return packet;
} }
// A possible Opus packet that contains FEC is the following.
// The frame is 20 ms in duration.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |0|0|0|0|1|0|0|0|x|1|x|x|x|x|x|x|x| |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
// | Compressed frame 1 (N-2 bytes)... :
// : |
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Packet* CreateOpusFecPacket(uint8_t payload_type, size_t payload_length,
uint8_t payload_value) {
Packet* packet = new Packet;
packet->header.payloadType = payload_type;
packet->header.timestamp = kBaseTimestamp;
packet->header.sequenceNumber = kSequenceNumber;
packet->payload_length = payload_length;
uint8_t* payload = new uint8_t[packet->payload_length];
payload[0] = 0x08;
payload[1] = 0x40;
memset(&payload[2], payload_value, payload_length - 2);
packet->payload = payload;
return packet;
}
// Create a packet with all payload bytes set to |payload_value|. // Create a packet with all payload bytes set to |payload_value|.
Packet* CreatePacket(uint8_t payload_type, size_t payload_length, Packet* CreatePacket(uint8_t payload_type, size_t payload_length,
uint8_t payload_value) { uint8_t payload_value, bool opus_fec = false) {
Packet* packet = new Packet; Packet* packet = new Packet;
packet->header.payloadType = payload_type; packet->header.payloadType = payload_type;
packet->header.timestamp = kBaseTimestamp; packet->header.timestamp = kBaseTimestamp;
packet->header.sequenceNumber = kSequenceNumber; packet->header.sequenceNumber = kSequenceNumber;
packet->payload_length = payload_length; packet->payload_length = payload_length;
uint8_t* payload = new uint8_t[packet->payload_length]; uint8_t* payload = new uint8_t[packet->payload_length];
memset(payload, payload_value, payload_length);
packet->payload = payload; packet->payload = payload;
if (opus_fec) {
CreateOpusFecPayload(packet->payload, packet->payload_length,
payload_value);
} else {
memset(payload, payload_value, payload_length);
}
return packet; return packet;
} }
@ -726,7 +731,7 @@ TEST(FecPayloadSplitter, MixedPayload) {
decoder_database.RegisterPayload(0, kDecoderOpus); decoder_database.RegisterPayload(0, kDecoderOpus);
decoder_database.RegisterPayload(1, kDecoderPCMu); decoder_database.RegisterPayload(1, kDecoderPCMu);
Packet* packet = CreateOpusFecPacket(0, 10, 0xFF); Packet* packet = CreatePacket(0, 10, 0xFF, true);
packet_list.push_back(packet); packet_list.push_back(packet);
packet = CreatePacket(0, 10, 0); // Non-FEC Opus payload. packet = CreatePacket(0, 10, 0); // Non-FEC Opus payload.
@ -774,4 +779,67 @@ TEST(FecPayloadSplitter, MixedPayload) {
delete packet; delete packet;
} }
TEST(FecPayloadSplitter, EmbedFecInRed) {
PacketList packet_list;
DecoderDatabase decoder_database;
const int kTimestampOffset = 20 * 48; // 20 ms * 48 kHz.
uint8_t payload_types[] = {0, 0};
decoder_database.RegisterPayload(0, kDecoderOpus);
Packet* packet = CreateRedPayload(2, payload_types, kTimestampOffset, true);
packet_list.push_back(packet);
PayloadSplitter splitter;
EXPECT_EQ(PayloadSplitter::kOK,
splitter.SplitRed(&packet_list));
EXPECT_EQ(PayloadSplitter::kOK,
splitter.SplitFec(&packet_list, &decoder_database));
EXPECT_EQ(4u, packet_list.size());
// Check first packet. FEC packet copied from primary payload in RED.
packet = packet_list.front();
EXPECT_EQ(0, packet->header.payloadType);
EXPECT_EQ(kBaseTimestamp - kTimestampOffset, packet->header.timestamp);
EXPECT_EQ(kPayloadLength, packet->payload_length);
EXPECT_FALSE(packet->primary);
EXPECT_EQ(packet->payload[3], 1);
delete [] packet->payload;
delete packet;
packet_list.pop_front();
// Check second packet. Normal packet copied from primary payload in RED.
packet = packet_list.front();
EXPECT_EQ(0, packet->header.payloadType);
EXPECT_EQ(kBaseTimestamp, packet->header.timestamp);
EXPECT_EQ(kPayloadLength, packet->payload_length);
EXPECT_TRUE(packet->primary);
EXPECT_EQ(packet->payload[3], 1);
delete [] packet->payload;
delete packet;
packet_list.pop_front();
// Check third packet. FEC packet copied from secondary payload in RED.
packet = packet_list.front();
EXPECT_EQ(0, packet->header.payloadType);
EXPECT_EQ(kBaseTimestamp - 2 * kTimestampOffset, packet->header.timestamp);
EXPECT_EQ(kPayloadLength, packet->payload_length);
EXPECT_FALSE(packet->primary);
EXPECT_EQ(packet->payload[3], 0);
delete [] packet->payload;
delete packet;
packet_list.pop_front();
// Check fourth packet. Normal packet copied from primary payload in RED.
packet = packet_list.front();
EXPECT_EQ(0, packet->header.payloadType);
EXPECT_EQ(kBaseTimestamp - kTimestampOffset, packet->header.timestamp);
EXPECT_EQ(kPayloadLength, packet->payload_length);
EXPECT_TRUE(packet->primary);
EXPECT_EQ(packet->payload[3], 0);
delete [] packet->payload;
delete packet;
packet_list.pop_front();
}
} // namespace webrtc } // namespace webrtc