【音视频 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.mzph.cn/news/584849.shtml

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

相关文章

Linux系统下隧道代理HTTP

在Linux系统下配置隧道代理HTTP是一个涉及网络技术的话题&#xff0c;主要目的是在客户端和服务器之间建立一个安全的通信通道。下面将详细解释如何进行配置。 一、了解基本概念 在开始之前&#xff0c;需要了解几个关键概念&#xff1a;代理服务器、隧道代理和HTTP协议。代理…

VsCode的介绍和入门详细讲解

VS Code&#xff08;Visual Studio Code&#xff09;是由 Microsoft 开发的一款轻量级开源编辑器&#xff0c;支持多种语言和框架的编写、调试和测试。它拥有丰富的扩展生态系统&#xff0c;可以满足不同开发者的需求。 下面是 VS Code 的入门详细讲解&#xff1a; 下载和安装…

使用Python绘制各种图表

1、折线图&#xff08;Line Chart&#xff09; import matplotlib.pyplot as plt # 数据 x [1, 2, 3, 4, 5] y [2, 4, 1, 3, 7] # 绘制折线图 plt.plot(x, y) plt.title(折线图示例) plt.xlabel(X轴) plt.ylabel(Y轴) plt.show() 2、柱状图&#xff08;Bar…

算法专题四:前缀和

前缀和 一.一维前缀和(模板)&#xff1a;1.思路一&#xff1a;暴力解法2.思路二&#xff1a;前缀和思路 二. 二维前缀和(模板)&#xff1a;1.思路一&#xff1a;构造前缀和数组 三.寻找数组的中心下标&#xff1a;1.思路一&#xff1a;前缀和 四.除自身以外数组的乘积&#xff…

3、Git分支操作与团队协作

Git分支操作 1.什么是分支2. 分支的好处3. 分支的操作3.1 查看分支3.2 创建分支3.3 切换分支3.4 修改分支3.5 合并分支3.6 产生和解决冲突 4. 创建分支和切换分支图解5. Git团队协作机制团队内协作跨团队协作 均在git bash中进行操作。事先建好本地工作库 1.什么是分支 在版本…

GLTF 编辑器实现逼真3D动物毛发效果

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 要实现逼真的3D动物毛发效果&#xff0c;可以采用以下技术和方法&…

接口和抽象类

在Java编程语言中&#xff0c;抽象类和接口都是用于定义抽象概念的重要工具。它们都提供了一种方式来创建可重用的代码&#xff0c;并且都可以被其他类继承或实现。然而&#xff0c;尽管它们有一些相似之处&#xff0c;但也存在一些显著的区别。本文将探讨抽象类和接口的相同点…

Vue学习day_03

普通组件的注册 局部注册: 创建一个components的文件夹 在里面写上对应的.vue文件 在对应的vue里面写上对应的3部分 template写上对应的核心代码 盒子等 style 写上对应的css修饰 在App.vue里面进行引用 import 导包 格式是 import 起个名字 from 位置 在写一个component…

AutoSAR(基础入门篇)3.3-Autosar中RTE的数据一致性与Interface接口

目录 一、RTE的数据一致性 1、什么是数据一致性 2、数据一致性的实现机制 2.1、利用RTE管理<

3d导入模型怎样显示原本材质---模大狮模型网

要在导入3D模型时保留原本的材质&#xff0c;您可以尝试以下方法&#xff1a; 导入前检查文件格式&#xff1a;确保您所使用的3D软件支持导入模型的文件格式。不同的软件对文件格式的支持有所差异&#xff0c;选择正确的文件格式可以更好地保留原始材质。 使用正确的材质库&am…

【Python篇】python库讲解(wordcloud | jieba)

文章目录 &#x1f354;jieba库&#x1f354;wordcloud库&#x1f339;解释 &#x1f354;jieba库 jieba库是一个流行的中文分词工具&#xff0c;它基于统计算法和词频字典&#xff0c;能够将连续的汉字序列切割成有意义的词语。下面是对jieba库的简要理论说明&#xff1a; 分…

云计算基础、Issa、Pssa、Saas区别

一、了解云计算 云计算&#xff08;cloud computing&#xff09;是分布式计算的一种&#xff0c;指的是通过网络“云”将巨大的数据计算处理程序分解成无数个小程序&#xff0c;然后&#xff0c;通过多部服务器组成的系统进行处理和分析这些小程序得到结果并返回给用户。云计算…

复数值神经网络可能是深度学习的未来

一、说明 复数这种东西,在人的头脑中似乎抽象、似乎复杂,然而,对于计算机来说,一点也不抽象,不复杂,那么,将复数概念推广到神经网络会是什么结果呢?本篇介绍国外的一些同行的尝试实践,请我们注意观察他们的进展。

【机组期末速成】指令系统|机器指令概述|操作数类型与操作类型|寻址方式|指令格式

&#x1f3a5; 个人主页&#xff1a;深鱼~&#x1f525;收录专栏&#xff1a;计算机组成原理&#x1f304;欢迎 &#x1f44d;点赞✍评论⭐收藏 目录 前言&#xff1a; 一、本章考点总览 二、考点分析 1、以下有关指令系统的说法中错误的是&#xff08; &#xff09;。 2…

python将Excel的每一个sheet页生成PDF文件

这里我们使用Excel应用导出PDF 使用win32com.client.Dispatch("Excel.Application")启动Excel使用excel.Workbooks.Open(excel文件的绝对路径)打开文件循环每一个sheet&#xff0c;使用.ExportAsFixedFormat(0, os.path.join(parent, f"pdf_data/{_sheet.Name}…

idea远程开发环境搭建

idea远程开发环境搭建 一、安装包下载二、环境准备2.1服务器端jdk、maven安装&#xff0c;代码下载略2.2JetBrainsClients下载配置 三、远程环境配置3.1 创建项目3.2 填写服务器连接信息![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/4aa09073af5e4a66a5e83e7c5d1…

Unity 通过鼠标框选绘制矩形区域

鼠标拖动的同时绘制一块同等大小的区域&#xff1a;如下 using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// 通过鼠标框选绘制矩形区域 /// </summary> /// public enum MouseType {left 0,right 1,middle 2 …

MySQL线上慢SQL问题分析处理小记

相同数据量表结构&#xff0c;线上执行12s 本地执行0.1s过程分析 1. 慢SQL信息 SELECT t1.id,t2.idFROM t_platform_target_standard_target_index t1LEFT JOIN t_platform_target_standard t2 ON t1.target_number t2.target_numberWHERE t1.delete_flag 0 AND t2.user_num …

基于STM32的光电传感器应用开发实例

基于STM32的光电传感器应用开发是一种常见的嵌入式系统应用&#xff0c;光电传感器可以用于检测物体的有无、位置、颜色、亮度等信息&#xff0c;被广泛应用于工业自动化、机器人技术、智能家居等领域。本文将介绍如何在STM32上进行光电传感器应用开发&#xff0c;并提供相应的…

Word 将页面方向更改为横向或纵向

文章目录 更改整个文档的方向更改部分页面的方向方法1&#xff1a;方法2&#xff1a; 参考链接 更改整个文档的方向 选择“布局”>“方向”&#xff0c;选择“纵向”或“横向”。 更改部分页面的方向 需要达到下图结果&#xff1a; 方法1&#xff1a; 选:中你要在横向页面…