Linux下librtmp使用及编程实战

Posted 悠悠南山下

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux下librtmp使用及编程实战相关的知识,希望对你有一定的参考价值。

  最近想做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=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(&timestamp, 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

以上是关于Linux下librtmp使用及编程实战的主要内容,如果未能解决你的问题,请参考以下文章

ffmpeg添加库实战:移植srs-librtmp到ffmpeg

Spark入门实战系列--3.Spark编程模型(下)--IDEA搭建及实战

Shell编程实战

Shell编程实战

linux打开终端如何启动scala,如何在终端下运行Scala代码片段?

Scala并发编程reactloop代码实战具体解释