// Copyright (c) 2011 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/callback.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) : session_(NULL), initialized_(false), rtp_writer_(message_loop) { } RtpVideoWriter::~RtpVideoWriter() { Close(); } void RtpVideoWriter::Init(protocol::Session* session, const InitializedCallback& callback) { session_ = session; 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(); if (session_) { session_->CancelChannelCreation(kVideoRtpChannelName); session_->CancelChannelCreation(kVideoRtcpChannelName); session_ = NULL; } } bool RtpVideoWriter::is_connected() { return rtp_channel_.get() && rtcp_channel_.get(); } void RtpVideoWriter::ProcessVideoPacket(const VideoPacket* packet, const base::Closure& 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(); } int RtpVideoWriter::GetPendingPackets() { return rtp_writer_.GetPendingPackets(); } } // namespace protocol } // namespace remoting