Cómo pasar correctamente un archivo FileDescriptor a FFmpeg usando JNI en Android

Estoy tratando de recuperar los metadatos en Android usando FFmpeg, JNI y un Java FileDescriptor y no está funcionando. Sé FFmpeg apoya el protocolo de tubería por lo que estoy tratando de emmulate: " cat test.mp3 | ffmpeg i pipe:0 " mediante programación. Utilizo el siguiente código para obtener un FileDescriptor de un recurso incluido con la aplicación de Android:

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

Entonces, en mi código nativo (en C ++) consigo el FileDescriptor llamando:

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

A continuación, pasar el descriptor de archivo pipe # (En C) a 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; 

Mi problema es que avformat_open_input no falla, pero tampoco me permite recuperar metadatos o marcos. El mismo código funciona si utilizo un archivo URI normal (por ejemplo, file: //sdcard/test.mp3) como la ruta. ¿Qué estoy haciendo mal? Gracias por adelantado.

Nota : si desea ver todo el código que estoy tratando de resolver el problema con el fin de proporcionar esta funcionalidad para mi biblioteca: FFmpegMediaMetadataRetriever .

Java

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

do

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

Y ahora podemos continuar como de costumbre:

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

Mejor aún, utilice <android/asset_manager.h> , de la siguiente manera:

Java

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

do

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

Descargo de responsabilidad : se omitió la comprobación de errores por brevedad, pero los recursos se liberan correctamente, excepto para fd . Debe close(fd) cuando haya terminado.

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

Piensa que deberías comenzar con AssetFileDescripter. http://developer.android.com/reference/android/content/res/AssetFileDescriptor.html

  • Uso de Fluidsynth para reproducir notas de SoundFonts en Android
  • Android NDK R8E falta stdlib.h
  • Puente nativo entre Python y Dalvik o AAF
  • Android JNI APK Embalaje
  • Creación / búsqueda de bibliotecas compartidas desde el código fuente de Android
  • Excepción actualizando juego a cocos2d-x v2.0
  • Callback Listener en Unity - Cómo llamar al método de archivo de script desde UnityPlayerActivity en Android
  • Definir macro para registrar error con archivo y línea en android
  • Construye la librería Android-openssl para la plataforma 2.1
  • Obtener un puntero JNIEnv válido
  • Android, genera archivos de encabezado jni con javah, muestra error que no puede encontrar org.opencv.core.Mat
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.