Home | History | Annotate | Download | only in ndk
      1 /*
      2  * Copyright (C) 2014 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 "NdkMediaCodec"
     21 
     22 #include "NdkMediaCodec.h"
     23 #include "NdkMediaError.h"
     24 #include "NdkMediaCryptoPriv.h"
     25 #include "NdkMediaFormatPriv.h"
     26 
     27 #include <utils/Log.h>
     28 #include <utils/StrongPointer.h>
     29 #include <gui/Surface.h>
     30 
     31 #include <media/stagefright/foundation/ALooper.h>
     32 #include <media/stagefright/foundation/AMessage.h>
     33 
     34 #include <media/stagefright/PersistentSurface.h>
     35 #include <media/stagefright/MediaCodec.h>
     36 #include <media/stagefright/MediaErrors.h>
     37 #include <media/MediaCodecBuffer.h>
     38 #include <android/native_window.h>
     39 
     40 using namespace android;
     41 
     42 
     43 static media_status_t translate_error(status_t err) {
     44     if (err == OK) {
     45         return AMEDIA_OK;
     46     } else if (err == -EAGAIN) {
     47         return (media_status_t) AMEDIACODEC_INFO_TRY_AGAIN_LATER;
     48     }
     49     ALOGE("sf error code: %d", err);
     50     return AMEDIA_ERROR_UNKNOWN;
     51 }
     52 
     53 enum {
     54     kWhatActivityNotify,
     55     kWhatRequestActivityNotifications,
     56     kWhatStopActivityNotifications,
     57 };
     58 
     59 struct AMediaCodecPersistentSurface : public Surface {
     60     sp<PersistentSurface> mPersistentSurface;
     61     AMediaCodecPersistentSurface(
     62             const sp<IGraphicBufferProducer>& igbp,
     63             const sp<PersistentSurface>& ps)
     64             : Surface(igbp) {
     65         mPersistentSurface = ps;
     66     }
     67     virtual ~AMediaCodecPersistentSurface() {
     68         //mPersistentSurface ref will be let go off here
     69     }
     70 };
     71 
     72 class CodecHandler: public AHandler {
     73 private:
     74     AMediaCodec* mCodec;
     75 public:
     76     explicit CodecHandler(AMediaCodec *codec);
     77     virtual void onMessageReceived(const sp<AMessage> &msg);
     78 };
     79 
     80 typedef void (*OnCodecEvent)(AMediaCodec *codec, void *userdata);
     81 
     82 struct AMediaCodec {
     83     sp<android::MediaCodec> mCodec;
     84     sp<ALooper> mLooper;
     85     sp<CodecHandler> mHandler;
     86     sp<AMessage> mActivityNotification;
     87     int32_t mGeneration;
     88     bool mRequestedActivityNotification;
     89     OnCodecEvent mCallback;
     90     void *mCallbackUserData;
     91 };
     92 
     93 CodecHandler::CodecHandler(AMediaCodec *codec) {
     94     mCodec = codec;
     95 }
     96 
     97 void CodecHandler::onMessageReceived(const sp<AMessage> &msg) {
     98 
     99     switch (msg->what()) {
    100         case kWhatRequestActivityNotifications:
    101         {
    102             if (mCodec->mRequestedActivityNotification) {
    103                 break;
    104             }
    105 
    106             mCodec->mCodec->requestActivityNotification(mCodec->mActivityNotification);
    107             mCodec->mRequestedActivityNotification = true;
    108             break;
    109         }
    110 
    111         case kWhatActivityNotify:
    112         {
    113             {
    114                 int32_t generation;
    115                 msg->findInt32("generation", &generation);
    116 
    117                 if (generation != mCodec->mGeneration) {
    118                     // stale
    119                     break;
    120                 }
    121 
    122                 mCodec->mRequestedActivityNotification = false;
    123             }
    124 
    125             if (mCodec->mCallback) {
    126                 mCodec->mCallback(mCodec, mCodec->mCallbackUserData);
    127             }
    128             break;
    129         }
    130 
    131         case kWhatStopActivityNotifications:
    132         {
    133             sp<AReplyToken> replyID;
    134             msg->senderAwaitsResponse(&replyID);
    135 
    136             mCodec->mGeneration++;
    137             mCodec->mRequestedActivityNotification = false;
    138 
    139             sp<AMessage> response = new AMessage;
    140             response->postReply(replyID);
    141             break;
    142         }
    143 
    144         default:
    145             ALOGE("shouldn't be here");
    146             break;
    147     }
    148 
    149 }
    150 
    151 
    152 static void requestActivityNotification(AMediaCodec *codec) {
    153     (new AMessage(kWhatRequestActivityNotifications, codec->mHandler))->post();
    154 }
    155 
    156 extern "C" {
    157 
    158 static AMediaCodec * createAMediaCodec(const char *name, bool name_is_type, bool encoder) {
    159     AMediaCodec *mData = new AMediaCodec();
    160     mData->mLooper = new ALooper;
    161     mData->mLooper->setName("NDK MediaCodec_looper");
    162     size_t res = mData->mLooper->start(
    163             false,      // runOnCallingThread
    164             true,       // canCallJava XXX
    165             PRIORITY_FOREGROUND);
    166     if (res != OK) {
    167         ALOGE("Failed to start the looper");
    168         AMediaCodec_delete(mData);
    169         return NULL;
    170     }
    171     if (name_is_type) {
    172         mData->mCodec = android::MediaCodec::CreateByType(mData->mLooper, name, encoder);
    173     } else {
    174         mData->mCodec = android::MediaCodec::CreateByComponentName(mData->mLooper, name);
    175     }
    176     if (mData->mCodec == NULL) {  // failed to create codec
    177         AMediaCodec_delete(mData);
    178         return NULL;
    179     }
    180     mData->mHandler = new CodecHandler(mData);
    181     mData->mLooper->registerHandler(mData->mHandler);
    182     mData->mGeneration = 1;
    183     mData->mRequestedActivityNotification = false;
    184     mData->mCallback = NULL;
    185 
    186     return mData;
    187 }
    188 
    189 EXPORT
    190 AMediaCodec* AMediaCodec_createCodecByName(const char *name) {
    191     return createAMediaCodec(name, false, false);
    192 }
    193 
    194 EXPORT
    195 AMediaCodec* AMediaCodec_createDecoderByType(const char *mime_type) {
    196     return createAMediaCodec(mime_type, true, false);
    197 }
    198 
    199 EXPORT
    200 AMediaCodec* AMediaCodec_createEncoderByType(const char *name) {
    201     return createAMediaCodec(name, true, true);
    202 }
    203 
    204 EXPORT
    205 media_status_t AMediaCodec_delete(AMediaCodec *mData) {
    206     if (mData != NULL) {
    207         if (mData->mCodec != NULL) {
    208             mData->mCodec->release();
    209             mData->mCodec.clear();
    210         }
    211 
    212         if (mData->mLooper != NULL) {
    213             if (mData->mHandler != NULL) {
    214                 mData->mLooper->unregisterHandler(mData->mHandler->id());
    215             }
    216             mData->mLooper->stop();
    217             mData->mLooper.clear();
    218         }
    219         delete mData;
    220     }
    221     return AMEDIA_OK;
    222 }
    223 
    224 EXPORT
    225 media_status_t AMediaCodec_configure(
    226         AMediaCodec *mData,
    227         const AMediaFormat* format,
    228         ANativeWindow* window,
    229         AMediaCrypto *crypto,
    230         uint32_t flags) {
    231     sp<AMessage> nativeFormat;
    232     AMediaFormat_getFormat(format, &nativeFormat);
    233     ALOGV("configure with format: %s", nativeFormat->debugString(0).c_str());
    234     sp<Surface> surface = NULL;
    235     if (window != NULL) {
    236         surface = (Surface*) window;
    237     }
    238 
    239     return translate_error(mData->mCodec->configure(nativeFormat, surface,
    240             crypto ? crypto->mCrypto : NULL, flags));
    241 }
    242 
    243 EXPORT
    244 media_status_t AMediaCodec_start(AMediaCodec *mData) {
    245     status_t ret =  mData->mCodec->start();
    246     if (ret != OK) {
    247         return translate_error(ret);
    248     }
    249     mData->mActivityNotification = new AMessage(kWhatActivityNotify, mData->mHandler);
    250     mData->mActivityNotification->setInt32("generation", mData->mGeneration);
    251     requestActivityNotification(mData);
    252     return AMEDIA_OK;
    253 }
    254 
    255 EXPORT
    256 media_status_t AMediaCodec_stop(AMediaCodec *mData) {
    257     media_status_t ret = translate_error(mData->mCodec->stop());
    258 
    259     sp<AMessage> msg = new AMessage(kWhatStopActivityNotifications, mData->mHandler);
    260     sp<AMessage> response;
    261     msg->postAndAwaitResponse(&response);
    262     mData->mActivityNotification.clear();
    263 
    264     return ret;
    265 }
    266 
    267 EXPORT
    268 media_status_t AMediaCodec_flush(AMediaCodec *mData) {
    269     return translate_error(mData->mCodec->flush());
    270 }
    271 
    272 EXPORT
    273 ssize_t AMediaCodec_dequeueInputBuffer(AMediaCodec *mData, int64_t timeoutUs) {
    274     size_t idx;
    275     status_t ret = mData->mCodec->dequeueInputBuffer(&idx, timeoutUs);
    276     requestActivityNotification(mData);
    277     if (ret == OK) {
    278         return idx;
    279     }
    280     return translate_error(ret);
    281 }
    282 
    283 EXPORT
    284 uint8_t* AMediaCodec_getInputBuffer(AMediaCodec *mData, size_t idx, size_t *out_size) {
    285     android::Vector<android::sp<android::MediaCodecBuffer> > abufs;
    286     if (mData->mCodec->getInputBuffers(&abufs) == 0) {
    287         size_t n = abufs.size();
    288         if (idx >= n) {
    289             ALOGE("buffer index %zu out of range", idx);
    290             return NULL;
    291         }
    292         if (abufs[idx] == NULL) {
    293             ALOGE("buffer index %zu is NULL", idx);
    294             return NULL;
    295         }
    296         if (out_size != NULL) {
    297             *out_size = abufs[idx]->capacity();
    298         }
    299         return abufs[idx]->data();
    300     }
    301     ALOGE("couldn't get input buffers");
    302     return NULL;
    303 }
    304 
    305 EXPORT
    306 uint8_t* AMediaCodec_getOutputBuffer(AMediaCodec *mData, size_t idx, size_t *out_size) {
    307     android::Vector<android::sp<android::MediaCodecBuffer> > abufs;
    308     if (mData->mCodec->getOutputBuffers(&abufs) == 0) {
    309         size_t n = abufs.size();
    310         if (idx >= n) {
    311             ALOGE("buffer index %zu out of range", idx);
    312             return NULL;
    313         }
    314         if (out_size != NULL) {
    315             *out_size = abufs[idx]->capacity();
    316         }
    317         return abufs[idx]->data();
    318     }
    319     ALOGE("couldn't get output buffers");
    320     return NULL;
    321 }
    322 
    323 EXPORT
    324 media_status_t AMediaCodec_queueInputBuffer(AMediaCodec *mData,
    325         size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags) {
    326 
    327     AString errorMsg;
    328     status_t ret = mData->mCodec->queueInputBuffer(idx, offset, size, time, flags, &errorMsg);
    329     return translate_error(ret);
    330 }
    331 
    332 EXPORT
    333 ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec *mData,
    334         AMediaCodecBufferInfo *info, int64_t timeoutUs) {
    335     size_t idx;
    336     size_t offset;
    337     size_t size;
    338     uint32_t flags;
    339     int64_t presentationTimeUs;
    340     status_t ret = mData->mCodec->dequeueOutputBuffer(&idx, &offset, &size, &presentationTimeUs,
    341             &flags, timeoutUs);
    342     requestActivityNotification(mData);
    343     switch (ret) {
    344         case OK:
    345             info->offset = offset;
    346             info->size = size;
    347             info->flags = flags;
    348             info->presentationTimeUs = presentationTimeUs;
    349             return idx;
    350         case -EAGAIN:
    351             return AMEDIACODEC_INFO_TRY_AGAIN_LATER;
    352         case android::INFO_FORMAT_CHANGED:
    353             return AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED;
    354         case INFO_OUTPUT_BUFFERS_CHANGED:
    355             return AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED;
    356         default:
    357             break;
    358     }
    359     return translate_error(ret);
    360 }
    361 
    362 EXPORT
    363 AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec *mData) {
    364     sp<AMessage> format;
    365     mData->mCodec->getOutputFormat(&format);
    366     return AMediaFormat_fromMsg(&format);
    367 }
    368 
    369 EXPORT
    370 media_status_t AMediaCodec_releaseOutputBuffer(AMediaCodec *mData, size_t idx, bool render) {
    371     if (render) {
    372         return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx));
    373     } else {
    374         return translate_error(mData->mCodec->releaseOutputBuffer(idx));
    375     }
    376 }
    377 
    378 EXPORT
    379 media_status_t AMediaCodec_releaseOutputBufferAtTime(
    380         AMediaCodec *mData, size_t idx, int64_t timestampNs) {
    381     ALOGV("render @ %" PRId64, timestampNs);
    382     return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx, timestampNs));
    383 }
    384 
    385 EXPORT
    386 media_status_t AMediaCodec_setOutputSurface(AMediaCodec *mData, ANativeWindow* window) {
    387     sp<Surface> surface = NULL;
    388     if (window != NULL) {
    389         surface = (Surface*) window;
    390     }
    391     return translate_error(mData->mCodec->setSurface(surface));
    392 }
    393 
    394 EXPORT
    395 media_status_t AMediaCodec_createInputSurface(AMediaCodec *mData, ANativeWindow **surface) {
    396     if (surface == NULL || mData == NULL) {
    397         return AMEDIA_ERROR_INVALID_PARAMETER;
    398     }
    399     *surface = NULL;
    400 
    401     sp<IGraphicBufferProducer> igbp = NULL;
    402     status_t err = mData->mCodec->createInputSurface(&igbp);
    403     if (err != NO_ERROR) {
    404         return translate_error(err);
    405     }
    406 
    407     *surface = new Surface(igbp);
    408     ANativeWindow_acquire(*surface);
    409     return AMEDIA_OK;
    410 }
    411 
    412 EXPORT
    413 media_status_t AMediaCodec_createPersistentInputSurface(ANativeWindow **surface) {
    414     if (surface == NULL) {
    415         return AMEDIA_ERROR_INVALID_PARAMETER;
    416     }
    417     *surface = NULL;
    418 
    419     sp<PersistentSurface> ps = MediaCodec::CreatePersistentInputSurface();
    420     if (ps == NULL) {
    421         return AMEDIA_ERROR_UNKNOWN;
    422     }
    423 
    424     sp<IGraphicBufferProducer> igbp = ps->getBufferProducer();
    425     if (igbp == NULL) {
    426         return AMEDIA_ERROR_UNKNOWN;
    427     }
    428 
    429     *surface = new AMediaCodecPersistentSurface(igbp, ps);
    430     ANativeWindow_acquire(*surface);
    431 
    432     return AMEDIA_OK;
    433 }
    434 
    435 EXPORT
    436 media_status_t AMediaCodec_setInputSurface(
    437         AMediaCodec *mData, ANativeWindow *surface) {
    438 
    439     if (surface == NULL || mData == NULL) {
    440         return AMEDIA_ERROR_INVALID_PARAMETER;
    441     }
    442 
    443     AMediaCodecPersistentSurface *aMediaPersistentSurface =
    444             static_cast<AMediaCodecPersistentSurface *>(surface);
    445     if (aMediaPersistentSurface->mPersistentSurface == NULL) {
    446         return AMEDIA_ERROR_INVALID_PARAMETER;
    447     }
    448 
    449     return translate_error(mData->mCodec->setInputSurface(
    450             aMediaPersistentSurface->mPersistentSurface));
    451 }
    452 
    453 EXPORT
    454 media_status_t AMediaCodec_setParameters(
    455         AMediaCodec *mData, const AMediaFormat* params) {
    456     if (params == NULL || mData == NULL) {
    457         return AMEDIA_ERROR_INVALID_PARAMETER;
    458     }
    459     sp<AMessage> nativeParams;
    460     AMediaFormat_getFormat(params, &nativeParams);
    461     ALOGV("setParameters: %s", nativeParams->debugString(0).c_str());
    462 
    463     return translate_error(mData->mCodec->setParameters(nativeParams));
    464 }
    465 
    466 EXPORT
    467 media_status_t AMediaCodec_signalEndOfInputStream(AMediaCodec *mData) {
    468 
    469     if (mData == NULL) {
    470         return AMEDIA_ERROR_INVALID_PARAMETER;
    471     }
    472 
    473     status_t err = mData->mCodec->signalEndOfInputStream();
    474     if (err == INVALID_OPERATION) {
    475         return AMEDIA_ERROR_INVALID_OPERATION;
    476     }
    477 
    478     return translate_error(err);
    479 
    480 }
    481 
    482 //EXPORT
    483 media_status_t AMediaCodec_setNotificationCallback(AMediaCodec *mData, OnCodecEvent callback,
    484         void *userdata) {
    485     mData->mCallback = callback;
    486     mData->mCallbackUserData = userdata;
    487     return AMEDIA_OK;
    488 }
    489 
    490 typedef struct AMediaCodecCryptoInfo {
    491         int numsubsamples;
    492         uint8_t key[16];
    493         uint8_t iv[16];
    494         cryptoinfo_mode_t mode;
    495         cryptoinfo_pattern_t pattern;
    496         size_t *clearbytes;
    497         size_t *encryptedbytes;
    498 } AMediaCodecCryptoInfo;
    499 
    500 EXPORT
    501 media_status_t AMediaCodec_queueSecureInputBuffer(
    502         AMediaCodec* codec,
    503         size_t idx,
    504         off_t offset,
    505         AMediaCodecCryptoInfo* crypto,
    506         uint64_t time,
    507         uint32_t flags) {
    508 
    509     CryptoPlugin::SubSample *subSamples = new CryptoPlugin::SubSample[crypto->numsubsamples];
    510     for (int i = 0; i < crypto->numsubsamples; i++) {
    511         subSamples[i].mNumBytesOfClearData = crypto->clearbytes[i];
    512         subSamples[i].mNumBytesOfEncryptedData = crypto->encryptedbytes[i];
    513     }
    514 
    515     CryptoPlugin::Pattern pattern;
    516     pattern.mEncryptBlocks = crypto->pattern.encryptBlocks;
    517     pattern.mSkipBlocks = crypto->pattern.skipBlocks;
    518 
    519     AString errormsg;
    520     status_t err  = codec->mCodec->queueSecureInputBuffer(idx,
    521             offset,
    522             subSamples,
    523             crypto->numsubsamples,
    524             crypto->key,
    525             crypto->iv,
    526             (CryptoPlugin::Mode)crypto->mode,
    527             pattern,
    528             time,
    529             flags,
    530             &errormsg);
    531     if (err != 0) {
    532         ALOGE("queSecureInputBuffer: %s", errormsg.c_str());
    533     }
    534     delete [] subSamples;
    535     return translate_error(err);
    536 }
    537 
    538 
    539 EXPORT
    540 void AMediaCodecCryptoInfo_setPattern(AMediaCodecCryptoInfo *info,
    541         cryptoinfo_pattern_t *pattern) {
    542     info->pattern.encryptBlocks = pattern->encryptBlocks;
    543     info->pattern.skipBlocks = pattern->skipBlocks;
    544 }
    545 
    546 EXPORT
    547 AMediaCodecCryptoInfo *AMediaCodecCryptoInfo_new(
    548         int numsubsamples,
    549         uint8_t key[16],
    550         uint8_t iv[16],
    551         cryptoinfo_mode_t mode,
    552         size_t *clearbytes,
    553         size_t *encryptedbytes) {
    554 
    555     // size needed to store all the crypto data
    556     size_t cryptosize = sizeof(AMediaCodecCryptoInfo) + sizeof(size_t) * numsubsamples * 2;
    557     AMediaCodecCryptoInfo *ret = (AMediaCodecCryptoInfo*) malloc(cryptosize);
    558     if (!ret) {
    559         ALOGE("couldn't allocate %zu bytes", cryptosize);
    560         return NULL;
    561     }
    562     ret->numsubsamples = numsubsamples;
    563     memcpy(ret->key, key, 16);
    564     memcpy(ret->iv, iv, 16);
    565     ret->mode = mode;
    566     ret->pattern.encryptBlocks = 0;
    567     ret->pattern.skipBlocks = 0;
    568 
    569     // clearbytes and encryptedbytes point at the actual data, which follows
    570     ret->clearbytes = (size_t*) (ret + 1); // point immediately after the struct
    571     ret->encryptedbytes = ret->clearbytes + numsubsamples; // point after the clear sizes
    572 
    573     memcpy(ret->clearbytes, clearbytes, numsubsamples * sizeof(size_t));
    574     memcpy(ret->encryptedbytes, encryptedbytes, numsubsamples * sizeof(size_t));
    575 
    576     return ret;
    577 }
    578 
    579 
    580 EXPORT
    581 media_status_t AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo* info) {
    582     free(info);
    583     return AMEDIA_OK;
    584 }
    585 
    586 EXPORT
    587 size_t AMediaCodecCryptoInfo_getNumSubSamples(AMediaCodecCryptoInfo* ci) {
    588     return ci->numsubsamples;
    589 }
    590 
    591 EXPORT
    592 media_status_t AMediaCodecCryptoInfo_getKey(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
    593     if (!ci) {
    594         return AMEDIA_ERROR_INVALID_OBJECT;
    595     }
    596     if (!dst) {
    597         return AMEDIA_ERROR_INVALID_PARAMETER;
    598     }
    599     memcpy(dst, ci->key, 16);
    600     return AMEDIA_OK;
    601 }
    602 
    603 EXPORT
    604 media_status_t AMediaCodecCryptoInfo_getIV(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
    605     if (!ci) {
    606         return AMEDIA_ERROR_INVALID_OBJECT;
    607     }
    608     if (!dst) {
    609         return AMEDIA_ERROR_INVALID_PARAMETER;
    610     }
    611     memcpy(dst, ci->iv, 16);
    612     return AMEDIA_OK;
    613 }
    614 
    615 EXPORT
    616 cryptoinfo_mode_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo* ci) {
    617     if (!ci) {
    618         return (cryptoinfo_mode_t) AMEDIA_ERROR_INVALID_OBJECT;
    619     }
    620     return ci->mode;
    621 }
    622 
    623 EXPORT
    624 media_status_t AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
    625     if (!ci) {
    626         return AMEDIA_ERROR_INVALID_OBJECT;
    627     }
    628     if (!dst) {
    629         return AMEDIA_ERROR_INVALID_PARAMETER;
    630     }
    631     memcpy(dst, ci->clearbytes, sizeof(size_t) * ci->numsubsamples);
    632     return AMEDIA_OK;
    633 }
    634 
    635 EXPORT
    636 media_status_t AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
    637     if (!ci) {
    638         return AMEDIA_ERROR_INVALID_OBJECT;
    639     }
    640     if (!dst) {
    641         return AMEDIA_ERROR_INVALID_PARAMETER;
    642     }
    643     memcpy(dst, ci->encryptedbytes, sizeof(size_t) * ci->numsubsamples);
    644     return AMEDIA_OK;
    645 }
    646 
    647 } // extern "C"
    648 
    649