/* * 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/base/checks.h" #include "webrtc/base/logging.h" #include "webrtc/base/trace_event.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" #include "webrtc/modules/rtp_rtcp/source/byte_io.h" #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_format_vp9.h" #include "webrtc/system_wrappers/include/critical_section_wrapper.h" namespace webrtc { enum { REDForFECHeaderLength = 1 }; struct RtpPacket { uint16_t rtpHeaderLength; ForwardErrorCorrection::Packet* pkt; }; RTPSenderVideo::RTPSenderVideo(Clock* clock, RTPSenderInterface* rtpSender) : _rtpSender(*rtpSender), crit_(CriticalSectionWrapper::CreateCriticalSection()), _videoType(kRtpVideoGeneric), _maxBitrate(0), _retransmissionSettings(kRetransmitBaseLayer), // Generic FEC _fec(), _fecEnabled(false), _payloadTypeRED(-1), _payloadTypeFEC(-1), delta_fec_params_(), key_fec_params_(), producer_fec_(&_fec), _fecOverheadRate(clock, NULL), _videoBitrate(clock, NULL) { 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() { } void RTPSenderVideo::SetVideoCodecType(RtpVideoCodecTypes videoType) { _videoType = videoType; } RtpVideoCodecTypes RTPSenderVideo::VideoCodecType() const { return _videoType; } // Static. RtpUtility::Payload* RTPSenderVideo::CreateVideoPayload( const char payloadName[RTP_PAYLOAD_NAME_SIZE], const int8_t payloadType, const uint32_t maxBitRate) { RtpVideoCodecTypes videoType = kRtpVideoGeneric; if (RtpUtility::StringCompare(payloadName, "VP8", 3)) { videoType = kRtpVideoVp8; } else if (RtpUtility::StringCompare(payloadName, "VP9", 3)) { videoType = kRtpVideoVp9; } else if (RtpUtility::StringCompare(payloadName, "H264", 4)) { videoType = kRtpVideoH264; } else if (RtpUtility::StringCompare(payloadName, "I420", 4)) { videoType = kRtpVideoGeneric; } else { videoType = kRtpVideoGeneric; } RtpUtility::Payload* payload = new RtpUtility::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 payload; } void RTPSenderVideo::SendVideoPacket(uint8_t* data_buffer, const size_t payload_length, const size_t rtp_header_length, uint16_t seq_num, const uint32_t capture_timestamp, int64_t capture_time_ms, StorageType storage) { if (_rtpSender.SendToNetwork(data_buffer, payload_length, rtp_header_length, capture_time_ms, storage, RtpPacketSender::kNormalPriority) == 0) { _videoBitrate.Update(payload_length + rtp_header_length); TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"), "Video::PacketNormal", "timestamp", capture_timestamp, "seqnum", seq_num); } else { LOG(LS_WARNING) << "Failed to send video packet " << seq_num; } } void RTPSenderVideo::SendVideoPacketAsRed(uint8_t* data_buffer, const size_t payload_length, const size_t rtp_header_length, uint16_t media_seq_num, const uint32_t capture_timestamp, int64_t capture_time_ms, StorageType media_packet_storage, bool protect) { rtc::scoped_ptr red_packet; std::vector fec_packets; StorageType fec_storage = kDontRetransmit; uint16_t next_fec_sequence_number = 0; { // Only protect while creating RED and FEC packets, not when sending. CriticalSectionScoped cs(crit_.get()); red_packet.reset(producer_fec_.BuildRedPacket( data_buffer, payload_length, rtp_header_length, _payloadTypeRED)); if (protect) { producer_fec_.AddRtpPacketAndGenerateFec(data_buffer, payload_length, rtp_header_length); } uint16_t num_fec_packets = producer_fec_.NumAvailableFecPackets(); if (num_fec_packets > 0) { next_fec_sequence_number = _rtpSender.AllocateSequenceNumber(num_fec_packets); fec_packets = producer_fec_.GetFecPackets( _payloadTypeRED, _payloadTypeFEC, next_fec_sequence_number, rtp_header_length); RTC_DCHECK_EQ(num_fec_packets, fec_packets.size()); if (_retransmissionSettings & kRetransmitFECPackets) fec_storage = kAllowRetransmission; } } if (_rtpSender.SendToNetwork( red_packet->data(), red_packet->length() - rtp_header_length, rtp_header_length, capture_time_ms, media_packet_storage, RtpPacketSender::kNormalPriority) == 0) { _videoBitrate.Update(red_packet->length()); TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"), "Video::PacketRed", "timestamp", capture_timestamp, "seqnum", media_seq_num); } else { LOG(LS_WARNING) << "Failed to send RED packet " << media_seq_num; } for (RedPacket* fec_packet : fec_packets) { if (_rtpSender.SendToNetwork( fec_packet->data(), fec_packet->length() - rtp_header_length, rtp_header_length, capture_time_ms, fec_storage, RtpPacketSender::kNormalPriority) == 0) { _fecOverheadRate.Update(fec_packet->length()); TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"), "Video::PacketFec", "timestamp", capture_timestamp, "seqnum", next_fec_sequence_number); } else { LOG(LS_WARNING) << "Failed to send FEC packet " << next_fec_sequence_number; } delete fec_packet; ++next_fec_sequence_number; } } void RTPSenderVideo::SetGenericFECStatus(const bool enable, const uint8_t payloadTypeRED, const uint8_t payloadTypeFEC) { CriticalSectionScoped cs(crit_.get()); _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; } void RTPSenderVideo::GenericFECStatus(bool& enable, uint8_t& payloadTypeRED, uint8_t& payloadTypeFEC) const { CriticalSectionScoped cs(crit_.get()); enable = _fecEnabled; payloadTypeRED = _payloadTypeRED; payloadTypeFEC = _payloadTypeFEC; } size_t RTPSenderVideo::FECPacketOverhead() const { CriticalSectionScoped cs(crit_.get()); if (_fecEnabled) { // Overhead is FEC headers plus RED for FEC header plus anything in RTP // header beyond the 12 bytes base header (CSRC list, extensions...) // This reason for the header extensions to be included here is that // from an FEC viewpoint, they are part of the payload to be protected. // (The base RTP header is already protected by the FEC header.) return ForwardErrorCorrection::PacketOverhead() + REDForFECHeaderLength + (_rtpSender.RTPHeaderLength() - kRtpHeaderSize); } return 0; } void RTPSenderVideo::SetFecParameters(const FecProtectionParams* delta_params, const FecProtectionParams* key_params) { CriticalSectionScoped cs(crit_.get()); RTC_DCHECK(delta_params); RTC_DCHECK(key_params); delta_fec_params_ = *delta_params; key_fec_params_ = *key_params; } 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 size_t payloadSize, const RTPFragmentationHeader* fragmentation, const RTPVideoHeader* rtpHdr) { if (payloadSize == 0) { return -1; } rtc::scoped_ptr packetizer( RtpPacketizer::Create(videoType, _rtpSender.MaxDataPayloadLength(), &(rtpHdr->codecHeader), frameType)); StorageType storage; bool fec_enabled; { CriticalSectionScoped cs(crit_.get()); FecProtectionParams* fec_params = frameType == kVideoFrameKey ? &key_fec_params_ : &delta_fec_params_; producer_fec_.SetFecParameters(fec_params, 0); storage = packetizer->GetStorageType(_retransmissionSettings); fec_enabled = _fecEnabled; } // Register CVO rtp header extension at the first time when we receive a frame // with pending rotation. RTPSenderInterface::CVOMode cvo_mode = RTPSenderInterface::kCVONone; if (rtpHdr && rtpHdr->rotation != kVideoRotation_0) { cvo_mode = _rtpSender.ActivateCVORtpHeaderExtension(); } uint16_t rtp_header_length = _rtpSender.RTPHeaderLength(); size_t payload_bytes_to_send = payloadSize; const uint8_t* data = payloadData; // TODO(changbin): we currently don't support to configure the codec to // output multiple partitions for VP8. Should remove below check after the // issue is fixed. const RTPFragmentationHeader* frag = (videoType == kRtpVideoVp8) ? NULL : fragmentation; packetizer->SetPayloadData(data, payload_bytes_to_send, frag); bool last = false; while (!last) { uint8_t dataBuffer[IP_PACKET_SIZE] = {0}; size_t payload_bytes_in_packet = 0; if (!packetizer->NextPacket(&dataBuffer[rtp_header_length], &payload_bytes_in_packet, &last)) { 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); // According to // 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)). The MTSI client may also add the payload bytes onto the last RTP // packet in each group of packets which make up another type of frame // (e.g. a P-Frame) only if the current value is different from the previous // value sent. // Here we are adding it to every packet of every frame at this point. if (!rtpHdr) { RTC_DCHECK(!_rtpSender.IsRtpHeaderExtensionRegistered( kRtpExtensionVideoRotation)); } else if (cvo_mode == RTPSenderInterface::kCVOActivated) { // Checking whether CVO header extension is registered will require taking // a lock. It'll be a no-op if it's not registered. // TODO(guoweis): For now, all packets sent will carry the CVO such that // the RTP header length is consistent, although the receiver side will // only exam the packets with marker bit set. size_t packetSize = payloadSize + rtp_header_length; RtpUtility::RtpHeaderParser rtp_parser(dataBuffer, packetSize); RTPHeader rtp_header; rtp_parser.Parse(rtp_header); _rtpSender.UpdateVideoRotation(dataBuffer, packetSize, rtp_header, rtpHdr->rotation); } if (fec_enabled) { SendVideoPacketAsRed(dataBuffer, payload_bytes_in_packet, rtp_header_length, _rtpSender.SequenceNumber(), captureTimeStamp, capture_time_ms, storage, packetizer->GetProtectionType() == kProtectedPacket); } else { SendVideoPacket(dataBuffer, payload_bytes_in_packet, rtp_header_length, _rtpSender.SequenceNumber(), captureTimeStamp, capture_time_ms, storage); } } TRACE_EVENT_ASYNC_END1( "webrtc", "Video", capture_time_ms, "timestamp", _rtpSender.Timestamp()); return 0; } void RTPSenderVideo::SetMaxConfiguredBitrateVideo(const uint32_t maxBitrate) { _maxBitrate = maxBitrate; } uint32_t RTPSenderVideo::MaxConfiguredBitrateVideo() const { return _maxBitrate; } 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 { CriticalSectionScoped cs(crit_.get()); return _retransmissionSettings; } void RTPSenderVideo::SetSelectiveRetransmissions(uint8_t settings) { CriticalSectionScoped cs(crit_.get()); _retransmissionSettings = settings; } } // namespace webrtc