blob: bcc7ff3da83fad28a78d18c328b402a5650ffea2 [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>
Jooyung Han86cbf712019-02-21 15:25:02 +090028#include <ui/PublicFormat.h>
Jooyung Han27d84b72019-02-21 15:12:59 +090029#include <private/android/AHardwareBufferHelpers.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:
Emilian Peev44df34d2019-02-12 09:30:15 -080073 case AIMAGE_FORMAT_DEPTH_JPEG:
Yin-Chia Yehc3603822016-01-18 22:11:19 -080074 return true;
Jiwen 'Steve' Cai55ec2562017-04-12 15:56:41 -070075 case AIMAGE_FORMAT_PRIVATE:
76 // For private format, cpu usage is prohibited.
77 return !hasCpuUsage;
Yin-Chia Yehc3603822016-01-18 22:11:19 -080078 default:
79 return false;
80 }
81}
82
83int
84AImageReader::getNumPlanesForFormat(int32_t format) {
85 switch (format) {
86 case AIMAGE_FORMAT_YUV_420_888:
87 return 3;
Jiwen 'Steve' Caide2a5442017-02-08 14:41:41 -080088 case AIMAGE_FORMAT_RGBA_8888:
89 case AIMAGE_FORMAT_RGBX_8888:
90 case AIMAGE_FORMAT_RGB_888:
91 case AIMAGE_FORMAT_RGB_565:
92 case AIMAGE_FORMAT_RGBA_FP16:
Yin-Chia Yehc3603822016-01-18 22:11:19 -080093 case AIMAGE_FORMAT_JPEG:
94 case AIMAGE_FORMAT_RAW16:
95 case AIMAGE_FORMAT_RAW_PRIVATE:
96 case AIMAGE_FORMAT_RAW10:
97 case AIMAGE_FORMAT_RAW12:
98 case AIMAGE_FORMAT_DEPTH16:
99 case AIMAGE_FORMAT_DEPTH_POINT_CLOUD:
Shuzhen Wang1cfbbec2018-10-08 13:55:28 -0700100 case AIMAGE_FORMAT_Y8:
Shuzhen Wang68ac7ad2019-01-30 14:03:28 -0800101 case AIMAGE_FORMAT_HEIC:
Emilian Peev44df34d2019-02-12 09:30:15 -0800102 case AIMAGE_FORMAT_DEPTH_JPEG:
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800103 return 1;
Jiwen 'Steve' Cai55ec2562017-04-12 15:56:41 -0700104 case AIMAGE_FORMAT_PRIVATE:
105 return 0;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800106 default:
107 return -1;
108 }
109}
110
111void
112AImageReader::FrameListener::onFrameAvailable(const BufferItem& /*item*/) {
113 Mutex::Autolock _l(mLock);
114 sp<AImageReader> reader = mReader.promote();
115 if (reader == nullptr) {
116 ALOGW("A frame is available after AImageReader closed!");
117 return; // reader has been closed
118 }
119 if (mListener.onImageAvailable == nullptr) {
120 return; // No callback registered
121 }
122
123 sp<AMessage> msg = new AMessage(AImageReader::kWhatImageAvailable, reader->mHandler);
124 msg->setPointer(AImageReader::kCallbackFpKey, (void *) mListener.onImageAvailable);
125 msg->setPointer(AImageReader::kContextKey, mListener.context);
126 msg->post();
127}
128
129media_status_t
130AImageReader::FrameListener::setImageListener(AImageReader_ImageListener* listener) {
131 Mutex::Autolock _l(mLock);
132 if (listener == nullptr) {
Yin-Chia Yeh1d0955c2016-05-16 01:14:13 -0700133 mListener.context = nullptr;
134 mListener.onImageAvailable = nullptr;
135 } else {
136 mListener = *listener;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800137 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800138 return AMEDIA_OK;
139}
140
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800141void
142AImageReader::BufferRemovedListener::onBufferFreed(const wp<GraphicBuffer>& graphicBuffer) {
143 Mutex::Autolock _l(mLock);
144 sp<AImageReader> reader = mReader.promote();
145 if (reader == nullptr) {
146 ALOGW("A frame is available after AImageReader closed!");
147 return; // reader has been closed
148 }
149 if (mListener.onBufferRemoved == nullptr) {
150 return; // No callback registered
151 }
152
153 sp<GraphicBuffer> gBuffer = graphicBuffer.promote();
154 if (gBuffer == nullptr) {
155 ALOGW("A buffer being freed has gone away!");
156 return; // buffer is already destroyed
157 }
158
159 sp<AMessage> msg = new AMessage(AImageReader::kWhatBufferRemoved, reader->mHandler);
160 msg->setPointer(
161 AImageReader::kCallbackFpKey, (void*) mListener.onBufferRemoved);
162 msg->setPointer(AImageReader::kContextKey, mListener.context);
163 msg->setObject(AImageReader::kGraphicBufferKey, gBuffer);
164 msg->post();
165}
166
167media_status_t
168AImageReader::BufferRemovedListener::setBufferRemovedListener(
169 AImageReader_BufferRemovedListener* listener) {
170 Mutex::Autolock _l(mLock);
171 if (listener == nullptr) {
172 mListener.context = nullptr;
173 mListener.onBufferRemoved = nullptr;
174 } else {
175 mListener = *listener;
176 }
177 return AMEDIA_OK;
178}
179
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800180media_status_t
181AImageReader::setImageListenerLocked(AImageReader_ImageListener* listener) {
182 return mFrameListener->setImageListener(listener);
183}
184
185media_status_t
186AImageReader::setImageListener(AImageReader_ImageListener* listener) {
187 Mutex::Autolock _l(mLock);
188 return setImageListenerLocked(listener);
189}
190
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800191media_status_t
192AImageReader::setBufferRemovedListenerLocked(AImageReader_BufferRemovedListener* listener) {
193 return mBufferRemovedListener->setBufferRemovedListener(listener);
194}
195
196media_status_t
197AImageReader::setBufferRemovedListener(AImageReader_BufferRemovedListener* listener) {
198 Mutex::Autolock _l(mLock);
199 return setBufferRemovedListenerLocked(listener);
200}
201
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800202void AImageReader::CallbackHandler::onMessageReceived(
203 const sp<AMessage> &msg) {
204 switch (msg->what()) {
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800205 case kWhatBufferRemoved:
206 {
207 AImageReader_BufferRemovedCallback onBufferRemoved;
208 void* context;
209 bool found = msg->findPointer(kCallbackFpKey, (void**) &onBufferRemoved);
210 if (!found || onBufferRemoved == nullptr) {
211 ALOGE("%s: Cannot find onBufferRemoved callback fp!", __FUNCTION__);
212 return;
213 }
214 found = msg->findPointer(kContextKey, &context);
215 if (!found) {
216 ALOGE("%s: Cannot find callback context!", __FUNCTION__);
217 return;
218 }
219 sp<RefBase> bufferToFree;
220 found = msg->findObject(kGraphicBufferKey, &bufferToFree);
221 if (!found || bufferToFree == nullptr) {
222 ALOGE("%s: Cannot find the buffer to free!", __FUNCTION__);
223 return;
224 }
225
226 // TODO(jwcai) Someone from Android graphics team stating this should just be a
227 // static_cast.
228 AHardwareBuffer* outBuffer = reinterpret_cast<AHardwareBuffer*>(bufferToFree.get());
229
230 // At this point, bufferToFree holds the last reference to the GraphicBuffer owned by
231 // this AImageReader, and the reference will be gone once this function returns.
232 (*onBufferRemoved)(context, mReader, outBuffer);
233 break;
234 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800235 case kWhatImageAvailable:
236 {
237 AImageReader_ImageCallback onImageAvailable;
238 void* context;
239 bool found = msg->findPointer(kCallbackFpKey, (void**) &onImageAvailable);
240 if (!found || onImageAvailable == nullptr) {
241 ALOGE("%s: Cannot find onImageAvailable callback fp!", __FUNCTION__);
242 return;
243 }
244 found = msg->findPointer(kContextKey, &context);
245 if (!found) {
246 ALOGE("%s: Cannot find callback context!", __FUNCTION__);
247 return;
248 }
249 (*onImageAvailable)(context, mReader);
250 break;
251 }
252 default:
253 ALOGE("%s: unknown message type %d", __FUNCTION__, msg->what());
254 break;
255 }
256}
257
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800258AImageReader::AImageReader(int32_t width,
259 int32_t height,
260 int32_t format,
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700261 uint64_t usage,
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800262 int32_t maxImages)
263 : mWidth(width),
264 mHeight(height),
265 mFormat(format),
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700266 mUsage(usage),
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800267 mMaxImages(maxImages),
268 mNumPlanes(getNumPlanesForFormat(format)),
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800269 mFrameListener(new FrameListener(this)),
270 mBufferRemovedListener(new BufferRemovedListener(this)) {}
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800271
272media_status_t
273AImageReader::init() {
274 PublicFormat publicFormat = static_cast<PublicFormat>(mFormat);
Jooyung Han86cbf712019-02-21 15:25:02 +0900275 mHalFormat = mapPublicFormatToHalFormat(publicFormat);
276 mHalDataSpace = mapPublicFormatToHalDataspace(publicFormat);
Jooyung Han27d84b72019-02-21 15:12:59 +0900277 mHalUsage = AHardwareBuffer_convertToGrallocUsageBits(mUsage);
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800278
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800279 sp<IGraphicBufferProducer> gbProducer;
280 sp<IGraphicBufferConsumer> gbConsumer;
281 BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
282
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700283 String8 consumerName = String8::format("ImageReader-%dx%df%xu%" PRIu64 "m%d-%d-%d",
284 mWidth, mHeight, mFormat, mUsage, mMaxImages, getpid(),
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800285 createProcessUniqueId());
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800286
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800287 mBufferItemConsumer =
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800288 new BufferItemConsumer(gbConsumer, mHalUsage, mMaxImages, /*controlledByApp*/ true);
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800289 if (mBufferItemConsumer == nullptr) {
290 ALOGE("Failed to allocate BufferItemConsumer");
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800291 return AMEDIA_ERROR_UNKNOWN;
292 }
293
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800294 mProducer = gbProducer;
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800295 mBufferItemConsumer->setName(consumerName);
296 mBufferItemConsumer->setFrameAvailableListener(mFrameListener);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800297 mBufferItemConsumer->setBufferFreedListener(mBufferRemovedListener);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800298
299 status_t res;
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800300 res = mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800301 if (res != OK) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800302 ALOGE("Failed to set BufferItemConsumer buffer size");
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800303 return AMEDIA_ERROR_UNKNOWN;
304 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800305 res = mBufferItemConsumer->setDefaultBufferFormat(mHalFormat);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800306 if (res != OK) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800307 ALOGE("Failed to set BufferItemConsumer buffer format");
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800308 return AMEDIA_ERROR_UNKNOWN;
309 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800310 res = mBufferItemConsumer->setDefaultBufferDataSpace(mHalDataSpace);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800311 if (res != OK) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800312 ALOGE("Failed to set BufferItemConsumer buffer dataSpace");
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800313 return AMEDIA_ERROR_UNKNOWN;
314 }
Khushalff20fd42019-01-22 15:31:00 -0800315 if (mUsage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
316 gbConsumer->setConsumerIsProtected(true);
317 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800318
319 mSurface = new Surface(mProducer, /*controlledByApp*/true);
320 if (mSurface == nullptr) {
321 ALOGE("Failed to create surface");
322 return AMEDIA_ERROR_UNKNOWN;
323 }
324 mWindow = static_cast<ANativeWindow*>(mSurface.get());
325
326 for (int i = 0; i < mMaxImages; i++) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800327 BufferItem* buffer = new BufferItem;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800328 mBuffers.push_back(buffer);
329 }
330
331 mCbLooper = new ALooper;
332 mCbLooper->setName(consumerName.string());
Aurimas Liutikas214c8332016-02-19 14:48:23 -0800333 res = mCbLooper->start(
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800334 /*runOnCallingThread*/false,
335 /*canCallJava*/ true,
336 PRIORITY_DEFAULT);
Aurimas Liutikas214c8332016-02-19 14:48:23 -0800337 if (res != OK) {
338 ALOGE("Failed to start the looper");
339 return AMEDIA_ERROR_UNKNOWN;
340 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800341 mHandler = new CallbackHandler(this);
342 mCbLooper->registerHandler(mHandler);
343
344 return AMEDIA_OK;
345}
346
347AImageReader::~AImageReader() {
348 Mutex::Autolock _l(mLock);
349 AImageReader_ImageListener nullListener = {nullptr, nullptr};
350 setImageListenerLocked(&nullListener);
351
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800352 AImageReader_BufferRemovedListener nullBufferRemovedListener = {nullptr, nullptr};
353 setBufferRemovedListenerLocked(&nullBufferRemovedListener);
354
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800355 if (mCbLooper != nullptr) {
356 mCbLooper->unregisterHandler(mHandler->id());
357 mCbLooper->stop();
358 }
359 mCbLooper.clear();
360 mHandler.clear();
361
362 // Close all previously acquired images
363 for (auto it = mAcquiredImages.begin();
364 it != mAcquiredImages.end(); it++) {
365 AImage* image = *it;
Yin-Chia Yehb29fce72017-11-06 17:16:22 -0800366 Mutex::Autolock _l(image->mLock);
Yin-Chia Yeh55baca22019-01-07 13:54:13 -0800367 // Do not alter mAcquiredImages while we are iterating on it
368 releaseImageLocked(image, /*releaseFenceFd*/-1, /*clearCache*/false);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800369 }
Yin-Chia Yeh55baca22019-01-07 13:54:13 -0800370 mAcquiredImages.clear();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800371
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800372 // Delete Buffer Items
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800373 for (auto it = mBuffers.begin();
374 it != mBuffers.end(); it++) {
375 delete *it;
376 }
377
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800378 if (mBufferItemConsumer != nullptr) {
379 mBufferItemConsumer->abandon();
380 mBufferItemConsumer->setFrameAvailableListener(nullptr);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800381 }
Jayant Chowdhary249e1f22018-09-24 15:07:45 -0700382
383 if (mWindowHandle != nullptr) {
384 int size = mWindowHandle->data[0];
385 hidl_vec<uint8_t> halToken;
386 halToken.setToExternal(
387 reinterpret_cast<uint8_t *>(&mWindowHandle->data[1]), size);
388 deleteHalToken(halToken);
389 native_handle_delete(mWindowHandle);
390 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800391}
392
393media_status_t
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800394AImageReader::acquireImageLocked(/*out*/AImage** image, /*out*/int* acquireFenceFd) {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800395 *image = nullptr;
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800396 BufferItem* buffer = getBufferItemLocked();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800397 if (buffer == nullptr) {
398 ALOGW("Unable to acquire a lockedBuffer, very likely client tries to lock more than"
399 " maxImages buffers");
400 return AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED;
401 }
402
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800403 // When the output paramter fence is not NULL, we are acquiring the image asynchronously.
404 bool waitForFence = acquireFenceFd == nullptr;
405 status_t res = mBufferItemConsumer->acquireBuffer(buffer, 0, waitForFence);
406
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800407 if (res != NO_ERROR) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800408 returnBufferItemLocked(buffer);
409 if (res != BufferQueue::NO_BUFFER_AVAILABLE) {
410 if (res == INVALID_OPERATION) {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800411 return AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED;
412 } else {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800413 ALOGE("%s: Acquire image failed with some unknown error: %s (%d)",
414 __FUNCTION__, strerror(-res), res);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800415 return AMEDIA_ERROR_UNKNOWN;
416 }
417 }
418 return AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE;
419 }
420
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800421 const int bufferWidth = getBufferWidth(buffer);
422 const int bufferHeight = getBufferHeight(buffer);
423 const int bufferFmt = buffer->mGraphicBuffer->getPixelFormat();
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800424 const int bufferUsage = buffer->mGraphicBuffer->getUsage();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800425
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800426 const int readerWidth = mWidth;
427 const int readerHeight = mHeight;
428 const int readerFmt = mHalFormat;
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800429 const int readerUsage = mHalUsage;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800430
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800431 // Check if the producer buffer configurations match what AImageReader configured. Add some
432 // extra checks for non-opaque formats.
433 if (!isFormatOpaque(readerFmt)) {
434 // Check if the left-top corner of the crop rect is origin, we currently assume this point
435 // is zero, will revisit this once this assumption turns out problematic.
436 Point lt = buffer->mCrop.leftTop();
437 if (lt.x != 0 || lt.y != 0) {
438 ALOGE("Crop left top corner [%d, %d] not at origin", lt.x, lt.y);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800439 return AMEDIA_ERROR_UNKNOWN;
440 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800441
442 // Check if the producer buffer configurations match what ImageReader configured.
Jiwen 'Steve' Cai755ca9b2017-03-31 16:59:50 -0700443 ALOGV_IF(readerWidth != bufferWidth || readerHeight != bufferHeight,
444 "%s: Buffer size: %dx%d, doesn't match AImageReader configured size: %dx%d",
445 __FUNCTION__, bufferWidth, bufferHeight, readerWidth, readerHeight);
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800446
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800447 // Check if the buffer usage is a super set of reader's usage bits, aka all usage bits that
448 // ImageReader requested has been supported from the producer side.
449 ALOGD_IF((readerUsage | bufferUsage) != bufferUsage,
450 "%s: Producer buffer usage: %x, doesn't cover all usage bits AImageReader "
451 "configured: %x",
452 __FUNCTION__, bufferUsage, readerUsage);
453
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800454 if (readerFmt != bufferFmt) {
455 if (readerFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && isPossiblyYUV(bufferFmt)) {
456 // Special casing for when producer switches to a format compatible with flexible
457 // YUV.
458 mHalFormat = bufferFmt;
459 ALOGD("%s: Overriding buffer format YUV_420_888 to 0x%x.", __FUNCTION__, bufferFmt);
460 } else {
461 // Return the buffer to the queue. No need to provide fence, as this buffer wasn't
462 // used anywhere yet.
463 mBufferItemConsumer->releaseBuffer(*buffer);
464 returnBufferItemLocked(buffer);
465
466 ALOGE("%s: Output buffer format: 0x%x, ImageReader configured format: 0x%x",
467 __FUNCTION__, bufferFmt, readerFmt);
468
469 return AMEDIA_ERROR_UNKNOWN;
470 }
471 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800472 }
473
474 if (mHalFormat == HAL_PIXEL_FORMAT_BLOB) {
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700475 *image = new AImage(this, mFormat, mUsage, buffer, buffer->mTimestamp,
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800476 readerWidth, readerHeight, mNumPlanes);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800477 } else {
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700478 *image = new AImage(this, mFormat, mUsage, buffer, buffer->mTimestamp,
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800479 bufferWidth, bufferHeight, mNumPlanes);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800480 }
481 mAcquiredImages.push_back(*image);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800482
483 // When the output paramter fence is not NULL, we are acquiring the image asynchronously.
484 if (acquireFenceFd != nullptr) {
485 *acquireFenceFd = buffer->mFence->dup();
486 }
487
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800488 return AMEDIA_OK;
489}
490
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800491BufferItem*
492AImageReader::getBufferItemLocked() {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800493 if (mBuffers.empty()) {
494 return nullptr;
495 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800496 // Return a BufferItem pointer and remove it from the list
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800497 auto it = mBuffers.begin();
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800498 BufferItem* buffer = *it;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800499 mBuffers.erase(it);
500 return buffer;
501}
502
503void
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800504AImageReader::returnBufferItemLocked(BufferItem* buffer) {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800505 mBuffers.push_back(buffer);
506}
507
508void
Yin-Chia Yeh55baca22019-01-07 13:54:13 -0800509AImageReader::releaseImageLocked(AImage* image, int releaseFenceFd, bool clearCache) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800510 BufferItem* buffer = image->mBuffer;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800511 if (buffer == nullptr) {
512 // This should not happen, but is not fatal
513 ALOGW("AImage %p has no buffer!", image);
514 return;
515 }
516
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800517 int unlockFenceFd = -1;
518 media_status_t ret = image->unlockImageIfLocked(&unlockFenceFd);
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800519 if (ret < 0) {
520 ALOGW("%s: AImage %p is cannot be unlocked.", __FUNCTION__, image);
521 return;
522 }
523
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800524 sp<Fence> unlockFence = unlockFenceFd > 0 ? new Fence(unlockFenceFd) : Fence::NO_FENCE;
525 sp<Fence> releaseFence = releaseFenceFd > 0 ? new Fence(releaseFenceFd) : Fence::NO_FENCE;
526 sp<Fence> bufferFence = Fence::merge("AImageReader", unlockFence, releaseFence);
527 mBufferItemConsumer->releaseBuffer(*buffer, bufferFence);
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800528 returnBufferItemLocked(buffer);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800529 image->mBuffer = nullptr;
Yin-Chia Yehb29fce72017-11-06 17:16:22 -0800530 image->mLockedBuffer = nullptr;
531 image->mIsClosed = true;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800532
Yin-Chia Yeh55baca22019-01-07 13:54:13 -0800533 if (!clearCache) {
534 return;
535 }
536
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800537 bool found = false;
538 // cleanup acquired image list
539 for (auto it = mAcquiredImages.begin();
540 it != mAcquiredImages.end(); it++) {
541 AImage* readerCopy = *it;
542 if (readerCopy == image) {
543 found = true;
544 mAcquiredImages.erase(it);
545 break;
546 }
547 }
548 if (!found) {
549 ALOGE("Error: AImage %p is not generated by AImageReader %p",
550 image, this);
551 }
552}
553
Jayant Chowdhary249e1f22018-09-24 15:07:45 -0700554media_status_t AImageReader::getWindowNativeHandle(native_handle **handle) {
555 if (mWindowHandle != nullptr) {
556 *handle = mWindowHandle;
557 return AMEDIA_OK;
558 }
559 sp<HGraphicBufferProducer> hgbp =
560 new TWGraphicBufferProducer<HGraphicBufferProducer>(mProducer);
561 HalToken halToken;
562 if (!createHalToken(hgbp, &halToken)) {
563 return AMEDIA_ERROR_UNKNOWN;
564 }
565 mWindowHandle = convertHalTokenToNativeHandle(halToken);
566 if (!mWindowHandle) {
567 return AMEDIA_ERROR_UNKNOWN;
568 }
569 *handle = mWindowHandle;
570 return AMEDIA_OK;
571}
572
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800573int
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800574AImageReader::getBufferWidth(BufferItem* buffer) {
575 if (buffer == NULL) return -1;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800576
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800577 if (!buffer->mCrop.isEmpty()) {
578 return buffer->mCrop.getWidth();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800579 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800580
581 return buffer->mGraphicBuffer->getWidth();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800582}
583
584int
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800585AImageReader::getBufferHeight(BufferItem* buffer) {
586 if (buffer == NULL) return -1;
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800587
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800588 if (!buffer->mCrop.isEmpty()) {
589 return buffer->mCrop.getHeight();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800590 }
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800591
592 return buffer->mGraphicBuffer->getHeight();
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800593}
594
595media_status_t
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800596AImageReader::acquireNextImage(/*out*/AImage** image, /*out*/int* acquireFenceFd) {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800597 Mutex::Autolock _l(mLock);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800598 return acquireImageLocked(image, acquireFenceFd);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800599}
600
601media_status_t
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800602AImageReader::acquireLatestImage(/*out*/AImage** image, /*out*/int* acquireFenceFd) {
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800603 if (image == nullptr) {
604 return AMEDIA_ERROR_INVALID_PARAMETER;
605 }
606 Mutex::Autolock _l(mLock);
607 *image = nullptr;
608 AImage* prevImage = nullptr;
609 AImage* nextImage = nullptr;
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800610 media_status_t ret = acquireImageLocked(&prevImage, acquireFenceFd);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800611 if (prevImage == nullptr) {
612 return ret;
613 }
614 for (;;) {
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800615 ret = acquireImageLocked(&nextImage, acquireFenceFd);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800616 if (nextImage == nullptr) {
617 *image = prevImage;
618 return AMEDIA_OK;
619 }
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800620
621 if (acquireFenceFd == nullptr) {
622 // No need for release fence here since the prevImage is unused and acquireImageLocked
623 // has already waited for acquired fence to be signaled.
624 prevImage->close();
625 } else {
626 // Use the acquire fence as release fence, so that producer can wait before trying to
627 // refill the buffer.
628 prevImage->close(*acquireFenceFd);
629 }
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800630 prevImage->free();
631 prevImage = nextImage;
632 nextImage = nullptr;
633 }
634}
635
Jayant Chowdhary249e1f22018-09-24 15:07:45 -0700636static native_handle_t *convertHalTokenToNativeHandle(
637 const HalToken &halToken) {
638 // We attempt to store halToken in the ints of the native_handle_t after its
639 // size. The first int stores the size of the token. We store this in an int
640 // to avoid alignment issues where size_t and int do not have the same
641 // alignment.
642 size_t nhDataByteSize = halToken.size();
643 if (nhDataByteSize > kWindowHalTokenSizeMax) {
644 // The size of the token isn't reasonable..
645 return nullptr;
646 }
647 size_t numInts = ceil(nhDataByteSize / sizeof(int)) + 1;
648
649 // We don't check for overflow, whether numInts can fit in an int, since we
650 // expect kWindowHalTokenSizeMax to be a reasonable limit.
651 // create a native_handle_t with 0 numFds and numInts number of ints.
652 native_handle_t *nh =
653 native_handle_create(0, numInts);
654 if (!nh) {
655 return nullptr;
656 }
657 // Store the size of the token in the first int.
658 nh->data[0] = nhDataByteSize;
659 memcpy(&(nh->data[1]), halToken.data(), nhDataByteSize);
660 return nh;
661}
662
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800663EXPORT
664media_status_t AImageReader_new(
665 int32_t width, int32_t height, int32_t format, int32_t maxImages,
666 /*out*/AImageReader** reader) {
667 ALOGV("%s", __FUNCTION__);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800668 return AImageReader_newWithUsage(
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700669 width, height, format, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, maxImages, reader);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800670}
671
Jayant Chowdhary249e1f22018-09-24 15:07:45 -0700672extern "C" {
673
674EXPORT
675media_status_t AImageReader_getWindowNativeHandle(
676 AImageReader *reader, /*out*/native_handle_t **handle) {
677 if (reader == nullptr || handle == nullptr) {
678 return AMEDIA_ERROR_INVALID_PARAMETER;
679 }
680 return reader->getWindowNativeHandle(handle);
681}
682
683} //extern "C"
684
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800685EXPORT
686media_status_t AImageReader_newWithUsage(
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700687 int32_t width, int32_t height, int32_t format, uint64_t usage,
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800688 int32_t maxImages, /*out*/ AImageReader** reader) {
689 ALOGV("%s", __FUNCTION__);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800690
691 if (width < 1 || height < 1) {
692 ALOGE("%s: image dimension must be positive: w:%d h:%d",
693 __FUNCTION__, width, height);
694 return AMEDIA_ERROR_INVALID_PARAMETER;
695 }
696
697 if (maxImages < 1) {
698 ALOGE("%s: max outstanding image count must be at least 1 (%d)",
699 __FUNCTION__, maxImages);
700 return AMEDIA_ERROR_INVALID_PARAMETER;
701 }
702
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800703 if (maxImages > BufferQueueDefs::NUM_BUFFER_SLOTS) {
704 ALOGE("%s: max outstanding image count (%d) cannot be larget than %d.",
705 __FUNCTION__, maxImages, BufferQueueDefs::NUM_BUFFER_SLOTS);
706 return AMEDIA_ERROR_INVALID_PARAMETER;
707 }
708
Jiwen 'Steve' Cai55ec2562017-04-12 15:56:41 -0700709 if (!AImageReader::isSupportedFormatAndUsage(format, usage)) {
710 ALOGE("%s: format %d is not supported with usage 0x%" PRIx64 " by AImageReader",
711 __FUNCTION__, format, usage);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800712 return AMEDIA_ERROR_INVALID_PARAMETER;
713 }
714
715 if (reader == nullptr) {
716 ALOGE("%s: reader argument is null", __FUNCTION__);
717 return AMEDIA_ERROR_INVALID_PARAMETER;
718 }
719
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800720 AImageReader* tmpReader = new AImageReader(
Jiwen 'Steve' Caie31bc872017-04-21 17:13:18 -0700721 width, height, format, usage, maxImages);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800722 if (tmpReader == nullptr) {
723 ALOGE("%s: AImageReader allocation failed", __FUNCTION__);
724 return AMEDIA_ERROR_UNKNOWN;
725 }
726 media_status_t ret = tmpReader->init();
727 if (ret != AMEDIA_OK) {
728 ALOGE("%s: AImageReader initialization failed!", __FUNCTION__);
729 delete tmpReader;
730 return ret;
731 }
732 *reader = tmpReader;
733 (*reader)->incStrong((void*) AImageReader_new);
734 return AMEDIA_OK;
735}
736
737EXPORT
738void AImageReader_delete(AImageReader* reader) {
739 ALOGV("%s", __FUNCTION__);
740 if (reader != nullptr) {
741 reader->decStrong((void*) AImageReader_delete);
742 }
743 return;
744}
745
746EXPORT
747media_status_t AImageReader_getWindow(AImageReader* reader, /*out*/ANativeWindow** window) {
Yin-Chia Yehe2ded212017-11-08 15:51:02 -0800748 ALOGV("%s", __FUNCTION__);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800749 if (reader == nullptr || window == nullptr) {
750 ALOGE("%s: invalid argument. reader %p, window %p",
751 __FUNCTION__, reader, window);
752 return AMEDIA_ERROR_INVALID_PARAMETER;
753 }
754 *window = reader->getWindow();
755 return AMEDIA_OK;
756}
757
758EXPORT
759media_status_t AImageReader_getWidth(const AImageReader* reader, /*out*/int32_t* width) {
760 ALOGV("%s", __FUNCTION__);
761 if (reader == nullptr || width == nullptr) {
762 ALOGE("%s: invalid argument. reader %p, width %p",
763 __FUNCTION__, reader, width);
764 return AMEDIA_ERROR_INVALID_PARAMETER;
765 }
766 *width = reader->getWidth();
767 return AMEDIA_OK;
768}
769
770EXPORT
771media_status_t AImageReader_getHeight(const AImageReader* reader, /*out*/int32_t* height) {
772 ALOGV("%s", __FUNCTION__);
773 if (reader == nullptr || height == nullptr) {
774 ALOGE("%s: invalid argument. reader %p, height %p",
775 __FUNCTION__, reader, height);
776 return AMEDIA_ERROR_INVALID_PARAMETER;
777 }
778 *height = reader->getHeight();
779 return AMEDIA_OK;
780}
781
782EXPORT
783media_status_t AImageReader_getFormat(const AImageReader* reader, /*out*/int32_t* format) {
784 ALOGV("%s", __FUNCTION__);
785 if (reader == nullptr || format == nullptr) {
786 ALOGE("%s: invalid argument. reader %p, format %p",
787 __FUNCTION__, reader, format);
788 return AMEDIA_ERROR_INVALID_PARAMETER;
789 }
790 *format = reader->getFormat();
791 return AMEDIA_OK;
792}
793
794EXPORT
795media_status_t AImageReader_getMaxImages(const AImageReader* reader, /*out*/int32_t* maxImages) {
796 ALOGV("%s", __FUNCTION__);
797 if (reader == nullptr || maxImages == nullptr) {
798 ALOGE("%s: invalid argument. reader %p, maxImages %p",
799 __FUNCTION__, reader, maxImages);
800 return AMEDIA_ERROR_INVALID_PARAMETER;
801 }
802 *maxImages = reader->getMaxImages();
803 return AMEDIA_OK;
804}
805
806EXPORT
807media_status_t AImageReader_acquireNextImage(AImageReader* reader, /*out*/AImage** image) {
808 ALOGV("%s", __FUNCTION__);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800809 return AImageReader_acquireNextImageAsync(reader, image, nullptr);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800810}
811
812EXPORT
813media_status_t AImageReader_acquireLatestImage(AImageReader* reader, /*out*/AImage** image) {
814 ALOGV("%s", __FUNCTION__);
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800815 return AImageReader_acquireLatestImageAsync(reader, image, nullptr);
816}
817
818EXPORT
819media_status_t AImageReader_acquireNextImageAsync(
820 AImageReader* reader, /*out*/AImage** image, /*out*/int* acquireFenceFd) {
821 ALOGV("%s", __FUNCTION__);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800822 if (reader == nullptr || image == nullptr) {
Jiwen 'Steve' Cai2f1a4732017-02-04 17:31:55 -0800823 ALOGE("%s: invalid argument. reader %p, image %p",
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800824 __FUNCTION__, reader, image);
825 return AMEDIA_ERROR_INVALID_PARAMETER;
826 }
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800827 return reader->acquireNextImage(image, acquireFenceFd);
828}
829
830EXPORT
831media_status_t AImageReader_acquireLatestImageAsync(
832 AImageReader* reader, /*out*/AImage** image, /*out*/int* acquireFenceFd) {
833 ALOGV("%s", __FUNCTION__);
834 if (reader == nullptr || image == nullptr) {
835 ALOGE("%s: invalid argument. reader %p, image %p",
836 __FUNCTION__, reader, image);
837 return AMEDIA_ERROR_INVALID_PARAMETER;
838 }
839 return reader->acquireLatestImage(image, acquireFenceFd);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800840}
841
842EXPORT
843media_status_t AImageReader_setImageListener(
844 AImageReader* reader, AImageReader_ImageListener* listener) {
845 ALOGV("%s", __FUNCTION__);
Yin-Chia Yeh1d0955c2016-05-16 01:14:13 -0700846 if (reader == nullptr) {
847 ALOGE("%s: invalid argument! reader %p", __FUNCTION__, reader);
Yin-Chia Yehc3603822016-01-18 22:11:19 -0800848 return AMEDIA_ERROR_INVALID_PARAMETER;
849 }
850
851 reader->setImageListener(listener);
852 return AMEDIA_OK;
853}
Jiwen 'Steve' Caie1689962017-02-20 16:59:05 -0800854
855EXPORT
856media_status_t AImageReader_setBufferRemovedListener(
857 AImageReader* reader, AImageReader_BufferRemovedListener* listener) {
858 ALOGV("%s", __FUNCTION__);
859 if (reader == nullptr) {
860 ALOGE("%s: invalid argument! reader %p", __FUNCTION__, reader);
861 return AMEDIA_ERROR_INVALID_PARAMETER;
862 }
863
864 reader->setBufferRemovedListener(listener);
865 return AMEDIA_OK;
866}