// Copyright (c) 2012 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/codec/video_encoder_verbatim.h" #include "base/logging.h" #include "base/stl_util.h" #include "remoting/base/util.h" #include "remoting/proto/video.pb.h" #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" namespace remoting { static const int kPacketSize = 1024 * 1024; VideoEncoderVerbatim::VideoEncoderVerbatim() : max_packet_size_(kPacketSize) { } void VideoEncoderVerbatim::SetMaxPacketSize(int size) { max_packet_size_ = size; } VideoEncoderVerbatim::~VideoEncoderVerbatim() { } void VideoEncoderVerbatim::Encode( const webrtc::DesktopFrame* frame, const DataAvailableCallback& data_available_callback) { callback_ = data_available_callback; encode_start_time_ = base::Time::Now(); webrtc::DesktopRegion::Iterator iter(frame->updated_region()); while (!iter.IsAtEnd()) { const webrtc::DesktopRect& rect = iter.rect(); iter.Advance(); EncodeRect(frame, rect, iter.IsAtEnd()); } callback_.Reset(); } void VideoEncoderVerbatim::EncodeRect(const webrtc::DesktopFrame* frame, const webrtc::DesktopRect& rect, bool last) { CHECK(frame->data()); const int stride = frame->stride(); const int bytes_per_pixel = 4; const int row_size = bytes_per_pixel * rect.width(); scoped_ptr packet(new VideoPacket()); PrepareUpdateStart(frame, rect, packet.get()); const uint8* in = frame->data() + rect.top() * stride + rect.left() * bytes_per_pixel; // TODO(hclam): Fill in the sequence number. uint8* out = GetOutputBuffer(packet.get(), max_packet_size_); int filled = 0; int row_pos = 0; // Position in the current row in bytes. int row_y = 0; // Current row. while (row_y < rect.height()) { // Prepare a message for sending out. if (!packet.get()) { packet.reset(new VideoPacket()); out = GetOutputBuffer(packet.get(), max_packet_size_); filled = 0; } if (row_y < rect.height()) { int bytes_to_copy = std::min(row_size - row_pos, max_packet_size_ - filled); memcpy(out + filled, in + row_pos, bytes_to_copy); row_pos += bytes_to_copy; filled += bytes_to_copy; // Jump to the next row when we've reached the end of the current row. if (row_pos == row_size) { row_pos = 0; in += stride; ++row_y; } } if (row_y == rect.height()) { DCHECK_EQ(row_pos, 0); packet->mutable_data()->resize(filled); packet->set_flags(packet->flags() | VideoPacket::LAST_PACKET); packet->set_capture_time_ms(frame->capture_time_ms()); packet->set_encode_time_ms( (base::Time::Now() - encode_start_time_).InMillisecondsRoundedUp()); if (!frame->dpi().is_zero()) { packet->mutable_format()->set_x_dpi(frame->dpi().x()); packet->mutable_format()->set_y_dpi(frame->dpi().y()); } if (last) packet->set_flags(packet->flags() | VideoPacket::LAST_PARTITION); } // If we have filled the current packet, then send it. if (filled == max_packet_size_ || row_y == rect.height()) { packet->mutable_data()->resize(filled); callback_.Run(packet.Pass()); } } } void VideoEncoderVerbatim::PrepareUpdateStart(const webrtc::DesktopFrame* frame, const webrtc::DesktopRect& rect, VideoPacket* packet) { packet->set_flags(packet->flags() | VideoPacket::FIRST_PACKET); VideoPacketFormat* format = packet->mutable_format(); format->set_x(rect.left()); format->set_y(rect.top()); format->set_width(rect.width()); format->set_height(rect.height()); format->set_encoding(VideoPacketFormat::ENCODING_VERBATIM); if (frame->size().equals(screen_size_)) { screen_size_ = frame->size(); format->set_screen_width(screen_size_.width()); format->set_screen_height(screen_size_.height()); } } uint8* VideoEncoderVerbatim::GetOutputBuffer(VideoPacket* packet, size_t size) { packet->mutable_data()->resize(size); return reinterpret_cast(string_as_array(packet->mutable_data())); } } // namespace remoting