platform_video_frame_utils.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. // Copyright 2018 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. #include "media/gpu/chromeos/platform_video_frame_utils.h"
  5. #include <drm_fourcc.h>
  6. #include <xf86drm.h>
  7. #include <limits>
  8. #include "base/bind.h"
  9. #include "base/callback_helpers.h"
  10. #include "base/containers/fixed_flat_set.h"
  11. #include "base/dcheck_is_on.h"
  12. #include "base/files/file.h"
  13. #include "base/files/file_path.h"
  14. #include "base/files/scoped_file.h"
  15. #include "base/no_destructor.h"
  16. #include "base/posix/eintr_wrapper.h"
  17. #include "base/strings/string_util.h"
  18. #include "base/strings/stringprintf.h"
  19. #include "base/synchronization/lock.h"
  20. #include "gpu/ipc/common/gpu_memory_buffer_support.h"
  21. #include "media/base/color_plane_layout.h"
  22. #include "media/base/format_utils.h"
  23. #include "media/base/scopedfd_helper.h"
  24. #include "media/base/video_frame_layout.h"
  25. #include "media/base/video_util.h"
  26. #include "media/gpu/buffer_validation.h"
  27. #include "media/gpu/macros.h"
  28. #include "ui/gfx/buffer_format_util.h"
  29. #include "ui/gfx/buffer_types.h"
  30. #include "ui/gfx/gpu_memory_buffer.h"
  31. #include "ui/gfx/linux/drm_util_linux.h"
  32. #include "ui/gfx/linux/gbm_buffer.h"
  33. #include "ui/gfx/linux/gbm_device.h"
  34. #include "ui/gfx/linux/gbm_util.h"
  35. #include "ui/gfx/linux/gbm_wrapper.h"
  36. #include "ui/gfx/linux/native_pixmap_dmabuf.h"
  37. #include "ui/gfx/native_pixmap.h"
  38. namespace media {
  39. namespace {
  40. // GbmDeviceWrapper is a singleton that provides thread-safe access to a
  41. // ui::GbmDevice for the purposes of creating native BOs. The ui::GbmDevice is
  42. // initialized with the first non-vgem render node found that works starting at
  43. // /dev/dri/renderD128. Note that we have our own FD to the render node (i.e.,
  44. // it's not shared with other components). Therefore, there should not be any
  45. // concurrency issues if other components in the GPU process (like the VA-API
  46. // driver) access the render node using their own FD.
  47. class GbmDeviceWrapper {
  48. public:
  49. GbmDeviceWrapper(const GbmDeviceWrapper&) = delete;
  50. GbmDeviceWrapper& operator=(const GbmDeviceWrapper&) = delete;
  51. static GbmDeviceWrapper* Get() {
  52. static base::NoDestructor<GbmDeviceWrapper> gbm_device_wrapper;
  53. return gbm_device_wrapper.get();
  54. }
  55. // Creates a native BO and returns it as a GpuMemoryBufferHandle. Returns
  56. // gfx::GpuMemoryBufferHandle() on failure.
  57. gfx::GpuMemoryBufferHandle CreateGpuMemoryBuffer(
  58. gfx::BufferFormat format,
  59. const gfx::Size& size,
  60. gfx::BufferUsage buffer_usage) {
  61. base::AutoLock lock(lock_);
  62. if (!gbm_device_)
  63. return gfx::GpuMemoryBufferHandle();
  64. const int fourcc_format = ui::GetFourCCFormatFromBufferFormat(format);
  65. if (fourcc_format == DRM_FORMAT_INVALID)
  66. return gfx::GpuMemoryBufferHandle();
  67. std::unique_ptr<ui::GbmBuffer> buffer =
  68. CreateGbmBuffer(fourcc_format, size, buffer_usage);
  69. if (!buffer)
  70. return gfx::GpuMemoryBufferHandle();
  71. gfx::NativePixmapHandle native_pixmap_handle = buffer->ExportHandle();
  72. if (native_pixmap_handle.planes.empty())
  73. return gfx::GpuMemoryBufferHandle();
  74. gfx::GpuMemoryBufferHandle gmb_handle;
  75. gmb_handle.type = gfx::GpuMemoryBufferType::NATIVE_PIXMAP;
  76. gmb_handle.id = GetNextGpuMemoryBufferId();
  77. gmb_handle.native_pixmap_handle = std::move(native_pixmap_handle);
  78. return gmb_handle;
  79. }
  80. std::unique_ptr<ui::GbmBuffer> ImportGpuMemoryBuffer(
  81. gfx::BufferFormat format,
  82. const gfx::Size& size,
  83. gfx::NativePixmapHandle handle) {
  84. base::AutoLock lock(lock_);
  85. if (!gbm_device_)
  86. return nullptr;
  87. const int fourcc_format = ui::GetFourCCFormatFromBufferFormat(format);
  88. if (fourcc_format == DRM_FORMAT_INVALID)
  89. return nullptr;
  90. return gbm_device_->CreateBufferFromHandle(fourcc_format, size,
  91. std::move(handle));
  92. }
  93. private:
  94. GbmDeviceWrapper() {
  95. constexpr char kRenderNodeFilePattern[] = "/dev/dri/renderD%d";
  96. // This loop ends on either the first card that does not exist or the first
  97. // one that results in the creation of a gbm device.
  98. for (int i = 128;; i++) {
  99. base::FilePath dev_path(FILE_PATH_LITERAL(
  100. base::StringPrintf(kRenderNodeFilePattern, i).c_str()));
  101. render_node_file_ =
  102. base::File(dev_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
  103. if (!render_node_file_.IsValid())
  104. return;
  105. // Skip the virtual graphics memory manager device.
  106. drmVersionPtr version =
  107. drmGetVersion(render_node_file_.GetPlatformFile());
  108. if (!version)
  109. continue;
  110. std::string version_name(
  111. version->name,
  112. base::checked_cast<std::string::size_type>(version->name_len));
  113. drmFreeVersion(version);
  114. if (base::EqualsCaseInsensitiveASCII(version_name, "vgem"))
  115. continue;
  116. gbm_device_ = ui::CreateGbmDevice(render_node_file_.GetPlatformFile());
  117. if (gbm_device_)
  118. return;
  119. }
  120. }
  121. ~GbmDeviceWrapper() = default;
  122. std::unique_ptr<ui::GbmBuffer> CreateGbmBuffer(int fourcc_format,
  123. const gfx::Size& size,
  124. gfx::BufferUsage buffer_usage)
  125. EXCLUSIVE_LOCKS_REQUIRED(lock_) {
  126. uint32_t flags = ui::BufferUsageToGbmFlags(buffer_usage);
  127. std::unique_ptr<ui::GbmBuffer> buffer =
  128. gbm_device_->CreateBuffer(fourcc_format, size, flags);
  129. if (buffer)
  130. return buffer;
  131. // For certain use cases, allocated buffers must be able to be set via kms
  132. // on a CRTC. For those cases, the GBM_BO_USE_SCANOUT flag is required.
  133. // For other use cases, GBM_BO_USE_SCANOUT may be preferred but is
  134. // ultimately optional, so we can fall back to allocation without that
  135. // flag.
  136. constexpr auto kScanoutUsages = base::MakeFixedFlatSet<gfx::BufferUsage>(
  137. {gfx::BufferUsage::SCANOUT,
  138. gfx::BufferUsage::PROTECTED_SCANOUT_VDA_WRITE,
  139. gfx::BufferUsage::SCANOUT_FRONT_RENDERING});
  140. if (!kScanoutUsages.contains(buffer_usage))
  141. flags &= ~GBM_BO_USE_SCANOUT;
  142. return gbm_device_->CreateBuffer(fourcc_format, size, flags);
  143. }
  144. friend class base::NoDestructor<GbmDeviceWrapper>;
  145. base::Lock lock_;
  146. base::File render_node_file_ GUARDED_BY(lock_);
  147. std::unique_ptr<ui::GbmDevice> gbm_device_ GUARDED_BY(lock_);
  148. };
  149. gfx::GpuMemoryBufferHandle AllocateGpuMemoryBufferHandle(
  150. VideoPixelFormat pixel_format,
  151. const gfx::Size& coded_size,
  152. gfx::BufferUsage buffer_usage) {
  153. gfx::GpuMemoryBufferHandle gmb_handle;
  154. auto buffer_format = VideoPixelFormatToGfxBufferFormat(pixel_format);
  155. if (!buffer_format)
  156. return gmb_handle;
  157. return GbmDeviceWrapper::Get()->CreateGpuMemoryBuffer(
  158. *buffer_format, coded_size, buffer_usage);
  159. }
  160. } // namespace
  161. gfx::GpuMemoryBufferId GetNextGpuMemoryBufferId() {
  162. static base::NoDestructor<base::Lock> id_lock;
  163. static int next_gpu_memory_buffer_id = 0;
  164. base::AutoLock lock(*id_lock);
  165. CHECK_LT(next_gpu_memory_buffer_id, std::numeric_limits<int>::max());
  166. return gfx::GpuMemoryBufferId(next_gpu_memory_buffer_id++);
  167. }
  168. scoped_refptr<VideoFrame> CreateGpuMemoryBufferVideoFrame(
  169. VideoPixelFormat pixel_format,
  170. const gfx::Size& coded_size,
  171. const gfx::Rect& visible_rect,
  172. const gfx::Size& natural_size,
  173. base::TimeDelta timestamp,
  174. gfx::BufferUsage buffer_usage) {
  175. auto gmb_handle =
  176. AllocateGpuMemoryBufferHandle(pixel_format, coded_size, buffer_usage);
  177. if (gmb_handle.is_null() || gmb_handle.type != gfx::NATIVE_PIXMAP)
  178. return nullptr;
  179. const bool supports_zero_copy_webgpu_import =
  180. gmb_handle.native_pixmap_handle.supports_zero_copy_webgpu_import;
  181. auto buffer_format = VideoPixelFormatToGfxBufferFormat(pixel_format);
  182. DCHECK(buffer_format);
  183. gpu::GpuMemoryBufferSupport support;
  184. std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
  185. support.CreateGpuMemoryBufferImplFromHandle(
  186. std::move(gmb_handle), coded_size, *buffer_format, buffer_usage,
  187. base::NullCallback());
  188. if (!gpu_memory_buffer)
  189. return nullptr;
  190. // The empty mailbox is ok because this VideoFrame is not rendered.
  191. const gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes] = {};
  192. auto frame = VideoFrame::WrapExternalGpuMemoryBuffer(
  193. visible_rect, natural_size, std::move(gpu_memory_buffer), mailbox_holders,
  194. base::NullCallback(), timestamp);
  195. if (!frame)
  196. return nullptr;
  197. // We only support importing non-DISJOINT multi-planar GbmBuffer right now.
  198. // TODO(crbug.com/1258986): Add DISJOINT support.
  199. frame->metadata().is_webgpu_compatible = supports_zero_copy_webgpu_import;
  200. return frame;
  201. }
  202. scoped_refptr<VideoFrame> CreatePlatformVideoFrame(
  203. VideoPixelFormat pixel_format,
  204. const gfx::Size& coded_size,
  205. const gfx::Rect& visible_rect,
  206. const gfx::Size& natural_size,
  207. base::TimeDelta timestamp,
  208. gfx::BufferUsage buffer_usage) {
  209. auto gmb_handle =
  210. AllocateGpuMemoryBufferHandle(pixel_format, coded_size, buffer_usage);
  211. if (gmb_handle.is_null() || gmb_handle.type != gfx::NATIVE_PIXMAP)
  212. return nullptr;
  213. std::vector<ColorPlaneLayout> planes;
  214. for (const auto& plane : gmb_handle.native_pixmap_handle.planes)
  215. planes.emplace_back(plane.stride, plane.offset, plane.size);
  216. auto layout = VideoFrameLayout::CreateWithPlanes(
  217. pixel_format, coded_size, std::move(planes),
  218. VideoFrameLayout::kBufferAddressAlignment,
  219. gmb_handle.native_pixmap_handle.modifier);
  220. if (!layout)
  221. return nullptr;
  222. std::vector<base::ScopedFD> dmabuf_fds;
  223. for (auto& plane : gmb_handle.native_pixmap_handle.planes)
  224. dmabuf_fds.emplace_back(plane.fd.release());
  225. auto frame = VideoFrame::WrapExternalDmabufs(
  226. *layout, visible_rect, natural_size, std::move(dmabuf_fds), timestamp);
  227. if (!frame)
  228. return nullptr;
  229. return frame;
  230. }
  231. absl::optional<VideoFrameLayout> GetPlatformVideoFrameLayout(
  232. VideoPixelFormat pixel_format,
  233. const gfx::Size& coded_size,
  234. gfx::BufferUsage buffer_usage) {
  235. // |visible_rect| and |natural_size| do not matter here. |coded_size| is set
  236. // as a dummy variable.
  237. auto frame =
  238. CreatePlatformVideoFrame(pixel_format, coded_size, gfx::Rect(coded_size),
  239. coded_size, base::TimeDelta(), buffer_usage);
  240. return frame ? absl::make_optional<VideoFrameLayout>(frame->layout())
  241. : absl::nullopt;
  242. }
  243. gfx::GpuMemoryBufferHandle CreateGpuMemoryBufferHandle(
  244. const VideoFrame* video_frame) {
  245. DCHECK(video_frame);
  246. gfx::GpuMemoryBufferHandle handle;
  247. switch (video_frame->storage_type()) {
  248. case VideoFrame::STORAGE_GPU_MEMORY_BUFFER:
  249. handle = video_frame->GetGpuMemoryBuffer()->CloneHandle();
  250. // TODO(crbug.com/1097956): handle a failure gracefully.
  251. CHECK_EQ(handle.type, gfx::NATIVE_PIXMAP)
  252. << "The cloned handle has an unexpected type: " << handle.type;
  253. CHECK(!handle.native_pixmap_handle.planes.empty())
  254. << "The cloned handle has no planes";
  255. break;
  256. case VideoFrame::STORAGE_DMABUFS: {
  257. const size_t num_planes = VideoFrame::NumPlanes(video_frame->format());
  258. std::vector<base::ScopedFD> duped_fds =
  259. DuplicateFDs(video_frame->DmabufFds());
  260. // TODO(crbug.com/1036174): Replace this duplication with a check.
  261. // Duplicate the fd of the last plane until the number of fds are the same
  262. // as the number of planes.
  263. while (num_planes != duped_fds.size()) {
  264. int duped_fd = -1;
  265. duped_fd = HANDLE_EINTR(dup(duped_fds.back().get()));
  266. // TODO(crbug.com/1097956): handle a failure gracefully.
  267. PCHECK(duped_fd >= 0) << "Failed duplicating a dma-buf fd";
  268. duped_fds.emplace_back(duped_fd);
  269. }
  270. handle.type = gfx::NATIVE_PIXMAP;
  271. handle.id = GetNextGpuMemoryBufferId();
  272. DCHECK_EQ(video_frame->layout().planes().size(), num_planes);
  273. handle.native_pixmap_handle.modifier = video_frame->layout().modifier();
  274. for (size_t i = 0; i < num_planes; ++i) {
  275. const auto& plane = video_frame->layout().planes()[i];
  276. handle.native_pixmap_handle.planes.emplace_back(
  277. plane.stride, plane.offset, plane.size, std::move(duped_fds[i]));
  278. }
  279. } break;
  280. default:
  281. NOTREACHED() << "Unsupported storage type: "
  282. << video_frame->storage_type();
  283. }
  284. CHECK_EQ(handle.type, gfx::NATIVE_PIXMAP);
  285. if (video_frame->format() == PIXEL_FORMAT_MJPEG)
  286. return handle;
  287. #if DCHECK_IS_ON()
  288. const bool is_handle_valid =
  289. !handle.is_null() &&
  290. VerifyGpuMemoryBufferHandle(video_frame->format(),
  291. video_frame->coded_size(), handle);
  292. DLOG_IF(WARNING, !is_handle_valid)
  293. << __func__ << "(): Created GpuMemoryBufferHandle is invalid";
  294. #endif // DCHECK_IS_ON()
  295. return handle;
  296. }
  297. scoped_refptr<gfx::NativePixmapDmaBuf> CreateNativePixmapDmaBuf(
  298. const VideoFrame* video_frame) {
  299. DCHECK(video_frame);
  300. // Create a native pixmap from the frame's memory buffer handle.
  301. gfx::GpuMemoryBufferHandle gpu_memory_buffer_handle =
  302. CreateGpuMemoryBufferHandle(video_frame);
  303. if (gpu_memory_buffer_handle.is_null() ||
  304. gpu_memory_buffer_handle.type != gfx::NATIVE_PIXMAP) {
  305. VLOGF(1) << "Failed to create native GpuMemoryBufferHandle";
  306. return nullptr;
  307. }
  308. auto buffer_format =
  309. VideoPixelFormatToGfxBufferFormat(video_frame->layout().format());
  310. if (!buffer_format) {
  311. VLOGF(1) << "Unexpected video frame format";
  312. return nullptr;
  313. }
  314. auto native_pixmap = base::MakeRefCounted<gfx::NativePixmapDmaBuf>(
  315. video_frame->coded_size(), *buffer_format,
  316. std::move(gpu_memory_buffer_handle.native_pixmap_handle));
  317. DCHECK(native_pixmap->AreDmaBufFdsValid());
  318. return native_pixmap;
  319. }
  320. bool CanImportGpuMemoryBufferHandle(
  321. const gfx::Size& size,
  322. gfx::BufferFormat format,
  323. const gfx::GpuMemoryBufferHandle& gmb_handle) {
  324. if (gmb_handle.type != gfx::GpuMemoryBufferType::NATIVE_PIXMAP) {
  325. VLOGF(1) << "The handle type (" << gmb_handle.type << ") is unsupported";
  326. return false;
  327. }
  328. const auto pixel_format = GfxBufferFormatToVideoPixelFormat(format);
  329. if (!pixel_format) {
  330. VLOGF(1) << "Unsupported buffer format: "
  331. << gfx::BufferFormatToString(format);
  332. return false;
  333. }
  334. if (!VerifyGpuMemoryBufferHandle(*pixel_format, size, gmb_handle)) {
  335. VLOGF(1) << "Invalid GpuMemoryBufferHandle provided";
  336. return false;
  337. }
  338. gfx::NativePixmapHandle native_pixmap_handle =
  339. gfx::CloneHandleForIPC(gmb_handle.native_pixmap_handle);
  340. if (native_pixmap_handle.planes.empty()) {
  341. VLOGF(1) << "Could not duplicate the NativePixmapHandle";
  342. return false;
  343. }
  344. return !!GbmDeviceWrapper::Get()->ImportGpuMemoryBuffer(
  345. format, size, std::move(native_pixmap_handle));
  346. }
  347. } // namespace media