Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,186 @@
# Copyright (c) 2023 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.
import("../../../webrtc.gni")
visibility = [
":*",
"../:video_coding_legacy",
"../:video_coding_unittests",
]
rtc_library("deprecated_decoding_state") {
sources = [
"decoding_state.cc",
"decoding_state.h",
]
deps = [
":deprecated_frame_buffer",
":deprecated_jitter_buffer_common",
":deprecated_packet",
"../../../common_video",
"../../../modules:module_api_public",
"../../../rtc_base:logging",
]
}
rtc_library("deprecated_event_wrapper") {
sources = [
"event_wrapper.cc",
"event_wrapper.h",
]
deps = [ "../../../rtc_base:rtc_event" ]
}
rtc_library("deprecated_jitter_buffer_common") {
sources = [ "jitter_buffer_common.h" ]
}
rtc_library("deprecated_jitter_buffer") {
sources = [
"jitter_buffer.cc",
"jitter_buffer.h",
]
deps = [
":deprecated_decoding_state",
":deprecated_event_wrapper",
":deprecated_frame_buffer",
":deprecated_jitter_buffer_common",
":deprecated_packet",
"../../../api:field_trials_view",
"../../../api/units:timestamp",
"../../../modules:module_api",
"../../../modules:module_api_public",
"../../../modules/video_coding:video_codec_interface",
"../../../modules/video_coding/timing:inter_frame_delay_variation_calculator",
"../../../modules/video_coding/timing:jitter_estimator",
"../../../rtc_base:checks",
"../../../rtc_base:logging",
"../../../rtc_base:macromagic",
"../../../rtc_base/synchronization:mutex",
"../../../system_wrappers",
]
absl_deps = [ "//third_party/abseil-cpp/absl/memory" ]
}
rtc_library("deprecated_frame_buffer") {
sources = [
"frame_buffer.cc",
"frame_buffer.h",
]
deps = [
":deprecated_jitter_buffer_common",
":deprecated_packet",
":deprecated_session_info",
"../../../api/video:encoded_image",
"../../../api/video:video_rtp_headers",
"../../../modules/video_coding:codec_globals_headers",
"../../../modules/video_coding:encoded_frame",
"../../../rtc_base:checks",
"../../../rtc_base:event_tracer",
"../../../rtc_base:logging",
]
}
rtc_library("deprecated_packet") {
sources = [
"packet.cc",
"packet.h",
]
deps = [
"../../../api:rtp_headers",
"../../../api:rtp_packet_info",
"../../../api/units:timestamp",
"../../../api/video:video_frame_type",
"../../../modules/rtp_rtcp:rtp_rtcp_format",
"../../../modules/rtp_rtcp:rtp_video_header",
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
rtc_library("deprecated_receiver") {
sources = [
"receiver.cc",
"receiver.h",
]
deps = [
":deprecated_event_wrapper",
":deprecated_jitter_buffer",
":deprecated_jitter_buffer_common",
":deprecated_packet",
"../../../api:field_trials_view",
"../../../api/video:encoded_image",
"../../../modules/video_coding",
"../../../modules/video_coding:encoded_frame",
"../../../modules/video_coding:video_codec_interface",
"../../../modules/video_coding/timing:timing_module",
"../../../rtc_base:event_tracer",
"../../../rtc_base:logging",
"../../../rtc_base:safe_conversions",
"../../../system_wrappers",
]
absl_deps = [ "//third_party/abseil-cpp/absl/memory" ]
}
rtc_library("deprecated_session_info") {
deps = [
":deprecated_jitter_buffer_common",
":deprecated_packet",
"../../../modules:module_api",
"../../../modules:module_api_public",
"../../../modules/video_coding:codec_globals_headers",
"../../../rtc_base:logging",
]
sources = [
"session_info.cc",
"session_info.h",
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:variant" ]
}
rtc_library("deprecated_stream_generator") {
deps = [
":deprecated_packet",
"../../../rtc_base:checks",
]
sources = [
"stream_generator.cc",
"stream_generator.h",
]
}
rtc_library("deprecated_unittests") {
testonly = true
sources = [
"decoding_state_unittest.cc",
"jitter_buffer_unittest.cc",
"receiver_unittest.cc",
"session_info_unittest.cc",
]
visibility += [ "../../../modules/*" ]
deps = [
":deprecated_decoding_state",
":deprecated_frame_buffer",
":deprecated_jitter_buffer",
":deprecated_jitter_buffer_common",
":deprecated_packet",
":deprecated_receiver",
":deprecated_session_info",
":deprecated_stream_generator",
"../../../common_video",
"../../../modules/rtp_rtcp:rtp_video_header",
"../../../modules/video_coding:codec_globals_headers",
"../../../modules/video_coding:encoded_frame",
"../../../modules/video_coding/timing:timing_module",
"../../../rtc_base:checks",
"../../../system_wrappers",
"../../../test:scoped_key_value_config",
"../../../test:test_support",
]
absl_deps = [ "//third_party/abseil-cpp/absl/memory" ]
}

View file

@ -0,0 +1,368 @@
/*
* 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.
*/
#include "modules/video_coding/deprecated/decoding_state.h"
#include "common_video/h264/h264_common.h"
#include "modules/include/module_common_types_public.h"
#include "modules/video_coding/deprecated/frame_buffer.h"
#include "modules/video_coding/deprecated/jitter_buffer_common.h"
#include "modules/video_coding/deprecated/packet.h"
#include "rtc_base/logging.h"
namespace webrtc {
VCMDecodingState::VCMDecodingState()
: sequence_num_(0),
time_stamp_(0),
picture_id_(kNoPictureId),
temporal_id_(kNoTemporalIdx),
tl0_pic_id_(kNoTl0PicIdx),
full_sync_(true),
in_initial_state_(true) {
memset(frame_decoded_, 0, sizeof(frame_decoded_));
}
VCMDecodingState::~VCMDecodingState() {}
void VCMDecodingState::Reset() {
// TODO(mikhal): Verify - not always would want to reset the sync
sequence_num_ = 0;
time_stamp_ = 0;
picture_id_ = kNoPictureId;
temporal_id_ = kNoTemporalIdx;
tl0_pic_id_ = kNoTl0PicIdx;
full_sync_ = true;
in_initial_state_ = true;
memset(frame_decoded_, 0, sizeof(frame_decoded_));
received_sps_.clear();
received_pps_.clear();
}
uint32_t VCMDecodingState::time_stamp() const {
return time_stamp_;
}
uint16_t VCMDecodingState::sequence_num() const {
return sequence_num_;
}
bool VCMDecodingState::IsOldFrame(const VCMFrameBuffer* frame) const {
RTC_DCHECK(frame);
if (in_initial_state_)
return false;
return !IsNewerTimestamp(frame->RtpTimestamp(), time_stamp_);
}
bool VCMDecodingState::IsOldPacket(const VCMPacket* packet) const {
RTC_DCHECK(packet);
if (in_initial_state_)
return false;
return !IsNewerTimestamp(packet->timestamp, time_stamp_);
}
void VCMDecodingState::SetState(const VCMFrameBuffer* frame) {
RTC_DCHECK(frame);
RTC_CHECK_GE(frame->GetHighSeqNum(), 0);
if (!UsingFlexibleMode(frame))
UpdateSyncState(frame);
sequence_num_ = static_cast<uint16_t>(frame->GetHighSeqNum());
time_stamp_ = frame->RtpTimestamp();
picture_id_ = frame->PictureId();
temporal_id_ = frame->TemporalId();
tl0_pic_id_ = frame->Tl0PicId();
for (const NaluInfo& nalu : frame->GetNaluInfos()) {
if (nalu.type == H264::NaluType::kPps) {
if (nalu.pps_id < 0) {
RTC_LOG(LS_WARNING) << "Received pps without pps id.";
} else if (nalu.sps_id < 0) {
RTC_LOG(LS_WARNING) << "Received pps without sps id.";
} else {
received_pps_[nalu.pps_id] = nalu.sps_id;
}
} else if (nalu.type == H264::NaluType::kSps) {
if (nalu.sps_id < 0) {
RTC_LOG(LS_WARNING) << "Received sps without sps id.";
} else {
received_sps_.insert(nalu.sps_id);
}
}
}
if (UsingFlexibleMode(frame)) {
uint16_t frame_index = picture_id_ % kFrameDecodedLength;
if (in_initial_state_) {
frame_decoded_cleared_to_ = frame_index;
} else if (frame->FrameType() == VideoFrameType::kVideoFrameKey) {
memset(frame_decoded_, 0, sizeof(frame_decoded_));
frame_decoded_cleared_to_ = frame_index;
} else {
if (AheadOfFramesDecodedClearedTo(frame_index)) {
while (frame_decoded_cleared_to_ != frame_index) {
frame_decoded_cleared_to_ =
(frame_decoded_cleared_to_ + 1) % kFrameDecodedLength;
frame_decoded_[frame_decoded_cleared_to_] = false;
}
}
}
frame_decoded_[frame_index] = true;
}
in_initial_state_ = false;
}
void VCMDecodingState::CopyFrom(const VCMDecodingState& state) {
sequence_num_ = state.sequence_num_;
time_stamp_ = state.time_stamp_;
picture_id_ = state.picture_id_;
temporal_id_ = state.temporal_id_;
tl0_pic_id_ = state.tl0_pic_id_;
full_sync_ = state.full_sync_;
in_initial_state_ = state.in_initial_state_;
frame_decoded_cleared_to_ = state.frame_decoded_cleared_to_;
memcpy(frame_decoded_, state.frame_decoded_, sizeof(frame_decoded_));
received_sps_ = state.received_sps_;
received_pps_ = state.received_pps_;
}
bool VCMDecodingState::UpdateEmptyFrame(const VCMFrameBuffer* frame) {
bool empty_packet = frame->GetHighSeqNum() == frame->GetLowSeqNum();
if (in_initial_state_ && empty_packet) {
// Drop empty packets as long as we are in the initial state.
return true;
}
if ((empty_packet && ContinuousSeqNum(frame->GetHighSeqNum())) ||
ContinuousFrame(frame)) {
// Continuous empty packets or continuous frames can be dropped if we
// advance the sequence number.
sequence_num_ = frame->GetHighSeqNum();
time_stamp_ = frame->RtpTimestamp();
return true;
}
return false;
}
void VCMDecodingState::UpdateOldPacket(const VCMPacket* packet) {
RTC_DCHECK(packet);
if (packet->timestamp == time_stamp_) {
// Late packet belonging to the last decoded frame - make sure we update the
// last decoded sequence number.
sequence_num_ = LatestSequenceNumber(packet->seqNum, sequence_num_);
}
}
void VCMDecodingState::SetSeqNum(uint16_t new_seq_num) {
sequence_num_ = new_seq_num;
}
bool VCMDecodingState::in_initial_state() const {
return in_initial_state_;
}
bool VCMDecodingState::full_sync() const {
return full_sync_;
}
void VCMDecodingState::UpdateSyncState(const VCMFrameBuffer* frame) {
if (in_initial_state_)
return;
if (frame->TemporalId() == kNoTemporalIdx ||
frame->Tl0PicId() == kNoTl0PicIdx) {
full_sync_ = true;
} else if (frame->FrameType() == VideoFrameType::kVideoFrameKey ||
frame->LayerSync()) {
full_sync_ = true;
} else if (full_sync_) {
// Verify that we are still in sync.
// Sync will be broken if continuity is true for layers but not for the
// other methods (PictureId and SeqNum).
if (UsingPictureId(frame)) {
// First check for a valid tl0PicId.
if (frame->Tl0PicId() - tl0_pic_id_ > 1) {
full_sync_ = false;
} else {
full_sync_ = ContinuousPictureId(frame->PictureId());
}
} else {
full_sync_ =
ContinuousSeqNum(static_cast<uint16_t>(frame->GetLowSeqNum()));
}
}
}
bool VCMDecodingState::ContinuousFrame(const VCMFrameBuffer* frame) const {
// Check continuity based on the following hierarchy:
// - Temporal layers (stop here if out of sync).
// - Picture Id when available.
// - Sequence numbers.
// Return true when in initial state.
// Note that when a method is not applicable it will return false.
RTC_DCHECK(frame);
// A key frame is always considered continuous as it doesn't refer to any
// frames and therefore won't introduce any errors even if prior frames are
// missing.
if (frame->FrameType() == VideoFrameType::kVideoFrameKey &&
HaveSpsAndPps(frame->GetNaluInfos())) {
return true;
}
// When in the initial state we always require a key frame to start decoding.
if (in_initial_state_)
return false;
if (ContinuousLayer(frame->TemporalId(), frame->Tl0PicId()))
return true;
// tl0picId is either not used, or should remain unchanged.
if (frame->Tl0PicId() != tl0_pic_id_)
return false;
// Base layers are not continuous or temporal layers are inactive.
// In the presence of temporal layers, check for Picture ID/sequence number
// continuity if sync can be restored by this frame.
if (!full_sync_ && !frame->LayerSync())
return false;
if (UsingPictureId(frame)) {
if (UsingFlexibleMode(frame)) {
return ContinuousFrameRefs(frame);
} else {
return ContinuousPictureId(frame->PictureId());
}
} else {
return ContinuousSeqNum(static_cast<uint16_t>(frame->GetLowSeqNum())) &&
HaveSpsAndPps(frame->GetNaluInfos());
}
}
bool VCMDecodingState::ContinuousPictureId(int picture_id) const {
int next_picture_id = picture_id_ + 1;
if (picture_id < picture_id_) {
// Wrap
if (picture_id_ >= 0x80) {
// 15 bits used for picture id
return ((next_picture_id & 0x7FFF) == picture_id);
} else {
// 7 bits used for picture id
return ((next_picture_id & 0x7F) == picture_id);
}
}
// No wrap
return (next_picture_id == picture_id);
}
bool VCMDecodingState::ContinuousSeqNum(uint16_t seq_num) const {
return seq_num == static_cast<uint16_t>(sequence_num_ + 1);
}
bool VCMDecodingState::ContinuousLayer(int temporal_id, int tl0_pic_id) const {
// First, check if applicable.
if (temporal_id == kNoTemporalIdx || tl0_pic_id == kNoTl0PicIdx)
return false;
// If this is the first frame to use temporal layers, make sure we start
// from base.
else if (tl0_pic_id_ == kNoTl0PicIdx && temporal_id_ == kNoTemporalIdx &&
temporal_id == 0)
return true;
// Current implementation: Look for base layer continuity.
if (temporal_id != 0)
return false;
return (static_cast<uint8_t>(tl0_pic_id_ + 1) == tl0_pic_id);
}
bool VCMDecodingState::ContinuousFrameRefs(const VCMFrameBuffer* frame) const {
uint8_t num_refs = frame->CodecSpecific()->codecSpecific.VP9.num_ref_pics;
for (uint8_t r = 0; r < num_refs; ++r) {
uint16_t frame_ref = frame->PictureId() -
frame->CodecSpecific()->codecSpecific.VP9.p_diff[r];
uint16_t frame_index = frame_ref % kFrameDecodedLength;
if (AheadOfFramesDecodedClearedTo(frame_index) ||
!frame_decoded_[frame_index]) {
return false;
}
}
return true;
}
bool VCMDecodingState::UsingPictureId(const VCMFrameBuffer* frame) const {
return (frame->PictureId() != kNoPictureId && picture_id_ != kNoPictureId);
}
bool VCMDecodingState::UsingFlexibleMode(const VCMFrameBuffer* frame) const {
bool is_flexible_mode =
frame->CodecSpecific()->codecType == kVideoCodecVP9 &&
frame->CodecSpecific()->codecSpecific.VP9.flexible_mode;
if (is_flexible_mode && frame->PictureId() == kNoPictureId) {
RTC_LOG(LS_WARNING) << "Frame is marked as using flexible mode but no"
"picture id is set.";
return false;
}
return is_flexible_mode;
}
// TODO(philipel): change how check work, this check practially
// limits the max p_diff to 64.
bool VCMDecodingState::AheadOfFramesDecodedClearedTo(uint16_t index) const {
// No way of knowing for sure if we are actually ahead of
// frame_decoded_cleared_to_. We just make the assumption
// that we are not trying to reference back to a very old
// index, but instead are referencing a newer index.
uint16_t diff =
index > frame_decoded_cleared_to_
? kFrameDecodedLength - (index - frame_decoded_cleared_to_)
: frame_decoded_cleared_to_ - index;
return diff > kFrameDecodedLength / 2;
}
bool VCMDecodingState::HaveSpsAndPps(const std::vector<NaluInfo>& nalus) const {
std::set<int> new_sps;
std::map<int, int> new_pps;
for (const NaluInfo& nalu : nalus) {
// Check if this nalu actually contains sps/pps information or dependencies.
if (nalu.sps_id == -1 && nalu.pps_id == -1)
continue;
switch (nalu.type) {
case H264::NaluType::kPps:
if (nalu.pps_id < 0) {
RTC_LOG(LS_WARNING) << "Received pps without pps id.";
} else if (nalu.sps_id < 0) {
RTC_LOG(LS_WARNING) << "Received pps without sps id.";
} else {
new_pps[nalu.pps_id] = nalu.sps_id;
}
break;
case H264::NaluType::kSps:
if (nalu.sps_id < 0) {
RTC_LOG(LS_WARNING) << "Received sps without sps id.";
} else {
new_sps.insert(nalu.sps_id);
}
break;
default: {
int needed_sps = -1;
auto pps_it = new_pps.find(nalu.pps_id);
if (pps_it != new_pps.end()) {
needed_sps = pps_it->second;
} else {
auto pps_it2 = received_pps_.find(nalu.pps_id);
if (pps_it2 == received_pps_.end()) {
return false;
}
needed_sps = pps_it2->second;
}
if (new_sps.find(needed_sps) == new_sps.end() &&
received_sps_.find(needed_sps) == received_sps_.end()) {
return false;
}
break;
}
}
}
return true;
}
} // namespace webrtc

View file

@ -0,0 +1,89 @@
/*
* 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.
*/
#ifndef MODULES_VIDEO_CODING_DEPRECATED_DECODING_STATE_H_
#define MODULES_VIDEO_CODING_DEPRECATED_DECODING_STATE_H_
#include <cstdint>
#include <map>
#include <set>
#include <vector>
namespace webrtc {
// Forward declarations
struct NaluInfo;
class VCMFrameBuffer;
class VCMPacket;
class VCMDecodingState {
public:
// The max number of bits used to reference back
// to a previous frame when using flexible mode.
static const uint16_t kNumRefBits = 7;
static const uint16_t kFrameDecodedLength = 1 << kNumRefBits;
VCMDecodingState();
~VCMDecodingState();
// Check for old frame
bool IsOldFrame(const VCMFrameBuffer* frame) const;
// Check for old packet
bool IsOldPacket(const VCMPacket* packet) const;
// Check for frame continuity based on current decoded state. Use best method
// possible, i.e. temporal info, picture ID or sequence number.
bool ContinuousFrame(const VCMFrameBuffer* frame) const;
void SetState(const VCMFrameBuffer* frame);
void CopyFrom(const VCMDecodingState& state);
bool UpdateEmptyFrame(const VCMFrameBuffer* frame);
// Update the sequence number if the timestamp matches current state and the
// sequence number is higher than the current one. This accounts for packets
// arriving late.
void UpdateOldPacket(const VCMPacket* packet);
void SetSeqNum(uint16_t new_seq_num);
void Reset();
uint32_t time_stamp() const;
uint16_t sequence_num() const;
// Return true if at initial state.
bool in_initial_state() const;
// Return true when sync is on - decode all layers.
bool full_sync() const;
private:
void UpdateSyncState(const VCMFrameBuffer* frame);
// Designated continuity functions
bool ContinuousPictureId(int picture_id) const;
bool ContinuousSeqNum(uint16_t seq_num) const;
bool ContinuousLayer(int temporal_id, int tl0_pic_id) const;
bool ContinuousFrameRefs(const VCMFrameBuffer* frame) const;
bool UsingPictureId(const VCMFrameBuffer* frame) const;
bool UsingFlexibleMode(const VCMFrameBuffer* frame) const;
bool AheadOfFramesDecodedClearedTo(uint16_t index) const;
bool HaveSpsAndPps(const std::vector<NaluInfo>& nalus) const;
// Keep state of last decoded frame.
// TODO(mikhal/stefan): create designated classes to handle these types.
uint16_t sequence_num_;
uint32_t time_stamp_;
int picture_id_;
int temporal_id_;
int tl0_pic_id_;
bool full_sync_; // Sync flag when temporal layers are used.
bool in_initial_state_;
// Used to check references in flexible mode.
bool frame_decoded_[kFrameDecodedLength];
uint16_t frame_decoded_cleared_to_;
std::set<int> received_sps_;
std::map<int, int> received_pps_;
};
} // namespace webrtc
#endif // MODULES_VIDEO_CODING_DEPRECATED_DECODING_STATE_H_

View file

@ -0,0 +1,712 @@
/*
* 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.
*/
#include "modules/video_coding/deprecated/decoding_state.h"
#include "modules/rtp_rtcp/source/rtp_video_header.h"
#include "modules/video_coding/codecs/interface/common_constants.h"
#include "modules/video_coding/codecs/vp8/include/vp8_globals.h"
#include "modules/video_coding/codecs/vp9/include/vp9_globals.h"
#include "modules/video_coding/deprecated/frame_buffer.h"
#include "modules/video_coding/deprecated/packet.h"
#include "modules/video_coding/deprecated/session_info.h"
#include "test/gtest.h"
namespace webrtc {
TEST(TestDecodingState, Sanity) {
VCMDecodingState dec_state;
dec_state.Reset();
EXPECT_TRUE(dec_state.in_initial_state());
EXPECT_TRUE(dec_state.full_sync());
}
TEST(TestDecodingState, FrameContinuity) {
VCMDecodingState dec_state;
// Check that makes decision based on correct method.
VCMFrameBuffer frame;
VCMFrameBuffer frame_key;
VCMPacket packet;
packet.video_header.is_first_packet_in_frame = true;
packet.timestamp = 1;
packet.seqNum = 0xffff;
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
packet.video_header.codec = kVideoCodecVP8;
auto& vp8_header =
packet.video_header.video_type_header.emplace<RTPVideoHeaderVP8>();
vp8_header.pictureId = 0x007F;
FrameData frame_data;
frame_data.rtt_ms = 0;
frame_data.rolling_average_packets_per_frame = -1;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
// Always start with a key frame.
dec_state.Reset();
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
packet.video_header.frame_type = VideoFrameType::kVideoFrameKey;
EXPECT_LE(0, frame_key.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame_key));
dec_state.SetState(&frame);
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
// Use pictureId
packet.video_header.is_first_packet_in_frame = false;
vp8_header.pictureId = 0x0002;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
frame.Reset();
vp8_header.pictureId = 0;
packet.seqNum = 10;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
// Use sequence numbers.
vp8_header.pictureId = kNoPictureId;
frame.Reset();
packet.seqNum = dec_state.sequence_num() - 1u;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
frame.Reset();
packet.seqNum = dec_state.sequence_num() + 1u;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
// Insert another packet to this frame
packet.seqNum++;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
// Verify wrap.
EXPECT_LE(dec_state.sequence_num(), 0xffff);
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
// Insert packet with temporal info.
dec_state.Reset();
frame.Reset();
vp8_header.tl0PicIdx = 0;
vp8_header.temporalIdx = 0;
vp8_header.pictureId = 0;
packet.seqNum = 1;
packet.timestamp = 1;
EXPECT_TRUE(dec_state.full_sync());
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
dec_state.SetState(&frame);
EXPECT_TRUE(dec_state.full_sync());
frame.Reset();
// 1 layer up - still good.
vp8_header.tl0PicIdx = 0;
vp8_header.temporalIdx = 1;
vp8_header.pictureId = 1;
packet.seqNum = 2;
packet.timestamp = 2;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
EXPECT_TRUE(dec_state.full_sync());
frame.Reset();
// Lost non-base layer packet => should update sync parameter.
vp8_header.tl0PicIdx = 0;
vp8_header.temporalIdx = 3;
vp8_header.pictureId = 3;
packet.seqNum = 4;
packet.timestamp = 4;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
// Now insert the next non-base layer (belonging to a next tl0PicId).
frame.Reset();
vp8_header.tl0PicIdx = 1;
vp8_header.temporalIdx = 2;
vp8_header.pictureId = 4;
packet.seqNum = 5;
packet.timestamp = 5;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
// Checking continuity and not updating the state - this should not trigger
// an update of sync state.
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
EXPECT_TRUE(dec_state.full_sync());
// Next base layer (dropped interim non-base layers) - should update sync.
frame.Reset();
vp8_header.tl0PicIdx = 1;
vp8_header.temporalIdx = 0;
vp8_header.pictureId = 5;
packet.seqNum = 6;
packet.timestamp = 6;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
EXPECT_FALSE(dec_state.full_sync());
// Check wrap for temporal layers.
frame.Reset();
vp8_header.tl0PicIdx = 0x00FF;
vp8_header.temporalIdx = 0;
vp8_header.pictureId = 6;
packet.seqNum = 7;
packet.timestamp = 7;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
dec_state.SetState(&frame);
EXPECT_FALSE(dec_state.full_sync());
frame.Reset();
vp8_header.tl0PicIdx = 0x0000;
vp8_header.temporalIdx = 0;
vp8_header.pictureId = 7;
packet.seqNum = 8;
packet.timestamp = 8;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
// The current frame is not continuous
dec_state.SetState(&frame);
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
}
TEST(TestDecodingState, UpdateOldPacket) {
VCMDecodingState dec_state;
// Update only if zero size and newer than previous.
// Should only update if the timeStamp match.
VCMFrameBuffer frame;
VCMPacket packet;
packet.timestamp = 1;
packet.seqNum = 1;
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
FrameData frame_data;
frame_data.rtt_ms = 0;
frame_data.rolling_average_packets_per_frame = -1;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
dec_state.SetState(&frame);
EXPECT_EQ(dec_state.sequence_num(), 1);
// Insert an empty packet that does not belong to the same frame.
// => Sequence num should be the same.
packet.timestamp = 2;
dec_state.UpdateOldPacket(&packet);
EXPECT_EQ(dec_state.sequence_num(), 1);
// Now insert empty packet belonging to the same frame.
packet.timestamp = 1;
packet.seqNum = 2;
packet.video_header.frame_type = VideoFrameType::kEmptyFrame;
packet.sizeBytes = 0;
dec_state.UpdateOldPacket(&packet);
EXPECT_EQ(dec_state.sequence_num(), 2);
// Now insert delta packet belonging to the same frame.
packet.timestamp = 1;
packet.seqNum = 3;
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
packet.sizeBytes = 1400;
dec_state.UpdateOldPacket(&packet);
EXPECT_EQ(dec_state.sequence_num(), 3);
// Insert a packet belonging to an older timestamp - should not update the
// sequence number.
packet.timestamp = 0;
packet.seqNum = 4;
packet.video_header.frame_type = VideoFrameType::kEmptyFrame;
packet.sizeBytes = 0;
dec_state.UpdateOldPacket(&packet);
EXPECT_EQ(dec_state.sequence_num(), 3);
}
TEST(TestDecodingState, MultiLayerBehavior) {
// Identify sync/non-sync when more than one layer.
VCMDecodingState dec_state;
// Identify packets belonging to old frames/packets.
// Set state for current frames.
// tl0PicIdx 0, temporal id 0.
VCMFrameBuffer frame;
VCMPacket packet;
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
packet.video_header.codec = kVideoCodecVP8;
packet.timestamp = 0;
packet.seqNum = 0;
auto& vp8_header =
packet.video_header.video_type_header.emplace<RTPVideoHeaderVP8>();
vp8_header.tl0PicIdx = 0;
vp8_header.temporalIdx = 0;
vp8_header.pictureId = 0;
FrameData frame_data;
frame_data.rtt_ms = 0;
frame_data.rolling_average_packets_per_frame = -1;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
dec_state.SetState(&frame);
// tl0PicIdx 0, temporal id 1.
frame.Reset();
packet.timestamp = 1;
packet.seqNum = 1;
vp8_header.tl0PicIdx = 0;
vp8_header.temporalIdx = 1;
vp8_header.pictureId = 1;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
EXPECT_TRUE(dec_state.full_sync());
// Lost tl0PicIdx 0, temporal id 2.
// Insert tl0PicIdx 0, temporal id 3.
frame.Reset();
packet.timestamp = 3;
packet.seqNum = 3;
vp8_header.tl0PicIdx = 0;
vp8_header.temporalIdx = 3;
vp8_header.pictureId = 3;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
EXPECT_FALSE(dec_state.full_sync());
// Insert next base layer
frame.Reset();
packet.timestamp = 4;
packet.seqNum = 4;
vp8_header.tl0PicIdx = 1;
vp8_header.temporalIdx = 0;
vp8_header.pictureId = 4;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
EXPECT_FALSE(dec_state.full_sync());
// Insert key frame - should update sync value.
// A key frame is always a base layer.
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameKey;
packet.video_header.is_first_packet_in_frame = true;
packet.timestamp = 5;
packet.seqNum = 5;
vp8_header.tl0PicIdx = 2;
vp8_header.temporalIdx = 0;
vp8_header.pictureId = 5;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
EXPECT_TRUE(dec_state.full_sync());
// After sync, a continuous PictureId is required
// (continuous base layer is not enough )
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
packet.timestamp = 6;
packet.seqNum = 6;
vp8_header.tl0PicIdx = 3;
vp8_header.temporalIdx = 0;
vp8_header.pictureId = 6;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
EXPECT_TRUE(dec_state.full_sync());
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
packet.video_header.is_first_packet_in_frame = true;
packet.timestamp = 8;
packet.seqNum = 8;
vp8_header.tl0PicIdx = 4;
vp8_header.temporalIdx = 0;
vp8_header.pictureId = 8;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
EXPECT_TRUE(dec_state.full_sync());
dec_state.SetState(&frame);
EXPECT_FALSE(dec_state.full_sync());
// Insert a non-ref frame - should update sync value.
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
packet.video_header.is_first_packet_in_frame = true;
packet.timestamp = 9;
packet.seqNum = 9;
vp8_header.tl0PicIdx = 4;
vp8_header.temporalIdx = 2;
vp8_header.pictureId = 9;
vp8_header.layerSync = true;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
dec_state.SetState(&frame);
EXPECT_TRUE(dec_state.full_sync());
// The following test will verify the sync flag behavior after a loss.
// Create the following pattern:
// Update base layer, lose packet 1 (sync flag on, layer 2), insert packet 3
// (sync flag on, layer 2) check continuity and sync flag after inserting
// packet 2 (sync flag on, layer 1).
// Base layer.
frame.Reset();
dec_state.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
packet.video_header.is_first_packet_in_frame = true;
packet.markerBit = 1;
packet.timestamp = 0;
packet.seqNum = 0;
vp8_header.tl0PicIdx = 0;
vp8_header.temporalIdx = 0;
vp8_header.pictureId = 0;
vp8_header.layerSync = false;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
dec_state.SetState(&frame);
EXPECT_TRUE(dec_state.full_sync());
// Layer 2 - 2 packets (insert one, lose one).
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
packet.video_header.is_first_packet_in_frame = true;
packet.markerBit = 0;
packet.timestamp = 1;
packet.seqNum = 1;
vp8_header.tl0PicIdx = 0;
vp8_header.temporalIdx = 2;
vp8_header.pictureId = 1;
vp8_header.layerSync = true;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
// Layer 1
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
packet.video_header.is_first_packet_in_frame = true;
packet.markerBit = 1;
packet.timestamp = 2;
packet.seqNum = 3;
vp8_header.tl0PicIdx = 0;
vp8_header.temporalIdx = 1;
vp8_header.pictureId = 2;
vp8_header.layerSync = true;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
EXPECT_TRUE(dec_state.full_sync());
}
TEST(TestDecodingState, DiscontinuousPicIdContinuousSeqNum) {
VCMDecodingState dec_state;
VCMFrameBuffer frame;
VCMPacket packet;
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameKey;
packet.video_header.codec = kVideoCodecVP8;
packet.timestamp = 0;
packet.seqNum = 0;
auto& vp8_header =
packet.video_header.video_type_header.emplace<RTPVideoHeaderVP8>();
vp8_header.tl0PicIdx = 0;
vp8_header.temporalIdx = 0;
vp8_header.pictureId = 0;
FrameData frame_data;
frame_data.rtt_ms = 0;
frame_data.rolling_average_packets_per_frame = -1;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
dec_state.SetState(&frame);
EXPECT_TRUE(dec_state.full_sync());
// Continuous sequence number but discontinuous picture id. This implies a
// a loss and we have to fall back to only decoding the base layer.
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
packet.timestamp += 3000;
++packet.seqNum;
vp8_header.temporalIdx = 1;
vp8_header.pictureId = 2;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
EXPECT_FALSE(dec_state.full_sync());
}
TEST(TestDecodingState, OldInput) {
VCMDecodingState dec_state;
// Identify packets belonging to old frames/packets.
// Set state for current frames.
VCMFrameBuffer frame;
VCMPacket packet;
packet.timestamp = 10;
packet.seqNum = 1;
FrameData frame_data;
frame_data.rtt_ms = 0;
frame_data.rolling_average_packets_per_frame = -1;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
dec_state.SetState(&frame);
packet.timestamp = 9;
EXPECT_TRUE(dec_state.IsOldPacket(&packet));
// Check for old frame
frame.Reset();
frame.InsertPacket(packet, 0, frame_data);
EXPECT_TRUE(dec_state.IsOldFrame(&frame));
}
TEST(TestDecodingState, PictureIdRepeat) {
VCMDecodingState dec_state;
VCMFrameBuffer frame;
VCMPacket packet;
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
packet.video_header.codec = kVideoCodecVP8;
packet.timestamp = 0;
packet.seqNum = 0;
auto& vp8_header =
packet.video_header.video_type_header.emplace<RTPVideoHeaderVP8>();
vp8_header.tl0PicIdx = 0;
vp8_header.temporalIdx = 0;
vp8_header.pictureId = 0;
FrameData frame_data;
frame_data.rtt_ms = 0;
frame_data.rolling_average_packets_per_frame = -1;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
dec_state.SetState(&frame);
// tl0PicIdx 0, temporal id 1.
frame.Reset();
++packet.timestamp;
++packet.seqNum;
vp8_header.temporalIdx++;
vp8_header.pictureId++;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
frame.Reset();
// Testing only gap in tl0PicIdx when tl0PicIdx in continuous.
vp8_header.tl0PicIdx += 3;
vp8_header.temporalIdx++;
vp8_header.tl0PicIdx = 1;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
}
TEST(TestDecodingState, FrameContinuityFlexibleModeKeyFrame) {
VCMDecodingState dec_state;
VCMFrameBuffer frame;
VCMPacket packet;
packet.video_header.is_first_packet_in_frame = true;
packet.timestamp = 1;
packet.seqNum = 0xffff;
uint8_t data[] = "I need a data pointer for this test!";
packet.sizeBytes = sizeof(data);
packet.dataPtr = data;
packet.video_header.codec = kVideoCodecVP9;
auto& vp9_hdr =
packet.video_header.video_type_header.emplace<RTPVideoHeaderVP9>();
vp9_hdr.picture_id = 10;
vp9_hdr.flexible_mode = true;
FrameData frame_data;
frame_data.rtt_ms = 0;
frame_data.rolling_average_packets_per_frame = -1;
// Key frame as first frame
packet.video_header.frame_type = VideoFrameType::kVideoFrameKey;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
// Key frame again
vp9_hdr.picture_id = 11;
frame.Reset();
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
// Ref to 11, continuous
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
vp9_hdr.picture_id = 12;
vp9_hdr.num_ref_pics = 1;
vp9_hdr.pid_diff[0] = 1;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
}
TEST(TestDecodingState, FrameContinuityFlexibleModeOutOfOrderFrames) {
VCMDecodingState dec_state;
VCMFrameBuffer frame;
VCMPacket packet;
packet.video_header.is_first_packet_in_frame = true;
packet.timestamp = 1;
packet.seqNum = 0xffff;
uint8_t data[] = "I need a data pointer for this test!";
packet.sizeBytes = sizeof(data);
packet.dataPtr = data;
packet.video_header.codec = kVideoCodecVP9;
auto& vp9_hdr =
packet.video_header.video_type_header.emplace<RTPVideoHeaderVP9>();
vp9_hdr.picture_id = 10;
vp9_hdr.flexible_mode = true;
FrameData frame_data;
frame_data.rtt_ms = 0;
frame_data.rolling_average_packets_per_frame = -1;
// Key frame as first frame
packet.video_header.frame_type = VideoFrameType::kVideoFrameKey;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
// Ref to 10, continuous
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
vp9_hdr.picture_id = 15;
vp9_hdr.num_ref_pics = 1;
vp9_hdr.pid_diff[0] = 5;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
// Out of order, last id 15, this id 12, ref to 10, continuous
frame.Reset();
vp9_hdr.picture_id = 12;
vp9_hdr.pid_diff[0] = 2;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
// Ref 10, 12, 15, continuous
frame.Reset();
vp9_hdr.picture_id = 20;
vp9_hdr.num_ref_pics = 3;
vp9_hdr.pid_diff[0] = 10;
vp9_hdr.pid_diff[1] = 8;
vp9_hdr.pid_diff[2] = 5;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
}
TEST(TestDecodingState, FrameContinuityFlexibleModeGeneral) {
VCMDecodingState dec_state;
VCMFrameBuffer frame;
VCMPacket packet;
packet.video_header.is_first_packet_in_frame = true;
packet.timestamp = 1;
packet.seqNum = 0xffff;
uint8_t data[] = "I need a data pointer for this test!";
packet.sizeBytes = sizeof(data);
packet.dataPtr = data;
packet.video_header.codec = kVideoCodecVP9;
auto& vp9_hdr =
packet.video_header.video_type_header.emplace<RTPVideoHeaderVP9>();
vp9_hdr.picture_id = 10;
vp9_hdr.flexible_mode = true;
FrameData frame_data;
frame_data.rtt_ms = 0;
frame_data.rolling_average_packets_per_frame = -1;
// Key frame as first frame
packet.video_header.frame_type = VideoFrameType::kVideoFrameKey;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
// Delta frame as first frame
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
// Key frame then delta frame
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameKey;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
dec_state.SetState(&frame);
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
vp9_hdr.num_ref_pics = 1;
vp9_hdr.picture_id = 15;
vp9_hdr.pid_diff[0] = 5;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
// Ref to 11, not continuous
frame.Reset();
vp9_hdr.picture_id = 16;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
// Ref to 15, continuous
frame.Reset();
vp9_hdr.picture_id = 16;
vp9_hdr.pid_diff[0] = 1;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
// Ref to 11 and 15, not continuous
frame.Reset();
vp9_hdr.picture_id = 20;
vp9_hdr.num_ref_pics = 2;
vp9_hdr.pid_diff[0] = 9;
vp9_hdr.pid_diff[1] = 5;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
// Ref to 10, 15 and 16, continuous
frame.Reset();
vp9_hdr.picture_id = 22;
vp9_hdr.num_ref_pics = 3;
vp9_hdr.pid_diff[0] = 12;
vp9_hdr.pid_diff[1] = 7;
vp9_hdr.pid_diff[2] = 6;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
// Key Frame, continuous
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameKey;
vp9_hdr.picture_id = VCMDecodingState::kFrameDecodedLength - 2;
vp9_hdr.num_ref_pics = 0;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
// Frame at last index, ref to KF, continuous
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
vp9_hdr.picture_id = VCMDecodingState::kFrameDecodedLength - 1;
vp9_hdr.num_ref_pics = 1;
vp9_hdr.pid_diff[0] = 1;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
// Frame after wrapping buffer length, ref to last index, continuous
frame.Reset();
vp9_hdr.picture_id = 0;
vp9_hdr.num_ref_pics = 1;
vp9_hdr.pid_diff[0] = 1;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
// Frame after wrapping start frame, ref to 0, continuous
frame.Reset();
vp9_hdr.picture_id = 20;
vp9_hdr.num_ref_pics = 1;
vp9_hdr.pid_diff[0] = 20;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
// Frame after wrapping start frame, ref to 10, not continuous
frame.Reset();
vp9_hdr.picture_id = 23;
vp9_hdr.num_ref_pics = 1;
vp9_hdr.pid_diff[0] = 13;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
// Key frame, continuous
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameKey;
vp9_hdr.picture_id = 25;
vp9_hdr.num_ref_pics = 0;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
// Ref to KF, continuous
frame.Reset();
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
vp9_hdr.picture_id = 26;
vp9_hdr.num_ref_pics = 1;
vp9_hdr.pid_diff[0] = 1;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_TRUE(dec_state.ContinuousFrame(&frame));
dec_state.SetState(&frame);
// Ref to frame previous to KF, not continuous
frame.Reset();
vp9_hdr.picture_id = 30;
vp9_hdr.num_ref_pics = 1;
vp9_hdr.pid_diff[0] = 30;
EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data));
EXPECT_FALSE(dec_state.ContinuousFrame(&frame));
}
} // namespace webrtc

