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. ...
随机推荐
- (转载)office 2003 gaozhi.msi 缺失提示问题修复
某些GHOST版win7,自带office 2003,每次启动word,它都会提示"稿纸没安装"云云,找不到那个文件.可是我搜遍了硬盘,确实没有那个文件.每次都要点取消,这个提示才 ...
- 从asp.net到jsp:3分钟看透Jsp&Servlet
零:JSP是谁? 话说故事的开头是这样的:JSP全名为Java Server Pages,其根本是一个简化的Servlet设计:后来的详细事情有请各位自便.美女→找→谷哥 or 帅哥→找→度娘 插播: ...
- PHP:is_string()字符串函数
is_string() is_string() - 检测变量是否是字符串. 描述:bool is_string( mixed $var ) 如果var是sring则返回TRUE,否则返回FALSE.m ...
- java: 非法字符: \65279
IDEA导入项目后,编译的时候出现Error:(1, 1) java: 非法字符: \65279: 修改:找到编译报错的文件,用Notepad++工具,以UTF-8无BOM格式编码保存,然后重新编译即 ...
- Mysql--select基础查询
基本语法:select 查询列表 from 表名 查询列表可以是表中字段.常量值.表达式.函数:查询的结果是一个虚拟的表格. 注意: ①sql语言大小写不敏感 ②关键字不能分行或略写 ③一般书写方式为 ...
- MySQL的备份与恢复理解与备份策略
MySQL的备份主要分为逻辑备份和物理备份 逻辑备份 在MySQL中逻辑备份的最大优点是对各种存储引擎都可以用同样的方法来备份.而物理备份则不同,不同的存储引擎有着不同的备份方法.Mysql中的逻辑备 ...
- 项目实战14.1—ELK 企业内部日志分析系统
本文收录在Linux运维企业架构实战系列 一.els.elk 的介绍 1.els,elk els:ElasticSearch,Logstash,Kibana,Beats elk:ElasticSear ...
- 三十三、MySQL 导入数据
MySQL 导入数据 本章节我们为大家介绍几种简单的 MySQL 导出的数据的命令. 1.mysql 命令导入 使用 mysql 命令导入语法格式为: mysql -u用户名 -p密码 < 要导 ...
- ubuntu18.04 and Linux mint 19安装virtualbox
1.1 安装Virtualbox root@amarsoft-ZHAOYANG-K43c-:~# apt-get install virtualbox -y 1.2 显示Virtualbox桌面图 ...
- API Star:一个 Python 3 的 API 框架
为了在 Python 中快速构建 API,我主要依赖于 Flask.最近我遇到了一个名为 "API Star" 的基于 Python 3 的新 API 框架.由于几个原因,我对它很 ...