Sync-packet insertion into NetEq4. This is related to r3883 & r4052 for NetEq 3.

BUG=
R=henrik.lundin@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@4850 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
turaj@webrtc.org 2013-09-26 00:27:56 +00:00
parent 6b1e21924a
commit 7b75ac6756
7 changed files with 365 additions and 39 deletions

View File

@ -105,7 +105,8 @@ class NetEq {
kFrameSplitError,
kRedundancySplitError,
kPacketBufferCorruption,
kOversizePacket
kOversizePacket,
kSyncPacketNotAccepted
};
static const int kMaxNumPacketsInBuffer = 240; // TODO(hlundin): Remove.
@ -127,6 +128,18 @@ class NetEq {
int length_bytes,
uint32_t receive_timestamp) = 0;
// Inserts a sync-packet into packet queue. Sync-packets are decoded to
// silence and are intended to keep AV-sync intact in an event of long packet
// losses when Video NACK is enabled but Audio NACK is not. Clients of NetEq
// might insert sync-packet when they observe that buffer level of NetEq is
// decreasing below a certain threshold, defined by the application.
// Sync-packets should have the same payload type as the last audio payload
// type, i.e. they cannot have DTMF or CNG payload type, nor a codec change
// can be implied by inserting a sync-packet.
// Returns kOk on success, kFail on failure.
virtual int InsertSyncPacket(const WebRtcRTPHeader& rtp_header,
uint32_t receive_timestamp) = 0;
// Instructs NetEq to deliver 10 ms of audio data. The data is written to
// |output_audio|, which can hold (at least) |max_length| elements.
// The number of channels that were written to the output is provided in
@ -244,10 +257,6 @@ class NetEq {
virtual int DecodedRtpInfo(int* sequence_number,
uint32_t* timestamp) const = 0;
// Not implemented.
virtual int InsertSyncPacket(const WebRtcRTPHeader& rtp_header,
uint32_t receive_timestamp) = 0;
// Sets the background noise mode.
virtual void SetBackgroundNoiseMode(NetEqBackgroundNoiseMode mode) = 0;

View File

@ -130,7 +130,7 @@ int NetEqImpl::InsertPacket(const WebRtcRTPHeader& rtp_header,
", ssrc=" << rtp_header.header.ssrc <<
", len=" << length_bytes;
int error = InsertPacketInternal(rtp_header, payload, length_bytes,
receive_timestamp);
receive_timestamp, false);
if (error != 0) {
LOG_FERR1(LS_WARNING, InsertPacketInternal, error);
error_code_ = error;
@ -139,6 +139,27 @@ int NetEqImpl::InsertPacket(const WebRtcRTPHeader& rtp_header,
return kOK;
}
int NetEqImpl::InsertSyncPacket(const WebRtcRTPHeader& rtp_header,
uint32_t receive_timestamp) {
CriticalSectionScoped lock(crit_sect_.get());
LOG(LS_VERBOSE) << "InsertPacket-Sync: ts="
<< rtp_header.header.timestamp <<
", sn=" << rtp_header.header.sequenceNumber <<
", pt=" << static_cast<int>(rtp_header.header.payloadType) <<
", ssrc=" << rtp_header.header.ssrc;
const uint8_t kSyncPayload[] = { 's', 'y', 'n', 'c' };
int error = InsertPacketInternal(
rtp_header, kSyncPayload, sizeof(kSyncPayload), receive_timestamp, true);
if (error != 0) {
LOG_FERR1(LS_WARNING, InsertPacketInternal, error);
error_code_ = error;
return kFail;
}
return kOK;
}
int NetEqImpl::GetAudio(size_t max_length, int16_t* output_audio,
int* samples_per_channel, int* num_channels,
NetEqOutputType* type) {
@ -372,11 +393,6 @@ int NetEqImpl::DecodedRtpInfo(int* sequence_number, uint32_t* timestamp) const {
return 0;
}
int NetEqImpl::InsertSyncPacket(const WebRtcRTPHeader& /* rtp_header */,
uint32_t /* receive_timestamp */) {
return kNotImplemented;
}
void NetEqImpl::SetBackgroundNoiseMode(NetEqBackgroundNoiseMode mode) {
CriticalSectionScoped lock(crit_sect_.get());
assert(background_noise_.get());
@ -391,15 +407,34 @@ NetEqBackgroundNoiseMode NetEqImpl::BackgroundNoiseMode() const {
// Methods below this line are private.
int NetEqImpl::InsertPacketInternal(const WebRtcRTPHeader& rtp_header,
const uint8_t* payload,
int length_bytes,
uint32_t receive_timestamp) {
uint32_t receive_timestamp,
bool is_sync_packet) {
if (!payload) {
LOG_F(LS_ERROR) << "payload == NULL";
return kInvalidPointer;
}
// Sanity checks for sync-packets.
if (is_sync_packet) {
if (decoder_database_->IsDtmf(rtp_header.header.payloadType) ||
decoder_database_->IsRed(rtp_header.header.payloadType) ||
decoder_database_->IsComfortNoise(rtp_header.header.payloadType)) {
LOG_F(LS_ERROR) << "Sync-packet with an unacceptable payload type "
<< rtp_header.header.payloadType;
return kSyncPacketNotAccepted;
}
if (first_packet_ ||
rtp_header.header.payloadType != current_rtp_payload_type_ ||
rtp_header.header.ssrc != ssrc_) {
// Even if |current_rtp_payload_type_| is 0xFF, sync-packet isn't
// accepted.
LOG_F(LS_ERROR) << "Changing codec, SSRC or first packet "
"with sync-packet.";
return kSyncPacketNotAccepted;
}
}
PacketList packet_list;
RTPHeader main_header;
{
@ -418,6 +453,7 @@ int NetEqImpl::InsertPacketInternal(const WebRtcRTPHeader& rtp_header,
packet->primary = true;
packet->waiting_time = 0;
packet->payload = new uint8_t[packet->payload_length];
packet->sync_packet = is_sync_packet;
if (!packet->payload) {
LOG_F(LS_ERROR) << "Payload pointer is NULL.";
}
@ -455,11 +491,13 @@ int NetEqImpl::InsertPacketInternal(const WebRtcRTPHeader& rtp_header,
timestamp_scaler_->Reset();
}
// Update RTCP statistics.
rtcp_.Update(main_header, receive_timestamp);
// Update RTCP statistics, only for regular packets.
if (!is_sync_packet)
rtcp_.Update(main_header, receive_timestamp);
// Check for RED payload type, and separate payloads into several packets.
if (decoder_database_->IsRed(main_header.payloadType)) {
assert(!is_sync_packet); // We had a sanity check for this.
if (payload_splitter_->SplitRed(&packet_list) != PayloadSplitter::kOK) {
LOG_FERR1(LS_WARNING, SplitRed, packet_list.size());
PacketBuffer::DeleteAllPackets(&packet_list);
@ -492,6 +530,7 @@ int NetEqImpl::InsertPacketInternal(const WebRtcRTPHeader& rtp_header,
assert(current_packet);
assert(current_packet->payload);
if (decoder_database_->IsDtmf(current_packet->header.payloadType)) {
assert(!current_packet->sync_packet); // We had a sanity check for this.
DtmfEvent event;
int ret = DtmfBuffer::ParseEvent(
current_packet->header.timestamp,
@ -519,7 +558,8 @@ int NetEqImpl::InsertPacketInternal(const WebRtcRTPHeader& rtp_header,
}
// Split payloads into smaller chunks. This also verifies that all payloads
// are of a known payload type.
// are of a known payload type. SplitAudio() method is protected against
// sync-packets.
int ret = payload_splitter_->SplitAudio(&packet_list, *decoder_database_);
if (ret != PayloadSplitter::kOK) {
LOG_FERR1(LS_WARNING, SplitAudio, packet_list.size());
@ -534,8 +574,8 @@ int NetEqImpl::InsertPacketInternal(const WebRtcRTPHeader& rtp_header,
}
}
// Update bandwidth estimate.
if (!packet_list.empty()) {
// Update bandwidth estimate, if the packet is not sync-packet.
if (!packet_list.empty() && !packet_list.front()->sync_packet) {
// The list can be empty here if we got nothing but DTMF payloads.
AudioDecoder* decoder =
decoder_database_->GetDecoder(main_header.payloadType);
@ -1160,7 +1200,18 @@ int NetEqImpl::DecodeLoop(PacketList* packet_list, Operations* operation,
packet_list->pop_front();
int payload_length = packet->payload_length;
int16_t decode_length;
if (!packet->primary) {
if (packet->sync_packet) {
// Decode to silence with the same frame size as the last decode.
LOG(LS_VERBOSE) << "Decoding sync-packet: " <<
" ts=" << packet->header.timestamp <<
", sn=" << packet->header.sequenceNumber <<
", pt=" << static_cast<int>(packet->header.payloadType) <<
", ssrc=" << packet->header.ssrc <<
", len=" << packet->payload_length;
memset(&decoded_buffer_[*decoded_length], 0, decoder_frame_length_ *
decoder->channels() * sizeof(decoded_buffer_[0]));
decode_length = decoder_frame_length_;
} else if (!packet->primary) {
// This is a redundant payload; call the special decoder method.
LOG(LS_VERBOSE) << "Decoding packet (redundant):" <<
" ts=" << packet->header.timestamp <<
@ -1696,8 +1747,8 @@ int NetEqImpl::ExtractPackets(int required_samples, PacketList* packet_list) {
AudioDecoder* decoder = decoder_database_->GetDecoder(
packet->header.payloadType);
if (decoder) {
packet_duration = decoder->PacketDuration(packet->payload,
packet->payload_length);
packet_duration = packet->sync_packet ? decoder_frame_length_ :
decoder->PacketDuration(packet->payload, packet->payload_length);
} else {
LOG_FERR1(LS_WARNING, GetDecoder, packet->header.payloadType) <<
"Could not find a decoder for a packet about to be extracted.";

View File

@ -76,6 +76,18 @@ class NetEqImpl : public webrtc::NetEq {
int length_bytes,
uint32_t receive_timestamp);
// Inserts a sync-packet into packet queue. Sync-packets are decoded to
// silence and are intended to keep AV-sync intact in an event of long packet
// losses when Video NACK is enabled but Audio NACK is not. Clients of NetEq
// might insert sync-packet when they observe that buffer level of NetEq is
// decreasing below a certain threshold, defined by the application.
// Sync-packets should have the same payload type as the last audio payload
// type, i.e. they cannot have DTMF or CNG payload type, nor a codec change
// can be implied by inserting a sync-packet.
// Returns kOk on success, kFail on failure.
virtual int InsertSyncPacket(const WebRtcRTPHeader& rtp_header,
uint32_t receive_timestamp);
// Instructs NetEq to deliver 10 ms of audio data. The data is written to
// |output_audio|, which can hold (at least) |max_length| elements.
// The number of channels that were written to the output is provided in
@ -181,9 +193,6 @@ class NetEqImpl : public webrtc::NetEq {
// Gets background noise mode.
virtual NetEqBackgroundNoiseMode BackgroundNoiseMode() const;
virtual int InsertSyncPacket(const WebRtcRTPHeader& rtp_header,
uint32_t receive_timestamp);
private:
static const int kOutputSizeMs = 10;
static const int kMaxFrameSize = 2880; // 60 ms @ 48 kHz.
@ -196,7 +205,8 @@ class NetEqImpl : public webrtc::NetEq {
int InsertPacketInternal(const WebRtcRTPHeader& rtp_header,
const uint8_t* payload,
int length_bytes,
uint32_t receive_timestamp);
uint32_t receive_timestamp,
bool is_sync_packet);
// Delivers 10 ms of audio data. The data is written to |output|, which can

View File

@ -30,6 +30,20 @@
namespace webrtc {
static bool IsAllZero(const int16_t* buf, int buf_length) {
bool all_zero = true;
for (int n = 0; n < buf_length && all_zero; ++n)
all_zero = buf[n] == 0;
return all_zero;
}
static bool IsAllNonZero(const int16_t* buf, int buf_length) {
bool all_non_zero = true;
for (int n = 0; n < buf_length && all_non_zero; ++n)
all_non_zero = buf[n] != 0;
return all_non_zero;
}
class RefFiles {
public:
RefFiles(const std::string& input_file, const std::string& output_file);
@ -856,4 +870,224 @@ TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(BackgroundNoise)) {
CheckBgnOff(32000, kBgnFade);
EXPECT_EQ(kBgnFade, neteq_->BackgroundNoiseMode());
}
TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(SyncPacketInsert)) {
WebRtcRTPHeader rtp_info;
uint32_t receive_timestamp = 0;
// For the readability use the following payloads instead of the defaults of
// this test.
uint8_t kPcm16WbPayloadType = 1;
uint8_t kCngNbPayloadType = 2;
uint8_t kCngWbPayloadType = 3;
uint8_t kCngSwb32PayloadType = 4;
uint8_t kCngSwb48PayloadType = 5;
uint8_t kAvtPayloadType = 6;
uint8_t kRedPayloadType = 7;
uint8_t kIsacPayloadType = 9; // Payload type 8 is already registered.
// Register decoders.
ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderPCM16Bwb,
kPcm16WbPayloadType));
ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGnb, kCngNbPayloadType));
ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGwb, kCngWbPayloadType));
ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGswb32kHz,
kCngSwb32PayloadType));
ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderCNGswb48kHz,
kCngSwb48PayloadType));
ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderAVT, kAvtPayloadType));
ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderRED, kRedPayloadType));
ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderISAC, kIsacPayloadType));
PopulateRtpInfo(0, 0, &rtp_info);
rtp_info.header.payloadType = kPcm16WbPayloadType;
// The first packet injected cannot be sync-packet.
EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp));
// Payload length of 10 ms PCM16 16 kHz.
const int kPayloadBytes = kBlockSize16kHz * sizeof(int16_t);
uint8_t payload[kPayloadBytes] = {0};
ASSERT_EQ(0, neteq_->InsertPacket(
rtp_info, payload, kPayloadBytes, receive_timestamp));
// Next packet. Last packet contained 10 ms audio.
rtp_info.header.sequenceNumber++;
rtp_info.header.timestamp += kBlockSize16kHz;
receive_timestamp += kBlockSize16kHz;
// Unacceptable payload types CNG, AVT (DTMF), RED.
rtp_info.header.payloadType = kCngNbPayloadType;
EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp));
rtp_info.header.payloadType = kCngWbPayloadType;
EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp));
rtp_info.header.payloadType = kCngSwb32PayloadType;
EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp));
rtp_info.header.payloadType = kCngSwb48PayloadType;
EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp));
rtp_info.header.payloadType = kAvtPayloadType;
EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp));
rtp_info.header.payloadType = kRedPayloadType;
EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp));
// Change of codec cannot be initiated with a sync packet.
rtp_info.header.payloadType = kIsacPayloadType;
EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp));
// Change of SSRC is not allowed with a sync packet.
rtp_info.header.payloadType = kPcm16WbPayloadType;
++rtp_info.header.ssrc;
EXPECT_EQ(-1, neteq_->InsertSyncPacket(rtp_info, receive_timestamp));
--rtp_info.header.ssrc;
EXPECT_EQ(0, neteq_->InsertSyncPacket(rtp_info, receive_timestamp));
}
// First insert several noise like packets, then sync-packets. Decoding all
// packets should not produce error, statistics should not show any packet loss
// and sync-packets should decode to zero.
TEST_F(NetEqDecodingTest, DISABLED_ON_ANDROID(SyncPacketDecode)) {
WebRtcRTPHeader rtp_info;
PopulateRtpInfo(0, 0, &rtp_info);
const int kPayloadBytes = kBlockSize16kHz * sizeof(int16_t);
uint8_t payload[kPayloadBytes];
int16_t decoded[kBlockSize16kHz];
for (int n = 0; n < kPayloadBytes; ++n) {
payload[n] = (rand() & 0xF0) + 1; // Non-zero random sequence.
}
// Insert some packets which decode to noise. We are not interested in
// actual decoded values.
NetEqOutputType output_type;
int num_channels;
int samples_per_channel;
uint32_t receive_timestamp = 0;
int delay_samples = 0;
for (int n = 0; n < 100; ++n) {
ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes,
receive_timestamp));
ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded,
&samples_per_channel, &num_channels,
&output_type));
ASSERT_EQ(kBlockSize16kHz, samples_per_channel);
ASSERT_EQ(1, num_channels);
// Even if there is RTP packet in NetEq's buffer, the first frame pulled
// from NetEq starts with few zero samples. Here we measure this delay.
if (n == 0) {
while(decoded[delay_samples] == 0) delay_samples++;
}
rtp_info.header.sequenceNumber++;
rtp_info.header.timestamp += kBlockSize16kHz;
receive_timestamp += kBlockSize16kHz;
}
const int kNumSyncPackets = 10;
// Insert sync-packets, the decoded sequence should be all-zero.
for (int n = 0; n < kNumSyncPackets; ++n) {
ASSERT_EQ(0, neteq_->InsertSyncPacket(rtp_info, receive_timestamp));
ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded,
&samples_per_channel, &num_channels,
&output_type));
ASSERT_EQ(kBlockSize16kHz, samples_per_channel);
ASSERT_EQ(1, num_channels);
EXPECT_TRUE(IsAllZero(&decoded[delay_samples],
samples_per_channel * num_channels - delay_samples));
delay_samples = 0; // Delay only matters in the first frame.
rtp_info.header.sequenceNumber++;
rtp_info.header.timestamp += kBlockSize16kHz;
receive_timestamp += kBlockSize16kHz;
}
// We insert a regular packet, if sync packet are not correctly buffered then
// network statistics would show some packet loss.
ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes,
receive_timestamp));
ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded,
&samples_per_channel, &num_channels,
&output_type));
// Make sure the last inserted packet is decoded and there are non-zero
// samples.
EXPECT_FALSE(IsAllZero(decoded, samples_per_channel * num_channels));
NetEqNetworkStatistics network_stats;
ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats));
// Expecting a "clean" network.
EXPECT_EQ(0, network_stats.packet_loss_rate);
EXPECT_EQ(0, network_stats.expand_rate);
EXPECT_EQ(0, network_stats.accelerate_rate);
EXPECT_EQ(0, network_stats.preemptive_rate);
}
// Test if the size of the packet buffer reported correctly when containing
// sync packets. Also, test if network packets override sync packets. That is to
// prefer decoding a network packet to a sync packet, if both have same sequence
// number and timestamp.
TEST_F(NetEqDecodingTest,
DISABLED_ON_ANDROID(SyncPacketBufferSizeAndOverridenByNetworkPackets)) {
WebRtcRTPHeader rtp_info;
PopulateRtpInfo(0, 0, &rtp_info);
const int kPayloadBytes = kBlockSize16kHz * sizeof(int16_t);
uint8_t payload[kPayloadBytes];
int16_t decoded[kBlockSize16kHz];
for (int n = 0; n < kPayloadBytes; ++n) {
payload[n] = (rand() & 0xF0) + 1; // Non-zero random sequence.
}
// Insert some packets which decode to noise. We are not interested in
// actual decoded values.
NetEqOutputType output_type;
int num_channels;
int samples_per_channel;
uint32_t receive_timestamp = 0;
for (int n = 0; n < 1; ++n) {
ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes,
receive_timestamp));
ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded,
&samples_per_channel, &num_channels,
&output_type));
ASSERT_EQ(kBlockSize16kHz, samples_per_channel);
ASSERT_EQ(1, num_channels);
rtp_info.header.sequenceNumber++;
rtp_info.header.timestamp += kBlockSize16kHz;
receive_timestamp += kBlockSize16kHz;
}
const int kNumSyncPackets = 10;
WebRtcRTPHeader first_sync_packet_rtp_info;
memcpy(&first_sync_packet_rtp_info, &rtp_info, sizeof(rtp_info));
// Insert sync-packets, but no decoding.
for (int n = 0; n < kNumSyncPackets; ++n) {
ASSERT_EQ(0, neteq_->InsertSyncPacket(rtp_info, receive_timestamp));
rtp_info.header.sequenceNumber++;
rtp_info.header.timestamp += kBlockSize16kHz;
receive_timestamp += kBlockSize16kHz;
}
NetEqNetworkStatistics network_stats;
ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats));
EXPECT_EQ(kNumSyncPackets * 10, network_stats.current_buffer_size_ms);
// Rewind |rtp_info| to that of the first sync packet.
memcpy(&rtp_info, &first_sync_packet_rtp_info, sizeof(rtp_info));
// Insert.
for (int n = 0; n < kNumSyncPackets; ++n) {
ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload, kPayloadBytes,
receive_timestamp));
rtp_info.header.sequenceNumber++;
rtp_info.header.timestamp += kBlockSize16kHz;
receive_timestamp += kBlockSize16kHz;
}
// Decode.
for (int n = 0; n < kNumSyncPackets; ++n) {
ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded,
&samples_per_channel, &num_channels,
&output_type));
ASSERT_EQ(kBlockSize16kHz, samples_per_channel);
ASSERT_EQ(1, num_channels);
EXPECT_TRUE(IsAllNonZero(decoded, samples_per_channel * num_channels));
}
}
} // namespace

