xref: /aosp_15_r20/frameworks/av/media/libheif/HeifDecoderImpl.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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