blob: 8b846bee2bbf3fcb77ac51b7f8bac4a8460a81d7 [file] [log] [blame]
Chong Zhangea280cb2017-08-02 11:10:10 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "HeifDecoderImpl"
19
20#include "HeifDecoderImpl.h"
21
22#include <stdio.h>
23
24#include <binder/IMemory.h>
25#include <drm/drm_framework_common.h>
26#include <media/IDataSource.h>
27#include <media/mediametadataretriever.h>
28#include <media/stagefright/MediaSource.h>
29#include <private/media/VideoFrame.h>
30#include <utils/Log.h>
31#include <utils/RefBase.h>
32
33HeifDecoder* createHeifDecoder() {
34 return new android::HeifDecoderImpl();
35}
36
37namespace android {
38
39/*
40 * HeifDataSource
41 *
42 * Proxies data requests over IDataSource interface from MediaMetadataRetriever
43 * to the HeifStream interface we received from the heif decoder client.
44 */
45class HeifDataSource : public BnDataSource {
46public:
47 /*
48 * Constructs HeifDataSource; will take ownership of |stream|.
49 */
50 HeifDataSource(HeifStream* stream)
51 : mStream(stream), mReadPos(0), mEOS(false) {}
52
53 ~HeifDataSource() override {}
54
55 /*
56 * Initializes internal resources.
57 */
58 bool init();
59
60 sp<IMemory> getIMemory() override { return mMemory; }
61 ssize_t readAt(off64_t offset, size_t size) override;
62 status_t getSize(off64_t* size) override ;
63 void close() {}
64 uint32_t getFlags() override { return 0; }
65 String8 toString() override { return String8("HeifDataSource"); }
66 sp<DecryptHandle> DrmInitialization(const char*) override {
67 return nullptr;
68 }
69
70private:
71 /*
72 * Buffer size for passing the read data to mediaserver. Set to 64K
73 * (which is what MediaDataSource Java API's jni implementation uses).
74 */
75 enum {
76 kBufferSize = 64 * 1024,
77 };
78 sp<IMemory> mMemory;
79 std::unique_ptr<HeifStream> mStream;
80 off64_t mReadPos;
81 bool mEOS;
82};
83
84bool HeifDataSource::init() {
85 sp<MemoryDealer> memoryDealer =
86 new MemoryDealer(kBufferSize, "HeifDataSource");
87 mMemory = memoryDealer->allocate(kBufferSize);
88 if (mMemory == nullptr) {
89 ALOGE("Failed to allocate shared memory!");
90 return false;
91 }
92 return true;
93}
94
95ssize_t HeifDataSource::readAt(off64_t offset, size_t size) {
96 ALOGV("readAt: offset=%lld, size=%zu", (long long)offset, size);
97
98 if (size == 0) {
99 return mEOS ? ERROR_END_OF_STREAM : 0;
100 }
101
102 if (offset < mReadPos) {
103 // try seek, then rewind/skip, fail if none worked
104 if (mStream->seek(offset)) {
105 ALOGV("readAt: seek to offset=%lld", (long long)offset);
106 mReadPos = offset;
107 mEOS = false;
108 } else if (mStream->rewind()) {
109 ALOGV("readAt: rewind to offset=0");
110 mReadPos = 0;
111 mEOS = false;
112 } else {
113 ALOGE("readAt: couldn't seek or rewind!");
114 mEOS = true;
115 }
116 }
117
118 if (mEOS) {
119 ALOGV("readAt: EOS");
120 return ERROR_END_OF_STREAM;
121 }
122
123 if (offset > mReadPos) {
124 // skipping
125 size_t skipSize = offset - mReadPos;
126 size_t bytesSkipped = mStream->read(nullptr, skipSize);
127 if (bytesSkipped <= skipSize) {
128 mReadPos += bytesSkipped;
129 }
130 if (bytesSkipped != skipSize) {
131 mEOS = true;
132 return ERROR_END_OF_STREAM;
133 }
134 }
135
136 if (size > kBufferSize) {
137 size = kBufferSize;
138 }
139 size_t bytesRead = mStream->read(mMemory->pointer(), size);
140 if (bytesRead > size || bytesRead == 0) {
141 // bytesRead is invalid
142 mEOS = true;
143 return ERROR_END_OF_STREAM;
144 } if (bytesRead < size) {
145 // read some bytes but not all, set EOS and return ERROR_END_OF_STREAM next time
146 mEOS = true;
147 }
148 mReadPos += bytesRead;
149 return bytesRead;
150}
151
152status_t HeifDataSource::getSize(off64_t* size) {
153 if (!mStream->hasLength()) {
154 *size = -1;
155 ALOGE("getSize: not supported!");
156 return ERROR_UNSUPPORTED;
157 }
158 *size = mStream->getLength();
159 ALOGV("getSize: size=%lld", (long long)*size);
160 return OK;
161}
162
163/////////////////////////////////////////////////////////////////////////
164
165HeifDecoderImpl::HeifDecoderImpl() :
166 // output color format should always be set via setOutputColor(), in case
167 // it's not, default to HAL_PIXEL_FORMAT_RGB_565.
168 mOutputColor(HAL_PIXEL_FORMAT_RGB_565),
169 mCurScanline(0) {
170}
171
172HeifDecoderImpl::~HeifDecoderImpl() {
173}
174
175bool HeifDecoderImpl::init(HeifStream* stream, HeifFrameInfo* frameInfo) {
176 sp<HeifDataSource> dataSource = new HeifDataSource(stream);
177 if (!dataSource->init()) {
178 return false;
179 }
180 mDataSource = dataSource;
181
182 mRetriever = new MediaMetadataRetriever();
183 status_t err = mRetriever->setDataSource(mDataSource, "video/mp4");
184 if (err != OK) {
185 ALOGE("failed to set data source!");
186
187 mRetriever.clear();
188 mDataSource.clear();
189 return false;
190 }
191 ALOGV("successfully set data source.");
192
193 const char* hasVideo = mRetriever->extractMetadata(METADATA_KEY_HAS_VIDEO);
194 if (!hasVideo || strcasecmp(hasVideo, "yes")) {
195 ALOGE("no video: %s", hasVideo ? hasVideo : "null");
196 return false;
197 }
198
199 mFrameMemory = mRetriever->getFrameAtTime(0,
200 IMediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
201 mOutputColor, true /*metaOnly*/);
202 if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
203 ALOGE("getFrameAtTime: videoFrame is a nullptr");
204 return false;
205 }
206
207 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
208
209 ALOGV("Meta dimension %dx%d, display %dx%d, angle %d, iccSize %d",
210 videoFrame->mWidth,
211 videoFrame->mHeight,
212 videoFrame->mDisplayWidth,
213 videoFrame->mDisplayHeight,
214 videoFrame->mRotationAngle,
215 videoFrame->mIccSize);
216
217 if (frameInfo != nullptr) {
218 frameInfo->set(
219 videoFrame->mWidth,
220 videoFrame->mHeight,
221 videoFrame->mRotationAngle,
222 videoFrame->mBytesPerPixel,
223 videoFrame->mIccSize,
224 videoFrame->getFlattenedIccData());
225 }
226 return true;
227}
228
229bool HeifDecoderImpl::getEncodedColor(HeifEncodedColor* /*outColor*/) const {
230 ALOGW("getEncodedColor: not implemented!");
231 return false;
232}
233
234bool HeifDecoderImpl::setOutputColor(HeifColorFormat heifColor) {
235 switch(heifColor) {
236 case kHeifColorFormat_RGB565:
237 {
238 mOutputColor = HAL_PIXEL_FORMAT_RGB_565;
239 return true;
240 }
241 case kHeifColorFormat_RGBA_8888:
242 {
243 mOutputColor = HAL_PIXEL_FORMAT_RGBA_8888;
244 return true;
245 }
246 case kHeifColorFormat_BGRA_8888:
247 {
248 mOutputColor = HAL_PIXEL_FORMAT_BGRA_8888;
249 return true;
250 }
251 default:
252 break;
253 }
254 ALOGE("Unsupported output color format %d", heifColor);
255 return false;
256}
257
258bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) {
259 mFrameMemory = mRetriever->getFrameAtTime(0,
260 IMediaSource::ReadOptions::SEEK_PREVIOUS_SYNC, mOutputColor);
261 if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
262 ALOGE("getFrameAtTime: videoFrame is a nullptr");
263 return false;
264 }
265
266 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
267 ALOGV("Decoded dimension %dx%d, display %dx%d, angle %d, rowbytes %d, size %d",
268 videoFrame->mWidth,
269 videoFrame->mHeight,
270 videoFrame->mDisplayWidth,
271 videoFrame->mDisplayHeight,
272 videoFrame->mRotationAngle,
273 videoFrame->mRowBytes,
274 videoFrame->mSize);
275
276 if (frameInfo != nullptr) {
277 frameInfo->set(
278 videoFrame->mWidth,
279 videoFrame->mHeight,
280 videoFrame->mRotationAngle,
281 videoFrame->mBytesPerPixel,
282 videoFrame->mIccSize,
283 videoFrame->getFlattenedIccData());
284 }
285 return true;
286}
287
288bool HeifDecoderImpl::getScanline(uint8_t* dst) {
289 if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
290 return false;
291 }
292 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
293 if (mCurScanline >= videoFrame->mHeight) {
294 return false;
295 }
296 uint8_t* src = videoFrame->getFlattenedData() + videoFrame->mRowBytes * mCurScanline++;
297 memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mWidth);
298 return true;
299}
300
301size_t HeifDecoderImpl::skipScanlines(size_t count) {
302 if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
303 return 0;
304 }
305 VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
306
307 uint32_t oldScanline = mCurScanline;
308 mCurScanline += count;
309 if (mCurScanline >= videoFrame->mHeight) {
310 mCurScanline = videoFrame->mHeight;
311 }
312 return (mCurScanline > oldScanline) ? (mCurScanline - oldScanline) : 0;
313}
314
315} // namespace android