• 既能够用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 
    #include 
    
    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137

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

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

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

    #include 
    #include 
    #include 
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    就上面八九个方法,还包括了创建实例和停止/销毁实例,核心方法就五六个,就搞定了全部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,具体用法和场景,看视频介绍;

  • 相关阅读:
    java计算机毕业设计基于安卓/微信的农产品特产销售商城小程序 uniAPP
    【UNIX网络编程第三版】阅读笔记(一):代码环境搭建
    四位十进制数字频率计VHDL,仿真视频、代码
    9-AJAX-下-axios
    基于单片机的空调的温度控制系统设计
    非线性在深度模型中的意义
    C++模板编程(22)---显式实例化Explicit Instantiation
    六、c++代码中的安全风险-fopen
    嵌入式软件的低功耗设计
    【2023.11.6】OpenAI发布会——近期chatgpt被攻击,不能使用
  • 原文地址:https://blog.csdn.net/xiejiashu/article/details/132621191