FFmpeg实现rtp推流

news/2024/7/10 18:37:51 标签: ffmpeg

以下是一个简单的示例代码,演示了如何使用 UDP 或 TCP 进行音视频传输:
代码未经验证,供参考

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <libavformat/avformat.h>

#define RTPPKT_LEN 1400
#define IPOUT_PAYNUM 1

// UDP Socket
int udpSocket;

// TCP Socket
int tcpSocket;

// UDP 发送函数
int udp_write_buffer(void* opaque, uint8_t* buf, int buf_size) {
    struct sockaddr_in destAddr;
    memset(&destAddr, 0, sizeof(destAddr));
    destAddr.sin_family = AF_INET;
    destAddr.sin_port = htons(1234); // 设置目标端口号
    destAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 设置目标IP地址

    ssize_t sentBytes = sendto(udpSocket, buf, buf_size, 0, (struct sockaddr*)&destAddr, sizeof(destAddr));
    if (sentBytes < 0) {
        perror("UDP 发送失败");
        return -1;
    }

    return 0;
}

// TCP 发送函数
int tcp_write_buffer(void* opaque, uint8_t* buf, int buf_size) {
    ssize_t sentBytes = send(tcpSocket, buf, buf_size, 0);
    if (sentBytes < 0) {
        perror("TCP 发送失败");
        return -1;
    }

    return 0;
}

int main() {
    av_register_all();
    avformat_network_init();

    // 创建输入格式上下文
    AVFormatContext* inputFormatCtx = avformat_alloc_context();

    // 打开输入文件或网络流
    const char* inputUrl = "input.mp4";
    int ret = avformat_open_input(&inputFormatCtx, inputUrl, nullptr, nullptr);
    if (ret != 0) {
        printf("无法打开输入文件:%s\n", inputUrl);
        return -1;
    }

    // 获取流信息
    ret = avformat_find_stream_info(inputFormatCtx, nullptr);
    if (ret < 0) {
        printf("无法获取流信息\n");
        return -1;
    }

    // 寻找视频流索引
    int videoStreamIndex = -1;
    for (unsigned int i = 0; i < inputFormatCtx->nb_streams; i++) {
        if (inputFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStreamIndex = i;
            break;
        }
    }

    // 创建输出格式上下文
    AVFormatContext* outputFormatCtx = nullptr;
    ret = avformat_alloc_output_context2(&outputFormatCtx, nullptr, "rtp", "rtp://127.0.0.1:1234"); // 设置输出 URL
    if (ret < 0) {
        printf("无法创建输出格式上下文\n");
        return -1;
    }

    // 创建 AVIO 缓存空间
    unsigned char* outBuffer = (unsigned char*)av_malloc(RTPPKT_LEN * IPOUT_PAYNUM);

    // 创建 UDP Socket
    udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (udpSocket < 0) {
        perror("无法创建 UDP Socket");
        return -1;
    }

    // 创建 TCP Socket
    tcpSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (tcpSocket < 0) {
        perror("无法创建 TCP Socket");
        return -1;
    }

    // 创建 AVIO 上下文
    AVIOContext* avioOut;
    if (strcmp("UDP", "RTP/AVP") == 0) {
        avioOut = avio_alloc_context(outBuffer, RTPPKT_LEN * IPOUT_PAYNUM, 1, nullptr, nullptr, udp_write_buffer, nullptr);
    } else if (strcmp("TCP", "RTP/AVP") == 0) {
        avioOut = avio_alloc_context(outBuffer, RTPPKT_LEN * IPOUT_PAYNUM, 1, nullptr, nullptr, tcp_write_buffer, nullptr);
    }

    if (!avioOut) {
        printf("无法创建 AVIO 上下文\n");
        return -1;
    }

    avioOut->max_packet_size = RTPPKT_LEN;

    outputFormatCtx->pb = avioOut;
    outputFormatCtx->flags = AVFMT_FLAG_CUSTOM_IO;

    // 复制视频流参数到输出格式上下文
    AVStream* outputStream = avformat_new_stream(outputFormatCtx, nullptr);
    ret = avcodec_parameters_copy(outputStream->codecpar, inputFormatCtx->streams[videoStreamIndex]->codecpar);
    if (ret < 0) {
        printf("无法复制视频流参数\n");
        return -1;
    }

    // 写入输出头部
    ret = avformat_write_header(outputFormatCtx, nullptr);
    if (ret < 0) {
        printf("无法写入输出头部\n");
        return -1;
    }

    // 开始读取和发送数据包
    AVPacket packet;
    while (av_read_frame(inputFormatCtx, &packet) >= 0) {
        if (packet.stream_index == videoStreamIndex) {
            // 发送数据包到输出上下文
            ret = av_interleaved_write_frame(outputFormatCtx, &packet);
            if (ret < 0) {
                printf("无法发送数据包\n");
                return -1;
            }
        }

        av_packet_unref(&packet);
    }

    // 写入输出尾部
    ret = av_write_trailer(outputFormatCtx);
    if (ret < 0) {
        printf("无法写入输出尾部\n");
        return -1;
    }

    // 关闭 Socket
    close(udpSocket);
    close(tcpSocket);

    // 释放资源
    avformat_close_input(&inputFormatCtx);
    avformat_free_context(inputFormatCtx);
    avformat_free_context(outputFormatCtx);
    avio_context_free(&avioOut);
    av_free(outBuffer);

    return 0;
}

