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
......@@ -34,3 +34,4 @@
/cmake-build-debug/
/.idea/
/c_wrapper/.idea/
/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 @@
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)
......
......@@ -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");
......
......@@ -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代码)
......
[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";
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服务器配置///////////
......
......@@ -48,7 +48,7 @@ string makeAdtsConfig(const uint8_t *pcAdts);
void getAACInfo(const AACFrame &adts,int &iSampleRate,int &iChannel);
/**
/**
* aac帧,包含adts头
*/
class AACFrame : public Frame {
......@@ -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);
if (_hlsRecorder) {
_hlsRecorder->inputFrame(frame);
}
#endif //defined(ENABLE_HLS)
#if defined(ENABLE_MP4V2)
if (_mp4Maker) {
_mp4Maker->inputFrame(frame);
#if defined(ENABLE_MP4RECORD)
if (_mp4Recorder) {
_mp4Recorder->inputFrame(frame);
}
#endif //defined(ENABLE_MP4V2)
#endif //defined(ENABLE_MP4RECORD)
}
void MediaRecorder::addTrack(const Track::Ptr &track) {
#if defined(ENABLE_HLS)
if (_hlsMaker) {
_hlsMaker->addTrack(track);
if (_hlsRecorder) {
_hlsRecorder->addTrack(track);
}
#endif //defined(ENABLE_HLS)
#if defined(ENABLE_MP4RECORD)
if (_mp4Maker) {
_mp4Maker->addTrack(track);
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();
}else{
//这是非第一个的关键帧(h265有多种关键帧)
merged_frame = frame;
//这里的代码逻辑是让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{
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,
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论