View file

@ -0,0 +1,41 @@
/*
* 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.
*/
#include "modules/video_coding/deprecated/event_wrapper.h"
#include "rtc_base/event.h"
namespace webrtc {
class EventWrapperImpl : public EventWrapper {
public:
~EventWrapperImpl() override {}
bool Set() override {
event_.Set();
return true;
}
// TODO(bugs.webrtc.org/14366): Migrate to TimeDelta.
EventTypeWrapper Wait(int max_time_ms) override {
return event_.Wait(TimeDelta::Millis(max_time_ms)) ? kEventSignaled
: kEventTimeout;
}
private:
rtc::Event event_;
};
// static
EventWrapper* EventWrapper::Create() {
return new EventWrapperImpl();
}
} // namespace webrtc

View file

@ -0,0 +1,47 @@
/*
* 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.
*/
#ifndef MODULES_VIDEO_CODING_DEPRECATED_EVENT_WRAPPER_H_
#define MODULES_VIDEO_CODING_DEPRECATED_EVENT_WRAPPER_H_
namespace webrtc {
enum EventTypeWrapper { kEventSignaled = 1, kEventTimeout = 2 };
class EventWrapper {
public:
// Factory method. Constructor disabled.
static EventWrapper* Create();
virtual ~EventWrapper() {}
// Releases threads who are calling Wait() and has started waiting. Please
// note that a thread calling Wait() will not start waiting immediately.
// assumptions to the contrary is a very common source of issues in
// multithreaded programming.
// Set is sticky in the sense that it will release at least one thread
// either immediately or some time in the future.
virtual bool Set() = 0;
// Puts the calling thread into a wait state. The thread may be released
// by a Set() call depending on if other threads are waiting and if so on
// timing. The thread that was released will reset the event before leaving
// preventing more threads from being released. If multiple threads
// are waiting for the same Set(), only one (random) thread is guaranteed to
// be released. It is possible that multiple (random) threads are released
// Depending on timing.
//
// `max_time_ms` is the maximum time to wait in milliseconds.
// TODO(bugs.webrtc.org/14366): Migrate to TimeDelta.
virtual EventTypeWrapper Wait(int max_time_ms) = 0;
};
} // namespace webrtc
#endif // MODULES_VIDEO_CODING_DEPRECATED_EVENT_WRAPPER_H_

