ffmpeg监听和接收rtp音视频流

news/2025/2/26 13:36:08

     在网上找了一圈关于ffmpeg如何接收rtp流的Example,都是使用ffmpeg 命令行来实现的,还没看到用相关ffmpeg库和API来实现的,于是自己写了个小程序,来验证ffmpeg在本地监听rtp流,代码在ffmpeg4.1+win10环境下测试可行:

      有几个关键点:

      1)需要提供video/audio sdp信息,用于描述基本媒体信息

      2)需要自动检测一个可用的端口


#include <iostream>
#include <sstream>
#include <fstream>

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/mathematics.h>
#include <libavutil/audio_fifo.h>
#include <libavutil/opt.h>
#include <libswresample/swresample.h>
}

#define WIN32

#ifdef WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <net/if.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/select.h>
#endif

#pragma warning(disable : 4996)
#pragma comment(lib, "WS2_32")

bool getAvailableListenPort(unsigned short *port)
{
    std::string CLIENT_IP("127.0.0.1");

    bool result = true;

#ifdef WIN32
    WSADATA wsa;
    /*初始化socket资源*/
    if (WSAStartup(MAKEWORD(1, 1), &wsa) != 0)
    {
        return false;   //代表失败
    }
#endif

    // 1. 创建一个socket
    //int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_UDP);
    int sock = socket(AF_INET, SOCK_DGRAM, 0);

    // 2. 创建一个sockaddr,并将它的端口号设为0
    struct sockaddr_in addrto;
    memset(&addrto, 0, sizeof(struct sockaddr_in));
    addrto.sin_family = AF_INET;
    addrto.sin_addr.s_addr = inet_addr(CLIENT_IP.c_str());
    addrto.sin_port = 0;

    // 3. 绑定
    int ret = ::bind(sock, (struct sockaddr*) & (addrto), sizeof(struct sockaddr_in));
    if (0 != ret)
    {
        return false;
    }

    // 4. 利用getsockname获取
    struct sockaddr_in connAddr;
    memset(&connAddr, 0, sizeof(struct sockaddr_in));
#ifdef WIN32
    int len = sizeof(connAddr);
#else
    unsigned int len = sizeof(connAddr);
#endif
    ret = ::getsockname(sock, (sockaddr*)&connAddr, &len);

    if (0 != ret)
    {
        return false;
    }

    *port = ntohs(connAddr.sin_port); // 获取端口号


#ifdef WIN32
    if (0 != closesocket(sock))
#else
    if (0 != close(sock))
#endif
    {
        result = false;
    }
    return result;
}

