[go: up one dir, main page]

media/gpu/V4L2VideoDecodeAccelerator: Raise PLATFORM_FAILURE if the resolution is unsupported

The maximum resolution supported by guado is 1080p in video decoding. HW decoder
on guado (V4L2VideoDecoder) won't raise an error when the initial resolution
(e.g. 2K video) is unsupported. It causes chrome to not fallback and make 2k
video decoding undoable. This CL adds workaround to V4L2VideoDecoder for this
issue. It parses every vp8 encoded frame, checks the resolution on key frame and
invokes NotifyError() if the resolution is unsupported.

(cherry picked from commit aa95d92bdf80bcfa94dcfce56251c0e3035d4721)

Bug: b:131366944
Test: run video_decode_accelerator_unittest on guado with 2K video file and get NotifyError
Test: 2K video can be seen on guado
Change-Id: Ic1ba2fa4a281dee6953d1696f991681217364f2b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1637001
Reviewed-by: Alexandre Courbot <acourbot@chromium.org>
Commit-Queue: Hirokazu Honda <hiroh@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#665508}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1650543
Reviewed-by: Hirokazu Honda <hiroh@chromium.org>
Cr-Commit-Position: refs/branch-heads/3809@{#205}
Cr-Branched-From: d82dec1a818f378c464ba307ddd9c92133eac355-refs/heads/master@{#665002}
diff --git a/media/gpu/v4l2/BUILD.gn b/media/gpu/v4l2/BUILD.gn
index d756ef22..906be540 100644
--- a/media/gpu/v4l2/BUILD.gn
+++ b/media/gpu/v4l2/BUILD.gn
@@ -39,6 +39,8 @@
     "v4l2_image_processor.h",
     "v4l2_slice_video_decode_accelerator.cc",
     "v4l2_slice_video_decode_accelerator.h",
+    "v4l2_stateful_workaround.cc",
+    "v4l2_stateful_workaround.h",
     "v4l2_video_decode_accelerator.cc",
     "v4l2_video_decode_accelerator.h",
     "v4l2_video_encode_accelerator.cc",
diff --git a/media/gpu/v4l2/v4l2_stateful_workaround.cc b/media/gpu/v4l2/v4l2_stateful_workaround.cc
new file mode 100644
index 0000000..221856a
--- /dev/null
+++ b/media/gpu/v4l2/v4l2_stateful_workaround.cc
@@ -0,0 +1,131 @@
+// Copyright 2019 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 "media/gpu/v4l2/v4l2_stateful_workaround.h"
+
+#include <string.h>
+
+#include <linux/videodev2.h>
+
+#include "base/containers/small_map.h"
+#include "base/memory/ptr_util.h"
+#include "media/gpu/macros.h"
+#include "media/parsers/vp8_parser.h"
+#include "media/video/video_decode_accelerator.h"
+
+namespace media {
+
+// If the given resolution is not supported by the driver, some IOCTL must
+// return some error code (e.g. EIO). However, there is a driver that doesn't
+// follow this specification, for example go2001. This will be called before
+// a bitstream to the driver in the driver. This parses the bitstream, gets
+// its resolution and compares with the supported resolution.
+// Returns true if the resolution is supported or this workaround is
+// unnecessary. Otherwise return false.
+// This class is currently created only on guado when codec is VP8.
+// TODO(crbug.com/968945): Check this workaround is necessary for other codecs
+// and other devices.
+class SupportResolutionChecker : public V4L2StatefulWorkaround {
+ public:
+  static std::unique_ptr<V4L2StatefulWorkaround> CreateIfNeeded(
+      V4L2Device::Type device_type,
+      VideoCodecProfile profile);
+  ~SupportResolutionChecker() override = default;
+
+  Result Apply(const uint8_t* data, size_t size, size_t* endpos) override;
+
+ private:
+  using SupportedProfileMap = base::small_map<
+      std::map<VideoCodecProfile, VideoDecodeAccelerator::SupportedProfile>>;
+
+  SupportResolutionChecker(SupportedProfileMap supported_profile_map)
+      : supported_profile_map_(std::move(supported_profile_map)),
+        vp8_parser_(std::make_unique<Vp8Parser>()) {}
+
+  SupportedProfileMap supported_profile_map_;
+  const std::unique_ptr<Vp8Parser> vp8_parser_;
+};
+
+std::unique_ptr<V4L2StatefulWorkaround>
+SupportResolutionChecker::CreateIfNeeded(V4L2Device::Type device_type,
+                                         VideoCodecProfile profile) {
+  scoped_refptr<V4L2Device> device = V4L2Device::Create();
+  if (device_type == V4L2Device::Type::kDecoder && profile >= VP8PROFILE_MIN &&
+      profile <= VP8PROFILE_MAX) {
+    if (!device->Open(V4L2Device::Type::kDecoder, V4L2_PIX_FMT_VP8)) {
+      VPLOGF(1) << "Failed to open device for profile: " << profile
+                << " fourcc: " << FourccToString(V4L2_PIX_FMT_VP8);
+      return nullptr;
+    }
+
+    // Get the driver name.
+    struct v4l2_capability caps;
+    if (device->Ioctl(VIDIOC_QUERYCAP, &caps) != 0) {
+      VPLOGF(1) << "ioctl() failed: VIDIOC_QUERYCAP"
+                << ", caps check failed: 0x" << std::hex << caps.capabilities;
+      return nullptr;
+    }
+    constexpr char go2001[] = "go2001";
+    if (strcmp(reinterpret_cast<const char*>(caps.driver), go2001))
+      return nullptr;
+  }
+
+  constexpr uint32_t supported_input_fourccs[] = {
+      V4L2_PIX_FMT_VP8,
+  };
+  auto supported_profiles = device->GetSupportedDecodeProfiles(
+      base::size(supported_input_fourccs), supported_input_fourccs);
+  SupportedProfileMap supported_profile_map;
+  for (const auto& profile : supported_profiles)
+    supported_profile_map[profile.profile] = profile;
+
+  VLOGF(2) << "Create SupportResolutionChecker workaround";
+  return base::WrapUnique(
+      new SupportResolutionChecker(std::move(supported_profile_map)));
+}
+
+V4L2StatefulWorkaround::Result SupportResolutionChecker::Apply(
+    const uint8_t* data,
+    size_t size,
+    size_t* endpos) {
+  Vp8FrameHeader fhdr;
+  vp8_parser_->ParseFrame(data, size, &fhdr);
+  if (fhdr.IsKeyframe()) {
+    DCHECK(supported_profile_map_.find(VP8PROFILE_ANY) !=
+           supported_profile_map_.end());
+    const auto& supported_profile = supported_profile_map_[VP8PROFILE_ANY];
+    const auto& min_resolution = supported_profile.min_resolution;
+    const auto& max_resolution = supported_profile.max_resolution;
+    const gfx::Rect current_resolution(fhdr.width, fhdr.height);
+    if (!gfx::Rect(max_resolution).Contains(current_resolution) ||
+        !(current_resolution).Contains(gfx::Rect(min_resolution))) {
+      VLOGF(1) << "Resolution is unsupported: "
+               << current_resolution.size().ToString()
+               << ", min supported resolution: " << min_resolution.ToString()
+               << ", max supported resolution: " << max_resolution.ToString();
+      return Result::NotifyError;
+    }
+  }
+  return Result::Success;
+}
+
+std::vector<std::unique_ptr<V4L2StatefulWorkaround>>
+CreateV4L2StatefulWorkarounds(V4L2Device::Type device_type,
+                              VideoCodecProfile profile) {
+  using CreateWorkaroundFuncType = std::unique_ptr<V4L2StatefulWorkaround> (*)(
+      V4L2Device::Type device_type, VideoCodecProfile profile);
+  const CreateWorkaroundFuncType kWorkaroundFactoryFunction[] = {
+      &SupportResolutionChecker::CreateIfNeeded,
+  };
+
+  std::vector<std::unique_ptr<V4L2StatefulWorkaround>> workarounds;
+  for (const auto func : kWorkaroundFactoryFunction) {
+    auto vw = func(device_type, profile);
+    if (vw)
+      workarounds.push_back(std::move(vw));
+  }
+  return workarounds;
+}
+
+}  // namespace media
diff --git a/media/gpu/v4l2/v4l2_stateful_workaround.h b/media/gpu/v4l2/v4l2_stateful_workaround.h
new file mode 100644
index 0000000..c0a746a
--- /dev/null
+++ b/media/gpu/v4l2/v4l2_stateful_workaround.h
@@ -0,0 +1,42 @@
+// Copyright 2019 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.
+
+#ifndef MEDIA_GPU_V4L2_V4L2_STATEFUL_WORKAROUND_H_
+#define MEDIA_GPU_V4L2_V4L2_STATEFUL_WORKAROUND_H_
+
+#include <memory>
+#include <vector>
+
+#include "media/base/video_types.h"
+#include "media/gpu/v4l2/v4l2_device.h"
+
+namespace media {
+
+class V4L2StatefulWorkaround {
+ public:
+  enum class Result {
+    Success,      // The workaround is successfully applied.
+    NotifyError,  // The caller must notify an error for Chrome. For example,
+                  // VDA will call NotifyError() if this is returned.
+  };
+
+  virtual ~V4L2StatefulWorkaround() = default;
+
+  // Apply the workaround.
+  virtual Result Apply(const uint8_t* data, size_t size, size_t* endpos) = 0;
+
+ protected:
+  V4L2StatefulWorkaround() = default;
+
+  DISALLOW_COPY_AND_ASSIGN(V4L2StatefulWorkaround);
+};
+
+// Create necessary workarounds on the device for |device_type| and |profile|.
+std::vector<std::unique_ptr<V4L2StatefulWorkaround>>
+CreateV4L2StatefulWorkarounds(V4L2Device::Type device_type,
+                              VideoCodecProfile profile);
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_V4L2_V4L2_STATEFUL_WORKAROUND_H_
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
index 7e55c00..7bd8ca6 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
@@ -34,6 +34,7 @@
 #include "media/gpu/image_processor_factory.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/v4l2/v4l2_image_processor.h"
+#include "media/gpu/v4l2/v4l2_stateful_workaround.h"
 #include "media/video/h264_parser.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gl/gl_context.h"
@@ -317,6 +318,9 @@
     return false;
   }
 
+  workarounds_ =
+      CreateV4L2StatefulWorkarounds(V4L2Device::Type::kDecoder, config.profile);
+
   output_mode_ = config.output_mode;
 
   input_queue_ = device_->GetQueue(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
@@ -981,6 +985,14 @@
                                                       size_t* endpos) {
   DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
 
+  for (auto& workaround : workarounds_) {
+    auto result = workaround->Apply(data, size, endpos);
+    if (result == V4L2StatefulWorkaround::Result::NotifyError) {
+      NOTIFY_ERROR(PLATFORM_FAILURE);
+      return false;
+    }
+  }
+
   if (video_profile_ >= H264PROFILE_MIN && video_profile_ <= H264PROFILE_MAX) {
     // For H264, we need to feed HW one frame at a time.  This is going to take
     // some parsing of our input stream.
@@ -1930,6 +1942,7 @@
   output_queue_ = nullptr;
 
   decoder_h264_parser_ = nullptr;
+  workarounds_.clear();
 
   base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
       this);
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.h b/media/gpu/v4l2/v4l2_video_decode_accelerator.h
index c66c1bd9..e42749a 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.h
@@ -47,6 +47,7 @@
 namespace media {
 
 class H264Parser;
+class V4L2StatefulWorkaround;
 
 // This class handles video accelerators directly through a V4L2 device exported
 // by the hardware blocks.
@@ -507,6 +508,11 @@
   // For H264 decode, hardware requires that we send it frame-sized chunks.
   // We'll need to parse the stream.
   std::unique_ptr<H264Parser> decoder_h264_parser_;
+
+  // Workaround for V4L2VideoDecodeAccelerator. This is created only if some
+  // workaround is necessary for the V4L2VideoDecodeAccelerator.
+  std::vector<std::unique_ptr<V4L2StatefulWorkaround>> workarounds_;
+
   // Set if the decoder has a pending incomplete frame in an input buffer.
   bool decoder_partial_frame_pending_;