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