Generic video-codec support.
Labels frames as key/delta, also marks the first RTP packet of a frame as such, to allow proper reconstruction even if packets are received out of order. BUG=1442 TBR=ajm@webrtc.org Review URL: https://webrtc-codereview.appspot.com/1207004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@3680 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
22
webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h
Normal file
22
webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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 WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VIDEO_GENERIC_H_
|
||||
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VIDEO_GENERIC_H_
|
||||
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace RtpFormatVideoGeneric {
|
||||
static const uint8_t kKeyFrameBit = 0x01;
|
||||
static const uint8_t kFirstPacketBit = 0x02;
|
||||
} // namespace RtpFormatVideoGeneric
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VIDEO_GENERIC_H_
|
||||
@ -313,11 +313,13 @@ class RTPPayloadVideoStrategy : public RTPPayloadStrategy {
|
||||
const WebRtc_UWord32 frequency,
|
||||
const WebRtc_UWord8 channels,
|
||||
const WebRtc_UWord32 rate) const {
|
||||
RtpVideoCodecTypes videoType = kRtpNoVideo;
|
||||
RtpVideoCodecTypes videoType = kRtpGenericVideo;
|
||||
if (ModuleRTPUtility::StringCompare(payloadName, "VP8", 3)) {
|
||||
videoType = kRtpVp8Video;
|
||||
} else if (ModuleRTPUtility::StringCompare(payloadName, "I420", 4)) {
|
||||
videoType = kRtpNoVideo;
|
||||
videoType = kRtpGenericVideo;
|
||||
} else if (ModuleRTPUtility::StringCompare(payloadName, "GENERIC", 7)) {
|
||||
videoType = kRtpGenericVideo;
|
||||
} else if (ModuleRTPUtility::StringCompare(payloadName, "ULPFEC", 6)) {
|
||||
videoType = kRtpFecVideo;
|
||||
} else {
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
#include <cstring> // memcpy()
|
||||
|
||||
#include "webrtc/modules/rtp_rtcp/source/receiver_fec.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_payload_registry.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
|
||||
@ -261,7 +262,7 @@ WebRtc_Word32 RTPReceiverVideo::SetCodecType(
|
||||
const RtpVideoCodecTypes video_type,
|
||||
WebRtcRTPHeader* rtp_header) const {
|
||||
switch (video_type) {
|
||||
case kRtpNoVideo:
|
||||
case kRtpGenericVideo:
|
||||
rtp_header->type.Video.codec = kRTPVideoGeneric;
|
||||
break;
|
||||
case kRtpVp8Video:
|
||||
@ -295,7 +296,7 @@ WebRtc_Word32 RTPReceiverVideo::ParseVideoCodecSpecificSwitch(
|
||||
// All receive functions release critical_section_receiver_video_ before
|
||||
// returning.
|
||||
switch (video_type) {
|
||||
case kRtpNoVideo:
|
||||
case kRtpGenericVideo:
|
||||
rtp_header->type.Video.isFirstPacket = is_first_packet;
|
||||
return ReceiveGenericCodec(rtp_header, payload_data, payload_data_length);
|
||||
case kRtpVp8Video:
|
||||
@ -376,8 +377,15 @@ WebRtc_Word32 RTPReceiverVideo::ReceiveVp8Codec(
|
||||
WebRtc_Word32 RTPReceiverVideo::ReceiveGenericCodec(
|
||||
WebRtcRTPHeader* rtp_header,
|
||||
const WebRtc_UWord8* payload_data,
|
||||
const WebRtc_UWord16 payload_data_length) {
|
||||
rtp_header->frameType = kVideoFrameKey;
|
||||
WebRtc_UWord16 payload_data_length) {
|
||||
uint8_t generic_header = *payload_data++;
|
||||
--payload_data_length;
|
||||
|
||||
rtp_header->frameType =
|
||||
((generic_header & RtpFormatVideoGeneric::kKeyFrameBit) != 0) ?
|
||||
kVideoFrameKey : kVideoFrameDelta;
|
||||
rtp_header->type.Video.isFirstPacket =
|
||||
(generic_header & RtpFormatVideoGeneric::kFirstPacketBit) != 0;
|
||||
|
||||
critical_section_receiver_video_->Leave();
|
||||
|
||||
|
||||
@ -86,6 +86,7 @@
|
||||
'video_codec_information.h',
|
||||
'rtp_format_vp8.cc',
|
||||
'rtp_format_vp8.h',
|
||||
'rtp_format_video_generic.h',
|
||||
'vp8_partition_aggregator.cc',
|
||||
'vp8_partition_aggregator.h',
|
||||
# Mocks
|
||||
|
||||
@ -312,7 +312,7 @@ WebRtc_Word32 RTPSender::SendOutgoingData(
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
RtpVideoCodecTypes video_type = kRtpNoVideo;
|
||||
RtpVideoCodecTypes video_type = kRtpGenericVideo;
|
||||
if (CheckPayloadType(payload_type, &video_type) != 0) {
|
||||
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_,
|
||||
"%s invalid argument failed to find payload_type:%d",
|
||||
@ -1133,9 +1133,7 @@ VideoCodecInformation *RTPSender::CodecInformationVideo() {
|
||||
}
|
||||
|
||||
RtpVideoCodecTypes RTPSender::VideoCodecType() const {
|
||||
if (audio_configured_) {
|
||||
return kRtpNoVideo;
|
||||
}
|
||||
assert(!audio_configured_ && "Sender is an audio stream!");
|
||||
return video_->VideoCodecType();
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_sender.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
|
||||
@ -220,4 +221,63 @@ TEST_F(RtpSenderTest, DISABLED_TrafficSmoothing) {
|
||||
// Verify transmission time offset.
|
||||
EXPECT_EQ(kStoredTimeInMs * 90, rtp_header.extension.transmissionTimeOffset);
|
||||
}
|
||||
|
||||
TEST_F(RtpSenderTest, SendGenericVideo) {
|
||||
char payload_name[RTP_PAYLOAD_NAME_SIZE] = "GENERIC";
|
||||
const uint8_t payload_type = 127;
|
||||
ASSERT_EQ(0, rtp_sender_->RegisterPayload(payload_name, payload_type, 90000,
|
||||
0, 1500));
|
||||
uint8_t payload[] = {47, 11, 32, 93, 89};
|
||||
|
||||
// Send keyframe
|
||||
ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kVideoFrameKey, payload_type, 1234,
|
||||
4321, payload, sizeof(payload),
|
||||
NULL));
|
||||
|
||||
ModuleRTPUtility::RTPHeaderParser rtp_parser(transport_.last_sent_packet_,
|
||||
transport_.last_sent_packet_len_);
|
||||
webrtc::WebRtcRTPHeader rtp_header;
|
||||
ASSERT_TRUE(rtp_parser.Parse(rtp_header));
|
||||
|
||||
const uint8_t* payload_data = ModuleRTPUtility::GetPayloadData(&rtp_header,
|
||||
transport_.last_sent_packet_);
|
||||
uint8_t generic_header = *payload_data++;
|
||||
|
||||
ASSERT_EQ(sizeof(payload) + sizeof(generic_header),
|
||||
ModuleRTPUtility::GetPayloadDataLength(&rtp_header,
|
||||
transport_.last_sent_packet_len_));
|
||||
|
||||
EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kKeyFrameBit);
|
||||
EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kFirstPacketBit);
|
||||
|
||||
EXPECT_EQ(0, memcmp(payload, payload_data, sizeof(payload)));
|
||||
|
||||
// Send delta frame
|
||||
payload[0] = 13;
|
||||
payload[1] = 42;
|
||||
payload[4] = 13;
|
||||
|
||||
ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kVideoFrameDelta, payload_type,
|
||||
1234, 4321, payload,
|
||||
sizeof(payload), NULL));
|
||||
|
||||
ModuleRTPUtility::RTPHeaderParser rtp_parser2(transport_.last_sent_packet_,
|
||||
transport_.last_sent_packet_len_);
|
||||
ASSERT_TRUE(rtp_parser.Parse(rtp_header));
|
||||
|
||||
payload_data = ModuleRTPUtility::GetPayloadData(&rtp_header,
|
||||
transport_.last_sent_packet_);
|
||||
generic_header = *payload_data++;
|
||||
|
||||
EXPECT_FALSE(generic_header & RtpFormatVideoGeneric::kKeyFrameBit);
|
||||
EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kFirstPacketBit);
|
||||
|
||||
ASSERT_EQ(sizeof(payload) + sizeof(generic_header),
|
||||
ModuleRTPUtility::GetPayloadDataLength(&rtp_header,
|
||||
transport_.last_sent_packet_len_));
|
||||
|
||||
EXPECT_EQ(0, memcmp(payload, payload_data, sizeof(payload)));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
|
||||
#include "producer_fec.h"
|
||||
#include "rtp_format_vp8.h"
|
||||
#include "rtp_format_video_generic.h"
|
||||
|
||||
namespace webrtc {
|
||||
enum { REDForFECHeaderLength = 1 };
|
||||
@ -37,7 +38,7 @@ RTPSenderVideo::RTPSenderVideo(const WebRtc_Word32 id,
|
||||
_rtpSender(*rtpSender),
|
||||
_sendVideoCritsect(CriticalSectionWrapper::CreateCriticalSection()),
|
||||
|
||||
_videoType(kRtpNoVideo),
|
||||
_videoType(kRtpGenericVideo),
|
||||
_videoCodecInformation(NULL),
|
||||
_maxBitrate(0),
|
||||
_retransmissionSettings(kRetransmitBaseLayer),
|
||||
@ -89,11 +90,13 @@ WebRtc_Word32 RTPSenderVideo::RegisterVideoPayload(
|
||||
ModuleRTPUtility::Payload*& payload) {
|
||||
CriticalSectionScoped cs(_sendVideoCritsect);
|
||||
|
||||
RtpVideoCodecTypes videoType = kRtpNoVideo;
|
||||
RtpVideoCodecTypes videoType = kRtpGenericVideo;
|
||||
if (ModuleRTPUtility::StringCompare(payloadName, "VP8",3)) {
|
||||
videoType = kRtpVp8Video;
|
||||
} else if (ModuleRTPUtility::StringCompare(payloadName, "I420", 4)) {
|
||||
videoType = kRtpNoVideo;
|
||||
videoType = kRtpGenericVideo;
|
||||
} else if (ModuleRTPUtility::StringCompare(payloadName, "GENERIC", 7)) {
|
||||
videoType = kRtpGenericVideo;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
@ -285,9 +288,9 @@ RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType,
|
||||
WebRtc_Word32 retVal = -1;
|
||||
switch(videoType)
|
||||
{
|
||||
case kRtpNoVideo:
|
||||
retVal = SendGeneric(payloadType, captureTimeStamp, capture_time_ms,
|
||||
payloadData, payloadSize);
|
||||
case kRtpGenericVideo:
|
||||
retVal = SendGeneric(frameType, payloadType, captureTimeStamp,
|
||||
capture_time_ms, payloadData, payloadSize);
|
||||
break;
|
||||
case kRtpVp8Video:
|
||||
retVal = SendVP8(frameType,
|
||||
@ -312,67 +315,59 @@ RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType,
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
RTPSenderVideo::SendGeneric(const WebRtc_Word8 payloadType,
|
||||
const uint32_t captureTimeStamp,
|
||||
int64_t capture_time_ms,
|
||||
const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord32 payloadSize)
|
||||
{
|
||||
WebRtc_UWord16 payloadBytesInPacket = 0;
|
||||
WebRtc_UWord32 bytesSent = 0;
|
||||
WebRtc_Word32 payloadBytesToSend = payloadSize;
|
||||
int32_t RTPSenderVideo::SendGeneric(const FrameType frame_type,
|
||||
const int8_t payload_type,
|
||||
const uint32_t capture_timestamp,
|
||||
int64_t capture_time_ms,
|
||||
const uint8_t* payload,
|
||||
uint32_t size) {
|
||||
assert(frame_type == kVideoFrameKey || frame_type == kVideoFrameDelta);
|
||||
uint16_t rtp_header_length = _rtpSender.RTPHeaderLength();
|
||||
uint16_t max_length = _rtpSender.MaxPayloadLength() - FECPacketOverhead() -
|
||||
rtp_header_length - (1 /* generic header length */);
|
||||
|
||||
const WebRtc_UWord8* data = payloadData;
|
||||
WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength();
|
||||
WebRtc_UWord16 maxLength = _rtpSender.MaxPayloadLength() -
|
||||
FECPacketOverhead() - rtpHeaderLength;
|
||||
WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE];
|
||||
// Fragment packets more evenly by splitting the payload up evenly.
|
||||
uint32_t num_packets = (size + max_length - 1) / max_length;
|
||||
uint32_t payload_length = (size + num_packets - 1) / num_packets;
|
||||
assert(payload_length <= max_length);
|
||||
|
||||
// Fragment packet into packets of max MaxPayloadLength bytes payload.
|
||||
while (payloadBytesToSend > 0)
|
||||
{
|
||||
if (payloadBytesToSend > maxLength)
|
||||
{
|
||||
payloadBytesInPacket = maxLength;
|
||||
payloadBytesToSend -= payloadBytesInPacket;
|
||||
// MarkerBit is 0
|
||||
if(_rtpSender.BuildRTPheader(dataBuffer,
|
||||
payloadType,
|
||||
false,
|
||||
captureTimeStamp) != rtpHeaderLength)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
payloadBytesInPacket = (WebRtc_UWord16)payloadBytesToSend;
|
||||
payloadBytesToSend = 0;
|
||||
// MarkerBit is 1
|
||||
if(_rtpSender.BuildRTPheader(dataBuffer, payloadType, true,
|
||||
captureTimeStamp) != rtpHeaderLength)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// Fragment packet into packets of max MaxPayloadLength bytes payload.
|
||||
uint8_t buffer[IP_PACKET_SIZE];
|
||||
|
||||
// Put payload in packet
|
||||
memcpy(&dataBuffer[rtpHeaderLength], &data[bytesSent],
|
||||
payloadBytesInPacket);
|
||||
bytesSent += payloadBytesInPacket;
|
||||
uint8_t generic_header = RtpFormatVideoGeneric::kFirstPacketBit;
|
||||
if (frame_type == kVideoFrameKey) {
|
||||
generic_header |= RtpFormatVideoGeneric::kKeyFrameBit;
|
||||
}
|
||||
|
||||
if(-1 == SendVideoPacket(dataBuffer,
|
||||
payloadBytesInPacket,
|
||||
rtpHeaderLength,
|
||||
capture_time_ms,
|
||||
kAllowRetransmission,
|
||||
true))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
while (size > 0) {
|
||||
if (size < payload_length) {
|
||||
payload_length = size;
|
||||
}
|
||||
return 0;
|
||||
size -= payload_length;
|
||||
|
||||
// MarkerBit is 1 on final packet (bytes_to_send == 0)
|
||||
if (_rtpSender.BuildRTPheader(buffer, payload_type, size == 0,
|
||||
capture_timestamp) != rtp_header_length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t* out_ptr = &buffer[rtp_header_length];
|
||||
|
||||
// Put generic header in packet
|
||||
*out_ptr++ = generic_header;
|
||||
// Remove first-packet bit, following packets are intermediate
|
||||
generic_header &= ~RtpFormatVideoGeneric::kFirstPacketBit;
|
||||
|
||||
// Put payload in packet
|
||||
memcpy(out_ptr, payload, payload_length);
|
||||
payload += payload_length;
|
||||
|
||||
if (SendVideoPacket(buffer, payload_length + 1, rtp_header_length,
|
||||
capture_time_ms, kAllowRetransmission, true)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
VideoCodecInformation*
|
||||
|
||||
@ -97,11 +97,11 @@ protected:
|
||||
bool protect);
|
||||
|
||||
private:
|
||||
WebRtc_Word32 SendGeneric(const WebRtc_Word8 payloadType,
|
||||
const uint32_t captureTimeStamp,
|
||||
int64_t capture_time_ms,
|
||||
const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord32 payloadSize);
|
||||
WebRtc_Word32 SendGeneric(const FrameType frame_type,
|
||||
const int8_t payload_type,
|
||||
const uint32_t capture_timestamp,
|
||||
int64_t capture_time_ms,
|
||||
const uint8_t* payload, const uint32_t size);
|
||||
|
||||
WebRtc_Word32 SendVP8(const FrameType frameType,
|
||||
const WebRtc_Word8 payloadType,
|
||||
|
||||
@ -197,7 +197,7 @@ void RTPPayload::SetType(RtpVideoCodecTypes videoType) {
|
||||
type = videoType;
|
||||
|
||||
switch (type) {
|
||||
case kRtpNoVideo:
|
||||
case kRtpGenericVideo:
|
||||
break;
|
||||
case kRtpVp8Video: {
|
||||
info.VP8.nonReferenceFrame = false;
|
||||
@ -520,7 +520,7 @@ bool RTPPayloadParser::Parse(RTPPayload& parsedPacket) const {
|
||||
parsedPacket.SetType(_videoType);
|
||||
|
||||
switch (_videoType) {
|
||||
case kRtpNoVideo:
|
||||
case kRtpGenericVideo:
|
||||
return ParseGeneric(parsedPacket);
|
||||
case kRtpVp8Video:
|
||||
return ParseVP8(parsedPacket);
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
namespace webrtc {
|
||||
enum RtpVideoCodecTypes
|
||||
{
|
||||
kRtpNoVideo = 0,
|
||||
kRtpGenericVideo = 0,
|
||||
kRtpFecVideo = 10,
|
||||
kRtpVp8Video = 11
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user