View file

@ -0,0 +1,264 @@
/*
* 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 "modules/video_coding/deprecated/frame_buffer.h"
#include <string.h>
#include "api/video/encoded_image.h"
#include "api/video/video_timing.h"
#include "modules/video_coding/deprecated/packet.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/trace_event.h"
namespace webrtc {
VCMFrameBuffer::VCMFrameBuffer()
: _state(kStateEmpty), _nackCount(0), _latestPacketTimeMs(-1) {}
VCMFrameBuffer::~VCMFrameBuffer() {}
webrtc::VideoFrameType VCMFrameBuffer::FrameType() const {
return _sessionInfo.FrameType();
}
int32_t VCMFrameBuffer::GetLowSeqNum() const {
return _sessionInfo.LowSequenceNumber();
}
int32_t VCMFrameBuffer::GetHighSeqNum() const {
return _sessionInfo.HighSequenceNumber();
}
int VCMFrameBuffer::PictureId() const {
return _sessionInfo.PictureId();
}
int VCMFrameBuffer::TemporalId() const {
return _sessionInfo.TemporalId();
}
bool VCMFrameBuffer::LayerSync() const {
return _sessionInfo.LayerSync();
}
int VCMFrameBuffer::Tl0PicId() const {
return _sessionInfo.Tl0PicId();
}
std::vector<NaluInfo> VCMFrameBuffer::GetNaluInfos() const {
return _sessionInfo.GetNaluInfos();
}
void VCMFrameBuffer::SetGofInfo(const GofInfoVP9& gof_info, size_t idx) {
TRACE_EVENT0("webrtc", "VCMFrameBuffer::SetGofInfo");
_sessionInfo.SetGofInfo(gof_info, idx);
// TODO(asapersson): Consider adding hdr->VP9.ref_picture_id for testing.
_codecSpecificInfo.codecSpecific.VP9.temporal_idx =
gof_info.temporal_idx[idx];
_codecSpecificInfo.codecSpecific.VP9.temporal_up_switch =
gof_info.temporal_up_switch[idx];
}
// Insert packet
VCMFrameBufferEnum VCMFrameBuffer::InsertPacket(const VCMPacket& packet,
int64_t timeInMs,
const FrameData& frame_data) {
TRACE_EVENT0("webrtc", "VCMFrameBuffer::InsertPacket");
RTC_DCHECK(!(NULL == packet.dataPtr && packet.sizeBytes > 0));
if (packet.dataPtr != NULL) {
_payloadType = packet.payloadType;
}
if (kStateEmpty == _state) {
// First packet (empty and/or media) inserted into this frame.
// store some info and set some initial values.
SetRtpTimestamp(packet.timestamp);
// We only take the ntp timestamp of the first packet of a frame.
ntp_time_ms_ = packet.ntp_time_ms_;
_codec = packet.codec();
if (packet.video_header.frame_type != VideoFrameType::kEmptyFrame) {
// first media packet
SetState(kStateIncomplete);
}
}
size_t oldSize = encoded_image_buffer_ ? encoded_image_buffer_->size() : 0;
uint32_t requiredSizeBytes =
size() + packet.sizeBytes +
(packet.insertStartCode ? kH264StartCodeLengthBytes : 0);
if (requiredSizeBytes > oldSize) {
const uint8_t* prevBuffer = data();
const uint32_t increments =
requiredSizeBytes / kBufferIncStepSizeBytes +
(requiredSizeBytes % kBufferIncStepSizeBytes > 0);
const uint32_t newSize = oldSize + increments * kBufferIncStepSizeBytes;
if (newSize > kMaxJBFrameSizeBytes) {
RTC_LOG(LS_ERROR) << "Failed to insert packet due to frame being too "
"big.";
return kSizeError;
}
if (data() == nullptr) {
encoded_image_buffer_ = EncodedImageBuffer::Create(newSize);
SetEncodedData(encoded_image_buffer_);
set_size(0);
} else {
RTC_CHECK(encoded_image_buffer_ != nullptr);
RTC_DCHECK_EQ(encoded_image_buffer_->data(), data());
encoded_image_buffer_->Realloc(newSize);
}
_sessionInfo.UpdateDataPointers(prevBuffer, data());
}
if (packet.width() > 0 && packet.height() > 0) {
_encodedWidth = packet.width();
_encodedHeight = packet.height();
}
// Don't copy payload specific data for empty packets (e.g padding packets).
if (packet.sizeBytes > 0)
CopyCodecSpecific(&packet.video_header);
int retVal = _sessionInfo.InsertPacket(
packet, encoded_image_buffer_ ? encoded_image_buffer_->data() : nullptr,
frame_data);
if (retVal == -1) {
return kSizeError;
} else if (retVal == -2) {
return kDuplicatePacket;
} else if (retVal == -3) {
return kOutOfBoundsPacket;
}
// update size
set_size(size() + static_cast<uint32_t>(retVal));
_latestPacketTimeMs = timeInMs;
// http://www.etsi.org/deliver/etsi_ts/126100_126199/126114/12.07.00_60/
// ts_126114v120700p.pdf Section 7.4.5.
// The MTSI client shall add the payload bytes as defined in this clause
// onto the last RTP packet in each group of packets which make up a key
// frame (I-frame or IDR frame in H.264 (AVC), or an IRAP picture in H.265
// (HEVC)).
if (packet.markerBit) {
rotation_ = packet.video_header.rotation;
content_type_ = packet.video_header.content_type;
if (packet.video_header.video_timing.flags != VideoSendTiming::kInvalid) {
timing_.encode_start_ms =
ntp_time_ms_ + packet.video_header.video_timing.encode_start_delta_ms;
timing_.encode_finish_ms =
ntp_time_ms_ +
packet.video_header.video_timing.encode_finish_delta_ms;
timing_.packetization_finish_ms =
ntp_time_ms_ +
packet.video_header.video_timing.packetization_finish_delta_ms;
timing_.pacer_exit_ms =
ntp_time_ms_ + packet.video_header.video_timing.pacer_exit_delta_ms;
timing_.network_timestamp_ms =
ntp_time_ms_ +
packet.video_header.video_timing.network_timestamp_delta_ms;
timing_.network2_timestamp_ms =
ntp_time_ms_ +
packet.video_header.video_timing.network2_timestamp_delta_ms;
}
timing_.flags = packet.video_header.video_timing.flags;
}
if (packet.is_first_packet_in_frame()) {
SetPlayoutDelay(packet.video_header.playout_delay);
}
if (_sessionInfo.complete()) {
SetState(kStateComplete);
return kCompleteSession;
}
return kIncomplete;
}
int64_t VCMFrameBuffer::LatestPacketTimeMs() const {
TRACE_EVENT0("webrtc", "VCMFrameBuffer::LatestPacketTimeMs");
return _latestPacketTimeMs;
}
void VCMFrameBuffer::IncrementNackCount() {
TRACE_EVENT0("webrtc", "VCMFrameBuffer::IncrementNackCount");
_nackCount++;
}
int16_t VCMFrameBuffer::GetNackCount() const {
TRACE_EVENT0("webrtc", "VCMFrameBuffer::GetNackCount");
return _nackCount;
}
bool VCMFrameBuffer::HaveFirstPacket() const {
TRACE_EVENT0("webrtc", "VCMFrameBuffer::HaveFirstPacket");
return _sessionInfo.HaveFirstPacket();
}
int VCMFrameBuffer::NumPackets() const {
TRACE_EVENT0("webrtc", "VCMFrameBuffer::NumPackets");
return _sessionInfo.NumPackets();
}
void VCMFrameBuffer::Reset() {
TRACE_EVENT0("webrtc", "VCMFrameBuffer::Reset");
set_size(0);
_sessionInfo.Reset();
_payloadType = 0;
_nackCount = 0;
_latestPacketTimeMs = -1;
_state = kStateEmpty;
VCMEncodedFrame::Reset();
}
// Set state of frame
void VCMFrameBuffer::SetState(VCMFrameBufferStateEnum state) {
TRACE_EVENT0("webrtc", "VCMFrameBuffer::SetState");
if (_state == state) {
return;
}
switch (state) {
case kStateIncomplete:
// we can go to this state from state kStateEmpty
RTC_DCHECK_EQ(_state, kStateEmpty);
// Do nothing, we received a packet
break;
case kStateComplete:
RTC_DCHECK(_state == kStateEmpty || _state == kStateIncomplete);
break;
case kStateEmpty:
// Should only be set to empty through Reset().
RTC_DCHECK_NOTREACHED();
break;
}
_state = state;
}
// Get current state of frame
VCMFrameBufferStateEnum VCMFrameBuffer::GetState() const {
return _state;
}
void VCMFrameBuffer::PrepareForDecode(bool continuous) {
TRACE_EVENT0("webrtc", "VCMFrameBuffer::PrepareForDecode");
size_t bytes_removed = _sessionInfo.MakeDecodable();
set_size(size() - bytes_removed);
// Transfer frame information to EncodedFrame and create any codec
// specific information.
_frameType = _sessionInfo.FrameType();
_missingFrame = !continuous;
}
} // namespace webrtc

View file

@ -0,0 +1,87 @@
/*
* 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.
*/
#ifndef MODULES_VIDEO_CODING_DEPRECATED_FRAME_BUFFER_H_
#define MODULES_VIDEO_CODING_DEPRECATED_FRAME_BUFFER_H_
#include <stddef.h>
#include <stdint.h>
#include <vector>
#include "modules/video_coding/codecs/vp9/include/vp9_globals.h"
#include "modules/video_coding/deprecated/jitter_buffer_common.h"
#include "modules/video_coding/deprecated/packet.h"
#include "modules/video_coding/deprecated/session_info.h"
#include "modules/video_coding/encoded_frame.h"
namespace webrtc {
class VCMFrameBuffer : public VCMEncodedFrame {
public:
VCMFrameBuffer();
virtual ~VCMFrameBuffer();
virtual void Reset();
VCMFrameBufferEnum InsertPacket(const VCMPacket& packet,
int64_t timeInMs,
const FrameData& frame_data);
// State
// Get current state of frame
VCMFrameBufferStateEnum GetState() const;
void PrepareForDecode(bool continuous);
bool IsSessionComplete() const;
bool HaveFirstPacket() const;
int NumPackets() const;
// Sequence numbers
// Get lowest packet sequence number in frame
int32_t GetLowSeqNum() const;
// Get highest packet sequence number in frame
int32_t GetHighSeqNum() const;
int PictureId() const;
int TemporalId() const;
bool LayerSync() const;
int Tl0PicId() const;
std::vector<NaluInfo> GetNaluInfos() const;
void SetGofInfo(const GofInfoVP9& gof_info, size_t idx);
// Increments a counter to keep track of the number of packets of this frame
// which were NACKed before they arrived.
void IncrementNackCount();
// Returns the number of packets of this frame which were NACKed before they
// arrived.
int16_t GetNackCount() const;
int64_t LatestPacketTimeMs() const;
webrtc::VideoFrameType FrameType() const;
private:
void SetState(VCMFrameBufferStateEnum state); // Set state of frame
VCMFrameBufferStateEnum _state; // Current state of the frame
// Set with SetEncodedData, but keep pointer to the concrete class here, to
// enable reallocation and mutation.
rtc::scoped_refptr<EncodedImageBuffer> encoded_image_buffer_;
VCMSessionInfo _sessionInfo;
uint16_t _nackCount;
int64_t _latestPacketTimeMs;
};
} // namespace webrtc
#endif // MODULES_VIDEO_CODING_DEPRECATED_FRAME_BUFFER_H_

View file

