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:
- Java.lang.UnsatisfiedLinkError: No se ha encontrado ninguna implementación para Boolean
- Falló la aserción de OpenCV nMatToBitmap
- Error de compilación con ndk jni para curl
- Qt para Android - el equivalente de startActivityForResult no funciona
- Cómo mantener un objeto C ++ dentro del código Java? ¿Posible?
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 .
- No se ha encontrado JNI_Onload () y VM ha cerrado
- Dalvik está buscando el archivo .so con extensión '.0' - ¿por qué?
- Para utilizar el JNI, o no utilizar el JNI (rendimiento de Android)
- Llamar a un método java desde c ++ en Android
- java.lang.UnsatisfiedLinkError: No se ha encontrado ninguna implementación debido a que la biblioteca no está cargando
- No se encontró JNI_OnLoad en ... saltando init
- JNI: no se puede encontrar la biblioteca en java.library.path al ejecutar JUnit
- Android: Uso de JNI desde NativeActivity
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