基于librtmp的推流实现

2023-05-16

1.推流

配置好rtmpdump库后,我们可以先用命令行来推流看下效果

2.流程图

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

使用RTMP_SendPacket()发布流的时候的函数执行流程图如下图所示。

image

流程图中关键函数的作用如下所列

  • 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。

下面将本地FLV文件发布到RTMP流媒体服务器。这个只是简单的demo。

#define uint32_t unsigned int


#define HTON16(x)  ((x>>8&0xff)|(x<<8&0xff00))
#define HTON24(x)  ((x>>16&0xff)|(x<<16&0xff0000)|(x&0xff00))
#define HTON32(x)  ((x>>24&0xff)|(x>>8&0xff00)|\
         (x<<8&0xff0000)|(x<<24&0xff000000))
#define HTONTIME(x) ((x>>16&0xff)|(x<<16&0xff0000)|(x&0xff00)|(x&0xff000000))
 
/*read 1 byte*/
int ReadU8(uint32_t *u8,FILE*fp){
         if(fread(u8,1,1,fp)!=1)
                   return 0;
         return 1;
}
/*read 2 byte*/
int ReadU16(uint32_t *u16,FILE*fp){
         if(fread(u16,2,1,fp)!=1)
                   return 0;
         *u16=HTON16(*u16);
         return 1;
}
/*read 3 byte*/
int ReadU24(uint32_t *u24,FILE*fp){
         if(fread(u24,3,1,fp)!=1)
                   return 0;
         *u24=HTON24(*u24);
         return 1;
}
/*read 4 byte*/
int ReadU32(uint32_t *u32,FILE*fp){
         if(fread(u32,4,1,fp)!=1)
                   return 0;
         *u32=HTON32(*u32);
         return 1;
}
/*read 1 byte,and loopback 1 byte at once*/
int PeekU8(uint32_t *u8,FILE*fp){
         if(fread(u8,1,1,fp)!=1)
                   return 0;
         fseek(fp,-1,SEEK_CUR);
         return 1;
}
/*read 4 byte and convert to time format*/
int ReadTime(uint32_t *utime,FILE*fp){
         if(fread(utime,4,1,fp)!=1)
                   return 0;
         *utime=HTONTIME(*utime);
         return 1;

}

