Linux下librtmp使用及编程实战

摘要:
最近,我想做一个RTMP流媒体和直播的小项目,但我不想使用FFmpeg直接推送流媒体。FFmpeg流媒体特别简单,因为它集成了编码和librtmp,这是没有意义的。FFmpeg流媒体的例子可以在雷神公司的博客中找到。这主要基于librmtp,结合libx264进行压缩和一些实验,它包括三个部分:rtmp流保存、flv文件流和h264流。首先,rtmp流保存/**RTMPRec。cpp**创建时间:2017年1月11日*作者:tla001*/#include“RTMPRec.h”#include”sockInit.h“RTMPRec::RTMPRec{//TODOAuto-generatedconstructorstubpUrl=url;outFile=文件名;bufSize=1024*1024*10;buf=newchar[bufSize];countSize=0;b_live_stream=true;rtmp=rtmp_Alloc();}RTMPRec::~RTMPRec(){//TODOAuto-generateddestructorstubif(fp!=0){fwrite;memset;countSize+=nread;RTMP_LogPrintf;}}voidRTMPRec::doSave(){this-˃start();}Push flv文件主要指/*RTMPPushFlv。cpp**创建时间:2017年1月11日*作者:tla001*/#include“RTMPPushFlv.h”#include”sockInit.h“RTMPPashFlv::RTMPPushFlv{//TODOAuto-generatedconstructorstubrtmpUrl=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::~RTMPPush Flv(){//TODO自动生成的涡轮增压器tubif(fp!

  最近想做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.
");
        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
");
            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
");
        RTMP_Free(rtmp);
        return -1;
    }

    if (!RTMP_ConnectStream(rtmp, 0)) {
        RTMP_Log(RTMP_LOGERROR, "ConnectStream Err
");
        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
", 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
");
        //RTMP_Free(rtmp);
        return -1;
    }
    RTMP_EnableWrite(rtmp);
    if (!RTMP_Connect(rtmp, NULL)) {
        RTMP_Log(RTMP_LOGERROR, "Connect Err
");
        //RTMP_Free(rtmp);
        return -1;
    }

    if (!RTMP_ConnectStream(rtmp, 0)) {
        RTMP_Log(RTMP_LOGERROR, "ConnectStream Err
");
        //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
", 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
");
            break;
        }
        if (!RTMP_Write(rtmp, p_file_buf, 11 + datalength + 4)) {
            RTMP_Log(RTMP_LOGERROR, "Rtmp Write Error
");
            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
");
    }
}

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 
");
    RTMP_InitVideoParams(width,height,fps,rate,src_pix_fmt,false);
    printf("inited 
");
    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
",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 
");
                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 
");
                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使用及编程实战》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Nginx使用教程(二):Nginx配置性能优化之worker配置Fiddler使用随笔下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Tess4J -4.0.2- Linux 实践 [解决:Tess4J

Tess4J是Tesseract的Java JNA wrapper。本文介绍了在CentOS 7 操作系统中使用Tess4J的步骤及注意事项。在正式开始之前,先花一点篇幅,对相关的技术作一简要介绍。 一点点背景 Tesseract Tesseract 是一个著名的开源OCR引擎,支持100多种语言,可以开箱即用。还可以通过训练方式支持更多语言。Tesser...

Ubuntu下制作系统启动盘

制作系统U盘: $ sudo umount /dev/sdc1 $ sudo dd if=/home/abby/software/ubuntu-16.04.3-desktop-amd64.iso of=/dev/sdc 3100800+0 records in 3100800+0records out 1587609600 bytes (1.6...

makefile初步制作,arm-linux- (gcc/ld/objcopy/objdump)详解

在linux中输入vi Makefile 来实现创建Makefile文件 注意:命令行前必须加TAB键 例如:将两个文件led.c和crt0.S汇编文件,制作一个Makefile文件 1 1led.bin : crt0.S led.c 2 2 arm-linux-gcc -c -o crt0.o crt0.c 3 3 arm-linux-gcc -c...

Linux配置文件的修改

在很多时候,我们需要对Linux的配置文件进行修改。此时就涉及到了不同Linux发行版的修改配置问题。下面就以主流的几个操作系统(Unix:Solaris,Linux:Ubuntu,Redhat)作为说明,添加当前目录到环境变量,来具体阐述Linux的配置文件修改问题。 修改用户配置文件 此修改仅对修改的用户有效,假如修改的用户是user1,那么仅对us...

CentOS6.5下设置静态IP

最近使用VM学习Linux时,使用的是带有桌面的那种,每次在重启Linux后需要手动联网,由于是在XShell上操作Linux,每次再进入VM操作Linux联网会十分麻烦,于是想到了一个办法--设置静态IP 编辑网卡文件 /etc/sysconfig/network-scripts/ifcfg-eth0 网卡文件设置之前 网卡文件设置之后 然后重启网卡 s...

Linux驱动开发之环境搭建快速构建内核树

之前重新编译了Ubuntu下的内核(Linux驱动开发之环境搭建----Ubuntu 12.4 编译内核),可不是很完美,现在构建基于Ubuntu官方版本的内核树. 1. 先查看内核版本 cody@kdev:~$ uname -a Linux kdev 3.2.0-29-generic-pae #46-Ubuntu SMP Fri Jul 27 17:25...