我正在开发一个依赖 FFmpeg 来检索音频元数据的 Android 应用程序。我知道可以使用 FFMpeg 以编程方式检索专辑封面。然而,一旦您解码了艺术作品(MP3 中的视频帧),如何生成图像文件(PNG)以在应用程序中使用?我已经搜索遍了,但似乎找不到有效的示例。
编辑,这是解决方案:
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
void retrieve_album_art(const char *path, const char *album_art_file) {
int i, ret = 0;
if (!path) {
printf("Path is NULL\n");
return;
}
AVFormatContext *pFormatCtx = avformat_alloc_context();
printf("Opening %s\n", path);
// open the specified path
if (avformat_open_input(&pFormatCtx, path, NULL, NULL) != 0) {
printf("avformat_open_input() failed");
goto fail;
}
// read the format headers
if (pFormatCtx->iformat->read_header(pFormatCtx) < 0) {
printf("could not read the format header\n");
goto fail;
}
// find the first attached picture, if available
for (i = 0; i < pFormatCtx->nb_streams; i++)
if (pFormatCtx->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC) {
AVPacket pkt = pFormatCtx->streams[i]->attached_pic;
FILE* album_art = fopen(album_art_file, "wb");
ret = fwrite(pkt.data, pkt.size, 1, album_art);
fclose(album_art);
av_free_packet(&pkt);
break;
}
if (ret) {
printf("Wrote album art to %s\n", album_art_file);
}
fail:
av_free(pFormatCtx);
// this line crashes for some reason...
//avformat_free_context(pFormatCtx);
}
int main() {
avformat_network_init();
av_register_all();
const char *path = "some url";
const char *album_art_file = "some path";
retrieve_album_art(path, album_art_file);
return 0;
}
要以编程方式使用 ffmpeg,我认为您必须在 libavformat (它是 ffmpeg 的一部分)中调用 read_apic()。
从命令行,你显然可以这样做:
ffmpeg -i input.mp3 -an -vcodec copy cover.jpg
-an: disables audio
-vcodec codec: force video codec ('copy' to copy stream)
命令行行为意味着封面艺术图像被视为另一个视频流(仅包含一帧),因此以通常的方式使用 libavformat 来解复用流的视频部分应该会生成该图像。
用于解复用的示例代码:ffmpeg/docs/examples/demuxing.c 通过对 mp3 中的视频流进行解复用而获得的第一个(也是唯一的)AVPacket 将包含 JPEG 文件(仍编码为 JPEG,未解码) .
AVFormatContext* fmt_ctx;
// set up fmt_ctx to read first video stream
AVPacket pkt;
av_read_frame(fmt_ctx, &pkt);
FILE* image_file = fopen("image.jpg", "wb");
int result = fwrite(pkt.data, pkt.size, 1, image_file);
fclose(image_file);
如果有多个图像,我认为它们将被视为单独的视频流,而不是同一流中的单独数据包。第一个流将是具有最大分辨率的流。
所有这些可能都是通过 read_apic() 在内部实现的。
ID3v2 规范允许任何图像格式,但建议使用 JPEG 或 PNG。实际上,ID3 中的所有图像都是 JPEG。
编辑:将一些不太有用的部分移至后记:
附注
ffmpeg -i input.mp3 -f ffmetadata metadata.txt
将生成一个包含元数据的类似 ini 的文件,但其中甚至没有引用图像,因此这不是一个有用的方法。
附注ID3v2 标签中可能有“多个图像”。当存在多于一张图像或多于一种类型的图像时,您可能必须处理这种情况。 附注ffmpeg 可能不是最好的软件。使用
id3lib、TagLib 或 ID3 的其他 实现之一。这些可以用作库(可从您选择的语言调用)或用作命令行实用程序。这里有 TagLib 的示例 C++ 代码:如何使用 TagLib 以不同的音频格式读取/写入封面? 以及 id3lib:如何使用 id3lib 从音频文件中获取专辑封面。
作为上面答案的补充,我还需要一种调整输出图像大小的方法,因此我在试验当前答案中的命令时找到了以下命令:ffmpeg -i input.mp3 -filter:v scale=-2:250 -an output.jpeg
所以这基本上将输出图像缩放到您想要的任何比例或值。
我看到它已经解决了,但如果这可以帮助,我认为你在那里崩溃了:
// this line crashes for some reason...
//avformat_free_context(pFormatCtx);
是因为你提前释放了AVPacket:
AVPacket pkt = pFormatCtx->streams[i]->attached_pic;
....
av_free_packet(&pkt);
但是你不引用它,复制它,或者其他什么,所以当你尝试 avformat_free_context() 时,它会迭代流及其部分,并尝试双重释放 ->attached_pic。否则,感谢示例代码,它对我有帮助!