最近想做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. 流媒体 8——因特网 tcp/ip

    1 因特网 1.1 因特网的结构 组成因特网的子网之间在物理上的相互连接都是通过网关设备实现的.通过网关设备互相连接在一起的不同的网络通常称为子网 (subnetwork),因为它们是大网络之中的网络 ...

  2. linux 命令——56 netstat(转)

    netstat命令用于显示与IP.TCP.UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况.netstat是在内核中访问网络及相关信息的程序,它能提供TCP连接,TCP和UDP ...

  3. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

  4. ELF文件的格式和加载过程

    http://blog.csdn.net/lingfong_cool/article/details/7832896 (一) ELF 文件的格式       ELF 文件类型 (1) 可重定位文件(  ...

  5. linux apache 不解析php文件显示源码

    首先检查是否安装PHP 没有的话就先安装 如果安装过 在/etc/httpd/conf/httpd.conf文件中 在<IfModule mime_module>里面 AddType ap ...

  6. OI,我的决心

    虽然从初一就开始NOIP,但沉溺于游戏编程等各种乱七八糟的技术,一直没对算法有过透彻的研究. ——————————简单的来说就是水过了—————————— 我生于一个弱省,就读于一所弱校(我们全区的都 ...

  7. Java 解决IE浏览器下载文件,文件名出现乱码问题

    /** * 区分ie 和其他浏览器的下载文件乱码问题 * @param request * @param fileName * @return */ public String getFileName ...

  8. 前端JS转图片为base64并压缩、调整尺寸脚本

    image to base64 to blob //////////////////////////////////////////////////////////////////////////// ...

  9. 固定table的表头同时固定列

    table表格是我们最常使用的数据显示一种形式,但有时候数据比较多的时候 就需要我们去固定表头,固定列.我这里用简单的css样式配合两句js脚本来实现,希望能够去帮到你. <div class= ...

  10. 【Mysql】mysql中bigint、int、mediumint、smallint 和 tinyint的取值范围

    1.bigint 从 -2^63 (-9223372036854775808) 到 2^63-1 (9223372036854775807) 的整型数据(所有数字),无符号的范围是0到 1844674 ...