@ -0,0 +1,895 @@
/*
* 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 "modules/video_coding/deprecated/jitter_buffer.h"
#include <algorithm>
#include <limits>
#include <utility>
#include "api/units/timestamp.h"
#include "modules/video_coding/deprecated/frame_buffer.h"
#include "modules/video_coding/deprecated/jitter_buffer_common.h"
#include "modules/video_coding/deprecated/packet.h"
#include "modules/video_coding/timing/inter_frame_delay_variation_calculator.h"
#include "modules/video_coding/timing/jitter_estimator.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/clock.h"
namespace webrtc {
// Use this rtt if no value has been reported.
static const int64_t kDefaultRtt = 200;
typedef std::pair<uint32_t, VCMFrameBuffer*> FrameListPair;
bool IsKeyFrame(FrameListPair pair) {
return pair.second->FrameType() == VideoFrameType::kVideoFrameKey;
}
bool HasNonEmptyState(FrameListPair pair) {
return pair.second->GetState() != kStateEmpty;
}
void FrameList::InsertFrame(VCMFrameBuffer* frame) {
insert(rbegin().base(), FrameListPair(frame->RtpTimestamp(), frame));
}
VCMFrameBuffer* FrameList::PopFrame(uint32_t timestamp) {
FrameList::iterator it = find(timestamp);
if (it == end())
return NULL;
VCMFrameBuffer* frame = it->second;
erase(it);
return frame;
}
VCMFrameBuffer* FrameList::Front() const {
return begin()->second;
}
VCMFrameBuffer* FrameList::Back() const {
return rbegin()->second;
}
int FrameList::RecycleFramesUntilKeyFrame(FrameList::iterator* key_frame_it,
UnorderedFrameList* free_frames) {
int drop_count = 0;
FrameList::iterator it = begin();
while (!empty()) {
// Throw at least one frame.
it->second->Reset();
free_frames->push_back(it->second);
erase(it++);
++drop_count;
if (it != end() &&
it->second->FrameType() == VideoFrameType::kVideoFrameKey) {
*key_frame_it = it;
return drop_count;
}
}
*key_frame_it = end();
return drop_count;
}
void FrameList::CleanUpOldOrEmptyFrames(VCMDecodingState* decoding_state,
UnorderedFrameList* free_frames) {
while (!empty()) {
VCMFrameBuffer* oldest_frame = Front();
bool remove_frame = false;
if (oldest_frame->GetState() == kStateEmpty && size() > 1) {
// This frame is empty, try to update the last decoded state and drop it
// if successful.
remove_frame = decoding_state->UpdateEmptyFrame(oldest_frame);
} else {
remove_frame = decoding_state->IsOldFrame(oldest_frame);
}
if (!remove_frame) {
break;
}
free_frames->push_back(oldest_frame);
erase(begin());
}
}
void FrameList::Reset(UnorderedFrameList* free_frames) {
while (!empty()) {
begin()->second->Reset();
free_frames->push_back(begin()->second);
erase(begin());
}
}
VCMJitterBuffer::VCMJitterBuffer(Clock* clock,
std::unique_ptr<EventWrapper> event,
const FieldTrialsView& field_trials)
: clock_(clock),
running_(false),
frame_event_(std::move(event)),
max_number_of_frames_(kStartNumberOfFrames),
free_frames_(),
decodable_frames_(),
incomplete_frames_(),
last_decoded_state_(),
first_packet_since_reset_(true),
num_consecutive_old_packets_(0),
num_packets_(0),
num_duplicated_packets_(0),
jitter_estimate_(clock, field_trials),
missing_sequence_numbers_(SequenceNumberLessThan()),
latest_received_sequence_number_(0),
max_nack_list_size_(0),
max_packet_age_to_nack_(0),
max_incomplete_time_ms_(0),
average_packets_per_frame_(0.0f),
frame_counter_(0) {
for (int i = 0; i < kStartNumberOfFrames; i++)
free_frames_.push_back(new VCMFrameBuffer());
}
VCMJitterBuffer::~VCMJitterBuffer() {
Stop();
for (UnorderedFrameList::iterator it = free_frames_.begin();
it != free_frames_.end(); ++it) {
delete *it;
}
for (FrameList::iterator it = incomplete_frames_.begin();
it != incomplete_frames_.end(); ++it) {
delete it->second;
}
for (FrameList::iterator it = decodable_frames_.begin();
it != decodable_frames_.end(); ++it) {
delete it->second;
}
}
void VCMJitterBuffer::Start() {
MutexLock lock(&mutex_);
running_ = true;
num_consecutive_old_packets_ = 0;
num_packets_ = 0;
num_duplicated_packets_ = 0;
// Start in a non-signaled state.
waiting_for_completion_.frame_size = 0;
waiting_for_completion_.timestamp = 0;
waiting_for_completion_.latest_packet_time = -1;
first_packet_since_reset_ = true;
last_decoded_state_.Reset();
decodable_frames_.Reset(&free_frames_);
incomplete_frames_.Reset(&free_frames_);
}
void VCMJitterBuffer::Stop() {
MutexLock lock(&mutex_);
running_ = false;
last_decoded_state_.Reset();
// Make sure we wake up any threads waiting on these events.
frame_event_->Set();
}
bool VCMJitterBuffer::Running() const {
MutexLock lock(&mutex_);
return running_;
}
void VCMJitterBuffer::Flush() {
MutexLock lock(&mutex_);
FlushInternal();
}
void VCMJitterBuffer::FlushInternal() {
decodable_frames_.Reset(&free_frames_);
incomplete_frames_.Reset(&free_frames_);
last_decoded_state_.Reset(); // TODO(mikhal): sync reset.
num_consecutive_old_packets_ = 0;
// Also reset the jitter and delay estimates
jitter_estimate_.Reset();
inter_frame_delay_.Reset();
waiting_for_completion_.frame_size = 0;
waiting_for_completion_.timestamp = 0;
waiting_for_completion_.latest_packet_time = -1;
first_packet_since_reset_ = true;
missing_sequence_numbers_.clear();
}
int VCMJitterBuffer::num_packets() const {
MutexLock lock(&mutex_);
return num_packets_;
}
int VCMJitterBuffer::num_duplicated_packets() const {
MutexLock lock(&mutex_);
return num_duplicated_packets_;
}
// Returns immediately or a `max_wait_time_ms` ms event hang waiting for a
// complete frame, `max_wait_time_ms` decided by caller.
VCMEncodedFrame* VCMJitterBuffer::NextCompleteFrame(uint32_t max_wait_time_ms) {
MutexLock lock(&mutex_);
if (!running_) {
return nullptr;
}
CleanUpOldOrEmptyFrames();
if (decodable_frames_.empty() ||
decodable_frames_.Front()->GetState() != kStateComplete) {
const int64_t end_wait_time_ms =
clock_->TimeInMilliseconds() + max_wait_time_ms;
int64_t wait_time_ms = max_wait_time_ms;
while (wait_time_ms > 0) {
mutex_.Unlock();
const EventTypeWrapper ret =
frame_event_->Wait(static_cast<uint32_t>(wait_time_ms));
mutex_.Lock();
if (ret == kEventSignaled) {
// Are we shutting down the jitter buffer?
if (!running_) {
return nullptr;
}
// Finding oldest frame ready for decoder.
CleanUpOldOrEmptyFrames();
if (decodable_frames_.empty() ||
decodable_frames_.Front()->GetState() != kStateComplete) {
wait_time_ms = end_wait_time_ms - clock_->TimeInMilliseconds();
} else {
break;
}
} else {
break;
}
}
}
if (decodable_frames_.empty() ||
decodable_frames_.Front()->GetState() != kStateComplete) {
return nullptr;
}
return decodable_frames_.Front();
}
VCMEncodedFrame* VCMJitterBuffer::ExtractAndSetDecode(uint32_t timestamp) {
MutexLock lock(&mutex_);
if (!running_) {
return NULL;
}
// Extract the frame with the desired timestamp.
VCMFrameBuffer* frame = decodable_frames_.PopFrame(timestamp);
bool continuous = true;
if (!frame) {
frame = incomplete_frames_.PopFrame(timestamp);
if (frame)
continuous = last_decoded_state_.ContinuousFrame(frame);
else
return NULL;
}
// Frame pulled out from jitter buffer, update the jitter estimate.
const bool retransmitted = (frame->GetNackCount() > 0);
if (retransmitted) {
jitter_estimate_.FrameNacked();
} else if (frame->size() > 0) {
// Ignore retransmitted and empty frames.
if (waiting_for_completion_.latest_packet_time >= 0) {
UpdateJitterEstimate(waiting_for_completion_, true);
}
if (frame->GetState() == kStateComplete) {
UpdateJitterEstimate(*frame, false);
} else {
// Wait for this one to get complete.
waiting_for_completion_.frame_size = frame->size();
waiting_for_completion_.latest_packet_time = frame->LatestPacketTimeMs();
waiting_for_completion_.timestamp = frame->RtpTimestamp();
}
}
// The state must be changed to decoding before cleaning up zero sized
// frames to avoid empty frames being cleaned up and then given to the
// decoder. Propagates the missing_frame bit.
frame->PrepareForDecode(continuous);
// We have a frame - update the last decoded state and nack list.
last_decoded_state_.SetState(frame);
DropPacketsFromNackList(last_decoded_state_.sequence_num());
UpdateAveragePacketsPerFrame(frame->NumPackets());
return frame;
}
// Release frame when done with decoding. Should never be used to release
// frames from within the jitter buffer.
void VCMJitterBuffer::ReleaseFrame(VCMEncodedFrame* frame) {
RTC_CHECK(frame != nullptr);
MutexLock lock(&mutex_);
VCMFrameBuffer* frame_buffer = static_cast<VCMFrameBuffer*>(frame);
RecycleFrameBuffer(frame_buffer);
}
// Gets frame to use for this timestamp. If no match, get empty frame.
VCMFrameBufferEnum VCMJitterBuffer::GetFrame(const VCMPacket& packet,
VCMFrameBuffer** frame,
FrameList** frame_list) {
*frame = incomplete_frames_.PopFrame(packet.timestamp);
if (*frame != NULL) {
*frame_list = &incomplete_frames_;
return kNoError;
}
*frame = decodable_frames_.PopFrame(packet.timestamp);
if (*frame != NULL) {
*frame_list = &decodable_frames_;
return kNoError;
}
*frame_list = NULL;
// No match, return empty frame.
*frame = GetEmptyFrame();
if (*frame == NULL) {
// No free frame! Try to reclaim some...
RTC_LOG(LS_WARNING) << "Unable to get empty frame; Recycling.";
bool found_key_frame = RecycleFramesUntilKeyFrame();
*frame = GetEmptyFrame();
RTC_CHECK(*frame);
if (!found_key_frame) {
RecycleFrameBuffer(*frame);
return kFlushIndicator;
}
}
(*frame)->Reset();
return kNoError;
}
int64_t VCMJitterBuffer::LastPacketTime(const VCMEncodedFrame* frame,
bool* retransmitted) const {
RTC_DCHECK(retransmitted);
MutexLock lock(&mutex_);
const VCMFrameBuffer* frame_buffer =
static_cast<const VCMFrameBuffer*>(frame);
*retransmitted = (frame_buffer->GetNackCount() > 0);
return frame_buffer->LatestPacketTimeMs();
}
VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet,
bool* retransmitted) {
MutexLock lock(&mutex_);
++num_packets_;
// Does this packet belong to an old frame?
if (last_decoded_state_.IsOldPacket(&packet)) {
// Account only for media packets.
if (packet.sizeBytes > 0) {
num_consecutive_old_packets_++;
}
// Update last decoded sequence number if the packet arrived late and
// belongs to a frame with a timestamp equal to the last decoded
// timestamp.
last_decoded_state_.UpdateOldPacket(&packet);
DropPacketsFromNackList(last_decoded_state_.sequence_num());
// Also see if this old packet made more incomplete frames continuous.
FindAndInsertContinuousFramesWithState(last_decoded_state_);
if (num_consecutive_old_packets_ > kMaxConsecutiveOldPackets) {
RTC_LOG(LS_WARNING)
<< num_consecutive_old_packets_
<< " consecutive old packets received. Flushing the jitter buffer.";
FlushInternal();
return kFlushIndicator;
}
return kOldPacket;
}
num_consecutive_old_packets_ = 0;
VCMFrameBuffer* frame;
FrameList* frame_list;
const VCMFrameBufferEnum error = GetFrame(packet, &frame, &frame_list);
if (error != kNoError)
return error;
Timestamp now = clock_->CurrentTime();
// We are keeping track of the first and latest seq numbers, and
// the number of wraps to be able to calculate how many packets we expect.
if (first_packet_since_reset_) {
// Now it's time to start estimating jitter
// reset the delay estimate.
inter_frame_delay_.Reset();
}
// Empty packets may bias the jitter estimate (lacking size component),
// therefore don't let empty packet trigger the following updates:
if (packet.video_header.frame_type != VideoFrameType::kEmptyFrame) {
if (waiting_for_completion_.timestamp == packet.timestamp) {
// This can get bad if we have a lot of duplicate packets,
// we will then count some packet multiple times.
waiting_for_completion_.frame_size += packet.sizeBytes;
waiting_for_completion_.latest_packet_time = now.ms();
} else if (waiting_for_completion_.latest_packet_time >= 0 &&
waiting_for_completion_.latest_packet_time + 2000 <= now.ms()) {
// A packet should never be more than two seconds late
UpdateJitterEstimate(waiting_for_completion_, true);
waiting_for_completion_.latest_packet_time = -1;
waiting_for_completion_.frame_size = 0;
waiting_for_completion_.timestamp = 0;
}
}
VCMFrameBufferStateEnum previous_state = frame->GetState();
// Insert packet.
FrameData frame_data;
frame_data.rtt_ms = kDefaultRtt;
frame_data.rolling_average_packets_per_frame = average_packets_per_frame_;
VCMFrameBufferEnum buffer_state =
frame->InsertPacket(packet, now.ms(), frame_data);
if (buffer_state > 0) {
if (first_packet_since_reset_) {
latest_received_sequence_number_ = packet.seqNum;
first_packet_since_reset_ = false;
} else {
if (IsPacketRetransmitted(packet)) {
frame->IncrementNackCount();
}
if (!UpdateNackList(packet.seqNum) &&
packet.video_header.frame_type != VideoFrameType::kVideoFrameKey) {
buffer_state = kFlushIndicator;
}
latest_received_sequence_number_ =
LatestSequenceNumber(latest_received_sequence_number_, packet.seqNum);
}
}
// Is the frame already in the decodable list?
bool continuous = IsContinuous(*frame);
switch (buffer_state) {
case kGeneralError:
case kTimeStampError:
case kSizeError: {
RecycleFrameBuffer(frame);
break;
}
case kCompleteSession: {
if (previous_state != kStateComplete) {
if (continuous) {
// Signal that we have a complete session.
frame_event_->Set();
}
}
*retransmitted = (frame->GetNackCount() > 0);
if (continuous) {
decodable_frames_.InsertFrame(frame);
FindAndInsertContinuousFrames(*frame);
} else {
incomplete_frames_.InsertFrame(frame);
}
break;
}
case kIncomplete: {
if (frame->GetState() == kStateEmpty &&
last_decoded_state_.UpdateEmptyFrame(frame)) {
RecycleFrameBuffer(frame);
return kNoError;
} else {
incomplete_frames_.InsertFrame(frame);
}
break;
}
case kNoError:
case kOutOfBoundsPacket:
case kDuplicatePacket: {
// Put back the frame where it came from.
if (frame_list != NULL) {
frame_list->InsertFrame(frame);
} else {
RecycleFrameBuffer(frame);
}
++num_duplicated_packets_;
break;
}
case kFlushIndicator:
RecycleFrameBuffer(frame);
return kFlushIndicator;
default:
RTC_DCHECK_NOTREACHED();
}
return buffer_state;
}
bool VCMJitterBuffer::IsContinuousInState(
const VCMFrameBuffer& frame,
const VCMDecodingState& decoding_state) const {
// Is this frame complete and continuous?
return (frame.GetState() == kStateComplete) &&
decoding_state.ContinuousFrame(&frame);
}
bool VCMJitterBuffer::IsContinuous(const VCMFrameBuffer& frame) const {
if (IsContinuousInState(frame, last_decoded_state_)) {
return true;
}
VCMDecodingState decoding_state;
decoding_state.CopyFrom(last_decoded_state_);
for (FrameList::const_iterator it = decodable_frames_.begin();
it != decodable_frames_.end(); ++it) {
VCMFrameBuffer* decodable_frame = it->second;
if (IsNewerTimestamp(decodable_frame->RtpTimestamp(),
frame.RtpTimestamp())) {
break;
}
decoding_state.SetState(decodable_frame);
if (IsContinuousInState(frame, decoding_state)) {
return true;
}
}
return false;
}
void VCMJitterBuffer::FindAndInsertContinuousFrames(
const VCMFrameBuffer& new_frame) {
VCMDecodingState decoding_state;
decoding_state.CopyFrom(last_decoded_state_);
decoding_state.SetState(&new_frame);
FindAndInsertContinuousFramesWithState(decoding_state);
}
void VCMJitterBuffer::FindAndInsertContinuousFramesWithState(
const VCMDecodingState& original_decoded_state) {
// Copy original_decoded_state so we can move the state forward with each
// decodable frame we find.
VCMDecodingState decoding_state;
decoding_state.CopyFrom(original_decoded_state);
// When temporal layers are available, we search for a complete or decodable
// frame until we hit one of the following:
// 1. Continuous base or sync layer.
// 2. The end of the list was reached.
for (FrameList::iterator it = incomplete_frames_.begin();
it != incomplete_frames_.end();) {
VCMFrameBuffer* frame = it->second;
if (IsNewerTimestamp(original_decoded_state.time_stamp(),
frame->RtpTimestamp())) {
++it;
continue;
}
if (IsContinuousInState(*frame, decoding_state)) {
decodable_frames_.InsertFrame(frame);
incomplete_frames_.erase(it++);
decoding_state.SetState(frame);
} else if (frame->TemporalId() <= 0) {
break;
} else {
++it;
}
}
}
uint32_t VCMJitterBuffer::EstimatedJitterMs() {
MutexLock lock(&mutex_);
const double rtt_mult = 1.0f;
return jitter_estimate_.GetJitterEstimate(rtt_mult, absl::nullopt).ms();
}
void VCMJitterBuffer::SetNackSettings(size_t max_nack_list_size,
int max_packet_age_to_nack,
int max_incomplete_time_ms) {
MutexLock lock(&mutex_);
RTC_DCHECK_GE(max_packet_age_to_nack, 0);
RTC_DCHECK_GE(max_incomplete_time_ms_, 0);
max_nack_list_size_ = max_nack_list_size;
max_packet_age_to_nack_ = max_packet_age_to_nack;
max_incomplete_time_ms_ = max_incomplete_time_ms;
}
int VCMJitterBuffer::NonContinuousOrIncompleteDuration() {
if (incomplete_frames_.empty()) {
return 0;
}
uint32_t start_timestamp = incomplete_frames_.Front()->RtpTimestamp();
if (!decodable_frames_.empty()) {
start_timestamp = decodable_frames_.Back()->RtpTimestamp();
}
return incomplete_frames_.Back()->RtpTimestamp() - start_timestamp;
}
uint16_t VCMJitterBuffer::EstimatedLowSequenceNumber(
const VCMFrameBuffer& frame) const {
RTC_DCHECK_GE(frame.GetLowSeqNum(), 0);
if (frame.HaveFirstPacket())
return frame.GetLowSeqNum();
// This estimate is not accurate if more than one packet with lower sequence
// number is lost.
return frame.GetLowSeqNum() - 1;
}
std::vector<uint16_t> VCMJitterBuffer::GetNackList(bool* request_key_frame) {
MutexLock lock(&mutex_);
*request_key_frame = false;
if (last_decoded_state_.in_initial_state()) {
VCMFrameBuffer* next_frame = NextFrame();
const bool first_frame_is_key =
next_frame &&
next_frame->FrameType() == VideoFrameType::kVideoFrameKey &&
next_frame->HaveFirstPacket();
if (!first_frame_is_key) {
bool have_non_empty_frame =
decodable_frames_.end() != find_if(decodable_frames_.begin(),
decodable_frames_.end(),
HasNonEmptyState);
if (!have_non_empty_frame) {
have_non_empty_frame =
incomplete_frames_.end() != find_if(incomplete_frames_.begin(),
incomplete_frames_.end(),
HasNonEmptyState);
}
bool found_key_frame = RecycleFramesUntilKeyFrame();
if (!found_key_frame) {
*request_key_frame = have_non_empty_frame;
return std::vector<uint16_t>();
}
}
}
if (TooLargeNackList()) {
*request_key_frame = !HandleTooLargeNackList();
}
if (max_incomplete_time_ms_ > 0) {
int non_continuous_incomplete_duration =
NonContinuousOrIncompleteDuration();
if (non_continuous_incomplete_duration > 90 * max_incomplete_time_ms_) {
RTC_LOG_F(LS_WARNING) << "Too long non-decodable duration: "
<< non_continuous_incomplete_duration << " > "
<< 90 * max_incomplete_time_ms_;
FrameList::reverse_iterator rit = find_if(
incomplete_frames_.rbegin(), incomplete_frames_.rend(), IsKeyFrame);
if (rit == incomplete_frames_.rend()) {
// Request a key frame if we don't have one already.
*request_key_frame = true;
return std::vector<uint16_t>();
} else {
// Skip to the last key frame. If it's incomplete we will start
// NACKing it.
// Note that the estimated low sequence number is correct for VP8
// streams because only the first packet of a key frame is marked.
last_decoded_state_.Reset();
DropPacketsFromNackList(EstimatedLowSequenceNumber(*rit->second));
}
}
}
std::vector<uint16_t> nack_list(missing_sequence_numbers_.begin(),
missing_sequence_numbers_.end());
return nack_list;
}
VCMFrameBuffer* VCMJitterBuffer::NextFrame() const {
if (!decodable_frames_.empty())
return decodable_frames_.Front();
if (!incomplete_frames_.empty())
return incomplete_frames_.Front();
return NULL;
}
bool VCMJitterBuffer::UpdateNackList(uint16_t sequence_number) {
// Make sure we don't add packets which are already too old to be decoded.
if (!last_decoded_state_.in_initial_state()) {
latest_received_sequence_number_ = LatestSequenceNumber(
latest_received_sequence_number_, last_decoded_state_.sequence_num());
}
if (IsNewerSequenceNumber(sequence_number,
latest_received_sequence_number_)) {
// Push any missing sequence numbers to the NACK list.
for (uint16_t i = latest_received_sequence_number_ + 1;
IsNewerSequenceNumber(sequence_number, i); ++i) {
missing_sequence_numbers_.insert(missing_sequence_numbers_.end(), i);
}
if (TooLargeNackList() && !HandleTooLargeNackList()) {
RTC_LOG(LS_WARNING) << "Requesting key frame due to too large NACK list.";
return false;
}
if (MissingTooOldPacket(sequence_number) &&
!HandleTooOldPackets(sequence_number)) {
RTC_LOG(LS_WARNING)
<< "Requesting key frame due to missing too old packets";
return false;
}
} else {
missing_sequence_numbers_.erase(sequence_number);
}
return true;
}
bool VCMJitterBuffer::TooLargeNackList() const {
return missing_sequence_numbers_.size() > max_nack_list_size_;
}
bool VCMJitterBuffer::HandleTooLargeNackList() {
// Recycle frames until the NACK list is small enough. It is likely cheaper to
// request a key frame than to retransmit this many missing packets.
RTC_LOG_F(LS_WARNING) << "NACK list has grown too large: "
<< missing_sequence_numbers_.size() << " > "
<< max_nack_list_size_;
bool key_frame_found = false;
while (TooLargeNackList()) {
key_frame_found = RecycleFramesUntilKeyFrame();
}
return key_frame_found;
}
bool VCMJitterBuffer::MissingTooOldPacket(
uint16_t latest_sequence_number) const {
if (missing_sequence_numbers_.empty()) {
return false;
}
const uint16_t age_of_oldest_missing_packet =
latest_sequence_number - *missing_sequence_numbers_.begin();
// Recycle frames if the NACK list contains too old sequence numbers as
// the packets may have already been dropped by the sender.
return age_of_oldest_missing_packet > max_packet_age_to_nack_;
}
bool VCMJitterBuffer::HandleTooOldPackets(uint16_t latest_sequence_number) {
bool key_frame_found = false;
const uint16_t age_of_oldest_missing_packet =
latest_sequence_number - *missing_sequence_numbers_.begin();
RTC_LOG_F(LS_WARNING) << "NACK list contains too old sequence numbers: "
<< age_of_oldest_missing_packet << " > "
<< max_packet_age_to_nack_;
while (MissingTooOldPacket(latest_sequence_number)) {
key_frame_found = RecycleFramesUntilKeyFrame();
}
return key_frame_found;
}
void VCMJitterBuffer::DropPacketsFromNackList(
uint16_t last_decoded_sequence_number) {
// Erase all sequence numbers from the NACK list which we won't need any
// longer.
missing_sequence_numbers_.erase(
missing_sequence_numbers_.begin(),
missing_sequence_numbers_.upper_bound(last_decoded_sequence_number));
}
VCMFrameBuffer* VCMJitterBuffer::GetEmptyFrame() {
if (free_frames_.empty()) {
if (!TryToIncreaseJitterBufferSize()) {
return NULL;
}
}
VCMFrameBuffer* frame = free_frames_.front();
free_frames_.pop_front();
return frame;
}
bool VCMJitterBuffer::TryToIncreaseJitterBufferSize() {
if (max_number_of_frames_ >= kMaxNumberOfFrames)
return false;
free_frames_.push_back(new VCMFrameBuffer());
++max_number_of_frames_;
return true;
}
// Recycle oldest frames up to a key frame, used if jitter buffer is completely
// full.
bool VCMJitterBuffer::RecycleFramesUntilKeyFrame() {
// First release incomplete frames, and only release decodable frames if there
// are no incomplete ones.
FrameList::iterator key_frame_it;
bool key_frame_found = false;
int dropped_frames = 0;
dropped_frames += incomplete_frames_.RecycleFramesUntilKeyFrame(
&key_frame_it, &free_frames_);
key_frame_found = key_frame_it != incomplete_frames_.end();
if (dropped_frames == 0) {
dropped_frames += decodable_frames_.RecycleFramesUntilKeyFrame(
&key_frame_it, &free_frames_);
key_frame_found = key_frame_it != decodable_frames_.end();
}
if (key_frame_found) {
RTC_LOG(LS_INFO) << "Found key frame while dropping frames.";
// Reset last decoded state to make sure the next frame decoded is a key
// frame, and start NACKing from here.
last_decoded_state_.Reset();
DropPacketsFromNackList(EstimatedLowSequenceNumber(*key_frame_it->second));
} else if (decodable_frames_.empty()) {
// All frames dropped. Reset the decoding state and clear missing sequence
// numbers as we're starting fresh.
last_decoded_state_.Reset();
missing_sequence_numbers_.clear();
}
return key_frame_found;
}
void VCMJitterBuffer::UpdateAveragePacketsPerFrame(int current_number_packets) {
if (frame_counter_ > kFastConvergeThreshold) {
average_packets_per_frame_ =
average_packets_per_frame_ * (1 - kNormalConvergeMultiplier) +
current_number_packets * kNormalConvergeMultiplier;
} else if (frame_counter_ > 0) {
average_packets_per_frame_ =
average_packets_per_frame_ * (1 - kFastConvergeMultiplier) +
current_number_packets * kFastConvergeMultiplier;
frame_counter_++;
} else {
average_packets_per_frame_ = current_number_packets;
frame_counter_++;
}
}
// Must be called under the critical section `mutex_`.
void VCMJitterBuffer::CleanUpOldOrEmptyFrames() {
decodable_frames_.CleanUpOldOrEmptyFrames(&last_decoded_state_,
&free_frames_);
incomplete_frames_.CleanUpOldOrEmptyFrames(&last_decoded_state_,
&free_frames_);
if (!last_decoded_state_.in_initial_state()) {
DropPacketsFromNackList(last_decoded_state_.sequence_num());
}
}
// Must be called from within `mutex_`.
bool VCMJitterBuffer::IsPacketRetransmitted(const VCMPacket& packet) const {
return missing_sequence_numbers_.find(packet.seqNum) !=
missing_sequence_numbers_.end();
}
// Must be called under the critical section `mutex_`. Should never be
// called with retransmitted frames, they must be filtered out before this
// function is called.
void VCMJitterBuffer::UpdateJitterEstimate(const VCMJitterSample& sample,
bool incomplete_frame) {
if (sample.latest_packet_time == -1) {
return;
}
UpdateJitterEstimate(sample.latest_packet_time, sample.timestamp,
sample.frame_size, incomplete_frame);
}
// Must be called under the critical section mutex_. Should never be
// called with retransmitted frames, they must be filtered out before this
// function is called.
void VCMJitterBuffer::UpdateJitterEstimate(const VCMFrameBuffer& frame,
bool incomplete_frame) {
if (frame.LatestPacketTimeMs() == -1) {
return;
}
// No retransmitted frames should be a part of the jitter
// estimate.
UpdateJitterEstimate(frame.LatestPacketTimeMs(), frame.RtpTimestamp(),
frame.size(), incomplete_frame);
}
// Must be called under the critical section `mutex_`. Should never be
// called with retransmitted frames, they must be filtered out before this
// function is called.
void VCMJitterBuffer::UpdateJitterEstimate(int64_t latest_packet_time_ms,
uint32_t timestamp,
unsigned int frame_size,
bool /*incomplete_frame*/) {
if (latest_packet_time_ms == -1) {
return;
}
auto frame_delay = inter_frame_delay_.Calculate(
timestamp, Timestamp::Millis(latest_packet_time_ms));
bool not_reordered = frame_delay.has_value();
// Filter out frames which have been reordered in time by the network
if (not_reordered) {
// Update the jitter estimate with the new samples
jitter_estimate_.UpdateEstimate(*frame_delay, DataSize::Bytes(frame_size));
}
}
void VCMJitterBuffer::RecycleFrameBuffer(VCMFrameBuffer* frame) {
frame->Reset();
free_frames_.push_back(frame);
}
} // namespace webrtc

