Извлечение кадров из видео с помощью ndk media api в Android studio без использования ffmpeg или opencv

#android #video #android-ndk #media #frames

Вопрос:

Я пытаюсь извлечь кадры из медиафайла с помощью NDK. Код, написанный на уровне C . Я прилагаю написанный мной код.

Эта функция находится в NDK, используемой для хранения кадров в каталоге SD-карт

 void WriteFile(AImage* image) {

    int planeCount;
    
    media_status_t status = AImage_getNumberOfPlanes(image, amp;planeCount);
    
   ASSERT(status == AMEDIA_OK amp;amp; planeCount == 1,
           "Error: getNumberOfPlanes() planeCount = %d", planeCount);
    uint8_t *data = nullptr;
    int len = 0;
    AImage_getPlaneData(image, 0, amp;data, amp;len);

    DIR *dir = opendir(kDirName);
    if (dir) {
        closedir(dir);
    } else {
        std::string cmd = "mkdir -p ";
        cmd  = kDirName;
        system(cmd.c_str());
    }

    struct timespec ts {
            0, 0
    };
    
    clock_gettime(CLOCK_REALTIME, amp;ts);
    struct tm localTime;
    localtime_r(amp;ts.tv_sec, amp;localTime);

    std::string fileName = kDirName;
    std::string dash("-");
    fileName  = kFileName   std::to_string(localTime.tm_mon)  
                std::to_string(localTime.tm_mday)   dash  
                std::to_string(localTime.tm_hour)  
                std::to_string(localTime.tm_min)  
                std::to_string(localTime.tm_sec)   ".jpg";
    FILE *file = fopen(fileName.c_str(), "wb");
    if (file amp;amp; data amp;amp; len) {
        fwrite(data, 1, len, file);
        fclose(file);

        if (callback_) {
            callback_(callbackCtx_, fileName.c_str());
        }
    } else {
        if (file)
            fclose(file);
    }
    AImage_delete(image);
}

void imageCallback(void *context, AImageReader *reader) {

    int32_t format;
    media_status_t status = AImageReader_getFormat (reader, amp;format);

    AImage* image;
    status = AImageReader_acquireLatestImage(reader, amp;image);

    status = AImage_getFormat(image, amp;format);

    // Create a thread and write out the jpeg files
    std::thread writeFileHandler(amp;ImageReader::WriteFile, this, image);
    writeFileHandler.detach();

}

This function resides in C   layer which is called from Java layer 

extern "C" {

jboolean Java_com_example_nativecodec_NativeCodec_createSampleMediaPlayer(JNIEnv* env,
                                                                             jclass clazz, jobject assetMgr, jstring filename)
{
    LOGV("@@@ create");

    // convert Java string to UTF-8
    const char *utf8 = env->GetStringUTFChars(filename, NULL);
    LOGV("opening %s", utf8);

    off_t outStart, outLen;
    int fd = AAsset_openFileDescriptor(AAssetManager_open(AAssetManager_fromJava(env, assetMgr), 
    utf8, 0), amp;outStart, amp;outLen);

    env->ReleaseStringUTFChars(filename, utf8);
    
    if (fd < 0) {
        LOGE("failed to open file: %s %d (%s)", utf8, fd, strerror(errno));
        return JNI_FALSE;
    }

    data.fd = fd;

    workerdata *d = amp;data;

    AMediaExtractor *ex = AMediaExtractor_new();
    
    media_status_t err = AMediaExtractor_setDataSourceFd(ex, d->fd, static_cast<off64_t> 
    (outStart), static_cast<off64_t>(outLen));
    close(d->fd);
    if (err != AMEDIA_OK) {
        LOGV("setDataSource error: %d", err);
        return JNI_FALSE;
    }

    int numtracks = AMediaExtractor_getTrackCount(ex);

    AMediaCodec *codec = NULL;

    LOGV("input has %d tracks", numtracks);
    for (int i = 0; i < numtracks; i  ) {
        AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
        const char *s = AMediaFormat_toString(format);
        LOGV("track %d format: %s", i, s);
        const char *mime;
        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, amp;mime)) {
            LOGV("no mime type");
            return JNI_FALSE;
        } else if (!strncmp(mime, "video/", 6)) {
            // Omitting most error handling for clarity.
            // Production code should check for errors.

            // Configure the decoder to render to a surface
            AMediaExtractor_selectTrack(ex, i);
            codec = AMediaCodec_createDecoderByType(mime);

            AImageReader* imageReader;
            ANativeWindow* surface;

            // This setting works
            media_status_t status = AImageReader_new(480, 360, AIMAGE_FORMAT_YUV_420_888, 1, 
            amp;imageReader);

            status = AImageReader_getWindow(imageReader, amp;surface);

            AImageReader_ImageListener* imageListener = new AImageReader_ImageListener();
            imageListener->onImageAvailable = amp;imageCallback;
            AImageReader_setImageListener(imageReader, imageListener);

            AMediaCodec_configure(codec, format, d->window, NULL, 0);
            d->ex = ex;
            d->codec = codec;
            d->renderstart = -1;
            d->sawInputEOS = false;
            d->sawOutputEOS = false;
            d->isPlaying = false;
            d->renderonce = true;
            AMediaCodec_start(codec);
        }
        AMediaFormat_delete(format);
    }

    mlooper = new mylooper();
    mlooper->post(kMsgCodecBuffer, d);

    return JNI_TRUE;
}

 

Это имя каталога и имена файлов SD-карты.

 static const char *kDirName = "/sdcard/DCIM/Camera/";
static const char *kFileName = "capture";
 

Объявление структуры в native-codec-jni.cpp

 typedef struct {
    int fd;
    ANativeWindow* window;
    AMediaExtractor* ex;
    AMediaCodec *codec;
    int64_t renderstart;
    bool sawInputEOS;
    bool sawOutputEOS;
    bool isPlaying;
    bool renderonce;
} workerdata;

workerdata data = {-1, NULL, NULL, NULL, 0, false, false, false, false};


void *callbackCtx_;
std::function<void(void *ctx, const char* fileName)> callback_;

void RegisterCallback(void* ctx, std::function<void(void* ctx, const char*fileName)> func) {
    callbackCtx_ = ctx;
    callback_ = func;
}

 

Это фактический код вызова. Я сохранил видеоклип в папке «Ресурсы» и отправил по этому пути.

 if(mRadio3.isChecked()){
    mSampleCreated = createSampleMediaPlayer(getResources().getAssets(),mSourceString);
}
 

Когда я выполнил код, ошибок не было, но я не вижу никаких изображений на SD-карте.

Ищу помощи.