blob: c3eb4379dba08137a37f47b3c8141e7516387655 [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:
Shuzhen Wang68ac7ad2019-01-30 14:03:28 -080072 case AIMAGE_FORMAT_HEIC:
Yin-Chia Yehc3603822016-01-18 22:11:19 -080073 return true;
Jiwen 'Steve' Cai55ec2562017-04-12 15:56:41 -070074 case AIMAGE_FORMAT_PRIVATE:
75 // For private format, cpu usage is prohibited.
76 return !hasCpuUsage;
Yin-Chia Yehc3603822016-01-18 22:11:19 -080077 default:
78 return false;
79 }
80}
81
82int
83AImageReader::getNumPlanesForFormat(int32_t format) {
84 switch (format) {
85 case AIMAGE_FORMAT_YUV_420_888:
86 return 3;
Jiwen 'Steve' Caide2a5442017-02-08 14:41:41 -080087 case AIMAGE_FORMAT_RGBA_8888:
88 case AIMAGE_FORMAT_RGBX_8888:
89 case AIMAGE_FORMAT_RGB_888:
90 case AIMAGE_FORMAT_RGB_565:
91 case AIMAGE_FORMAT_RGBA_FP16:
Yin-Chia Yehc3603822016-01-18 22:11:19 -080092 case AIMAGE_FORMAT_JPEG:
93 case AIMAGE_FORMAT_RAW16:
94 case AIMAGE_FORMAT_RAW_PRIVATE:
95 case AIMAGE_FORMAT_RAW10:
96 case AIMAGE_FORMAT_RAW12:
97 case AIMAGE_FORMAT_DEPTH16:
98 case AIMAGE_FORMAT_DEPTH_POINT_CLOUD:
Shuzhen Wang1cfbbec2018-10-08 13:55:28 -070099 case AIMAGE_FORMAT_Y8:
Shuzhen Wang68ac7ad2019-01-30 14:03:28 -0800100 case AIMAGE_FORMAT_HEIC:
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800101 return 1;
Jiwen 'Steve' Cai55ec2562017-04-12 15:56:41 -0700102 case AIMAGE_FORMAT_PRIVATE:
103 return 0;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800104 default:
105 return -1;
106 }
107}
108
109void
110AImageReader::FrameListener::onFrameAvailable(const BufferItem& /*item*/) {
111 Mutex::Autolock _l(mLock);
112 sp<AImageReader> reader = mReader.promote();
113 if (reader == nullptr) {
114 ALOGW("A frame is available after AImageReader closed!");
115 return; // reader has been closed
116 }
117 if (mListener.onImageAvailable == nullptr) {
118 return; // No callback registered
119 }
120
121 sp<AMessage> msg = new AMessage(AImageReader::kWhatImageAvailable, reader->mHandler);
122 msg->setPointer(AImageReader::kCallbackFpKey, (void *) mListener.onImageAvailable);
123 msg->setPointer(AImageReader::kContextKey, mListener.context);
124 msg->post();
125}
126
127media_status_t
128AImageReader::FrameListener::setImageListener(AImageReader_ImageListener* listener) {
129 Mutex::Autolock _l(mLock);
130 if (listener == nullptr) {
Yin-Chia Yeh1d0955c2016-05-16 01:14:13 -0700131 mListener.context = nullptr;
132 mListener.onImageAvailable = nullptr;
133 } else {
134 mListener = *listener;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800135 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800136 return AMEDIA_OK;
137}
138
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800139void
140AImageReader::BufferRemovedListener::onBufferFreed(const wp<GraphicBuffer>& graphicBuffer) {
141 Mutex::Autolock _l(mLock);
142 sp<AImageReader> reader = mReader.promote();
143 if (reader == nullptr) {
144 ALOGW("A frame is available after AImageReader closed!");
145 return; // reader has been closed
146 }
147 if (mListener.onBufferRemoved == nullptr) {
148 return; // No callback registered
149 }
150
151 sp<GraphicBuffer> gBuffer = graphicBuffer.promote();
152 if (gBuffer == nullptr) {
153 ALOGW("A buffer being freed has gone away!");
154 return; // buffer is already destroyed
155 }
156
157 sp<AMessage> msg = new AMessage(AImageReader::kWhatBufferRemoved, reader->mHandler);
158 msg->setPointer(
159 AImageReader::kCallbackFpKey, (void*) mListener.onBufferRemoved);
160 msg->setPointer(AImageReader::kContextKey, mListener.context);
161 msg->setObject(AImageReader::kGraphicBufferKey, gBuffer);
162 msg->post();
163}
164
165media_status_t
166AImageReader::BufferRemovedListener::setBufferRemovedListener(
167 AImageReader_BufferRemovedListener* listener) {
168 Mutex::Autolock _l(mLock);
169 if (listener == nullptr) {
170 mListener.context = nullptr;
171 mListener.onBufferRemoved = nullptr;
172 } else {
173 mListener = *listener;
174 }
175 return AMEDIA_OK;
176}
177
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800178media_status_t
179AImageReader::setImageListenerLocked(AImageReader_ImageListener* listener) {
180 return mFrameListener->setImageListener(listener);
181}
182
183media_status_t
184AImageReader::setImageListener(AImageReader_ImageListener* listener) {
185 Mutex::Autolock _l(mLock);
186 return setImageListenerLocked(listener);
187}
188
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800189media_status_t
190AImageReader::setBufferRemovedListenerLocked(AImageReader_BufferRemovedListener* listener) {
191 return mBufferRemovedListener->setBufferRemovedListener(listener);
192}
193
194media_status_t
195AImageReader::setBufferRemovedListener(AImageReader_BufferRemovedListener* listener) {
196 Mutex::Autolock _l(mLock);
197 return setBufferRemovedListenerLocked(listener);
198}
199
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800200void AImageReader::CallbackHandler::onMessageReceived(
201 const sp<AMessage> &msg) {
202 switch (msg->what()) {
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800203 case kWhatBufferRemoved:
204 {
205 AImageReader_BufferRemovedCallback onBufferRemoved;
206 void* context;
207 bool found = msg->findPointer(kCallbackFpKey, (void**) &onBufferRemoved);
208 if (!found || onBufferRemoved == nullptr) {
209 ALOGE("%s: Cannot find onBufferRemoved callback fp!", __FUNCTION__);
210 return;
211 }
212 found = msg->findPointer(kContextKey, &context);
213 if (!found) {
214 ALOGE("%s: Cannot find callback context!", __FUNCTION__);
215 return;
216 }
217 sp<RefBase> bufferToFree;
218 found = msg->findObject(kGraphicBufferKey, &bufferToFree);
219 if (!found || bufferToFree == nullptr) {
220 ALOGE("%s: Cannot find the buffer to free!", __FUNCTION__);
221 return;
222 }
223
224 // TODO(jwcai) Someone from Android graphics team stating this should just be a
225 // static_cast.
226 AHardwareBuffer* outBuffer = reinterpret_cast<AHardwareBuffer*>(bufferToFree.get());
227
228 // At this point, bufferToFree holds the last reference to the GraphicBuffer owned by
229 // this AImageReader, and the reference will be gone once this function returns.
230 (*onBufferRemoved)(context, mReader, outBuffer);
231 break;
232 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800233 case kWhatImageAvailable:
234 {
235 AImageReader_ImageCallback onImageAvailable;
236 void* context;
237 bool found = msg->findPointer(kCallbackFpKey, (void**) &onImageAvailable);
238 if (!found || onImageAvailable == nullptr) {
239 ALOGE("%s: Cannot find onImageAvailable callback fp!", __FUNCTION__);
240 return;
241 }
242 found = msg->findPointer(kContextKey, &context);
243 if (!found) {
244 ALOGE("%s: Cannot find callback context!", __FUNCTION__);
245 return;
246 }
247 (*onImageAvailable)(context, mReader);
248 break;
249 }
250 default:
251 ALOGE("%s: unknown message type %d", __FUNCTION__, msg->what());
252 break;
253 }
254}
255
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800256AImageReader::AImageReader(int32_t width,
257 int32_t height,
258 int32_t format,
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700259 uint64_t usage,
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800260 int32_t maxImages)
261 : mWidth(width),
262 mHeight(height),
263 mFormat(format),
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700264 mUsage(usage),
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800265 mMaxImages(maxImages),
266 mNumPlanes(getNumPlanesForFormat(format)),
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800267 mFrameListener(new FrameListener(this)),
268 mBufferRemovedListener(new BufferRemovedListener(this)) {}
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800269
270media_status_t
271AImageReader::init() {
272 PublicFormat publicFormat = static_cast<PublicFormat>(mFormat);
273 mHalFormat = android_view_Surface_mapPublicFormatToHalFormat(publicFormat);
274 mHalDataSpace = android_view_Surface_mapPublicFormatToHalDataspace(publicFormat);
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700275 mHalUsage = android_hardware_HardwareBuffer_convertToGrallocUsageBits(mUsage);
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800276
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800277 sp<IGraphicBufferProducer> gbProducer;
278 sp<IGraphicBufferConsumer> gbConsumer;
279 BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
280
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700281 String8 consumerName = String8::format("ImageReader-%dx%df%xu%" PRIu64 "m%d-%d-%d",
282 mWidth, mHeight, mFormat, mUsage, mMaxImages, getpid(),
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800283 createProcessUniqueId());
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800284
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800285 mBufferItemConsumer =
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800286 new BufferItemConsumer(gbConsumer, mHalUsage, mMaxImages, /*controlledByApp*/ true);
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800287 if (mBufferItemConsumer == nullptr) {
288 ALOGE("Failed to allocate BufferItemConsumer");
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800289 return AMEDIA_ERROR_UNKNOWN;
290 }
291
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800292 mProducer = gbProducer;
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800293 mBufferItemConsumer->setName(consumerName);
294 mBufferItemConsumer->setFrameAvailableListener(mFrameListener);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800295 mBufferItemConsumer->setBufferFreedListener(mBufferRemovedListener);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800296
297 status_t res;
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800298 res = mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800299 if (res != OK) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800300 ALOGE("Failed to set BufferItemConsumer buffer size");
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800301 return AMEDIA_ERROR_UNKNOWN;
302 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800303 res = mBufferItemConsumer->setDefaultBufferFormat(mHalFormat);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800304 if (res != OK) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800305 ALOGE("Failed to set BufferItemConsumer buffer format");
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800306 return AMEDIA_ERROR_UNKNOWN;
307 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800308 res = mBufferItemConsumer->setDefaultBufferDataSpace(mHalDataSpace);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800309 if (res != OK) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800310 ALOGE("Failed to set BufferItemConsumer buffer dataSpace");
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800311 return AMEDIA_ERROR_UNKNOWN;
312 }
Khushalff20fd42019-01-22 15:31:00 -0800313 if (mUsage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
314 gbConsumer->setConsumerIsProtected(true);
315 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800316
317 mSurface = new Surface(mProducer, /*controlledByApp*/true);
318 if (mSurface == nullptr) {
319 ALOGE("Failed to create surface");
320 return AMEDIA_ERROR_UNKNOWN;
321 }
322 mWindow = static_cast<ANativeWindow*>(mSurface.get());
323
324 for (int i = 0; i < mMaxImages; i++) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800325 BufferItem* buffer = new BufferItem;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800326 mBuffers.push_back(buffer);
327 }
328
329 mCbLooper = new ALooper;
330 mCbLooper->setName(consumerName.string());
Aurimas Liutikas214c8332016-02-19 14:48:23 -0800331 res = mCbLooper->start(
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800332 /*runOnCallingThread*/false,
333 /*canCallJava*/ true,
334 PRIORITY_DEFAULT);
Aurimas Liutikas214c8332016-02-19 14:48:23 -0800335 if (res != OK) {
336 ALOGE("Failed to start the looper");
337 return AMEDIA_ERROR_UNKNOWN;
338 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800339 mHandler = new CallbackHandler(this);
340 mCbLooper->registerHandler(mHandler);
341
342 return AMEDIA_OK;
343}
344
345AImageReader::~AImageReader() {
346 Mutex::Autolock _l(mLock);
347 AImageReader_ImageListener nullListener = {nullptr, nullptr};
348 setImageListenerLocked(&nullListener);
349
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800350 AImageReader_BufferRemovedListener nullBufferRemovedListener = {nullptr, nullptr};
351 setBufferRemovedListenerLocked(&nullBufferRemovedListener);
352
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800353 if (mCbLooper != nullptr) {
354 mCbLooper->unregisterHandler(mHandler->id());
355 mCbLooper->stop();
356 }
357 mCbLooper.clear();
358 mHandler.clear();
359
360 // Close all previously acquired images
361 for (auto it = mAcquiredImages.begin();
362 it != mAcquiredImages.end(); it++) {
363 AImage* image = *it;
Yin-Chia Yehb29fce72017-11-06 17:16:22 -0800364 Mutex::Autolock _l(image->mLock);
Yin-Chia Yeh55baca22019-01-07 13:54:13 -0800365 // Do not alter mAcquiredImages while we are iterating on it
366 releaseImageLocked(image, /*releaseFenceFd*/-1, /*clearCache*/false);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800367 }
Yin-Chia Yeh55baca22019-01-07 13:54:13 -0800368 mAcquiredImages.clear();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800369
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800370 // Delete Buffer Items
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800371 for (auto it = mBuffers.begin();
372 it != mBuffers.end(); it++) {
373 delete *it;
374 }
375
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800376 if (mBufferItemConsumer != nullptr) {
377 mBufferItemConsumer->abandon();
378 mBufferItemConsumer->setFrameAvailableListener(nullptr);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800379 }
Jayant Chowdhary249e1f22018-09-24 15:07:45 -0700380
381 if (mWindowHandle != nullptr) {
382 int size = mWindowHandle->data[0];
383 hidl_vec<uint8_t> halToken;
384 halToken.setToExternal(
385 reinterpret_cast<uint8_t *>(&mWindowHandle->data[1]), size);
386 deleteHalToken(halToken);
387 native_handle_delete(mWindowHandle);
388 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800389}
390
391media_status_t
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800392AImageReader::acquireImageLocked(/*out*/AImage** image, /*out*/int* acquireFenceFd) {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800393 *image = nullptr;
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800394 BufferItem* buffer = getBufferItemLocked();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800395 if (buffer == nullptr) {
396 ALOGW("Unable to acquire a lockedBuffer, very likely client tries to lock more than"
397 " maxImages buffers");
398 return AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED;
399 }
400
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800401 // When the output paramter fence is not NULL, we are acquiring the image asynchronously.
402 bool waitForFence = acquireFenceFd == nullptr;
403 status_t res = mBufferItemConsumer->acquireBuffer(buffer, 0, waitForFence);
404
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800405 if (res != NO_ERROR) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800406 returnBufferItemLocked(buffer);
407 if (res != BufferQueue::NO_BUFFER_AVAILABLE) {
408 if (res == INVALID_OPERATION) {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800409 return AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED;
410 } else {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800411 ALOGE("%s: Acquire image failed with some unknown error: %s (%d)",
412 __FUNCTION__, strerror(-res), res);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800413 return AMEDIA_ERROR_UNKNOWN;
414 }
415 }
416 return AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE;
417 }
418
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800419 const int bufferWidth = getBufferWidth(buffer);
420 const int bufferHeight = getBufferHeight(buffer);
421 const int bufferFmt = buffer->mGraphicBuffer->getPixelFormat();
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800422 const int bufferUsage = buffer->mGraphicBuffer->getUsage();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800423
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800424 const int readerWidth = mWidth;
425 const int readerHeight = mHeight;
426 const int readerFmt = mHalFormat;
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800427 const int readerUsage = mHalUsage;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800428
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800429 // Check if the producer buffer configurations match what AImageReader configured. Add some
430 // extra checks for non-opaque formats.
431 if (!isFormatOpaque(readerFmt)) {
432 // Check if the left-top corner of the crop rect is origin, we currently assume this point
433 // is zero, will revisit this once this assumption turns out problematic.
434 Point lt = buffer->mCrop.leftTop();
435 if (lt.x != 0 || lt.y != 0) {
436 ALOGE("Crop left top corner [%d, %d] not at origin", lt.x, lt.y);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800437 return AMEDIA_ERROR_UNKNOWN;
438 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800439
440 // Check if the producer buffer configurations match what ImageReader configured.
Jiwen 'Steve' Cai755ca9b2017-03-31 16:59:50 -0700441 ALOGV_IF(readerWidth != bufferWidth || readerHeight != bufferHeight,
442 "%s: Buffer size: %dx%d, doesn't match AImageReader configured size: %dx%d",
443 __FUNCTION__, bufferWidth, bufferHeight, readerWidth, readerHeight);
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800444
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800445 // Check if the buffer usage is a super set of reader's usage bits, aka all usage bits that
446 // ImageReader requested has been supported from the producer side.
447 ALOGD_IF((readerUsage | bufferUsage) != bufferUsage,
448 "%s: Producer buffer usage: %x, doesn't cover all usage bits AImageReader "
449 "configured: %x",
450 __FUNCTION__, bufferUsage, readerUsage);
451
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800452 if (readerFmt != bufferFmt) {
453 if (readerFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && isPossiblyYUV(bufferFmt)) {
454 // Special casing for when producer switches to a format compatible with flexible
455 // YUV.
456 mHalFormat = bufferFmt;
457 ALOGD("%s: Overriding buffer format YUV_420_888 to 0x%x.", __FUNCTION__, bufferFmt);
458 } else {
459 // Return the buffer to the queue. No need to provide fence, as this buffer wasn't
460 // used anywhere yet.
461 mBufferItemConsumer->releaseBuffer(*buffer);
462 returnBufferItemLocked(buffer);
463
464 ALOGE("%s: Output buffer format: 0x%x, ImageReader configured format: 0x%x",
465 __FUNCTION__, bufferFmt, readerFmt);
466
467 return AMEDIA_ERROR_UNKNOWN;
468 }
469 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800470 }
471
472 if (mHalFormat == HAL_PIXEL_FORMAT_BLOB) {
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700473 *image = new AImage(this, mFormat, mUsage, buffer, buffer->mTimestamp,
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800474 readerWidth, readerHeight, mNumPlanes);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800475 } else {
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700476 *image = new AImage(this, mFormat, mUsage, buffer, buffer->mTimestamp,
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800477 bufferWidth, bufferHeight, mNumPlanes);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800478 }
479 mAcquiredImages.push_back(*image);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800480
481 // When the output paramter fence is not NULL, we are acquiring the image asynchronously.
482 if (acquireFenceFd != nullptr) {
483 *acquireFenceFd = buffer->mFence->dup();
484 }
485
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800486 return AMEDIA_OK;
487}
488
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800489BufferItem*
490AImageReader::getBufferItemLocked() {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800491 if (mBuffers.empty()) {
492 return nullptr;
493 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800494 // Return a BufferItem pointer and remove it from the list
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800495 auto it = mBuffers.begin();
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800496 BufferItem* buffer = *it;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800497 mBuffers.erase(it);
498 return buffer;
499}
500
501void
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800502AImageReader::returnBufferItemLocked(BufferItem* buffer) {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800503 mBuffers.push_back(buffer);
504}
505
506void
Yin-Chia Yeh55baca22019-01-07 13:54:13 -0800507AImageReader::releaseImageLocked(AImage* image, int releaseFenceFd, bool clearCache) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800508 BufferItem* buffer = image->mBuffer;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800509 if (buffer == nullptr) {
510 // This should not happen, but is not fatal
511 ALOGW("AImage %p has no buffer!", image);
512 return;
513 }
514
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800515 int unlockFenceFd = -1;
516 media_status_t ret = image->unlockImageIfLocked(&unlockFenceFd);
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800517 if (ret < 0) {
518 ALOGW("%s: AImage %p is cannot be unlocked.", __FUNCTION__, image);
519 return;
520 }
521
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800522 sp<Fence> unlockFence = unlockFenceFd > 0 ? new Fence(unlockFenceFd) : Fence::NO_FENCE;
523 sp<Fence> releaseFence = releaseFenceFd > 0 ? new Fence(releaseFenceFd) : Fence::NO_FENCE;
524 sp<Fence> bufferFence = Fence::merge("AImageReader", unlockFence, releaseFence);
525 mBufferItemConsumer->releaseBuffer(*buffer, bufferFence);
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800526 returnBufferItemLocked(buffer);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800527 image->mBuffer = nullptr;
Yin-Chia Yehb29fce72017-11-06 17:16:22 -0800528 image->mLockedBuffer = nullptr;
529 image->mIsClosed = true;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800530
Yin-Chia Yeh55baca22019-01-07 13:54:13 -0800531 if (!clearCache) {
532 return;
533 }
534
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800535 bool found = false;
536 // cleanup acquired image list
537 for (auto it = mAcquiredImages.begin();
538 it != mAcquiredImages.end(); it++) {
539 AImage* readerCopy = *it;
540 if (readerCopy == image) {
541 found = true;
542 mAcquiredImages.erase(it);
543 break;
544 }
545 }
546 if (!found) {
547 ALOGE("Error: AImage %p is not generated by AImageReader %p",
548 image, this);
549 }
550}
551
Jayant Chowdhary249e1f22018-09-24 15:07:45 -0700552media_status_t AImageReader::getWindowNativeHandle(native_handle **handle) {
553 if (mWindowHandle != nullptr) {
554 *handle = mWindowHandle;
555 return AMEDIA_OK;
556 }
557 sp<HGraphicBufferProducer> hgbp =
558 new TWGraphicBufferProducer<HGraphicBufferProducer>(mProducer);
559 HalToken halToken;
560 if (!createHalToken(hgbp, &halToken)) {
561 return AMEDIA_ERROR_UNKNOWN;
562 }
563 mWindowHandle = convertHalTokenToNativeHandle(halToken);
564 if (!mWindowHandle) {
565 return AMEDIA_ERROR_UNKNOWN;
566 }
567 *handle = mWindowHandle;
568 return AMEDIA_OK;
569}
570
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800571int
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800572AImageReader::getBufferWidth(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.getWidth();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800577 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800578
579 return buffer->mGraphicBuffer->getWidth();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800580}
581
582int
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800583AImageReader::getBufferHeight(BufferItem* buffer) {
584 if (buffer == NULL) return -1;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800585
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800586 if (!buffer->mCrop.isEmpty()) {
587 return buffer->mCrop.getHeight();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800588 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800589
590 return buffer->mGraphicBuffer->getHeight();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800591}
592
593media_status_t
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800594AImageReader::acquireNextImage(/*out*/AImage** image, /*out*/int* acquireFenceFd) {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800595 Mutex::Autolock _l(mLock);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800596 return acquireImageLocked(image, acquireFenceFd);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800597}
598
599media_status_t
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800600AImageReader::acquireLatestImage(/*out*/AImage** image, /*out*/int* acquireFenceFd) {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800601 if (image == nullptr) {
602 return AMEDIA_ERROR_INVALID_PARAMETER;
603 }
604 Mutex::Autolock _l(mLock);
605 *image = nullptr;
606 AImage* prevImage = nullptr;
607 AImage* nextImage = nullptr;
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800608 media_status_t ret = acquireImageLocked(&prevImage, acquireFenceFd);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800609 if (prevImage == nullptr) {
610 return ret;
611 }
612 for (;;) {
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800613 ret = acquireImageLocked(&nextImage, acquireFenceFd);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800614 if (nextImage == nullptr) {
615 *image = prevImage;
616 return AMEDIA_OK;
617 }
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800618
619 if (acquireFenceFd == nullptr) {
620 // No need for release fence here since the prevImage is unused and acquireImageLocked
621 // has already waited for acquired fence to be signaled.
622 prevImage->close();
623 } else {
624 // Use the acquire fence as release fence, so that producer can wait before trying to
625 // refill the buffer.
626 prevImage->close(*acquireFenceFd);
627 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800628 prevImage->free();
629 prevImage = nextImage;
630 nextImage = nullptr;
631 }
632}
633
Jayant Chowdhary249e1f22018-09-24 15:07:45 -0700634static native_handle_t *convertHalTokenToNativeHandle(
635 const HalToken &halToken) {
636 // We attempt to store halToken in the ints of the native_handle_t after its
637 // size. The first int stores the size of the token. We store this in an int
638 // to avoid alignment issues where size_t and int do not have the same
639 // alignment.
640 size_t nhDataByteSize = halToken.size();
641 if (nhDataByteSize > kWindowHalTokenSizeMax) {
642 // The size of the token isn't reasonable..
643 return nullptr;
644 }
645 size_t numInts = ceil(nhDataByteSize / sizeof(int)) + 1;
646
647 // We don't check for overflow, whether numInts can fit in an int, since we
648 // expect kWindowHalTokenSizeMax to be a reasonable limit.
649 // create a native_handle_t with 0 numFds and numInts number of ints.
650 native_handle_t *nh =
651 native_handle_create(0, numInts);
652 if (!nh) {
653 return nullptr;
654 }
655 // Store the size of the token in the first int.
656 nh->data[0] = nhDataByteSize;
657 memcpy(&(nh->data[1]), halToken.data(), nhDataByteSize);
658 return nh;
659}
660
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800661EXPORT
662media_status_t AImageReader_new(
663 int32_t width, int32_t height, int32_t format, int32_t maxImages,
664 /*out*/AImageReader** reader) {
665 ALOGV("%s", __FUNCTION__);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800666 return AImageReader_newWithUsage(
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700667 width, height, format, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, maxImages, reader);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800668}
669
Jayant Chowdhary249e1f22018-09-24 15:07:45 -0700670extern "C" {
671
672EXPORT
673media_status_t AImageReader_getWindowNativeHandle(
674 AImageReader *reader, /*out*/native_handle_t **handle) {
675 if (reader == nullptr || handle == nullptr) {
676 return AMEDIA_ERROR_INVALID_PARAMETER;
677 }
678 return reader->getWindowNativeHandle(handle);
679}
680
681} //extern "C"
682
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800683EXPORT
684media_status_t AImageReader_newWithUsage(
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700685 int32_t width, int32_t height, int32_t format, uint64_t usage,
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800686 int32_t maxImages, /*out*/ AImageReader** reader) {
687 ALOGV("%s", __FUNCTION__);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800688
689 if (width < 1 || height < 1) {
690 ALOGE("%s: image dimension must be positive: w:%d h:%d",
691 __FUNCTION__, width, height);
692 return AMEDIA_ERROR_INVALID_PARAMETER;
693 }
694
695 if (maxImages < 1) {
696 ALOGE("%s: max outstanding image count must be at least 1 (%d)",
697 __FUNCTION__, maxImages);
698 return AMEDIA_ERROR_INVALID_PARAMETER;
699 }
700
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800701 if (maxImages > BufferQueueDefs::NUM_BUFFER_SLOTS) {
702 ALOGE("%s: max outstanding image count (%d) cannot be larget than %d.",
703 __FUNCTION__, maxImages, BufferQueueDefs::NUM_BUFFER_SLOTS);
704 return AMEDIA_ERROR_INVALID_PARAMETER;
705 }
706
Jiwen 'Steve' Cai55ec2562017-04-12 15:56:41 -0700707 if (!AImageReader::isSupportedFormatAndUsage(format, usage)) {
708 ALOGE("%s: format %d is not supported with usage 0x%" PRIx64 " by AImageReader",
709 __FUNCTION__, format, usage);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800710 return AMEDIA_ERROR_INVALID_PARAMETER;
711 }
712
713 if (reader == nullptr) {
714 ALOGE("%s: reader argument is null", __FUNCTION__);
715 return AMEDIA_ERROR_INVALID_PARAMETER;
716 }
717
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800718 AImageReader* tmpReader = new AImageReader(
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700719 width, height, format, usage, maxImages);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800720 if (tmpReader == nullptr) {
721 ALOGE("%s: AImageReader allocation failed", __FUNCTION__);
722 return AMEDIA_ERROR_UNKNOWN;
723 }
724 media_status_t ret = tmpReader->init();
725 if (ret != AMEDIA_OK) {
726 ALOGE("%s: AImageReader initialization failed!", __FUNCTION__);
727 delete tmpReader;
728 return ret;
729 }
730 *reader = tmpReader;
731 (*reader)->incStrong((void*) AImageReader_new);
732 return AMEDIA_OK;
733}
734
735EXPORT
736void AImageReader_delete(AImageReader* reader) {
737 ALOGV("%s", __FUNCTION__);
738 if (reader != nullptr) {
739 reader->decStrong((void*) AImageReader_delete);
740 }
741 return;
742}
743
744EXPORT
745media_status_t AImageReader_getWindow(AImageReader* reader, /*out*/ANativeWindow** window) {
Yin-Chia Yehe2ded212017-11-08 15:51:02 -0800746 ALOGV("%s", __FUNCTION__);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800747 if (reader == nullptr || window == nullptr) {
748 ALOGE("%s: invalid argument. reader %p, window %p",
749 __FUNCTION__, reader, window);
750 return AMEDIA_ERROR_INVALID_PARAMETER;
751 }
752 *window = reader->getWindow();
753 return AMEDIA_OK;
754}
755
756EXPORT
757media_status_t AImageReader_getWidth(const AImageReader* reader, /*out*/int32_t* width) {
758 ALOGV("%s", __FUNCTION__);
759 if (reader == nullptr || width == nullptr) {
760 ALOGE("%s: invalid argument. reader %p, width %p",
761 __FUNCTION__, reader, width);
762 return AMEDIA_ERROR_INVALID_PARAMETER;
763 }
764 *width = reader->getWidth();
765 return AMEDIA_OK;
766}
767
768EXPORT
769media_status_t AImageReader_getHeight(const AImageReader* reader, /*out*/int32_t* height) {
770 ALOGV("%s", __FUNCTION__);
771 if (reader == nullptr || height == nullptr) {
772 ALOGE("%s: invalid argument. reader %p, height %p",
773 __FUNCTION__, reader, height);
774 return AMEDIA_ERROR_INVALID_PARAMETER;
775 }
776 *height = reader->getHeight();
777 return AMEDIA_OK;
778}
779
780EXPORT
781media_status_t AImageReader_getFormat(const AImageReader* reader, /*out*/int32_t* format) {
782 ALOGV("%s", __FUNCTION__);
783 if (reader == nullptr || format == nullptr) {
784 ALOGE("%s: invalid argument. reader %p, format %p",
785 __FUNCTION__, reader, format);
786 return AMEDIA_ERROR_INVALID_PARAMETER;
787 }
788 *format = reader->getFormat();
789 return AMEDIA_OK;
790}
791
792EXPORT
793media_status_t AImageReader_getMaxImages(const AImageReader* reader, /*out*/int32_t* maxImages) {
794 ALOGV("%s", __FUNCTION__);
795 if (reader == nullptr || maxImages == nullptr) {
796 ALOGE("%s: invalid argument. reader %p, maxImages %p",
797 __FUNCTION__, reader, maxImages);
798 return AMEDIA_ERROR_INVALID_PARAMETER;
799 }
800 *maxImages = reader->getMaxImages();
801 return AMEDIA_OK;
802}
803
804EXPORT
805media_status_t AImageReader_acquireNextImage(AImageReader* reader, /*out*/AImage** image) {
806 ALOGV("%s", __FUNCTION__);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800807 return AImageReader_acquireNextImageAsync(reader, image, nullptr);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800808}
809
810EXPORT
811media_status_t AImageReader_acquireLatestImage(AImageReader* reader, /*out*/AImage** image) {
812 ALOGV("%s", __FUNCTION__);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800813 return AImageReader_acquireLatestImageAsync(reader, image, nullptr);
814}
815
816EXPORT
817media_status_t AImageReader_acquireNextImageAsync(
818 AImageReader* reader, /*out*/AImage** image, /*out*/int* acquireFenceFd) {
819 ALOGV("%s", __FUNCTION__);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800820 if (reader == nullptr || image == nullptr) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800821 ALOGE("%s: invalid argument. reader %p, image %p",
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800822 __FUNCTION__, reader, image);
823 return AMEDIA_ERROR_INVALID_PARAMETER;
824 }
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800825 return reader->acquireNextImage(image, acquireFenceFd);
826}
827
828EXPORT
829media_status_t AImageReader_acquireLatestImageAsync(
830 AImageReader* reader, /*out*/AImage** image, /*out*/int* acquireFenceFd) {
831 ALOGV("%s", __FUNCTION__);
832 if (reader == nullptr || image == nullptr) {
833 ALOGE("%s: invalid argument. reader %p, image %p",
834 __FUNCTION__, reader, image);
835 return AMEDIA_ERROR_INVALID_PARAMETER;
836 }
837 return reader->acquireLatestImage(image, acquireFenceFd);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800838}
839
840EXPORT
841media_status_t AImageReader_setImageListener(
842 AImageReader* reader, AImageReader_ImageListener* listener) {
843 ALOGV("%s", __FUNCTION__);
Yin-Chia Yeh1d0955c2016-05-16 01:14:13 -0700844 if (reader == nullptr) {
845 ALOGE("%s: invalid argument! reader %p", __FUNCTION__, reader);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800846 return AMEDIA_ERROR_INVALID_PARAMETER;
847 }
848
849 reader->setImageListener(listener);
850 return AMEDIA_OK;
851}
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800852
853EXPORT
854media_status_t AImageReader_setBufferRemovedListener(
855 AImageReader* reader, AImageReader_BufferRemovedListener* listener) {
856 ALOGV("%s", __FUNCTION__);
857 if (reader == nullptr) {
858 ALOGE("%s: invalid argument! reader %p", __FUNCTION__, reader);
859 return AMEDIA_ERROR_INVALID_PARAMETER;
860 }
861
862 reader->setBufferRemovedListener(listener);
863 return AMEDIA_OK;
864}