音视频推流

推流协议有librtmp,librtp,jrtplib等;

推流工具有ffmepg,obs.lib,ndi等;

一  librtmp推流

使用librtmp发布RTMP流的可以使用两种API:RTMP_SendPacket()和RTMP_Write()。

RTMP_SendPacket流程如下:

InitSockets():初始化Socket
RTMP_Alloc():为结构体“RTMP”分配内存。
RTMP_Init():初始化结构体“RTMP”中的成员变量。
RTMP_SetupURL():设置输入的RTMP连接的URL。
RTMP_EnableWrite():发布流的时候必须要使用。如果不使用则代表接收流。
RTMP_Connect():建立RTMP连接,创建一个RTMP协议规范中的NetConnection。
RTMP_ConnectStream():创建一个RTMP协议规范中的NetStream。
Delay:发布流过程中的延时,保证按正常播放速度发送数据。
RTMP_SendPacket():发送一个RTMP数据RTMPPacket。
RTMP_Close():关闭RTMP连接。
RTMP_Free():释放结构体“RTMP”。
CleanupSockets():关闭Socket。
 

RTMP拉流流程:

RTMP_Init();//初始化结构体  
InitSockets();//初始化Socket  
RTMP_ParseURL();//解析输入URL  
RTMP_SetupStream();//一些设置  
fopen();//打开文件,准备写入  
RTMP_Connect();//建立NetConnection  
RTMP_ConnectStream()//建立NetStream  
Download();//下载函数,使用RTMP_Read()进行下载
RTMP_Close();//关闭连接  
fclose();//关闭文件  
CleanupSockets();//清理Socket  

二 JRtpLib

jrtplib是c++实现的rtp协议库,支持跨平台。librtp也是一种rtp协议库。

常用api

RTPSession sess; 创建一个session实例

Create对rtpsession初始化

SendPacket发送数据

PollData接收rtp或rtcp数据

三  ffmepg推流。

通常我们将采集的yuv/rgb raw和pcm数据编码成h264/aac,写mp4文件。推流将outfile改成url即可,本质是一样的。

ffmpeg -re -stream_loop -1 -i /home/test.mp4 -an -vcodec libvpx -payload_type 100 -f rtp rtp://127.0.0.1:5004?pkt_size=1200 -vn -acodec libopus -payload_type 111 -ar 48000 -ac 2 -f rtp rtp://127.0.0.1:5002?pkt_size=1200

-re:按照帧率发送,否则会按照最高速率发送
-i:指定输入文件名
-stream_loop:指定循环读取视频源的次数,-1为无限循环
-vcodec( -c:v ):设置视频编解码器,未设置时则使用与输入文件相同之编解码器
-acodec ( -c:a ):设置音频编解码器,未设置时则使用与输入文件相同之编解码器
-f:设置输出格式
-pkt_size:rtp发送的最大长度
-payload_type:rtp的负载类型
-an:不处理音频
-vn:不处理视频
-ar:设置音频采样率
-ac:设置音频声道数量

运行以下代码前,需提前运行服务器(需推流到服务器),并提供推流地址,例如:rtmp://,rtp//等。


#include <stdio.h>
 
#define __STDC_CONSTANT_MACROS
 
extern "C"
{
    #include <libavformat/avformat.h>
    #include <libavutil/mathematics.h>
    #include <libavutil/time.h>
};
 
