blob: 4680362a02d20eb75094e2ab7b91ca90d3f6a2a5 [file] [log] [blame]
// 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<VideoPacket> 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<uint8*>(string_as_array(packet->mutable_data()));
}
} // namespace remoting