首页 > 其他分享 >FFmpeg - 将网络流保存到文件

FFmpeg - 将网络流保存到文件

时间:2024-12-24 19:44:19浏览次数:5  
标签:__ FFmpeg 网络 ret 保存 avformat packet av errMsg

1. 开发环境

● FFmpeg版本:7.1

● 开发环境:Ubuntu20.04

2. 基本流程

  • 网络包 -> 解复用/解封装 -> PES裸流 -> 再复用/封装 -> 保存到文件/转为网络流

3. 命令行实现网络流保存

  • ZL_MediaKit流媒体服务器地址: 192.168.16.230

  • 启动一个FFmpeg进程,向流媒体服务器推RTSP

    # TCP方式推RTSP流
    $ ffmpeg -re -stream_loop -1 -i ./clock.mp4 -vcodec h264 -acodec aac -f rtsp -rtsp_transport tcp rtsp://192.168.16.230/live/test
    
    # UDP方式推RTSP流,这也是RTSP的默认方式
    $ ffmpeg -re -stream_loop -1 -i ./clock.mp4 -vcodec h264 -acodec aac -f rtsp rtsp://192.168.16.230/live/test
    
    # -re: 实时速率读取输入,按照输入文件的原始帧率进行输出
    # -stream_loop -1: -1 表示无限循环,如果你想循环特定次数,可以用正整数替换 -1
    # -i ./clock.mp4: 指定输入文件为一个本地文件clock.mp4
    # -vcodec h264: 设置视频编码器为H.264,如果输入文件已经是H.264编码了,并不会重新编码
    # -acodec aac: 设置音频编码器为 AAC,同样,如果输入文件已经是 AAC 编码,不会重新编码。
    # -f rtsp: 指定输出格式为 RTSP 协议
    # -rtsp_transport tcp: 指定 RTSP 传输使用 TCP 协议,而不是默认的 UDP。TCP 通常更可靠,特别是在网络不稳定的情况下
    # rtsp://192.168.16.230/live/test: 这是输出的 RTSP 流地址。在这个例子中,流将被推送到 IP 地址为 192.168.16.230 的流媒体服务器,路径为 /live/test
    
  • 另启一个FFmpeg进程,从流媒体服务器拉取RTSP流,并保存为MP4格式

    $ ffmpeg -i rtsp://192.168.16.230/live/test -c copy output.mp4
    
  • 如下图:

  • ffplay播放保存的output.mp4

    $ ffplay output.mp4
    

4. FFmpeg代码

4.1 包含FFmpeg头文件

  • 建议不要一次引入FFmpeg所有的头文件,用到哪个模块就包含响应的头文件;这样一来可以帮助我们了解ffmpeg中各个模块功能划分,二来编译出的文件也没有那么大
  • 本例程中包含两个头文件即可
    extern "C"{
    #include "libavutil/mem.h"
    #include "libavformat/avformat.h"
    }
    

4.2 初始化FFmpeg

  • 本例中需要拉取网络流,因此需要初始化FFmpeg的网络组件
  • 设置FFmpeg内部日志等级
    // 初始化ffmpeg
    int Test::ffmpeg_init(void)
    {
        // 初始化FFmpeg的网络组件
        avformat_network_init();
        // 设置FFmpeg内部日志等级
        av_log_set_level(AV_LOG_INFO);
        
        return 0;
    }
    

4.3 打开输入源

  • 本例程中的输入源是一个网络流,可以是RTSP/RTMP...
    int Test::open_input(std::string iurl)
    {
        // 错误日志
        char errMsg[1024] = {0};
        
        // 输入格式上下文
        av_inputCtx = avformat_alloc_context();
        // 打开输入源
        int ret = avformat_open_input(&av_inputCtx, iurl.c_str(), nullptr, nullptr);
        if(ret < 0)	{
            av_strerror(ret, errMsg, sizeof(errMsg));
            fprintf(stderr, "%s errMsg:%s\n", __func__, errMsg);
            return -1;
        }
        
        // 从输入源中查找流
        ret = avformat_find_stream_info(av_inputCtx, nullptr);
        if(ret < 0)
        {
            // 关闭输入源
            avformat_close_input(&av_inputCtx);
            // 释放输入上下文
            avformat_free_context(av_inputCtx);
    
            av_strerror(ret, errMsg, sizeof(errMsg));
            fprintf(stderr, "%s errMsg:%s\n", __func__, errMsg);
            return -2;
        }
        
        fprintf(stdout, "%s open %s success!", __func__, iurl);
        
        return 0;
    }
    