int receiveVideo()
{
    printf("receiveVideo() start ");
    int ret, i;
    std::string ipaddr("127.0.0.1");
    unsigned short port;
    ret = getAvailableListenPort(&port);

    std::string ifsdp("input.sdp");
    std::ofstream sdp(ifsdp);
    sdp << "v=0\r\n"
        << "o=- 0 0 IN IP4 " << ipaddr << "\r\n"
        << "s=No Name\r\n"
        << "c=IN IP4 " << ipaddr << "\r\n"
        << "t=0 0\r\n"
        << "m=" << "video" << " " << port << " RTP/AVP 96\r\n"
        << "a=rtpmap:96 H264/90000\r\n";
    sdp.close();

    av_register_all();
    avcodec_register_all();
    avformat_network_init();            
            
    AVFormatContext* fmtCtx(NULL);
    fmtCtx = avformat_alloc_context();
    fmtCtx->flags |= AVFMT_NOFILE;
    fmtCtx->protocol_whitelist =(char*)"file,udp,rtp";    


    AVInputFormat* ifmt = av_find_input_format("sdp");
    if (!ifmt) {
        std::cerr << "can't find input format" << std::endl;
        return 1;    
    }

    printf("step1: open rtp input accroding to sdp, listen port:%u \n", port);
    //if (avformat_open_input(&fmtCtx, "rtp://@127.0.0.1:9999", ifmt, NULL) != 0) {
    if (avformat_open_input(&fmtCtx, ifsdp.c_str(), ifmt, NULL) != 0) {
        printf("Failed to open input stream information\n");
        return false;
    }
    printf("avformat_open_input done\n");
    
    printf("step2: find stream info, block until key frame arrived \n");
    if ((ret = avformat_find_stream_info(fmtCtx, 0)) < 0) {
        printf("Failed to retrieve input stream information");
        return false;
    }
    
    AVStream* st = fmtCtx->streams[0];
    AVCodecID videoCodecId = st->codec->codec_id;
    int width = st->codec->width;
    int height = st->codec->height;


    printf("step3: find stream info done: time base: %d/%d, type:%s, width:%d, height:%d \n",
        st->time_base.num,
        st->time_base.den,
        avcodec_get_name(st->codec->codec_id),
        width,
        height);

    if (videoCodecId != AV_CODEC_ID_H264) {
        printf("video codec info not correct!");
        return false;
    }    

    printf("step4: start to receive video frame\n");
    av_read_play(fmtCtx);

    AVPacket m_avPacket;
    int count = 0;
    memset(&m_avPacket, 0, sizeof(m_avPacket));

    while (count++ <50) {
        av_init_packet(&m_avPacket);    
        ret = av_read_frame(fmtCtx, &m_avPacket);
        if (ret < 0) {
            printf("av_read_frame error\n");
            continue;
        }
        printf("Receive video frame packet(%d th), dts %ld, size %d\n",count, m_avPacket.dts, m_avPacket.size);
        av_packet_unref(&m_avPacket);
    }

    printf("program finished");
}

int receiveAudio()
{
    printf("receiveAudio() start ");
    int ret, i;
    std::string ipaddr("127.0.0.1");
    unsigned short port;
    ret = getAvailableListenPort(&port);

    std::string ifsdp("inputAudio.sdp");
    std::ofstream sdp(ifsdp);
    sdp << "v=0\r\n"
        << "o=- 0 0 IN IP4 " << ipaddr << "\r\n"
        << "s=No Name\r\n"
        << "c=IN IP4 " << ipaddr << "\r\n"
        << "t=0 0\r\n"
        << "m=" << "audio" << " " << port << " RTP/AVP 97\r\n"
        << "a=rtpmap:97 opus/48000/2\r\n";
    sdp.close();

    av_register_all();
    avcodec_register_all();
    avformat_network_init();

    AVFormatContext* fmtCtx(NULL);
    fmtCtx = avformat_alloc_context();
    fmtCtx->flags |= AVFMT_NOFILE;
    fmtCtx->protocol_whitelist = (char*)"file,udp,rtp";
    
    AVInputFormat* ifmt = av_find_input_format("sdp");
    if (!ifmt) {
        std::cerr << "can't find input format" << std::endl;
        return 1;
    }

    printf("step1: open rtp input accroding to sdp, listen port:%u \n", port);
    //if (avformat_open_input(&fmtCtx, "rtp://@127.0.0.1:9999", ifmt, NULL) != 0) {
    if (avformat_open_input(&fmtCtx, ifsdp.c_str(), ifmt, NULL) != 0) {
        printf("Failed to open input stream information\n");
        return false;
    }
    printf("avformat_open_input done\n");

    printf("step2: find stream info, block until key frame arrived \n");
    if ((ret = avformat_find_stream_info(fmtCtx, 0)) < 0) {
        printf("Failed to retrieve input stream information");
        return false;
    }

    AVStream* st = fmtCtx->streams[0];
    AVCodecID audioCodecId = st->codec->codec_id;
    int sampleRate = st->codec->sample_rate;
    int channels = st->codec->channels;

    printf("step3: find stream info done: time base: %d/%d, type:%s, channels:%d, sampleRate:%d \n",
        st->time_base.num,
        st->time_base.den,
        avcodec_get_name(st->codec->codec_id),
        channels,
        sampleRate);

    if (audioCodecId != AV_CODEC_ID_OPUS) {
        printf("audio codec info not correct!");
        return false;
    }

    printf("step4: start to receive audio frame\n");
    av_read_play(fmtCtx);

    AVPacket m_avPacket;
    int count = 0;
    memset(&m_avPacket, 0, sizeof(m_avPacket));

    while (count++ < 50) {
        av_init_packet(&m_avPacket);
        ret = av_read_frame(fmtCtx, &m_avPacket);
        if (ret < 0) {
            printf("av_read_frame error\n");
            continue;
        }
        printf("Receive audio frame packet(%d th), dts %ld, size %d\n", count, m_avPacket.dts, m_avPacket.size);
        av_packet_unref(&m_avPacket);
    }

    avformat_close_input(&fmtCtx);
    avformat_free_context(fmtCtx);

    printf("program finished");
}