这个示例代码基于 FFmpeg 库,演示了如何使用 UDP 或 TCP 进行音视频传输。具体的步骤包括:

  1. 注册 FFmpeg 库并初始化网络模块。
  2. 创建输入格式上下文,并打开输入文件或网络流。
  3. 获取流信息,并找到视频流索引。
  4. 创建输出格式上下文,并设置输出协议为 RTP。
  5. 创建 UDP 或 TCP Socket。
  6. 创建 AVIO 缓存空间和 AVIO 上下文,并将其赋值给输出格式上下文。
  7. 复制视频流参数到输出格式上下文。
  8. 写入输出头部。
  9. 通过循环读取输入文件的数据包,并发送到输出格式上下文。
  10. 写入输出尾部。
  11. 关闭 Socket 和释放资源。

您可以根据实际需求修改相关参数,如输入文件、目标地址、端口号等,以适配您的具体场景。


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

相关文章

GLTF vs FBX:应该使用哪种格式?

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 概括地说&#xff0c;如果要将数据传输到 Unity 或虚幻引擎等游戏引擎…

普里姆算法c语言实现

以下是普里姆算法&#xff08;Prims Algorithm&#xff09;的C语言实现&#xff1a; #include <stdio.h>#include <stdlib.h>#include <stdbool.h>#define INF 99999 // 无穷大&#xff0c;表示不可达int n; // 顶点数int m; // 边数int graph[10][10]; // …

Flink 状态管理与容错机制(CheckPoint SavePoint)的关系

一、什么是状态 无状态计算的例子&#xff1a; 例如一个加法算子&#xff0c;第一次输入235那么以后我多次数据23的时候得到的结果都是5。得出的结论就是&#xff0c;相同的输入都会得到相同的结果&#xff0c;与次数无关。 有状态计算的例子&#xff1a; 访问量的统计&#x…

3GPP在SHF频段基于中继的5G高速列车场景中的标准化

文章目录 信道模型实验μ参考信号初始接入方法波形比较 RRH&#xff1a;remote radio head 远程无线头 HTS&#xff1a;high speed train 高速移动列车 信道模型 考虑搭配RRH和车载中继站之间的LOS路径以及各种环境&#xff08;开放或峡谷&#xff09;&#xff0c;在本次实验场…

朴素贝叶斯(NBM,Naive Bayesian Model), NB 算法 简介

朴素贝叶斯&#xff08;NBM&#xff0c;Naive Bayesian Model&#xff09;, NB 算法 分类算法 在贝叶斯原理的基础上&#xff0c;预先假定了特征与特征之间的相互独立。 贝叶斯的原理&#xff1a;当不知道这个事物实际情况的时候&#xff0c;我们可以根据一些相关的条件来判…

MATLAB遗传算法工具箱的三种使用方法

MATLAB中有三种调用遗传算法的方式&#xff1a; 一、遗传算法的开源文件 下载“gatbx”压缩包文件&#xff0c;解压后&#xff0c;里面有多个.m文件&#xff0c;可以看到这些文件的编辑日期都是1998年&#xff0c;很古老了。 这些文件包含了遗传算法的基础操作&#xff0c;包含…

微信小程序 动态设置状态栏样式

onLoad(options) {//修改状态栏标题wx.setNavigationBarTitle({title: 页面标题, //页面标题success: () > {}, //接口调用成功的回调函数fail: () > {}, //接口调用失败的回调函数complete: () > {} //接口调用结束的回调函数&#xff08;调用成功、失败…

CSS3过渡与动画,2D与3D

背景和边框 1. border-image 该属性用于定义元素边框的背景图像 语法&#xff1a;border-image:none | url(img) imagesection [/imagewidth] imaghandling 其中imagesection定义用于边框不同部分的图像部分。imagesection值可以由图像上的4条分隔线组成&#xff0c;每条线以…