blob: a63a2dfd9b3133ae50cb5d388cd231089cab243c [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>
25#include <drm/drm_framework_common.h>
26#include <media/IDataSource.h>
27#include <media/mediametadataretriever.h>
Dongwon Kangd91dc5a2017-10-10 00:07:09 -070028#include <media/MediaSource.h>
Chong Zhangd3e0d862017-10-03 13:17:13 -070029#include <media/stagefright/foundation/ADebug.h>
Chong Zhangea280cb2017-08-02 11:10:10 -070030#include <private/media/VideoFrame.h>
31#include <utils/Log.h>
32#include <utils/RefBase.h>
33
34HeifDecoder* createHeifDecoder() {
35 return new android::HeifDecoderImpl();
36}
37
38namespace android {
39
40/*
41 * HeifDataSource
42 *
43 * Proxies data requests over IDataSource interface from MediaMetadataRetriever
44 * to the HeifStream interface we received from the heif decoder client.
45 */
46class HeifDataSource : public BnDataSource {
47public:
48 /*
49 * Constructs HeifDataSource; will take ownership of |stream|.
50 */
51 HeifDataSource(HeifStream* stream)
Chong Zhang3f3ffab2017-08-23 13:51:59 -070052 : mStream(stream), mEOS(false),
53 mCachedOffset(0), mCachedSize(0), mCacheBufferSize(0) {}
Chong Zhangea280cb2017-08-02 11:10:10 -070054
55 ~HeifDataSource() override {}
56
57 /*
58 * Initializes internal resources.
59 */
60 bool init();
61
62 sp<IMemory> getIMemory() override { return mMemory; }
63 ssize_t readAt(off64_t offset, size_t size) override;
64 status_t getSize(off64_t* size) override ;
65 void close() {}
66 uint32_t getFlags() override { return 0; }
67 String8 toString() override { return String8("HeifDataSource"); }
68 sp<DecryptHandle> DrmInitialization(const char*) override {
69 return nullptr;
70 }
71
72private:
Chong Zhangea280cb2017-08-02 11:10:10 -070073 enum {
Chong Zhang3f3ffab2017-08-23 13:51:59 -070074 /*
75 * Buffer size for passing the read data to mediaserver. Set to 64K
76 * (which is what MediaDataSource Java API's jni implementation uses).
77 */
Chong Zhangea280cb2017-08-02 11:10:10 -070078 kBufferSize = 64 * 1024,
Chong Zhang3f3ffab2017-08-23 13:51:59 -070079 /*
80 * Initial and max cache buffer size.
81 */
82 kInitialCacheBufferSize = 4 * 1024 * 1024,
83 kMaxCacheBufferSize = 64 * 1024 * 1024,
Chong Zhangea280cb2017-08-02 11:10:10 -070084 };
85 sp<IMemory> mMemory;
86 std::unique_ptr<HeifStream> mStream;
Chong Zhangea280cb2017-08-02 11:10:10 -070087 bool mEOS;
Chong Zhang3f3ffab2017-08-23 13:51:59 -070088 std::unique_ptr<uint8_t> mCache;
89 off64_t mCachedOffset;
90 size_t mCachedSize;
91 size_t mCacheBufferSize;
Chong Zhangea280cb2017-08-02 11:10:10 -070092};
93
94bool HeifDataSource::init() {
95 sp<MemoryDealer> memoryDealer =
96 new MemoryDealer(kBufferSize, "HeifDataSource");
97 mMemory = memoryDealer->allocate(kBufferSize);
98 if (mMemory == nullptr) {
99 ALOGE("Failed to allocate shared memory!");
100 return false;
101 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700102 mCache.reset(new uint8_t[kInitialCacheBufferSize]);
103 if (mCache.get() == nullptr) {
104 ALOGE("mFailed to allocate cache!");
105 return false;
106 }
107 mCacheBufferSize = kInitialCacheBufferSize;
Chong Zhangea280cb2017-08-02 11:10:10 -0700108 return true;
109}
110
111ssize_t HeifDataSource::readAt(off64_t offset, size_t size) {
112 ALOGV("readAt: offset=%lld, size=%zu", (long long)offset, size);
113
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700114 if (offset < mCachedOffset) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700115 // try seek, then rewind/skip, fail if none worked
116 if (mStream->seek(offset)) {
117 ALOGV("readAt: seek to offset=%lld", (long long)offset);
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700118 mCachedOffset = offset;
119 mCachedSize = 0;
Chong Zhangea280cb2017-08-02 11:10:10 -0700120 mEOS = false;
121 } else if (mStream->rewind()) {
122 ALOGV("readAt: rewind to offset=0");
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700123 mCachedOffset = 0;
124 mCachedSize = 0;
Chong Zhangea280cb2017-08-02 11:10:10 -0700125 mEOS = false;
126 } else {
127 ALOGE("readAt: couldn't seek or rewind!");
128 mEOS = true;
129 }
130 }
131
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700132 if (mEOS && (offset < mCachedOffset ||
133 offset >= (off64_t)(mCachedOffset + mCachedSize))) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700134 ALOGV("readAt: EOS");
135 return ERROR_END_OF_STREAM;
136 }
137
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700138 // at this point, offset must be >= mCachedOffset, other cases should
139 // have been caught above.
140 CHECK(offset >= mCachedOffset);
141
142 if (size == 0) {
143 return 0;
Chong Zhangea280cb2017-08-02 11:10:10 -0700144 }
145
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700146 // Can only read max of kBufferSize
Chong Zhangea280cb2017-08-02 11:10:10 -0700147 if (size > kBufferSize) {
148 size = kBufferSize;
149 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700150
151 // copy from cache if the request falls entirely in cache
152 if (offset + size <= mCachedOffset + mCachedSize) {
153 memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size);
154 return size;
155 }
156
157 // need to fetch more, check if we need to expand the cache buffer.
158 if ((off64_t)(offset + size) > mCachedOffset + kMaxCacheBufferSize) {
159 // it's reaching max cache buffer size, need to roll window, and possibly
160 // expand the cache buffer.
161 size_t newCacheBufferSize = mCacheBufferSize;
162 std::unique_ptr<uint8_t> newCache;
163 uint8_t* dst = mCache.get();
164 if (newCacheBufferSize < kMaxCacheBufferSize) {
165 newCacheBufferSize = kMaxCacheBufferSize;
166 newCache.reset(new uint8_t[newCacheBufferSize]);
167 dst = newCache.get();
168 }
169
170 // when rolling the cache window, try to keep about half the old bytes
171 // in case that the client goes back.
172 off64_t newCachedOffset = offset - (off64_t)(newCacheBufferSize / 2);
173 if (newCachedOffset < mCachedOffset) {
174 newCachedOffset = mCachedOffset;
175 }
176
177 int64_t newCachedSize = (int64_t)(mCachedOffset + mCachedSize) - newCachedOffset;
178 if (newCachedSize > 0) {
179 // in this case, the new cache region partially overlop the old cache,
180 // move the portion of the cache we want to save to the beginning of
181 // the cache buffer.
182 memcpy(dst, mCache.get() + newCachedOffset - mCachedOffset, newCachedSize);
183 } else if (newCachedSize < 0){
184 // in this case, the new cache region is entirely out of the old cache,
185 // in order to guarantee sequential read, we need to skip a number of
186 // bytes before reading.
187 size_t bytesToSkip = -newCachedSize;
188 size_t bytesSkipped = mStream->read(nullptr, bytesToSkip);
189 if (bytesSkipped != bytesToSkip) {
190 // bytesSkipped is invalid, there is not enough bytes to reach
191 // the requested offset.
192 ALOGE("readAt: skip failed, EOS");
193
194 mEOS = true;
195 mCachedOffset = newCachedOffset;
196 mCachedSize = 0;
197 return ERROR_END_OF_STREAM;
198 }
199 // set cache size to 0, since we're not keeping any old cache
200 newCachedSize = 0;
201 }
202
203 if (newCache.get() != nullptr) {
204 mCache.reset(newCache.release());
205 mCacheBufferSize = newCacheBufferSize;
206 }
207 mCachedOffset = newCachedOffset;
208 mCachedSize = newCachedSize;
209
210 ALOGV("readAt: rolling cache window to (%lld, %zu), cache buffer size %zu",
211 (long long)mCachedOffset, mCachedSize, mCacheBufferSize);
212 } else {
213 // expand cache buffer, but no need to roll the window
214 size_t newCacheBufferSize = mCacheBufferSize;
215 while (offset + size > mCachedOffset + newCacheBufferSize) {
216 newCacheBufferSize *= 2;
217 }
218 CHECK(newCacheBufferSize <= kMaxCacheBufferSize);
219 if (mCacheBufferSize < newCacheBufferSize) {
220 uint8_t* newCache = new uint8_t[newCacheBufferSize];
221 memcpy(newCache, mCache.get(), mCachedSize);
222 mCache.reset(newCache);
223 mCacheBufferSize = newCacheBufferSize;
224
225 ALOGV("readAt: current cache window (%lld, %zu), new cache buffer size %zu",
226 (long long) mCachedOffset, mCachedSize, mCacheBufferSize);
227 }
228 }
229 size_t bytesToRead = offset + size - mCachedOffset - mCachedSize;
230 size_t bytesRead = mStream->read(mCache.get() + mCachedSize, bytesToRead);
231 if (bytesRead > bytesToRead || bytesRead == 0) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700232 // bytesRead is invalid
233 mEOS = true;
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700234 bytesRead = 0;
235 } else if (bytesRead < bytesToRead) {
236 // read some bytes but not all, set EOS
Chong Zhangea280cb2017-08-02 11:10:10 -0700237 mEOS = true;
238 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700239 mCachedSize += bytesRead;
240 ALOGV("readAt: current cache window (%lld, %zu)",
241 (long long) mCachedOffset, mCachedSize);
242
243 // here bytesAvailable could be negative if offset jumped past EOS.
244 int64_t bytesAvailable = mCachedOffset + mCachedSize - offset;
245 if (bytesAvailable <= 0) {
246 return ERROR_END_OF_STREAM;
247 }
248 if (bytesAvailable < (int64_t)size) {
249 size = bytesAvailable;
250 }
251 memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size);
252 return size;
Chong Zhangea280cb2017-08-02 11:10:10 -0700253}
254
255status_t HeifDataSource::getSize(off64_t* size) {
256 if (!mStream->hasLength()) {
257 *size = -1;
258 ALOGE("getSize: not supported!");
259 return ERROR_UNSUPPORTED;
260 }
261 *size = mStream->getLength();
262 ALOGV("getSize: size=%lld", (long long)*size);
263 return OK;
264}
265
266/////////////////////////////////////////////////////////////////////////
267
268HeifDecoderImpl::HeifDecoderImpl() :
269 // output color format should always be set via setOutputColor(), in case
270 // it's not, default to HAL_PIXEL_FORMAT_RGB_565.
271 mOutputColor(HAL_PIXEL_FORMAT_RGB_565),
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700272 mCurScanline(0),
Chong Zhangd3e0d862017-10-03 13:17:13 -0700273 mFrameDecoded(false),
274 mHasImage(false),
275 mHasVideo(false) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700276}
277
278HeifDecoderImpl::~HeifDecoderImpl() {
279}
280
281bool HeifDecoderImpl::init(HeifStream* stream, HeifFrameInfo* frameInfo) {
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700282 mFrameDecoded = false;
Chong Zhangd3e0d862017-10-03 13:17:13 -0700283 mFrameMemory.clear();
284
Chong Zhangea280cb2017-08-02 11:10:10 -0700285 sp<HeifDataSource> dataSource = new HeifDataSource(stream);
286 if (!dataSource->init()) {
287 return false;
288 }
289 mDataSource = dataSource;
290
291 mRetriever = new MediaMetadataRetriever();
Chong Zhangd3e0d862017-10-03 13:17:13 -0700292 status_t err = mRetriever->setDataSource(mDataSource, "image/heif");
Chong Zhangea280cb2017-08-02 11:10:10 -0700293 if (err != OK) {
294 ALOGE("failed to set data source!");
295
296 mRetriever.clear();
297 mDataSource.clear();
298 return false;
299 }
300 ALOGV("successfully set data source.");
301
Chong Zhangd3e0d862017-10-03 13:17:13 -0700302 const char* hasImage = mRetriever->extractMetadata(METADATA_KEY_HAS_IMAGE);
Chong Zhangea280cb2017-08-02 11:10:10 -0700303 const char* hasVideo = mRetriever->extractMetadata(METADATA_KEY_HAS_VIDEO);
Chong Zhangd3e0d862017-10-03 13:17:13 -0700304
305 mHasImage = hasImage && !strcasecmp(hasImage, "yes");
306 mHasVideo = hasVideo && !strcasecmp(hasVideo, "yes");
307 if (mHasImage) {
308 // image index < 0 to retrieve primary image
309 mFrameMemory = mRetriever->getImageAtIndex(
310 -1, mOutputColor, true /*metaOnly*/);
311 } else if (mHasVideo) {
312 mFrameMemory = mRetriever->getFrameAtTime(0,
313 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
314 mOutputColor, true /*metaOnly*/);
Chong Zhangea280cb2017-08-02 11:10:10 -0700315 }
316
Chong Zhangea280cb2017-08-02 11:10:10 -0700317 if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
318 ALOGE("getFrameAtTime: videoFrame is a nullptr");
319 return false;
320 }
321
322 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
323
324 ALOGV("Meta dimension %dx%d, display %dx%d, angle %d, iccSize %d",
325 videoFrame->mWidth,
326 videoFrame->mHeight,
327 videoFrame->mDisplayWidth,
328 videoFrame->mDisplayHeight,
329 videoFrame->mRotationAngle,
330 videoFrame->mIccSize);
331
332 if (frameInfo != nullptr) {
333 frameInfo->set(
Chong Zhangee079fe2017-08-23 13:51:17 -0700334 videoFrame->mDisplayWidth,
335 videoFrame->mDisplayHeight,
Chong Zhangea280cb2017-08-02 11:10:10 -0700336 videoFrame->mRotationAngle,
337 videoFrame->mBytesPerPixel,
338 videoFrame->mIccSize,
339 videoFrame->getFlattenedIccData());
340 }
341 return true;
342}
343
344bool HeifDecoderImpl::getEncodedColor(HeifEncodedColor* /*outColor*/) const {
345 ALOGW("getEncodedColor: not implemented!");
346 return false;
347}
348
349bool HeifDecoderImpl::setOutputColor(HeifColorFormat heifColor) {
350 switch(heifColor) {
351 case kHeifColorFormat_RGB565:
352 {
353 mOutputColor = HAL_PIXEL_FORMAT_RGB_565;
354 return true;
355 }
356 case kHeifColorFormat_RGBA_8888:
357 {
358 mOutputColor = HAL_PIXEL_FORMAT_RGBA_8888;
359 return true;
360 }
361 case kHeifColorFormat_BGRA_8888:
362 {
363 mOutputColor = HAL_PIXEL_FORMAT_BGRA_8888;
364 return true;
365 }
366 default:
367 break;
368 }
369 ALOGE("Unsupported output color format %d", heifColor);
370 return false;
371}
372
373bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) {
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700374 // reset scanline pointer
375 mCurScanline = 0;
376
377 if (mFrameDecoded) {
378 return true;
379 }
380
Chong Zhangd3e0d862017-10-03 13:17:13 -0700381 if (mHasImage) {
382 // image index < 0 to retrieve primary image
383 mFrameMemory = mRetriever->getImageAtIndex(-1, mOutputColor);
384 } else if (mHasVideo) {
385 mFrameMemory = mRetriever->getFrameAtTime(0,
386 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC, mOutputColor);
387 }
388
Chong Zhangea280cb2017-08-02 11:10:10 -0700389 if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
390 ALOGE("getFrameAtTime: videoFrame is a nullptr");
391 return false;
392 }
393
394 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700395 if (videoFrame->mSize == 0 ||
396 mFrameMemory->size() < videoFrame->getFlattenedSize()) {
397 ALOGE("getFrameAtTime: videoFrame size is invalid");
398 return false;
399 }
400
Chong Zhangea280cb2017-08-02 11:10:10 -0700401 ALOGV("Decoded dimension %dx%d, display %dx%d, angle %d, rowbytes %d, size %d",
402 videoFrame->mWidth,
403 videoFrame->mHeight,
404 videoFrame->mDisplayWidth,
405 videoFrame->mDisplayHeight,
406 videoFrame->mRotationAngle,
407 videoFrame->mRowBytes,
408 videoFrame->mSize);
409
410 if (frameInfo != nullptr) {
411 frameInfo->set(
Chong Zhangee079fe2017-08-23 13:51:17 -0700412 videoFrame->mDisplayWidth,
413 videoFrame->mDisplayHeight,
Chong Zhangea280cb2017-08-02 11:10:10 -0700414 videoFrame->mRotationAngle,
415 videoFrame->mBytesPerPixel,
416 videoFrame->mIccSize,
417 videoFrame->getFlattenedIccData());
418 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700419 mFrameDecoded = true;
Chong Zhang4c6398b2017-09-07 17:43:19 -0700420
421 // Aggressive clear to avoid holding on to resources
422 mRetriever.clear();
423 mDataSource.clear();
Chong Zhangea280cb2017-08-02 11:10:10 -0700424 return true;
425}
426
427bool HeifDecoderImpl::getScanline(uint8_t* dst) {
428 if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
429 return false;
430 }
431 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
Chong Zhangee079fe2017-08-23 13:51:17 -0700432 if (mCurScanline >= videoFrame->mDisplayHeight) {
433 ALOGE("no more scanline available");
Chong Zhangea280cb2017-08-02 11:10:10 -0700434 return false;
435 }
436 uint8_t* src = videoFrame->getFlattenedData() + videoFrame->mRowBytes * mCurScanline++;
Chong Zhangee079fe2017-08-23 13:51:17 -0700437 memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mDisplayWidth);
Chong Zhangea280cb2017-08-02 11:10:10 -0700438 return true;
439}
440
441size_t HeifDecoderImpl::skipScanlines(size_t count) {
442 if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
443 return 0;
444 }
445 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
446
447 uint32_t oldScanline = mCurScanline;
448 mCurScanline += count;
Chong Zhangee079fe2017-08-23 13:51:17 -0700449 if (mCurScanline > videoFrame->mDisplayHeight) {
450 mCurScanline = videoFrame->mDisplayHeight;
Chong Zhangea280cb2017-08-02 11:10:10 -0700451 }
452 return (mCurScanline > oldScanline) ? (mCurScanline - oldScanline) : 0;
453}
454
455} // namespace android