diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..946b404b --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +release/ filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 1c66f6b5..853ad8f7 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,5 @@ /cmake-build-debug/ /.idea/ -/c_wrapper/.idea/ \ No newline at end of file +/c_wrapper/.idea/ +/release/mac/Debug/ \ No newline at end of file diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 2bb23400..57e7c83d 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 2bb234006c852b1d1a61a0e9a7f39dde7105fe34 +Subproject commit 57e7c83d5667b1e06fb8f5ca73dbe3f04a9fc67f diff --git a/3rdpart/media-server b/3rdpart/media-server index 6df71e01..40edf624 160000 --- a/3rdpart/media-server +++ b/3rdpart/media-server @@ -1 +1 @@ -Subproject commit 6df71e01c174cdfe69e597cc4acb766a20b28620 +Subproject commit 40edf6243d9d99676062062efdec203b24a178aa diff --git a/CMakeLists.txt b/CMakeLists.txt index e7683d85..5c1265ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/README.md b/README.md index 4e09398f..efff6ddf 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ - Well performance and stable test,can be used commercially. - Support linux, macos, ios, android, Windows Platforms. - Very low latency(lower then one second), video opened immediately. +- **Now Support websocket-flv!** ## Features @@ -116,7 +117,12 @@ - Apple OSX(Darwin), both 32 and 64bits. - All hardware with x86/x86_64/arm/mips cpu. - Windows. -- **You must use git to clone the complete code. Do not download the source code by downloading zip package. Otherwise, the sub-module code will not be downloaded by default.** +- **You must use git to clone the complete code. Do not download the source code by downloading zip package. Otherwise, the sub-module code will not be downloaded by default.You can do it like this:** +``` +git clone https://github.com/zlmediakit/ZLMediaKit.git +cd ZLMediaKit +git submodule update --init +``` @@ -231,7 +237,7 @@ It is recommended to compile on Ubuntu or MacOS,compiling on windows is cumber ## Usage - As server: - ``` + ```cpp TcpServer::Ptr rtspSrv(new TcpServer()); TcpServer::Ptr rtmpSrv(new TcpServer()); TcpServer::Ptr httpSrv(new TcpServer()); @@ -244,7 +250,7 @@ It is recommended to compile on Ubuntu or MacOS,compiling on windows is cumber ``` - As player: - ``` + ```cpp MediaPlayer::Ptr player(new MediaPlayer()); weak_ptr 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"); diff --git a/README_CN.md b/README_CN.md index 866c5b3f..504cf0b6 100644 --- a/README_CN.md +++ b/README_CN.md @@ -11,6 +11,7 @@ - 代码经过大量的稳定性、性能测试,可满足商用服务器项目。 - 支持linux、macos、ios、android、windows平台 - 支持画面秒开(GOP缓存)、极低延时(1秒内) +- **支持websocket-flv直播** - [ZLMediaKit高并发实现原理](https://github.com/xiongziliang/ZLMediaKit/wiki/ZLMediaKit%E9%AB%98%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86) ## 项目定位 @@ -127,7 +128,12 @@ ## 编译要求 - 编译器支持C++11,GCC4.8/Clang3.3/VC2015或以上 - cmake3.2或以上 -- **必须使用git下载完整的代码,不要使用下载zip包的方式下载源码,否则子模块代码默认不下载!** +- **必须使用git下载完整的代码,不要使用下载zip包的方式下载源码,否则子模块代码默认不下载!你可以像以下这样操作:** +``` +git clone https://github.com/zlmediakit/ZLMediaKit.git +cd ZLMediaKit +git submodule update --init +``` ## 编译(Linux) - 我的编译环境 @@ -219,7 +225,7 @@ ``` ## 使用方法 - 作为服务器: - ``` + ```cpp TcpServer::Ptr rtspSrv(new TcpServer()); TcpServer::Ptr rtmpSrv(new TcpServer()); TcpServer::Ptr httpSrv(new TcpServer()); @@ -232,7 +238,7 @@ ``` - 作为播放器: - ``` + ```cpp MediaPlayer::Ptr player(new MediaPlayer()); weak_ptr 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代码) diff --git a/conf/config.ini b/conf/config.ini new file mode 100644 index 00000000..8a36fdf5 --- /dev/null +++ b/conf/config.ini @@ -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=404 Not Found

您访问的资源不存在!


