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/base/util.h"
6
7#include <math.h>
8
9#include "base/logging.h"
10#include "base/strings/stringprintf.h"
11#include "base/time/time.h"
12#include "media/base/video_frame.h"
13#include "media/base/yuv_convert.h"
14#include "third_party/libyuv/include/libyuv/convert.h"
15#include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
16
17using media::VideoFrame;
18
19namespace remoting {
20
21enum { kBytesPerPixelRGB32 = 4 };
22
23// Do not write LOG messages in this routine since it is called from within
24// our LOG message handler. Bad things will happen.
25std::string GetTimestampString() {
26  base::Time t = base::Time::NowFromSystemTime();
27  base::Time::Exploded tex;
28  t.LocalExplode(&tex);
29  return base::StringPrintf("%02d%02d/%02d%02d%02d:",
30                            tex.month, tex.day_of_month,
31                            tex.hour, tex.minute, tex.second);
32}
33
34int CalculateRGBOffset(int x, int y, int stride) {
35  return stride * y + kBytesPerPixelRGB32 * x;
36}
37
38int CalculateYOffset(int x, int y, int stride) {
39  DCHECK(((x & 1) == 0) && ((y & 1) == 0));
40  return stride * y + x;
41}
42
43int CalculateUVOffset(int x, int y, int stride) {
44  DCHECK(((x & 1) == 0) && ((y & 1) == 0));
45  return stride * y / 2 + x / 2;
46}
47
48void ConvertAndScaleYUVToRGB32Rect(
49    const uint8* source_yplane,
50    const uint8* source_uplane,
51    const uint8* source_vplane,
52    int source_ystride,
53    int source_uvstride,
54    const webrtc::DesktopSize& source_size,
55    const webrtc::DesktopRect& source_buffer_rect,
56    uint8* dest_buffer,
57    int dest_stride,
58    const webrtc::DesktopSize& dest_size,
59    const webrtc::DesktopRect& dest_buffer_rect,
60    const webrtc::DesktopRect& dest_rect) {
61  // N.B. It is caller's responsibility to check if strides are large enough. We
62  // cannot do it here anyway.
63  DCHECK(DoesRectContain(webrtc::DesktopRect::MakeSize(source_size),
64                         source_buffer_rect));
65  DCHECK(DoesRectContain(webrtc::DesktopRect::MakeSize(dest_size),
66                         dest_buffer_rect));
67  DCHECK(DoesRectContain(dest_buffer_rect, dest_rect));
68  DCHECK(DoesRectContain(ScaleRect(source_buffer_rect, source_size, dest_size),
69                         dest_rect));
70
71  // If the source and/or destination buffers don't start at (0, 0)
72  // offset the pointers to pretend we have complete buffers.
73  int y_offset = - CalculateYOffset(source_buffer_rect.left(),
74                                    source_buffer_rect.top(),
75                                    source_ystride);
76  int uv_offset = - CalculateUVOffset(source_buffer_rect.left(),
77                                      source_buffer_rect.top(),
78                                      source_uvstride);
79  int rgb_offset = - CalculateRGBOffset(dest_buffer_rect.left(),
80                                        dest_buffer_rect.top(),
81                                        dest_stride);
82
83  // See if scaling is needed.
84  if (source_size.equals(dest_size)) {
85    // Calculate the inner rectangle that can be copied by the optimized
86    // libyuv::I420ToARGB().
87    webrtc::DesktopRect inner_rect =
88        webrtc::DesktopRect::MakeLTRB(RoundToTwosMultiple(dest_rect.left() + 1),
89                                      RoundToTwosMultiple(dest_rect.top() + 1),
90                                      dest_rect.right(), dest_rect.bottom());
91
92    // Offset pointers to point to the top left corner of the inner rectangle.
93    y_offset += CalculateYOffset(inner_rect.left(), inner_rect.top(),
94                                 source_ystride);
95    uv_offset += CalculateUVOffset(inner_rect.left(), inner_rect.top(),
96                                   source_uvstride);
97    rgb_offset += CalculateRGBOffset(inner_rect.left(), inner_rect.top(),
98                                     dest_stride);
99
100    libyuv::I420ToARGB(source_yplane + y_offset, source_ystride,
101                       source_uplane + uv_offset, source_uvstride,
102                       source_vplane + uv_offset, source_uvstride,
103                       dest_buffer + rgb_offset, dest_stride,
104                       inner_rect.width(), inner_rect.height());
105
106    // Now see if some pixels weren't copied due to alignment.
107    if (!dest_rect.equals(inner_rect)) {
108      webrtc::DesktopRect outer_rect =
109          webrtc::DesktopRect::MakeLTRB(RoundToTwosMultiple(dest_rect.left()),
110                                        RoundToTwosMultiple(dest_rect.top()),
111                                        dest_rect.right(), dest_rect.bottom());
112
113      webrtc::DesktopVector offset(outer_rect.left() - inner_rect.left(),
114                                   outer_rect.top() - inner_rect.top());
115
116      // Offset the pointers to point to the top left corner of the outer
117      // rectangle.
118      y_offset += CalculateYOffset(offset.x(), offset.y(), source_ystride);
119      uv_offset += CalculateUVOffset(offset.x(), offset.y(), source_uvstride);
120      rgb_offset += CalculateRGBOffset(offset.x(), offset.y(), dest_stride);
121
122      // Draw unaligned edges.
123      webrtc::DesktopRegion edges(dest_rect);
124      edges.Subtract(inner_rect);
125      for (webrtc::DesktopRegion::Iterator i(edges); !i.IsAtEnd();
126           i.Advance()) {
127        webrtc::DesktopRect rect = i.rect();
128        rect.Translate(-outer_rect.left(), -outer_rect.top());
129        media::ScaleYUVToRGB32WithRect(source_yplane + y_offset,
130                                       source_uplane + uv_offset,
131                                       source_vplane + uv_offset,
132                                       dest_buffer + rgb_offset,
133                                       source_size.width(),
134                                       source_size.height(),
135                                       dest_size.width(),
136                                       dest_size.height(),
137                                       rect.left(),
138                                       rect.top(),
139                                       rect.right(),
140                                       rect.bottom(),
141                                       source_ystride,
142                                       source_uvstride,
143                                       dest_stride);
144      }
145    }
146  } else {
147    media::ScaleYUVToRGB32WithRect(source_yplane + y_offset,
148                                   source_uplane + uv_offset,
149                                   source_vplane + uv_offset,
150                                   dest_buffer + rgb_offset,
151                                   source_size.width(),
152                                   source_size.height(),
153                                   dest_size.width(),
154                                   dest_size.height(),
155                                   dest_rect.left(),
156                                   dest_rect.top(),
157                                   dest_rect.right(),
158                                   dest_rect.bottom(),
159                                   source_ystride,
160                                   source_uvstride,
161                                   dest_stride);
162  }
163}
164
165int RoundToTwosMultiple(int x) {
166  return x & (~1);
167}
168
169webrtc::DesktopRect AlignRect(const webrtc::DesktopRect& rect) {
170  int x = RoundToTwosMultiple(rect.left());
171  int y = RoundToTwosMultiple(rect.top());
172  int right = RoundToTwosMultiple(rect.right() + 1);
173  int bottom = RoundToTwosMultiple(rect.bottom() + 1);
174  return webrtc::DesktopRect::MakeLTRB(x, y, right, bottom);
175}
176
177webrtc::DesktopRect ScaleRect(const webrtc::DesktopRect& rect,
178                              const webrtc::DesktopSize& in_size,
179                              const webrtc::DesktopSize& out_size) {
180  int left = (rect.left() * out_size.width()) / in_size.width();
181  int top = (rect.top() * out_size.height()) / in_size.height();
182  int right = (rect.right() * out_size.width() + in_size.width() - 1) /
183      in_size.width();
184  int bottom = (rect.bottom() * out_size.height() + in_size.height() - 1) /
185      in_size.height();
186  return webrtc::DesktopRect::MakeLTRB(left, top, right, bottom);
187}
188
189void CopyRGB32Rect(const uint8* source_buffer,
190                   int source_stride,
191                   const webrtc::DesktopRect& source_buffer_rect,
192                   uint8* dest_buffer,
193                   int dest_stride,
194                   const webrtc::DesktopRect& dest_buffer_rect,
195                   const webrtc::DesktopRect& dest_rect) {
196  DCHECK(DoesRectContain(dest_buffer_rect, dest_rect));
197  DCHECK(DoesRectContain(source_buffer_rect, dest_rect));
198
199  // Get the address of the starting point.
200  source_buffer += CalculateRGBOffset(
201      dest_rect.left() - source_buffer_rect.left(),
202      dest_rect.top() - source_buffer_rect.top(),
203      source_stride);
204  dest_buffer += CalculateRGBOffset(
205      dest_rect.left() - dest_buffer_rect.left(),
206      dest_rect.top() - dest_buffer_rect.top(),
207      source_stride);
208
209  // Copy pixels in the rectangle line by line.
210  const int bytes_per_line = kBytesPerPixelRGB32 * dest_rect.width();
211  for (int i = 0 ; i < dest_rect.height(); ++i) {
212    memcpy(dest_buffer, source_buffer, bytes_per_line);
213    source_buffer += source_stride;
214    dest_buffer += dest_stride;
215  }
216}
217
218std::string ReplaceLfByCrLf(const std::string& in) {
219  std::string out;
220  out.resize(2 * in.size());
221  char* out_p_begin = &out[0];
222  char* out_p = out_p_begin;
223  const char* in_p_begin = &in[0];
224  const char* in_p_end = &in[in.size()];
225  for (const char* in_p = in_p_begin; in_p < in_p_end; ++in_p) {
226    char c = *in_p;
227    if (c == '\n') {
228      *out_p++ = '\r';
229    }
230    *out_p++ = c;
231  }
232  out.resize(out_p - out_p_begin);
233  return out;
234}
235
236std::string ReplaceCrLfByLf(const std::string& in) {
237  std::string out;
238  out.resize(in.size());
239  char* out_p_begin = &out[0];
240  char* out_p = out_p_begin;
241  const char* in_p_begin = &in[0];
242  const char* in_p_end = &in[in.size()];
243  for (const char* in_p = in_p_begin; in_p < in_p_end; ++in_p) {
244    char c = *in_p;
245    if ((c == '\r') && (in_p + 1 < in_p_end) && (*(in_p + 1) == '\n')) {
246      *out_p++ = '\n';
247      ++in_p;
248    } else {
249      *out_p++ = c;
250    }
251  }
252  out.resize(out_p - out_p_begin);
253  return out;
254}
255
256bool StringIsUtf8(const char* data, size_t length) {
257  const char* ptr = data;
258  const char* ptr_end = data + length;
259  while (ptr != ptr_end) {
260    if ((*ptr & 0x80) == 0) {
261      // Single-byte symbol.
262      ++ptr;
263    } else if ((*ptr & 0xc0) == 0x80 || (*ptr & 0xfe) == 0xfe) {
264      // Invalid first byte.
265      return false;
266    } else {
267      // First byte of a multi-byte symbol. The bits from 2 to 6 are the count
268      // of continuation bytes (up to 5 of them).
269      for (char first = *ptr << 1; first & 0x80; first <<= 1) {
270        ++ptr;
271
272        // Missing continuation byte.
273        if (ptr == ptr_end)
274          return false;
275
276        // Invalid continuation byte.
277        if ((*ptr & 0xc0) != 0x80)
278          return false;
279      }
280
281      ++ptr;
282    }
283  }
284
285  return true;
286}
287
288bool DoesRectContain(const webrtc::DesktopRect& a,
289                     const webrtc::DesktopRect& b) {
290  webrtc::DesktopRect intersection(a);
291  intersection.IntersectWith(b);
292  return intersection.equals(b);
293}
294
295}  // namespace remoting
296