webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc

187 lines
5.8 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.
*/
/*
* This file contains the implementation of the VP8 packetizer class.
*/
#include "rtp_format_vp8.h"
#include <cassert> // assert
#include <string.h> // memcpy
namespace webrtc {
RTPFormatVP8::RTPFormatVP8(const WebRtc_UWord8* payload_data,
const WebRtc_UWord32 payload_size,
const RTPFragmentationHeader* fragmentation,
const VP8PacketizerMode mode)
:
payload_data_(payload_data),
payload_size_(payload_size),
bytes_sent_(0),
mode_(mode),
beginning_(true),
first_fragment_(true),
vp8_header_bytes_(1)
{
if (fragmentation == NULL)
{
// Cannot do kStrict or kAggregate without fragmentation info.
// Change to kSloppy.
mode_ = kSloppy;
}
else
{
frag_info_ = *fragmentation;
}
}
bool RTPFormatVP8::NextPacket(const int max_payload_len, WebRtc_UWord8* buffer,
int* bytes_to_send)
{
// Convenience variables
const int num_fragments = frag_info_.fragmentationVectorSize;
// Which fragment are we in?
int frag_ix = 0;
while ((frag_ix + 1 < num_fragments) &&
(bytes_sent_ >= frag_info_.fragmentationOffset[frag_ix + 1]))
{
++frag_ix;
}
// How much data to send in this packet?
int send_bytes = 0;
bool last_fragment = false;
switch (mode_)
{
case kAggregate:
{
// Check if we are at the beginning of a new partition.
if (first_fragment_)
{
// Check if this fragment fits in one packet.
if (frag_info_.fragmentationLength[frag_ix] + vp8_header_bytes_
<= max_payload_len)
{
// Pack as many whole partitions we can into this packet;
// don't fragment.
while (send_bytes + vp8_header_bytes_
+ frag_info_.fragmentationLength[frag_ix]
<= max_payload_len)
{
send_bytes += frag_info_.fragmentationLength[frag_ix];
++frag_ix;
}
// This packet ends on a complete fragment.
last_fragment = true;
break; // Jump out of case statement.
}
}
// Either we are not starting this packet with a new partition,
// or the partition is too large for a packet.
// Move on to "case kStrict".
// NOTE: break intentionally omitted!
}
case kStrict: // Can also continue to here from kAggregate.
{
// Find out how much is left to send in the current partition.
const int remaining_bytes = frag_info_.fragmentationOffset[frag_ix]
- bytes_sent_ + frag_info_.fragmentationLength[frag_ix];
assert(remaining_bytes > 0);
assert(remaining_bytes <= frag_info_.fragmentationLength[frag_ix]);
if (remaining_bytes + vp8_header_bytes_ > max_payload_len)
{
// send one full packet
send_bytes = max_payload_len - vp8_header_bytes_;
}
else
{
// last packet from this partition
send_bytes = remaining_bytes;
last_fragment = true;
}
break;
}
case kSloppy:
{
// Send a full packet, or what is left of the payload.
const int remaining_bytes = payload_size_ - bytes_sent_;
if (remaining_bytes + vp8_header_bytes_ > max_payload_len)
{
send_bytes = max_payload_len - vp8_header_bytes_;
last_fragment = false;
}
else
{
send_bytes = remaining_bytes;
last_fragment = true;
}
break;
}
default:
// Should not end up here
assert(false);
}
// Write the payload header and the payload to buffer.
*bytes_to_send = WriteHeaderAndPayload(send_bytes, last_fragment, buffer);
// Anything left to send?
if (bytes_sent_ < payload_size_)
return false;
else
return true;
}
int RTPFormatVP8::WriteHeaderAndPayload(const int send_bytes,
const bool last_fragment,
WebRtc_UWord8* buffer)
{
// Write the VP8 payload header.
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// | RSV |I|N|FI |B|
// +-+-+-+-+-+-+-+-+
// PictureID always present in first packet
const int pictureid_present = beginning_;
// TODO(hlundin): must pipe this info from VP8 encoder
const int nonref_frame = 0;
buffer[0] = 0 | (pictureid_present << 4) // I
| (nonref_frame << 3) // N
| (!first_fragment_ << 2) | (!last_fragment << 1) // FI
| (beginning_);
// Copy the payload.
assert(bytes_sent_ + send_bytes <= payload_size_);
memcpy(&buffer[vp8_header_bytes_], &payload_data_[bytes_sent_], send_bytes);
// Update state variables.
beginning_ = false; // next packet cannot be first packet in frame
// next packet starts new fragment if this ended one
first_fragment_ = last_fragment;
bytes_sent_ += send_bytes;
// Return total length of written data.
return send_bytes + vp8_header_bytes_;
}
} // namespace webrtc