【发布时间】:2014-09-02 06:34:16
【问题描述】:
我正在尝试使用 FFmpeg、JNI 和 Java FileDescriptor 在 Android 中检索元数据,但它不起作用。我知道 FFmpeg 支持pipe protocol,所以我试图以编程方式模拟:“cat test.mp3 | ffmpeg i pipe:0”。我使用以下代码从与 Android 应用程序捆绑的资产中获取 FileDescriptor:
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;
}
然后我将文件描述符管道#(在 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 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 不会失败,但它也不会让我检索任何元数据或帧,如果我使用常规文件 URI(例如 file://sdcard/test.mp3),相同的代码可以工作作为路径。我究竟做错了什么?提前致谢。
注意:如果您想查看我正在尝试解决问题以便为我的库提供此功能的所有代码:FFmpegMediaMetadataRetriever。
【问题讨论】:
-
你检查
jniGetFDFromFileDescriptor()返回的值吗? -
我做到了,我知道它是有效的,因为 FFmpeg 不会失败。我仍然可以从文件中检索一些基本信息(持续时间、编解码器),但与使用文件 URI 的信息不同。我知道 FFmpeg 代码没问题,因为运行“cat test.mp3 | ./mycode”会产生正确的输出。使用文件描述符或将文件馈送到我的 FFmpeg 代码的方式时,这似乎是一个查找问题。
-
见mbcdev.com/2012/04/03/… - 你有非零
fd.getStartOffset() -
很好的发现。让我试试看它是否有效。
-
据我所知,调用 getContext().getAssets().openFd("test.mp3") 返回的 FileDecriptor 指向 APK 本身, fd.getStartOffset() 提供了来自 APK 的特定 mp3 资产。在尝试使用 FFmpeg 打开文件之前,我需要找到该偏移量,否则它无法正确分析文件。这有意义吗?
标签: android android-ndk ffmpeg java-native-interface metadata