ZLMediaKit-4.0
+#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 + diff --git a/release/linux/Release/MediaServer b/release/linux/Release/MediaServer new file mode 100755 index 00000000..890469f7 Binary files /dev/null and b/release/linux/Release/MediaServer differ diff --git a/release/linux/Release/libcrypto.so.1.0.1e b/release/linux/Release/libcrypto.so.1.0.1e new file mode 100755 index 00000000..bc780929 Binary files /dev/null and b/release/linux/Release/libcrypto.so.1.0.1e differ diff --git a/release/linux/Release/libflv.a b/release/linux/Release/libflv.a new file mode 100755 index 00000000..ad2698ff Binary files /dev/null and b/release/linux/Release/libflv.a differ diff --git a/release/linux/Release/libjsoncpp.a b/release/linux/Release/libjsoncpp.a new file mode 100755 index 00000000..c2fe4ef1 Binary files /dev/null and b/release/linux/Release/libjsoncpp.a differ diff --git a/release/linux/Release/libmov.a b/release/linux/Release/libmov.a new file mode 100755 index 00000000..4cfb4984 Binary files /dev/null and b/release/linux/Release/libmov.a differ diff --git a/release/linux/Release/libmpeg.a b/release/linux/Release/libmpeg.a new file mode 100755 index 00000000..873b0403 Binary files /dev/null and b/release/linux/Release/libmpeg.a differ diff --git a/release/linux/Release/libmysqlclient.so.16.0.0 b/release/linux/Release/libmysqlclient.so.16.0.0 new file mode 100755 index 00000000..e05df331 Binary files /dev/null and b/release/linux/Release/libmysqlclient.so.16.0.0 differ diff --git a/release/linux/Release/libssl.so.1.0.1e b/release/linux/Release/libssl.so.1.0.1e new file mode 100755 index 00000000..d1f7a440 Binary files /dev/null and b/release/linux/Release/libssl.so.1.0.1e differ diff --git a/release/linux/Release/libzlmediakit.a b/release/linux/Release/libzlmediakit.a new file mode 100755 index 00000000..b6cf1de5 Binary files /dev/null and b/release/linux/Release/libzlmediakit.a differ diff --git a/release/linux/Release/libzltoolkit.a b/release/linux/Release/libzltoolkit.a new file mode 100755 index 00000000..2f0df396 Binary files /dev/null and b/release/linux/Release/libzltoolkit.a differ diff --git a/release/linux/Release/readme.md b/release/linux/Release/readme.md new file mode 100644 index 00000000..bf736569 --- /dev/null +++ b/release/linux/Release/readme.md @@ -0,0 +1,9 @@ +执行可执行程序时,请在终端输入: + +``` +export LD_LIBRARY_PATH=./ +./MediaServer -d & +``` + +如果由于so动态库链接失败导致运行不起来,请重建so库软链接 +如果由于端口权限问题导致启动失败,请修改配置文件中端口号,或者以root权限运行 diff --git a/release/linux/Release/ssl.p12 b/release/linux/Release/ssl.p12 new file mode 100755 index 00000000..fe42e11f Binary files /dev/null and b/release/linux/Release/ssl.p12 differ diff --git a/release/linux/Release/test_benchmark b/release/linux/Release/test_benchmark new file mode 100755 index 00000000..edfd5d4f Binary files /dev/null and b/release/linux/Release/test_benchmark differ diff --git a/release/linux/Release/test_httpApi b/release/linux/Release/test_httpApi new file mode 100755 index 00000000..e61a538a Binary files /dev/null and b/release/linux/Release/test_httpApi differ diff --git a/release/linux/Release/test_httpClient b/release/linux/Release/test_httpClient new file mode 100755 index 00000000..c77ae594 Binary files /dev/null and b/release/linux/Release/test_httpClient differ diff --git a/release/linux/Release/test_pusher b/release/linux/Release/test_pusher new file mode 100755 index 00000000..2ad92502 Binary files /dev/null and b/release/linux/Release/test_pusher differ diff --git a/release/linux/Release/test_pusherMp4 b/release/linux/Release/test_pusherMp4 new file mode 100755 index 00000000..f7247502 Binary files /dev/null and b/release/linux/Release/test_pusherMp4 differ diff --git a/release/linux/Release/test_server b/release/linux/Release/test_server new file mode 100755 index 00000000..1e40836d Binary files /dev/null and b/release/linux/Release/test_server differ diff --git a/release/mac/Release/MediaServer b/release/mac/Release/MediaServer new file mode 100755 index 00000000..a6c6cc71 Binary files /dev/null and b/release/mac/Release/MediaServer differ diff --git a/release/mac/Release/libcrypto.1.0.0.dylib b/release/mac/Release/libcrypto.1.0.0.dylib new file mode 100644 index 00000000..16d67d1d Binary files /dev/null and b/release/mac/Release/libcrypto.1.0.0.dylib differ diff --git a/release/mac/Release/libfaac.0.0.0.dylib b/release/mac/Release/libfaac.0.0.0.dylib new file mode 100644 index 00000000..a1093f8f Binary files /dev/null and b/release/mac/Release/libfaac.0.0.0.dylib differ diff --git a/release/mac/Release/libflv.a b/release/mac/Release/libflv.a new file mode 100644 index 00000000..7271c9a2 Binary files /dev/null and b/release/mac/Release/libflv.a differ diff --git a/release/mac/Release/libjsoncpp.a b/release/mac/Release/libjsoncpp.a new file mode 100644 index 00000000..7ce43685 Binary files /dev/null and b/release/mac/Release/libjsoncpp.a differ diff --git a/release/mac/Release/libmov.a b/release/mac/Release/libmov.a new file mode 100644 index 00000000..7baaf5d2 Binary files /dev/null and b/release/mac/Release/libmov.a differ diff --git a/release/mac/Release/libmp4v2.2.dylib b/release/mac/Release/libmp4v2.2.dylib new file mode 100644 index 00000000..32d9d75e Binary files /dev/null and b/release/mac/Release/libmp4v2.2.dylib differ diff --git a/release/mac/Release/libmpeg.a b/release/mac/Release/libmpeg.a new file mode 100644 index 00000000..bf592422 Binary files /dev/null and b/release/mac/Release/libmpeg.a differ diff --git a/release/mac/Release/libmysqlclient.21.dylib b/release/mac/Release/libmysqlclient.21.dylib new file mode 100644 index 00000000..8330d43d Binary files /dev/null and b/release/mac/Release/libmysqlclient.21.dylib differ diff --git a/release/mac/Release/libssl.1.0.0.dylib b/release/mac/Release/libssl.1.0.0.dylib new file mode 100644 index 00000000..16d67d1d Binary files /dev/null and b/release/mac/Release/libssl.1.0.0.dylib differ diff --git a/release/mac/Release/libx264.155.dylib b/release/mac/Release/libx264.155.dylib new file mode 100644 index 00000000..669853bf Binary files /dev/null and b/release/mac/Release/libx264.155.dylib differ diff --git a/release/mac/Release/libzlmediakit.a b/release/mac/Release/libzlmediakit.a new file mode 100644 index 00000000..95a8b4a7 Binary files /dev/null and b/release/mac/Release/libzlmediakit.a differ diff --git a/release/mac/Release/libzltoolkit.a b/release/mac/Release/libzltoolkit.a new file mode 100644 index 00000000..cd16d54d Binary files /dev/null and b/release/mac/Release/libzltoolkit.a differ diff --git a/release/mac/Release/readme.md b/release/mac/Release/readme.md new file mode 100644 index 00000000..20d0b0aa --- /dev/null +++ b/release/mac/Release/readme.md @@ -0,0 +1,9 @@ +执行可执行程序时,请在终端输入: + +``` +export DYLD_LIBRARY_PATH=./ +./MediaServer -d & +``` + +如果由于so动态库链接失败导致运行不起来,请重建so库软链接 +如果由于端口权限问题导致启动失败,请修改配置文件中端口号,或者以root权限运行 diff --git a/release/mac/Release/ssl.p12 b/release/mac/Release/ssl.p12 new file mode 100644 index 00000000..fe42e11f Binary files /dev/null and b/release/mac/Release/ssl.p12 differ diff --git a/release/mac/Release/test_benchmark b/release/mac/Release/test_benchmark new file mode 100755 index 00000000..0d834e47 Binary files /dev/null and b/release/mac/Release/test_benchmark differ diff --git a/release/mac/Release/test_httpApi b/release/mac/Release/test_httpApi new file mode 100755 index 00000000..6dfa9efd Binary files /dev/null and b/release/mac/Release/test_httpApi differ diff --git a/release/mac/Release/test_httpClient b/release/mac/Release/test_httpClient new file mode 100755 index 00000000..81a292bc Binary files /dev/null and b/release/mac/Release/test_httpClient differ diff --git a/release/mac/Release/test_player b/release/mac/Release/test_player new file mode 100755 index 00000000..d73ef2df Binary files /dev/null and b/release/mac/Release/test_player differ diff --git a/release/mac/Release/test_pusher b/release/mac/Release/test_pusher new file mode 100755 index 00000000..2b2fa37a Binary files /dev/null and b/release/mac/Release/test_pusher differ diff --git a/release/mac/Release/test_pusherMp4 b/release/mac/Release/test_pusherMp4 new file mode 100755 index 00000000..153a9409 Binary files /dev/null and b/release/mac/Release/test_pusherMp4 differ diff --git a/release/mac/Release/test_server b/release/mac/Release/test_server new file mode 100755 index 00000000..d173faf3 Binary files /dev/null and b/release/mac/Release/test_server differ diff --git a/release/windows/Release/MediaServer.exe b/release/windows/Release/MediaServer.exe new file mode 100644 index 00000000..5a034af7 Binary files /dev/null and b/release/windows/Release/MediaServer.exe differ diff --git a/release/windows/Release/jsoncpp.lib b/release/windows/Release/jsoncpp.lib new file mode 100644 index 00000000..832c4cc8 Binary files /dev/null and b/release/windows/Release/jsoncpp.lib differ diff --git a/release/windows/Release/libcrypto-1_1.dll b/release/windows/Release/libcrypto-1_1.dll new file mode 100644 index 00000000..a5cf4688 Binary files /dev/null and b/release/windows/Release/libcrypto-1_1.dll differ diff --git a/release/windows/Release/libmysql.dll b/release/windows/Release/libmysql.dll new file mode 100644 index 00000000..4e40ffd2 Binary files /dev/null and b/release/windows/Release/libmysql.dll differ diff --git a/release/windows/Release/libssl-1_1.dll b/release/windows/Release/libssl-1_1.dll new file mode 100644 index 00000000..ef23944d Binary files /dev/null and b/release/windows/Release/libssl-1_1.dll differ diff --git a/release/windows/Release/mov.lib b/release/windows/Release/mov.lib new file mode 100644 index 00000000..4526802b Binary files /dev/null and b/release/windows/Release/mov.lib differ diff --git a/release/windows/Release/mpeg.lib b/release/windows/Release/mpeg.lib new file mode 100644 index 00000000..1d566d79 Binary files /dev/null and b/release/windows/Release/mpeg.lib differ diff --git a/release/windows/Release/readme.md b/release/windows/Release/readme.md new file mode 100644 index 00000000..c99ba5fe --- /dev/null +++ b/release/windows/Release/readme.md @@ -0,0 +1,3 @@ +执行可执行程序时,可以直接双击MediaServer运行 + +如果由于端口权限问题导致启动失败,请修改配置文件(.ini后缀的文件)中端口号,然后再运行 diff --git a/release/windows/Release/ssl.p12 b/release/windows/Release/ssl.p12 new file mode 100644 index 00000000..fe42e11f Binary files /dev/null and b/release/windows/Release/ssl.p12 differ diff --git a/release/windows/Release/test_benchmark.exe b/release/windows/Release/test_benchmark.exe new file mode 100644 index 00000000..1eebc1d0 Binary files /dev/null and b/release/windows/Release/test_benchmark.exe differ diff --git a/release/windows/Release/test_httpApi.exe b/release/windows/Release/test_httpApi.exe new file mode 100644 index 00000000..513ea222 Binary files /dev/null and b/release/windows/Release/test_httpApi.exe differ diff --git a/release/windows/Release/test_httpClient.exe b/release/windows/Release/test_httpClient.exe new file mode 100644 index 00000000..91ba52fc Binary files /dev/null and b/release/windows/Release/test_httpClient.exe differ diff --git a/release/windows/Release/test_pusher.exe b/release/windows/Release/test_pusher.exe new file mode 100644 index 00000000..16cf5fa1 Binary files /dev/null and b/release/windows/Release/test_pusher.exe differ diff --git a/release/windows/Release/test_pusherMp4.exe b/release/windows/Release/test_pusherMp4.exe new file mode 100644 index 00000000..56f763c4 Binary files /dev/null and b/release/windows/Release/test_pusherMp4.exe differ diff --git a/release/windows/Release/test_server.exe b/release/windows/Release/test_server.exe new file mode 100644 index 00000000..273a5da7 Binary files /dev/null and b/release/windows/Release/test_server.exe differ diff --git a/release/windows/Release/zlmediakit.lib b/release/windows/Release/zlmediakit.lib new file mode 100644 index 00000000..50711f86 Binary files /dev/null and b/release/windows/Release/zlmediakit.lib differ diff --git a/release/windows/Release/zltoolkit.lib b/release/windows/Release/zltoolkit.lib new file mode 100644 index 00000000..f5c4b3c2 Binary files /dev/null and b/release/windows/Release/zltoolkit.lib differ diff --git a/server/WebHook.cpp b/server/WebHook.cpp index dffaf735..72fb7b37 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -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"){ diff --git a/src/Common/MediaSink.cpp b/src/Common/MediaSink.cpp index 4d45f82e..b2263e54 100644 --- a/src/Common/MediaSink.cpp +++ b/src/Common/MediaSink.cpp @@ -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(); diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index 7f1535a6..cb668f37 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -123,6 +123,7 @@ MediaSource::Ptr MediaSource::find( lock_guard 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; diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index 8a560997..a54bc8d8 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -186,6 +186,14 @@ public: } virtual int readerCount() = 0; + + /** + * 获取track + * @return + */ + virtual vector getTracks(bool trackReady) const{ + return vector(0); + } protected: void regist() ; bool unregist() ; diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 1b4bf795..63a22495 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -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 diff --git a/src/Common/config.h b/src/Common/config.h index 6817e02e..47c06bfd 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -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服务器配置/////////// diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index 2af513f6..156c4122 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -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(); } diff --git a/src/Extension/H264Rtmp.h b/src/Extension/H264Rtmp.h index ed015e76..68802d68 100644 --- a/src/Extension/H264Rtmp.h +++ b/src/Extension/H264Rtmp.h @@ -36,6 +36,7 @@ using namespace toolkit; namespace mediakit{ /** * h264 Rtmp解码类 + * 将 h264 over rtmp 解复用出 h264-Frame */ class H264RtmpDecoder : public RtmpCodec ,public ResourcePoolHelper { public: diff --git a/src/Extension/H264Rtp.cpp b/src/Extension/H264Rtp.cpp index 51581c6c..8a961e34 100644 --- a/src/Extension/H264Rtp.cpp +++ b/src/Extension/H264Rtp.cpp @@ -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(); diff --git a/src/Extension/H264Rtp.h b/src/Extension/H264Rtp.h index 09f2346d..94ad7c46 100644 --- a/src/Extension/H264Rtp.h +++ b/src/Extension/H264Rtp.h @@ -36,6 +36,8 @@ namespace mediakit{ /** * h264 rtp解码类 + * 将 h264 over rtsp-rtp 解复用出 h264-Frame + * rfc3984 */ class H264RtpDecoder : public RtpCodec , public ResourcePoolHelper { public: diff --git a/src/Extension/H265Rtp.h b/src/Extension/H265Rtp.h index f243baf2..fbe84031 100644 --- a/src/Extension/H265Rtp.h +++ b/src/Extension/H265Rtp.h @@ -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 { public: diff --git a/src/Extension/SPSParser.c b/src/Extension/SPSParser.c index dbc90ed4..b073b9cb 100644 --- a/src/Extension/SPSParser.c +++ b/src/Extension/SPSParser.c @@ -439,7 +439,8 @@ static inline unsigned int showBitsLong(void *pvHandle, int iN) if (iN <= 32) { return showBits(ptPtr, iN); - } + } + return 0; } diff --git a/src/Http/HttpCookieManager.h b/src/Http/HttpCookieManager.h index 172f4b08..4d67e386 100644 --- a/src/Http/HttpCookieManager.h +++ b/src/Http/HttpCookieManager.h @@ -30,6 +30,7 @@ #include #include #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 , public noncopyable{ +class HttpServerCookie : public AnyStorage , public noncopyable{ public: typedef std::shared_ptr Ptr; /** @@ -108,6 +109,8 @@ public: * @return */ std::shared_ptr > getLock(); + + private: string cookieExpireTime() const ; private: diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index 367790c5..4f9118c5 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -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 &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 weakSelf = dynamic_pointer_cast(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(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(), 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(cookie_path); //记录能否访问 - (*cookie)[kAccessErrKey] = errMsg; + (*cookie)[kAccessErrKey].set(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()); } 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()); } 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() { diff --git a/src/Http/HttpSession.h b/src/Http/HttpSession.h index e1df8b1c..d99bff95 100644 --- a/src/Http/HttpSession.h +++ b/src/Http/HttpSession.h @@ -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 &cb = nullptr); inline bool checkWebSocket(); inline bool emitHttpEvent(bool doInvoke); inline void urlDecode(Parser &parser); diff --git a/src/Http/WebSocketSession.h b/src/Http/WebSocketSession.h index ab461f5f..6862e9bc 100644 --- a/src/Http/WebSocketSession.h +++ b/src/Http/WebSocketSession.h @@ -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(header)._opcode = WebSocketHeader::PONG; - HttpSessionType::encode(header,(uint8_t *)_remian_data.data(),_remian_data.size()); + HttpSessionType::encode(header,std::make_shared(_remian_data)); } break; case WebSocketHeader::CONTINUATION:{ @@ -132,8 +132,7 @@ protected: break; case WebSocketHeader::TEXT: case WebSocketHeader::BINARY:{ - BufferString::Ptr buffer = std::make_shared(_remian_data); - _session->onRecv(buffer); + _session->onRecv(std::make_shared(_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 onBeforeSendCB; diff --git a/src/Http/WebSocketSplitter.cpp b/src/Http/WebSocketSplitter.cpp index 280e3a1c..42fa47f0 100644 --- a/src/Http/WebSocketSplitter.cpp +++ b/src/Http/WebSocketSplitter.cpp @@ -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(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); } } diff --git a/src/Http/WebSocketSplitter.h b/src/Http/WebSocketSplitter.h index 53447f62..159c5465 100644 --- a/src/Http/WebSocketSplitter.h +++ b/src/Http/WebSocketSplitter.h @@ -31,8 +31,10 @@ #include #include #include -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: diff --git a/src/MediaFile/MP4Muxer.cpp b/src/MediaFile/MP4Muxer.cpp index 92b0135c..a8a38ab4 100644 --- a/src/MediaFile/MP4Muxer.cpp +++ b/src/MediaFile/MP4Muxer.cpp @@ -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: diff --git a/src/MediaFile/Mp4Maker.cpp b/src/MediaFile/MP4Recorder.cpp similarity index 93% rename from src/MediaFile/Mp4Maker.cpp rename to src/MediaFile/MP4Recorder.cpp index 3cf2a7ec..658243c9 100644 --- a/src/MediaFile/Mp4Maker.cpp +++ b/src/MediaFile/MP4Recorder.cpp @@ -28,7 +28,7 @@ #include #include #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){ diff --git a/src/MediaFile/Mp4Maker.h b/src/MediaFile/MP4Recorder.h similarity index 95% rename from src/MediaFile/Mp4Maker.h rename to src/MediaFile/MP4Recorder.h index cef69701..ca97cb3f 100644 --- a/src/MediaFile/Mp4Maker.h +++ b/src/MediaFile/MP4Recorder.h @@ -55,14 +55,14 @@ public: string strStreamId;//流ID string strVhost;//vhost }; -class Mp4Maker : public MediaSink{ +class MP4Recorder : public MediaSink{ public: - typedef std::shared_ptr Ptr; - Mp4Maker(const string &strPath, + typedef std::shared_ptr Ptr; + MP4Recorder(const string &strPath, const string &strVhost , const string &strApp, const string &strStreamId); - virtual ~Mp4Maker(); + virtual ~MP4Recorder(); private: /** * 某Track输出frame,在onAllTrackReady触发后才会调用此方法 diff --git a/src/MediaFile/MediaRecorder.cpp b/src/MediaFile/MediaRecorder.cpp index 71eac5e4..24309dfa 100644 --- a/src/MediaFile/MediaRecorder.cpp +++ b/src/MediaFile/MediaRecorder.cpp @@ -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) } diff --git a/src/MediaFile/MediaRecorder.h b/src/MediaFile/MediaRecorder.h index da06885e..70ff0093 100644 --- a/src/MediaFile/MediaRecorder.h +++ b/src/MediaFile/MediaRecorder.h @@ -30,7 +30,7 @@ #include #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 _hlsMaker; + std::shared_ptr _hlsRecorder; #endif //defined(ENABLE_HLS) #if defined(ENABLE_MP4RECORD) - std::shared_ptr _mp4Maker; + std::shared_ptr _mp4Recorder; #endif //defined(ENABLE_MP4RECORD) }; diff --git a/src/MediaFile/Stamp.cpp b/src/MediaFile/Stamp.cpp index d4b67df9..57045733 100644 --- a/src/MediaFile/Stamp.cpp +++ b/src/MediaFile/Stamp.cpp @@ -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(); + _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; diff --git a/src/MediaFile/Stamp.h b/src/MediaFile/Stamp.h index e6b2d3b2..4e6241b1 100644 --- a/src/MediaFile/Stamp.h +++ b/src/MediaFile/Stamp.h @@ -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 _ticker; + SmoothTicker _ticker; }; }//namespace mediakit diff --git a/src/MediaFile/TsMuxer.cpp b/src/MediaFile/TsMuxer.cpp index 6bd4840e..f0d1092a 100644 --- a/src/MediaFile/TsMuxer.cpp +++ b/src/MediaFile/TsMuxer.cpp @@ -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(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(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: { diff --git a/src/MediaFile/TsMuxer.h b/src/MediaFile/TsMuxer.h index 476a56ae..b4ea0fab 100644 --- a/src/MediaFile/TsMuxer.h +++ b/src/MediaFile/TsMuxer.h @@ -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 _codec_to_trackid; - string _config_frame_cache; + List _frameCached; }; }//namespace mediakit diff --git a/src/Rtmp/FlvMuxer.cpp b/src/Rtmp/FlvMuxer.cpp index 6354b40a..e47ba1d3 100644 --- a/src/Rtmp/FlvMuxer.cpp +++ b/src/Rtmp/FlvMuxer.cpp @@ -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() { diff --git a/src/Rtmp/FlvMuxer.h b/src/Rtmp/FlvMuxer.h index 99be3275..7f48d3fd 100644 --- a/src/Rtmp/FlvMuxer.h +++ b/src/Rtmp/FlvMuxer.h @@ -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]; }; diff --git a/src/Rtmp/RtmpPlayer.h b/src/Rtmp/RtmpPlayer.h index 142ab5fb..7d479d1e 100644 --- a/src/Rtmp/RtmpPlayer.h +++ b/src/Rtmp/RtmpPlayer.h @@ -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 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{ diff --git a/src/Rtmp/RtmpProtocol.cpp b/src/Rtmp/RtmpProtocol.cpp index 6fef2632..ad21351f 100644 --- a/src/Rtmp/RtmpProtocol.cpp +++ b/src/Rtmp/RtmpProtocol.cpp @@ -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 diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index 10f76773..b1a045b1 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -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 g_mapCmd; + static unordered_map 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(); - 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(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(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); } diff --git a/src/Rtmp/RtmpSession.h b/src/Rtmp/RtmpSession.h index c6f1d858..fbb8cf38 100644 --- a/src/Rtmp/RtmpSession.h +++ b/src/Rtmp/RtmpSession.h @@ -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::RingReader::Ptr _pRingReader; std::shared_ptr _pPublisherSrc; std::weak_ptr _pPlayerSrc; - uint32_t _aui32FirstStamp[2] = {0}; + //时间戳修整器 + Stamp _stamp[2]; //消耗的总流量 uint64_t _ui64TotalBytes = 0; diff --git a/src/Rtmp/RtmpToRtspMediaSource.h b/src/Rtmp/RtmpToRtspMediaSource.h index 98c059f2..4b8138b6 100644 --- a/src/Rtmp/RtmpToRtspMediaSource.h +++ b/src/Rtmp/RtmpToRtspMediaSource.h @@ -96,6 +96,17 @@ public: int readerCount() override { return RtmpMediaSource::readerCount() + (_muxer ? _muxer->readerCount() : 0); } + + /** + * 获取track + * @return + */ + vector getTracks(bool trackReady) const override { + if(!_demuxer){ + return this->RtmpMediaSource::getTracks(trackReady); + } + return _demuxer->getTracks(trackReady); + } private: RtmpDemuxer::Ptr _demuxer; MultiMediaSourceMuxer::Ptr _muxer; diff --git a/src/Rtmp/amf.h b/src/Rtmp/amf.h index 878af604..8463d780 100644 --- a/src/Rtmp/amf.h +++ b/src/Rtmp/amf.h @@ -31,6 +31,7 @@ #include #include #include +#include enum AMFType { AMF_NUMBER, AMF_INTEGER, diff --git a/src/Rtsp/RtpBroadCaster.cpp b/src/Rtsp/RtpMultiCaster.cpp similarity index 90% rename from src/Rtsp/RtpBroadCaster.cpp rename to src/Rtsp/RtpMultiCaster.cpp index b5e4b7dd..1de5bcfe 100644 --- a/src/Rtsp/RtpBroadCaster.cpp +++ b/src/Rtsp/RtpMultiCaster.cpp @@ -26,7 +26,7 @@ #include #include -#include "RtpBroadCaster.h" +#include "RtpMultiCaster.h" #include "Util/util.h" #include "Network/sockutil.h" #include "RtspSession.h" @@ -81,10 +81,10 @@ void MultiCastAddressMaker::release(uint32_t iAddr){ } -recursive_mutex RtpBroadCaster::g_mtx; -unordered_map > RtpBroadCaster::g_mapBroadCaster; +recursive_mutex RtpMultiCaster::g_mtx; +unordered_map > RtpMultiCaster::g_mapBroadCaster; -void RtpBroadCaster::setDetachCB(void* listener, const onDetach& cb) { +void RtpMultiCaster::setDetachCB(void* listener, const onDetach& cb) { lock_guard lck(_mtx); if(cb){ _mapDetach.emplace(listener,cb); @@ -92,12 +92,12 @@ void RtpBroadCaster::setDetachCB(void* listener, const onDetach& cb) { _mapDetach.erase(listener); } } -RtpBroadCaster::~RtpBroadCaster() { +RtpMultiCaster::~RtpMultiCaster() { _pReader->setReadCB(nullptr); _pReader->setDetachCB(nullptr); DebugL; } -RtpBroadCaster::RtpBroadCaster(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream) { +RtpMultiCaster::RtpMultiCaster(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream) { auto src = dynamic_pointer_cast(MediaSource::find(RTSP_SCHEMA,strVhost,strApp, strStream)); if(!src){ auto strErr = StrPrinter << "未找到媒体源:" << strVhost << " " << strApp << " " << strStream << endl; @@ -148,22 +148,22 @@ RtpBroadCaster::RtpBroadCaster(const EventPoller::Ptr &poller,const string &strL << strVhost << " " << strApp << " " << strStream; } -uint16_t RtpBroadCaster::getPort(TrackType trackType){ +uint16_t RtpMultiCaster::getPort(TrackType trackType){ return _apUdpSock[trackType]->get_local_port(); } -string RtpBroadCaster::getIP(){ +string RtpMultiCaster::getIP(){ return inet_ntoa(_aPeerUdpAddr[0].sin_addr); } -RtpBroadCaster::Ptr RtpBroadCaster::make(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream){ +RtpMultiCaster::Ptr RtpMultiCaster::make(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream){ try{ - auto ret = Ptr(new RtpBroadCaster(poller,strLocalIp,strVhost,strApp,strStream),[poller](RtpBroadCaster *ptr){ + auto ret = Ptr(new RtpMultiCaster(poller,strLocalIp,strVhost,strApp,strStream),[poller](RtpMultiCaster *ptr){ poller->async([ptr]() { delete ptr; }); }); lock_guard lck(g_mtx); string strKey = StrPrinter << strLocalIp << " " << strVhost << " " << strApp << " " << strStream << endl; - weak_ptr weakPtr = ret; + weak_ptr weakPtr = ret; g_mapBroadCaster.emplace(strKey,weakPtr); return ret; }catch (std::exception &ex) { @@ -172,7 +172,7 @@ RtpBroadCaster::Ptr RtpBroadCaster::make(const EventPoller::Ptr &poller,const st } } -RtpBroadCaster::Ptr RtpBroadCaster::get(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream) { +RtpMultiCaster::Ptr RtpMultiCaster::get(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream) { string strKey = StrPrinter << strLocalIp << " " << strVhost << " " << strApp << " " << strStream << endl; lock_guard lck(g_mtx); auto it = g_mapBroadCaster.find(strKey); diff --git a/src/Rtsp/RtpBroadCaster.h b/src/Rtsp/RtpMultiCaster.h similarity index 93% rename from src/Rtsp/RtpBroadCaster.h rename to src/Rtsp/RtpMultiCaster.h index 10a5bb48..e95802b1 100644 --- a/src/Rtsp/RtpBroadCaster.h +++ b/src/Rtsp/RtpMultiCaster.h @@ -65,18 +65,18 @@ private: recursive_mutex _mtx; unordered_set _setBadAddr; }; -class RtpBroadCaster { +class RtpMultiCaster { public: - typedef std::shared_ptr Ptr; + typedef std::shared_ptr Ptr; typedef function onDetach; - virtual ~RtpBroadCaster(); + virtual ~RtpMultiCaster(); static Ptr get(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream); void setDetachCB(void *listener,const onDetach &cb); uint16_t getPort(TrackType trackType); string getIP(); private: static recursive_mutex g_mtx; - static unordered_map > g_mapBroadCaster; + static unordered_map > g_mapBroadCaster; static Ptr make(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream); std::shared_ptr _multiAddr; @@ -86,7 +86,7 @@ private: Socket::Ptr _apUdpSock[2]; struct sockaddr_in _aPeerUdpAddr[2]; - RtpBroadCaster(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream); + RtpMultiCaster(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream); }; diff --git a/src/Rtsp/RtpReceiver.cpp b/src/Rtsp/RtpReceiver.cpp index adbf40bd..f2ff31f7 100644 --- a/src/Rtsp/RtpReceiver.cpp +++ b/src/Rtsp/RtpReceiver.cpp @@ -44,29 +44,47 @@ RtpReceiver::RtpReceiver() {} RtpReceiver::~RtpReceiver() {} bool RtpReceiver::handleOneRtp(int track_index,SdpTrack::Ptr &track, unsigned char *rtp_raw_ptr, unsigned int rtp_raw_len) { + if(rtp_raw_len < 12){ + WarnL << "rtp包太小:" << rtp_raw_len; + return false; + } + + uint8_t padding = 0; + if (rtp_raw_ptr[0] & 0x40) { + //获取padding大小 + padding = rtp_raw_ptr[rtp_raw_len - 1]; + //移除padding flag + rtp_raw_ptr[0] &= ~0x40; + //移除padding字节 + rtp_raw_len -= padding; + } + auto rtp_ptr = _rtp_pool.obtain(); auto &rtp = *rtp_ptr; - auto length = rtp_raw_len + 4; + rtp.type = track->_type; rtp.interleaved = 2 * track->_type; rtp.mark = rtp_raw_ptr[1] >> 7; rtp.PT = rtp_raw_ptr[1] & 0x7F; - //序列号 - memcpy(&rtp.sequence,rtp_raw_ptr+2,2);//内存对齐 + + //序列号,内存对齐 + memcpy(&rtp.sequence, rtp_raw_ptr + 2, 2); rtp.sequence = ntohs(rtp.sequence); - //时间戳 - memcpy(&rtp.timeStamp, rtp_raw_ptr+4, 4);//内存对齐 + + //时间戳,内存对齐 + memcpy(&rtp.timeStamp, rtp_raw_ptr + 4, 4); + rtp.timeStamp = ntohl(rtp.timeStamp); if(!track->_samplerate){ //无法把时间戳转换成毫秒 return false; } //时间戳转换成毫秒 - rtp.timeStamp = ntohl(rtp.timeStamp) * 1000LL / track->_samplerate; - //ssrc - memcpy(&rtp.ssrc,rtp_raw_ptr+8,4);//内存对齐 + rtp.timeStamp = rtp.timeStamp * 1000LL / track->_samplerate; + + //ssrc,内存对齐 + memcpy(&rtp.ssrc, rtp_raw_ptr + 8, 4); rtp.ssrc = ntohl(rtp.ssrc); - rtp.type = track->_type; if (track->_ssrc != rtp.ssrc) { if (track->_ssrc == 0) { @@ -99,19 +117,19 @@ bool RtpReceiver::handleOneRtp(int track_index,SdpTrack::Ptr &track, unsigned ch rtp.offset += ext; } - if(length <= rtp.offset){ - WarnL << "无有效负载的rtp包:" << length << "<=" << (int)rtp.offset; + if(rtp_raw_len + 4 <= rtp.offset){ + WarnL << "无有效负载的rtp包:" << rtp_raw_len << " <= " << (int)rtp.offset; return false; } - if(length > RTP_MAX_SIZE){ - WarnL << "超大的rtp包:" << length << ">" << RTP_MAX_SIZE; + if(rtp_raw_len > RTP_MAX_SIZE){ + WarnL << "超大的rtp包:" << rtp_raw_len << " > " << RTP_MAX_SIZE; return false; } //设置rtp负载长度 - rtp.setCapacity(length); - rtp.setSize(length); + rtp.setCapacity(rtp_raw_len + 4); + rtp.setSize(rtp_raw_len + 4); uint8_t *payload_ptr = (uint8_t *)rtp.data(); payload_ptr[0] = '$'; payload_ptr[1] = rtp.interleaved; diff --git a/src/Rtsp/Rtsp.cpp b/src/Rtsp/Rtsp.cpp index e2521796..8cf3dae6 100644 --- a/src/Rtsp/Rtsp.cpp +++ b/src/Rtsp/Rtsp.cpp @@ -30,6 +30,61 @@ namespace mediakit{ +static void getAttrSdp(const map &attr, _StrPrinter &printer){ + const map::value_type *ptr = nullptr; + for(auto &pr : attr){ + if(pr.first == "control"){ + ptr = ≺ + continue; + } + if(pr.second.empty()){ + printer << "a=" << pr.first << "\r\n"; + }else{ + printer << "a=" << pr.first << ":" << pr.second << "\r\n"; + } + } + if(ptr){ + printer << "a=" << ptr->first << ":" << ptr->second << "\r\n"; + } +} +string SdpTrack::toString() const { + _StrPrinter _printer; + switch (_type){ + case TrackTitle:{ + _printer << "v=" << 0 << "\r\n"; + if(!_o.empty()){ + _printer << "o="<< _o << "\r\n"; + } + if(!_c.empty()){ + _printer << "c=" << _c << "\r\n"; + } + if(!_t.empty()){ + _printer << "t=" << _t << "\r\n"; + } + + _printer << "s=RTSP Session, streamed by the ZLMediaKit\r\n"; + _printer << "i=ZLMediaKit Live Stream\r\n"; + getAttrSdp(_attr,_printer); + } + break; + case TrackAudio: + case TrackVideo:{ + if(_type == TrackAudio){ + _printer << "m=audio 0 RTP/AVP " << _pt << "\r\n"; + }else{ + _printer << "m=video 0 RTP/AVP " << _pt << "\r\n"; + } + if(!_b.empty()){ + _printer << "b=" <<_b << "\r\n"; + } + getAttrSdp(_attr,_printer); + } + break; + default: + break; + } + return _printer; +} void SdpParser::load(const string &sdp) { _track_map.clear(); string key; @@ -164,5 +219,28 @@ vector SdpParser::getAvailableTrack() const { return ret; } +string SdpParser::toString() const { + string title,audio,video; + for(auto &pr : _track_map){ + switch (pr.second->_type){ + case TrackTitle:{ + title = pr.second->toString(); + } + break; + case TrackVideo:{ + video = pr.second->toString(); + } + break; + case TrackAudio:{ + audio = pr.second->toString(); + } + break; + default: + break; + } + } + return title + video + audio; +} + }//namespace mediakit diff --git a/src/Rtsp/Rtsp.h b/src/Rtsp/Rtsp.h index fda033b9..e005c971 100644 --- a/src/Rtsp/Rtsp.h +++ b/src/Rtsp/Rtsp.h @@ -59,7 +59,7 @@ public: uint32_t timeStamp; uint16_t sequence; uint32_t ssrc; - uint8_t offset; + uint32_t offset; TrackType type; }; @@ -90,6 +90,8 @@ public: map _other; map _attr; + + string toString() const; public: int _pt; string _codec; @@ -118,6 +120,7 @@ public: bool available() const; SdpTrack::Ptr getTrack(TrackType type) const; vector getAvailableTrack() const; + string toString() const ; private: map _track_map; }; diff --git a/src/Rtsp/RtspDemuxer.cpp b/src/Rtsp/RtspDemuxer.cpp index faecd3e2..06d75e83 100644 --- a/src/Rtsp/RtspDemuxer.cpp +++ b/src/Rtsp/RtspDemuxer.cpp @@ -38,10 +38,6 @@ RtspDemuxer::RtspDemuxer(const string& sdp) { loadSdp(SdpParser(sdp)); } -RtspDemuxer::RtspDemuxer(const SdpParser &attr) { - loadSdp(attr); -} - void RtspDemuxer::loadSdp(const SdpParser &attr) { auto tracks = attr.getAvailableTrack(); for (auto &track : tracks){ diff --git a/src/Rtsp/RtspDemuxer.h b/src/Rtsp/RtspDemuxer.h index a6e692bb..8a4d407e 100644 --- a/src/Rtsp/RtspDemuxer.h +++ b/src/Rtsp/RtspDemuxer.h @@ -41,7 +41,6 @@ class RtspDemuxer : public Demuxer{ public: typedef std::shared_ptr Ptr; RtspDemuxer(const string &sdp); - RtspDemuxer(const SdpParser &parser); virtual ~RtspDemuxer(){}; /** diff --git a/src/Rtsp/RtspMuxer.cpp b/src/Rtsp/RtspMuxer.cpp index c78a7e5c..9f4da62a 100644 --- a/src/Rtsp/RtspMuxer.cpp +++ b/src/Rtsp/RtspMuxer.cpp @@ -39,7 +39,7 @@ RtspMuxer::RtspMuxer(const TitleSdp::Ptr &title){ } void RtspMuxer::onTrackReady(const Track::Ptr &track) { - //根据track生产sdp + //根据track生成sdp Sdp::Ptr sdp = track->getSdp(); if (!sdp) { return; diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index df20c36b..126135fe 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -219,14 +219,14 @@ void RtspPlayer::handleResDESCRIBE(const Parser& parser) { _strContentBase.pop_back(); } + SdpParser sdpParser(parser.Content()); //解析sdp - _sdpParser.load(parser.Content()); - _aTrackInfo = _sdpParser.getAvailableTrack(); + _aTrackInfo = sdpParser.getAvailableTrack(); if (_aTrackInfo.empty()) { throw std::runtime_error("无有效的Sdp Track"); } - if (!onCheckSDP(parser.Content(), _sdpParser)) { + if (!onCheckSDP(sdpParser.toString())) { throw std::runtime_error("onCheckSDP faied"); } @@ -450,7 +450,7 @@ void RtspPlayer::handleResPAUSE(const Parser& parser, bool bPause) { auto strRtpTime = FindField(strTrack.data(), "rtptime=", ";"); auto idx = getTrackIndexByControlSuffix(strControlSuffix); if(idx != -1){ - _aiFistStamp[idx] = atoll(strRtpTime.data()) * 1000 / _aTrackInfo[idx]->_samplerate; + _aiFistStamp[idx] = _aTrackInfo[idx]->_samplerate>0?atoll(strRtpTime.data()) * 1000 / _aTrackInfo[idx]->_samplerate :1; _aiNowStamp[idx] = _aiFistStamp[idx]; DebugL << "rtptime(ms):" << strControlSuffix <<" " << strRtpTime; } diff --git a/src/Rtsp/RtspPlayer.h b/src/Rtsp/RtspPlayer.h index 4023548b..5977539b 100644 --- a/src/Rtsp/RtspPlayer.h +++ b/src/Rtsp/RtspPlayer.h @@ -46,7 +46,7 @@ using namespace toolkit; namespace mediakit { -//实现了rtsp播放器协议部分的功能 +//实现了rtsp播放器协议部分的功能,及数据接收功能 class RtspPlayer: public PlayerBase,public TcpClient, public RtspSplitter, public RtpReceiver { public: typedef std::shared_ptr Ptr; @@ -59,7 +59,7 @@ public: float getPacketLossRate(TrackType type) const override; protected: //派生类回调函数 - virtual bool onCheckSDP(const string &strSdp, const SdpParser &parser) = 0; + virtual bool onCheckSDP(const string &strSdp) = 0; virtual void onRecvRTP(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track) = 0; uint32_t getProgressMilliSecond() const; void seekToMilliSecond(uint32_t ms); @@ -124,7 +124,6 @@ private: void createUdpSockIfNecessary(int track_idx); private: string _strUrl; - SdpParser _sdpParser; vector _aTrackInfo; function _onHandshake; Socket::Ptr _apRtpSock[2]; //RTP端口,trackid idx 为数组下标 diff --git a/src/Rtsp/RtspPlayerImp.h b/src/Rtsp/RtspPlayerImp.h index 68bf53cc..effa7328 100644 --- a/src/Rtsp/RtspPlayerImp.h +++ b/src/Rtsp/RtspPlayerImp.h @@ -61,12 +61,12 @@ public: }; private: //派生类回调函数 - bool onCheckSDP(const string &sdp, const SdpParser &parser) override { + bool onCheckSDP(const string &sdp) override { _pRtspMediaSrc = dynamic_pointer_cast(_pMediaSrc); if(_pRtspMediaSrc){ _pRtspMediaSrc->onGetSDP(sdp); } - _parser.reset(new RtspDemuxer(parser)); + _parser.reset(new RtspDemuxer(sdp)); return true; } void onRecvRTP(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track) override { diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index a7449920..eb0fa6e8 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -113,7 +113,7 @@ void RtspSession::onError(const SockException& err) { } void RtspSession::onManager() { - GET_CONFIG(uint32_t,handshake_sec,Rtsp::kKeepAliveSecond); + GET_CONFIG(uint32_t,handshake_sec,Rtsp::kHandshakeSecond); GET_CONFIG(uint32_t,keep_alive_sec,Rtsp::kKeepAliveSecond); if (_ticker.createdTime() > handshake_sec * 1000) { @@ -153,24 +153,24 @@ void RtspSession::onWholeRtspPacket(Parser &parser) { } typedef void (RtspSession::*rtsp_request_handler)(const Parser &parser); - static unordered_map s_handler_map; + static unordered_map s_cmd_functions; static onceToken token( []() { - s_handler_map.emplace("OPTIONS",&RtspSession::handleReq_Options); - s_handler_map.emplace("DESCRIBE",&RtspSession::handleReq_Describe); - s_handler_map.emplace("ANNOUNCE",&RtspSession::handleReq_ANNOUNCE); - s_handler_map.emplace("RECORD",&RtspSession::handleReq_RECORD); - s_handler_map.emplace("SETUP",&RtspSession::handleReq_Setup); - s_handler_map.emplace("PLAY",&RtspSession::handleReq_Play); - s_handler_map.emplace("PAUSE",&RtspSession::handleReq_Pause); - s_handler_map.emplace("TEARDOWN",&RtspSession::handleReq_Teardown); - s_handler_map.emplace("GET",&RtspSession::handleReq_Get); - s_handler_map.emplace("POST",&RtspSession::handleReq_Post); - s_handler_map.emplace("SET_PARAMETER",&RtspSession::handleReq_SET_PARAMETER); - s_handler_map.emplace("GET_PARAMETER",&RtspSession::handleReq_SET_PARAMETER); + s_cmd_functions.emplace("OPTIONS",&RtspSession::handleReq_Options); + s_cmd_functions.emplace("DESCRIBE",&RtspSession::handleReq_Describe); + s_cmd_functions.emplace("ANNOUNCE",&RtspSession::handleReq_ANNOUNCE); + s_cmd_functions.emplace("RECORD",&RtspSession::handleReq_RECORD); + s_cmd_functions.emplace("SETUP",&RtspSession::handleReq_Setup); + s_cmd_functions.emplace("PLAY",&RtspSession::handleReq_Play); + s_cmd_functions.emplace("PAUSE",&RtspSession::handleReq_Pause); + s_cmd_functions.emplace("TEARDOWN",&RtspSession::handleReq_Teardown); + s_cmd_functions.emplace("GET",&RtspSession::handleReq_Get); + s_cmd_functions.emplace("POST",&RtspSession::handleReq_Post); + s_cmd_functions.emplace("SET_PARAMETER",&RtspSession::handleReq_SET_PARAMETER); + s_cmd_functions.emplace("GET_PARAMETER",&RtspSession::handleReq_SET_PARAMETER); }, []() {}); - auto it = s_handler_map.find(strCmd); - if (it == s_handler_map.end()) { + auto it = s_cmd_functions.find(strCmd); + if (it == s_cmd_functions.end()) { sendRtspResponse("403 Forbidden"); shutdown(SockException(Err_shutdown,StrPrinter << "403 Forbidden:" << strCmd)); return; @@ -242,13 +242,13 @@ void RtspSession::handleReq_ANNOUNCE(const Parser &parser) { throw SockException(Err_shutdown,err); } - _strSession = makeRandStr(12); - _strSdp = parser.Content(); - _aTrackInfo = SdpParser(_strSdp).getAvailableTrack(); + SdpParser sdpParser(parser.Content()); + _strSession = makeRandStr(12); + _aTrackInfo = sdpParser.getAvailableTrack(); _pushSrc = std::make_shared(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid); _pushSrc->setListener(dynamic_pointer_cast(shared_from_this())); - _pushSrc->onGetSDP(_strSdp); + _pushSrc->onGetSDP(sdpParser.toString()); sendRtspResponse("200 OK"); } @@ -361,12 +361,11 @@ void RtspSession::onAuthSuccess() { strongSelf->shutdown(SockException(Err_shutdown,err)); return; } - //找到了响应的rtsp流 - strongSelf->_strSdp = rtsp_src->getSdp(); - SdpParser sdpParser(strongSelf->_strSdp); - strongSelf->_aTrackInfo = sdpParser.getAvailableTrack(); + //找到了相应的rtsp流 + strongSelf->_aTrackInfo = SdpParser(rtsp_src->getSdp()).getAvailableTrack(); if (strongSelf->_aTrackInfo.empty()) { //该流无效 + DebugL << "无trackInfo,该流无效"; strongSelf->send_StreamNotFound(); strongSelf->shutdown(SockException(Err_shutdown,"can not find any availabe track in sdp")); return; @@ -383,7 +382,7 @@ void RtspSession::onAuthSuccess() { {"Content-Base",strongSelf->_strContentBase + "/", "x-Accept-Retransmit","our-retransmit", "x-Accept-Dynamic-Rate","1" - },strongSelf->_strSdp); + },rtsp_src->getSdp()); }); } void RtspSession::onAuthFailed(const string &realm,const string &why,bool close) { @@ -675,14 +674,14 @@ void RtspSession::handleReq_Setup(const Parser &parser) { } break; case Rtsp::RTP_MULTICAST: { - if(!_pBrdcaster){ - _pBrdcaster = RtpBroadCaster::get(getPoller(),get_local_ip(),_mediaInfo._vhost, _mediaInfo._app, _mediaInfo._streamid); - if (!_pBrdcaster) { + if(!_multicaster){ + _multicaster = RtpMultiCaster::get(getPoller(),get_local_ip(),_mediaInfo._vhost, _mediaInfo._app, _mediaInfo._streamid); + if (!_multicaster) { send_NotAcceptable(); throw SockException(Err_shutdown, "can not get a available udp multicast socket"); } weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - _pBrdcaster->setDetachCB(this, [weakSelf]() { + _multicaster->setDetachCB(this, [weakSelf]() { auto strongSelf = weakSelf.lock(); if(!strongSelf) { return; @@ -690,7 +689,7 @@ void RtspSession::handleReq_Setup(const Parser &parser) { strongSelf->safeShutdown(SockException(Err_shutdown,"ring buffer detached")); }); } - int iSrvPort = _pBrdcaster->getPort(trackRef->_type); + int iSrvPort = _multicaster->getPort(trackRef->_type); //我们用trackIdx区分rtp和rtcp包 //由于组播udp端口是共享的,而rtcp端口为组播udp端口+1,所以rtcp端口需要改成共享端口 auto pSockRtcp = UDPServer::Instance().getSock(getPoller(),get_local_ip().data(),2*trackIdx + 1,iSrvPort + 1); @@ -704,7 +703,7 @@ void RtspSession::handleReq_Setup(const Parser &parser) { sendRtspResponse("200 OK", {"Transport",StrPrinter << "RTP/AVP;multicast;" - << "destination=" << _pBrdcaster->getIP() << ";" + << "destination=" << _multicaster->getIP() << ";" << "source=" << get_local_ip() << ";" << "port=" << iSrvPort << "-" << pSockRtcp->get_local_port() << ";" << "ttl=" << udpTTL << ";" @@ -918,6 +917,12 @@ inline void RtspSession::send_NotAcceptable() { void RtspSession::onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx) { + GET_CONFIG(bool,modify_stamp,Rtsp::kModifyStamp); + if(modify_stamp){ + int64_t dts_out; + _stamp[trackidx].revise(0, 0, dts_out, dts_out); + rtppt->timeStamp = dts_out; + } _pushSrc->onWrite(rtppt, false); } inline void RtspSession::onRcvPeerUdpData(int intervaled, const Buffer::Ptr &pBuf, const struct sockaddr& addr) { diff --git a/src/Rtsp/RtspSession.h b/src/Rtsp/RtspSession.h index 51561c6c..90ebc94f 100644 --- a/src/Rtsp/RtspSession.h +++ b/src/Rtsp/RtspSession.h @@ -36,7 +36,7 @@ #include "Common/config.h" #include "Network/TcpSession.h" #include "Player/PlayerBase.h" -#include "RtpBroadCaster.h" +#include "RtpMultiCaster.h" #include "RtspMediaSource.h" #include "RtspSplitter.h" #include "RtpReceiver.h" @@ -76,6 +76,7 @@ public: RtspSession(const Socket::Ptr &pSock); virtual ~RtspSession(); + ////TcpSession override//// void onRecv(const Buffer::Ptr &pBuf) override; void onError(const SockException &err) override; void onManager() override; @@ -119,60 +120,100 @@ protected: */ virtual void onRtcpPacket(int iTrackidx, SdpTrack::Ptr &track, unsigned char *pucData, unsigned int uiLen); private: - void handleReq_Options(const Parser &parser); //处理options方法 - void handleReq_Describe(const Parser &parser); //处理describe方法 - void handleReq_ANNOUNCE(const Parser &parser); //处理options方法 - void handleReq_RECORD(const Parser &parser); //处理options方法 - void handleReq_Setup(const Parser &parser); //处理setup方法 - void handleReq_Play(const Parser &parser); //处理play方法 - void handleReq_Pause(const Parser &parser); //处理pause方法 - void handleReq_Teardown(const Parser &parser); //处理teardown方法 - void handleReq_Get(const Parser &parser); //处理Get方法 - void handleReq_Post(const Parser &parser); //处理Post方法 - void handleReq_SET_PARAMETER(const Parser &parser); //处理SET_PARAMETER方法 + //处理options方法,获取服务器能力 + void handleReq_Options(const Parser &parser); + //处理describe方法,请求服务器rtsp sdp信息 + void handleReq_Describe(const Parser &parser); + //处理ANNOUNCE方法,请求推流,附带sdp + void handleReq_ANNOUNCE(const Parser &parser); + //处理record方法,开始推流 + void handleReq_RECORD(const Parser &parser); + //处理setup方法,播放和推流协商rtp传输方式用 + void handleReq_Setup(const Parser &parser); + //处理play方法,开始或恢复播放 + void handleReq_Play(const Parser &parser); + //处理pause方法,暂停播放 + void handleReq_Pause(const Parser &parser); + //处理teardown方法,结束播放 + void handleReq_Teardown(const Parser &parser); + //处理Get方法,rtp over http才用到 + void handleReq_Get(const Parser &parser); + //处理Post方法,rtp over http才用到 + void handleReq_Post(const Parser &parser); + //处理SET_PARAMETER、GET_PARAMETER方法,一般用于心跳 + void handleReq_SET_PARAMETER(const Parser &parser); - void inline send_StreamNotFound(); //rtsp资源未找到 - void inline send_UnsupportedTransport(); //不支持的传输模式 - void inline send_SessionNotFound(); //会话id错误 - void inline send_NotAcceptable(); //rtsp同时播放数限制 + //rtsp资源未找到 + void inline send_StreamNotFound(); + //不支持的传输模式 + void inline send_UnsupportedTransport(); + //会话id错误 + void inline send_SessionNotFound(); + //一般rtsp服务器打开端口失败时触发 + void inline send_NotAcceptable(); + //ssrc转字符串 inline string printSSRC(uint32_t ui32Ssrc); + + //获取track下标 inline int getTrackIndexByTrackType(TrackType type); inline int getTrackIndexByControlSuffix(const string &controlSuffix); inline int getTrackIndexByInterleaved(int interleaved); + //一般用于接收udp打洞包,也用于rtsp推流 inline void onRcvPeerUdpData(int intervaled, const Buffer::Ptr &pBuf, const struct sockaddr &addr); + //配合onRcvPeerUdpData使用 inline void startListenPeerUdpData(int iTrackIdx); - //认证相关 + ////rtsp专有认证相关//// + //认证成功 void onAuthSuccess(); + //认证失败 void onAuthFailed(const string &realm,const string &why,bool close = true); + //开始走rtsp专有认证流程 void onAuthUser(const string &realm,const string &authorization); + //校验base64方式的认证加密 void onAuthBasic(const string &realm,const string &strBase64); + //校验md5方式的认证加密 void onAuthDigest(const string &realm,const string &strMd5); + //发送rtp给客户端 void sendRtpPacket(const RtpPacket::Ptr &pkt); + //回复客户端 bool sendRtspResponse(const string &res_code,const std::initializer_list &header, const string &sdp = "" , const char *protocol = "RTSP/1.0"); bool sendRtspResponse(const string &res_code,const StrCaseMap &header = StrCaseMap(), const string &sdp = "",const char *protocol = "RTSP/1.0"); + //服务器发送rtcp void sendSenderReport(bool overTcp,int iTrackIndex); private: + //用于判断客户端是否超时 Ticker _ticker; + //收到的seq,回复时一致 int _iCseq = 0; + //ContentBase string _strContentBase; - string _strSdp; + //Session号 string _strSession; + //是否第一次播放,第一次播放需要鉴权,第二次播放属于暂停恢复 bool _bFirstPlay = true; + //url解析后保存的相关信息 MediaInfo _mediaInfo; + //rtsp播放器绑定的直播源 std::weak_ptr _pMediaSrc; + //直播源读取器 RingBuffer::RingReader::Ptr _pRtpReader; + //推流或拉流客户端采用的rtp传输方式 Rtsp::eRtpType _rtpType = Rtsp::RTP_Invalid; + //sdp里面有效的track,包含音频或视频 vector _aTrackInfo; - - //RTP over udp - Socket::Ptr _apRtpSock[2]; //RTP端口,trackid idx 为数组下标 - Socket::Ptr _apRtcpSock[2];//RTCP端口,trackid idx 为数组下标 + ////////RTP over udp//////// + //RTP端口,trackid idx 为数组下标 + Socket::Ptr _apRtpSock[2]; + //RTCP端口,trackid idx 为数组下标 + Socket::Ptr _apRtcpSock[2]; + //标记是否收到播放的udp打洞包,收到播放的udp打洞包后才能知道其外网udp端口号 unordered_set _udpSockConnected; - //RTP over udp_multicast - RtpBroadCaster::Ptr _pBrdcaster; + ////////RTP over udp_multicast//////// + //共享的rtp组播对象 + RtpMultiCaster::Ptr _multicaster; //登录认证 string _strNonce; @@ -184,13 +225,16 @@ private: //一次发送 get 一次发送post,需要通过x-sessioncookie关联起来 string _http_x_sessioncookie; function _onRecv; + //是否开始发送rtp bool _enableSendRtp; - //rtsp推流相关 RtspToRtmpMediaSource::Ptr _pushSrc; - - RtcpCounter _aRtcpCnt[2]; //rtcp统计,trackid idx 为数组下标 - Ticker _aRtcpTicker[2]; //rtcp发送时间,trackid idx 为数组下标 + //rtcp统计,trackid idx 为数组下标 + RtcpCounter _aRtcpCnt[2]; + //rtcp发送时间,trackid idx 为数组下标 + Ticker _aRtcpTicker[2]; + //时间戳修整器 + Stamp _stamp[2]; }; /** diff --git a/src/Rtsp/RtspToRtmpMediaSource.h b/src/Rtsp/RtspToRtmpMediaSource.h index c5a165e4..04980dff 100644 --- a/src/Rtsp/RtspToRtmpMediaSource.h +++ b/src/Rtsp/RtspToRtmpMediaSource.h @@ -88,6 +88,17 @@ public: int readerCount() override { return RtspMediaSource::readerCount() + (_muxer ? _muxer->readerCount() : 0); } + + /** + * 获取track + * @return + */ + vector getTracks(bool trackReady) const override { + if(!_demuxer){ + return this->RtspMediaSource::getTracks(trackReady); + } + return _demuxer->getTracks(trackReady); + } private: RtspDemuxer::Ptr _demuxer; MultiMediaSourceMuxer::Ptr _muxer; diff --git a/tests/bom.cpp b/tests/bom.cpp new file mode 100644 index 00000000..3502d382 --- /dev/null +++ b/tests/bom.cpp @@ -0,0 +1,170 @@ +#include +#include +#if !defined(_WIN32) +#include +#endif //!defined(_WIN32) +#include +#include "Util/CMD.h" +#include "Util/util.h" +#include "Util/logger.h" +#include "Util/File.h" +#include "Util/uv_errno.h" + +using namespace std; +using namespace toolkit; + +class CMD_main : public CMD { +public: + CMD_main() { + _parser.reset(new OptionParser(nullptr)); + + + (*_parser) << Option('r',/*该选项简称,如果是\x00则说明无简称*/ + "rm",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/ + Option::ArgNone,/*该选项后面必须跟值*/ + nullptr,/*该选项默认值*/ + false,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/ + "是否删除或添加bom,默认添加bom头",/*该选项说明文字*/ + nullptr); + + (*_parser) << Option('f',/*该选项简称,如果是\x00则说明无简称*/ + "filter",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/ + Option::ArgRequired,/*该选项后面必须跟值*/ + "c,cpp,cxx,c,h,hpp",/*该选项默认值*/ + true,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/ + "文件后缀过滤器",/*该选项说明文字*/ + nullptr); + + (*_parser) << Option('i',/*该选项简称,如果是\x00则说明无简称*/ + "in",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/ + Option::ArgRequired,/*该选项后面必须跟值*/ + nullptr,/*该选项默认值*/ + true,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/ + "文件夹或文件",/*该选项说明文字*/ + nullptr); + } + + virtual ~CMD_main() {} + + virtual const char *description() const { + return "添加或删除bom"; + } +}; + +void get_file_path(const char *path, const char *file_name, char *file_path) { + strcpy(file_path, path); + if (file_path[strlen(file_path) - 1] != '/') { + strcat(file_path, "/"); + } + strcat(file_path, file_name); +} + +template +void for_each_file(const char *path, FUNC &&func){ + DIR *dir; + dirent *dir_info; + char file_path[PATH_MAX]; + if (File::is_file(path)) { + func(path); + return; + } + if (File::is_dir(path)) { + if ((dir = opendir(path)) == NULL) { + closedir(dir); + return; + } + while ((dir_info = readdir(dir)) != NULL) { + if (File::is_special_dir(dir_info->d_name)) { + continue; + } + get_file_path(path, dir_info->d_name, file_path); + for_each_file(file_path,std::forward(func)); + } + closedir(dir); + return; + } +} + +static const char s_bom[] = "\xEF\xBB\xBF"; + +void add_or_rm_bom(const char *file,bool rm_bom){ + auto file_str = File::loadFile(file); + if(rm_bom){ + file_str.erase(0, sizeof(s_bom) - 1); + }else{ + file_str.insert(0,s_bom,sizeof(s_bom) - 1); + } + File::saveFile(file_str,file); +} + +void process_file(const char *file,bool rm_bom){ + std::shared_ptr fp(fopen(file, "rb+"), [](FILE *fp) { + if (fp) { + fclose(fp); + } + }); + + if (!fp) { + WarnL << "打开文件失败:" << file << " " << get_uv_errmsg(); + return; + } + + bool have_bom = rm_bom; + char buf[sizeof(s_bom) - 1] = {0}; + + if (sizeof(buf) == fread(buf,1,sizeof(buf),fp.get())) { + have_bom = (memcmp(s_bom, buf, sizeof(s_bom) - 1) == 0); + } + + if (have_bom == !rm_bom) { +// DebugL << "无需" << (rm_bom ? "删除" : "添加") << "bom:" << file; + return; + } + + fp = nullptr; + add_or_rm_bom(file,rm_bom); + InfoL << (rm_bom ? "删除" : "添加") << "bom:" << file; +} + +int main(int argc, char *argv[]) { + CMD_main cmd_main; + try { + cmd_main.operator()(argc, argv); + } catch (std::exception &ex) { + cout << ex.what() << endl; + return -1; + } + + bool rm_bom = cmd_main.hasKey("rm"); + string path = cmd_main["in"]; + string filter = cmd_main["filter"]; + auto vec = split(filter,","); + + set filter_set; + for(auto ext : vec){ + filter_set.emplace(ext); + } + + bool no_filter = filter_set.find("*") != filter_set.end(); + //设置日志 + Logger::Instance().add(std::make_shared()); + + for_each_file(path.data(),[&](const char *path){ + if(!no_filter){ + //开启了过滤器 + auto pos = strstr(path,"."); + if(pos == nullptr){ + //没有后缀 + return; + } + auto ext = pos + 1; + if(filter_set.find(ext) == filter_set.end()){ + //后缀不匹配 + return; + } + } + //该文件匹配 + process_file(path,rm_bom); + }); + return 0; +} diff --git a/tests/test_player.cpp b/tests/test_player.cpp index 6ddfb256..142a6036 100644 --- a/tests/test_player.cpp +++ b/tests/test_player.cpp @@ -39,42 +39,6 @@ using namespace std; using namespace toolkit; using namespace mediakit; -#ifdef WIN32 -std::string Utf8ToGbk(std::string src_str){ - - int len = MultiByteToWideChar(CP_UTF8, 0, src_str.c_str(), -1, NULL, 0); - wchar_t* wszGBK = new wchar_t[len + 1]; - memset(wszGBK, 0, len * 2 + 2); - MultiByteToWideChar(CP_UTF8, 0, src_str.c_str(), -1, wszGBK, len); - len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL); - char* szGBK = new char[len + 1]; - memset(szGBK, 0, len + 1); - WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL); - string strTemp(szGBK); - if (wszGBK) delete[] wszGBK; - if (szGBK) delete[] szGBK; - return strTemp; -} - -class log4Channel : public LogChannel { -public: - log4Channel(const string &name = "log4Channel", LogLevel level = LTrace) :LogChannel(name, level) - { - - } - ~log4Channel() {} - void write(const Logger &logger, const LogContextPtr &logContext) override - { - if (_level > logContext->_level) { - return; - } - - printf("%s %s\n", logContext->_function, Utf8ToGbk(logContext->str()).c_str()); - } -}; -#else -typedef ConsoleChannel log4Channel; -#endif #ifdef WIN32 #include @@ -106,7 +70,7 @@ int main(int argc, char *argv[]) { //设置退出信号处理函数 signal(SIGINT, [](int) { SDLDisplayerHelper::Instance().shutdown(); }); //设置日志 - Logger::Instance().add(std::make_shared()); + Logger::Instance().add(std::make_shared()); Logger::Instance().setWriter(std::make_shared()); if (argc != 3) {