Linux下librtmp使用及编程实战
最近想做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=**;
buf=new char[bufSize];
countSize=;
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 -;
}
InitSockets();
RTMP_Init(rtmp);
//set connection timeout,default 30s
rtmp->Link.timeout=;
if (!RTMP_SetupURL(rtmp,const_cast<char*>(rtmpUrl.c_str()))) {
RTMP_Log(RTMP_LOGERROR, "SetupURL Err\n");
RTMP_Free(rtmp);
return -;
}
if (b_live_stream) {
rtmp->Link.lFlags |= RTMP_LF_LIVE;
} //1hour
RTMP_SetBufferMS(rtmp, * ); if (!RTMP_Connect(rtmp, NULL)) {
RTMP_Log(RTMP_LOGERROR, "Connect Err\n");
RTMP_Free(rtmp);
return -;
} if (!RTMP_ConnectStream(rtmp, )) {
RTMP_Log(RTMP_LOGERROR, "ConnectStream Err\n");
RTMP_Free(rtmp);
RTMP_Close(rtmp);
return -;
}
}
void RTMPRec::run(){
worker();
}
void RTMPRec::worker(){
int nread;
while ((nread = RTMP_Read(rtmp, buf, bufSize)) != ) {
fwrite(buf, , (size_t)nread, fp);
memset(buf,,bufSize);
countSize += nread;
RTMP_LogPrintf("Receive: %5dByte, Total: %5.2fkB\n", nread, countSize * 1.0 / );
}
}
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 = ;
now_time = ; pre_frame_time = ;
lasttime = ;
b_next_is_key = ;
pre_tag_size = ; type = ;
datalength = ;
timestamp = ; 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 -;
}
InitSockets();
RTMP_Init(rtmp);
//set connection timeout,default 30s
rtmp->Link.timeout = ;
if (!RTMP_SetupURL(rtmp, const_cast<char*>(rtmpUrl.c_str()))) {
RTMP_Log(RTMP_LOGERROR, "SetupURL Err\n");
//RTMP_Free(rtmp);
return -;
}
RTMP_EnableWrite(rtmp);
if (!RTMP_Connect(rtmp, NULL)) {
RTMP_Log(RTMP_LOGERROR, "Connect Err\n");
//RTMP_Free(rtmp);
return -;
} if (!RTMP_ConnectStream(rtmp, )) {
RTMP_Log(RTMP_LOGERROR, "ConnectStream Err\n");
//RTMP_Close(rtmp);
//RTMP_Free(rtmp);
return -;
} //jump over FLV Header
fseek(fp, , SEEK_SET);
//jump over previousTagSizen
fseek(fp, , SEEK_CUR);
return ;
} void RTMPPushFlv::run(){
worker();
}
void RTMPPushFlv::worker(){
log_info("Start to send data ...");
start_time = RTMP_GetTime();
while () {
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();
continue;
} //jump over type
fseek(fp, , SEEK_CUR);
if (!ReadU24(&datalength, fp)) {
break;
}
if (!ReadTime(×tamp, fp)) {
break;
}
//jump back
fseek(fp, -, SEEK_CUR); p_file_buf = (char *) malloc( + datalength + );
memset(p_file_buf, , + datalength + );
if (fread(p_file_buf, , + datalength + , fp) != ( + datalength + )) {
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, + datalength + )) {
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, , SEEK_CUR) != ) {
break;
}
if (!PeekU8(&type, fp)) {
break;
}
if (type == 0x17) {
b_next_is_key = ;
} else {
b_next_is_key = ;
}
fseek(fp, -, 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=;
static void sig_user(int signo){
if(signo==SIGINT){
runflag=;
printf("received SIGINT\n");
}
} void pushYUVByH264(){ char url[]="rtmp://localhost/live/test1";
int width=;
int height=;
int outSize=;
int baseFrameSize=width*height;
const long bufferSize=baseFrameSize*;
char buffer[bufferSize]; int fps=;
int rate=; 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,,RTMP_LOGINFO);
printf("connected \n");
RTMP_InitVideoParams(width,height,fps,rate,src_pix_fmt,false);
printf("inited \n");
runflag=;
unsigned int tick = ;
unsigned int tick_gap = /fps;
uint32_t now=,last_update=;
int index=;
while(runflag){
if(index!=){
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, , baseFrameSize*/, fp) != baseFrameSize*/){
// Loop
fseek(fp, , SEEK_SET);
fread(buffer, , baseFrameSize*/, fp);
//fclose(fp);
//break;
}
printf("read file \n");
break;
case AV_PIX_FMT_YUV422P:
if (fread(buffer, , baseFrameSize*, fp) != baseFrameSize*){
// Loop
fseek(fp, , SEEK_SET);
fread(buffer, , baseFrameSize*, fp);
//fclose(fp);
//break;
}
break;
case AV_PIX_FMT_RGB24:
if (fread(buffer, , baseFrameSize*, fp) != baseFrameSize*){
// Loop
fseek(fp, , SEEK_SET);
fread(buffer, , baseFrameSize*, 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)*);
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
Linux下librtmp使用及编程实战的更多相关文章
- Linux 下Input系统应用编程实战
作者:杨源鑫(也是我们的校园代理) 经授权转载于公众号嵌入式开发圈,有些许修改. 什么是input子系统?不管是什么操作系统,都有一个程序用于管理各种输入设备,哪些是输入设备?比如,电脑键盘.鼠标,智 ...
- Linux下的C Socket编程 -- server端的继续研究
Linux下的C Socket编程(四) 延长server的生命周期 在前面的一个个例子中,server在处理完一个连接后便会立即结束掉自己,然而这种server并不科学啊,server应该是能够一直 ...
- Linux下的C Socket编程 -- server端的简单示例
Linux下的C Socket编程(三) server端的简单示例 经过前面的client端的学习,我们已经知道了如何创建socket,所以接下来就是去绑定他到具体的一个端口上面去. 绑定socket ...
- Linux下的C Socket编程 -- 获取对方IP地址
Linux下的C Socket编程(二) 获取域名对应的IP地址 经过上面的讨论,如果我们想要连接到远程的服务器,我们需要知道对方的IP地址,系统函数gethostbyname便能够实现这个目的.它能 ...
- Linux下的C Socket编程 -- 简介与client端的处理
Linux下的C Socket编程(一) 介绍 Socket是进程间通信的方式之一,是进程间的通信.这里说的进程并不一定是在同一台机器上也有可能是通过网络连接的不同机器上.只要他们之间建立起了sock ...
- Linux下高并发网络编程
Linux下高并发网络编程 1.修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时, 最高的并发数量都要受到系统对用户单一进程同时可打 ...
- linux下C语言多线程编程实例
用一个实例.来学习linux下C语言多线程编程实例. 代码目的:通过创建两个线程来实现对一个数的递加.代码: //包含的头文件 #include <pthread.h> #include ...
- 【ARM-Linux开发】linux下Eclipse进行C编程时动态链接库的生成和使用
linux下Eclipse进行C编程时动态链接库的生成和使用 引用 http://linux.chinaitlab.com/soft/864157.html 欢迎进入Linux社区论坛,与200万技术 ...
- Linux下搭建C/C++编程环境
Linux下搭建C/C++编程环境 1.KDevelop下载 wget -O KDevelop.AppImage https://download.kde.org/stable/kdevelop/5. ...
随机推荐
- leetcode--5 Longest Palindromic Substring
1. 题目: Given a string S, find the longest palindromic substring in S. You may assume that the maximu ...
- 访问mongo数据库报错
It looks like you are trying to access MongoDB over HTTP on the native driver port. 出错原因: 1.没有安装mong ...
- UVALive 4731 Cellular Network(贪心,dp)
分析: 状态是一些有序的集合,这些集合互不相交,并集为所有区域.显然枚举集合元素是哪些是无法承受的, 写出期望的计算式,会发现,当每个集合的大小确定了以后,概率大的优先访问是最优的. 因此先对u从大到 ...
- Linux 文件的压缩与解压
1. tar结尾压缩命令 [root@test ~]# tar -cvf grub.tar /boot/grub/ 查看压缩包文件 [root@test ~]# tar -vtf grub.tar ...
- SpringMVC+Hibernate框架快速搭建
1. 新建Maven项目springmvc 2. pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" ...
- Java代码工具箱之链接Oracle
1. 需要oracle的 odbc jar包 2. 代码 3. 注意:ps对象和statement对象最好用完立即释放,尤其是读写数据库代码出现在 for 循环语句中时. 否则会出现游标不够的情况, ...
- java算法面试题:递归算法题1
递归算法题1 一个整数,大于0,不用循环和本地变量,按照n,2n,4n,8n的顺序递增,当值大于5000时,把值按照指定顺序输出来.例:n=1237则输出为:1237,2474,4948,9896,9 ...
- ES6 Reflect使用笔记
Reflect Reflect 对象和Proxy对象一样, 为操作对象提供了新的API. 为什么使用 Reflect的方式来操作对象? 将 Object 对象上一些明显属于内部的方法放到 Reflec ...
- 1025: [SCOI2009]游戏
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 2727 Solved: 1794[Submit][Status][Discuss] Descripti ...
- Java - 网络
要事为先,你如果想要在这个行业发展下去的话,实际上三角形的三个点在支撑着你发展,一个是技术.一个是管理(不是说管理别人,是管理你自己的时间,管理你自己的精力).还有一个就是沟通,注重这三点均衡的发展. ...