Files
platform-external-webrtc/webrtc/modules/audio_coding/neteq4/payload_splitter.cc
henrik.lundin@webrtc.org d94659dc27 Initial upload of NetEq4
This is the first public upload of the new NetEq, version 4.

It has been through extensive internal review during the course of
the project.

TEST=trybots

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@3425 4adac7df-926f-26a2-2b94-8c16560cd09d
2013-01-29 12:09:21 +00:00

369 lines
14 KiB
C++

/*
* 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.
*/
#include "webrtc/modules/audio_coding/neteq4/payload_splitter.h"
#include <assert.h>
#include "webrtc/modules/audio_coding/neteq4/decoder_database.h"
namespace webrtc {
// The method loops through a list of packets {A, B, C, ...}. Each packet is
// split into its corresponding RED payloads, {A1, A2, ...}, which is
// temporarily held in the list |new_packets|.
// When the first packet in |packet_list| has been processed, the orignal packet
// is replaced by the new ones in |new_packets|, so that |packet_list| becomes:
// {A1, A2, ..., B, C, ...}. The method then continues with B, and C, until all
// the original packets have been replaced by their split payloads.
int PayloadSplitter::SplitRed(PacketList* packet_list) {
int ret = kOK;
PacketList::iterator it = packet_list->begin();
while (it != packet_list->end()) {
PacketList new_packets; // An empty list to store the split packets in.
Packet* red_packet = (*it);
assert(red_packet->payload);
uint8_t* payload_ptr = red_packet->payload;
// Read RED headers (according to RFC 2198):
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |F| block PT | timestamp offset | block length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Last RED header:
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// |0| Block PT |
// +-+-+-+-+-+-+-+-+
bool last_block = false;
int sum_length = 0;
while (!last_block) {
Packet* new_packet = new Packet;
new_packet->header = red_packet->header;
// Check the F bit. If F == 0, this was the last block.
last_block = ((*payload_ptr & 0x80) == 0);
// Bits 1 through 7 are payload type.
new_packet->header.payloadType = payload_ptr[0] & 0x7F;
if (last_block) {
// No more header data to read.
++sum_length; // Account for RED header size of 1 byte.
new_packet->payload_length = red_packet->payload_length - sum_length;
new_packet->primary = true; // Last block is always primary.
payload_ptr += 1; // Advance to first payload byte.
} else {
// Bits 8 through 21 are timestamp offset.
int timestamp_offset = (payload_ptr[1] << 6) +
((payload_ptr[2] & 0xFC) >> 2);
new_packet->header.timestamp = red_packet->header.timestamp -
timestamp_offset;
// Bits 22 through 31 are payload length.
new_packet->payload_length = ((payload_ptr[2] & 0x03) << 8) +
payload_ptr[3];
new_packet->primary = false;
payload_ptr += 4; // Advance to next RED header.
}
sum_length += new_packet->payload_length;
sum_length += 4; // Account for RED header size of 4 bytes.
// Store in new list of packets.
new_packets.push_back(new_packet);
}
// Populate the new packets with payload data.
// |payload_ptr| now points at the first payload byte.
PacketList::iterator new_it;
for (new_it = new_packets.begin(); new_it != new_packets.end(); ++new_it) {
int payload_length = (*new_it)->payload_length;
if (payload_ptr + payload_length >
red_packet->payload + red_packet->payload_length) {
// The block lengths in the RED headers do not match the overall packet
// length. Something is corrupt. Discard this and the remaining
// payloads from this packet.
while (new_it != new_packets.end()) {
// Payload should not have been allocated yet.
assert(!(*new_it)->payload);
delete (*new_it);
new_it = new_packets.erase(new_it);
}
ret = kRedLengthMismatch;
break;
}
(*new_it)->payload = new uint8_t[payload_length];
memcpy((*new_it)->payload, payload_ptr, payload_length);
payload_ptr += payload_length;
}
// Reverse the order of the new packets, so that the primary payload is
// always first.
new_packets.reverse();
// Insert new packets into original list, before the element pointed to by
// iterator |it|.
packet_list->splice(it, new_packets, new_packets.begin(),
new_packets.end());
// Delete old packet payload.
delete [] (*it)->payload;
delete (*it);
// Remove |it| from the packet list. This operation effectively moves the
// iterator |it| to the next packet in the list. Thus, we do not have to
// increment it manually.
it = packet_list->erase(it);
}
return ret;
}
int PayloadSplitter::CheckRedPayloads(PacketList* packet_list,
const DecoderDatabase& decoder_database) {
PacketList::iterator it = packet_list->begin();
int main_payload_type = -1;
int num_deleted_packets = 0;
while (it != packet_list->end()) {
uint8_t this_payload_type = (*it)->header.payloadType;
if (!decoder_database.IsDtmf(this_payload_type) &&
!decoder_database.IsComfortNoise(this_payload_type)) {
if (main_payload_type == -1) {
// This is the first packet in the list which is non-DTMF non-CNG.
main_payload_type = this_payload_type;
} else {
if (this_payload_type != main_payload_type) {
// We do not allow redundant payloads of a different type.
// Discard this payload.
delete [] (*it)->payload;
delete (*it);
// Remove |it| from the packet list. This operation effectively
// moves the iterator |it| to the next packet in the list. Thus, we
// do not have to increment it manually.
it = packet_list->erase(it);
++num_deleted_packets;
continue;
}
}
}
++it;
}
return num_deleted_packets;
}
int PayloadSplitter::SplitAudio(PacketList* packet_list,
const DecoderDatabase& decoder_database) {
PacketList::iterator it = packet_list->begin();
// Iterate through all packets in |packet_list|.
while (it != packet_list->end()) {
Packet* packet = (*it); // Just to make the notation more intuitive.
// Get codec type for this payload.
const DecoderDatabase::DecoderInfo* info =
decoder_database.GetDecoderInfo(packet->header.payloadType);
if (!info) {
return kUnknownPayloadType;
}
PacketList new_packets;
switch (info->codec_type) {
case kDecoderPCMu:
case kDecoderPCMa: {
// 8 bytes per ms; 8 timestamps per ms.
SplitBySamples(packet, 8, 8, &new_packets);
break;
}
case kDecoderPCMu_2ch:
case kDecoderPCMa_2ch: {
// 2 * 8 bytes per ms; 8 timestamps per ms.
SplitBySamples(packet, 2 * 8, 8, &new_packets);
break;
}
case kDecoderG722: {
// 8 bytes per ms; 16 timestamps per ms.
SplitBySamples(packet, 8, 16, &new_packets);
break;
}
case kDecoderPCM16B: {
// 16 bytes per ms; 8 timestamps per ms.
SplitBySamples(packet, 16, 8, &new_packets);
break;
}
case kDecoderPCM16Bwb: {
// 32 bytes per ms; 16 timestamps per ms.
SplitBySamples(packet, 32, 16, &new_packets);
break;
}
case kDecoderPCM16Bswb32kHz: {
// 64 bytes per ms; 32 timestamps per ms.
SplitBySamples(packet, 64, 32, &new_packets);
break;
}
case kDecoderPCM16Bswb48kHz: {
// 96 bytes per ms; 48 timestamps per ms.
SplitBySamples(packet, 96, 48, &new_packets);
break;
}
case kDecoderPCM16B_2ch: {
// 2 * 16 bytes per ms; 8 timestamps per ms.
SplitBySamples(packet, 2 * 16, 8, &new_packets);
break;
}
case kDecoderPCM16Bwb_2ch: {
// 2 * 32 bytes per ms; 16 timestamps per ms.
SplitBySamples(packet, 2 * 32, 16, &new_packets);
break;
}
case kDecoderPCM16Bswb32kHz_2ch: {
// 2 * 64 bytes per ms; 32 timestamps per ms.
SplitBySamples(packet, 2 * 64, 32, &new_packets);
break;
}
case kDecoderPCM16Bswb48kHz_2ch: {
// 2 * 96 bytes per ms; 48 timestamps per ms.
SplitBySamples(packet, 2 * 96, 48, &new_packets);
break;
}
case kDecoderPCM16B_5ch: {
// 5 * 16 bytes per ms; 8 timestamps per ms.
SplitBySamples(packet, 5 * 16, 8, &new_packets);
break;
}
case kDecoderILBC: {
int bytes_per_frame;
int timestamps_per_frame;
if (packet->payload_length >= 950) {
return kTooLargePayload;
} else if (packet->payload_length % 38 == 0) {
// 20 ms frames.
bytes_per_frame = 38;
timestamps_per_frame = 160;
} else if (packet->payload_length % 50 == 0) {
// 30 ms frames.
bytes_per_frame = 50;
timestamps_per_frame = 240;
} else {
return kFrameSplitError;
}
int ret = SplitByFrames(packet, bytes_per_frame, timestamps_per_frame,
&new_packets);
if (ret < 0) {
return ret;
} else if (ret == kNoSplit) {
// Do not split at all. Simply advance to the next packet in the list.
++it;
// We do not have any new packets to insert, and should not delete the
// old one. Skip the code after the switch case, and jump straight to
// the next packet in the while loop.
continue;
}
break;
}
default: {
// Do not split at all. Simply advance to the next packet in the list.
++it;
// We do not have any new packets to insert, and should not delete the
// old one. Skip the code after the switch case, and jump straight to
// the next packet in the while loop.
continue;
}
}
// Insert new packets into original list, before the element pointed to by
// iterator |it|.
packet_list->splice(it, new_packets, new_packets.begin(),
new_packets.end());
// Delete old packet payload.
delete [] (*it)->payload;
delete (*it);
// Remove |it| from the packet list. This operation effectively moves the
// iterator |it| to the next packet in the list. Thus, we do not have to
// increment it manually.
it = packet_list->erase(it);
}
return 0;
}
void PayloadSplitter::SplitBySamples(const Packet* packet,
int bytes_per_ms,
int timestamps_per_ms,
PacketList* new_packets) {
assert(packet);
assert(new_packets);
int split_size_bytes = packet->payload_length;
// Find a "chunk size" >= 20 ms and < 40 ms.
int min_chunk_size = bytes_per_ms * 20;
// Reduce the split size by half as long as |split_size_bytes| is at least
// twice the minimum chunk size (so that the resulting size is at least as
// large as the minimum chunk size).
while (split_size_bytes >= 2 * min_chunk_size) {
split_size_bytes >>= 1;
}
int timestamps_per_chunk =
split_size_bytes * timestamps_per_ms / bytes_per_ms;
uint32_t timestamp = packet->header.timestamp;
uint8_t* payload_ptr = packet->payload;
int len = packet->payload_length;
while (len >= (2 * split_size_bytes)) {
Packet* new_packet = new Packet;
new_packet->payload_length = split_size_bytes;
new_packet->header = packet->header;
new_packet->header.timestamp = timestamp;
timestamp += timestamps_per_chunk;
new_packet->primary = packet->primary;
new_packet->payload = new uint8_t[split_size_bytes];
memcpy(new_packet->payload, payload_ptr, split_size_bytes);
payload_ptr += split_size_bytes;
new_packets->push_back(new_packet);
len -= split_size_bytes;
}
if (len > 0) {
Packet* new_packet = new Packet;
new_packet->payload_length = len;
new_packet->header = packet->header;
new_packet->header.timestamp = timestamp;
new_packet->primary = packet->primary;
new_packet->payload = new uint8_t[len];
memcpy(new_packet->payload, payload_ptr, len);
payload_ptr += len;
new_packets->push_back(new_packet);
}
}
int PayloadSplitter::SplitByFrames(const Packet* packet,
int bytes_per_frame,
int timestamps_per_frame,
PacketList* new_packets) {
if (packet->payload_length % bytes_per_frame != 0) {
return kFrameSplitError;
}
int num_frames = packet->payload_length / bytes_per_frame;
if (num_frames == 1) {
// Special case. Do not split the payload.
return kNoSplit;
}
uint32_t timestamp = packet->header.timestamp;
uint8_t* payload_ptr = packet->payload;
int len = packet->payload_length;
while (len > 0) {
assert(len >= bytes_per_frame);
Packet* new_packet = new Packet;
new_packet->payload_length = bytes_per_frame;
new_packet->header = packet->header;
new_packet->header.timestamp = timestamp;
timestamp += timestamps_per_frame;
new_packet->primary = packet->primary;
new_packet->payload = new uint8_t[bytes_per_frame];
memcpy(new_packet->payload, payload_ptr, bytes_per_frame);
payload_ptr += bytes_per_frame;
new_packets->push_back(new_packet);
len -= bytes_per_frame;
}
return kOK;
}
} // namespace webrtc