Commit a3c8cb11 by baiyfcu Committed by GitHub

Merge pull request #5 from zlmediakit/master

update
parents d3a4fc1f 3c4d7498
release/ filter=lfs diff=lfs merge=lfs -text
...@@ -33,4 +33,5 @@ ...@@ -33,4 +33,5 @@
/cmake-build-debug/ /cmake-build-debug/
/.idea/ /.idea/
/c_wrapper/.idea/ /c_wrapper/.idea/
\ No newline at end of file /release/mac/Debug/
\ No newline at end of file
Subproject commit 2bb234006c852b1d1a61a0e9a7f39dde7105fe34 Subproject commit 57e7c83d5667b1e06fb8f5ca73dbe3f04a9fc67f
Subproject commit 6df71e01c174cdfe69e597cc4acb766a20b28620 Subproject commit 40edf6243d9d99676062062efdec203b24a178aa
...@@ -2,13 +2,34 @@ ...@@ -2,13 +2,34 @@
cmake_minimum_required(VERSION 3.1.3) cmake_minimum_required(VERSION 3.1.3)
#使能c++11 #使能c++11
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
#加载自定义模块 #加载自定义模块
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake")
#设置库文件路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) #set(CMAKE_BUILD_TYPE "Release")
#设置可执行程序路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) 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) set(ToolKit_Root ${CMAKE_CURRENT_SOURCE_DIR}/3rdpart/ZLToolKit/src)
...@@ -106,7 +127,7 @@ add_library(zltoolkit STATIC ${ToolKit_src_list}) ...@@ -106,7 +127,7 @@ add_library(zltoolkit STATIC ${ToolKit_src_list})
add_library(zlmediakit STATIC ${MediaKit_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 #libmpeg
if(ENABLE_HLS) if(ENABLE_HLS)
aux_source_directory(${MediaServer_Root}/libmpeg/include src_mpeg) aux_source_directory(${MediaServer_Root}/libmpeg/include src_mpeg)
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
- Well performance and stable test,can be used commercially. - Well performance and stable test,can be used commercially.
- Support linux, macos, ios, android, Windows Platforms. - Support linux, macos, ios, android, Windows Platforms.
- Very low latency(lower then one second), video opened immediately. - Very low latency(lower then one second), video opened immediately.
- **Now Support websocket-flv!**
## Features ## Features
...@@ -116,7 +117,12 @@ ...@@ -116,7 +117,12 @@
- Apple OSX(Darwin), both 32 and 64bits. - Apple OSX(Darwin), both 32 and 64bits.
- All hardware with x86/x86_64/arm/mips cpu. - All hardware with x86/x86_64/arm/mips cpu.
- Windows. - 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 ...@@ -231,7 +237,7 @@ It is recommended to compile on Ubuntu or MacOS,compiling on windows is cumber
## Usage ## Usage
- As server: - As server:
``` ```cpp
TcpServer::Ptr rtspSrv(new TcpServer()); TcpServer::Ptr rtspSrv(new TcpServer());
TcpServer::Ptr rtmpSrv(new TcpServer()); TcpServer::Ptr rtmpSrv(new TcpServer());
TcpServer::Ptr httpSrv(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 ...@@ -244,7 +250,7 @@ It is recommended to compile on Ubuntu or MacOS,compiling on windows is cumber
``` ```
- As player: - As player:
``` ```cpp
MediaPlayer::Ptr player(new MediaPlayer()); MediaPlayer::Ptr player(new MediaPlayer());
weak_ptr<MediaPlayer> weakPlayer = player; weak_ptr<MediaPlayer> weakPlayer = player;
player->setOnPlayResult([weakPlayer](const SockException &ex) { player->setOnPlayResult([weakPlayer](const SockException &ex) {
...@@ -273,7 +279,7 @@ It is recommended to compile on Ubuntu or MacOS,compiling on windows is cumber ...@@ -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/"); player->play("rtsp://admin:jzan123456@192.168.0.122/");
``` ```
- As proxy server: - As proxy server:
``` ```cpp
//support rtmp and rtsp url //support rtmp and rtsp url
//just support H264+AAC //just support H264+AAC
auto urlList = {"rtmp://live.hkstv.hk.lxdns.com/live/hks", 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 ...@@ -288,7 +294,7 @@ It is recommended to compile on Ubuntu or MacOS,compiling on windows is cumber
``` ```
- As puser: - As puser:
``` ```cpp
PlayerProxy::Ptr player(new PlayerProxy("app","stream")); PlayerProxy::Ptr player(new PlayerProxy("app","stream"));
player->play("rtmp://live.hkstv.hk.lxdns.com/live/hks"); player->play("rtmp://live.hkstv.hk.lxdns.com/live/hks");
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
- 代码经过大量的稳定性、性能测试,可满足商用服务器项目。 - 代码经过大量的稳定性、性能测试,可满足商用服务器项目。
- 支持linux、macos、ios、android、windows平台 - 支持linux、macos、ios、android、windows平台
- 支持画面秒开(GOP缓存)、极低延时(1秒内) - 支持画面秒开(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) - [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 @@ ...@@ -127,7 +128,12 @@
## 编译要求 ## 编译要求
- 编译器支持C++11,GCC4.8/Clang3.3/VC2015或以上 - 编译器支持C++11,GCC4.8/Clang3.3/VC2015或以上
- cmake3.2或以上 - cmake3.2或以上
- **必须使用git下载完整的代码,不要使用下载zip包的方式下载源码,否则子模块代码默认不下载!** - **必须使用git下载完整的代码,不要使用下载zip包的方式下载源码,否则子模块代码默认不下载!你可以像以下这样操作:**
```
git clone https://github.com/zlmediakit/ZLMediaKit.git
cd ZLMediaKit
git submodule update --init
```
## 编译(Linux) ## 编译(Linux)
- 我的编译环境 - 我的编译环境
...@@ -219,7 +225,7 @@ ...@@ -219,7 +225,7 @@
``` ```
## 使用方法 ## 使用方法
- 作为服务器: - 作为服务器:
``` ```cpp
TcpServer::Ptr rtspSrv(new TcpServer()); TcpServer::Ptr rtspSrv(new TcpServer());
TcpServer::Ptr rtmpSrv(new TcpServer()); TcpServer::Ptr rtmpSrv(new TcpServer());
TcpServer::Ptr httpSrv(new TcpServer()); TcpServer::Ptr httpSrv(new TcpServer());
...@@ -232,7 +238,7 @@ ...@@ -232,7 +238,7 @@
``` ```
- 作为播放器: - 作为播放器:
``` ```cpp
MediaPlayer::Ptr player(new MediaPlayer()); MediaPlayer::Ptr player(new MediaPlayer());
weak_ptr<MediaPlayer> weakPlayer = player; weak_ptr<MediaPlayer> weakPlayer = player;
player->setOnPlayResult([weakPlayer](const SockException &ex) { player->setOnPlayResult([weakPlayer](const SockException &ex) {
...@@ -261,7 +267,7 @@ ...@@ -261,7 +267,7 @@
player->play("rtsp://admin:jzan123456@192.168.0.122/"); player->play("rtsp://admin:jzan123456@192.168.0.122/");
``` ```
- 作为代理服务器: - 作为代理服务器:
``` ```cpp
//support rtmp and rtsp url //support rtmp and rtsp url
//just support H264+AAC //just support H264+AAC
auto urlList = {"rtmp://live.hkstv.hk.lxdns.com/live/hks", auto urlList = {"rtmp://live.hkstv.hk.lxdns.com/live/hks",
...@@ -285,7 +291,7 @@ ...@@ -285,7 +291,7 @@
``` ```
- 作为推流客户端器: - 作为推流客户端器:
``` ```cpp
PlayerProxy::Ptr player(new PlayerProxy("app","stream")); PlayerProxy::Ptr player(new PlayerProxy("app","stream"));
//拉一个流,生成一个RtmpMediaSource,源的名称是"app/stream" //拉一个流,生成一个RtmpMediaSource,源的名称是"app/stream"
//你也可以以其他方式生成RtmpMediaSource,比如说MP4文件(请研读MediaReader代码) //你也可以以其他方式生成RtmpMediaSource,比如说MP4文件(请研读MediaReader代码)
......
[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
执行可执行程序时,请在终端输入:
```
export LD_LIBRARY_PATH=./
./MediaServer -d &
```
如果由于so动态库链接失败导致运行不起来,请重建so库软链接
如果由于端口权限问题导致启动失败,请修改配置文件中端口号,或者以root权限运行
执行可执行程序时,请在终端输入:
```
export DYLD_LIBRARY_PATH=./
./MediaServer -d &
```
如果由于so动态库链接失败导致运行不起来,请重建so库软链接
如果由于端口权限问题导致启动失败,请修改配置文件中端口号,或者以root权限运行
执行可执行程序时,可以直接双击MediaServer运行
如果由于端口权限问题导致启动失败,请修改配置文件(.ini后缀的文件)中端口号,然后再运行
...@@ -74,7 +74,7 @@ const string kOnHttpAccess = HOOK_FIELD"on_http_access"; ...@@ -74,7 +74,7 @@ const string kOnHttpAccess = HOOK_FIELD"on_http_access";
const string kAdminParams = HOOK_FIELD"admin_params"; const string kAdminParams = HOOK_FIELD"admin_params";
onceToken token([](){ onceToken token([](){
mINI::Instance()[kEnable] = true; mINI::Instance()[kEnable] = false;
mINI::Instance()[kTimeoutSec] = 10; mINI::Instance()[kTimeoutSec] = 10;
mINI::Instance()[kOnPublish] = "https://127.0.0.1/index/hook/on_publish"; mINI::Instance()[kOnPublish] = "https://127.0.0.1/index/hook/on_publish";
mINI::Instance()[kOnPlay] = "https://127.0.0.1/index/hook/on_play"; mINI::Instance()[kOnPlay] = "https://127.0.0.1/index/hook/on_play";
...@@ -318,7 +318,7 @@ void installWebHook(){ ...@@ -318,7 +318,7 @@ void installWebHook(){
do_http_hook(hook_stream_not_found,body, nullptr); do_http_hook(hook_stream_not_found,body, nullptr);
}); });
#ifdef ENABLE_MP4V2 #ifdef ENABLE_MP4RECORD
//录制mp4文件成功后广播 //录制mp4文件成功后广播
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastRecordMP4,[](BroadcastRecordMP4Args){ NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastRecordMP4,[](BroadcastRecordMP4Args){
if(!hook_enable || hook_record_mp4.empty()){ if(!hook_enable || hook_record_mp4.empty()){
...@@ -338,7 +338,7 @@ void installWebHook(){ ...@@ -338,7 +338,7 @@ void installWebHook(){
//执行hook //执行hook
do_http_hook(hook_record_mp4,body, nullptr); do_http_hook(hook_record_mp4,body, nullptr);
}); });
#endif //ENABLE_MP4V2 #endif //ENABLE_MP4RECORD
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastShellLogin,[](BroadcastShellLoginArgs){ NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastShellLogin,[](BroadcastShellLoginArgs){
if(!hook_enable || hook_shell_login.empty() || sender.get_peer_ip() == "127.0.0.1"){ 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) { ...@@ -70,7 +70,7 @@ void MediaSink::inputFrame(const Frame::Ptr &frame) {
it->second->inputFrame(frame); it->second->inputFrame(frame);
if(!_allTrackReady && !_trackReadyCallback.empty() && it->second->ready()){ if(!_allTrackReady && !_trackReadyCallback.empty() && it->second->ready()){
//Track由未就绪状态换成就绪状态,我们就触发onTrackReady回调 //Track由未就绪状态换成就绪状态,我们就触发onTrackReady回调
auto it_callback = _trackReadyCallback.find(codec_id); auto it_callback = _trackReadyCallback.find(codec_id);
if(it_callback != _trackReadyCallback.end()){ if(it_callback != _trackReadyCallback.end()){
it_callback->second(); it_callback->second();
......
...@@ -123,6 +123,7 @@ MediaSource::Ptr MediaSource::find( ...@@ -123,6 +123,7 @@ MediaSource::Ptr MediaSource::find(
lock_guard<recursive_mutex> lock(g_mtxMediaSrc); lock_guard<recursive_mutex> lock(g_mtxMediaSrc);
MediaSource::Ptr ret; MediaSource::Ptr ret;
//查找某一媒体源,找到后返回
searchMedia(schema, vhost, app, id, searchMedia(schema, vhost, app, id,
[&](SchemaVhostAppStreamMap::iterator &it0 , [&](SchemaVhostAppStreamMap::iterator &it0 ,
VhostAppStreamMap::iterator &it1, VhostAppStreamMap::iterator &it1,
...@@ -138,7 +139,7 @@ MediaSource::Ptr MediaSource::find( ...@@ -138,7 +139,7 @@ MediaSource::Ptr MediaSource::find(
return true; return true;
}); });
if(!ret && bMake){ if(!ret && bMake){
//查找某一媒体源,找到后返回 //未查找媒体源,则创建一个
ret = MediaReader::onMakeMediaSource(schema, vhost,app,id); ret = MediaReader::onMakeMediaSource(schema, vhost,app,id);
} }
return ret; return ret;
......
...@@ -186,6 +186,14 @@ public: ...@@ -186,6 +186,14 @@ public:
} }
virtual int readerCount() = 0; virtual int readerCount() = 0;
/**
* 获取track
* @return
*/
virtual vector<Track::Ptr> getTracks(bool trackReady) const{
return vector<Track::Ptr>(0);
}
protected: protected:
void regist() ; void regist() ;
bool unregist() ; bool unregist() ;
......
...@@ -161,13 +161,16 @@ namespace Rtsp { ...@@ -161,13 +161,16 @@ namespace Rtsp {
const string kAuthBasic = RTSP_FIELD"authBasic"; const string kAuthBasic = RTSP_FIELD"authBasic";
const string kHandshakeSecond = RTSP_FIELD"handshakeSecond"; const string kHandshakeSecond = RTSP_FIELD"handshakeSecond";
const string kKeepAliveSecond = RTSP_FIELD"keepAliveSecond"; 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([](){ onceToken token([](){
//默认Md5方式认证 //默认Md5方式认证
mINI::Instance()[kAuthBasic] = 0; mINI::Instance()[kAuthBasic] = 0;
mINI::Instance()[kHandshakeSecond] = 15; mINI::Instance()[kHandshakeSecond] = 15;
mINI::Instance()[kKeepAliveSecond] = 15; mINI::Instance()[kKeepAliveSecond] = 15;
mINI::Instance()[kDirectProxy] = 1; mINI::Instance()[kDirectProxy] = 1;
mINI::Instance()[kModifyStamp] = true;
},nullptr); },nullptr);
} //namespace Rtsp } //namespace Rtsp
......
...@@ -209,6 +209,8 @@ extern const string kKeepAliveSecond; ...@@ -209,6 +209,8 @@ extern const string kKeepAliveSecond;
//假定您的拉流源地址不是264或265或AAC,那么你可以使用直接代理的方式来支持rtsp代理 //假定您的拉流源地址不是264或265或AAC,那么你可以使用直接代理的方式来支持rtsp代理
//默认开启rtsp直接代理,rtmp由于没有这些问题,是强制开启直接代理的 //默认开启rtsp直接代理,rtmp由于没有这些问题,是强制开启直接代理的
extern const string kDirectProxy; extern const string kDirectProxy;
//rtsp推流是否修改时间戳
extern const string kModifyStamp;
} //namespace Rtsp } //namespace Rtsp
////////////RTMP服务器配置/////////// ////////////RTMP服务器配置///////////
......
...@@ -48,7 +48,7 @@ string makeAdtsConfig(const uint8_t *pcAdts); ...@@ -48,7 +48,7 @@ string makeAdtsConfig(const uint8_t *pcAdts);
void getAACInfo(const AACFrame &adts,int &iSampleRate,int &iChannel); void getAACInfo(const AACFrame &adts,int &iSampleRate,int &iChannel);
/** /**
* aac帧,包含adts头 * aac帧,包含adts头
*/ */
class AACFrame : public Frame { class AACFrame : public Frame {
...@@ -156,10 +156,10 @@ public: ...@@ -156,10 +156,10 @@ public:
* @param aac_cfg aac两个字节的配置信息 * @param aac_cfg aac两个字节的配置信息
*/ */
AACTrack(const string &aac_cfg){ AACTrack(const string &aac_cfg){
if(aac_cfg.size() != 2){ if(aac_cfg.size() < 2){
throw std::invalid_argument("adts配置必须2个字节"); throw std::invalid_argument("adts配置必须最少2个字节");
} }
_cfg = aac_cfg; _cfg = aac_cfg.substr(0,2);
onReady(); onReady();
} }
......
...@@ -36,6 +36,7 @@ using namespace toolkit; ...@@ -36,6 +36,7 @@ using namespace toolkit;
namespace mediakit{ namespace mediakit{
/** /**
* h264 Rtmp解码类 * h264 Rtmp解码类
* 将 h264 over rtmp 解复用出 h264-Frame
*/ */
class H264RtmpDecoder : public RtmpCodec ,public ResourcePoolHelper<H264Frame> { class H264RtmpDecoder : public RtmpCodec ,public ResourcePoolHelper<H264Frame> {
public: public:
......
...@@ -90,7 +90,24 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { ...@@ -90,7 +90,24 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
* Type==7:SPS frame * Type==7:SPS frame
* Type==8:PPS 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; const uint8_t *frame = (uint8_t *) rtppack->data() + rtppack->offset;
int length = rtppack->size() - rtppack->offset; int length = rtppack->size() - rtppack->offset;
NALU nal; NALU nal;
...@@ -145,7 +162,7 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { ...@@ -145,7 +162,7 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
FU fu; FU fu;
MakeFU(frame[1], fu); MakeFU(frame[1], fu);
if (fu.S) { if (fu.S) {
//该帧的第一个rtp包 //该帧的第一个rtp包 FU-A start
char tmp = (nal.forbidden_zero_bit << 7 | nal.nal_ref_idc << 5 | fu.type); char tmp = (nal.forbidden_zero_bit << 7 | nal.nal_ref_idc << 5 | fu.type);
_h264frame->buffer.assign("\x0\x0\x0\x1", 4); _h264frame->buffer.assign("\x0\x0\x0\x1", 4);
_h264frame->buffer.push_back(tmp); _h264frame->buffer.push_back(tmp);
...@@ -164,14 +181,14 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { ...@@ -164,14 +181,14 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
} }
if (!fu.E) { if (!fu.E) {
//该帧的中间rtp包 //该帧的中间rtp包 FU-A mid
_h264frame->buffer.append((char *)frame + 2, length - 2); _h264frame->buffer.append((char *)frame + 2, length - 2);
//该函数return时,保存下当前sequence,以便下次对比seq是否连续 //该函数return时,保存下当前sequence,以便下次对比seq是否连续
_lastSeq = rtppack->sequence; _lastSeq = rtppack->sequence;
return false; return false;
} }
//该帧最后一个rtp包 //该帧最后一个rtp包 FU-A end
_h264frame->buffer.append((char *)frame + 2, length - 2); _h264frame->buffer.append((char *)frame + 2, length - 2);
_h264frame->timeStamp = rtppack->timeStamp; _h264frame->timeStamp = rtppack->timeStamp;
auto key = _h264frame->keyFrame(); auto key = _h264frame->keyFrame();
......
...@@ -36,6 +36,8 @@ namespace mediakit{ ...@@ -36,6 +36,8 @@ namespace mediakit{
/** /**
* h264 rtp解码类 * h264 rtp解码类
* 将 h264 over rtsp-rtp 解复用出 h264-Frame
* rfc3984
*/ */
class H264RtpDecoder : public RtpCodec , public ResourcePoolHelper<H264Frame> { class H264RtpDecoder : public RtpCodec , public ResourcePoolHelper<H264Frame> {
public: public:
......
...@@ -37,6 +37,8 @@ namespace mediakit{ ...@@ -37,6 +37,8 @@ namespace mediakit{
/** /**
* h265 rtp解码类 * 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> { class H265RtpDecoder : public RtpCodec , public ResourcePoolHelper<H265Frame> {
public: public:
......
...@@ -439,7 +439,8 @@ static inline unsigned int showBitsLong(void *pvHandle, int iN) ...@@ -439,7 +439,8 @@ static inline unsigned int showBitsLong(void *pvHandle, int iN)
if (iN <= 32) { if (iN <= 32) {
return showBits(ptPtr, iN); return showBits(ptPtr, iN);
} }
return 0;
} }
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
#include "Util/mini.h" #include "Util/mini.h"
#include "Util/util.h"
#include "Util/TimeTicker.h" #include "Util/TimeTicker.h"
#include "Network/Socket.h" #include "Network/Socket.h"
#include "Common/Parser.h" #include "Common/Parser.h"
...@@ -47,7 +48,7 @@ class HttpCookieManager; ...@@ -47,7 +48,7 @@ class HttpCookieManager;
/** /**
* cookie对象,用于保存cookie的一些相关属性 * cookie对象,用于保存cookie的一些相关属性
*/ */
class HttpServerCookie : public map<string,string> , public noncopyable{ class HttpServerCookie : public AnyStorage , public noncopyable{
public: public:
typedef std::shared_ptr<HttpServerCookie> Ptr; typedef std::shared_ptr<HttpServerCookie> Ptr;
/** /**
...@@ -108,6 +109,8 @@ public: ...@@ -108,6 +109,8 @@ public:
* @return * @return
*/ */
std::shared_ptr<lock_guard<mutex> > getLock(); std::shared_ptr<lock_guard<mutex> > getLock();
private: private:
string cookieExpireTime() const ; string cookieExpireTime() const ;
private: private:
......
...@@ -211,13 +211,25 @@ inline bool HttpSession::checkWebSocket(){ ...@@ -211,13 +211,25 @@ inline bool HttpSession::checkWebSocket(){
if(!_parser["Sec-WebSocket-Protocol"].empty()){ if(!_parser["Sec-WebSocket-Protocol"].empty()){
headerOut["Sec-WebSocket-Protocol"] = _parser["Sec-WebSocket-Protocol"]; headerOut["Sec-WebSocket-Protocol"] = _parser["Sec-WebSocket-Protocol"];
} }
sendResponse("101 Switching Protocols",headerOut,"");
checkLiveFlvStream(true); 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,"");
return true; return true;
} }
//http-flv 链接格式:http://vhost-url:port/app/streamid.flv?key1=value1&key2=value2 //http-flv 链接格式:http://vhost-url:port/app/streamid.flv?key1=value1&key2=value2
//如果url(除去?以及后面的参数)后缀是.flv,那么表明该url是一个http-flv直播。 //如果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(),'.'); auto pos = strrchr(_parser.Url().data(),'.');
if(!pos){ if(!pos){
//未找到".flv"后缀 //未找到".flv"后缀
...@@ -240,7 +252,7 @@ inline bool HttpSession::checkLiveFlvStream(bool over_websocket){ ...@@ -240,7 +252,7 @@ inline bool HttpSession::checkLiveFlvStream(bool over_websocket){
bool bClose = (strcasecmp(_parser["Connection"].data(),"close") == 0) || ( ++_iReqCnt > reqCnt); bool bClose = (strcasecmp(_parser["Connection"].data(),"close") == 0) || ( ++_iReqCnt > reqCnt);
weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this()); 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(); auto strongSelf = weakSelf.lock();
if(!strongSelf){ if(!strongSelf){
//本对象已经销毁 //本对象已经销毁
...@@ -249,35 +261,32 @@ inline bool HttpSession::checkLiveFlvStream(bool over_websocket){ ...@@ -249,35 +261,32 @@ inline bool HttpSession::checkLiveFlvStream(bool over_websocket){
auto rtmp_src = dynamic_pointer_cast<RtmpMediaSource>(src); auto rtmp_src = dynamic_pointer_cast<RtmpMediaSource>(src);
if(!rtmp_src){ if(!rtmp_src){
//未找到该流 //未找到该流
if(!over_websocket){ sendNotFound(bClose);
sendNotFound(bClose);
}
if(bClose){ if(bClose){
shutdown(SockException(Err_shutdown,"flv stream not found")); shutdown(SockException(Err_shutdown,"flv stream not found"));
} }
return; return;
} }
//找到流了 //找到流了
auto onRes = [this,rtmp_src,over_websocket](const string &err){ auto onRes = [this,rtmp_src,cb](const string &err){
bool authSuccess = err.empty(); bool authSuccess = err.empty();
if(!authSuccess){ if(!authSuccess){
if(!over_websocket){ sendResponse("401 Unauthorized", makeHttpHeader(true,err.size()),err);
sendResponse("401 Unauthorized", makeHttpHeader(true,err.size()),err);
}
shutdown(SockException(Err_shutdown,StrPrinter << "401 Unauthorized:" << err)); shutdown(SockException(Err_shutdown,StrPrinter << "401 Unauthorized:" << err));
return ; return ;
} }
if(!over_websocket) { if(!cb) {
//找到rtmp源,发送http头,负载后续发送 //找到rtmp源,发送http头,负载后续发送
sendResponse("200 OK", makeHttpHeader(false, 0, get_mime_type(".flv")), ""); sendResponse("200 OK", makeHttpHeader(false, 0, get_mime_type(".flv")), "");
}else{
cb();
} }
//开始发送rtmp负载 //开始发送rtmp负载
//关闭tcp_nodelay ,优化性能 //关闭tcp_nodelay ,优化性能
SockUtil::setNoDelay(_sock->rawFD(),false); SockUtil::setNoDelay(_sock->rawFD(),false);
(*this) << SocketFlags(kSockFlags); (*this) << SocketFlags(kSockFlags);
_flv_over_websocket = over_websocket;
try{ try{
start(getPoller(),rtmp_src); start(getPoller(),rtmp_src);
}catch (std::exception &ex){ }catch (std::exception &ex){
...@@ -403,7 +412,7 @@ inline void HttpSession::canAccessPath(const string &path_in,bool is_dir,const f ...@@ -403,7 +412,7 @@ inline void HttpSession::canAccessPath(const string &path_in,bool is_dir,const f
//上次鉴权失败,如果url发生变更,那么也重新鉴权 //上次鉴权失败,如果url发生变更,那么也重新鉴权
if (_parser.Params().empty() || _parser.Params() == cookie->getUid()) { if (_parser.Params().empty() || _parser.Params() == cookie->getUid()) {
//url参数未变,那么判断无权限访问 //url参数未变,那么判断无权限访问
callback(accessErr.empty() ? "无权限访问该目录" : accessErr, nullptr); callback(accessErr.empty() ? "无权限访问该目录" : accessErr.get<string>(), nullptr);
return; return;
} }
} }
...@@ -427,9 +436,9 @@ inline void HttpSession::canAccessPath(const string &path_in,bool is_dir,const f ...@@ -427,9 +436,9 @@ inline void HttpSession::canAccessPath(const string &path_in,bool is_dir,const f
//对cookie上锁 //对cookie上锁
auto lck = cookie->getLock(); 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(); auto strongSelf = weakSelf.lock();
...@@ -480,7 +489,8 @@ inline void HttpSession::Handle_Req_GET(int64_t &content_len) { ...@@ -480,7 +489,8 @@ inline void HttpSession::Handle_Req_GET(int64_t &content_len) {
} }
//再看看是否为http-flv直播请求 //再看看是否为http-flv直播请求
if(checkLiveFlvStream(false)){ if(checkLiveFlvStream()){
//若是,return!
return; return;
} }
...@@ -520,7 +530,7 @@ inline void HttpSession::Handle_Req_GET(int64_t &content_len) { ...@@ -520,7 +530,7 @@ inline void HttpSession::Handle_Req_GET(int64_t &content_len) {
} }
auto headerOut = makeHttpHeader(bClose,strMeun.size()); auto headerOut = makeHttpHeader(bClose,strMeun.size());
if(cookie){ 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); sendResponse(errMsg.empty() ? "200 OK" : "401 Unauthorized" , headerOut, strMeun);
throw SockException(bClose ? Err_shutdown : Err_success,"close connection after access folder"); 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) { ...@@ -555,7 +565,7 @@ inline void HttpSession::Handle_Req_GET(int64_t &content_len) {
if(!errMsg.empty()){ if(!errMsg.empty()){
auto headerOut = makeHttpHeader(bClose,errMsg.size()); auto headerOut = makeHttpHeader(bClose,errMsg.size());
if(cookie){ if(cookie){
headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey]); headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get<string>());
} }
sendResponse("401 Unauthorized" , headerOut, errMsg); sendResponse("401 Unauthorized" , headerOut, errMsg);
throw SockException(bClose ? Err_shutdown : Err_success,"close connection after access file failed"); throw SockException(bClose ? Err_shutdown : Err_success,"close connection after access file failed");
...@@ -954,12 +964,12 @@ void HttpSession::onWrite(const Buffer::Ptr &buffer) { ...@@ -954,12 +964,12 @@ void HttpSession::onWrite(const Buffer::Ptr &buffer) {
header._reserved = 0; header._reserved = 0;
header._opcode = WebSocketHeader::BINARY; header._opcode = WebSocketHeader::BINARY;
header._mask_flag = false; 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){ void HttpSession::onWebSocketEncodeData(const Buffer::Ptr &buffer){
_ui64TotalBytes += len; _ui64TotalBytes += buffer->size();
SocketHelper::send((char *)ptr,len); send(buffer);
} }
void HttpSession::onDetach() { void HttpSession::onDetach() {
......
...@@ -104,14 +104,13 @@ protected: ...@@ -104,14 +104,13 @@ protected:
/** /**
* 发送数据进行websocket协议打包后回调 * 发送数据进行websocket协议打包后回调
* @param ptr * @param buffer
* @param len
*/ */
void onWebSocketEncodeData(const uint8_t *ptr,uint64_t len) override; void onWebSocketEncodeData(const Buffer::Ptr &buffer) override;
private: private:
inline void Handle_Req_GET(int64_t &content_len); inline void Handle_Req_GET(int64_t &content_len);
inline void Handle_Req_POST(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 checkWebSocket();
inline bool emitHttpEvent(bool doInvoke); inline bool emitHttpEvent(bool doInvoke);
inline void urlDecode(Parser &parser); inline void urlDecode(Parser &parser);
......
...@@ -89,7 +89,7 @@ protected: ...@@ -89,7 +89,7 @@ protected:
header._reserved = 0; header._reserved = 0;
header._opcode = WebSocketHeader::TEXT; header._opcode = WebSocketHeader::TEXT;
header._mask_flag = false; header._mask_flag = false;
strongSelf->WebSocketSplitter::encode(header,(uint8_t *)buf->data(),buf->size()); strongSelf->WebSocketSplitter::encode(header,buf);
} }
return buf->size(); return buf->size();
}); });
...@@ -118,12 +118,12 @@ protected: ...@@ -118,12 +118,12 @@ protected:
switch (header._opcode){ switch (header._opcode){
case WebSocketHeader::CLOSE:{ case WebSocketHeader::CLOSE:{
HttpSessionType::encode(header,nullptr,0); HttpSessionType::encode(header,nullptr);
} }
break; break;
case WebSocketHeader::PING:{ case WebSocketHeader::PING:{
const_cast<WebSocketHeader&>(header)._opcode = WebSocketHeader::PONG; 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; break;
case WebSocketHeader::CONTINUATION:{ case WebSocketHeader::CONTINUATION:{
...@@ -132,8 +132,7 @@ protected: ...@@ -132,8 +132,7 @@ protected:
break; break;
case WebSocketHeader::TEXT: case WebSocketHeader::TEXT:
case WebSocketHeader::BINARY:{ case WebSocketHeader::BINARY:{
BufferString::Ptr buffer = std::make_shared<BufferString>(_remian_data); _session->onRecv(std::make_shared<BufferString>(_remian_data));
_session->onRecv(buffer);
} }
break; break;
default: default:
...@@ -145,11 +144,10 @@ protected: ...@@ -145,11 +144,10 @@ protected:
/** /**
* 发送数据进行websocket协议打包后回调 * 发送数据进行websocket协议打包后回调
* @param ptr * @param buffer
* @param len
*/ */
void onWebSocketEncodeData(const uint8_t *ptr,uint64_t len) override{ void onWebSocketEncodeData(const Buffer::Ptr &buffer) override{
SocketHelper::send((char *)ptr,len); SocketHelper::send(buffer);
} }
private: private:
typedef function<int(const Buffer::Ptr &buf)> onBeforeSendCB; typedef function<int(const Buffer::Ptr &buf)> onBeforeSendCB;
......
...@@ -164,9 +164,9 @@ void WebSocketSplitter::onPlayloadData(uint8_t *ptr, uint64_t len) { ...@@ -164,9 +164,9 @@ void WebSocketSplitter::onPlayloadData(uint8_t *ptr, uint64_t len) {
onWebSocketDecodePlayload(*this, _mask_flag ? ptr - len : ptr, len, _playload_offset); 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; string ret;
uint64_t len = buffer ? buffer->size() : 0;
uint8_t byte = header._fin << 7 | ((header._reserved & 0x07) << 4) | (header._opcode & 0x0F) ; uint8_t byte = header._fin << 7 | ((header._reserved & 0x07) << 4) | (header._opcode & 0x0F) ;
ret.push_back(byte); ret.push_back(byte);
...@@ -195,16 +195,16 @@ void WebSocketSplitter::encode(const WebSocketHeader &header,uint8_t *data, cons ...@@ -195,16 +195,16 @@ void WebSocketSplitter::encode(const WebSocketHeader &header,uint8_t *data, cons
ret.append((char *)header._mask.data(),4); 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(len > 0){
if(mask_flag){ if(mask_flag){
uint8_t *ptr = data; uint8_t *ptr = (uint8_t*)buffer->data();
for(int i = 0; i < len ; ++i,++ptr){ for(int i = 0; i < len ; ++i,++ptr){
*(ptr) ^= header._mask[i % 4]; *(ptr) ^= header._mask[i % 4];
} }
} }
onWebSocketEncodeData(data,len); onWebSocketEncodeData(buffer);
} }
} }
......
...@@ -31,8 +31,10 @@ ...@@ -31,8 +31,10 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <memory> #include <memory>
using namespace std; #include "Network/Buffer.h"
using namespace std;
using namespace toolkit;
namespace mediakit { namespace mediakit {
...@@ -85,12 +87,10 @@ public: ...@@ -85,12 +87,10 @@ public:
/** /**
* 编码一个数据包 * 编码一个数据包
* 将触发2次onWebSocketEncodeData回调 * 将触发2次onWebSocketEncodeData回调
* 第一次是数据头,第二次是负载数据
* @param header 数据头 * @param header 数据头
* @param data 负载数据 * @param buffer 负载数据
* @param len 负载数据长度
*/ */
void encode(const WebSocketHeader &header,uint8_t *data,const uint64_t len); void encode(const WebSocketHeader &header,const Buffer::Ptr &buffer);
protected: protected:
/** /**
* 收到一个webSocket数据包包头,后续将继续触发onWebSocketDecodePlayload回调 * 收到一个webSocket数据包包头,后续将继续触发onWebSocketDecodePlayload回调
...@@ -119,7 +119,7 @@ protected: ...@@ -119,7 +119,7 @@ protected:
* @param ptr 数据指针 * @param ptr 数据指针
* @param len 数据指针长度 * @param len 数据指针长度
*/ */
virtual void onWebSocketEncodeData(const uint8_t *ptr,uint64_t len){}; virtual void onWebSocketEncodeData(const Buffer::Ptr &buffer){};
private: private:
void onPlayloadData(uint8_t *data,uint64_t len); void onPlayloadData(uint8_t *data,uint64_t len);
private: private:
......
...@@ -135,9 +135,7 @@ void MP4Muxer::onTrackReady(const Track::Ptr &track) { ...@@ -135,9 +135,7 @@ void MP4Muxer::onTrackReady(const Track::Ptr &track) {
WarnL << "添加AAC Track失败:" << track_id; WarnL << "添加AAC Track失败:" << track_id;
return; return;
} }
track_info info; _codec_to_trackid[track->getCodecId()].track_id = track_id;
info.track_id = track_id;
_codec_to_trackid[track->getCodecId()] = info;
} }
break; break;
case CodecH264: { case CodecH264: {
...@@ -170,9 +168,7 @@ void MP4Muxer::onTrackReady(const Track::Ptr &track) { ...@@ -170,9 +168,7 @@ void MP4Muxer::onTrackReady(const Track::Ptr &track) {
WarnL << "添加H264 Track失败:" << track_id; WarnL << "添加H264 Track失败:" << track_id;
return; return;
} }
track_info info; _codec_to_trackid[track->getCodecId()].track_id = track_id;
info.track_id = track_id;
_codec_to_trackid[track->getCodecId()] = info;
} }
break; break;
case CodecH265: { case CodecH265: {
...@@ -205,9 +201,7 @@ void MP4Muxer::onTrackReady(const Track::Ptr &track) { ...@@ -205,9 +201,7 @@ void MP4Muxer::onTrackReady(const Track::Ptr &track) {
WarnL << "添加H265 Track失败:" << track_id; WarnL << "添加H265 Track失败:" << track_id;
return; return;
} }
track_info info; _codec_to_trackid[track->getCodecId()].track_id = track_id;
info.track_id = track_id;
_codec_to_trackid[track->getCodecId()] = info;
} }
break; break;
default: default:
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
#include <ctime> #include <ctime>
#include <sys/stat.h> #include <sys/stat.h>
#include "Common/config.h" #include "Common/config.h"
#include "Mp4Maker.h" #include "MP4Recorder.h"
#include "Util/util.h" #include "Util/util.h"
#include "Util/NoticeCenter.h" #include "Util/NoticeCenter.h"
#include "Thread/WorkThreadPool.h" #include "Thread/WorkThreadPool.h"
...@@ -53,7 +53,7 @@ string timeStr(const char *fmt) { ...@@ -53,7 +53,7 @@ string timeStr(const char *fmt) {
return buffer; return buffer;
} }
Mp4Maker::Mp4Maker(const string& strPath, MP4Recorder::MP4Recorder(const string& strPath,
const string &strVhost, const string &strVhost,
const string &strApp, const string &strApp,
const string &strStreamId) { const string &strStreamId) {
...@@ -64,11 +64,11 @@ Mp4Maker::Mp4Maker(const string& strPath, ...@@ -64,11 +64,11 @@ Mp4Maker::Mp4Maker(const string& strPath,
_info.strVhost = strVhost; _info.strVhost = strVhost;
_info.strFolder = strPath; _info.strFolder = strPath;
} }
Mp4Maker::~Mp4Maker() { MP4Recorder::~MP4Recorder() {
closeFile(); closeFile();
} }
void Mp4Maker::createFile() { void MP4Recorder::createFile() {
closeFile(); closeFile();
auto strDate = timeStr("%Y-%m-%d"); auto strDate = timeStr("%Y-%m-%d");
auto strTime = timeStr("%H-%M-%S"); auto strTime = timeStr("%H-%M-%S");
...@@ -100,7 +100,7 @@ void Mp4Maker::createFile() { ...@@ -100,7 +100,7 @@ void Mp4Maker::createFile() {
} }
} }
void Mp4Maker::asyncClose() { void MP4Recorder::asyncClose() {
auto muxer = _muxer; auto muxer = _muxer;
auto strFileTmp = _strFileTmp; auto strFileTmp = _strFileTmp;
auto strFile = _strFile; auto strFile = _strFile;
...@@ -121,14 +121,14 @@ void Mp4Maker::asyncClose() { ...@@ -121,14 +121,14 @@ void Mp4Maker::asyncClose() {
}); });
} }
void Mp4Maker::closeFile() { void MP4Recorder::closeFile() {
if (_muxer) { if (_muxer) {
asyncClose(); asyncClose();
_muxer = nullptr; _muxer = nullptr;
} }
} }
void Mp4Maker::onTrackFrame(const Frame::Ptr &frame) { void MP4Recorder::onTrackFrame(const Frame::Ptr &frame) {
GET_CONFIG(uint32_t,recordSec,Record::kFileSecond); GET_CONFIG(uint32_t,recordSec,Record::kFileSecond);
if(!_muxer || ((_createFileTicker.elapsedTime() > recordSec * 1000) && if(!_muxer || ((_createFileTicker.elapsedTime() > recordSec * 1000) &&
(!_haveVideo || (_haveVideo && frame->keyFrame()))) ){ (!_haveVideo || (_haveVideo && frame->keyFrame()))) ){
...@@ -145,7 +145,7 @@ void Mp4Maker::onTrackFrame(const Frame::Ptr &frame) { ...@@ -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做准备 //保存所有的track,为创建MP4MuxerFile做准备
_tracks.emplace_back(track); _tracks.emplace_back(track);
if(track->getTrackType() == TrackVideo){ if(track->getTrackType() == TrackVideo){
......
...@@ -55,14 +55,14 @@ public: ...@@ -55,14 +55,14 @@ public:
string strStreamId;//流ID string strStreamId;//流ID
string strVhost;//vhost string strVhost;//vhost
}; };
class Mp4Maker : public MediaSink{ class MP4Recorder : public MediaSink{
public: public:
typedef std::shared_ptr<Mp4Maker> Ptr; typedef std::shared_ptr<MP4Recorder> Ptr;
Mp4Maker(const string &strPath, MP4Recorder(const string &strPath,
const string &strVhost , const string &strVhost ,
const string &strApp, const string &strApp,
const string &strStreamId); const string &strStreamId);
virtual ~Mp4Maker(); virtual ~MP4Recorder();
private: private:
/** /**
* 某Track输出frame,在onAllTrackReady触发后才会调用此方法 * 某Track输出frame,在onAllTrackReady触发后才会调用此方法
......
...@@ -58,15 +58,15 @@ MediaRecorder::MediaRecorder(const string &strVhost_tmp, ...@@ -58,15 +58,15 @@ MediaRecorder::MediaRecorder(const string &strVhost_tmp,
string m3u8FilePath; string m3u8FilePath;
if(enableVhost){ if(enableVhost){
m3u8FilePath = hlsPath + "/" + strVhost + "/" + strApp + "/" + strId + "/hls.m3u8"; 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{ }else{
m3u8FilePath = hlsPath + "/" + strApp + "/" + strId + "/hls.m3u8"; 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) #endif //defined(ENABLE_HLS)
#if defined(ENABLE_MP4V2) #if defined(ENABLE_MP4RECORD)
GET_CONFIG(string,recordPath,Record::kFilePath); GET_CONFIG(string,recordPath,Record::kFilePath);
GET_CONFIG(string,recordAppName,Record::kAppName); GET_CONFIG(string,recordAppName,Record::kAppName);
...@@ -77,9 +77,9 @@ MediaRecorder::MediaRecorder(const string &strVhost_tmp, ...@@ -77,9 +77,9 @@ MediaRecorder::MediaRecorder(const string &strVhost_tmp,
} else { } else {
mp4FilePath = recordPath + "/" + recordAppName + "/" + strApp + "/" + strId + "/"; 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() { MediaRecorder::~MediaRecorder() {
...@@ -87,28 +87,28 @@ MediaRecorder::~MediaRecorder() { ...@@ -87,28 +87,28 @@ MediaRecorder::~MediaRecorder() {
void MediaRecorder::inputFrame(const Frame::Ptr &frame) { void MediaRecorder::inputFrame(const Frame::Ptr &frame) {
#if defined(ENABLE_HLS) #if defined(ENABLE_HLS)
if (_hlsMaker) { if (_hlsRecorder) {
_hlsMaker->inputFrame(frame); _hlsRecorder->inputFrame(frame);
} }
#endif //defined(ENABLE_HLS) #endif //defined(ENABLE_HLS)
#if defined(ENABLE_MP4V2) #if defined(ENABLE_MP4RECORD)
if (_mp4Maker) { if (_mp4Recorder) {
_mp4Maker->inputFrame(frame); _mp4Recorder->inputFrame(frame);
} }
#endif //defined(ENABLE_MP4V2) #endif //defined(ENABLE_MP4RECORD)
} }
void MediaRecorder::addTrack(const Track::Ptr &track) { void MediaRecorder::addTrack(const Track::Ptr &track) {
#if defined(ENABLE_HLS) #if defined(ENABLE_HLS)
if (_hlsMaker) { if (_hlsRecorder) {
_hlsMaker->addTrack(track); _hlsRecorder->addTrack(track);
} }
#endif //defined(ENABLE_HLS) #endif //defined(ENABLE_HLS)
#if defined(ENABLE_MP4RECORD) #if defined(ENABLE_MP4RECORD)
if (_mp4Maker) { if (_mp4Recorder) {
_mp4Maker->addTrack(track); _mp4Recorder->addTrack(track);
} }
#endif //defined(ENABLE_MP4RECORD) #endif //defined(ENABLE_MP4RECORD)
} }
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
#include <memory> #include <memory>
#include "Player/PlayerBase.h" #include "Player/PlayerBase.h"
#include "Common/MediaSink.h" #include "Common/MediaSink.h"
#include "Mp4Maker.h" #include "MP4Recorder.h"
#include "HlsRecorder.h" #include "HlsRecorder.h"
using namespace toolkit; using namespace toolkit;
...@@ -61,11 +61,11 @@ public: ...@@ -61,11 +61,11 @@ public:
void addTrack(const Track::Ptr & track) override; void addTrack(const Track::Ptr & track) override;
private: private:
#if defined(ENABLE_HLS) #if defined(ENABLE_HLS)
std::shared_ptr<HlsRecorder> _hlsMaker; std::shared_ptr<HlsRecorder> _hlsRecorder;
#endif //defined(ENABLE_HLS) #endif //defined(ENABLE_HLS)
#if defined(ENABLE_MP4RECORD) #if defined(ENABLE_MP4RECORD)
std::shared_ptr<Mp4Maker> _mp4Maker; std::shared_ptr<MP4Recorder> _mp4Recorder;
#endif //defined(ENABLE_MP4RECORD) #endif //defined(ENABLE_MP4RECORD)
}; };
......
...@@ -29,16 +29,22 @@ ...@@ -29,16 +29,22 @@
namespace mediakit { namespace mediakit {
void Stamp::revise(uint32_t dts, uint32_t pts, int64_t &dts_out, int64_t &pts_out) { 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){ if(_first){
//记录第一次时间戳,后面好计算时间戳增量 //记录第一次时间戳,后面好计算时间戳增量
_start_dts = dts; _start_dts = dts;
_first = false; _first = false;
_ticker = std::make_shared<SmoothTicker>(); _ticker.resetTime();
} }
//pts和dts的差值 if (!dts) {
int pts_dts_diff = pts - dts; //没有解码时间戳,我们生成解码时间戳
if(_modifyStamp){ dts = _ticker.elapsedTime();
dts = _ticker->elapsedTime();
} }
//相对时间戳 //相对时间戳
...@@ -60,11 +66,6 @@ void Stamp::revise(uint32_t dts, uint32_t pts, int64_t &dts_out, int64_t &pts_ou ...@@ -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; _dts_inc = dts_out;
//////////////以下是播放时间戳的计算////////////////// //////////////以下是播放时间戳的计算//////////////////
if(!pts){
//没有播放时间戳
pts = dts;
}
if(pts_dts_diff > 200 || pts_dts_diff < -200){ if(pts_dts_diff > 200 || pts_dts_diff < -200){
//如果差值大于200毫秒,则认为由于回环导致时间戳错乱了 //如果差值大于200毫秒,则认为由于回环导致时间戳错乱了
pts_dts_diff = 0; pts_dts_diff = 0;
......
...@@ -33,17 +33,18 @@ using namespace toolkit; ...@@ -33,17 +33,18 @@ using namespace toolkit;
namespace mediakit { namespace mediakit {
//该类解决时间戳回环、回退问题
//计算相对时间戳或者产生平滑时间戳
class Stamp { class Stamp {
public: public:
Stamp(bool modifyStamp = false) {_modifyStamp = modifyStamp;}; Stamp() = default;
~Stamp() = default; ~Stamp() = default;
void revise(uint32_t dts, uint32_t pts, int64_t &dts_out, int64_t &pts_out); void revise(uint32_t dts, uint32_t pts, int64_t &dts_out, int64_t &pts_out);
private: private:
int64_t _start_dts = 0; int64_t _start_dts = 0;
int64_t _dts_inc = 0; int64_t _dts_inc = 0;
bool _first = true; bool _first = true;
bool _modifyStamp; SmoothTicker _ticker;
std::shared_ptr<SmoothTicker> _ticker;
}; };
}//namespace mediakit }//namespace mediakit
......
...@@ -42,19 +42,13 @@ TsMuxer::~TsMuxer() { ...@@ -42,19 +42,13 @@ TsMuxer::~TsMuxer() {
void TsMuxer::addTrack(const Track::Ptr &track) { void TsMuxer::addTrack(const Track::Ptr &track) {
switch (track->getCodecId()){ switch (track->getCodecId()){
case CodecH264: { case CodecH264: {
track_info info; _codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_H264, nullptr, 0);
info.track_id = mpeg_ts_add_stream(_context, PSI_STREAM_H264, nullptr, 0);
_codec_to_trackid[track->getCodecId()] = info;
} break; } break;
case CodecH265: { case CodecH265: {
track_info info; _codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_H265, nullptr, 0);
info.track_id = mpeg_ts_add_stream(_context, PSI_STREAM_H265, nullptr, 0);
_codec_to_trackid[track->getCodecId()] = info;
}break; }break;
case CodecAAC: { case CodecAAC: {
track_info info; _codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_AAC, nullptr, 0);
info.track_id = mpeg_ts_add_stream(_context, PSI_STREAM_AAC, nullptr, 0);
_codec_to_trackid[track->getCodecId()] = info;
}break; }break;
default: default:
break; break;
...@@ -73,38 +67,28 @@ void TsMuxer::inputFrame(const Frame::Ptr &frame) { ...@@ -73,38 +67,28 @@ void TsMuxer::inputFrame(const Frame::Ptr &frame) {
switch (frame->getCodecId()){ switch (frame->getCodecId()){
case CodecH265: case CodecH265:
case CodecH264: { case CodecH264: {
//这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理,
Buffer::Ptr merged_frame ; if (!_frameCached.empty() && _frameCached.back()->dts() != frame->dts()) {
if(frame->configFrame()){ Frame::Ptr back = _frameCached.back();
//配置帧,缓存后直接返回,以便下次输入关键帧时使用 Buffer::Ptr merged_frame = back;
_config_frame_cache.append("\x00\x00\x00\x01",4); if(_frameCached.size() != 1){
_config_frame_cache.append(frame->data() + frame->prefixSize(),frame->size() - frame->prefixSize()); string merged;
break; _frameCached.for_each([&](const Frame::Ptr &frame){
} if(frame->prefixSize()){
merged.append(frame->data(),frame->size());
if(frame->keyFrame()){ } else{
//关键帧 merged.append("\x00\x00\x00\x01",4);
if(!_config_frame_cache.empty()){ merged.append(frame->data(),frame->size());
//有配置帧,那么配置帧合并关键帧后输入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(merged));
merged_frame = std::make_shared<BufferString>(std::move(_config_frame_cache));
_config_frame_cache.clear();
}else{
//这是非第一个的关键帧(h265有多种关键帧)
merged_frame = frame;
} }
}else{ track_info.stamp.revise(back->dts(),back->pts(),dts_out,pts_out);
//这里是普通帧,例如B/P, _timestamp = dts_out;
merged_frame = frame; mpeg_ts_write(_context, track_info.track_id, back->keyFrame() ? 0x0001 : 0, pts_out * 90LL, dts_out * 90LL, merged_frame->data(), merged_frame->size());
//sps、pps这些配置帧清空掉 _frameCached.clear();
_config_frame_cache.clear();
} }
_frameCached.emplace_back(Frame::getCacheAbleFrame(frame));
//输入到ts文件
track_info.stamp.revise(frame->dts(),frame->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());
} }
break; break;
default: { default: {
......
...@@ -38,12 +38,12 @@ using namespace toolkit; ...@@ -38,12 +38,12 @@ using namespace toolkit;
namespace mediakit { namespace mediakit {
class TsMuxer { class TsMuxer : public MediaSink {
public: public:
TsMuxer(); TsMuxer();
virtual ~TsMuxer(); virtual ~TsMuxer();
void addTrack(const Track::Ptr &track) ; void addTrack(const Track::Ptr &track) override;
void inputFrame(const Frame::Ptr &frame) ; void inputFrame(const Frame::Ptr &frame) override;
protected: protected:
virtual void onTs(const void *packet, int bytes,uint32_t timestamp,int flags) = 0; virtual void onTs(const void *packet, int bytes,uint32_t timestamp,int flags) = 0;
void resetTracks(); void resetTracks();
...@@ -60,7 +60,7 @@ private: ...@@ -60,7 +60,7 @@ private:
Stamp stamp; Stamp stamp;
}; };
unordered_map<int,track_info> _codec_to_trackid; unordered_map<int,track_info> _codec_to_trackid;
string _config_frame_cache; List<Frame::Ptr> _frameCached;
}; };
}//namespace mediakit }//namespace mediakit
......
...@@ -76,8 +76,6 @@ void FlvMuxer::start(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr & ...@@ -76,8 +76,6 @@ void FlvMuxer::start(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &
} }
void FlvMuxer::onWriteFlvHeader(const RtmpMediaSource::Ptr &mediaSrc) { void FlvMuxer::onWriteFlvHeader(const RtmpMediaSource::Ptr &mediaSrc) {
CLEAR_ARR(_aui32FirstStamp);
//发送flv文件头 //发送flv文件头
char flv_file_header[] = "FLV\x1\x5\x0\x0\x0\x9"; // have audio and have video 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; bool is_have_audio = false,is_have_video = false;
...@@ -158,20 +156,9 @@ void FlvMuxer::onWriteFlvTag(uint8_t ui8Type, const Buffer::Ptr &buffer, uint32_ ...@@ -158,20 +156,9 @@ void FlvMuxer::onWriteFlvTag(uint8_t ui8Type, const Buffer::Ptr &buffer, uint32_
} }
void FlvMuxer::onWriteRtmp(const RtmpPacket::Ptr &pkt) { void FlvMuxer::onWriteRtmp(const RtmpPacket::Ptr &pkt) {
auto modifiedStamp = pkt->timeStamp; int64_t dts_out;
auto &firstStamp = _aui32FirstStamp[pkt->typeId % 2]; _stamp[pkt->typeId % 2].revise(pkt->timeStamp, 0, dts_out, dts_out);
if(!firstStamp){ onWriteFlvTag(pkt, dts_out);
firstStamp = modifiedStamp;
}
if(modifiedStamp >= firstStamp){
//计算时间戳增量
modifiedStamp -= firstStamp;
}else{
//发生回环,重新计算时间戳增量
CLEAR_ARR(_aui32FirstStamp);
modifiedStamp = 0;
}
onWriteFlvTag(pkt, modifiedStamp);
} }
void FlvMuxer::stop() { void FlvMuxer::stop() {
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "Rtmp/Rtmp.h" #include "Rtmp/Rtmp.h"
#include "Rtmp/RtmpMediaSource.h" #include "Rtmp/RtmpMediaSource.h"
#include "Network/Socket.h" #include "Network/Socket.h"
#include "MediaFile/Stamp.h"
using namespace toolkit; using namespace toolkit;
namespace mediakit { namespace mediakit {
...@@ -52,7 +53,8 @@ private: ...@@ -52,7 +53,8 @@ private:
void onWriteFlvTag(uint8_t ui8Type, const Buffer::Ptr &buffer, uint32_t ui32TimeStamp); void onWriteFlvTag(uint8_t ui8Type, const Buffer::Ptr &buffer, uint32_t ui32TimeStamp);
private: private:
RtmpMediaSource::RingType::RingReader::Ptr _ring_reader; RtmpMediaSource::RingType::RingReader::Ptr _ring_reader;
uint32_t _aui32FirstStamp[2] = {0}; //时间戳修整器
Stamp _stamp[2];
}; };
......
...@@ -44,7 +44,7 @@ using namespace toolkit; ...@@ -44,7 +44,7 @@ using namespace toolkit;
using namespace mediakit::Client; using namespace mediakit::Client;
namespace mediakit { namespace mediakit {
//实现了rtmp播放器协议部分的功能,及数据接收功能
class RtmpPlayer:public PlayerBase, public TcpClient, public RtmpProtocol{ class RtmpPlayer:public PlayerBase, public TcpClient, public RtmpProtocol{
public: public:
typedef std::shared_ptr<RtmpPlayer> Ptr; typedef std::shared_ptr<RtmpPlayer> Ptr;
...@@ -63,11 +63,11 @@ protected: ...@@ -63,11 +63,11 @@ protected:
void onMediaData_l(const RtmpPacket::Ptr &chunkData); void onMediaData_l(const RtmpPacket::Ptr &chunkData);
void onPlayResult_l(const SockException &ex); void onPlayResult_l(const SockException &ex);
//for Tcpclient //form Tcpclient
void onRecv(const Buffer::Ptr &pBuf) override; void onRecv(const Buffer::Ptr &pBuf) override;
void onConnect(const SockException &err) override; void onConnect(const SockException &err) override;
void onErr(const SockException &ex) override; void onErr(const SockException &ex) override;
//fro RtmpProtocol //from RtmpProtocol
void onRtmpChunk(RtmpPacket &chunkData) override; void onRtmpChunk(RtmpPacket &chunkData) override;
void onStreamDry(uint32_t ui32StreamId) override; void onStreamDry(uint32_t ui32StreamId) override;
void onSendRawData(const Buffer::Ptr &buffer) override{ void onSendRawData(const Buffer::Ptr &buffer) override{
......
...@@ -532,10 +532,6 @@ void RtmpProtocol::handle_rtmp() { ...@@ -532,10 +532,6 @@ void RtmpProtocol::handle_rtmp() {
static const size_t HEADER_LENGTH[] = { 12, 8, 4, 1 }; static const size_t HEADER_LENGTH[] = { 12, 8, 4, 1 };
size_t iHeaderLen = HEADER_LENGTH[flags >> 6]; size_t iHeaderLen = HEADER_LENGTH[flags >> 6];
_iNowChunkID = flags & 0x3f; _iNowChunkID = flags & 0x3f;
if(_iNowChunkID >10){
int i=0;
i++;
}
switch (_iNowChunkID) { switch (_iNowChunkID) {
case 0: { case 0: {
//0 值表示二字节形式,并且 ID 范围 64 - 319 //0 值表示二字节形式,并且 ID 范围 64 - 319
......
...@@ -63,7 +63,7 @@ void RtmpSession::onError(const SockException& err) { ...@@ -63,7 +63,7 @@ void RtmpSession::onError(const SockException& err) {
} }
void RtmpSession::onManager() { 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); GET_CONFIG(uint32_t,keep_alive_sec,Rtmp::kKeepAliveSecond);
if (_ticker.createdTime() > handshake_sec * 1000) { if (_ticker.createdTime() > handshake_sec * 1000) {
...@@ -397,20 +397,20 @@ void RtmpSession::setMetaData(AMFDecoder &dec) { ...@@ -397,20 +397,20 @@ void RtmpSession::setMetaData(AMFDecoder &dec) {
void RtmpSession::onProcessCmd(AMFDecoder &dec) { void RtmpSession::onProcessCmd(AMFDecoder &dec) {
typedef void (RtmpSession::*rtmpCMDHandle)(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([]() { static onceToken token([]() {
g_mapCmd.emplace("connect",&RtmpSession::onCmd_connect); s_cmd_functions.emplace("connect",&RtmpSession::onCmd_connect);
g_mapCmd.emplace("createStream",&RtmpSession::onCmd_createStream); s_cmd_functions.emplace("createStream",&RtmpSession::onCmd_createStream);
g_mapCmd.emplace("publish",&RtmpSession::onCmd_publish); s_cmd_functions.emplace("publish",&RtmpSession::onCmd_publish);
g_mapCmd.emplace("deleteStream",&RtmpSession::onCmd_deleteStream); s_cmd_functions.emplace("deleteStream",&RtmpSession::onCmd_deleteStream);
g_mapCmd.emplace("play",&RtmpSession::onCmd_play); s_cmd_functions.emplace("play",&RtmpSession::onCmd_play);
g_mapCmd.emplace("play2",&RtmpSession::onCmd_play2); s_cmd_functions.emplace("play2",&RtmpSession::onCmd_play2);
g_mapCmd.emplace("seek",&RtmpSession::onCmd_seek); s_cmd_functions.emplace("seek",&RtmpSession::onCmd_seek);
g_mapCmd.emplace("pause",&RtmpSession::onCmd_pause);}, []() {}); s_cmd_functions.emplace("pause",&RtmpSession::onCmd_pause);}, []() {});
std::string method = dec.load<std::string>(); std::string method = dec.load<std::string>();
auto it = g_mapCmd.find(method); auto it = s_cmd_functions.find(method);
if (it == g_mapCmd.end()) { if (it == s_cmd_functions.end()) {
TraceP(this) << "can not support cmd:" << method; TraceP(this) << "can not support cmd:" << method;
return; return;
} }
...@@ -444,10 +444,12 @@ void RtmpSession::onRtmpChunk(RtmpPacket &chunkData) { ...@@ -444,10 +444,12 @@ void RtmpSession::onRtmpChunk(RtmpPacket &chunkData) {
throw std::runtime_error("Not a rtmp publisher!"); throw std::runtime_error("Not a rtmp publisher!");
} }
GET_CONFIG(bool,rtmp_modify_stamp,Rtmp::kModifyStamp); GET_CONFIG(bool,rtmp_modify_stamp,Rtmp::kModifyStamp);
if(rtmp_modify_stamp){ 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);
_pPublisherSrc->onWrite(std::make_shared<RtmpPacket>(std::move(chunkData))); chunkData.timeStamp = dts_out;
}
_pPublisherSrc->onWrite(std::make_shared<RtmpPacket>(std::move(chunkData)));
} }
break; break;
default: default:
...@@ -473,20 +475,10 @@ void RtmpSession::onCmd_seek(AMFDecoder &dec) { ...@@ -473,20 +475,10 @@ void RtmpSession::onCmd_seek(AMFDecoder &dec) {
} }
void RtmpSession::onSendMedia(const RtmpPacket::Ptr &pkt) { void RtmpSession::onSendMedia(const RtmpPacket::Ptr &pkt) {
auto modifiedStamp = pkt->timeStamp; //rtmp播放器时间戳从零开始
auto &firstStamp = _aui32FirstStamp[pkt->typeId % 2]; int64_t dts_out;
if(!firstStamp){ _stamp[pkt->typeId % 2].revise(pkt->timeStamp, 0, dts_out, dts_out);
firstStamp = modifiedStamp; sendRtmp(pkt->typeId, pkt->streamId, pkt, dts_out, pkt->chunkId);
}
if(modifiedStamp >= firstStamp){
//计算时间戳增量
modifiedStamp -= firstStamp;
}else{
//发生回环,重新计算时间戳增量
CLEAR_ARR(_aui32FirstStamp);
modifiedStamp = 0;
}
sendRtmp(pkt->typeId, pkt->streamId, pkt, modifiedStamp, pkt->chunkId);
} }
......
...@@ -37,6 +37,8 @@ ...@@ -37,6 +37,8 @@
#include "Util/util.h" #include "Util/util.h"
#include "Util/TimeTicker.h" #include "Util/TimeTicker.h"
#include "Network/TcpSession.h" #include "Network/TcpSession.h"
#include "MediaFile/Stamp.h"
using namespace toolkit; using namespace toolkit;
namespace mediakit { namespace mediakit {
...@@ -88,11 +90,11 @@ private: ...@@ -88,11 +90,11 @@ private:
MediaInfo _mediaInfo; MediaInfo _mediaInfo;
double _dNowReqID = 0; double _dNowReqID = 0;
Ticker _ticker;//数据接收时间 Ticker _ticker;//数据接收时间
SmoothTicker _stampTicker[2];//时间戳生产器
RingBuffer<RtmpPacket::Ptr>::RingReader::Ptr _pRingReader; RingBuffer<RtmpPacket::Ptr>::RingReader::Ptr _pRingReader;
std::shared_ptr<RtmpMediaSource> _pPublisherSrc; std::shared_ptr<RtmpMediaSource> _pPublisherSrc;
std::weak_ptr<RtmpMediaSource> _pPlayerSrc; std::weak_ptr<RtmpMediaSource> _pPlayerSrc;
uint32_t _aui32FirstStamp[2] = {0}; //时间戳修整器
Stamp _stamp[2];
//消耗的总流量 //消耗的总流量
uint64_t _ui64TotalBytes = 0; uint64_t _ui64TotalBytes = 0;
......
...@@ -96,6 +96,17 @@ public: ...@@ -96,6 +96,17 @@ public:
int readerCount() override { int readerCount() override {
return RtmpMediaSource::readerCount() + (_muxer ? _muxer->readerCount() : 0); 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: private:
RtmpDemuxer::Ptr _demuxer; RtmpDemuxer::Ptr _demuxer;
MultiMediaSourceMuxer::Ptr _muxer; MultiMediaSourceMuxer::Ptr _muxer;
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#include <map> #include <map>
#include <stdexcept>
enum AMFType { enum AMFType {
AMF_NUMBER, AMF_NUMBER,
AMF_INTEGER, AMF_INTEGER,
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论