【音视频 ffmpeg 学习】 RTMP推流 mp4文件

1.RTMP(实时消息传输协议)是Adobe 公司开发的一个基于TCP的应用层协议。
2.RTMP协议中基本的数据单元称为消息(Message)。
3.当RTMP协议在互联网中传输数据的时候,消息会被拆分成更小的单元,称为消息块(Chunk)。
(1). linux 环境准备
安装nginx 和 rtmp模块
下载nginx安装包
下载地址:http://nginx.org/download
下载 rtmp模块 到nginx 模块下
wget https://github.com/arut/nginx-rtmp-module/archive/master.zip

(2)编译
到这个目录下
执行命令

./configure --prefix=/usr/local/nginx --with-http_realip_module --with-http_addition_module --with-http_ssl_module --with-http_gzip_static_module --with-http_secure_link_module --with-http_stub_status_module --with-stream --with-pcre=/home/king/share/nginx/pcre-8.41 --with-zlib=/home/king/share/nginx/zlib-1.2.11 --with-openssl=/home/king/share/nginx/openssl-1.1.0g --add-module=/home/king/share/nginx/ngx_http_request_count_module

make && sudo make install

修改 配置文件
在这里插入图片描述

vim /usr/local/nginx/conf/nginx.conf

增加以下 

rtmp {          
        server {            
            listen 1935;         # 端口  
            chunk_size 4000;
            application live { # 请求路径     
                live on;        
            }
        }
}
配置完成启动服务  
sudo ./sbin/nginx -c conf/nginx.conf

netstat -anop |grep 1935

在这里插入图片描述
push.h

#ifndef PUSHSTREAMTHREAD_H
#define PUSHSTREAMTHREAD_H

#include <QObject>
#include <QThread>
#include <QDebug>

extern "C" {
    #include "libavdevice/avdevice.h"    // 调用输入设备需要的头文件
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libavutil/avutil.h"
    #include "libswscale/swscale.h"
    #include "libavutil/imgutils.h"
    #include "libavutil/pixfmt.h"
    #include "libavutil/error.h"
    #include "libswresample/swresample.h"
    #include "libavfilter/avfilter.h"
    #include "libavutil/time.h"
}

class PushStreamThread : public QThread
{
    Q_OBJECT
public:
    PushStreamThread(QThread *parent =nullptr);
    ~PushStreamThread();
    void run() override;
    void set_stop_flag(bool stop);

private:
    bool stop_flag = false;

    const AVOutputFormat *ofmt;
    AVFormatContext *ifmt_ctx = nullptr;  //输入上下文
    AVFormatContext *ofmt_ctx = nullptr;  //输出上下文

    const char *in_filename;
    const char *outUrl;
    int ret;
    uint32_t i = 0;
    int videoIndex = -1;
    int frame_index = 0;
    int64_t start_time = 0;

};

#endif // PUSHSTREAMTHREAD_H

push.cpp

#include "pushstreamthread.h"

PushStreamThread::PushStreamThread(QThread *parent)
    :QThread(parent)
{
    avdevice_register_all();
    avformat_network_init();
}

PushStreamThread::~PushStreamThread()
{
    if(ifmt_ctx){
        avformat_close_input(&ifmt_ctx);
    }
    if (ifmt_ctx && ofmt_ctx->pb && !(ofmt_ctx->flags & AVFMT_NOFILE))
        avio_close(ofmt_ctx->pb);
    if (ifmt_ctx) {
      avformat_free_context(ofmt_ctx);
    }
}


