ffmpeg关于avformat_write_header改变流时间基time_base的说明

最近在用ffmpeg做直播推流功能,之前做的本地录制,封装格式是mp4,直播的时候,mp4不支持,需要改为flv,但是改为flv之后,用ffmpeg命令行进行拉流,拉下来的流存储成flv文件。播放起来,视频显得特别快,原本2分钟的视频,10秒不到就播放完了。

为此将音频和视频单独推流,视频没问题,音频出了问题,录制4分钟的音频,在文件中大概只有2分钟。

下面是用ffmpeg拉流时的命令行,从红色方框可以看出,1分钟拉下来的流不到30ms。正常情况下,这个speed应该在比1大一点点。

在这里插入图片描述

下面一个正常拉流情况下的speed值。
在这里插入图片描述

为此用ffmpeg命令行推流试了下,ffmpeg命令行推流是ok的,然后调试ffmpeg工具,如何调试,本人写了博客。
vs2017调试ffmpeg源码

经过一段时间,发现,问题出在avformat_write_header函数上,在write之前,已经对avformat_new_stream的AVStream做了time_base设置,设置为1/48000,跟音频的采样率保持一致。
但是avformat_write_header之后,time_base变成了1/1000。

封装格式为mp4的时候,不会出现这种情况,看来应该是跟封装格式有关。
下面的图片来自博客ffmpeg # 各种封装格式的time_base

在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
你可以使用FFmpeg库中的avformatavcodec模块来实现将h264编码的视频保存为ts格式的功能。下面是一个简单的代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> int main(int argc, char *argv[]) { const char *input_file = "input.h264"; const char *output_file = "output.ts"; AVFormatContext *in_ctx = NULL, *out_ctx = NULL; AVCodec *codec = NULL; AVCodecContext *codec_ctx = NULL; AVPacket packet; int ret = 0; // 打开输入文件 if ((ret = avformat_open_input(&in_ctx, input_file, NULL, NULL)) < 0) { fprintf(stderr, "Error opening input file: %s\n", av_err2str(ret)); goto end; } // 查找视频 if ((ret = avformat_find_stream_info(in_ctx, NULL)) < 0) { fprintf(stderr, "Error finding stream information: %s\n", av_err2str(ret)); goto end; } // 打开输出文件 if ((ret = avformat_alloc_output_context2(&out_ctx, NULL, NULL, output_file)) < 0) { fprintf(stderr, "Error opening output file: %s\n", av_err2str(ret)); goto end; } // 添加视频 codec = avcodec_find_encoder(AV_CODEC_ID_H264); if (!codec) { fprintf(stderr, "H.264 codec not found\n"); goto end; } codec_ctx = avcodec_alloc_context3(codec); codec_ctx->width = in_ctx->streams[0]->codecpar->width; codec_ctx->height = in_ctx->streams[0]->codecpar->height; codec_ctx->pix_fmt = in_ctx->streams[0]->codecpar->format; codec_ctx->time_base = in_ctx->streams[0]->time_base; if ((ret = avcodec_open2(codec_ctx, codec, NULL)) < 0) { fprintf(stderr, "Error opening codec: %s\n", av_err2str(ret)); goto end; } AVStream *out_stream = avformat_new_stream(out_ctx, codec); if (!out_stream) { fprintf(stderr, "Error creating output stream\n"); goto end; } out_stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; out_stream->codecpar->codec_id = codec->id; out_stream->codecpar->width = codec_ctx->width; out_stream->codecpar->height = codec_ctx->height; out_stream->codecpar->format = codec_ctx->pix_fmt; out_stream->codecpar->bit_rate = codec_ctx->bit_rate; out_stream->codecpar->extradata = codec_ctx->extradata; out_stream->codecpar->extradata_size = codec_ctx->extradata_size; avcodec_parameters_to_context(out_stream->codec, out_stream->codecpar); // 打开输出文件 if (!(out_ctx->oformat->flags & AVFMT_NOFILE)) { if ((ret = avio_open(&out_ctx->pb, output_file, AVIO_FLAG_WRITE)) < 0) { fprintf(stderr, "Error opening output file: %s\n", av_err2str(ret)); goto end; } } // 写文件头 if ((ret = avformat_write_header(out_ctx, NULL)) < 0) { fprintf(stderr, "Error writing header: %s\n", av_err2str(ret)); goto end; } // 读取并编码每一帧 while (1) { AVStream *in_stream = in_ctx->streams[0]; if ((ret = av_read_frame(in_ctx, &packet)) < 0) { break; } if (packet.stream_index != 0) { av_packet_unref(&packet); continue; } av_packet_rescale_ts(&packet, in_stream->time_base, codec_ctx->time_base); if ((ret = avcodec_send_packet(codec_ctx, &packet)) < 0) { fprintf(stderr, "Error sending packet to encoder: %s\n", av_err2str(ret)); av_packet_unref(&packet); break; } while (1) { AVFrame *frame = av_frame_alloc(); if (!frame) { fprintf(stderr, "Error allocating frame\n"); ret = AVERROR(ENOMEM); goto end; } ret = avcodec_receive_frame(codec_ctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { av_frame_free(&frame); break; } else if (ret < 0) { fprintf(stderr, "Error receiving frame from encoder: %s\n", av_err2str(ret)); av_frame_free(&frame); goto end; } frame->pts = frame->best_effort_timestamp; AVPacket out_packet; av_init_packet(&out_packet); out_packet.data = NULL; out_packet.size = 0; if ((ret = avcodec_send_frame(codec_ctx, frame)) < 0) { fprintf(stderr, "Error sending frame to encoder: %s\n", av_err2str(ret)); av_frame_free(&frame); goto end; } while (1) { ret = avcodec_receive_packet(codec_ctx, &out_packet); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { av_packet_unref(&out_packet); break; } else if (ret < 0) { fprintf(stderr, "Error receiving packet from encoder: %s\n", av_err2str(ret)); av_packet_unref(&out_packet); goto end; } out_packet.stream_index = 0; av_packet_rescale_ts(&out_packet, codec_ctx->time_base, out_stream->time_base); if ((ret = av_interleaved_write_frame(out_ctx, &out_packet)) < 0) { fprintf(stderr, "Error writing packet to output file: %s\n", av_err2str(ret)); av_packet_unref(&out_packet); goto end; } av_packet_unref(&out_packet); } av_frame_free(&frame); } av_packet_unref(&packet); } // 写文件尾 if ((ret = av_write_trailer(out_ctx)) < 0) { fprintf(stderr, "Error writing trailer: %s\n", av_err2str(ret)); goto end; } end: if (codec_ctx) { avcodec_free_context(&codec_ctx); } if (in_ctx) { avformat_close_input(&in_ctx); } if (out_ctx) { if (!(out_ctx->oformat->flags & AVFMT_NOFILE)) { avio_closep(&out_ctx->pb); } avformat_free_context(out_ctx); } return ret; } ``` 需要注意的是,这只是一个简单的示例代码,实际使用时需要根据具体的需求进行修改和完善。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值