如何正确通过在Android中使用JNI资产的FileDescriptor到FFmpeg的 [英] How to properly pass an asset FileDescriptor to FFmpeg using JNI in Android

查看:2959
本文介绍了如何正确通过在Android中使用JNI资产的FileDescriptor到FFmpeg的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用的FFmpeg,JNI和Java的Andr​​oid 以检索元数据 FileDescriptor的,它是不是'工作。我知道FFmpeg的支持管道协议所以我想emmulate:猫请将test.mp3 | ffmpeg的我管:0 编程。我用下面的code,以获得与Android应用程序捆绑在一起的资产FileDescriptor的:

I'm trying to retrieve metadata in Android using FFmpeg, JNI and a Java FileDescriptor and it isn't' working. I know FFmpeg supports the pipe protocol so I'm trying to emmulate: "cat test.mp3 | ffmpeg i pipe:0" programmatically. I use the following code to get a FileDescriptor from an asset bundled with the Android application:

FileDescriptor fd = getContext().getAssets().openFd("test.mp3").getFileDescriptor();
setDataSource(fd, 0, 0x7ffffffffffffffL); // native function, shown below

然后,在我的家乡(在C ++)code我通过调用获得的FileDescriptor:

Then, in my native (In C++) code I get the FileDescriptor by calling:

static void wseemann_media_FFmpegMediaMetadataRetriever_setDataSource(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
    //...

    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); // function contents show below

    //...
}

// function contents
static int jniGetFDFromFileDescriptor(JNIEnv * env, jobject fileDescriptor) {
    jint fd = -1;
    jclass fdClass = env->FindClass("java/io/FileDescriptor");

    if (fdClass != NULL) {
        jfieldID fdClassDescriptorFieldID = env->GetFieldID(fdClass, "descriptor", "I");
        if (fdClassDescriptorFieldID != NULL && fileDescriptor != NULL) {
            fd = env->GetIntField(fileDescriptor, fdClassDescriptorFieldID);
        }
    }

    return fd;
}

我然后传递文件描述符管#(C语言)到FFmpeg的:

I then pass the file descriptor pipe # (In C) to FFmpeg:

char path[256] = "";

FILE *file = fdopen(fd, "rb");

if (file && (fseek(file, offset, SEEK_SET) == 0)) {
    char str[20];
    sprintf(str, "pipe:%d", fd);
    strcat(path, str);
}

State *state = av_mallocz(sizeof(State));
state->pFormatCtx = NULL;

if (avformat_open_input(&state->pFormatCtx, path, NULL, &options) != 0) { // Note: path is in the format "pipe:<the FD #>"
    printf("Metadata could not be retrieved\n");
    *ps = NULL;
    return FAILURE;
}

if (avformat_find_stream_info(state->pFormatCtx, NULL) < 0) {
    printf("Metadata could not be retrieved\n");
    avformat_close_input(&state->pFormatCtx);
    *ps = NULL;
    return FAILURE;
}

// Find the first audio and video stream
for (i = 0; i < state->pFormatCtx->nb_streams; i++) {
    if (state->pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) {
        video_index = i;
    }

    if (state->pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) {
        audio_index = i;
    }

    set_codec(state->pFormatCtx, i);
}

if (audio_index >= 0) {
    stream_component_open(state, audio_index);
}

if (video_index >= 0) {
    stream_component_open(state, video_index);
}

printf("Found metadata\n");
AVDictionaryEntry *tag = NULL;
while ((tag = av_dict_get(state->pFormatCtx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
    printf("Key %s: \n", tag->key);
    printf("Value %s: \n", tag->value);
}

*ps = state;
return SUCCESS;

我的问题是 avformat_open_input 不会失败,但它也没有让我取回任何元数据或帧,如果我用一个普通同code工作文件URI(如文件://sdcard/test.mp3)作为路径。我究竟做错了什么?先谢谢了。

My issue is avformat_open_input doesn't fail but it also doesn't let me retrieve any metadata or frames, The same code works if I use a regular file URI (e.g file://sdcard/test.mp3) as the path. What am I doing wrong? Thanks in advance.

注意:如果你想看看所有的code我试图解决这一问题,以便为我的图书馆这一功能:的 FFmpegMediaMetadataRetriever

Note: if you would like to look at all of the code I'm trying to solve the issue in order to provide this functionality for my library: FFmpegMediaMetadataRetriever.

推荐答案

的Java

AssetFileDescriptor afd = getContext().getAssets().openFd("test.mp3");
setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), fd.getLength());

C

void ***_setDataSource(JNIEnv *env, jobject thiz, 
    jobject fileDescriptor, jlong offset, jlong length)
{
    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);

    char path[20];
    sprintf(path, "pipe:%d", fd);

    State *state = av_mallocz(sizeof(State));
    state->pFormatCtx =  avformat_alloc_context();
    state->pFormatCtx->skip_initial_bytes = offset;
    state->pFormatCtx->iformat = av_find_input_format("mp3");

现在我们可以继续像往常一样:

and now we can continue as usual:

if (avformat_open_input(&state->pFormatCtx, path, NULL, &options) != 0) {
    printf("Metadata could not be retrieved\n");
    *ps = NULL;
    return FAILURE;
}
...


更妙的是,使用&LT;机器人/ asset_manager.h&GT; ,就像这样:

的Java

setDataSource(getContext().getAssets(), "test.mp3");

C

#include <android/asset_manager_jni.h>

void ***_setDataSource(JNIEnv *env, jobject thiz, 
    jobject assetManager, jstring assetName)
{
    AAssetManager* assetManager = AAssetManager_fromJava(env, assetManager);
    const char *szAssetName = (*env)->GetStringUTFChars(env, assetName, NULL);
    AAsset* asset = AAssetManager_open(assetManager, szAssetName, AASSET_MODE_RANDOM);
    (*env)->ReleaseStringUTFChars(env, assetName, szAssetName);
    off_t offset, length;
    int fd = AAsset_openFileDescriptor(asset, &offset, &length);
    AAsset_close(asset);

免责声明:检查省略错误简洁,但资源被正确释放,除了 FD 。你必须关闭(FD)完成时。

Disclaimer: error checking was omitted for brevity, but resources are released correctly, except for fd. You must close(fd) when finished.

这篇关于如何正确通过在Android中使用JNI资产的FileDescriptor到FFmpeg的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