• 【Qt+FFMPEG】- FFMPEG转码详细流程


    一:FFMPEG转码过程

    输入视频的封装格式是FLV,视频编码标准是H.264,音频编码标准是AAC;输出视频的封装格式是AVI,视频编码标准是MPEG2,音频编码标准是MP3

     

     

    二:FFMPEG转码流程

     

    本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓ 

    三:函数介绍

     

    四:转码具体代码实现

    4.1 转码类的定义如下

    1. #ifndef TRANSCODINGVIDEO_H
    2. #define TRANSCODINGVIDEO_H
    3. #include <QObject>
    4. #include<QThread>
    5. //当前C++兼容C语言
    6. extern "C"
    7. {
    8. //avcodec:编解码(最重要的库)
    9. #include <libavcodec/avcodec.h>
    10. //avformat:封装格式处理
    11. #include <libavformat/avformat.h>
    12. //swscale:视频像素数据格式转换
    13. #include <libswscale/swscale.h>
    14. //avdevice:各种设备的输入输出
    15. #include <libavdevice/avdevice.h>
    16. //avutil:工具库(大部分库都需要这个库的支持)
    17. #include <libavutil/avutil.h>
    18. }
    19. class transcodingVideo : public QThread
    20. {
    21. Q_OBJECT
    22. public:
    23. transcodingVideo();
    24. //打开H264视频文件
    25. void openFile(QString file);
    26. //根据我们需要的封装格式进行处理
    27. void outPut(QString fileout);
    28. AVFormatContext *forContext,*formatout;//保存数据的结构体 forContext存输入进来的视频信息;formatout存储最终输出的视频信息
    29. AVPacket *pkt;//pkt
    30. int videoType;
    31. signals:
    32. public slots:
    33. };
    34. #endif // TRANSCODINGVIDEO_H

    4.2 转码具体实现过程如下 

    1. #include "transcodingvideo.h"
    2. #include<QDebug>
    3. transcodingVideo::transcodingVideo()
    4. {
    5. //注册组件
    6. av_register_all();
    7. forContext= avformat_alloc_context();
    8. }
    9. void transcodingVideo::openFile(QString file)
    10. {
    11. //打开输入视频
    12. int res=avformat_open_input(&forContext,file.toStdString().c_str(),nullptr,nullptr);
    13. //判断是否打开成功
    14. if(res<0)
    15. {
    16. qDebug()<<"打开失败";
    17. return;
    18. }
    19. //打开视频文件成功,获取文件信息
    20. res = avformat_find_stream_info(forContext,nullptr);//查看有没有相关视频流信息
    21. if(res<0)//判断是否有流媒体
    22. {
    23. qDebug()<<"没有流媒体信息"<<endl;
    24. return;
    25. }
    26. //一个视频流有多股码流,存在forContentext中streams数组中
    27. int videoType=-1;
    28. for(int i=0;i<forContext->nb_streams;i++) //i小于流的个数
    29. {
    30. if(forContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)//视频流
    31. {
    32. videoType=i;//标识类型
    33. break;
    34. }
    35. }
    36. if(videoType==-1)
    37. {
    38. qDebug()<<"没有视频流相关信息"<<endl;
    39. return;
    40. }
    41. qDebug()<<"输入的准备已经完成";
    42. }
    43. void transcodingVideo::outPut(QString fileout)
    44. {
    45. //猜测编码器
    46. AVOutputFormat *avformat = av_guess_format(nullptr,fileout.toStdString().c_str(),nullptr);
    47. if(avformat==nullptr)
    48. {
    49. qDebug()<<"没有编码器!";
    50. return;
    51. }
    52. qDebug()<<"AVOutputFormat";
    53. //保存输出视频信息的结构体
    54. formatout = avformat_alloc_context();
    55. //设置输出格式
    56. formatout->oformat = avformat;
    57. //打开视频流 文件流
    58. //参数1:输入输出的上下文对象
    59. //参数2:文件流路径
    60. //参数3:文件打开格式 写的方式
    61. int res=avio_open(&formatout->pb,fileout.toStdString().c_str(),AVIO_FLAG_WRITE);
    62. if(res<0)
    63. {
    64. qDebug()<<"open file error";
    65. return;
    66. }
    67. qDebug()<<"avio_open";
    68. //新建视频流
    69. //参数1:视频信息结构体
    70. //参数2:新建流 的 返回新建流 的地址
    71. AVStream *newStream =avformat_new_stream(formatout,nullptr);
    72. if(newStream==nullptr)
    73. {
    74. qDebug()<<"打开视频流失败";
    75. return;
    76. }
    77. qDebug()<<"newStream";
    78. //编码器对应参数设置 拷贝参数设置 newStream:输入进入流的参数设置
    79. res = avcodec_parameters_copy(newStream->codecpar,forContext->streams[videoType]->codecpar);
    80. qDebug()<<"res="<<res;
    81. if(res<0)
    82. {
    83. qDebug()<<"拷贝失败!";
    84. return;
    85. }
    86. qDebug()<<"res="<<res;
    87. //设置新的流里面 codec_tag 设置为0
    88. newStream->codecpar->codec_tag = 0;
    89. //头部信息写入----写入成功与否
    90. res = avformat_write_header(formatout,nullptr);//formatout封装格式的结构体
    91. //判断写入成功与否
    92. if(res<0)
    93. {
    94. qDebug()<<"写入头部信息失败!";
    95. return;
    96. }
    97. qDebug()<<"res="<<res;
    98. //开始读取码流数据
    99. pkt = (AVPacket*)malloc(sizeof(AVPacket));
    100. //算出这张图有多大
    101. int size = newStream->codecpar->width*newStream->codecpar->height;
    102. av_new_packet(pkt,size);
    103. int frameCount=0;
    104. //一帧一帧的读取
    105. while(av_read_frame(forContext,pkt)==0)
    106. {
    107. //判断这一帧这是不是视频流
    108. if(pkt->stream_index==videoType)
    109. {
    110. frameCount++;
    111. //如果是视频流----判断有没有设置过 时间基
    112. if(pkt->pts==AV_NOPTS_VALUE)
    113. {
    114. //时间基 time_base AVRational属性
    115. AVRational timebase=forContext->streams[videoType]->time_base;
    116. //计算帧之间的长度(duration) double强制转换
    117. int64_t duration=(double)AV_TIME_BASE/av_q2d(forContext->streams[videoType]->r_frame_rate);
    118. //计算显示时间基(pts):公式:(当前帧数*两帧之间的长度))/(输入时间基*AV_TIME_BASE)
    119. pkt->pts = (double)(frameCount*duration)/(av_q2d(timebase)*AV_TIME_BASE);
    120. //解码时间基(dts)
    121. pkt->dts = pkt->pts;
    122. //目标两帧之间的长度
    123. pkt->duration = duration/(double)(av_q2d(timebase)*AV_TIME_BASE);
    124. }
    125. else if(pkt->pts < pkt->dts)//显示 时间基 小于 解码时间基 不要这样子的
    126. {
    127. continue;
    128. }
    129. //上述步骤为 时间基设置
    130. //解码 时间基 真正的转换 如下:
    131. //显示时间基的转换
    132. pkt->pts = av_rescale_q_rnd(pkt->pts,forContext->streams[videoType]->time_base,
    133. newStream->time_base,(AVRounding)(AV_ROUND_INF | AV_ROUND_PASS_MINMAX));
    134. //解码时间基的转换
    135. pkt->dts = av_rescale_q_rnd(pkt->dts,forContext->streams[videoType]->time_base,
    136. newStream->time_base,(AVRounding)(AV_ROUND_INF | AV_ROUND_PASS_MINMAX));
    137. //数据时长设置
    138. pkt->duration = av_rescale_q(pkt->duration,forContext->streams[videoType]->time_base,
    139. newStream->time_base);
    140. //数据位置的设置 数据在流信息中的设置
    141. pkt->pos = -1;
    142. //数据包的标记:结合AV_PKT_FLAG_KEY使用 最小为1表示这一帧是一个关键帧
    143. pkt->flags |=AV_PKT_FLAG_KEY;
    144. //标记:当前写入的这一帧是视频流
    145. pkt->stream_index = 0;
    146. //转码后的数据包 写入 目标视频信息 结构体 中
    147. av_interleaved_write_frame(formatout,pkt);
    148. }
    149. //清空处理:重新设置包
    150. av_packet_unref(pkt);
    151. }
    152. //写入尾巴帧
    153. av_write_trailer(formatout);
    154. //用完之后进行 关闭 处理 :关闭猜测完的流
    155. avio_close(formatout->pb);//对应avio_open()
    156. qDebug()<<"avio_close";
    157. //释放malloc的空间 释放保存信息的结构体
    158. av_free(formatout);
    159. qDebug()<<"av_free";
    160. //关闭输入流
    161. avformat_close_input(&forContext);//对应avformat_open_inpu
    162. qDebug()<<"avformat_close_input";
    163. //释放forContext结构体空间
    164. av_free(forContext);
    165. qDebug()<<"av_free";
    166. }

    4.3 转码结果测试如下 

    1. //视频转码操作
    2. transcodingVideo *p = new transcodingVideo; //转码
    3. p->openFile("outfile.h264");
    4. p->outPut("outfile.mp4");

     

     

    编码所得到的.h264文件经过转码操作生成.mp4文件,可打开.mp4文件进行播放

    如果你对音视频开发感兴趣,觉得文章对您有帮助,别忘了点赞、收藏哦!或者对本文的一些阐述有自己的看法,有任何问题,欢迎在下方评论区讨论! 

     本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

  • 相关阅读:
    异步回调
    QProcess 调用 ffmpeg来处理音频
    想要彻底搞的性能优化,得先从底层逻辑开始了解~
    Linux内存管理 | 一、内存管理的由来及思想
    Java面向对象编程
    【新书推荐】AI时代,当程序员遇到ChatGPT,开发效率飞起来!
    机器学习基础知识
    Mental Poker- Part 2
    个人主页汇总 | 私信没空看,建议b站
    FL Studio21.1.0水果中文官方网站
  • 原文地址:https://blog.csdn.net/m0_60259116/article/details/127091769