video_decoder_vpx.cc 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. // Copyright 2013 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. #include "remoting/codec/video_decoder_vpx.h"
  5. #include <math.h>
  6. #include <stdint.h>
  7. #include "base/check_op.h"
  8. #include "base/logging.h"
  9. #include "base/memory/ptr_util.h"
  10. #include "remoting/base/util.h"
  11. #include "remoting/proto/video.pb.h"
  12. #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
  13. #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"
  14. #include "third_party/libyuv/include/libyuv/convert_argb.h"
  15. #include "third_party/libyuv/include/libyuv/convert_from.h"
  16. #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
  17. #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
  18. #include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
  19. namespace remoting {
  20. namespace {
  21. void RenderRect(vpx_image_t* image,
  22. webrtc::DesktopRect rect,
  23. VideoDecoder::PixelFormat pixel_format,
  24. webrtc::DesktopFrame* frame) {
  25. auto yuv_to_rgb_function = libyuv::I420ToARGB;
  26. int u_offset;
  27. int v_offset;
  28. switch (image->fmt) {
  29. case VPX_IMG_FMT_I420: {
  30. // Align position of the top left corner so that its coordinates are
  31. // always even.
  32. rect = webrtc::DesktopRect::MakeLTRB(rect.left() & ~1, rect.top() & ~1,
  33. rect.right(), rect.bottom());
  34. u_offset = rect.top() / 2 * image->stride[1] + rect.left() / 2;
  35. v_offset = rect.top() / 2 * image->stride[2] + rect.left() / 2;
  36. yuv_to_rgb_function = (pixel_format == VideoDecoder::PixelFormat::BGRA)
  37. ? libyuv::I420ToARGB
  38. : libyuv::I420ToABGR;
  39. break;
  40. }
  41. // VP8 only outputs I420 frames, but VP9 can also produce I444.
  42. case VPX_IMG_FMT_I444: {
  43. u_offset = rect.top() * image->stride[1] + rect.left();
  44. v_offset = rect.top() * image->stride[2] + rect.left();
  45. yuv_to_rgb_function = (pixel_format == VideoDecoder::PixelFormat::BGRA)
  46. ? libyuv::I444ToARGB
  47. : libyuv::I444ToABGR;
  48. break;
  49. }
  50. default: {
  51. LOG(ERROR) << "Unsupported image format:" << image->fmt;
  52. return;
  53. }
  54. }
  55. int y_offset = rect.top() * image->stride[0] + rect.left();
  56. uint8_t* image_data_ptr = frame->GetFrameDataAtPos(rect.top_left());
  57. yuv_to_rgb_function(image->planes[0] + y_offset, image->stride[0],
  58. image->planes[1] + u_offset, image->stride[1],
  59. image->planes[2] + v_offset, image->stride[2],
  60. image_data_ptr, frame->stride(), rect.width(),
  61. rect.height());
  62. }
  63. } // namespace
  64. // static
  65. std::unique_ptr<VideoDecoderVpx> VideoDecoderVpx::CreateForVP8() {
  66. return base::WrapUnique(new VideoDecoderVpx(vpx_codec_vp8_dx()));
  67. }
  68. // static
  69. std::unique_ptr<VideoDecoderVpx> VideoDecoderVpx::CreateForVP9() {
  70. return base::WrapUnique(new VideoDecoderVpx(vpx_codec_vp9_dx()));
  71. }
  72. VideoDecoderVpx::~VideoDecoderVpx() = default;
  73. void VideoDecoderVpx::SetPixelFormat(PixelFormat pixel_format) {
  74. pixel_format_ = pixel_format;
  75. }
  76. bool VideoDecoderVpx::DecodePacket(const VideoPacket& packet,
  77. webrtc::DesktopFrame* frame) {
  78. // Pass the packet to the codec to process.
  79. vpx_codec_err_t ret = vpx_codec_decode(
  80. codec_.get(), reinterpret_cast<const uint8_t*>(packet.data().data()),
  81. packet.data().size(), nullptr, 0);
  82. if (ret != VPX_CODEC_OK) {
  83. const char* error = vpx_codec_error(codec_.get());
  84. const char* error_detail = vpx_codec_error_detail(codec_.get());
  85. LOG(ERROR) << "Decoding failed:" << (error ? error : "(NULL)") << "\n"
  86. << "Details: " << (error_detail ? error_detail : "(NULL)");
  87. return false;
  88. }
  89. // Fetch the decoded video frame.
  90. vpx_codec_iter_t iter = nullptr;
  91. vpx_image_t* image = vpx_codec_get_frame(codec_.get(), &iter);
  92. if (!image) {
  93. LOG(ERROR) << "No video frame decoded.";
  94. return false;
  95. }
  96. if (!webrtc::DesktopSize(image->d_w, image->d_h).equals(frame->size())) {
  97. LOG(ERROR) << "Size of the encoded frame doesn't match size in the header.";
  98. return false;
  99. }
  100. // Determine which areas have been updated.
  101. webrtc::DesktopRegion* region = frame->mutable_updated_region();
  102. region->Clear();
  103. for (int i = 0; i < packet.dirty_rects_size(); ++i) {
  104. Rect proto_rect = packet.dirty_rects(i);
  105. webrtc::DesktopRect rect =
  106. webrtc::DesktopRect::MakeXYWH(proto_rect.x(), proto_rect.y(),
  107. proto_rect.width(), proto_rect.height());
  108. region->AddRect(rect);
  109. RenderRect(image, rect, pixel_format_, frame);
  110. }
  111. return true;
  112. }
  113. VideoDecoderVpx::VideoDecoderVpx(vpx_codec_iface_t* codec) {
  114. codec_.reset(new vpx_codec_ctx_t);
  115. vpx_codec_dec_cfg config;
  116. config.w = 0;
  117. config.h = 0;
  118. config.threads = 2;
  119. vpx_codec_err_t ret = vpx_codec_dec_init(codec_.get(), codec, &config, 0);
  120. CHECK_EQ(VPX_CODEC_OK, ret);
  121. }
  122. } // namespace remoting