/* * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "webrtc/modules/rtp_rtcp/source/rtp_sender_video.h" #include #include #include #include "webrtc/modules/rtp_rtcp/source/producer_fec.h" #include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h" #include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h" #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/system_wrappers/interface/trace_event.h" namespace webrtc { enum { REDForFECHeaderLength = 1 }; struct RtpPacket { uint16_t rtpHeaderLength; ForwardErrorCorrection::Packet* pkt; }; RTPSenderVideo::RTPSenderVideo(const int32_t id, Clock* clock, RTPSenderInterface* rtpSender) : _id(id), _rtpSender(*rtpSender), _sendVideoCritsect(CriticalSectionWrapper::CreateCriticalSection()), _videoType(kRtpVideoGeneric), _videoCodecInformation(NULL), _maxBitrate(0), _retransmissionSettings(kRetransmitBaseLayer), // Generic FEC _fec(id), _fecEnabled(false), _payloadTypeRED(-1), _payloadTypeFEC(-1), _numberFirstPartition(0), delta_fec_params_(), key_fec_params_(), producer_fec_(&_fec), _fecOverheadRate(clock), _videoBitrate(clock) { memset(&delta_fec_params_, 0, sizeof(delta_fec_params_)); memset(&key_fec_params_, 0, sizeof(key_fec_params_)); delta_fec_params_.max_fec_frames = key_fec_params_.max_fec_frames = 1; delta_fec_params_.fec_mask_type = key_fec_params_.fec_mask_type = kFecMaskRandom; } RTPSenderVideo::~RTPSenderVideo() { if(_videoCodecInformation) { delete _videoCodecInformation; } delete _sendVideoCritsect; } void RTPSenderVideo::SetVideoCodecType(RtpVideoCodecTypes videoType) { CriticalSectionScoped cs(_sendVideoCritsect); _videoType = videoType; } RtpVideoCodecTypes RTPSenderVideo::VideoCodecType() const { return _videoType; } int32_t RTPSenderVideo::RegisterVideoPayload( const char payloadName[RTP_PAYLOAD_NAME_SIZE], const int8_t payloadType, const uint32_t maxBitRate, ModuleRTPUtility::Payload*& payload) { CriticalSectionScoped cs(_sendVideoCritsect); RtpVideoCodecTypes videoType = kRtpVideoGeneric; if (ModuleRTPUtility::StringCompare(payloadName, "VP8",3)) { videoType = kRtpVideoVp8; } else if (ModuleRTPUtility::StringCompare(payloadName, "I420", 4)) { videoType = kRtpVideoGeneric; } else { videoType = kRtpVideoGeneric; } payload = new ModuleRTPUtility::Payload; payload->name[RTP_PAYLOAD_NAME_SIZE - 1] = 0; strncpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE - 1); payload->typeSpecific.Video.videoCodecType = videoType; payload->typeSpecific.Video.maxRate = maxBitRate; payload->audio = false; return 0; } int32_t RTPSenderVideo::SendVideoPacket(uint8_t* data_buffer, const uint16_t payload_length, const uint16_t rtp_header_length, const uint32_t capture_timestamp, int64_t capture_time_ms, StorageType storage, bool protect) { if(_fecEnabled) { int ret = 0; int fec_overhead_sent = 0; int video_sent = 0; RedPacket* red_packet = producer_fec_.BuildRedPacket(data_buffer, payload_length, rtp_header_length, _payloadTypeRED); TRACE_EVENT_INSTANT2("webrtc_rtp", "Video::PacketRed", "timestamp", capture_timestamp, "seqnum", _rtpSender.SequenceNumber()); // Sending the media packet with RED header. int packet_success = _rtpSender.SendToNetwork( red_packet->data(), red_packet->length() - rtp_header_length, rtp_header_length, capture_time_ms, storage, PacedSender::kNormalPriority); ret |= packet_success; if (packet_success == 0) { video_sent += red_packet->length(); } delete red_packet; red_packet = NULL; if (protect) { ret = producer_fec_.AddRtpPacketAndGenerateFec(data_buffer, payload_length, rtp_header_length); if (ret != 0) return ret; } while (producer_fec_.FecAvailable()) { red_packet = producer_fec_.GetFecPacket( _payloadTypeRED, _payloadTypeFEC, _rtpSender.IncrementSequenceNumber(), rtp_header_length); StorageType storage = kDontRetransmit; if (_retransmissionSettings & kRetransmitFECPackets) { storage = kAllowRetransmission; } TRACE_EVENT_INSTANT2("webrtc_rtp", "Video::PacketFec", "timestamp", capture_timestamp, "seqnum", _rtpSender.SequenceNumber()); // Sending FEC packet with RED header. int packet_success = _rtpSender.SendToNetwork( red_packet->data(), red_packet->length() - rtp_header_length, rtp_header_length, capture_time_ms, storage, PacedSender::kNormalPriority); ret |= packet_success; if (packet_success == 0) { fec_overhead_sent += red_packet->length(); } delete red_packet; red_packet = NULL; } _videoBitrate.Update(video_sent); _fecOverheadRate.Update(fec_overhead_sent); return ret; } TRACE_EVENT_INSTANT2("webrtc_rtp", "Video::PacketNormal", "timestamp", capture_timestamp, "seqnum", _rtpSender.SequenceNumber()); int ret = _rtpSender.SendToNetwork(data_buffer, payload_length, rtp_header_length, capture_time_ms, storage, PacedSender::kNormalPriority); if (ret == 0) { _videoBitrate.Update(payload_length + rtp_header_length); } return ret; } int32_t RTPSenderVideo::SendRTPIntraRequest() { // RFC 2032 // 5.2.1. Full intra-frame Request (FIR) packet uint16_t length = 8; uint8_t data[8]; data[0] = 0x80; data[1] = 192; data[2] = 0; data[3] = 1; // length ModuleRTPUtility::AssignUWord32ToBuffer(data+4, _rtpSender.SSRC()); TRACE_EVENT_INSTANT1("webrtc_rtp", "Video::IntraRequest", "seqnum", _rtpSender.SequenceNumber()); return _rtpSender.SendToNetwork(data, 0, length, -1, kDontStore, PacedSender::kNormalPriority); } int32_t RTPSenderVideo::SetGenericFECStatus(const bool enable, const uint8_t payloadTypeRED, const uint8_t payloadTypeFEC) { _fecEnabled = enable; _payloadTypeRED = payloadTypeRED; _payloadTypeFEC = payloadTypeFEC; memset(&delta_fec_params_, 0, sizeof(delta_fec_params_)); memset(&key_fec_params_, 0, sizeof(key_fec_params_)); delta_fec_params_.max_fec_frames = key_fec_params_.max_fec_frames = 1; delta_fec_params_.fec_mask_type = key_fec_params_.fec_mask_type = kFecMaskRandom; return 0; } int32_t RTPSenderVideo::GenericFECStatus(bool& enable, uint8_t& payloadTypeRED, uint8_t& payloadTypeFEC) const { enable = _fecEnabled; payloadTypeRED = _payloadTypeRED; payloadTypeFEC = _payloadTypeFEC; return 0; } uint16_t RTPSenderVideo::FECPacketOverhead() const { if (_fecEnabled) { return ForwardErrorCorrection::PacketOverhead() + REDForFECHeaderLength; } return 0; } int32_t RTPSenderVideo::SetFecParameters( const FecProtectionParams* delta_params, const FecProtectionParams* key_params) { assert(delta_params); assert(key_params); delta_fec_params_ = *delta_params; key_fec_params_ = *key_params; return 0; } int32_t RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType, const FrameType frameType, const int8_t payloadType, const uint32_t captureTimeStamp, int64_t capture_time_ms, const uint8_t* payloadData, const uint32_t payloadSize, const RTPFragmentationHeader* fragmentation, VideoCodecInformation* codecInfo, const RTPVideoTypeHeader* rtpTypeHdr) { if( payloadSize == 0) { return -1; } if (frameType == kVideoFrameKey) { producer_fec_.SetFecParameters(&key_fec_params_, _numberFirstPartition); } else { producer_fec_.SetFecParameters(&delta_fec_params_, _numberFirstPartition); } // Default setting for number of first partition packets: // Will be extracted in SendVP8 for VP8 codec; other codecs use 0 _numberFirstPartition = 0; int32_t retVal = -1; switch(videoType) { case kRtpVideoGeneric: retVal = SendGeneric(frameType, payloadType, captureTimeStamp, capture_time_ms, payloadData, payloadSize); break; case kRtpVideoVp8: retVal = SendVP8(frameType, payloadType, captureTimeStamp, capture_time_ms, payloadData, payloadSize, fragmentation, rtpTypeHdr); break; default: assert(false); break; } if(retVal <= 0) { return retVal; } WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "%s(timestamp:%u)", __FUNCTION__, captureTimeStamp); return 0; } 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 */); // 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. uint8_t buffer[IP_PACKET_SIZE]; uint8_t generic_header = RtpFormatVideoGeneric::kFirstPacketBit; if (frame_type == kVideoFrameKey) { generic_header |= RtpFormatVideoGeneric::kKeyFrameBit; } while (size > 0) { if (size < payload_length) { payload_length = size; } size -= payload_length; // MarkerBit is 1 on final packet (bytes_to_send == 0) if (_rtpSender.BuildRTPheader(buffer, payload_type, size == 0, capture_timestamp, capture_time_ms) != 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_timestamp, capture_time_ms, kAllowRetransmission, true)) { return -1; } } return 0; } VideoCodecInformation* RTPSenderVideo::CodecInformationVideo() { return _videoCodecInformation; } void RTPSenderVideo::SetMaxConfiguredBitrateVideo(const uint32_t maxBitrate) { _maxBitrate = maxBitrate; } uint32_t RTPSenderVideo::MaxConfiguredBitrateVideo() const { return _maxBitrate; } int32_t RTPSenderVideo::SendVP8(const FrameType frameType, const int8_t payloadType, const uint32_t captureTimeStamp, int64_t capture_time_ms, const uint8_t* payloadData, const uint32_t payloadSize, const RTPFragmentationHeader* fragmentation, const RTPVideoTypeHeader* rtpTypeHdr) { const uint16_t rtpHeaderLength = _rtpSender.RTPHeaderLength(); int32_t payloadBytesToSend = payloadSize; const uint8_t* data = payloadData; uint16_t maxPayloadLengthVP8 = _rtpSender.MaxDataPayloadLength(); assert(rtpTypeHdr); // Initialize disregarding partition boundaries: this will use kEqualSize // packetization mode, which produces ~equal size packets for each frame. RtpFormatVp8 packetizer(data, payloadBytesToSend, rtpTypeHdr->VP8, maxPayloadLengthVP8); StorageType storage = kAllowRetransmission; if (rtpTypeHdr->VP8.temporalIdx == 0 && !(_retransmissionSettings & kRetransmitBaseLayer)) { storage = kDontRetransmit; } if (rtpTypeHdr->VP8.temporalIdx > 0 && !(_retransmissionSettings & kRetransmitHigherLayers)) { storage = kDontRetransmit; } bool last = false; _numberFirstPartition = 0; // |rtpTypeHdr->VP8.temporalIdx| is zero for base layers, or -1 if the field // isn't used. We currently only protect base layers. bool protect = (rtpTypeHdr->VP8.temporalIdx < 1); while (!last) { // Write VP8 Payload Descriptor and VP8 payload. uint8_t dataBuffer[IP_PACKET_SIZE] = {0}; int payloadBytesInPacket = 0; int packetStartPartition = packetizer.NextPacket(&dataBuffer[rtpHeaderLength], &payloadBytesInPacket, &last); // TODO(holmer): Temporarily disable first partition packet counting // to avoid a bug in ProducerFec which doesn't properly handle // important packets. // if (packetStartPartition == 0) // { // ++_numberFirstPartition; // } // else if (packetStartPartition < 0) { return -1; } // Write RTP header. // Set marker bit true if this is the last packet in frame. _rtpSender.BuildRTPheader(dataBuffer, payloadType, last, captureTimeStamp, capture_time_ms); if (-1 == SendVideoPacket(dataBuffer, payloadBytesInPacket, rtpHeaderLength, captureTimeStamp, capture_time_ms, storage, protect)) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "RTPSenderVideo::SendVP8 failed to send packet number" " %d", _rtpSender.SequenceNumber()); } } TRACE_EVENT_ASYNC_END1("webrtc", "Video", capture_time_ms, "timestamp", _rtpSender.Timestamp()); return 0; } void RTPSenderVideo::ProcessBitrate() { _videoBitrate.Process(); _fecOverheadRate.Process(); } uint32_t RTPSenderVideo::VideoBitrateSent() const { return _videoBitrate.BitrateLast(); } uint32_t RTPSenderVideo::FecOverheadRate() const { return _fecOverheadRate.BitrateLast(); } int RTPSenderVideo::SelectiveRetransmissions() const { return _retransmissionSettings; } int RTPSenderVideo::SetSelectiveRetransmissions(uint8_t settings) { _retransmissionSettings = settings; return 0; } } // namespace webrtc