自学内容网 自学内容网

ffmpeg RTP PS推流

要实现 CRtpSendPs 类,使其能够将 H264 数据通过 RTP PS 流推送到指定的 URL,并支持 TCP 和 UDP 传输方式,您需要使用 FFmpeg 库。以下是该类的实现示例,包括必要的初始化、推流和退出函数。

步骤

  1. 初始化 FFmpeg 库:需要初始化 FFmpeg 网络、格式和编码器相关的库。
  2. 配置 RTP 传输:使用 RTP 封装 PS (Program Stream) 格式。
  3. 推送数据:接收 H264 数据并将其封装为 RTP 包,发送到指定的目标 URL(支持 UDP 和 TCP)。
  4. 退出处理:释放相关资源。

完整代码实现

 
#include <iostream>
#include <string>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavdevice/avdevice.h>
#include <libavfilter/avfilter.h>
#include <libswscale/swscale.h>

class CRtpSendPs
{
public:
    CRtpSendPs();
    ~CRtpSendPs();

    int Init(const char *url, bool bUdpOrTcp);  // 初始化,设置目标 URL 和传输协议
    int PushStream(const void *pH264Data, uint32_t u32Len);  // 推送视频数据
    int Exit();  // 清理资源

private:
    AVFormatContext *outputFmtCtx;
    AVStream *outputStream;
    AVCodecContext *codecCtx;
    AVPacket pkt;
    bool isUdp;
    std::string outputUrl;
    bool isInitialized;
};

CRtpSendPs::CRtpSendPs() : outputFmtCtx(nullptr), outputStream(nullptr), codecCtx(nullptr), isInitialized(false)
{
    av_register_all();
    avformat_network_init();
    avcodec_register_all();
    avdevice_register_all();
    isUdp = true;
    isInitialized = false;
}

CRtpSendPs::~CRtpSendPs()
{
    Exit();
}

int CRtpSendPs::Init(const char *url, bool bUdpOrTcp)
{
    if (isInitialized) {
        std::cerr << "Already initialized!" << std::endl;
        return -1;
    }

    isUdp = bUdpOrTcp;
    outputUrl = url;

    // Create output context
    AVOutputFormat *fmt = av_guess_format(NULL, outputUrl.c_str(), NULL);
    if (!fmt) {
        std::cerr << "Could not guess output format!" << std::endl;
        return -1;
    }

    int ret = avformat_alloc_output_context2(&outputFmtCtx, fmt, NULL, outputUrl.c_str());
    if (ret < 0) {
        std::cerr << "Failed to create output context!" << std::endl;
        return ret;
    }

    // Create a new stream for the output
    outputStream = avformat_new_stream(outputFmtCtx, NULL);
    if (!outputStream) {
        std::cerr << "Failed to create new stream!" << std::endl;
        return AVERROR(ENOMEM);
    }

    // Setup codec parameters (for H264)
    AVCodec *encoder = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!encoder) {
        std::cerr << "H264 encoder not found!" << std::endl;
        return AVERROR_ENCODER_NOT_FOUND;
    }

    codecCtx = avcodec_alloc_context3(encoder);
    if (!codecCtx) {
        std::cerr << "Failed to allocate codec context!" << std::endl;
        return AVERROR(ENOMEM);
    }

    codecCtx->codec_id = AV_CODEC_ID_H264;
    codecCtx->bit_rate = 1000000;
    codecCtx->width = 1920;
    codecCtx->height = 1080;
    codecCtx->time_base = {1, 30};  // 30 fps
    codecCtx->gop_size = 12;
    codecCtx->max_b_frames = 3;
    codecCtx->pix_fmt = AV_PIX_FMT_YUV420P;

    ret = avcodec_open2(codecCtx, encoder, NULL);
    if (ret < 0) {
        std::cerr << "Failed to open codec!" << std::endl;
        return ret;
    }

    // Copy codec parameters to the stream
    ret = avcodec_parameters_from_context(outputStream->codecpar, codecCtx);
    if (ret < 0) {
        std::cerr << "Failed to copy codec parameters!" << std::endl;
        return ret;
    }

    // Open the output file or network stream
    if (!(outputFmtCtx->oformat->flags & AVFMT_NOFILE)) {
        ret = avio_open(&outputFmtCtx->pb, outputUrl.c_str(), AVIO_FLAG_WRITE);
        if (ret < 0) {
            std::cerr << "Failed to open output URL!" << std::endl;
            return ret;
        }
    }

    // Write the header of the file (or stream)
    ret = avformat_write_header(outputFmtCtx, NULL);
    if (ret < 0) {
        std::cerr << "Failed to write header!" << std::endl;
        return ret;
    }

    isInitialized = true;
    return 0;
}

int CRtpSendPs::PushStream(const void *pH264Data, uint32_t u32Len)
{
    if (!isInitialized) {
        std::cerr << "Not initialized!" << std::endl;
        return -1;
    }

    // Create a packet and fill it with H264 data
    av_init_packet(&pkt);
    pkt.data = (uint8_t *)pH264Data;
    pkt.size = u32Len;
    pkt.stream_index = outputStream->index;
    pkt.flags |= AV_PKT_FLAG_KEY;  // Assuming it's a keyframe, adjust if needed

    // Write the packet to the RTP stream
    int ret = av_write_frame(outputFmtCtx, &pkt);
    if (ret < 0) {
        std::cerr << "Error writing frame!" << std::endl;
        return ret;
    }

    return 0;
}

int CRtpSendPs::Exit()
{
    if (!isInitialized) {
        return 0;  // Already exited
    }

    av_write_trailer(outputFmtCtx);

    // Close the output stream and free resources
    if (outputFmtCtx && !(outputFmtCtx->oformat->flags & AVFMT_NOFILE)) {
        avio_closep(&outputFmtCtx->pb);
    }

    avcodec_free_context(&codecCtx);
    avformat_free_context(outputFmtCtx);

    isInitialized = false;
    return 0;
}

代码说明

  1. CRtpSendPs 类的构造函数和析构函数

    • Init():初始化 FFmpeg 输出格式和编码器配置,设置输出 URL 并建立连接。
    • PushStream():接受一帧 H264 数据并将其封装为 RTP 包,然后通过网络发送。
    • Exit():释放资源并关闭输出流。
  2. FFmpeg 配置

    • 使用 avformat_alloc_output_context2() 创建输出流上下文。
    • 使用 avcodec_find_encoder() 查找 H264 编码器,初始化编码器上下文。
    • 使用 av_write_frame() 将数据包发送到指定的输出 URL(支持 RTP 协议)。
  3. RTP 传输:通过 RTP 推送数据流,支持 UDP 和 TCP(通过 URL 中的协议部分决定)。

编译命令

在编译前,确保已经安装了 FFmpeg 库。如果没有安装,您可以使用以下命令:

sudo apt-get install libavformat-dev libavcodec-dev libavutil-dev libavdevice-dev libswscale-dev

然后使用以下命令来编译代码:

 
g++ -o rtp_send_ps rtp_send_ps.cpp -lavformat -lavcodec -lavutil -lavdevice -lswscale -lavfilter -lm -lz

运行示例

 
./rtp_send_ps rtp://192.168.0.49:5000

此命令将会把 H264 数据通过 RTP 协议推送到目标 IP 地址 192.168.0.49,端口 5000

注意事项

  • 您需要确保目标服务器或设备能够接收 RTP 流。
  • 在真实应用中,PushStream() 中的 H264 数据可以从文件或网络中获取,并通过该函数传输。

原文地址:https://blog.csdn.net/jzjhome/article/details/144068035

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!