当前位置:   article > 正文

ffmpeg 将h264格式文件编码为MP4文件_ffmpeg h264转mp4

ffmpeg h264转mp4

1、在编码过程中涉及到一些时间戳相关概念主要:

PTS:度量解码后的视频帧什么时候被显示出来;

DTS:读入内存中的bit流在什么时候开始送入解码器中进行解码;

duration:表示当前帧的持续时间占多少个时间单位;

相关参数的理解可以参考:https://www.jianshu.com/p/1bc03e97004e/

2、实现流程图:

 

 3、程序实现:

  1. void MainWindow::h264Tomp4()
  2. {
  3. AVFormatContext* pInFmtCtx = nullptr;
  4. int nInStreamIndex = -1;
  5. AVFormatContext* pOutFmtCtx = nullptr;
  6. AVStream* pOutVStream = nullptr;
  7. int nOutStreamIndex = -1;
  8. AVCodecContext* pOutCodecCtx = nullptr;
  9. do{
  10. //打开输入文件,并填充AVFormatContext上下文
  11. QString strInFile = "in.h264";
  12. if(avformat_open_input(&pInFmtCtx, strInFile.toStdString().c_str(), NULL, NULL) < 0)
  13. {
  14. qDebug() << "cannot open input file." << endl;
  15. break;
  16. }
  17. //查找输入文件中的流信息
  18. if(avformat_find_stream_info(pInFmtCtx, NULL) < 0){
  19. qDebug() << "cannot find stream info." << endl;
  20. break;
  21. }
  22. //查找视频流在流信息中的索引的索引
  23. for(size_t i = 0;i < pInFmtCtx->nb_streams;i++){
  24. if(pInFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
  25. nInStreamIndex = (int)i;
  26. break;
  27. }
  28. }
  29. //获取输入视频流的编码器参数
  30. AVCodecParameters *codecPara = pInFmtCtx->streams[nInStreamIndex]->codecpar;
  31. //创建AVFormatContext上下文
  32. QString strOutFile = "out.mp4";
  33. if(avformat_alloc_output_context2(&pOutFmtCtx, NULL, NULL, strOutFile.toStdString().c_str()) < 0){
  34. qDebug() << "cannot alloc outfile context." << endl;
  35. break;
  36. }
  37. //打开输出文件并填充AVFormatContext上下文数据
  38. if(avio_open(&pOutFmtCtx->pb, strOutFile.toStdString().c_str(), AVIO_FLAG_READ_WRITE) < 0){
  39. qDebug() << "cannot open output file." << endl;
  40. break;
  41. }
  42. //为输出AVFormatContext上下文添加视频流,并设置参数
  43. if(nullptr == (pOutVStream = avformat_new_stream(pOutFmtCtx, NULL)))
  44. {
  45. qDebug() << "cannot new output stream." << endl;
  46. break;
  47. }
  48. pOutVStream->time_base.den = 25;
  49. pOutVStream->time_base.num = 1;
  50. nOutStreamIndex = pOutVStream->index;
  51. //mp4中的视频也是h264编码,所以输入和输出编码器一致:
  52. //查找编码器
  53. AVCodec* outCodec = const_cast<AVCodec*>(avcodec_find_encoder(codecPara->codec_id));
  54. if(outCodec==NULL){
  55. printf("Cannot find any encoder.\n");
  56. break;
  57. }
  58. //创建输出编码器上下文
  59. pOutCodecCtx = avcodec_alloc_context3(outCodec);
  60. AVCodecParameters* outCodecPara = pOutFmtCtx->streams[pOutVStream->index]->codecpar;
  61. if(avcodec_parameters_copy(outCodecPara, codecPara)<0){
  62. qDebug() << "cannot copy codecparam";
  63. break;
  64. }
  65. if(avcodec_parameters_to_context(pOutCodecCtx, outCodecPara)<0){
  66. qDebug() << "avcodec_parameters_to_context failed";
  67. break;
  68. }
  69. pOutCodecCtx->time_base.den=25;
  70. pOutCodecCtx->time_base.num=1;
  71. //打开输出流编码器
  72. if(avcodec_open2(pOutCodecCtx,outCodec,NULL) < 0){
  73. qDebug() << "Cannot open output codec";
  74. break;
  75. }
  76. //写入文件头
  77. if(avformat_write_header(pOutFmtCtx, NULL) < 0){
  78. qDebug() << "Cannot write header to file";
  79. break;
  80. }
  81. //
  82. AVPacket pkt;
  83. std::int64_t llFrameIndex = 0;
  84. AVStream* inVStream = pInFmtCtx->streams[nInStreamIndex];
  85. //循环读取帧
  86. while(av_read_frame(pInFmtCtx, &pkt) >= 0){
  87. //判断是视频流
  88. if(pkt.stream_index == nInStreamIndex){
  89. if(pkt.pts == AV_NOPTS_VALUE){
  90. //PTS:度量解码后的视频帧什么时候被显示出来
  91. //DTS:读入内存中的bit流在什么时候开始送入解码器中进行解码
  92. //获取时间基:即用来度量单位刻度时间
  93. AVRational time_base1 = inVStream->time_base;
  94. //计算两帧视频之间的实际间隔时间,单位是us
  95. int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(inVStream->r_frame_rate);
  96. //计算PTS,即经过多少个刻度显示该帧视频
  97. pkt.pts = (double)(llFrameIndex*calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE);
  98. pkt.dts = pkt.pts;
  99. //两帧间隔时间占多少个刻度
  100. pkt.duration = (double)calc_duration / (double)(av_q2d(time_base1)*AV_TIME_BASE);
  101. llFrameIndex++;
  102. }
  103. //将输入流中时间基的PTS转换到输出流时间基中的PTS
  104. pkt.pts = av_rescale_q_rnd(pkt.pts, inVStream->time_base, pOutVStream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
  105. //将输入流中时间基的DTS转换到输出流时间基中的DTS
  106. pkt.dts = av_rescale_q_rnd(pkt.dts, inVStream->time_base, pOutVStream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
  107. //将输入流中时间基的duration转换到输出流时间基中的duration
  108. pkt.duration = av_rescale_q(pkt.duration, inVStream->time_base, pOutVStream->time_base);
  109. pkt.pos = -1;
  110. pkt.stream_index = nOutStreamIndex;
  111. //写入一帧数据
  112. if (av_interleaved_write_frame(pOutFmtCtx, &pkt) < 0) {
  113. break;
  114. }
  115. av_packet_unref(&pkt);
  116. }
  117. }
  118. av_write_trailer(pOutFmtCtx);
  119. }while(0);
  120. if(pOutCodecCtx)
  121. {
  122. avcodec_close(pOutCodecCtx);
  123. avcodec_free_context(&pOutCodecCtx);
  124. }
  125. if(pInFmtCtx)
  126. {
  127. avformat_free_context(pInFmtCtx);
  128. }
  129. if(pOutFmtCtx)
  130. {
  131. if(pOutFmtCtx->pb)
  132. {
  133. avio_close(pOutFmtCtx->pb);
  134. }
  135. avformat_free_context(pOutFmtCtx);
  136. }
  137. }

 4、下面使用自己写的播放器播放一下MP4文件:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/491172
推荐阅读
相关标签
  

闽ICP备14008679号