void PushStreamThread::run()
{
    qDebug() << "run:" << QThread::currentThreadId();
    //in_filename  = "cuc_ieschool.mov";
    //in_filename  = "cuc_ieschool.mkv";
    //in_filename  = "cuc_ieschool.ts";
    //in_filename  = "cuc_ieschool.mp4";
    //in_filename  = "cuc_ieschool.h264";
    in_filename  = "hlzmj.mp4";//输入URL(Input file URL)  video=ov9734_azurewave_camera  test.mp4
    //in_filename  = "shanghai03_p.h264";
    outUrl = "rtmp://192.168.222.92:1935/live";//输出 URL(Output URL)[RTMP]  rtmp://localhost/publishlive/livestream
    //out_filename = "rtp://233.233.233.233:6666";//输出 URL(Output URL)[UDP]
    //const AVInputFormat *ifmt = av_find_input_format("dshow");
    //AVDictionary *options = nullptr;

//    av_dict_set(&options, "video_size",  "640*480", 0);
//    av_dict_set(&options, "framerate",  "30", 0);
    //输入(Input)
    ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0);
    if (ret < 0) {
        qDebug() <<  "ifmt_ctx avformat_open_input failed:" << ret;
        return;
    }
    ret = avformat_find_stream_info(ifmt_ctx, 0);
    if (ret < 0) {
        qDebug()<< "ifmt_ctx avformat_find_stream_info failed:"<< ret;
        return;
    }


    ret = avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", outUrl);
    if (ret < 0)
    {
        qDebug() << "ofmt_ctx avformat_alloc_output_context2 failed";
        return;
    }

    ofmt = ofmt_ctx->oformat;


    for (i = 0; i < ifmt_ctx->nb_streams; i++)
   {
       //这里开始要创建一个新的AVStream
       AVStream *stream = ifmt_ctx->streams[i];

       //判断是否是videoIndex。这里先记录下视频流。后面会对这个流进行操作
       if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
       {
           videoIndex = i;
       }

       //创建输出流
       const AVCodec *c = avcodec_find_decoder(stream->codecpar->codec_id);
       AVStream *os = avformat_new_stream(ofmt_ctx, c);

       //应该将编解码器的参数从input中复制过来
       // 这里要注意的是,因为 os->codec这样的取法,已经过时了。所以使用codecpar
       ret = avcodec_parameters_copy(os->codecpar, stream->codecpar);
       if (ret < 0)
       {
           qDebug() << "ofmt_ctx os->codecpar avcodec_parameters_copy failed";
           return;
       }
       qDebug() << "avcodec_parameters_copy success!" ;
       qDebug() << "avcodec_parameters_copy success! in stream codec tag" << stream->codecpar->codec_tag;
       qDebug() << "avcodec_parameters_copy success! out stream  codec tag" << os->codecpar->codec_tag ;

       //复制成功之后。还需要设置 codec_tag(编码器的信息?)
       os->codecpar->codec_tag = 0;
   }

   //检查一遍我们的输出
   av_dump_format(ofmt_ctx, 0, outUrl, 1);

   //开始使用io进行推流
   //通过AVIO_FLAG_WRITE这个标记位,打开输出的AVFormatContext->AVIOContext
   ret = avio_open(&ofmt_ctx->pb, outUrl, AVIO_FLAG_WRITE);
   if (ret < 0)
   {
        qDebug() << "ofmt_ctx->pb avio_open failed" << ret;
        return;
   }
   qDebug() << "avio_open success!";
   //先写头
   ret = avformat_write_header(ofmt_ctx, 0);
   if (ret < 0)
   {
       qDebug() << "ofmt_ctx avformat_write_header failed" << ret;
       return;
   }
   //取得到每一帧的数据,写入
   AVPacket pkt;

   //为了让我们的代码发送流的速度,相当于整个视频播放的数据。需要记录程序开始的时间
   //后面再根据,每一帧的时间。做适当的延迟,防止我们的代码发送的太快了
   long long start_time = av_gettime();
   //记录视频帧的index,用来计算pts
   long long frame_index = 0;

   while (!stop_flag)
   {
       //输入输出视频流
       AVStream *in_stream, *out_stream;

       //从输入流中读取数据 frame到AVPacket当中
       ret = av_read_frame(ifmt_ctx, &pkt);
       if (ret < 0)
       {
           qDebug() << "ifmt_ctx av_read_frame break";
           break;
       }

       //没有显示时间的时候,才会进入计算和校验
       //没有封装格式的裸流(例如H.264裸流)是不包含PTS、DTS这些参数的。在发送这种数据的时候,需要自己计算并写入AVPacket的PTS,DTS,duration等参数。如果没有pts,则进行计算
       if (pkt.pts == AV_NOPTS_VALUE)
       {
           //AVRational time_base:时基。通过该值可以把PTS,DTS转化为真正的时间。
            //先得到流中的time_base
           AVRational time_base = ifmt_ctx->streams[videoIndex]->time_base;
           //开始校对pts和 dts.通过time_base和dts转成真正的时间
           //得到的是每一帧的时间
           /*
           r_frame_rate 基流帧速率 。取得是时间戳内最小的帧的速率 。每一帧的时间就是等于 time_base/r_frame_rate
           av_q2d 转化为double类型
           */
           int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(ifmt_ctx->streams[videoIndex]->r_frame_rate);
           //配置参数  这些时间,都是通过 av_q2d(time_base) * AV_TIME_BASE 来转成实际的参数
           pkt.pts = (double)(frame_index * calc_duration) / (double)av_q2d(time_base) * AV_TIME_BASE;
           //一个GOP中,如果存在B帧的话,只有I帧的dts就不等于pts
           pkt.dts = pkt.pts;
           pkt.duration = (double)calc_duration / (double)av_q2d(time_base) * AV_TIME_BASE;
       }

       //开始处理延迟.只有等于视频的帧,才会处理
       if (pkt.stream_index == videoIndex)
       {
           //需要计算当前处理的时间和开始处理时间之间的间隔??

           //0.先取时间基数
           AVRational time_base = ifmt_ctx->streams[videoIndex]->time_base;

           //AV_TIME_BASE_Q 用小数表示的时间基数。等于时间基数的倒数
           AVRational time_base_r = { 1, AV_TIME_BASE };

           //计算视频播放的时间. 公式等于 pkt.dts * time_base / time_base_r`
           //.其实就是 stream中的time_base和定义的time_base直接的比例
           int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_r);
           //计算实际视频的播放时间。 视频实际播放的时间=代码处理的时间??
           int64_t now_time = av_gettime() - start_time;

           qDebug() << time_base.num << " " << time_base.den << "  " << pkt.dts << "  " << pkt.pts << "   " << pts_time;
           //如果显示的pts time 比当前的时间迟,就需要手动让程序睡一会,再发送出去,保持当前的发送时间和pts相同
           if (pts_time > now_time)
           {
               //睡眠一段时间(目的是让当前视频记录的播放时间与实际时间同步)
               av_usleep((unsigned int)(pts_time - now_time));
           }
       }

               //重新计算一次pts和dts.主要是通过 in_s的time_base 和 out_s的time_base进行计算和校对
       //先取得stream
       in_stream = ifmt_ctx->streams[pkt.stream_index];
       out_stream = ofmt_ctx->streams[pkt.stream_index];

       //重新开始指定时间戳
       //计算延时后,重新指定时间戳。 这次是根据 in_stream 和 output_stream之间的比例
       //计算dts时,不再直接用pts,因为如有有B帧,就会不同
       //pts,dts,duration都也相同
       pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
       pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
       pkt.duration = (int)av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
       //再次标记字节流的位置,-1表示不知道字节流的位置
       pkt.pos = -1;

       //如果当前的帧是视频帧,则将我们定义的frame_index往后推
       if (pkt.stream_index == videoIndex)
       {
           qDebug() << "Send" << frame_index << "video frames to output URL" ;
           frame_index++;
       }

       //发送!!!
       ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
       if (ret < 0)
       {
           qDebug() << "发送数据包出错";
           break;
       }

       //使用完了,记得释放
       av_packet_unref(&pkt);
   }
   //写文件尾(Write file trailer)
   av_write_trailer(ofmt_ctx);
   if(ifmt_ctx){
       avformat_close_input(&ifmt_ctx);
   }
   if (ofmt_ctx && ofmt_ctx->pb && !(ofmt_ctx->flags & AVFMT_NOFILE))
       avio_close(ofmt_ctx->pb);
   if (ifmt_ctx) {
     avformat_free_context(ofmt_ctx);
   }
}

