既能够用ffmpeg命令做RTSP流转RTMP流,又可以像调用avcodec/avfilter库一样逻辑编程

又有一个需求:我们现在想做一款多路RTSP拉流转RTMP推流到CDN进行直播的功能,注意啊,是多路,原来我们有两种方式,一种是用ffmpeg.exe进行:

ffmpeg -i “rtsp://192.168.0.99:8554/1” -c:v libx264 -c:a aac -f flv “rtmp://127.0.0.1:1935/live/test”

同样,这种方式会有一个问题,那就是断线重连的问题,我们不能很好地知道拉流是不是断线了,或者当前的推流状态是什么样子的,推流中还是重连中,同时,我们也只能默认重编码成H.264和AAC,这种机器性能消耗会很高,我们完全可以在源流是H.264或者AAC编码的时候,直接-c:v copy或者-c:a copy,但是我们不好判断。

每一路流我们先ffprobe一下,看看视频编码格式是H.264还是H.265,再重新启动ffmpeg.exe拉流转推流?

很显然,上面的方法搞个几路是没问题的,搞多了就不靠谱了,维护一堆ffmpeg既不稳定又没成长!~


另一种方式,就是用ffmpeg的SDK进行开发,avfilter、avcodec一整套,获取avformat,再把avpacket导给RTMP推流,整套下来,开发难度较高,要工程化的比较稳定,还需要老师傅!看看大模型咋写的?

#include <libavformat/avformat.h>
#include <libavutil/time.h>int main(int argc, char **argv) {AVFormatContext *pFormatCtx = NULL;AVOutputFormat *oFormat = NULL;AVStream *in_stream = NULL;AVStream *out_stream = NULL;AVCodecContext *pCodecCtxIn = NULL;AVCodecContext *pCodecCtxOut = NULL;AVPacket pkt;int ret, i;if (argc < 3) {printf("Usage: %s <input RTSP URL> <output RTMP URL>
", argv[0]);return -1;}// Register all formats and codecsav_register_all();// Open input RTSP URLif ((ret = avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)) != 0) {printf("Cannot open input stream: %s
", av_err2str(ret));return -1;}// Retrieve stream informationif ((ret = avformat_find_stream_info(pFormatCtx, NULL)) < 0) {printf("Cannot find stream information: %s
", av_err2str(ret));return -1;}// Find the first audio streamfor (i = 0; i < pFormatCtx->nb_streams; i++) {if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {in_stream = pFormatCtx->streams[i];break;}}// If no audio stream is found, exitif (!in_stream) {printf("Didn't find a audio stream!
");return -1;}// Get a pointer to the codec context for the audio streampCodecCtxIn = in_stream->codec;// Find the first video streamfor (i = 0; i < pFormatCtx->nb_streams; i++) {if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {out_stream = pFormatCtx->streams[i];break;}}// If no video stream is found, exitif (!out_stream) {printf("Didn't find a video stream!
");return -1;}// Get a pointer to the codec context for the video streampCodecCtxOut = out_stream->codec;// Open output RTMP URLif ((ret = avformat_alloc_output_context2(&pFormatCtx, NULL, "flv", argv[2])) < 0) {printf("Cannot create output context: %s
", av_err2str(ret));return -1;}// Add stream to the output contextif (avformat_new_stream(pFormatCtx, out_stream->codec->codec) < 0) {printf("Error adding new stream to output context
");return -1;}// Write headerif (avformat_write_header(pFormatCtx, NULL) < 0) {printf("Error occurred when opening output file.
");return -1;}// Process each packet from input streamwhile (av_read_frame(pFormatCtx, &pkt) >= 0) {// Is this a packet from the audio stream?if (pkt.stream_index == in_stream->index) {// Decode audio frameAVCodecContext *pCodecOut = pCodecCtxOut;ret = avcodec_send_packet(pCodecOut, &pkt);if (ret < 0) {printf("Error sending a packet for decoding
");return -1;}while (ret >= 0) {ret = avcodec_receive_frame(pCodecOut, NULL);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;} else if (ret < 0) {printf("Error during decoding
");return -1;}// Write the decoded frame to the output fileav_interleaved_write_frame(pFormatCtx, &pkt);}} else if (pkt.stream_index == out_stream->index) {// Is this a packet for the video stream?if (pkt.pts == in_stream->start_time) {// Write video frameret = av_interleaved_write_frame(pFormatCtx, &pkt);if (ret < 0) {printf("Error during writing
");return -1;}}}}// Close the output contextavformat_free_context(pFormatCtx);return 0;
}

注意啊,以上代码没有经过验证,太麻烦了,罗里吧嗦一大堆;

那么,有没有一种办法,能简化ffmpeg的SDK调用过程呢,既能解决重连的问题,又能解决SDK纷繁复杂的调用过程问题,而且还有整个过程中对音视频编码判断和重编码跳过的流程?

