vaapi_mjpeg_decode_accelerator.cc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. // Copyright 2015 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/vaapi/vaapi_mjpeg_decode_accelerator.h"
  5. #include <stddef.h>
  6. #include <sys/mman.h>
  7. #include <va/va.h>
  8. #include <array>
  9. #include <utility>
  10. #include "base/bind.h"
  11. #include "base/callback_helpers.h"
  12. #include "base/files/scoped_file.h"
  13. #include "base/location.h"
  14. #include "base/logging.h"
  15. #include "base/memory/page_size.h"
  16. #include "base/memory/shared_memory_mapping.h"
  17. #include "base/memory/unsafe_shared_memory_region.h"
  18. #include "base/metrics/histogram_macros.h"
  19. #include "base/numerics/checked_math.h"
  20. #include "base/numerics/safe_conversions.h"
  21. #include "base/task/single_thread_task_runner.h"
  22. #include "base/threading/thread_task_runner_handle.h"
  23. #include "base/trace_event/trace_event.h"
  24. #include "gpu/ipc/common/gpu_memory_buffer_impl.h"
  25. #include "media/base/bind_to_current_loop.h"
  26. #include "media/base/bitstream_buffer.h"
  27. #include "media/base/format_utils.h"
  28. #include "media/base/video_frame.h"
  29. #include "media/base/video_frame_layout.h"
  30. #include "media/base/video_util.h"
  31. #include "media/gpu/chromeos/fourcc.h"
  32. #include "media/gpu/chromeos/libyuv_image_processor_backend.h"
  33. #include "media/gpu/chromeos/platform_video_frame_utils.h"
  34. #include "media/gpu/macros.h"
  35. #include "media/gpu/vaapi/va_surface.h"
  36. #include "media/gpu/vaapi/vaapi_image_decoder.h"
  37. #include "media/gpu/vaapi/vaapi_utils.h"
  38. #include "media/gpu/vaapi/vaapi_wrapper.h"
  39. #include "third_party/abseil-cpp/absl/types/optional.h"
  40. #include "ui/gfx/geometry/rect.h"
  41. #include "ui/gfx/geometry/size.h"
  42. #include "ui/gfx/gpu_memory_buffer.h"
  43. namespace media {
  44. namespace {
  45. void ReportToVAJDAResponseToClientUMA(
  46. chromeos_camera::MjpegDecodeAccelerator::Error response) {
  47. UMA_HISTOGRAM_ENUMERATION(
  48. "Media.VAJDA.ResponseToClient", response,
  49. chromeos_camera::MjpegDecodeAccelerator::Error::MJDA_ERROR_CODE_MAX + 1);
  50. }
  51. chromeos_camera::MjpegDecodeAccelerator::Error VaapiJpegDecodeStatusToError(
  52. VaapiImageDecodeStatus status) {
  53. switch (status) {
  54. case VaapiImageDecodeStatus::kSuccess:
  55. return chromeos_camera::MjpegDecodeAccelerator::Error::NO_ERRORS;
  56. case VaapiImageDecodeStatus::kParseFailed:
  57. return chromeos_camera::MjpegDecodeAccelerator::Error::PARSE_JPEG_FAILED;
  58. case VaapiImageDecodeStatus::kUnsupportedSubsampling:
  59. return chromeos_camera::MjpegDecodeAccelerator::Error::UNSUPPORTED_JPEG;
  60. default:
  61. return chromeos_camera::MjpegDecodeAccelerator::Error::PLATFORM_FAILURE;
  62. }
  63. }
  64. bool VerifyDataSize(const VAImage* image) {
  65. const gfx::Size dimensions(base::strict_cast<int>(image->width),
  66. base::strict_cast<int>(image->height));
  67. size_t min_size = 0;
  68. if (image->format.fourcc == VA_FOURCC_I420) {
  69. min_size = VideoFrame::AllocationSize(PIXEL_FORMAT_I420, dimensions);
  70. } else if (image->format.fourcc == VA_FOURCC_NV12) {
  71. min_size = VideoFrame::AllocationSize(PIXEL_FORMAT_NV12, dimensions);
  72. } else if (image->format.fourcc == VA_FOURCC_YUY2 ||
  73. image->format.fourcc == VA_FOURCC('Y', 'U', 'Y', 'V')) {
  74. min_size = VideoFrame::AllocationSize(PIXEL_FORMAT_YUY2, dimensions);
  75. } else {
  76. return false;
  77. }
  78. return base::strict_cast<size_t>(image->data_size) >= min_size;
  79. }
  80. } // namespace
  81. void VaapiMjpegDecodeAccelerator::NotifyError(int32_t task_id, Error error) {
  82. if (!task_runner_->BelongsToCurrentThread()) {
  83. task_runner_->PostTask(
  84. FROM_HERE,
  85. base::BindOnce(&VaapiMjpegDecodeAccelerator::NotifyError,
  86. weak_this_factory_.GetWeakPtr(), task_id, error));
  87. return;
  88. }
  89. VLOGF(1) << "Notifying of error " << error;
  90. // |error| shouldn't be NO_ERRORS because successful decodes should be handled
  91. // by VideoFrameReady().
  92. DCHECK_NE(chromeos_camera::MjpegDecodeAccelerator::Error::NO_ERRORS, error);
  93. ReportToVAJDAResponseToClientUMA(error);
  94. DCHECK(client_);
  95. client_->NotifyError(task_id, error);
  96. }
  97. void VaapiMjpegDecodeAccelerator::VideoFrameReady(int32_t task_id) {
  98. DCHECK(task_runner_->BelongsToCurrentThread());
  99. ReportToVAJDAResponseToClientUMA(
  100. chromeos_camera::MjpegDecodeAccelerator::Error::NO_ERRORS);
  101. client_->VideoFrameReady(task_id);
  102. }
  103. VaapiMjpegDecodeAccelerator::VaapiMjpegDecodeAccelerator(
  104. const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
  105. : task_runner_(base::ThreadTaskRunnerHandle::Get()),
  106. io_task_runner_(io_task_runner),
  107. client_(nullptr),
  108. decoder_thread_("VaapiMjpegDecoderThread"),
  109. weak_this_factory_(this) {}
  110. // Some members expect to be destroyed on the |decoder_thread_|.
  111. void VaapiMjpegDecodeAccelerator::CleanUpOnDecoderThread() {
  112. DCHECK(decoder_task_runner_->BelongsToCurrentThread());
  113. DCHECK(vpp_vaapi_wrapper_->HasOneRef());
  114. vpp_vaapi_wrapper_.reset();
  115. decoder_.reset();
  116. image_processor_.reset();
  117. }
  118. VaapiMjpegDecodeAccelerator::~VaapiMjpegDecodeAccelerator() {
  119. DCHECK(task_runner_->BelongsToCurrentThread());
  120. VLOGF(2) << "Destroying VaapiMjpegDecodeAccelerator";
  121. weak_this_factory_.InvalidateWeakPtrs();
  122. if (decoder_task_runner_) {
  123. // base::Unretained() is fine here because we control |decoder_task_runner_|
  124. // lifetime.
  125. decoder_task_runner_->PostTask(
  126. FROM_HERE,
  127. base::BindOnce(&VaapiMjpegDecodeAccelerator::CleanUpOnDecoderThread,
  128. base::Unretained(this)));
  129. }
  130. decoder_thread_.Stop();
  131. }
  132. void VaapiMjpegDecodeAccelerator::InitializeOnDecoderTaskRunner(
  133. chromeos_camera::MjpegDecodeAccelerator::InitCB init_cb) {
  134. DCHECK(decoder_task_runner_->BelongsToCurrentThread());
  135. decoder_ = std::make_unique<media::VaapiJpegDecoder>();
  136. if (!decoder_->Initialize(base::BindRepeating(
  137. &ReportVaapiErrorToUMA,
  138. "Media.VaapiMjpegDecodeAccelerator.VAAPIError"))) {
  139. VLOGF(1) << "Failed initializing |decoder_|";
  140. std::move(init_cb).Run(false);
  141. return;
  142. }
  143. vpp_vaapi_wrapper_ = VaapiWrapper::Create(
  144. VaapiWrapper::kVideoProcess, VAProfileNone,
  145. EncryptionScheme::kUnencrypted,
  146. base::BindRepeating(&ReportVaapiErrorToUMA,
  147. "Media.VaapiMjpegDecodeAccelerator.Vpp.VAAPIError"));
  148. if (!vpp_vaapi_wrapper_) {
  149. VLOGF(1) << "Failed initializing VAAPI for VPP";
  150. std::move(init_cb).Run(false);
  151. return;
  152. }
  153. // Size is irrelevant for a VPP context.
  154. if (!vpp_vaapi_wrapper_->CreateContext(gfx::Size())) {
  155. VLOGF(1) << "Failed to create context for VPP";
  156. std::move(init_cb).Run(false);
  157. return;
  158. }
  159. std::move(init_cb).Run(true);
  160. }
  161. void VaapiMjpegDecodeAccelerator::InitializeOnTaskRunner(
  162. chromeos_camera::MjpegDecodeAccelerator::Client* client,
  163. chromeos_camera::MjpegDecodeAccelerator::InitCB init_cb) {
  164. DCHECK(task_runner_->BelongsToCurrentThread());
  165. client_ = client;
  166. if (!decoder_thread_.Start()) {
  167. VLOGF(1) << "Failed to start decoding thread.";
  168. std::move(init_cb).Run(false);
  169. return;
  170. }
  171. decoder_task_runner_ = decoder_thread_.task_runner();
  172. // base::Unretained() is fine here because we control |decoder_task_runner_|
  173. // lifetime.
  174. decoder_task_runner_->PostTask(
  175. FROM_HERE,
  176. base::BindOnce(
  177. &VaapiMjpegDecodeAccelerator::InitializeOnDecoderTaskRunner,
  178. base::Unretained(this), std::move(init_cb)));
  179. }
  180. void VaapiMjpegDecodeAccelerator::InitializeAsync(
  181. chromeos_camera::MjpegDecodeAccelerator::Client* client,
  182. chromeos_camera::MjpegDecodeAccelerator::InitCB init_cb) {
  183. VLOGF(2);
  184. DCHECK(task_runner_->BelongsToCurrentThread());
  185. // To guarantee that the caller receives an asynchronous call after the
  186. // return path, we are making use of InitializeOnTaskRunner.
  187. task_runner_->PostTask(
  188. FROM_HERE,
  189. base::BindOnce(&VaapiMjpegDecodeAccelerator::InitializeOnTaskRunner,
  190. weak_this_factory_.GetWeakPtr(), client,
  191. BindToCurrentLoop(std::move(init_cb))));
  192. }
  193. void VaapiMjpegDecodeAccelerator::CreateImageProcessor(
  194. const VideoFrame* src_frame,
  195. const VideoFrame* dst_frame) {
  196. DCHECK(decoder_task_runner_->BelongsToCurrentThread());
  197. // The fourcc of |src_frame| will be either Fourcc(YUYV) or Fourcc(YU12) based
  198. // on the implementation of OutputPictureLibYuvOnTaskRunner(). The fourcc of
  199. // |dst_frame| should have been validated in DecodeImpl().
  200. const auto src_fourcc = Fourcc::FromVideoPixelFormat(src_frame->format());
  201. DCHECK(src_fourcc.has_value());
  202. const auto dst_fourcc = Fourcc::FromVideoPixelFormat(dst_frame->format());
  203. DCHECK(dst_fourcc.has_value());
  204. const ImageProcessorBackend::PortConfig input_config(
  205. *src_fourcc, src_frame->coded_size(), src_frame->layout().planes(),
  206. src_frame->visible_rect(), {src_frame->storage_type()});
  207. const ImageProcessorBackend::PortConfig output_config(
  208. *dst_fourcc, dst_frame->coded_size(), dst_frame->layout().planes(),
  209. dst_frame->visible_rect(), {dst_frame->storage_type()});
  210. if (image_processor_ && image_processor_->input_config() == input_config &&
  211. image_processor_->output_config() == output_config) {
  212. return;
  213. }
  214. // The error callback is posted to the same thread that
  215. // LibYUVImageProcessorBackend::Create() is called on
  216. // (i.e., |decoder_thread_|) and we control the lifetime of |decoder_thread_|.
  217. // Therefore, base::Unretained(this) is safe.
  218. image_processor_ = LibYUVImageProcessorBackend::Create(
  219. input_config, output_config, ImageProcessorBackend::OutputMode::IMPORT,
  220. VIDEO_ROTATION_0,
  221. base::BindRepeating(&VaapiMjpegDecodeAccelerator::OnImageProcessorError,
  222. base::Unretained(this)),
  223. decoder_task_runner_);
  224. }
  225. bool VaapiMjpegDecodeAccelerator::OutputPictureLibYuvOnTaskRunner(
  226. int32_t task_id,
  227. std::unique_ptr<ScopedVAImage> scoped_image,
  228. scoped_refptr<VideoFrame> video_frame,
  229. const gfx::Rect& crop_rect) {
  230. DCHECK(decoder_task_runner_->BelongsToCurrentThread());
  231. TRACE_EVENT1("jpeg", __func__, "task_id", task_id);
  232. DCHECK(scoped_image);
  233. const VAImage* image = scoped_image->image();
  234. DCHECK(VerifyDataSize(image));
  235. const gfx::Size src_size(base::strict_cast<int>(image->width),
  236. base::strict_cast<int>(image->height));
  237. DCHECK(gfx::Rect(src_size).Contains(crop_rect));
  238. // Wrap |image| into VideoFrame.
  239. std::vector<int32_t> strides(image->num_planes);
  240. for (uint32_t i = 0; i < image->num_planes; ++i) {
  241. if (!base::CheckedNumeric<uint32_t>(image->pitches[i])
  242. .AssignIfValid(&strides[i])) {
  243. VLOGF(1) << "Invalid VAImage stride " << image->pitches[i]
  244. << " for plane " << i;
  245. return false;
  246. }
  247. }
  248. auto* const data = static_cast<uint8_t*>(scoped_image->va_buffer()->data());
  249. scoped_refptr<VideoFrame> src_frame;
  250. switch (image->format.fourcc) {
  251. case VA_FOURCC_YUY2:
  252. case VA_FOURCC('Y', 'U', 'Y', 'V'): {
  253. auto layout = VideoFrameLayout::CreateWithStrides(PIXEL_FORMAT_YUY2,
  254. src_size, strides);
  255. if (!layout.has_value()) {
  256. VLOGF(1) << "Failed to create video frame layout";
  257. return false;
  258. }
  259. src_frame = VideoFrame::WrapExternalDataWithLayout(
  260. *layout, crop_rect, crop_rect.size(), data + image->offsets[0],
  261. base::strict_cast<size_t>(image->data_size), base::TimeDelta());
  262. break;
  263. }
  264. case VA_FOURCC_I420: {
  265. auto layout = VideoFrameLayout::CreateWithStrides(PIXEL_FORMAT_I420,
  266. src_size, strides);
  267. if (!layout.has_value()) {
  268. VLOGF(1) << "Failed to create video frame layout";
  269. return false;
  270. }
  271. src_frame = VideoFrame::WrapExternalYuvDataWithLayout(
  272. *layout, crop_rect, crop_rect.size(), data + image->offsets[0],
  273. data + image->offsets[1], data + image->offsets[2],
  274. base::TimeDelta());
  275. break;
  276. }
  277. default:
  278. VLOGF(1) << "Unsupported VA image format: "
  279. << FourccToString(image->format.fourcc);
  280. return false;
  281. }
  282. if (!src_frame) {
  283. VLOGF(1) << "Failed to create video frame";
  284. return false;
  285. }
  286. CreateImageProcessor(src_frame.get(), video_frame.get());
  287. if (!image_processor_) {
  288. VLOGF(1) << "Failed to create image processor";
  289. return false;
  290. }
  291. image_processor_->Process(
  292. std::move(src_frame), std::move(video_frame),
  293. base::BindOnce(
  294. [](scoped_refptr<base::SingleThreadTaskRunner> runner,
  295. base::OnceClosure cb, scoped_refptr<VideoFrame> frame) {
  296. runner->PostTask(FROM_HERE, std::move(cb));
  297. },
  298. task_runner_,
  299. base::BindOnce(&VaapiMjpegDecodeAccelerator::VideoFrameReady,
  300. weak_this_factory_.GetWeakPtr(), task_id)));
  301. return true;
  302. }
  303. void VaapiMjpegDecodeAccelerator::OnImageProcessorError() {
  304. DCHECK(decoder_task_runner_->BelongsToCurrentThread());
  305. VLOGF(1) << "Failed to process frames using the libyuv image processor";
  306. NotifyError(kInvalidTaskId, PLATFORM_FAILURE);
  307. image_processor_.reset();
  308. }
  309. bool VaapiMjpegDecodeAccelerator::OutputPictureVppOnTaskRunner(
  310. int32_t task_id,
  311. const ScopedVASurface* surface,
  312. scoped_refptr<VideoFrame> video_frame,
  313. const gfx::Rect& crop_rect) {
  314. DCHECK(decoder_task_runner_->BelongsToCurrentThread());
  315. DCHECK(surface);
  316. DCHECK(video_frame);
  317. DCHECK(gfx::Rect(surface->size()).Contains(crop_rect));
  318. TRACE_EVENT1("jpeg", __func__, "task_id", task_id);
  319. scoped_refptr<gfx::NativePixmap> pixmap =
  320. CreateNativePixmapDmaBuf(video_frame.get());
  321. if (!pixmap) {
  322. VLOGF(1) << "Failed to create NativePixmap from VideoFrame";
  323. return false;
  324. }
  325. // Bind a VA surface to |video_frame|.
  326. scoped_refptr<VASurface> output_surface =
  327. vpp_vaapi_wrapper_->CreateVASurfaceForPixmap(std::move(pixmap));
  328. if (!output_surface) {
  329. VLOGF(1) << "Cannot create VA surface for output buffer";
  330. return false;
  331. }
  332. scoped_refptr<VASurface> src_surface = base::MakeRefCounted<VASurface>(
  333. surface->id(), surface->size(), surface->format(),
  334. /*release_cb=*/base::DoNothing());
  335. // We should call vaSyncSurface() when passing surface between contexts, but
  336. // on Intel platform, we don't have to call vaSyncSurface() because the
  337. // underlying drivers handle synchronization between different contexts. See:
  338. // https://lists.01.org/hyperkitty/list/intel-vaapi-media@lists.01.org/message/YNFLDHHHQM2ZBFPMH7D3U6GLMOELHPFL/
  339. const bool is_intel_backend =
  340. VaapiWrapper::GetImplementationType() == VAImplementation::kIntelI965 ||
  341. VaapiWrapper::GetImplementationType() == VAImplementation::kIntelIHD;
  342. if (!is_intel_backend && !vpp_vaapi_wrapper_->SyncSurface(surface->id())) {
  343. VLOGF(1) << "Cannot sync VPP input surface";
  344. return false;
  345. }
  346. if (!vpp_vaapi_wrapper_->BlitSurface(*src_surface, *output_surface,
  347. crop_rect)) {
  348. VLOGF(1) << "Cannot convert decoded image into output buffer";
  349. return false;
  350. }
  351. // Sync target surface since the buffer is returning to client.
  352. if (!vpp_vaapi_wrapper_->SyncSurface(output_surface->id())) {
  353. VLOGF(1) << "Cannot sync VPP output surface";
  354. return false;
  355. }
  356. task_runner_->PostTask(
  357. FROM_HERE, base::BindOnce(&VaapiMjpegDecodeAccelerator::VideoFrameReady,
  358. weak_this_factory_.GetWeakPtr(), task_id));
  359. return true;
  360. }
  361. void VaapiMjpegDecodeAccelerator::DecodeFromShmTask(
  362. int32_t task_id,
  363. base::WritableSharedMemoryMapping mapping,
  364. scoped_refptr<VideoFrame> dst_frame) {
  365. DVLOGF(4);
  366. DCHECK(decoder_task_runner_->BelongsToCurrentThread());
  367. TRACE_EVENT0("jpeg", __func__);
  368. auto src_image = mapping.GetMemoryAsSpan<uint8_t>();
  369. DecodeImpl(task_id, src_image, std::move(dst_frame));
  370. }
  371. void VaapiMjpegDecodeAccelerator::DecodeFromDmaBufTask(
  372. int32_t task_id,
  373. base::ScopedFD src_dmabuf_fd,
  374. size_t src_size,
  375. off_t src_offset,
  376. scoped_refptr<VideoFrame> dst_frame) {
  377. DVLOGF(4);
  378. DCHECK(decoder_task_runner_->BelongsToCurrentThread());
  379. TRACE_EVENT0("jpeg", __func__);
  380. // The DMA-buf FD should be mapped as read-only since it may only have read
  381. // permission, e.g. when it comes from camera driver.
  382. DCHECK(src_dmabuf_fd.is_valid());
  383. DCHECK_GT(src_size, 0u);
  384. void* src_addr = mmap(nullptr, src_size, PROT_READ, MAP_SHARED,
  385. src_dmabuf_fd.get(), src_offset);
  386. if (src_addr == MAP_FAILED) {
  387. VPLOGF(1) << "Failed to map input DMA buffer";
  388. NotifyError(task_id, UNREADABLE_INPUT);
  389. return;
  390. }
  391. base::span<const uint8_t> src_image =
  392. base::make_span(static_cast<const uint8_t*>(src_addr), src_size);
  393. DecodeImpl(task_id, src_image, std::move(dst_frame));
  394. const int ret = munmap(src_addr, src_size);
  395. DPCHECK(ret == 0);
  396. }
  397. void VaapiMjpegDecodeAccelerator::DecodeImpl(
  398. int32_t task_id,
  399. base::span<const uint8_t> src_image,
  400. scoped_refptr<VideoFrame> dst_frame) {
  401. VaapiImageDecodeStatus status = decoder_->Decode(src_image);
  402. if (status != VaapiImageDecodeStatus::kSuccess) {
  403. VLOGF(1) << "Failed to decode JPEG image";
  404. NotifyError(task_id, VaapiJpegDecodeStatusToError(status));
  405. return;
  406. }
  407. const ScopedVASurface* surface = decoder_->GetScopedVASurface();
  408. DCHECK(surface);
  409. DCHECK(surface->IsValid());
  410. // For camera captures, we assume that the visible size is the same as the
  411. // coded size.
  412. if (dst_frame->visible_rect().size() != dst_frame->coded_size() ||
  413. dst_frame->visible_rect().x() != 0 ||
  414. dst_frame->visible_rect().y() != 0) {
  415. VLOGF(1)
  416. << "The video frame visible size should be the same as the coded size";
  417. NotifyError(task_id, INVALID_ARGUMENT);
  418. return;
  419. }
  420. // Note that |surface->size()| is the visible size of the JPEG image. The
  421. // underlying VASurface size (coded size) can be larger because of alignments.
  422. if (surface->size().width() < dst_frame->visible_rect().width() ||
  423. surface->size().height() < dst_frame->visible_rect().height()) {
  424. VLOGF(1) << "Invalid JPEG image and video frame sizes: "
  425. << surface->size().ToString() << ", "
  426. << dst_frame->visible_rect().size().ToString();
  427. NotifyError(task_id, INVALID_ARGUMENT);
  428. return;
  429. }
  430. // For DMA-buf backed |dst_frame|, we will import it as a VA surface and use
  431. // VPP to convert the decoded |surface| into it, if the formats and sizes are
  432. // supported.
  433. const auto dst_frame_fourcc =
  434. Fourcc::FromVideoPixelFormat(dst_frame->format());
  435. if (!dst_frame_fourcc) {
  436. VLOGF(1) << "Unsupported video frame format: " << dst_frame->format();
  437. NotifyError(task_id, PLATFORM_FAILURE);
  438. return;
  439. }
  440. const auto dst_frame_va_fourcc = dst_frame_fourcc->ToVAFourCC();
  441. if (!dst_frame_va_fourcc) {
  442. VLOGF(1) << "Unsupported video frame format: " << dst_frame->format();
  443. NotifyError(task_id, PLATFORM_FAILURE);
  444. return;
  445. }
  446. // Crop and scale the decoded image into |dst_frame|.
  447. // The VPP is known to have some problems with odd-sized buffers, so we
  448. // request a crop rectangle whose dimensions are aligned to 2.
  449. const gfx::Rect crop_rect = CropSizeForScalingToTarget(
  450. surface->size(), dst_frame->visible_rect().size(), /*alignment=*/2u);
  451. if (crop_rect.IsEmpty()) {
  452. VLOGF(1) << "Failed to calculate crop rectangle for "
  453. << surface->size().ToString() << " to "
  454. << dst_frame->visible_rect().size().ToString();
  455. NotifyError(task_id, PLATFORM_FAILURE);
  456. return;
  457. }
  458. // TODO(kamesan): move HasDmaBufs() to DCHECK when we deprecate
  459. // shared-memory-backed video frame.
  460. // Check all the sizes involved until we figure out the definition of min/max
  461. // resolutions in the VPP profile (b/195312242).
  462. if (dst_frame->HasDmaBufs() &&
  463. VaapiWrapper::IsVppResolutionAllowed(surface->size()) &&
  464. VaapiWrapper::IsVppResolutionAllowed(crop_rect.size()) &&
  465. VaapiWrapper::IsVppResolutionAllowed(dst_frame->visible_rect().size()) &&
  466. VaapiWrapper::IsVppSupportedForJpegDecodedSurfaceToFourCC(
  467. surface->format(), *dst_frame_va_fourcc)) {
  468. if (!OutputPictureVppOnTaskRunner(task_id, surface, std::move(dst_frame),
  469. crop_rect)) {
  470. VLOGF(1) << "Output picture using VPP failed";
  471. NotifyError(task_id, PLATFORM_FAILURE);
  472. }
  473. return;
  474. }
  475. // Fallback to do conversion by libyuv. This happens when:
  476. // 1. |dst_frame| is backed by shared memory.
  477. // 2. VPP doesn't support the format conversion. This is intended for AMD
  478. // VAAPI driver whose VPP only supports converting decoded 4:2:0 JPEGs.
  479. std::unique_ptr<ScopedVAImage> image =
  480. decoder_->GetImage(*dst_frame_va_fourcc, &status);
  481. if (status != VaapiImageDecodeStatus::kSuccess) {
  482. NotifyError(task_id, VaapiJpegDecodeStatusToError(status));
  483. return;
  484. }
  485. DCHECK_EQ(image->image()->width, surface->size().width());
  486. DCHECK_EQ(image->image()->height, surface->size().height());
  487. if (!OutputPictureLibYuvOnTaskRunner(task_id, std::move(image),
  488. std::move(dst_frame), crop_rect)) {
  489. VLOGF(1) << "Output picture using libyuv failed";
  490. NotifyError(task_id, PLATFORM_FAILURE);
  491. }
  492. }
  493. void VaapiMjpegDecodeAccelerator::Decode(
  494. BitstreamBuffer bitstream_buffer,
  495. scoped_refptr<VideoFrame> video_frame) {
  496. DCHECK(io_task_runner_->BelongsToCurrentThread());
  497. TRACE_EVENT1("jpeg", __func__, "input_id", bitstream_buffer.id());
  498. DVLOGF(4) << "Mapping new input buffer id: " << bitstream_buffer.id()
  499. << " size: " << bitstream_buffer.size();
  500. if (bitstream_buffer.id() < 0) {
  501. VLOGF(1) << "Invalid bitstream_buffer, id: " << bitstream_buffer.id();
  502. NotifyError(bitstream_buffer.id(), INVALID_ARGUMENT);
  503. return;
  504. }
  505. // Validate output video frame.
  506. if (!video_frame->IsMappable() && !video_frame->HasDmaBufs()) {
  507. VLOGF(1) << "Unsupported output frame storage type";
  508. NotifyError(bitstream_buffer.id(), INVALID_ARGUMENT);
  509. return;
  510. }
  511. if ((video_frame->visible_rect().width() & 1) ||
  512. (video_frame->visible_rect().height() & 1)) {
  513. VLOGF(1) << "Video frame visible size has odd dimension";
  514. NotifyError(bitstream_buffer.id(), PLATFORM_FAILURE);
  515. return;
  516. }
  517. auto region = bitstream_buffer.TakeRegion();
  518. auto mapping =
  519. region.MapAt(bitstream_buffer.offset(), bitstream_buffer.size());
  520. if (!mapping.IsValid()) {
  521. VLOGF(1) << "Failed to map input buffer";
  522. NotifyError(bitstream_buffer.id(), UNREADABLE_INPUT);
  523. return;
  524. }
  525. // It's safe to use base::Unretained(this) because |decoder_task_runner_| runs
  526. // tasks on |decoder_thread_| which is stopped in the destructor of |this|.
  527. decoder_task_runner_->PostTask(
  528. FROM_HERE, base::BindOnce(&VaapiMjpegDecodeAccelerator::DecodeFromShmTask,
  529. base::Unretained(this), bitstream_buffer.id(),
  530. std::move(mapping), std::move(video_frame)));
  531. }
  532. void VaapiMjpegDecodeAccelerator::Decode(int32_t task_id,
  533. base::ScopedFD src_dmabuf_fd,
  534. size_t src_size,
  535. off_t src_offset,
  536. scoped_refptr<VideoFrame> dst_frame) {
  537. DCHECK(io_task_runner_->BelongsToCurrentThread());
  538. TRACE_EVENT1("jpeg", __func__, "task_id", task_id);
  539. if (task_id < 0) {
  540. VLOGF(1) << "Invalid task id: " << task_id;
  541. NotifyError(task_id, INVALID_ARGUMENT);
  542. return;
  543. }
  544. // Validate input arguments.
  545. if (!src_dmabuf_fd.is_valid()) {
  546. VLOGF(1) << "Invalid input buffer FD";
  547. NotifyError(task_id, INVALID_ARGUMENT);
  548. return;
  549. }
  550. if (src_size == 0) {
  551. VLOGF(1) << "Input buffer size is zero";
  552. NotifyError(task_id, INVALID_ARGUMENT);
  553. return;
  554. }
  555. const size_t page_size = base::GetPageSize();
  556. if (src_offset < 0 || src_offset % page_size != 0) {
  557. VLOGF(1) << "Input buffer offset (" << src_offset
  558. << ") should be non-negative and aligned to page size ("
  559. << page_size << ")";
  560. NotifyError(task_id, INVALID_ARGUMENT);
  561. return;
  562. }
  563. // Validate output video frame.
  564. if (!dst_frame->IsMappable() && !dst_frame->HasDmaBufs()) {
  565. VLOGF(1) << "Unsupported output frame storage type";
  566. NotifyError(task_id, INVALID_ARGUMENT);
  567. return;
  568. }
  569. if ((dst_frame->visible_rect().width() & 1) ||
  570. (dst_frame->visible_rect().height() & 1)) {
  571. VLOGF(1) << "Output frame visible size has odd dimension";
  572. NotifyError(task_id, PLATFORM_FAILURE);
  573. return;
  574. }
  575. // It's safe to use base::Unretained(this) because |decoder_task_runner_| runs
  576. // tasks on |decoder_thread_| which is stopped in the destructor of |this|.
  577. decoder_task_runner_->PostTask(
  578. FROM_HERE,
  579. base::BindOnce(&VaapiMjpegDecodeAccelerator::DecodeFromDmaBufTask,
  580. base::Unretained(this), task_id, std::move(src_dmabuf_fd),
  581. src_size, src_offset, std::move(dst_frame)));
  582. }
  583. bool VaapiMjpegDecodeAccelerator::IsSupported() {
  584. return VaapiWrapper::IsDecodeSupported(VAProfileJPEGBaseline);
  585. }
  586. } // namespace media