/* * 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 "rtp_receiver_video.h" #include //assert #include // memcpy() #include #include "critical_section_wrapper.h" #include "receiver_fec.h" #include "rtp_rtcp_impl.h" #include "rtp_utility.h" #include "trace.h" namespace webrtc { WebRtc_UWord32 BitRateBPS(WebRtc_UWord16 x ) { return (x & 0x3fff) * WebRtc_UWord32(pow(10.0f,(2 + (x >> 14)))); } RTPReceiverVideo::RTPReceiverVideo(const WebRtc_Word32 id, ModuleRtpRtcpImpl* owner) : _id(id), _criticalSectionReceiverVideo( CriticalSectionWrapper::CreateCriticalSection()), _currentFecFrameDecoded(false), _receiveFEC(NULL) { } RTPReceiverVideo::~RTPReceiverVideo() { delete _criticalSectionReceiverVideo; delete _receiveFEC; } ModuleRTPUtility::Payload* RTPReceiverVideo::RegisterReceiveVideoPayload( const char payloadName[RTP_PAYLOAD_NAME_SIZE], const WebRtc_Word8 payloadType, const WebRtc_UWord32 maxRate) { RtpVideoCodecTypes videoType = kRtpNoVideo; if (ModuleRTPUtility::StringCompare(payloadName, "VP8", 3)) { videoType = kRtpVp8Video; } else if (ModuleRTPUtility::StringCompare(payloadName, "I420", 4)) { videoType = kRtpNoVideo; } else if (ModuleRTPUtility::StringCompare(payloadName, "ULPFEC", 6)) { // store this if (_receiveFEC == NULL) { _receiveFEC = new ReceiverFEC(_id, this); } _receiveFEC->SetPayloadTypeFEC(payloadType); videoType = kRtpFecVideo; } else { return NULL; } ModuleRTPUtility::Payload* 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 = maxRate; payload->audio = false; return payload; } // we have no critext when calling this // we are not allowed to have any critsects when calling // CallbackOfReceivedPayloadData WebRtc_Word32 RTPReceiverVideo::ParseVideoCodecSpecific( WebRtcRTPHeader* rtpHeader, const WebRtc_UWord8* payloadData, const WebRtc_UWord16 payloadDataLength, const RtpVideoCodecTypes videoType, const bool isRED, const WebRtc_UWord8* incomingRtpPacket, const WebRtc_UWord16 incomingRtpPacketSize, const WebRtc_Word64 nowMS) { WebRtc_Word32 retVal = 0; _criticalSectionReceiverVideo->Enter(); if (isRED) { if(_receiveFEC == NULL) { _criticalSectionReceiverVideo->Leave(); return -1; } bool FECpacket = false; retVal = _receiveFEC->AddReceivedFECPacket( rtpHeader, incomingRtpPacket, payloadDataLength, FECpacket); if (retVal != -1) { retVal = _receiveFEC->ProcessReceivedFEC(); } _criticalSectionReceiverVideo->Leave(); if(retVal == 0 && FECpacket) { // Callback with the received FEC packet. // The normal packets are delivered after parsing. // This contains the original RTP packet header but with // empty payload and data length. rtpHeader->frameType = kFrameEmpty; // We need this for the routing. WebRtc_Word32 retVal = SetCodecType(videoType, rtpHeader); if(retVal != 0) { return retVal; } // Pass the length of FEC packets so that they can be accounted for in // the bandwidth estimator. retVal = CallbackOfReceivedPayloadData(NULL, payloadDataLength, rtpHeader); } } else { // will leave the _criticalSectionReceiverVideo critsect retVal = ParseVideoCodecSpecificSwitch(rtpHeader, payloadData, payloadDataLength, videoType); } return retVal; } WebRtc_Word32 RTPReceiverVideo::BuildRTPheader( const WebRtcRTPHeader* rtpHeader, WebRtc_UWord8* dataBuffer) const { dataBuffer[0] = static_cast(0x80); // version 2 dataBuffer[1] = static_cast(rtpHeader->header.payloadType); if (rtpHeader->header.markerBit) { dataBuffer[1] |= kRtpMarkerBitMask; // MarkerBit is 1 } ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer + 2, rtpHeader->header.sequenceNumber); ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer + 4, rtpHeader->header.timestamp); ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer + 8, rtpHeader->header.ssrc); WebRtc_Word32 rtpHeaderLength = 12; // Add the CSRCs if any if (rtpHeader->header.numCSRCs > 0) { if (rtpHeader->header.numCSRCs > 16) { // error assert(false); } WebRtc_UWord8* ptr = &dataBuffer[rtpHeaderLength]; for (WebRtc_UWord32 i = 0; i < rtpHeader->header.numCSRCs; ++i) { ModuleRTPUtility::AssignUWord32ToBuffer(ptr, rtpHeader->header.arrOfCSRCs[i]); ptr +=4; } dataBuffer[0] = (dataBuffer[0]&0xf0) | rtpHeader->header.numCSRCs; // Update length of header rtpHeaderLength += sizeof(WebRtc_UWord32)*rtpHeader->header.numCSRCs; } return rtpHeaderLength; } WebRtc_Word32 RTPReceiverVideo::ReceiveRecoveredPacketCallback( WebRtcRTPHeader* rtpHeader, const WebRtc_UWord8* payloadData, const WebRtc_UWord16 payloadDataLength) { // TODO(pwestin) Re-factor this to avoid the messy critsect handling. _criticalSectionReceiverVideo->Enter(); _currentFecFrameDecoded = true; ModuleRTPUtility::Payload* payload = NULL; if (PayloadTypeToPayload(rtpHeader->header.payloadType, payload) != 0) { _criticalSectionReceiverVideo->Leave(); return -1; } // here we can re-create the original lost packet so that we can use it for // the relay we need to re-create the RED header too WebRtc_UWord8 recoveredPacket[IP_PACKET_SIZE]; WebRtc_UWord16 rtpHeaderLength = (WebRtc_UWord16)BuildRTPheader( rtpHeader, recoveredPacket); const WebRtc_UWord8 REDForFECHeaderLength = 1; // replace pltype recoveredPacket[1] &= 0x80; // reset recoveredPacket[1] += REDPayloadType(); // replace with RED payload type // add RED header recoveredPacket[rtpHeaderLength] = rtpHeader->header.payloadType; // f-bit always 0 memcpy(recoveredPacket + rtpHeaderLength + REDForFECHeaderLength, payloadData, payloadDataLength); return ParseVideoCodecSpecificSwitch( rtpHeader, payloadData, payloadDataLength, payload->typeSpecific.Video.videoCodecType); } WebRtc_Word32 RTPReceiverVideo::SetCodecType(const RtpVideoCodecTypes videoType, WebRtcRTPHeader* rtpHeader) const { switch (videoType) { case kRtpNoVideo: rtpHeader->type.Video.codec = kRTPVideoGeneric; break; case kRtpVp8Video: rtpHeader->type.Video.codec = kRTPVideoVP8; break; case kRtpFecVideo: rtpHeader->type.Video.codec = kRTPVideoFEC; break; } return 0; } WebRtc_Word32 RTPReceiverVideo::ParseVideoCodecSpecificSwitch( WebRtcRTPHeader* rtpHeader, const WebRtc_UWord8* payloadData, const WebRtc_UWord16 payloadDataLength, const RtpVideoCodecTypes videoType) { WebRtc_Word32 retVal = SetCodecType(videoType, rtpHeader); if (retVal != 0) { _criticalSectionReceiverVideo->Leave(); return retVal; } WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "%s(timestamp:%u)", __FUNCTION__, rtpHeader->header.timestamp); // All receive functions release _criticalSectionReceiverVideo before // returning. switch (videoType) { case kRtpNoVideo: return ReceiveGenericCodec(rtpHeader, payloadData, payloadDataLength); case kRtpVp8Video: return ReceiveVp8Codec(rtpHeader, payloadData, payloadDataLength); case kRtpFecVideo: break; } _criticalSectionReceiverVideo->Leave(); return -1; } WebRtc_Word32 RTPReceiverVideo::ReceiveVp8Codec( WebRtcRTPHeader* rtpHeader, const WebRtc_UWord8* payloadData, const WebRtc_UWord16 payloadDataLength) { bool success; ModuleRTPUtility::RTPPayload parsedPacket; if (payloadDataLength == 0) { success = true; parsedPacket.info.VP8.dataLength = 0; } else { ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpVp8Video, payloadData, payloadDataLength, _id); success = rtpPayloadParser.Parse(parsedPacket); } // from here down we only work on local data _criticalSectionReceiverVideo->Leave(); if (!success) { return -1; } if (parsedPacket.info.VP8.dataLength == 0) { // we have an "empty" VP8 packet, it's ok, could be one way video // Inform the jitter buffer about this packet. rtpHeader->frameType = kFrameEmpty; if (CallbackOfReceivedPayloadData(NULL, 0, rtpHeader) != 0) { return -1; } return 0; } rtpHeader->frameType = (parsedPacket.frameType == ModuleRTPUtility::kIFrame) ? kVideoFrameKey : kVideoFrameDelta; RTPVideoHeaderVP8 *toHeader = &rtpHeader->type.Video.codecHeader.VP8; ModuleRTPUtility::RTPPayloadVP8 *fromHeader = &parsedPacket.info.VP8; rtpHeader->type.Video.isFirstPacket = fromHeader->beginningOfPartition && (fromHeader->partitionID == 0); toHeader->pictureId = fromHeader->hasPictureID ? fromHeader->pictureID : kNoPictureId; toHeader->tl0PicIdx = fromHeader->hasTl0PicIdx ? fromHeader->tl0PicIdx : kNoTl0PicIdx; if (fromHeader->hasTID) { toHeader->temporalIdx = fromHeader->tID; toHeader->layerSync = fromHeader->layerSync; } else { toHeader->temporalIdx = kNoTemporalIdx; toHeader->layerSync = false; } toHeader->keyIdx = fromHeader->hasKeyIdx ? fromHeader->keyIdx : kNoKeyIdx; toHeader->frameWidth = fromHeader->frameWidth; toHeader->frameHeight = fromHeader->frameHeight; toHeader->partitionId = fromHeader->partitionID; toHeader->beginningOfPartition = fromHeader->beginningOfPartition; if(CallbackOfReceivedPayloadData(parsedPacket.info.VP8.data, parsedPacket.info.VP8.dataLength, rtpHeader) != 0) { return -1; } return 0; } WebRtc_Word32 RTPReceiverVideo::ReceiveGenericCodec( WebRtcRTPHeader* rtpHeader, const WebRtc_UWord8* payloadData, const WebRtc_UWord16 payloadDataLength) { rtpHeader->frameType = kVideoFrameKey; if(((SequenceNumber() + 1) == rtpHeader->header.sequenceNumber) && (TimeStamp() != rtpHeader->header.timestamp)) { rtpHeader->type.Video.isFirstPacket = true; } _criticalSectionReceiverVideo->Leave(); if(CallbackOfReceivedPayloadData(payloadData, payloadDataLength, rtpHeader) != 0) { return -1; } return 0; } } // namespace webrtc