有,看看EasyAVFilter怎么完成这项工作:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "EasyAVFilterAPI.h"#ifdef _WIN32
#pragma comment(lib,"EasyAVFilter.lib")
#endif
int Easy_APICALL __AVFilterCallBack(void* userPtr, EASY_AV_FILTER_STATE_T status, int progress, int errCode, const char *errMsg)
{return 0;
}int main(int argc, char** argv)
{//创建ffmpeg实例Easy_Handle avFilterHandle = NULL;EasyAVFilter_Create(&avFilterHandle);//设置回调函数,获取回调信息EasyAVFilter_SetCallback(avFilterHandle,__AVFilterCallBack,0);//将RTSP流转成RTMP流EasyAVFilter_AddInput(avFilterHandle, "rtsp://admin:admin@112.112.112.212:554/ch1/main/av_stream", 1);EasyAVFilter_AddFilter(avFilterHandle, "-vcodec copy -acodec aac -ac 2 -strict -2");EasyAVFilter_AddFilter(avFilterHandle, "-f flv");EasyAVFilter_SetOutput(avFilterHandle, "rtmp://172.81.216.155:13519/live/IbMkUXeVR?sign=SxMk8X6VRz", 0);//验证参数设置是否正确char filterCommand[256] = { 0 };EasyAVFilter_GetFilters(avFilterHandle, filterCommand);printf("command: %s\n", filterCommand);//开始RTSP转RTMP工作EasyAVFilter_Start(avFilterHandle, 0, 8, 10);//注意,文件转码不需要循环读取,第二个参数从1改成0getchar();EasyAVFilter_Stop(avFilterHandle);EasyAVFilter_Release(&avFilterHandle);return 0;
}

就上面八九个方法,还包括了创建实例和停止/销毁实例,核心方法就五六个,就搞定了全部ffmpeg.exe所有的功能,还能支持重连!!!

方法名称说明
EasyAVFilter_Create创建句柄,相当于创建了一个ffmpeg.exe
EasyAVFilter_Release释放句柄
EasyAVFilter_SetCallback设置回调函数和自定义指针,回调过程中的各种媒体信息/连接信息/转码进度
EasyAVFilter_AddInput添加输入参数(源地址)
EasyAVFilter_AddFilter添加中间参数,如:转码,兼容ffmpeg命令所有参数(例如-vcodec copy -acodec aac)
EasyAVFilter_SetOutput设置输出参数(目标地址) ,支持默认转码H.264和自动根据源编码进行转码
EasyAVFilter_GetFilters获取所有参数(review参数输入是否正确)
EasyAVFilter_Start开始工作,支持0次重连和N次重连
EasyAVFilter_Stop停止工作

详细信息可以直接看https://www.easydarwin.org/tools/153.html,具体用法和场景,看视频介绍;

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

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

相关文章

java 多线程

01.多线程类java.lang.Thread 这里继承Thread类的方法是比较常用的一种&#xff0c;如果说你只是想起一条线程。没有什么其它特殊的要求&#xff0c;那么可以使用Thread.&#xff08;笔者推荐使用Runable&#xff0c;后头会说明为什么&#xff09;。下面来看一个简单的实例&…

Vue结合ElementUi修改<el-table>表格的背景颜色和表头样式

本项目在开发过程中vueelementui &#xff0c; 页面中使用了table表格的样式&#xff0c; 需要对原先的样式进行修改&#xff0c;以下是简单的修改样式内容&#xff1a;项目中某个 html中引用的table表格内容,定义了div的class : device_err :<div class"device_err&q…

成集云 | 钉钉财务费用单同步至畅捷通 | 解决方案

源系统成集云目标系统 方案介绍 财务管理作为企业管理中重要的组成部分&#xff0c;在企业的发展和成长中扮演着重要角色&#xff0c;成集云以钉钉费用单OA审批与畅捷通TCloud系统为例&#xff0c;与钉钉连接器深度融合&#xff0c;通过数据处理和字段匹配实现了费用…

【核磁共振成像】相位差重建

目录 一、相位差map重建一般步骤和反正切函数主值范围二、反正切运算三、可预期相位误差和伴随场的校正四、图形变形校正 一、相位差map重建一般步骤和反正切函数主值范围 MRI是一个相敏成像模态&#xff0c;MR原始数据傅里叶变换后的复数图像中每个像素值有模和相位。标准模重…

PL端DDR4读写测试实验(未完成)

文章目录 DDR4介绍实验过程编写XDC使用IP核上板验证TODO 参考 DDR4介绍 开发板PL有一颗16bit的DDR4。 先说明硬件信号&#xff08;按该芯片&#xff09;&#xff1a; 信号名说明DQData input/output&#xff0c;双向数据线&#xff08;这个芯片是x16的&#xff0c;使用DQ[15…

Gradle 如何配置全局 mavenCentral()

我们都知道 Gradle 会使用 Maven 的中央仓库。 在 Gradle 的配置文件中&#xff0c;通常有一个 mavenCentral() 如果我们想把 mavenCentral() 的仓库地址全局替换掉别的仓库地址的话。 我们可以在 C:\Users\yhu\.gradle 目录下创建一个 init.gradle 文件。 文件中的代码为&a…