4.4 创建输出流

  • 本例程中的将输出流保存到文件中,输出流就是一个文件
    // 创建输出流
    int Test::open_output(std::string ourl)
    {
        // 错误日志
        char errMsg[1024] = {0};
        
        // 输出上下文, ts流
        int ret = avformat_alloc_output_context2(&av_outputCtx, nullptr, "mpegts", ourl.c_str());
        if(ret < 0){
            av_strerror(ret, errMsg, sizeof(errMsg));
            fprintf(stderr, "%s avformat_alloc_output_context2 failed! errMsg:%s\n", __func__, errMsg);
            return -1;
        }
        
        // 创建输出流,写入文件调用avio_open2
        ret = avio_open2(&av_outputCtx->pb, ourl.c_str(), AVIO_FLAG_READ_WRITE, nullptr, nullptr);
            if(ret < 0){
            av_strerror(ret, errMsg, sizeof(errMsg));
            fprintf(stderr, "%s avio_open2 failed! errMsg:%s\n", __func__, errMsg);
            goto ERR1;
        }
        
        // 遍历输入流,拷贝输入流的编码参数到输出流
        for(int i = 0; i < av_inputCtx->nb_streams; i++){
        
            // 创建输出流
            AVStream *out_stream = avformat_new_stream(av_outputCtx, nullptr);
            // 拷贝输入流的编码参数到输出流
            ret = avcodec_parameters_copy(out_stream->codecpar, av_inputCtx->streams[i]->codecpar);
            if(ret < 0){
                av_strerror(ret, errMsg, sizeof(errMsg));
                fprintf(stderr, "%s avcodec_parameters_copy failed! errMsg:%s\n", __func__, errMsg);
                goto ERR2;
            }
        
        }
        
        // 初始化媒体文件的头部信息
        ret = avformat_write_header(av_outputCtx, nullptr);
        if(ret < 0){
            av_strerror(ret, errMsg, sizeof(errMsg));
            fprintf(stderr, "%s avformat_write_header failed! errMsg:%s\n", __func__, errMsg);
            goto ERR2;
        }
        
        fprintf(stdout, "%s open %s success!", __func__, ourl.c_str());
        
        return 0;
        
    ERR2:
        if(av_outputCtx){
            avio_closep(&av_outputCtx->pb);
        }
        
    ERR1:
        avformat_free_context(av_outputCtx);
        
        return ret;
    }
    

4.5 从输入流中读取一个包

  • 从输入流中读取一个包
    std::shared_ptr<AVPacket> Test::read_packet_from_source()
    {
      // av packet, std::shared_ptr
      std::shared_ptr<AVPacket> packet(av_packet_alloc(), [](AVPacket *p){av_packet_free(&p);});
      if(!packet){
        fprintf(stderr, "%s av_packet_alloc failed!", __func__);
        return nullptr;
      }
    
      // 从输入流中获取一个编码后的包 PES包
      int ret = av_read_frame(av_inputCtx, packet.get());
      if(ret < 0){
        av_strerror(ret, errMsg, sizeof(errMsg));
        fprintf(stderr, "%s avformat_write_header failed! errMsg:%s\n", __func__, errMsg);
        return nullptr;
      }
    	
      return packet;
    }
    

4.6 向输出流中写入一个包

  • 向输出流中写入一个包
    // 向输出流中写包
    int Test::write_packet_to_target(std::shared_ptr<AVPacket> packet)
    {
      auto inputStream = av_inputCtx->streams[packet->stream_index];
      auto outputStream = av_outputCtx->streams[packet->stream_index];
    
    	// 时间基转换
    	av_packet_rescale_ts(packet.get(), inputStream->time_base, outputStream->time_base);
    	return av_interleaved_write_frame(av_outputCtx, packet.get());
    }
    

4.7 拉取RTSP流,并保存到文件

  • 拉取RTSP流,并保存到文件
// 获取RTSP网络流,保存到文件;也可以获取其它协议的网络流,如RTMP,SRT
int Test::rtsp_save_to_file(void)
{
  // 初始化
  ffmpeg_init();
  // 打开输入流
  int ret = open_input(std::string("rtsp://192.168.16.230/live/test"));
  if(ret < 0){
    return -1;
  }

  // 打开输出流
  ret = open_output(std::string("/tmp/test.ts"));

  // 循环从输入流中读包,写入到输出流中
  while(true){
    auto packet = read_packet_from_source();
    if(packet){
      write_packet_to_target(packet);
      fprintf(stdout, "%s writePacket success", __func__);
    }else{
      fprintf(stderr, "%s writePacket failed!\n", __func__);
      return -1;
    }
  }

  return 0;
}

