【音视频 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_modulemake && 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.confnetstat -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++){//这里开始要创建一个新的AVStreamAVStream *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这样的取法,已经过时了。所以使用codecparret = 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->AVIOContextret = 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,用来计算ptslong 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_baseAVRational time_base = ifmt_ctx->streams[videoIndex]->time_base;//开始校对pts和 dts.通过time_base和dts转成真正的时间//得到的是每一帧的时间/*r_frame_rate 基流帧速率 。取得是时间戳内最小的帧的速率 。每一帧的时间就是等于 time_base/r_frame_rateav_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就不等于ptspkt.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进行计算和校对//先取得streamin_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 播放流
相关文章:

【音视频 ffmpeg 学习】 RTMP推流 mp4文件
1.RTMP(实时消息传输协议)是Adobe 公司开发的一个基于TCP的应用层协议。 2.RTMP协议中基本的数据单元称为消息(Message)。 3.当RTMP协议在互联网中传输数据的时候,消息会被拆分成更小的单元,称为消息块(Chunkÿ…...

跨进程通信 macOS XPC 创建实例
一:简介 XPC 是 macOS 里苹果官方比较推荐和安全的的进程间通信机制。 集成流程简单,但是比较绕。 主要需要集成 XPC Server 这个模块,这个模块最终会被 apple 的根进程 launchd 管理和以独立进程的方法唤起和关闭, 我们主app 进…...

Python圣诞树代码
Python圣诞树代码 # 小黄 2023/12/25import turtle as t # as就是取个别名,后续调用的t都是turtle from turtle import * import random as rn 100.0speed(20) # 定义速度 pensize(5) # 画笔宽度 screensize(800, 800, bgblack) # 定义背景颜色,可…...

flask之文件管理系统-项目 JRP上线啦!!! ---修订版,兼容Windows和Linux系统
上一章的版本https://blog.csdn.net/weixin_44517278/article/details/135275066,在Windows下debug完成无异常后,上传到我的树莓下开始正式服役 由于开发环境是Windows,使用环境是Linux,导致最后没能成功运行起来 这个版本是今天去…...
希尔排序:排序算法中的调优大师
希尔排序:排序算法中的调优大师 大家好,我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天,让我们一同探讨一个经典而高效的排序算法——希尔排序。…...
LeetCode 1185. 一周中的第几天
一、题目 1、题目描述 给你一个日期,请你设计一个算法来判断它是对应一周中的哪一天。 输入为三个整数:day、month 和 year,分别表示日、月、年。 您返回的结果必须是这几个值中的一个 {"Sunday", "Monday", "Tues…...

大数据学习(30)-Spark Shuffle
&&大数据学习&& 🔥系列专栏: 👑哲学语录: 承认自己的无知,乃是开启智慧的大门 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一下博主哦ᾑ…...
Linux部署ELK
大家好,我是升仔 引言 在复杂的系统架构中,日志管理是一个关键的环节。ELK栈提供了一个高效的解决方案,能够帮助我们快速定位问题、分析数据,并实现实时监控。部署ELK栈是一项挑战,但收益巨大。 基础安装和配置 环境准…...

Python 实现 PDF 到 Word 文档的高效转换(DOC、DOCX)
PDF(Portable Document Format)已成为一种广泛使用的电子文档格式。PDF的主要优势是跨平台,可以在不同设备上呈现一致的外观。然而,当我们需要对文件内容进行编辑或修改,直接编辑PDF文件会非常困难,而且效果…...

【MYSQL】MYSQL 的学习教程(七)之 慢 SQL 优化思路
1. 慢 SQL 优化思路 慢查询日志记录慢 SQLexplain 分析 SQL 的执行计划profile 分析执行耗时Optimizer Trace 分析详情确定问题并采用相应的措施 1. 慢查询日志记录慢 SQL 如何定位慢SQL呢? 我们可以通过 慢查询日志 来查看慢 SQL。 ①:开启慢查询日志…...

unity学习笔记----游戏练习0
一、修复植物种植的问题 1.当手上存在植物时,再次点击卡片上的植物就会在手上添加新的植物,需要修改成只有手上没有植物时才能再次获取到植物。需要修改AddPlant方法。 public bool AddPlant(PlantType plantType) { //防止手上出现多个植…...
ai概念:强人工智能介绍、迁移学习
强人工智能(Strong Artificial Intelligence,SAI)是指一种具有与人类智能相媲美或超越人类智能水平的人工智能系统。与弱人工智能(Weak Artificial Intelligence,WAI)不同,强人工智能具有更高级…...
go语言设计模式-单例模式
建造型设计模式-单例模式 是用来控制类型实例的数量的,当需要确保一个类型只有一个实例时,就需要使用单例模式。 即把实例的访问进行收口,不能谁都能 new 类,所以单例模式还会提供一个2访问该实例的全局端口,一般都会…...

超维空间S2无人机使用说明书——51、基础版——使用yolov8进行目标跟踪
引言:为了提高yolo识别的质量,提高了yolo的版本,改用yolov8进行物体识别,同时系统兼容了低版本的yolo,包括基于C的yolov3和yolov4,以及yolov7。 简介,为了提高识别速度,系统采用了G…...

Transformer(seq2seq、self-attention)学习笔记
在self-attention 基础上记录一篇Transformer学习笔记 Transformer的网络结构EncoderDecoder 模型训练与评估 Transformer的网络结构 Transformer是一种seq2seq 模型。输入一个序列,经过encoder、decoder输出结果也是一个序列,输出序列的长度由模型决定…...

2023-12-29 服务器开发-centos部署ftp
摘要: 2023-12-29 服务器开发-centos-部署ftp 部署ftp vsftpd(very secure FTP daemon)是Linux下的一款小巧轻快、安全易用的FTP服务器软件。本教程介绍如何在Linux实例上安装并配置vsftpd。 前提条件 已创建ECS实例并为实例分配了公网IP地址。 背景…...
螺旋数字阵(100%用例)C卷 (JavaPythonNode.jsC语言C++)
疫情期间,小明隔离在家,百无聊赖,在纸上写数字玩。他发明了一种写法: 给出数字个数n和行数m (0 < n <= 999,0 < m <= 999) ,从左上角的1开始,按照顺时针螺旋向内写方式,依次写出2,3...n,最终形成一个m行矩阵 小明对这个矩阵有些要求 1.每行数字的个数一样多…...
AUTOSAR从入门到精通-网络通信(UDPNm)(二)
目录 前言 原理 UdpNm工作原理 UdpNm与CanNM的区别联系 网络管理算法...

显示器与按键(LCD 1602 + button)
一、实验目的: (1)学习lcd 1602的编程与使用、 (2)机械式复位开关button软件消抖的方法。 二、实验内容: 1、必做:先显示开机画面,:在1602显示器上,分两行…...

2020年认证杯SPSSPRO杯数学建模B题(第一阶段)分布式无线广播全过程文档及程序
2020年认证杯SPSSPRO杯数学建模 B题 分布式无线广播 原题再现: 以广播的方式来进行无线网通信,必须解决发送互相冲突的问题。无线网的许多基础通信协议都使用了令牌的方法来解决这个问题,在同一个时间段内,只有唯一一个拿到令牌…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...

day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...