【音视频 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 播放流
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二叉树的中序遍历,力扣

目录 题目地址&#xff1a; 题目&#xff1a; 解题方法&#xff1a; 解题分析&#xff1a; 解题思路&#xff1a; 代码实现&#xff1a; 注&#xff1a; 代码实现&#xff08;递归&#xff09;&#xff1a; 代码实现&#xff08;迭代&#xff09;&#xff1a; 题目地址&#xf…

【目标检测】yolov8结构及代码分析

yolov8代码:https://github.com/ultralytics/ultralytics yolov8的整体结构如下图&#xff08;来自mmyolo&#xff09;&#xff1a; yolov8的配置文件&#xff1a; # Ultralytics YOLO &#x1f680;, AGPL-3.0 license # YOLOv8 object detection model with P3-P5 outputs.…

跨境电商迎来综合竞争力比拼时代 五大趋势解读跨境2024

过去几年&#xff0c;跨境电商成为外贸出口增长的一大亮点&#xff0c;随着年底国务院办公厅《关于加快内外贸一体化发展的若干措施》的发布&#xff0c;跨境电商在促进经济发展、助力内外贸一体化发展方面的价值更加凸显。 这是跨境电商变化最快的时代&#xff0c;也是跨境电…

GrayLog日志平台的基本使用-ssh之Email报警

1、首先编辑并添加邮件配置到server.conf&#xff08;注意&#xff1a;是添加&#xff09; vim /etc/graylog/server/server.conf # Email transport transport_email_enabled true transport_email_hostname smtp.qq.com transport_email_port 465 transport_email_use_a…

Net6 Core webApi发布到IIS

Net6 Core Api发布到IIS不同于webapi&#xff0c;依赖框架不同&#xff0c;配置也移至项目内Program.cs 一、发布到指定文件夹和IIS&#xff0c;不过注意IIS应用程序池选择的是 “无托管代码“ 在IIS管理器中点击浏览&#xff0c;访问接口路径报500.19&#xff0c;原因是所依赖…

杰发科技AC7840——EEPROM初探

0.序 7840和7801的模拟EEPROM使用不太一样 1.现象 按照官方Demo&#xff0c;在这样的配置下&#xff0c;我们看到存储是这样的&#xff08;连续三个数字1 2 3&#xff09;。 使用串口工具的多帧发送功能 看不出多少规律 修改代码后 发现如下规律&#xff1a; 前四个字节是…

GBASE南大通用携手宇信科技打造“一表通”全链路解决方案

什么是“一表通”&#xff1f; “一表通”是国家金融监督管理总局为发挥统计监督效能、完善银行保险监管统计制度、推进监管数据标准化建设、打破数据壁垒&#xff0c;而制定的新型监管数据统计规范。相较于以往的报送接口&#xff0c;“一表通”提高了对报送时效性、校验准确…

stm32学习笔记:TIM-定时中断和外部时钟

定时器四部分讲解内容&#xff0c;本文是第一部分 ​​​​​TIM简介 基本定时器 时基单元&#xff1a;预分频器、计数器、自动重装载寄存器 预分频器之前&#xff0c;连接的就是基准计数时钟的输入&#xff0c;由于基本定时器只能选择内部时钟&#xff0c;所以可以认为这根…

【MySQL】数据库之存储过程(“SQL语句的脚本“)

目录 一、什么是存储过程&#xff1f; 二、存储过程的作用 三、如何创建、调用、查看、删除、修改存储过程 四、存储过程的参数&#xff08;输入参数&#xff0c;输出参数&#xff0c;输入输出参数&#xff09; 第一种&#xff1a;输入参数 第二种&#xff1a;输出参数 …

【数据结构】什么是二叉树?

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 &#x1f4cc;二叉树的定义 &#x1f4cc;二叉树的特点 &#x1f4cc;特殊二叉树 &#x1f4cc;二叉树的性质 &#x1f4cc;二叉树的存储结构 &#x1f4cc;二叉树…

链表精选题集

目录 1 链表翻转 题目链接&#xff1a; 解题&#xff1a; 试错版&#xff1a; 2 找中间节点 题目链接: 题解&#xff1a; 3 找倒数第k个节点 题目链接&#xff1a; 题解&#xff1a; 4 将两个升序链表合并为一个升序链表 题目链接&#xff1a; 题解&#xff1a; …

Spark SQL

目录 一、Spark SQL简介 &#xff08;一&#xff09;从Shark说起 &#xff08;二&#xff09;Spark SQL架构 &#xff08;三&#xff09;为什么推出Spark SQL 二、DataFrame概述 三、DataFrame的创建 四、DataFrame的保存 五、DataFrame的常用操作 六、从RDD转换得到D…

nodejs+vue+ElementUi农产品团购销售系统zto2c

目标是为了完成小区团购平台的设计和实现&#xff0c;在疫情当下的环境&#xff0c;方便小区业主购入生活所需&#xff0c;减小居民的生活压力 采用B/S模式架构系统&#xff0c;开发简单&#xff0c;只需要连接网络即可登录本系统&#xff0c;不需要安装任何客户端。开发工具采…

mxxWechatBot微信机器人V2版本文档说明

大家伙&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 先看这里 一、前言二、mxxWechatBot流程图三、怎么使用&#xff1f; 一、前言 经过不断地探索与研究&#xff0c;mxxWechatBot正式上线&#xff0c;届时全面开放使用。 mxxWechatBot&am…

LLM之RAG实战(十一)| 使用Mistral-7B和Langchain搭建基于PDF文件的聊天机器人

在本文中&#xff0c;使用LangChain、HuggingFaceEmbeddings和HuggingFace的Mistral-7B LLM创建一个简单的Python程序&#xff0c;可以从任何pdf文件中回答问题。 一、LangChain简介 LangChain是一个在语言模型之上开发上下文感知应用程序的框架。LangChain使用带prompt和few-…

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

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

听GPT 讲Rust源代码--src/tools(34)

File: rust/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs 文件"collection_is_never_read.rs"位于Rust源代码中的clippy_lints工具中&#xff0c;其作用是检查在集合类型&#xff08;如Vec、HashMap等&#xff09;的实例上执行的操作是否被忽略了…

学习笔记13——Spring整合Mybatis、junit、AOP、事务

学习笔记系列开头惯例发布一些寻亲消息 链接&#xff1a;https://baobeihuijia.com/bbhj/ Mybatis - Spring&#xff08;使用第三方包new一个对象bean&#xff09; 原始的Mybatis与数据库交互【通过sqlmapconfig来配置和连接】 初始化SqlSessionFactory获得连接获取数据层接口…

Jmeter吞吐量控制器总结

吞吐量控制器(Throughput Controller) 场景&#xff1a; 在同一个线程组里, 有10个并发, 7个做A业务, 3个做B业务,要模拟这种场景,可以通过吞吐量模拟器来实现。 添加吞吐量控制器 用法1: Percent Executions 在一个线程组内分别建立两个吞吐量控制器, 分别放业务A和业务B …

【三维目标检测/自动驾驶】IA-BEV:基于结构先验和自增强学习的实例感知三维目标检测(AAAI 2024)

系列文章目录 论文&#xff1a;Instance-aware Multi-Camera 3D Object Detection with Structural Priors Mining and Self-Boosting Learning 地址&#xff1a;https://arxiv.org/pdf/2312.08004.pdf 来源&#xff1a;复旦大学 英特尔Shanghai Key Lab /美团 文章目录 系列文…