View file

@ -0,0 +1,282 @@
/*
* 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.
*/
#ifndef MODULES_VIDEO_CODING_DEPRECATED_JITTER_BUFFER_H_
#define MODULES_VIDEO_CODING_DEPRECATED_JITTER_BUFFER_H_
#include <list>
#include <map>
#include <memory>
#include <set>
#include <vector>
#include "api/field_trials_view.h"
#include "modules/include/module_common_types.h"
#include "modules/include/module_common_types_public.h"
#include "modules/video_coding/deprecated/decoding_state.h"
#include "modules/video_coding/deprecated/event_wrapper.h"
#include "modules/video_coding/deprecated/jitter_buffer_common.h"
#include "modules/video_coding/include/video_coding_defines.h"
#include "modules/video_coding/timing/inter_frame_delay_variation_calculator.h"
#include "modules/video_coding/timing/jitter_estimator.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
namespace webrtc {
// forward declarations
class Clock;
class VCMFrameBuffer;
class VCMPacket;
class VCMEncodedFrame;
typedef std::list<VCMFrameBuffer*> UnorderedFrameList;
struct VCMJitterSample {
VCMJitterSample() : timestamp(0), frame_size(0), latest_packet_time(-1) {}
uint32_t timestamp;
uint32_t frame_size;
int64_t latest_packet_time;
};
class TimestampLessThan {
public:
bool operator()(uint32_t timestamp1, uint32_t timestamp2) const {
return IsNewerTimestamp(timestamp2, timestamp1);
}
};
class FrameList
: public std::map<uint32_t, VCMFrameBuffer*, TimestampLessThan> {
public:
void InsertFrame(VCMFrameBuffer* frame);
VCMFrameBuffer* PopFrame(uint32_t timestamp);
VCMFrameBuffer* Front() const;
VCMFrameBuffer* Back() const;
int RecycleFramesUntilKeyFrame(FrameList::iterator* key_frame_it,
UnorderedFrameList* free_frames);
void CleanUpOldOrEmptyFrames(VCMDecodingState* decoding_state,
UnorderedFrameList* free_frames);
void Reset(UnorderedFrameList* free_frames);
};
class VCMJitterBuffer {
public:
VCMJitterBuffer(Clock* clock,
std::unique_ptr<EventWrapper> event,
const FieldTrialsView& field_trials);
~VCMJitterBuffer();
VCMJitterBuffer(const VCMJitterBuffer&) = delete;
VCMJitterBuffer& operator=(const VCMJitterBuffer&) = delete;
// Initializes and starts jitter buffer.
void Start() RTC_LOCKS_EXCLUDED(mutex_);
// Signals all internal events and stops the jitter buffer.
void Stop() RTC_LOCKS_EXCLUDED(mutex_);
// Returns true if the jitter buffer is running.
bool Running() const RTC_LOCKS_EXCLUDED(mutex_);
// Empty the jitter buffer of all its data.
void Flush() RTC_LOCKS_EXCLUDED(mutex_);
// Gets number of packets received.
int num_packets() const RTC_LOCKS_EXCLUDED(mutex_);
// Gets number of duplicated packets received.
int num_duplicated_packets() const RTC_LOCKS_EXCLUDED(mutex_);
// Wait `max_wait_time_ms` for a complete frame to arrive.
// If found, a pointer to the frame is returned. Returns nullptr otherwise.
VCMEncodedFrame* NextCompleteFrame(uint32_t max_wait_time_ms)
RTC_LOCKS_EXCLUDED(mutex_);
// Extract frame corresponding to input timestamp.
// Frame will be set to a decoding state.
VCMEncodedFrame* ExtractAndSetDecode(uint32_t timestamp)
RTC_LOCKS_EXCLUDED(mutex_);
// Releases a frame returned from the jitter buffer, should be called when
// done with decoding.
void ReleaseFrame(VCMEncodedFrame* frame) RTC_LOCKS_EXCLUDED(mutex_);
// Returns the time in ms when the latest packet was inserted into the frame.
// Retransmitted is set to true if any of the packets belonging to the frame
// has been retransmitted.
int64_t LastPacketTime(const VCMEncodedFrame* frame,
bool* retransmitted) const RTC_LOCKS_EXCLUDED(mutex_);
// Inserts a packet into a frame returned from GetFrame().
// If the return value is <= 0, `frame` is invalidated and the pointer must
// be dropped after this function returns.
VCMFrameBufferEnum InsertPacket(const VCMPacket& packet, bool* retransmitted)
RTC_LOCKS_EXCLUDED(mutex_);
// Returns the estimated jitter in milliseconds.
uint32_t EstimatedJitterMs() RTC_LOCKS_EXCLUDED(mutex_);
void SetNackSettings(size_t max_nack_list_size,
int max_packet_age_to_nack,
int max_incomplete_time_ms) RTC_LOCKS_EXCLUDED(mutex_);
// Returns a list of the sequence numbers currently missing.
std::vector<uint16_t> GetNackList(bool* request_key_frame)
RTC_LOCKS_EXCLUDED(mutex_);
private:
class SequenceNumberLessThan {
public:
bool operator()(const uint16_t& sequence_number1,
const uint16_t& sequence_number2) const {
return IsNewerSequenceNumber(sequence_number2, sequence_number1);
}
};
typedef std::set<uint16_t, SequenceNumberLessThan> SequenceNumberSet;
// Gets the frame assigned to the timestamp of the packet. May recycle
// existing frames if no free frames are available. Returns an error code if
// failing, or kNoError on success. `frame_list` contains which list the
// packet was in, or NULL if it was not in a FrameList (a new frame).
VCMFrameBufferEnum GetFrame(const VCMPacket& packet,
VCMFrameBuffer** frame,
FrameList** frame_list)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns true if `frame` is continuous in `decoding_state`, not taking
// decodable frames into account.
bool IsContinuousInState(const VCMFrameBuffer& frame,
const VCMDecodingState& decoding_state) const
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns true if `frame` is continuous in the `last_decoded_state_`, taking
// all decodable frames into account.
bool IsContinuous(const VCMFrameBuffer& frame) const
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Looks for frames in `incomplete_frames_` which are continuous in the
// provided `decoded_state`. Starts the search from the timestamp of
// `decoded_state`.
void FindAndInsertContinuousFramesWithState(
const VCMDecodingState& decoded_state)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Looks for frames in `incomplete_frames_` which are continuous in
// `last_decoded_state_` taking all decodable frames into account. Starts
// the search from `new_frame`.
void FindAndInsertContinuousFrames(const VCMFrameBuffer& new_frame)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
VCMFrameBuffer* NextFrame() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns true if the NACK list was updated to cover sequence numbers up to
// `sequence_number`. If false a key frame is needed to get into a state where
// we can continue decoding.
bool UpdateNackList(uint16_t sequence_number)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
bool TooLargeNackList() const;
// Returns true if the NACK list was reduced without problem. If false a key
// frame is needed to get into a state where we can continue decoding.
bool HandleTooLargeNackList() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
bool MissingTooOldPacket(uint16_t latest_sequence_number) const
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns true if the too old packets was successfully removed from the NACK
// list. If false, a key frame is needed to get into a state where we can
// continue decoding.
bool HandleTooOldPackets(uint16_t latest_sequence_number)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Drops all packets in the NACK list up until `last_decoded_sequence_number`.
void DropPacketsFromNackList(uint16_t last_decoded_sequence_number);
// Gets an empty frame, creating a new frame if necessary (i.e. increases
// jitter buffer size).
VCMFrameBuffer* GetEmptyFrame() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Attempts to increase the size of the jitter buffer. Returns true on
// success, false otherwise.
bool TryToIncreaseJitterBufferSize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Recycles oldest frames until a key frame is found. Used if jitter buffer is
// completely full. Returns true if a key frame was found.
bool RecycleFramesUntilKeyFrame() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Update rolling average of packets per frame.
void UpdateAveragePacketsPerFrame(int current_number_packets_)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Cleans the frame list in the JB from old/empty frames.
// Should only be called prior to actual use.
void CleanUpOldOrEmptyFrames() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns true if `packet` is likely to have been retransmitted.
bool IsPacketRetransmitted(const VCMPacket& packet) const;
// The following three functions update the jitter estimate with the
// payload size, receive time and RTP timestamp of a frame.
void UpdateJitterEstimate(const VCMJitterSample& sample,
bool incomplete_frame);
void UpdateJitterEstimate(const VCMFrameBuffer& frame, bool incomplete_frame);
void UpdateJitterEstimate(int64_t latest_packet_time_ms,
uint32_t timestamp,
unsigned int frame_size,
bool incomplete_frame);
int NonContinuousOrIncompleteDuration() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
uint16_t EstimatedLowSequenceNumber(const VCMFrameBuffer& frame) const;
// Reset frame buffer and return it to free_frames_.
void RecycleFrameBuffer(VCMFrameBuffer* frame)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Empty the jitter buffer of all its data.
void FlushInternal() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
Clock* clock_;
// If we are running (have started) or not.
bool running_;
mutable Mutex mutex_;
// Event to signal when we have a frame ready for decoder.
std::unique_ptr<EventWrapper> frame_event_;
// Number of allocated frames.
int max_number_of_frames_;
UnorderedFrameList free_frames_ RTC_GUARDED_BY(mutex_);
FrameList decodable_frames_ RTC_GUARDED_BY(mutex_);
FrameList incomplete_frames_ RTC_GUARDED_BY(mutex_);
VCMDecodingState last_decoded_state_ RTC_GUARDED_BY(mutex_);
bool first_packet_since_reset_;
// Number of packets in a row that have been too old.
int num_consecutive_old_packets_;
// Number of packets received.
int num_packets_ RTC_GUARDED_BY(mutex_);
// Number of duplicated packets received.
int num_duplicated_packets_ RTC_GUARDED_BY(mutex_);
// Jitter estimation.
// Filter for estimating jitter.
JitterEstimator jitter_estimate_;
// Calculates network delays used for jitter calculations.
InterFrameDelayVariationCalculator inter_frame_delay_;
VCMJitterSample waiting_for_completion_;
// Holds the internal NACK list (the missing sequence numbers).
SequenceNumberSet missing_sequence_numbers_;
uint16_t latest_received_sequence_number_;
size_t max_nack_list_size_;
int max_packet_age_to_nack_; // Measured in sequence numbers.
int max_incomplete_time_ms_;
// Estimated rolling average of packets per frame
float average_packets_per_frame_;
// average_packets_per_frame converges fast if we have fewer than this many
// frames.
int frame_counter_;
};
} // namespace webrtc
#endif // MODULES_VIDEO_CODING_DEPRECATED_JITTER_BUFFER_H_

View file

@ -0,0 +1,59 @@
/*
* 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.
*/
#ifndef MODULES_VIDEO_CODING_DEPRECATED_JITTER_BUFFER_COMMON_H_
#define MODULES_VIDEO_CODING_DEPRECATED_JITTER_BUFFER_COMMON_H_
namespace webrtc {
// Used to estimate rolling average of packets per frame.
static const float kFastConvergeMultiplier = 0.4f;
static const float kNormalConvergeMultiplier = 0.2f;
enum { kMaxNumberOfFrames = 300 };
enum { kStartNumberOfFrames = 6 };
enum { kMaxVideoDelayMs = 10000 };
enum { kPacketsPerFrameMultiplier = 5 };
enum { kFastConvergeThreshold = 5 };
enum VCMJitterBufferEnum {
kMaxConsecutiveOldFrames = 60,
kMaxConsecutiveOldPackets = 300,
// TODO(sprang): Reduce this limit once codecs don't sometimes wildly
// overshoot bitrate target.
kMaxPacketsInSession = 1400, // Allows ~2MB frames.
kBufferIncStepSizeBytes = 30000, // >20 packets.
kMaxJBFrameSizeBytes = 4000000 // sanity don't go above 4Mbyte.
};
enum VCMFrameBufferEnum {
kOutOfBoundsPacket = -7,
kNotInitialized = -6,
kOldPacket = -5,
kGeneralError = -4,
kFlushIndicator = -3, // Indicator that a flush has occurred.
kTimeStampError = -2,
kSizeError = -1,
kNoError = 0,
kIncomplete = 1, // Frame incomplete.
kCompleteSession = 3, // at least one layer in the frame complete.
kDuplicatePacket = 5 // We're receiving a duplicate packet.
};
enum VCMFrameBufferStateEnum {
kStateEmpty, // frame popped by the RTP receiver
kStateIncomplete, // frame that have one or more packet(s) stored
kStateComplete, // frame that have all packets
};
enum { kH264StartCodeLengthBytes = 4 };
} // namespace webrtc
#endif // MODULES_VIDEO_CODING_DEPRECATED_JITTER_BUFFER_COMMON_H_

View file

@ -0,0 +1,68 @@
/*
* 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.
*/
#include "modules/video_coding/deprecated/packet.h"
#include "api/rtp_headers.h"
namespace webrtc {
VCMPacket::VCMPacket()
: payloadType(0),
timestamp(0),
ntp_time_ms_(0),
seqNum(0),
dataPtr(NULL),
sizeBytes(0),
markerBit(false),
timesNacked(-1),
completeNALU(kNaluUnset),
insertStartCode(false),
video_header() {
}
VCMPacket::VCMPacket(const uint8_t* ptr,
size_t size,
const RTPHeader& rtp_header,
const RTPVideoHeader& videoHeader,
int64_t ntp_time_ms,
Timestamp receive_time)
: payloadType(rtp_header.payloadType),
timestamp(rtp_header.timestamp),
ntp_time_ms_(ntp_time_ms),
seqNum(rtp_header.sequenceNumber),
dataPtr(ptr),
sizeBytes(size),
markerBit(rtp_header.markerBit),
timesNacked(-1),
completeNALU(kNaluIncomplete),
insertStartCode(videoHeader.codec == kVideoCodecH264 &&
videoHeader.is_first_packet_in_frame),
video_header(videoHeader),
packet_info(rtp_header, receive_time) {
if (is_first_packet_in_frame() && markerBit) {
completeNALU = kNaluComplete;
} else if (is_first_packet_in_frame()) {
completeNALU = kNaluStart;
} else if (markerBit) {
completeNALU = kNaluEnd;
} else {
completeNALU = kNaluIncomplete;
}
// Playout decisions are made entirely based on first packet in a frame.
if (!is_first_packet_in_frame()) {
video_header.playout_delay = absl::nullopt;
}
}
VCMPacket::~VCMPacket() = default;
} // namespace webrtc

View file

@ -0,0 +1,80 @@
/*
* 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.
*/
#ifndef MODULES_VIDEO_CODING_DEPRECATED_PACKET_H_
#define MODULES_VIDEO_CODING_DEPRECATED_PACKET_H_
#include <stddef.h>
#include <stdint.h>
#include "absl/types/optional.h"
#include "api/rtp_headers.h"
#include "api/rtp_packet_info.h"
#include "api/units/timestamp.h"
#include "api/video/video_frame_type.h"
#include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h"
#include "modules/rtp_rtcp/source/rtp_video_header.h"
namespace webrtc {
// Used to indicate if a received packet contain a complete NALU (or equivalent)
enum VCMNaluCompleteness {
kNaluUnset = 0, // Packet has not been filled.
kNaluComplete = 1, // Packet can be decoded as is.
kNaluStart, // Packet contain beginning of NALU
kNaluIncomplete, // Packet is not beginning or end of NALU
kNaluEnd, // Packet is the end of a NALU
};
class VCMPacket {
public:
VCMPacket();
VCMPacket(const uint8_t* ptr,
size_t size,
const RTPHeader& rtp_header,
const RTPVideoHeader& video_header,
int64_t ntp_time_ms,
Timestamp receive_time);
~VCMPacket();
VideoCodecType codec() const { return video_header.codec; }
int width() const { return video_header.width; }
int height() const { return video_header.height; }
bool is_first_packet_in_frame() const {
return video_header.is_first_packet_in_frame;
}
bool is_last_packet_in_frame() const {
return video_header.is_last_packet_in_frame;
}
uint8_t payloadType;
uint32_t timestamp;
// NTP time of the capture time in local timebase in milliseconds.
int64_t ntp_time_ms_;
uint16_t seqNum;
const uint8_t* dataPtr;
size_t sizeBytes;
bool markerBit;
int timesNacked;
VCMNaluCompleteness completeNALU; // Default is kNaluIncomplete.
bool insertStartCode; // True if a start code should be inserted before this
// packet.
RTPVideoHeader video_header;
absl::optional<RtpGenericFrameDescriptor> generic_descriptor;
RtpPacketInfo packet_info;
};
} // namespace webrtc
#endif // MODULES_VIDEO_CODING_DEPRECATED_PACKET_H_

View file

@ -0,0 +1,184 @@
/*
* 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 "modules/video_coding/deprecated/receiver.h"
#include <cstdint>
#include <cstdlib>
#include <utility>
#include <vector>
#include "absl/memory/memory.h"
#include "api/video/encoded_image.h"
#include "modules/video_coding/deprecated/jitter_buffer_common.h"
#include "modules/video_coding/encoded_frame.h"
#include "modules/video_coding/internal_defines.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/trace_event.h"
#include "system_wrappers/include/clock.h"
namespace webrtc {
enum { kMaxReceiverDelayMs = 10000 };
VCMReceiver::VCMReceiver(VCMTiming* timing,
Clock* clock,
const FieldTrialsView& field_trials)
: VCMReceiver::VCMReceiver(timing,
clock,
absl::WrapUnique(EventWrapper::Create()),
absl::WrapUnique(EventWrapper::Create()),
field_trials) {}
VCMReceiver::VCMReceiver(VCMTiming* timing,
Clock* clock,
std::unique_ptr<EventWrapper> receiver_event,
std::unique_ptr<EventWrapper> jitter_buffer_event,
const FieldTrialsView& field_trials)
: clock_(clock),
jitter_buffer_(clock_, std::move(jitter_buffer_event), field_trials),
timing_(timing),
render_wait_event_(std::move(receiver_event)),
max_video_delay_ms_(kMaxVideoDelayMs) {
jitter_buffer_.Start();
}
VCMReceiver::~VCMReceiver() {
render_wait_event_->Set();
}
int32_t VCMReceiver::InsertPacket(const VCMPacket& packet) {
// Insert the packet into the jitter buffer. The packet can either be empty or
// contain media at this point.
bool retransmitted = false;
const VCMFrameBufferEnum ret =
jitter_buffer_.InsertPacket(packet, &retransmitted);
if (ret == kOldPacket) {
return VCM_OK;
} else if (ret == kFlushIndicator) {
return VCM_FLUSH_INDICATOR;
} else if (ret < 0) {
return VCM_JITTER_BUFFER_ERROR;
}
if (ret == kCompleteSession && !retransmitted) {
// We don't want to include timestamps which have suffered from
// retransmission here, since we compensate with extra retransmission
// delay within the jitter estimate.
timing_->IncomingTimestamp(packet.timestamp, clock_->CurrentTime());
}
return VCM_OK;
}
VCMEncodedFrame* VCMReceiver::FrameForDecoding(uint16_t max_wait_time_ms,
bool prefer_late_decoding) {
const int64_t start_time_ms = clock_->TimeInMilliseconds();
int64_t render_time_ms = 0;
// Exhaust wait time to get a complete frame for decoding.
VCMEncodedFrame* found_frame =
jitter_buffer_.NextCompleteFrame(max_wait_time_ms);
if (found_frame == nullptr) {
return nullptr;
}
uint32_t frame_timestamp = found_frame->RtpTimestamp();
if (absl::optional<VideoPlayoutDelay> playout_delay =
found_frame->EncodedImage().PlayoutDelay()) {
timing_->set_min_playout_delay(playout_delay->min());
timing_->set_max_playout_delay(playout_delay->max());
}
// We have a frame - Set timing and render timestamp.
timing_->SetJitterDelay(
TimeDelta::Millis(jitter_buffer_.EstimatedJitterMs()));
const Timestamp now = clock_->CurrentTime();
const int64_t now_ms = now.ms();
timing_->UpdateCurrentDelay(frame_timestamp);
render_time_ms = timing_->RenderTime(frame_timestamp, now).ms();
// Check render timing.
bool timing_error = false;
// Assume that render timing errors are due to changes in the video stream.
if (render_time_ms < 0) {
timing_error = true;
} else if (std::abs(render_time_ms - now_ms) > max_video_delay_ms_) {
int frame_delay = static_cast<int>(std::abs(render_time_ms - now_ms));
RTC_LOG(LS_WARNING)
<< "A frame about to be decoded is out of the configured "
"delay bounds ("
<< frame_delay << " > " << max_video_delay_ms_
<< "). Resetting the video jitter buffer.";
timing_error = true;
} else if (static_cast<int>(timing_->TargetVideoDelay().ms()) >
max_video_delay_ms_) {
RTC_LOG(LS_WARNING) << "The video target delay has grown larger than "
<< max_video_delay_ms_
<< " ms. Resetting jitter buffer.";
timing_error = true;
}
if (timing_error) {
// Timing error => reset timing and flush the jitter buffer.
jitter_buffer_.Flush();
timing_->Reset();
return NULL;
}
if (prefer_late_decoding) {
// Decode frame as close as possible to the render timestamp.
const int32_t available_wait_time =
max_wait_time_ms -
static_cast<int32_t>(clock_->TimeInMilliseconds() - start_time_ms);
uint16_t new_max_wait_time =
static_cast<uint16_t>(VCM_MAX(available_wait_time, 0));
uint32_t wait_time_ms = rtc::saturated_cast<uint32_t>(
timing_
->MaxWaitingTime(Timestamp::Millis(render_time_ms),
clock_->CurrentTime(),
/*too_many_frames_queued=*/false)
.ms());
if (new_max_wait_time < wait_time_ms) {
// We're not allowed to wait until the frame is supposed to be rendered,
// waiting as long as we're allowed to avoid busy looping, and then return
// NULL. Next call to this function might return the frame.
render_wait_event_->Wait(new_max_wait_time);
return NULL;
}
// Wait until it's time to render.
render_wait_event_->Wait(wait_time_ms);
}
// Extract the frame from the jitter buffer and set the render time.
VCMEncodedFrame* frame = jitter_buffer_.ExtractAndSetDecode(frame_timestamp);
if (frame == NULL) {
return NULL;
}
frame->SetRenderTime(render_time_ms);
TRACE_EVENT_ASYNC_STEP1("webrtc", "Video", frame->RtpTimestamp(),
"SetRenderTS", "render_time", frame->RenderTimeMs());
return frame;
}
void VCMReceiver::ReleaseFrame(VCMEncodedFrame* frame) {
jitter_buffer_.ReleaseFrame(frame);
}
void VCMReceiver::SetNackSettings(size_t max_nack_list_size,
int max_packet_age_to_nack,
int max_incomplete_time_ms) {
jitter_buffer_.SetNackSettings(max_nack_list_size, max_packet_age_to_nack,
max_incomplete_time_ms);
}
std::vector<uint16_t> VCMReceiver::NackList(bool* request_key_frame) {
return jitter_buffer_.GetNackList(request_key_frame);
}
} // namespace webrtc