int push_packet(char* infile,char* rtmp_server_path){
	RTMP *rtmp = NULL;
	RTMPPacket *packet = NULL;
	uint32_t start_time = 0;
	uint32_t now_time = 0;
	//上一帧时间戳
	long pre_frame_time = 0;
	long lasttime = 0;
	//下一帧是否是关键帧
	int bNextIsKey = 1;
	uint32_t preTagSize = 0;
	uint32_t type = 0;
	uint32_t datalength = 0;
	uint32_t timestamp = 0;
	uint32_t streamid = 0;
	FILE* fp = NULL;
	fp = fopen(infile,"rb");
	if(!fp){		
		RTMP_LogPrintf("Open File Error\n");
		CleanupSockets();
		return -1;
	}
	//初始化Socket
	if(!InitSockets()){
		RTMP_LogPrintf("Init Socket Err\n");
		return -1;
	}
	//为结构体“RTMP”分配内存
	rtmp = RTMP_Alloc();
	if(!rtmp){
		RTMP_LogPrintf("RTMP_Alloc failed\n");
		return -1;
	}
	//初始化结构体“RTMP”中的成员变量
	RTMP_Init(rtmp);
	rtmp->Link.timeout = 5;
	if(!RTMP_SetupURL(rtmp, rtmp_server_path)){
		RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n");
        RTMP_Free(rtmp);
        CleanupSockets();
        return -1;
	}
	//发布流的时候必须要使用。如果不使用则代表接收流。
	RTMP_EnableWrite(rtmp);
	//建立RTMP连接,创建一个RTMP协议规范中的NetConnection
	if (!RTMP_Connect(rtmp,NULL)){
                   RTMP_Log(RTMP_LOGERROR,"Connect Err\n");
                   RTMP_Free(rtmp);
                   CleanupSockets();
                   return -1;
    }
	//创建一个RTMP协议规范中的NetStream
     if (!RTMP_ConnectStream(rtmp,0)){
                   RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n");
                   RTMP_Close(rtmp);
                   RTMP_Free(rtmp);
                   CleanupSockets();
                   return -1;
    }

	packet=(RTMPPacket*)malloc(sizeof(RTMPPacket));
    RTMPPacket_Alloc(packet,1024*64);
    RTMPPacket_Reset(packet);
    packet->m_hasAbsTimestamp = 0;        
    packet->m_nChannel = 0x04;
    packet->m_nInfoField2 = rtmp->m_stream_id;
    RTMP_LogPrintf("Start to send data ...\n");
	//跳过flv的9个头字节
    fseek(fp,9,SEEK_SET);     
   	//跳过previousTagSize所占的4个字节
    fseek(fp,4,SEEK_CUR);   
    start_time=RTMP_GetTime();
	while(1)
    {
    	//如果下一帧是关键帧,且上一帧的时间戳比现在的时间还长(外部时钟),说明推流速度过快了,可以延时下
    	if((((now_time=RTMP_GetTime())-start_time)<(pre_frame_time)) && bNextIsKey)
		{        
            //wait for 1 sec if the send process is too fast
             //这个机制不好,需要改进
             if(pre_frame_time>lasttime){
                  RTMP_LogPrintf("TimeStamp:%8lu ms\n",pre_frame_time);
                  lasttime=pre_frame_time;
             }
			sleep(1);//等待1秒
            continue;
        }       
     	//not quite the same as FLV spec
        if(!ReadU8(&type,fp))//读取type     
        	break;
        if(!ReadU24(&datalength,fp))//读取datalength的长度
         	break;
        if(!ReadTime(&timestamp,fp))//从flv的head读取时间戳
         	break;
        if(!ReadU24(&streamid,fp))//读取streamid的类型
         	break;
        if(type!=0x08&&type!=0x09){//如果不是音频或视频类型
            //jump over non_audio and non_video frame,
            //jump over next previousTagSizen at the same time
            fseek(fp,datalength+4,SEEK_CUR);//跳过脚本以及脚本后的previousTagSize
            continue;
        }      
		//把flv的音频和视频数据写入到packet的body中
        if(fread(packet->m_body,1,datalength,fp)!=datalength)
            break;
        //packet header  大尺寸
        packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
        packet->m_nTimeStamp = timestamp;//时间戳
        packet->m_packetType = type;//包类型
        packet->m_nBodySize  = datalength;//包大小
        pre_frame_time=timestamp;//把包读取到的时间戳赋值给上一帧时间戳变量
        if (!RTMP_IsConnected(rtmp)){//确认连接
             RTMP_Log(RTMP_LOGERROR,"rtmp is not connect\n");
             break;
        }
		RTMP_Log(RTMP_LOGINFO,"send packet\n");
		//发送一个RTMP数据RTMPPacket
       if (!RTMP_SendPacket(rtmp,packet,0)) {
             RTMP_Log(RTMP_LOGERROR,"Send Error\n");
             break;
       }
       if(!ReadU32(&preTagSize,fp))
       		break;                 
       if(!PeekU8(&type,fp))//去读下一帧的类型
           break;
       if(type==0x09){//如果下一帧是视频
            if(fseek(fp,11,SEEK_CUR)!=0)
                 break;
            if(!PeekU8(&type,fp)){
                 break;
            }
            if(type==0x17)//如果视频帧是关键帧
                bNextIsKey=1;
            else
                bNextIsKey=0;
            fseek(fp,-11,SEEK_CUR);
       }
    }               
   RTMP_LogPrintf("\nSend Data Over!\n");
        
   if(fp) {
      fclose(fp);
   }
   if (rtmp!=NULL){
   	  //关闭RTMP连接
      RTMP_Close(rtmp); 
	  //释放结构体RTMP
      RTMP_Free(rtmp); 
      rtmp=NULL;
   }
   if (packet!=NULL){
      RTMPPacket_Free(packet);    
      free(packet);
      packet=NULL;
   }
   //关闭Socket
   CleanupSockets();
   return 0;
	
}

