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 数据可以从文件或网络中获取,并通过该函数传输。
相关推荐
心动啊1213 小时前
FFMPeg在Python中的使用
ffmpeg
猿小路9 小时前
视频流熟知
ffmpeg·h.264
chen_22710 小时前
动态桌面方案
c++·qt·ffmpeg·kanzi
GeniuswongAir1 天前
苹果电脑上启动一个 RTSP 推流,用来做测试
ffmpeg
Benny的老巢1 天前
n8n工作流通过Execute Command用FFmpeg处理音频,报错 stderr maxBuffer length exceeded的解决方案
ffmpeg·音频合成·n8n·n8n工作流·execute command
haibindev2 天前
【终极踩坑指南】Windows 10上MsQuic证书加载失败?坑不在证书,而在Schannel!
直播·http3·quic·流媒体
七夜zippoe2 天前
Spring Data JPA原理与实战 Repository接口的魔法揭秘
java·ffmpeg·事务·jpa·repository
Benny的老巢2 天前
n8n工作流中FFmpeg 视频截取失败排查:文件路径和参数顺序错误解决方案
chrome·ffmpeg·音视频
RockWang.3 天前
【配置】FFmpeg配置环境ubuntu踩坑记录。
ffmpeg
王者鳜錸3 天前
Java使用FFmpeg获取音频文件时长:完整实现与原理详解
java·开发语言·ffmpeg·音频时长