既能够用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 codecs
    av_register_all();

    // Open input RTSP URL
    if ((ret = avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)) != 0) {
        printf("Cannot open input stream: %s
", av_err2str(ret));
        return -1;
    }

    // Retrieve stream information
    if ((ret = avformat_find_stream_info(pFormatCtx, NULL)) < 0) {
        printf("Cannot find stream information: %s
", av_err2str(ret));
        return -1;
    }

    // Find the first audio stream
    for (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, exit
    if (!in_stream) {
        printf("Didn't find a audio stream!
");
        return -1;
    }

    // Get a pointer to the codec context for the audio stream
    pCodecCtxIn = in_stream->codec;

    // Find the first video stream
    for (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, exit
    if (!out_stream) {
        printf("Didn't find a video stream!
");
        return -1;
    }

    // Get a pointer to the codec context for the video stream
    pCodecCtxOut = out_stream->codec;

    // Open output RTMP URL
    if ((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 context
    if (avformat_new_stream(pFormatCtx, out_stream->codec->codec) < 0) {
        printf("Error adding new stream to output context
");
        return -1;
    }

    // Write header
    if (avformat_write_header(pFormatCtx, NULL) < 0) {
        printf("Error occurred when opening output file.
");
        return -1;
    }

    // Process each packet from input stream
    while (av_read_frame(pFormatCtx, &pkt) >= 0) {
        // Is this a packet from the audio stream?
        if (pkt.stream_index == in_stream->index) {
            // Decode audio frame
            AVCodecContext *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 file
                av_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 frame
                ret = av_interleaved_write_frame(pFormatCtx, &pkt);
                if (ret < 0) {
                    printf("Error during writing
");
                    return -1;
                }
            }
        }
    }

    // Close the output context
    avformat_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改成0
	getchar();
	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.niftyadmin.cn/n/4990953.html

相关文章

【Linux操作系统】信号量实现生生产者消费者模型

当涉及到多线程编程时&#xff0c;经常会遇到生产者消费者问题。在Linux系统编程中&#xff0c;我们可以使用信号量来实现生产者消费者模型&#xff0c;以确保线程之间的同步和互斥。 文章目录 什么是生产者消费者问题&#xff1f;使用信号量实现生产者消费者模型信号量的原理信…

Python数据分析实战-判断一组序列(列表)的变化趋势(附源码和实现效果)

实现功能 判断一组序列&#xff08;列表&#xff09;的变化趋势 实现代码 from sklearn.linear_model import LinearRegression import numpy as np # 计算相邻两个数之间的差值的均值&#xff0c;并判断变化趋势。 def trend(lst):diff [lst[i1] - lst[i] for i in range(…

CGLIB 问题

全部错误信息如下&#xff1a;&#xff08;为了大家能搜到&#xff0c;拼了&#xff0c;全部截下来~&#xff09; [main] INFO org.springframework.aop.framework.CglibAopProxy - Final method [protected final org.hibernate.Session org.springframework.orm.hibernate5.s…

跨境电商面临的法律与合规问题

跨境电商在全球范围内取得了飞速的发展&#xff0c;但这一领域也伴随着复杂的法律与合规问题&#xff0c;涉及国际法律、知识产权、税务、隐私等多个方面。在跨境电商中&#xff0c;合法合规的经营不仅有助于企业长期发展&#xff0c;还能增强消费者信任&#xff0c;提升市场竞…

计算机二级web操作题

第二套 1.对数组排序 <html> <title>对数组排序</title> <body> <script> //生成一个m、n之间的整数 function rand(m,n){ /* **********Found********** */ var iMath.random(); /* **********Found********** */ return Math.round((n-m…

玩客云 线刷Armbian 搭配Alist 阿里云盘 Jellyfin NovaVideoPlayer搞电视墙

啰嗦的背景 喜欢看电影&#xff0c;买了个投影仪&#xff0c;是这一切折腾的开端。 投影仪虽然有当贝系统&#xff0c;但是想看的电影总是需要**电视会员&#xff0c;那我肯定是不用的。因为有爱腾优的会员&#xff0c;最开始都是使用手机投屏&#xff0c;当呗的投影仪好就好…

1.9 动态解密ShellCode反弹

动态解密执行技术可以对抗杀软的磁盘特征查杀。其原理是将程序代码段中的代码进行加密&#xff0c;然后将加密后的代码回写到原始位置。当程序运行时&#xff0c;将动态解密加密代码&#xff0c;并将解密后的代码回写到原始位置&#xff0c;从而实现内存加载。这种技术可以有效…

正中优配:A股上半年净赚近3万亿:谁是“盈利王”?谁是“亏损王”?

忙碌的半年报披露季划上句号。 2023年上半年&#xff0c;A股5267家上市公司获得营收算计达35.44万亿元&#xff0c;归母净利算计达2.95万亿元。归母净利亏本的企业达1014家&#xff0c;占上市公司数量的比重达20%。 本年上半年&#xff0c;谁是上市公司“盈余王”&#xff1f…