使用 ffmpeg 添加元数据信息

Adding metadata informations with ffmpeg

本文关键字:信息 元数据 添加 ffmpeg 使用      更新时间:2023-10-16

我正在尝试读取,最重要的是使用 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的完整副本,而不是使用新添加的要保存的字段来保存它。但仍然会感到不安。

我错过了什么?

您需要使用另一个AVFormatContext来编写输出。

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

  1. 创建所需的输出格式上下文,avformat_alloc_output_context2
  2. 将流添加到输出格式上下文,avformat_new_stream
  3. 添加一些自定义元数据和写入标头
  4. 使用av_write_frame写入编码数据
  5. 写预告片

下面是一个可行的示例,对代码进行了少量修改。

#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