本机搭建RTMP服务器--------35

搭建流媒体服务器

  • 准备流媒体服务器(Linux或mac)
  • 编译并安装nginx开发
  • 配置RTMP服务并启动RTMP

 

下载nginx源码:

http://nginx.org/en/download.html

选择最新版本即可。

下载nginx-rtmp-module

地址

https://github.com/arut/nginx-rtmp-module    选择最新版本即可。

 

下载完成后进行解压. 

通过 tar -zxvf xxxxxx 命令

 

./configure --prefix=/usr/local/nginx  --add-module=/Users/yuanxuzhen/tools/nginx-rtmp-module  命令进行安装

 

--prefix=/usr/local/nginx 在/usr/local/nginx下进行安装

在执行的过程中如果遇到下面的问题

报错说明需要pcre和openssl我们通过下面的地址进行下载

pcre https://ftp.pcre.org/pub/pcre/

openssl  https://github.com/openssl/openssl

下载成功后进行解压

再一次执行命令

./configure --prefix=/usr/local/nginx --add-module=/Users/yuanxuzhen/tools/nginx-rtmp-module --with-pcre=/Users/yuanxuzhen/tools/pcre-8.00 --with-openssl=/Users/yuanxuzhen/tools/openssl-OpenSSL_1_1_1i

然后执行下面的命令

make -j 4 多个进程安装

sudo make install

 

然后我们在/usr/local/nginx下面就看到了安装的文件

进入sbin我们看到

可以看到有一个nginx的执行文件 

我们再进入conf目录下进行查看

需要在nginx的conf在添加下面的代码

#rtmp服务
rtmp{
    server{
        #指定服务端口
        listen 1935;  
        chunk_size 4000;
        
        #指定流应用
        application live
        {
            live on;
            allow play all;
        }
    }    

}


#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}

#rtmp服务
rtmp{
	server{
		#指定服务端口
		listen 1935;
		chunk_size 4000;
		
		#指定流应用
		application live
		{
			live on;
			allow play all;
		}


	}
	

}

http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

这里修改有一个技巧。nginx.conf 是只读文件,我们先通过

cp ./conf/nginx.conf  目的文件  

复制出来,然后进行修改。

在通过

sudo cp  目的文件  ./conf/nginx.conf 

将nginx.conf 进行修改

 

下面我们来启动rtmp服务器:

./sbin/nginx -c conf/nginx.conf

启动后我们可以通过下面的命令进行查看

通过上面的命令我们可以看到nginx已经在进行工作,1935端口在进行监听。

 

下面我们来进行推流

1、通过ffmpeg命令进行推流 ,通过ffplay进行播放

ffmpeg -re -i 文件 -c copy -f flv rtmp://localhost/live/room

ffplay rtmp://localhost/live/room

2、通过ffmpeg的api进行推流,通过ffplay进行播放

下面是https://blog.csdn.net/qq_15255121/article/details/115768611?spm=1001.2014.3001.5501 flv实战中将的推流代码

//
//  pushstream.c
//  PushStream
//
//  Created by yuanxuzhen on 2021/4/15.
//

#include "pushstream.h"
#include "librtmp/rtmp.h"

/*
 flv文件 头部有9个字节,
 第一个字节是字母F,
 第二个字节是L,
 第三个字节是V,
 第四个字节是版本号,
 第五个字节 1-5位保留,必须是0,第6位是否有音频tag 第7位保留必须是0,第8位是否有视频tag
 第六到九字节代表header的大小,必须是9
 */

static int status = 1;
void set_status(int state){
    status = state;
}

static FILE* open_flv(char* flvaddr){
    FILE* fp = NULL;
    fp = fopen(flvaddr, "rb");
    if(!fp){
        printf("failed to open flv:%s", flvaddr);
        return NULL;
    }
    fseek(fp, 9, SEEK_SET); //跳过flv头 9个字节
    fseek(fp, 4, SEEK_CUR); //跳过pretagsize 4个字节
    return fp;

}

static RTMP* connect_rtmp_server(char* rtmp_addr){
    RTMP* rtmp = NULL;
    rtmp = RTMP_Alloc();
    if(!rtmp){
        printf("NO Memory");
        goto __ERROR;
    }
    RTMP_Init(rtmp);
    //设置rtmp的超时时间和rtmp的连接地址
    rtmp->Link.timeout = 10;
    RTMP_SetupURL(rtmp, rtmp_addr);
    //设置推流还是拉流,设置开启是推流,不设置是拉流
    RTMP_EnableWrite(rtmp);

    //建立链接
    if(!RTMP_Connect(rtmp, NULL)){
        printf("failed to connect");
        goto __ERROR;
    }
    //创建流
    RTMP_ConnectStream(rtmp, 0);
    return rtmp;
__ERROR:
    if(rtmp){
        RTMP_Close(rtmp);
        RTMP_Free(rtmp);
    }
    
    return NULL;
}
static RTMPPacket *alloc_packet(){
    RTMPPacket *pack = NULL;
//    pack = (RTMPPacket*)malloc(sizeof(RTMPPacket));
    pack =(RTMPPacket*)malloc(sizeof(RTMPPacket));
    if(!pack){
        printf("NO Memory alloc_packet");
        return NULL;
    }
    RTMPPacket_Alloc(pack, 64 * 1024);
    RTMPPacket_Reset(pack);
    pack->m_hasAbsTimestamp = 0;
    pack->m_nChannel = 0x4;
    
    return pack;
}

