搭建流媒体服务器
- 准备流媒体服务器(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服务器,我们也就可以推流播放了。
慢一点再慢一点。
原理性的东西一直没有变。
以教为学才能更快的学好。
加油!!!!!!!!!!