
This change adds support for VP8 partitions in the video jitter buffer and the VP8 encoder and decoder wrappers. The feature is currently disabled by default since it requires a later version of libvpx. With this change the jitter buffer will also start keeping track of each packet header until decoding, and the VCMSessionInfo and VCMPacket objects will keep pointers into the encoded frame buffers. Review URL: http://webrtc-codereview.appspot.com/137021 git-svn-id: http://webrtc.googlecode.com/svn/trunk@558 4adac7df-926f-26a2-2b94-8c16560cd09d
422 lines
10 KiB
C++
422 lines
10 KiB
C++
/*
|
|
* Copyright (c) 2011 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 "../../../../engine_configurations.h"
|
|
#include "frame_buffer.h"
|
|
#include "packet.h"
|
|
|
|
#include <cassert>
|
|
#include <string.h>
|
|
|
|
#if defined(_WIN32)
|
|
// VS 2005: Don't warn for default initialized arrays. See help for more info.
|
|
#pragma warning(disable:4351)
|
|
#endif
|
|
|
|
namespace webrtc {
|
|
|
|
VCMFrameBuffer::VCMFrameBuffer()
|
|
:
|
|
_state(kStateFree),
|
|
_frameCounted(false),
|
|
_nackCount(0),
|
|
_latestPacketTimeMs(-1) {
|
|
}
|
|
|
|
VCMFrameBuffer::~VCMFrameBuffer() {
|
|
}
|
|
|
|
VCMFrameBuffer::VCMFrameBuffer(VCMFrameBuffer& rhs)
|
|
:
|
|
VCMEncodedFrame(rhs),
|
|
_state(rhs._state),
|
|
_frameCounted(rhs._frameCounted),
|
|
_sessionInfo(),
|
|
_nackCount(rhs._nackCount),
|
|
_latestPacketTimeMs(rhs._latestPacketTimeMs)
|
|
{
|
|
_sessionInfo = rhs._sessionInfo;
|
|
}
|
|
|
|
webrtc::FrameType
|
|
VCMFrameBuffer::FrameType() const
|
|
{
|
|
return _sessionInfo.FrameType();
|
|
}
|
|
|
|
void
|
|
VCMFrameBuffer::SetPreviousFrameLoss()
|
|
{
|
|
_sessionInfo.SetPreviousFrameLoss();
|
|
}
|
|
|
|
WebRtc_Word32
|
|
VCMFrameBuffer::GetLowSeqNum()
|
|
{
|
|
return _sessionInfo.GetLowSeqNum();
|
|
}
|
|
|
|
WebRtc_Word32
|
|
VCMFrameBuffer::GetHighSeqNum()
|
|
{
|
|
return _sessionInfo.GetHighSeqNum();
|
|
}
|
|
|
|
bool
|
|
VCMFrameBuffer::IsSessionComplete()
|
|
{
|
|
return _sessionInfo.IsSessionComplete();
|
|
}
|
|
|
|
// Insert packet
|
|
VCMFrameBufferEnum
|
|
VCMFrameBuffer::InsertPacket(const VCMPacket& packet, WebRtc_Word64 timeInMs)
|
|
{
|
|
if (_state == kStateDecoding)
|
|
{
|
|
// Do not insert packet
|
|
return kIncomplete;
|
|
}
|
|
|
|
// Sanity to check if the frame has been freed. (Too old for example)
|
|
if (_state == kStateFree)
|
|
{
|
|
return kStateError;
|
|
}
|
|
|
|
// is this packet part of this frame
|
|
if (TimeStamp() && (TimeStamp() != packet.timestamp))
|
|
{
|
|
return kTimeStampError;
|
|
}
|
|
|
|
// sanity checks
|
|
if (_size + packet.sizeBytes +
|
|
(packet.insertStartCode ? kH264StartCodeLengthBytes : 0 )
|
|
> kMaxJBFrameSizeBytes)
|
|
{
|
|
return kSizeError;
|
|
}
|
|
if (NULL == packet.dataPtr && packet.sizeBytes > 0)
|
|
{
|
|
return kSizeError;
|
|
}
|
|
if ((packet.frameType != kFrameEmpty) &&
|
|
(!_sessionInfo.HaveStartSeqNumber()))
|
|
{
|
|
_sessionInfo.SetStartSeqNumber(packet.seqNum);
|
|
}
|
|
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;
|
|
_codec = packet.codec;
|
|
if (packet.frameType != kFrameEmpty)
|
|
{
|
|
// first media packet
|
|
SetState(kStateIncomplete);
|
|
}
|
|
}
|
|
|
|
WebRtc_UWord32 requiredSizeBytes = Length() + packet.sizeBytes +
|
|
(packet.insertStartCode ? kH264StartCodeLengthBytes : 0);
|
|
if (requiredSizeBytes >= _size)
|
|
{
|
|
const WebRtc_UWord8* prevBuffer = _buffer;
|
|
const WebRtc_UWord32 increments = requiredSizeBytes /
|
|
kBufferIncStepSizeBytes +
|
|
(requiredSizeBytes %
|
|
kBufferIncStepSizeBytes > 0);
|
|
const WebRtc_UWord32 newSize = _size +
|
|
increments * kBufferIncStepSizeBytes;
|
|
if (newSize > kMaxJBFrameSizeBytes)
|
|
{
|
|
return kSizeError;
|
|
}
|
|
if (VerifyAndAllocate(newSize) == -1)
|
|
{
|
|
return kSizeError;
|
|
}
|
|
_sessionInfo.UpdateDataPointers(_buffer, prevBuffer);
|
|
}
|
|
|
|
CopyCodecSpecific(&packet.codecSpecificHeader);
|
|
|
|
WebRtc_Word64 retVal = _sessionInfo.InsertPacket(packet, _buffer);
|
|
if (retVal == -1)
|
|
{
|
|
return kSizeError;
|
|
}
|
|
else if (retVal == -2)
|
|
{
|
|
return kDuplicatePacket;
|
|
}
|
|
// update length
|
|
_length = Length() + static_cast<WebRtc_UWord32>(retVal);
|
|
|
|
_latestPacketTimeMs = timeInMs;
|
|
|
|
if (_sessionInfo.IsSessionComplete())
|
|
{
|
|
return kCompleteSession;
|
|
}
|
|
else
|
|
{
|
|
// this layer is not complete
|
|
if (_state == kStateComplete)
|
|
{
|
|
// we already have a complete layer
|
|
// wait for all independent layers belonging to the same frame
|
|
_state = kStateIncomplete;
|
|
}
|
|
}
|
|
return kIncomplete;
|
|
}
|
|
|
|
WebRtc_Word64
|
|
VCMFrameBuffer::LatestPacketTimeMs()
|
|
{
|
|
return _latestPacketTimeMs;
|
|
}
|
|
|
|
// Zero out all entries in list up to and including the (first)
|
|
// entry equal to _lowSeqNum
|
|
WebRtc_Word32
|
|
VCMFrameBuffer::ZeroOutSeqNum(WebRtc_Word32* list, WebRtc_Word32 num)
|
|
{
|
|
if (_sessionInfo.ZeroOutSeqNum(list, num) != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Zero out all entries in list up to and including the (first) entry equal to
|
|
// _lowSeqNum. Hybrid mode: 1. Don't NACK FEC packets 2. Make a smart decision
|
|
// on whether to NACK or not
|
|
|
|
WebRtc_Word32
|
|
VCMFrameBuffer::ZeroOutSeqNumHybrid(WebRtc_Word32* list,
|
|
WebRtc_Word32 num,
|
|
float rttScore)
|
|
{
|
|
return _sessionInfo.ZeroOutSeqNumHybrid(list, num, rttScore);
|
|
}
|
|
|
|
void
|
|
VCMFrameBuffer::IncrementNackCount()
|
|
{
|
|
_nackCount++;
|
|
}
|
|
|
|
WebRtc_Word16
|
|
VCMFrameBuffer::GetNackCount() const
|
|
{
|
|
return _nackCount;
|
|
}
|
|
|
|
bool
|
|
VCMFrameBuffer::HaveLastPacket()
|
|
{
|
|
return _sessionInfo.HaveLastPacket();
|
|
}
|
|
|
|
bool
|
|
VCMFrameBuffer::ForceSetHaveLastPacket()
|
|
{
|
|
_sessionInfo.ForceSetHaveLastPacket();
|
|
return _sessionInfo.IsSessionComplete();
|
|
}
|
|
|
|
void
|
|
VCMFrameBuffer::Reset()
|
|
{
|
|
_length = 0;
|
|
_timeStamp = 0;
|
|
_sessionInfo.Reset();
|
|
_frameCounted = false;
|
|
_payloadType = 0;
|
|
_nackCount = 0;
|
|
_latestPacketTimeMs = -1;
|
|
_state = kStateFree;
|
|
VCMEncodedFrame::Reset();
|
|
}
|
|
|
|
// Makes sure the session contains a decodable stream.
|
|
void
|
|
VCMFrameBuffer::MakeSessionDecodable()
|
|
{
|
|
WebRtc_UWord32 retVal;
|
|
#ifdef INDEPENDENT_PARTITIONS
|
|
if (_codec != kVideoCodecVP8) {
|
|
retVal = _sessionInfo.MakeDecodable(_buffer);
|
|
_length -= retVal;
|
|
}
|
|
#else
|
|
retVal = _sessionInfo.MakeDecodable(_buffer);
|
|
_length -= retVal;
|
|
#endif
|
|
}
|
|
|
|
// Set state of frame
|
|
void
|
|
VCMFrameBuffer::SetState(VCMFrameBufferStateEnum state)
|
|
{
|
|
if (_state == state)
|
|
{
|
|
return;
|
|
}
|
|
switch (state)
|
|
{
|
|
case kStateFree:
|
|
// Reset everything
|
|
// We can go to this state from all other states.
|
|
// The one setting the state to free must ensure
|
|
// that the frame is removed from the timestamp
|
|
// ordered frame list in the jb.
|
|
Reset();
|
|
break;
|
|
|
|
case kStateIncomplete:
|
|
// we can go to this state from state kStateEmpty
|
|
assert(_state == kStateEmpty ||
|
|
_state == kStateDecoding);
|
|
|
|
// Do nothing, we received a packet
|
|
break;
|
|
|
|
case kStateComplete:
|
|
assert(_state == kStateEmpty ||
|
|
_state == kStateIncomplete ||
|
|
_state == kStateDecodable);
|
|
|
|
break;
|
|
|
|
case kStateEmpty:
|
|
assert(_state == kStateFree);
|
|
// Do nothing
|
|
break;
|
|
|
|
case kStateDecoding:
|
|
// we can go to this state from state kStateComplete kStateIncomplete
|
|
assert(_state == kStateComplete || _state == kStateIncomplete ||
|
|
_state == kStateDecodable);
|
|
// Transfer frame information to EncodedFrame and create any codec
|
|
// specific information
|
|
RestructureFrameInformation();
|
|
break;
|
|
|
|
case kStateDecodable:
|
|
if (_state == kStateComplete)
|
|
{
|
|
// if complete, obviously decodable, keep as is.
|
|
return;
|
|
}
|
|
assert(_state == kStateEmpty ||
|
|
_state == kStateIncomplete);
|
|
break;
|
|
|
|
default:
|
|
// Should never happen
|
|
assert(!"FrameBuffer::SetState Incorrect frame buffer state as input");
|
|
return;
|
|
}
|
|
_state = state;
|
|
}
|
|
|
|
void
|
|
VCMFrameBuffer::RestructureFrameInformation()
|
|
{
|
|
PrepareForDecode();
|
|
_frameType = ConvertFrameType(_sessionInfo.FrameType());
|
|
_completeFrame = _sessionInfo.IsSessionComplete();
|
|
_missingFrame = _sessionInfo.PreviousFrameLoss();
|
|
}
|
|
|
|
WebRtc_Word32
|
|
VCMFrameBuffer::ExtractFromStorage(const EncodedVideoData& frameFromStorage)
|
|
{
|
|
_frameType = ConvertFrameType(frameFromStorage.frameType);
|
|
_timeStamp = frameFromStorage.timeStamp;
|
|
_payloadType = frameFromStorage.payloadType;
|
|
_encodedWidth = frameFromStorage.encodedWidth;
|
|
_encodedHeight = frameFromStorage.encodedHeight;
|
|
_missingFrame = frameFromStorage.missingFrame;
|
|
_completeFrame = frameFromStorage.completeFrame;
|
|
_renderTimeMs = frameFromStorage.renderTimeMs;
|
|
_codec = frameFromStorage.codec;
|
|
if (VerifyAndAllocate(frameFromStorage.payloadSize) < 0)
|
|
{
|
|
return VCM_MEMORY;
|
|
}
|
|
memcpy(_buffer, frameFromStorage.payloadData, frameFromStorage.payloadSize);
|
|
_length = frameFromStorage.payloadSize;
|
|
return VCM_OK;
|
|
}
|
|
|
|
// Set counted status (as counted by JB or not)
|
|
void VCMFrameBuffer::SetCountedFrame(bool frameCounted)
|
|
{
|
|
_frameCounted = frameCounted;
|
|
}
|
|
|
|
bool VCMFrameBuffer::GetCountedFrame()
|
|
{
|
|
return _frameCounted;
|
|
}
|
|
|
|
// Get current state of frame
|
|
VCMFrameBufferStateEnum
|
|
VCMFrameBuffer::GetState() const
|
|
{
|
|
return _state;
|
|
}
|
|
|
|
// Get current state of frame
|
|
VCMFrameBufferStateEnum
|
|
VCMFrameBuffer::GetState(WebRtc_UWord32& timeStamp) const
|
|
{
|
|
timeStamp = TimeStamp();
|
|
return GetState();
|
|
}
|
|
|
|
bool
|
|
VCMFrameBuffer::IsRetransmitted()
|
|
{
|
|
return _sessionInfo.IsRetransmitted();
|
|
}
|
|
|
|
void
|
|
VCMFrameBuffer::PrepareForDecode()
|
|
{
|
|
#ifdef INDEPENDENT_PARTITIONS
|
|
if (_codec == kVideoCodecVP8)
|
|
{
|
|
_length =
|
|
_sessionInfo.BuildVP8FragmentationHeader(_buffer, _length,
|
|
&_fragmentation);
|
|
}
|
|
else
|
|
{
|
|
_length = _sessionInfo.PrepareForDecode(_buffer, _codec);
|
|
}
|
|
#else
|
|
_length = _sessionInfo.PrepareForDecode(_buffer, _codec);
|
|
#endif
|
|
}
|
|
|
|
}
|