View file

@ -0,0 +1,68 @@
/*
* 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.
*/
#ifndef MODULES_VIDEO_CODING_DEPRECATED_RECEIVER_H_
#define MODULES_VIDEO_CODING_DEPRECATED_RECEIVER_H_
#include <memory>
#include <vector>
#include "api/field_trials_view.h"
#include "modules/video_coding/deprecated/event_wrapper.h"
#include "modules/video_coding/deprecated/jitter_buffer.h"
#include "modules/video_coding/deprecated/packet.h"
#include "modules/video_coding/include/video_coding_defines.h"
#include "modules/video_coding/timing/timing.h"
namespace webrtc {
class Clock;
class VCMEncodedFrame;
class VCMReceiver {
public:
VCMReceiver(VCMTiming* timing,
Clock* clock,
const FieldTrialsView& field_trials);
// Using this constructor, you can specify a different event implemetation for
// the jitter buffer. Useful for unit tests when you want to simulate incoming
// packets, in which case the jitter buffer's wait event is different from
// that of VCMReceiver itself.
VCMReceiver(VCMTiming* timing,
Clock* clock,
std::unique_ptr<EventWrapper> receiver_event,
std::unique_ptr<EventWrapper> jitter_buffer_event,
const FieldTrialsView& field_trials);
~VCMReceiver();
int32_t InsertPacket(const VCMPacket& packet);
VCMEncodedFrame* FrameForDecoding(uint16_t max_wait_time_ms,
bool prefer_late_decoding);
void ReleaseFrame(VCMEncodedFrame* frame);
// NACK.
void SetNackSettings(size_t max_nack_list_size,
int max_packet_age_to_nack,
int max_incomplete_time_ms);
std::vector<uint16_t> NackList(bool* request_key_frame);
private:
Clock* const clock_;
VCMJitterBuffer jitter_buffer_;
VCMTiming* timing_;
std::unique_ptr<EventWrapper> render_wait_event_;
int max_video_delay_ms_;
};
} // namespace webrtc
#endif // MODULES_VIDEO_CODING_DEPRECATED_RECEIVER_H_

View file

