您的位置:首页 > 其它

最简单的基于librtmp的示例:发布(FLV通过RTMP发布)

2015-12-04 10:18 796 查看
=====================================================

最简单的基于libRTMP的示例系列文章列表:

最简单的基于librtmp的示例:接收(RTMP保存为FLV)

最简单的基于librtmp的示例:发布(FLV通过RTMP发布)

最简单的基于librtmp的示例:发布H.264(H.264通过RTMP发布)

=====================================================

本文记录一个基于libRTMP的发布流媒体的程序:Simplest libRTMP Send FLV。该程序可以将本地FLV文件发布到RTMP流媒体服务器。是最简单的基于libRTMP的流媒体发布示例。



流程图

使用librtmp发布RTMP流的可以使用两种API:RTMP_SendPacket()和RTMP_Write()。使用RTMP_SendPacket()发布流的时候的函数执行流程图如下图所示。使用RTMP_Write()发布流的时候的函数执行流程图相差不大。



流程图中关键函数的作用如下所列:
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。

源代码

源代码中包含了使用两种API函数RTMP_SendPacket()和RTMP_Write()发布流媒体的源代码,如下所示。

[cpp] view
plaincopy

/**

* Simplest Librtmp Send FLV

*

* 雷霄骅,张晖

* leixiaohua1020@126.com

* zhanghuicuc@gmail.com

* 中国传媒大学/数字电视技术

* Communication University of China / Digital TV Technology

* http://blog.csdn.net/leixiaohua1020
*

* 本程序用于将FLV格式的视音频文件使用RTMP推送至RTMP流媒体服务器。

* This program can send local flv file to net server as a rtmp live stream.

*/

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <stdint.h>

#ifndef WIN32

#include <unistd.h>

#endif

#include "librtmp/rtmp_sys.h"

#include "librtmp/log.h"

#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 InitSockets()

{

WORD version;

WSADATA wsaData;

version=MAKEWORD(2,2);

return (WSAStartup(version, &wsaData) == 0);

}

void CleanupSockets()

{

WSACleanup();

}

//Publish using RTMP_SendPacket()

int publish_using_packet(){

RTMP *rtmp=NULL;

RTMPPacket *packet=NULL;

uint32_t start_time=0;

uint32_t now_time=0;

//the timestamp of the previous frame

long pre_frame_time=0;

long lasttime=0;

int bNextIsKey=1;

uint32_t preTagsize=0;

//packet attributes

uint32_t type=0;

uint32_t datalength=0;

uint32_t timestamp=0;

uint32_t streamid=0;

FILE*fp=NULL;

fp=fopen("cuc_ieschool.flv","rb");

if (!fp){

RTMP_LogPrintf("Open File Error.\n");

CleanupSockets();

return -1;

}

/* set log level */

//RTMP_LogLevel loglvl=RTMP_LOGDEBUG;

//RTMP_LogSetLevel(loglvl);

if (!InitSockets()){

RTMP_LogPrintf("Init Socket Err\n");

return -1;

}

rtmp=RTMP_Alloc();

RTMP_Init(rtmp);

//set connection timeout,default 30s

rtmp->Link.timeout=5;

if(!RTMP_SetupURL(rtmp,"rtmp://localhost/publishlive/livestream"))

{

RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n");

RTMP_Free(rtmp);

CleanupSockets();

return -1;

}

//if unable,the AMF command would be 'play' instead of 'publish'

RTMP_EnableWrite(rtmp);

if (!RTMP_Connect(rtmp,NULL)){

RTMP_Log(RTMP_LOGERROR,"Connect Err\n");

RTMP_Free(rtmp);

CleanupSockets();

return -1;

}

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");

//jump over FLV Header

fseek(fp,9,SEEK_SET);

//jump over previousTagSizen

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

//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(1000);

continue;

}

//not quite the same as FLV spec

if(!ReadU8(&type,fp))

break;

if(!ReadU24(&datalength,fp))

break;

if(!ReadTime(×tamp,fp))

