c++ - 使用 ffmpeg 添加元数据信息

标签 c++ ffmpeg metadata

我正在尝试使用 ffmpeg 读取元数据并且最重要的是将元数据写入文件。但我得到了 sigseg。

int main(int argc, char **argv) {

    av_register_all();

    AVFormatContext* ctx;
    std::string path("/home/stefan/test_track.mp3");

    ctx = avformat_alloc_context();

    if (avformat_open_input(&ctx, path.c_str(), 0, 0) < 0)
        std::cout << "error1" << std::endl;

    if (avformat_find_stream_info(ctx, 0) < 0)
        std::cout << "error2" << std::endl;

    AVDictionaryEntry *tag = nullptr;
    tag = av_dict_get(ctx->metadata, "artist", tag, AV_DICT_IGNORE_SUFFIX);
    std::cout << tag->key << " : " << tag->value << std::endl;

    av_dict_set(&ctx->metadata, "TPFL", "testtest", 0);

    std::cout << "test!" << std::endl;
    int status = avformat_write_header(ctx, &ctx->metadata);

    if(status == 0)
        std::cout << "test1" << std::endl;

    return 0;
}

我还尝试制作 AVDictionary 的完整拷贝,而不是通过添加我要保存的新字段来保存它。但仍然是 sigseg。

我错过了什么?

最佳答案

您需要使用另一个 AVFormatContext写你的输出。

在您的示例中,只需添加一些元数据并复制编解码器,因此 ffmpeg 库的复用步骤是

  • 创建所需的输出格式上下文,avformat_alloc_output_context2
  • 将流添加到输出格式上下文中,avformat_new_stream
  • 添加一些自定义元数据并写入标题
  • 使用 av_write_frame写入编码数据
  • 写预告片

  • 下面是一个对您的代码进行少量修改的可行示例。
    #include <iostream>
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #include <libavformat/avformat.h>
    
    #ifdef __cplusplus 
    }
    #endif
    
    
    int main(int argc, char **argv) {
    
        av_register_all();
        avcodec_register_all();
    
        AVFormatContext* ctx;
        std::string path("./input.mp3");
    
        ctx = avformat_alloc_context();
    
        if (avformat_open_input(&ctx, path.c_str(), 0, 0) < 0)
            std::cout << "error1" << std::endl;
    
        if (avformat_find_stream_info(ctx, 0) < 0)
            std::cout << "error2" << std::endl;
    
        AVDictionaryEntry *tag = nullptr;
        tag = av_dict_get(ctx->metadata, "artist", tag, AV_DICT_IGNORE_SUFFIX);
        std::cout << tag->key << " : " << tag->value << std::endl;
    
        av_dict_set(&ctx->metadata, "TPFL", "testtest", 0);
    
        std::cout << "test!" << std::endl;
    
        int status;
    
        AVFormatContext* ofmt_ctx;
        AVOutputFormat* ofmt = av_guess_format("mp3", "./out.mp3", NULL);
        status = avformat_alloc_output_context2(&ofmt_ctx, ofmt, "mp3", "./out.mp3");
        if (status < 0) {
            std::cerr << "could not allocate output format" << std::endl;
            return 0;
        }
    
        int audio_stream_index = 0;
        for (unsigned i = 0; i < ctx->nb_streams; i++) {
            if (ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
                audio_stream_index = i;
                const AVCodec *c = avcodec_find_encoder(ctx->streams[i]->codecpar->codec_id);
                if (c) {
                    AVStream *ostream = avformat_new_stream(ofmt_ctx, c);
                    avcodec_parameters_copy(ostream->codecpar, ctx->streams[i]->codecpar);
                    ostream->codecpar->codec_tag = 0;
                }
                break;
            }
        }
    
        av_dict_set(&ofmt_ctx->metadata, "TPFL", "testtest", 0);
    
        av_dump_format(ofmt_ctx, 0, "./out.mp3", 1);
    
        if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
            avio_open(&ofmt_ctx->pb, "./out.mp3", AVIO_FLAG_WRITE);
        }
    
        if (avformat_init_output(ofmt_ctx, NULL) == AVSTREAM_INIT_IN_WRITE_HEADER) {
            status = avformat_write_header(ofmt_ctx, NULL);
        }
    
        AVPacket *pkt = av_packet_alloc();
        av_init_packet(pkt);
        pkt->data = NULL;
        pkt->size = 0;
    
        while (av_read_frame(ctx, pkt) == 0) {
            if (pkt->stream_index == audio_stream_index) {
                // this is optional, we are copying the stream
                av_packet_rescale_ts(pkt, ctx->streams[audio_stream_index]->time_base,
                                     ofmt_ctx->streams[audio_stream_index]->time_base);
                av_write_frame(ofmt_ctx, pkt);
            }
        }
    
        av_packet_free(&pkt);
    
        av_write_trailer(ofmt_ctx);
    
        avformat_close_input(&ctx);
        avformat_free_context(ofmt_ctx);
        avformat_free_context(ctx);
    
        if(status == 0)
            std::cout << "test1" << std::endl;
    
        return 0;
    }
    

    完成后,您可以使用ffprobe output.mp3检查写入的元数据。
    Input #0, mp3, from 'out.mp3':
      Metadata:
        TPFL            : testtest
        encoder         : Lavf57.75.100
      Duration: 00:00:08.75, start: 0.011995, bitrate: 128 kb/s
        Stream #0:0: Audio: mp3, 44100 Hz, stereo, s16p, 128 kb/s
        Metadata:
          encoder         : Lavf
    

    关于c++ - 使用 ffmpeg 添加元数据信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45050177/

    相关文章:

    bash - 带有时间戳的批处理间隔屏幕截图

    performance - 映射器中的大量小文件

    php - 在 WooCommerce 订单状态更改时添加用户元数据作为订单元

    c++ - 在另一个结构中重载结构的运算符会出错

    bash - FFMPEG:从带时间戳的图像创建电影

    c++ - 从数组中删除一个元素

    android - 如何为 Android 构建 SDL 库

    c# - 将 8BIM 配置文件元数据添加到 tiff 图像文件

    c++ - C2280 : attempting to reference a deleted function (union, 结构体,复制构造函数)

    c++ - LD_PRELOAD 仅适用于 malloc,不是免费的