static int read_u8(FILE* fp, unsigned int *u8){
    unsigned int tmp;
    if(fread(&tmp, 1, 1, fp) != 1){
        printf("Failed to read_u8!\n");
        return -1;
    }
    
    *u8 = tmp & 0xFF;
    return 0;
}



static int read_u24(FILE* fp, unsigned int *u24){
    unsigned int tmp;
    if(fread(&tmp, 1, 3, fp) != 3){
        printf("Failed to read_u24!\n");
        return -1;
    }
    *u24 = ((tmp >> 16) & 0xFF)| ((tmp << 16) & 0xFF0000) | (tmp &0xFF00);
    return 0;
}


static int read_u32(FILE* fp, unsigned int *u32){
    unsigned int tmp;
    if(fread(&tmp, 1, 4, fp) != 4){
        printf("Failed to read_u32!\n");
        return -1;
    }
    *u32 = ((tmp >> 24) & 0xFF) ||  ((tmp >> 8) & 0xFF00)| ((tmp << 8) & 0xFF0000) | ((tmp << 24)&0xFF000000);
    return 0;
}

static int read_ts(FILE *fp, unsigned int *ts){
    unsigned int tmp;
    if(fread(&tmp, 1, 4, fp) !=4) {
        printf("Failed to read_ts!\n");
        return -1;
    }
    
    *ts = ((tmp >> 16) & 0xFF) | ((tmp << 16) & 0xFF0000) | (tmp & 0xFF00) | (tmp & 0xFF000000);
    
    return 0;
}

int  read_data(FILE* fp, RTMPPacket **pack){
    /*
        * tag header
        * 第一个字节 TT(Tag Type), 0x08 音频,0x09 视频, 0x12 script
        * 2-4, Tag body 的长度, PreTagSize - Tag Header size
        * 5-7, 时间戳,单位是毫秒; script 它的时间戳是0
        * 第8个字节,扩展时间戳。真正时间戳结格 [扩展,时间戳] 一共是4字节。
        * 9-11, streamID, 0
        */
    unsigned int tt;
    unsigned int tag_data_size;
    unsigned int ts;
    unsigned int streamId;
    unsigned int tag_pre_size;

    int ret = -1;
    size_t data_size = 0;
    if(read_u8(fp, &tt)){
        goto __ERROR;
    }
    
    if(read_u24(fp, &tag_data_size)){
        goto __ERROR;
    }
    
    if(read_ts(fp, &ts)){
        goto __ERROR;
    }
    
    if(read_u24(fp, &streamId)){
        goto __ERROR;
    }
    data_size = fread((*pack)->m_body, 1, tag_data_size, fp);
    if(tag_data_size != data_size){
        printf("read data size error tag_data_size = %d, real data size = %d\n", tag_data_size, data_size);
        goto __ERROR;
    }
    (*pack)->m_headerType = RTMP_PACKET_SIZE_LARGE;
    (*pack)->m_nTimeStamp = ts;
    (*pack)->m_packetType = tt;
    (*pack)->m_nBodySize = tag_data_size;
    read_u32(fp, &tag_pre_size);
    ret = 0;
__ERROR:
    return ret;
    
}

static void send_data(FILE* fp, RTMP *rtmp){
    //1、创建RTMPPacket对象
    RTMPPacket *packet = NULL;
    unsigned int pre_ts = 0;
    packet = alloc_packet();
    packet->m_nInfoField2 = rtmp->m_stream_id;

    while (1) {
        //从flv读取文件
        //2.从flv文件中读取数据
        if(read_data(fp, &packet)){
            printf("over!\n");
            break;
        }
        //判断rtmp是否还处在链接状态
        if(!RTMP_IsConnected(rtmp)){
            printf("Disconnect....\n");
            break;
        }

        unsigned int diff = packet->m_nTimeStamp - pre_ts;
        usleep(diff * 1000);
        //发送数据
        RTMP_SendPacket(rtmp, packet, 0);
        pre_ts = packet->m_nTimeStamp;

        
    }
    
}




void push_stream(){
    char* rtmp_addr = "rtmp://localhost/live/room";

    char* flv = "/Users/yuanxuzhen/study/mac/PushStream/PushStream/output/test_yuv.flv";
    //读flv文件
    FILE* fp = open_flv(flv);
    
    //链接RTMP服务器
    RTMP* rtmp = connect_rtmp_server(rtmp_addr);
    
    //push video/audio data
    send_data(fp, rtmp);
    
}

通过ffplay rtmp://localhost/live/room 进行播放。

 

通过上面的配置,我们就在本地搭建了RTMP服务器,我们也就可以推流播放了。

 

慢一点再慢一点。

原理性的东西一直没有变。

以教为学才能更快的学好。

加油!!!!!!!!!!

 

 

 

 


版权声明:本文为qq_15255121原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。