@ -0,0 +1,493 @@
/* Copyright (c) 2013 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 "modules/video_coding/deprecated/receiver.h"
#include <string.h>
#include <cstdint>
#include <memory>
#include <queue>
#include <vector>
#include "modules/video_coding/deprecated/jitter_buffer_common.h"
#include "modules/video_coding/deprecated/packet.h"
#include "modules/video_coding/deprecated/stream_generator.h"
#include "modules/video_coding/encoded_frame.h"
#include "modules/video_coding/timing/timing.h"
#include "rtc_base/checks.h"
#include "system_wrappers/include/clock.h"
#include "test/gtest.h"
#include "test/scoped_key_value_config.h"
namespace webrtc {
class TestVCMReceiver : public ::testing::Test {
protected:
TestVCMReceiver()
: clock_(0),
timing_(&clock_, field_trials_),
receiver_(&timing_, &clock_, field_trials_),
stream_generator_(0, clock_.TimeInMilliseconds()) {}
int32_t InsertPacket(int index) {
VCMPacket packet;
bool packet_available = stream_generator_.GetPacket(&packet, index);
EXPECT_TRUE(packet_available);
if (!packet_available)
return kGeneralError; // Return here to avoid crashes below.
return receiver_.InsertPacket(packet);
}
int32_t InsertPacketAndPop(int index) {
VCMPacket packet;
bool packet_available = stream_generator_.PopPacket(&packet, index);
EXPECT_TRUE(packet_available);
if (!packet_available)
return kGeneralError; // Return here to avoid crashes below.
return receiver_.InsertPacket(packet);
}
int32_t InsertFrame(VideoFrameType frame_type, bool complete) {
int num_of_packets = complete ? 1 : 2;
stream_generator_.GenerateFrame(
frame_type,
(frame_type != VideoFrameType::kEmptyFrame) ? num_of_packets : 0,
(frame_type == VideoFrameType::kEmptyFrame) ? 1 : 0,
clock_.TimeInMilliseconds());
int32_t ret = InsertPacketAndPop(0);
if (!complete) {
// Drop the second packet.
VCMPacket packet;
stream_generator_.PopPacket(&packet, 0);
}
clock_.AdvanceTimeMilliseconds(kDefaultFramePeriodMs);
return ret;
}
bool DecodeNextFrame() {
VCMEncodedFrame* frame = receiver_.FrameForDecoding(0, false);
if (!frame)
return false;
receiver_.ReleaseFrame(frame);
return true;
}
test::ScopedKeyValueConfig field_trials_;
SimulatedClock clock_;
VCMTiming timing_;
VCMReceiver receiver_;
StreamGenerator stream_generator_;
};
TEST_F(TestVCMReceiver, NonDecodableDuration_Empty) {
const size_t kMaxNackListSize = 1000;
const int kMaxPacketAgeToNack = 1000;
const int kMaxNonDecodableDuration = 500;
const int kMinDelayMs = 500;
receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
kMaxNonDecodableDuration);
EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError);
// Advance time until it's time to decode the key frame.
clock_.AdvanceTimeMilliseconds(kMinDelayMs);
EXPECT_TRUE(DecodeNextFrame());
bool request_key_frame = false;
std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame);
EXPECT_FALSE(request_key_frame);
}
TEST_F(TestVCMReceiver, NonDecodableDuration_NoKeyFrame) {
const size_t kMaxNackListSize = 1000;
const int kMaxPacketAgeToNack = 1000;
const int kMaxNonDecodableDuration = 500;
receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
kMaxNonDecodableDuration);
const int kNumFrames = kDefaultFrameRate * kMaxNonDecodableDuration / 1000;
for (int i = 0; i < kNumFrames; ++i) {
EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError);
}
bool request_key_frame = false;
std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame);
EXPECT_TRUE(request_key_frame);
}
TEST_F(TestVCMReceiver, NonDecodableDuration_OneIncomplete) {
const size_t kMaxNackListSize = 1000;
const int kMaxPacketAgeToNack = 1000;
const int kMaxNonDecodableDuration = 500;
const int kMaxNonDecodableDurationFrames =
(kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000;
const int kMinDelayMs = 500;
receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
kMaxNonDecodableDuration);
timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs));
int64_t key_frame_inserted = clock_.TimeInMilliseconds();
EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError);
// Insert an incomplete frame.
EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, false), kNoError);
// Insert enough frames to have too long non-decodable sequence.
for (int i = 0; i < kMaxNonDecodableDurationFrames; ++i) {
EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError);
}
// Advance time until it's time to decode the key frame.
clock_.AdvanceTimeMilliseconds(kMinDelayMs - clock_.TimeInMilliseconds() -
key_frame_inserted);
EXPECT_TRUE(DecodeNextFrame());
// Make sure we get a key frame request.
bool request_key_frame = false;
std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame);
EXPECT_TRUE(request_key_frame);
}
TEST_F(TestVCMReceiver, NonDecodableDuration_NoTrigger) {
const size_t kMaxNackListSize = 1000;
const int kMaxPacketAgeToNack = 1000;
const int kMaxNonDecodableDuration = 500;
const int kMaxNonDecodableDurationFrames =
(kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000;
const int kMinDelayMs = 500;
receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
kMaxNonDecodableDuration);
timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs));
int64_t key_frame_inserted = clock_.TimeInMilliseconds();
EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError);
// Insert an incomplete frame.
EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, false), kNoError);
// Insert all but one frame to not trigger a key frame request due to
// too long duration of non-decodable frames.
for (int i = 0; i < kMaxNonDecodableDurationFrames - 1; ++i) {
EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError);
}
// Advance time until it's time to decode the key frame.
clock_.AdvanceTimeMilliseconds(kMinDelayMs - clock_.TimeInMilliseconds() -
key_frame_inserted);
EXPECT_TRUE(DecodeNextFrame());
// Make sure we don't get a key frame request since we haven't generated
// enough frames.
bool request_key_frame = false;
std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame);
EXPECT_FALSE(request_key_frame);
}
TEST_F(TestVCMReceiver, NonDecodableDuration_NoTrigger2) {
const size_t kMaxNackListSize = 1000;
const int kMaxPacketAgeToNack = 1000;
const int kMaxNonDecodableDuration = 500;
const int kMaxNonDecodableDurationFrames =
(kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000;
const int kMinDelayMs = 500;
receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
kMaxNonDecodableDuration);
timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs));
int64_t key_frame_inserted = clock_.TimeInMilliseconds();
EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError);
// Insert enough frames to have too long non-decodable sequence, except that
// we don't have any losses.
for (int i = 0; i < kMaxNonDecodableDurationFrames; ++i) {
EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError);
}
// Insert an incomplete frame.
EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, false), kNoError);
// Advance time until it's time to decode the key frame.
clock_.AdvanceTimeMilliseconds(kMinDelayMs - clock_.TimeInMilliseconds() -
key_frame_inserted);
EXPECT_TRUE(DecodeNextFrame());
// Make sure we don't get a key frame request since the non-decodable duration
// is only one frame.
bool request_key_frame = false;
std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame);
EXPECT_FALSE(request_key_frame);
}
TEST_F(TestVCMReceiver, NonDecodableDuration_KeyFrameAfterIncompleteFrames) {
const size_t kMaxNackListSize = 1000;
const int kMaxPacketAgeToNack = 1000;
const int kMaxNonDecodableDuration = 500;
const int kMaxNonDecodableDurationFrames =
(kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000;
const int kMinDelayMs = 500;
receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
kMaxNonDecodableDuration);
timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs));
int64_t key_frame_inserted = clock_.TimeInMilliseconds();
EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError);
// Insert an incomplete frame.
EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, false), kNoError);
// Insert enough frames to have too long non-decodable sequence.
for (int i = 0; i < kMaxNonDecodableDurationFrames; ++i) {
EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError);
}
EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError);
// Advance time until it's time to decode the key frame.
clock_.AdvanceTimeMilliseconds(kMinDelayMs - clock_.TimeInMilliseconds() -
key_frame_inserted);
EXPECT_TRUE(DecodeNextFrame());
// Make sure we don't get a key frame request since we have a key frame
// in the list.
bool request_key_frame = false;
std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame);
EXPECT_FALSE(request_key_frame);
}
// A simulated clock, when time elapses, will insert frames into the jitter
// buffer, based on initial settings.
class SimulatedClockWithFrames : public SimulatedClock {
public:
SimulatedClockWithFrames(StreamGenerator* stream_generator,
VCMReceiver* receiver)
: SimulatedClock(0),
stream_generator_(stream_generator),
receiver_(receiver) {}
virtual ~SimulatedClockWithFrames() {}
// If `stop_on_frame` is true and next frame arrives between now and
// now+`milliseconds`, the clock will be advanced to the arrival time of next
// frame.
// Otherwise, the clock will be advanced by `milliseconds`.
//
// For both cases, a frame will be inserted into the jitter buffer at the
// instant when the clock time is timestamps_.front().arrive_time.
//
// Return true if some frame arrives between now and now+`milliseconds`.
bool AdvanceTimeMilliseconds(int64_t milliseconds, bool stop_on_frame) {
return AdvanceTimeMicroseconds(milliseconds * 1000, stop_on_frame);
}
bool AdvanceTimeMicroseconds(int64_t microseconds, bool stop_on_frame) {
int64_t start_time = TimeInMicroseconds();
int64_t end_time = start_time + microseconds;
bool frame_injected = false;
while (!timestamps_.empty() &&
timestamps_.front().arrive_time <= end_time) {
RTC_DCHECK_GE(timestamps_.front().arrive_time, start_time);
SimulatedClock::AdvanceTimeMicroseconds(timestamps_.front().arrive_time -
TimeInMicroseconds());
GenerateAndInsertFrame((timestamps_.front().render_time + 500) / 1000);
timestamps_.pop();
frame_injected = true;
if (stop_on_frame)
return frame_injected;
}
if (TimeInMicroseconds() < end_time) {
SimulatedClock::AdvanceTimeMicroseconds(end_time - TimeInMicroseconds());
}
return frame_injected;
}
// Input timestamps are in unit Milliseconds.
// And `arrive_timestamps` must be positive and in increasing order.
// `arrive_timestamps` determine when we are going to insert frames into the
// jitter buffer.
// `render_timestamps` are the timestamps on the frame.
void SetFrames(const int64_t* arrive_timestamps,
const int64_t* render_timestamps,
size_t size) {
int64_t previous_arrive_timestamp = 0;
for (size_t i = 0; i < size; i++) {
RTC_CHECK_GE(arrive_timestamps[i], previous_arrive_timestamp);
timestamps_.push(TimestampPair(arrive_timestamps[i] * 1000,
render_timestamps[i] * 1000));
previous_arrive_timestamp = arrive_timestamps[i];
}
}
private:
struct TimestampPair {
TimestampPair(int64_t arrive_timestamp, int64_t render_timestamp)
: arrive_time(arrive_timestamp), render_time(render_timestamp) {}
int64_t arrive_time;
int64_t render_time;
};
void GenerateAndInsertFrame(int64_t render_timestamp_ms) {
VCMPacket packet;
stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameKey,
1, // media packets
0, // empty packets
render_timestamp_ms);
bool packet_available = stream_generator_->PopPacket(&packet, 0);
EXPECT_TRUE(packet_available);
if (!packet_available)
return; // Return here to avoid crashes below.
receiver_->InsertPacket(packet);
}
std::queue<TimestampPair> timestamps_;
StreamGenerator* stream_generator_;
VCMReceiver* receiver_;
};
// Use a SimulatedClockWithFrames
// Wait call will do either of these:
// 1. If `stop_on_frame` is true, the clock will be turned to the exact instant
// that the first frame comes and the frame will be inserted into the jitter
// buffer, or the clock will be turned to now + `max_time` if no frame comes in
// the window.
// 2. If `stop_on_frame` is false, the clock will be turn to now + `max_time`,
// and all the frames arriving between now and now + `max_time` will be
// inserted into the jitter buffer.
//
// This is used to simulate the JitterBuffer getting packets from internet as
// time elapses.
class FrameInjectEvent : public EventWrapper {
public:
FrameInjectEvent(SimulatedClockWithFrames* clock, bool stop_on_frame)
: clock_(clock), stop_on_frame_(stop_on_frame) {}
bool Set() override { return true; }
EventTypeWrapper Wait(int max_time_ms) override {
if (clock_->AdvanceTimeMilliseconds(max_time_ms, stop_on_frame_) &&
stop_on_frame_) {
return EventTypeWrapper::kEventSignaled;
} else {
return EventTypeWrapper::kEventTimeout;
}
}
private:
SimulatedClockWithFrames* clock_;
bool stop_on_frame_;
};
class VCMReceiverTimingTest : public ::testing::Test {
protected:
VCMReceiverTimingTest()
: clock_(&stream_generator_, &receiver_),
stream_generator_(0, clock_.TimeInMilliseconds()),
timing_(&clock_, field_trials_),
receiver_(
&timing_,
&clock_,
std::unique_ptr<EventWrapper>(new FrameInjectEvent(&clock_, false)),
std::unique_ptr<EventWrapper>(new FrameInjectEvent(&clock_, true)),
field_trials_) {}
virtual void SetUp() {}
test::ScopedKeyValueConfig field_trials_;
SimulatedClockWithFrames clock_;
StreamGenerator stream_generator_;
VCMTiming timing_;
VCMReceiver receiver_;
};
// Test whether VCMReceiver::FrameForDecoding handles parameter
// `max_wait_time_ms` correctly:
// 1. The function execution should never take more than `max_wait_time_ms`.
// 2. If the function exit before now + `max_wait_time_ms`, a frame must be
// returned.
TEST_F(VCMReceiverTimingTest, FrameForDecoding) {
const size_t kNumFrames = 100;
const int kFramePeriod = 40;
int64_t arrive_timestamps[kNumFrames];
int64_t render_timestamps[kNumFrames];
// Construct test samples.
// render_timestamps are the timestamps stored in the Frame;
// arrive_timestamps controls when the Frame packet got received.
for (size_t i = 0; i < kNumFrames; i++) {
// Preset frame rate to 25Hz.
// But we add a reasonable deviation to arrive_timestamps to mimic Internet
// fluctuation.
arrive_timestamps[i] =
(i + 1) * kFramePeriod + (i % 10) * ((i % 2) ? 1 : -1);
render_timestamps[i] = (i + 1) * kFramePeriod;
}
clock_.SetFrames(arrive_timestamps, render_timestamps, kNumFrames);
// Record how many frames we finally get out of the receiver.
size_t num_frames_return = 0;
const int64_t kMaxWaitTime = 30;
// Ideally, we should get all frames that we input in InitializeFrames.
// In the case that FrameForDecoding kills frames by error, we rely on the
// build bot to kill the test.
while (num_frames_return < kNumFrames) {
int64_t start_time = clock_.TimeInMilliseconds();
VCMEncodedFrame* frame = receiver_.FrameForDecoding(kMaxWaitTime, false);
int64_t end_time = clock_.TimeInMilliseconds();
// In any case the FrameForDecoding should not wait longer than
// max_wait_time.
// In the case that we did not get a frame, it should have been waiting for
// exactly max_wait_time. (By the testing samples we constructed above, we
// are sure there is no timing error, so the only case it returns with NULL
// is that it runs out of time.)
if (frame) {
receiver_.ReleaseFrame(frame);
++num_frames_return;
EXPECT_GE(kMaxWaitTime, end_time - start_time);
} else {
EXPECT_EQ(kMaxWaitTime, end_time - start_time);
}
}
}
// Test whether VCMReceiver::FrameForDecoding handles parameter
// `prefer_late_decoding` and `max_wait_time_ms` correctly:
// 1. The function execution should never take more than `max_wait_time_ms`.
// 2. If the function exit before now + `max_wait_time_ms`, a frame must be
// returned and the end time must be equal to the render timestamp - delay
// for decoding and rendering.
TEST_F(VCMReceiverTimingTest, FrameForDecodingPreferLateDecoding) {
const size_t kNumFrames = 100;
const int kFramePeriod = 40;
int64_t arrive_timestamps[kNumFrames];
int64_t render_timestamps[kNumFrames];
auto timings = timing_.GetTimings();
TimeDelta render_delay = timings.render_delay;
TimeDelta max_decode = timings.estimated_max_decode_time;
// Construct test samples.
// render_timestamps are the timestamps stored in the Frame;
// arrive_timestamps controls when the Frame packet got received.
for (size_t i = 0; i < kNumFrames; i++) {
// Preset frame rate to 25Hz.
// But we add a reasonable deviation to arrive_timestamps to mimic Internet
// fluctuation.
arrive_timestamps[i] =
(i + 1) * kFramePeriod + (i % 10) * ((i % 2) ? 1 : -1);
render_timestamps[i] = (i + 1) * kFramePeriod;
}
clock_.SetFrames(arrive_timestamps, render_timestamps, kNumFrames);
// Record how many frames we finally get out of the receiver.
size_t num_frames_return = 0;
const int64_t kMaxWaitTime = 30;
bool prefer_late_decoding = true;
while (num_frames_return < kNumFrames) {
int64_t start_time = clock_.TimeInMilliseconds();
VCMEncodedFrame* frame =
receiver_.FrameForDecoding(kMaxWaitTime, prefer_late_decoding);
int64_t end_time = clock_.TimeInMilliseconds();
if (frame) {
EXPECT_EQ(frame->RenderTimeMs() - max_decode.ms() - render_delay.ms(),
end_time);
receiver_.ReleaseFrame(frame);
++num_frames_return;
} else {
EXPECT_EQ(kMaxWaitTime, end_time - start_time);
}
}
}
} // namespace webrtc

View file

@ -0,0 +1,520 @@
/*
* 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 "modules/video_coding/deprecated/session_info.h"
#include <string.h>
#include <vector>
#include "absl/types/variant.h"
#include "modules/include/module_common_types.h"
#include "modules/include/module_common_types_public.h"
#include "modules/video_coding/codecs/interface/common_constants.h"
#include "modules/video_coding/codecs/vp8/include/vp8_globals.h"
#include "modules/video_coding/deprecated/jitter_buffer_common.h"
#include "modules/video_coding/deprecated/packet.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
uint16_t BufferToUWord16(const uint8_t* dataBuffer) {
return (dataBuffer[0] << 8) | dataBuffer[1];
}
} // namespace
VCMSessionInfo::VCMSessionInfo()
: complete_(false),
frame_type_(VideoFrameType::kVideoFrameDelta),
packets_(),
empty_seq_num_low_(-1),
empty_seq_num_high_(-1),
first_packet_seq_num_(-1),
last_packet_seq_num_(-1) {}
VCMSessionInfo::~VCMSessionInfo() {}
void VCMSessionInfo::UpdateDataPointers(const uint8_t* old_base_ptr,
const uint8_t* new_base_ptr) {
for (PacketIterator it = packets_.begin(); it != packets_.end(); ++it)
if ((*it).dataPtr != NULL) {
RTC_DCHECK(old_base_ptr != NULL && new_base_ptr != NULL);
(*it).dataPtr = new_base_ptr + ((*it).dataPtr - old_base_ptr);
}
}
int VCMSessionInfo::LowSequenceNumber() const {
if (packets_.empty())
return empty_seq_num_low_;
return packets_.front().seqNum;
}
int VCMSessionInfo::HighSequenceNumber() const {
if (packets_.empty())
return empty_seq_num_high_;
if (empty_seq_num_high_ == -1)
return packets_.back().seqNum;
return LatestSequenceNumber(packets_.back().seqNum, empty_seq_num_high_);
}
int VCMSessionInfo::PictureId() const {
if (packets_.empty())
return kNoPictureId;
if (packets_.front().video_header.codec == kVideoCodecVP8) {
return absl::get<RTPVideoHeaderVP8>(
packets_.front().video_header.video_type_header)
.pictureId;
} else if (packets_.front().video_header.codec == kVideoCodecVP9) {
return absl::get<RTPVideoHeaderVP9>(
packets_.front().video_header.video_type_header)
.picture_id;
} else {
return kNoPictureId;
}
}
int VCMSessionInfo::TemporalId() const {
if (packets_.empty())
return kNoTemporalIdx;
if (packets_.front().video_header.codec == kVideoCodecVP8) {
return absl::get<RTPVideoHeaderVP8>(
packets_.front().video_header.video_type_header)
.temporalIdx;
} else if (packets_.front().video_header.codec == kVideoCodecVP9) {
return absl::get<RTPVideoHeaderVP9>(
packets_.front().video_header.video_type_header)
.temporal_idx;
} else {
return kNoTemporalIdx;
}
}
bool VCMSessionInfo::LayerSync() const {
if (packets_.empty())
return false;
if (packets_.front().video_header.codec == kVideoCodecVP8) {
return absl::get<RTPVideoHeaderVP8>(
packets_.front().video_header.video_type_header)
.layerSync;
} else if (packets_.front().video_header.codec == kVideoCodecVP9) {
return absl::get<RTPVideoHeaderVP9>(
packets_.front().video_header.video_type_header)
.temporal_up_switch;
} else {
return false;
}
}
int VCMSessionInfo::Tl0PicId() const {
if (packets_.empty())
return kNoTl0PicIdx;
if (packets_.front().video_header.codec == kVideoCodecVP8) {
return absl::get<RTPVideoHeaderVP8>(
packets_.front().video_header.video_type_header)
.tl0PicIdx;
} else if (packets_.front().video_header.codec == kVideoCodecVP9) {
return absl::get<RTPVideoHeaderVP9>(
packets_.front().video_header.video_type_header)
.tl0_pic_idx;
} else {
return kNoTl0PicIdx;
}
}
std::vector<NaluInfo> VCMSessionInfo::GetNaluInfos() const {
if (packets_.empty() ||
packets_.front().video_header.codec != kVideoCodecH264)
return std::vector<NaluInfo>();
std::vector<NaluInfo> nalu_infos;
for (const VCMPacket& packet : packets_) {
const auto& h264 =
absl::get<RTPVideoHeaderH264>(packet.video_header.video_type_header);
for (size_t i = 0; i < h264.nalus_length; ++i) {
nalu_infos.push_back(h264.nalus[i]);
}
}
return nalu_infos;
}
void VCMSessionInfo::SetGofInfo(const GofInfoVP9& gof_info, size_t idx) {
if (packets_.empty())
return;
auto* vp9_header = absl::get_if<RTPVideoHeaderVP9>(
&packets_.front().video_header.video_type_header);
if (!vp9_header || vp9_header->flexible_mode)
return;
vp9_header->temporal_idx = gof_info.temporal_idx[idx];
vp9_header->temporal_up_switch = gof_info.temporal_up_switch[idx];
vp9_header->num_ref_pics = gof_info.num_ref_pics[idx];
for (uint8_t i = 0; i < gof_info.num_ref_pics[idx]; ++i) {
vp9_header->pid_diff[i] = gof_info.pid_diff[idx][i];
}
}
void VCMSessionInfo::Reset() {
complete_ = false;
frame_type_ = VideoFrameType::kVideoFrameDelta;
packets_.clear();
empty_seq_num_low_ = -1;
empty_seq_num_high_ = -1;
first_packet_seq_num_ = -1;
last_packet_seq_num_ = -1;
}
size_t VCMSessionInfo::SessionLength() const {
size_t length = 0;
for (PacketIteratorConst it = packets_.begin(); it != packets_.end(); ++it)
length += (*it).sizeBytes;
return length;
}
int VCMSessionInfo::NumPackets() const {
return packets_.size();
}
size_t VCMSessionInfo::InsertBuffer(uint8_t* frame_buffer,
PacketIterator packet_it) {
VCMPacket& packet = *packet_it;
PacketIterator it;
// Calculate the offset into the frame buffer for this packet.
size_t offset = 0;
for (it = packets_.begin(); it != packet_it; ++it)
offset += (*it).sizeBytes;
// Set the data pointer to pointing to the start of this packet in the
// frame buffer.
const uint8_t* packet_buffer = packet.dataPtr;
packet.dataPtr = frame_buffer + offset;
// We handle H.264 STAP-A packets in a special way as we need to remove the
// two length bytes between each NAL unit, and potentially add start codes.
// TODO(pbos): Remove H264 parsing from this step and use a fragmentation
// header supplied by the H264 depacketizer.
const size_t kH264NALHeaderLengthInBytes = 1;
const size_t kLengthFieldLength = 2;
const auto* h264 =
absl::get_if<RTPVideoHeaderH264>(&packet.video_header.video_type_header);
if (h264 && h264->packetization_type == kH264StapA) {
size_t required_length = 0;
const uint8_t* nalu_ptr = packet_buffer + kH264NALHeaderLengthInBytes;
while (nalu_ptr < packet_buffer + packet.sizeBytes) {
size_t length = BufferToUWord16(nalu_ptr);
required_length +=
length + (packet.insertStartCode ? kH264StartCodeLengthBytes : 0);
nalu_ptr += kLengthFieldLength + length;
}
ShiftSubsequentPackets(packet_it, required_length);
nalu_ptr = packet_buffer + kH264NALHeaderLengthInBytes;
uint8_t* frame_buffer_ptr = frame_buffer + offset;
while (nalu_ptr < packet_buffer + packet.sizeBytes) {
size_t length = BufferToUWord16(nalu_ptr);
nalu_ptr += kLengthFieldLength;
frame_buffer_ptr += Insert(nalu_ptr, length, packet.insertStartCode,
const_cast<uint8_t*>(frame_buffer_ptr));
nalu_ptr += length;
}
packet.sizeBytes = required_length;
return packet.sizeBytes;
}
ShiftSubsequentPackets(
packet_it, packet.sizeBytes +
(packet.insertStartCode ? kH264StartCodeLengthBytes : 0));
packet.sizeBytes =
Insert(packet_buffer, packet.sizeBytes, packet.insertStartCode,
const_cast<uint8_t*>(packet.dataPtr));
return packet.sizeBytes;
}
size_t VCMSessionInfo::Insert(const uint8_t* buffer,
size_t length,
bool insert_start_code,
uint8_t* frame_buffer) {
if (!buffer || !frame_buffer) {
return 0;
}
if (insert_start_code) {
const unsigned char startCode[] = {0, 0, 0, 1};
memcpy(frame_buffer, startCode, kH264StartCodeLengthBytes);
}
memcpy(frame_buffer + (insert_start_code ? kH264StartCodeLengthBytes : 0),
buffer, length);
length += (insert_start_code ? kH264StartCodeLengthBytes : 0);
return length;
}
void VCMSessionInfo::ShiftSubsequentPackets(PacketIterator it,
int steps_to_shift) {
++it;
if (it == packets_.end())
return;
uint8_t* first_packet_ptr = const_cast<uint8_t*>((*it).dataPtr);
int shift_length = 0;
// Calculate the total move length and move the data pointers in advance.
for (; it != packets_.end(); ++it) {
shift_length += (*it).sizeBytes;
if ((*it).dataPtr != NULL)
(*it).dataPtr += steps_to_shift;
}
memmove(first_packet_ptr + steps_to_shift, first_packet_ptr, shift_length);
}
void VCMSessionInfo::UpdateCompleteSession() {
if (HaveFirstPacket() && HaveLastPacket()) {
// Do we have all the packets in this session?
bool complete_session = true;
PacketIterator it = packets_.begin();
PacketIterator prev_it = it;
++it;
for (; it != packets_.end(); ++it) {
if (!InSequence(it, prev_it)) {
complete_session = false;
break;
}
prev_it = it;
}
complete_ = complete_session;
}
}
bool VCMSessionInfo::complete() const {
return complete_;
}
// Find the end of the NAL unit which the packet pointed to by `packet_it`
// belongs to. Returns an iterator to the last packet of the frame if the end
// of the NAL unit wasn't found.
VCMSessionInfo::PacketIterator VCMSessionInfo::FindNaluEnd(
PacketIterator packet_it) const {
if ((*packet_it).completeNALU == kNaluEnd ||
(*packet_it).completeNALU == kNaluComplete) {
return packet_it;
}
// Find the end of the NAL unit.
for (; packet_it != packets_.end(); ++packet_it) {
if (((*packet_it).completeNALU == kNaluComplete &&
(*packet_it).sizeBytes > 0) ||
// Found next NALU.
(*packet_it).completeNALU == kNaluStart)
return --packet_it;
if ((*packet_it).completeNALU == kNaluEnd)
return packet_it;
}
// The end wasn't found.
return --packet_it;
}
size_t VCMSessionInfo::DeletePacketData(PacketIterator start,
PacketIterator end) {
size_t bytes_to_delete = 0; // The number of bytes to delete.
PacketIterator packet_after_end = end;
++packet_after_end;
// Get the number of bytes to delete.
// Clear the size of these packets.
for (PacketIterator it = start; it != packet_after_end; ++it) {
bytes_to_delete += (*it).sizeBytes;
(*it).sizeBytes = 0;
(*it).dataPtr = NULL;
}
if (bytes_to_delete > 0)
ShiftSubsequentPackets(end, -static_cast<int>(bytes_to_delete));
return bytes_to_delete;
}
VCMSessionInfo::PacketIterator VCMSessionInfo::FindNextPartitionBeginning(
PacketIterator it) const {
while (it != packets_.end()) {
if (absl::get<RTPVideoHeaderVP8>((*it).video_header.video_type_header)
.beginningOfPartition) {
return it;
}
++it;
}
return it;
}
VCMSessionInfo::PacketIterator VCMSessionInfo::FindPartitionEnd(
PacketIterator it) const {
RTC_DCHECK_EQ((*it).codec(), kVideoCodecVP8);
PacketIterator prev_it = it;
const int partition_id =
absl::get<RTPVideoHeaderVP8>((*it).video_header.video_type_header)
.partitionId;
while (it != packets_.end()) {
bool beginning =
absl::get<RTPVideoHeaderVP8>((*it).video_header.video_type_header)
.beginningOfPartition;
int current_partition_id =
absl::get<RTPVideoHeaderVP8>((*it).video_header.video_type_header)
.partitionId;
bool packet_loss_found = (!beginning && !InSequence(it, prev_it));
if (packet_loss_found ||
(beginning && current_partition_id != partition_id)) {
// Missing packet, the previous packet was the last in sequence.
return prev_it;
}
prev_it = it;
++it;
}
return prev_it;
}
bool VCMSessionInfo::InSequence(const PacketIterator& packet_it,
const PacketIterator& prev_packet_it) {
// If the two iterators are pointing to the same packet they are considered
// to be in sequence.
return (packet_it == prev_packet_it ||
(static_cast<uint16_t>((*prev_packet_it).seqNum + 1) ==
(*packet_it).seqNum));
}
size_t VCMSessionInfo::MakeDecodable() {
size_t return_length = 0;
if (packets_.empty()) {
return 0;
}
PacketIterator it = packets_.begin();
// Make sure we remove the first NAL unit if it's not decodable.
if ((*it).completeNALU == kNaluIncomplete || (*it).completeNALU == kNaluEnd) {
PacketIterator nalu_end = FindNaluEnd(it);
return_length += DeletePacketData(it, nalu_end);
it = nalu_end;
}
PacketIterator prev_it = it;
// Take care of the rest of the NAL units.
for (; it != packets_.end(); ++it) {
bool start_of_nalu = ((*it).completeNALU == kNaluStart ||
(*it).completeNALU == kNaluComplete);
if (!start_of_nalu && !InSequence(it, prev_it)) {
// Found a sequence number gap due to packet loss.
PacketIterator nalu_end = FindNaluEnd(it);
return_length += DeletePacketData(it, nalu_end);
it = nalu_end;
}
prev_it = it;
}
return return_length;
}
bool VCMSessionInfo::HaveFirstPacket() const {
return !packets_.empty() && (first_packet_seq_num_ != -1);
}
bool VCMSessionInfo::HaveLastPacket() const {
return !packets_.empty() && (last_packet_seq_num_ != -1);
}
int VCMSessionInfo::InsertPacket(const VCMPacket& packet,
uint8_t* frame_buffer,
const FrameData& frame_data) {
if (packet.video_header.frame_type == VideoFrameType::kEmptyFrame) {
// Update sequence number of an empty packet.
// Only media packets are inserted into the packet list.
InformOfEmptyPacket(packet.seqNum);
return 0;
}
if (packets_.size() == kMaxPacketsInSession) {
RTC_LOG(LS_ERROR) << "Max number of packets per frame has been reached.";
return -1;
}
// Find the position of this packet in the packet list in sequence number
// order and insert it. Loop over the list in reverse order.
ReversePacketIterator rit = packets_.rbegin();
for (; rit != packets_.rend(); ++rit)
if (LatestSequenceNumber(packet.seqNum, (*rit).seqNum) == packet.seqNum)
break;
// Check for duplicate packets.
if (rit != packets_.rend() && (*rit).seqNum == packet.seqNum &&
(*rit).sizeBytes > 0)
return -2;
if (packet.codec() == kVideoCodecH264) {
frame_type_ = packet.video_header.frame_type;
if (packet.is_first_packet_in_frame() &&
(first_packet_seq_num_ == -1 ||
IsNewerSequenceNumber(first_packet_seq_num_, packet.seqNum))) {
first_packet_seq_num_ = packet.seqNum;
}
if (packet.markerBit &&
(last_packet_seq_num_ == -1 ||
IsNewerSequenceNumber(packet.seqNum, last_packet_seq_num_))) {
last_packet_seq_num_ = packet.seqNum;
}
} else {
// Only insert media packets between first and last packets (when
// available).
// Placing check here, as to properly account for duplicate packets.
// Check if this is first packet (only valid for some codecs)
// Should only be set for one packet per session.
if (packet.is_first_packet_in_frame() && first_packet_seq_num_ == -1) {
// The first packet in a frame signals the frame type.
frame_type_ = packet.video_header.frame_type;
// Store the sequence number for the first packet.
first_packet_seq_num_ = static_cast<int>(packet.seqNum);
} else if (first_packet_seq_num_ != -1 &&
IsNewerSequenceNumber(first_packet_seq_num_, packet.seqNum)) {
RTC_LOG(LS_WARNING)
<< "Received packet with a sequence number which is out "
"of frame boundaries";
return -3;
} else if (frame_type_ == VideoFrameType::kEmptyFrame &&
packet.video_header.frame_type != VideoFrameType::kEmptyFrame) {
// Update the frame type with the type of the first media packet.
// TODO(mikhal): Can this trigger?
frame_type_ = packet.video_header.frame_type;
}
// Track the marker bit, should only be set for one packet per session.
if (packet.markerBit && last_packet_seq_num_ == -1) {
last_packet_seq_num_ = static_cast<int>(packet.seqNum);
} else if (last_packet_seq_num_ != -1 &&
IsNewerSequenceNumber(packet.seqNum, last_packet_seq_num_)) {
RTC_LOG(LS_WARNING)
<< "Received packet with a sequence number which is out "
"of frame boundaries";
return -3;
}
}
// The insert operation invalidates the iterator `rit`.
PacketIterator packet_list_it = packets_.insert(rit.base(), packet);
size_t returnLength = InsertBuffer(frame_buffer, packet_list_it);
UpdateCompleteSession();
return static_cast<int>(returnLength);
}
void VCMSessionInfo::InformOfEmptyPacket(uint16_t seq_num) {
// Empty packets may be FEC or filler packets. They are sequential and
// follow the data packets, therefore, we should only keep track of the high
// and low sequence numbers and may assume that the packets in between are
// empty packets belonging to the same frame (timestamp).
if (empty_seq_num_high_ == -1)
empty_seq_num_high_ = seq_num;
else
empty_seq_num_high_ = LatestSequenceNumber(seq_num, empty_seq_num_high_);
if (empty_seq_num_low_ == -1 ||
IsNewerSequenceNumber(empty_seq_num_low_, seq_num))
empty_seq_num_low_ = seq_num;
}
} // namespace webrtc

View file

@ -0,0 +1,121 @@
/*
* 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.
*/
#ifndef MODULES_VIDEO_CODING_DEPRECATED_SESSION_INFO_H_
#define MODULES_VIDEO_CODING_DEPRECATED_SESSION_INFO_H_
#include <stddef.h>
#include <stdint.h>
#include <list>
#include <vector>
#include "modules/video_coding/codecs/h264/include/h264_globals.h"
#include "modules/video_coding/codecs/vp9/include/vp9_globals.h"
#include "modules/video_coding/deprecated/packet.h"
namespace webrtc {
// Used to pass data from jitter buffer to session info.
// This data is then used in determining whether a frame is decodable.
struct FrameData {
int64_t rtt_ms;
float rolling_average_packets_per_frame;
};
class VCMSessionInfo {
public:
VCMSessionInfo();
~VCMSessionInfo();
void UpdateDataPointers(const uint8_t* old_base_ptr,
const uint8_t* new_base_ptr);
void Reset();
int InsertPacket(const VCMPacket& packet,
uint8_t* frame_buffer,
const FrameData& frame_data);
bool complete() const;
// Makes the frame decodable. I.e., only contain decodable NALUs. All
// non-decodable NALUs will be deleted and packets will be moved to in
// memory to remove any empty space.
// Returns the number of bytes deleted from the session.
size_t MakeDecodable();
size_t SessionLength() const;
int NumPackets() const;
bool HaveFirstPacket() const;
bool HaveLastPacket() const;
webrtc::VideoFrameType FrameType() const { return frame_type_; }
int LowSequenceNumber() const;
// Returns highest sequence number, media or empty.
int HighSequenceNumber() const;
int PictureId() const;
int TemporalId() const;
bool LayerSync() const;
int Tl0PicId() const;
std::vector<NaluInfo> GetNaluInfos() const;
void SetGofInfo(const GofInfoVP9& gof_info, size_t idx);
private:
enum { kMaxVP8Partitions = 9 };
typedef std::list<VCMPacket> PacketList;
typedef PacketList::iterator PacketIterator;
typedef PacketList::const_iterator PacketIteratorConst;
typedef PacketList::reverse_iterator ReversePacketIterator;
void InformOfEmptyPacket(uint16_t seq_num);
// Finds the packet of the beginning of the next VP8 partition. If
// none is found the returned iterator points to `packets_.end()`.
// `it` is expected to point to the last packet of the previous partition,
// or to the first packet of the frame. `packets_skipped` is incremented
// for each packet found which doesn't have the beginning bit set.
PacketIterator FindNextPartitionBeginning(PacketIterator it) const;
// Returns an iterator pointing to the last packet of the partition pointed to
// by `it`.
PacketIterator FindPartitionEnd(PacketIterator it) const;
static bool InSequence(const PacketIterator& it,
const PacketIterator& prev_it);
size_t InsertBuffer(uint8_t* frame_buffer, PacketIterator packetIterator);
size_t Insert(const uint8_t* buffer,
size_t length,
bool insert_start_code,
uint8_t* frame_buffer);
void ShiftSubsequentPackets(PacketIterator it, int steps_to_shift);
PacketIterator FindNaluEnd(PacketIterator packet_iter) const;
// Deletes the data of all packets between `start` and `end`, inclusively.
// Note that this function doesn't delete the actual packets.
size_t DeletePacketData(PacketIterator start, PacketIterator end);
void UpdateCompleteSession();
bool complete_;
webrtc::VideoFrameType frame_type_;
// Packets in this frame.
PacketList packets_;
int empty_seq_num_low_;
int empty_seq_num_high_;
// The following two variables correspond to the first and last media packets
// in a session defined by the first packet flag and the marker bit.
// They are not necessarily equal to the front and back packets, as packets
// may enter out of order.
// TODO(mikhal): Refactor the list to use a map.
int first_packet_seq_num_;
int last_packet_seq_num_;
};
} // namespace webrtc
#endif // MODULES_VIDEO_CODING_DEPRECATED_SESSION_INFO_H_

