camera_roll_thumbnail_decoder_impl.cc 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // Copyright 2021 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 "ash/components/phonehub/camera_roll_thumbnail_decoder_impl.h"
  5. #include "ash/components/multidevice/logging/logging.h"
  6. #include "ash/components/phonehub/camera_roll_item.h"
  7. #include "ash/components/phonehub/proto/phonehub_api.pb.h"
  8. #include "base/bind.h"
  9. #include "base/callback.h"
  10. #include "base/containers/flat_map.h"
  11. #include "base/memory/weak_ptr.h"
  12. #include "services/data_decoder/public/cpp/decode_image.h"
  13. #include "services/data_decoder/public/mojom/image_decoder.mojom.h"
  14. #include "ui/gfx/image/image.h"
  15. #include "ui/gfx/image/image_skia.h"
  16. namespace ash {
  17. namespace phonehub {
  18. namespace {
  19. using BatchDecodeCallback =
  20. ::base::OnceCallback<void(CameraRollThumbnailDecoder::BatchDecodeResult,
  21. const std::vector<CameraRollItem>&)>;
  22. } // namespace
  23. CameraRollThumbnailDecoderImpl::DecodeRequest::DecodeRequest(
  24. const proto::CameraRollItem& item_proto)
  25. : item_proto_(item_proto) {}
  26. CameraRollThumbnailDecoderImpl::DecodeRequest::~DecodeRequest() = default;
  27. const proto::CameraRollItemMetadata&
  28. CameraRollThumbnailDecoderImpl::DecodeRequest::GetMetadata() const {
  29. return item_proto_.metadata();
  30. }
  31. void CameraRollThumbnailDecoderImpl::DecodeRequest::CompleteWithDecodedBitmap(
  32. const SkBitmap& bitmap) {
  33. gfx::ImageSkia image_skia = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
  34. if (!image_skia.isNull()) {
  35. image_skia.MakeThreadSafe();
  36. decoded_thumbnail_ = gfx::Image(image_skia);
  37. }
  38. is_completed_ = true;
  39. }
  40. void CameraRollThumbnailDecoderImpl::DecodeRequest::CompleteWithExistingImage(
  41. const gfx::Image& image) {
  42. decoded_thumbnail_ = image;
  43. is_completed_ = true;
  44. }
  45. const std::string&
  46. CameraRollThumbnailDecoderImpl::DecodeRequest::GetEncodedThumbnail() const {
  47. return item_proto_.thumbnail().data();
  48. }
  49. CameraRollThumbnailDecoderImpl::DecoderDelegate::DecoderDelegate() = default;
  50. CameraRollThumbnailDecoderImpl::DecoderDelegate::~DecoderDelegate() = default;
  51. void CameraRollThumbnailDecoderImpl::DecoderDelegate::DecodeThumbnail(
  52. const DecodeRequest& request,
  53. data_decoder::DecodeImageCallback callback) {
  54. const std::string& encoded_thumbnail = request.GetEncodedThumbnail();
  55. data_decoder::DecodeImage(
  56. &data_decoder_, base::as_bytes(base::make_span(encoded_thumbnail)),
  57. data_decoder::mojom::ImageCodec::kDefault,
  58. /*shrink_to_fit=*/true, data_decoder::kDefaultMaxSizeInBytes,
  59. /*desired_image_frame_size=*/gfx::Size(), std::move(callback));
  60. }
  61. CameraRollThumbnailDecoderImpl::CameraRollThumbnailDecoderImpl()
  62. : decoder_delegate_(std::make_unique<DecoderDelegate>()) {}
  63. CameraRollThumbnailDecoderImpl::~CameraRollThumbnailDecoderImpl() = default;
  64. void CameraRollThumbnailDecoderImpl::BatchDecode(
  65. const proto::FetchCameraRollItemsResponse& response,
  66. const std::vector<CameraRollItem>& current_items,
  67. BatchDecodeCallback callback) {
  68. CancelPendingRequests();
  69. for (const proto::CameraRollItem& item_proto : response.items()) {
  70. pending_requests_.emplace_back(item_proto);
  71. }
  72. pending_callback_ = std::move(callback);
  73. base::flat_map<std::string, const gfx::Image*> existing_thumbnail_map;
  74. for (const CameraRollItem& item : current_items) {
  75. existing_thumbnail_map[item.metadata().key()] = &item.thumbnail();
  76. }
  77. for (DecodeRequest& request : pending_requests_) {
  78. if (!request.GetEncodedThumbnail().empty()) {
  79. // Thumbnail of this item is sent by the phone either because it is new or
  80. // has been changed.
  81. decoder_delegate_->DecodeThumbnail(
  82. request, base::BindOnce(
  83. &CameraRollThumbnailDecoderImpl::OnThumbnailDecoded,
  84. weak_ptr_factory_.GetWeakPtr(), request.GetMetadata()));
  85. } else if (existing_thumbnail_map.contains(request.GetMetadata().key())) {
  86. // Existing items that have not changed should have their thumbnails
  87. // already decoded.
  88. request.CompleteWithExistingImage(
  89. *(existing_thumbnail_map.at(request.GetMetadata().key())));
  90. } else {
  91. // Thumbnail for this item is not already available but it is not sent by
  92. // the phone. Most likely that this is an outdated response. Ignore it and
  93. // wait for the next response.
  94. CancelPendingRequests();
  95. return;
  96. }
  97. }
  98. // Check if all requests are already completed at this point.
  99. CheckPendingThumbnailRequests();
  100. }
  101. void CameraRollThumbnailDecoderImpl::OnThumbnailDecoded(
  102. const proto::CameraRollItemMetadata& metadata,
  103. const SkBitmap& thumbnail_bitmap) {
  104. for (DecodeRequest& request : pending_requests_) {
  105. if (request.GetMetadata().key() != metadata.key()) {
  106. continue;
  107. }
  108. request.CompleteWithDecodedBitmap(thumbnail_bitmap);
  109. CheckPendingThumbnailRequests();
  110. break;
  111. }
  112. }
  113. void CameraRollThumbnailDecoderImpl::CheckPendingThumbnailRequests() {
  114. for (const DecodeRequest& request : pending_requests_) {
  115. if (!request.is_completed()) {
  116. return;
  117. }
  118. }
  119. // All pending requests have been completed. Replace current items with this
  120. // new batch.
  121. std::vector<CameraRollItem> new_items;
  122. for (const DecodeRequest& request : pending_requests_) {
  123. // The decoded thumbnail image can be null if the thumbnail bytes cannot be
  124. // decoded.
  125. if (!request.decoded_thumbnail().IsEmpty()) {
  126. new_items.emplace_back(request.GetMetadata(),
  127. request.decoded_thumbnail());
  128. } else {
  129. PA_LOG(ERROR) << "Failed to decode thumbnail for file "
  130. << request.GetMetadata().file_name();
  131. }
  132. }
  133. pending_requests_.clear();
  134. std::move(pending_callback_).Run(BatchDecodeResult::kCompleted, new_items);
  135. }
  136. void CameraRollThumbnailDecoderImpl::CancelPendingRequests() {
  137. weak_ptr_factory_.InvalidateWeakPtrs();
  138. pending_requests_.clear();
  139. if (!pending_callback_.is_null()) {
  140. std::vector<CameraRollItem> empty_items;
  141. std::move(pending_callback_)
  142. .Run(BatchDecodeResult::kCancelled, empty_items);
  143. }
  144. }
  145. } // namespace phonehub
  146. } // namespace ash