最近想做rtmp的推流、直播的小项目,不想直接使用FFmpeg进行推流,FFmpeg进行推流特别简单,因为它已经将编码以及librtmp都集成好了,没啥意思。FFmpeg推流的例子,在雷神的博客里可以找到。这里主要是基于librmtp,结合libx264进行压缩,进行一些实验,包括三大部分:
- rtmp流保存
- flv文件推流
- h264推流
首先是rtmp流保存
/* * RTMPRec.cpp * * Created on: Jan 11, 2017 * Author: tla001 */ #include "RTMPRec.h" #include "sockInit.h" RTMPRec::RTMPRec(const string url,const string filename) { // TODO Auto-generated constructor stub rtmpUrl=url; outFile=filename; bufSize=1024*1024*10; buf=new char[bufSize]; countSize=0; b_live_stream=true; rtmp=RTMP_Alloc(); } RTMPRec::~RTMPRec() { // TODO Auto-generated destructor stub if (fp != NULL) { fclose(fp); fp = NULL; } if (buf != NULL) { delete[] buf; buf = NULL; } CleanupSockets(); if (rtmp != NULL) { RTMP_Close(rtmp); RTMP_Free(rtmp); rtmp = NULL; } } int RTMPRec::init(){ fp=fopen(outFile.c_str(),"wb"); if(NULL==fp){ RTMP_LogPrintf("Open File Error.\n"); return -1; } InitSockets(); RTMP_Init(rtmp); //set connection timeout,default 30s rtmp->Link.timeout=10; if (!RTMP_SetupURL(rtmp,const_cast<char*>(rtmpUrl.c_str()))) { RTMP_Log(RTMP_LOGERROR, "SetupURL Err\n"); RTMP_Free(rtmp); return -1; } if (b_live_stream) { rtmp->Link.lFlags |= RTMP_LF_LIVE; } //1hour RTMP_SetBufferMS(rtmp, 3600 * 1000); if (!RTMP_Connect(rtmp, NULL)) { RTMP_Log(RTMP_LOGERROR, "Connect Err\n"); RTMP_Free(rtmp); return -1; } if (!RTMP_ConnectStream(rtmp, 0)) { RTMP_Log(RTMP_LOGERROR, "ConnectStream Err\n"); RTMP_Free(rtmp); RTMP_Close(rtmp); return -1; } } void RTMPRec::run(){ worker(); } void RTMPRec::worker(){ int nread; while ((nread = RTMP_Read(rtmp, buf, bufSize)) != 0) { fwrite(buf, 1, (size_t)nread, fp); memset(buf,0,bufSize); countSize += nread; RTMP_LogPrintf("Receive: %5dByte, Total: %5.2fkB\n", nread, countSize * 1.0 / 1024); } } void RTMPRec::doSave(){ this->start(); }
推送flv文件
主要指根据flv文件储存结构进行读取与解析的
/* * RTMPPushFlv.cpp * * Created on: Jan 11, 2017 * Author: tla001 */ #include "RTMPPushFlv.h" #include "sockInit.h" RTMPPushFlv::RTMPPushFlv(const string url) { // TODO Auto-generated constructor stub rtmpUrl=url; fp=NULL; start_time = 0; now_time = 0; pre_frame_time = 0; lasttime = 0; b_next_is_key = 1; pre_tag_size = 0; type = 0; datalength = 0; timestamp = 0; rtmp = RTMP_Alloc(); } RTMPPushFlv::~RTMPPushFlv() { // TODO Auto-generated destructor stub if (fp != NULL) { fclose(fp); fp = NULL; } CleanupSockets(); if (rtmp != NULL) { RTMP_Close(rtmp); RTMP_Free(rtmp); rtmp = NULL; } if (p_file_buf != NULL) { free(p_file_buf); p_file_buf = NULL; } } int RTMPPushFlv::init(const string filename){ inFile=filename; fp = fopen(inFile.c_str(), "rb"); if (NULL == fp) { log_err("Open File Error"); return -1; } InitSockets(); RTMP_Init(rtmp); //set connection timeout,default 30s rtmp->Link.timeout = 5; if (!RTMP_SetupURL(rtmp, const_cast<char*>(rtmpUrl.c_str()))) { RTMP_Log(RTMP_LOGERROR, "SetupURL Err\n"); //RTMP_Free(rtmp); return -1; } RTMP_EnableWrite(rtmp); if (!RTMP_Connect(rtmp, NULL)) { RTMP_Log(RTMP_LOGERROR, "Connect Err\n"); //RTMP_Free(rtmp); return -1; } if (!RTMP_ConnectStream(rtmp, 0)) { RTMP_Log(RTMP_LOGERROR, "ConnectStream Err\n"); //RTMP_Close(rtmp); //RTMP_Free(rtmp); return -1; } //jump over FLV Header fseek(fp, 9, SEEK_SET); //jump over previousTagSizen fseek(fp, 4, SEEK_CUR); return 0; } void RTMPPushFlv::run(){ worker(); } void RTMPPushFlv::worker(){ log_info("Start to send data ..."); start_time = RTMP_GetTime(); while (1) { if ((((now_time = RTMP_GetTime()) - start_time) < (pre_frame_time)) && b_next_is_key) { //wait for 1 sec if the send process is too fast //this mechanism is not very good,need some improvement if (pre_frame_time > lasttime) { RTMP_LogPrintf("TimeStamp:%8lu ms\n", pre_frame_time); lasttime = pre_frame_time; } sleep(1); continue; } //jump over type fseek(fp, 1, SEEK_CUR); if (!ReadU24(&datalength, fp)) { break; } if (!ReadTime(×tamp, fp)) { break; } //jump back fseek(fp, -8, SEEK_CUR); p_file_buf = (char *) malloc(11 + datalength + 4); memset(p_file_buf, 0, 11 + datalength + 4); if (fread(p_file_buf, 1, 11 + datalength + 4, fp) != (11 + datalength + 4)) { break; } pre_frame_time = timestamp; if (!RTMP_IsConnected(rtmp)) { RTMP_Log(RTMP_LOGERROR, "rtmp is not connect\n"); break; } if (!RTMP_Write(rtmp, p_file_buf, 11 + datalength + 4)) { RTMP_Log(RTMP_LOGERROR, "Rtmp Write Error\n"); break; } free(p_file_buf); p_file_buf = NULL; if (!PeekU8(&type, fp)) { break; } if (0x09 == type) { if (fseek(fp, 11, SEEK_CUR) != 0) { break; } if (!PeekU8(&type, fp)) { break; } if (type == 0x17) { b_next_is_key = 1; } else { b_next_is_key = 0; } fseek(fp, -11, SEEK_CUR); } } log_info("Send Data Over"); } void RTMPPushFlv::doPush(){ this->start(); }
h264数据推流
包括yuv数据的读入,h264编码,librtmp分包传输
这里对参考的库进行了必要的修改,可以实现设置参数后,不同数据格式可以自动转换为420p。
/* * RTMPPushH264.cpp * * Created on: Jan 12, 2017 * Author: tla001 */ #include "RTMPPushH264.h" #include "librtmp/log.h" int runflag=0; static void sig_user(int signo){ if(signo==SIGINT){ runflag=0; printf("received SIGINT\n"); } } void pushYUVByH264(){ char url[]="rtmp://localhost/live/test1"; int width=640; int height=360; int outSize=1024; int baseFrameSize=width*height; const long bufferSize=baseFrameSize*3; char buffer[bufferSize]; int fps=25; int rate=400; char *frame=NULL; if(signal(SIGINT,sig_user)==SIG_ERR) perror("catch SIGINT err"); FILE* fp = fopen("test_640x360_yuv420p.yuv", "rb"); enum AVPixelFormat src_pix_fmt=AV_PIX_FMT_YUV420P; RTMP_CreatePublish(url,outSize,1,RTMP_LOGINFO); printf("connected \n"); RTMP_InitVideoParams(width,height,fps,rate,src_pix_fmt,false); printf("inited \n"); runflag=1; unsigned int tick = 0; unsigned int tick_gap = 1000/fps; uint32_t now=0,last_update=0; int index=0; while(runflag){ if(index!=0){ RTMP_SendScreenCapture((char*)buffer,height,tick); printf("send frame index -- %d\n",index); } last_update=RTMP_GetTime(); switch(src_pix_fmt){ case AV_PIX_FMT_YUV420P: if (fread(buffer, 1, baseFrameSize*3/2, fp) != baseFrameSize*3/2){ // Loop fseek(fp, 0, SEEK_SET); fread(buffer, 1, baseFrameSize*3/2, fp); //fclose(fp); //break; } printf("read file \n"); break; case AV_PIX_FMT_YUV422P: if (fread(buffer, 1, baseFrameSize*2, fp) != baseFrameSize*2){ // Loop fseek(fp, 0, SEEK_SET); fread(buffer, 1, baseFrameSize*2, fp); //fclose(fp); //break; } break; case AV_PIX_FMT_RGB24: if (fread(buffer, 1, baseFrameSize*3, fp) != baseFrameSize*3){ // Loop fseek(fp, 0, SEEK_SET); fread(buffer, 1, baseFrameSize*3, fp); //fclose(fp); //break; } break; default: printf("Not supports this format \n"); break; } tick +=tick_gap; now=RTMP_GetTime(); usleep((tick_gap-now+last_update)*1000); index++; } RTMP_DeletePublish(); fclose(fp); }
完整工程
https://github.com/tla001/RTMPApp
相关链接
rmtp服务器配置
http://www.cnblogs.com/tla001/p/6263215.html
h264 rtmp封装
https://github.com/njk888/LibRtmpH264
librtmp学习
http://blog.csdn.net/leixiaohua1020/article/details/15814587