View file

@ -0,0 +1,469 @@
/*
* 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 "modules/video_coding/deprecated/session_info.h"
#include <string.h>
#include "modules/video_coding/deprecated/packet.h"
#include "test/gtest.h"
namespace webrtc {
class TestSessionInfo : public ::testing::Test {
protected:
virtual void SetUp() {
memset(packet_buffer_, 0, sizeof(packet_buffer_));
memset(frame_buffer_, 0, sizeof(frame_buffer_));
session_.Reset();
packet_.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
packet_.sizeBytes = packet_buffer_size();
packet_.dataPtr = packet_buffer_;
packet_.seqNum = 0;
packet_.timestamp = 0;
frame_data.rtt_ms = 0;
frame_data.rolling_average_packets_per_frame = -1;
}
void FillPacket(uint8_t start_value) {
for (size_t i = 0; i < packet_buffer_size(); ++i)
packet_buffer_[i] = start_value + i;
}
void VerifyPacket(uint8_t* start_ptr, uint8_t start_value) {
for (size_t j = 0; j < packet_buffer_size(); ++j) {
ASSERT_EQ(start_value + j, start_ptr[j]);
}
}
size_t packet_buffer_size() const {
return sizeof(packet_buffer_) / sizeof(packet_buffer_[0]);
}
size_t frame_buffer_size() const {
return sizeof(frame_buffer_) / sizeof(frame_buffer_[0]);
}
enum { kPacketBufferSize = 10 };
uint8_t packet_buffer_[kPacketBufferSize];
uint8_t frame_buffer_[10 * kPacketBufferSize];
VCMSessionInfo session_;
VCMPacket packet_;
FrameData frame_data;
};
class TestNalUnits : public TestSessionInfo {
protected:
virtual void SetUp() {
TestSessionInfo::SetUp();
packet_.video_header.codec = kVideoCodecVP8;
}
bool VerifyNalu(int offset, int packets_expected, int start_value) {
EXPECT_GE(session_.SessionLength(),
packets_expected * packet_buffer_size());
for (int i = 0; i < packets_expected; ++i) {
int packet_index = (offset + i) * packet_buffer_size();
VerifyPacket(frame_buffer_ + packet_index, start_value + i);
}
return true;
}
};
class TestNackList : public TestSessionInfo {
protected:
static const size_t kMaxSeqNumListLength = 30;
virtual void SetUp() {
TestSessionInfo::SetUp();
seq_num_list_length_ = 0;
memset(seq_num_list_, 0, sizeof(seq_num_list_));
}
void BuildSeqNumList(uint16_t low, uint16_t high) {
size_t i = 0;
while (low != high + 1) {
EXPECT_LT(i, kMaxSeqNumListLength);
if (i >= kMaxSeqNumListLength) {
seq_num_list_length_ = kMaxSeqNumListLength;
return;
}
seq_num_list_[i] = low;
low++;
i++;
}
seq_num_list_length_ = i;
}
void VerifyAll(int value) {
for (int i = 0; i < seq_num_list_length_; ++i)
EXPECT_EQ(seq_num_list_[i], value);
}
int seq_num_list_[kMaxSeqNumListLength];
int seq_num_list_length_;
};
TEST_F(TestSessionInfo, TestSimpleAPIs) {
packet_.video_header.is_first_packet_in_frame = true;
packet_.seqNum = 0xFFFE;
packet_.sizeBytes = packet_buffer_size();
packet_.video_header.frame_type = VideoFrameType::kVideoFrameKey;
FillPacket(0);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
EXPECT_FALSE(session_.HaveLastPacket());
EXPECT_EQ(VideoFrameType::kVideoFrameKey, session_.FrameType());
packet_.video_header.is_first_packet_in_frame = false;
packet_.markerBit = true;
packet_.seqNum += 1;
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
EXPECT_TRUE(session_.HaveLastPacket());
EXPECT_EQ(packet_.seqNum, session_.HighSequenceNumber());
EXPECT_EQ(0xFFFE, session_.LowSequenceNumber());
// Insert empty packet which will be the new high sequence number.
// To make things more difficult we will make sure to have a wrap here.
packet_.video_header.is_first_packet_in_frame = false;
packet_.markerBit = true;
packet_.seqNum = 2;
packet_.sizeBytes = 0;
packet_.video_header.frame_type = VideoFrameType::kEmptyFrame;
EXPECT_EQ(0, session_.InsertPacket(packet_, frame_buffer_, frame_data));
EXPECT_EQ(packet_.seqNum, session_.HighSequenceNumber());
}
TEST_F(TestSessionInfo, NormalOperation) {
packet_.seqNum = 0xFFFF;
packet_.video_header.is_first_packet_in_frame = true;
packet_.markerBit = false;
FillPacket(0);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.video_header.is_first_packet_in_frame = false;
for (int i = 1; i < 9; ++i) {
packet_.seqNum += 1;
FillPacket(i);
ASSERT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
}
packet_.seqNum += 1;
packet_.markerBit = true;
FillPacket(9);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
EXPECT_EQ(10 * packet_buffer_size(), session_.SessionLength());
for (int i = 0; i < 10; ++i) {
SCOPED_TRACE("Calling VerifyPacket");
VerifyPacket(frame_buffer_ + i * packet_buffer_size(), i);
}
}
TEST_F(TestSessionInfo, OutOfBoundsPackets1PacketFrame) {
packet_.seqNum = 0x0001;
packet_.video_header.is_first_packet_in_frame = true;
packet_.markerBit = true;
FillPacket(1);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.seqNum = 0x0004;
packet_.video_header.is_first_packet_in_frame = true;
packet_.markerBit = true;
FillPacket(1);
EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data));
packet_.seqNum = 0x0000;
packet_.video_header.is_first_packet_in_frame = false;
packet_.markerBit = false;
FillPacket(1);
EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data));
}
TEST_F(TestSessionInfo, SetMarkerBitOnce) {
packet_.seqNum = 0x0005;
packet_.video_header.is_first_packet_in_frame = false;
packet_.markerBit = true;
FillPacket(1);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
++packet_.seqNum;
packet_.video_header.is_first_packet_in_frame = true;
packet_.markerBit = true;
FillPacket(1);
EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data));
}
TEST_F(TestSessionInfo, OutOfBoundsPacketsBase) {
// Allow packets in the range 5-6.
packet_.seqNum = 0x0005;
packet_.video_header.is_first_packet_in_frame = true;
packet_.markerBit = false;
FillPacket(1);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
// Insert an older packet with a first packet set.
packet_.seqNum = 0x0004;
packet_.video_header.is_first_packet_in_frame = true;
packet_.markerBit = true;
FillPacket(1);
EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data));
packet_.seqNum = 0x0006;
packet_.video_header.is_first_packet_in_frame = true;
packet_.markerBit = true;
FillPacket(1);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.seqNum = 0x0008;
packet_.video_header.is_first_packet_in_frame = false;
packet_.markerBit = true;
FillPacket(1);
EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data));
}
TEST_F(TestSessionInfo, OutOfBoundsPacketsWrap) {
packet_.seqNum = 0xFFFE;
packet_.video_header.is_first_packet_in_frame = true;
packet_.markerBit = false;
FillPacket(1);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.seqNum = 0x0004;
packet_.video_header.is_first_packet_in_frame = false;
packet_.markerBit = true;
FillPacket(1);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.seqNum = 0x0002;
packet_.video_header.is_first_packet_in_frame = false;
packet_.markerBit = false;
FillPacket(1);
ASSERT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.seqNum = 0xFFF0;
packet_.video_header.is_first_packet_in_frame = false;
packet_.markerBit = false;
FillPacket(1);
EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data));
packet_.seqNum = 0x0006;
packet_.video_header.is_first_packet_in_frame = false;
packet_.markerBit = false;
FillPacket(1);
EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data));
}
TEST_F(TestSessionInfo, OutOfBoundsOutOfOrder) {
// Insert out of bound regular packets, and then the first and last packet.
// Verify that correct bounds are maintained.
packet_.seqNum = 0x0003;
packet_.video_header.is_first_packet_in_frame = false;
packet_.markerBit = false;
FillPacket(1);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
// Insert an older packet with a first packet set.
packet_.seqNum = 0x0005;
packet_.video_header.is_first_packet_in_frame = true;
packet_.markerBit = false;
FillPacket(1);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.seqNum = 0x0004;
packet_.video_header.is_first_packet_in_frame = false;
packet_.markerBit = false;
FillPacket(1);
EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data));
packet_.seqNum = 0x0010;
packet_.video_header.is_first_packet_in_frame = false;
packet_.markerBit = false;
FillPacket(1);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.seqNum = 0x0008;
packet_.video_header.is_first_packet_in_frame = false;
packet_.markerBit = true;
FillPacket(1);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.seqNum = 0x0009;
packet_.video_header.is_first_packet_in_frame = false;
packet_.markerBit = false;
FillPacket(1);
EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data));
}
TEST_F(TestNalUnits, OnlyReceivedEmptyPacket) {
packet_.video_header.is_first_packet_in_frame = false;
packet_.completeNALU = kNaluComplete;
packet_.video_header.frame_type = VideoFrameType::kEmptyFrame;
packet_.sizeBytes = 0;
packet_.seqNum = 0;
packet_.markerBit = false;
EXPECT_EQ(0, session_.InsertPacket(packet_, frame_buffer_, frame_data));
EXPECT_EQ(0U, session_.MakeDecodable());
EXPECT_EQ(0U, session_.SessionLength());
}
TEST_F(TestNalUnits, OneIsolatedNaluLoss) {
packet_.video_header.is_first_packet_in_frame = true;
packet_.completeNALU = kNaluComplete;
packet_.seqNum = 0;
packet_.markerBit = false;
FillPacket(0);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.video_header.is_first_packet_in_frame = false;
packet_.completeNALU = kNaluComplete;
packet_.seqNum += 2;
packet_.markerBit = true;
FillPacket(2);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
EXPECT_EQ(0U, session_.MakeDecodable());
EXPECT_EQ(2 * packet_buffer_size(), session_.SessionLength());
SCOPED_TRACE("Calling VerifyNalu");
EXPECT_TRUE(VerifyNalu(0, 1, 0));
SCOPED_TRACE("Calling VerifyNalu");
EXPECT_TRUE(VerifyNalu(1, 1, 2));
}
TEST_F(TestNalUnits, LossInMiddleOfNalu) {
packet_.video_header.is_first_packet_in_frame = true;
packet_.completeNALU = kNaluComplete;
packet_.seqNum = 0;
packet_.markerBit = false;
FillPacket(0);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.video_header.is_first_packet_in_frame = false;
packet_.completeNALU = kNaluEnd;
packet_.seqNum += 2;
packet_.markerBit = true;
FillPacket(2);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
EXPECT_EQ(packet_buffer_size(), session_.MakeDecodable());
EXPECT_EQ(packet_buffer_size(), session_.SessionLength());
SCOPED_TRACE("Calling VerifyNalu");
EXPECT_TRUE(VerifyNalu(0, 1, 0));
}
TEST_F(TestNalUnits, StartAndEndOfLastNalUnitLost) {
packet_.video_header.is_first_packet_in_frame = true;
packet_.completeNALU = kNaluComplete;
packet_.seqNum = 0;
packet_.markerBit = false;
FillPacket(0);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.video_header.is_first_packet_in_frame = false;
packet_.completeNALU = kNaluIncomplete;
packet_.seqNum += 2;
packet_.markerBit = false;
FillPacket(1);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
EXPECT_EQ(packet_buffer_size(), session_.MakeDecodable());
EXPECT_EQ(packet_buffer_size(), session_.SessionLength());
SCOPED_TRACE("Calling VerifyNalu");
EXPECT_TRUE(VerifyNalu(0, 1, 0));
}
TEST_F(TestNalUnits, ReorderWrapNoLoss) {
packet_.seqNum = 0xFFFF;
packet_.video_header.is_first_packet_in_frame = false;
packet_.completeNALU = kNaluIncomplete;
packet_.seqNum += 1;
packet_.markerBit = false;
FillPacket(1);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.video_header.is_first_packet_in_frame = true;
packet_.completeNALU = kNaluComplete;
packet_.seqNum -= 1;
packet_.markerBit = false;
FillPacket(0);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.video_header.is_first_packet_in_frame = false;
packet_.completeNALU = kNaluEnd;
packet_.seqNum += 2;
packet_.markerBit = true;
FillPacket(2);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
EXPECT_EQ(0U, session_.MakeDecodable());
EXPECT_EQ(3 * packet_buffer_size(), session_.SessionLength());
SCOPED_TRACE("Calling VerifyNalu");
EXPECT_TRUE(VerifyNalu(0, 1, 0));
}
TEST_F(TestNalUnits, WrapLosses) {
packet_.seqNum = 0xFFFF;
packet_.video_header.is_first_packet_in_frame = false;
packet_.completeNALU = kNaluIncomplete;
packet_.markerBit = false;
FillPacket(1);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.video_header.is_first_packet_in_frame = false;
packet_.completeNALU = kNaluEnd;
packet_.seqNum += 2;
packet_.markerBit = true;
FillPacket(2);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
EXPECT_EQ(2 * packet_buffer_size(), session_.MakeDecodable());
EXPECT_EQ(0U, session_.SessionLength());
}
TEST_F(TestNalUnits, ReorderWrapLosses) {
packet_.seqNum = 0xFFFF;
packet_.video_header.is_first_packet_in_frame = false;
packet_.completeNALU = kNaluEnd;
packet_.seqNum += 2;
packet_.markerBit = true;
FillPacket(2);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
packet_.seqNum -= 2;
packet_.video_header.is_first_packet_in_frame = false;
packet_.completeNALU = kNaluIncomplete;
packet_.markerBit = false;
FillPacket(1);
EXPECT_EQ(packet_buffer_size(), static_cast<size_t>(session_.InsertPacket(
packet_, frame_buffer_, frame_data)));
EXPECT_EQ(2 * packet_buffer_size(), session_.MakeDecodable());
EXPECT_EQ(0U, session_.SessionLength());
}
} // namespace webrtc

View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2013 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 "modules/video_coding/deprecated/stream_generator.h"
#include <string.h>
#include <list>
#include "modules/video_coding/deprecated/packet.h"
#include "rtc_base/checks.h"
namespace webrtc {
StreamGenerator::StreamGenerator(uint16_t start_seq_num, int64_t current_time)
: packets_(), sequence_number_(start_seq_num), start_time_(current_time) {}
void StreamGenerator::Init(uint16_t start_seq_num, int64_t current_time) {
packets_.clear();
sequence_number_ = start_seq_num;
start_time_ = current_time;
memset(packet_buffer_, 0, sizeof(packet_buffer_));
}
void StreamGenerator::GenerateFrame(VideoFrameType type,
int num_media_packets,
int num_empty_packets,
int64_t time_ms) {
uint32_t timestamp = 90 * (time_ms - start_time_);
for (int i = 0; i < num_media_packets; ++i) {
const int packet_size =
(kFrameSize + num_media_packets / 2) / num_media_packets;
bool marker_bit = (i == num_media_packets - 1);
packets_.push_back(GeneratePacket(sequence_number_, timestamp, packet_size,
(i == 0), marker_bit, type));
++sequence_number_;
}
for (int i = 0; i < num_empty_packets; ++i) {
packets_.push_back(GeneratePacket(sequence_number_, timestamp, 0, false,
false, VideoFrameType::kEmptyFrame));
++sequence_number_;
}
}
VCMPacket StreamGenerator::GeneratePacket(uint16_t sequence_number,
uint32_t timestamp,
unsigned int size,
bool first_packet,
bool marker_bit,
VideoFrameType type) {
RTC_CHECK_LT(size, kMaxPacketSize);
VCMPacket packet;
packet.seqNum = sequence_number;
packet.timestamp = timestamp;
packet.video_header.frame_type = type;
packet.video_header.is_first_packet_in_frame = first_packet;
packet.markerBit = marker_bit;
packet.sizeBytes = size;
packet.dataPtr = packet_buffer_;
if (packet.is_first_packet_in_frame())
packet.completeNALU = kNaluStart;
else if (packet.markerBit)
packet.completeNALU = kNaluEnd;
else
packet.completeNALU = kNaluIncomplete;
return packet;
}
bool StreamGenerator::PopPacket(VCMPacket* packet, int index) {
std::list<VCMPacket>::iterator it = GetPacketIterator(index);
if (it == packets_.end())
return false;
if (packet)
*packet = (*it);
packets_.erase(it);
return true;
}
bool StreamGenerator::GetPacket(VCMPacket* packet, int index) {
std::list<VCMPacket>::iterator it = GetPacketIterator(index);
if (it == packets_.end())
return false;
if (packet)
*packet = (*it);
return true;
}
bool StreamGenerator::NextPacket(VCMPacket* packet) {
if (packets_.empty())
return false;
if (packet != NULL)
*packet = packets_.front();
packets_.pop_front();
return true;
}
void StreamGenerator::DropLastPacket() {
packets_.pop_back();
}
uint16_t StreamGenerator::NextSequenceNumber() const {
if (packets_.empty())
return sequence_number_;
return packets_.front().seqNum;
}
int StreamGenerator::PacketsRemaining() const {
return packets_.size();
}
std::list<VCMPacket>::iterator StreamGenerator::GetPacketIterator(int index) {
std::list<VCMPacket>::iterator it = packets_.begin();
for (int i = 0; i < index; ++i) {
++it;
if (it == packets_.end())
break;
}
return it;
}
} // namespace webrtc

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2013 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.
*/
#ifndef MODULES_VIDEO_CODING_DEPRECATED_STREAM_GENERATOR_H_
#define MODULES_VIDEO_CODING_DEPRECATED_STREAM_GENERATOR_H_
#include <stdint.h>
#include <list>
#include "modules/video_coding/deprecated/packet.h"
namespace webrtc {
const unsigned int kDefaultBitrateKbps = 1000;
const unsigned int kDefaultFrameRate = 25;
const unsigned int kMaxPacketSize = 1500;
const unsigned int kFrameSize =
(kDefaultBitrateKbps + kDefaultFrameRate * 4) / (kDefaultFrameRate * 8);
const int kDefaultFramePeriodMs = 1000 / kDefaultFrameRate;
class StreamGenerator {
public:
StreamGenerator(uint16_t start_seq_num, int64_t current_time);
StreamGenerator(const StreamGenerator&) = delete;
StreamGenerator& operator=(const StreamGenerator&) = delete;
void Init(uint16_t start_seq_num, int64_t current_time);
// `time_ms` denotes the timestamp you want to put on the frame, and the unit
// is millisecond. GenerateFrame will translate `time_ms` into a 90kHz
// timestamp and put it on the frame.
void GenerateFrame(VideoFrameType type,
int num_media_packets,
int num_empty_packets,
int64_t time_ms);
bool PopPacket(VCMPacket* packet, int index);
void DropLastPacket();
bool GetPacket(VCMPacket* packet, int index);
bool NextPacket(VCMPacket* packet);
uint16_t NextSequenceNumber() const;
int PacketsRemaining() const;
private:
VCMPacket GeneratePacket(uint16_t sequence_number,
uint32_t timestamp,
unsigned int size,
bool first_packet,
bool marker_bit,
VideoFrameType type);
std::list<VCMPacket>::iterator GetPacketIterator(int index);
std::list<VCMPacket> packets_;
uint16_t sequence_number_;
int64_t start_time_;
uint8_t packet_buffer_[kMaxPacketSize];
};
} // namespace webrtc
#endif // MODULES_VIDEO_CODING_DEPRECATED_STREAM_GENERATOR_H_