blob: bbc14a9d597007cd57629de9a25296c05f115567 [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
18#define LOG_TAG "HeifDecoderImpl"
19
20#include "HeifDecoderImpl.h"
21
22#include <stdio.h>
23
24#include <binder/IMemory.h>
Dongwon Kang49ce6712018-01-24 10:16:25 -080025#include <binder/MemoryDealer.h>
Chong Zhangea280cb2017-08-02 11:10:10 -070026#include <drm/drm_framework_common.h>
27#include <media/IDataSource.h>
28#include <media/mediametadataretriever.h>
Dongwon Kangd91dc5a2017-10-10 00:07:09 -070029#include <media/MediaSource.h>
Chong Zhangd3e0d862017-10-03 13:17:13 -070030#include <media/stagefright/foundation/ADebug.h>
Chong Zhangea280cb2017-08-02 11:10:10 -070031#include <private/media/VideoFrame.h>
32#include <utils/Log.h>
33#include <utils/RefBase.h>
34
35HeifDecoder* createHeifDecoder() {
36 return new android::HeifDecoderImpl();
37}
38
39namespace android {
40
41/*
42 * HeifDataSource
43 *
44 * Proxies data requests over IDataSource interface from MediaMetadataRetriever
45 * to the HeifStream interface we received from the heif decoder client.
46 */
47class HeifDataSource : public BnDataSource {
48public:
49 /*
50 * Constructs HeifDataSource; will take ownership of |stream|.
51 */
52 HeifDataSource(HeifStream* stream)
Chong Zhang3f3ffab2017-08-23 13:51:59 -070053 : mStream(stream), mEOS(false),
54 mCachedOffset(0), mCachedSize(0), mCacheBufferSize(0) {}
Chong Zhangea280cb2017-08-02 11:10:10 -070055
56 ~HeifDataSource() override {}
57
58 /*
59 * Initializes internal resources.
60 */
61 bool init();
62
63 sp<IMemory> getIMemory() override { return mMemory; }
64 ssize_t readAt(off64_t offset, size_t size) override;
65 status_t getSize(off64_t* size) override ;
66 void close() {}
67 uint32_t getFlags() override { return 0; }
68 String8 toString() override { return String8("HeifDataSource"); }
Chong Zhangea280cb2017-08-02 11:10:10 -070069
70private:
Chong Zhangea280cb2017-08-02 11:10:10 -070071 enum {
Chong Zhang3f3ffab2017-08-23 13:51:59 -070072 /*
73 * Buffer size for passing the read data to mediaserver. Set to 64K
74 * (which is what MediaDataSource Java API's jni implementation uses).
75 */
Chong Zhangea280cb2017-08-02 11:10:10 -070076 kBufferSize = 64 * 1024,
Chong Zhang3f3ffab2017-08-23 13:51:59 -070077 /*
78 * Initial and max cache buffer size.
79 */
80 kInitialCacheBufferSize = 4 * 1024 * 1024,
81 kMaxCacheBufferSize = 64 * 1024 * 1024,
Chong Zhangea280cb2017-08-02 11:10:10 -070082 };
83 sp<IMemory> mMemory;
84 std::unique_ptr<HeifStream> mStream;
Chong Zhangea280cb2017-08-02 11:10:10 -070085 bool mEOS;
Chong Zhangcd031922018-08-24 13:03:19 -070086 std::unique_ptr<uint8_t[]> mCache;
Chong Zhang3f3ffab2017-08-23 13:51:59 -070087 off64_t mCachedOffset;
88 size_t mCachedSize;
89 size_t mCacheBufferSize;
Chong Zhangea280cb2017-08-02 11:10:10 -070090};
91
92bool HeifDataSource::init() {
93 sp<MemoryDealer> memoryDealer =
94 new MemoryDealer(kBufferSize, "HeifDataSource");
95 mMemory = memoryDealer->allocate(kBufferSize);
96 if (mMemory == nullptr) {
97 ALOGE("Failed to allocate shared memory!");
98 return false;
99 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700100 mCache.reset(new uint8_t[kInitialCacheBufferSize]);
101 if (mCache.get() == nullptr) {
102 ALOGE("mFailed to allocate cache!");
103 return false;
104 }
105 mCacheBufferSize = kInitialCacheBufferSize;
Chong Zhangea280cb2017-08-02 11:10:10 -0700106 return true;
107}
108
109ssize_t HeifDataSource::readAt(off64_t offset, size_t size) {
110 ALOGV("readAt: offset=%lld, size=%zu", (long long)offset, size);
111
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700112 if (offset < mCachedOffset) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700113 // try seek, then rewind/skip, fail if none worked
114 if (mStream->seek(offset)) {
115 ALOGV("readAt: seek to offset=%lld", (long long)offset);
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700116 mCachedOffset = offset;
117 mCachedSize = 0;
Chong Zhangea280cb2017-08-02 11:10:10 -0700118 mEOS = false;
119 } else if (mStream->rewind()) {
120 ALOGV("readAt: rewind to offset=0");
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700121 mCachedOffset = 0;
122 mCachedSize = 0;
Chong Zhangea280cb2017-08-02 11:10:10 -0700123 mEOS = false;
124 } else {
125 ALOGE("readAt: couldn't seek or rewind!");
126 mEOS = true;
127 }
128 }
129
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700130 if (mEOS && (offset < mCachedOffset ||
131 offset >= (off64_t)(mCachedOffset + mCachedSize))) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700132 ALOGV("readAt: EOS");
133 return ERROR_END_OF_STREAM;
134 }
135
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700136 // at this point, offset must be >= mCachedOffset, other cases should
137 // have been caught above.
138 CHECK(offset >= mCachedOffset);
139
Sungtak Lee237f9032018-03-05 15:21:33 -0800140 off64_t resultOffset;
141 if (__builtin_add_overflow(offset, size, &resultOffset)) {
142 return ERROR_IO;
143 }
144
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700145 if (size == 0) {
146 return 0;
Chong Zhangea280cb2017-08-02 11:10:10 -0700147 }
148
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700149 // Can only read max of kBufferSize
Chong Zhangea280cb2017-08-02 11:10:10 -0700150 if (size > kBufferSize) {
151 size = kBufferSize;
152 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700153
154 // copy from cache if the request falls entirely in cache
155 if (offset + size <= mCachedOffset + mCachedSize) {
156 memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size);
157 return size;
158 }
159
160 // need to fetch more, check if we need to expand the cache buffer.
161 if ((off64_t)(offset + size) > mCachedOffset + kMaxCacheBufferSize) {
162 // it's reaching max cache buffer size, need to roll window, and possibly
163 // expand the cache buffer.
164 size_t newCacheBufferSize = mCacheBufferSize;
Chong Zhangcd031922018-08-24 13:03:19 -0700165 std::unique_ptr<uint8_t[]> newCache;
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700166 uint8_t* dst = mCache.get();
167 if (newCacheBufferSize < kMaxCacheBufferSize) {
168 newCacheBufferSize = kMaxCacheBufferSize;
169 newCache.reset(new uint8_t[newCacheBufferSize]);
170 dst = newCache.get();
171 }
172
173 // when rolling the cache window, try to keep about half the old bytes
174 // in case that the client goes back.
175 off64_t newCachedOffset = offset - (off64_t)(newCacheBufferSize / 2);
176 if (newCachedOffset < mCachedOffset) {
177 newCachedOffset = mCachedOffset;
178 }
179
180 int64_t newCachedSize = (int64_t)(mCachedOffset + mCachedSize) - newCachedOffset;
181 if (newCachedSize > 0) {
182 // in this case, the new cache region partially overlop the old cache,
183 // move the portion of the cache we want to save to the beginning of
184 // the cache buffer.
185 memcpy(dst, mCache.get() + newCachedOffset - mCachedOffset, newCachedSize);
186 } else if (newCachedSize < 0){
187 // in this case, the new cache region is entirely out of the old cache,
188 // in order to guarantee sequential read, we need to skip a number of
189 // bytes before reading.
190 size_t bytesToSkip = -newCachedSize;
191 size_t bytesSkipped = mStream->read(nullptr, bytesToSkip);
192 if (bytesSkipped != bytesToSkip) {
193 // bytesSkipped is invalid, there is not enough bytes to reach
194 // the requested offset.
195 ALOGE("readAt: skip failed, EOS");
196
197 mEOS = true;
198 mCachedOffset = newCachedOffset;
199 mCachedSize = 0;
200 return ERROR_END_OF_STREAM;
201 }
202 // set cache size to 0, since we're not keeping any old cache
203 newCachedSize = 0;
204 }
205
206 if (newCache.get() != nullptr) {
207 mCache.reset(newCache.release());
208 mCacheBufferSize = newCacheBufferSize;
209 }
210 mCachedOffset = newCachedOffset;
211 mCachedSize = newCachedSize;
212
213 ALOGV("readAt: rolling cache window to (%lld, %zu), cache buffer size %zu",
214 (long long)mCachedOffset, mCachedSize, mCacheBufferSize);
215 } else {
216 // expand cache buffer, but no need to roll the window
217 size_t newCacheBufferSize = mCacheBufferSize;
218 while (offset + size > mCachedOffset + newCacheBufferSize) {
219 newCacheBufferSize *= 2;
220 }
221 CHECK(newCacheBufferSize <= kMaxCacheBufferSize);
222 if (mCacheBufferSize < newCacheBufferSize) {
223 uint8_t* newCache = new uint8_t[newCacheBufferSize];
224 memcpy(newCache, mCache.get(), mCachedSize);
225 mCache.reset(newCache);
226 mCacheBufferSize = newCacheBufferSize;
227
228 ALOGV("readAt: current cache window (%lld, %zu), new cache buffer size %zu",
229 (long long) mCachedOffset, mCachedSize, mCacheBufferSize);
230 }
231 }
232 size_t bytesToRead = offset + size - mCachedOffset - mCachedSize;
233 size_t bytesRead = mStream->read(mCache.get() + mCachedSize, bytesToRead);
234 if (bytesRead > bytesToRead || bytesRead == 0) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700235 // bytesRead is invalid
236 mEOS = true;
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700237 bytesRead = 0;
238 } else if (bytesRead < bytesToRead) {
239 // read some bytes but not all, set EOS
Chong Zhangea280cb2017-08-02 11:10:10 -0700240 mEOS = true;
241 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700242 mCachedSize += bytesRead;
243 ALOGV("readAt: current cache window (%lld, %zu)",
244 (long long) mCachedOffset, mCachedSize);
245
246 // here bytesAvailable could be negative if offset jumped past EOS.
247 int64_t bytesAvailable = mCachedOffset + mCachedSize - offset;
248 if (bytesAvailable <= 0) {
249 return ERROR_END_OF_STREAM;
250 }
251 if (bytesAvailable < (int64_t)size) {
252 size = bytesAvailable;
253 }
254 memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size);
255 return size;
Chong Zhangea280cb2017-08-02 11:10:10 -0700256}
257
258status_t HeifDataSource::getSize(off64_t* size) {
259 if (!mStream->hasLength()) {
260 *size = -1;
261 ALOGE("getSize: not supported!");
262 return ERROR_UNSUPPORTED;
263 }
264 *size = mStream->getLength();
265 ALOGV("getSize: size=%lld", (long long)*size);
266 return OK;
267}
268
269/////////////////////////////////////////////////////////////////////////
270
Chong Zhang0c1407f2018-05-02 17:09:05 -0700271struct HeifDecoderImpl::DecodeThread : public Thread {
272 explicit DecodeThread(HeifDecoderImpl *decoder) : mDecoder(decoder) {}
273
274private:
275 HeifDecoderImpl* mDecoder;
276
277 bool threadLoop();
278
279 DISALLOW_EVIL_CONSTRUCTORS(DecodeThread);
280};
281
282bool HeifDecoderImpl::DecodeThread::threadLoop() {
283 return mDecoder->decodeAsync();
284}
285
286/////////////////////////////////////////////////////////////////////////
287
Chong Zhangea280cb2017-08-02 11:10:10 -0700288HeifDecoderImpl::HeifDecoderImpl() :
289 // output color format should always be set via setOutputColor(), in case
290 // it's not, default to HAL_PIXEL_FORMAT_RGB_565.
291 mOutputColor(HAL_PIXEL_FORMAT_RGB_565),
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700292 mCurScanline(0),
Chong Zhang0c1407f2018-05-02 17:09:05 -0700293 mWidth(0),
294 mHeight(0),
Chong Zhangd3e0d862017-10-03 13:17:13 -0700295 mFrameDecoded(false),
296 mHasImage(false),
Chong Zhang0c1407f2018-05-02 17:09:05 -0700297 mHasVideo(false),
298 mAvailableLines(0),
299 mNumSlices(1),
300 mSliceHeight(0),
301 mAsyncDecodeDone(false) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700302}
303
304HeifDecoderImpl::~HeifDecoderImpl() {
Chong Zhang0c1407f2018-05-02 17:09:05 -0700305 if (mThread != nullptr) {
306 mThread->join();
307 }
Chong Zhangea280cb2017-08-02 11:10:10 -0700308}
309
310bool HeifDecoderImpl::init(HeifStream* stream, HeifFrameInfo* frameInfo) {
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700311 mFrameDecoded = false;
Chong Zhangd3e0d862017-10-03 13:17:13 -0700312 mFrameMemory.clear();
313
Chong Zhangea280cb2017-08-02 11:10:10 -0700314 sp<HeifDataSource> dataSource = new HeifDataSource(stream);
315 if (!dataSource->init()) {
316 return false;
317 }
318 mDataSource = dataSource;
319
320 mRetriever = new MediaMetadataRetriever();
Chong Zhangd3e0d862017-10-03 13:17:13 -0700321 status_t err = mRetriever->setDataSource(mDataSource, "image/heif");
Chong Zhangea280cb2017-08-02 11:10:10 -0700322 if (err != OK) {
323 ALOGE("failed to set data source!");
324
325 mRetriever.clear();
326 mDataSource.clear();
327 return false;
328 }
329 ALOGV("successfully set data source.");
330
Chong Zhangd3e0d862017-10-03 13:17:13 -0700331 const char* hasImage = mRetriever->extractMetadata(METADATA_KEY_HAS_IMAGE);
Chong Zhangea280cb2017-08-02 11:10:10 -0700332 const char* hasVideo = mRetriever->extractMetadata(METADATA_KEY_HAS_VIDEO);
Chong Zhangd3e0d862017-10-03 13:17:13 -0700333
334 mHasImage = hasImage && !strcasecmp(hasImage, "yes");
335 mHasVideo = hasVideo && !strcasecmp(hasVideo, "yes");
Chong Zhang0c1407f2018-05-02 17:09:05 -0700336 sp<IMemory> sharedMem;
Chong Zhangd3e0d862017-10-03 13:17:13 -0700337 if (mHasImage) {
338 // image index < 0 to retrieve primary image
Chong Zhang0c1407f2018-05-02 17:09:05 -0700339 sharedMem = mRetriever->getImageAtIndex(
Chong Zhangd3e0d862017-10-03 13:17:13 -0700340 -1, mOutputColor, true /*metaOnly*/);
341 } else if (mHasVideo) {
Chong Zhang0c1407f2018-05-02 17:09:05 -0700342 sharedMem = mRetriever->getFrameAtTime(0,
Chong Zhangd3e0d862017-10-03 13:17:13 -0700343 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
344 mOutputColor, true /*metaOnly*/);
Chong Zhangea280cb2017-08-02 11:10:10 -0700345 }
346
Chong Zhang0c1407f2018-05-02 17:09:05 -0700347 if (sharedMem == nullptr || sharedMem->pointer() == nullptr) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700348 ALOGE("getFrameAtTime: videoFrame is a nullptr");
349 return false;
350 }
351
Chong Zhang0c1407f2018-05-02 17:09:05 -0700352 VideoFrame* videoFrame = static_cast<VideoFrame*>(sharedMem->pointer());
Chong Zhangea280cb2017-08-02 11:10:10 -0700353
354 ALOGV("Meta dimension %dx%d, display %dx%d, angle %d, iccSize %d",
355 videoFrame->mWidth,
356 videoFrame->mHeight,
357 videoFrame->mDisplayWidth,
358 videoFrame->mDisplayHeight,
359 videoFrame->mRotationAngle,
360 videoFrame->mIccSize);
361
362 if (frameInfo != nullptr) {
363 frameInfo->set(
Chong Zhang70b0afd2018-02-27 12:43:06 -0800364 videoFrame->mWidth,
365 videoFrame->mHeight,
Chong Zhangea280cb2017-08-02 11:10:10 -0700366 videoFrame->mRotationAngle,
367 videoFrame->mBytesPerPixel,
368 videoFrame->mIccSize,
369 videoFrame->getFlattenedIccData());
370 }
Chong Zhang0c1407f2018-05-02 17:09:05 -0700371 mWidth = videoFrame->mWidth;
372 mHeight = videoFrame->mHeight;
373 if (mHasImage && videoFrame->mTileHeight >= 512 && mWidth >= 3000 && mHeight >= 2000 ) {
374 // Try decoding in slices only if the image has tiles and is big enough.
375 mSliceHeight = videoFrame->mTileHeight;
376 mNumSlices = (videoFrame->mHeight + mSliceHeight - 1) / mSliceHeight;
377 ALOGV("mSliceHeight %u, mNumSlices %zu", mSliceHeight, mNumSlices);
378 }
Chong Zhangea280cb2017-08-02 11:10:10 -0700379 return true;
380}
381
382bool HeifDecoderImpl::getEncodedColor(HeifEncodedColor* /*outColor*/) const {
383 ALOGW("getEncodedColor: not implemented!");
384 return false;
385}
386
387bool HeifDecoderImpl::setOutputColor(HeifColorFormat heifColor) {
388 switch(heifColor) {
389 case kHeifColorFormat_RGB565:
390 {
391 mOutputColor = HAL_PIXEL_FORMAT_RGB_565;
392 return true;
393 }
394 case kHeifColorFormat_RGBA_8888:
395 {
396 mOutputColor = HAL_PIXEL_FORMAT_RGBA_8888;
397 return true;
398 }
399 case kHeifColorFormat_BGRA_8888:
400 {
401 mOutputColor = HAL_PIXEL_FORMAT_BGRA_8888;
402 return true;
403 }
404 default:
405 break;
406 }
407 ALOGE("Unsupported output color format %d", heifColor);
408 return false;
409}
410
Chong Zhang0c1407f2018-05-02 17:09:05 -0700411bool HeifDecoderImpl::decodeAsync() {
412 for (size_t i = 1; i < mNumSlices; i++) {
413 ALOGV("decodeAsync(): decoding slice %zu", i);
414 size_t top = i * mSliceHeight;
415 size_t bottom = (i + 1) * mSliceHeight;
416 if (bottom > mHeight) {
417 bottom = mHeight;
418 }
419 sp<IMemory> frameMemory = mRetriever->getImageRectAtIndex(
420 -1, mOutputColor, 0, top, mWidth, bottom);
421 {
422 Mutex::Autolock autolock(mLock);
423
424 if (frameMemory == nullptr || frameMemory->pointer() == nullptr) {
425 mAsyncDecodeDone = true;
426 mScanlineReady.signal();
427 break;
428 }
429 mFrameMemory = frameMemory;
430 mAvailableLines = bottom;
431 ALOGV("decodeAsync(): available lines %zu", mAvailableLines);
432 mScanlineReady.signal();
433 }
434 }
435 // Aggressive clear to avoid holding on to resources
436 mRetriever.clear();
437 mDataSource.clear();
438 return false;
439}
440
Chong Zhangea280cb2017-08-02 11:10:10 -0700441bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) {
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700442 // reset scanline pointer
443 mCurScanline = 0;
444
445 if (mFrameDecoded) {
446 return true;
447 }
448
Chong Zhang0c1407f2018-05-02 17:09:05 -0700449 // See if we want to decode in slices to allow client to start
450 // scanline processing in parallel with decode. If this fails
451 // we fallback to decoding the full frame.
452 if (mHasImage && mNumSlices > 1) {
453 // get first slice and metadata
454 sp<IMemory> frameMemory = mRetriever->getImageRectAtIndex(
455 -1, mOutputColor, 0, 0, mWidth, mSliceHeight);
456
457 if (frameMemory == nullptr || frameMemory->pointer() == nullptr) {
458 ALOGE("decode: metadata is a nullptr");
459 return false;
460 }
461
462 VideoFrame* videoFrame = static_cast<VideoFrame*>(frameMemory->pointer());
463
464 if (frameInfo != nullptr) {
465 frameInfo->set(
466 videoFrame->mWidth,
467 videoFrame->mHeight,
468 videoFrame->mRotationAngle,
469 videoFrame->mBytesPerPixel,
470 videoFrame->mIccSize,
471 videoFrame->getFlattenedIccData());
472 }
473
474 mFrameMemory = frameMemory;
475 mAvailableLines = mSliceHeight;
476 mThread = new DecodeThread(this);
477 if (mThread->run("HeifDecode", ANDROID_PRIORITY_FOREGROUND) == OK) {
478 mFrameDecoded = true;
479 return true;
480 }
481
482 // Fallback to decode without slicing
483 mThread.clear();
484 mNumSlices = 1;
485 mSliceHeight = 0;
486 mAvailableLines = 0;
487 mFrameMemory.clear();
488 }
489
Chong Zhangd3e0d862017-10-03 13:17:13 -0700490 if (mHasImage) {
491 // image index < 0 to retrieve primary image
492 mFrameMemory = mRetriever->getImageAtIndex(-1, mOutputColor);
493 } else if (mHasVideo) {
494 mFrameMemory = mRetriever->getFrameAtTime(0,
495 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC, mOutputColor);
496 }
497
Chong Zhangea280cb2017-08-02 11:10:10 -0700498 if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
Chong Zhang0c1407f2018-05-02 17:09:05 -0700499 ALOGE("decode: videoFrame is a nullptr");
Chong Zhangea280cb2017-08-02 11:10:10 -0700500 return false;
501 }
502
503 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700504 if (videoFrame->mSize == 0 ||
505 mFrameMemory->size() < videoFrame->getFlattenedSize()) {
Chong Zhang0c1407f2018-05-02 17:09:05 -0700506 ALOGE("decode: videoFrame size is invalid");
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700507 return false;
508 }
509
Chong Zhangea280cb2017-08-02 11:10:10 -0700510 ALOGV("Decoded dimension %dx%d, display %dx%d, angle %d, rowbytes %d, size %d",
511 videoFrame->mWidth,
512 videoFrame->mHeight,
513 videoFrame->mDisplayWidth,
514 videoFrame->mDisplayHeight,
515 videoFrame->mRotationAngle,
516 videoFrame->mRowBytes,
517 videoFrame->mSize);
518
519 if (frameInfo != nullptr) {
520 frameInfo->set(
Chong Zhang70b0afd2018-02-27 12:43:06 -0800521 videoFrame->mWidth,
522 videoFrame->mHeight,
Chong Zhangea280cb2017-08-02 11:10:10 -0700523 videoFrame->mRotationAngle,
524 videoFrame->mBytesPerPixel,
525 videoFrame->mIccSize,
526 videoFrame->getFlattenedIccData());
527 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700528 mFrameDecoded = true;
Chong Zhang4c6398b2017-09-07 17:43:19 -0700529
Chong Zhang0c1407f2018-05-02 17:09:05 -0700530 // Aggressively clear to avoid holding on to resources
Chong Zhang4c6398b2017-09-07 17:43:19 -0700531 mRetriever.clear();
532 mDataSource.clear();
Chong Zhangea280cb2017-08-02 11:10:10 -0700533 return true;
534}
535
Chong Zhang0c1407f2018-05-02 17:09:05 -0700536bool HeifDecoderImpl::getScanlineInner(uint8_t* dst) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700537 if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
538 return false;
539 }
540 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
Chong Zhangea280cb2017-08-02 11:10:10 -0700541 uint8_t* src = videoFrame->getFlattenedData() + videoFrame->mRowBytes * mCurScanline++;
Chong Zhang70b0afd2018-02-27 12:43:06 -0800542 memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mWidth);
Chong Zhangea280cb2017-08-02 11:10:10 -0700543 return true;
544}
545
Chong Zhang0c1407f2018-05-02 17:09:05 -0700546bool HeifDecoderImpl::getScanline(uint8_t* dst) {
547 if (mCurScanline >= mHeight) {
548 ALOGE("no more scanline available");
549 return false;
Chong Zhangea280cb2017-08-02 11:10:10 -0700550 }
Chong Zhangea280cb2017-08-02 11:10:10 -0700551
Chong Zhang0c1407f2018-05-02 17:09:05 -0700552 if (mNumSlices > 1) {
553 Mutex::Autolock autolock(mLock);
554
555 while (!mAsyncDecodeDone && mCurScanline >= mAvailableLines) {
556 mScanlineReady.wait(mLock);
557 }
558 return (mCurScanline < mAvailableLines) ? getScanlineInner(dst) : false;
559 }
560
561 return getScanlineInner(dst);
562}
563
564size_t HeifDecoderImpl::skipScanlines(size_t count) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700565 uint32_t oldScanline = mCurScanline;
566 mCurScanline += count;
Chong Zhang0c1407f2018-05-02 17:09:05 -0700567 if (mCurScanline > mHeight) {
568 mCurScanline = mHeight;
Chong Zhangea280cb2017-08-02 11:10:10 -0700569 }
570 return (mCurScanline > oldScanline) ? (mCurScanline - oldScanline) : 0;
571}
572
573} // namespace android