break;

if(!ReadU24(&streamid,fp))

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);

continue;

}

if(fread(packet->m_body,1,datalength,fp)!=datalength)

break;

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;

}

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_Close(rtmp);

RTMP_Free(rtmp);

rtmp=NULL;

}

if (packet!=NULL){

RTMPPacket_Free(packet);

free(packet);

packet=NULL;

}

CleanupSockets();

return 0;

}

//Publish using RTMP_Write()

int publish_using_write(){

uint32_t start_time=0;

uint32_t now_time=0;

uint32_t pre_frame_time=0;

uint32_t lasttime=0;

int bNextIsKey=0;

char* pFileBuf=NULL;

//read from tag header

uint32_t type=0;

uint32_t datalength=0;

uint32_t timestamp=0;

RTMP *rtmp=NULL;

FILE*fp=NULL;

fp=fopen("cuc_ieschool.flv","rb");

if (!fp){

RTMP_LogPrintf("Open File Error.\n");

CleanupSockets();

return -1;

}

/* set log level */

//RTMP_LogLevel loglvl=RTMP_LOGDEBUG;

//RTMP_LogSetLevel(loglvl);

if (!InitSockets()){

RTMP_LogPrintf("Init Socket Err\n");

return -1;

}

rtmp=RTMP_Alloc();

RTMP_Init(rtmp);

//set connection timeout,default 30s

rtmp->Link.timeout=5;

if(!RTMP_SetupURL(rtmp,"rtmp://localhost/publishlive/livestream"))

{

RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n");

RTMP_Free(rtmp);

CleanupSockets();

return -1;

}

RTMP_EnableWrite(rtmp);

//1hour

RTMP_SetBufferMS(rtmp, 3600*1000);

if (!RTMP_Connect(rtmp,NULL)){

RTMP_Log(RTMP_LOGERROR,"Connect Err\n");

RTMP_Free(rtmp);

CleanupSockets();

return -1;

}

if (!RTMP_ConnectStream(rtmp,0)){

RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n");

RTMP_Close(rtmp);

RTMP_Free(rtmp);

CleanupSockets();

return -1;

}

printf("Start to send data ...\n");

//jump over FLV Header

fseek(fp,9,SEEK_SET);

//jump over previousTagSizen

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

//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(1000);

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);

pFileBuf=(char*)malloc(11+datalength+4);

memset(pFileBuf,0,11+datalength+4);

if(fread(pFileBuf,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,pFileBuf,11+datalength+4)){

RTMP_Log(RTMP_LOGERROR,"Rtmp Write Error\n");

break;

}

free(pFileBuf);

pFileBuf=NULL;

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_Close(rtmp);

RTMP_Free(rtmp);

rtmp=NULL;

}

if(pFileBuf){

free(pFileBuf);

pFileBuf=NULL;

}

CleanupSockets();

return 0;

}

int main(int argc, char* argv[]){

//2 Methods:

publish_using_packet();

//publish_using_write();

return 0;

}

运行结果

程序运行后,会将“cuc_ieschool.flv”文件以直播流的形式发布到“rtmp://localhost/publishlive/livestream”的URL。修改文件名称和RTMP的URL可以实现将任意flv文件发布到任意RTMP的URL。

下载

Simplest LibRTMP Example

项目主页

SourceForge:https://sourceforge.net/projects/simplestlibrtmpexample/

Github:https://github.com/leixiaohua1020/simplest_librtmp_example

开源中国:http://git.oschina.net/leixiaohua1020/simplest_librtmp_example

CSDN下载:http://download.csdn.net/detail/leixiaohua1020/8291757

本工程包含了LibRTMP的使用示例,包含如下子工程:

simplest_librtmp_receive: 接收RTMP流媒体并在本地保存成FLV格式的文件。

simplest_librtmp_send_flv: 将FLV格式的视音频文件使用RTMP推送至RTMP流媒体服务器。

simplest_librtmp_send264: 将内存中的H.264数据推送至RTMP流媒体服务器。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: