1 // Copyright 2014 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.
5 #include <libdrm/drm_fourcc.h>
6 #include <linux/videodev2.h>
8 #include "base/numerics/safe_conversions.h"
9 #include "content/common/gpu/media/generic_v4l2_device.h"
10 #if defined(ARCH_CPU_ARMEL)
11 #include "content/common/gpu/media/tegra_v4l2_device.h"
16 V4L2Device::V4L2Device(Type type
) : type_(type
) {
19 V4L2Device::~V4L2Device() {
23 scoped_refptr
<V4L2Device
> V4L2Device::Create(Type type
) {
24 DVLOG(3) << __PRETTY_FUNCTION__
;
26 scoped_refptr
<GenericV4L2Device
> generic_device(new GenericV4L2Device(type
));
27 if (generic_device
->Initialize())
28 return generic_device
;
30 #if defined(ARCH_CPU_ARMEL)
31 scoped_refptr
<TegraV4L2Device
> tegra_device(new TegraV4L2Device(type
));
32 if (tegra_device
->Initialize())
36 DVLOG(1) << "Failed to create V4L2Device";
37 return scoped_refptr
<V4L2Device
>();
41 media::VideoPixelFormat
V4L2Device::V4L2PixFmtToVideoPixelFormat(
44 case V4L2_PIX_FMT_NV12
:
45 case V4L2_PIX_FMT_NV12M
:
46 // MT21 is similar to NV21. We only need the information like number of
47 // planes, plane size, and bytes per row. We do not set the format of
48 // media::VideoFrame to be MT21. Pretend it is NV12 so we don't need to
49 // add PIXEL_FORMAT_MT21.
50 // TODO(wuchengli): find a better way to handle this.
51 case V4L2_PIX_FMT_MT21
:
52 return media::PIXEL_FORMAT_NV12
;
54 case V4L2_PIX_FMT_YUV420
:
55 case V4L2_PIX_FMT_YUV420M
:
56 return media::PIXEL_FORMAT_I420
;
58 case V4L2_PIX_FMT_RGB32
:
59 return media::PIXEL_FORMAT_ARGB
;
62 LOG(FATAL
) << "Add more cases as needed";
63 return media::PIXEL_FORMAT_UNKNOWN
;
68 uint32
V4L2Device::VideoPixelFormatToV4L2PixFmt(
69 media::VideoPixelFormat format
) {
71 case media::PIXEL_FORMAT_NV12
:
72 return V4L2_PIX_FMT_NV12M
;
74 case media::PIXEL_FORMAT_I420
:
75 return V4L2_PIX_FMT_YUV420M
;
78 LOG(FATAL
) << "Add more cases as needed";
84 uint32
V4L2Device::VideoCodecProfileToV4L2PixFmt(
85 media::VideoCodecProfile profile
,
87 if (profile
>= media::H264PROFILE_MIN
&&
88 profile
<= media::H264PROFILE_MAX
) {
90 return V4L2_PIX_FMT_H264_SLICE
;
92 return V4L2_PIX_FMT_H264
;
93 } else if (profile
>= media::VP8PROFILE_MIN
&&
94 profile
<= media::VP8PROFILE_MAX
) {
96 return V4L2_PIX_FMT_VP8_FRAME
;
98 return V4L2_PIX_FMT_VP8
;
99 } else if (profile
>= media::VP9PROFILE_MIN
&&
100 profile
<= media::VP9PROFILE_MAX
) {
101 return V4L2_PIX_FMT_VP9
;
103 LOG(FATAL
) << "Add more cases as needed";
109 uint32_t V4L2Device::V4L2PixFmtToDrmFormat(uint32_t format
) {
111 case V4L2_PIX_FMT_NV12
:
112 case V4L2_PIX_FMT_NV12M
:
113 return DRM_FORMAT_NV12
;
115 case V4L2_PIX_FMT_MT21
:
116 // TODO(wuchengli): Change to DRM_FORMAT_MT21.
117 return DRM_FORMAT_MT12
;
119 case V4L2_PIX_FMT_YUV420
:
120 case V4L2_PIX_FMT_YUV420M
:
121 return DRM_FORMAT_YUV420
;
123 case V4L2_PIX_FMT_RGB32
:
124 return DRM_FORMAT_ARGB8888
;
127 DVLOG(1) << "Add more cases as needed";
133 gfx::Size
V4L2Device::CodedSizeFromV4L2Format(struct v4l2_format format
) {
134 gfx::Size coded_size
;
135 gfx::Size visible_size
;
136 media::VideoPixelFormat frame_format
= media::PIXEL_FORMAT_UNKNOWN
;
137 size_t bytesperline
= 0;
138 // Total bytes in the frame.
139 size_t sizeimage
= 0;
141 if (V4L2_TYPE_IS_MULTIPLANAR(format
.type
)) {
142 DCHECK_GT(format
.fmt
.pix_mp
.num_planes
, 0);
144 base::checked_cast
<int>(format
.fmt
.pix_mp
.plane_fmt
[0].bytesperline
);
145 for (size_t i
= 0; i
< format
.fmt
.pix_mp
.num_planes
; ++i
) {
147 base::checked_cast
<int>(format
.fmt
.pix_mp
.plane_fmt
[i
].sizeimage
);
149 visible_size
.SetSize(base::checked_cast
<int>(format
.fmt
.pix_mp
.width
),
150 base::checked_cast
<int>(format
.fmt
.pix_mp
.height
));
152 V4L2Device::V4L2PixFmtToVideoPixelFormat(format
.fmt
.pix_mp
.pixelformat
);
154 bytesperline
= base::checked_cast
<int>(format
.fmt
.pix
.bytesperline
);
155 sizeimage
= base::checked_cast
<int>(format
.fmt
.pix
.sizeimage
);
156 visible_size
.SetSize(base::checked_cast
<int>(format
.fmt
.pix
.width
),
157 base::checked_cast
<int>(format
.fmt
.pix
.height
));
159 V4L2Device::V4L2PixFmtToVideoPixelFormat(format
.fmt
.pix
.pixelformat
);
162 // V4L2 does not provide per-plane bytesperline (bpl) when different
163 // components are sharing one physical plane buffer. In this case, it only
164 // provides bpl for the first component in the plane. So we can't depend on it
165 // for calculating height, because bpl may vary within one physical plane
166 // buffer. For example, YUV420 contains 3 components in one physical plane,
167 // with Y at 8 bits per pixel, and Cb/Cr at 4 bits per pixel per component,
168 // but we only get 8 pits per pixel from bytesperline in physical plane 0.
169 // So we need to get total frame bpp from elsewhere to calculate coded height.
171 // We need bits per pixel for one component only to calculate
172 // coded_width from bytesperline.
173 int plane_horiz_bits_per_pixel
=
174 media::VideoFrame::PlaneHorizontalBitsPerPixel(frame_format
, 0);
176 // Adding up bpp for each component will give us total bpp for all components.
178 for (size_t i
= 0; i
< media::VideoFrame::NumPlanes(frame_format
); ++i
)
179 total_bpp
+= media::VideoFrame::PlaneBitsPerPixel(frame_format
, i
);
181 if (sizeimage
== 0 || bytesperline
== 0 || plane_horiz_bits_per_pixel
== 0 ||
182 total_bpp
== 0 || (bytesperline
* 8) % plane_horiz_bits_per_pixel
!= 0) {
183 LOG(ERROR
) << "Invalid format provided";
187 // Coded width can be calculated by taking the first component's bytesperline,
188 // which in V4L2 always applies to the first component in physical plane
190 int coded_width
= bytesperline
* 8 / plane_horiz_bits_per_pixel
;
191 // Sizeimage is coded_width * coded_height * total_bpp.
192 int coded_height
= sizeimage
* 8 / coded_width
/ total_bpp
;
194 coded_size
.SetSize(coded_width
, coded_height
);
195 // It's possible the driver gave us a slightly larger sizeimage than what
196 // would be calculated from coded size. This is technically not allowed, but
197 // some drivers (Exynos) like to have some additional alignment that is not a
198 // multiple of bytesperline. The best thing we can do is to compensate by
199 // aligning to next full row.
200 if (sizeimage
> media::VideoFrame::AllocationSize(frame_format
, coded_size
))
201 coded_size
.SetSize(coded_width
, coded_height
+ 1);
202 DVLOG(3) << "coded_size=" << coded_size
.ToString();
204 // Sanity checks. Calculated coded size has to contain given visible size
205 // and fulfill buffer byte size requirements.
206 DCHECK(gfx::Rect(coded_size
).Contains(gfx::Rect(visible_size
)));
208 media::VideoFrame::AllocationSize(frame_format
, coded_size
));
213 void V4L2Device::GetSupportedResolution(uint32_t pixelformat
,
214 gfx::Size
* min_resolution
,
215 gfx::Size
* max_resolution
) {
216 max_resolution
->SetSize(0, 0);
217 min_resolution
->SetSize(0, 0);
218 v4l2_frmsizeenum frame_size
;
219 memset(&frame_size
, 0, sizeof(frame_size
));
220 frame_size
.pixel_format
= pixelformat
;
221 for (; Ioctl(VIDIOC_ENUM_FRAMESIZES
, &frame_size
) == 0; ++frame_size
.index
) {
222 if (frame_size
.type
== V4L2_FRMSIZE_TYPE_DISCRETE
) {
223 if (frame_size
.discrete
.width
>=
224 base::checked_cast
<uint32_t>(max_resolution
->width()) &&
225 frame_size
.discrete
.height
>=
226 base::checked_cast
<uint32_t>(max_resolution
->height())) {
227 max_resolution
->SetSize(frame_size
.discrete
.width
,
228 frame_size
.discrete
.height
);
230 if (min_resolution
->IsEmpty() ||
231 (frame_size
.discrete
.width
<=
232 base::checked_cast
<uint32_t>(min_resolution
->width()) &&
233 frame_size
.discrete
.height
<=
234 base::checked_cast
<uint32_t>(min_resolution
->height()))) {
235 min_resolution
->SetSize(frame_size
.discrete
.width
,
236 frame_size
.discrete
.height
);
238 } else if (frame_size
.type
== V4L2_FRMSIZE_TYPE_STEPWISE
||
239 frame_size
.type
== V4L2_FRMSIZE_TYPE_CONTINUOUS
) {
240 max_resolution
->SetSize(frame_size
.stepwise
.max_width
,
241 frame_size
.stepwise
.max_height
);
242 min_resolution
->SetSize(frame_size
.stepwise
.min_width
,
243 frame_size
.stepwise
.min_height
);
247 if (max_resolution
->IsEmpty()) {
248 max_resolution
->SetSize(1920, 1088);
249 LOG(ERROR
) << "GetSupportedResolution failed to get maximum resolution for "
250 << "fourcc " << std::hex
<< pixelformat
251 << ", fall back to " << max_resolution
->ToString();
253 if (min_resolution
->IsEmpty()) {
254 min_resolution
->SetSize(16, 16);
255 LOG(ERROR
) << "GetSupportedResolution failed to get minimum resolution for "
256 << "fourcc " << std::hex
<< pixelformat
257 << ", fall back to " << min_resolution
->ToString();
261 media::VideoDecodeAccelerator::SupportedProfiles
262 V4L2Device::GetSupportedDecodeProfiles(const size_t num_formats
,
263 const uint32_t pixelformats
[]) {
264 DCHECK_EQ(type_
, kDecoder
);
265 media::VideoDecodeAccelerator::SupportedProfiles profiles
;
266 media::VideoDecodeAccelerator::SupportedProfile profile
;
267 v4l2_fmtdesc fmtdesc
;
268 memset(&fmtdesc
, 0, sizeof(fmtdesc
));
269 fmtdesc
.type
= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
;
271 for (; Ioctl(VIDIOC_ENUM_FMT
, &fmtdesc
) == 0; ++fmtdesc
.index
) {
272 if (std::find(pixelformats
, pixelformats
+ num_formats
,
273 fmtdesc
.pixelformat
) == pixelformats
+ num_formats
)
275 int min_profile
, max_profile
;
276 switch (fmtdesc
.pixelformat
) {
277 case V4L2_PIX_FMT_H264
:
278 case V4L2_PIX_FMT_H264_SLICE
:
279 min_profile
= media::H264PROFILE_MIN
;
280 max_profile
= media::H264PROFILE_MAX
;
282 case V4L2_PIX_FMT_VP8
:
283 case V4L2_PIX_FMT_VP8_FRAME
:
284 min_profile
= media::VP8PROFILE_MIN
;
285 max_profile
= media::VP8PROFILE_MAX
;
287 case V4L2_PIX_FMT_VP9
:
288 min_profile
= media::VP9PROFILE_MIN
;
289 max_profile
= media::VP9PROFILE_MAX
;
292 NOTREACHED() << "Unhandled pixelformat " << std::hex
293 << fmtdesc
.pixelformat
;
296 GetSupportedResolution(fmtdesc
.pixelformat
, &profile
.min_resolution
,
297 &profile
.max_resolution
);
298 for (int media_profile
= min_profile
; media_profile
<= max_profile
;
300 profile
.profile
= static_cast<media::VideoCodecProfile
>(media_profile
);
301 profiles
.push_back(profile
);
307 } // namespace content