summaryrefslogtreecommitdiff
path: root/chromium/media/gpu/v4l2/v4l2_stateful_workaround.cc
blob: 8ce8798497a594e6483c28212caf22b534e23c91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// 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) {
  if (device_type != V4L2Device::Type::kDecoder || profile < VP8PROFILE_MIN ||
      profile > VP8PROFILE_MAX) {
    return nullptr;
  }

  scoped_refptr<V4L2Device> device = V4L2Device::Create();
  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