最近想做rtmp的推流、直播的小项目,不想直接使用FFmpeg进行推流,FFmpeg进行推流特别简单,因为它已经将编码以及librtmp都集成好了,没啥意思。FFmpeg推流的例子,在雷神的博客里可以找到。这里主要是基于librmtp,结合libx264进行压缩,进行一些实验,包括三大部分:

  1. rtmp流保存
  2. flv文件推流
  3. 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(&timestamp, 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使用及编程实战的更多相关文章

  1. Linux 下Input系统应用编程实战

    作者:杨源鑫(也是我们的校园代理) 经授权转载于公众号嵌入式开发圈,有些许修改. 什么是input子系统?不管是什么操作系统,都有一个程序用于管理各种输入设备,哪些是输入设备?比如,电脑键盘.鼠标,智 ...

  2. Linux下的C Socket编程 -- server端的继续研究

    Linux下的C Socket编程(四) 延长server的生命周期 在前面的一个个例子中,server在处理完一个连接后便会立即结束掉自己,然而这种server并不科学啊,server应该是能够一直 ...

  3. Linux下的C Socket编程 -- server端的简单示例

    Linux下的C Socket编程(三) server端的简单示例 经过前面的client端的学习,我们已经知道了如何创建socket,所以接下来就是去绑定他到具体的一个端口上面去. 绑定socket ...

  4. Linux下的C Socket编程 -- 获取对方IP地址

    Linux下的C Socket编程(二) 获取域名对应的IP地址 经过上面的讨论,如果我们想要连接到远程的服务器,我们需要知道对方的IP地址,系统函数gethostbyname便能够实现这个目的.它能 ...

  5. Linux下的C Socket编程 -- 简介与client端的处理

    Linux下的C Socket编程(一) 介绍 Socket是进程间通信的方式之一,是进程间的通信.这里说的进程并不一定是在同一台机器上也有可能是通过网络连接的不同机器上.只要他们之间建立起了sock ...

  6. Linux下高并发网络编程

      Linux下高并发网络编程 1.修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时, 最高的并发数量都要受到系统对用户单一进程同时可打 ...

  7. linux下C语言多线程编程实例

    用一个实例.来学习linux下C语言多线程编程实例. 代码目的:通过创建两个线程来实现对一个数的递加.代码: //包含的头文件 #include <pthread.h> #include ...

  8. 【ARM-Linux开发】linux下Eclipse进行C编程时动态链接库的生成和使用

    linux下Eclipse进行C编程时动态链接库的生成和使用 引用 http://linux.chinaitlab.com/soft/864157.html 欢迎进入Linux社区论坛,与200万技术 ...

  9. Linux下搭建C/C++编程环境

    Linux下搭建C/C++编程环境 1.KDevelop下载 wget -O KDevelop.AppImage https://download.kde.org/stable/kdevelop/5. ...

随机推荐

  1. HDU 1059 Dividing 分配(多重背包,母函数)

    题意: 两个人共同收藏了一些石头,现在要分道扬镳,得分资产了,石头具有不同的收藏价值,分别为1.2.3.4.5.6共6个价钱.问:是否能公平分配? 输入: 每行为一个测试例子,每行包括6个数字,分别对 ...

  2. GridView的 PreRender事件与范例--GridView + CheckBox,点选多列资料(复选删除)

    GridView的 PreRender事件与范例--GridView + CheckBox,点选多列资料(复选删除) 之前有一个范例,相同的结果可以用两种作法来实践 [GridView] 资料系结表达 ...

  3. java基础面试题:java中实现多态的机制是什么?

    靠的是父类或接口的引用指向子类或实现类的对象, 调用的方法是内存中正在运行的那个对象的方法.

  4. GNU Parallel Tutorial

    GNU Parallel Tutorial Prerequisites Input sources A single input source Multiple input sources Linki ...

  5. Date.prototype.Format---对Date的扩展

    // 对Date的扩展,将 Date 转化为指定格式的String // 月(M).日(d).小时(h).分(m).秒(s).季度(q) 可以用 1-2 个占位符, // 年(y)可以用 1-4 个占 ...

  6. spring MVC体系结构和请求控制器

    MVC处理过程 spring MVC架构模式都进行了分层设计如下 数据访问接口:DAO层 处理业务逻辑层:service层 数据实体:POJO 负责前端请求的接受并处理:servlet 负责前端页面展 ...

  7. swoole 连接池

    proxy_pool.php <?php class ProxyServer { protected $frontends; protected $backends; /** * @var sw ...

  8. hprose 1.0(rpc 框架) - 关于跨域和P3P的声明

    private function sendHeader($context) { if ($this->onSendHeader !== null) { $sendHeader = $this-& ...

  9. django处理上传文件配置

    1.sttings中配置 'django.template.context_processors.media' TEMPLATES = [ { 'BACKEND': 'django.template. ...

  10. hihocoder#1098 : 最小生成树二·Kruscal算法

    #1098 : 最小生成树二·Kruscal算法 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 随着小Hi拥有城市数目的增加,在之间所使用的Prim算法已经无法继续使用 ...