int main() {
    //receiveVideo();
    receiveAudio();
}

 


http://www.niftyadmin.cn/n/4598701.html

相关文章

windows下使用camera(opencv方式)

一. opencv支持读取摄像头&#xff0c;支持视频录制或显示&#xff0c; 具体代码 //1. 初始化视频录制文件 String recFileName std::to_string(count) "_rec.avi"; int wfourcc VideoWriter::fourcc(M, J, P, G); writer VideoWriter(recFileName,…

JS TypeError:*is undefined

JS在解析JSON数据时&#xff0c;操作生疏可能犯的错误。 success:function(data){ if(data "failure"){ alert("请求出错,请刷新页面再操作"); } var res eval(data); var msgList res[0].msgList; var pgInfo res[0].pageInfo; var page ""…

windows下使用camera (ffmpeg dshow方式)

方法概要&#xff1a;先读取camera支持的mode&#xff0c;然后用ffmpeg dshow打开camera并解码压缩流 一. 用相关代码读取本地camera当前支持的输出mode&#xff0c;方便后续选择mode来打开camera&#xff1a; bool EnumerateCameras(vector<int>& camIdx) { …

根据匹配词个数排序

法一&#xff1a; select title,(case when CHARINDEX(Log4net,title)>0 then 1 else 0 end )(case when CHARINDEX(oracle,title)>0 then 1 else 0 end ) counts from T_KNOWLEDGE where title like %Log4net% or title like %oracle% order by counts desc 法二&#x…

流媒体服务器原理和架构解析

原文&#xff1a; https://blog.csdn.net/xuheazx/article/details/52020933 一个完整的多媒体文件是由音频和视频两部分组成的&#xff0c;H264、Xvid等就是视频编码格式&#xff0c;MP3、AAC等就是音频编码格式&#xff0c;字幕文件只是附加文件。目前大部分的播放器产品对于…

ffmepg 音频重采样

因为业务需要&#xff0c;直播推流过程中&#xff0c;pcm的音频需要从48k采样率&#xff0c;重采样到44.1k&#xff0c;然后再编码程aac&#xff0c;封装成rtmp推送出去。这里只描述如何做pcm数据的重采样。 整个过程中&#xff0c;音频输入&#xff1a;PCM/S16/48000/2 重采…

webrtc 学习要点

总结一下webrtc的关键技术&#xff0c;如果搞懂了以下问题&#xff0c;webrtc应该算基本掌握了 1. 通话建立的大概流程。 a&#xff09;收集建立点对点的 IP candidates &#xff08;local&#xff0c; srvReflex&#xff0c;peerReflex&#xff0c; relay&#xff09;&#…

linux bash启动停止脚本,第二弹

本文是之前的watchdate的shell脚本的改进wdate,同样先上图:1)脚本加入chkconfig管理head -5 /etc/init.d/wdate #!/bin/bash #auth:chentp0601qq.com # #wdate Start/Stop the watchdate daemon # # chkconfig: 2345 71 55chkconfig --add wdate2)开始贴代码#!/bin/bas…