blob: 1adecb969b5f8d06bebe8b3ec4c408d99748036d [file] [log] [blame]
Yin-Chia Yehc3603822016-01-18 22:11:19 -08001/*
2 * Copyright (C) 2016 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#include <inttypes.h>
18
19//#define LOG_NDEBUG 0
20#define LOG_TAG "NdkImageReader"
21
22#include "NdkImagePriv.h"
23#include "NdkImageReaderPriv.h"
24
Mathias Agopian05d19b02017-02-28 16:28:19 -080025#include <cutils/atomic.h>
Yin-Chia Yehc3603822016-01-18 22:11:19 -080026#include <utils/Log.h>
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -080027#include <android_media_Utils.h>
Yin-Chia Yehc3603822016-01-18 22:11:19 -080028#include <android_runtime/android_view_Surface.h>
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -080029#include <android_runtime/android_hardware_HardwareBuffer.h>
Jiwen 'Steve' Cai755ca9b2017-03-31 16:59:50 -070030#include <grallocusage/GrallocUsageConversion.h>
Jayant Chowdhary249e1f22018-09-24 15:07:45 -070031#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
Yin-Chia Yehc3603822016-01-18 22:11:19 -080032
33using namespace android;
34
35namespace {
36 // Get an ID that's unique within this process.
37 static int32_t createProcessUniqueId() {
38 static volatile int32_t globalCounter = 0;
39 return android_atomic_inc(&globalCounter);
40 }
41}
42
43const char* AImageReader::kCallbackFpKey = "Callback";
44const char* AImageReader::kContextKey = "Context";
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -080045const char* AImageReader::kGraphicBufferKey = "GraphicBuffer";
Yin-Chia Yehc3603822016-01-18 22:11:19 -080046
Jayant Chowdhary249e1f22018-09-24 15:07:45 -070047static constexpr int kWindowHalTokenSizeMax = 256;
48
49static native_handle_t *convertHalTokenToNativeHandle(const HalToken &halToken);
50
Yin-Chia Yehc3603822016-01-18 22:11:19 -080051bool
Jiwen 'Steve' Cai55ec2562017-04-12 15:56:41 -070052AImageReader::isSupportedFormatAndUsage(int32_t format, uint64_t usage) {
53 // Check whether usage has either CPU_READ_OFTEN or CPU_READ set. Note that check against
54 // AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN (0x6) is sufficient as it implies
55 // AHARDWAREBUFFER_USAGE_CPU_READ (0x2).
56 bool hasCpuUsage = usage & AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN;
Yin-Chia Yehc3603822016-01-18 22:11:19 -080057 switch (format) {
Jiwen 'Steve' Caide2a5442017-02-08 14:41:41 -080058 case AIMAGE_FORMAT_RGBA_8888:
59 case AIMAGE_FORMAT_RGBX_8888:
60 case AIMAGE_FORMAT_RGB_888:
61 case AIMAGE_FORMAT_RGB_565:
62 case AIMAGE_FORMAT_RGBA_FP16:
Yin-Chia Yehc3603822016-01-18 22:11:19 -080063 case AIMAGE_FORMAT_YUV_420_888:
64 case AIMAGE_FORMAT_JPEG:
65 case AIMAGE_FORMAT_RAW16:
66 case AIMAGE_FORMAT_RAW_PRIVATE:
67 case AIMAGE_FORMAT_RAW10:
68 case AIMAGE_FORMAT_RAW12:
69 case AIMAGE_FORMAT_DEPTH16:
70 case AIMAGE_FORMAT_DEPTH_POINT_CLOUD:
Shuzhen Wang1cfbbec2018-10-08 13:55:28 -070071 case AIMAGE_FORMAT_Y8:
Yin-Chia Yehc3603822016-01-18 22:11:19 -080072 return true;
Jiwen 'Steve' Cai55ec2562017-04-12 15:56:41 -070073 case AIMAGE_FORMAT_PRIVATE:
74 // For private format, cpu usage is prohibited.
75 return !hasCpuUsage;
Yin-Chia Yehc3603822016-01-18 22:11:19 -080076 default:
77 return false;
78 }
79}
80
81int
82AImageReader::getNumPlanesForFormat(int32_t format) {
83 switch (format) {
84 case AIMAGE_FORMAT_YUV_420_888:
85 return 3;
Jiwen 'Steve' Caide2a5442017-02-08 14:41:41 -080086 case AIMAGE_FORMAT_RGBA_8888:
87 case AIMAGE_FORMAT_RGBX_8888:
88 case AIMAGE_FORMAT_RGB_888:
89 case AIMAGE_FORMAT_RGB_565:
90 case AIMAGE_FORMAT_RGBA_FP16:
Yin-Chia Yehc3603822016-01-18 22:11:19 -080091 case AIMAGE_FORMAT_JPEG:
92 case AIMAGE_FORMAT_RAW16:
93 case AIMAGE_FORMAT_RAW_PRIVATE:
94 case AIMAGE_FORMAT_RAW10:
95 case AIMAGE_FORMAT_RAW12:
96 case AIMAGE_FORMAT_DEPTH16:
97 case AIMAGE_FORMAT_DEPTH_POINT_CLOUD:
Shuzhen Wang1cfbbec2018-10-08 13:55:28 -070098 case AIMAGE_FORMAT_Y8:
Yin-Chia Yehc3603822016-01-18 22:11:19 -080099 return 1;
Jiwen 'Steve' Cai55ec2562017-04-12 15:56:41 -0700100 case AIMAGE_FORMAT_PRIVATE:
101 return 0;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800102 default:
103 return -1;
104 }
105}
106
107void
108AImageReader::FrameListener::onFrameAvailable(const BufferItem& /*item*/) {
109 Mutex::Autolock _l(mLock);
110 sp<AImageReader> reader = mReader.promote();
111 if (reader == nullptr) {
112 ALOGW("A frame is available after AImageReader closed!");
113 return; // reader has been closed
114 }
115 if (mListener.onImageAvailable == nullptr) {
116 return; // No callback registered
117 }
118
119 sp<AMessage> msg = new AMessage(AImageReader::kWhatImageAvailable, reader->mHandler);
120 msg->setPointer(AImageReader::kCallbackFpKey, (void *) mListener.onImageAvailable);
121 msg->setPointer(AImageReader::kContextKey, mListener.context);
122 msg->post();
123}
124
125media_status_t
126AImageReader::FrameListener::setImageListener(AImageReader_ImageListener* listener) {
127 Mutex::Autolock _l(mLock);
128 if (listener == nullptr) {
Yin-Chia Yeh1d0955c2016-05-16 01:14:13 -0700129 mListener.context = nullptr;
130 mListener.onImageAvailable = nullptr;
131 } else {
132 mListener = *listener;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800133 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800134 return AMEDIA_OK;
135}
136
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800137void
138AImageReader::BufferRemovedListener::onBufferFreed(const wp<GraphicBuffer>& graphicBuffer) {
139 Mutex::Autolock _l(mLock);
140 sp<AImageReader> reader = mReader.promote();
141 if (reader == nullptr) {
142 ALOGW("A frame is available after AImageReader closed!");
143 return; // reader has been closed
144 }
145 if (mListener.onBufferRemoved == nullptr) {
146 return; // No callback registered
147 }
148
149 sp<GraphicBuffer> gBuffer = graphicBuffer.promote();
150 if (gBuffer == nullptr) {
151 ALOGW("A buffer being freed has gone away!");
152 return; // buffer is already destroyed
153 }
154
155 sp<AMessage> msg = new AMessage(AImageReader::kWhatBufferRemoved, reader->mHandler);
156 msg->setPointer(
157 AImageReader::kCallbackFpKey, (void*) mListener.onBufferRemoved);
158 msg->setPointer(AImageReader::kContextKey, mListener.context);
159 msg->setObject(AImageReader::kGraphicBufferKey, gBuffer);
160 msg->post();
161}
162
163media_status_t
164AImageReader::BufferRemovedListener::setBufferRemovedListener(
165 AImageReader_BufferRemovedListener* listener) {
166 Mutex::Autolock _l(mLock);
167 if (listener == nullptr) {
168 mListener.context = nullptr;
169 mListener.onBufferRemoved = nullptr;
170 } else {
171 mListener = *listener;
172 }
173 return AMEDIA_OK;
174}
175
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800176media_status_t
177AImageReader::setImageListenerLocked(AImageReader_ImageListener* listener) {
178 return mFrameListener->setImageListener(listener);
179}
180
181media_status_t
182AImageReader::setImageListener(AImageReader_ImageListener* listener) {
183 Mutex::Autolock _l(mLock);
184 return setImageListenerLocked(listener);
185}
186
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800187media_status_t
188AImageReader::setBufferRemovedListenerLocked(AImageReader_BufferRemovedListener* listener) {
189 return mBufferRemovedListener->setBufferRemovedListener(listener);
190}
191
192media_status_t
193AImageReader::setBufferRemovedListener(AImageReader_BufferRemovedListener* listener) {
194 Mutex::Autolock _l(mLock);
195 return setBufferRemovedListenerLocked(listener);
196}
197
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800198void AImageReader::CallbackHandler::onMessageReceived(
199 const sp<AMessage> &msg) {
200 switch (msg->what()) {
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800201 case kWhatBufferRemoved:
202 {
203 AImageReader_BufferRemovedCallback onBufferRemoved;
204 void* context;
205 bool found = msg->findPointer(kCallbackFpKey, (void**) &onBufferRemoved);
206 if (!found || onBufferRemoved == nullptr) {
207 ALOGE("%s: Cannot find onBufferRemoved callback fp!", __FUNCTION__);
208 return;
209 }
210 found = msg->findPointer(kContextKey, &context);
211 if (!found) {
212 ALOGE("%s: Cannot find callback context!", __FUNCTION__);
213 return;
214 }
215 sp<RefBase> bufferToFree;
216 found = msg->findObject(kGraphicBufferKey, &bufferToFree);
217 if (!found || bufferToFree == nullptr) {
218 ALOGE("%s: Cannot find the buffer to free!", __FUNCTION__);
219 return;
220 }
221
222 // TODO(jwcai) Someone from Android graphics team stating this should just be a
223 // static_cast.
224 AHardwareBuffer* outBuffer = reinterpret_cast<AHardwareBuffer*>(bufferToFree.get());
225
226 // At this point, bufferToFree holds the last reference to the GraphicBuffer owned by
227 // this AImageReader, and the reference will be gone once this function returns.
228 (*onBufferRemoved)(context, mReader, outBuffer);
229 break;
230 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800231 case kWhatImageAvailable:
232 {
233 AImageReader_ImageCallback onImageAvailable;
234 void* context;
235 bool found = msg->findPointer(kCallbackFpKey, (void**) &onImageAvailable);
236 if (!found || onImageAvailable == nullptr) {
237 ALOGE("%s: Cannot find onImageAvailable callback fp!", __FUNCTION__);
238 return;
239 }
240 found = msg->findPointer(kContextKey, &context);
241 if (!found) {
242 ALOGE("%s: Cannot find callback context!", __FUNCTION__);
243 return;
244 }
245 (*onImageAvailable)(context, mReader);
246 break;
247 }
248 default:
249 ALOGE("%s: unknown message type %d", __FUNCTION__, msg->what());
250 break;
251 }
252}
253
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800254AImageReader::AImageReader(int32_t width,
255 int32_t height,
256 int32_t format,
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700257 uint64_t usage,
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800258 int32_t maxImages)
259 : mWidth(width),
260 mHeight(height),
261 mFormat(format),
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700262 mUsage(usage),
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800263 mMaxImages(maxImages),
264 mNumPlanes(getNumPlanesForFormat(format)),
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800265 mFrameListener(new FrameListener(this)),
266 mBufferRemovedListener(new BufferRemovedListener(this)) {}
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800267
268media_status_t
269AImageReader::init() {
270 PublicFormat publicFormat = static_cast<PublicFormat>(mFormat);
271 mHalFormat = android_view_Surface_mapPublicFormatToHalFormat(publicFormat);
272 mHalDataSpace = android_view_Surface_mapPublicFormatToHalDataspace(publicFormat);
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700273 mHalUsage = android_hardware_HardwareBuffer_convertToGrallocUsageBits(mUsage);
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800274
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800275 sp<IGraphicBufferProducer> gbProducer;
276 sp<IGraphicBufferConsumer> gbConsumer;
277 BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
278
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700279 String8 consumerName = String8::format("ImageReader-%dx%df%xu%" PRIu64 "m%d-%d-%d",
280 mWidth, mHeight, mFormat, mUsage, mMaxImages, getpid(),
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800281 createProcessUniqueId());
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800282
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800283 mBufferItemConsumer =
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800284 new BufferItemConsumer(gbConsumer, mHalUsage, mMaxImages, /*controlledByApp*/ true);
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800285 if (mBufferItemConsumer == nullptr) {
286 ALOGE("Failed to allocate BufferItemConsumer");
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800287 return AMEDIA_ERROR_UNKNOWN;
288 }
289
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800290 mProducer = gbProducer;
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800291 mBufferItemConsumer->setName(consumerName);
292 mBufferItemConsumer->setFrameAvailableListener(mFrameListener);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800293 mBufferItemConsumer->setBufferFreedListener(mBufferRemovedListener);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800294
295 status_t res;
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800296 res = mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800297 if (res != OK) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800298 ALOGE("Failed to set BufferItemConsumer buffer size");
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800299 return AMEDIA_ERROR_UNKNOWN;
300 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800301 res = mBufferItemConsumer->setDefaultBufferFormat(mHalFormat);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800302 if (res != OK) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800303 ALOGE("Failed to set BufferItemConsumer buffer format");
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800304 return AMEDIA_ERROR_UNKNOWN;
305 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800306 res = mBufferItemConsumer->setDefaultBufferDataSpace(mHalDataSpace);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800307 if (res != OK) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800308 ALOGE("Failed to set BufferItemConsumer buffer dataSpace");
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800309 return AMEDIA_ERROR_UNKNOWN;
310 }
311
312 mSurface = new Surface(mProducer, /*controlledByApp*/true);
313 if (mSurface == nullptr) {
314 ALOGE("Failed to create surface");
315 return AMEDIA_ERROR_UNKNOWN;
316 }
317 mWindow = static_cast<ANativeWindow*>(mSurface.get());
318
319 for (int i = 0; i < mMaxImages; i++) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800320 BufferItem* buffer = new BufferItem;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800321 mBuffers.push_back(buffer);
322 }
323
324 mCbLooper = new ALooper;
325 mCbLooper->setName(consumerName.string());
Aurimas Liutikas214c8332016-02-19 14:48:23 -0800326 res = mCbLooper->start(
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800327 /*runOnCallingThread*/false,
328 /*canCallJava*/ true,
329 PRIORITY_DEFAULT);
Aurimas Liutikas214c8332016-02-19 14:48:23 -0800330 if (res != OK) {
331 ALOGE("Failed to start the looper");
332 return AMEDIA_ERROR_UNKNOWN;
333 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800334 mHandler = new CallbackHandler(this);
335 mCbLooper->registerHandler(mHandler);
336
337 return AMEDIA_OK;
338}
339
340AImageReader::~AImageReader() {
341 Mutex::Autolock _l(mLock);
342 AImageReader_ImageListener nullListener = {nullptr, nullptr};
343 setImageListenerLocked(&nullListener);
344
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800345 AImageReader_BufferRemovedListener nullBufferRemovedListener = {nullptr, nullptr};
346 setBufferRemovedListenerLocked(&nullBufferRemovedListener);
347
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800348 if (mCbLooper != nullptr) {
349 mCbLooper->unregisterHandler(mHandler->id());
350 mCbLooper->stop();
351 }
352 mCbLooper.clear();
353 mHandler.clear();
354
355 // Close all previously acquired images
356 for (auto it = mAcquiredImages.begin();
357 it != mAcquiredImages.end(); it++) {
358 AImage* image = *it;
Yin-Chia Yehb29fce72017-11-06 17:16:22 -0800359 Mutex::Autolock _l(image->mLock);
Yin-Chia Yeh55fc8e72017-09-19 17:28:25 -0700360 releaseImageLocked(image, /*releaseFenceFd*/-1);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800361 }
362
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800363 // Delete Buffer Items
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800364 for (auto it = mBuffers.begin();
365 it != mBuffers.end(); it++) {
366 delete *it;
367 }
368
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800369 if (mBufferItemConsumer != nullptr) {
370 mBufferItemConsumer->abandon();
371 mBufferItemConsumer->setFrameAvailableListener(nullptr);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800372 }
Jayant Chowdhary249e1f22018-09-24 15:07:45 -0700373
374 if (mWindowHandle != nullptr) {
375 int size = mWindowHandle->data[0];
376 hidl_vec<uint8_t> halToken;
377 halToken.setToExternal(
378 reinterpret_cast<uint8_t *>(&mWindowHandle->data[1]), size);
379 deleteHalToken(halToken);
380 native_handle_delete(mWindowHandle);
381 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800382}
383
384media_status_t
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800385AImageReader::acquireImageLocked(/*out*/AImage** image, /*out*/int* acquireFenceFd) {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800386 *image = nullptr;
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800387 BufferItem* buffer = getBufferItemLocked();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800388 if (buffer == nullptr) {
389 ALOGW("Unable to acquire a lockedBuffer, very likely client tries to lock more than"
390 " maxImages buffers");
391 return AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED;
392 }
393
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800394 // When the output paramter fence is not NULL, we are acquiring the image asynchronously.
395 bool waitForFence = acquireFenceFd == nullptr;
396 status_t res = mBufferItemConsumer->acquireBuffer(buffer, 0, waitForFence);
397
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800398 if (res != NO_ERROR) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800399 returnBufferItemLocked(buffer);
400 if (res != BufferQueue::NO_BUFFER_AVAILABLE) {
401 if (res == INVALID_OPERATION) {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800402 return AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED;
403 } else {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800404 ALOGE("%s: Acquire image failed with some unknown error: %s (%d)",
405 __FUNCTION__, strerror(-res), res);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800406 return AMEDIA_ERROR_UNKNOWN;
407 }
408 }
409 return AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE;
410 }
411
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800412 const int bufferWidth = getBufferWidth(buffer);
413 const int bufferHeight = getBufferHeight(buffer);
414 const int bufferFmt = buffer->mGraphicBuffer->getPixelFormat();
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800415 const int bufferUsage = buffer->mGraphicBuffer->getUsage();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800416
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800417 const int readerWidth = mWidth;
418 const int readerHeight = mHeight;
419 const int readerFmt = mHalFormat;
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800420 const int readerUsage = mHalUsage;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800421
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800422 // Check if the producer buffer configurations match what AImageReader configured. Add some
423 // extra checks for non-opaque formats.
424 if (!isFormatOpaque(readerFmt)) {
425 // Check if the left-top corner of the crop rect is origin, we currently assume this point
426 // is zero, will revisit this once this assumption turns out problematic.
427 Point lt = buffer->mCrop.leftTop();
428 if (lt.x != 0 || lt.y != 0) {
429 ALOGE("Crop left top corner [%d, %d] not at origin", lt.x, lt.y);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800430 return AMEDIA_ERROR_UNKNOWN;
431 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800432
433 // Check if the producer buffer configurations match what ImageReader configured.
Jiwen 'Steve' Cai755ca9b2017-03-31 16:59:50 -0700434 ALOGV_IF(readerWidth != bufferWidth || readerHeight != bufferHeight,
435 "%s: Buffer size: %dx%d, doesn't match AImageReader configured size: %dx%d",
436 __FUNCTION__, bufferWidth, bufferHeight, readerWidth, readerHeight);
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800437
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800438 // Check if the buffer usage is a super set of reader's usage bits, aka all usage bits that
439 // ImageReader requested has been supported from the producer side.
440 ALOGD_IF((readerUsage | bufferUsage) != bufferUsage,
441 "%s: Producer buffer usage: %x, doesn't cover all usage bits AImageReader "
442 "configured: %x",
443 __FUNCTION__, bufferUsage, readerUsage);
444
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800445 if (readerFmt != bufferFmt) {
446 if (readerFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && isPossiblyYUV(bufferFmt)) {
447 // Special casing for when producer switches to a format compatible with flexible
448 // YUV.
449 mHalFormat = bufferFmt;
450 ALOGD("%s: Overriding buffer format YUV_420_888 to 0x%x.", __FUNCTION__, bufferFmt);
451 } else {
452 // Return the buffer to the queue. No need to provide fence, as this buffer wasn't
453 // used anywhere yet.
454 mBufferItemConsumer->releaseBuffer(*buffer);
455 returnBufferItemLocked(buffer);
456
457 ALOGE("%s: Output buffer format: 0x%x, ImageReader configured format: 0x%x",
458 __FUNCTION__, bufferFmt, readerFmt);
459
460 return AMEDIA_ERROR_UNKNOWN;
461 }
462 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800463 }
464
465 if (mHalFormat == HAL_PIXEL_FORMAT_BLOB) {
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700466 *image = new AImage(this, mFormat, mUsage, buffer, buffer->mTimestamp,
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800467 readerWidth, readerHeight, mNumPlanes);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800468 } else {
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700469 *image = new AImage(this, mFormat, mUsage, buffer, buffer->mTimestamp,
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800470 bufferWidth, bufferHeight, mNumPlanes);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800471 }
472 mAcquiredImages.push_back(*image);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800473
474 // When the output paramter fence is not NULL, we are acquiring the image asynchronously.
475 if (acquireFenceFd != nullptr) {
476 *acquireFenceFd = buffer->mFence->dup();
477 }
478
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800479 return AMEDIA_OK;
480}
481
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800482BufferItem*
483AImageReader::getBufferItemLocked() {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800484 if (mBuffers.empty()) {
485 return nullptr;
486 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800487 // Return a BufferItem pointer and remove it from the list
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800488 auto it = mBuffers.begin();
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800489 BufferItem* buffer = *it;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800490 mBuffers.erase(it);
491 return buffer;
492}
493
494void
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800495AImageReader::returnBufferItemLocked(BufferItem* buffer) {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800496 mBuffers.push_back(buffer);
497}
498
499void
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800500AImageReader::releaseImageLocked(AImage* image, int releaseFenceFd) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800501 BufferItem* buffer = image->mBuffer;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800502 if (buffer == nullptr) {
503 // This should not happen, but is not fatal
504 ALOGW("AImage %p has no buffer!", image);
505 return;
506 }
507
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800508 int unlockFenceFd = -1;
509 media_status_t ret = image->unlockImageIfLocked(&unlockFenceFd);
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800510 if (ret < 0) {
511 ALOGW("%s: AImage %p is cannot be unlocked.", __FUNCTION__, image);
512 return;
513 }
514
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800515 sp<Fence> unlockFence = unlockFenceFd > 0 ? new Fence(unlockFenceFd) : Fence::NO_FENCE;
516 sp<Fence> releaseFence = releaseFenceFd > 0 ? new Fence(releaseFenceFd) : Fence::NO_FENCE;
517 sp<Fence> bufferFence = Fence::merge("AImageReader", unlockFence, releaseFence);
518 mBufferItemConsumer->releaseBuffer(*buffer, bufferFence);
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800519 returnBufferItemLocked(buffer);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800520 image->mBuffer = nullptr;
Yin-Chia Yehb29fce72017-11-06 17:16:22 -0800521 image->mLockedBuffer = nullptr;
522 image->mIsClosed = true;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800523
524 bool found = false;
525 // cleanup acquired image list
526 for (auto it = mAcquiredImages.begin();
527 it != mAcquiredImages.end(); it++) {
528 AImage* readerCopy = *it;
529 if (readerCopy == image) {
530 found = true;
531 mAcquiredImages.erase(it);
532 break;
533 }
534 }
535 if (!found) {
536 ALOGE("Error: AImage %p is not generated by AImageReader %p",
537 image, this);
538 }
539}
540
Jayant Chowdhary249e1f22018-09-24 15:07:45 -0700541media_status_t AImageReader::getWindowNativeHandle(native_handle **handle) {
542 if (mWindowHandle != nullptr) {
543 *handle = mWindowHandle;
544 return AMEDIA_OK;
545 }
546 sp<HGraphicBufferProducer> hgbp =
547 new TWGraphicBufferProducer<HGraphicBufferProducer>(mProducer);
548 HalToken halToken;
549 if (!createHalToken(hgbp, &halToken)) {
550 return AMEDIA_ERROR_UNKNOWN;
551 }
552 mWindowHandle = convertHalTokenToNativeHandle(halToken);
553 if (!mWindowHandle) {
554 return AMEDIA_ERROR_UNKNOWN;
555 }
556 *handle = mWindowHandle;
557 return AMEDIA_OK;
558}
559
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800560int
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800561AImageReader::getBufferWidth(BufferItem* buffer) {
562 if (buffer == NULL) return -1;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800563
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800564 if (!buffer->mCrop.isEmpty()) {
565 return buffer->mCrop.getWidth();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800566 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800567
568 return buffer->mGraphicBuffer->getWidth();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800569}
570
571int
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800572AImageReader::getBufferHeight(BufferItem* buffer) {
573 if (buffer == NULL) return -1;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800574
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800575 if (!buffer->mCrop.isEmpty()) {
576 return buffer->mCrop.getHeight();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800577 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800578
579 return buffer->mGraphicBuffer->getHeight();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800580}
581
582media_status_t
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800583AImageReader::acquireNextImage(/*out*/AImage** image, /*out*/int* acquireFenceFd) {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800584 Mutex::Autolock _l(mLock);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800585 return acquireImageLocked(image, acquireFenceFd);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800586}
587
588media_status_t
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800589AImageReader::acquireLatestImage(/*out*/AImage** image, /*out*/int* acquireFenceFd) {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800590 if (image == nullptr) {
591 return AMEDIA_ERROR_INVALID_PARAMETER;
592 }
593 Mutex::Autolock _l(mLock);
594 *image = nullptr;
595 AImage* prevImage = nullptr;
596 AImage* nextImage = nullptr;
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800597 media_status_t ret = acquireImageLocked(&prevImage, acquireFenceFd);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800598 if (prevImage == nullptr) {
599 return ret;
600 }
601 for (;;) {
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800602 ret = acquireImageLocked(&nextImage, acquireFenceFd);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800603 if (nextImage == nullptr) {
604 *image = prevImage;
605 return AMEDIA_OK;
606 }
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800607
608 if (acquireFenceFd == nullptr) {
609 // No need for release fence here since the prevImage is unused and acquireImageLocked
610 // has already waited for acquired fence to be signaled.
611 prevImage->close();
612 } else {
613 // Use the acquire fence as release fence, so that producer can wait before trying to
614 // refill the buffer.
615 prevImage->close(*acquireFenceFd);
616 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800617 prevImage->free();
618 prevImage = nextImage;
619 nextImage = nullptr;
620 }
621}
622
Jayant Chowdhary249e1f22018-09-24 15:07:45 -0700623static native_handle_t *convertHalTokenToNativeHandle(
624 const HalToken &halToken) {
625 // We attempt to store halToken in the ints of the native_handle_t after its
626 // size. The first int stores the size of the token. We store this in an int
627 // to avoid alignment issues where size_t and int do not have the same
628 // alignment.
629 size_t nhDataByteSize = halToken.size();
630 if (nhDataByteSize > kWindowHalTokenSizeMax) {
631 // The size of the token isn't reasonable..
632 return nullptr;
633 }
634 size_t numInts = ceil(nhDataByteSize / sizeof(int)) + 1;
635
636 // We don't check for overflow, whether numInts can fit in an int, since we
637 // expect kWindowHalTokenSizeMax to be a reasonable limit.
638 // create a native_handle_t with 0 numFds and numInts number of ints.
639 native_handle_t *nh =
640 native_handle_create(0, numInts);
641 if (!nh) {
642 return nullptr;
643 }
644 // Store the size of the token in the first int.
645 nh->data[0] = nhDataByteSize;
646 memcpy(&(nh->data[1]), halToken.data(), nhDataByteSize);
647 return nh;
648}
649
650static sp<HGraphicBufferProducer> convertNativeHandleToHGBP (
651 const native_handle_t *handle) {
652 // Read the size of the halToken vec<uint8_t>
653 hidl_vec<uint8_t> halToken;
654 halToken.setToExternal(
655 reinterpret_cast<uint8_t *>(const_cast<int *>(&(handle->data[1]))),
656 handle->data[0]);
657 sp<HGraphicBufferProducer> hgbp =
658 HGraphicBufferProducer::castFrom(retrieveHalInterface(halToken));
659 return hgbp;
660}
661
662EXPORT
663sp<HGraphicBufferProducer> AImageReader_getHGBPFromHandle(
664 const native_handle_t *handle) {
665 if (handle == nullptr) {
666 return nullptr;
667 }
668 if (handle->numFds != 0 ||
669 handle->numInts < ceil(sizeof(size_t) / sizeof(int))) {
670 return nullptr;
671 }
672 return convertNativeHandleToHGBP(handle);
673}
674
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800675EXPORT
676media_status_t AImageReader_new(
677 int32_t width, int32_t height, int32_t format, int32_t maxImages,
678 /*out*/AImageReader** reader) {
679 ALOGV("%s", __FUNCTION__);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800680 return AImageReader_newWithUsage(
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700681 width, height, format, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, maxImages, reader);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800682}
683
Jayant Chowdhary249e1f22018-09-24 15:07:45 -0700684extern "C" {
685
686EXPORT
687media_status_t AImageReader_getWindowNativeHandle(
688 AImageReader *reader, /*out*/native_handle_t **handle) {
689 if (reader == nullptr || handle == nullptr) {
690 return AMEDIA_ERROR_INVALID_PARAMETER;
691 }
692 return reader->getWindowNativeHandle(handle);
693}
694
695} //extern "C"
696
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800697EXPORT
698media_status_t AImageReader_newWithUsage(
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700699 int32_t width, int32_t height, int32_t format, uint64_t usage,
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800700 int32_t maxImages, /*out*/ AImageReader** reader) {
701 ALOGV("%s", __FUNCTION__);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800702
703 if (width < 1 || height < 1) {
704 ALOGE("%s: image dimension must be positive: w:%d h:%d",
705 __FUNCTION__, width, height);
706 return AMEDIA_ERROR_INVALID_PARAMETER;
707 }
708
709 if (maxImages < 1) {
710 ALOGE("%s: max outstanding image count must be at least 1 (%d)",
711 __FUNCTION__, maxImages);
712 return AMEDIA_ERROR_INVALID_PARAMETER;
713 }
714
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800715 if (maxImages > BufferQueueDefs::NUM_BUFFER_SLOTS) {
716 ALOGE("%s: max outstanding image count (%d) cannot be larget than %d.",
717 __FUNCTION__, maxImages, BufferQueueDefs::NUM_BUFFER_SLOTS);
718 return AMEDIA_ERROR_INVALID_PARAMETER;
719 }
720
Jiwen 'Steve' Cai55ec2562017-04-12 15:56:41 -0700721 if (!AImageReader::isSupportedFormatAndUsage(format, usage)) {
722 ALOGE("%s: format %d is not supported with usage 0x%" PRIx64 " by AImageReader",
723 __FUNCTION__, format, usage);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800724 return AMEDIA_ERROR_INVALID_PARAMETER;
725 }
726
727 if (reader == nullptr) {
728 ALOGE("%s: reader argument is null", __FUNCTION__);
729 return AMEDIA_ERROR_INVALID_PARAMETER;
730 }
731
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800732 AImageReader* tmpReader = new AImageReader(
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700733 width, height, format, usage, maxImages);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800734 if (tmpReader == nullptr) {
735 ALOGE("%s: AImageReader allocation failed", __FUNCTION__);
736 return AMEDIA_ERROR_UNKNOWN;
737 }
738 media_status_t ret = tmpReader->init();
739 if (ret != AMEDIA_OK) {
740 ALOGE("%s: AImageReader initialization failed!", __FUNCTION__);
741 delete tmpReader;
742 return ret;
743 }
744 *reader = tmpReader;
745 (*reader)->incStrong((void*) AImageReader_new);
746 return AMEDIA_OK;
747}
748
749EXPORT
750void AImageReader_delete(AImageReader* reader) {
751 ALOGV("%s", __FUNCTION__);
752 if (reader != nullptr) {
753 reader->decStrong((void*) AImageReader_delete);
754 }
755 return;
756}
757
758EXPORT
759media_status_t AImageReader_getWindow(AImageReader* reader, /*out*/ANativeWindow** window) {
Yin-Chia Yehe2ded212017-11-08 15:51:02 -0800760 ALOGV("%s", __FUNCTION__);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800761 if (reader == nullptr || window == nullptr) {
762 ALOGE("%s: invalid argument. reader %p, window %p",
763 __FUNCTION__, reader, window);
764 return AMEDIA_ERROR_INVALID_PARAMETER;
765 }
766 *window = reader->getWindow();
767 return AMEDIA_OK;
768}
769
770EXPORT
771media_status_t AImageReader_getWidth(const AImageReader* reader, /*out*/int32_t* width) {
772 ALOGV("%s", __FUNCTION__);
773 if (reader == nullptr || width == nullptr) {
774 ALOGE("%s: invalid argument. reader %p, width %p",
775 __FUNCTION__, reader, width);
776 return AMEDIA_ERROR_INVALID_PARAMETER;
777 }
778 *width = reader->getWidth();
779 return AMEDIA_OK;
780}
781
782EXPORT
783media_status_t AImageReader_getHeight(const AImageReader* reader, /*out*/int32_t* height) {
784 ALOGV("%s", __FUNCTION__);
785 if (reader == nullptr || height == nullptr) {
786 ALOGE("%s: invalid argument. reader %p, height %p",
787 __FUNCTION__, reader, height);
788 return AMEDIA_ERROR_INVALID_PARAMETER;
789 }
790 *height = reader->getHeight();
791 return AMEDIA_OK;
792}
793
794EXPORT
795media_status_t AImageReader_getFormat(const AImageReader* reader, /*out*/int32_t* format) {
796 ALOGV("%s", __FUNCTION__);
797 if (reader == nullptr || format == nullptr) {
798 ALOGE("%s: invalid argument. reader %p, format %p",
799 __FUNCTION__, reader, format);
800 return AMEDIA_ERROR_INVALID_PARAMETER;
801 }
802 *format = reader->getFormat();
803 return AMEDIA_OK;
804}
805
806EXPORT
807media_status_t AImageReader_getMaxImages(const AImageReader* reader, /*out*/int32_t* maxImages) {
808 ALOGV("%s", __FUNCTION__);
809 if (reader == nullptr || maxImages == nullptr) {
810 ALOGE("%s: invalid argument. reader %p, maxImages %p",
811 __FUNCTION__, reader, maxImages);
812 return AMEDIA_ERROR_INVALID_PARAMETER;
813 }
814 *maxImages = reader->getMaxImages();
815 return AMEDIA_OK;
816}
817
818EXPORT
819media_status_t AImageReader_acquireNextImage(AImageReader* reader, /*out*/AImage** image) {
820 ALOGV("%s", __FUNCTION__);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800821 return AImageReader_acquireNextImageAsync(reader, image, nullptr);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800822}
823
824EXPORT
825media_status_t AImageReader_acquireLatestImage(AImageReader* reader, /*out*/AImage** image) {
826 ALOGV("%s", __FUNCTION__);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800827 return AImageReader_acquireLatestImageAsync(reader, image, nullptr);
828}
829
830EXPORT
831media_status_t AImageReader_acquireNextImageAsync(
832 AImageReader* reader, /*out*/AImage** image, /*out*/int* acquireFenceFd) {
833 ALOGV("%s", __FUNCTION__);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800834 if (reader == nullptr || image == nullptr) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800835 ALOGE("%s: invalid argument. reader %p, image %p",
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800836 __FUNCTION__, reader, image);
837 return AMEDIA_ERROR_INVALID_PARAMETER;
838 }
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800839 return reader->acquireNextImage(image, acquireFenceFd);
840}
841
842EXPORT
843media_status_t AImageReader_acquireLatestImageAsync(
844 AImageReader* reader, /*out*/AImage** image, /*out*/int* acquireFenceFd) {
845 ALOGV("%s", __FUNCTION__);
846 if (reader == nullptr || image == nullptr) {
847 ALOGE("%s: invalid argument. reader %p, image %p",
848 __FUNCTION__, reader, image);
849 return AMEDIA_ERROR_INVALID_PARAMETER;
850 }
851 return reader->acquireLatestImage(image, acquireFenceFd);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800852}
853
854EXPORT
855media_status_t AImageReader_setImageListener(
856 AImageReader* reader, AImageReader_ImageListener* listener) {
857 ALOGV("%s", __FUNCTION__);
Yin-Chia Yeh1d0955c2016-05-16 01:14:13 -0700858 if (reader == nullptr) {
859 ALOGE("%s: invalid argument! reader %p", __FUNCTION__, reader);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800860 return AMEDIA_ERROR_INVALID_PARAMETER;
861 }
862
863 reader->setImageListener(listener);
864 return AMEDIA_OK;
865}
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800866
867EXPORT
868media_status_t AImageReader_setBufferRemovedListener(
869 AImageReader* reader, AImageReader_BufferRemovedListener* listener) {
870 ALOGV("%s", __FUNCTION__);
871 if (reader == nullptr) {
872 ALOGE("%s: invalid argument! reader %p", __FUNCTION__, reader);
873 return AMEDIA_ERROR_INVALID_PARAMETER;
874 }
875
876 reader->setBufferRemovedListener(listener);
877 return AMEDIA_OK;
878}