原生小程序 wxs 语法(详细)

WXS WXS&#xff08;WeiXin Script&#xff09;是内联在 WXML 中的脚本段。通过 WXS 可以在模版中内联少量处理脚本&#xff0c;丰富模板的数据预处理能力。另外&#xff0c; WXS 还可以用来编写简单的 WXS 事件响应函数。 从语法上看&#xff0c; WXS 类似于有少量限制的 Java…

Java 正则表达式

一、概念 正则表达式&#xff0c;又称规则表达式&#xff0c;是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到 z 之间的字母&#xff09;和特殊字符&#xff08;称为"元字符"&#xff09;&#xff0c;正则表达式使用单个字符串来描述、匹配…

JAVA类和对象

如何创建类 ⚠Java当中一切皆对象 ⚠如何描述对象&#xff1f;用类。 我们可以类当成一个模板&#xff0c;用来描述对象特征&#xff0c;行为等等 //自定义的类型 -> 你定义的一个 Java当中 没有的类型 class PetDog {public String name;//名字public String color;//颜…

leecode 数据库:1158. 市场分析 I

数据导入&#xff1a; SQL Schema&#xff1a; Create table If Not Exists Users (user_id int, join_date date, favorite_brand varchar(10)); Create table If Not Exists Orders (order_id int, order_date date, item_id int, buyer_id int, seller_id int); Create tab…

数学建模:主成分分析法

&#x1f506; 文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 主成分分析法 算法流程 构建原始数据矩阵 X X X &#xff0c;其中矩阵的形状为 x ∗ n x * n x∗n &#xff0c;有 m m m 个对象&#xff0c; n n n 个评价指标。然后进行矩阵的归一化处理。首先计算矩…

vue3 组合式api中 ref 和$parent 的使用

ref 的使用 vue3中&#xff0c; 在 组件中添加一个 component ref“xxx” &#xff0c;就可以在父组件中得到 子组件的 dom 对象&#xff0c; 以及 虚拟的 dom 对象&#xff0c; 有了虚拟 dom, 我们就可以在父组件中控制子组件的显示了 ref 的使用方法 vue3中ref 的特点 以上…

给设计团队管理者的6个建议优漫动游

即使以前从未担任过领导职务&#xff0c;作为一名设计师&#xff0c;你也已经习惯了仔细地考虑用户体验&#xff0c;这一技能在你领导团队时也会很有用。你需要设计一个环境和结构来使你的成员们完成最好的工作&#xff0c;让公司和用户都能受益。一本名为《DesignLeadershipHa…

删除、移动、复制文件时总是要卡在99%一段时间解决方法

Win10文件夹重命名、移动、删除等操作卡顿3-5秒。 原因分析: 查看发现&#xff0c;卡顿期间资源管理器无响应&#xff0c;并且其高度占用CPU资源&#xff0c;但是对于非文件夹文件操作没有问题。 解决方案: 1、双击“此电脑”&#xff0c;选择“查看”&#xff0c;再选择“选…

DockerCompose常用命令

DockerCompose常用命令 在上一篇博客中&#xff0c;我们对DockerCompose有了一个初步的认识&#xff0c;以及介绍了多种安装方式&#xff0c;本文继续介绍DockerCompose的常用命令。 DockerCompose中常常用到两个术语&#xff0c;一个是服务&#xff0c;一个是项目。服务常常代…

Redis面试题大全含答案

1.什么是Redis&#xff1f; 答&#xff1a;Remote Dictionary Server(Redis)是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。 它通常被称为数据结构服务器&#xff0c;因为值&#xff08;value&…

三、mycat分库分表

第五章 分库分表 一个数据库由很多表的构成&#xff0c;每个表对应着不同的业务&#xff0c;垂直切分是指按照业 务将表进行分类&#xff0c;分布到不同 的数据库上面&#xff0c;这样也就将数据或者说压力分担到不同 的库上面&#xff0c;如下图&#xff1a; 系统被切分成了&…

常静相伴:深度解析C++中的const与static关键字

个人主页&#xff1a;北海 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏✨收录专栏&#xff1a;C/C&#x1f91d;希望作者的文章能对你有所帮助&#xff0c;有不足的地方请在评论区留言指正&#xff0c;大家一起学习交流&#xff01;&#x1f9…

时间切片

1. 下次绘制交互 (INP) 下次绘制交互 (INP) 是一项新的指标&#xff0c;浏览器计划于 2024 年 3 月将其取代取代首次输入延迟 (FID) &#xff0c;成为最新的 Web Core Vitals(Web 核心性能指标)。 2. 时间切片-scheduler.yield 背景&#xff1a;用户任务完成自动释放控制权给主…

打怪(easy)

B-打怪(easy)_第二十届同济大学程序设计竞赛&#xff08;同步赛&#xff09; (nowcoder.com) 问题描述&#xff1a;初始攻击是1&#xff0c;防御是0&#xff0c;血量无穷。怪物防御力永远为0&#xff0c;只有初始血量和攻击力。双方每次受到的攻击会掉对手攻击-自己防御的血量…