void PushStreamThread::set_stop_flag(bool stop)
{
    stop_flag = stop;
}

使用 vcl 播放流
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/278520.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Freeswitch实现的Volte网视频通知应用

现在运营商的Volte网络已经很好的支持视频通话了&#xff0c;因此在原来的电话语音通知的基础上&#xff0c;可以更进一步实现视频的通知&#xff0c;让用户有更好的体验&#xff0c;本文就从技术角度&#xff0c;基于Freeswitch来实现此类应用&#xff08;本文假设读者已对Fre…

C语言编写Windows程序:组合启用/禁用Telnet客户端,并Telnet指定ip和端口

本文程序是将启用/禁用Telnet客户端的命令进行组合&#xff0c;单个命令的解析可参考文章&#xff1a; 启用/禁用Windows功能中的Telnet客户端的命令_()命令将阻止使用telnintel-CSDN博客 源代码如下&#xff1a; #include <stdio.h> #include <stdlib.h> #include…

【Java EE初阶五】wait及notify关键字

1. wait和notify的概念 所谓的wait和notify其实就是等待、通知机制&#xff1b;该机制的作用域join类似&#xff1b;由于多个线程之间是随机调度的&#xff0c;引入wait和notify就是为了能够从应用层面上&#xff0c;干预到多个不同线程代码的执行顺序&#xff0c;此处的干预&a…

