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
28using namespace android;
29
30#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
31
32AImage::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
41AImage::~AImage() {
42    if (!mIsClosed) {
43        LOG_ALWAYS_FATAL(
44                "Error: AImage %p is deleted before returning buffer to AImageReader!", this);
45    }
46}
47
48bool
49AImage::isClosed() const {
50    Mutex::Autolock _l(mLock);
51    return mIsClosed;
52}
53
54void
55AImage::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
72void
73AImage::free() {
74    if (!isClosed()) {
75        ALOGE("Cannot free AImage before close!");
76        return;
77    }
78    Mutex::Autolock _l(mLock);
79    delete this;
80}
81
82void
83AImage::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
92void
93AImage::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
102media_status_t
103AImage::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
116media_status_t
117AImage::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
130media_status_t
131AImage::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
144media_status_t
145AImage::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
158media_status_t
159AImage::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
172media_status_t
173AImage::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
227media_status_t
228AImage::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
301uint32_t
302AImage::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
335media_status_t
336AImage::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
500EXPORT
501void 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
515EXPORT
516media_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
526EXPORT
527media_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
537EXPORT
538media_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
548EXPORT
549media_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
574EXPORT
575media_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
585EXPORT
586media_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
596EXPORT
597media_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
608EXPORT
609media_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
620EXPORT
621media_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