使用FFmpeg获取专辑封面

16

我正在开发一款Android应用程序,它依赖于FFmpeg来检索音频元数据。 我知道可以使用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;
}
2个回答

34

要以编程方式使用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 可能不是最适合此任务的软件。使用 id3libTagLib 或其他 ID3 实现 中的一个。它们可以作为库(可从您选择的语言中调用)或作为命令行实用程序使用。这里有 TagLib 的示例 C++ 代码:如何使用 TagLib 在不同的音频格式中读取/写入封面艺术?,以及 id3lib 的示例代码:如何使用 id3lib 从音频文件中获取专辑封面艺术?


@WilliamSeemann:我不明白为什么这不能满足你的需求。 我 确实 描述了两种使用 ffmpeg 编程方式从 mp3 文件中获取图像的方法:一种是通过调用 read_apic(),另一种是通过调用 demuxing.c 示例中显示的 av_read_frame()。 - Alex I
我需要它们以文件形式而不是直接从缓冲区渲染出来。只是为了澄清,你从av_read_frame()获取的AVPacket实际上只包含一个JPEG文件(仍然压缩),而不是解码后的图像或其他任何东西。如果需要,您可以直接将其保存到文件中,我现在正在为您编写一些示例代码。 - Alex I
@WilliamSeemann: “将未解码的数据包写入文件是相当无用的” - 为什么呢?实际上,这是一个JPEG文件,而不是“数据包”。你确实说过“我并不在乎图像的格式,但我需要它们以文件形式”,这样你就可以得到一个以JPEG格式保存的图像文件。 - Alex I
我当时并没有立刻意识到我可以直接将数据包写入文件。感谢您的帮助。 - William Seemann
谢谢,这也帮助了我。 - Suraj Jain

4

作为对上面答案的补充,我需要一种调整输出图像大小的方法。在尝试当前答案中的命令时,我发现了下面这个命令:

ffmpeg -i input.mp3 -filter:v scale=-2:250 -an output.jpeg

所以,这基本上将输出图像按照您想要的任何比例或值进行缩放。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接