int main(int argc, char* argv[])
{
    AVOutputFormat *ofmt = NULL;
    //Input AVFormatContext and Output AVFormatContext
    //上下文管理对象
    AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
    AVPacket pkt;
    const char *in_filename, *out_filename;
    int ret, i;
    int videoindex=-1;
    int frame_index=0;
    int64_t start_time=0;
    //in_filename  = "cuc_ieschool.mov";
    //in_filename  = "cuc_ieschool.mkv";
    //in_filename  = "cuc_ieschool.ts";
    //in_filename  = "cuc_ieschool.mp4";
    //in_filename  = "cuc_ieschool.h264";
    in_filename  = "./video/input.mp4"; //本地文件转码,直接采集的数据编码也行
    //in_filename  = "shanghai03_p.h264";
 
    out_filename = "rtmp://localhost/dash/test";  //输出 URL(Output URL)[RTMP]
    //out_filename = "rtp://233.233.233.233:6666";//输出 URL(Output URL)[UDP]
 
    av_register_all();
    //Network
    avformat_network_init();
    //Input
    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
        printf( "Could not open input file.");
        goto end;
    }
    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
        printf( "Failed to retrieve input stream information");
        goto end;
    }
 
    for(i=0; i<ifmt_ctx->nb_streams; i++)
        if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
            videoindex=i;
            break;
        }
 
    av_dump_format(ifmt_ctx, 0, in_filename, 0);
 
    //Output
 
    avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", out_filename); //RTMP
    //avformat_alloc_output_context2(&ofmt_ctx, NULL, "mpegts", out_filename);//UDP
 
    if (!ofmt_ctx) {
        printf( "Could not create output context\n");
        ret = AVERROR_UNKNOWN;
        goto end;
    }
    ofmt = ofmt_ctx->oformat;
    for (i = 0; i < ifmt_ctx->nb_streams; i++) {
        //Create output AVStream according to input AVStream
        AVStream *in_stream = ifmt_ctx->streams[i];
        AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
        if (!out_stream) {
            printf( "Failed allocating output stream\n");
            ret = AVERROR_UNKNOWN;
            goto end;
        }
        //Copy the settings of AVCodecContext
        ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
        if (ret < 0) {
            printf( "Failed to copy context from input to output stream codec context\n");
            goto end;
        }
        out_stream->codec->codec_tag = 0;
        if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
            out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
    }
    //Dump Format------------------
    av_dump_format(ofmt_ctx, 0, out_filename, 1);
    //Open output URL
    if (!(ofmt->flags & AVFMT_NOFILE)) {
        ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
        if (ret < 0) {
            printf( "Could not open output URL '%s'", out_filename);
            goto end;
        }
    }
    //Write file header
    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        printf( "Error occurred when opening output URL\n");
        goto end;
    }
 
    start_time=av_gettime();
    while (1) {
        AVStream *in_stream, *out_stream;
        //Get an AVPacket
        ret = av_read_frame(ifmt_ctx, &pkt);
        if (ret < 0)
            break;
        //FIX:No PTS (Example: Raw H.264)
        //Simple Write PTS
        if(pkt.pts==AV_NOPTS_VALUE){
            //Write PTS
            AVRational time_base1=ifmt_ctx->streams[videoindex]->time_base;
            //Duration between 2 frames (us)
            int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx->streams[videoindex]->r_frame_rate);
            //Parameters
            pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);
            pkt.dts=pkt.pts;
            pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);
        }
        //Important:Delay
        if(pkt.stream_index==videoindex){
            AVRational time_base=ifmt_ctx->streams[videoindex]->time_base;
            AVRational time_base_q={1,AV_TIME_BASE};
            int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q);
            int64_t now_time = av_gettime() - start_time;
            if (pts_time > now_time)
                av_usleep(pts_time - now_time);
 
        }
 
        in_stream  = ifmt_ctx->streams[pkt.stream_index];
        out_stream = ofmt_ctx->streams[pkt.stream_index];
        /* copy packet */
        //Convert PTS/DTS
        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;
        //Print to Screen
        if(pkt.stream_index==videoindex){
            //printf("Send %8d video frames to output URL\n",frame_index);
            frame_index++;
        }
        //ret = av_write_frame(ofmt_ctx, &pkt);
        ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
 
        if (ret < 0) {
            printf( "Error muxing packet\n");
            break;
        }
 
        av_free_packet(&pkt);
 
    }
    //Write file trailer
    av_write_trailer(ofmt_ctx);
end:
    avformat_close_input(&ifmt_ctx);
    /* close output */
    if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
        avio_close(ofmt_ctx->pb);
    avformat_free_context(ofmt_ctx);
    if (ret < 0 && ret != AVERROR_EOF) {
        printf( "Error occurred.\n");
        return -1;
    }
    return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

步基

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值