1 // Copyright 2017 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
5 #include "media/mojo/mojom/video_frame_mojom_traits.h"
6
7 #include <utility>
8 #include <vector>
9
10 #include "base/callback_helpers.h"
11 #include "base/logging.h"
12 #include "build/build_config.h"
13 #include "gpu/ipc/common/gpu_memory_buffer_support.h"
14 #include "media/base/color_plane_layout.h"
15 #include "media/base/format_utils.h"
16 #include "media/mojo/common/mojo_shared_buffer_video_frame.h"
17 #include "media/mojo/mojom/video_frame_metadata_mojom_traits.h"
18 #include "mojo/public/cpp/base/time_mojom_traits.h"
19 #include "mojo/public/cpp/system/handle.h"
20 #include "ui/gfx/mojom/buffer_types_mojom_traits.h"
21 #include "ui/gfx/mojom/color_space_mojom_traits.h"
22 #include "ui/gfx/mojom/hdr_metadata_mojom_traits.h"
23
24 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
25 #include "base/posix/eintr_wrapper.h"
26 #endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
27
28 namespace mojo {
29
30 namespace {
31
MakeVideoFrameData(const media::VideoFrame * input)32 media::mojom::VideoFrameDataPtr MakeVideoFrameData(
33 const media::VideoFrame* input) {
34 if (input->metadata()->end_of_stream) {
35 return media::mojom::VideoFrameData::NewEosData(
36 media::mojom::EosVideoFrameData::New());
37 }
38
39 if (input->storage_type() == media::VideoFrame::STORAGE_MOJO_SHARED_BUFFER) {
40 const media::MojoSharedBufferVideoFrame* mojo_frame =
41 static_cast<const media::MojoSharedBufferVideoFrame*>(input);
42
43 // Mojo shared buffer handles are always writable. For example,
44 // cdm_video_decoder in ToCdmVideoFrame maps a frame writable; these frames
45 // are returned via callback and reused in ToCdmVideoFrame. Since returning
46 // via callback involves a Clone(), and since cloning a region read-only
47 // makes both the source handle and the cloned handle read-only, it must be
48 // cloned writable.
49 mojo::ScopedSharedBufferHandle dup = mojo_frame->Handle().Clone(
50 mojo::SharedBufferHandle::AccessMode::READ_WRITE);
51 DCHECK(dup.is_valid());
52 size_t num_planes = media::VideoFrame::NumPlanes(mojo_frame->format());
53 std::vector<uint32_t> offsets(num_planes);
54 std::vector<int32_t> strides(num_planes);
55 for (size_t i = 0; i < num_planes; ++i) {
56 offsets[i] = mojo_frame->PlaneOffset(i);
57 strides[i] = mojo_frame->stride(i);
58 }
59
60 return media::mojom::VideoFrameData::NewSharedBufferData(
61 media::mojom::SharedBufferVideoFrameData::New(
62 std::move(dup), mojo_frame->MappedSize(), std::move(strides),
63 std::move(offsets)));
64 }
65
66 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
67 if (input->storage_type() == media::VideoFrame::STORAGE_DMABUFS) {
68 std::vector<mojo::PlatformHandle> dmabuf_fds;
69
70 const size_t num_planes = media::VideoFrame::NumPlanes(input->format());
71 dmabuf_fds.reserve(num_planes);
72 for (size_t i = 0; i < num_planes; i++) {
73 const int dmabuf_fd = HANDLE_EINTR(dup(input->DmabufFds()[i].get()));
74 dmabuf_fds.emplace_back(base::ScopedFD(dmabuf_fd));
75 DCHECK(dmabuf_fds.back().is_valid());
76 }
77
78 return media::mojom::VideoFrameData::NewDmabufData(
79 media::mojom::DmabufVideoFrameData::New(std::move(dmabuf_fds)));
80 }
81 #endif
82
83 std::vector<gpu::MailboxHolder> mailbox_holder(media::VideoFrame::kMaxPlanes);
84 size_t num_planes = media::VideoFrame::NumPlanes(input->format());
85 DCHECK_LE(num_planes, mailbox_holder.size());
86 for (size_t i = 0; i < num_planes; i++)
87 mailbox_holder[i] = input->mailbox_holder(i);
88
89 if (input->storage_type() == media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER) {
90 gfx::GpuMemoryBufferHandle gpu_memory_buffer_handle;
91 if (input->HasGpuMemoryBuffer())
92 gpu_memory_buffer_handle = input->GetGpuMemoryBuffer()->CloneHandle();
93 return media::mojom::VideoFrameData::NewGpuMemoryBufferData(
94 media::mojom::GpuMemoryBufferVideoFrameData::New(
95 std::move(gpu_memory_buffer_handle), std::move(mailbox_holder)));
96 } else if (input->HasTextures()) {
97 return media::mojom::VideoFrameData::NewMailboxData(
98 media::mojom::MailboxVideoFrameData::New(
99 std::move(mailbox_holder), std::move(input->ycbcr_info())));
100 }
101
102 NOTREACHED() << "Unsupported VideoFrame conversion";
103 return nullptr;
104 }
105
106 } // namespace
107
108 // static
109 media::mojom::VideoFrameDataPtr StructTraits<media::mojom::VideoFrameDataView,
110 scoped_refptr<media::VideoFrame>>::
data(const scoped_refptr<media::VideoFrame> & input)111 data(const scoped_refptr<media::VideoFrame>& input) {
112 return media::mojom::VideoFrameDataPtr(MakeVideoFrameData(input.get()));
113 }
114
115 // static
116 bool StructTraits<media::mojom::VideoFrameDataView,
117 scoped_refptr<media::VideoFrame>>::
Read(media::mojom::VideoFrameDataView input,scoped_refptr<media::VideoFrame> * output)118 Read(media::mojom::VideoFrameDataView input,
119 scoped_refptr<media::VideoFrame>* output) {
120 // View of the |data| member of the input media::mojom::VideoFrame.
121 media::mojom::VideoFrameDataDataView data;
122 input.GetDataDataView(&data);
123
124 if (data.is_eos_data()) {
125 *output = media::VideoFrame::CreateEOSFrame();
126 return !!*output;
127 }
128
129 media::VideoPixelFormat format;
130 if (!input.ReadFormat(&format))
131 return false;
132
133 gfx::Size coded_size;
134 if (!input.ReadCodedSize(&coded_size))
135 return false;
136
137 gfx::Rect visible_rect;
138 if (!input.ReadVisibleRect(&visible_rect))
139 return false;
140
141 if (!gfx::Rect(coded_size).Contains(visible_rect))
142 return false;
143
144 gfx::Size natural_size;
145 if (!input.ReadNaturalSize(&natural_size))
146 return false;
147
148 base::TimeDelta timestamp;
149 if (!input.ReadTimestamp(×tamp))
150 return false;
151
152 scoped_refptr<media::VideoFrame> frame;
153 if (data.is_shared_buffer_data()) {
154 media::mojom::SharedBufferVideoFrameDataDataView shared_buffer_data;
155 data.GetSharedBufferDataDataView(&shared_buffer_data);
156
157 std::vector<int32_t> strides;
158 if (!shared_buffer_data.ReadStrides(&strides))
159 return false;
160
161 std::vector<uint32_t> offsets;
162 if (!shared_buffer_data.ReadOffsets(&offsets))
163 return false;
164 frame = media::MojoSharedBufferVideoFrame::Create(
165 format, coded_size, visible_rect, natural_size,
166 shared_buffer_data.TakeFrameData(),
167 shared_buffer_data.frame_data_size(), std::move(offsets),
168 std::move(strides), timestamp);
169 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
170 } else if (data.is_dmabuf_data()) {
171 media::mojom::DmabufVideoFrameDataDataView dmabuf_data;
172 data.GetDmabufDataDataView(&dmabuf_data);
173
174 std::vector<mojo::PlatformHandle> dmabuf_fds_data;
175 if (!dmabuf_data.ReadDmabufFds(&dmabuf_fds_data))
176 return false;
177
178 const size_t num_planes = media::VideoFrame::NumPlanes(format);
179 std::vector<int> strides =
180 media::VideoFrame::ComputeStrides(format, coded_size);
181 if (num_planes != strides.size())
182 return false;
183 if (num_planes != dmabuf_fds_data.size())
184 return false;
185
186 std::vector<media::ColorPlaneLayout> planes(num_planes);
187 for (size_t i = 0; i < num_planes; i++) {
188 planes[i].stride = strides[i];
189 planes[i].offset = 0;
190 planes[i].size = static_cast<size_t>(
191 media::VideoFrame::PlaneSize(format, i, coded_size).GetArea());
192 }
193
194 auto layout = media::VideoFrameLayout::CreateWithPlanes(format, coded_size,
195 std::move(planes));
196 if (!layout)
197 return false;
198
199 std::vector<base::ScopedFD> dmabuf_fds;
200 dmabuf_fds.reserve(num_planes);
201 for (size_t i = 0; i < num_planes; i++) {
202 dmabuf_fds.push_back(dmabuf_fds_data[i].TakeFD());
203 DCHECK(dmabuf_fds.back().is_valid());
204 }
205 frame = media::VideoFrame::WrapExternalDmabufs(
206 *layout, visible_rect, natural_size, std::move(dmabuf_fds), timestamp);
207 #endif
208 } else if (data.is_gpu_memory_buffer_data()) {
209 media::mojom::GpuMemoryBufferVideoFrameDataDataView gpu_memory_buffer_data;
210 data.GetGpuMemoryBufferDataDataView(&gpu_memory_buffer_data);
211
212 gfx::GpuMemoryBufferHandle gpu_memory_buffer_handle;
213 if (!gpu_memory_buffer_data.ReadGpuMemoryBufferHandle(
214 &gpu_memory_buffer_handle)) {
215 return false;
216 }
217
218 std::vector<gpu::MailboxHolder> mailbox_holder;
219 if (!gpu_memory_buffer_data.ReadMailboxHolder(&mailbox_holder)) {
220 DLOG(WARNING) << "Failed to get mailbox holder";
221 }
222 if (mailbox_holder.size() > media::VideoFrame::kMaxPlanes) {
223 DLOG(ERROR) << "The size of mailbox holder is too large: "
224 << mailbox_holder.size();
225 return false;
226 }
227
228 gpu::MailboxHolder mailbox_holder_array[media::VideoFrame::kMaxPlanes];
229 for (size_t i = 0; i < mailbox_holder.size(); i++)
230 mailbox_holder_array[i] = mailbox_holder[i];
231
232 base::Optional<gfx::BufferFormat> buffer_format =
233 VideoPixelFormatToGfxBufferFormat(format);
234 if (!buffer_format)
235 return false;
236
237 gpu::GpuMemoryBufferSupport support;
238 std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
239 support.CreateGpuMemoryBufferImplFromHandle(
240 std::move(gpu_memory_buffer_handle), coded_size, *buffer_format,
241 gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE,
242 base::NullCallback());
243 if (!gpu_memory_buffer)
244 return false;
245
246 frame = media::VideoFrame::WrapExternalGpuMemoryBuffer(
247 visible_rect, natural_size, std::move(gpu_memory_buffer),
248 mailbox_holder_array, media::VideoFrame::ReleaseMailboxCB(), timestamp);
249 } else if (data.is_mailbox_data()) {
250 media::mojom::MailboxVideoFrameDataDataView mailbox_data;
251 data.GetMailboxDataDataView(&mailbox_data);
252
253 std::vector<gpu::MailboxHolder> mailbox_holder;
254 if (!mailbox_data.ReadMailboxHolder(&mailbox_holder))
255 return false;
256
257 gpu::MailboxHolder mailbox_holder_array[media::VideoFrame::kMaxPlanes];
258 for (size_t i = 0; i < media::VideoFrame::kMaxPlanes; i++)
259 mailbox_holder_array[i] = mailbox_holder[i];
260
261 base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info;
262 if (!mailbox_data.ReadYcbcrData(&ycbcr_info))
263 return false;
264
265 frame = media::VideoFrame::WrapNativeTextures(
266 format, mailbox_holder_array, media::VideoFrame::ReleaseMailboxCB(),
267 coded_size, visible_rect, natural_size, timestamp);
268 frame->set_ycbcr_info(ycbcr_info);
269 } else {
270 // TODO(sandersd): Switch on the union tag to avoid this ugliness?
271 NOTREACHED();
272 return false;
273 }
274
275 if (!frame)
276 return false;
277
278 media::VideoFrameMetadata metadata;
279 if (!input.ReadMetadata(&metadata))
280 return false;
281
282 frame->set_metadata(metadata);
283
284 gfx::ColorSpace color_space;
285 if (!input.ReadColorSpace(&color_space))
286 return false;
287 frame->set_color_space(color_space);
288
289 base::Optional<gfx::HDRMetadata> hdr_metadata;
290 if (!input.ReadHdrMetadata(&hdr_metadata))
291 return false;
292 frame->set_hdr_metadata(std::move(hdr_metadata));
293
294 *output = std::move(frame);
295 return true;
296 }
297
298 } // namespace mojo
299