小米电脑管家 - 手机平板电脑家居互联

系列文章目录 前言 联想电脑安装小米电脑管家实现设备互联 如图&#xff0c;将 小米平板 5 Pro 作为联想笔记本 GeekPro 5000 &#xff08;这垃圾电脑&#xff09;的副屏。 可以在小米平板控制笔记本&#xff0c;如图所示 一、官方使用手册 参考&#xff1a;小米电脑管家帮助 …

大语言模型发展史

前言 2023年可谓是生成式AI元年&#xff0c;大语言模型从崭露头角到锋芒毕露&#xff0c;已然成为人工智能领域的关键推动力。这一创新性的技术不仅在自然语言处理领域崭露头角&#xff0c;更深刻地改变了我们对人机交互、智能助手和信息处理的认知。那么大语言模型的发展历程…

thinkphp+vue+mysql企业车辆管理系统m117l

“企业车辆管理系统”是运用php语言和vue框架&#xff0c;以Mysql数据库为基础而发出来的。为保证我国经济的持续性发展&#xff0c;必须要让互联网信息时代在我国日益壮大&#xff0c;蓬勃发展。伴随着信息社会的飞速发展&#xff0c;企业车辆管理系统所面临的问题也一个接一个…

基于策略模式和简单工厂模式实现zip、tar、rar、7z四种压缩文件格式的解压

推荐语 这篇技术文章深入探讨了基于策略模式和简单工厂模式实现四种常见压缩文件格式的解压方法。通过阅读该文章&#xff0c;你将了解到如何利用这两种设计模式来实现灵活、可扩展的解压功能&#xff0c;同时适应不同的压缩文件格式。如果你对设计模式和文件处理感兴趣或刚好…

github鉴权失败

问题&#xff1a; 如上图所示 git push 时发生了报错&#xff0c;鉴权失败&#xff1b; 解决方案 Settings->Developer settings->Personal access tokens->Generate new token。创建新的访问密钥&#xff0c;勾选repo栏&#xff0c;选择有效期&#xff0c;为密钥命…

Android MVVM 写法

前言 Model&#xff1a;负责数据逻辑 View&#xff1a;负责视图逻辑 ViewModel&#xff1a;负责业务逻辑 持有关系&#xff1a; 1、ViewModel 持有 View 2、ViewModel 持有 Model 3、Model 持有 ViewModel 辅助工具&#xff1a;DataBinding 执行流程&#xff1a;View &g…

OpenStack云计算(-) 简介与部署Keystone

一.OpenStack简介 什么是云计算:云计算是一种按使用量付费的模式,这种模式提供可用的、便捷的、按需的网络访问,进入可配置的计算资源共享池(资源包括网络,服务器,存储,应用软件,服务) 云计算所包含的几个层次服务&#xff1a; SaaS ( Software as a Service ) :把在线软件作…

