|  | /* | 
|  | *  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/video_coding/frame_buffer.h" | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include "webrtc/base/checks.h" | 
|  | #include "webrtc/base/logging.h" | 
|  | #include "webrtc/modules/video_coding/packet.h" | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | VCMFrameBuffer::VCMFrameBuffer() | 
|  | : | 
|  | _state(kStateEmpty), | 
|  | _nackCount(0), | 
|  | _latestPacketTimeMs(-1) { | 
|  | } | 
|  |  | 
|  | VCMFrameBuffer::~VCMFrameBuffer() { | 
|  | } | 
|  |  | 
|  | VCMFrameBuffer::VCMFrameBuffer(const VCMFrameBuffer& rhs) | 
|  | : | 
|  | VCMEncodedFrame(rhs), | 
|  | _state(rhs._state), | 
|  | _sessionInfo(), | 
|  | _nackCount(rhs._nackCount), | 
|  | _latestPacketTimeMs(rhs._latestPacketTimeMs) { | 
|  | _sessionInfo = rhs._sessionInfo; | 
|  | _sessionInfo.UpdateDataPointers(rhs._buffer, _buffer); | 
|  | } | 
|  |  | 
|  | webrtc::FrameType | 
|  | VCMFrameBuffer::FrameType() const { | 
|  | return _sessionInfo.FrameType(); | 
|  | } | 
|  |  | 
|  | int32_t | 
|  | VCMFrameBuffer::GetLowSeqNum() const { | 
|  | return _sessionInfo.LowSequenceNumber(); | 
|  | } | 
|  |  | 
|  | int32_t | 
|  | VCMFrameBuffer::GetHighSeqNum() const { | 
|  | return _sessionInfo.HighSequenceNumber(); | 
|  | } | 
|  |  | 
|  | int VCMFrameBuffer::PictureId() const { | 
|  | return _sessionInfo.PictureId(); | 
|  | } | 
|  |  | 
|  | int VCMFrameBuffer::TemporalId() const { | 
|  | return _sessionInfo.TemporalId(); | 
|  | } | 
|  |  | 
|  | bool VCMFrameBuffer::LayerSync() const { | 
|  | return _sessionInfo.LayerSync(); | 
|  | } | 
|  |  | 
|  | int VCMFrameBuffer::Tl0PicId() const { | 
|  | return _sessionInfo.Tl0PicId(); | 
|  | } | 
|  |  | 
|  | bool VCMFrameBuffer::NonReference() const { | 
|  | return _sessionInfo.NonReference(); | 
|  | } | 
|  |  | 
|  | void VCMFrameBuffer::SetGofInfo(const GofInfoVP9& gof_info, size_t idx) { | 
|  | _sessionInfo.SetGofInfo(gof_info, idx); | 
|  | // TODO(asapersson): Consider adding hdr->VP9.ref_picture_id for testing. | 
|  | _codecSpecificInfo.codecSpecific.VP9.temporal_idx = | 
|  | gof_info.temporal_idx[idx]; | 
|  | _codecSpecificInfo.codecSpecific.VP9.temporal_up_switch = | 
|  | gof_info.temporal_up_switch[idx]; | 
|  | } | 
|  |  | 
|  | bool | 
|  | VCMFrameBuffer::IsSessionComplete() const { | 
|  | return _sessionInfo.complete(); | 
|  | } | 
|  |  | 
|  | // Insert packet | 
|  | VCMFrameBufferEnum | 
|  | VCMFrameBuffer::InsertPacket(const VCMPacket& packet, | 
|  | int64_t timeInMs, | 
|  | VCMDecodeErrorMode decode_error_mode, | 
|  | const FrameData& frame_data) { | 
|  | assert(!(NULL == packet.dataPtr && packet.sizeBytes > 0)); | 
|  | if (packet.dataPtr != NULL) { | 
|  | _payloadType = packet.payloadType; | 
|  | } | 
|  |  | 
|  | if (kStateEmpty == _state) { | 
|  | // First packet (empty and/or media) inserted into this frame. | 
|  | // store some info and set some initial values. | 
|  | _timeStamp = packet.timestamp; | 
|  | // We only take the ntp timestamp of the first packet of a frame. | 
|  | ntp_time_ms_ = packet.ntp_time_ms_; | 
|  | _codec = packet.codec; | 
|  | if (packet.frameType != kEmptyFrame) { | 
|  | // first media packet | 
|  | SetState(kStateIncomplete); | 
|  | } | 
|  | } | 
|  |  | 
|  | uint32_t requiredSizeBytes = Length() + packet.sizeBytes + | 
|  | (packet.insertStartCode ? kH264StartCodeLengthBytes : 0); | 
|  | if (requiredSizeBytes >= _size) { | 
|  | const uint8_t* prevBuffer = _buffer; | 
|  | const uint32_t increments = requiredSizeBytes / | 
|  | kBufferIncStepSizeBytes + | 
|  | (requiredSizeBytes % | 
|  | kBufferIncStepSizeBytes > 0); | 
|  | const uint32_t newSize = _size + | 
|  | increments * kBufferIncStepSizeBytes; | 
|  | if (newSize > kMaxJBFrameSizeBytes) { | 
|  | LOG(LS_ERROR) << "Failed to insert packet due to frame being too " | 
|  | "big."; | 
|  | return kSizeError; | 
|  | } | 
|  | VerifyAndAllocate(newSize); | 
|  | _sessionInfo.UpdateDataPointers(prevBuffer, _buffer); | 
|  | } | 
|  |  | 
|  | if (packet.width > 0 && packet.height > 0) { | 
|  | _encodedWidth = packet.width; | 
|  | _encodedHeight = packet.height; | 
|  | } | 
|  |  | 
|  | // Don't copy payload specific data for empty packets (e.g padding packets). | 
|  | if (packet.sizeBytes > 0) | 
|  | CopyCodecSpecific(&packet.codecSpecificHeader); | 
|  |  | 
|  | int retVal = _sessionInfo.InsertPacket(packet, _buffer, | 
|  | decode_error_mode, | 
|  | frame_data); | 
|  | if (retVal == -1) { | 
|  | return kSizeError; | 
|  | } else if (retVal == -2) { | 
|  | return kDuplicatePacket; | 
|  | } else if (retVal == -3) { | 
|  | return kOutOfBoundsPacket; | 
|  | } | 
|  | // update length | 
|  | _length = Length() + static_cast<uint32_t>(retVal); | 
|  |  | 
|  | _latestPacketTimeMs = timeInMs; | 
|  |  | 
|  | // 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)). | 
|  | if (packet.markerBit) { | 
|  | RTC_DCHECK(!_rotation_set); | 
|  | _rotation = packet.codecSpecificHeader.rotation; | 
|  | _rotation_set = true; | 
|  | } | 
|  |  | 
|  | if (_sessionInfo.complete()) { | 
|  | SetState(kStateComplete); | 
|  | return kCompleteSession; | 
|  | } else if (_sessionInfo.decodable()) { | 
|  | SetState(kStateDecodable); | 
|  | return kDecodableSession; | 
|  | } | 
|  | return kIncomplete; | 
|  | } | 
|  |  | 
|  | int64_t | 
|  | VCMFrameBuffer::LatestPacketTimeMs() const { | 
|  | return _latestPacketTimeMs; | 
|  | } | 
|  |  | 
|  | void | 
|  | VCMFrameBuffer::IncrementNackCount() { | 
|  | _nackCount++; | 
|  | } | 
|  |  | 
|  | int16_t | 
|  | VCMFrameBuffer::GetNackCount() const { | 
|  | return _nackCount; | 
|  | } | 
|  |  | 
|  | bool | 
|  | VCMFrameBuffer::HaveFirstPacket() const { | 
|  | return _sessionInfo.HaveFirstPacket(); | 
|  | } | 
|  |  | 
|  | bool | 
|  | VCMFrameBuffer::HaveLastPacket() const { | 
|  | return _sessionInfo.HaveLastPacket(); | 
|  | } | 
|  |  | 
|  | int | 
|  | VCMFrameBuffer::NumPackets() const { | 
|  | return _sessionInfo.NumPackets(); | 
|  | } | 
|  |  | 
|  | void | 
|  | VCMFrameBuffer::Reset() { | 
|  | _length = 0; | 
|  | _timeStamp = 0; | 
|  | _sessionInfo.Reset(); | 
|  | _payloadType = 0; | 
|  | _nackCount = 0; | 
|  | _latestPacketTimeMs = -1; | 
|  | _state = kStateEmpty; | 
|  | VCMEncodedFrame::Reset(); | 
|  | } | 
|  |  | 
|  | // Set state of frame | 
|  | void | 
|  | VCMFrameBuffer::SetState(VCMFrameBufferStateEnum state) { | 
|  | if (_state == state) { | 
|  | return; | 
|  | } | 
|  | switch (state) { | 
|  | case kStateIncomplete: | 
|  | // we can go to this state from state kStateEmpty | 
|  | assert(_state == kStateEmpty); | 
|  |  | 
|  | // Do nothing, we received a packet | 
|  | break; | 
|  |  | 
|  | case kStateComplete: | 
|  | assert(_state == kStateEmpty || | 
|  | _state == kStateIncomplete || | 
|  | _state == kStateDecodable); | 
|  |  | 
|  | break; | 
|  |  | 
|  | case kStateEmpty: | 
|  | // Should only be set to empty through Reset(). | 
|  | assert(false); | 
|  | break; | 
|  |  | 
|  | case kStateDecodable: | 
|  | assert(_state == kStateEmpty || | 
|  | _state == kStateIncomplete); | 
|  | break; | 
|  | } | 
|  | _state = state; | 
|  | } | 
|  |  | 
|  | // Get current state of frame | 
|  | VCMFrameBufferStateEnum | 
|  | VCMFrameBuffer::GetState() const { | 
|  | return _state; | 
|  | } | 
|  |  | 
|  | // Get current state of frame | 
|  | VCMFrameBufferStateEnum | 
|  | VCMFrameBuffer::GetState(uint32_t& timeStamp) const { | 
|  | timeStamp = TimeStamp(); | 
|  | return GetState(); | 
|  | } | 
|  |  | 
|  | bool | 
|  | VCMFrameBuffer::IsRetransmitted() const { | 
|  | return _sessionInfo.session_nack(); | 
|  | } | 
|  |  | 
|  | void | 
|  | VCMFrameBuffer::PrepareForDecode(bool continuous) { | 
|  | #ifdef INDEPENDENT_PARTITIONS | 
|  | if (_codec == kVideoCodecVP8) { | 
|  | _length = | 
|  | _sessionInfo.BuildVP8FragmentationHeader(_buffer, _length, | 
|  | &_fragmentation); | 
|  | } else { | 
|  | size_t bytes_removed = _sessionInfo.MakeDecodable(); | 
|  | _length -= bytes_removed; | 
|  | } | 
|  | #else | 
|  | size_t bytes_removed = _sessionInfo.MakeDecodable(); | 
|  | _length -= bytes_removed; | 
|  | #endif | 
|  | // Transfer frame information to EncodedFrame and create any codec | 
|  | // specific information. | 
|  | _frameType = _sessionInfo.FrameType(); | 
|  | _completeFrame = _sessionInfo.complete(); | 
|  | _missingFrame = !continuous; | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |