• ffmpeg封装和解封装介绍-(8)解封装和封装重构


    头文件:

    xformat.h

    1. #pragma once
    2. /// 封装和解封装基类
    3. #include
    4. struct AVFormatContext;
    5. struct AVCodecParameters;
    6. struct AVPacket;
    7. struct XRational
    8. {
    9. int num; ///< Numerator
    10. int den; ///< Denominator
    11. };
    12. class XFormat
    13. {
    14. public:
    15. ///
    16. /// 复制参数 线程安全
    17. ///
    18. /// 对应c_->streams 下标
    19. /// 输出参数
    20. /// 是否成功
    21. bool CopyPara(int stream_index, AVCodecParameters* dst);
    22. ///
    23. /// 设置上下文,并且清理上次的设置的值,如果传递NULL,相当于关闭上下文3
    24. /// 线程安全
    25. ///
    26. ///
    27. void set_c(AVFormatContext* c);
    28. int audio_index() { return audio_index_; }
    29. int video_index() { return video_index_; }
    30. XRational video_time_base(){ return video_time_base_; }
    31. XRational audio_time_base() { return audio_time_base_; }
    32. protected:
    33. AVFormatContext* c_ = nullptr; //封装解封装上下文
    34. std::mutex mux_; //c_ 资源互斥
    35. int video_index_ = 0;//video和audio在stream中索引
    36. int audio_index_ = 1;
    37. XRational video_time_base_ = {1,25};
    38. XRational audio_time_base_ = {1,9000};
    39. };

    xdemux.h

    1. #pragma once
    2. #include "xformat.h"
    3. class XDemux :public XFormat
    4. {
    5. public:
    6. ///
    7. /// 打开解封装
    8. ///
    9. /// 解封装地址 支持rtsp
    10. /// 失败返回nullptr
    11. static AVFormatContext* Open(const char* url);
    12. ///
    13. /// 读取一帧数据
    14. ///
    15. /// 输出数据
    16. /// 是否成功
    17. bool Read(AVPacket* pkt);
    18. };

    xmux.h

    1. #pragma once
    2. #include "xformat.h"
    3. //
    4. /// 媒体封装
    5. class XMux :public XFormat
    6. {
    7. public:
    8. //
    9. 打开封装
    10. static AVFormatContext* Open(const char* url);
    11. bool WriteHead();
    12. bool Write(AVPacket* pkt);
    13. bool WriteEnd();
    14. };

    源文件:

    main.cpp

    1. #include
    2. #include
    3. #include "xdemux.h"
    4. #include "xmux.h"
    5. using namespace std;
    6. extern "C" { //指定函数是c语言函数,函数名不包含重载标注
    7. //引用ffmpeg头文件
    8. #include
    9. }
    10. //预处理指令导入库
    11. #pragma comment(lib,"avformat.lib")
    12. #pragma comment(lib,"avutil.lib")
    13. #pragma comment(lib,"avcodec.lib")
    14. void PrintErr(int err)
    15. {
    16. char buf[1024] = { 0 };
    17. av_strerror(err, buf, sizeof(buf) - 1);
    18. cerr << endl;
    19. }
    20. #define CERR(err) if(err!=0){ PrintErr(err);getchar();return -1;}
    21. int main(int argc, char* argv[])
    22. {
    23. //打开媒体文件
    24. const char* url = "v1080.mp4";
    25. /// 解封装
    26. //解封装输入上下文
    27. XDemux demux;
    28. auto demux_c = demux.Open(url);
    29. demux.set_c(demux_c);
    30. /// 封装
    31. //编码器上下文
    32. const char* out_url = "test_mux.mp4";
    33. XMux mux;
    34. auto mux_c = mux.Open(out_url);
    35. mux.set_c(mux_c);
    36. auto mvs = mux_c->streams[mux.video_index()]; //视频流信息
    37. auto mas = mux_c->streams[mux.audio_index()]; //视频流信息
    38. //有视频
    39. if (demux.video_index() >= 0)
    40. {
    41. mvs->time_base.num = demux.video_time_base().num;
    42. mvs->time_base.den = demux.video_time_base().den;
    43. //复制视频参数
    44. demux.CopyPara(demux.video_index(), mvs->codecpar);
    45. }
    46. //有音频
    47. if (demux.audio_index() >= 0)
    48. {
    49. mas->time_base.num = demux.audio_time_base().num;
    50. mas->time_base.den = demux.audio_time_base().den;
    51. //复制音频参数
    52. demux.CopyPara(demux.audio_index(), mas->codecpar);
    53. }
    54. mux.WriteHead();
    55. /// 截取10 ~ 20 秒之间的音频视频 取多不取少
    56. // 假定 9 11秒有关键帧 我们取第9秒
    57. double begin_sec = 10.0; //截取开始时间
    58. double end_sec = 20.0; //截取结束时间
    59. long long begin_pts = 0;
    60. long long begin_audio_pts = 0; //音频的开始时间
    61. long long end_pts = 0;
    62. AVPacket pkt;
    63. for (;;)
    64. {
    65. if (!demux.Read(&pkt))
    66. {
    67. break;
    68. }
    69. pkt.pos = -1;
    70. //写入音视频帧 会清理pkt
    71. mux.Write(&pkt);
    72. }
    73. //写入结尾 包含文件偏移索引
    74. mux.WriteEnd();
    75. /*re = av_write_trailer(ec);
    76. if (re != 0)PrintErr(re);*/
    77. //avformat_close_input(&ic);
    78. demux.set_c(nullptr);
    79. mux.set_c(nullptr);
    80. getchar();
    81. return 0;
    82. }

    xformat.cpp

    1. #include "xformat.h"
    2. #include
    3. #include
    4. using namespace std;
    5. extern "C" { //指定函数是c语言函数,函数名不包含重载标注
    6. //引用ffmpeg头文件
    7. #include
    8. }
    9. //预处理指令导入库
    10. #pragma comment(lib,"avformat.lib")
    11. #pragma comment(lib,"avutil.lib")
    12. using namespace std;
    13. void XFormat::set_c(AVFormatContext* c)
    14. {
    15. unique_lock lock(mux_);
    16. if (c_) //清理原值
    17. {
    18. if (c_->oformat) //输出上下文
    19. {
    20. if (c_->pb)
    21. avio_closep(&c_->pb);
    22. avformat_free_context(c_);
    23. }
    24. else if (c_->iformat) //输入上下文
    25. {
    26. avformat_close_input(&c_);
    27. }
    28. else
    29. {
    30. avformat_free_context(c_);
    31. }
    32. }
    33. c_ = c;
    34. if (!c_)return;
    35. //用于区分是否有音频或者视频流
    36. audio_index_ = -1;
    37. video_index_ = -1;
    38. //区分音视频stream 索引
    39. for (int i = 0; i < c->nb_streams; i++)
    40. {
    41. //音频
    42. if (c->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
    43. {
    44. audio_index_ = i;
    45. audio_time_base_.den = c->streams[i]->time_base.den;
    46. audio_time_base_.num = c->streams[i]->time_base.num;
    47. }
    48. else if (c->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
    49. {
    50. video_index_ = i;
    51. video_time_base_.den = c->streams[i]->time_base.den;
    52. video_time_base_.num = c->streams[i]->time_base.num;
    53. }
    54. }
    55. }
    56. ///
    57. /// 复制参数 线程安全
    58. ///
    59. /// 对应c_->streams 下标
    60. /// 输出参数
    61. /// 是否成功
    62. bool XFormat::CopyPara(int stream_index, AVCodecParameters* dst)
    63. {
    64. unique_lock lock(mux_);
    65. if (!c_)
    66. {
    67. return false;
    68. }
    69. if (stream_index<0 || stream_index>c_->nb_streams)
    70. return false;
    71. auto re = avcodec_parameters_copy(dst, c_->streams[stream_index]->codecpar);
    72. if (re < 0)
    73. {
    74. return false;
    75. }
    76. return true;
    77. }

    xdemux.cpp

    1. #include "xdemux.h"
    2. #include
    3. #include
    4. using namespace std;
    5. extern "C" { //指定函数是c语言函数,函数名不包含重载标注
    6. //引用ffmpeg头文件
    7. #include
    8. }
    9. static void PrintErr(int err)
    10. {
    11. char buf[1024] = { 0 };
    12. av_strerror(err, buf, sizeof(buf) - 1);
    13. cerr << buf << endl;
    14. }
    15. #define BERR(err) if(err!= 0){PrintErr(err);return 0;}
    16. AVFormatContext* XDemux::Open(const char* url)
    17. {
    18. AVFormatContext* c = nullptr;
    19. //打开封装上下文
    20. auto re = avformat_open_input(&c, url, nullptr, nullptr);
    21. BERR(re);
    22. //获取媒体信息
    23. re = avformat_find_stream_info(c, nullptr);
    24. BERR(re);
    25. //打印输入封装信息
    26. av_dump_format(c, 0, url, 0);
    27. return c;
    28. }
    29. bool XDemux::Read(AVPacket* pkt)
    30. {
    31. unique_lock lock(mux_);
    32. if (!c_)return false;
    33. auto re = av_read_frame(c_, pkt);
    34. BERR(re);
    35. return true;
    36. }

    xmux.cpp

    1. #include "xmux.h"
    2. #include
    3. #include
    4. using namespace std;
    5. extern "C" { //指定函数是c语言函数,函数名不包含重载标注
    6. //引用ffmpeg头文件
    7. #include
    8. }
    9. static void PrintErr(int err)
    10. {
    11. char buf[1024] = { 0 };
    12. av_strerror(err, buf, sizeof(buf) - 1);
    13. cerr << buf << endl;
    14. }
    15. #define BERR(err) if(err!= 0){PrintErr(err);return 0;}
    16. //
    17. 打开封装
    18. AVFormatContext* XMux::Open(const char* url)
    19. {
    20. AVFormatContext* c = nullptr;
    21. //创建上下文
    22. auto re = avformat_alloc_output_context2(&c, NULL, NULL, url);
    23. BERR(re);
    24. //添加视频音频流
    25. auto vs = avformat_new_stream(c, NULL); //视频流
    26. vs->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
    27. auto as = avformat_new_stream(c, NULL); //音频流
    28. as->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
    29. //打开IO
    30. re = avio_open(&c->pb, url, AVIO_FLAG_WRITE);
    31. BERR(re);
    32. return c;
    33. }
    34. bool XMux::Write(AVPacket* pkt)
    35. {
    36. unique_lock lock(mux_);
    37. if (!c_)return false;
    38. //写入一帧数据,内部缓冲排序dts,通过pkt=null 可以写入缓冲
    39. auto re = av_interleaved_write_frame(c_,pkt);
    40. BERR(re);
    41. return true;
    42. }
    43. bool XMux::WriteEnd()
    44. {
    45. unique_lock lock(mux_);
    46. if (!c_)return false;
    47. av_interleaved_write_frame(c_, nullptr);//写入排序缓冲
    48. auto re = av_write_trailer(c_);
    49. BERR(re);
    50. return true;
    51. }
    52. bool XMux::WriteHead()
    53. {
    54. unique_lock lock(mux_);
    55. if (!c_)return false;
    56. auto re = avformat_write_header(c_, nullptr);
    57. BERR(re);
    58. //打印输出上下文
    59. av_dump_format(c_, 0, c_->url, 1);
    60. return true;
    61. }

    运行结果:

    重新生成了一个名字为test_mux.mp4文件

  • 相关阅读:
    使用STM32怎么喂狗 (IWDG)
    【Python 实战】---- 批量将图片转base64导出到excel中
    基于SpringBoot+Vue的超市货物管理系统
    [机缘参悟-33]:眼见不一定为实,大多数时候“眼见为虚”
    HCIA --- ACL(访问控制列表)
    实战|基于YOLOv10与MobileSAM实现目标检测与分割【附完整源码】
    【论文阅读】- 我对“AlexNet”的理解
    (附源码)计算机毕业设计SSM酒店停车管理系统
    electron-vite工具打包后通过内置配置文件动态修改接口地址实现方法
    HTML知识点
  • 原文地址:https://blog.csdn.net/m0_51386664/article/details/139704620