1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "remoting/codec/video_encoder_verbatim.h"
6
7#include "base/logging.h"
8#include "base/stl_util.h"
9#include "remoting/base/util.h"
10#include "remoting/proto/video.pb.h"
11#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
12
13namespace remoting {
14
15static const int kPacketSize = 1024 * 1024;
16
17VideoEncoderVerbatim::VideoEncoderVerbatim()
18    : max_packet_size_(kPacketSize) {
19}
20
21void VideoEncoderVerbatim::SetMaxPacketSize(int size) {
22  max_packet_size_ = size;
23}
24
25VideoEncoderVerbatim::~VideoEncoderVerbatim() {
26}
27
28void VideoEncoderVerbatim::Encode(
29    const webrtc::DesktopFrame* frame,
30    const DataAvailableCallback& data_available_callback) {
31  callback_ = data_available_callback;
32  encode_start_time_ = base::Time::Now();
33
34  webrtc::DesktopRegion::Iterator iter(frame->updated_region());
35  while (!iter.IsAtEnd()) {
36    const webrtc::DesktopRect& rect = iter.rect();
37    iter.Advance();
38    EncodeRect(frame, rect, iter.IsAtEnd());
39  }
40
41  callback_.Reset();
42}
43
44void VideoEncoderVerbatim::EncodeRect(const webrtc::DesktopFrame* frame,
45                                      const webrtc::DesktopRect& rect,
46                                      bool last) {
47  CHECK(frame->data());
48  const int stride = frame->stride();
49  const int bytes_per_pixel = 4;
50  const int row_size = bytes_per_pixel * rect.width();
51
52  scoped_ptr<VideoPacket> packet(new VideoPacket());
53  PrepareUpdateStart(frame, rect, packet.get());
54  const uint8* in = frame->data() +
55      rect.top() * stride + rect.left() * bytes_per_pixel;
56  // TODO(hclam): Fill in the sequence number.
57  uint8* out = GetOutputBuffer(packet.get(), max_packet_size_);
58  int filled = 0;
59  int row_pos = 0;  // Position in the current row in bytes.
60  int row_y = 0;  // Current row.
61  while (row_y < rect.height()) {
62    // Prepare a message for sending out.
63    if (!packet.get()) {
64      packet.reset(new VideoPacket());
65      out = GetOutputBuffer(packet.get(), max_packet_size_);
66      filled = 0;
67    }
68
69    if (row_y < rect.height()) {
70      int bytes_to_copy =
71          std::min(row_size - row_pos, max_packet_size_ - filled);
72      memcpy(out + filled, in + row_pos, bytes_to_copy);
73      row_pos += bytes_to_copy;
74      filled += bytes_to_copy;
75
76      // Jump to the next row when we've reached the end of the current row.
77      if (row_pos == row_size) {
78        row_pos = 0;
79        in += stride;
80        ++row_y;
81      }
82    }
83
84    if (row_y == rect.height()) {
85      DCHECK_EQ(row_pos, 0);
86
87      packet->mutable_data()->resize(filled);
88      packet->set_flags(packet->flags() | VideoPacket::LAST_PACKET);
89
90      packet->set_capture_time_ms(frame->capture_time_ms());
91      packet->set_encode_time_ms(
92          (base::Time::Now() - encode_start_time_).InMillisecondsRoundedUp());
93      if (!frame->dpi().is_zero()) {
94        packet->mutable_format()->set_x_dpi(frame->dpi().x());
95        packet->mutable_format()->set_y_dpi(frame->dpi().y());
96      }
97      if (last)
98        packet->set_flags(packet->flags() | VideoPacket::LAST_PARTITION);
99    }
100
101    // If we have filled the current packet, then send it.
102    if (filled == max_packet_size_ || row_y == rect.height()) {
103      packet->mutable_data()->resize(filled);
104      callback_.Run(packet.Pass());
105    }
106  }
107}
108
109void VideoEncoderVerbatim::PrepareUpdateStart(const webrtc::DesktopFrame* frame,
110                                              const webrtc::DesktopRect& rect,
111                                              VideoPacket* packet) {
112  packet->set_flags(packet->flags() | VideoPacket::FIRST_PACKET);
113
114  VideoPacketFormat* format = packet->mutable_format();
115  format->set_x(rect.left());
116  format->set_y(rect.top());
117  format->set_width(rect.width());
118  format->set_height(rect.height());
119  format->set_encoding(VideoPacketFormat::ENCODING_VERBATIM);
120  if (frame->size().equals(screen_size_)) {
121    screen_size_ = frame->size();
122    format->set_screen_width(screen_size_.width());
123    format->set_screen_height(screen_size_.height());
124  }
125}
126
127uint8* VideoEncoderVerbatim::GetOutputBuffer(VideoPacket* packet, size_t size) {
128  packet->mutable_data()->resize(size);
129  return reinterpret_cast<uint8*>(string_as_array(packet->mutable_data()));
130}
131
132}  // namespace remoting
133