blob: ae85f8346da12f428457ebbd61648630d3def57d [file] [log] [blame]
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/protocol/rtp_video_writer.h"
#include "base/bind.h"
#include "base/task.h"
#include "net/base/io_buffer.h"
#include "remoting/base/compound_buffer.h"
#include "remoting/base/constants.h"
#include "remoting/proto/video.pb.h"
#include "remoting/protocol/rtp_writer.h"
#include "remoting/protocol/session.h"
namespace remoting {
namespace protocol {
namespace {
const int kMtu = 1200;
} // namespace
RtpVideoWriter::RtpVideoWriter(base::MessageLoopProxy* message_loop)
: initialized_(false),
rtp_writer_(message_loop) {
}
RtpVideoWriter::~RtpVideoWriter() {
Close();
}
void RtpVideoWriter::Init(protocol::Session* session,
const InitializedCallback& callback) {
initialized_callback_ = callback;
session->CreateDatagramChannel(
kVideoRtpChannelName,
base::Bind(&RtpVideoWriter::OnChannelReady,
base::Unretained(this), true));
session->CreateDatagramChannel(
kVideoRtcpChannelName,
base::Bind(&RtpVideoWriter::OnChannelReady,
base::Unretained(this), false));
}
void RtpVideoWriter::OnChannelReady(bool rtp, net::Socket* socket) {
if (!socket) {
if (!initialized_) {
initialized_ = true;
initialized_callback_.Run(false);
}
return;
}
if (rtp) {
DCHECK(!rtp_channel_.get());
rtp_channel_.reset(socket);
rtp_writer_.Init(socket);
} else {
DCHECK(!rtcp_channel_.get());
rtcp_channel_.reset(socket);
// TODO(sergeyu): Use RTCP channel somehow.
}
if (rtp_channel_.get() && rtcp_channel_.get()) {
DCHECK(!initialized_);
initialized_ = true;
initialized_callback_.Run(true);
}
}
void RtpVideoWriter::Close() {
rtp_writer_.Close();
rtp_channel_.reset();
rtcp_channel_.reset();
}
void RtpVideoWriter::ProcessVideoPacket(const VideoPacket* packet, Task* done) {
CHECK(packet->format().encoding() == VideoPacketFormat::ENCODING_VP8)
<< "Only VP8 is supported in RTP.";
CompoundBuffer payload;
// TODO(sergeyu): This copy would not be necessary CompoundBuffer was used
// inside of VideoPacket.
payload.AppendCopyOf(packet->data().data(), packet->data().size());
Vp8Descriptor vp8_desriptor;
// TODO(sergeyu): Add a flag in VideoPacket that indicates whether this is a
// key frame or not.
vp8_desriptor.non_reference_frame = false;
vp8_desriptor.picture_id = kuint32max;
int position = 0;
while (position < payload.total_bytes()) {
int size = std::min(kMtu, payload.total_bytes() - position);
// Frame beginning flag is set only for the first packet in the first
// partition.
vp8_desriptor.frame_beginning =
(packet->flags() & VideoPacket::FIRST_PACKET) != 0 && position == 0;
// Marker bit is set only for the last packet in the last partition.
bool marker = (position + size) == payload.total_bytes() &&
(packet->flags() & VideoPacket::LAST_PACKET) != 0;
// Set fragmentation flag appropriately.
if (position == 0) {
if (size == payload.total_bytes()) {
vp8_desriptor.fragmentation_info = Vp8Descriptor::NOT_FRAGMENTED;
} else {
vp8_desriptor.fragmentation_info = Vp8Descriptor::FIRST_FRAGMENT;
}
} else {
if (position + size == payload.total_bytes()) {
vp8_desriptor.fragmentation_info = Vp8Descriptor::LAST_FRAGMENT;
} else {
vp8_desriptor.fragmentation_info = Vp8Descriptor::MIDDLE_FRAGMENT;
}
}
// Create CompoundBuffer for the chunk.
CompoundBuffer chunk;
chunk.CopyFrom(payload, position, position + size);
// And send it.
rtp_writer_.SendPacket(packet->timestamp(), marker, vp8_desriptor, chunk);
position += size;
}
DCHECK_EQ(position, payload.total_bytes());
done->Run();
delete done;
}
int RtpVideoWriter::GetPendingPackets() {
return rtp_writer_.GetPendingPackets();
}
} // namespace protocol
} // namespace remoting