void showuse(){
	RTMP_LogPrintf("--rtmp|-h  		  help\n");
	RTMP_LogPrintf("--rtmp|-v  		  version\n");
	RTMP_LogPrintf("--rtmp|-i url 	  push file\n");
	RTMP_LogPrintf("--rtmp|-s url 	  URL (e.g. rtmp://host[:port]/path)\n");

}
int
main(int argc, char **argv)
{
	//设置log为info级别
	RTMP_debuglevel = RTMP_LOGINFO;
	char* infile = "in.flv";
	char* rtmpserverpath = "rtmp://localhost:1935/hls";
	struct option opts[] = {
    	{"help",  no_argument, NULL, 'h'},
    	{"version",    no_argument,       NULL, 'v'},
    	{"infile", no_argument,       NULL, 'i'},
		{"rtmpserver", no_argument,       NULL, 's'} 
	};
	extern char* optarg;
	int opt = 0; 
	//后面带冒号的说明我们要加参数
	//如  ./rtmpdump -i in.flv -s rtmp://localhost:1935/hls
	//h和v没有冒号,可以后面不加参数,如 ./rtmp h   
    while ((opt = getopt_long(argc, argv, "i:s:hv", opts, NULL)) != -1) {
		//printf("opts:%s\n",optarg);
        switch (opt) {
        case 'i': 
            infile = strdup(optarg);
			printf("infile:%s\n",infile);
            break;
		case 's':
			rtmpserverpath = strdup(optarg);
			printf("rtmpserverpath:%s\n",rtmpserverpath);
			break;
        case 'h': 
            showuse();
            return 0;
        case 'v': 
			RTMP_LogPrintf("version 1.0 by hxk\n");
            return 0;
        default:
            showuse();
            return -1;
        }
    }
	push_packet(infile,rtmpserverpath);
	return 0;
}

编译完后我们可以运行一下,

./rtmpdump -i in.flv -s rtmp://localhost:1935/hls

可以看到我们在推流了。然后用ffplay来播放一下,发现成功了。

image

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于librtmp的推流实现 的相关文章

