ffmpeg 最简单的转码封装mp4文件
来源:互联网 发布:国家电网试题软件 编辑:程序博客网 时间:2024/06/01 22:45
转自:http://www.rosoo.net/a/201411/17115.html
本例简单实现了解码后的video重新编码264之后在mux成MP4文件的过程,主要是用来记录muxing的方法。
下面详细说一下细节:
大家都知道一般解码出来的数据都是播放顺序,解码器是将编码顺序的数据重新按照解码后的播放顺序输出的。而编码器是把数据根据解码需要的顺序重新排序保存的。
当然,以上情况只在有帧的情况下才有用,否则只有IP帧的话解码和编码的顺序是一样的
比如:解码后的数据是IBBP,那要将这个数据编码的话,编码后的数据保存的格式就是IPBB
这只是内部的处理,对于用ffmpeg的库的我们不用太过关心 ,但是 , 要注意,我们将数据塞给编码器的时候,要给顺序的播放加上顺序的时间标记,其实很简单只要保证你送给编码器的每一frame的pts都是顺序的就可以了, 否则编码器会报 “non-strictly-monotonic pts at frame” , 究其原因,是因为编码器需要送进来的frame时间上是递增的,为什么需要这个就得去本研究编码器了
if( pic.i_pts <= largest_pts ) { if( cli_log_level >= X264_LOG_DEBUG || pts_warning_cnt < MAX_PTS_WARNING ) x264_cli_log( "x264", X264_LOG_WARNING , "non-strictly-monotonic pts at frame %d (%"PRId64" <= %"PRId64")\n" , i_frame, pic.i_pts, largest_pts ); else if( pts_warning_cnt == MAX_PTS_WARNING ) x264_cli_log( "x264", X264_LOG_WARNING , "too many nonmonotonic pts warnings, suppressing further ones\n" ); pts_warning_cnt++; pic.i_pts = largest_pts + ticks_per_frame; }
在将数据送到编码器后,进行编码输出得到的pkt有自己的pts和dts等数据,但是这个数据记得吗?是用我们自己送进去的pts来表示的,所以在和原来 的audio mux的时候,会出现严重的音视频不同步,现在想想这个问题,就很容易理解了,两边的pts差距很大,当然解码后做同步的时候会差很多。
其实ffmpeg在解码的时候将解码出来的顺序时间戳给了frame的pkt_pts这个成员,所以我们可以直接用这个值赋值给frame的pts,在送进编码器,这样编码出来的pkt中的时间戳就和原来的audio对上了。
ret = avcodec_decode_video2(video_dec_ctx, pFrame, &got_picture,pkt); if (ret < 0) { delete pkt; return 0; } pFrame->pts = pFrame->pkt_pts; //赋值解码后的pts
最后在进行mux成mp4文件就ok了
在mux的过程中,有个接口av_rescale_q_rnd,这个是用来换算pts的,因为在设定mp4输出格式的时候time_base这个值是和 原来的文件不一样的,所以要用这个来重新算分装数据的在新的mp4中的pts和dts等数据,具体原因后续会继续往里研究
直接上代码:
const char* SRC_FILE = "1.mkv"; const char* OUT_FILE = "outfile.h264"; const char* OUT_FMT_FILE = "outfmtfile.mp4"; int main() { av_register_all(); AVFormatContext* pFormat = NULL; if (avformat_open_input(&pFormat, SRC_FILE, NULL, NULL) < 0) { return 0; } AVCodecContext* video_dec_ctx = NULL; AVCodec* video_dec = NULL; if (avformat_find_stream_info(pFormat, NULL) < 0) { return 0; } av_dump_format(pFormat, 0, SRC_FILE, 0); video_dec_ctx = pFormat->streams[0]->codec; video_dec = avcodec_find_decoder(video_dec_ctx->codec_id); if (avcodec_open2(video_dec_ctx, video_dec, NULL) < 0) { return 0; } AVFormatContext* pOFormat = NULL; AVOutputFormat* ofmt = NULL; if (avformat_alloc_output_context2(&pOFormat, NULL, NULL, OUT_FILE) < 0) { return 0; } ofmt = pOFormat->oformat; if (avio_open(&(pOFormat->pb), OUT_FILE, AVIO_FLAG_READ_WRITE) < 0) { return 0; } AVCodecContext *video_enc_ctx = NULL; AVCodec *video_enc = NULL; video_enc = avcodec_find_encoder(AV_CODEC_ID_H264); AVStream *video_st = avformat_new_stream(pOFormat, video_enc); if (!video_st) return 0; video_enc_ctx = video_st->codec; video_enc_ctx->width = video_dec_ctx->width; video_enc_ctx->height = video_dec_ctx->height; video_enc_ctx->pix_fmt = PIX_FMT_YUV420P; video_enc_ctx->time_base.num = 1; video_enc_ctx->time_base.den = 25; video_enc_ctx->bit_rate = video_dec_ctx->bit_rate; video_enc_ctx->gop_size = 250; video_enc_ctx->max_b_frames = 10; //H264 //pCodecCtx->me_range = 16; //pCodecCtx->max_qdiff = 4; video_enc_ctx->qmin = 10; video_enc_ctx->qmax = 51; if (avcodec_open2(video_enc_ctx, video_enc, NULL) < 0) { printf("编码器打开失败!\n"); return 0; } printf("Output264video Information====================\n"); av_dump_format(pOFormat, 0, OUT_FILE, 1); printf("Output264video Information====================\n"); //mp4 file AVFormatContext* pMp4Format = NULL; AVOutputFormat* pMp4OFormat = NULL; if (avformat_alloc_output_context2(&pMp4Format, NULL, NULL, OUT_FMT_FILE) < 0) { return 0; } pMp4OFormat = pMp4Format->oformat; if (avio_open(&(pMp4Format->pb), OUT_FMT_FILE, AVIO_FLAG_READ_WRITE) < 0) { return 0; } for (int i = 0; i < pFormat->nb_streams; i++) { AVStream *in_stream = pFormat->streams[i]; AVStream *out_stream = avformat_new_stream(pMp4Format, in_stream->codec->codec); if (!out_stream) { return 0; } int ret = 0; ret = avcodec_copy_context(out_stream->codec, in_stream->codec); if (ret < 0) { fprintf(stderr, "Failed to copy context from input to output stream codec context\n"); return 0; } out_stream->codec->codec_tag = 0; if (pMp4Format->oformat->flags & AVFMT_GLOBALHEADER) out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; } av_dump_format(pMp4Format, 0, OUT_FMT_FILE, 1); if (avformat_write_header(pMp4Format, NULL) < 0) { return 0; } //// av_opt_set(video_enc_ctx->priv_data, "preset", "superfast", 0); av_opt_set(video_enc_ctx->priv_data, "tune", "zerolatency", 0); avformat_write_header(pOFormat, NULL); AVPacket *pkt = new AVPacket(); av_init_packet(pkt); AVFrame *pFrame = avcodec_alloc_frame(); int ts = 0; while (1) { if (av_read_frame(pFormat, pkt) < 0) { avio_close(pOFormat->pb); av_write_trailer(pMp4Format); avio_close(pMp4Format->pb); delete pkt; return 0; } if (pkt->stream_index == 0) { int got_picture = 0, ret = 0; ret = avcodec_decode_video2(video_dec_ctx, pFrame, &got_picture, pkt); if (ret < 0) { delete pkt; return 0; } pFrame->pts = pFrame->pkt_pts;//ts++; if (got_picture) { AVPacket *tmppkt = new AVPacket; av_init_packet(tmppkt); int size = video_enc_ctx->width*video_enc_ctx->height * 3 / 2; char* buf = new char[size]; memset(buf, 0, size); tmppkt->data = (uint8_t*)buf; tmppkt->size = size; ret = avcodec_encode_video2(video_enc_ctx, tmppkt, pFrame, &got_picture); if (ret < 0) { avio_close(pOFormat->pb); delete buf; return 0; } if (got_picture) { //ret = av_interleaved_write_frame(pOFormat, tmppkt); AVStream *in_stream = pFormat->streams[pkt->stream_index]; AVStream *out_stream = pMp4Format->streams[pkt->stream_index]; tmppkt->pts = av_rescale_q_rnd(tmppkt->pts, in_stream->time_base , out_stream->time_base, AV_ROUND_NEAR_INF); tmppkt->dts = av_rescale_q_rnd(tmppkt->dts, in_stream->time_base , out_stream->time_base, AV_ROUND_NEAR_INF); tmppkt->duration = av_rescale_q(tmppkt->duration, in_stream->time_base , out_stream->time_base); tmppkt->pos = -1; ret = av_interleaved_write_frame(pMp4Format, tmppkt); if (ret < 0) return 0; delete tmppkt; delete buf; } } //avcodec_free_frame(&pFrame); } else if (pkt->stream_index == 1) { AVStream *in_stream = pFormat->streams[pkt->stream_index]; AVStream *out_stream = pMp4Format->streams[pkt->stream_index]; pkt->pts = av_rescale_q_rnd(pkt->pts, in_stream->time_base , out_stream->time_base, AV_ROUND_NEAR_INF); pkt->dts = av_rescale_q_rnd(pkt->dts, in_stream->time_base , out_stream->time_base, AV_ROUND_NEAR_INF); pkt->duration = av_rescale_q(pkt->duration, in_stream->time_base , out_stream->time_base); pkt->pos = -1; if (av_interleaved_write_frame(pMp4Format, pkt) < 0) return 0; } } avcodec_free_frame(&pFrame); return 0; }
0 0
- ffmpeg 最简单的转码封装mp4文件
- ffmpeg 最简单的转码封装mp4文件
- 转: FFMpeg 封装MP4 文件
- 利用FFmpeg转码生成MP4文件
- 基于FFmpeg的封装格式MP4(TS)
- ffmpeg代码笔记4:获取MP4文件的码率
- 使用ffmpeg转码的MP4文件需要加载完了才能播放的解决办法
- 使用ffmpeg转码的MP4文件需要加载完了才能播放的解决办法
- PHP+FFMPEG自动转码H264标准Mp4文件
- PHP+FFMPEG自动转码H264标准Mp4文件
- ffmpeg封装PCM到MP4
- ffmpeg封装H246为MP4
- 关于ffmpeg封装h264为mp4的问题
- 最简单的基于FFMPEG的转码程序
- 最简单的基于FFMPEG的转码程序
- 最简单的基于FFMPEG的转码程序
- 最简单的基于FFMPEG的转码程序
- 最简单的基于FFMPEG的转码程序
- 苹果appstore应用名下面的开发者名称哪里填写或修改的?
- Java/C++中静态成员函数
- C++Primer第五版 练习11.17(解答)
- [LeetCode]Two Sum
- Objective - C 面向对象高级特性 - 包装类 | 类处理 | 类别 | 扩展 | 协议 | 委托 | 异常处理 | 反射
- ffmpeg 最简单的转码封装mp4文件
- mysql导入数据
- 深刻认识CGI
- 软件工程习题2
- C++Primer第五版 练习11.19(解答)
- 反斜杠在C/C++中的作用
- TransactionScope使用说明
- Lisp---牛顿迭代法求平方根
- 一名平庸程序员的自白