Как правильно передать asset FileDescriptor в FFmpeg с помощью JNI в Android



Я пытаюсь получить метаданные в Android с помощью FFmpeg, JNI и JavaFileDescriptor , и это не работает. Я знаю, что FFmpeg поддерживает протокол pipe protocol , поэтому я пытаюсь эмулировать: "cat test.mp3 | ffmpeg i pipe:0" программно. Я использую следующий код для получения FileDescriptor из актива, входящего в комплект приложения для Android:



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


Затем в моем родном (на C++) коде я получаю FileDescriptor, вызывая:



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;
}


Затем я передаю файловый дескриптор pipe # (In C) в 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 retrievedn");
*ps = NULL;
return FAILURE;
}

if (avformat_find_stream_info(state->pFormatCtx, NULL) < 0) {
printf("Metadata could not be retrievedn");
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 metadatan");
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 не дает сбоя, но также не позволяет мне получить метаданные или фреймы, тот же код работает, если я использую обычный файл URI (например, файл://sdcard/test.mp3) в качестве пути. Что я делаю не так? Заранее спасибо.



Примечание : Если вы хотите посмотреть на весь код, я пытаюсь решить эту проблему, чтобы обеспечить эту функциональность для моей библиотеки: FFmpegMediaMetadataRetriever.

608   2  

2 ответов:

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");

А теперь мы можем продолжить как обычно:

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

Еще лучше использовать <android/asset_manager.h>, например:

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. Вы должны close(fd), Когда закончите.

 FileDescriptor fd = getContext().getAssets().openFd("test.mp3").getFileDescriptor();

Думаю, вам следует начать с AssetFileDescripter. http://developer.android.com/reference/android/content/res/AssetFileDescriptor.html

Comments

    Ничего не найдено.