最简单的基于librtmp的示例:发布(FLV通过RTMP发布)
=====================================================
最简单的基于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()发布流媒体的源代码,如下所示。
/**
* 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流媒体服务器。
最简单的基于librtmp的示例:发布(FLV通过RTMP发布)的更多相关文章
- 最简单的基于librtmp的示例:接收(RTMP保存为FLV)
===================================================== 最简单的基于libRTMP的示例系列文章列表: 最简单的基于librtmp的示例:接收(RT ...
- 最简单的基于librtmp的示例:发布H.264(H.264通过RTMP发布)
===================================================== 最简单的基于libRTMP的示例系列文章列表: 最简单的基于librtmp的示例:接收(RT ...
- 最简单的基于DirectShow的示例:获取Filter信息
===================================================== 最简单的基于DirectShow的示例文章列表: 最简单的基于DirectShow的示例:视 ...
- 最简单的基于DirectShow的示例:视频播放器自定义版
===================================================== 最简单的基于DirectShow的示例文章列表: 最简单的基于DirectShow的示例:视 ...
- 最简单的基于DirectShow的示例:视频播放器图形界面版
===================================================== 最简单的基于DirectShow的示例文章列表: 最简单的基于DirectShow的示例:视 ...
- 最简单的基于DirectShow的示例:视频播放器
===================================================== 最简单的基于DirectShow的示例文章列表: 最简单的基于DirectShow的示例:视 ...
- 最简单的基于Flash的流媒体示例:RTMP推送和接收(ActionScript)
===================================================== Flash流媒体文章列表: 最简单的基于Flash的流媒体示例:RTMP推送和接收(Acti ...
- 转:最简单的基于 DirectShow 的视频播放器
50行代码实现的一个最简单的基于 DirectShow 的视频播放器 本文介绍一个最简单的基于 DirectShow 的视频播放器.该播放器对于初学者来说是十分有用的,它包含了使用 DirectSho ...
- 最简单的基于Flash的流媒体示例:网页播放器(HTTP,RTMP,HLS)
http://blog.csdn.net/leixiaohua1020/article/details/43936415 ======================================= ...
随机推荐
- 使用webgl(three.js)搭建一个3D建筑,3D消防模拟——第三课
项目背景 消防安全一直是各大都市关注的重要课题,在消防体系中,特别是高楼消防体系中,消防系统整体布控与监控,火情有效准确定位,防火器材定位,人员逃生路径规划,火情预警,消防演习都是特别重要的环节.所以 ...
- Automap sqlalchemy.ext.automap 自动映射数据库表结构
from sqlalchemy.ext.automap import automap_base from sqlalchemy.orm import Session from sqlalchemy i ...
- HttpClient 实现 get,post请求
private String sendPost(Map<String,Object> data, String url) { CloseableHttpClient httpClient ...
- easyui datagrid editor combobox添加空选则清空combobox框
<script type='text/javascript'> var editIndex = undefined; $(function() { $('#tb1').datagrid({ ...
- bash的工作特性及其使用方法
bash的工作特性之命令执行状态返回值和命令展开所涉及的内容及其示例演出 !脚本执行与调试1.绝对路径执行,要求文件有执行权限2.以sh命令执行,不要求文件有执行权限3..加空格或source命令执行 ...
- JSP运行过程 JSP脚本 静态动态包含 jsp指令 jsp内置对象jsp四大作用域 jsp动作元素 EL表达式 JSTL 设计模式 JSP开发模式 EL内置对象
Day38 JSP JSP的运行过程具体如下: (1)客户端发出请求,请求访问JSP文件. (2)JSP容器先将JSP文件转换成一个Java源文件(Java Servlet源程序),在转换过程中,如果 ...
- aways on 配置部署(一)——准备工作
sqlserver的aways on 配置需要经历三个步骤,前面两个步骤是对aways on 配置的一个准备步骤. 经过了一个星期的研究,终于成功的完成了前两个步骤,期间参考了很多的资料和博客,总感觉 ...
- python学习之路前端-jQuery
jQuery简介 JQuery是继prototype之后又一个优秀的Javascript库.它是轻量级的js库 ,它兼容CSS3,还兼容各种浏览器(IE 6.0+, FF1.5+, Safa ...
- 让你的代码量减少3倍!使用kotlin开发Android(四) kotlin bean背后的秘密
上一篇我们介绍了缩短五倍的java bean,不知道你在看的时候有没有一种疑问捏? 本文同步自博主的私人博客wing的地方酒馆 再来回顾一下,两种代码的对比 public class User { p ...
- XCode使用技巧
XCode使用技巧 自动生成get.set方法 @property 用法 #import <Foundation/Foundation.h> @interface People : NSO ...