要使用RTMP发布FLV数据,首先需要清楚FLV的封装格式。可以查看:FLV视频封装格式详解 在运行本实例之前,需要先建立好RTMP服务器,并且建立好RTMPdump编译和运行的环境。 可以参考:
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 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) { //video
if (fseek(fp, 11, SEEK_CUR) != 0) {
break;
}
if (!PeekU8(&type, fp)) {
break;
}
if (type == 0x17) { //flv key frame
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();
}
注意 if (0x09 == type) 这里,表示发送的是视频数据。在视频数据的第一个字节里,存储的是视频的信息。再下面的 (type == 0x17) 是为了查找key frame (for AVC, a seekable frame) ,主要目的是通过该帧来进行同步。
在将数据推送到服务器之前,应该先启动客户端请求,这样就不会丢失FLV的一些信息。
在流媒体播放器中,请求RTMP流数据:rtmp://192.168.0.5:1935/live 然后再运行上面程序,效果如下: