mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-10-06 20:23:43 +08:00
commit
a3c8cb11b3
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
release/ filter=lfs diff=lfs merge=lfs -text
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -34,3 +34,4 @@
|
|||
/cmake-build-debug/
|
||||
/.idea/
|
||||
/c_wrapper/.idea/
|
||||
/release/mac/Debug/
|
|
@ -1 +1 @@
|
|||
Subproject commit 2bb234006c852b1d1a61a0e9a7f39dde7105fe34
|
||||
Subproject commit 57e7c83d5667b1e06fb8f5ca73dbe3f04a9fc67f
|
|
@ -1 +1 @@
|
|||
Subproject commit 6df71e01c174cdfe69e597cc4acb766a20b28620
|
||||
Subproject commit 40edf6243d9d99676062062efdec203b24a178aa
|
|
@ -2,13 +2,34 @@
|
|||
cmake_minimum_required(VERSION 3.1.3)
|
||||
#使能c++11
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
#加载自定义模块
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake")
|
||||
#设置库文件路径
|
||||
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
|
||||
#设置可执行程序路径
|
||||
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||
|
||||
#set(CMAKE_BUILD_TYPE "Release")
|
||||
|
||||
if(${CMAKE_BUILD_TYPE} MATCHES "Release")
|
||||
message(STATUS "Release版本")
|
||||
set(BuildType "Release")
|
||||
else()
|
||||
set(BuildType "Debug")
|
||||
message(STATUS "Debug版本")
|
||||
endif()
|
||||
|
||||
#设置bin和lib库目录
|
||||
set(RELEASE_DIR ${CMAKE_SOURCE_DIR}/release)
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/linux/${BuildType})
|
||||
SET(EXECUTABLE_OUTPUT_PATH ${RELEASE_DIR}/linux/${BuildType})
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/windows/${BuildType})
|
||||
SET(EXECUTABLE_OUTPUT_PATH ${RELEASE_DIR}/windows/${BuildType})
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
||||
SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/mac/${BuildType})
|
||||
SET(EXECUTABLE_OUTPUT_PATH ${RELEASE_DIR}/mac/${BuildType})
|
||||
endif ()
|
||||
|
||||
LINK_DIRECTORIES(${LIBRARY_OUTPUT_PATH})
|
||||
|
||||
|
||||
#设置工程源码根目录
|
||||
set(ToolKit_Root ${CMAKE_CURRENT_SOURCE_DIR}/3rdpart/ZLToolKit/src)
|
||||
|
@ -106,7 +127,7 @@ add_library(zltoolkit STATIC ${ToolKit_src_list})
|
|||
add_library(zlmediakit STATIC ${MediaKit_src_list})
|
||||
|
||||
|
||||
set(VS_FALGS "/wd4819 /wd4996 /wd4018 /wd4267 /wd4244 /wd4101 /wd4828 /wd4309 /wd4573 /utf-8" )
|
||||
set(VS_FALGS "/wd4819 /wd4996 /wd4018 /wd4267 /wd4244 /wd4101 /wd4828 /wd4309 /wd4573" )
|
||||
#libmpeg
|
||||
if(ENABLE_HLS)
|
||||
aux_source_directory(${MediaServer_Root}/libmpeg/include src_mpeg)
|
||||
|
|
16
README.md
16
README.md
|
@ -13,6 +13,7 @@
|
|||
- Well performance and stable test,can be used commercially.
|
||||
- Support linux, macos, ios, android, Windows Platforms.
|
||||
- Very low latency(lower then one second), video opened immediately.
|
||||
- **Now Support websocket-flv!**
|
||||
|
||||
## Features
|
||||
|
||||
|
@ -116,7 +117,12 @@
|
|||
- Apple OSX(Darwin), both 32 and 64bits.
|
||||
- All hardware with x86/x86_64/arm/mips cpu.
|
||||
- Windows.
|
||||
- **You must use git to clone the complete code. Do not download the source code by downloading zip package. Otherwise, the sub-module code will not be downloaded by default.**
|
||||
- **You must use git to clone the complete code. Do not download the source code by downloading zip package. Otherwise, the sub-module code will not be downloaded by default.You can do it like this:**
|
||||
```
|
||||
git clone https://github.com/zlmediakit/ZLMediaKit.git
|
||||
cd ZLMediaKit
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -231,7 +237,7 @@ It is recommended to compile on Ubuntu or MacOS,compiling on windows is cumber
|
|||
## Usage
|
||||
|
||||
- As server:
|
||||
```
|
||||
```cpp
|
||||
TcpServer::Ptr rtspSrv(new TcpServer());
|
||||
TcpServer::Ptr rtmpSrv(new TcpServer());
|
||||
TcpServer::Ptr httpSrv(new TcpServer());
|
||||
|
@ -244,7 +250,7 @@ It is recommended to compile on Ubuntu or MacOS,compiling on windows is cumber
|
|||
```
|
||||
|
||||
- As player:
|
||||
```
|
||||
```cpp
|
||||
MediaPlayer::Ptr player(new MediaPlayer());
|
||||
weak_ptr<MediaPlayer> weakPlayer = player;
|
||||
player->setOnPlayResult([weakPlayer](const SockException &ex) {
|
||||
|
@ -273,7 +279,7 @@ It is recommended to compile on Ubuntu or MacOS,compiling on windows is cumber
|
|||
player->play("rtsp://admin:jzan123456@192.168.0.122/");
|
||||
```
|
||||
- As proxy server:
|
||||
```
|
||||
```cpp
|
||||
//support rtmp and rtsp url
|
||||
//just support H264+AAC
|
||||
auto urlList = {"rtmp://live.hkstv.hk.lxdns.com/live/hks",
|
||||
|
@ -288,7 +294,7 @@ It is recommended to compile on Ubuntu or MacOS,compiling on windows is cumber
|
|||
```
|
||||
|
||||
- As puser:
|
||||
```
|
||||
```cpp
|
||||
PlayerProxy::Ptr player(new PlayerProxy("app","stream"));
|
||||
player->play("rtmp://live.hkstv.hk.lxdns.com/live/hks");
|
||||
|
||||
|
|
16
README_CN.md
16
README_CN.md
|
@ -11,6 +11,7 @@
|
|||
- 代码经过大量的稳定性、性能测试,可满足商用服务器项目。
|
||||
- 支持linux、macos、ios、android、windows平台
|
||||
- 支持画面秒开(GOP缓存)、极低延时(1秒内)
|
||||
- **支持websocket-flv直播**
|
||||
- [ZLMediaKit高并发实现原理](https://github.com/xiongziliang/ZLMediaKit/wiki/ZLMediaKit%E9%AB%98%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86)
|
||||
|
||||
## 项目定位
|
||||
|
@ -127,7 +128,12 @@
|
|||
## 编译要求
|
||||
- 编译器支持C++11,GCC4.8/Clang3.3/VC2015或以上
|
||||
- cmake3.2或以上
|
||||
- **必须使用git下载完整的代码,不要使用下载zip包的方式下载源码,否则子模块代码默认不下载!**
|
||||
- **必须使用git下载完整的代码,不要使用下载zip包的方式下载源码,否则子模块代码默认不下载!你可以像以下这样操作:**
|
||||
```
|
||||
git clone https://github.com/zlmediakit/ZLMediaKit.git
|
||||
cd ZLMediaKit
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
## 编译(Linux)
|
||||
- 我的编译环境
|
||||
|
@ -219,7 +225,7 @@
|
|||
```
|
||||
## 使用方法
|
||||
- 作为服务器:
|
||||
```
|
||||
```cpp
|
||||
TcpServer::Ptr rtspSrv(new TcpServer());
|
||||
TcpServer::Ptr rtmpSrv(new TcpServer());
|
||||
TcpServer::Ptr httpSrv(new TcpServer());
|
||||
|
@ -232,7 +238,7 @@
|
|||
```
|
||||
|
||||
- 作为播放器:
|
||||
```
|
||||
```cpp
|
||||
MediaPlayer::Ptr player(new MediaPlayer());
|
||||
weak_ptr<MediaPlayer> weakPlayer = player;
|
||||
player->setOnPlayResult([weakPlayer](const SockException &ex) {
|
||||
|
@ -261,7 +267,7 @@
|
|||
player->play("rtsp://admin:jzan123456@192.168.0.122/");
|
||||
```
|
||||
- 作为代理服务器:
|
||||
```
|
||||
```cpp
|
||||
//support rtmp and rtsp url
|
||||
//just support H264+AAC
|
||||
auto urlList = {"rtmp://live.hkstv.hk.lxdns.com/live/hks",
|
||||
|
@ -285,7 +291,7 @@
|
|||
```
|
||||
|
||||
- 作为推流客户端器:
|
||||
```
|
||||
```cpp
|
||||
PlayerProxy::Ptr player(new PlayerProxy("app","stream"));
|
||||
//拉一个流,生成一个RtmpMediaSource,源的名称是"app/stream"
|
||||
//你也可以以其他方式生成RtmpMediaSource,比如说MP4文件(请研读MediaReader代码)
|
||||
|
|
163
conf/config.ini
Normal file
163
conf/config.ini
Normal file
|
@ -0,0 +1,163 @@
|
|||
[api]
|
||||
#是否调试http api,启用调试后,会打印每次http请求的内容和回复
|
||||
apiDebug=1
|
||||
#一些比较敏感的http api在访问时需要提供secret,否则无权限调用
|
||||
#如果是通过127.0.0.1访问,那么可以不提供secret
|
||||
secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc
|
||||
|
||||
[ffmpeg]
|
||||
#FFmpeg可执行程序路径
|
||||
bin=/usr/local/bin/ffmpeg
|
||||
#FFmpeg拉流再推流的命令模板,通过该模板可以设置再编码的一些参数
|
||||
cmd=%s -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s
|
||||
#FFmpeg日志的路径,如果置空则不生成FFmpeg日志
|
||||
log=/Users/xzl/git/ZLMediaKit/release/mac/Release/ffmpeg/ffmpeg.log
|
||||
|
||||
[general]
|
||||
#是否启用虚拟主机
|
||||
enableVhost=1
|
||||
#播放器或推流器在断开后会触发hook.on_flow_report事件(使用多少流量事件),
|
||||
#flowThreshold参数控制触发hook.on_flow_report事件阈值,使用流量超过该阈值后才触发,单位KB
|
||||
flowThreshold=1024
|
||||
#播放最多等待时间,单位毫秒
|
||||
#播放在播放某个流时,如果该流不存在,
|
||||
#ZLMediaKit会最多让播放器等待maxStreamWaitMS毫秒
|
||||
#如果在这个时间内,该流注册成功,那么会立即返回播放器播放成功
|
||||
#否则返回播放器未找到该流,该机制的目的是可以先播放再推流
|
||||
maxStreamWaitMS=5000
|
||||
#某个流无人观看时,触发hook.on_stream_none_reader事件的最大等待时间,单位毫秒
|
||||
#在配合hook.on_stream_none_reader事件时,可以做到无人观看自动停止拉流或停止接收推流
|
||||
streamNoneReaderDelayMS=5000
|
||||
|
||||
[hls]
|
||||
#hls写文件的buf大小,调整参数可以提高文件io性能
|
||||
fileBufSize=65536
|
||||
#hls保存文件路径
|
||||
filePath=/Users/xzl/git/ZLMediaKit/release/mac/Release/httpRoot
|
||||
#hls最大切片时间
|
||||
segDur=3
|
||||
#m3u8索引中,hls保留切片个数(实际保留切片个数大2~3个)
|
||||
segNum=3
|
||||
|
||||
[hook]
|
||||
#在推流时,如果url参数匹对admin_params,那么可以不经过hook鉴权直接推流成功,播放时亦然
|
||||
#该配置项的目的是为了开发者自己调试测试,该参数暴露后会有泄露隐私的安全隐患
|
||||
admin_params=secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc
|
||||
#是否启用hook事件,启用后,推拉流都将进行鉴权
|
||||
enable=0
|
||||
#播放器或推流器使用流量事件,置空则关闭
|
||||
on_flow_report=https://127.0.0.1/index/hook/on_flow_report
|
||||
#访问http文件鉴权事件,置空则关闭鉴权
|
||||
on_http_access=https://127.0.0.1/index/hook/on_http_access
|
||||
#播放鉴权事件,置空则关闭鉴权
|
||||
on_play=https://127.0.0.1/index/hook/on_play
|
||||
#推流鉴权事件,置空则关闭鉴权
|
||||
on_publish=https://127.0.0.1/index/hook/on_publish
|
||||
#录制mp4切片完成事件
|
||||
on_record_mp4=https://127.0.0.1/index/hook/on_record_mp4
|
||||
#rtsp播放鉴权事件,此事件中比对rtsp的用户名密码
|
||||
on_rtsp_auth=https://127.0.0.1/index/hook/on_rtsp_auth
|
||||
#rtsp播放是否开启鉴权事件,置空则关闭rtsp鉴权。rtsp播放鉴权还支持url方式鉴权
|
||||
#建议开发者统一采用url参数方式鉴权,rtsp用户名密码鉴权一般在设备上用的比较多
|
||||
on_rtsp_realm=https://127.0.0.1/index/hook/on_rtsp_realm
|
||||
#远程telnet调试鉴权事件
|
||||
on_shell_login=https://127.0.0.1/index/hook/on_shell_login
|
||||
#直播流注册或注销事件
|
||||
on_stream_changed=https://127.0.0.1/index/hook/on_stream_changed
|
||||
#无人观看流事件,通过该事件,可以选择是否关闭无人观看的流。配合general.streamNoneReaderDelayMS选项一起使用
|
||||
on_stream_none_reader=https://127.0.0.1/index/hook/on_stream_none_reader
|
||||
#播放时,未找到流事件,通过配合hook.on_stream_none_reader事件可以完成按需拉流
|
||||
on_stream_not_found=https://127.0.0.1/index/hook/on_stream_not_found
|
||||
#hook api最大等待回复时间,单位秒
|
||||
timeoutSec=10
|
||||
|
||||
[http]
|
||||
#http服务器字符编码,windows上默认gb2312
|
||||
charSet=utf-8
|
||||
#http链接超时时间
|
||||
keepAliveSecond=10
|
||||
#keep-alive类型的链接最多复用次数
|
||||
maxReqCount=100
|
||||
#http请求体最大字节数,如果post的body太大,则不适合缓存body在内存
|
||||
maxReqSize=4096
|
||||
#404网页内容,用户可以自定义404网页
|
||||
notFound=<html><head><title>404 Not Found</title></head><body bgcolor="white"><center><h1>您访问的资源不存在!</h1></center><hr><center>ZLMediaKit-4.0</center></body></html>
|
||||
#http服务器监听端口
|
||||
port=80
|
||||
#http文件服务器根目录
|
||||
rootPath=/Users/xzl/git/ZLMediaKit/release/mac/Release/httpRoot
|
||||
#http文件服务器读文件缓存大小,单位BYTE,调整该参数可以优化文件io性能
|
||||
sendBufSize=65536
|
||||
#https服务器监听端口
|
||||
sslport=443
|
||||
|
||||
[multicast]
|
||||
#rtp组播截止组播ip地址
|
||||
addrMax=239.255.255.255
|
||||
#rtp组播起始组播ip地址
|
||||
addrMin=239.0.0.0
|
||||
#组播udp ttl
|
||||
udpTTL=64
|
||||
|
||||
[record]
|
||||
#mp4录制或mp4点播的应用名,通过限制应用名,可以防止随意点播
|
||||
appName=record
|
||||
#mp4录制写文件缓存,单位BYTE,调整参数可以提高文件io性能
|
||||
fileBufSize=65536
|
||||
#mp4录制保存路径
|
||||
filePath=/Users/xzl/git/ZLMediaKit/release/mac/Release/httpRoot
|
||||
#mp4录制切片时间,单位秒
|
||||
fileSecond=3600
|
||||
#mp4点播每次流化数据量,单位毫秒,
|
||||
#减少该值可以让点播数据发送量更平滑,增大该值则更节省cpu资源
|
||||
sampleMS=100
|
||||
|
||||
[rtmp]
|
||||
#rtmp必须在此时间内完成握手,否则服务器会断开链接,单位秒
|
||||
handshakeSecond=15
|
||||
#rtmp超时时间,如果该时间内未收到客户端的数据,
|
||||
#或者tcp发送缓存超过这个时间,则会断开连接,单位秒
|
||||
keepAliveSecond=15
|
||||
#在接收rtmp推流时,是否重新生成时间戳(很多推流器的时间戳着实很烂)
|
||||
modifyStamp=1
|
||||
#rtmp服务器监听端口
|
||||
port=1935
|
||||
|
||||
[rtp]
|
||||
#音频mtu大小,该参数限制rtp最大字节数,推荐不要超过1400
|
||||
#加大该值会明显增加直播延时
|
||||
audioMtuSize=600
|
||||
#如果rtp的序列号连续clearCount次有序,那么rtp将不再排序(目的减少rtp排序导致的延时)
|
||||
clearCount=10
|
||||
#rtp时间戳回环时间,单位毫秒
|
||||
cycleMS=46800000
|
||||
#rtp排序map缓存大小,加大该值可能会增大延时,但是rtp乱序问题会减小
|
||||
maxRtpCount=50
|
||||
#视频mtu大小,该参数限制rtp最大字节数,推荐不要超过1400
|
||||
videoMtuSize=1400
|
||||
|
||||
[rtsp]
|
||||
#rtsp专有鉴权方式是采用base64还是md5方式
|
||||
authBasic=0
|
||||
#rtsp拉流代理是否是直接代理模式
|
||||
#直接代理后支持任意编码格式,但是会导致GOP缓存无法定位到I帧,可能会导致开播花屏
|
||||
#并且如果是tcp方式拉流,如果rtp大于mtu会导致无法使用udp方式代理
|
||||
#假定您的拉流源地址不是264或265或AAC,那么你可以使用直接代理的方式来支持rtsp代理
|
||||
#默认开启rtsp直接代理,rtmp由于没有这些问题,是强制开启直接代理的
|
||||
directProxy=1
|
||||
#rtsp必须在此时间内完成握手,否则服务器会断开链接,单位秒
|
||||
handshakeSecond=15
|
||||
#rtsp超时时间,如果该时间内未收到客户端的数据,
|
||||
#或者tcp发送缓存超过这个时间,则会断开连接,单位秒
|
||||
keepAliveSecond=15
|
||||
#rtsp服务器监听地址
|
||||
port=554
|
||||
#rtsps服务器监听地址
|
||||
sslport=322
|
||||
|
||||
[shell]
|
||||
#调试telnet服务器接受最大bufffer大小
|
||||
maxReqSize=1024
|
||||
#调试telnet服务器监听端口
|
||||
port=9000
|
||||
|
BIN
release/linux/Release/MediaServer
Executable file
BIN
release/linux/Release/MediaServer
Executable file
Binary file not shown.
BIN
release/linux/Release/libcrypto.so.1.0.1e
Executable file
BIN
release/linux/Release/libcrypto.so.1.0.1e
Executable file
Binary file not shown.
BIN
release/linux/Release/libflv.a
Executable file
BIN
release/linux/Release/libflv.a
Executable file
Binary file not shown.
BIN
release/linux/Release/libjsoncpp.a
Executable file
BIN
release/linux/Release/libjsoncpp.a
Executable file
Binary file not shown.
BIN
release/linux/Release/libmov.a
Executable file
BIN
release/linux/Release/libmov.a
Executable file
Binary file not shown.
BIN
release/linux/Release/libmpeg.a
Executable file
BIN
release/linux/Release/libmpeg.a
Executable file
Binary file not shown.
BIN
release/linux/Release/libmysqlclient.so.16.0.0
Executable file
BIN
release/linux/Release/libmysqlclient.so.16.0.0
Executable file
Binary file not shown.
BIN
release/linux/Release/libssl.so.1.0.1e
Executable file
BIN
release/linux/Release/libssl.so.1.0.1e
Executable file
Binary file not shown.
BIN
release/linux/Release/libzlmediakit.a
Executable file
BIN
release/linux/Release/libzlmediakit.a
Executable file
Binary file not shown.
BIN
release/linux/Release/libzltoolkit.a
Executable file
BIN
release/linux/Release/libzltoolkit.a
Executable file
Binary file not shown.
9
release/linux/Release/readme.md
Normal file
9
release/linux/Release/readme.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
执行可执行程序时,请在终端输入:
|
||||
|
||||
```
|
||||
export LD_LIBRARY_PATH=./
|
||||
./MediaServer -d &
|
||||
```
|
||||
|
||||
如果由于so动态库链接失败导致运行不起来,请重建so库软链接
|
||||
如果由于端口权限问题导致启动失败,请修改配置文件中端口号,或者以root权限运行
|
BIN
release/linux/Release/ssl.p12
Executable file
BIN
release/linux/Release/ssl.p12
Executable file
Binary file not shown.
BIN
release/linux/Release/test_benchmark
Executable file
BIN
release/linux/Release/test_benchmark
Executable file
Binary file not shown.
BIN
release/linux/Release/test_httpApi
Executable file
BIN
release/linux/Release/test_httpApi
Executable file
Binary file not shown.
BIN
release/linux/Release/test_httpClient
Executable file
BIN
release/linux/Release/test_httpClient
Executable file
Binary file not shown.
BIN
release/linux/Release/test_pusher
Executable file
BIN
release/linux/Release/test_pusher
Executable file
Binary file not shown.
BIN
release/linux/Release/test_pusherMp4
Executable file
BIN
release/linux/Release/test_pusherMp4
Executable file
Binary file not shown.
BIN
release/linux/Release/test_server
Executable file
BIN
release/linux/Release/test_server
Executable file
Binary file not shown.
BIN
release/mac/Release/MediaServer
Executable file
BIN
release/mac/Release/MediaServer
Executable file
Binary file not shown.
BIN
release/mac/Release/libcrypto.1.0.0.dylib
Normal file
BIN
release/mac/Release/libcrypto.1.0.0.dylib
Normal file
Binary file not shown.
BIN
release/mac/Release/libfaac.0.0.0.dylib
Normal file
BIN
release/mac/Release/libfaac.0.0.0.dylib
Normal file
Binary file not shown.
BIN
release/mac/Release/libflv.a
Normal file
BIN
release/mac/Release/libflv.a
Normal file
Binary file not shown.
BIN
release/mac/Release/libjsoncpp.a
Normal file
BIN
release/mac/Release/libjsoncpp.a
Normal file
Binary file not shown.
BIN
release/mac/Release/libmov.a
Normal file
BIN
release/mac/Release/libmov.a
Normal file
Binary file not shown.
BIN
release/mac/Release/libmp4v2.2.dylib
Normal file
BIN
release/mac/Release/libmp4v2.2.dylib
Normal file
Binary file not shown.
BIN
release/mac/Release/libmpeg.a
Normal file
BIN
release/mac/Release/libmpeg.a
Normal file
Binary file not shown.
BIN
release/mac/Release/libmysqlclient.21.dylib
Normal file
BIN
release/mac/Release/libmysqlclient.21.dylib
Normal file
Binary file not shown.
BIN
release/mac/Release/libssl.1.0.0.dylib
Normal file
BIN
release/mac/Release/libssl.1.0.0.dylib
Normal file
Binary file not shown.
BIN
release/mac/Release/libx264.155.dylib
Normal file
BIN
release/mac/Release/libx264.155.dylib
Normal file
Binary file not shown.
BIN
release/mac/Release/libzlmediakit.a
Normal file
BIN
release/mac/Release/libzlmediakit.a
Normal file
Binary file not shown.
BIN
release/mac/Release/libzltoolkit.a
Normal file
BIN
release/mac/Release/libzltoolkit.a
Normal file
Binary file not shown.
9
release/mac/Release/readme.md
Normal file
9
release/mac/Release/readme.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
执行可执行程序时,请在终端输入:
|
||||
|
||||
```
|
||||
export DYLD_LIBRARY_PATH=./
|
||||
./MediaServer -d &
|
||||
```
|
||||
|
||||
如果由于so动态库链接失败导致运行不起来,请重建so库软链接
|
||||
如果由于端口权限问题导致启动失败,请修改配置文件中端口号,或者以root权限运行
|
BIN
release/mac/Release/ssl.p12
Normal file
BIN
release/mac/Release/ssl.p12
Normal file
Binary file not shown.
BIN
release/mac/Release/test_benchmark
Executable file
BIN
release/mac/Release/test_benchmark
Executable file
Binary file not shown.
BIN
release/mac/Release/test_httpApi
Executable file
BIN
release/mac/Release/test_httpApi
Executable file
Binary file not shown.
BIN
release/mac/Release/test_httpClient
Executable file
BIN
release/mac/Release/test_httpClient
Executable file
Binary file not shown.
BIN
release/mac/Release/test_player
Executable file
BIN
release/mac/Release/test_player
Executable file
Binary file not shown.
BIN
release/mac/Release/test_pusher
Executable file
BIN
release/mac/Release/test_pusher
Executable file
Binary file not shown.
BIN
release/mac/Release/test_pusherMp4
Executable file
BIN
release/mac/Release/test_pusherMp4
Executable file
Binary file not shown.
BIN
release/mac/Release/test_server
Executable file
BIN
release/mac/Release/test_server
Executable file
Binary file not shown.
BIN
release/windows/Release/MediaServer.exe
Normal file
BIN
release/windows/Release/MediaServer.exe
Normal file
Binary file not shown.
BIN
release/windows/Release/jsoncpp.lib
Normal file
BIN
release/windows/Release/jsoncpp.lib
Normal file
Binary file not shown.
BIN
release/windows/Release/libcrypto-1_1.dll
Normal file
BIN
release/windows/Release/libcrypto-1_1.dll
Normal file
Binary file not shown.
BIN
release/windows/Release/libmysql.dll
Normal file
BIN
release/windows/Release/libmysql.dll
Normal file
Binary file not shown.
BIN
release/windows/Release/libssl-1_1.dll
Normal file
BIN
release/windows/Release/libssl-1_1.dll
Normal file
Binary file not shown.
BIN
release/windows/Release/mov.lib
Normal file
BIN
release/windows/Release/mov.lib
Normal file
Binary file not shown.
BIN
release/windows/Release/mpeg.lib
Normal file
BIN
release/windows/Release/mpeg.lib
Normal file
Binary file not shown.
3
release/windows/Release/readme.md
Normal file
3
release/windows/Release/readme.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
执行可执行程序时,可以直接双击MediaServer运行
|
||||
|
||||
如果由于端口权限问题导致启动失败,请修改配置文件(.ini后缀的文件)中端口号,然后再运行
|
BIN
release/windows/Release/ssl.p12
Normal file
BIN
release/windows/Release/ssl.p12
Normal file
Binary file not shown.
BIN
release/windows/Release/test_benchmark.exe
Normal file
BIN
release/windows/Release/test_benchmark.exe
Normal file
Binary file not shown.
BIN
release/windows/Release/test_httpApi.exe
Normal file
BIN
release/windows/Release/test_httpApi.exe
Normal file
Binary file not shown.
BIN
release/windows/Release/test_httpClient.exe
Normal file
BIN
release/windows/Release/test_httpClient.exe
Normal file
Binary file not shown.
BIN
release/windows/Release/test_pusher.exe
Normal file
BIN
release/windows/Release/test_pusher.exe
Normal file
Binary file not shown.
BIN
release/windows/Release/test_pusherMp4.exe
Normal file
BIN
release/windows/Release/test_pusherMp4.exe
Normal file
Binary file not shown.
BIN
release/windows/Release/test_server.exe
Normal file
BIN
release/windows/Release/test_server.exe
Normal file
Binary file not shown.
BIN
release/windows/Release/zlmediakit.lib
Normal file
BIN
release/windows/Release/zlmediakit.lib
Normal file
Binary file not shown.
BIN
release/windows/Release/zltoolkit.lib
Normal file
BIN
release/windows/Release/zltoolkit.lib
Normal file
Binary file not shown.
|
@ -74,7 +74,7 @@ const string kOnHttpAccess = HOOK_FIELD"on_http_access";
|
|||
const string kAdminParams = HOOK_FIELD"admin_params";
|
||||
|
||||
onceToken token([](){
|
||||
mINI::Instance()[kEnable] = true;
|
||||
mINI::Instance()[kEnable] = false;
|
||||
mINI::Instance()[kTimeoutSec] = 10;
|
||||
mINI::Instance()[kOnPublish] = "https://127.0.0.1/index/hook/on_publish";
|
||||
mINI::Instance()[kOnPlay] = "https://127.0.0.1/index/hook/on_play";
|
||||
|
@ -318,7 +318,7 @@ void installWebHook(){
|
|||
do_http_hook(hook_stream_not_found,body, nullptr);
|
||||
});
|
||||
|
||||
#ifdef ENABLE_MP4V2
|
||||
#ifdef ENABLE_MP4RECORD
|
||||
//录制mp4文件成功后广播
|
||||
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastRecordMP4,[](BroadcastRecordMP4Args){
|
||||
if(!hook_enable || hook_record_mp4.empty()){
|
||||
|
@ -338,7 +338,7 @@ void installWebHook(){
|
|||
//执行hook
|
||||
do_http_hook(hook_record_mp4,body, nullptr);
|
||||
});
|
||||
#endif //ENABLE_MP4V2
|
||||
#endif //ENABLE_MP4RECORD
|
||||
|
||||
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastShellLogin,[](BroadcastShellLoginArgs){
|
||||
if(!hook_enable || hook_shell_login.empty() || sender.get_peer_ip() == "127.0.0.1"){
|
||||
|
|
|
@ -70,7 +70,7 @@ void MediaSink::inputFrame(const Frame::Ptr &frame) {
|
|||
it->second->inputFrame(frame);
|
||||
|
||||
if(!_allTrackReady && !_trackReadyCallback.empty() && it->second->ready()){
|
||||
//Track由未就绪状态装换成就绪状态,我们就触发onTrackReady回调
|
||||
//Track由未就绪状态转换成就绪状态,我们就触发onTrackReady回调
|
||||
auto it_callback = _trackReadyCallback.find(codec_id);
|
||||
if(it_callback != _trackReadyCallback.end()){
|
||||
it_callback->second();
|
||||
|
|
|
@ -123,6 +123,7 @@ MediaSource::Ptr MediaSource::find(
|
|||
|
||||
lock_guard<recursive_mutex> lock(g_mtxMediaSrc);
|
||||
MediaSource::Ptr ret;
|
||||
//查找某一媒体源,找到后返回
|
||||
searchMedia(schema, vhost, app, id,
|
||||
[&](SchemaVhostAppStreamMap::iterator &it0 ,
|
||||
VhostAppStreamMap::iterator &it1,
|
||||
|
@ -138,7 +139,7 @@ MediaSource::Ptr MediaSource::find(
|
|||
return true;
|
||||
});
|
||||
if(!ret && bMake){
|
||||
//查找某一媒体源,找到后返回
|
||||
//未查找媒体源,则创建一个
|
||||
ret = MediaReader::onMakeMediaSource(schema, vhost,app,id);
|
||||
}
|
||||
return ret;
|
||||
|
|
|
@ -186,6 +186,14 @@ public:
|
|||
}
|
||||
|
||||
virtual int readerCount() = 0;
|
||||
|
||||
/**
|
||||
* 获取track
|
||||
* @return
|
||||
*/
|
||||
virtual vector<Track::Ptr> getTracks(bool trackReady) const{
|
||||
return vector<Track::Ptr>(0);
|
||||
}
|
||||
protected:
|
||||
void regist() ;
|
||||
bool unregist() ;
|
||||
|
|
|
@ -161,13 +161,16 @@ namespace Rtsp {
|
|||
const string kAuthBasic = RTSP_FIELD"authBasic";
|
||||
const string kHandshakeSecond = RTSP_FIELD"handshakeSecond";
|
||||
const string kKeepAliveSecond = RTSP_FIELD"keepAliveSecond";
|
||||
const string kDirectProxy = RTSP_FIELD"directProxy";;
|
||||
const string kDirectProxy = RTSP_FIELD"directProxy";
|
||||
const string kModifyStamp = RTSP_FIELD"modifyStamp";
|
||||
|
||||
onceToken token([](){
|
||||
//默认Md5方式认证
|
||||
mINI::Instance()[kAuthBasic] = 0;
|
||||
mINI::Instance()[kHandshakeSecond] = 15;
|
||||
mINI::Instance()[kKeepAliveSecond] = 15;
|
||||
mINI::Instance()[kDirectProxy] = 1;
|
||||
mINI::Instance()[kModifyStamp] = true;
|
||||
},nullptr);
|
||||
|
||||
} //namespace Rtsp
|
||||
|
|
|
@ -209,6 +209,8 @@ extern const string kKeepAliveSecond;
|
|||
//假定您的拉流源地址不是264或265或AAC,那么你可以使用直接代理的方式来支持rtsp代理
|
||||
//默认开启rtsp直接代理,rtmp由于没有这些问题,是强制开启直接代理的
|
||||
extern const string kDirectProxy;
|
||||
//rtsp推流是否修改时间戳
|
||||
extern const string kModifyStamp;
|
||||
} //namespace Rtsp
|
||||
|
||||
////////////RTMP服务器配置///////////
|
||||
|
|
|
@ -156,10 +156,10 @@ public:
|
|||
* @param aac_cfg aac两个字节的配置信息
|
||||
*/
|
||||
AACTrack(const string &aac_cfg){
|
||||
if(aac_cfg.size() != 2){
|
||||
throw std::invalid_argument("adts配置必须为2个字节");
|
||||
if(aac_cfg.size() < 2){
|
||||
throw std::invalid_argument("adts配置必须最少2个字节");
|
||||
}
|
||||
_cfg = aac_cfg;
|
||||
_cfg = aac_cfg.substr(0,2);
|
||||
onReady();
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ using namespace toolkit;
|
|||
namespace mediakit{
|
||||
/**
|
||||
* h264 Rtmp解码类
|
||||
* 将 h264 over rtmp 解复用出 h264-Frame
|
||||
*/
|
||||
class H264RtmpDecoder : public RtmpCodec ,public ResourcePoolHelper<H264Frame> {
|
||||
public:
|
||||
|
|
|
@ -90,7 +90,24 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
|
|||
* Type==7:SPS frame
|
||||
* Type==8:PPS frame
|
||||
*/
|
||||
/*
|
||||
RTF3984 5.2节 Common Structure of the RTP Payload Format
|
||||
Table 1. Summary of NAL unit types and their payload structures
|
||||
|
||||
Type Packet Type name Section
|
||||
---------------------------------------------------------
|
||||
0 undefined -
|
||||
1-23 NAL unit Single NAL unit packet per H.264 5.6
|
||||
24 STAP-A Single-time aggregation packet 5.7.1
|
||||
25 STAP-B Single-time aggregation packet 5.7.1
|
||||
26 MTAP16 Multi-time aggregation packet 5.7.2
|
||||
27 MTAP24 Multi-time aggregation packet 5.7.2
|
||||
28 FU-A Fragmentation unit 5.8
|
||||
29 FU-B Fragmentation unit 5.8
|
||||
30-31 undefined -
|
||||
|
||||
|
||||
*/
|
||||
const uint8_t *frame = (uint8_t *) rtppack->data() + rtppack->offset;
|
||||
int length = rtppack->size() - rtppack->offset;
|
||||
NALU nal;
|
||||
|
@ -145,7 +162,7 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
|
|||
FU fu;
|
||||
MakeFU(frame[1], fu);
|
||||
if (fu.S) {
|
||||
//该帧的第一个rtp包
|
||||
//该帧的第一个rtp包 FU-A start
|
||||
char tmp = (nal.forbidden_zero_bit << 7 | nal.nal_ref_idc << 5 | fu.type);
|
||||
_h264frame->buffer.assign("\x0\x0\x0\x1", 4);
|
||||
_h264frame->buffer.push_back(tmp);
|
||||
|
@ -164,14 +181,14 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
|
|||
}
|
||||
|
||||
if (!fu.E) {
|
||||
//该帧的中间rtp包
|
||||
//该帧的中间rtp包 FU-A mid
|
||||
_h264frame->buffer.append((char *)frame + 2, length - 2);
|
||||
//该函数return时,保存下当前sequence,以便下次对比seq是否连续
|
||||
_lastSeq = rtppack->sequence;
|
||||
return false;
|
||||
}
|
||||
|
||||
//该帧最后一个rtp包
|
||||
//该帧最后一个rtp包 FU-A end
|
||||
_h264frame->buffer.append((char *)frame + 2, length - 2);
|
||||
_h264frame->timeStamp = rtppack->timeStamp;
|
||||
auto key = _h264frame->keyFrame();
|
||||
|
|
|
@ -36,6 +36,8 @@ namespace mediakit{
|
|||
|
||||
/**
|
||||
* h264 rtp解码类
|
||||
* 将 h264 over rtsp-rtp 解复用出 h264-Frame
|
||||
* rfc3984
|
||||
*/
|
||||
class H264RtpDecoder : public RtpCodec , public ResourcePoolHelper<H264Frame> {
|
||||
public:
|
||||
|
|
|
@ -37,6 +37,8 @@ namespace mediakit{
|
|||
|
||||
/**
|
||||
* h265 rtp解码类
|
||||
* 将 h265 over rtsp-rtp 解复用出 h265-Frame
|
||||
* 《草案(H265-over-RTP)draft-ietf-payload-rtp-h265-07.pdf》
|
||||
*/
|
||||
class H265RtpDecoder : public RtpCodec , public ResourcePoolHelper<H265Frame> {
|
||||
public:
|
||||
|
|
|
@ -440,6 +440,7 @@ static inline unsigned int showBitsLong(void *pvHandle, int iN)
|
|||
if (iN <= 32) {
|
||||
return showBits(ptPtr, iN);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include "Util/mini.h"
|
||||
#include "Util/util.h"
|
||||
#include "Util/TimeTicker.h"
|
||||
#include "Network/Socket.h"
|
||||
#include "Common/Parser.h"
|
||||
|
@ -47,7 +48,7 @@ class HttpCookieManager;
|
|||
/**
|
||||
* cookie对象,用于保存cookie的一些相关属性
|
||||
*/
|
||||
class HttpServerCookie : public map<string,string> , public noncopyable{
|
||||
class HttpServerCookie : public AnyStorage , public noncopyable{
|
||||
public:
|
||||
typedef std::shared_ptr<HttpServerCookie> Ptr;
|
||||
/**
|
||||
|
@ -108,6 +109,8 @@ public:
|
|||
* @return
|
||||
*/
|
||||
std::shared_ptr<lock_guard<mutex> > getLock();
|
||||
|
||||
|
||||
private:
|
||||
string cookieExpireTime() const ;
|
||||
private:
|
||||
|
|
|
@ -211,13 +211,25 @@ inline bool HttpSession::checkWebSocket(){
|
|||
if(!_parser["Sec-WebSocket-Protocol"].empty()){
|
||||
headerOut["Sec-WebSocket-Protocol"] = _parser["Sec-WebSocket-Protocol"];
|
||||
}
|
||||
|
||||
auto res_cb = [this,headerOut](){
|
||||
_flv_over_websocket = true;
|
||||
sendResponse("101 Switching Protocols",headerOut,"");
|
||||
};
|
||||
|
||||
//判断是否为websocket-flv
|
||||
if(checkLiveFlvStream(res_cb)){
|
||||
//这里是websocket-flv直播请求
|
||||
return true;
|
||||
}
|
||||
|
||||
//如果checkLiveFlvStream返回false,则代表不是websocket-flv,而是普通的websocket连接
|
||||
sendResponse("101 Switching Protocols",headerOut,"");
|
||||
checkLiveFlvStream(true);
|
||||
return true;
|
||||
}
|
||||
//http-flv 链接格式:http://vhost-url:port/app/streamid.flv?key1=value1&key2=value2
|
||||
//如果url(除去?以及后面的参数)后缀是.flv,那么表明该url是一个http-flv直播。
|
||||
inline bool HttpSession::checkLiveFlvStream(bool over_websocket){
|
||||
inline bool HttpSession::checkLiveFlvStream(const function<void()> &cb){
|
||||
auto pos = strrchr(_parser.Url().data(),'.');
|
||||
if(!pos){
|
||||
//未找到".flv"后缀
|
||||
|
@ -240,7 +252,7 @@ inline bool HttpSession::checkLiveFlvStream(bool over_websocket){
|
|||
bool bClose = (strcasecmp(_parser["Connection"].data(),"close") == 0) || ( ++_iReqCnt > reqCnt);
|
||||
|
||||
weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
|
||||
MediaSource::findAsync(_mediaInfo,weakSelf.lock(), true,[weakSelf,bClose,this,over_websocket](const MediaSource::Ptr &src){
|
||||
MediaSource::findAsync(_mediaInfo,weakSelf.lock(), true,[weakSelf,bClose,this,cb](const MediaSource::Ptr &src){
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf){
|
||||
//本对象已经销毁
|
||||
|
@ -249,35 +261,32 @@ inline bool HttpSession::checkLiveFlvStream(bool over_websocket){
|
|||
auto rtmp_src = dynamic_pointer_cast<RtmpMediaSource>(src);
|
||||
if(!rtmp_src){
|
||||
//未找到该流
|
||||
if(!over_websocket){
|
||||
sendNotFound(bClose);
|
||||
}
|
||||
if(bClose){
|
||||
shutdown(SockException(Err_shutdown,"flv stream not found"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
//找到流了
|
||||
auto onRes = [this,rtmp_src,over_websocket](const string &err){
|
||||
auto onRes = [this,rtmp_src,cb](const string &err){
|
||||
bool authSuccess = err.empty();
|
||||
if(!authSuccess){
|
||||
if(!over_websocket){
|
||||
sendResponse("401 Unauthorized", makeHttpHeader(true,err.size()),err);
|
||||
}
|
||||
shutdown(SockException(Err_shutdown,StrPrinter << "401 Unauthorized:" << err));
|
||||
return ;
|
||||
}
|
||||
|
||||
if(!over_websocket) {
|
||||
if(!cb) {
|
||||
//找到rtmp源,发送http头,负载后续发送
|
||||
sendResponse("200 OK", makeHttpHeader(false, 0, get_mime_type(".flv")), "");
|
||||
}else{
|
||||
cb();
|
||||
}
|
||||
|
||||
//开始发送rtmp负载
|
||||
//关闭tcp_nodelay ,优化性能
|
||||
SockUtil::setNoDelay(_sock->rawFD(),false);
|
||||
(*this) << SocketFlags(kSockFlags);
|
||||
_flv_over_websocket = over_websocket;
|
||||
try{
|
||||
start(getPoller(),rtmp_src);
|
||||
}catch (std::exception &ex){
|
||||
|
@ -403,7 +412,7 @@ inline void HttpSession::canAccessPath(const string &path_in,bool is_dir,const f
|
|||
//上次鉴权失败,如果url发生变更,那么也重新鉴权
|
||||
if (_parser.Params().empty() || _parser.Params() == cookie->getUid()) {
|
||||
//url参数未变,那么判断无权限访问
|
||||
callback(accessErr.empty() ? "无权限访问该目录" : accessErr, nullptr);
|
||||
callback(accessErr.empty() ? "无权限访问该目录" : accessErr.get<string>(), nullptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -427,9 +436,9 @@ inline void HttpSession::canAccessPath(const string &path_in,bool is_dir,const f
|
|||
//对cookie上锁
|
||||
auto lck = cookie->getLock();
|
||||
//记录用户能访问的路径
|
||||
(*cookie)[kCookiePathKey] = cookie_path;
|
||||
(*cookie)[kCookiePathKey].set<string>(cookie_path);
|
||||
//记录能否访问
|
||||
(*cookie)[kAccessErrKey] = errMsg;
|
||||
(*cookie)[kAccessErrKey].set<string>(errMsg);
|
||||
}
|
||||
|
||||
auto strongSelf = weakSelf.lock();
|
||||
|
@ -480,7 +489,8 @@ inline void HttpSession::Handle_Req_GET(int64_t &content_len) {
|
|||
}
|
||||
|
||||
//再看看是否为http-flv直播请求
|
||||
if(checkLiveFlvStream(false)){
|
||||
if(checkLiveFlvStream()){
|
||||
//若是,return!
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -520,7 +530,7 @@ inline void HttpSession::Handle_Req_GET(int64_t &content_len) {
|
|||
}
|
||||
auto headerOut = makeHttpHeader(bClose,strMeun.size());
|
||||
if(cookie){
|
||||
headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey]);
|
||||
headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get<string>());
|
||||
}
|
||||
sendResponse(errMsg.empty() ? "200 OK" : "401 Unauthorized" , headerOut, strMeun);
|
||||
throw SockException(bClose ? Err_shutdown : Err_success,"close connection after access folder");
|
||||
|
@ -555,7 +565,7 @@ inline void HttpSession::Handle_Req_GET(int64_t &content_len) {
|
|||
if(!errMsg.empty()){
|
||||
auto headerOut = makeHttpHeader(bClose,errMsg.size());
|
||||
if(cookie){
|
||||
headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey]);
|
||||
headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get<string>());
|
||||
}
|
||||
sendResponse("401 Unauthorized" , headerOut, errMsg);
|
||||
throw SockException(bClose ? Err_shutdown : Err_success,"close connection after access file failed");
|
||||
|
@ -954,12 +964,12 @@ void HttpSession::onWrite(const Buffer::Ptr &buffer) {
|
|||
header._reserved = 0;
|
||||
header._opcode = WebSocketHeader::BINARY;
|
||||
header._mask_flag = false;
|
||||
WebSocketSplitter::encode(header,(uint8_t *)buffer->data(),buffer->size());
|
||||
WebSocketSplitter::encode(header,buffer);
|
||||
}
|
||||
|
||||
void HttpSession::onWebSocketEncodeData(const uint8_t *ptr,uint64_t len){
|
||||
_ui64TotalBytes += len;
|
||||
SocketHelper::send((char *)ptr,len);
|
||||
void HttpSession::onWebSocketEncodeData(const Buffer::Ptr &buffer){
|
||||
_ui64TotalBytes += buffer->size();
|
||||
send(buffer);
|
||||
}
|
||||
|
||||
void HttpSession::onDetach() {
|
||||
|
|
|
@ -104,14 +104,13 @@ protected:
|
|||
|
||||
/**
|
||||
* 发送数据进行websocket协议打包后回调
|
||||
* @param ptr
|
||||
* @param len
|
||||
* @param buffer
|
||||
*/
|
||||
void onWebSocketEncodeData(const uint8_t *ptr,uint64_t len) override;
|
||||
void onWebSocketEncodeData(const Buffer::Ptr &buffer) override;
|
||||
private:
|
||||
inline void Handle_Req_GET(int64_t &content_len);
|
||||
inline void Handle_Req_POST(int64_t &content_len);
|
||||
inline bool checkLiveFlvStream(bool over_websocket = false);
|
||||
inline bool checkLiveFlvStream(const function<void()> &cb = nullptr);
|
||||
inline bool checkWebSocket();
|
||||
inline bool emitHttpEvent(bool doInvoke);
|
||||
inline void urlDecode(Parser &parser);
|
||||
|
|
|
@ -89,7 +89,7 @@ protected:
|
|||
header._reserved = 0;
|
||||
header._opcode = WebSocketHeader::TEXT;
|
||||
header._mask_flag = false;
|
||||
strongSelf->WebSocketSplitter::encode(header,(uint8_t *)buf->data(),buf->size());
|
||||
strongSelf->WebSocketSplitter::encode(header,buf);
|
||||
}
|
||||
return buf->size();
|
||||
});
|
||||
|
@ -118,12 +118,12 @@ protected:
|
|||
|
||||
switch (header._opcode){
|
||||
case WebSocketHeader::CLOSE:{
|
||||
HttpSessionType::encode(header,nullptr,0);
|
||||
HttpSessionType::encode(header,nullptr);
|
||||
}
|
||||
break;
|
||||
case WebSocketHeader::PING:{
|
||||
const_cast<WebSocketHeader&>(header)._opcode = WebSocketHeader::PONG;
|
||||
HttpSessionType::encode(header,(uint8_t *)_remian_data.data(),_remian_data.size());
|
||||
HttpSessionType::encode(header,std::make_shared<BufferString>(_remian_data));
|
||||
}
|
||||
break;
|
||||
case WebSocketHeader::CONTINUATION:{
|
||||
|
@ -132,8 +132,7 @@ protected:
|
|||
break;
|
||||
case WebSocketHeader::TEXT:
|
||||
case WebSocketHeader::BINARY:{
|
||||
BufferString::Ptr buffer = std::make_shared<BufferString>(_remian_data);
|
||||
_session->onRecv(buffer);
|
||||
_session->onRecv(std::make_shared<BufferString>(_remian_data));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -145,11 +144,10 @@ protected:
|
|||
|
||||
/**
|
||||
* 发送数据进行websocket协议打包后回调
|
||||
* @param ptr
|
||||
* @param len
|
||||
* @param buffer
|
||||
*/
|
||||
void onWebSocketEncodeData(const uint8_t *ptr,uint64_t len) override{
|
||||
SocketHelper::send((char *)ptr,len);
|
||||
void onWebSocketEncodeData(const Buffer::Ptr &buffer) override{
|
||||
SocketHelper::send(buffer);
|
||||
}
|
||||
private:
|
||||
typedef function<int(const Buffer::Ptr &buf)> onBeforeSendCB;
|
||||
|
|
|
@ -164,9 +164,9 @@ void WebSocketSplitter::onPlayloadData(uint8_t *ptr, uint64_t len) {
|
|||
onWebSocketDecodePlayload(*this, _mask_flag ? ptr - len : ptr, len, _playload_offset);
|
||||
}
|
||||
|
||||
void WebSocketSplitter::encode(const WebSocketHeader &header,uint8_t *data, const uint64_t len) {
|
||||
void WebSocketSplitter::encode(const WebSocketHeader &header,const Buffer::Ptr &buffer) {
|
||||
string ret;
|
||||
|
||||
uint64_t len = buffer ? buffer->size() : 0;
|
||||
uint8_t byte = header._fin << 7 | ((header._reserved & 0x07) << 4) | (header._opcode & 0x0F) ;
|
||||
ret.push_back(byte);
|
||||
|
||||
|
@ -195,16 +195,16 @@ void WebSocketSplitter::encode(const WebSocketHeader &header,uint8_t *data, cons
|
|||
ret.append((char *)header._mask.data(),4);
|
||||
}
|
||||
|
||||
onWebSocketEncodeData((uint8_t*)ret.data(),ret.size());
|
||||
onWebSocketEncodeData(std::make_shared<BufferString>(std::move(ret)));
|
||||
|
||||
if(len > 0){
|
||||
if(mask_flag){
|
||||
uint8_t *ptr = data;
|
||||
uint8_t *ptr = (uint8_t*)buffer->data();
|
||||
for(int i = 0; i < len ; ++i,++ptr){
|
||||
*(ptr) ^= header._mask[i % 4];
|
||||
}
|
||||
}
|
||||
onWebSocketEncodeData(data,len);
|
||||
onWebSocketEncodeData(buffer);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,8 +31,10 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
using namespace std;
|
||||
#include "Network/Buffer.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace toolkit;
|
||||
|
||||
namespace mediakit {
|
||||
|
||||
|
@ -85,12 +87,10 @@ public:
|
|||
/**
|
||||
* 编码一个数据包
|
||||
* 将触发2次onWebSocketEncodeData回调
|
||||
* 第一次是数据头,第二次是负载数据
|
||||
* @param header 数据头
|
||||
* @param data 负载数据
|
||||
* @param len 负载数据长度
|
||||
* @param buffer 负载数据
|
||||
*/
|
||||
void encode(const WebSocketHeader &header,uint8_t *data,const uint64_t len);
|
||||
void encode(const WebSocketHeader &header,const Buffer::Ptr &buffer);
|
||||
protected:
|
||||
/**
|
||||
* 收到一个webSocket数据包包头,后续将继续触发onWebSocketDecodePlayload回调
|
||||
|
@ -119,7 +119,7 @@ protected:
|
|||
* @param ptr 数据指针
|
||||
* @param len 数据指针长度
|
||||
*/
|
||||
virtual void onWebSocketEncodeData(const uint8_t *ptr,uint64_t len){};
|
||||
virtual void onWebSocketEncodeData(const Buffer::Ptr &buffer){};
|
||||
private:
|
||||
void onPlayloadData(uint8_t *data,uint64_t len);
|
||||
private:
|
||||
|
|
|
@ -135,9 +135,7 @@ void MP4Muxer::onTrackReady(const Track::Ptr &track) {
|
|||
WarnL << "添加AAC Track失败:" << track_id;
|
||||
return;
|
||||
}
|
||||
track_info info;
|
||||
info.track_id = track_id;
|
||||
_codec_to_trackid[track->getCodecId()] = info;
|
||||
_codec_to_trackid[track->getCodecId()].track_id = track_id;
|
||||
}
|
||||
break;
|
||||
case CodecH264: {
|
||||
|
@ -170,9 +168,7 @@ void MP4Muxer::onTrackReady(const Track::Ptr &track) {
|
|||
WarnL << "添加H264 Track失败:" << track_id;
|
||||
return;
|
||||
}
|
||||
track_info info;
|
||||
info.track_id = track_id;
|
||||
_codec_to_trackid[track->getCodecId()] = info;
|
||||
_codec_to_trackid[track->getCodecId()].track_id = track_id;
|
||||
}
|
||||
break;
|
||||
case CodecH265: {
|
||||
|
@ -205,9 +201,7 @@ void MP4Muxer::onTrackReady(const Track::Ptr &track) {
|
|||
WarnL << "添加H265 Track失败:" << track_id;
|
||||
return;
|
||||
}
|
||||
track_info info;
|
||||
info.track_id = track_id;
|
||||
_codec_to_trackid[track->getCodecId()] = info;
|
||||
_codec_to_trackid[track->getCodecId()].track_id = track_id;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include <ctime>
|
||||
#include <sys/stat.h>
|
||||
#include "Common/config.h"
|
||||
#include "Mp4Maker.h"
|
||||
#include "MP4Recorder.h"
|
||||
#include "Util/util.h"
|
||||
#include "Util/NoticeCenter.h"
|
||||
#include "Thread/WorkThreadPool.h"
|
||||
|
@ -53,7 +53,7 @@ string timeStr(const char *fmt) {
|
|||
return buffer;
|
||||
}
|
||||
|
||||
Mp4Maker::Mp4Maker(const string& strPath,
|
||||
MP4Recorder::MP4Recorder(const string& strPath,
|
||||
const string &strVhost,
|
||||
const string &strApp,
|
||||
const string &strStreamId) {
|
||||
|
@ -64,11 +64,11 @@ Mp4Maker::Mp4Maker(const string& strPath,
|
|||
_info.strVhost = strVhost;
|
||||
_info.strFolder = strPath;
|
||||
}
|
||||
Mp4Maker::~Mp4Maker() {
|
||||
MP4Recorder::~MP4Recorder() {
|
||||
closeFile();
|
||||
}
|
||||
|
||||
void Mp4Maker::createFile() {
|
||||
void MP4Recorder::createFile() {
|
||||
closeFile();
|
||||
auto strDate = timeStr("%Y-%m-%d");
|
||||
auto strTime = timeStr("%H-%M-%S");
|
||||
|
@ -100,7 +100,7 @@ void Mp4Maker::createFile() {
|
|||
}
|
||||
}
|
||||
|
||||
void Mp4Maker::asyncClose() {
|
||||
void MP4Recorder::asyncClose() {
|
||||
auto muxer = _muxer;
|
||||
auto strFileTmp = _strFileTmp;
|
||||
auto strFile = _strFile;
|
||||
|
@ -121,14 +121,14 @@ void Mp4Maker::asyncClose() {
|
|||
});
|
||||
}
|
||||
|
||||
void Mp4Maker::closeFile() {
|
||||
void MP4Recorder::closeFile() {
|
||||
if (_muxer) {
|
||||
asyncClose();
|
||||
_muxer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Mp4Maker::onTrackFrame(const Frame::Ptr &frame) {
|
||||
void MP4Recorder::onTrackFrame(const Frame::Ptr &frame) {
|
||||
GET_CONFIG(uint32_t,recordSec,Record::kFileSecond);
|
||||
if(!_muxer || ((_createFileTicker.elapsedTime() > recordSec * 1000) &&
|
||||
(!_haveVideo || (_haveVideo && frame->keyFrame()))) ){
|
||||
|
@ -145,7 +145,7 @@ void Mp4Maker::onTrackFrame(const Frame::Ptr &frame) {
|
|||
}
|
||||
}
|
||||
|
||||
void Mp4Maker::onTrackReady(const Track::Ptr & track){
|
||||
void MP4Recorder::onTrackReady(const Track::Ptr & track){
|
||||
//保存所有的track,为创建MP4MuxerFile做准备
|
||||
_tracks.emplace_back(track);
|
||||
if(track->getTrackType() == TrackVideo){
|
|
@ -55,14 +55,14 @@ public:
|
|||
string strStreamId;//流ID
|
||||
string strVhost;//vhost
|
||||
};
|
||||
class Mp4Maker : public MediaSink{
|
||||
class MP4Recorder : public MediaSink{
|
||||
public:
|
||||
typedef std::shared_ptr<Mp4Maker> Ptr;
|
||||
Mp4Maker(const string &strPath,
|
||||
typedef std::shared_ptr<MP4Recorder> Ptr;
|
||||
MP4Recorder(const string &strPath,
|
||||
const string &strVhost ,
|
||||
const string &strApp,
|
||||
const string &strStreamId);
|
||||
virtual ~Mp4Maker();
|
||||
virtual ~MP4Recorder();
|
||||
private:
|
||||
/**
|
||||
* 某Track输出frame,在onAllTrackReady触发后才会调用此方法
|
|
@ -58,15 +58,15 @@ MediaRecorder::MediaRecorder(const string &strVhost_tmp,
|
|||
string m3u8FilePath;
|
||||
if(enableVhost){
|
||||
m3u8FilePath = hlsPath + "/" + strVhost + "/" + strApp + "/" + strId + "/hls.m3u8";
|
||||
_hlsMaker.reset(new HlsRecorder(m3u8FilePath,string(VHOST_KEY) + "=" + strVhost ,hlsBufSize, hlsDuration, hlsNum));
|
||||
_hlsRecorder.reset(new HlsRecorder(m3u8FilePath,string(VHOST_KEY) + "=" + strVhost ,hlsBufSize, hlsDuration, hlsNum));
|
||||
}else{
|
||||
m3u8FilePath = hlsPath + "/" + strApp + "/" + strId + "/hls.m3u8";
|
||||
_hlsMaker.reset(new HlsRecorder(m3u8FilePath,"",hlsBufSize, hlsDuration, hlsNum));
|
||||
_hlsRecorder.reset(new HlsRecorder(m3u8FilePath,"",hlsBufSize, hlsDuration, hlsNum));
|
||||
}
|
||||
}
|
||||
#endif //defined(ENABLE_HLS)
|
||||
|
||||
#if defined(ENABLE_MP4V2)
|
||||
#if defined(ENABLE_MP4RECORD)
|
||||
GET_CONFIG(string,recordPath,Record::kFilePath);
|
||||
GET_CONFIG(string,recordAppName,Record::kAppName);
|
||||
|
||||
|
@ -77,9 +77,9 @@ MediaRecorder::MediaRecorder(const string &strVhost_tmp,
|
|||
} else {
|
||||
mp4FilePath = recordPath + "/" + recordAppName + "/" + strApp + "/" + strId + "/";
|
||||
}
|
||||
_mp4Maker.reset(new Mp4Maker(mp4FilePath,strVhost,strApp,strId));
|
||||
_mp4Recorder.reset(new MP4Recorder(mp4FilePath,strVhost,strApp,strId));
|
||||
}
|
||||
#endif //defined(ENABLE_MP4V2)
|
||||
#endif //defined(ENABLE_MP4RECORD)
|
||||
}
|
||||
|
||||
MediaRecorder::~MediaRecorder() {
|
||||
|
@ -87,28 +87,28 @@ MediaRecorder::~MediaRecorder() {
|
|||
|
||||
void MediaRecorder::inputFrame(const Frame::Ptr &frame) {
|
||||
#if defined(ENABLE_HLS)
|
||||
if (_hlsMaker) {
|
||||
_hlsMaker->inputFrame(frame);
|
||||
}
|
||||
#endif //defined(ENABLE_HLS)
|
||||
|
||||
#if defined(ENABLE_MP4V2)
|
||||
if (_mp4Maker) {
|
||||
_mp4Maker->inputFrame(frame);
|
||||
}
|
||||
#endif //defined(ENABLE_MP4V2)
|
||||
}
|
||||
|
||||
void MediaRecorder::addTrack(const Track::Ptr &track) {
|
||||
#if defined(ENABLE_HLS)
|
||||
if (_hlsMaker) {
|
||||
_hlsMaker->addTrack(track);
|
||||
if (_hlsRecorder) {
|
||||
_hlsRecorder->inputFrame(frame);
|
||||
}
|
||||
#endif //defined(ENABLE_HLS)
|
||||
|
||||
#if defined(ENABLE_MP4RECORD)
|
||||
if (_mp4Maker) {
|
||||
_mp4Maker->addTrack(track);
|
||||
if (_mp4Recorder) {
|
||||
_mp4Recorder->inputFrame(frame);
|
||||
}
|
||||
#endif //defined(ENABLE_MP4RECORD)
|
||||
}
|
||||
|
||||
void MediaRecorder::addTrack(const Track::Ptr &track) {
|
||||
#if defined(ENABLE_HLS)
|
||||
if (_hlsRecorder) {
|
||||
_hlsRecorder->addTrack(track);
|
||||
}
|
||||
#endif //defined(ENABLE_HLS)
|
||||
|
||||
#if defined(ENABLE_MP4RECORD)
|
||||
if (_mp4Recorder) {
|
||||
_mp4Recorder->addTrack(track);
|
||||
}
|
||||
#endif //defined(ENABLE_MP4RECORD)
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#include <memory>
|
||||
#include "Player/PlayerBase.h"
|
||||
#include "Common/MediaSink.h"
|
||||
#include "Mp4Maker.h"
|
||||
#include "MP4Recorder.h"
|
||||
#include "HlsRecorder.h"
|
||||
|
||||
using namespace toolkit;
|
||||
|
@ -61,11 +61,11 @@ public:
|
|||
void addTrack(const Track::Ptr & track) override;
|
||||
private:
|
||||
#if defined(ENABLE_HLS)
|
||||
std::shared_ptr<HlsRecorder> _hlsMaker;
|
||||
std::shared_ptr<HlsRecorder> _hlsRecorder;
|
||||
#endif //defined(ENABLE_HLS)
|
||||
|
||||
#if defined(ENABLE_MP4RECORD)
|
||||
std::shared_ptr<Mp4Maker> _mp4Maker;
|
||||
std::shared_ptr<MP4Recorder> _mp4Recorder;
|
||||
#endif //defined(ENABLE_MP4RECORD)
|
||||
};
|
||||
|
||||
|
|
|
@ -29,16 +29,22 @@
|
|||
namespace mediakit {
|
||||
|
||||
void Stamp::revise(uint32_t dts, uint32_t pts, int64_t &dts_out, int64_t &pts_out) {
|
||||
if(!pts){
|
||||
//没有播放时间戳,使其赋值为解码时间戳
|
||||
pts = dts;
|
||||
}
|
||||
//pts和dts的差值
|
||||
int pts_dts_diff = pts - dts;
|
||||
|
||||
if(_first){
|
||||
//记录第一次时间戳,后面好计算时间戳增量
|
||||
_start_dts = dts;
|
||||
_first = false;
|
||||
_ticker = std::make_shared<SmoothTicker>();
|
||||
_ticker.resetTime();
|
||||
}
|
||||
//pts和dts的差值
|
||||
int pts_dts_diff = pts - dts;
|
||||
if(_modifyStamp){
|
||||
dts = _ticker->elapsedTime();
|
||||
if (!dts) {
|
||||
//没有解码时间戳,我们生成解码时间戳
|
||||
dts = _ticker.elapsedTime();
|
||||
}
|
||||
|
||||
//相对时间戳
|
||||
|
@ -60,11 +66,6 @@ void Stamp::revise(uint32_t dts, uint32_t pts, int64_t &dts_out, int64_t &pts_ou
|
|||
_dts_inc = dts_out;
|
||||
|
||||
//////////////以下是播放时间戳的计算//////////////////
|
||||
if(!pts){
|
||||
//没有播放时间戳
|
||||
pts = dts;
|
||||
}
|
||||
|
||||
if(pts_dts_diff > 200 || pts_dts_diff < -200){
|
||||
//如果差值大于200毫秒,则认为由于回环导致时间戳错乱了
|
||||
pts_dts_diff = 0;
|
||||
|
|
|
@ -33,17 +33,18 @@ using namespace toolkit;
|
|||
|
||||
namespace mediakit {
|
||||
|
||||
//该类解决时间戳回环、回退问题
|
||||
//计算相对时间戳或者产生平滑时间戳
|
||||
class Stamp {
|
||||
public:
|
||||
Stamp(bool modifyStamp = false) {_modifyStamp = modifyStamp;};
|
||||
Stamp() = default;
|
||||
~Stamp() = default;
|
||||
void revise(uint32_t dts, uint32_t pts, int64_t &dts_out, int64_t &pts_out);
|
||||
private:
|
||||
int64_t _start_dts = 0;
|
||||
int64_t _dts_inc = 0;
|
||||
bool _first = true;
|
||||
bool _modifyStamp;
|
||||
std::shared_ptr<SmoothTicker> _ticker;
|
||||
SmoothTicker _ticker;
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
|
|
|
@ -42,19 +42,13 @@ TsMuxer::~TsMuxer() {
|
|||
void TsMuxer::addTrack(const Track::Ptr &track) {
|
||||
switch (track->getCodecId()){
|
||||
case CodecH264: {
|
||||
track_info info;
|
||||
info.track_id = mpeg_ts_add_stream(_context, PSI_STREAM_H264, nullptr, 0);
|
||||
_codec_to_trackid[track->getCodecId()] = info;
|
||||
_codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_H264, nullptr, 0);
|
||||
} break;
|
||||
case CodecH265: {
|
||||
track_info info;
|
||||
info.track_id = mpeg_ts_add_stream(_context, PSI_STREAM_H265, nullptr, 0);
|
||||
_codec_to_trackid[track->getCodecId()] = info;
|
||||
_codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_H265, nullptr, 0);
|
||||
}break;
|
||||
case CodecAAC: {
|
||||
track_info info;
|
||||
info.track_id = mpeg_ts_add_stream(_context, PSI_STREAM_AAC, nullptr, 0);
|
||||
_codec_to_trackid[track->getCodecId()] = info;
|
||||
_codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_AAC, nullptr, 0);
|
||||
}break;
|
||||
default:
|
||||
break;
|
||||
|
@ -73,38 +67,28 @@ void TsMuxer::inputFrame(const Frame::Ptr &frame) {
|
|||
switch (frame->getCodecId()){
|
||||
case CodecH265:
|
||||
case CodecH264: {
|
||||
|
||||
Buffer::Ptr merged_frame ;
|
||||
if(frame->configFrame()){
|
||||
//配置帧,缓存后直接返回,以便下次输入关键帧时使用
|
||||
_config_frame_cache.append("\x00\x00\x00\x01",4);
|
||||
_config_frame_cache.append(frame->data() + frame->prefixSize(),frame->size() - frame->prefixSize());
|
||||
break;
|
||||
}
|
||||
|
||||
if(frame->keyFrame()){
|
||||
//关键帧
|
||||
if(!_config_frame_cache.empty()){
|
||||
//有配置帧,那么配置帧合并关键帧后输入ts打包
|
||||
_config_frame_cache.append("\x00\x00\x00\x01",4);
|
||||
_config_frame_cache.append(frame->data() + frame->prefixSize(),frame->size() - frame->prefixSize());
|
||||
merged_frame = std::make_shared<BufferString>(std::move(_config_frame_cache));
|
||||
_config_frame_cache.clear();
|
||||
//这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理,
|
||||
if (!_frameCached.empty() && _frameCached.back()->dts() != frame->dts()) {
|
||||
Frame::Ptr back = _frameCached.back();
|
||||
Buffer::Ptr merged_frame = back;
|
||||
if(_frameCached.size() != 1){
|
||||
string merged;
|
||||
_frameCached.for_each([&](const Frame::Ptr &frame){
|
||||
if(frame->prefixSize()){
|
||||
merged.append(frame->data(),frame->size());
|
||||
} else{
|
||||
//这是非第一个的关键帧(h265有多种关键帧)
|
||||
merged_frame = frame;
|
||||
merged.append("\x00\x00\x00\x01",4);
|
||||
merged.append(frame->data(),frame->size());
|
||||
}
|
||||
}else{
|
||||
//这里是普通帧,例如B/P,
|
||||
merged_frame = frame;
|
||||
//sps、pps这些配置帧清空掉
|
||||
_config_frame_cache.clear();
|
||||
});
|
||||
merged_frame = std::make_shared<BufferString>(std::move(merged));
|
||||
}
|
||||
|
||||
//输入到ts文件
|
||||
track_info.stamp.revise(frame->dts(),frame->pts(),dts_out,pts_out);
|
||||
track_info.stamp.revise(back->dts(),back->pts(),dts_out,pts_out);
|
||||
_timestamp = dts_out;
|
||||
mpeg_ts_write(_context, track_info.track_id, frame->keyFrame() ? 0x0001 : 0, pts_out * 90LL, dts_out * 90LL, merged_frame->data(), merged_frame->size());
|
||||
mpeg_ts_write(_context, track_info.track_id, back->keyFrame() ? 0x0001 : 0, pts_out * 90LL, dts_out * 90LL, merged_frame->data(), merged_frame->size());
|
||||
_frameCached.clear();
|
||||
}
|
||||
_frameCached.emplace_back(Frame::getCacheAbleFrame(frame));
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
|
|
|
@ -38,12 +38,12 @@ using namespace toolkit;
|
|||
|
||||
namespace mediakit {
|
||||
|
||||
class TsMuxer {
|
||||
class TsMuxer : public MediaSink {
|
||||
public:
|
||||
TsMuxer();
|
||||
virtual ~TsMuxer();
|
||||
void addTrack(const Track::Ptr &track) ;
|
||||
void inputFrame(const Frame::Ptr &frame) ;
|
||||
void addTrack(const Track::Ptr &track) override;
|
||||
void inputFrame(const Frame::Ptr &frame) override;
|
||||
protected:
|
||||
virtual void onTs(const void *packet, int bytes,uint32_t timestamp,int flags) = 0;
|
||||
void resetTracks();
|
||||
|
@ -60,7 +60,7 @@ private:
|
|||
Stamp stamp;
|
||||
};
|
||||
unordered_map<int,track_info> _codec_to_trackid;
|
||||
string _config_frame_cache;
|
||||
List<Frame::Ptr> _frameCached;
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
|
|
|
@ -76,8 +76,6 @@ void FlvMuxer::start(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &
|
|||
}
|
||||
|
||||
void FlvMuxer::onWriteFlvHeader(const RtmpMediaSource::Ptr &mediaSrc) {
|
||||
CLEAR_ARR(_aui32FirstStamp);
|
||||
|
||||
//发送flv文件头
|
||||
char flv_file_header[] = "FLV\x1\x5\x0\x0\x0\x9"; // have audio and have video
|
||||
bool is_have_audio = false,is_have_video = false;
|
||||
|
@ -158,20 +156,9 @@ void FlvMuxer::onWriteFlvTag(uint8_t ui8Type, const Buffer::Ptr &buffer, uint32_
|
|||
}
|
||||
|
||||
void FlvMuxer::onWriteRtmp(const RtmpPacket::Ptr &pkt) {
|
||||
auto modifiedStamp = pkt->timeStamp;
|
||||
auto &firstStamp = _aui32FirstStamp[pkt->typeId % 2];
|
||||
if(!firstStamp){
|
||||
firstStamp = modifiedStamp;
|
||||
}
|
||||
if(modifiedStamp >= firstStamp){
|
||||
//计算时间戳增量
|
||||
modifiedStamp -= firstStamp;
|
||||
}else{
|
||||
//发生回环,重新计算时间戳增量
|
||||
CLEAR_ARR(_aui32FirstStamp);
|
||||
modifiedStamp = 0;
|
||||
}
|
||||
onWriteFlvTag(pkt, modifiedStamp);
|
||||
int64_t dts_out;
|
||||
_stamp[pkt->typeId % 2].revise(pkt->timeStamp, 0, dts_out, dts_out);
|
||||
onWriteFlvTag(pkt, dts_out);
|
||||
}
|
||||
|
||||
void FlvMuxer::stop() {
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "Rtmp/Rtmp.h"
|
||||
#include "Rtmp/RtmpMediaSource.h"
|
||||
#include "Network/Socket.h"
|
||||
#include "MediaFile/Stamp.h"
|
||||
using namespace toolkit;
|
||||
|
||||
namespace mediakit {
|
||||
|
@ -52,7 +53,8 @@ private:
|
|||
void onWriteFlvTag(uint8_t ui8Type, const Buffer::Ptr &buffer, uint32_t ui32TimeStamp);
|
||||
private:
|
||||
RtmpMediaSource::RingType::RingReader::Ptr _ring_reader;
|
||||
uint32_t _aui32FirstStamp[2] = {0};
|
||||
//时间戳修整器
|
||||
Stamp _stamp[2];
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ using namespace toolkit;
|
|||
using namespace mediakit::Client;
|
||||
|
||||
namespace mediakit {
|
||||
|
||||
//实现了rtmp播放器协议部分的功能,及数据接收功能
|
||||
class RtmpPlayer:public PlayerBase, public TcpClient, public RtmpProtocol{
|
||||
public:
|
||||
typedef std::shared_ptr<RtmpPlayer> Ptr;
|
||||
|
@ -63,11 +63,11 @@ protected:
|
|||
void onMediaData_l(const RtmpPacket::Ptr &chunkData);
|
||||
void onPlayResult_l(const SockException &ex);
|
||||
|
||||
//for Tcpclient
|
||||
//form Tcpclient
|
||||
void onRecv(const Buffer::Ptr &pBuf) override;
|
||||
void onConnect(const SockException &err) override;
|
||||
void onErr(const SockException &ex) override;
|
||||
//fro RtmpProtocol
|
||||
//from RtmpProtocol
|
||||
void onRtmpChunk(RtmpPacket &chunkData) override;
|
||||
void onStreamDry(uint32_t ui32StreamId) override;
|
||||
void onSendRawData(const Buffer::Ptr &buffer) override{
|
||||
|
|
|
@ -532,10 +532,6 @@ void RtmpProtocol::handle_rtmp() {
|
|||
static const size_t HEADER_LENGTH[] = { 12, 8, 4, 1 };
|
||||
size_t iHeaderLen = HEADER_LENGTH[flags >> 6];
|
||||
_iNowChunkID = flags & 0x3f;
|
||||
if(_iNowChunkID >10){
|
||||
int i=0;
|
||||
i++;
|
||||
}
|
||||
switch (_iNowChunkID) {
|
||||
case 0: {
|
||||
//0 值表示二字节形式,并且 ID 范围 64 - 319
|
||||
|
|
|
@ -63,7 +63,7 @@ void RtmpSession::onError(const SockException& err) {
|
|||
}
|
||||
|
||||
void RtmpSession::onManager() {
|
||||
GET_CONFIG(uint32_t,handshake_sec,Rtmp::kKeepAliveSecond);
|
||||
GET_CONFIG(uint32_t,handshake_sec,Rtmp::kHandshakeSecond);
|
||||
GET_CONFIG(uint32_t,keep_alive_sec,Rtmp::kKeepAliveSecond);
|
||||
|
||||
if (_ticker.createdTime() > handshake_sec * 1000) {
|
||||
|
@ -397,20 +397,20 @@ void RtmpSession::setMetaData(AMFDecoder &dec) {
|
|||
|
||||
void RtmpSession::onProcessCmd(AMFDecoder &dec) {
|
||||
typedef void (RtmpSession::*rtmpCMDHandle)(AMFDecoder &dec);
|
||||
static unordered_map<string, rtmpCMDHandle> g_mapCmd;
|
||||
static unordered_map<string, rtmpCMDHandle> s_cmd_functions;
|
||||
static onceToken token([]() {
|
||||
g_mapCmd.emplace("connect",&RtmpSession::onCmd_connect);
|
||||
g_mapCmd.emplace("createStream",&RtmpSession::onCmd_createStream);
|
||||
g_mapCmd.emplace("publish",&RtmpSession::onCmd_publish);
|
||||
g_mapCmd.emplace("deleteStream",&RtmpSession::onCmd_deleteStream);
|
||||
g_mapCmd.emplace("play",&RtmpSession::onCmd_play);
|
||||
g_mapCmd.emplace("play2",&RtmpSession::onCmd_play2);
|
||||
g_mapCmd.emplace("seek",&RtmpSession::onCmd_seek);
|
||||
g_mapCmd.emplace("pause",&RtmpSession::onCmd_pause);}, []() {});
|
||||
s_cmd_functions.emplace("connect",&RtmpSession::onCmd_connect);
|
||||
s_cmd_functions.emplace("createStream",&RtmpSession::onCmd_createStream);
|
||||
s_cmd_functions.emplace("publish",&RtmpSession::onCmd_publish);
|
||||
s_cmd_functions.emplace("deleteStream",&RtmpSession::onCmd_deleteStream);
|
||||
s_cmd_functions.emplace("play",&RtmpSession::onCmd_play);
|
||||
s_cmd_functions.emplace("play2",&RtmpSession::onCmd_play2);
|
||||
s_cmd_functions.emplace("seek",&RtmpSession::onCmd_seek);
|
||||
s_cmd_functions.emplace("pause",&RtmpSession::onCmd_pause);}, []() {});
|
||||
|
||||
std::string method = dec.load<std::string>();
|
||||
auto it = g_mapCmd.find(method);
|
||||
if (it == g_mapCmd.end()) {
|
||||
auto it = s_cmd_functions.find(method);
|
||||
if (it == s_cmd_functions.end()) {
|
||||
TraceP(this) << "can not support cmd:" << method;
|
||||
return;
|
||||
}
|
||||
|
@ -445,7 +445,9 @@ void RtmpSession::onRtmpChunk(RtmpPacket &chunkData) {
|
|||
}
|
||||
GET_CONFIG(bool,rtmp_modify_stamp,Rtmp::kModifyStamp);
|
||||
if(rtmp_modify_stamp){
|
||||
chunkData.timeStamp = _stampTicker[chunkData.typeId % 2].elapsedTime();
|
||||
int64_t dts_out;
|
||||
_stamp[chunkData.typeId % 2].revise(0, 0, dts_out, dts_out);
|
||||
chunkData.timeStamp = dts_out;
|
||||
}
|
||||
_pPublisherSrc->onWrite(std::make_shared<RtmpPacket>(std::move(chunkData)));
|
||||
}
|
||||
|
@ -473,20 +475,10 @@ void RtmpSession::onCmd_seek(AMFDecoder &dec) {
|
|||
}
|
||||
|
||||
void RtmpSession::onSendMedia(const RtmpPacket::Ptr &pkt) {
|
||||
auto modifiedStamp = pkt->timeStamp;
|
||||
auto &firstStamp = _aui32FirstStamp[pkt->typeId % 2];
|
||||
if(!firstStamp){
|
||||
firstStamp = modifiedStamp;
|
||||
}
|
||||
if(modifiedStamp >= firstStamp){
|
||||
//计算时间戳增量
|
||||
modifiedStamp -= firstStamp;
|
||||
}else{
|
||||
//发生回环,重新计算时间戳增量
|
||||
CLEAR_ARR(_aui32FirstStamp);
|
||||
modifiedStamp = 0;
|
||||
}
|
||||
sendRtmp(pkt->typeId, pkt->streamId, pkt, modifiedStamp, pkt->chunkId);
|
||||
//rtmp播放器时间戳从零开始
|
||||
int64_t dts_out;
|
||||
_stamp[pkt->typeId % 2].revise(pkt->timeStamp, 0, dts_out, dts_out);
|
||||
sendRtmp(pkt->typeId, pkt->streamId, pkt, dts_out, pkt->chunkId);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
#include "Util/util.h"
|
||||
#include "Util/TimeTicker.h"
|
||||
#include "Network/TcpSession.h"
|
||||
#include "MediaFile/Stamp.h"
|
||||
|
||||
using namespace toolkit;
|
||||
|
||||
namespace mediakit {
|
||||
|
@ -88,11 +90,11 @@ private:
|
|||
MediaInfo _mediaInfo;
|
||||
double _dNowReqID = 0;
|
||||
Ticker _ticker;//数据接收时间
|
||||
SmoothTicker _stampTicker[2];//时间戳生产器
|
||||
RingBuffer<RtmpPacket::Ptr>::RingReader::Ptr _pRingReader;
|
||||
std::shared_ptr<RtmpMediaSource> _pPublisherSrc;
|
||||
std::weak_ptr<RtmpMediaSource> _pPlayerSrc;
|
||||
uint32_t _aui32FirstStamp[2] = {0};
|
||||
//时间戳修整器
|
||||
Stamp _stamp[2];
|
||||
//消耗的总流量
|
||||
uint64_t _ui64TotalBytes = 0;
|
||||
|
||||
|
|
|
@ -96,6 +96,17 @@ public:
|
|||
int readerCount() override {
|
||||
return RtmpMediaSource::readerCount() + (_muxer ? _muxer->readerCount() : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取track
|
||||
* @return
|
||||
*/
|
||||
vector<Track::Ptr> getTracks(bool trackReady) const override {
|
||||
if(!_demuxer){
|
||||
return this->RtmpMediaSource::getTracks(trackReady);
|
||||
}
|
||||
return _demuxer->getTracks(trackReady);
|
||||
}
|
||||
private:
|
||||
RtmpDemuxer::Ptr _demuxer;
|
||||
MultiMediaSourceMuxer::Ptr _muxer;
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
enum AMFType {
|
||||
AMF_NUMBER,
|
||||
AMF_INTEGER,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user