Merge pull request #5 from zlmediakit/master

update
This commit is contained in:
baiyfcu 2019-09-02 20:31:04 +08:00 committed by GitHub
commit a3c8cb11b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
116 changed files with 908 additions and 384 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
release/ filter=lfs diff=lfs merge=lfs -text

3
.gitignore vendored
View File

@ -33,4 +33,5 @@
/cmake-build-debug/
/.idea/
/c_wrapper/.idea/
/c_wrapper/.idea/
/release/mac/Debug/

@ -1 +1 @@
Subproject commit 2bb234006c852b1d1a61a0e9a7f39dde7105fe34
Subproject commit 57e7c83d5667b1e06fb8f5ca73dbe3f04a9fc67f

@ -1 +1 @@
Subproject commit 6df71e01c174cdfe69e597cc4acb766a20b28620
Subproject commit 40edf6243d9d99676062062efdec203b24a178aa

View File

@ -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()
#binlib
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)

View File

@ -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 MacOScompiling 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 MacOScompiling 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 MacOScompiling 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 MacOScompiling on windows is cumber
```
- As puser
```
```cpp
PlayerProxy::Ptr player(new PlayerProxy("app","stream"));
player->play("rtmp://live.hkstv.hk.lxdns.com/live/hks");

View File

@ -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++11GCC4.8/Clang3.3/VC2015或以上
- cmake3.2或以上
- **必须使用git下载完整的代码不要使用下载zip包的方式下载源码否则子模块代码默认不下载**
- **必须使用git下载完整的代码不要使用下载zip包的方式下载源码否则子模块代码默认不下载你可以像以下这样操作:**
```
git clone https://github.com/zlmediakit/ZLMediaKit.git
cd ZLMediaKit
git submodule update --init
```
## 编译(Linux)
- 我的编译环境
@ -219,7 +225,7 @@
```
## 使用方法
- 作为服务器:
```
```cpp
TcpServer::Ptr rtspSrv(new TcpServer());
TcpServer::Ptr rtmpSrv(new TcpServer());
TcpServer::Ptr httpSrv(new TcpServer());
@ -232,7 +238,7 @@
```
- 作为播放器:
```
```cpp
MediaPlayer::Ptr player(new MediaPlayer());
weak_ptr<MediaPlayer> weakPlayer = player;
player->setOnPlayResult([weakPlayer](const SockException &ex) {
@ -261,7 +267,7 @@
player->play("rtsp://admin:jzan123456@192.168.0.122/");
```
- 作为代理服务器:
```
```cpp
//support rtmp and rtsp url
//just support H264+AAC
auto urlList = {"rtmp://live.hkstv.hk.lxdns.com/live/hks",
@ -285,7 +291,7 @@
```
- 作为推流客户端器:
```
```cpp
PlayerProxy::Ptr player(new PlayerProxy("app","stream"));
//拉一个流生成一个RtmpMediaSource源的名称是"app/stream"
//你也可以以其他方式生成RtmpMediaSource比如说MP4文件请研读MediaReader代码

163
conf/config.ini Normal file
View File

@ -0,0 +1,163 @@
[api]
#是否调试http api,启用调试后会打印每次http请求的内容和回复
apiDebug=1
#一些比较敏感的http api在访问时需要提供secret否则无权限调用
#如果是通过127.0.0.1访问,那么可以不提供secret
secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc
[ffmpeg]
#FFmpeg可执行程序路径
bin=/usr/local/bin/ffmpeg
#FFmpeg拉流再推流的命令模板通过该模板可以设置再编码的一些参数
cmd=%s -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s
#FFmpeg日志的路径如果置空则不生成FFmpeg日志
log=/Users/xzl/git/ZLMediaKit/release/mac/Release/ffmpeg/ffmpeg.log
[general]
#是否启用虚拟主机
enableVhost=1
#播放器或推流器在断开后会触发hook.on_flow_report事件(使用多少流量事件)
#flowThreshold参数控制触发hook.on_flow_report事件阈值使用流量超过该阈值后才触发单位KB
flowThreshold=1024
#播放最多等待时间,单位毫秒
#播放在播放某个流时,如果该流不存在,
#ZLMediaKit会最多让播放器等待maxStreamWaitMS毫秒
#如果在这个时间内,该流注册成功,那么会立即返回播放器播放成功
#否则返回播放器未找到该流,该机制的目的是可以先播放再推流
maxStreamWaitMS=5000
#某个流无人观看时触发hook.on_stream_none_reader事件的最大等待时间单位毫秒
#在配合hook.on_stream_none_reader事件时可以做到无人观看自动停止拉流或停止接收推流
streamNoneReaderDelayMS=5000
[hls]
#hls写文件的buf大小调整参数可以提高文件io性能
fileBufSize=65536
#hls保存文件路径
filePath=/Users/xzl/git/ZLMediaKit/release/mac/Release/httpRoot
#hls最大切片时间
segDur=3
#m3u8索引中,hls保留切片个数(实际保留切片个数大2~3个)
segNum=3
[hook]
#在推流时如果url参数匹对admin_params那么可以不经过hook鉴权直接推流成功播放时亦然
#该配置项的目的是为了开发者自己调试测试,该参数暴露后会有泄露隐私的安全隐患
admin_params=secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc
#是否启用hook事件启用后推拉流都将进行鉴权
enable=0
#播放器或推流器使用流量事件,置空则关闭
on_flow_report=https://127.0.0.1/index/hook/on_flow_report
#访问http文件鉴权事件置空则关闭鉴权
on_http_access=https://127.0.0.1/index/hook/on_http_access
#播放鉴权事件,置空则关闭鉴权
on_play=https://127.0.0.1/index/hook/on_play
#推流鉴权事件,置空则关闭鉴权
on_publish=https://127.0.0.1/index/hook/on_publish
#录制mp4切片完成事件
on_record_mp4=https://127.0.0.1/index/hook/on_record_mp4
#rtsp播放鉴权事件此事件中比对rtsp的用户名密码
on_rtsp_auth=https://127.0.0.1/index/hook/on_rtsp_auth
#rtsp播放是否开启鉴权事件置空则关闭rtsp鉴权。rtsp播放鉴权还支持url方式鉴权
#建议开发者统一采用url参数方式鉴权rtsp用户名密码鉴权一般在设备上用的比较多
on_rtsp_realm=https://127.0.0.1/index/hook/on_rtsp_realm
#远程telnet调试鉴权事件
on_shell_login=https://127.0.0.1/index/hook/on_shell_login
#直播流注册或注销事件
on_stream_changed=https://127.0.0.1/index/hook/on_stream_changed
#无人观看流事件通过该事件可以选择是否关闭无人观看的流。配合general.streamNoneReaderDelayMS选项一起使用
on_stream_none_reader=https://127.0.0.1/index/hook/on_stream_none_reader
#播放时未找到流事件通过配合hook.on_stream_none_reader事件可以完成按需拉流
on_stream_not_found=https://127.0.0.1/index/hook/on_stream_not_found
#hook api最大等待回复时间单位秒
timeoutSec=10
[http]
#http服务器字符编码windows上默认gb2312
charSet=utf-8
#http链接超时时间
keepAliveSecond=10
#keep-alive类型的链接最多复用次数
maxReqCount=100
#http请求体最大字节数如果post的body太大则不适合缓存body在内存
maxReqSize=4096
#404网页内容用户可以自定义404网页
notFound=<html><head><title>404 Not Found</title></head><body bgcolor="white"><center><h1>您访问的资源不存在!</h1></center><hr><center>ZLMediaKit-4.0</center></body></html>
#http服务器监听端口
port=80
#http文件服务器根目录
rootPath=/Users/xzl/git/ZLMediaKit/release/mac/Release/httpRoot
#http文件服务器读文件缓存大小单位BYTE调整该参数可以优化文件io性能
sendBufSize=65536
#https服务器监听端口
sslport=443
[multicast]
#rtp组播截止组播ip地址
addrMax=239.255.255.255
#rtp组播起始组播ip地址
addrMin=239.0.0.0
#组播udp ttl
udpTTL=64
[record]
#mp4录制或mp4点播的应用名通过限制应用名可以防止随意点播
appName=record
#mp4录制写文件缓存单位BYTE,调整参数可以提高文件io性能
fileBufSize=65536
#mp4录制保存路径
filePath=/Users/xzl/git/ZLMediaKit/release/mac/Release/httpRoot
#mp4录制切片时间单位秒
fileSecond=3600
#mp4点播每次流化数据量单位毫秒
#减少该值可以让点播数据发送量更平滑增大该值则更节省cpu资源
sampleMS=100
[rtmp]
#rtmp必须在此时间内完成握手否则服务器会断开链接单位秒
handshakeSecond=15
#rtmp超时时间如果该时间内未收到客户端的数据
#或者tcp发送缓存超过这个时间则会断开连接单位秒
keepAliveSecond=15
#在接收rtmp推流时是否重新生成时间戳(很多推流器的时间戳着实很烂)
modifyStamp=1
#rtmp服务器监听端口
port=1935
[rtp]
#音频mtu大小该参数限制rtp最大字节数推荐不要超过1400
#加大该值会明显增加直播延时
audioMtuSize=600
#如果rtp的序列号连续clearCount次有序那么rtp将不再排序(目的减少rtp排序导致的延时)
clearCount=10
#rtp时间戳回环时间单位毫秒
cycleMS=46800000
#rtp排序map缓存大小加大该值可能会增大延时但是rtp乱序问题会减小
maxRtpCount=50
#视频mtu大小该参数限制rtp最大字节数推荐不要超过1400
videoMtuSize=1400
[rtsp]
#rtsp专有鉴权方式是采用base64还是md5方式
authBasic=0
#rtsp拉流代理是否是直接代理模式
#直接代理后支持任意编码格式但是会导致GOP缓存无法定位到I帧可能会导致开播花屏
#并且如果是tcp方式拉流如果rtp大于mtu会导致无法使用udp方式代理
#假定您的拉流源地址不是264或265或AAC那么你可以使用直接代理的方式来支持rtsp代理
#默认开启rtsp直接代理rtmp由于没有这些问题是强制开启直接代理的
directProxy=1
#rtsp必须在此时间内完成握手否则服务器会断开链接单位秒
handshakeSecond=15
#rtsp超时时间如果该时间内未收到客户端的数据
#或者tcp发送缓存超过这个时间则会断开连接单位秒
keepAliveSecond=15
#rtsp服务器监听地址
port=554
#rtsps服务器监听地址
sslport=322
[shell]
#调试telnet服务器接受最大bufffer大小
maxReqSize=1024
#调试telnet服务器监听端口
port=9000

BIN
release/linux/Release/MediaServer Executable file

Binary file not shown.

Binary file not shown.

BIN
release/linux/Release/libflv.a Executable file

Binary file not shown.

Binary file not shown.

BIN
release/linux/Release/libmov.a Executable file

Binary file not shown.

BIN
release/linux/Release/libmpeg.a Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,9 @@
执行可执行程序时,请在终端输入:
```
export LD_LIBRARY_PATH=./
./MediaServer -d &
```
如果由于so动态库链接失败导致运行不起来请重建so库软链接
如果由于端口权限问题导致启动失败请修改配置文件中端口号或者以root权限运行

BIN
release/linux/Release/ssl.p12 Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
release/linux/Release/test_pusher Executable file

Binary file not shown.

Binary file not shown.

BIN
release/linux/Release/test_server Executable file

Binary file not shown.

BIN
release/mac/Release/MediaServer Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,9 @@
执行可执行程序时,请在终端输入:
```
export DYLD_LIBRARY_PATH=./
./MediaServer -d &
```
如果由于so动态库链接失败导致运行不起来请重建so库软链接
如果由于端口权限问题导致启动失败请修改配置文件中端口号或者以root权限运行

BIN
release/mac/Release/ssl.p12 Normal file

Binary file not shown.

Binary file not shown.

BIN
release/mac/Release/test_httpApi Executable file

Binary file not shown.

Binary file not shown.

BIN
release/mac/Release/test_player Executable file

Binary file not shown.

BIN
release/mac/Release/test_pusher Executable file

Binary file not shown.

Binary file not shown.

BIN
release/mac/Release/test_server Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,3 @@
执行可执行程序时可以直接双击MediaServer运行
如果由于端口权限问题导致启动失败,请修改配置文件(.ini后缀的文件)中端口号,然后再运行

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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"){

View File

@ -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();

View File

@ -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;

View File

@ -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() ;

View File

@ -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

View File

@ -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服务器配置///////////

View File

@ -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();
}

View File

@ -36,6 +36,7 @@ using namespace toolkit;
namespace mediakit{
/**
* h264 Rtmp解码类
* h264 over rtmp h264-Frame
*/
class H264RtmpDecoder : public RtmpCodec ,public ResourcePoolHelper<H264Frame> {
public:

View File

@ -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();

View File

@ -36,6 +36,8 @@ namespace mediakit{
/**
* h264 rtp解码类
* h264 over rtsp-rtp h264-Frame
* rfc3984
*/
class H264RtpDecoder : public RtpCodec , public ResourcePoolHelper<H264Frame> {
public:

View File

@ -37,6 +37,8 @@ namespace mediakit{
/**
* h265 rtp解码类
* h265 over rtsp-rtp h265-Frame
* H265-over-RTPdraft-ietf-payload-rtp-h265-07.pdf
*/
class H265RtpDecoder : public RtpCodec , public ResourcePoolHelper<H265Frame> {
public:

View File

@ -439,7 +439,8 @@ static inline unsigned int showBitsLong(void *pvHandle, int iN)
if (iN <= 32) {
return showBits(ptPtr, iN);
}
}
return 0;
}

View File

@ -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:

View File

@ -211,13 +211,25 @@ inline bool HttpSession::checkWebSocket(){
if(!_parser["Sec-WebSocket-Protocol"].empty()){
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;
}
//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);
}
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);
}
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() {

View File

@ -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);

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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:
/**
*
* 2onWebSocketEncodeData回调
*
* @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:

View File

@ -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:

View File

@ -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){

View File

@ -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输出frameonAllTrackReady触发后才会调用此方法

View File

@ -58,15 +58,15 @@ MediaRecorder::MediaRecorder(const string &strVhost_tmp,
string m3u8FilePath;
if(enableVhost){
m3u8FilePath = hlsPath + "/" + strVhost + "/" + strApp + "/" + strId + "/hls.m3u8";
_hlsMaker.reset(new HlsRecorder(m3u8FilePath,string(VHOST_KEY) + "=" + strVhost ,hlsBufSize, hlsDuration, hlsNum));
_hlsRecorder.reset(new HlsRecorder(m3u8FilePath,string(VHOST_KEY) + "=" + strVhost ,hlsBufSize, hlsDuration, hlsNum));
}else{
m3u8FilePath = hlsPath + "/" + strApp + "/" + strId + "/hls.m3u8";
_hlsMaker.reset(new HlsRecorder(m3u8FilePath,"",hlsBufSize, hlsDuration, hlsNum));
_hlsRecorder.reset(new HlsRecorder(m3u8FilePath,"",hlsBufSize, hlsDuration, hlsNum));
}
}
#endif //defined(ENABLE_HLS)
#if defined(ENABLE_MP4V2)
#if defined(ENABLE_MP4RECORD)
GET_CONFIG(string,recordPath,Record::kFilePath);
GET_CONFIG(string,recordAppName,Record::kAppName);
@ -77,9 +77,9 @@ MediaRecorder::MediaRecorder(const string &strVhost_tmp,
} else {
mp4FilePath = recordPath + "/" + recordAppName + "/" + strApp + "/" + strId + "/";
}
_mp4Maker.reset(new Mp4Maker(mp4FilePath,strVhost,strApp,strId));
_mp4Recorder.reset(new MP4Recorder(mp4FilePath,strVhost,strApp,strId));
}
#endif //defined(ENABLE_MP4V2)
#endif //defined(ENABLE_MP4RECORD)
}
MediaRecorder::~MediaRecorder() {
@ -87,28 +87,28 @@ MediaRecorder::~MediaRecorder() {
void MediaRecorder::inputFrame(const Frame::Ptr &frame) {
#if defined(ENABLE_HLS)
if (_hlsMaker) {
_hlsMaker->inputFrame(frame);
}
#endif //defined(ENABLE_HLS)
#if defined(ENABLE_MP4V2)
if (_mp4Maker) {
_mp4Maker->inputFrame(frame);
}
#endif //defined(ENABLE_MP4V2)
}
void MediaRecorder::addTrack(const Track::Ptr &track) {
#if defined(ENABLE_HLS)
if (_hlsMaker) {
_hlsMaker->addTrack(track);
if (_hlsRecorder) {
_hlsRecorder->inputFrame(frame);
}
#endif //defined(ENABLE_HLS)
#if defined(ENABLE_MP4RECORD)
if (_mp4Maker) {
_mp4Maker->addTrack(track);
if (_mp4Recorder) {
_mp4Recorder->inputFrame(frame);
}
#endif //defined(ENABLE_MP4RECORD)
}
void MediaRecorder::addTrack(const Track::Ptr &track) {
#if defined(ENABLE_HLS)
if (_hlsRecorder) {
_hlsRecorder->addTrack(track);
}
#endif //defined(ENABLE_HLS)
#if defined(ENABLE_MP4RECORD)
if (_mp4Recorder) {
_mp4Recorder->addTrack(track);
}
#endif //defined(ENABLE_MP4RECORD)
}

View File

@ -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)
};

View File

@ -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;

View File

@ -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

View File

@ -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());
}
});
merged_frame = std::make_shared<BufferString>(std::move(merged));
}
}else{
//这里是普通帧例如B/P
merged_frame = frame;
//sps、pps这些配置帧清空掉
_config_frame_cache.clear();
track_info.stamp.revise(back->dts(),back->pts(),dts_out,pts_out);
_timestamp = dts_out;
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();
}
//输入到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());
_frameCached.emplace_back(Frame::getCacheAbleFrame(frame));
}
break;
default: {

View File

@ -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

View File

@ -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() {

View File

@ -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];
};

View File

@ -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{

View File

@ -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

View File

@ -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;
}
@ -444,10 +444,12 @@ void RtmpSession::onRtmpChunk(RtmpPacket &chunkData) {
throw std::runtime_error("Not a rtmp publisher!");
}
GET_CONFIG(bool,rtmp_modify_stamp,Rtmp::kModifyStamp);
if(rtmp_modify_stamp){
chunkData.timeStamp = _stampTicker[chunkData.typeId % 2].elapsedTime();
}
_pPublisherSrc->onWrite(std::make_shared<RtmpPacket>(std::move(chunkData)));
if(rtmp_modify_stamp){
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)));
}
break;
default:
@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -31,6 +31,7 @@
#include <vector>
#include <unordered_map>
#include <map>
#include <stdexcept>
enum AMFType {
AMF_NUMBER,
AMF_INTEGER,

Some files were not shown because too many files have changed in this diff Show More