随机推荐

  • 【matlab】利用matlab在图形中绘制箭头、标注、圈圈 - 很帅很酷炫

    转载声明 xff1a 感谢 xff1a MyBear 尊重原作者劳动 xff1a http www 360doc com content 14 0527 21 1054746 381542462 shtml 一 二维箭头 1 xff0e 调
  • 【杂谈】甘于平凡?还是思索求生?

    前言 不觉然 xff0c 已19年 xff0c 不知不觉 xff0c 求学生涯至此告一段落 有感觉 xff0c 岁月的痕迹开始发酵 xff0c 身体抑或精神 xff0c 今不如往 思考下 xff0c 互联网浪潮之下 xff0c 之后 xff
  • 匈牙利算法-看这篇绝对就够了!

    本文讲述的是匈牙利算法 xff0c 即图论中寻找最大匹配的算法 xff0c 暂不考虑加权的最大匹配 xff08 用KM算法实现 xff09 xff0c 文章整体结构如下 xff1a 基础概念介绍 算法的实现 好的 xff0c 开始 xff0
  • 面试的一般流程及其常见的问题

    又是一年毕业季 xff0c 也要踏上求职之路 xff0c 在这段时间也关注很多求职方面的消息 下面是一些面试的一般流程及其常见的问题 xff1a 面试职位 xff1a XXXX 开始语 xff1a 你好 xff0c 首先祝贺你通过了前几个环
  • 构建库函数(STM32)

    一 定义外设的各基地址 xff0c 参考存储器映射 span class token comment 由存储器的映射可知 xff0c 片上外设基地址0x4000 0000 span span class token macro proper
  • PID控制器原理概述

    PID控制 PID概述 xff1a 控制框图 xff1a 增量式PID和位置式PID特点 xff1a PID控制参数整定口诀 xff1a 注 xff1a 本文部分内容摘自 先进PID控制MATLAB仿真 xff08 第4版 xff09 刘金
  • PyQt5 事件处理机制

    PyQt5 事件处理机制 PyQt为事件处理提供了两种机制 xff1a 高级的信号与槽机制 xff0c 以及低级的事件处理机制 信号与槽可以说是对事件处理机制的高级封装 常见事件类型 xff1a 键盘事件 xff1a 按键按下和松开 鼠标事
  • PyQt5 实现串口接数据波形显示工具

    PyQt5 实现串口接数据波形显示工具 工具简述主程序代码Qt Designer设计UI界面程序运行效果 工具简述 基于PyQt5开发UI界面使用QtDesigner设计 xff0c 需要使用到serial模块 xff08 串口库 xff0
  • ROS CMakeLists.txt的编写学习

    调用ROS中的函数 xff0c cmakelists的编写学习过程 如有错误 xff0c 请留言指教 多谢 A 首先要了解的 CMakeLists txt是CMake的构建系统构建软件包的输入文件 任何兼容的CMake都包含了描述如何构建代
  • 【Node】Buffer 与 Stream

    node 为什么会出现 Buffer 这个模块 在最初的时候 xff0c JavaScript 只运行在浏览器端 xff0c 对于处理 Unicode 编码的字符串很容易 xff0c 但是对于处理二进制以及非 Unicode 编码的数据便无
  • ROS的tf包中坐标变换的方法

    1 setRotation函数的参数 在坐标变换的时候常有这样的写法 xff1a tfTutorialsAdding a frame C 43 43 transform setOrigin span class hljs symbol tf
  • 卡尔曼滤波的理解以及参数调整

    一 前言 卡尔曼滤波器是一种最优线性状态估计方法 xff08 等价于 在最小均方误差准则下的最佳线性滤波器 xff09 xff0c 所谓状态估计就是通过数学方法寻求与观测数据最佳拟合的状态向量 在移动机器人导航方面 xff0c 卡尔曼滤波是
  • ROS自定义msg类型及使用

    一 创建msg消息 参考 xff1a CreatingMsgAndSrv 首先创建一个空的package单独存放msg类型 xff08 当然也可以在任意的package中自定义msg类型 xff09 这里为便于说明 xff0c 建立一个名为
  • WebRTC-集成qsv硬解码实现

    1 Window下QSV硬解码配置 在libavcodec codec list c下添加 amp ff h264 qsv decoder 在ffmpeg generate gni下加入 34 libavcodec h264idct c 3
  • [ROS] ROS基础,创建工作空间,创建功能包,ros功能包相关命令,Catkin编译系统,catkin_make的编译方式

    1 工作空间 工作空间 xff08 work space xff09 是ROS系统中存放工程开发相关的文件夹 xff0c 其目录结构如下 xff1a src xff1a 代码空间 xff08 Source Space xff09 xff0c
  • ijkplayer-添加播放截图功能

    应用播放的时候需要截图 xff0c 可以在上层使用TexturView来使用截图 xff0c 不过太具有局限性呢 xff0c 还是在底层处理比较好 那么先分析下可以在哪里加截图呢 xff1f 看到网上很多做的都不能支持硬解截图 xff0c
  • avformat_seek_file及其flag含义

    我们从ijk中seek的处理流程来看ffmpeg的这个问题 int ffp seek to l FFPlayer ffp long msec assert ffp VideoState is 61 ffp gt is int64 t sta
  • 单例模式

    单例模式 xff1a include lt iostream gt using namespace std class Singleton public Singleton cout lt lt 34 Singleton虚构函数 34 lt
  • ffmpeg系列-解决ffmpeg获取aac音频文件duration不准

    这个问题是这样产生的 xff0c 一同事反应会随机出现ijk获取到的aac文件的duration不准 xff0c 发来一看 xff0c 确实不准 xff0c 在AE或者系统mediaplayer中得到的都是8 4秒 xff08 准确时间是M
  • 基于librtmp的推流实现

    1 推流 配置好rtmpdump库后 xff0c 我们可以先用命令行来推流看下效果 2 流程图 使用librtmp发布RTMP流的可以使用两种API xff1a RTMP SendPacket 和RTMP Write 使用RTMP Send