zoukankan      html  css  js  c++  java
  • 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.
    ");
            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

  • 相关阅读:
    Windows python 鼠标键盘监控及控制
    python 执行adb shell 命令
    python Windows提示框
    判断function属于函数或方法
    生成不同时间下的LOG
    pyqt5 QCalendar类中常用的方法
    python字符串大小写转换
    configparser 模块的基本方法
    QGridLayout类中常用的方法
    Day049--jQuery的文档操作和事件介绍
  • 原文地址:https://www.cnblogs.com/tla001/p/6323092.html
Copyright © 2011-2022 走看看