vaapi_jpeg_encode_accelerator.cc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  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. #include "media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h"
  5. #include <stddef.h>
  6. #include <memory>
  7. #include <utility>
  8. #include "base/bind.h"
  9. #include "base/callback_helpers.h"
  10. #include "base/logging.h"
  11. #include "base/memory/shared_memory_mapping.h"
  12. #include "base/memory/unsafe_shared_memory_region.h"
  13. #include "base/metrics/histogram_functions.h"
  14. #include "base/metrics/histogram_macros.h"
  15. #include "base/numerics/safe_conversions.h"
  16. #include "base/sequence_checker.h"
  17. #include "base/task/thread_pool.h"
  18. #include "base/threading/thread_task_runner_handle.h"
  19. #include "base/trace_event/trace_event.h"
  20. #include "gpu/ipc/common/gpu_memory_buffer_support.h"
  21. #include "media/base/bind_to_current_loop.h"
  22. #include "media/base/video_frame.h"
  23. #include "media/gpu/chromeos/platform_video_frame_utils.h"
  24. #include "media/gpu/macros.h"
  25. #include "media/gpu/vaapi/vaapi_jpeg_encoder.h"
  26. #include "media/gpu/vaapi/vaapi_utils.h"
  27. #include "media/parsers/jpeg_parser.h"
  28. namespace media {
  29. namespace {
  30. // UMA results that the VaapiJpegEncodeAccelerator class reports.
  31. // These values are persisted to logs, and should therefore never be renumbered
  32. // nor reused.
  33. enum VAJEAEncoderResult {
  34. kSuccess = 0,
  35. kError,
  36. kMaxValue = kError,
  37. };
  38. static void ReportToVAJEAEncodeResultUMA(VAJEAEncoderResult result) {
  39. UMA_HISTOGRAM_ENUMERATION("Media.VAJEA.EncoderResult", result);
  40. }
  41. } // namespace
  42. VaapiJpegEncodeAccelerator::EncodeRequest::EncodeRequest(
  43. int32_t task_id,
  44. scoped_refptr<VideoFrame> video_frame,
  45. base::WritableSharedMemoryMapping exif_mapping,
  46. base::WritableSharedMemoryMapping output_mapping,
  47. int quality)
  48. : task_id(task_id),
  49. video_frame(std::move(video_frame)),
  50. exif_mapping(std::move(exif_mapping)),
  51. output_mapping(std::move(output_mapping)),
  52. quality(quality) {}
  53. VaapiJpegEncodeAccelerator::EncodeRequest::~EncodeRequest() {}
  54. class VaapiJpegEncodeAccelerator::Encoder {
  55. public:
  56. Encoder(scoped_refptr<VaapiWrapper> vaapi_wrapper,
  57. scoped_refptr<VaapiWrapper> vpp_vaapi_wrapper,
  58. base::RepeatingCallback<void(int32_t, size_t)> video_frame_ready_cb,
  59. base::RepeatingCallback<void(int32_t, Status)> notify_error_cb);
  60. Encoder(const Encoder&) = delete;
  61. Encoder& operator=(const Encoder&) = delete;
  62. ~Encoder();
  63. // Processes one encode task with DMA-buf.
  64. void EncodeWithDmaBufTask(scoped_refptr<VideoFrame> input_frame,
  65. scoped_refptr<VideoFrame> output_frame,
  66. int32_t task_id,
  67. int quality,
  68. base::WritableSharedMemoryMapping exif_mapping);
  69. // Processes one encode |request|.
  70. void EncodeTask(std::unique_ptr<EncodeRequest> request);
  71. private:
  72. std::unique_ptr<VaapiJpegEncoder> jpeg_encoder_;
  73. scoped_refptr<VaapiWrapper> vaapi_wrapper_;
  74. scoped_refptr<VaapiWrapper> vpp_vaapi_wrapper_;
  75. std::unique_ptr<gpu::GpuMemoryBufferSupport> gpu_memory_buffer_support_;
  76. // |cached_output_buffer_| is the last allocated VABuffer during EncodeTask().
  77. // If the next call to EncodeTask() does not require a buffer bigger than the
  78. // size of |cached_output_buffer_|, |cached_output_buffer_| will be reused.
  79. std::unique_ptr<ScopedVABuffer> cached_output_buffer_;
  80. base::RepeatingCallback<void(int32_t, size_t)> video_frame_ready_cb_;
  81. base::RepeatingCallback<void(int32_t, Status)> notify_error_cb_;
  82. // The current VA surface ID used for encoding. Only used for Non-DMA-buf use
  83. // case.
  84. VASurfaceID va_surface_id_;
  85. // The size of the surface associated with |va_surface_id_|.
  86. gfx::Size input_size_;
  87. // The format used to create VAContext. Only used for DMA-buf use case.
  88. uint32_t va_format_;
  89. SEQUENCE_CHECKER(sequence_checker_);
  90. };
  91. VaapiJpegEncodeAccelerator::Encoder::Encoder(
  92. scoped_refptr<VaapiWrapper> vaapi_wrapper,
  93. scoped_refptr<VaapiWrapper> vpp_vaapi_wrapper,
  94. base::RepeatingCallback<void(int32_t, size_t)> video_frame_ready_cb,
  95. base::RepeatingCallback<void(int32_t, Status)> notify_error_cb)
  96. : jpeg_encoder_(new VaapiJpegEncoder(vaapi_wrapper)),
  97. vaapi_wrapper_(std::move(vaapi_wrapper)),
  98. vpp_vaapi_wrapper_(std::move(vpp_vaapi_wrapper)),
  99. gpu_memory_buffer_support_(new gpu::GpuMemoryBufferSupport()),
  100. video_frame_ready_cb_(std::move(video_frame_ready_cb)),
  101. notify_error_cb_(std::move(notify_error_cb)),
  102. va_surface_id_(VA_INVALID_SURFACE),
  103. input_size_(gfx::Size()),
  104. va_format_(0) {}
  105. VaapiJpegEncodeAccelerator::Encoder::~Encoder() {
  106. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  107. // Destroy ScopedVABuffer before VaapiWrappers are destroyed to ensure
  108. // VADisplay is valid on ScopedVABuffer's destruction.
  109. cached_output_buffer_.reset();
  110. }
  111. void VaapiJpegEncodeAccelerator::Encoder::EncodeWithDmaBufTask(
  112. scoped_refptr<VideoFrame> input_frame,
  113. scoped_refptr<VideoFrame> output_frame,
  114. int32_t task_id,
  115. int quality,
  116. base::WritableSharedMemoryMapping exif_mapping) {
  117. DVLOGF(4);
  118. TRACE_EVENT0("jpeg", "EncodeWithDmaBufTask");
  119. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  120. gfx::Size input_size = input_frame->coded_size();
  121. gfx::BufferFormat buffer_format = gfx::BufferFormat::YUV_420_BIPLANAR;
  122. uint32_t va_format = VaapiWrapper::BufferFormatToVARTFormat(buffer_format);
  123. bool context_changed = input_size != input_size_ || va_format != va_format_;
  124. if (context_changed) {
  125. vaapi_wrapper_->DestroyContextAndSurfaces(
  126. std::vector<VASurfaceID>({va_surface_id_}));
  127. va_surface_id_ = VA_INVALID_SURFACE;
  128. va_format_ = 0;
  129. input_size_ = gfx::Size();
  130. std::vector<VASurfaceID> va_surfaces;
  131. if (!vaapi_wrapper_->CreateContextAndSurfaces(
  132. va_format, input_size, {VaapiWrapper::SurfaceUsageHint::kGeneric},
  133. 1, &va_surfaces)) {
  134. VLOGF(1) << "Failed to create VA surface";
  135. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  136. return;
  137. }
  138. va_surface_id_ = va_surfaces[0];
  139. va_format_ = va_format;
  140. input_size_ = input_size;
  141. }
  142. DCHECK(input_frame);
  143. scoped_refptr<gfx::NativePixmap> pixmap =
  144. CreateNativePixmapDmaBuf(input_frame.get());
  145. if (!pixmap) {
  146. VLOGF(1) << "Failed to create NativePixmap from VideoFrame";
  147. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  148. return;
  149. }
  150. // We need to explicitly blit the bound input surface here to make sure the
  151. // input we sent to VAAPI encoder is in tiled NV12 format since implicit
  152. // tiling logic is not contained in every driver.
  153. auto input_surface =
  154. vpp_vaapi_wrapper_->CreateVASurfaceForPixmap(std::move(pixmap));
  155. if (!input_surface) {
  156. VLOGF(1) << "Failed to create input va surface";
  157. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  158. return;
  159. }
  160. auto blit_surface =
  161. base::MakeRefCounted<VASurface>(va_surface_id_, input_size, va_format,
  162. base::DoNothing() /* release_cb */);
  163. if (!vpp_vaapi_wrapper_->BlitSurface(*input_surface, *blit_surface)) {
  164. VLOGF(1) << "Failed to blit surfaces";
  165. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  166. return;
  167. }
  168. // We should call vaSyncSurface() when passing surface between contexts. See:
  169. // https://lists.01.org/pipermail/intel-vaapi-media/2019-June/000131.html
  170. // Sync |blit_surface| since it it passing to the JPEG encoding context.
  171. if (!vpp_vaapi_wrapper_->SyncSurface(blit_surface->id())) {
  172. VLOGF(1) << "Cannot sync VPP output surface";
  173. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  174. return;
  175. }
  176. // Create output buffer for encoding result.
  177. size_t max_coded_buffer_size =
  178. VaapiJpegEncoder::GetMaxCodedBufferSize(input_size);
  179. if (context_changed || !cached_output_buffer_ ||
  180. cached_output_buffer_->size() < max_coded_buffer_size) {
  181. cached_output_buffer_.reset();
  182. auto output_buffer = vaapi_wrapper_->CreateVABuffer(VAEncCodedBufferType,
  183. max_coded_buffer_size);
  184. if (!output_buffer) {
  185. VLOGF(1) << "Failed to create VA buffer for encoding output";
  186. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  187. return;
  188. }
  189. cached_output_buffer_ = std::move(output_buffer);
  190. }
  191. // Prepare exif.
  192. const uint8_t* exif_buffer = nullptr;
  193. size_t exif_buffer_size = 0;
  194. if (exif_mapping.IsValid()) {
  195. exif_buffer = exif_mapping.GetMemoryAs<uint8_t>();
  196. exif_buffer_size = exif_mapping.size();
  197. }
  198. if (!jpeg_encoder_->Encode(input_size, /*exif_buffer=*/nullptr,
  199. /*exif_buffer_size=*/0u, quality,
  200. blit_surface->id(), cached_output_buffer_->id(),
  201. /*exif_offset=*/nullptr)) {
  202. VLOGF(1) << "Encode JPEG failed";
  203. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  204. return;
  205. }
  206. // Create gmb buffer from output VideoFrame. Since the JPEG VideoFrame's coded
  207. // size is the 2D image size, we should use (buffer_size, 1) as the R8 gmb's
  208. // size, where buffer_size can be obtained from the first plane's size.
  209. auto output_gmb_handle = CreateGpuMemoryBufferHandle(output_frame.get());
  210. DCHECK(!output_gmb_handle.is_null());
  211. // In this case, we use the R_8 buffer with height == 1 to represent a data
  212. // container. As a result, we use plane.stride as size of the data here since
  213. // plane.size might be larger due to height alignment.
  214. const gfx::Size output_gmb_buffer_size(
  215. base::checked_cast<int32_t>(output_frame->layout().planes()[0].stride),
  216. 1);
  217. auto output_gmb_buffer =
  218. gpu_memory_buffer_support_->CreateGpuMemoryBufferImplFromHandle(
  219. std::move(output_gmb_handle), output_gmb_buffer_size,
  220. gfx::BufferFormat::R_8, gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE,
  221. base::DoNothing());
  222. if (output_gmb_buffer == nullptr) {
  223. VLOGF(1) << "Failed to create GpuMemoryBufferImpl from handle";
  224. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  225. return;
  226. }
  227. const bool is_mapped = output_gmb_buffer->Map();
  228. if (!is_mapped) {
  229. VLOGF(1) << "Map the output gmb buffer failed";
  230. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  231. return;
  232. }
  233. base::ScopedClosureRunner output_gmb_buffer_unmapper(base::BindOnce(
  234. &gfx::GpuMemoryBuffer::Unmap, base::Unretained(output_gmb_buffer.get())));
  235. // Get the encoded output. DownloadFromVABuffer() is a blocking call. It
  236. // would wait until encoding is finished.
  237. uint8_t* output_memory = static_cast<uint8_t*>(output_gmb_buffer->memory(0));
  238. size_t encoded_size = 0;
  239. // Since the format of |output_gmb_buffer| is gfx::BufferFormat::R_8, we can
  240. // use its area as the maximum bytes we need to download to avoid buffer
  241. // overflow.
  242. // Since we didn't supply EXIF data to the JPEG encoder, it creates a default
  243. // APP0 segment in the header. We will download the result to an offset and
  244. // replace the APP0 segment by APP1 including EXIF data:
  245. // SOI + APP0 (2 + 2 + 14 bytes) + other data
  246. // -> SOI + APP1 (2 + 2 + |exif_buffer_size| bytes) + other data
  247. // Note that |exif_buffer_size| >= 14 since EXIF + TIFF headers are 14 bytes,
  248. // and <= (2^16-1)-2 since APP1 data size is stored in 2 bytes.
  249. // TODO(b/171369066, b/171340559): Remove this workaround when Intel iHD
  250. // driver has fixed the EXIF handling.
  251. constexpr size_t kApp0DataSize = 14;
  252. constexpr size_t kMaxExifSize = ((1u << 16) - 1) - 2;
  253. if (exif_buffer_size > 0 &&
  254. (exif_buffer_size < kApp0DataSize || exif_buffer_size > kMaxExifSize)) {
  255. VLOGF(1) << "Unexpected EXIF data size (" << exif_buffer_size << ")";
  256. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  257. return;
  258. }
  259. const size_t output_offset =
  260. exif_buffer_size > 0 ? exif_buffer_size - kApp0DataSize : 0;
  261. const size_t output_size =
  262. base::checked_cast<size_t>(output_gmb_buffer->GetSize().GetArea());
  263. if (output_offset >= output_size) {
  264. VLOGF(1) << "Output buffer size (" << output_size << ") is too small";
  265. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  266. return;
  267. }
  268. uint8_t* frame_content = output_memory + output_offset;
  269. const size_t max_frame_size = output_size - output_offset;
  270. if (!vaapi_wrapper_->DownloadFromVABuffer(cached_output_buffer_->id(),
  271. blit_surface->id(), frame_content,
  272. max_frame_size, &encoded_size)) {
  273. VLOGF(1) << "Failed to retrieve output image from VA coded buffer";
  274. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  275. return;
  276. }
  277. CHECK_LE(encoded_size, max_frame_size);
  278. if (exif_buffer_size > 0) {
  279. // Check the output header is 2+2+14 bytes APP0 as expected.
  280. constexpr uint8_t kJpegSoiAndApp0Header[] = {
  281. 0xFF, JPEG_SOI, 0xFF, JPEG_APP0, 0x00, 0x10,
  282. };
  283. if (encoded_size < std::size(kJpegSoiAndApp0Header)) {
  284. VLOGF(1) << "Unexpected JPEG data size received from encoder";
  285. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  286. return;
  287. }
  288. for (size_t i = 0; i < std::size(kJpegSoiAndApp0Header); ++i) {
  289. if (frame_content[i] != kJpegSoiAndApp0Header[i]) {
  290. VLOGF(1) << "Unexpected JPEG header received from encoder";
  291. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  292. return;
  293. }
  294. }
  295. // Copy the EXIF data into preserved space.
  296. const uint8_t jpeg_soi_and_app1_header[] = {
  297. 0xFF,
  298. JPEG_SOI,
  299. 0xFF,
  300. JPEG_APP1,
  301. static_cast<uint8_t>((exif_buffer_size + 2) / 256),
  302. static_cast<uint8_t>((exif_buffer_size + 2) % 256),
  303. };
  304. CHECK_GE(output_size, std::size(jpeg_soi_and_app1_header));
  305. if (exif_buffer_size > output_size - std::size(jpeg_soi_and_app1_header)) {
  306. VLOGF(1) << "Insufficient buffer size reserved for JPEG APP1 data";
  307. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  308. return;
  309. }
  310. memcpy(output_memory, jpeg_soi_and_app1_header,
  311. std::size(jpeg_soi_and_app1_header));
  312. memcpy(output_memory + std::size(jpeg_soi_and_app1_header), exif_buffer,
  313. exif_buffer_size);
  314. encoded_size += output_offset;
  315. }
  316. video_frame_ready_cb_.Run(task_id, encoded_size);
  317. }
  318. void VaapiJpegEncodeAccelerator::Encoder::EncodeTask(
  319. std::unique_ptr<EncodeRequest> request) {
  320. DVLOGF(4);
  321. TRACE_EVENT0("jpeg", "EncodeTask");
  322. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  323. const int task_id = request->task_id;
  324. gfx::Size input_size = request->video_frame->coded_size();
  325. // Recreate VASurface if the video frame's size changed.
  326. bool context_changed =
  327. input_size != input_size_ || va_surface_id_ == VA_INVALID_SURFACE;
  328. if (context_changed) {
  329. vaapi_wrapper_->DestroyContextAndSurfaces(
  330. std::vector<VASurfaceID>({va_surface_id_}));
  331. va_surface_id_ = VA_INVALID_SURFACE;
  332. input_size_ = gfx::Size();
  333. std::vector<VASurfaceID> va_surfaces;
  334. if (!vaapi_wrapper_->CreateContextAndSurfaces(
  335. VA_RT_FORMAT_YUV420, input_size,
  336. {VaapiWrapper::SurfaceUsageHint::kGeneric}, 1, &va_surfaces)) {
  337. VLOGF(1) << "Failed to create VA surface";
  338. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  339. return;
  340. }
  341. va_surface_id_ = va_surfaces[0];
  342. input_size_ = input_size;
  343. }
  344. if (!vaapi_wrapper_->UploadVideoFrameToSurface(*request->video_frame,
  345. va_surface_id_, input_size_)) {
  346. VLOGF(1) << "Failed to upload video frame to VA surface";
  347. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  348. return;
  349. }
  350. // Create output buffer for encoding result.
  351. size_t max_coded_buffer_size =
  352. VaapiJpegEncoder::GetMaxCodedBufferSize(input_size);
  353. if (context_changed || !cached_output_buffer_ ||
  354. cached_output_buffer_->size() < max_coded_buffer_size) {
  355. cached_output_buffer_.reset();
  356. auto output_buffer = vaapi_wrapper_->CreateVABuffer(VAEncCodedBufferType,
  357. max_coded_buffer_size);
  358. if (!output_buffer) {
  359. VLOGF(1) << "Failed to create VA buffer for encoding output";
  360. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  361. return;
  362. }
  363. cached_output_buffer_ = std::move(output_buffer);
  364. }
  365. uint8_t* exif_buffer = nullptr;
  366. size_t exif_buffer_size = 0;
  367. if (request->exif_mapping.IsValid()) {
  368. exif_buffer = request->exif_mapping.GetMemoryAs<uint8_t>();
  369. exif_buffer_size = request->exif_mapping.size();
  370. }
  371. // When the exif buffer contains a thumbnail, the VAAPI encoder would
  372. // generate a corrupted JPEG. We can work around the problem by supplying an
  373. // all-zero buffer with the same size and fill in the real exif buffer after
  374. // encoding.
  375. // TODO(shenghao): Remove this mechanism after b/79840013 is fixed.
  376. std::vector<uint8_t> exif_buffer_dummy(exif_buffer_size, 0);
  377. size_t exif_offset = 0;
  378. if (!jpeg_encoder_->Encode(input_size, exif_buffer_dummy.data(),
  379. exif_buffer_size, request->quality, va_surface_id_,
  380. cached_output_buffer_->id(), &exif_offset)) {
  381. VLOGF(1) << "Encode JPEG failed";
  382. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  383. return;
  384. }
  385. // Get the encoded output. DownloadFromVABuffer() is a blocking call. It
  386. // would wait until encoding is finished.
  387. size_t encoded_size = 0;
  388. if (!vaapi_wrapper_->DownloadFromVABuffer(
  389. cached_output_buffer_->id(), va_surface_id_,
  390. request->output_mapping.GetMemoryAs<uint8_t>(),
  391. request->output_mapping.size(), &encoded_size)) {
  392. VLOGF(1) << "Failed to retrieve output image from VA coded buffer";
  393. notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
  394. return;
  395. }
  396. // Copy the real exif buffer into preserved space.
  397. memcpy(request->output_mapping.GetMemoryAs<uint8_t>() + exif_offset,
  398. exif_buffer, exif_buffer_size);
  399. video_frame_ready_cb_.Run(task_id, encoded_size);
  400. }
  401. VaapiJpegEncodeAccelerator::VaapiJpegEncodeAccelerator(
  402. scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
  403. : task_runner_(base::ThreadTaskRunnerHandle::Get()),
  404. io_task_runner_(std::move(io_task_runner)),
  405. encoder_thread_("VaapiJpegEncoderThread"),
  406. weak_this_factory_(this) {
  407. VLOGF(2);
  408. weak_this_ = weak_this_factory_.GetWeakPtr();
  409. }
  410. // Destroy |encoder_| on |encoder_thread_|.
  411. void VaapiJpegEncodeAccelerator::CleanUpOnEncoderThread() {
  412. DCHECK(encoder_task_runner_->BelongsToCurrentThread());
  413. encoder_.reset();
  414. }
  415. VaapiJpegEncodeAccelerator::~VaapiJpegEncodeAccelerator() {
  416. DCHECK(task_runner_->BelongsToCurrentThread());
  417. VLOGF(2) << "Destroying VaapiJpegEncodeAccelerator";
  418. weak_this_factory_.InvalidateWeakPtrs();
  419. // base::Unretained() is fine here because we control |encoder_task_runner_|
  420. // lifetime.
  421. if (encoder_task_runner_) {
  422. encoder_task_runner_->PostTask(
  423. FROM_HERE,
  424. base::BindOnce(&VaapiJpegEncodeAccelerator::CleanUpOnEncoderThread,
  425. base::Unretained(this)));
  426. }
  427. encoder_thread_.Stop();
  428. }
  429. void VaapiJpegEncodeAccelerator::NotifyError(int32_t task_id, Status status) {
  430. DCHECK(task_runner_->BelongsToCurrentThread());
  431. VLOGF(1) << "task_id=" << task_id << ", status=" << status;
  432. DCHECK(client_);
  433. client_->NotifyError(task_id, status);
  434. }
  435. void VaapiJpegEncodeAccelerator::VideoFrameReady(int32_t task_id,
  436. size_t encoded_picture_size) {
  437. DVLOGF(4) << "task_id=" << task_id << ", size=" << encoded_picture_size;
  438. DCHECK(task_runner_->BelongsToCurrentThread());
  439. ReportToVAJEAEncodeResultUMA(VAJEAEncoderResult::kSuccess);
  440. client_->VideoFrameReady(task_id, encoded_picture_size);
  441. }
  442. void VaapiJpegEncodeAccelerator::InitializeOnEncoderTaskRunner(
  443. chromeos_camera::JpegEncodeAccelerator::InitCB init_cb) {
  444. DCHECK(encoder_task_runner_->BelongsToCurrentThread());
  445. if (!VaapiWrapper::IsJpegEncodeSupported()) {
  446. VLOGF(1) << "Jpeg encoder is not supported.";
  447. std::move(init_cb).Run(HW_JPEG_ENCODE_NOT_SUPPORTED);
  448. return;
  449. }
  450. scoped_refptr<VaapiWrapper> vaapi_wrapper = VaapiWrapper::Create(
  451. VaapiWrapper::kEncodeConstantBitrate, VAProfileJPEGBaseline,
  452. EncryptionScheme::kUnencrypted,
  453. base::BindRepeating(&ReportVaapiErrorToUMA,
  454. "Media.VaapiJpegEncodeAccelerator.VAAPIError"));
  455. if (!vaapi_wrapper) {
  456. VLOGF(1) << "Failed initializing VAAPI";
  457. std::move(init_cb).Run(PLATFORM_FAILURE);
  458. return;
  459. }
  460. scoped_refptr<VaapiWrapper> vpp_vaapi_wrapper = VaapiWrapper::Create(
  461. VaapiWrapper::kVideoProcess, VAProfileNone,
  462. EncryptionScheme::kUnencrypted,
  463. base::BindRepeating(&ReportVaapiErrorToUMA,
  464. "Media.VaapiJpegEncodeAccelerator.Vpp.VAAPIError"));
  465. if (!vpp_vaapi_wrapper) {
  466. VLOGF(1) << "Failed initializing VAAPI wrapper for VPP";
  467. std::move(init_cb).Run(PLATFORM_FAILURE);
  468. return;
  469. }
  470. // Size is irrelevant for a VPP context.
  471. if (!vpp_vaapi_wrapper->CreateContext(gfx::Size())) {
  472. VLOGF(1) << "Failed to create context for VPP";
  473. std::move(init_cb).Run(PLATFORM_FAILURE);
  474. return;
  475. }
  476. encoder_ = std::make_unique<Encoder>(
  477. std::move(vaapi_wrapper), std::move(vpp_vaapi_wrapper),
  478. BindPostTask(
  479. task_runner_,
  480. base::BindRepeating(&VaapiJpegEncodeAccelerator::VideoFrameReady,
  481. weak_this_)),
  482. BindPostTask(task_runner_,
  483. base::BindRepeating(&VaapiJpegEncodeAccelerator::NotifyError,
  484. weak_this_)));
  485. std::move(init_cb).Run(ENCODE_OK);
  486. }
  487. void VaapiJpegEncodeAccelerator::InitializeOnTaskRunner(
  488. chromeos_camera::JpegEncodeAccelerator::Client* client,
  489. chromeos_camera::JpegEncodeAccelerator::InitCB init_cb) {
  490. DCHECK(task_runner_->BelongsToCurrentThread());
  491. client_ = client;
  492. if (!encoder_thread_.Start()) {
  493. VLOGF(1) << "Failed to start encoding thread.";
  494. std::move(init_cb).Run(THREAD_CREATION_FAILED);
  495. return;
  496. }
  497. encoder_task_runner_ = encoder_thread_.task_runner();
  498. DCHECK(encoder_task_runner_);
  499. // base::Unretained() is fine here because we control |encoder_task_runner_|
  500. // lifetime.
  501. encoder_task_runner_->PostTask(
  502. FROM_HERE,
  503. base::BindOnce(&VaapiJpegEncodeAccelerator::InitializeOnEncoderTaskRunner,
  504. base::Unretained(this), std::move(init_cb)));
  505. }
  506. void VaapiJpegEncodeAccelerator::InitializeAsync(
  507. chromeos_camera::JpegEncodeAccelerator::Client* client,
  508. chromeos_camera::JpegEncodeAccelerator::InitCB init_cb) {
  509. VLOGF(2);
  510. DCHECK(task_runner_->BelongsToCurrentThread());
  511. // To guarantee that the caller receives an asynchronous call after the
  512. // return path, we are making use of InitializeOnTaskRunner.
  513. task_runner_->PostTask(
  514. FROM_HERE,
  515. base::BindOnce(&VaapiJpegEncodeAccelerator::InitializeOnTaskRunner,
  516. weak_this_, client,
  517. BindToCurrentLoop(std::move(init_cb))));
  518. }
  519. size_t VaapiJpegEncodeAccelerator::GetMaxCodedBufferSize(
  520. const gfx::Size& picture_size) {
  521. return VaapiJpegEncoder::GetMaxCodedBufferSize(picture_size);
  522. }
  523. void VaapiJpegEncodeAccelerator::Encode(scoped_refptr<VideoFrame> video_frame,
  524. int quality,
  525. BitstreamBuffer* exif_buffer,
  526. BitstreamBuffer output_buffer) {
  527. DVLOGF(4);
  528. DCHECK(io_task_runner_->BelongsToCurrentThread());
  529. int32_t task_id = output_buffer.id();
  530. TRACE_EVENT1("jpeg", "Encode", "task_id", task_id);
  531. // TODO(shenghao): support other YUV formats.
  532. if (video_frame->format() != VideoPixelFormat::PIXEL_FORMAT_I420) {
  533. VLOGF(1) << "Unsupported input format: " << video_frame->format();
  534. task_runner_->PostTask(
  535. FROM_HERE, base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError,
  536. weak_this_, task_id, INVALID_ARGUMENT));
  537. return;
  538. }
  539. base::WritableSharedMemoryMapping exif_mapping;
  540. if (exif_buffer) {
  541. base::UnsafeSharedMemoryRegion exif_region = exif_buffer->TakeRegion();
  542. exif_mapping =
  543. exif_region.MapAt(exif_buffer->offset(), exif_buffer->size());
  544. if (!exif_mapping.IsValid()) {
  545. VLOGF(1) << "Failed to map exif buffer";
  546. task_runner_->PostTask(
  547. FROM_HERE, base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError,
  548. weak_this_, task_id, PLATFORM_FAILURE));
  549. return;
  550. }
  551. if (exif_mapping.size() > kMaxMarkerSizeAllowed) {
  552. VLOGF(1) << "Exif buffer too big: " << exif_mapping.size();
  553. task_runner_->PostTask(
  554. FROM_HERE, base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError,
  555. weak_this_, task_id, INVALID_ARGUMENT));
  556. return;
  557. }
  558. }
  559. base::UnsafeSharedMemoryRegion output_region = output_buffer.TakeRegion();
  560. base::WritableSharedMemoryMapping output_mapping =
  561. output_region.MapAt(output_buffer.offset(), output_buffer.size());
  562. if (!output_mapping.IsValid()) {
  563. VLOGF(1) << "Failed to map output buffer";
  564. task_runner_->PostTask(
  565. FROM_HERE,
  566. base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError, weak_this_,
  567. task_id, INACCESSIBLE_OUTPUT_BUFFER));
  568. return;
  569. }
  570. auto request = std::make_unique<EncodeRequest>(
  571. task_id, std::move(video_frame), std::move(exif_mapping),
  572. std::move(output_mapping), quality);
  573. encoder_task_runner_->PostTask(
  574. FROM_HERE,
  575. base::BindOnce(&VaapiJpegEncodeAccelerator::Encoder::EncodeTask,
  576. base::Unretained(encoder_.get()), std::move(request)));
  577. }
  578. void VaapiJpegEncodeAccelerator::EncodeWithDmaBuf(
  579. scoped_refptr<VideoFrame> input_frame,
  580. scoped_refptr<VideoFrame> output_frame,
  581. int quality,
  582. int32_t task_id,
  583. BitstreamBuffer* exif_buffer) {
  584. DVLOGF(4);
  585. DCHECK(io_task_runner_->BelongsToCurrentThread());
  586. TRACE_EVENT1("jpeg", "Encode", "task_id", task_id);
  587. // TODO(wtlee): Supports other formats.
  588. if (input_frame->format() != VideoPixelFormat::PIXEL_FORMAT_NV12) {
  589. VLOGF(1) << "Unsupported input format: " << input_frame->format();
  590. task_runner_->PostTask(
  591. FROM_HERE, base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError,
  592. weak_this_, task_id, INVALID_ARGUMENT));
  593. return;
  594. }
  595. if (output_frame->format() != VideoPixelFormat::PIXEL_FORMAT_MJPEG) {
  596. VLOGF(1) << "Unsupported output format: " << output_frame->format();
  597. task_runner_->PostTask(
  598. FROM_HERE, base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError,
  599. weak_this_, task_id, INVALID_ARGUMENT));
  600. return;
  601. }
  602. base::WritableSharedMemoryMapping exif_mapping;
  603. if (exif_buffer) {
  604. base::UnsafeSharedMemoryRegion exif_region = exif_buffer->TakeRegion();
  605. exif_mapping =
  606. exif_region.MapAt(exif_buffer->offset(), exif_buffer->size());
  607. if (!exif_mapping.IsValid()) {
  608. LOG(ERROR) << "Failed to map exif buffer";
  609. task_runner_->PostTask(
  610. FROM_HERE, base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError,
  611. weak_this_, task_id, PLATFORM_FAILURE));
  612. return;
  613. }
  614. if (exif_mapping.size() > kMaxMarkerSizeAllowed) {
  615. LOG(ERROR) << "Exif buffer too big: " << exif_mapping.size();
  616. task_runner_->PostTask(
  617. FROM_HERE, base::BindOnce(&VaapiJpegEncodeAccelerator::NotifyError,
  618. weak_this_, task_id, INVALID_ARGUMENT));
  619. return;
  620. }
  621. }
  622. encoder_task_runner_->PostTask(
  623. FROM_HERE,
  624. base::BindOnce(&VaapiJpegEncodeAccelerator::Encoder::EncodeWithDmaBufTask,
  625. base::Unretained(encoder_.get()), input_frame,
  626. output_frame, task_id, quality, std::move(exif_mapping)));
  627. }
  628. } // namespace media