1*ec779b8eSAndroid Build Coastguard Worker /*
2*ec779b8eSAndroid Build Coastguard Worker * Copyright (C) 2017 The Android Open Source Project
3*ec779b8eSAndroid Build Coastguard Worker *
4*ec779b8eSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*ec779b8eSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*ec779b8eSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*ec779b8eSAndroid Build Coastguard Worker *
8*ec779b8eSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*ec779b8eSAndroid Build Coastguard Worker *
10*ec779b8eSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*ec779b8eSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*ec779b8eSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*ec779b8eSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*ec779b8eSAndroid Build Coastguard Worker * limitations under the License.
15*ec779b8eSAndroid Build Coastguard Worker */
16*ec779b8eSAndroid Build Coastguard Worker
17*ec779b8eSAndroid Build Coastguard Worker //#define LOG_NDEBUG 0
18*ec779b8eSAndroid Build Coastguard Worker #include "include/HeifDecoderAPI.h"
19*ec779b8eSAndroid Build Coastguard Worker #define LOG_TAG "HeifDecoderImpl"
20*ec779b8eSAndroid Build Coastguard Worker
21*ec779b8eSAndroid Build Coastguard Worker #include "HeifDecoderImpl.h"
22*ec779b8eSAndroid Build Coastguard Worker
23*ec779b8eSAndroid Build Coastguard Worker #include <stdio.h>
24*ec779b8eSAndroid Build Coastguard Worker
25*ec779b8eSAndroid Build Coastguard Worker #include <android/IDataSource.h>
26*ec779b8eSAndroid Build Coastguard Worker #include <binder/IMemory.h>
27*ec779b8eSAndroid Build Coastguard Worker #include <binder/MemoryDealer.h>
28*ec779b8eSAndroid Build Coastguard Worker #include <drm/drm_framework_common.h>
29*ec779b8eSAndroid Build Coastguard Worker #include <media/mediametadataretriever.h>
30*ec779b8eSAndroid Build Coastguard Worker #include <media/stagefright/MediaSource.h>
31*ec779b8eSAndroid Build Coastguard Worker #include <media/stagefright/foundation/ADebug.h>
32*ec779b8eSAndroid Build Coastguard Worker #include <private/media/VideoFrame.h>
33*ec779b8eSAndroid Build Coastguard Worker #include <utils/Log.h>
34*ec779b8eSAndroid Build Coastguard Worker #include <utils/RefBase.h>
35*ec779b8eSAndroid Build Coastguard Worker #include <algorithm>
36*ec779b8eSAndroid Build Coastguard Worker #include <vector>
37*ec779b8eSAndroid Build Coastguard Worker
createHeifDecoder()38*ec779b8eSAndroid Build Coastguard Worker HeifDecoder* createHeifDecoder() {
39*ec779b8eSAndroid Build Coastguard Worker return new android::HeifDecoderImpl();
40*ec779b8eSAndroid Build Coastguard Worker }
41*ec779b8eSAndroid Build Coastguard Worker
42*ec779b8eSAndroid Build Coastguard Worker namespace android {
43*ec779b8eSAndroid Build Coastguard Worker
initFrameInfo(HeifFrameInfo * info,const VideoFrame * videoFrame)44*ec779b8eSAndroid Build Coastguard Worker void initFrameInfo(HeifFrameInfo *info, const VideoFrame *videoFrame) {
45*ec779b8eSAndroid Build Coastguard Worker info->mWidth = videoFrame->mDisplayWidth;
46*ec779b8eSAndroid Build Coastguard Worker // Number of scanlines is mDisplayHeight. Clamp it to mHeight to guard
47*ec779b8eSAndroid Build Coastguard Worker // against malformed streams claiming that mDisplayHeight is greater than
48*ec779b8eSAndroid Build Coastguard Worker // mHeight.
49*ec779b8eSAndroid Build Coastguard Worker info->mHeight = std::min(videoFrame->mDisplayHeight, videoFrame->mHeight);
50*ec779b8eSAndroid Build Coastguard Worker info->mRotationAngle = videoFrame->mRotationAngle;
51*ec779b8eSAndroid Build Coastguard Worker info->mBytesPerPixel = videoFrame->mBytesPerPixel;
52*ec779b8eSAndroid Build Coastguard Worker info->mDurationUs = videoFrame->mDurationUs;
53*ec779b8eSAndroid Build Coastguard Worker info->mBitDepth = videoFrame->mBitDepth;
54*ec779b8eSAndroid Build Coastguard Worker if (videoFrame->mIccSize > 0) {
55*ec779b8eSAndroid Build Coastguard Worker info->mIccData.assign(
56*ec779b8eSAndroid Build Coastguard Worker videoFrame->getFlattenedIccData(),
57*ec779b8eSAndroid Build Coastguard Worker videoFrame->getFlattenedIccData() + videoFrame->mIccSize);
58*ec779b8eSAndroid Build Coastguard Worker } else {
59*ec779b8eSAndroid Build Coastguard Worker // clear old Icc data if there is no Icc data.
60*ec779b8eSAndroid Build Coastguard Worker info->mIccData.clear();
61*ec779b8eSAndroid Build Coastguard Worker }
62*ec779b8eSAndroid Build Coastguard Worker }
63*ec779b8eSAndroid Build Coastguard Worker
64*ec779b8eSAndroid Build Coastguard Worker /*
65*ec779b8eSAndroid Build Coastguard Worker * HeifDataSource
66*ec779b8eSAndroid Build Coastguard Worker *
67*ec779b8eSAndroid Build Coastguard Worker * Proxies data requests over IDataSource interface from MediaMetadataRetriever
68*ec779b8eSAndroid Build Coastguard Worker * to the HeifStream interface we received from the heif decoder client.
69*ec779b8eSAndroid Build Coastguard Worker */
70*ec779b8eSAndroid Build Coastguard Worker class HeifDataSource : public BnDataSource {
71*ec779b8eSAndroid Build Coastguard Worker public:
72*ec779b8eSAndroid Build Coastguard Worker /*
73*ec779b8eSAndroid Build Coastguard Worker * Constructs HeifDataSource; will take ownership of |stream|.
74*ec779b8eSAndroid Build Coastguard Worker */
HeifDataSource(HeifStream * stream)75*ec779b8eSAndroid Build Coastguard Worker HeifDataSource(HeifStream* stream)
76*ec779b8eSAndroid Build Coastguard Worker : mStream(stream), mEOS(false),
77*ec779b8eSAndroid Build Coastguard Worker mCachedOffset(0), mCachedSize(0), mCacheBufferSize(0) {}
78*ec779b8eSAndroid Build Coastguard Worker
~HeifDataSource()79*ec779b8eSAndroid Build Coastguard Worker ~HeifDataSource() override {}
80*ec779b8eSAndroid Build Coastguard Worker
81*ec779b8eSAndroid Build Coastguard Worker /*
82*ec779b8eSAndroid Build Coastguard Worker * Initializes internal resources.
83*ec779b8eSAndroid Build Coastguard Worker */
84*ec779b8eSAndroid Build Coastguard Worker bool init();
85*ec779b8eSAndroid Build Coastguard Worker
getIMemory()86*ec779b8eSAndroid Build Coastguard Worker sp<IMemory> getIMemory() override { return mMemory; }
87*ec779b8eSAndroid Build Coastguard Worker ssize_t readAt(off64_t offset, size_t size) override;
88*ec779b8eSAndroid Build Coastguard Worker status_t getSize(off64_t* size) override ;
close()89*ec779b8eSAndroid Build Coastguard Worker void close() {}
getFlags()90*ec779b8eSAndroid Build Coastguard Worker uint32_t getFlags() override { return 0; }
toString()91*ec779b8eSAndroid Build Coastguard Worker String8 toString() override { return String8("HeifDataSource"); }
92*ec779b8eSAndroid Build Coastguard Worker
93*ec779b8eSAndroid Build Coastguard Worker private:
94*ec779b8eSAndroid Build Coastguard Worker enum {
95*ec779b8eSAndroid Build Coastguard Worker /*
96*ec779b8eSAndroid Build Coastguard Worker * Buffer size for passing the read data to mediaserver. Set to 64K
97*ec779b8eSAndroid Build Coastguard Worker * (which is what MediaDataSource Java API's jni implementation uses).
98*ec779b8eSAndroid Build Coastguard Worker */
99*ec779b8eSAndroid Build Coastguard Worker kBufferSize = 64 * 1024,
100*ec779b8eSAndroid Build Coastguard Worker /*
101*ec779b8eSAndroid Build Coastguard Worker * Initial and max cache buffer size.
102*ec779b8eSAndroid Build Coastguard Worker */
103*ec779b8eSAndroid Build Coastguard Worker kInitialCacheBufferSize = 4 * 1024 * 1024,
104*ec779b8eSAndroid Build Coastguard Worker kMaxCacheBufferSize = 64 * 1024 * 1024,
105*ec779b8eSAndroid Build Coastguard Worker };
106*ec779b8eSAndroid Build Coastguard Worker sp<IMemory> mMemory;
107*ec779b8eSAndroid Build Coastguard Worker std::unique_ptr<HeifStream> mStream;
108*ec779b8eSAndroid Build Coastguard Worker bool mEOS;
109*ec779b8eSAndroid Build Coastguard Worker std::unique_ptr<uint8_t[]> mCache;
110*ec779b8eSAndroid Build Coastguard Worker off64_t mCachedOffset;
111*ec779b8eSAndroid Build Coastguard Worker size_t mCachedSize;
112*ec779b8eSAndroid Build Coastguard Worker size_t mCacheBufferSize;
113*ec779b8eSAndroid Build Coastguard Worker };
114*ec779b8eSAndroid Build Coastguard Worker
init()115*ec779b8eSAndroid Build Coastguard Worker bool HeifDataSource::init() {
116*ec779b8eSAndroid Build Coastguard Worker sp<MemoryDealer> memoryDealer =
117*ec779b8eSAndroid Build Coastguard Worker new MemoryDealer(kBufferSize, "HeifDataSource");
118*ec779b8eSAndroid Build Coastguard Worker mMemory = memoryDealer->allocate(kBufferSize);
119*ec779b8eSAndroid Build Coastguard Worker if (mMemory == nullptr) {
120*ec779b8eSAndroid Build Coastguard Worker ALOGE("Failed to allocate shared memory!");
121*ec779b8eSAndroid Build Coastguard Worker return false;
122*ec779b8eSAndroid Build Coastguard Worker }
123*ec779b8eSAndroid Build Coastguard Worker mCache.reset(new uint8_t[kInitialCacheBufferSize]);
124*ec779b8eSAndroid Build Coastguard Worker if (mCache.get() == nullptr) {
125*ec779b8eSAndroid Build Coastguard Worker ALOGE("mFailed to allocate cache!");
126*ec779b8eSAndroid Build Coastguard Worker return false;
127*ec779b8eSAndroid Build Coastguard Worker }
128*ec779b8eSAndroid Build Coastguard Worker mCacheBufferSize = kInitialCacheBufferSize;
129*ec779b8eSAndroid Build Coastguard Worker return true;
130*ec779b8eSAndroid Build Coastguard Worker }
131*ec779b8eSAndroid Build Coastguard Worker
readAt(off64_t offset,size_t size)132*ec779b8eSAndroid Build Coastguard Worker ssize_t HeifDataSource::readAt(off64_t offset, size_t size) {
133*ec779b8eSAndroid Build Coastguard Worker ALOGV("readAt: offset=%lld, size=%zu", (long long)offset, size);
134*ec779b8eSAndroid Build Coastguard Worker
135*ec779b8eSAndroid Build Coastguard Worker if (offset < mCachedOffset) {
136*ec779b8eSAndroid Build Coastguard Worker // try seek, then rewind/skip, fail if none worked
137*ec779b8eSAndroid Build Coastguard Worker if (mStream->seek(offset)) {
138*ec779b8eSAndroid Build Coastguard Worker ALOGV("readAt: seek to offset=%lld", (long long)offset);
139*ec779b8eSAndroid Build Coastguard Worker mCachedOffset = offset;
140*ec779b8eSAndroid Build Coastguard Worker mCachedSize = 0;
141*ec779b8eSAndroid Build Coastguard Worker mEOS = false;
142*ec779b8eSAndroid Build Coastguard Worker } else if (mStream->rewind()) {
143*ec779b8eSAndroid Build Coastguard Worker ALOGV("readAt: rewind to offset=0");
144*ec779b8eSAndroid Build Coastguard Worker mCachedOffset = 0;
145*ec779b8eSAndroid Build Coastguard Worker mCachedSize = 0;
146*ec779b8eSAndroid Build Coastguard Worker mEOS = false;
147*ec779b8eSAndroid Build Coastguard Worker } else {
148*ec779b8eSAndroid Build Coastguard Worker ALOGE("readAt: couldn't seek or rewind!");
149*ec779b8eSAndroid Build Coastguard Worker mEOS = true;
150*ec779b8eSAndroid Build Coastguard Worker }
151*ec779b8eSAndroid Build Coastguard Worker }
152*ec779b8eSAndroid Build Coastguard Worker
153*ec779b8eSAndroid Build Coastguard Worker if (mEOS && (offset < mCachedOffset ||
154*ec779b8eSAndroid Build Coastguard Worker offset >= (off64_t)(mCachedOffset + mCachedSize))) {
155*ec779b8eSAndroid Build Coastguard Worker ALOGV("readAt: EOS");
156*ec779b8eSAndroid Build Coastguard Worker return ERROR_END_OF_STREAM;
157*ec779b8eSAndroid Build Coastguard Worker }
158*ec779b8eSAndroid Build Coastguard Worker
159*ec779b8eSAndroid Build Coastguard Worker // at this point, offset must be >= mCachedOffset, other cases should
160*ec779b8eSAndroid Build Coastguard Worker // have been caught above.
161*ec779b8eSAndroid Build Coastguard Worker CHECK(offset >= mCachedOffset);
162*ec779b8eSAndroid Build Coastguard Worker
163*ec779b8eSAndroid Build Coastguard Worker off64_t resultOffset;
164*ec779b8eSAndroid Build Coastguard Worker if (__builtin_add_overflow(offset, size, &resultOffset)) {
165*ec779b8eSAndroid Build Coastguard Worker return ERROR_IO;
166*ec779b8eSAndroid Build Coastguard Worker }
167*ec779b8eSAndroid Build Coastguard Worker
168*ec779b8eSAndroid Build Coastguard Worker if (size == 0) {
169*ec779b8eSAndroid Build Coastguard Worker return 0;
170*ec779b8eSAndroid Build Coastguard Worker }
171*ec779b8eSAndroid Build Coastguard Worker
172*ec779b8eSAndroid Build Coastguard Worker // Can only read max of kBufferSize
173*ec779b8eSAndroid Build Coastguard Worker if (size > kBufferSize) {
174*ec779b8eSAndroid Build Coastguard Worker size = kBufferSize;
175*ec779b8eSAndroid Build Coastguard Worker }
176*ec779b8eSAndroid Build Coastguard Worker
177*ec779b8eSAndroid Build Coastguard Worker // copy from cache if the request falls entirely in cache
178*ec779b8eSAndroid Build Coastguard Worker if (offset + size <= mCachedOffset + mCachedSize) {
179*ec779b8eSAndroid Build Coastguard Worker memcpy(mMemory->unsecurePointer(), mCache.get() + offset - mCachedOffset, size);
180*ec779b8eSAndroid Build Coastguard Worker return size;
181*ec779b8eSAndroid Build Coastguard Worker }
182*ec779b8eSAndroid Build Coastguard Worker
183*ec779b8eSAndroid Build Coastguard Worker // need to fetch more, check if we need to expand the cache buffer.
184*ec779b8eSAndroid Build Coastguard Worker if ((off64_t)(offset + size) > mCachedOffset + kMaxCacheBufferSize) {
185*ec779b8eSAndroid Build Coastguard Worker // it's reaching max cache buffer size, need to roll window, and possibly
186*ec779b8eSAndroid Build Coastguard Worker // expand the cache buffer.
187*ec779b8eSAndroid Build Coastguard Worker size_t newCacheBufferSize = mCacheBufferSize;
188*ec779b8eSAndroid Build Coastguard Worker std::unique_ptr<uint8_t[]> newCache;
189*ec779b8eSAndroid Build Coastguard Worker uint8_t* dst = mCache.get();
190*ec779b8eSAndroid Build Coastguard Worker if (newCacheBufferSize < kMaxCacheBufferSize) {
191*ec779b8eSAndroid Build Coastguard Worker newCacheBufferSize = kMaxCacheBufferSize;
192*ec779b8eSAndroid Build Coastguard Worker newCache.reset(new uint8_t[newCacheBufferSize]);
193*ec779b8eSAndroid Build Coastguard Worker dst = newCache.get();
194*ec779b8eSAndroid Build Coastguard Worker }
195*ec779b8eSAndroid Build Coastguard Worker
196*ec779b8eSAndroid Build Coastguard Worker // when rolling the cache window, try to keep about half the old bytes
197*ec779b8eSAndroid Build Coastguard Worker // in case that the client goes back.
198*ec779b8eSAndroid Build Coastguard Worker off64_t newCachedOffset = offset - (off64_t)(newCacheBufferSize / 2);
199*ec779b8eSAndroid Build Coastguard Worker if (newCachedOffset < mCachedOffset) {
200*ec779b8eSAndroid Build Coastguard Worker newCachedOffset = mCachedOffset;
201*ec779b8eSAndroid Build Coastguard Worker }
202*ec779b8eSAndroid Build Coastguard Worker
203*ec779b8eSAndroid Build Coastguard Worker int64_t newCachedSize = (int64_t)(mCachedOffset + mCachedSize) - newCachedOffset;
204*ec779b8eSAndroid Build Coastguard Worker if (newCachedSize > 0) {
205*ec779b8eSAndroid Build Coastguard Worker // in this case, the new cache region partially overlop the old cache,
206*ec779b8eSAndroid Build Coastguard Worker // move the portion of the cache we want to save to the beginning of
207*ec779b8eSAndroid Build Coastguard Worker // the cache buffer.
208*ec779b8eSAndroid Build Coastguard Worker memcpy(dst, mCache.get() + newCachedOffset - mCachedOffset, newCachedSize);
209*ec779b8eSAndroid Build Coastguard Worker } else if (newCachedSize < 0){
210*ec779b8eSAndroid Build Coastguard Worker // in this case, the new cache region is entirely out of the old cache,
211*ec779b8eSAndroid Build Coastguard Worker // in order to guarantee sequential read, we need to skip a number of
212*ec779b8eSAndroid Build Coastguard Worker // bytes before reading.
213*ec779b8eSAndroid Build Coastguard Worker size_t bytesToSkip = -newCachedSize;
214*ec779b8eSAndroid Build Coastguard Worker size_t bytesSkipped = mStream->read(nullptr, bytesToSkip);
215*ec779b8eSAndroid Build Coastguard Worker if (bytesSkipped != bytesToSkip) {
216*ec779b8eSAndroid Build Coastguard Worker // bytesSkipped is invalid, there is not enough bytes to reach
217*ec779b8eSAndroid Build Coastguard Worker // the requested offset.
218*ec779b8eSAndroid Build Coastguard Worker ALOGE("readAt: skip failed, EOS");
219*ec779b8eSAndroid Build Coastguard Worker
220*ec779b8eSAndroid Build Coastguard Worker mEOS = true;
221*ec779b8eSAndroid Build Coastguard Worker mCachedOffset = newCachedOffset;
222*ec779b8eSAndroid Build Coastguard Worker mCachedSize = 0;
223*ec779b8eSAndroid Build Coastguard Worker return ERROR_END_OF_STREAM;
224*ec779b8eSAndroid Build Coastguard Worker }
225*ec779b8eSAndroid Build Coastguard Worker // set cache size to 0, since we're not keeping any old cache
226*ec779b8eSAndroid Build Coastguard Worker newCachedSize = 0;
227*ec779b8eSAndroid Build Coastguard Worker }
228*ec779b8eSAndroid Build Coastguard Worker
229*ec779b8eSAndroid Build Coastguard Worker if (newCache.get() != nullptr) {
230*ec779b8eSAndroid Build Coastguard Worker mCache.reset(newCache.release());
231*ec779b8eSAndroid Build Coastguard Worker mCacheBufferSize = newCacheBufferSize;
232*ec779b8eSAndroid Build Coastguard Worker }
233*ec779b8eSAndroid Build Coastguard Worker mCachedOffset = newCachedOffset;
234*ec779b8eSAndroid Build Coastguard Worker mCachedSize = newCachedSize;
235*ec779b8eSAndroid Build Coastguard Worker
236*ec779b8eSAndroid Build Coastguard Worker ALOGV("readAt: rolling cache window to (%lld, %zu), cache buffer size %zu",
237*ec779b8eSAndroid Build Coastguard Worker (long long)mCachedOffset, mCachedSize, mCacheBufferSize);
238*ec779b8eSAndroid Build Coastguard Worker } else {
239*ec779b8eSAndroid Build Coastguard Worker // expand cache buffer, but no need to roll the window
240*ec779b8eSAndroid Build Coastguard Worker size_t newCacheBufferSize = mCacheBufferSize;
241*ec779b8eSAndroid Build Coastguard Worker while (offset + size > mCachedOffset + newCacheBufferSize) {
242*ec779b8eSAndroid Build Coastguard Worker newCacheBufferSize *= 2;
243*ec779b8eSAndroid Build Coastguard Worker }
244*ec779b8eSAndroid Build Coastguard Worker CHECK(newCacheBufferSize <= kMaxCacheBufferSize);
245*ec779b8eSAndroid Build Coastguard Worker if (mCacheBufferSize < newCacheBufferSize) {
246*ec779b8eSAndroid Build Coastguard Worker uint8_t* newCache = new uint8_t[newCacheBufferSize];
247*ec779b8eSAndroid Build Coastguard Worker memcpy(newCache, mCache.get(), mCachedSize);
248*ec779b8eSAndroid Build Coastguard Worker mCache.reset(newCache);
249*ec779b8eSAndroid Build Coastguard Worker mCacheBufferSize = newCacheBufferSize;
250*ec779b8eSAndroid Build Coastguard Worker
251*ec779b8eSAndroid Build Coastguard Worker ALOGV("readAt: current cache window (%lld, %zu), new cache buffer size %zu",
252*ec779b8eSAndroid Build Coastguard Worker (long long) mCachedOffset, mCachedSize, mCacheBufferSize);
253*ec779b8eSAndroid Build Coastguard Worker }
254*ec779b8eSAndroid Build Coastguard Worker }
255*ec779b8eSAndroid Build Coastguard Worker size_t bytesToRead = offset + size - mCachedOffset - mCachedSize;
256*ec779b8eSAndroid Build Coastguard Worker size_t bytesRead = mStream->read(mCache.get() + mCachedSize, bytesToRead);
257*ec779b8eSAndroid Build Coastguard Worker if (bytesRead > bytesToRead || bytesRead == 0) {
258*ec779b8eSAndroid Build Coastguard Worker // bytesRead is invalid
259*ec779b8eSAndroid Build Coastguard Worker mEOS = true;
260*ec779b8eSAndroid Build Coastguard Worker bytesRead = 0;
261*ec779b8eSAndroid Build Coastguard Worker } else if (bytesRead < bytesToRead) {
262*ec779b8eSAndroid Build Coastguard Worker // read some bytes but not all, set EOS
263*ec779b8eSAndroid Build Coastguard Worker mEOS = true;
264*ec779b8eSAndroid Build Coastguard Worker }
265*ec779b8eSAndroid Build Coastguard Worker mCachedSize += bytesRead;
266*ec779b8eSAndroid Build Coastguard Worker ALOGV("readAt: current cache window (%lld, %zu)",
267*ec779b8eSAndroid Build Coastguard Worker (long long) mCachedOffset, mCachedSize);
268*ec779b8eSAndroid Build Coastguard Worker
269*ec779b8eSAndroid Build Coastguard Worker // here bytesAvailable could be negative if offset jumped past EOS.
270*ec779b8eSAndroid Build Coastguard Worker int64_t bytesAvailable = mCachedOffset + mCachedSize - offset;
271*ec779b8eSAndroid Build Coastguard Worker if (bytesAvailable <= 0) {
272*ec779b8eSAndroid Build Coastguard Worker return ERROR_END_OF_STREAM;
273*ec779b8eSAndroid Build Coastguard Worker }
274*ec779b8eSAndroid Build Coastguard Worker if (bytesAvailable < (int64_t)size) {
275*ec779b8eSAndroid Build Coastguard Worker size = bytesAvailable;
276*ec779b8eSAndroid Build Coastguard Worker }
277*ec779b8eSAndroid Build Coastguard Worker memcpy(mMemory->unsecurePointer(), mCache.get() + offset - mCachedOffset, size);
278*ec779b8eSAndroid Build Coastguard Worker return size;
279*ec779b8eSAndroid Build Coastguard Worker }
280*ec779b8eSAndroid Build Coastguard Worker
getSize(off64_t * size)281*ec779b8eSAndroid Build Coastguard Worker status_t HeifDataSource::getSize(off64_t* size) {
282*ec779b8eSAndroid Build Coastguard Worker if (!mStream->hasLength()) {
283*ec779b8eSAndroid Build Coastguard Worker *size = -1;
284*ec779b8eSAndroid Build Coastguard Worker ALOGE("getSize: not supported!");
285*ec779b8eSAndroid Build Coastguard Worker return ERROR_UNSUPPORTED;
286*ec779b8eSAndroid Build Coastguard Worker }
287*ec779b8eSAndroid Build Coastguard Worker *size = mStream->getLength();
288*ec779b8eSAndroid Build Coastguard Worker ALOGV("getSize: size=%lld", (long long)*size);
289*ec779b8eSAndroid Build Coastguard Worker return OK;
290*ec779b8eSAndroid Build Coastguard Worker }
291*ec779b8eSAndroid Build Coastguard Worker
292*ec779b8eSAndroid Build Coastguard Worker /////////////////////////////////////////////////////////////////////////
293*ec779b8eSAndroid Build Coastguard Worker
294*ec779b8eSAndroid Build Coastguard Worker struct HeifDecoderImpl::DecodeThread : public Thread {
DecodeThreadandroid::HeifDecoderImpl::DecodeThread295*ec779b8eSAndroid Build Coastguard Worker explicit DecodeThread(HeifDecoderImpl *decoder) : mDecoder(decoder) {}
296*ec779b8eSAndroid Build Coastguard Worker
297*ec779b8eSAndroid Build Coastguard Worker private:
298*ec779b8eSAndroid Build Coastguard Worker HeifDecoderImpl* mDecoder;
299*ec779b8eSAndroid Build Coastguard Worker
300*ec779b8eSAndroid Build Coastguard Worker bool threadLoop();
301*ec779b8eSAndroid Build Coastguard Worker
302*ec779b8eSAndroid Build Coastguard Worker DISALLOW_EVIL_CONSTRUCTORS(DecodeThread);
303*ec779b8eSAndroid Build Coastguard Worker };
304*ec779b8eSAndroid Build Coastguard Worker
threadLoop()305*ec779b8eSAndroid Build Coastguard Worker bool HeifDecoderImpl::DecodeThread::threadLoop() {
306*ec779b8eSAndroid Build Coastguard Worker return mDecoder->decodeAsync();
307*ec779b8eSAndroid Build Coastguard Worker }
308*ec779b8eSAndroid Build Coastguard Worker
309*ec779b8eSAndroid Build Coastguard Worker /////////////////////////////////////////////////////////////////////////
310*ec779b8eSAndroid Build Coastguard Worker
HeifDecoderImpl()311*ec779b8eSAndroid Build Coastguard Worker HeifDecoderImpl::HeifDecoderImpl() :
312*ec779b8eSAndroid Build Coastguard Worker // output color format should always be set via setOutputColor(), in case
313*ec779b8eSAndroid Build Coastguard Worker // it's not, default to HAL_PIXEL_FORMAT_RGB_565.
314*ec779b8eSAndroid Build Coastguard Worker mOutputColor(HAL_PIXEL_FORMAT_RGB_565),
315*ec779b8eSAndroid Build Coastguard Worker mCurScanline(0),
316*ec779b8eSAndroid Build Coastguard Worker mTotalScanline(0),
317*ec779b8eSAndroid Build Coastguard Worker mFrameDecoded(false),
318*ec779b8eSAndroid Build Coastguard Worker mHasImage(false),
319*ec779b8eSAndroid Build Coastguard Worker mHasVideo(false),
320*ec779b8eSAndroid Build Coastguard Worker mSequenceLength(0),
321*ec779b8eSAndroid Build Coastguard Worker mAvailableLines(0),
322*ec779b8eSAndroid Build Coastguard Worker mNumSlices(1),
323*ec779b8eSAndroid Build Coastguard Worker mSliceHeight(0),
324*ec779b8eSAndroid Build Coastguard Worker mAsyncDecodeDone(false) {
325*ec779b8eSAndroid Build Coastguard Worker }
326*ec779b8eSAndroid Build Coastguard Worker
~HeifDecoderImpl()327*ec779b8eSAndroid Build Coastguard Worker HeifDecoderImpl::~HeifDecoderImpl() {
328*ec779b8eSAndroid Build Coastguard Worker if (mThread != nullptr) {
329*ec779b8eSAndroid Build Coastguard Worker mThread->join();
330*ec779b8eSAndroid Build Coastguard Worker }
331*ec779b8eSAndroid Build Coastguard Worker }
332*ec779b8eSAndroid Build Coastguard Worker
init(HeifStream * stream,HeifFrameInfo * frameInfo)333*ec779b8eSAndroid Build Coastguard Worker bool HeifDecoderImpl::init(HeifStream* stream, HeifFrameInfo* frameInfo) {
334*ec779b8eSAndroid Build Coastguard Worker mFrameDecoded = false;
335*ec779b8eSAndroid Build Coastguard Worker mFrameMemory.clear();
336*ec779b8eSAndroid Build Coastguard Worker
337*ec779b8eSAndroid Build Coastguard Worker sp<HeifDataSource> dataSource = new HeifDataSource(stream);
338*ec779b8eSAndroid Build Coastguard Worker if (!dataSource->init()) {
339*ec779b8eSAndroid Build Coastguard Worker return false;
340*ec779b8eSAndroid Build Coastguard Worker }
341*ec779b8eSAndroid Build Coastguard Worker mDataSource = dataSource;
342*ec779b8eSAndroid Build Coastguard Worker
343*ec779b8eSAndroid Build Coastguard Worker return reinit(frameInfo);
344*ec779b8eSAndroid Build Coastguard Worker }
345*ec779b8eSAndroid Build Coastguard Worker
reinit(HeifFrameInfo * frameInfo)346*ec779b8eSAndroid Build Coastguard Worker bool HeifDecoderImpl::reinit(HeifFrameInfo* frameInfo) {
347*ec779b8eSAndroid Build Coastguard Worker mFrameDecoded = false;
348*ec779b8eSAndroid Build Coastguard Worker mFrameMemory.clear();
349*ec779b8eSAndroid Build Coastguard Worker
350*ec779b8eSAndroid Build Coastguard Worker sp<MediaMetadataRetriever> retriever = new MediaMetadataRetriever();
351*ec779b8eSAndroid Build Coastguard Worker status_t err = retriever->setDataSource(mDataSource, "image/heif");
352*ec779b8eSAndroid Build Coastguard Worker if (err != OK) {
353*ec779b8eSAndroid Build Coastguard Worker ALOGE("failed to set data source!");
354*ec779b8eSAndroid Build Coastguard Worker mRetriever.clear();
355*ec779b8eSAndroid Build Coastguard Worker mDataSource.clear();
356*ec779b8eSAndroid Build Coastguard Worker return false;
357*ec779b8eSAndroid Build Coastguard Worker }
358*ec779b8eSAndroid Build Coastguard Worker {
359*ec779b8eSAndroid Build Coastguard Worker Mutex::Autolock _l(mRetrieverLock);
360*ec779b8eSAndroid Build Coastguard Worker mRetriever = retriever;
361*ec779b8eSAndroid Build Coastguard Worker }
362*ec779b8eSAndroid Build Coastguard Worker ALOGV("successfully set data source.");
363*ec779b8eSAndroid Build Coastguard Worker
364*ec779b8eSAndroid Build Coastguard Worker const char* hasImage = retriever->extractMetadata(METADATA_KEY_HAS_IMAGE);
365*ec779b8eSAndroid Build Coastguard Worker const char* hasVideo = retriever->extractMetadata(METADATA_KEY_HAS_VIDEO);
366*ec779b8eSAndroid Build Coastguard Worker
367*ec779b8eSAndroid Build Coastguard Worker mHasImage = hasImage && !strcasecmp(hasImage, "yes");
368*ec779b8eSAndroid Build Coastguard Worker mHasVideo = hasVideo && !strcasecmp(hasVideo, "yes");
369*ec779b8eSAndroid Build Coastguard Worker
370*ec779b8eSAndroid Build Coastguard Worker HeifFrameInfo* defaultInfo = nullptr;
371*ec779b8eSAndroid Build Coastguard Worker if (mHasImage) {
372*ec779b8eSAndroid Build Coastguard Worker // image index < 0 to retrieve primary image
373*ec779b8eSAndroid Build Coastguard Worker sp<IMemory> sharedMem = retriever->getImageAtIndex(
374*ec779b8eSAndroid Build Coastguard Worker -1, mOutputColor, true /*metaOnly*/);
375*ec779b8eSAndroid Build Coastguard Worker
376*ec779b8eSAndroid Build Coastguard Worker if (sharedMem == nullptr || sharedMem->unsecurePointer() == nullptr) {
377*ec779b8eSAndroid Build Coastguard Worker ALOGE("init: videoFrame is a nullptr");
378*ec779b8eSAndroid Build Coastguard Worker return false;
379*ec779b8eSAndroid Build Coastguard Worker }
380*ec779b8eSAndroid Build Coastguard Worker
381*ec779b8eSAndroid Build Coastguard Worker // TODO: Using unsecurePointer() has some associated security pitfalls
382*ec779b8eSAndroid Build Coastguard Worker // (see declaration for details).
383*ec779b8eSAndroid Build Coastguard Worker // Either document why it is safe in this case or address the
384*ec779b8eSAndroid Build Coastguard Worker // issue (e.g. by copying).
385*ec779b8eSAndroid Build Coastguard Worker VideoFrame* videoFrame = static_cast<VideoFrame*>(sharedMem->unsecurePointer());
386*ec779b8eSAndroid Build Coastguard Worker
387*ec779b8eSAndroid Build Coastguard Worker ALOGV("Image dimension %dx%d, display %dx%d, angle %d, iccSize %d, bitDepth %d",
388*ec779b8eSAndroid Build Coastguard Worker videoFrame->mWidth,
389*ec779b8eSAndroid Build Coastguard Worker videoFrame->mHeight,
390*ec779b8eSAndroid Build Coastguard Worker videoFrame->mDisplayWidth,
391*ec779b8eSAndroid Build Coastguard Worker videoFrame->mDisplayHeight,
392*ec779b8eSAndroid Build Coastguard Worker videoFrame->mRotationAngle,
393*ec779b8eSAndroid Build Coastguard Worker videoFrame->mIccSize,
394*ec779b8eSAndroid Build Coastguard Worker videoFrame->mBitDepth);
395*ec779b8eSAndroid Build Coastguard Worker
396*ec779b8eSAndroid Build Coastguard Worker initFrameInfo(&mImageInfo, videoFrame);
397*ec779b8eSAndroid Build Coastguard Worker
398*ec779b8eSAndroid Build Coastguard Worker if (videoFrame->mTileHeight >= 512) {
399*ec779b8eSAndroid Build Coastguard Worker // Try decoding in slices only if the image has tiles and is big enough.
400*ec779b8eSAndroid Build Coastguard Worker mSliceHeight = videoFrame->mTileHeight;
401*ec779b8eSAndroid Build Coastguard Worker ALOGV("mSliceHeight %u", mSliceHeight);
402*ec779b8eSAndroid Build Coastguard Worker }
403*ec779b8eSAndroid Build Coastguard Worker
404*ec779b8eSAndroid Build Coastguard Worker defaultInfo = &mImageInfo;
405*ec779b8eSAndroid Build Coastguard Worker }
406*ec779b8eSAndroid Build Coastguard Worker
407*ec779b8eSAndroid Build Coastguard Worker if (mHasVideo) {
408*ec779b8eSAndroid Build Coastguard Worker sp<IMemory> sharedMem = retriever->getFrameAtTime(0,
409*ec779b8eSAndroid Build Coastguard Worker MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
410*ec779b8eSAndroid Build Coastguard Worker mOutputColor, true /*metaOnly*/);
411*ec779b8eSAndroid Build Coastguard Worker
412*ec779b8eSAndroid Build Coastguard Worker if (sharedMem == nullptr || sharedMem->unsecurePointer() == nullptr) {
413*ec779b8eSAndroid Build Coastguard Worker ALOGE("init: videoFrame is a nullptr");
414*ec779b8eSAndroid Build Coastguard Worker return false;
415*ec779b8eSAndroid Build Coastguard Worker }
416*ec779b8eSAndroid Build Coastguard Worker
417*ec779b8eSAndroid Build Coastguard Worker // TODO: Using unsecurePointer() has some associated security pitfalls
418*ec779b8eSAndroid Build Coastguard Worker // (see declaration for details).
419*ec779b8eSAndroid Build Coastguard Worker // Either document why it is safe in this case or address the
420*ec779b8eSAndroid Build Coastguard Worker // issue (e.g. by copying).
421*ec779b8eSAndroid Build Coastguard Worker VideoFrame* videoFrame = static_cast<VideoFrame*>(
422*ec779b8eSAndroid Build Coastguard Worker sharedMem->unsecurePointer());
423*ec779b8eSAndroid Build Coastguard Worker
424*ec779b8eSAndroid Build Coastguard Worker ALOGV("Sequence dimension %dx%d, display %dx%d, angle %d, iccSize %d",
425*ec779b8eSAndroid Build Coastguard Worker videoFrame->mWidth,
426*ec779b8eSAndroid Build Coastguard Worker videoFrame->mHeight,
427*ec779b8eSAndroid Build Coastguard Worker videoFrame->mDisplayWidth,
428*ec779b8eSAndroid Build Coastguard Worker videoFrame->mDisplayHeight,
429*ec779b8eSAndroid Build Coastguard Worker videoFrame->mRotationAngle,
430*ec779b8eSAndroid Build Coastguard Worker videoFrame->mIccSize);
431*ec779b8eSAndroid Build Coastguard Worker
432*ec779b8eSAndroid Build Coastguard Worker initFrameInfo(&mSequenceInfo, videoFrame);
433*ec779b8eSAndroid Build Coastguard Worker
434*ec779b8eSAndroid Build Coastguard Worker const char* frameCount = retriever->extractMetadata(METADATA_KEY_VIDEO_FRAME_COUNT);
435*ec779b8eSAndroid Build Coastguard Worker if (frameCount == nullptr) {
436*ec779b8eSAndroid Build Coastguard Worker android_errorWriteWithInfoLog(0x534e4554, "215002587", -1, NULL, 0);
437*ec779b8eSAndroid Build Coastguard Worker ALOGD("No valid sequence information in metadata");
438*ec779b8eSAndroid Build Coastguard Worker return false;
439*ec779b8eSAndroid Build Coastguard Worker }
440*ec779b8eSAndroid Build Coastguard Worker mSequenceLength = atoi(frameCount);
441*ec779b8eSAndroid Build Coastguard Worker
442*ec779b8eSAndroid Build Coastguard Worker if (defaultInfo == nullptr) {
443*ec779b8eSAndroid Build Coastguard Worker defaultInfo = &mSequenceInfo;
444*ec779b8eSAndroid Build Coastguard Worker }
445*ec779b8eSAndroid Build Coastguard Worker }
446*ec779b8eSAndroid Build Coastguard Worker
447*ec779b8eSAndroid Build Coastguard Worker if (defaultInfo == nullptr) {
448*ec779b8eSAndroid Build Coastguard Worker ALOGD("No valid image or sequence available");
449*ec779b8eSAndroid Build Coastguard Worker return false;
450*ec779b8eSAndroid Build Coastguard Worker }
451*ec779b8eSAndroid Build Coastguard Worker
452*ec779b8eSAndroid Build Coastguard Worker if (frameInfo != nullptr) {
453*ec779b8eSAndroid Build Coastguard Worker *frameInfo = *defaultInfo;
454*ec779b8eSAndroid Build Coastguard Worker }
455*ec779b8eSAndroid Build Coastguard Worker
456*ec779b8eSAndroid Build Coastguard Worker // default total scanline, this might change if decodeSequence() is used
457*ec779b8eSAndroid Build Coastguard Worker mTotalScanline = defaultInfo->mHeight;
458*ec779b8eSAndroid Build Coastguard Worker
459*ec779b8eSAndroid Build Coastguard Worker return true;
460*ec779b8eSAndroid Build Coastguard Worker }
461*ec779b8eSAndroid Build Coastguard Worker
getSequenceInfo(HeifFrameInfo * frameInfo,size_t * frameCount)462*ec779b8eSAndroid Build Coastguard Worker bool HeifDecoderImpl::getSequenceInfo(
463*ec779b8eSAndroid Build Coastguard Worker HeifFrameInfo* frameInfo, size_t *frameCount) {
464*ec779b8eSAndroid Build Coastguard Worker ALOGV("%s", __FUNCTION__);
465*ec779b8eSAndroid Build Coastguard Worker if (!mHasVideo) {
466*ec779b8eSAndroid Build Coastguard Worker return false;
467*ec779b8eSAndroid Build Coastguard Worker }
468*ec779b8eSAndroid Build Coastguard Worker if (frameInfo != nullptr) {
469*ec779b8eSAndroid Build Coastguard Worker *frameInfo = mSequenceInfo;
470*ec779b8eSAndroid Build Coastguard Worker }
471*ec779b8eSAndroid Build Coastguard Worker if (frameCount != nullptr) {
472*ec779b8eSAndroid Build Coastguard Worker *frameCount = mSequenceLength;
473*ec779b8eSAndroid Build Coastguard Worker }
474*ec779b8eSAndroid Build Coastguard Worker return true;
475*ec779b8eSAndroid Build Coastguard Worker }
476*ec779b8eSAndroid Build Coastguard Worker
getEncodedColor(HeifEncodedColor *) const477*ec779b8eSAndroid Build Coastguard Worker bool HeifDecoderImpl::getEncodedColor(HeifEncodedColor* /*outColor*/) const {
478*ec779b8eSAndroid Build Coastguard Worker ALOGW("getEncodedColor: not implemented!");
479*ec779b8eSAndroid Build Coastguard Worker return false;
480*ec779b8eSAndroid Build Coastguard Worker }
481*ec779b8eSAndroid Build Coastguard Worker
setOutputColor(HeifColorFormat heifColor)482*ec779b8eSAndroid Build Coastguard Worker bool HeifDecoderImpl::setOutputColor(HeifColorFormat heifColor) {
483*ec779b8eSAndroid Build Coastguard Worker android_pixel_format_t outputColor;
484*ec779b8eSAndroid Build Coastguard Worker switch(heifColor) {
485*ec779b8eSAndroid Build Coastguard Worker case kHeifColorFormat_RGB565:
486*ec779b8eSAndroid Build Coastguard Worker {
487*ec779b8eSAndroid Build Coastguard Worker outputColor = HAL_PIXEL_FORMAT_RGB_565;
488*ec779b8eSAndroid Build Coastguard Worker break;
489*ec779b8eSAndroid Build Coastguard Worker }
490*ec779b8eSAndroid Build Coastguard Worker case kHeifColorFormat_RGBA_8888:
491*ec779b8eSAndroid Build Coastguard Worker {
492*ec779b8eSAndroid Build Coastguard Worker outputColor = HAL_PIXEL_FORMAT_RGBA_8888;
493*ec779b8eSAndroid Build Coastguard Worker break;
494*ec779b8eSAndroid Build Coastguard Worker }
495*ec779b8eSAndroid Build Coastguard Worker case kHeifColorFormat_BGRA_8888:
496*ec779b8eSAndroid Build Coastguard Worker {
497*ec779b8eSAndroid Build Coastguard Worker outputColor = HAL_PIXEL_FORMAT_BGRA_8888;
498*ec779b8eSAndroid Build Coastguard Worker break;
499*ec779b8eSAndroid Build Coastguard Worker }
500*ec779b8eSAndroid Build Coastguard Worker case kHeifColorFormat_RGBA_1010102:
501*ec779b8eSAndroid Build Coastguard Worker {
502*ec779b8eSAndroid Build Coastguard Worker outputColor = HAL_PIXEL_FORMAT_RGBA_1010102;
503*ec779b8eSAndroid Build Coastguard Worker break;
504*ec779b8eSAndroid Build Coastguard Worker }
505*ec779b8eSAndroid Build Coastguard Worker default:
506*ec779b8eSAndroid Build Coastguard Worker ALOGE("Unsupported output color format %d", heifColor);
507*ec779b8eSAndroid Build Coastguard Worker return false;
508*ec779b8eSAndroid Build Coastguard Worker }
509*ec779b8eSAndroid Build Coastguard Worker if (outputColor == mOutputColor) {
510*ec779b8eSAndroid Build Coastguard Worker return true;
511*ec779b8eSAndroid Build Coastguard Worker }
512*ec779b8eSAndroid Build Coastguard Worker
513*ec779b8eSAndroid Build Coastguard Worker mOutputColor = outputColor;
514*ec779b8eSAndroid Build Coastguard Worker
515*ec779b8eSAndroid Build Coastguard Worker if (mFrameDecoded) {
516*ec779b8eSAndroid Build Coastguard Worker return reinit(nullptr);
517*ec779b8eSAndroid Build Coastguard Worker }
518*ec779b8eSAndroid Build Coastguard Worker return true;
519*ec779b8eSAndroid Build Coastguard Worker }
520*ec779b8eSAndroid Build Coastguard Worker
decodeAsync()521*ec779b8eSAndroid Build Coastguard Worker bool HeifDecoderImpl::decodeAsync() {
522*ec779b8eSAndroid Build Coastguard Worker wp<MediaMetadataRetriever> weakRetriever;
523*ec779b8eSAndroid Build Coastguard Worker {
524*ec779b8eSAndroid Build Coastguard Worker Mutex::Autolock _l(mRetrieverLock);
525*ec779b8eSAndroid Build Coastguard Worker weakRetriever = mRetriever;
526*ec779b8eSAndroid Build Coastguard Worker }
527*ec779b8eSAndroid Build Coastguard Worker
528*ec779b8eSAndroid Build Coastguard Worker for (size_t i = 1; i < mNumSlices; i++) {
529*ec779b8eSAndroid Build Coastguard Worker sp<MediaMetadataRetriever> retriever = weakRetriever.promote();
530*ec779b8eSAndroid Build Coastguard Worker if (retriever == nullptr) {
531*ec779b8eSAndroid Build Coastguard Worker return false;
532*ec779b8eSAndroid Build Coastguard Worker }
533*ec779b8eSAndroid Build Coastguard Worker
534*ec779b8eSAndroid Build Coastguard Worker ALOGV("decodeAsync(): decoding slice %zu", i);
535*ec779b8eSAndroid Build Coastguard Worker size_t top = i * mSliceHeight;
536*ec779b8eSAndroid Build Coastguard Worker size_t bottom = (i + 1) * mSliceHeight;
537*ec779b8eSAndroid Build Coastguard Worker if (bottom > mImageInfo.mHeight) {
538*ec779b8eSAndroid Build Coastguard Worker bottom = mImageInfo.mHeight;
539*ec779b8eSAndroid Build Coastguard Worker }
540*ec779b8eSAndroid Build Coastguard Worker
541*ec779b8eSAndroid Build Coastguard Worker sp<IMemory> frameMemory = retriever->getImageRectAtIndex(
542*ec779b8eSAndroid Build Coastguard Worker -1, mOutputColor, 0, top, mImageInfo.mWidth, bottom);
543*ec779b8eSAndroid Build Coastguard Worker {
544*ec779b8eSAndroid Build Coastguard Worker Mutex::Autolock autolock(mLock);
545*ec779b8eSAndroid Build Coastguard Worker
546*ec779b8eSAndroid Build Coastguard Worker if (frameMemory == nullptr || frameMemory->unsecurePointer() == nullptr) {
547*ec779b8eSAndroid Build Coastguard Worker mAsyncDecodeDone = true;
548*ec779b8eSAndroid Build Coastguard Worker mScanlineReady.signal();
549*ec779b8eSAndroid Build Coastguard Worker break;
550*ec779b8eSAndroid Build Coastguard Worker }
551*ec779b8eSAndroid Build Coastguard Worker mFrameMemory = frameMemory;
552*ec779b8eSAndroid Build Coastguard Worker mAvailableLines = bottom;
553*ec779b8eSAndroid Build Coastguard Worker ALOGV("decodeAsync(): available lines %zu", mAvailableLines);
554*ec779b8eSAndroid Build Coastguard Worker mScanlineReady.signal();
555*ec779b8eSAndroid Build Coastguard Worker }
556*ec779b8eSAndroid Build Coastguard Worker }
557*ec779b8eSAndroid Build Coastguard Worker // Hold on to mDataSource in case the client wants to redecode.
558*ec779b8eSAndroid Build Coastguard Worker
559*ec779b8eSAndroid Build Coastguard Worker {
560*ec779b8eSAndroid Build Coastguard Worker Mutex::Autolock _l(mRetrieverLock);
561*ec779b8eSAndroid Build Coastguard Worker mRetriever.clear();
562*ec779b8eSAndroid Build Coastguard Worker }
563*ec779b8eSAndroid Build Coastguard Worker
564*ec779b8eSAndroid Build Coastguard Worker return false;
565*ec779b8eSAndroid Build Coastguard Worker }
566*ec779b8eSAndroid Build Coastguard Worker
decode(HeifFrameInfo * frameInfo)567*ec779b8eSAndroid Build Coastguard Worker bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) {
568*ec779b8eSAndroid Build Coastguard Worker // reset scanline pointer
569*ec779b8eSAndroid Build Coastguard Worker mCurScanline = 0;
570*ec779b8eSAndroid Build Coastguard Worker
571*ec779b8eSAndroid Build Coastguard Worker if (mFrameDecoded) {
572*ec779b8eSAndroid Build Coastguard Worker return true;
573*ec779b8eSAndroid Build Coastguard Worker }
574*ec779b8eSAndroid Build Coastguard Worker
575*ec779b8eSAndroid Build Coastguard Worker sp<MediaMetadataRetriever> retriever;
576*ec779b8eSAndroid Build Coastguard Worker {
577*ec779b8eSAndroid Build Coastguard Worker Mutex::Autolock _l(mRetrieverLock);
578*ec779b8eSAndroid Build Coastguard Worker if (mRetriever == nullptr) {
579*ec779b8eSAndroid Build Coastguard Worker ALOGE("Failed to get MediaMetadataRetriever!");
580*ec779b8eSAndroid Build Coastguard Worker return false;
581*ec779b8eSAndroid Build Coastguard Worker }
582*ec779b8eSAndroid Build Coastguard Worker
583*ec779b8eSAndroid Build Coastguard Worker retriever = mRetriever;
584*ec779b8eSAndroid Build Coastguard Worker }
585*ec779b8eSAndroid Build Coastguard Worker
586*ec779b8eSAndroid Build Coastguard Worker // See if we want to decode in slices to allow client to start
587*ec779b8eSAndroid Build Coastguard Worker // scanline processing in parallel with decode. If this fails
588*ec779b8eSAndroid Build Coastguard Worker // we fallback to decoding the full frame.
589*ec779b8eSAndroid Build Coastguard Worker if (mHasImage) {
590*ec779b8eSAndroid Build Coastguard Worker if (mSliceHeight >= 512 &&
591*ec779b8eSAndroid Build Coastguard Worker mImageInfo.mWidth >= 3000 &&
592*ec779b8eSAndroid Build Coastguard Worker mImageInfo.mHeight >= 2000 ) {
593*ec779b8eSAndroid Build Coastguard Worker // Try decoding in slices only if the image has tiles and is big enough.
594*ec779b8eSAndroid Build Coastguard Worker mNumSlices = (mImageInfo.mHeight + mSliceHeight - 1) / mSliceHeight;
595*ec779b8eSAndroid Build Coastguard Worker ALOGV("mSliceHeight %u, mNumSlices %zu", mSliceHeight, mNumSlices);
596*ec779b8eSAndroid Build Coastguard Worker }
597*ec779b8eSAndroid Build Coastguard Worker
598*ec779b8eSAndroid Build Coastguard Worker if (mNumSlices > 1) {
599*ec779b8eSAndroid Build Coastguard Worker // get first slice and metadata
600*ec779b8eSAndroid Build Coastguard Worker sp<IMemory> frameMemory = retriever->getImageRectAtIndex(
601*ec779b8eSAndroid Build Coastguard Worker -1, mOutputColor, 0, 0, mImageInfo.mWidth, mSliceHeight);
602*ec779b8eSAndroid Build Coastguard Worker
603*ec779b8eSAndroid Build Coastguard Worker if (frameMemory == nullptr || frameMemory->unsecurePointer() == nullptr) {
604*ec779b8eSAndroid Build Coastguard Worker ALOGE("decode: metadata is a nullptr");
605*ec779b8eSAndroid Build Coastguard Worker return false;
606*ec779b8eSAndroid Build Coastguard Worker }
607*ec779b8eSAndroid Build Coastguard Worker
608*ec779b8eSAndroid Build Coastguard Worker // TODO: Using unsecurePointer() has some associated security pitfalls
609*ec779b8eSAndroid Build Coastguard Worker // (see declaration for details).
610*ec779b8eSAndroid Build Coastguard Worker // Either document why it is safe in this case or address the
611*ec779b8eSAndroid Build Coastguard Worker // issue (e.g. by copying).
612*ec779b8eSAndroid Build Coastguard Worker VideoFrame* videoFrame = static_cast<VideoFrame*>(frameMemory->unsecurePointer());
613*ec779b8eSAndroid Build Coastguard Worker
614*ec779b8eSAndroid Build Coastguard Worker if (frameInfo != nullptr) {
615*ec779b8eSAndroid Build Coastguard Worker initFrameInfo(frameInfo, videoFrame);
616*ec779b8eSAndroid Build Coastguard Worker }
617*ec779b8eSAndroid Build Coastguard Worker mFrameMemory = frameMemory;
618*ec779b8eSAndroid Build Coastguard Worker mAvailableLines = mSliceHeight;
619*ec779b8eSAndroid Build Coastguard Worker mThread = new DecodeThread(this);
620*ec779b8eSAndroid Build Coastguard Worker if (mThread->run("HeifDecode", ANDROID_PRIORITY_FOREGROUND) == OK) {
621*ec779b8eSAndroid Build Coastguard Worker mFrameDecoded = true;
622*ec779b8eSAndroid Build Coastguard Worker return true;
623*ec779b8eSAndroid Build Coastguard Worker }
624*ec779b8eSAndroid Build Coastguard Worker // Fallback to decode without slicing
625*ec779b8eSAndroid Build Coastguard Worker mThread.clear();
626*ec779b8eSAndroid Build Coastguard Worker mNumSlices = 1;
627*ec779b8eSAndroid Build Coastguard Worker mSliceHeight = 0;
628*ec779b8eSAndroid Build Coastguard Worker mAvailableLines = 0;
629*ec779b8eSAndroid Build Coastguard Worker mFrameMemory.clear();
630*ec779b8eSAndroid Build Coastguard Worker }
631*ec779b8eSAndroid Build Coastguard Worker }
632*ec779b8eSAndroid Build Coastguard Worker
633*ec779b8eSAndroid Build Coastguard Worker if (mHasImage) {
634*ec779b8eSAndroid Build Coastguard Worker // image index < 0 to retrieve primary image
635*ec779b8eSAndroid Build Coastguard Worker mFrameMemory = retriever->getImageAtIndex(-1, mOutputColor);
636*ec779b8eSAndroid Build Coastguard Worker } else if (mHasVideo) {
637*ec779b8eSAndroid Build Coastguard Worker mFrameMemory = retriever->getFrameAtTime(0,
638*ec779b8eSAndroid Build Coastguard Worker MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC, mOutputColor);
639*ec779b8eSAndroid Build Coastguard Worker }
640*ec779b8eSAndroid Build Coastguard Worker
641*ec779b8eSAndroid Build Coastguard Worker if (mFrameMemory == nullptr || mFrameMemory->unsecurePointer() == nullptr) {
642*ec779b8eSAndroid Build Coastguard Worker ALOGE("decode: videoFrame is a nullptr");
643*ec779b8eSAndroid Build Coastguard Worker return false;
644*ec779b8eSAndroid Build Coastguard Worker }
645*ec779b8eSAndroid Build Coastguard Worker
646*ec779b8eSAndroid Build Coastguard Worker // TODO: Using unsecurePointer() has some associated security pitfalls
647*ec779b8eSAndroid Build Coastguard Worker // (see declaration for details).
648*ec779b8eSAndroid Build Coastguard Worker // Either document why it is safe in this case or address the
649*ec779b8eSAndroid Build Coastguard Worker // issue (e.g. by copying).
650*ec779b8eSAndroid Build Coastguard Worker VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->unsecurePointer());
651*ec779b8eSAndroid Build Coastguard Worker if (videoFrame->mSize == 0 ||
652*ec779b8eSAndroid Build Coastguard Worker mFrameMemory->size() < videoFrame->getFlattenedSize()) {
653*ec779b8eSAndroid Build Coastguard Worker ALOGE("decode: videoFrame size is invalid");
654*ec779b8eSAndroid Build Coastguard Worker return false;
655*ec779b8eSAndroid Build Coastguard Worker }
656*ec779b8eSAndroid Build Coastguard Worker
657*ec779b8eSAndroid Build Coastguard Worker ALOGV("Decoded dimension %dx%d, display %dx%d, angle %d, rowbytes %d, size %d",
658*ec779b8eSAndroid Build Coastguard Worker videoFrame->mWidth,
659*ec779b8eSAndroid Build Coastguard Worker videoFrame->mHeight,
660*ec779b8eSAndroid Build Coastguard Worker videoFrame->mDisplayWidth,
661*ec779b8eSAndroid Build Coastguard Worker videoFrame->mDisplayHeight,
662*ec779b8eSAndroid Build Coastguard Worker videoFrame->mRotationAngle,
663*ec779b8eSAndroid Build Coastguard Worker videoFrame->mRowBytes,
664*ec779b8eSAndroid Build Coastguard Worker videoFrame->mSize);
665*ec779b8eSAndroid Build Coastguard Worker
666*ec779b8eSAndroid Build Coastguard Worker if (frameInfo != nullptr) {
667*ec779b8eSAndroid Build Coastguard Worker initFrameInfo(frameInfo, videoFrame);
668*ec779b8eSAndroid Build Coastguard Worker
669*ec779b8eSAndroid Build Coastguard Worker }
670*ec779b8eSAndroid Build Coastguard Worker mFrameDecoded = true;
671*ec779b8eSAndroid Build Coastguard Worker
672*ec779b8eSAndroid Build Coastguard Worker // Aggressively clear to avoid holding on to resources
673*ec779b8eSAndroid Build Coastguard Worker {
674*ec779b8eSAndroid Build Coastguard Worker Mutex::Autolock _l(mRetrieverLock);
675*ec779b8eSAndroid Build Coastguard Worker mRetriever.clear();
676*ec779b8eSAndroid Build Coastguard Worker }
677*ec779b8eSAndroid Build Coastguard Worker
678*ec779b8eSAndroid Build Coastguard Worker // Hold on to mDataSource in case the client wants to redecode.
679*ec779b8eSAndroid Build Coastguard Worker return true;
680*ec779b8eSAndroid Build Coastguard Worker }
681*ec779b8eSAndroid Build Coastguard Worker
decodeSequence(int frameIndex,HeifFrameInfo * frameInfo)682*ec779b8eSAndroid Build Coastguard Worker bool HeifDecoderImpl::decodeSequence(int frameIndex, HeifFrameInfo* frameInfo) {
683*ec779b8eSAndroid Build Coastguard Worker ALOGV("%s: frame index %d", __FUNCTION__, frameIndex);
684*ec779b8eSAndroid Build Coastguard Worker if (!mHasVideo) {
685*ec779b8eSAndroid Build Coastguard Worker return false;
686*ec779b8eSAndroid Build Coastguard Worker }
687*ec779b8eSAndroid Build Coastguard Worker
688*ec779b8eSAndroid Build Coastguard Worker if (frameIndex < 0 || frameIndex >= mSequenceLength) {
689*ec779b8eSAndroid Build Coastguard Worker ALOGE("invalid frame index: %d, total frames %zu", frameIndex, mSequenceLength);
690*ec779b8eSAndroid Build Coastguard Worker return false;
691*ec779b8eSAndroid Build Coastguard Worker }
692*ec779b8eSAndroid Build Coastguard Worker
693*ec779b8eSAndroid Build Coastguard Worker mCurScanline = 0;
694*ec779b8eSAndroid Build Coastguard Worker
695*ec779b8eSAndroid Build Coastguard Worker // set total scanline to sequence height now
696*ec779b8eSAndroid Build Coastguard Worker mTotalScanline = mSequenceInfo.mHeight;
697*ec779b8eSAndroid Build Coastguard Worker
698*ec779b8eSAndroid Build Coastguard Worker sp<MediaMetadataRetriever> retriever;
699*ec779b8eSAndroid Build Coastguard Worker {
700*ec779b8eSAndroid Build Coastguard Worker Mutex::Autolock _l(mRetrieverLock);
701*ec779b8eSAndroid Build Coastguard Worker retriever = mRetriever;
702*ec779b8eSAndroid Build Coastguard Worker if (retriever == nullptr) {
703*ec779b8eSAndroid Build Coastguard Worker ALOGE("failed to get MediaMetadataRetriever!");
704*ec779b8eSAndroid Build Coastguard Worker return false;
705*ec779b8eSAndroid Build Coastguard Worker }
706*ec779b8eSAndroid Build Coastguard Worker }
707*ec779b8eSAndroid Build Coastguard Worker
708*ec779b8eSAndroid Build Coastguard Worker mFrameMemory = retriever->getFrameAtIndex(frameIndex, mOutputColor);
709*ec779b8eSAndroid Build Coastguard Worker if (mFrameMemory == nullptr || mFrameMemory->unsecurePointer() == nullptr) {
710*ec779b8eSAndroid Build Coastguard Worker ALOGE("decode: videoFrame is a nullptr");
711*ec779b8eSAndroid Build Coastguard Worker return false;
712*ec779b8eSAndroid Build Coastguard Worker }
713*ec779b8eSAndroid Build Coastguard Worker
714*ec779b8eSAndroid Build Coastguard Worker // TODO: Using unsecurePointer() has some associated security pitfalls
715*ec779b8eSAndroid Build Coastguard Worker // (see declaration for details).
716*ec779b8eSAndroid Build Coastguard Worker // Either document why it is safe in this case or address the
717*ec779b8eSAndroid Build Coastguard Worker // issue (e.g. by copying).
718*ec779b8eSAndroid Build Coastguard Worker VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->unsecurePointer());
719*ec779b8eSAndroid Build Coastguard Worker if (videoFrame->mSize == 0 ||
720*ec779b8eSAndroid Build Coastguard Worker mFrameMemory->size() < videoFrame->getFlattenedSize()) {
721*ec779b8eSAndroid Build Coastguard Worker ALOGE("decode: videoFrame size is invalid");
722*ec779b8eSAndroid Build Coastguard Worker return false;
723*ec779b8eSAndroid Build Coastguard Worker }
724*ec779b8eSAndroid Build Coastguard Worker
725*ec779b8eSAndroid Build Coastguard Worker ALOGV("Decoded dimension %dx%d, display %dx%d, angle %d, rowbytes %d, size %d",
726*ec779b8eSAndroid Build Coastguard Worker videoFrame->mWidth,
727*ec779b8eSAndroid Build Coastguard Worker videoFrame->mHeight,
728*ec779b8eSAndroid Build Coastguard Worker videoFrame->mDisplayWidth,
729*ec779b8eSAndroid Build Coastguard Worker videoFrame->mDisplayHeight,
730*ec779b8eSAndroid Build Coastguard Worker videoFrame->mRotationAngle,
731*ec779b8eSAndroid Build Coastguard Worker videoFrame->mRowBytes,
732*ec779b8eSAndroid Build Coastguard Worker videoFrame->mSize);
733*ec779b8eSAndroid Build Coastguard Worker
734*ec779b8eSAndroid Build Coastguard Worker if (frameInfo != nullptr) {
735*ec779b8eSAndroid Build Coastguard Worker initFrameInfo(frameInfo, videoFrame);
736*ec779b8eSAndroid Build Coastguard Worker }
737*ec779b8eSAndroid Build Coastguard Worker return true;
738*ec779b8eSAndroid Build Coastguard Worker }
739*ec779b8eSAndroid Build Coastguard Worker
getScanlineInner(uint8_t * dst)740*ec779b8eSAndroid Build Coastguard Worker bool HeifDecoderImpl::getScanlineInner(uint8_t* dst) {
741*ec779b8eSAndroid Build Coastguard Worker if (mFrameMemory == nullptr || mFrameMemory->unsecurePointer() == nullptr) {
742*ec779b8eSAndroid Build Coastguard Worker return false;
743*ec779b8eSAndroid Build Coastguard Worker }
744*ec779b8eSAndroid Build Coastguard Worker // TODO: Using unsecurePointer() has some associated security pitfalls
745*ec779b8eSAndroid Build Coastguard Worker // (see declaration for details).
746*ec779b8eSAndroid Build Coastguard Worker // Either document why it is safe in this case or address the
747*ec779b8eSAndroid Build Coastguard Worker // issue (e.g. by copying).
748*ec779b8eSAndroid Build Coastguard Worker VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->unsecurePointer());
749*ec779b8eSAndroid Build Coastguard Worker uint8_t* src = videoFrame->getFlattenedData() +
750*ec779b8eSAndroid Build Coastguard Worker (videoFrame->mRowBytes * (mCurScanline + videoFrame->mDisplayTop)) +
751*ec779b8eSAndroid Build Coastguard Worker (videoFrame->mBytesPerPixel * videoFrame->mDisplayLeft);
752*ec779b8eSAndroid Build Coastguard Worker mCurScanline++;
753*ec779b8eSAndroid Build Coastguard Worker // Do not try to copy more than |videoFrame->mWidth| pixels.
754*ec779b8eSAndroid Build Coastguard Worker uint32_t width = std::min(videoFrame->mDisplayWidth, videoFrame->mWidth);
755*ec779b8eSAndroid Build Coastguard Worker memcpy(dst, src, videoFrame->mBytesPerPixel * width);
756*ec779b8eSAndroid Build Coastguard Worker return true;
757*ec779b8eSAndroid Build Coastguard Worker }
758*ec779b8eSAndroid Build Coastguard Worker
getScanline(uint8_t * dst)759*ec779b8eSAndroid Build Coastguard Worker bool HeifDecoderImpl::getScanline(uint8_t* dst) {
760*ec779b8eSAndroid Build Coastguard Worker if (mCurScanline >= mTotalScanline) {
761*ec779b8eSAndroid Build Coastguard Worker ALOGE("no more scanline available");
762*ec779b8eSAndroid Build Coastguard Worker return false;
763*ec779b8eSAndroid Build Coastguard Worker }
764*ec779b8eSAndroid Build Coastguard Worker
765*ec779b8eSAndroid Build Coastguard Worker if (mNumSlices > 1) {
766*ec779b8eSAndroid Build Coastguard Worker Mutex::Autolock autolock(mLock);
767*ec779b8eSAndroid Build Coastguard Worker
768*ec779b8eSAndroid Build Coastguard Worker while (!mAsyncDecodeDone && mCurScanline >= mAvailableLines) {
769*ec779b8eSAndroid Build Coastguard Worker mScanlineReady.wait(mLock);
770*ec779b8eSAndroid Build Coastguard Worker }
771*ec779b8eSAndroid Build Coastguard Worker return (mCurScanline < mAvailableLines) ? getScanlineInner(dst) : false;
772*ec779b8eSAndroid Build Coastguard Worker }
773*ec779b8eSAndroid Build Coastguard Worker
774*ec779b8eSAndroid Build Coastguard Worker return getScanlineInner(dst);
775*ec779b8eSAndroid Build Coastguard Worker }
776*ec779b8eSAndroid Build Coastguard Worker
skipScanlines(size_t count)777*ec779b8eSAndroid Build Coastguard Worker size_t HeifDecoderImpl::skipScanlines(size_t count) {
778*ec779b8eSAndroid Build Coastguard Worker uint32_t oldScanline = mCurScanline;
779*ec779b8eSAndroid Build Coastguard Worker mCurScanline += count;
780*ec779b8eSAndroid Build Coastguard Worker if (mCurScanline > mTotalScanline) {
781*ec779b8eSAndroid Build Coastguard Worker mCurScanline = mTotalScanline;
782*ec779b8eSAndroid Build Coastguard Worker }
783*ec779b8eSAndroid Build Coastguard Worker return (mCurScanline > oldScanline) ? (mCurScanline - oldScanline) : 0;
784*ec779b8eSAndroid Build Coastguard Worker }
785*ec779b8eSAndroid Build Coastguard Worker
getColorDepth()786*ec779b8eSAndroid Build Coastguard Worker uint32_t HeifDecoderImpl::getColorDepth() {
787*ec779b8eSAndroid Build Coastguard Worker HeifFrameInfo* info = &mImageInfo;
788*ec779b8eSAndroid Build Coastguard Worker if (info != nullptr) {
789*ec779b8eSAndroid Build Coastguard Worker return mImageInfo.mBitDepth;
790*ec779b8eSAndroid Build Coastguard Worker } else {
791*ec779b8eSAndroid Build Coastguard Worker return 0;
792*ec779b8eSAndroid Build Coastguard Worker }
793*ec779b8eSAndroid Build Coastguard Worker }
794*ec779b8eSAndroid Build Coastguard Worker
795*ec779b8eSAndroid Build Coastguard Worker } // namespace android
796