blob: ee4075fd9cdf7f1fe2a7e1801d41774c3c88cd1c [file] [log] [blame]
Chong Zhangea280cb2017-08-02 11:10:10 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
Liz Kammer3ec5a0b2021-07-21 09:30:07 -040018#include "include/HeifDecoderAPI.h"
Chong Zhangea280cb2017-08-02 11:10:10 -070019#define LOG_TAG "HeifDecoderImpl"
20
21#include "HeifDecoderImpl.h"
22
23#include <stdio.h>
24
Marco Nelissendab79b32019-11-18 08:25:47 -080025#include <android/IDataSource.h>
Chong Zhangea280cb2017-08-02 11:10:10 -070026#include <binder/IMemory.h>
Dongwon Kang49ce6712018-01-24 10:16:25 -080027#include <binder/MemoryDealer.h>
Chong Zhangea280cb2017-08-02 11:10:10 -070028#include <drm/drm_framework_common.h>
Chong Zhangea280cb2017-08-02 11:10:10 -070029#include <media/mediametadataretriever.h>
Marco Nelissen7291da62019-12-17 13:01:55 -080030#include <media/stagefright/MediaSource.h>
Chong Zhangd3e0d862017-10-03 13:17:13 -070031#include <media/stagefright/foundation/ADebug.h>
Chong Zhangea280cb2017-08-02 11:10:10 -070032#include <private/media/VideoFrame.h>
33#include <utils/Log.h>
34#include <utils/RefBase.h>
Vignesh Venkatasubramanian1b1dcd42024-02-06 01:00:03 +000035#include <algorithm>
Chong Zhang8541d302019-07-12 11:19:47 -070036#include <vector>
Chong Zhangea280cb2017-08-02 11:10:10 -070037
38HeifDecoder* createHeifDecoder() {
39 return new android::HeifDecoderImpl();
40}
41
42namespace android {
43
Chong Zhang8541d302019-07-12 11:19:47 -070044void initFrameInfo(HeifFrameInfo *info, const VideoFrame *videoFrame) {
Vignesh Venkatasubramanian7fe67942023-06-22 23:08:43 +000045 info->mWidth = videoFrame->mDisplayWidth;
Vignesh Venkatasubramanian1b1dcd42024-02-06 01:00:03 +000046 // Number of scanlines is mDisplayHeight. Clamp it to mHeight to guard
47 // against malformed streams claiming that mDisplayHeight is greater than
48 // mHeight.
49 info->mHeight = std::min(videoFrame->mDisplayHeight, videoFrame->mHeight);
Chong Zhang8541d302019-07-12 11:19:47 -070050 info->mRotationAngle = videoFrame->mRotationAngle;
51 info->mBytesPerPixel = videoFrame->mBytesPerPixel;
Chong Zhang0af4db62019-08-23 15:34:58 -070052 info->mDurationUs = videoFrame->mDurationUs;
Dichen Zhangf4066492022-02-17 17:32:53 -080053 info->mBitDepth = videoFrame->mBitDepth;
Chong Zhang8541d302019-07-12 11:19:47 -070054 if (videoFrame->mIccSize > 0) {
55 info->mIccData.assign(
56 videoFrame->getFlattenedIccData(),
57 videoFrame->getFlattenedIccData() + videoFrame->mIccSize);
58 } else {
59 // clear old Icc data if there is no Icc data.
60 info->mIccData.clear();
61 }
62}
63
Chong Zhangea280cb2017-08-02 11:10:10 -070064/*
65 * HeifDataSource
66 *
67 * Proxies data requests over IDataSource interface from MediaMetadataRetriever
68 * to the HeifStream interface we received from the heif decoder client.
69 */
70class HeifDataSource : public BnDataSource {
71public:
72 /*
73 * Constructs HeifDataSource; will take ownership of |stream|.
74 */
75 HeifDataSource(HeifStream* stream)
Chong Zhang3f3ffab2017-08-23 13:51:59 -070076 : mStream(stream), mEOS(false),
77 mCachedOffset(0), mCachedSize(0), mCacheBufferSize(0) {}
Chong Zhangea280cb2017-08-02 11:10:10 -070078
79 ~HeifDataSource() override {}
80
81 /*
82 * Initializes internal resources.
83 */
84 bool init();
85
86 sp<IMemory> getIMemory() override { return mMemory; }
87 ssize_t readAt(off64_t offset, size_t size) override;
88 status_t getSize(off64_t* size) override ;
89 void close() {}
90 uint32_t getFlags() override { return 0; }
91 String8 toString() override { return String8("HeifDataSource"); }
Chong Zhangea280cb2017-08-02 11:10:10 -070092
93private:
Chong Zhangea280cb2017-08-02 11:10:10 -070094 enum {
Chong Zhang3f3ffab2017-08-23 13:51:59 -070095 /*
96 * Buffer size for passing the read data to mediaserver. Set to 64K
97 * (which is what MediaDataSource Java API's jni implementation uses).
98 */
Chong Zhangea280cb2017-08-02 11:10:10 -070099 kBufferSize = 64 * 1024,
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700100 /*
101 * Initial and max cache buffer size.
102 */
103 kInitialCacheBufferSize = 4 * 1024 * 1024,
104 kMaxCacheBufferSize = 64 * 1024 * 1024,
Chong Zhangea280cb2017-08-02 11:10:10 -0700105 };
106 sp<IMemory> mMemory;
107 std::unique_ptr<HeifStream> mStream;
Chong Zhangea280cb2017-08-02 11:10:10 -0700108 bool mEOS;
Chong Zhangcd031922018-08-24 13:03:19 -0700109 std::unique_ptr<uint8_t[]> mCache;
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700110 off64_t mCachedOffset;
111 size_t mCachedSize;
112 size_t mCacheBufferSize;
Chong Zhangea280cb2017-08-02 11:10:10 -0700113};
114
115bool HeifDataSource::init() {
116 sp<MemoryDealer> memoryDealer =
117 new MemoryDealer(kBufferSize, "HeifDataSource");
118 mMemory = memoryDealer->allocate(kBufferSize);
119 if (mMemory == nullptr) {
120 ALOGE("Failed to allocate shared memory!");
121 return false;
122 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700123 mCache.reset(new uint8_t[kInitialCacheBufferSize]);
124 if (mCache.get() == nullptr) {
125 ALOGE("mFailed to allocate cache!");
126 return false;
127 }
128 mCacheBufferSize = kInitialCacheBufferSize;
Chong Zhangea280cb2017-08-02 11:10:10 -0700129 return true;
130}
131
132ssize_t HeifDataSource::readAt(off64_t offset, size_t size) {
133 ALOGV("readAt: offset=%lld, size=%zu", (long long)offset, size);
134
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700135 if (offset < mCachedOffset) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700136 // try seek, then rewind/skip, fail if none worked
137 if (mStream->seek(offset)) {
138 ALOGV("readAt: seek to offset=%lld", (long long)offset);
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700139 mCachedOffset = offset;
140 mCachedSize = 0;
Chong Zhangea280cb2017-08-02 11:10:10 -0700141 mEOS = false;
142 } else if (mStream->rewind()) {
143 ALOGV("readAt: rewind to offset=0");
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700144 mCachedOffset = 0;
145 mCachedSize = 0;
Chong Zhangea280cb2017-08-02 11:10:10 -0700146 mEOS = false;
147 } else {
148 ALOGE("readAt: couldn't seek or rewind!");
149 mEOS = true;
150 }
151 }
152
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700153 if (mEOS && (offset < mCachedOffset ||
154 offset >= (off64_t)(mCachedOffset + mCachedSize))) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700155 ALOGV("readAt: EOS");
156 return ERROR_END_OF_STREAM;
157 }
158
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700159 // at this point, offset must be >= mCachedOffset, other cases should
160 // have been caught above.
161 CHECK(offset >= mCachedOffset);
162
Sungtak Lee237f9032018-03-05 15:21:33 -0800163 off64_t resultOffset;
164 if (__builtin_add_overflow(offset, size, &resultOffset)) {
165 return ERROR_IO;
166 }
167
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700168 if (size == 0) {
169 return 0;
Chong Zhangea280cb2017-08-02 11:10:10 -0700170 }
171
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700172 // Can only read max of kBufferSize
Chong Zhangea280cb2017-08-02 11:10:10 -0700173 if (size > kBufferSize) {
174 size = kBufferSize;
175 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700176
177 // copy from cache if the request falls entirely in cache
178 if (offset + size <= mCachedOffset + mCachedSize) {
Ytai Ben-Tsvi7dd39722019-09-05 15:14:30 -0700179 memcpy(mMemory->unsecurePointer(), mCache.get() + offset - mCachedOffset, size);
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700180 return size;
181 }
182
183 // need to fetch more, check if we need to expand the cache buffer.
184 if ((off64_t)(offset + size) > mCachedOffset + kMaxCacheBufferSize) {
185 // it's reaching max cache buffer size, need to roll window, and possibly
186 // expand the cache buffer.
187 size_t newCacheBufferSize = mCacheBufferSize;
Chong Zhangcd031922018-08-24 13:03:19 -0700188 std::unique_ptr<uint8_t[]> newCache;
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700189 uint8_t* dst = mCache.get();
190 if (newCacheBufferSize < kMaxCacheBufferSize) {
191 newCacheBufferSize = kMaxCacheBufferSize;
192 newCache.reset(new uint8_t[newCacheBufferSize]);
193 dst = newCache.get();
194 }
195
196 // when rolling the cache window, try to keep about half the old bytes
197 // in case that the client goes back.
198 off64_t newCachedOffset = offset - (off64_t)(newCacheBufferSize / 2);
199 if (newCachedOffset < mCachedOffset) {
200 newCachedOffset = mCachedOffset;
201 }
202
203 int64_t newCachedSize = (int64_t)(mCachedOffset + mCachedSize) - newCachedOffset;
204 if (newCachedSize > 0) {
205 // in this case, the new cache region partially overlop the old cache,
206 // move the portion of the cache we want to save to the beginning of
207 // the cache buffer.
208 memcpy(dst, mCache.get() + newCachedOffset - mCachedOffset, newCachedSize);
209 } else if (newCachedSize < 0){
210 // in this case, the new cache region is entirely out of the old cache,
211 // in order to guarantee sequential read, we need to skip a number of
212 // bytes before reading.
213 size_t bytesToSkip = -newCachedSize;
214 size_t bytesSkipped = mStream->read(nullptr, bytesToSkip);
215 if (bytesSkipped != bytesToSkip) {
216 // bytesSkipped is invalid, there is not enough bytes to reach
217 // the requested offset.
218 ALOGE("readAt: skip failed, EOS");
219
220 mEOS = true;
221 mCachedOffset = newCachedOffset;
222 mCachedSize = 0;
223 return ERROR_END_OF_STREAM;
224 }
225 // set cache size to 0, since we're not keeping any old cache
226 newCachedSize = 0;
227 }
228
229 if (newCache.get() != nullptr) {
230 mCache.reset(newCache.release());
231 mCacheBufferSize = newCacheBufferSize;
232 }
233 mCachedOffset = newCachedOffset;
234 mCachedSize = newCachedSize;
235
236 ALOGV("readAt: rolling cache window to (%lld, %zu), cache buffer size %zu",
237 (long long)mCachedOffset, mCachedSize, mCacheBufferSize);
238 } else {
239 // expand cache buffer, but no need to roll the window
240 size_t newCacheBufferSize = mCacheBufferSize;
241 while (offset + size > mCachedOffset + newCacheBufferSize) {
242 newCacheBufferSize *= 2;
243 }
244 CHECK(newCacheBufferSize <= kMaxCacheBufferSize);
245 if (mCacheBufferSize < newCacheBufferSize) {
246 uint8_t* newCache = new uint8_t[newCacheBufferSize];
247 memcpy(newCache, mCache.get(), mCachedSize);
248 mCache.reset(newCache);
249 mCacheBufferSize = newCacheBufferSize;
250
251 ALOGV("readAt: current cache window (%lld, %zu), new cache buffer size %zu",
252 (long long) mCachedOffset, mCachedSize, mCacheBufferSize);
253 }
254 }
255 size_t bytesToRead = offset + size - mCachedOffset - mCachedSize;
256 size_t bytesRead = mStream->read(mCache.get() + mCachedSize, bytesToRead);
257 if (bytesRead > bytesToRead || bytesRead == 0) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700258 // bytesRead is invalid
259 mEOS = true;
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700260 bytesRead = 0;
261 } else if (bytesRead < bytesToRead) {
262 // read some bytes but not all, set EOS
Chong Zhangea280cb2017-08-02 11:10:10 -0700263 mEOS = true;
264 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700265 mCachedSize += bytesRead;
266 ALOGV("readAt: current cache window (%lld, %zu)",
267 (long long) mCachedOffset, mCachedSize);
268
269 // here bytesAvailable could be negative if offset jumped past EOS.
270 int64_t bytesAvailable = mCachedOffset + mCachedSize - offset;
271 if (bytesAvailable <= 0) {
272 return ERROR_END_OF_STREAM;
273 }
274 if (bytesAvailable < (int64_t)size) {
275 size = bytesAvailable;
276 }
Ytai Ben-Tsvi7dd39722019-09-05 15:14:30 -0700277 memcpy(mMemory->unsecurePointer(), mCache.get() + offset - mCachedOffset, size);
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700278 return size;
Chong Zhangea280cb2017-08-02 11:10:10 -0700279}
280
281status_t HeifDataSource::getSize(off64_t* size) {
282 if (!mStream->hasLength()) {
283 *size = -1;
284 ALOGE("getSize: not supported!");
285 return ERROR_UNSUPPORTED;
286 }
287 *size = mStream->getLength();
288 ALOGV("getSize: size=%lld", (long long)*size);
289 return OK;
290}
291
292/////////////////////////////////////////////////////////////////////////
293
Chong Zhang0c1407f2018-05-02 17:09:05 -0700294struct HeifDecoderImpl::DecodeThread : public Thread {
295 explicit DecodeThread(HeifDecoderImpl *decoder) : mDecoder(decoder) {}
296
297private:
298 HeifDecoderImpl* mDecoder;
299
300 bool threadLoop();
301
302 DISALLOW_EVIL_CONSTRUCTORS(DecodeThread);
303};
304
305bool HeifDecoderImpl::DecodeThread::threadLoop() {
306 return mDecoder->decodeAsync();
307}
308
309/////////////////////////////////////////////////////////////////////////
310
Chong Zhangea280cb2017-08-02 11:10:10 -0700311HeifDecoderImpl::HeifDecoderImpl() :
312 // output color format should always be set via setOutputColor(), in case
313 // it's not, default to HAL_PIXEL_FORMAT_RGB_565.
314 mOutputColor(HAL_PIXEL_FORMAT_RGB_565),
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700315 mCurScanline(0),
Chong Zhang8541d302019-07-12 11:19:47 -0700316 mTotalScanline(0),
Chong Zhangd3e0d862017-10-03 13:17:13 -0700317 mFrameDecoded(false),
318 mHasImage(false),
Chong Zhang0c1407f2018-05-02 17:09:05 -0700319 mHasVideo(false),
Chong Zhang8541d302019-07-12 11:19:47 -0700320 mSequenceLength(0),
Chong Zhang0c1407f2018-05-02 17:09:05 -0700321 mAvailableLines(0),
322 mNumSlices(1),
323 mSliceHeight(0),
324 mAsyncDecodeDone(false) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700325}
326
327HeifDecoderImpl::~HeifDecoderImpl() {
Chong Zhang0c1407f2018-05-02 17:09:05 -0700328 if (mThread != nullptr) {
329 mThread->join();
330 }
Chong Zhangea280cb2017-08-02 11:10:10 -0700331}
332
333bool HeifDecoderImpl::init(HeifStream* stream, HeifFrameInfo* frameInfo) {
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700334 mFrameDecoded = false;
Chong Zhangd3e0d862017-10-03 13:17:13 -0700335 mFrameMemory.clear();
336
Chong Zhangea280cb2017-08-02 11:10:10 -0700337 sp<HeifDataSource> dataSource = new HeifDataSource(stream);
338 if (!dataSource->init()) {
339 return false;
340 }
341 mDataSource = dataSource;
342
Leon Scroggins III7ca678b2020-01-22 14:09:55 -0500343 return reinit(frameInfo);
344}
345
346bool HeifDecoderImpl::reinit(HeifFrameInfo* frameInfo) {
347 mFrameDecoded = false;
348 mFrameMemory.clear();
349
Dichen Zhang92e51072022-06-08 18:26:37 -0700350 sp<MediaMetadataRetriever> retriever = new MediaMetadataRetriever();
351 status_t err = retriever->setDataSource(mDataSource, "image/heif");
Chong Zhangea280cb2017-08-02 11:10:10 -0700352 if (err != OK) {
353 ALOGE("failed to set data source!");
Chong Zhangea280cb2017-08-02 11:10:10 -0700354 mRetriever.clear();
355 mDataSource.clear();
356 return false;
357 }
Dichen Zhang92e51072022-06-08 18:26:37 -0700358 {
359 Mutex::Autolock _l(mRetrieverLock);
360 mRetriever = retriever;
361 }
Chong Zhangea280cb2017-08-02 11:10:10 -0700362 ALOGV("successfully set data source.");
363
Dichen Zhang92e51072022-06-08 18:26:37 -0700364 const char* hasImage = retriever->extractMetadata(METADATA_KEY_HAS_IMAGE);
365 const char* hasVideo = retriever->extractMetadata(METADATA_KEY_HAS_VIDEO);
Chong Zhangd3e0d862017-10-03 13:17:13 -0700366
367 mHasImage = hasImage && !strcasecmp(hasImage, "yes");
368 mHasVideo = hasVideo && !strcasecmp(hasVideo, "yes");
Chong Zhang8541d302019-07-12 11:19:47 -0700369
370 HeifFrameInfo* defaultInfo = nullptr;
Chong Zhangd3e0d862017-10-03 13:17:13 -0700371 if (mHasImage) {
372 // image index < 0 to retrieve primary image
Dichen Zhang92e51072022-06-08 18:26:37 -0700373 sp<IMemory> sharedMem = retriever->getImageAtIndex(
Chong Zhangd3e0d862017-10-03 13:17:13 -0700374 -1, mOutputColor, true /*metaOnly*/);
Chong Zhang8541d302019-07-12 11:19:47 -0700375
Ytai Ben-Tsvi7dd39722019-09-05 15:14:30 -0700376 if (sharedMem == nullptr || sharedMem->unsecurePointer() == nullptr) {
Chong Zhang8541d302019-07-12 11:19:47 -0700377 ALOGE("init: videoFrame is a nullptr");
378 return false;
379 }
380
Ytai Ben-Tsvi7dd39722019-09-05 15:14:30 -0700381 // TODO: Using unsecurePointer() has some associated security pitfalls
382 // (see declaration for details).
383 // Either document why it is safe in this case or address the
384 // issue (e.g. by copying).
385 VideoFrame* videoFrame = static_cast<VideoFrame*>(sharedMem->unsecurePointer());
Chong Zhang8541d302019-07-12 11:19:47 -0700386
Dichen Zhangf4066492022-02-17 17:32:53 -0800387 ALOGV("Image dimension %dx%d, display %dx%d, angle %d, iccSize %d, bitDepth %d",
Chong Zhang8541d302019-07-12 11:19:47 -0700388 videoFrame->mWidth,
389 videoFrame->mHeight,
390 videoFrame->mDisplayWidth,
391 videoFrame->mDisplayHeight,
392 videoFrame->mRotationAngle,
Dichen Zhangf4066492022-02-17 17:32:53 -0800393 videoFrame->mIccSize,
394 videoFrame->mBitDepth);
Chong Zhang8541d302019-07-12 11:19:47 -0700395
396 initFrameInfo(&mImageInfo, videoFrame);
397
398 if (videoFrame->mTileHeight >= 512) {
399 // Try decoding in slices only if the image has tiles and is big enough.
400 mSliceHeight = videoFrame->mTileHeight;
401 ALOGV("mSliceHeight %u", mSliceHeight);
402 }
403
404 defaultInfo = &mImageInfo;
Chong Zhangea280cb2017-08-02 11:10:10 -0700405 }
406
Chong Zhang8541d302019-07-12 11:19:47 -0700407 if (mHasVideo) {
Dichen Zhang92e51072022-06-08 18:26:37 -0700408 sp<IMemory> sharedMem = retriever->getFrameAtTime(0,
Chong Zhang8541d302019-07-12 11:19:47 -0700409 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
410 mOutputColor, true /*metaOnly*/);
411
Ytai Ben-Tsvi7dd39722019-09-05 15:14:30 -0700412 if (sharedMem == nullptr || sharedMem->unsecurePointer() == nullptr) {
Chong Zhang8541d302019-07-12 11:19:47 -0700413 ALOGE("init: videoFrame is a nullptr");
414 return false;
415 }
416
Ytai Ben-Tsvi7dd39722019-09-05 15:14:30 -0700417 // TODO: Using unsecurePointer() has some associated security pitfalls
418 // (see declaration for details).
419 // Either document why it is safe in this case or address the
420 // issue (e.g. by copying).
421 VideoFrame* videoFrame = static_cast<VideoFrame*>(
422 sharedMem->unsecurePointer());
Chong Zhang8541d302019-07-12 11:19:47 -0700423
424 ALOGV("Sequence dimension %dx%d, display %dx%d, angle %d, iccSize %d",
425 videoFrame->mWidth,
426 videoFrame->mHeight,
427 videoFrame->mDisplayWidth,
428 videoFrame->mDisplayHeight,
429 videoFrame->mRotationAngle,
430 videoFrame->mIccSize);
431
432 initFrameInfo(&mSequenceInfo, videoFrame);
433
Dichen Zhang92e51072022-06-08 18:26:37 -0700434 const char* frameCount = retriever->extractMetadata(METADATA_KEY_VIDEO_FRAME_COUNT);
Ray Essicke89e6322022-02-02 13:33:50 -0800435 if (frameCount == nullptr) {
436 android_errorWriteWithInfoLog(0x534e4554, "215002587", -1, NULL, 0);
437 ALOGD("No valid sequence information in metadata");
438 return false;
439 }
440 mSequenceLength = atoi(frameCount);
Chong Zhang8541d302019-07-12 11:19:47 -0700441
442 if (defaultInfo == nullptr) {
443 defaultInfo = &mSequenceInfo;
444 }
445 }
446
447 if (defaultInfo == nullptr) {
448 ALOGD("No valid image or sequence available");
Chong Zhangea280cb2017-08-02 11:10:10 -0700449 return false;
450 }
451
Chong Zhangea280cb2017-08-02 11:10:10 -0700452 if (frameInfo != nullptr) {
Chong Zhang8541d302019-07-12 11:19:47 -0700453 *frameInfo = *defaultInfo;
Chong Zhangea280cb2017-08-02 11:10:10 -0700454 }
Chong Zhang8541d302019-07-12 11:19:47 -0700455
456 // default total scanline, this might change if decodeSequence() is used
457 mTotalScanline = defaultInfo->mHeight;
458
459 return true;
460}
461
462bool HeifDecoderImpl::getSequenceInfo(
463 HeifFrameInfo* frameInfo, size_t *frameCount) {
464 ALOGV("%s", __FUNCTION__);
465 if (!mHasVideo) {
466 return false;
467 }
468 if (frameInfo != nullptr) {
469 *frameInfo = mSequenceInfo;
470 }
471 if (frameCount != nullptr) {
472 *frameCount = mSequenceLength;
Chong Zhang0c1407f2018-05-02 17:09:05 -0700473 }
Chong Zhangea280cb2017-08-02 11:10:10 -0700474 return true;
475}
476
477bool HeifDecoderImpl::getEncodedColor(HeifEncodedColor* /*outColor*/) const {
478 ALOGW("getEncodedColor: not implemented!");
479 return false;
480}
481
482bool HeifDecoderImpl::setOutputColor(HeifColorFormat heifColor) {
Dichen Zhangbedd4282023-04-19 21:10:04 +0000483 android_pixel_format_t outputColor;
Chong Zhangea280cb2017-08-02 11:10:10 -0700484 switch(heifColor) {
485 case kHeifColorFormat_RGB565:
486 {
Dichen Zhangbedd4282023-04-19 21:10:04 +0000487 outputColor = HAL_PIXEL_FORMAT_RGB_565;
Leon Scroggins III7ca678b2020-01-22 14:09:55 -0500488 break;
Chong Zhangea280cb2017-08-02 11:10:10 -0700489 }
490 case kHeifColorFormat_RGBA_8888:
491 {
Dichen Zhangbedd4282023-04-19 21:10:04 +0000492 outputColor = HAL_PIXEL_FORMAT_RGBA_8888;
Leon Scroggins III7ca678b2020-01-22 14:09:55 -0500493 break;
Chong Zhangea280cb2017-08-02 11:10:10 -0700494 }
495 case kHeifColorFormat_BGRA_8888:
496 {
Dichen Zhangbedd4282023-04-19 21:10:04 +0000497 outputColor = HAL_PIXEL_FORMAT_BGRA_8888;
Leon Scroggins III7ca678b2020-01-22 14:09:55 -0500498 break;
Chong Zhangea280cb2017-08-02 11:10:10 -0700499 }
Dichen Zhang1b3441a2021-11-17 11:54:43 -0800500 case kHeifColorFormat_RGBA_1010102:
501 {
Dichen Zhangbedd4282023-04-19 21:10:04 +0000502 outputColor = HAL_PIXEL_FORMAT_RGBA_1010102;
Dichen Zhang1b3441a2021-11-17 11:54:43 -0800503 break;
504 }
Chong Zhangea280cb2017-08-02 11:10:10 -0700505 default:
Leon Scroggins III7ca678b2020-01-22 14:09:55 -0500506 ALOGE("Unsupported output color format %d", heifColor);
507 return false;
Chong Zhangea280cb2017-08-02 11:10:10 -0700508 }
Dichen Zhangbedd4282023-04-19 21:10:04 +0000509 if (outputColor == mOutputColor) {
510 return true;
511 }
512
513 mOutputColor = outputColor;
Leon Scroggins III7ca678b2020-01-22 14:09:55 -0500514
515 if (mFrameDecoded) {
516 return reinit(nullptr);
517 }
518 return true;
Chong Zhangea280cb2017-08-02 11:10:10 -0700519}
520
Chong Zhang0c1407f2018-05-02 17:09:05 -0700521bool HeifDecoderImpl::decodeAsync() {
Dichen Zhang92e51072022-06-08 18:26:37 -0700522 wp<MediaMetadataRetriever> weakRetriever;
523 {
524 Mutex::Autolock _l(mRetrieverLock);
525 weakRetriever = mRetriever;
Dichen Zhang92e51072022-06-08 18:26:37 -0700526 }
527
Chong Zhang0c1407f2018-05-02 17:09:05 -0700528 for (size_t i = 1; i < mNumSlices; i++) {
Dichen Zhang92e51072022-06-08 18:26:37 -0700529 sp<MediaMetadataRetriever> retriever = weakRetriever.promote();
530 if (retriever == nullptr) {
531 return false;
532 }
533
Chong Zhang0c1407f2018-05-02 17:09:05 -0700534 ALOGV("decodeAsync(): decoding slice %zu", i);
535 size_t top = i * mSliceHeight;
536 size_t bottom = (i + 1) * mSliceHeight;
Chong Zhang8541d302019-07-12 11:19:47 -0700537 if (bottom > mImageInfo.mHeight) {
538 bottom = mImageInfo.mHeight;
Chong Zhang0c1407f2018-05-02 17:09:05 -0700539 }
Dichen Zhang92e51072022-06-08 18:26:37 -0700540
541 sp<IMemory> frameMemory = retriever->getImageRectAtIndex(
Chong Zhang8541d302019-07-12 11:19:47 -0700542 -1, mOutputColor, 0, top, mImageInfo.mWidth, bottom);
Chong Zhang0c1407f2018-05-02 17:09:05 -0700543 {
544 Mutex::Autolock autolock(mLock);
545
Ytai Ben-Tsvi7dd39722019-09-05 15:14:30 -0700546 if (frameMemory == nullptr || frameMemory->unsecurePointer() == nullptr) {
Chong Zhang0c1407f2018-05-02 17:09:05 -0700547 mAsyncDecodeDone = true;
548 mScanlineReady.signal();
549 break;
550 }
551 mFrameMemory = frameMemory;
552 mAvailableLines = bottom;
553 ALOGV("decodeAsync(): available lines %zu", mAvailableLines);
554 mScanlineReady.signal();
555 }
556 }
Leon Scroggins III7ca678b2020-01-22 14:09:55 -0500557 // Hold on to mDataSource in case the client wants to redecode.
Dichen Zhangf8230032022-12-01 18:39:23 -0800558
559 {
560 Mutex::Autolock _l(mRetrieverLock);
561 mRetriever.clear();
562 }
563
Chong Zhang0c1407f2018-05-02 17:09:05 -0700564 return false;
565}
566
Chong Zhangea280cb2017-08-02 11:10:10 -0700567bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) {
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700568 // reset scanline pointer
569 mCurScanline = 0;
570
571 if (mFrameDecoded) {
572 return true;
573 }
574
Dichen Zhang92e51072022-06-08 18:26:37 -0700575 sp<MediaMetadataRetriever> retriever;
576 {
577 Mutex::Autolock _l(mRetrieverLock);
578 if (mRetriever == nullptr) {
579 ALOGE("Failed to get MediaMetadataRetriever!");
580 return false;
581 }
582
583 retriever = mRetriever;
584 }
585
Chong Zhang0c1407f2018-05-02 17:09:05 -0700586 // See if we want to decode in slices to allow client to start
587 // scanline processing in parallel with decode. If this fails
588 // we fallback to decoding the full frame.
Chong Zhang8541d302019-07-12 11:19:47 -0700589 if (mHasImage) {
590 if (mSliceHeight >= 512 &&
591 mImageInfo.mWidth >= 3000 &&
592 mImageInfo.mHeight >= 2000 ) {
593 // Try decoding in slices only if the image has tiles and is big enough.
594 mNumSlices = (mImageInfo.mHeight + mSliceHeight - 1) / mSliceHeight;
595 ALOGV("mSliceHeight %u, mNumSlices %zu", mSliceHeight, mNumSlices);
Chong Zhang0c1407f2018-05-02 17:09:05 -0700596 }
597
Chong Zhang8541d302019-07-12 11:19:47 -0700598 if (mNumSlices > 1) {
599 // get first slice and metadata
Dichen Zhang92e51072022-06-08 18:26:37 -0700600 sp<IMemory> frameMemory = retriever->getImageRectAtIndex(
Chong Zhang8541d302019-07-12 11:19:47 -0700601 -1, mOutputColor, 0, 0, mImageInfo.mWidth, mSliceHeight);
Chong Zhang0c1407f2018-05-02 17:09:05 -0700602
Ytai Ben-Tsvi7dd39722019-09-05 15:14:30 -0700603 if (frameMemory == nullptr || frameMemory->unsecurePointer() == nullptr) {
Chong Zhang8541d302019-07-12 11:19:47 -0700604 ALOGE("decode: metadata is a nullptr");
605 return false;
606 }
607
Ytai Ben-Tsvi7dd39722019-09-05 15:14:30 -0700608 // TODO: Using unsecurePointer() has some associated security pitfalls
609 // (see declaration for details).
610 // Either document why it is safe in this case or address the
611 // issue (e.g. by copying).
612 VideoFrame* videoFrame = static_cast<VideoFrame*>(frameMemory->unsecurePointer());
Chong Zhang8541d302019-07-12 11:19:47 -0700613
614 if (frameInfo != nullptr) {
615 initFrameInfo(frameInfo, videoFrame);
616 }
617 mFrameMemory = frameMemory;
618 mAvailableLines = mSliceHeight;
619 mThread = new DecodeThread(this);
620 if (mThread->run("HeifDecode", ANDROID_PRIORITY_FOREGROUND) == OK) {
621 mFrameDecoded = true;
622 return true;
623 }
624 // Fallback to decode without slicing
625 mThread.clear();
626 mNumSlices = 1;
627 mSliceHeight = 0;
628 mAvailableLines = 0;
629 mFrameMemory.clear();
Chong Zhang0c1407f2018-05-02 17:09:05 -0700630 }
Chong Zhang0c1407f2018-05-02 17:09:05 -0700631 }
632
Chong Zhangd3e0d862017-10-03 13:17:13 -0700633 if (mHasImage) {
634 // image index < 0 to retrieve primary image
Dichen Zhang92e51072022-06-08 18:26:37 -0700635 mFrameMemory = retriever->getImageAtIndex(-1, mOutputColor);
Chong Zhangd3e0d862017-10-03 13:17:13 -0700636 } else if (mHasVideo) {
Dichen Zhang92e51072022-06-08 18:26:37 -0700637 mFrameMemory = retriever->getFrameAtTime(0,
Chong Zhangd3e0d862017-10-03 13:17:13 -0700638 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC, mOutputColor);
639 }
640
Ytai Ben-Tsvi7dd39722019-09-05 15:14:30 -0700641 if (mFrameMemory == nullptr || mFrameMemory->unsecurePointer() == nullptr) {
Chong Zhang0c1407f2018-05-02 17:09:05 -0700642 ALOGE("decode: videoFrame is a nullptr");
Chong Zhangea280cb2017-08-02 11:10:10 -0700643 return false;
644 }
645
Ytai Ben-Tsvi7dd39722019-09-05 15:14:30 -0700646 // TODO: Using unsecurePointer() has some associated security pitfalls
647 // (see declaration for details).
648 // Either document why it is safe in this case or address the
649 // issue (e.g. by copying).
650 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->unsecurePointer());
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700651 if (videoFrame->mSize == 0 ||
652 mFrameMemory->size() < videoFrame->getFlattenedSize()) {
Chong Zhang0c1407f2018-05-02 17:09:05 -0700653 ALOGE("decode: videoFrame size is invalid");
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700654 return false;
655 }
656
Chong Zhangea280cb2017-08-02 11:10:10 -0700657 ALOGV("Decoded dimension %dx%d, display %dx%d, angle %d, rowbytes %d, size %d",
658 videoFrame->mWidth,
659 videoFrame->mHeight,
660 videoFrame->mDisplayWidth,
661 videoFrame->mDisplayHeight,
662 videoFrame->mRotationAngle,
663 videoFrame->mRowBytes,
664 videoFrame->mSize);
665
666 if (frameInfo != nullptr) {
Chong Zhang8541d302019-07-12 11:19:47 -0700667 initFrameInfo(frameInfo, videoFrame);
668
Chong Zhangea280cb2017-08-02 11:10:10 -0700669 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700670 mFrameDecoded = true;
Chong Zhang4c6398b2017-09-07 17:43:19 -0700671
Chong Zhang0c1407f2018-05-02 17:09:05 -0700672 // Aggressively clear to avoid holding on to resources
Dichen Zhangf8230032022-12-01 18:39:23 -0800673 {
674 Mutex::Autolock _l(mRetrieverLock);
675 mRetriever.clear();
676 }
Leon Scroggins III7ca678b2020-01-22 14:09:55 -0500677
678 // Hold on to mDataSource in case the client wants to redecode.
Chong Zhangea280cb2017-08-02 11:10:10 -0700679 return true;
680}
681
Chong Zhang8541d302019-07-12 11:19:47 -0700682bool HeifDecoderImpl::decodeSequence(int frameIndex, HeifFrameInfo* frameInfo) {
683 ALOGV("%s: frame index %d", __FUNCTION__, frameIndex);
684 if (!mHasVideo) {
685 return false;
686 }
687
688 if (frameIndex < 0 || frameIndex >= mSequenceLength) {
689 ALOGE("invalid frame index: %d, total frames %zu", frameIndex, mSequenceLength);
690 return false;
691 }
692
693 mCurScanline = 0;
694
695 // set total scanline to sequence height now
696 mTotalScanline = mSequenceInfo.mHeight;
697
Dichen Zhang92e51072022-06-08 18:26:37 -0700698 sp<MediaMetadataRetriever> retriever;
699 {
700 Mutex::Autolock _l(mRetrieverLock);
701 retriever = mRetriever;
702 if (retriever == nullptr) {
703 ALOGE("failed to get MediaMetadataRetriever!");
704 return false;
705 }
706 }
707
708 mFrameMemory = retriever->getFrameAtIndex(frameIndex, mOutputColor);
Ytai Ben-Tsvi7dd39722019-09-05 15:14:30 -0700709 if (mFrameMemory == nullptr || mFrameMemory->unsecurePointer() == nullptr) {
Chong Zhang8541d302019-07-12 11:19:47 -0700710 ALOGE("decode: videoFrame is a nullptr");
711 return false;
712 }
713
Ytai Ben-Tsvi7dd39722019-09-05 15:14:30 -0700714 // TODO: Using unsecurePointer() has some associated security pitfalls
715 // (see declaration for details).
716 // Either document why it is safe in this case or address the
717 // issue (e.g. by copying).
718 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->unsecurePointer());
Chong Zhang8541d302019-07-12 11:19:47 -0700719 if (videoFrame->mSize == 0 ||
720 mFrameMemory->size() < videoFrame->getFlattenedSize()) {
721 ALOGE("decode: videoFrame size is invalid");
722 return false;
723 }
724
725 ALOGV("Decoded dimension %dx%d, display %dx%d, angle %d, rowbytes %d, size %d",
726 videoFrame->mWidth,
727 videoFrame->mHeight,
728 videoFrame->mDisplayWidth,
729 videoFrame->mDisplayHeight,
730 videoFrame->mRotationAngle,
731 videoFrame->mRowBytes,
732 videoFrame->mSize);
733
734 if (frameInfo != nullptr) {
735 initFrameInfo(frameInfo, videoFrame);
736 }
737 return true;
738}
739
Chong Zhang0c1407f2018-05-02 17:09:05 -0700740bool HeifDecoderImpl::getScanlineInner(uint8_t* dst) {
Ytai Ben-Tsvi7dd39722019-09-05 15:14:30 -0700741 if (mFrameMemory == nullptr || mFrameMemory->unsecurePointer() == nullptr) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700742 return false;
743 }
Ytai Ben-Tsvi7dd39722019-09-05 15:14:30 -0700744 // TODO: Using unsecurePointer() has some associated security pitfalls
745 // (see declaration for details).
746 // Either document why it is safe in this case or address the
747 // issue (e.g. by copying).
748 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->unsecurePointer());
Vignesh Venkatasubramanian7fe67942023-06-22 23:08:43 +0000749 uint8_t* src = videoFrame->getFlattenedData() +
750 (videoFrame->mRowBytes * (mCurScanline + videoFrame->mDisplayTop)) +
751 (videoFrame->mBytesPerPixel * videoFrame->mDisplayLeft);
752 mCurScanline++;
Vignesh Venkatasubramanian1b1dcd42024-02-06 01:00:03 +0000753 // Do not try to copy more than |videoFrame->mWidth| pixels.
754 uint32_t width = std::min(videoFrame->mDisplayWidth, videoFrame->mWidth);
755 memcpy(dst, src, videoFrame->mBytesPerPixel * width);
Chong Zhangea280cb2017-08-02 11:10:10 -0700756 return true;
757}
758
Chong Zhang0c1407f2018-05-02 17:09:05 -0700759bool HeifDecoderImpl::getScanline(uint8_t* dst) {
Chong Zhang8541d302019-07-12 11:19:47 -0700760 if (mCurScanline >= mTotalScanline) {
Chong Zhang0c1407f2018-05-02 17:09:05 -0700761 ALOGE("no more scanline available");
762 return false;
Chong Zhangea280cb2017-08-02 11:10:10 -0700763 }
Chong Zhangea280cb2017-08-02 11:10:10 -0700764
Chong Zhang0c1407f2018-05-02 17:09:05 -0700765 if (mNumSlices > 1) {
766 Mutex::Autolock autolock(mLock);
767
768 while (!mAsyncDecodeDone && mCurScanline >= mAvailableLines) {
769 mScanlineReady.wait(mLock);
770 }
771 return (mCurScanline < mAvailableLines) ? getScanlineInner(dst) : false;
772 }
773
774 return getScanlineInner(dst);
775}
776
777size_t HeifDecoderImpl::skipScanlines(size_t count) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700778 uint32_t oldScanline = mCurScanline;
779 mCurScanline += count;
Chong Zhang8541d302019-07-12 11:19:47 -0700780 if (mCurScanline > mTotalScanline) {
781 mCurScanline = mTotalScanline;
Chong Zhangea280cb2017-08-02 11:10:10 -0700782 }
783 return (mCurScanline > oldScanline) ? (mCurScanline - oldScanline) : 0;
784}
785
Dichen Zhangf4066492022-02-17 17:32:53 -0800786uint32_t HeifDecoderImpl::getColorDepth() {
787 HeifFrameInfo* info = &mImageInfo;
788 if (info != nullptr) {
789 return mImageInfo.mBitDepth;
790 } else {
791 return 0;
792 }
793}
794
Chong Zhangea280cb2017-08-02 11:10:10 -0700795} // namespace android