• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 "NdkImage"
21 
22 #include "NdkImagePriv.h"
23 #include "NdkImageReaderPriv.h"
24 
25 #include <utils/Log.h>
26 #include "hardware/camera3.h"
27 
28 using namespace android;
29 
30 #define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
31 
AImage(AImageReader * reader,int32_t format,CpuConsumer::LockedBuffer * buffer,int64_t timestamp,int32_t width,int32_t height,int32_t numPlanes)32 AImage::AImage(AImageReader* reader, int32_t format,
33         CpuConsumer::LockedBuffer* buffer, int64_t timestamp,
34         int32_t width, int32_t height, int32_t numPlanes) :
35         mReader(reader), mFormat(format),
36         mBuffer(buffer), mTimestamp(timestamp),
37         mWidth(width), mHeight(height), mNumPlanes(numPlanes) {
38 }
39 
40 // Can only be called by free() with mLock hold
~AImage()41 AImage::~AImage() {
42     if (!mIsClosed) {
43         LOG_ALWAYS_FATAL(
44                 "Error: AImage %p is deleted before returning buffer to AImageReader!", this);
45     }
46 }
47 
48 bool
isClosed() const49 AImage::isClosed() const {
50     Mutex::Autolock _l(mLock);
51     return mIsClosed;
52 }
53 
54 void
close()55 AImage::close() {
56     Mutex::Autolock _l(mLock);
57     if (mIsClosed) {
58         return;
59     }
60     sp<AImageReader> reader = mReader.promote();
61     if (reader == nullptr) {
62         LOG_ALWAYS_FATAL("Error: AImage not closed before AImageReader close!");
63         return;
64     }
65     reader->releaseImageLocked(this);
66     // Should have been set to nullptr in releaseImageLocked
67     // Set to nullptr here for extra safety only
68     mBuffer = nullptr;
69     mIsClosed = true;
70 }
71 
72 void
free()73 AImage::free() {
74     if (!isClosed()) {
75         ALOGE("Cannot free AImage before close!");
76         return;
77     }
78     Mutex::Autolock _l(mLock);
79     delete this;
80 }
81 
82 void
lockReader() const83 AImage::lockReader() const {
84     sp<AImageReader> reader = mReader.promote();
85     if (reader == nullptr) {
86         // Reader has been closed
87         return;
88     }
89     reader->mLock.lock();
90 }
91 
92 void
unlockReader() const93 AImage::unlockReader() const {
94     sp<AImageReader> reader = mReader.promote();
95     if (reader == nullptr) {
96         // Reader has been closed
97         return;
98     }
99     reader->mLock.unlock();
100 }
101 
102 media_status_t
getWidth(int32_t * width) const103 AImage::getWidth(int32_t* width) const {
104     if (width == nullptr) {
105         return AMEDIA_ERROR_INVALID_PARAMETER;
106     }
107     *width = -1;
108     if (isClosed()) {
109         ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
110         return AMEDIA_ERROR_INVALID_OBJECT;
111     }
112     *width = mWidth;
113     return AMEDIA_OK;
114 }
115 
116 media_status_t
getHeight(int32_t * height) const117 AImage::getHeight(int32_t* height) const {
118     if (height == nullptr) {
119         return AMEDIA_ERROR_INVALID_PARAMETER;
120     }
121     *height = -1;
122     if (isClosed()) {
123         ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
124         return AMEDIA_ERROR_INVALID_OBJECT;
125     }
126     *height = mHeight;
127     return AMEDIA_OK;
128 }
129 
130 media_status_t
getFormat(int32_t * format) const131 AImage::getFormat(int32_t* format) const {
132     if (format == nullptr) {
133         return AMEDIA_ERROR_INVALID_PARAMETER;
134     }
135     *format = -1;
136     if (isClosed()) {
137         ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
138         return AMEDIA_ERROR_INVALID_OBJECT;
139     }
140     *format = mFormat;
141     return AMEDIA_OK;
142 }
143 
144 media_status_t
getNumPlanes(int32_t * numPlanes) const145 AImage::getNumPlanes(int32_t* numPlanes) const {
146     if (numPlanes == nullptr) {
147         return AMEDIA_ERROR_INVALID_PARAMETER;
148     }
149     *numPlanes = -1;
150     if (isClosed()) {
151         ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
152         return AMEDIA_ERROR_INVALID_OBJECT;
153     }
154     *numPlanes = mNumPlanes;
155     return AMEDIA_OK;
156 }
157 
158 media_status_t
getTimestamp(int64_t * timestamp) const159 AImage::getTimestamp(int64_t* timestamp) const {
160     if (timestamp == nullptr) {
161         return AMEDIA_ERROR_INVALID_PARAMETER;
162     }
163     *timestamp = -1;
164     if (isClosed()) {
165         ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
166         return AMEDIA_ERROR_INVALID_OBJECT;
167     }
168     *timestamp = mTimestamp;
169     return AMEDIA_OK;
170 }
171 
172 media_status_t
getPlanePixelStride(int planeIdx,int32_t * pixelStride) const173 AImage::getPlanePixelStride(int planeIdx, /*out*/int32_t* pixelStride) const {
174     if (planeIdx < 0 || planeIdx >= mNumPlanes) {
175         ALOGE("Error: planeIdx %d out of bound [0,%d]",
176                 planeIdx, mNumPlanes - 1);
177         return AMEDIA_ERROR_INVALID_PARAMETER;
178     }
179     if (pixelStride == nullptr) {
180         return AMEDIA_ERROR_INVALID_PARAMETER;
181     }
182     if (isClosed()) {
183         ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
184         return AMEDIA_ERROR_INVALID_OBJECT;
185     }
186     int32_t fmt = mBuffer->flexFormat;
187     switch (fmt) {
188         case HAL_PIXEL_FORMAT_YCbCr_420_888:
189             *pixelStride = (planeIdx == 0) ? 1 : mBuffer->chromaStep;
190             return AMEDIA_OK;
191         case HAL_PIXEL_FORMAT_YCrCb_420_SP:
192             *pixelStride = (planeIdx == 0) ? 1 : 2;
193             return AMEDIA_OK;
194         case HAL_PIXEL_FORMAT_Y8:
195             *pixelStride = 1;
196             return AMEDIA_OK;
197         case HAL_PIXEL_FORMAT_YV12:
198             *pixelStride = 1;
199             return AMEDIA_OK;
200         case HAL_PIXEL_FORMAT_Y16:
201         case HAL_PIXEL_FORMAT_RAW16:
202         case HAL_PIXEL_FORMAT_RGB_565:
203             // Single plane 16bpp data.
204             *pixelStride = 2;
205             return AMEDIA_OK;
206         case HAL_PIXEL_FORMAT_RGBA_8888:
207         case HAL_PIXEL_FORMAT_RGBX_8888:
208             *pixelStride = 4;
209             return AMEDIA_OK;
210         case HAL_PIXEL_FORMAT_RGB_888:
211             // Single plane, 24bpp.
212             *pixelStride = 3;
213             return AMEDIA_OK;
214         case HAL_PIXEL_FORMAT_BLOB:
215         case HAL_PIXEL_FORMAT_RAW10:
216         case HAL_PIXEL_FORMAT_RAW12:
217         case HAL_PIXEL_FORMAT_RAW_OPAQUE:
218             // Blob is used for JPEG data, RAW10 and RAW12 is used for 10-bit and 12-bit raw data,
219             // those are single plane data without pixel stride defined
220             return AMEDIA_ERROR_UNSUPPORTED;
221         default:
222             ALOGE("Pixel format: 0x%x is unsupported", fmt);
223             return AMEDIA_ERROR_UNSUPPORTED;
224     }
225 }
226 
227 media_status_t
getPlaneRowStride(int planeIdx,int32_t * rowStride) const228 AImage::getPlaneRowStride(int planeIdx, /*out*/int32_t* rowStride) const {
229     if (planeIdx < 0 || planeIdx >= mNumPlanes) {
230         ALOGE("Error: planeIdx %d out of bound [0,%d]",
231                 planeIdx, mNumPlanes - 1);
232         return AMEDIA_ERROR_INVALID_PARAMETER;
233     }
234     if (rowStride == nullptr) {
235         return AMEDIA_ERROR_INVALID_PARAMETER;
236     }
237     if (isClosed()) {
238         ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
239         return AMEDIA_ERROR_INVALID_OBJECT;
240     }
241     int32_t fmt = mBuffer->flexFormat;
242     switch (fmt) {
243         case HAL_PIXEL_FORMAT_YCbCr_420_888:
244             *rowStride = (planeIdx == 0) ? mBuffer->stride : mBuffer->chromaStride;
245             return AMEDIA_OK;
246         case HAL_PIXEL_FORMAT_YCrCb_420_SP:
247             *rowStride = mBuffer->width;
248             return AMEDIA_OK;
249         case HAL_PIXEL_FORMAT_YV12:
250             if (mBuffer->stride % 16) {
251                 ALOGE("Stride %d is not 16 pixel aligned!", mBuffer->stride);
252                 return AMEDIA_ERROR_UNKNOWN;
253             }
254             *rowStride = (planeIdx == 0) ? mBuffer->stride : ALIGN(mBuffer->stride / 2, 16);
255             return AMEDIA_OK;
256         case HAL_PIXEL_FORMAT_RAW10:
257         case HAL_PIXEL_FORMAT_RAW12:
258             // RAW10 and RAW12 are used for 10-bit and 12-bit raw data, they are single plane
259             *rowStride = mBuffer->stride;
260             return AMEDIA_OK;
261         case HAL_PIXEL_FORMAT_Y8:
262             if (mBuffer->stride % 16) {
263                 ALOGE("Stride %d is not 16 pixel aligned!", mBuffer->stride);
264                 return AMEDIA_ERROR_UNKNOWN;
265             }
266             *rowStride = mBuffer->stride;
267             return AMEDIA_OK;
268         case HAL_PIXEL_FORMAT_Y16:
269         case HAL_PIXEL_FORMAT_RAW16:
270             // In native side, strides are specified in pixels, not in bytes.
271             // Single plane 16bpp bayer data. even width/height,
272             // row stride multiple of 16 pixels (32 bytes)
273             if (mBuffer->stride % 16) {
274                 ALOGE("Stride %d is not 16 pixel aligned!", mBuffer->stride);
275                 return AMEDIA_ERROR_UNKNOWN;
276             }
277             *rowStride = mBuffer->stride * 2;
278             return AMEDIA_OK;
279         case HAL_PIXEL_FORMAT_RGB_565:
280             *rowStride = mBuffer->stride * 2;
281             return AMEDIA_OK;
282         case HAL_PIXEL_FORMAT_RGBA_8888:
283         case HAL_PIXEL_FORMAT_RGBX_8888:
284             *rowStride = mBuffer->stride * 4;
285             return AMEDIA_OK;
286         case HAL_PIXEL_FORMAT_RGB_888:
287             // Single plane, 24bpp.
288             *rowStride = mBuffer->stride * 3;
289             return AMEDIA_OK;
290         case HAL_PIXEL_FORMAT_BLOB:
291         case HAL_PIXEL_FORMAT_RAW_OPAQUE:
292             // Blob is used for JPEG/Raw opaque data. It is single plane and has 0 row stride and
293             // no row stride defined
294             return AMEDIA_ERROR_UNSUPPORTED;
295         default:
296             ALOGE("%s Pixel format: 0x%x is unsupported", __FUNCTION__, fmt);
297           return AMEDIA_ERROR_UNSUPPORTED;
298     }
299 }
300 
301 uint32_t
getJpegSize() const302 AImage::getJpegSize() const {
303     if (mBuffer == nullptr) {
304         LOG_ALWAYS_FATAL("Error: buffer is null");
305     }
306 
307     uint32_t size = 0;
308     uint32_t width = mBuffer->width;
309     uint8_t* jpegBuffer = mBuffer->data;
310 
311     // First check for JPEG transport header at the end of the buffer
312     uint8_t* header = jpegBuffer + (width - sizeof(struct camera3_jpeg_blob));
313     struct camera3_jpeg_blob* blob = (struct camera3_jpeg_blob*)(header);
314     if (blob->jpeg_blob_id == CAMERA3_JPEG_BLOB_ID) {
315         size = blob->jpeg_size;
316         ALOGV("%s: Jpeg size = %d", __FUNCTION__, size);
317     }
318 
319     // failed to find size, default to whole buffer
320     if (size == 0) {
321         /*
322          * This is a problem because not including the JPEG header
323          * means that in certain rare situations a regular JPEG blob
324          * will be misidentified as having a header, in which case
325          * we will get a garbage size value.
326          */
327         ALOGW("%s: No JPEG header detected, defaulting to size=width=%d",
328                 __FUNCTION__, width);
329         size = width;
330     }
331 
332     return size;
333 }
334 
335 media_status_t
getPlaneData(int planeIdx,uint8_t ** data,int * dataLength) const336 AImage::getPlaneData(int planeIdx,/*out*/uint8_t** data, /*out*/int* dataLength) const {
337     if (planeIdx < 0 || planeIdx >= mNumPlanes) {
338         ALOGE("Error: planeIdx %d out of bound [0,%d]",
339                 planeIdx, mNumPlanes - 1);
340         return AMEDIA_ERROR_INVALID_PARAMETER;
341     }
342     if (data == nullptr || dataLength == nullptr) {
343         return AMEDIA_ERROR_INVALID_PARAMETER;
344     }
345     if (isClosed()) {
346         ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
347         return AMEDIA_ERROR_INVALID_OBJECT;
348     }
349 
350     uint32_t dataSize, ySize, cSize, cStride;
351     uint8_t* cb = nullptr;
352     uint8_t* cr = nullptr;
353     uint8_t* pData = nullptr;
354     int bytesPerPixel = 0;
355     int32_t fmt = mBuffer->flexFormat;
356 
357     switch (fmt) {
358         case HAL_PIXEL_FORMAT_YCbCr_420_888:
359             pData = (planeIdx == 0) ? mBuffer->data :
360                     (planeIdx == 1) ? mBuffer->dataCb : mBuffer->dataCr;
361             // only map until last pixel
362             if (planeIdx == 0) {
363                 dataSize = mBuffer->stride * (mBuffer->height - 1) + mBuffer->width;
364             } else {
365                 dataSize = mBuffer->chromaStride * (mBuffer->height / 2 - 1) +
366                         mBuffer->chromaStep * (mBuffer->width / 2 - 1) + 1;
367             }
368             break;
369         // NV21
370         case HAL_PIXEL_FORMAT_YCrCb_420_SP:
371             cr = mBuffer->data + (mBuffer->stride * mBuffer->height);
372             cb = cr + 1;
373             // only map until last pixel
374             ySize = mBuffer->width * (mBuffer->height - 1) + mBuffer->width;
375             cSize = mBuffer->width * (mBuffer->height / 2 - 1) + mBuffer->width - 1;
376 
377             pData = (planeIdx == 0) ? mBuffer->data :
378                     (planeIdx == 1) ? cb : cr;
379             dataSize = (planeIdx == 0) ? ySize : cSize;
380             break;
381         case HAL_PIXEL_FORMAT_YV12:
382             // Y and C stride need to be 16 pixel aligned.
383             if (mBuffer->stride % 16) {
384                 ALOGE("Stride %d is not 16 pixel aligned!", mBuffer->stride);
385                 return AMEDIA_ERROR_UNKNOWN;
386             }
387 
388             ySize = mBuffer->stride * mBuffer->height;
389             cStride = ALIGN(mBuffer->stride / 2, 16);
390             cr = mBuffer->data + ySize;
391             cSize = cStride * mBuffer->height / 2;
392             cb = cr + cSize;
393 
394             pData = (planeIdx == 0) ? mBuffer->data :
395                     (planeIdx == 1) ? cb : cr;
396             dataSize = (planeIdx == 0) ? ySize : cSize;
397             break;
398         case HAL_PIXEL_FORMAT_Y8:
399             // Single plane, 8bpp.
400 
401             pData = mBuffer->data;
402             dataSize = mBuffer->stride * mBuffer->height;
403             break;
404         case HAL_PIXEL_FORMAT_Y16:
405             bytesPerPixel = 2;
406 
407             pData = mBuffer->data;
408             dataSize = mBuffer->stride * mBuffer->height * bytesPerPixel;
409             break;
410         case HAL_PIXEL_FORMAT_BLOB:
411             // Used for JPEG data, height must be 1, width == size, single plane.
412             if (mBuffer->height != 1) {
413                 ALOGE("Jpeg should have height value one but got %d", mBuffer->height);
414                 return AMEDIA_ERROR_UNKNOWN;
415             }
416 
417             pData = mBuffer->data;
418             dataSize = getJpegSize();
419             break;
420         case HAL_PIXEL_FORMAT_RAW16:
421             // Single plane 16bpp bayer data.
422             bytesPerPixel = 2;
423             pData = mBuffer->data;
424             dataSize = mBuffer->stride * mBuffer->height * bytesPerPixel;
425             break;
426         case HAL_PIXEL_FORMAT_RAW_OPAQUE:
427             // Used for RAW_OPAQUE data, height must be 1, width == size, single plane.
428             if (mBuffer->height != 1) {
429                 ALOGE("RAW_OPAQUE should have height value one but got %d", mBuffer->height);
430                 return AMEDIA_ERROR_UNKNOWN;
431             }
432             pData = mBuffer->data;
433             dataSize = mBuffer->width;
434             break;
435         case HAL_PIXEL_FORMAT_RAW10:
436             // Single plane 10bpp bayer data.
437             if (mBuffer->width % 4) {
438                 ALOGE("Width is not multiple of 4 %d", mBuffer->width);
439                 return AMEDIA_ERROR_UNKNOWN;
440             }
441             if (mBuffer->height % 2) {
442                 ALOGE("Height is not multiple of 2 %d", mBuffer->height);
443                 return AMEDIA_ERROR_UNKNOWN;
444             }
445             if (mBuffer->stride < (mBuffer->width * 10 / 8)) {
446                 ALOGE("stride (%d) should be at least %d",
447                         mBuffer->stride, mBuffer->width * 10 / 8);
448                 return AMEDIA_ERROR_UNKNOWN;
449             }
450             pData = mBuffer->data;
451             dataSize = mBuffer->stride * mBuffer->height;
452             break;
453         case HAL_PIXEL_FORMAT_RAW12:
454             // Single plane 10bpp bayer data.
455             if (mBuffer->width % 4) {
456                 ALOGE("Width is not multiple of 4 %d", mBuffer->width);
457                 return AMEDIA_ERROR_UNKNOWN;
458             }
459             if (mBuffer->height % 2) {
460                 ALOGE("Height is not multiple of 2 %d", mBuffer->height);
461                 return AMEDIA_ERROR_UNKNOWN;
462             }
463             if (mBuffer->stride < (mBuffer->width * 12 / 8)) {
464                 ALOGE("stride (%d) should be at least %d",
465                         mBuffer->stride, mBuffer->width * 12 / 8);
466                 return AMEDIA_ERROR_UNKNOWN;
467             }
468             pData = mBuffer->data;
469             dataSize = mBuffer->stride * mBuffer->height;
470             break;
471         case HAL_PIXEL_FORMAT_RGBA_8888:
472         case HAL_PIXEL_FORMAT_RGBX_8888:
473             // Single plane, 32bpp.
474             bytesPerPixel = 4;
475             pData = mBuffer->data;
476             dataSize = mBuffer->stride * mBuffer->height * bytesPerPixel;
477             break;
478         case HAL_PIXEL_FORMAT_RGB_565:
479             // Single plane, 16bpp.
480             bytesPerPixel = 2;
481             pData = mBuffer->data;
482             dataSize = mBuffer->stride * mBuffer->height * bytesPerPixel;
483             break;
484         case HAL_PIXEL_FORMAT_RGB_888:
485             // Single plane, 24bpp.
486             bytesPerPixel = 3;
487             pData = mBuffer->data;
488             dataSize = mBuffer->stride * mBuffer->height * bytesPerPixel;
489             break;
490         default:
491             ALOGE("Pixel format: 0x%x is unsupported", fmt);
492             return AMEDIA_ERROR_UNSUPPORTED;
493     }
494 
495     *data = pData;
496     *dataLength = dataSize;
497     return AMEDIA_OK;
498 }
499 
500 EXPORT
AImage_delete(AImage * image)501 void AImage_delete(AImage* image) {
502     ALOGV("%s", __FUNCTION__);
503     if (image != nullptr) {
504         image->lockReader();
505         image->close();
506         image->unlockReader();
507         if (!image->isClosed()) {
508             LOG_ALWAYS_FATAL("Image close failed!");
509         }
510         image->free();
511     }
512     return;
513 }
514 
515 EXPORT
AImage_getWidth(const AImage * image,int32_t * width)516 media_status_t AImage_getWidth(const AImage* image, /*out*/int32_t* width) {
517     ALOGV("%s", __FUNCTION__);
518     if (image == nullptr || width == nullptr) {
519         ALOGE("%s: bad argument. image %p width %p",
520                 __FUNCTION__, image, width);
521         return AMEDIA_ERROR_INVALID_PARAMETER;
522     }
523     return image->getWidth(width);
524 }
525 
526 EXPORT
AImage_getHeight(const AImage * image,int32_t * height)527 media_status_t AImage_getHeight(const AImage* image, /*out*/int32_t* height) {
528     ALOGV("%s", __FUNCTION__);
529     if (image == nullptr || height == nullptr) {
530         ALOGE("%s: bad argument. image %p height %p",
531                 __FUNCTION__, image, height);
532         return AMEDIA_ERROR_INVALID_PARAMETER;
533     }
534     return image->getHeight(height);
535 }
536 
537 EXPORT
AImage_getFormat(const AImage * image,int32_t * format)538 media_status_t AImage_getFormat(const AImage* image, /*out*/int32_t* format) {
539     ALOGV("%s", __FUNCTION__);
540     if (image == nullptr || format == nullptr) {
541         ALOGE("%s: bad argument. image %p format %p",
542                 __FUNCTION__, image, format);
543         return AMEDIA_ERROR_INVALID_PARAMETER;
544     }
545     return image->getFormat(format);
546 }
547 
548 EXPORT
AImage_getCropRect(const AImage * image,AImageCropRect * rect)549 media_status_t AImage_getCropRect(const AImage* image, /*out*/AImageCropRect* rect) {
550     ALOGV("%s", __FUNCTION__);
551     if (image == nullptr || rect == nullptr) {
552         ALOGE("%s: bad argument. image %p rect %p",
553                 __FUNCTION__, image, rect);
554         return AMEDIA_ERROR_INVALID_PARAMETER;
555     }
556     // For now AImage only supports camera outputs where cropRect is always full window
557     int32_t width = -1;
558     media_status_t ret = image->getWidth(&width);
559     if (ret != AMEDIA_OK) {
560         return ret;
561     }
562     int32_t height = -1;
563     ret = image->getHeight(&height);
564     if (ret != AMEDIA_OK) {
565         return ret;
566     }
567     rect->left = 0;
568     rect->top = 0;
569     rect->right = width;
570     rect->bottom = height;
571     return AMEDIA_OK;
572 }
573 
574 EXPORT
AImage_getTimestamp(const AImage * image,int64_t * timestampNs)575 media_status_t AImage_getTimestamp(const AImage* image, /*out*/int64_t* timestampNs) {
576     ALOGV("%s", __FUNCTION__);
577     if (image == nullptr || timestampNs == nullptr) {
578         ALOGE("%s: bad argument. image %p timestampNs %p",
579                 __FUNCTION__, image, timestampNs);
580         return AMEDIA_ERROR_INVALID_PARAMETER;
581     }
582     return image->getTimestamp(timestampNs);
583 }
584 
585 EXPORT
AImage_getNumberOfPlanes(const AImage * image,int32_t * numPlanes)586 media_status_t AImage_getNumberOfPlanes(const AImage* image, /*out*/int32_t* numPlanes) {
587     ALOGV("%s", __FUNCTION__);
588     if (image == nullptr || numPlanes == nullptr) {
589         ALOGE("%s: bad argument. image %p numPlanes %p",
590                 __FUNCTION__, image, numPlanes);
591         return AMEDIA_ERROR_INVALID_PARAMETER;
592     }
593     return image->getNumPlanes(numPlanes);
594 }
595 
596 EXPORT
AImage_getPlanePixelStride(const AImage * image,int planeIdx,int32_t * pixelStride)597 media_status_t AImage_getPlanePixelStride(
598         const AImage* image, int planeIdx, /*out*/int32_t* pixelStride) {
599     ALOGV("%s", __FUNCTION__);
600     if (image == nullptr || pixelStride == nullptr) {
601         ALOGE("%s: bad argument. image %p pixelStride %p",
602                 __FUNCTION__, image, pixelStride);
603         return AMEDIA_ERROR_INVALID_PARAMETER;
604     }
605     return image->getPlanePixelStride(planeIdx, pixelStride);
606 }
607 
608 EXPORT
AImage_getPlaneRowStride(const AImage * image,int planeIdx,int32_t * rowStride)609 media_status_t AImage_getPlaneRowStride(
610         const AImage* image, int planeIdx, /*out*/int32_t* rowStride) {
611     ALOGV("%s", __FUNCTION__);
612     if (image == nullptr || rowStride == nullptr) {
613         ALOGE("%s: bad argument. image %p rowStride %p",
614                 __FUNCTION__, image, rowStride);
615         return AMEDIA_ERROR_INVALID_PARAMETER;
616     }
617     return image->getPlaneRowStride(planeIdx, rowStride);
618 }
619 
620 EXPORT
AImage_getPlaneData(const AImage * image,int planeIdx,uint8_t ** data,int * dataLength)621 media_status_t AImage_getPlaneData(
622         const AImage* image, int planeIdx,
623         /*out*/uint8_t** data, /*out*/int* dataLength) {
624     ALOGV("%s", __FUNCTION__);
625     if (image == nullptr || data == nullptr || dataLength == nullptr) {
626         ALOGE("%s: bad argument. image %p data %p dataLength %p",
627                 __FUNCTION__, image, data, dataLength);
628         return AMEDIA_ERROR_INVALID_PARAMETER;
629     }
630     return image->getPlaneData(planeIdx, data, dataLength);
631 }
632