使用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
- 在代码初始化阶段定义变量累计总样本数:
int64_t total_samples = 0; - 每处理一块数据时累加样本数:
int samples_per_block = bytesRead / (2 * enc_ctx->channels); total_samples += samples_per_block; pts += samples_per_block;
- 在调用
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
相关产品推荐
相关产品推荐

