blob: 0de75532db52fd282c583e3341ee96a9e6c4e90c [file] [log] [blame]
/*
* Copyright (c) 2011 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.
*/
/*
* Class for storing RTP packets.
*/
#include "rtp_packet_history.h"
#include <assert.h>
#include <cstring> // memset
#include "critical_section_wrapper.h"
#include "rtp_utility.h"
#include "trace.h"
namespace webrtc {
RTPPacketHistory::RTPPacketHistory(RtpRtcpClock* clock)
: clock_(*clock),
critsect_(CriticalSectionWrapper::CreateCriticalSection()),
store_(false),
prev_index_(0),
max_packet_length_(0) {
}
RTPPacketHistory::~RTPPacketHistory() {
Free();
delete critsect_;
}
void RTPPacketHistory::SetStorePacketsStatus(bool enable,
uint16_t number_to_store) {
if (enable) {
Allocate(number_to_store);
} else {
Free();
}
}
void RTPPacketHistory::Allocate(uint16_t number_to_store) {
assert(number_to_store > 0);
webrtc::CriticalSectionScoped cs(*critsect_);
if (store_) {
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1,
"SetStorePacketsStatus already set, number: %d", number_to_store);
return;
}
store_ = true;
stored_packets_.resize(number_to_store);
stored_seq_nums_.resize(number_to_store);
stored_lengths_.resize(number_to_store);
stored_times_.resize(number_to_store);
stored_resend_times_.resize(number_to_store);
stored_types_.resize(number_to_store);
}
void RTPPacketHistory::Free() {
webrtc::CriticalSectionScoped cs(*critsect_);
if (!store_) {
return;
}
std::vector<std::vector<uint8_t> >::iterator it;
for (it = stored_packets_.begin(); it != stored_packets_.end(); ++it) {
it->clear();
}
stored_packets_.clear();
stored_seq_nums_.clear();
stored_lengths_.clear();
stored_times_.clear();
stored_resend_times_.clear();
stored_types_.clear();
store_ = false;
prev_index_ = 0;
max_packet_length_ = 0;
}
bool RTPPacketHistory::StorePackets() const {
webrtc::CriticalSectionScoped cs(*critsect_);
return store_;
}
// private, lock should already be taken
void RTPPacketHistory::VerifyAndAllocatePacketLength(uint16_t packet_length) {
assert(packet_length > 0);
if (!store_) {
return;
}
if (packet_length <= max_packet_length_) {
return;
}
std::vector<std::vector<uint8_t> >::iterator it;
for (it = stored_packets_.begin(); it != stored_packets_.end(); ++it) {
it->resize(packet_length);
}
max_packet_length_ = packet_length;
}
int32_t RTPPacketHistory::PutRTPPacket(const uint8_t* packet,
uint16_t packet_length,
uint16_t max_packet_length,
StorageType type) {
if (type == kDontStore) {
return 0;
}
webrtc::CriticalSectionScoped cs(*critsect_);
if (!store_) {
return 0;
}
assert(packet);
assert(packet_length > 3);
VerifyAndAllocatePacketLength(max_packet_length);
if (packet_length > max_packet_length_) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, -1,
"Failed to store RTP packet, length: %d", packet_length);
return -1;
}
const uint16_t seq_num = (packet[2] << 8) + packet[3];
// Store packet
std::vector<std::vector<uint8_t> >::iterator it =
stored_packets_.begin() + prev_index_;
std::copy(packet, packet + packet_length, it->begin());
stored_seq_nums_[prev_index_] = seq_num;
stored_lengths_[prev_index_] = packet_length;
stored_times_[prev_index_] = clock_.GetTimeInMS();
stored_resend_times_[prev_index_] = 0; // packet not resent
stored_types_[prev_index_] = type;
++prev_index_;
if (prev_index_ >= stored_seq_nums_.size()) {
prev_index_ = 0;
}
return 0;
}
bool RTPPacketHistory::HasRTPPacket(uint16_t sequence_number) const {
webrtc::CriticalSectionScoped cs(*critsect_);
if (!store_) {
return false;
}
int32_t index = 0;
bool found = FindSeqNum(sequence_number, &index);
if (!found) {
return false;
}
uint16_t length = stored_lengths_.at(index);
if (length == 0 || length > max_packet_length_) {
// Invalid length.
return false;
}
return true;
}
bool RTPPacketHistory::GetRTPPacket(uint16_t sequence_number,
uint32_t min_elapsed_time_ms,
uint8_t* packet,
uint16_t* packet_length,
uint32_t* stored_time_ms,
StorageType* type) const {
webrtc::CriticalSectionScoped cs(*critsect_);
if (!store_) {
return false;
}
int32_t index = 0;
bool found = FindSeqNum(sequence_number, &index);
if (!found) {
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1,
"No match for getting seqNum %u", sequence_number);
return false;
}
uint16_t length = stored_lengths_.at(index);
if (length == 0 || length > max_packet_length_) {
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1,
"No match for getting seqNum %u, len %d", sequence_number, length);
return false;
}
if (length > *packet_length) {
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1,
"Input buffer too short for packet %u", sequence_number);
return false;
}
// Verify elapsed time since last retrieve.
uint32_t now = clock_.GetTimeInMS();
if (min_elapsed_time_ms > 0 &&
((now - stored_resend_times_.at(index)) < min_elapsed_time_ms)) {
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1,
"Skip getting packet %u, packet recently resent.", sequence_number);
*packet_length = 0;
return true;
}
// Get packet.
std::vector<std::vector<uint8_t> >::const_iterator it_found_packet =
stored_packets_.begin() + index;
std::copy(it_found_packet->begin(), it_found_packet->begin() + length, packet);
*packet_length = stored_lengths_.at(index);
*stored_time_ms = stored_times_.at(index);
*type = stored_types_.at(index);
return true;
}
void RTPPacketHistory::UpdateResendTime(uint16_t sequence_number) {
webrtc::CriticalSectionScoped cs(*critsect_);
if (!store_) {
return;
}
int32_t index = 0;
bool found = FindSeqNum(sequence_number, &index);
if (!found) {
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1,
"Failed to update resend time, seq num: %u.", sequence_number);
return;
}
stored_resend_times_[index] = clock_.GetTimeInMS();
}
// private, lock should already be taken
bool RTPPacketHistory::FindSeqNum(uint16_t sequence_number,
int32_t* index) const {
uint16_t temp_sequence_number = 0;
if (prev_index_ > 0) {
*index = prev_index_ - 1;
temp_sequence_number = stored_seq_nums_[*index];
} else {
*index = stored_seq_nums_.size() - 1;
temp_sequence_number = stored_seq_nums_[*index]; // wrap
}
int32_t idx = (prev_index_ - 1) - (temp_sequence_number - sequence_number);
if (idx >= 0 && idx < static_cast<int>(stored_seq_nums_.size())) {
*index = idx;
temp_sequence_number = stored_seq_nums_[*index];
}
if (temp_sequence_number != sequence_number) {
// We did not found a match, search all.
for (uint16_t m = 0; m < stored_seq_nums_.size(); m++) {
if (stored_seq_nums_[m] == sequence_number) {
*index = m;
temp_sequence_number = stored_seq_nums_[*index];
break;
}
}
}
if (temp_sequence_number == sequence_number) {
// We found a match.
return true;
}
return false;
}
} // namespace webrtc