You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用libavformat/avcodec生成的FLAC缺失时长元信息问题排查

问题:PCM16LE转FLAC后文件缺失时长元信息

我实现了PCM16LE转FLAC的代码,转换功能正常,但生成的FLAC文件没有时长元信息。使用ffmpeg -i命令查看文件详情时输出如下:

Input #0, flac, from 'test.linear_l.flac':
  Metadata:
    encoder         : Lavf57.25.100
  Duration: N/A, start: 0.000000, bitrate: N/A
  Stream #0:0: Audio: flac, 16000 Hz, stereo, s16

初始化代码

AVFormatContext* ofmt_ctx = NULL;
avformat_alloc_output_context2(&ofmt_ctx, nullptr, nullptr, outputFile);
if (!ofmt_ctx)
{
ERROR("Unable to create output context for file: " << outputFile);
return ERROR;
}

// FLAC code
AVCodec* encoder = avcodec_find_encoder(AV_CODEC_ID_FLAC);
if (!encoder)
{
avformat_free_context(ofmt_ctx);
ERROR("FLAC encoder not found for file: " << outputFile);
return ERROR;
}

AVStream* flacStream = avformat_new_stream(ofmt_ctx, encoder);
if (!flacStream)
{
avformat_free_context(ofmt_ctx);
ERROR("Could not create new flac stream for file: " << outputFile);
return ERROR;
}

AVCodecContext* enc_ctx = flacStream->codec;
enc_ctx->sample_fmt = AV_SAMPLE_FMT_S16;
enc_ctx->sample_rate = sampleRate;
enc_ctx->channels = theMaxChannels;
enc_ctx->bit_rate = 0;

flacStream->time_base.den = enc_ctx->sample_rate;
flacStream->time_base.num = 1;

if (avcodec_open2(enc_ctx, encoder, nullptr) < 0)
{
avformat_free_context(ofmt_ctx);
ERROR("Could not open codec context for file: " << outputFile);
return ERROR;
}

if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;


if (avio_open(&ofmt_ctx->pb, outputFile, AVIO_FLAG_WRITE) < 0)
{
avformat_free_context(ofmt_ctx);
ERROR("Could not open FLAC output file: " << outputFile);
return ERROR;
}

if (avformat_write_header(ofmt_ctx, nullptr) < 0)
{
avformat_free_context(ofmt_ctx);
ERROR("Could not write FLAC file header for file: " << outputFile);
return ERROR;
}

AVPacket pkt;
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;

AVFrame* frame = av_frame_alloc();
frame->nb_samples = enc_ctx->frame_size;
frame->format = enc_ctx->sample_fmt;
frame->channel_layout = enc_ctx->channel_layout;

int buffer_size = av_samples_get_buffer_size(NULL, enc_ctx->channels, enc_ctx->frame_size, enc_ctx->sample_fmt, 0);
if (buffer_size < 0)
{
avformat_free_context(ofmt_ctx);
ERROR("Could not get sample buffer size");
return ERROR;
}
uint16_t* samples = (uint16_t*)av_malloc(buffer_size);
if (!samples)
{
avformat_free_context(ofmt_ctx);
ERROR("Could not allocate " << buffer_size << " bytes for samples buffer");
return ERROR;
}

int ret = 0;

/* setup the data pointers in the AVFrame */
ret = avcodec_fill_audio_frame(frame, enc_ctx->channels, enc_ctx->sample_fmt, (const uint8_t*)samples, buffer_size, 0);

每块PCM数据处理代码

int got_output = 0;
frame->pts = pts;
pts+= (bytesRead/2);
ret = avcodec_encode_audio2(enc_ctx, &pkt, frame, &got_output);
if (got_output) 
{
av_interleaved_write_frame(ofmt_ctx, &pkt);
av_packet_unref(&pkt);
}

收尾代码

av_write_trailer(ofmt_ctx);
for (i = 0; i < ofmt_ctx->nb_streams; i++)
{
avcodec_close(ofmt_ctx->streams[i]->codec);
}
if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
avio_closep(&ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);   
av_freep(&samples);
av_frame_free(&frame);

我确定漏了某个简单步骤,但查了示例和文档没找到FLAC相关的明确说明,求解决。


解决方案

问题出在两个核心点:pts计算错误和未设置流的总时长(总样本数),FLAC格式依赖总样本数来生成时长元数据。

  • 修正pts的增量计算
    当前代码中pts += (bytesRead/2)忽略了声道数:PCM16LE每个样本占2字节,多声道下每一组样本包含所有声道的数据。正确的单块数据样本数应为bytesRead / (2 * enc_ctx->channels),更新代码如下:
// 替换原pts更新逻辑
int samples_per_block = bytesRead / (2 * enc_ctx->channels);
pts += samples_per_block;
  • 累计总样本数并设置流的duration
  1. 在代码初始化阶段定义变量累计总样本数:int64_t total_samples = 0;
  2. 每处理一块数据时累加样本数:
int samples_per_block = bytesRead / (2 * enc_ctx->channels);
total_samples += samples_per_block;
pts += samples_per_block;
  1. 在调用av_write_trailer()之前,设置FLAC流的duration(duration单位为流的time_base,即1/采样率,直接等于总样本数):
// 插入到av_write_trailer前
flacStream->duration = total_samples;
av_write_trailer(ofmt_ctx);
  • 补充设置channel_layout(可选但推荐)
    初始化AVFrame时,frame->channel_layout未正确赋值,可能导致元数据不完整,建议修改为:
frame->channel_layout = av_get_default_channel_layout(enc_ctx->channels);

这样修改后,生成的FLAC文件就能被ffmpeg正确识别时长信息了。

内容的提问来源于stack exchange,提问作者Brad Mitchell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 08:09:52