View File

@ -25,32 +25,49 @@ struct Packet {
int payload_length;
bool primary; // Primary, i.e., not redundant payload.
int waiting_time;
bool sync_packet;
// Constructor.
Packet()
: payload(NULL),
payload_length(0),
primary(true),
waiting_time(0) {
waiting_time(0),
sync_packet(false) {
}
// Comparison operators. Establish a packet ordering based on (1) timestamp,
// (2) sequence number, and (3) redundancy. Timestamp and sequence numbers
// are compared taking wrap-around into account. If both timestamp and
// sequence numbers are identical, a primary payload is considered "smaller"
// than a secondary.
// (2) sequence number, (3) regular packet vs sync-packet and (4) redundancy.
// Timestamp and sequence numbers are compared taking wrap-around into
// account. If both timestamp and sequence numbers are identical and one of
// the packets is sync-packet, the regular packet is considered earlier. For
// two regular packets with the same sequence number and timestamp a primary
// payload is considered "smaller" than a secondary.
bool operator==(const Packet& rhs) const {
return (this->header.timestamp == rhs.header.timestamp &&
this->header.sequenceNumber == rhs.header.sequenceNumber &&
this->primary == rhs.primary);
this->primary == rhs.primary &&
this->sync_packet == rhs.sync_packet);
}
bool operator!=(const Packet& rhs) const { return !operator==(rhs); }
bool operator<(const Packet& rhs) const {
if (this->header.timestamp == rhs.header.timestamp) {
if (this->header.sequenceNumber == rhs.header.sequenceNumber) {
// Timestamp and sequence numbers are identical. Deem left hand side
// to be "smaller" (i.e., "earlier") if it is primary, and right hand
// side is not.
// Timestamp and sequence numbers are identical. A sync packet should
// be recognized "larger" (i.e. "later") compared to a "network packet"
// (regular packet from network not sync-packet). If none of the packets
// are sync-packets, then deem the left hand side to be "smaller"
// (i.e., "earlier") if it is primary, and right hand side is not.
//
// The condition on sync packets to be larger than "network packets,"
// given same RTP sequence number and timestamp, guarantees that a
// "network packet" to be inserted in an earlier position into
// |packet_buffer_| compared to a sync packet of same timestamp and
// sequence number.
if (rhs.sync_packet)
return true;
if (this->sync_packet)
return false;
return (this->primary && !rhs.primary);
}
return (static_cast<uint16_t>(rhs.header.sequenceNumber

View File

@ -234,19 +234,19 @@ int PacketBuffer::NumSamplesInBuffer(DecoderDatabase* decoder_database,
int last_decoded_length) const {
PacketList::const_iterator it;
int num_samples = 0;
int last_duration = last_decoded_length;
for (it = buffer_.begin(); it != buffer_.end(); ++it) {
Packet* packet = (*it);
AudioDecoder* decoder =
decoder_database->GetDecoder(packet->header.payloadType);
if (decoder) {
int duration = decoder->PacketDuration(packet->payload,
packet->payload_length);
int duration = packet->sync_packet ? last_duration :
decoder->PacketDuration(packet->payload, packet->payload_length);
if (duration >= 0) {
num_samples += duration;
continue; // Go to next packet in loop.
last_duration = duration; // Save the most up-to-date (valid) duration.
}
}
num_samples += last_decoded_length;
num_samples += last_duration;
}
return num_samples;
}

View File

@ -163,6 +163,11 @@ int PayloadSplitter::SplitAudio(PacketList* packet_list,
if (!info) {
return kUnknownPayloadType;
}
// No splitting for a sync-packet.
if (packet->sync_packet) {
++it;
continue;
}
PacketList new_packets;
switch (info->codec_type) {
case kDecoderPCMu: