基于librtmp的推流实现
生活随笔
收集整理的這篇文章主要介紹了
基于librtmp的推流实现
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
1.推流
配置好rtmpdump庫后,我們可以先用命令行來推流看下效果
2.流程圖
使用librtmp發(fā)布RTMP流的可以使用兩種API:RTMP_SendPacket()和RTMP_Write()。
使用RTMP_SendPacket()發(fā)布流的時候的函數(shù)執(zhí)行流程圖如下圖所示。
流程圖中關(guān)鍵函數(shù)的作用如下所列:
- InitSockets():初始化Socket
- RTMP_Alloc():為結(jié)構(gòu)體“RTMP”分配內(nèi)存。
- RTMP_Init():初始化結(jié)構(gòu)體“RTMP”中的成員變量。
- RTMP_SetupURL():設(shè)置輸入的RTMP連接的URL。
- RTMP_EnableWrite():發(fā)布流的時候必須要使用。如果不使用則代表接收流。
- RTMP_Connect():建立RTMP連接,創(chuàng)建一個RTMP協(xié)議規(guī)范中的NetConnection。
- RTMP_ConnectStream():創(chuàng)建一個RTMP協(xié)議規(guī)范中的NetStream。
- Delay:發(fā)布流過程中的延時,保證按正常播放速度發(fā)送數(shù)據(jù)。
- RTMP_SendPacket():發(fā)送一個RTMP數(shù)據(jù)RTMPPacket。
- RTMP_Close():關(guān)閉RTMP連接。
- RTMP_Free():釋放結(jié)構(gòu)體“RTMP”。
- CleanupSockets():關(guān)閉Socket。
下面將本地FLV文件發(fā)布到RTMP流媒體服務(wù)器。這個只是簡單的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;//下一幀是否是關(guān)鍵幀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;}//初始化Socketif(!InitSockets()){RTMP_LogPrintf("Init Socket Err\n");return -1;}//為結(jié)構(gòu)體“RTMP”分配內(nèi)存rtmp = RTMP_Alloc();if(!rtmp){RTMP_LogPrintf("RTMP_Alloc failed\n");return -1;}//初始化結(jié)構(gòu)體“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;}//發(fā)布流的時候必須要使用。如果不使用則代表接收流。RTMP_EnableWrite(rtmp);//建立RTMP連接,創(chuàng)建一個RTMP協(xié)議規(guī)范中的NetConnectionif (!RTMP_Connect(rtmp,NULL)){RTMP_Log(RTMP_LOGERROR,"Connect Err\n");RTMP_Free(rtmp);CleanupSockets();return -1;}//創(chuàng)建一個RTMP協(xié)議規(guī)范中的NetStreamif (!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個頭字節(jié)fseek(fp,9,SEEK_SET); //跳過previousTagSize所占的4個字節(jié)fseek(fp,4,SEEK_CUR); start_time=RTMP_GetTime();while(1){//如果下一幀是關(guān)鍵幀,且上一幀的時間戳比現(xiàn)在的時間還長(外部時鐘),說明推流速度過快了,可以延時下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 specif(!ReadU8(&type,fp))//讀取type break;if(!ReadU24(&datalength,fp))//讀取datalength的長度break;if(!ReadTime(×tamp,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 timefseek(fp,datalength+4,SEEK_CUR);//跳過腳本以及腳本后的previousTagSizecontinue;} //把flv的音頻和視頻數(shù)據(jù)寫入到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");//發(fā)送一個RTMP數(shù)據(jù)RTMPPacketif (!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)//如果視頻幀是關(guān)鍵幀bNextIsKey=1;elsebNextIsKey=0;fseek(fp,-11,SEEK_CUR);}} RTMP_LogPrintf("\nSend Data Over!\n");if(fp) {fclose(fp);}if (rtmp!=NULL){//關(guān)閉RTMP連接RTMP_Close(rtmp); //釋放結(jié)構(gòu)體RTMPRTMP_Free(rtmp); rtmp=NULL;}if (packet!=NULL){RTMPPacket_Free(packet); free(packet);packet=NULL;}//關(guān)閉SocketCleanupSockets();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) {//設(shè)置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; //后面帶冒號的說明我們要加參數(shù)//如 ./rtmpdump -i in.flv -s rtmp://localhost:1935/hls//h和v沒有冒號,可以后面不加參數(shù),如 ./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來播放一下,發(fā)現(xiàn)成功了。
總結(jié)
以上是生活随笔為你收集整理的基于librtmp的推流实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Springboot整合阿里云短信SDK
- 下一篇: 计算机视觉中的对象跟踪(完整指南)