最近想做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=1024*1024*10;
buf=new char[bufSize];
countSize=0;
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 -1;
}
InitSockets();
RTMP_Init(rtmp);
//set connection timeout,default 30s
rtmp->Link.timeout=10;
if (!RTMP_SetupURL(rtmp,const_cast<char*>(rtmpUrl.c_str()))) {
RTMP_Log(RTMP_LOGERROR, "SetupURL Err\n");
RTMP_Free(rtmp);
return -1;
}
if (b_live_stream) {
rtmp->Link.lFlags |= RTMP_LF_LIVE;
}
//1hour
RTMP_SetBufferMS(rtmp, 3600 * 1000);
if (!RTMP_Connect(rtmp, NULL)) {
RTMP_Log(RTMP_LOGERROR, "Connect Err\n");
RTMP_Free(rtmp);
return -1;
}
if (!RTMP_ConnectStream(rtmp, 0)) {
RTMP_Log(RTMP_LOGERROR, "ConnectStream Err\n");
RTMP_Free(rtmp);
RTMP_Close(rtmp);
return -1;
}
}
void RTMPRec::run(){
worker();
}
void RTMPRec::worker(){
int nread;
while ((nread = RTMP_Read(rtmp, buf, bufSize)) != 0) {
fwrite(buf, 1, (size_t)nread, fp);
memset(buf,0,bufSize);
countSize += nread;
RTMP_LogPrintf("Receive: %5dByte, Total: %5.2fkB\n", nread, countSize * 1.0 / 1024);
}
}
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 = 0;
now_time = 0;
pre_frame_time = 0;
lasttime = 0;
b_next_is_key = 1;
pre_tag_size = 0;
type = 0;
datalength = 0;
timestamp = 0;
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 -1;
}
InitSockets();
RTMP_Init(rtmp);
//set connection timeout,default 30s
rtmp->Link.timeout = 5;
if (!RTMP_SetupURL(rtmp, const_cast<char*>(rtmpUrl.c_str()))) {
RTMP_Log(RTMP_LOGERROR, "SetupURL Err\n");
//RTMP_Free(rtmp);
return -1;
}
RTMP_EnableWrite(rtmp);
if (!RTMP_Connect(rtmp, NULL)) {
RTMP_Log(RTMP_LOGERROR, "Connect Err\n");
//RTMP_Free(rtmp);
return -1;
}
if (!RTMP_ConnectStream(rtmp, 0)) {
RTMP_Log(RTMP_LOGERROR, "ConnectStream Err\n");
//RTMP_Close(rtmp);
//RTMP_Free(rtmp);
return -1;
}
//jump over FLV Header
fseek(fp, 9, SEEK_SET);
//jump over previousTagSizen
fseek(fp, 4, SEEK_CUR);
return 0;
}
void RTMPPushFlv::run(){
worker();
}
void RTMPPushFlv::worker(){
log_info("Start to send data ...");
start_time = RTMP_GetTime();
while (1) {
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(1);
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);
p_file_buf = (char *) malloc(11 + datalength + 4);
memset(p_file_buf, 0, 11 + datalength + 4);
if (fread(p_file_buf, 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, p_file_buf, 11 + datalength + 4)) {
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, 11, SEEK_CUR) != 0) {
break;
}
if (!PeekU8(&type, fp)) {
break;
}
if (type == 0x17) {
b_next_is_key = 1;
} else {
b_next_is_key = 0;
}
fseek(fp, -11, 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=0;
static void sig_user(int signo){
if(signo==SIGINT){
runflag=0;
printf("received SIGINT\n");
}
}
void pushYUVByH264(){
char url[]="rtmp://localhost/live/test1";
int width=640;
int height=360;
int outSize=1024;
int baseFrameSize=width*height;
const long bufferSize=baseFrameSize*3;
char buffer[bufferSize];
int fps=25;
int rate=400;
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,1,RTMP_LOGINFO);
printf("connected \n");
RTMP_InitVideoParams(width,height,fps,rate,src_pix_fmt,false);
printf("inited \n");
runflag=1;
unsigned int tick = 0;
unsigned int tick_gap = 1000/fps;
uint32_t now=0,last_update=0;
int index=0;
while(runflag){
if(index!=0){
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, 1, baseFrameSize*3/2, fp) != baseFrameSize*3/2){
// Loop
fseek(fp, 0, SEEK_SET);
fread(buffer, 1, baseFrameSize*3/2, fp);
//fclose(fp);
//break;
}
printf("read file \n");
break;
case AV_PIX_FMT_YUV422P:
if (fread(buffer, 1, baseFrameSize*2, fp) != baseFrameSize*2){
// Loop
fseek(fp, 0, SEEK_SET);
fread(buffer, 1, baseFrameSize*2, fp);
//fclose(fp);
//break;
}
break;
case AV_PIX_FMT_RGB24:
if (fread(buffer, 1, baseFrameSize*3, fp) != baseFrameSize*3){
// Loop
fseek(fp, 0, SEEK_SET);
fread(buffer, 1, baseFrameSize*3, 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)*1000);
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
转载请注明出处:http://www.cnblogs.com/tla001/
一起学习,一起进步