// 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/base/util.h" #include <math.h> #include "base/logging.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" #include "media/base/video_frame.h" #include "media/base/yuv_convert.h" #include "third_party/libyuv/include/libyuv/convert.h" #include "third_party/webrtc/modules/desktop_capture/desktop_region.h" using media::VideoFrame; namespace remoting { enum { kBytesPerPixelRGB32 = 4 }; // Do not write LOG messages in this routine since it is called from within // our LOG message handler. Bad things will happen. std::string GetTimestampString() { base::Time t = base::Time::NowFromSystemTime(); base::Time::Exploded tex; t.LocalExplode(&tex); return base::StringPrintf("%02d%02d/%02d%02d%02d:", tex.month, tex.day_of_month, tex.hour, tex.minute, tex.second); } int CalculateRGBOffset(int x, int y, int stride) { return stride * y + kBytesPerPixelRGB32 * x; } int CalculateYOffset(int x, int y, int stride) { DCHECK(((x & 1) == 0) && ((y & 1) == 0)); return stride * y + x; } int CalculateUVOffset(int x, int y, int stride) { DCHECK(((x & 1) == 0) && ((y & 1) == 0)); return stride * y / 2 + x / 2; } void ConvertRGB32ToYUVWithRect(const uint8* rgb_plane, uint8* y_plane, uint8* u_plane, uint8* v_plane, int x, int y, int width, int height, int rgb_stride, int y_stride, int uv_stride) { int rgb_offset = CalculateRGBOffset(x, y, rgb_stride); int y_offset = CalculateYOffset(x, y, y_stride); int uv_offset = CalculateUVOffset(x, y, uv_stride);; libyuv::ARGBToI420(rgb_plane + rgb_offset, rgb_stride, y_plane + y_offset, y_stride, u_plane + uv_offset, uv_stride, v_plane + uv_offset, uv_stride, width, height); } void ConvertAndScaleYUVToRGB32Rect( const uint8* source_yplane, const uint8* source_uplane, const uint8* source_vplane, int source_ystride, int source_uvstride, const webrtc::DesktopSize& source_size, const webrtc::DesktopRect& source_buffer_rect, uint8* dest_buffer, int dest_stride, const webrtc::DesktopSize& dest_size, const webrtc::DesktopRect& dest_buffer_rect, const webrtc::DesktopRect& dest_rect) { // N.B. It is caller's responsibility to check if strides are large enough. We // cannot do it here anyway. DCHECK(DoesRectContain(webrtc::DesktopRect::MakeSize(source_size), source_buffer_rect)); DCHECK(DoesRectContain(webrtc::DesktopRect::MakeSize(dest_size), dest_buffer_rect)); DCHECK(DoesRectContain(dest_buffer_rect, dest_rect)); DCHECK(DoesRectContain(ScaleRect(source_buffer_rect, source_size, dest_size), dest_rect)); // If the source and/or destination buffers don't start at (0, 0) // offset the pointers to pretend we have complete buffers. int y_offset = - CalculateYOffset(source_buffer_rect.left(), source_buffer_rect.top(), source_ystride); int uv_offset = - CalculateUVOffset(source_buffer_rect.left(), source_buffer_rect.top(), source_uvstride); int rgb_offset = - CalculateRGBOffset(dest_buffer_rect.left(), dest_buffer_rect.top(), dest_stride); // See if scaling is needed. if (source_size.equals(dest_size)) { // Calculate the inner rectangle that can be copied by the optimized // libyuv::I420ToARGB(). webrtc::DesktopRect inner_rect = webrtc::DesktopRect::MakeLTRB(RoundToTwosMultiple(dest_rect.left() + 1), RoundToTwosMultiple(dest_rect.top() + 1), dest_rect.right(), dest_rect.bottom()); // Offset pointers to point to the top left corner of the inner rectangle. y_offset += CalculateYOffset(inner_rect.left(), inner_rect.top(), source_ystride); uv_offset += CalculateUVOffset(inner_rect.left(), inner_rect.top(), source_uvstride); rgb_offset += CalculateRGBOffset(inner_rect.left(), inner_rect.top(), dest_stride); libyuv::I420ToARGB(source_yplane + y_offset, source_ystride, source_uplane + uv_offset, source_uvstride, source_vplane + uv_offset, source_uvstride, dest_buffer + rgb_offset, dest_stride, inner_rect.width(), inner_rect.height()); // Now see if some pixels weren't copied due to alignment. if (!dest_rect.equals(inner_rect)) { webrtc::DesktopRect outer_rect = webrtc::DesktopRect::MakeLTRB(RoundToTwosMultiple(dest_rect.left()), RoundToTwosMultiple(dest_rect.top()), dest_rect.right(), dest_rect.bottom()); webrtc::DesktopVector offset(outer_rect.left() - inner_rect.left(), outer_rect.top() - inner_rect.top()); // Offset the pointers to point to the top left corner of the outer // rectangle. y_offset += CalculateYOffset(offset.x(), offset.y(), source_ystride); uv_offset += CalculateUVOffset(offset.x(), offset.y(), source_uvstride); rgb_offset += CalculateRGBOffset(offset.x(), offset.y(), dest_stride); // Draw unaligned edges. webrtc::DesktopRegion edges(dest_rect); edges.Subtract(inner_rect); for (webrtc::DesktopRegion::Iterator i(edges); !i.IsAtEnd(); i.Advance()) { webrtc::DesktopRect rect = i.rect(); rect.Translate(-outer_rect.left(), -outer_rect.top()); media::ScaleYUVToRGB32WithRect(source_yplane + y_offset, source_uplane + uv_offset, source_vplane + uv_offset, dest_buffer + rgb_offset, source_size.width(), source_size.height(), dest_size.width(), dest_size.height(), rect.left(), rect.top(), rect.right(), rect.bottom(), source_ystride, source_uvstride, dest_stride); } } } else { media::ScaleYUVToRGB32WithRect(source_yplane + y_offset, source_uplane + uv_offset, source_vplane + uv_offset, dest_buffer + rgb_offset, source_size.width(), source_size.height(), dest_size.width(), dest_size.height(), dest_rect.left(), dest_rect.top(), dest_rect.right(), dest_rect.bottom(), source_ystride, source_uvstride, dest_stride); } } int RoundToTwosMultiple(int x) { return x & (~1); } webrtc::DesktopRect AlignRect(const webrtc::DesktopRect& rect) { int x = RoundToTwosMultiple(rect.left()); int y = RoundToTwosMultiple(rect.top()); int right = RoundToTwosMultiple(rect.right() + 1); int bottom = RoundToTwosMultiple(rect.bottom() + 1); return webrtc::DesktopRect::MakeLTRB(x, y, right, bottom); } webrtc::DesktopRect ScaleRect(const webrtc::DesktopRect& rect, const webrtc::DesktopSize& in_size, const webrtc::DesktopSize& out_size) { int left = (rect.left() * out_size.width()) / in_size.width(); int top = (rect.top() * out_size.height()) / in_size.height(); int right = (rect.right() * out_size.width() + in_size.width() - 1) / in_size.width(); int bottom = (rect.bottom() * out_size.height() + in_size.height() - 1) / in_size.height(); return webrtc::DesktopRect::MakeLTRB(left, top, right, bottom); } void CopyRGB32Rect(const uint8* source_buffer, int source_stride, const webrtc::DesktopRect& source_buffer_rect, uint8* dest_buffer, int dest_stride, const webrtc::DesktopRect& dest_buffer_rect, const webrtc::DesktopRect& dest_rect) { DCHECK(DoesRectContain(dest_buffer_rect, dest_rect)); DCHECK(DoesRectContain(source_buffer_rect, dest_rect)); // Get the address of the starting point. source_buffer += CalculateRGBOffset( dest_rect.left() - source_buffer_rect.left(), dest_rect.top() - source_buffer_rect.top(), source_stride); dest_buffer += CalculateRGBOffset( dest_rect.left() - dest_buffer_rect.left(), dest_rect.top() - dest_buffer_rect.top(), source_stride); // Copy pixels in the rectangle line by line. const int bytes_per_line = kBytesPerPixelRGB32 * dest_rect.width(); for (int i = 0 ; i < dest_rect.height(); ++i) { memcpy(dest_buffer, source_buffer, bytes_per_line); source_buffer += source_stride; dest_buffer += dest_stride; } } std::string ReplaceLfByCrLf(const std::string& in) { std::string out; out.resize(2 * in.size()); char* out_p_begin = &out[0]; char* out_p = out_p_begin; const char* in_p_begin = &in[0]; const char* in_p_end = &in[in.size()]; for (const char* in_p = in_p_begin; in_p < in_p_end; ++in_p) { char c = *in_p; if (c == '\n') { *out_p++ = '\r'; } *out_p++ = c; } out.resize(out_p - out_p_begin); return out; } std::string ReplaceCrLfByLf(const std::string& in) { std::string out; out.resize(in.size()); char* out_p_begin = &out[0]; char* out_p = out_p_begin; const char* in_p_begin = &in[0]; const char* in_p_end = &in[in.size()]; for (const char* in_p = in_p_begin; in_p < in_p_end; ++in_p) { char c = *in_p; if ((c == '\r') && (in_p + 1 < in_p_end) && (*(in_p + 1) == '\n')) { *out_p++ = '\n'; ++in_p; } else { *out_p++ = c; } } out.resize(out_p - out_p_begin); return out; } bool StringIsUtf8(const char* data, size_t length) { const char* ptr = data; const char* ptr_end = data + length; while (ptr != ptr_end) { if ((*ptr & 0x80) == 0) { // Single-byte symbol. ++ptr; } else if ((*ptr & 0xc0) == 0x80 || (*ptr & 0xfe) == 0xfe) { // Invalid first byte. return false; } else { // First byte of a multi-byte symbol. The bits from 2 to 6 are the count // of continuation bytes (up to 5 of them). for (char first = *ptr << 1; first & 0x80; first <<= 1) { ++ptr; // Missing continuation byte. if (ptr == ptr_end) return false; // Invalid continuation byte. if ((*ptr & 0xc0) != 0x80) return false; } ++ptr; } } return true; } bool DoesRectContain(const webrtc::DesktopRect& a, const webrtc::DesktopRect& b) { webrtc::DesktopRect intersection(a); intersection.IntersectWith(b); return intersection.equals(b); } } // namespace remoting