要使用RTMP發布FLV資料,首先需要清楚FLV的封裝格式。可以查看:FLV視頻封裝格式詳解 在運行本執行個體之前,需要先建立好RTMP伺服器,並且建立好RTMPdump編譯和啟動並執行環境。 可以參考:
nginx 搭建rtmp流媒體伺服器
linux 編譯安裝TRMPdump(libRTMP)
rtmp的具體操作介面,libRTMP庫已經封裝好了,可以直接調用。
RTMPPushFlv.cpp
/*============================================================================= * FileName: RTMPPushFlv.cpp * Desc: * Author: licaibiao * LastChange: 2017-05-3 * =============================================================================*/ #include "RTMPPushFlv.h"#include "sockInit.h"RTMPPushFlv::RTMPPushFlv(const string url) {// TODO Auto-generated constructor stubrtmpUrl=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 stubif (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 30srtmp->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 Headerfseek(fp, 9, SEEK_SET);//jump over previousTagSizenfseek(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 improvementif (pre_frame_time > lasttime) {RTMP_LogPrintf("TimeStamp:%8lu ms\n", pre_frame_time);lasttime = pre_frame_time;}sleep(1);continue;}//jump over typefseek(fp, 1, SEEK_CUR);if (!ReadU24(&datalength, fp)) {break;}if (!ReadTime(×tamp, fp)) {break;}//jump backfseek(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) { //videoif (fseek(fp, 11, SEEK_CUR) != 0) {break;}if (!PeekU8(&type, fp)) {break;}if (type == 0x17) { //flv key frameb_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();}
注意 if (0x09 == type) 這裡,表示發送的是視頻資料。在視頻資料的第一個位元組裡,儲存的是視頻的資訊。再下面的 (type == 0x17) 是為了尋找key frame (for AVC, a seekable frame) ,主要目的是通過該幀來進行同步。
在將資料推送到伺服器之前,應該先啟動用戶端請求,這樣就不會丟失FLV的一些資訊。
在流媒體播放器中,請求RTMP流資料:rtmp://192.168.0.5:1935/live 然後再運行上面程式,效果如下:
完整工程:RTMPdump(libRTMP) 通過RTMP 發布FLV資料