blob: 839c179ba4b04cc27a2b010ca6551823bd6c5024 [file] [log] [blame]
/*
* Copyright (c) 2012 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.
*/
// This is the implementation of the PacketBuffer class. It is mostly based on
// an STL list. The list is kept sorted at all times so that the next packet to
// decode is at the beginning of the list.
#include "webrtc/modules/audio_coding/neteq4/packet_buffer.h"
#include <algorithm> // find_if()
#include "webrtc/modules/audio_coding/neteq4/decoder_database.h"
#include "webrtc/modules/audio_coding/neteq4/interface/audio_decoder.h"
namespace webrtc {
// Predicate used when inserting packets in the buffer list.
// Operator() returns true when |packet| goes before |new_packet|.
class NewTimestampIsLarger {
public:
explicit NewTimestampIsLarger(const Packet* new_packet)
: new_packet_(new_packet) {
}
bool operator()(Packet* packet) {
return (*new_packet_ >= *packet);
}
private:
const Packet* new_packet_;
};
// Constructor. The arguments define the maximum number of slots and maximum
// payload memory (excluding RTP headers) that the buffer will accept.
PacketBuffer::PacketBuffer(size_t max_number_of_packets,
size_t max_memory_bytes)
: max_number_of_packets_(max_number_of_packets),
max_memory_bytes_(max_memory_bytes),
current_memory_bytes_(0) {
}
// Destructor. All packets in the buffer will be destroyed.
PacketBuffer::~PacketBuffer() {
Flush();
}
// Flush the buffer. All packets in the buffer will be destroyed.
void PacketBuffer::Flush() {
DeleteAllPackets(&buffer_);
current_memory_bytes_ = 0;
}
int PacketBuffer::InsertPacket(Packet* packet) {
if (!packet || !packet->payload) {
if (packet) {
delete packet;
}
return kInvalidPacket;
}
int return_val = kOK;
if ((buffer_.size() >= max_number_of_packets_) ||
(current_memory_bytes_ + packet->payload_length
> static_cast<int>(max_memory_bytes_))) {
// Buffer is full. Flush it.
Flush();
return_val = kFlushed;
if ((buffer_.size() >= max_number_of_packets_) ||
(current_memory_bytes_ + packet->payload_length
> static_cast<int>(max_memory_bytes_))) {
// Buffer is still too small for the packet. Either the buffer limits are
// really small, or the packet is really large. Delete the packet and
// return an error.
delete [] packet->payload;
delete packet;
return kOversizePacket;
}
}
// Get an iterator pointing to the place in the buffer where the new packet
// should be inserted. The list is searched from the back, since the most
// likely case is that the new packet should be near the end of the list.
PacketList::reverse_iterator rit = std::find_if(
buffer_.rbegin(), buffer_.rend(),
NewTimestampIsLarger(packet));
buffer_.insert(rit.base(), packet); // Insert the packet at that position.
current_memory_bytes_ += packet->payload_length;
return return_val;
}
int PacketBuffer::InsertPacketList(PacketList* packet_list,
const DecoderDatabase& decoder_database,
uint8_t* current_rtp_payload_type,
uint8_t* current_cng_rtp_payload_type) {
bool flushed = false;
while (!packet_list->empty()) {
Packet* packet = packet_list->front();
if (decoder_database.IsComfortNoise(packet->header.payloadType)) {
if (*current_cng_rtp_payload_type != 0xFF &&
*current_cng_rtp_payload_type != packet->header.payloadType) {
// New CNG payload type implies new codec type.
*current_rtp_payload_type = 0xFF;
Flush();
flushed = true;
}
*current_cng_rtp_payload_type = packet->header.payloadType;
} else if (!decoder_database.IsDtmf(packet->header.payloadType)) {
// This must be speech.
if (*current_rtp_payload_type != 0xFF &&
*current_rtp_payload_type != packet->header.payloadType) {
*current_cng_rtp_payload_type = 0xFF;
Flush();
flushed = true;
}
*current_rtp_payload_type = packet->header.payloadType;
}
int return_val = InsertPacket(packet);
packet_list->pop_front();
if (return_val == kFlushed) {
// The buffer flushed, but this is not an error. We can still continue.
flushed = true;
} else if (return_val != kOK) {
// An error occurred. Delete remaining packets in list and return.
DeleteAllPackets(packet_list);
return return_val;
}
}
return flushed ? kFlushed : kOK;
}
int PacketBuffer::NextTimestamp(uint32_t* next_timestamp) const {
if (Empty()) {
return kBufferEmpty;
}
if (!next_timestamp) {
return kInvalidPointer;
}
*next_timestamp = buffer_.front()->header.timestamp;
return kOK;
}
int PacketBuffer::NextHigherTimestamp(uint32_t timestamp,
uint32_t* next_timestamp) const {
if (Empty()) {
return kBufferEmpty;
}
if (!next_timestamp) {
return kInvalidPointer;
}
PacketList::const_iterator it;
for (it = buffer_.begin(); it != buffer_.end(); ++it) {
if ((*it)->header.timestamp >= timestamp) {
// Found a packet matching the search.
*next_timestamp = (*it)->header.timestamp;
return kOK;
}
}
return kNotFound;
}
const RTPHeader* PacketBuffer::NextRtpHeader() const {
if (Empty()) {
return NULL;
}
return const_cast<const RTPHeader*>(&(buffer_.front()->header));
}
Packet* PacketBuffer::GetNextPacket(int* discard_count) {
if (Empty()) {
// Buffer is empty.
return NULL;
}
Packet* packet = buffer_.front();
// Assert that the packet sanity checks in InsertPacket method works.
assert(packet && packet->payload);
buffer_.pop_front();
current_memory_bytes_ -= packet->payload_length;
assert(current_memory_bytes_ >= 0); // Assert bookkeeping is correct.
// Discard other packets with the same timestamp. These are duplicates or
// redundant payloads that should not be used.
if (discard_count) {
*discard_count = 0;
}
while (!Empty() &&
buffer_.front()->header.timestamp == packet->header.timestamp) {
if (DiscardNextPacket() != kOK) {
assert(false); // Must be ok by design.
}
if (discard_count) {
++(*discard_count);
}
}
return packet;
}
int PacketBuffer::DiscardNextPacket() {
if (Empty()) {
return kBufferEmpty;
}
Packet* temp_packet = buffer_.front();
// Assert that the packet sanity checks in InsertPacket method works.
assert(temp_packet && temp_packet->payload);
current_memory_bytes_ -= temp_packet->payload_length;
assert(current_memory_bytes_ >= 0); // Assert bookkeeping is correct.
DeleteFirstPacket(&buffer_);
return kOK;
}
int PacketBuffer::DiscardOldPackets(uint32_t timestamp_limit) {
int discard_count = 0;
while (!Empty() &&
timestamp_limit != buffer_.front()->header.timestamp &&
static_cast<uint32_t>(timestamp_limit
- buffer_.front()->header.timestamp) <
0xFFFFFFFF / 2) {
if (DiscardNextPacket() != kOK) {
assert(false); // Must be ok by design.
}
++discard_count;
}
return 0;
}
int PacketBuffer::NumSamplesInBuffer(DecoderDatabase* decoder_database,
int last_decoded_length) const {
PacketList::const_iterator it;
int num_samples = 0;
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);
if (duration >= 0) {
num_samples += duration;
continue; // Go to next packet in loop.
}
}
num_samples += last_decoded_length;
}
return num_samples;
}
void PacketBuffer::IncrementWaitingTimes(int inc) {
PacketList::iterator it;
for (it = buffer_.begin(); it != buffer_.end(); ++it) {
(*it)->waiting_time += inc;
}
}
bool PacketBuffer::DeleteFirstPacket(PacketList* packet_list) {
if (packet_list->empty()) {
return false;
}
Packet* first_packet = packet_list->front();
delete [] first_packet->payload;
delete first_packet;
packet_list->pop_front();
return true;
}
void PacketBuffer::DeleteAllPackets(PacketList* packet_list) {
while (DeleteFirstPacket(packet_list)) {
// Continue while the list is not empty.
}
}
void PacketBuffer::BufferStat(int* num_packest,
int* max_num_packets,
int* current_memory_bytes,
int* max_memory_bytes) const {
*num_packest = buffer_.size();
*max_num_packets = max_number_of_packets_;
*current_memory_bytes = current_memory_bytes_;
*max_memory_bytes = max_memory_bytes_;
}
} // namespace webrtc