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(&timestamp))
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