blob: 2dfbdca024637c677bbd3229ecef46b4543ece70 [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"); }
69 sp<DecryptHandle> DrmInitialization(const char*) override {
70 return nullptr;
71 }
72
73private:
Chong Zhangea280cb2017-08-02 11:10:10 -070074 enum {
Chong Zhang3f3ffab2017-08-23 13:51:59 -070075 /*
76 * Buffer size for passing the read data to mediaserver. Set to 64K
77 * (which is what MediaDataSource Java API's jni implementation uses).
78 */
Chong Zhangea280cb2017-08-02 11:10:10 -070079 kBufferSize = 64 * 1024,
Chong Zhang3f3ffab2017-08-23 13:51:59 -070080 /*
81 * Initial and max cache buffer size.
82 */
83 kInitialCacheBufferSize = 4 * 1024 * 1024,
84 kMaxCacheBufferSize = 64 * 1024 * 1024,
Chong Zhangea280cb2017-08-02 11:10:10 -070085 };
86 sp<IMemory> mMemory;
87 std::unique_ptr<HeifStream> mStream;
Chong Zhangea280cb2017-08-02 11:10:10 -070088 bool mEOS;
Chong Zhang3f3ffab2017-08-23 13:51:59 -070089 std::unique_ptr<uint8_t> mCache;
90 off64_t mCachedOffset;
91 size_t mCachedSize;
92 size_t mCacheBufferSize;
Chong Zhangea280cb2017-08-02 11:10:10 -070093};
94
95bool HeifDataSource::init() {
96 sp<MemoryDealer> memoryDealer =
97 new MemoryDealer(kBufferSize, "HeifDataSource");
98 mMemory = memoryDealer->allocate(kBufferSize);
99 if (mMemory == nullptr) {
100 ALOGE("Failed to allocate shared memory!");
101 return false;
102 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700103 mCache.reset(new uint8_t[kInitialCacheBufferSize]);
104 if (mCache.get() == nullptr) {
105 ALOGE("mFailed to allocate cache!");
106 return false;
107 }
108 mCacheBufferSize = kInitialCacheBufferSize;
Chong Zhangea280cb2017-08-02 11:10:10 -0700109 return true;
110}
111
112ssize_t HeifDataSource::readAt(off64_t offset, size_t size) {
113 ALOGV("readAt: offset=%lld, size=%zu", (long long)offset, size);
114
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700115 if (offset < mCachedOffset) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700116 // try seek, then rewind/skip, fail if none worked
117 if (mStream->seek(offset)) {
118 ALOGV("readAt: seek to offset=%lld", (long long)offset);
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700119 mCachedOffset = offset;
120 mCachedSize = 0;
Chong Zhangea280cb2017-08-02 11:10:10 -0700121 mEOS = false;
122 } else if (mStream->rewind()) {
123 ALOGV("readAt: rewind to offset=0");
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700124 mCachedOffset = 0;
125 mCachedSize = 0;
Chong Zhangea280cb2017-08-02 11:10:10 -0700126 mEOS = false;
127 } else {
128 ALOGE("readAt: couldn't seek or rewind!");
129 mEOS = true;
130 }
131 }
132
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700133 if (mEOS && (offset < mCachedOffset ||
134 offset >= (off64_t)(mCachedOffset + mCachedSize))) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700135 ALOGV("readAt: EOS");
136 return ERROR_END_OF_STREAM;
137 }
138
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700139 // at this point, offset must be >= mCachedOffset, other cases should
140 // have been caught above.
141 CHECK(offset >= mCachedOffset);
142
143 if (size == 0) {
144 return 0;
Chong Zhangea280cb2017-08-02 11:10:10 -0700145 }
146
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700147 // Can only read max of kBufferSize
Chong Zhangea280cb2017-08-02 11:10:10 -0700148 if (size > kBufferSize) {
149 size = kBufferSize;
150 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700151
152 // copy from cache if the request falls entirely in cache
153 if (offset + size <= mCachedOffset + mCachedSize) {
154 memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size);
155 return size;
156 }
157
158 // need to fetch more, check if we need to expand the cache buffer.
159 if ((off64_t)(offset + size) > mCachedOffset + kMaxCacheBufferSize) {
160 // it's reaching max cache buffer size, need to roll window, and possibly
161 // expand the cache buffer.
162 size_t newCacheBufferSize = mCacheBufferSize;
163 std::unique_ptr<uint8_t> newCache;
164 uint8_t* dst = mCache.get();
165 if (newCacheBufferSize < kMaxCacheBufferSize) {
166 newCacheBufferSize = kMaxCacheBufferSize;
167 newCache.reset(new uint8_t[newCacheBufferSize]);
168 dst = newCache.get();
169 }
170
171 // when rolling the cache window, try to keep about half the old bytes
172 // in case that the client goes back.
173 off64_t newCachedOffset = offset - (off64_t)(newCacheBufferSize / 2);
174 if (newCachedOffset < mCachedOffset) {
175 newCachedOffset = mCachedOffset;
176 }
177
178 int64_t newCachedSize = (int64_t)(mCachedOffset + mCachedSize) - newCachedOffset;
179 if (newCachedSize > 0) {
180 // in this case, the new cache region partially overlop the old cache,
181 // move the portion of the cache we want to save to the beginning of
182 // the cache buffer.
183 memcpy(dst, mCache.get() + newCachedOffset - mCachedOffset, newCachedSize);
184 } else if (newCachedSize < 0){
185 // in this case, the new cache region is entirely out of the old cache,
186 // in order to guarantee sequential read, we need to skip a number of
187 // bytes before reading.
188 size_t bytesToSkip = -newCachedSize;
189 size_t bytesSkipped = mStream->read(nullptr, bytesToSkip);
190 if (bytesSkipped != bytesToSkip) {
191 // bytesSkipped is invalid, there is not enough bytes to reach
192 // the requested offset.
193 ALOGE("readAt: skip failed, EOS");
194
195 mEOS = true;
196 mCachedOffset = newCachedOffset;
197 mCachedSize = 0;
198 return ERROR_END_OF_STREAM;
199 }
200 // set cache size to 0, since we're not keeping any old cache
201 newCachedSize = 0;
202 }
203
204 if (newCache.get() != nullptr) {
205 mCache.reset(newCache.release());
206 mCacheBufferSize = newCacheBufferSize;
207 }
208 mCachedOffset = newCachedOffset;
209 mCachedSize = newCachedSize;
210
211 ALOGV("readAt: rolling cache window to (%lld, %zu), cache buffer size %zu",
212 (long long)mCachedOffset, mCachedSize, mCacheBufferSize);
213 } else {
214 // expand cache buffer, but no need to roll the window
215 size_t newCacheBufferSize = mCacheBufferSize;
216 while (offset + size > mCachedOffset + newCacheBufferSize) {
217 newCacheBufferSize *= 2;
218 }
219 CHECK(newCacheBufferSize <= kMaxCacheBufferSize);
220 if (mCacheBufferSize < newCacheBufferSize) {
221 uint8_t* newCache = new uint8_t[newCacheBufferSize];
222 memcpy(newCache, mCache.get(), mCachedSize);
223 mCache.reset(newCache);
224 mCacheBufferSize = newCacheBufferSize;
225
226 ALOGV("readAt: current cache window (%lld, %zu), new cache buffer size %zu",
227 (long long) mCachedOffset, mCachedSize, mCacheBufferSize);
228 }
229 }
230 size_t bytesToRead = offset + size - mCachedOffset - mCachedSize;
231 size_t bytesRead = mStream->read(mCache.get() + mCachedSize, bytesToRead);
232 if (bytesRead > bytesToRead || bytesRead == 0) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700233 // bytesRead is invalid
234 mEOS = true;
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700235 bytesRead = 0;
236 } else if (bytesRead < bytesToRead) {
237 // read some bytes but not all, set EOS
Chong Zhangea280cb2017-08-02 11:10:10 -0700238 mEOS = true;
239 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700240 mCachedSize += bytesRead;
241 ALOGV("readAt: current cache window (%lld, %zu)",
242 (long long) mCachedOffset, mCachedSize);
243
244 // here bytesAvailable could be negative if offset jumped past EOS.
245 int64_t bytesAvailable = mCachedOffset + mCachedSize - offset;
246 if (bytesAvailable <= 0) {
247 return ERROR_END_OF_STREAM;
248 }
249 if (bytesAvailable < (int64_t)size) {
250 size = bytesAvailable;
251 }
252 memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size);
253 return size;
Chong Zhangea280cb2017-08-02 11:10:10 -0700254}
255
256status_t HeifDataSource::getSize(off64_t* size) {
257 if (!mStream->hasLength()) {
258 *size = -1;
259 ALOGE("getSize: not supported!");
260 return ERROR_UNSUPPORTED;
261 }
262 *size = mStream->getLength();
263 ALOGV("getSize: size=%lld", (long long)*size);
264 return OK;
265}
266
267/////////////////////////////////////////////////////////////////////////
268
269HeifDecoderImpl::HeifDecoderImpl() :
270 // output color format should always be set via setOutputColor(), in case
271 // it's not, default to HAL_PIXEL_FORMAT_RGB_565.
272 mOutputColor(HAL_PIXEL_FORMAT_RGB_565),
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700273 mCurScanline(0),
Chong Zhangd3e0d862017-10-03 13:17:13 -0700274 mFrameDecoded(false),
275 mHasImage(false),
276 mHasVideo(false) {
Chong Zhangea280cb2017-08-02 11:10:10 -0700277}
278
279HeifDecoderImpl::~HeifDecoderImpl() {
280}
281
282bool HeifDecoderImpl::init(HeifStream* stream, HeifFrameInfo* frameInfo) {
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700283 mFrameDecoded = false;
Chong Zhangd3e0d862017-10-03 13:17:13 -0700284 mFrameMemory.clear();
285
Chong Zhangea280cb2017-08-02 11:10:10 -0700286 sp<HeifDataSource> dataSource = new HeifDataSource(stream);
287 if (!dataSource->init()) {
288 return false;
289 }
290 mDataSource = dataSource;
291
292 mRetriever = new MediaMetadataRetriever();
Chong Zhangd3e0d862017-10-03 13:17:13 -0700293 status_t err = mRetriever->setDataSource(mDataSource, "image/heif");
Chong Zhangea280cb2017-08-02 11:10:10 -0700294 if (err != OK) {
295 ALOGE("failed to set data source!");
296
297 mRetriever.clear();
298 mDataSource.clear();
299 return false;
300 }
301 ALOGV("successfully set data source.");
302
Chong Zhangd3e0d862017-10-03 13:17:13 -0700303 const char* hasImage = mRetriever->extractMetadata(METADATA_KEY_HAS_IMAGE);
Chong Zhangea280cb2017-08-02 11:10:10 -0700304 const char* hasVideo = mRetriever->extractMetadata(METADATA_KEY_HAS_VIDEO);
Chong Zhangd3e0d862017-10-03 13:17:13 -0700305
306 mHasImage = hasImage && !strcasecmp(hasImage, "yes");
307 mHasVideo = hasVideo && !strcasecmp(hasVideo, "yes");
308 if (mHasImage) {
309 // image index < 0 to retrieve primary image
310 mFrameMemory = mRetriever->getImageAtIndex(
311 -1, mOutputColor, true /*metaOnly*/);
312 } else if (mHasVideo) {
313 mFrameMemory = mRetriever->getFrameAtTime(0,
314 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
315 mOutputColor, true /*metaOnly*/);
Chong Zhangea280cb2017-08-02 11:10:10 -0700316 }
317
Chong Zhangea280cb2017-08-02 11:10:10 -0700318 if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
319 ALOGE("getFrameAtTime: videoFrame is a nullptr");
320 return false;
321 }
322
323 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
324
325 ALOGV("Meta dimension %dx%d, display %dx%d, angle %d, iccSize %d",
326 videoFrame->mWidth,
327 videoFrame->mHeight,
328 videoFrame->mDisplayWidth,
329 videoFrame->mDisplayHeight,
330 videoFrame->mRotationAngle,
331 videoFrame->mIccSize);
332
333 if (frameInfo != nullptr) {
334 frameInfo->set(
Chong Zhangee079fe2017-08-23 13:51:17 -0700335 videoFrame->mDisplayWidth,
336 videoFrame->mDisplayHeight,
Chong Zhangea280cb2017-08-02 11:10:10 -0700337 videoFrame->mRotationAngle,
338 videoFrame->mBytesPerPixel,
339 videoFrame->mIccSize,
340 videoFrame->getFlattenedIccData());
341 }
342 return true;
343}
344
345bool HeifDecoderImpl::getEncodedColor(HeifEncodedColor* /*outColor*/) const {
346 ALOGW("getEncodedColor: not implemented!");
347 return false;
348}
349
350bool HeifDecoderImpl::setOutputColor(HeifColorFormat heifColor) {
351 switch(heifColor) {
352 case kHeifColorFormat_RGB565:
353 {
354 mOutputColor = HAL_PIXEL_FORMAT_RGB_565;
355 return true;
356 }
357 case kHeifColorFormat_RGBA_8888:
358 {
359 mOutputColor = HAL_PIXEL_FORMAT_RGBA_8888;
360 return true;
361 }
362 case kHeifColorFormat_BGRA_8888:
363 {
364 mOutputColor = HAL_PIXEL_FORMAT_BGRA_8888;
365 return true;
366 }
367 default:
368 break;
369 }
370 ALOGE("Unsupported output color format %d", heifColor);
371 return false;
372}
373
374bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) {
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700375 // reset scanline pointer
376 mCurScanline = 0;
377
378 if (mFrameDecoded) {
379 return true;
380 }
381
Chong Zhangd3e0d862017-10-03 13:17:13 -0700382 if (mHasImage) {
383 // image index < 0 to retrieve primary image
384 mFrameMemory = mRetriever->getImageAtIndex(-1, mOutputColor);
385 } else if (mHasVideo) {
386 mFrameMemory = mRetriever->getFrameAtTime(0,
387 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC, mOutputColor);
388 }
389
Chong Zhangea280cb2017-08-02 11:10:10 -0700390 if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
391 ALOGE("getFrameAtTime: videoFrame is a nullptr");
392 return false;
393 }
394
395 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700396 if (videoFrame->mSize == 0 ||
397 mFrameMemory->size() < videoFrame->getFlattenedSize()) {
398 ALOGE("getFrameAtTime: videoFrame size is invalid");
399 return false;
400 }
401
Chong Zhangea280cb2017-08-02 11:10:10 -0700402 ALOGV("Decoded dimension %dx%d, display %dx%d, angle %d, rowbytes %d, size %d",
403 videoFrame->mWidth,
404 videoFrame->mHeight,
405 videoFrame->mDisplayWidth,
406 videoFrame->mDisplayHeight,
407 videoFrame->mRotationAngle,
408 videoFrame->mRowBytes,
409 videoFrame->mSize);
410
411 if (frameInfo != nullptr) {
412 frameInfo->set(
Chong Zhangee079fe2017-08-23 13:51:17 -0700413 videoFrame->mDisplayWidth,
414 videoFrame->mDisplayHeight,
Chong Zhangea280cb2017-08-02 11:10:10 -0700415 videoFrame->mRotationAngle,
416 videoFrame->mBytesPerPixel,
417 videoFrame->mIccSize,
418 videoFrame->getFlattenedIccData());
419 }
Chong Zhang3f3ffab2017-08-23 13:51:59 -0700420 mFrameDecoded = true;
Chong Zhang4c6398b2017-09-07 17:43:19 -0700421
422 // Aggressive clear to avoid holding on to resources
423 mRetriever.clear();
424 mDataSource.clear();
Chong Zhangea280cb2017-08-02 11:10:10 -0700425 return true;
426}
427
428bool HeifDecoderImpl::getScanline(uint8_t* dst) {
429 if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
430 return false;
431 }
432 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
Chong Zhangee079fe2017-08-23 13:51:17 -0700433 if (mCurScanline >= videoFrame->mDisplayHeight) {
434 ALOGE("no more scanline available");
Chong Zhangea280cb2017-08-02 11:10:10 -0700435 return false;
436 }
437 uint8_t* src = videoFrame->getFlattenedData() + videoFrame->mRowBytes * mCurScanline++;
Chong Zhangee079fe2017-08-23 13:51:17 -0700438 memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mDisplayWidth);
Chong Zhangea280cb2017-08-02 11:10:10 -0700439 return true;
440}
441
442size_t HeifDecoderImpl::skipScanlines(size_t count) {
443 if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
444 return 0;
445 }
446 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
447
448 uint32_t oldScanline = mCurScanline;
449 mCurScanline += count;
Chong Zhangee079fe2017-08-23 13:51:17 -0700450 if (mCurScanline > videoFrame->mDisplayHeight) {
451 mCurScanline = videoFrame->mDisplayHeight;
Chong Zhangea280cb2017-08-02 11:10:10 -0700452 }
453 return (mCurScanline > oldScanline) ? (mCurScanline - oldScanline) : 0;
454}
455
456} // namespace android