【开源】基于Vue+SpringBoot的公司货物订单管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 客户管理模块2.2 商品维护模块2.3 供应商管理模块2.4 订单管理模块 三、系统展示四、核心代码4.1 查询供应商信息4.2 新增商品信息4.3 查询客户信息4.4 新增订单信息4.5 添加跟进子订单 五、免责说明 一、摘要 1.1 项目…

Prometheus通过consul实现自动服务发现

环境,软件准备 本次演示环境&#xff0c;我是在虚拟机上安装 Linux 系统来执行操作&#xff0c;以下是安装的软件及版本&#xff1a; System: CentOS Linux release 7.6Docker: 24.0.5Prometheus: v2.37.6Consul: 1.6.1 注意&#xff1a;这里为了方便启动 Prometheus、Consul服…

Elasticsearch:使用 ELSER v2 文本扩展进行语义搜索

Elastic 提供了一个强大的 ELSER 供我们进行语义搜索。ELSER 是一种稀疏向量的搜索方法。我们无需对它做任何的微调及训练。它是一种 out-of-domain 的模型。目前它仅对英文进行支持。希望将来它能对其它的语言支持的更好。更多关于 ELSER 的知识&#xff0c;请参阅文章 “Elas…

YOLOv8改进 | 细节创新篇 | iAFF迭代注意力特征融合助力多目标细节涨点

一、本文介绍 本文给大家带来的改进机制是iAFF&#xff08;迭代注意力特征融合&#xff09;&#xff0c;其主要思想是通过改善特征融合过程来提高检测精度。传统的特征融合方法如加法或串联简单&#xff0c;未考虑到特定对象的融合适用性。iAFF通过引入多尺度通道注意力模块(我…

winserver2008 r2服务器iis配置支持flv,f4v,mp4格式视频

很多政府单位网站一直在使用WIN服务器&#xff0c;大部分网站都使用多年基本使用.NET或者CMS系统建站&#xff0c;系统环境也一直是老版本&#xff0c;今天在维护过程中又出现了新问题&#xff0c;上传的MP4文件不支持网站上播放&#xff0c;顺便也分享下解决过程。当我们架设的…

数字身份验证:跨境电商如何应对账户安全挑战?

在数字化时代&#xff0c;随着跨境电商的蓬勃发展&#xff0c;账户安全问题逐渐成为行业和消费者关注的焦点。随着网络犯罪日益猖獗&#xff0c;用户的数字身份安全面临着更加复杂的威胁。本文将深入探讨数字身份验证在跨境电商中的重要性&#xff0c;并探讨各种创新技术和策略…

回归预测 | MATLAB实ZOA-LSTM基于斑马优化算法优化长短期记忆神经网络的多输入单输出数据回归预测模型 (多指标,多图)

回归预测 | MATLAB实ZOA-LSTM基于斑马优化算法优化长短期记忆神经网络的多输入单输出数据回归预测模型 &#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实ZOA-LSTM基于斑马优化算法优化长短期记忆神经网络的多输入单输出数据回归预测模型 &#xff08;…

C++系列-第1章顺序结构-4-整型int

C系列-第1章顺序结构-4-整型int 在线练习&#xff1a; http://noi.openjudge.cn/ https://www.luogu.com.cn/ 总结 本文是C系列博客&#xff0c;主要讲述整型int的用法 整型int 在C中&#xff0c;int 是一个关键字&#xff0c;用于声明整型变量。int 类型用于存储整数&…

PHP序列化总结2--常见的魔术方法

魔术方法的概念 PHP的魔术方法是一种特殊的方法&#xff0c;用于覆盖PHP的默认操作。它们以双下划线&#xff08;__&#xff09;开头&#xff0c;后面跟着一些特定的字符串&#xff0c;如__construct()、__destruct()、__get()等。这些魔术方法在对象执行特定操作时被自动调用…

三台CentOS7.6虚拟机搭建Hadoop完全分布式集群(三)

这个是笔者大学时期的大数据课程使用三台CentOS7.6虚拟机搭建完全分布式集群的案例&#xff0c;已成功搭建完全分布式集群&#xff0c;并测试跑实例。 9 安装hbase 温馨提示&#xff1a;安装hbase先在master主节点上配置&#xff0c;然后远程复制到slave01或slave02 &#xf…