Move vp8 rtp depacketization to VideoRtpDepacketizerVp8
Bug: webrtc:11152 Change-Id: Ic2b7fd091cb4d095ce29fbe06196f6424c08fce1 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/161451 Reviewed-by: Markus Handell <handellm@webrtc.org> Commit-Queue: Danil Chapovalov <danilchap@webrtc.org> Cr-Commit-Position: refs/heads/master@{#30088}
This commit is contained in:
committed by
Commit Bot
parent
41466b7bef
commit
eae6896f76
@ -215,6 +215,8 @@ rtc_library("rtp_rtcp") {
|
||||
"source/video_rtp_depacketizer.h",
|
||||
"source/video_rtp_depacketizer_raw.cc",
|
||||
"source/video_rtp_depacketizer_raw.h",
|
||||
"source/video_rtp_depacketizer_vp8.cc",
|
||||
"source/video_rtp_depacketizer_vp8.h",
|
||||
]
|
||||
|
||||
if (rtc_enable_bwe_test_logging) {
|
||||
@ -480,6 +482,7 @@ if (rtc_include_tests) {
|
||||
"source/ulpfec_header_reader_writer_unittest.cc",
|
||||
"source/ulpfec_receiver_unittest.cc",
|
||||
"source/video_rtp_depacketizer_raw_unittest.cc",
|
||||
"source/video_rtp_depacketizer_vp8_unittest.cc",
|
||||
]
|
||||
deps = [
|
||||
":fec_test_helper",
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
#include "absl/types/optional.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_format.h"
|
||||
#include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
|
||||
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
|
||||
@ -56,7 +57,12 @@ std::unique_ptr<VideoRtpDepacketizer> CreateVideoRtpDepacketizer(
|
||||
VideoCodecType codec) {
|
||||
// TODO(bugs.webrtc.org/11152): switch on codec and create specialized
|
||||
// VideoRtpDepacketizers when they are migrated to new interface.
|
||||
return std::make_unique<LegacyRtpDepacketizer>(codec);
|
||||
switch (codec) {
|
||||
case kVideoCodecVP8:
|
||||
return std::make_unique<VideoRtpDepacketizerVp8>();
|
||||
default:
|
||||
return std::make_unique<LegacyRtpDepacketizer>(codec);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
||||
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.h"
|
||||
#include "modules/video_coding/codecs/interface/common_constants.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
@ -33,119 +34,6 @@ constexpr int kTBit = 0x20;
|
||||
constexpr int kKBit = 0x10;
|
||||
constexpr int kYBit = 0x20;
|
||||
|
||||
int ParseVP8PictureID(RTPVideoHeaderVP8* vp8,
|
||||
const uint8_t** data,
|
||||
size_t* data_length,
|
||||
size_t* parsed_bytes) {
|
||||
if (*data_length == 0)
|
||||
return -1;
|
||||
|
||||
vp8->pictureId = (**data & 0x7F);
|
||||
if (**data & 0x80) {
|
||||
(*data)++;
|
||||
(*parsed_bytes)++;
|
||||
if (--(*data_length) == 0)
|
||||
return -1;
|
||||
// PictureId is 15 bits
|
||||
vp8->pictureId = (vp8->pictureId << 8) + **data;
|
||||
}
|
||||
(*data)++;
|
||||
(*parsed_bytes)++;
|
||||
(*data_length)--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ParseVP8Tl0PicIdx(RTPVideoHeaderVP8* vp8,
|
||||
const uint8_t** data,
|
||||
size_t* data_length,
|
||||
size_t* parsed_bytes) {
|
||||
if (*data_length == 0)
|
||||
return -1;
|
||||
|
||||
vp8->tl0PicIdx = **data;
|
||||
(*data)++;
|
||||
(*parsed_bytes)++;
|
||||
(*data_length)--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ParseVP8TIDAndKeyIdx(RTPVideoHeaderVP8* vp8,
|
||||
const uint8_t** data,
|
||||
size_t* data_length,
|
||||
size_t* parsed_bytes,
|
||||
bool has_tid,
|
||||
bool has_key_idx) {
|
||||
if (*data_length == 0)
|
||||
return -1;
|
||||
|
||||
if (has_tid) {
|
||||
vp8->temporalIdx = ((**data >> 6) & 0x03);
|
||||
vp8->layerSync = (**data & 0x20) ? true : false; // Y bit
|
||||
}
|
||||
if (has_key_idx) {
|
||||
vp8->keyIdx = (**data & 0x1F);
|
||||
}
|
||||
(*data)++;
|
||||
(*parsed_bytes)++;
|
||||
(*data_length)--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ParseVP8Extension(RTPVideoHeaderVP8* vp8,
|
||||
const uint8_t* data,
|
||||
size_t data_length) {
|
||||
RTC_DCHECK_GT(data_length, 0);
|
||||
size_t parsed_bytes = 0;
|
||||
// Optional X field is present.
|
||||
bool has_picture_id = (*data & 0x80) ? true : false; // I bit
|
||||
bool has_tl0_pic_idx = (*data & 0x40) ? true : false; // L bit
|
||||
bool has_tid = (*data & 0x20) ? true : false; // T bit
|
||||
bool has_key_idx = (*data & 0x10) ? true : false; // K bit
|
||||
|
||||
// Advance data and decrease remaining payload size.
|
||||
data++;
|
||||
parsed_bytes++;
|
||||
data_length--;
|
||||
|
||||
if (has_picture_id) {
|
||||
if (ParseVP8PictureID(vp8, &data, &data_length, &parsed_bytes) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_tl0_pic_idx) {
|
||||
if (ParseVP8Tl0PicIdx(vp8, &data, &data_length, &parsed_bytes) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_tid || has_key_idx) {
|
||||
if (ParseVP8TIDAndKeyIdx(vp8, &data, &data_length, &parsed_bytes, has_tid,
|
||||
has_key_idx) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return static_cast<int>(parsed_bytes);
|
||||
}
|
||||
|
||||
int ParseVP8FrameSize(RtpDepacketizer::ParsedPayload* parsed_payload,
|
||||
const uint8_t* data,
|
||||
size_t data_length) {
|
||||
if (parsed_payload->video_header().frame_type !=
|
||||
VideoFrameType::kVideoFrameKey) {
|
||||
// Included in payload header for I-frames.
|
||||
return 0;
|
||||
}
|
||||
if (data_length < 10) {
|
||||
// For an I-frame we should always have the uncompressed VP8 header
|
||||
// in the beginning of the partition.
|
||||
return -1;
|
||||
}
|
||||
parsed_payload->video_header().width = ((data[7] << 8) + data[6]) & 0x3FFF;
|
||||
parsed_payload->video_header().height = ((data[9] << 8) + data[8]) & 0x3FFF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ValidateHeader(const RTPVideoHeaderVP8& hdr_info) {
|
||||
if (hdr_info.pictureId != kNoPictureId) {
|
||||
RTC_DCHECK_GE(hdr_info.pictureId, 0);
|
||||
@ -275,104 +163,19 @@ RtpPacketizerVp8::RawHeader RtpPacketizerVp8::BuildHeader(
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// VP8 format:
|
||||
//
|
||||
// Payload descriptor
|
||||
// 0 1 2 3 4 5 6 7
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// |X|R|N|S|PartID | (REQUIRED)
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// X: |I|L|T|K| RSV | (OPTIONAL)
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// I: | PictureID | (OPTIONAL)
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// L: | TL0PICIDX | (OPTIONAL)
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// T/K: |TID:Y| KEYIDX | (OPTIONAL)
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// Payload header (considered part of the actual payload, sent to decoder)
|
||||
// 0 1 2 3 4 5 6 7
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// |Size0|H| VER |P|
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// | ... |
|
||||
// + +
|
||||
bool RtpDepacketizerVp8::Parse(ParsedPayload* parsed_payload,
|
||||
const uint8_t* payload_data,
|
||||
size_t payload_data_length) {
|
||||
RTC_DCHECK(parsed_payload);
|
||||
if (payload_data_length == 0) {
|
||||
RTC_LOG(LS_ERROR) << "Empty payload.";
|
||||
int offset = VideoRtpDepacketizerVp8::ParseRtpPayload(
|
||||
rtc::MakeArrayView(payload_data, payload_data_length),
|
||||
&parsed_payload->video);
|
||||
if (offset == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse mandatory first byte of payload descriptor.
|
||||
bool extension = (*payload_data & 0x80) ? true : false; // X bit
|
||||
bool beginning_of_partition = (*payload_data & 0x10) ? true : false; // S bit
|
||||
int partition_id = (*payload_data & 0x0F); // PartID field
|
||||
|
||||
parsed_payload->video_header().width = 0;
|
||||
parsed_payload->video_header().height = 0;
|
||||
parsed_payload->video_header().is_first_packet_in_frame =
|
||||
beginning_of_partition && (partition_id == 0);
|
||||
parsed_payload->video_header().simulcastIdx = 0;
|
||||
parsed_payload->video_header().codec = kVideoCodecVP8;
|
||||
auto& vp8_header = parsed_payload->video_header()
|
||||
.video_type_header.emplace<RTPVideoHeaderVP8>();
|
||||
vp8_header.nonReference = (*payload_data & 0x20) ? true : false; // N bit
|
||||
vp8_header.partitionId = partition_id;
|
||||
vp8_header.beginningOfPartition = beginning_of_partition;
|
||||
vp8_header.pictureId = kNoPictureId;
|
||||
vp8_header.tl0PicIdx = kNoTl0PicIdx;
|
||||
vp8_header.temporalIdx = kNoTemporalIdx;
|
||||
vp8_header.layerSync = false;
|
||||
vp8_header.keyIdx = kNoKeyIdx;
|
||||
|
||||
if (partition_id > 8) {
|
||||
// Weak check for corrupt payload_data: PartID MUST NOT be larger than 8.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Advance payload_data and decrease remaining payload size.
|
||||
payload_data++;
|
||||
if (payload_data_length <= 1) {
|
||||
RTC_LOG(LS_ERROR) << "Error parsing VP8 payload descriptor!";
|
||||
return false;
|
||||
}
|
||||
payload_data_length--;
|
||||
|
||||
if (extension) {
|
||||
const int parsed_bytes =
|
||||
ParseVP8Extension(&vp8_header, payload_data, payload_data_length);
|
||||
if (parsed_bytes < 0)
|
||||
return false;
|
||||
payload_data += parsed_bytes;
|
||||
payload_data_length -= parsed_bytes;
|
||||
if (payload_data_length == 0) {
|
||||
RTC_LOG(LS_ERROR) << "Error parsing VP8 payload descriptor!";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Read P bit from payload header (only at beginning of first partition).
|
||||
if (beginning_of_partition && partition_id == 0) {
|
||||
parsed_payload->video_header().frame_type =
|
||||
(*payload_data & 0x01) ? VideoFrameType::kVideoFrameDelta
|
||||
: VideoFrameType::kVideoFrameKey;
|
||||
} else {
|
||||
parsed_payload->video_header().frame_type =
|
||||
VideoFrameType::kVideoFrameDelta;
|
||||
}
|
||||
|
||||
if (ParseVP8FrameSize(parsed_payload, payload_data, payload_data_length) !=
|
||||
0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
parsed_payload->payload = payload_data;
|
||||
parsed_payload->payload_length = payload_data_length;
|
||||
parsed_payload->payload = payload_data + offset;
|
||||
parsed_payload->payload_length = payload_data_length - offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
198
modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.cc
Normal file
198
modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.cc
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (c) 2019 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/rtp_rtcp/source/video_rtp_depacketizer_vp8.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/array_view.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_video_header.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
// VP8 format:
|
||||
//
|
||||
// Payload descriptor
|
||||
// 0 1 2 3 4 5 6 7
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// |X|R|N|S|PartID | (REQUIRED)
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// X: |I|L|T|K| RSV | (OPTIONAL)
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// I: | PictureID | (OPTIONAL)
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// L: | TL0PICIDX | (OPTIONAL)
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// T/K: |TID:Y| KEYIDX | (OPTIONAL)
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// Payload header (considered part of the actual payload, sent to decoder)
|
||||
// 0 1 2 3 4 5 6 7
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// |Size0|H| VER |P|
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// | ... |
|
||||
// + +
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
constexpr int kFailedToParse = 0;
|
||||
|
||||
int ParseVP8Descriptor(RTPVideoHeaderVP8* vp8,
|
||||
const uint8_t* data,
|
||||
size_t data_length) {
|
||||
RTC_DCHECK_GT(data_length, 0);
|
||||
int parsed_bytes = 0;
|
||||
// Parse mandatory first byte of payload descriptor.
|
||||
bool extension = (*data & 0x80) ? true : false; // X bit
|
||||
vp8->nonReference = (*data & 0x20) ? true : false; // N bit
|
||||
vp8->beginningOfPartition = (*data & 0x10) ? true : false; // S bit
|
||||
vp8->partitionId = (*data & 0x0F); // PartID field
|
||||
|
||||
data++;
|
||||
parsed_bytes++;
|
||||
data_length--;
|
||||
|
||||
if (!extension)
|
||||
return parsed_bytes;
|
||||
|
||||
if (data_length == 0)
|
||||
return kFailedToParse;
|
||||
// Optional X field is present.
|
||||
bool has_picture_id = (*data & 0x80) ? true : false; // I bit
|
||||
bool has_tl0_pic_idx = (*data & 0x40) ? true : false; // L bit
|
||||
bool has_tid = (*data & 0x20) ? true : false; // T bit
|
||||
bool has_key_idx = (*data & 0x10) ? true : false; // K bit
|
||||
|
||||
// Advance data and decrease remaining payload size.
|
||||
data++;
|
||||
parsed_bytes++;
|
||||
data_length--;
|
||||
|
||||
if (has_picture_id) {
|
||||
if (data_length == 0)
|
||||
return kFailedToParse;
|
||||
|
||||
vp8->pictureId = (*data & 0x7F);
|
||||
if (*data & 0x80) {
|
||||
data++;
|
||||
parsed_bytes++;
|
||||
if (--data_length == 0)
|
||||
return kFailedToParse;
|
||||
// PictureId is 15 bits
|
||||
vp8->pictureId = (vp8->pictureId << 8) + *data;
|
||||
}
|
||||
data++;
|
||||
parsed_bytes++;
|
||||
data_length--;
|
||||
}
|
||||
|
||||
if (has_tl0_pic_idx) {
|
||||
if (data_length == 0)
|
||||
return kFailedToParse;
|
||||
|
||||
vp8->tl0PicIdx = *data;
|
||||
data++;
|
||||
parsed_bytes++;
|
||||
data_length--;
|
||||
}
|
||||
|
||||
if (has_tid || has_key_idx) {
|
||||
if (data_length == 0)
|
||||
return kFailedToParse;
|
||||
|
||||
if (has_tid) {
|
||||
vp8->temporalIdx = ((*data >> 6) & 0x03);
|
||||
vp8->layerSync = (*data & 0x20) ? true : false; // Y bit
|
||||
}
|
||||
if (has_key_idx) {
|
||||
vp8->keyIdx = *data & 0x1F;
|
||||
}
|
||||
data++;
|
||||
parsed_bytes++;
|
||||
data_length--;
|
||||
}
|
||||
return parsed_bytes;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
absl::optional<VideoRtpDepacketizer::ParsedRtpPayload>
|
||||
VideoRtpDepacketizerVp8::Parse(rtc::CopyOnWriteBuffer rtp_payload) {
|
||||
rtc::ArrayView<const uint8_t> payload(rtp_payload.cdata(),
|
||||
rtp_payload.size());
|
||||
absl::optional<ParsedRtpPayload> result(absl::in_place);
|
||||
int offset = ParseRtpPayload(payload, &result->video_header);
|
||||
if (offset == kFailedToParse)
|
||||
return absl::nullopt;
|
||||
RTC_DCHECK_LT(offset, rtp_payload.size());
|
||||
result->video_payload =
|
||||
rtp_payload.Slice(offset, rtp_payload.size() - offset);
|
||||
return result;
|
||||
}
|
||||
|
||||
int VideoRtpDepacketizerVp8::ParseRtpPayload(
|
||||
rtc::ArrayView<const uint8_t> rtp_payload,
|
||||
RTPVideoHeader* video_header) {
|
||||
RTC_DCHECK(video_header);
|
||||
if (rtp_payload.empty()) {
|
||||
RTC_LOG(LS_ERROR) << "Empty rtp payload.";
|
||||
return kFailedToParse;
|
||||
}
|
||||
|
||||
video_header->simulcastIdx = 0;
|
||||
video_header->codec = kVideoCodecVP8;
|
||||
auto& vp8_header =
|
||||
video_header->video_type_header.emplace<RTPVideoHeaderVP8>();
|
||||
vp8_header.InitRTPVideoHeaderVP8();
|
||||
|
||||
const int descriptor_size =
|
||||
ParseVP8Descriptor(&vp8_header, rtp_payload.data(), rtp_payload.size());
|
||||
if (descriptor_size == kFailedToParse)
|
||||
return kFailedToParse;
|
||||
|
||||
if (vp8_header.partitionId > 8) {
|
||||
// Weak check for corrupt payload_data: PartID MUST NOT be larger than 8.
|
||||
return kFailedToParse;
|
||||
}
|
||||
video_header->is_first_packet_in_frame =
|
||||
vp8_header.beginningOfPartition && vp8_header.partitionId == 0;
|
||||
|
||||
int vp8_payload_size = rtp_payload.size() - descriptor_size;
|
||||
if (vp8_payload_size == 0) {
|
||||
RTC_LOG(LS_WARNING) << "Empty vp8 payload.";
|
||||
return kFailedToParse;
|
||||
}
|
||||
const uint8_t* vp8_payload = rtp_payload.data() + descriptor_size;
|
||||
|
||||
// Read P bit from payload header (only at beginning of first partition).
|
||||
if (video_header->is_first_packet_in_frame && (*vp8_payload & 0x01) == 0) {
|
||||
video_header->frame_type = VideoFrameType::kVideoFrameKey;
|
||||
|
||||
if (vp8_payload_size < 10) {
|
||||
// For an I-frame we should always have the uncompressed VP8 header
|
||||
// in the beginning of the partition.
|
||||
return kFailedToParse;
|
||||
}
|
||||
video_header->width = ((vp8_payload[7] << 8) + vp8_payload[6]) & 0x3FFF;
|
||||
video_header->height = ((vp8_payload[9] << 8) + vp8_payload[8]) & 0x3FFF;
|
||||
} else {
|
||||
video_header->frame_type = VideoFrameType::kVideoFrameDelta;
|
||||
|
||||
video_header->width = 0;
|
||||
video_header->height = 0;
|
||||
}
|
||||
|
||||
return descriptor_size;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
42
modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.h
Normal file
42
modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2019 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_RTP_RTCP_SOURCE_VIDEO_RTP_DEPACKETIZER_VP8_H_
|
||||
#define MODULES_RTP_RTCP_SOURCE_VIDEO_RTP_DEPACKETIZER_VP8_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/array_view.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_video_header.h"
|
||||
#include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class VideoRtpDepacketizerVp8 : public VideoRtpDepacketizer {
|
||||
public:
|
||||
VideoRtpDepacketizerVp8() = default;
|
||||
VideoRtpDepacketizerVp8(const VideoRtpDepacketizerVp8&) = delete;
|
||||
VideoRtpDepacketizerVp8& operator=(VideoRtpDepacketizerVp8&) = delete;
|
||||
~VideoRtpDepacketizerVp8() override = default;
|
||||
|
||||
// Parses vp8 rtp payload descriptor.
|
||||
// Returns zero on error or vp8 payload header offset on success.
|
||||
static int ParseRtpPayload(rtc::ArrayView<const uint8_t> rtp_payload,
|
||||
RTPVideoHeader* video_header);
|
||||
|
||||
absl::optional<ParsedRtpPayload> Parse(
|
||||
rtc::CopyOnWriteBuffer rtp_payload) override;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_SOURCE_VIDEO_RTP_DEPACKETIZER_VP8_H_
|
||||
236
modules/rtp_rtcp/source/video_rtp_depacketizer_vp8_unittest.cc
Normal file
236
modules/rtp_rtcp/source/video_rtp_depacketizer_vp8_unittest.cc
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (c) 2019 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/rtp_rtcp/source/video_rtp_depacketizer_vp8.h"
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_format_vp8.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
// Payload descriptor
|
||||
// 0 1 2 3 4 5 6 7
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// |X|R|N|S|PartID | (REQUIRED)
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// X: |I|L|T|K| RSV | (OPTIONAL)
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// I: | PictureID | (OPTIONAL)
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// L: | TL0PICIDX | (OPTIONAL)
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// T/K: |TID:Y| KEYIDX | (OPTIONAL)
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// Payload header
|
||||
// 0 1 2 3 4 5 6 7
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// |Size0|H| VER |P|
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// : :
|
||||
TEST(VideoRtpDepacketizerVp8Test, BasicHeader) {
|
||||
uint8_t packet[4] = {0};
|
||||
packet[0] = 0b0001'0100; // S = 1, PartID = 4.
|
||||
packet[1] = 0x01; // P frame.
|
||||
|
||||
RTPVideoHeader video_header;
|
||||
int offset = VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &video_header);
|
||||
|
||||
EXPECT_EQ(offset, 1);
|
||||
EXPECT_EQ(video_header.frame_type, VideoFrameType::kVideoFrameDelta);
|
||||
EXPECT_EQ(video_header.codec, kVideoCodecVP8);
|
||||
const auto& vp8_header =
|
||||
absl::get<RTPVideoHeaderVP8>(video_header.video_type_header);
|
||||
EXPECT_FALSE(vp8_header.nonReference);
|
||||
EXPECT_TRUE(vp8_header.beginningOfPartition);
|
||||
EXPECT_EQ(vp8_header.partitionId, 4);
|
||||
EXPECT_EQ(vp8_header.pictureId, kNoPictureId);
|
||||
EXPECT_EQ(vp8_header.tl0PicIdx, kNoTl0PicIdx);
|
||||
EXPECT_EQ(vp8_header.temporalIdx, kNoTemporalIdx);
|
||||
EXPECT_EQ(vp8_header.keyIdx, kNoKeyIdx);
|
||||
}
|
||||
|
||||
TEST(VideoRtpDepacketizerVp8Test, OneBytePictureID) {
|
||||
const uint8_t kPictureId = 17;
|
||||
uint8_t packet[10] = {0};
|
||||
packet[0] = 0b1010'0000;
|
||||
packet[1] = 0b1000'0000;
|
||||
packet[2] = kPictureId;
|
||||
|
||||
RTPVideoHeader video_header;
|
||||
int offset = VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &video_header);
|
||||
|
||||
EXPECT_EQ(offset, 3);
|
||||
const auto& vp8_header =
|
||||
absl::get<RTPVideoHeaderVP8>(video_header.video_type_header);
|
||||
EXPECT_EQ(vp8_header.pictureId, kPictureId);
|
||||
}
|
||||
|
||||
TEST(VideoRtpDepacketizerVp8Test, TwoBytePictureID) {
|
||||
const uint16_t kPictureId = 0x1234;
|
||||
uint8_t packet[10] = {0};
|
||||
packet[0] = 0b1010'0000;
|
||||
packet[1] = 0b1000'0000;
|
||||
packet[2] = 0x80 | 0x12;
|
||||
packet[3] = 0x34;
|
||||
|
||||
RTPVideoHeader video_header;
|
||||
int offset = VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &video_header);
|
||||
|
||||
EXPECT_EQ(offset, 4);
|
||||
const auto& vp8_header =
|
||||
absl::get<RTPVideoHeaderVP8>(video_header.video_type_header);
|
||||
EXPECT_EQ(vp8_header.pictureId, kPictureId);
|
||||
}
|
||||
|
||||
TEST(VideoRtpDepacketizerVp8Test, Tl0PicIdx) {
|
||||
const uint8_t kTl0PicIdx = 17;
|
||||
uint8_t packet[13] = {0};
|
||||
packet[0] = 0b1000'0000;
|
||||
packet[1] = 0b0100'0000;
|
||||
packet[2] = kTl0PicIdx;
|
||||
|
||||
RTPVideoHeader video_header;
|
||||
int offset = VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &video_header);
|
||||
|
||||
EXPECT_EQ(offset, 3);
|
||||
const auto& vp8_header =
|
||||
absl::get<RTPVideoHeaderVP8>(video_header.video_type_header);
|
||||
EXPECT_EQ(vp8_header.tl0PicIdx, kTl0PicIdx);
|
||||
}
|
||||
|
||||
TEST(VideoRtpDepacketizerVp8Test, TIDAndLayerSync) {
|
||||
uint8_t packet[10] = {0};
|
||||
packet[0] = 0b1000'0000;
|
||||
packet[1] = 0b0010'0000;
|
||||
packet[2] = 0b10'0'00000; // TID(2) + LayerSync(false)
|
||||
|
||||
RTPVideoHeader video_header;
|
||||
int offset = VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &video_header);
|
||||
|
||||
EXPECT_EQ(offset, 3);
|
||||
const auto& vp8_header =
|
||||
absl::get<RTPVideoHeaderVP8>(video_header.video_type_header);
|
||||
EXPECT_EQ(vp8_header.temporalIdx, 2);
|
||||
EXPECT_FALSE(vp8_header.layerSync);
|
||||
}
|
||||
|
||||
TEST(VideoRtpDepacketizerVp8Test, KeyIdx) {
|
||||
const uint8_t kKeyIdx = 17;
|
||||
uint8_t packet[10] = {0};
|
||||
packet[0] = 0b1000'0000;
|
||||
packet[1] = 0b0001'0000;
|
||||
packet[2] = kKeyIdx;
|
||||
|
||||
RTPVideoHeader video_header;
|
||||
int offset = VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &video_header);
|
||||
|
||||
EXPECT_EQ(offset, 3);
|
||||
const auto& vp8_header =
|
||||
absl::get<RTPVideoHeaderVP8>(video_header.video_type_header);
|
||||
EXPECT_EQ(vp8_header.keyIdx, kKeyIdx);
|
||||
}
|
||||
|
||||
TEST(VideoRtpDepacketizerVp8Test, MultipleExtensions) {
|
||||
uint8_t packet[10] = {0};
|
||||
packet[0] = 0b1010'0110; // X and N bit set, partID = 6
|
||||
packet[1] = 0b1111'0000;
|
||||
packet[2] = 0x80 | 0x12; // PictureID, high 7 bits.
|
||||
packet[3] = 0x34; // PictureID, low 8 bits.
|
||||
packet[4] = 42; // Tl0PicIdx.
|
||||
packet[5] = 0b01'1'10001;
|
||||
|
||||
RTPVideoHeader video_header;
|
||||
int offset = VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &video_header);
|
||||
|
||||
EXPECT_EQ(offset, 6);
|
||||
const auto& vp8_header =
|
||||
absl::get<RTPVideoHeaderVP8>(video_header.video_type_header);
|
||||
EXPECT_TRUE(vp8_header.nonReference);
|
||||
EXPECT_EQ(vp8_header.partitionId, 0b0110);
|
||||
EXPECT_EQ(vp8_header.pictureId, 0x1234);
|
||||
EXPECT_EQ(vp8_header.tl0PicIdx, 42);
|
||||
EXPECT_EQ(vp8_header.temporalIdx, 1);
|
||||
EXPECT_TRUE(vp8_header.layerSync);
|
||||
EXPECT_EQ(vp8_header.keyIdx, 0b10001);
|
||||
}
|
||||
|
||||
TEST(VideoRtpDepacketizerVp8Test, TooShortHeader) {
|
||||
uint8_t packet[4] = {0};
|
||||
packet[0] = 0b1000'0000;
|
||||
packet[1] = 0b1111'0000; // All extensions are enabled...
|
||||
packet[2] = 0x80 | 17; // ... but only 2 bytes PictureID is provided.
|
||||
packet[3] = 17; // PictureID, low 8 bits.
|
||||
|
||||
RTPVideoHeader unused;
|
||||
EXPECT_EQ(VideoRtpDepacketizerVp8::ParseRtpPayload(packet, &unused), 0);
|
||||
}
|
||||
|
||||
TEST(VideoRtpDepacketizerVp8Test, WithPacketizer) {
|
||||
uint8_t data[10] = {0};
|
||||
RtpPacketToSend packet(/*extenions=*/nullptr);
|
||||
RTPVideoHeaderVP8 input_header;
|
||||
input_header.nonReference = true;
|
||||
input_header.pictureId = 300;
|
||||
input_header.temporalIdx = 1;
|
||||
input_header.layerSync = false;
|
||||
input_header.tl0PicIdx = kNoTl0PicIdx; // Disable.
|
||||
input_header.keyIdx = 31;
|
||||
RtpPacketizerVp8 packetizer(data, /*limits=*/{}, input_header);
|
||||
EXPECT_EQ(packetizer.NumPackets(), 1u);
|
||||
ASSERT_TRUE(packetizer.NextPacket(&packet));
|
||||
|
||||
VideoRtpDepacketizerVp8 depacketizer;
|
||||
absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
|
||||
depacketizer.Parse(packet.PayloadBuffer());
|
||||
ASSERT_TRUE(parsed);
|
||||
|
||||
EXPECT_EQ(parsed->video_header.codec, kVideoCodecVP8);
|
||||
const auto& vp8_header =
|
||||
absl::get<RTPVideoHeaderVP8>(parsed->video_header.video_type_header);
|
||||
EXPECT_EQ(vp8_header.nonReference, input_header.nonReference);
|
||||
EXPECT_EQ(vp8_header.pictureId, input_header.pictureId);
|
||||
EXPECT_EQ(vp8_header.tl0PicIdx, input_header.tl0PicIdx);
|
||||
EXPECT_EQ(vp8_header.temporalIdx, input_header.temporalIdx);
|
||||
EXPECT_EQ(vp8_header.layerSync, input_header.layerSync);
|
||||
EXPECT_EQ(vp8_header.keyIdx, input_header.keyIdx);
|
||||
}
|
||||
|
||||
TEST(VideoRtpDepacketizerVp8Test, ReferencesInputCopyOnWriteBuffer) {
|
||||
constexpr size_t kHeaderSize = 5;
|
||||
uint8_t packet[16] = {0};
|
||||
packet[0] = 0b1000'0000;
|
||||
packet[1] = 0b1111'0000; // with all extensions,
|
||||
packet[2] = 15; // and one-byte picture id.
|
||||
|
||||
rtc::CopyOnWriteBuffer rtp_payload(packet);
|
||||
VideoRtpDepacketizerVp8 depacketizer;
|
||||
absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed =
|
||||
depacketizer.Parse(rtp_payload);
|
||||
ASSERT_TRUE(parsed);
|
||||
|
||||
EXPECT_EQ(parsed->video_payload.size(), rtp_payload.size() - kHeaderSize);
|
||||
// Compare pointers to check there was no copy on write buffer unsharing.
|
||||
EXPECT_EQ(parsed->video_payload.cdata(), rtp_payload.cdata() + kHeaderSize);
|
||||
}
|
||||
|
||||
TEST(VideoRtpDepacketizerVp8Test, FailsOnEmptyPayload) {
|
||||
rtc::ArrayView<const uint8_t> empty;
|
||||
RTPVideoHeader video_header;
|
||||
EXPECT_EQ(VideoRtpDepacketizerVp8::ParseRtpPayload(empty, &video_header), 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
Reference in New Issue
Block a user