4.8 释放资源

  • 释放资源
    // 释放资源
    void Test::release(void)
    {
      // 关闭输入源
      avformat_close_input(&av_inputCtx);
      // 释放输入上下文
    
      if(av_inputCtx)
        avformat_free_context(av_inputCtx);
    
      // 关闭输出源
      if(av_outputCtx->pb)
        avio_close(av_outputCtx->pb);
      // 释放输出格式上下文
      if(av_outputCtx)
        avformat_free_context(av_outputCtx);
    
      // 释放ffmpeg网络资源
      avformat_network_deinit();
    }
    

标签:__,FFmpeg,网络,ret,保存,avformat,packet,av,errMsg
From: https://www.cnblogs.com/zhijun1996/p/18628530

相关文章

  • 网络安全词云图与技术浅谈
    网络安全词云图与技术浅谈一、网络安全词云图生成为了直观地展示网络安全领域的关键术语,我们可以通过词云图(WordCloud)的形式来呈现。词云图是一种数据可视化工具,它通过字体大小和颜色的差异来突出显示文本中出现频率较高的词汇。以下是一些常用的网络安全术语,它们将构成......
  • [学习笔记] 网络流
    网络流,梳理一下然后看下trick。网络流主要难点在于建模,网络流很多trick现在已经很难有新意了。很多很好想的都是紫题,没啥含金量啊。最大流在残量网络中找到一条路径,设边集为\(u\),要求满足\(\min_{x\inu}C_x≠0\),即每条边残量皆不为\(0\)。此时将这条路径流满,流量就......
  • OrayUSBVHCI 驱动程序通常与 USB 虚拟主机控制器接口 (VHCI) 技术相关,这意味着它可
    OrayUSBVHCI是由上海贝斯特网络信息技术有限公司(ShanghaiBestOrayInformationTechnologyCo.,Ltd.)开发的一个USB驱动程序。它的版本是1.0.0.0,发布时间为2023年3月8日。OrayUSBVHCI驱动程序简介功能:OrayUSBVHCI 驱动程序通常与 USB虚拟主机控制器接口(VHCI)......
  • 漏洞扫描:网络安全的 “体检” 与 “防护指南”
    在当今数字化时代,网络安全如同守护城堡的坚固城墙,而漏洞扫描则是检查城墙是否存在缝隙与薄弱环节的重要手段。那么,究竟什么是漏洞扫描?又该如何进行呢?什么是漏洞扫描?漏洞扫描是一种安全检测过程,旨在识别计算机系统、网络或应用程序中的安全漏洞。这些漏洞可能被恶意用户利用以获......
  • 网络安全渗透实战!记一次攻防演练渗透测试实战,黑客技术零基础入门到实战教程!
    1、外网打点资产发现多测绘平台搜索https://hunter.qianxin.com/https://fofa.info/https://quake.360.cn/多语法搜索我给大家准备了一份全套的《网络安全入门+进阶学习资源包》包含各种常用工具和黑客技术电子书以及视频教程,需要的小伙伴可以扫描下方二维码或链接......
  • 【网络安全渗透测试零基础入门】之什么是文件包含漏洞&分类(非常详细)收藏这一篇就够了!
    一、前言大家好,我是强哥今天主要给大家讲解一下什么是文件包含漏洞、本地文件包含漏洞喜欢的朋友们,记得给我点赞支持和收藏一下,关注我,学习黑客技术。一、什么是文件包含漏洞1.文件包含漏洞概述和SQL注入等攻击方式一样,文件包含漏洞也是一种注入型漏洞,其本质就是输入......
  • 深度探秘神经网络模型:核心要点、多样类型与实践应用
    基本概念神经元与生物启发:人工神经网络受人类大脑中的生物神经元启发,生物神经元由细胞体、树突和轴突等组成,可处于兴奋或抑制状态,通过突触传递信息。神经网络组成:由大量相互连接的神经元组成,包括输入层接收数据、隐藏层处理数据、输出层产生最终结果,各层神经元通过权重连接,还有......
  • 重庆市某区教委城域网网络管理与态势感知项目
        重庆市某区教育委员会是区政府直辖的一级政府职能部门,主要负责本区的教育工作。项目现状    重庆市某区教育委员会肩负着该地区众多学校和教育机构的信息化重任。随着全区教育数字化转型进程的不断推进,如何确保城域网的稳定性与高效性运作已成为其核心关注......
  • 山东科技大学网络安全协议分析实训
     一、课程设计题目:                   网络安全协议分析实训                               二、课程设计主要参考资料:   《信息安全案例教程技术与应用》                     三、课程设......
  • 网络抓包调试实践
    网络抓包这个话题,有一定开发经验的多少都有所了解,常用软件Wireshark,Fiddler用起来也非常傻瓜,本文不会涉及。一般的介绍网络抓包的文章,到能抓到数据包通常就结束了。但显然认识工具是一会儿事儿,理解本质,清楚实践中啥时候该用,是另外一会儿事儿。让新人小白自行举一反三,可能多少......