f561f488fc
git-svn-id: http://webrtc.googlecode.com/svn/trunk@82 4adac7df-926f-26a2-2b94-8c16560cd09d
210 lines
6.6 KiB
C++
210 lines
6.6 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 "rtp_format_vp8.h"
|
|
|
|
#include <cassert> // assert
|
|
#include <string.h> // memcpy
|
|
|
|
namespace webrtc {
|
|
|
|
RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data,
|
|
WebRtc_UWord32 payload_size,
|
|
const RTPFragmentationHeader* fragmentation,
|
|
VP8PacketizerMode mode)
|
|
: payload_data_(payload_data),
|
|
payload_size_(payload_size),
|
|
payload_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;
|
|
}
|
|
}
|
|
|
|
RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data,
|
|
WebRtc_UWord32 payload_size)
|
|
: payload_data_(payload_data),
|
|
payload_size_(payload_size),
|
|
frag_info_(),
|
|
payload_bytes_sent_(0),
|
|
mode_(kSloppy),
|
|
beginning_(true),
|
|
first_fragment_(true),
|
|
vp8_header_bytes_(1)
|
|
{}
|
|
|
|
int RtpFormatVp8::GetFragIdx()
|
|
{
|
|
// Which fragment are we in?
|
|
int frag_ix = 0;
|
|
while ((frag_ix + 1 < frag_info_.fragmentationVectorSize) &&
|
|
(payload_bytes_sent_ >= frag_info_.fragmentationOffset[frag_ix + 1]))
|
|
{
|
|
++frag_ix;
|
|
}
|
|
return frag_ix;
|
|
}
|
|
|
|
int RtpFormatVp8::NextPacket(int max_payload_len, WebRtc_UWord8* buffer,
|
|
int* bytes_to_send, bool* last_packet)
|
|
{
|
|
// Convenience variables
|
|
const int num_fragments = frag_info_.fragmentationVectorSize;
|
|
int frag_ix = GetFragIdx(); //TODO (hlundin): Store frag_ix as a member?
|
|
int send_bytes = 0; // How much data to send in this packet.
|
|
bool end_of_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 ((frag_ix < num_fragments) &&
|
|
(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.
|
|
end_of_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]
|
|
- payload_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;
|
|
end_of_fragment = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case kSloppy:
|
|
{
|
|
// Send a full packet, or what is left of the payload.
|
|
const int remaining_bytes = payload_size_ - payload_bytes_sent_;
|
|
|
|
if (remaining_bytes + vp8_header_bytes_ > max_payload_len)
|
|
{
|
|
send_bytes = max_payload_len - vp8_header_bytes_;
|
|
end_of_fragment = false;
|
|
}
|
|
else
|
|
{
|
|
send_bytes = remaining_bytes;
|
|
end_of_fragment = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
// Should not end up here
|
|
assert(false);
|
|
return -1;
|
|
}
|
|
|
|
// Write the payload header and the payload to buffer.
|
|
*bytes_to_send = WriteHeaderAndPayload(send_bytes, end_of_fragment, buffer);
|
|
if (*bytes_to_send < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
*last_packet = payload_bytes_sent_ >= payload_size_;
|
|
assert(!*last_packet || (payload_bytes_sent_ == payload_size_));
|
|
return 0;
|
|
}
|
|
|
|
int RtpFormatVp8::WriteHeaderAndPayload(int send_bytes,
|
|
bool end_of_fragment,
|
|
WebRtc_UWord8* buffer)
|
|
{
|
|
// Write the VP8 payload header.
|
|
// 0 1 2 3 4 5 6 7
|
|
// +-+-+-+-+-+-+-+-+
|
|
// | RSV |I|N|FI |B|
|
|
// +-+-+-+-+-+-+-+-+
|
|
|
|
if (send_bytes < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
if (payload_bytes_sent_ + send_bytes > payload_size_)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// PictureID always present in first packet
|
|
const int picture_id_present = beginning_;
|
|
// TODO(hlundin): must pipe this info from VP8 encoder
|
|
const int kNonrefFrame = 0;
|
|
|
|
buffer[0] = 0;
|
|
if (picture_id_present) buffer[0] |= (0x01 << 4); // I
|
|
if (kNonrefFrame) buffer[0] |= (0x01 << 3); // N
|
|
if (!first_fragment_) buffer[0] |= (0x01 << 2); // FI
|
|
if (!end_of_fragment) buffer[0] |= (0x01 << 1); // FI
|
|
if (beginning_) buffer[0] |= 0x01; // B
|
|
|
|
memcpy(&buffer[vp8_header_bytes_], &payload_data_[payload_bytes_sent_],
|
|
send_bytes);
|
|
|
|
beginning_ = false; // next packet cannot be first packet in frame
|
|
// next packet starts new fragment if this ended one
|
|
first_fragment_ = end_of_fragment;
|
|
payload_bytes_sent_ += send_bytes;
|
|
|
|
// Return total length of written data.
|
|
return send_bytes + vp8_header_bytes_;
|
|
}
|
|
} // namespace webrtc
|