diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 17e82574..8611e88c 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 17e82574991134f798ae32f82d48e2d6c6b97b06 +Subproject commit 8611e88c2eda178973662dcfe180691ff1d8ba35 diff --git a/3rdpart/media-server b/3rdpart/media-server index 43facc34..d54285e9 160000 --- a/3rdpart/media-server +++ b/3rdpart/media-server @@ -1 +1 @@ -Subproject commit 43facc343afc2b5b70bbbc3c177f20dfa936f2bf +Subproject commit d54285e96260e6d56d68929ec9ace402b83ff6b0 diff --git a/AUTHORS b/AUTHORS index c80c92fb..655a46dd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -14,3 +14,7 @@ huohuo <913481084@qq.com> [γ瑞γミ](https://github.com/JerryLinGd) [茄子](https://github.com/taotaobujue2008) [好心情](<409257224@qq.com>) +[Xiaofeng Wang](https://github.com/wasphin) +[doodoocoder](https://github.com/doodoocoder) +[qingci](https://github.com/Colibrow) +Zhou Weimin \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 10148135..5332e02d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,15 +151,7 @@ endif() #添加rtp库用于rtp转ps/ts if(ENABLE_RTPPROXY AND ENABLE_HLS) message(STATUS "ENABLE_RTPPROXY defined") - include_directories(${MediaServer_Root}/librtp/include) - - aux_source_directory(${MediaServer_Root}/librtp/include src_rtp) - aux_source_directory(${MediaServer_Root}/librtp/source src_rtp) - aux_source_directory(${MediaServer_Root}/librtp/payload src_rtp) - add_library(rtp STATIC ${src_rtp}) add_definitions(-DENABLE_RTPPROXY) - list(APPEND LINK_LIB_LIST rtp) - list(APPEND CXX_API_TARGETS rtp) endif() #收集源代码 diff --git a/README.md b/README.md index 66dfd26b..d5bcf43c 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,28 @@ -![logo](https://raw.githubusercontent.com/zlmediakit/ZLMediaKit/master/www/logo.png) +![logo](https://raw.githubusercontent.com/xia-chu/ZLMediaKit/master/www/logo.png) -[english readme](https://github.com/xiongziliang/ZLMediaKit/blob/master/README_en.md) +[english readme](https://github.com/xia-chu/ZLMediaKit/blob/master/README_en.md) # 一个基于C++11的高性能运营级流媒体服务框架 - [![Build Status](https://travis-ci.org/xiongziliang/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xiongziliang/ZLMediaKit) - +[![license](http://img.shields.io/badge/license-MIT-green.svg)](https://github.com/xia-chu/ZLMediaKit/blob/master/LICENSE) +[![C++](https://img.shields.io/badge/language-c++-red.svg)](https://en.cppreference.com/) +[![platform](https://img.shields.io/badge/platform-linux%20|%20macos%20|%20windows-blue.svg)](https://github.com/xia-chu/ZLMediaKit) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-yellow.svg)](https://github.com/xia-chu/ZLMediaKit/pulls) +[![Build Status](https://travis-ci.org/xia-chu/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xia-chu/ZLMediaKit) ## 项目特点 - 基于C++11开发,避免使用裸指针,代码稳定可靠,性能优越。 -- 支持多种协议(RTSP/RTMP/HLS/HTTP-FLV/Websocket-FLV/GB28181/MP4),支持协议互转。 +- 支持多种协议(RTSP/RTMP/HLS/HTTP-FLV/Websocket-FLV/GB28181/HTTP-TS/Websocket-TS/HTTP-fMP4/Websocket-fMP4/MP4),支持协议互转。 - 使用多路复用/多线程/异步网络IO模式开发,并发性能优越,支持海量客户端连接。 - 代码经过长期大量的稳定性、性能测试,已经在线上商用验证已久。 - 支持linux、macos、ios、android、windows全平台。 -- 支持画面秒开、极低延时([500毫秒内,最低可达100毫秒](https://github.com/zlmediakit/ZLMediaKit/wiki/%E5%BB%B6%E6%97%B6%E6%B5%8B%E8%AF%95))。 -- 提供完善的标准[C API](https://github.com/xiongziliang/ZLMediaKit/tree/master/api/include),可以作SDK用,或供其他语言调用。 -- 提供完整的[MediaServer](https://github.com/xiongziliang/ZLMediaKit/tree/master/server)服务器,可以免开发直接部署为商用服务器。 -- 提供完善的[restful api](https://github.com/xiongziliang/ZLMediaKit/wiki/MediaServer%E6%94%AF%E6%8C%81%E7%9A%84HTTP-API)以及[web hook](https://github.com/xiongziliang/ZLMediaKit/wiki/MediaServer%E6%94%AF%E6%8C%81%E7%9A%84HTTP-HOOK-API),支持丰富的业务逻辑。 +- 支持画面秒开、极低延时([500毫秒内,最低可达100毫秒](https://github.com/xia-chu/ZLMediaKit/wiki/%E5%BB%B6%E6%97%B6%E6%B5%8B%E8%AF%95))。 +- 提供完善的标准[C API](https://github.com/xia-chu/ZLMediaKit/tree/master/api/include),可以作SDK用,或供其他语言调用。 +- 提供完整的[MediaServer](https://github.com/xia-chu/ZLMediaKit/tree/master/server)服务器,可以免开发直接部署为商用服务器。 +- 提供完善的[restful api](https://github.com/xia-chu/ZLMediaKit/wiki/MediaServer%E6%94%AF%E6%8C%81%E7%9A%84HTTP-API)以及[web hook](https://github.com/xia-chu/ZLMediaKit/wiki/MediaServer%E6%94%AF%E6%8C%81%E7%9A%84HTTP-HOOK-API),支持丰富的业务逻辑。 - 打通了视频监控协议栈与直播协议栈,对RTSP/RTMP支持都很完善。 -- 全面支持H265/H264/AAC/G711。 +- 全面支持H265/H264/AAC/G711/OPUS。 ## 项目定位 @@ -29,6 +32,8 @@ ## 功能清单 +### 功能一览 +图片 - RTSP[S] - RTSP[S] 服务器,支持RTMP/MP4/HLS转RTSP[S],支持亚马逊echo show这样的设备 @@ -38,7 +43,7 @@ - 服务器/客户端完整支持Basic/Digest方式的登录鉴权,全异步可配置化的鉴权接口 - 支持H265编码 - 服务器支持RTSP推流(包括`rtp over udp` `rtp over tcp`方式) - - 支持任意编码格式的rtsp推流,只是除H264/H265/AAC/G711外无法转协议 + - 支持H264/H265/AAC/G711/OPUS编码,其他编码能转发但不能转协议 - RTMP[S] - RTMP[S] 播放服务器,支持RTSP/MP4/HLS转RTMP @@ -47,16 +52,25 @@ - RTMP[S] 推流客户端 - 支持http[s]-flv直播 - 支持websocket-flv直播 - - 支持任意编码格式的rtmp推流,只是除H264/H265/AAC/G711外无法转协议 + - 支持H264/H265/AAC/G711/OPUS编码,其他编码能转发但不能转协议 - 支持[RTMP-H265](https://github.com/ksvc/FFmpeg/wiki) + - 支持[RTMP-OPUS](https://github.com/xia-chu/ZLMediaKit/wiki/RTMP%E5%AF%B9H265%E5%92%8COPUS%E7%9A%84%E6%94%AF%E6%8C%81) - HLS - 支持HLS文件生成,自带HTTP文件服务器 - - 通过cookie追踪技术,可以模拟HLS播放为长连接,实现丰富的业务逻辑 - - 支持完备的HLS用户追踪、播放统计等业务功能,可以实现HLS按需拉流等业务 + - 通过cookie追踪技术,可以模拟HLS播放为长连接,可以实现HLS按需拉流、播放统计等业务 - 支持HLS播发器,支持拉流HLS转rtsp/rtmp/mp4 + - 支持H264/H265/AAC/G711/OPUS编码 + +- TS + - 支持http[s]-ts直播 + - 支持ws[s]-ts直播 + +- fMP4 + - 支持http[s]-fmp4直播 + - 支持ws[s]-fmp4直播 -- HTTP[S] +- HTTP[S]与WebSocket - 服务器支持`目录索引生成`,`文件下载`,`表单提交请求` - 客户端提供`文件下载器(支持断点续传)`,`接口请求器`,`文件上传器` - 完整HTTP API服务器,可以作为web后台开发框架 @@ -65,12 +79,15 @@ - 支持WebSocket服务器和客户端 - 支持http文件访问鉴权 -- GB28181 - - 支持UDP/TCP国标RTP(PS或TS)推流,可以转换成RTSP/RTMP/HLS等协议 +- GB28181与RTP推流 + - 支持UDP/TCP国标RTP(PS或TS)推流服务器,可以转换成RTSP/RTMP/HLS等协议 + - 支持RTSP/RTMP/HLS转国标推流客户端,支持TCP/UDP模式,提供相应restful api + - 支持H264/H265/AAC/G711/OPUS编码 -- 点播 +- MP4点播与录制 - 支持录制为FLV/HLS/MP4 - RTSP/RTMP/HTTP-FLV/WS-FLV支持MP4文件点播,支持seek + - 支持H264/H265/AAC/G711/OPUS编码 - 其他 - 支持丰富的restful api以及web hook事件 @@ -83,28 +100,29 @@ - 提供c api sdk - 支持FFmpeg拉流代理任意格式的流 - 支持http api生成并返回实时截图 + - 支持按需解复用、转协议,当有人观看时才开启转协议 ## 更新日志 - 2020/5/17 新增支持hls播发器,支持hls拉流代理 ## 编译以及测试 -**编译前务必仔细参考wiki:[快速开始](https://github.com/xiongziliang/ZLMediaKit/wiki/%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B)操作!!!** +**编译前务必仔细参考wiki:[快速开始](https://github.com/xia-chu/ZLMediaKit/wiki/%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B)操作!!!** ## 怎么使用 你有三种方法使用ZLMediaKit,分别是: - - 1、使用c api,作为sdk使用,请参考[这里](https://github.com/xiongziliang/ZLMediaKit/tree/master/api/include). - - 2、作为独立的流媒体服务器使用,不想做c/c++开发的,可以参考[restful api](https://github.com/xiongziliang/ZLMediaKit/wiki/MediaServer%E6%94%AF%E6%8C%81%E7%9A%84HTTP-API)和[web hook](https://github.com/xiongziliang/ZLMediaKit/wiki/MediaServer%E6%94%AF%E6%8C%81%E7%9A%84HTTP-HOOK-API). - - 3、如果想做c/c++开发,添加业务逻辑增加功能,可以参考这里的[测试程序](https://github.com/xiongziliang/ZLMediaKit/tree/master/tests). + - 1、使用c api,作为sdk使用,请参考[这里](https://github.com/xia-chu/ZLMediaKit/tree/master/api/include). + - 2、作为独立的流媒体服务器使用,不想做c/c++开发的,可以参考[restful api](https://github.com/xia-chu/ZLMediaKit/wiki/MediaServer%E6%94%AF%E6%8C%81%E7%9A%84HTTP-API)和[web hook](https://github.com/xia-chu/ZLMediaKit/wiki/MediaServer%E6%94%AF%E6%8C%81%E7%9A%84HTTP-HOOK-API). + - 3、如果想做c/c++开发,添加业务逻辑增加功能,可以参考这里的[测试程序](https://github.com/xia-chu/ZLMediaKit/tree/master/tests). ## Docker 镜像 你可以从Docker Hub下载已经编译好的镜像并启动它: ```bash -docker run -id -p 1935:1935 -p 8080:80 gemfield/zlmediakit:20.04-runtime-ubuntu18.04 +docker run -id -p 1935:1935 -p 8080:80 -p 8554:554 -p 10000:10000 -p 10000:10000/udp panjjo/zlmediakit ``` 你也可以根据Dockerfile编译镜像: @@ -115,14 +133,17 @@ bash build_docker_images.sh ## 参考案例 - - [IOS摄像头实时录制,生成rtsp/rtmp/hls/http-flv](https://gitee.com/xiahcu/IOSMedia) - - [IOS rtmp/rtsp播放器,视频推流器](https://gitee.com/xiahcu/IOSPlayer) - - [支持linux、windows、mac的rtmp/rtsp播放器](https://github.com/xiongziliang/ZLMediaPlayer) + - [IOS摄像头实时录制,生成rtsp/rtmp/hls/http-flv](https://gitee.com/xia-chu/IOSMedia) + - [IOS rtmp/rtsp播放器,视频推流器](https://gitee.com/xia-chu/IOSPlayer) + - [支持linux、windows、mac的rtmp/rtsp播放器](https://github.com/xia-chu/ZLMediaPlayer) - [基于ZLMediaKit分支的管理WEB网站](https://github.com/chenxiaolei/ZLMediaKit_NVR_UI) - [基于ZLMediaKit主线的管理WEB网站](https://gitee.com/kkkkk5G/MediaServerUI) - [DotNetCore的RESTful客户端](https://github.com/MingZhuLiu/ZLMediaKit.DotNetCore.Sdk) + - [C#版本的Http API与Hook](https://github.com/chengxiaosheng/ZLMediaKit.HttpApi) - [GB28181-2016网络视频平台](https://github.com/swwheihei/wvp) - [node-js版本的GB28181平台](https://gitee.com/hfwudao/GB28181_Node_Http) + - [基于C SDK实现的推流客户端](https://github.com/hctym1995/ZLM_ApiDemo) + - [Go实现的海康ehome服务器](https://github.com/tsingeye/FreeEhome) ## 授权协议 @@ -143,7 +164,7 @@ bash build_docker_images.sh - 1、仔细看下readme、wiki,如果有必要可以查看下issue. - 2、如果您的问题还没解决,可以提issue. - 3、有些问题,如果不具备参考性的,无需在issue提的,可以在qq群提. - - 4、QQ私聊一般不接受无偿技术咨询和支持([为什么不提倡QQ私聊](https://github.com/xiongziliang/ZLMediaKit/wiki/%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E5%BB%BA%E8%AE%AEQQ%E7%A7%81%E8%81%8A%E5%92%A8%E8%AF%A2%E9%97%AE%E9%A2%98%EF%BC%9F)). + - 4、QQ私聊一般不接受无偿技术咨询和支持([为什么不提倡QQ私聊](https://github.com/xia-chu/ZLMediaKit/wiki/%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E5%BB%BA%E8%AE%AEQQ%E7%A7%81%E8%81%8A%E5%92%A8%E8%AF%A2%E9%97%AE%E9%A2%98%EF%BC%9F)). ## 致谢 @@ -165,11 +186,18 @@ bash build_docker_images.sh [茄子](https://github.com/taotaobujue2008) [好心情](<409257224@qq.com>) [浮沉](https://github.com/MingZhuLiu) +[Xiaofeng Wang](https://github.com/wasphin) +[doodoocoder](https://github.com/doodoocoder) +[qingci](https://github.com/Colibrow) +[swwheihei](https://github.com/swwheihei) +[KKKKK5G](https://gitee.com/kkkkk5G) +[Zhou Weimin]() + ## 捐赠 欢迎捐赠以便更好的推动项目的发展,谢谢您的支持! -[支付宝](https://gitee.com/xiahcu/other/raw/master/IMG_3919.JPG) +[支付宝](https://gitee.com/xia-chu/other/raw/master/IMG_3919.JPG) -[微信](https://gitee.com/xiahcu/other/raw/master/IMG_3920.JPG) +[微信](https://gitee.com/xia-chu/other/raw/master/IMG_3920.JPG) diff --git a/README_en.md b/README_en.md index 6eae5565..7193f7b2 100644 --- a/README_en.md +++ b/README_en.md @@ -2,12 +2,16 @@ # A lightweight ,high performance and stable stream server and client framework based on C++11. - [![Build Status](https://travis-ci.org/xiongziliang/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xiongziliang/ZLMediaKit) +[![license](http://img.shields.io/badge/license-MIT-green.svg)](https://github.com/xia-chu/ZLMediaKit/blob/master/LICENSE) +[![C++](https://img.shields.io/badge/language-c++-red.svg)](https://en.cppreference.com/) +[![platform](https://img.shields.io/badge/platform-linux%20|%20macos%20|%20windows-blue.svg)](https://github.com/xia-chu/ZLMediaKit) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-yellow.svg)](https://github.com/xia-chu/ZLMediaKit/pulls) +[![Build Status](https://travis-ci.org/xia-chu/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xia-chu/ZLMediaKit) ## Why ZLMediaKit? - Developed based on C++ 11, the code is stable and reliable, avoiding the use of raw pointers, cross-platform porting is simple and convenient, and the code is clear and concise. -- Support rich streaming media protocols(`RTSP/RTMP/HLS/HTTP-FLV/Websocket-flv`),and support Inter-protocol conversion. +- Support rich streaming media protocols(`RTSP/RTMP/HLS/HTTP-FLV/WebSocket-flv/HTTP-TS/WebSocket-TS/HTTP-fMP4/Websocket-fMP4/MP4`),and support Inter-protocol conversion. - Multiplexing asynchronous network IO based on epoll and multi thread,extreme performance. - Well performance and stable test,can be used commercially. - Support linux, macos, ios, android, Windows Platforms. @@ -20,15 +24,15 @@ - RTSP[S] player and pusher. - RTP Transport : `rtp over udp` `rtp over tcp` `rtp over http` `rtp udp multicast` . - Basic/Digest/Url Authentication. - - H264/H265/AAC/G711 codec. + - H265/H264/AAC/G711/OPUS codec. - Recorded as mp4. - Vod of mp4. - RTMP[S] - RTMP[S] server,support player and pusher. - RTMP[S] player and pusher. - - Support HTTP-FLV player. - - H264/H265/AAC/G711 codec. + - Support HTTP-FLV/WebSocket-FLV sever. + - H265/H264/AAC/G711/OPUS codec. - Recorded as flv or mp4. - Vod of mp4. - support [RTMP-H265](https://github.com/ksvc/FFmpeg/wiki) @@ -37,6 +41,12 @@ - RTSP RTMP can be converted into HLS,built-in HTTP server. - Play authentication based on cookie. - Support HLS player, support streaming HLS proxy to RTSP / RTMP / MP4. + +- TS + - Support HTTP-TS/WebSocket-TS sever. + +- fMP4 + - Support HTTP-fMP4/WebSocket-fMP4 sever. - HTTP[S] - HTTP server,suppor directory meun、RESTful http api. @@ -56,62 +66,6 @@ - Support TS / PS streaming push through RTP,and it can be converted to RTSP / RTMP / HLS / FLV. - Support real-time online screenshot http api. - -- Protocol conversion: - -| protocol/codec | H264 | H265 | AAC | other | -| :------------------------------: | :--: | :--: | :--: | :---: | -| RTSP[S] --> RTMP/HTTP[S]-FLV/FLV | Y | Y | Y | N | -| RTMP --> RTSP[S] | Y | Y | Y | N | -| RTSP[S] --> HLS | Y | Y | Y | N | -| RTMP --> HLS | Y | Y | Y | N | -| RTSP[S] --> MP4 | Y | Y | Y | N | -| RTMP --> MP4 | Y | Y | Y | N | -| MP4 --> RTSP[S] | Y | Y | Y | N | -| MP4 --> RTMP | Y | Y | Y | N | -| HLS --> RTSP/RTMP/MP4 | Y | Y | Y | N | - -- Stream generation: - -| feature/codec | H264 | H265 | AAC | other | -| :-----------: | :--: | :--: | :--: | :---: | -| RTSP[S] push | Y | Y | Y | Y | -| RTSP proxy | Y | Y | Y | Y | -| RTMP push | Y | Y | Y | Y | -| RTMP proxy | Y | Y | Y | Y | - -- RTP transport: - -| feature/transport | tcp | udp | http | udp_multicast | -| :-----------------: | :--: | :--: | :--: | :-----------: | -| RTSP[S] Play Server | Y | Y | Y | Y | -| RTSP[S] Push Server | Y | Y | N | N | -| RTSP Player | Y | Y | N | Y | -| RTSP Pusher | Y | Y | N | N | - - -- Server supported: - -| Server | Y/N | -| :-----------------: | :--: | -| RTSP[S] Play Server | Y | -| RTSP[S] Push Server | Y | -| RTMP | Y | -| HTTP[S]/WebSocket[S] | Y | - -- Client supported: - -| Client | Y/N | -| :---------: | :--: | -| RTSP Player | Y | -| RTSP Pusher | Y | -| RTMP Player | Y | -| RTMP Pusher | Y | -| HTTP[S] | Y | -| WebSocket[S] | Y | -| HLS player | Y | - - ## System Requirements - Compiler support c++11,GCC4.8/Clang3.3/VC2015 or above. @@ -191,8 +145,6 @@ git submodule update --init ``` - - ### Build on Android Now you can open android sudio project in `Android` folder,this is a `aar library` and damo project. @@ -298,7 +250,7 @@ git submodule update --init ## Docker Image You can pull a pre-built docker image from Docker Hub and run with ```bash -docker run -id -p 1935:1935 -p 8080:80 gemfield/zlmediakit +docker run -id -p 1935:1935 -p 8080:80 -p 8554:554 -p 10000:10000 -p 10000:10000/udp panjjo/zlmediakit ``` Dockerfile is also supplied to build images on Ubuntu 16.04 @@ -307,44 +259,6 @@ cd docker docker build -t zlmediakit . ``` -## Mirrors - -[ZLToolKit](http://git.oschina.net/xiahcu/ZLToolKit) - -[ZLMediaKit](http://git.oschina.net/xiahcu/ZLMediaKit) - - -## Licence - -``` -MIT License - -Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> -Copyright (c) 2019 Gemfield -Copyright (c) 2018 huohuo <913481084@qq.com> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -``` - - - ## Contact - Email:<1213642868@qq.com> - QQ chat group:542509000 diff --git a/api/include/mk_events_objects.h b/api/include/mk_events_objects.h index a6669fed..2aba7ada 100644 --- a/api/include/mk_events_objects.h +++ b/api/include/mk_events_objects.h @@ -19,25 +19,25 @@ extern "C" { ///////////////////////////////////////////MP4Info///////////////////////////////////////////// //MP4Info对象的C映射 typedef void* mk_mp4_info; -//MP4Info::ui64StartedTime +// GMT 标准时间,单位秒 API_EXPORT uint64_t API_CALL mk_mp4_info_get_start_time(const mk_mp4_info ctx); -//MP4Info::ui64TimeLen -API_EXPORT uint64_t API_CALL mk_mp4_info_get_time_len(const mk_mp4_info ctx); -//MP4Info::ui64FileSize +// 录像长度,单位秒 +API_EXPORT float API_CALL mk_mp4_info_get_time_len(const mk_mp4_info ctx); +// 文件大小,单位 BYTE API_EXPORT uint64_t API_CALL mk_mp4_info_get_file_size(const mk_mp4_info ctx); -//MP4Info::strFilePath +// 文件路径 API_EXPORT const char* API_CALL mk_mp4_info_get_file_path(const mk_mp4_info ctx); -//MP4Info::strFileName +// 文件名称 API_EXPORT const char* API_CALL mk_mp4_info_get_file_name(const mk_mp4_info ctx); -//MP4Info::strFolder +// 文件夹路径 API_EXPORT const char* API_CALL mk_mp4_info_get_folder(const mk_mp4_info ctx); -//MP4Info::strUrl +// 播放路径 API_EXPORT const char* API_CALL mk_mp4_info_get_url(const mk_mp4_info ctx); -//MP4Info::strVhost +// 应用名称 API_EXPORT const char* API_CALL mk_mp4_info_get_vhost(const mk_mp4_info ctx); -//MP4Info::strAppName +// 流 ID API_EXPORT const char* API_CALL mk_mp4_info_get_app(const mk_mp4_info ctx); -//MP4Info::strStreamId +// 虚拟主机 API_EXPORT const char* API_CALL mk_mp4_info_get_stream(const mk_mp4_info ctx); ///////////////////////////////////////////Parser///////////////////////////////////////////// @@ -276,13 +276,11 @@ typedef void* mk_publish_auth_invoker; /** * 执行Broadcast::PublishAuthInvoker * @param err_msg 为空或null则代表鉴权成功 - * @param enable_rtxp rtmp推流时是否运行转rtsp;rtsp推流时,是否允许转rtmp * @param enable_hls 是否允许转换hls * @param enable_mp4 是否运行MP4录制 */ API_EXPORT void API_CALL mk_publish_auth_invoker_do(const mk_publish_auth_invoker ctx, const char *err_msg, - int enable_rtxp, int enable_hls, int enable_mp4); diff --git a/api/include/mk_media.h b/api/include/mk_media.h index 87b8836d..d41f3dc7 100755 --- a/api/include/mk_media.h +++ b/api/include/mk_media.h @@ -26,14 +26,12 @@ typedef void *mk_media; * @param app 应用名,推荐为live * @param stream 流id,例如camera * @param duration 时长(单位秒),直播则为0 - * @param rtsp_enabled 是否启用rtsp协议 - * @param rtmp_enabled 是否启用rtmp协议 * @param hls_enabled 是否生成hls * @param mp4_enabled 是否生成mp4 * @return 对象指针 */ -API_EXPORT mk_media API_CALL mk_media_create(const char *vhost, const char *app, const char *stream, float duration, - int rtsp_enabled, int rtmp_enabled, int hls_enabled, int mp4_enabled); +API_EXPORT mk_media API_CALL mk_media_create(const char *vhost, const char *app, const char *stream, + float duration, int hls_enabled, int mp4_enabled); /** * 销毁媒体源 @@ -54,7 +52,7 @@ API_EXPORT void API_CALL mk_media_init_video(mk_media ctx, int track_id, int wid /** * 添加音频轨道 * @param ctx 对象指针 - * @param track_id 2:CodecAAC/3:CodecG711A/4:CodecG711U + * @param track_id 2:CodecAAC/3:CodecG711A/4:CodecG711U/5:OPUS * @param channel 通道数 * @param sample_bit 采样位数,只支持16 * @param sample_rate 采样率 @@ -95,7 +93,7 @@ API_EXPORT void API_CALL mk_media_input_h265(mk_media ctx, void *data, int len, * @param data 不包含adts头的单帧AAC数据 * @param len 单帧AAC数据字节数 * @param dts 时间戳,毫秒 - * @param adts adts头 + * @param adts adts头,可以为null */ API_EXPORT void API_CALL mk_media_input_aac(mk_media ctx, void *data, int len, uint32_t dts, void *adts); @@ -109,13 +107,13 @@ API_EXPORT void API_CALL mk_media_input_aac(mk_media ctx, void *data, int len, u API_EXPORT void API_CALL mk_media_input_pcm(mk_media ctx, void *data, int len, uint32_t pts); /** - * 输入单帧G711音频 + * 输入单帧OPUS/G711音频帧 * @param ctx 对象指针 - * @param data 单帧G711数据 - * @param len 单帧G711数据字节数 + * @param data 单帧音频数据 + * @param len 单帧音频数据字节数 * @param dts 时间戳,毫秒 */ -API_EXPORT void API_CALL mk_media_input_g711(mk_media ctx, void* data, int len, uint32_t dts); +API_EXPORT void API_CALL mk_media_input_audio(mk_media ctx, void* data, int len, uint32_t dts); /** * MediaSource.close()回调事件 diff --git a/api/include/mk_mediakit.h b/api/include/mk_mediakit.h index dc7c07c7..18cfd589 100755 --- a/api/include/mk_mediakit.h +++ b/api/include/mk_mediakit.h @@ -22,5 +22,6 @@ #include "mk_tcp.h" #include "mk_util.h" #include "mk_thread.h" +#include "mk_rtp_server.h" #endif /* MK_API_H_ */ diff --git a/api/include/mk_player.h b/api/include/mk_player.h index 88750e15..8b4f2ac9 100755 --- a/api/include/mk_player.h +++ b/api/include/mk_player.h @@ -31,7 +31,7 @@ typedef void(API_CALL *on_mk_play_event)(void *user_data,int err_code,const char * 收到音视频数据回调 * @param user_data 用户数据指针 * @param track_type 0:视频,1:音频 - * @param codec_id 0:H264,1:H265,2:AAC 3.G711A 4.G711U + * @param codec_id 0:H264,1:H265,2:AAC 3.G711A 4.G711U 5.OPUS * @param data 数据指针 * @param len 数据长度 * @param dts 解码时间戳,单位毫秒 diff --git a/api/include/mk_rtp_server.h b/api/include/mk_rtp_server.h new file mode 100644 index 00000000..451cf766 --- /dev/null +++ b/api/include/mk_rtp_server.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "mk_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* mk_rtp_server; + +/** + * 创建GB28181 RTP 服务器 + * @param port 监听端口,0则为随机 + * @param enable_tcp 创建udp端口时是否同时监听tcp端口 + * @param stream_id 该端口绑定的流id + * @return + */ +API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create(uint16_t port, int enable_tcp, const char *stream_id); + +/** + * 销毁GB28181 RTP 服务器 + * @param ctx 服务器对象 + */ +API_EXPORT void API_CALL mk_rtp_server_release(mk_rtp_server ctx); + +/** + * 获取本地监听的端口号 + * @param ctx 服务器对象 + * @return 端口号 + */ +API_EXPORT uint16_t API_CALL mk_rtp_server_port(mk_rtp_server ctx); + +/** + * GB28181 RTP 服务器接收流超时时触发 + * @param user_data 用户数据指针 + */ +typedef void(API_CALL *on_mk_rtp_server_detach)(void *user_data); + +/** + * 监听B28181 RTP 服务器接收流超时事件 + * @param ctx 服务器对象 + * @param cb 回调函数 + * @param user_data 回调函数用户数据指针 + */ +API_EXPORT void API_CALL mk_rtp_server_set_on_detach(mk_rtp_server ctx, on_mk_rtp_server_detach cb, void *user_data); + + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/api/source/mk_events.cpp b/api/source/mk_events.cpp index 12ee4a46..ace60eb0 100644 --- a/api/source/mk_events.cpp +++ b/api/source/mk_events.cpp @@ -101,11 +101,10 @@ API_EXPORT void API_CALL mk_events_listen(const mk_events *events){ s_events.on_mk_media_publish((mk_media_info) &args, (mk_publish_auth_invoker) &invoker, (mk_sock_info) &sender); - }else{ - GET_CONFIG(bool,toRtxp,General::kPublishToRtxp); - GET_CONFIG(bool,toHls,General::kPublishToHls); - GET_CONFIG(bool,toMP4,General::kPublishToMP4); - invoker("",toRtxp,toHls,toMP4); + } else { + GET_CONFIG(bool, toHls, General::kPublishToHls); + GET_CONFIG(bool, toMP4, General::kPublishToMP4); + invoker("", toHls, toMP4); } }); diff --git a/api/source/mk_events_objects.cpp b/api/source/mk_events_objects.cpp index 98a233d8..6ce06679 100644 --- a/api/source/mk_events_objects.cpp +++ b/api/source/mk_events_objects.cpp @@ -18,65 +18,65 @@ #include "Rtsp/RtspSession.h" using namespace mediakit; -///////////////////////////////////////////MP4Info///////////////////////////////////////////// +///////////////////////////////////////////RecordInfo///////////////////////////////////////////// API_EXPORT uint64_t API_CALL mk_mp4_info_get_start_time(const mk_mp4_info ctx){ assert(ctx); - MP4Info *info = (MP4Info *)ctx; - return info->ui64StartedTime; + RecordInfo *info = (RecordInfo *)ctx; + return info->start_time; } -API_EXPORT uint64_t API_CALL mk_mp4_info_get_time_len(const mk_mp4_info ctx){ +API_EXPORT float API_CALL mk_mp4_info_get_time_len(const mk_mp4_info ctx){ assert(ctx); - MP4Info *info = (MP4Info *)ctx; - return info->ui64TimeLen; + RecordInfo *info = (RecordInfo *)ctx; + return info->time_len; } API_EXPORT uint64_t API_CALL mk_mp4_info_get_file_size(const mk_mp4_info ctx){ assert(ctx); - MP4Info *info = (MP4Info *)ctx; - return info->ui64FileSize; + RecordInfo *info = (RecordInfo *)ctx; + return info->file_size; } API_EXPORT const char* API_CALL mk_mp4_info_get_file_path(const mk_mp4_info ctx){ assert(ctx); - MP4Info *info = (MP4Info *)ctx; - return info->strFilePath.c_str(); + RecordInfo *info = (RecordInfo *)ctx; + return info->file_path.c_str(); } API_EXPORT const char* API_CALL mk_mp4_info_get_file_name(const mk_mp4_info ctx){ assert(ctx); - MP4Info *info = (MP4Info *)ctx; - return info->strFileName.c_str(); + RecordInfo *info = (RecordInfo *)ctx; + return info->file_name.c_str(); } API_EXPORT const char* API_CALL mk_mp4_info_get_folder(const mk_mp4_info ctx){ assert(ctx); - MP4Info *info = (MP4Info *)ctx; - return info->strFolder.c_str(); + RecordInfo *info = (RecordInfo *)ctx; + return info->folder.c_str(); } API_EXPORT const char* API_CALL mk_mp4_info_get_url(const mk_mp4_info ctx){ assert(ctx); - MP4Info *info = (MP4Info *)ctx; - return info->strUrl.c_str(); + RecordInfo *info = (RecordInfo *)ctx; + return info->url.c_str(); } API_EXPORT const char* API_CALL mk_mp4_info_get_vhost(const mk_mp4_info ctx){ assert(ctx); - MP4Info *info = (MP4Info *)ctx; - return info->strVhost.c_str(); + RecordInfo *info = (RecordInfo *)ctx; + return info->vhost.c_str(); } API_EXPORT const char* API_CALL mk_mp4_info_get_app(const mk_mp4_info ctx){ assert(ctx); - MP4Info *info = (MP4Info *)ctx; - return info->strAppName.c_str(); + RecordInfo *info = (RecordInfo *)ctx; + return info->app.c_str(); } API_EXPORT const char* API_CALL mk_mp4_info_get_stream(const mk_mp4_info ctx){ assert(ctx); - MP4Info *info = (MP4Info *)ctx; - return info->strStreamId.c_str(); + RecordInfo *info = (RecordInfo *)ctx; + return info->stream.c_str(); } ///////////////////////////////////////////Parser///////////////////////////////////////////// @@ -256,7 +256,7 @@ static C get_http_header( const char *response_header[]){ } break; } - return std::move(header); + return header; } API_EXPORT mk_http_body API_CALL mk_http_body_from_multi_form(const char *key_val[],const char *file_path){ @@ -382,12 +382,11 @@ API_EXPORT void API_CALL mk_rtsp_auth_invoker_clone_release(const mk_rtsp_auth_i ///////////////////////////////////////////Broadcast::PublishAuthInvoker///////////////////////////////////////////// API_EXPORT void API_CALL mk_publish_auth_invoker_do(const mk_publish_auth_invoker ctx, const char *err_msg, - int enable_rtxp, int enable_hls, int enable_mp4){ assert(ctx); Broadcast::PublishAuthInvoker *invoker = (Broadcast::PublishAuthInvoker *)ctx; - (*invoker)(err_msg ? err_msg : "", enable_rtxp, enable_hls, enable_mp4); + (*invoker)(err_msg ? err_msg : "", enable_hls, enable_mp4); } API_EXPORT mk_publish_auth_invoker API_CALL mk_publish_auth_invoker_clone(const mk_publish_auth_invoker ctx){ diff --git a/api/source/mk_httpclient.cpp b/api/source/mk_httpclient.cpp index 93c9dd3a..dbafa55a 100755 --- a/api/source/mk_httpclient.cpp +++ b/api/source/mk_httpclient.cpp @@ -78,7 +78,7 @@ static C get_http_header( const char *response_header[]){ } break; } - return std::move(header); + return header; } API_EXPORT void API_CALL mk_http_requester_set_body(mk_http_requester ctx, mk_http_body body){ diff --git a/api/source/mk_media.cpp b/api/source/mk_media.cpp index bad5bf51..1ad6ba64 100755 --- a/api/source/mk_media.cpp +++ b/api/source/mk_media.cpp @@ -117,11 +117,10 @@ API_EXPORT int API_CALL mk_media_total_reader_count(mk_media ctx){ return (*obj)->getChannel()->totalReaderCount(); } -API_EXPORT mk_media API_CALL mk_media_create(const char *vhost, const char *app, const char *stream, float duration, - int rtsp_enabled, int rtmp_enabled, int hls_enabled, int mp4_enabled) { +API_EXPORT mk_media API_CALL mk_media_create(const char *vhost, const char *app, const char *stream, + float duration, int hls_enabled, int mp4_enabled) { assert(vhost && app && stream); - MediaHelper::Ptr *obj(new MediaHelper::Ptr(new MediaHelper(vhost, app, stream, duration, - rtsp_enabled, rtmp_enabled, hls_enabled, mp4_enabled))); + MediaHelper::Ptr *obj(new MediaHelper::Ptr(new MediaHelper(vhost, app, stream, duration, hls_enabled, mp4_enabled))); (*obj)->attachEvent(); return (mk_media) obj; } @@ -188,8 +187,8 @@ API_EXPORT void API_CALL mk_media_input_pcm(mk_media ctx, void *data , int len, #endif //ENABLE_FAAC } -API_EXPORT void API_CALL mk_media_input_g711(mk_media ctx, void* data, int len, uint32_t dts){ +API_EXPORT void API_CALL mk_media_input_audio(mk_media ctx, void* data, int len, uint32_t dts){ assert(ctx && data && len > 0); MediaHelper::Ptr* obj = (MediaHelper::Ptr*) ctx; - (*obj)->getChannel()->inputG711((char*)data, len, dts); + (*obj)->getChannel()->inputAudio((char*)data, len, dts); } diff --git a/api/source/mk_player.cpp b/api/source/mk_player.cpp index 4d900c82..1b287c4f 100755 --- a/api/source/mk_player.cpp +++ b/api/source/mk_player.cpp @@ -80,7 +80,7 @@ public: strong_self->onData(frame); } }); - for (auto &track : _player->getTracks()) { + for (auto &track : _player->getTracks(false)) { track->addDelegate(delegate); } } diff --git a/api/source/mk_proxyplayer.cpp b/api/source/mk_proxyplayer.cpp index 91e496af..0ae63f1d 100644 --- a/api/source/mk_proxyplayer.cpp +++ b/api/source/mk_proxyplayer.cpp @@ -16,7 +16,7 @@ using namespace mediakit; API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create(const char *vhost, const char *app, const char *stream, int hls_enabled, int mp4_enabled) { assert(vhost && app && stream); - PlayerProxy::Ptr *obj(new PlayerProxy::Ptr(new PlayerProxy(vhost, app, stream, true, true, hls_enabled, mp4_enabled))); + PlayerProxy::Ptr *obj(new PlayerProxy::Ptr(new PlayerProxy(vhost, app, stream, hls_enabled, mp4_enabled))); return (mk_proxy_player) obj; } diff --git a/api/source/mk_rtp_server.cpp b/api/source/mk_rtp_server.cpp new file mode 100644 index 00000000..a36b9363 --- /dev/null +++ b/api/source/mk_rtp_server.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "mk_rtp_server.h" +#include "Rtp/RtpServer.h" +using namespace mediakit; + +API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create(uint16_t port, int enable_tcp, const char *stream_id){ + RtpServer::Ptr *server = new RtpServer::Ptr(new RtpServer); + (*server)->start(port, stream_id, enable_tcp); + return server; +} + +API_EXPORT void API_CALL mk_rtp_server_release(mk_rtp_server ctx){ + RtpServer::Ptr *server = (RtpServer::Ptr *)ctx; + delete server; +} + +API_EXPORT uint16_t API_CALL mk_rtp_server_port(mk_rtp_server ctx){ + RtpServer::Ptr *server = (RtpServer::Ptr *)ctx; + return (*server)->getPort(); +} + +API_EXPORT void API_CALL mk_rtp_server_set_on_detach(mk_rtp_server ctx, on_mk_rtp_server_detach cb, void *user_data){ + RtpServer::Ptr *server = (RtpServer::Ptr *) ctx; + if (cb) { + (*server)->setOnDetach([cb, user_data]() { + cb(user_data); + }); + } else { + (*server)->setOnDetach(nullptr); + } +} diff --git a/api/tests/server.c b/api/tests/server.c index 07eaf12a..40ac9954 100644 --- a/api/tests/server.c +++ b/api/tests/server.c @@ -61,8 +61,8 @@ void API_CALL on_mk_media_publish(const mk_media_info url_info, mk_media_info_get_stream(url_info), mk_media_info_get_params(url_info)); - //允许推流,并且允许转rtxp/hls/mp4 - mk_publish_auth_invoker_do(invoker, NULL, 1, 1, 1); + //允许推流,并且允许转hls/mp4 + mk_publish_auth_invoker_do(invoker, NULL, 1, 1); } /** diff --git a/conf/config.ini b/conf/config.ini index 5964342f..755222a0 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -40,8 +40,6 @@ addMuteAudio=1 #拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始, #如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写) resetWhenRePlay=1 -#是否默认推流时转换成rtsp或rtmp,hook接口(on_publish)中可以覆盖该设置 -publishToRtxp=1 #是否默认推流时转换成hls,hook接口(on_publish)中可以覆盖该设置 publishToHls=1 #是否默认推流时mp4录像,hook接口(on_publish)中可以覆盖该设置 @@ -68,6 +66,8 @@ segDur=2 segNum=3 #HLS切片从m3u8文件中移除后,继续保留在磁盘上的个数 segRetain=5 +# 是否广播 ts 切片完成通知 +broadcastRecordTs=0 [hook] #在推流时,如果url参数匹对admin_params,那么可以不经过hook鉴权直接推流成功,播放时亦然 @@ -85,6 +85,8 @@ 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 +# 录制 hls ts 切片完成事件 +on_record_ts=https://127.0.0.1/index/hook/on_record_ts #rtsp播放鉴权事件,此事件中比对rtsp的用户名密码 on_rtsp_auth=https://127.0.0.1/index/hook/on_rtsp_auth #rtsp播放是否开启专属鉴权事件,置空则关闭rtsp鉴权。rtsp播放鉴权还支持url方式鉴权 diff --git a/postman/ZLMediaKit.postman_collection.json b/postman/ZLMediaKit.postman_collection.json index 498e9386..b20e2b4d 100644 --- a/postman/ZLMediaKit.postman_collection.json +++ b/postman/ZLMediaKit.postman_collection.json @@ -1048,6 +1048,108 @@ } }, "response": [] + }, + { + "name": "开始发送rtp(startSendRtp)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{ZLMediaKit_URL}}/index/api/startSendRtp?secret={{ZLMediaKit_secret}}&vhost={{defaultVhost}}&app=live&stream=obs&ssrc=1&dst_url=127.0.0.1&dst_port=10000&is_udp=0", + "host": [ + "{{ZLMediaKit_URL}}" + ], + "path": [ + "index", + "api", + "startSendRtp" + ], + "query": [ + { + "key": "secret", + "value": "{{ZLMediaKit_secret}}", + "description": "api操作密钥(配置文件配置),如果操作ip是127.0.0.1,则不需要此参数" + }, + { + "key": "vhost", + "value": "{{defaultVhost}}", + "description": "虚拟主机,例如__defaultVhost__" + }, + { + "key": "app", + "value": "live", + "description": "应用名,例如 live" + }, + { + "key": "stream", + "value": "obs", + "description": "流id,例如 obs" + }, + { + "key": "ssrc", + "value": "1", + "description": "rtp的ssrc" + }, + { + "key": "dst_url", + "value": "127.0.0.1", + "description": "目标ip或域名" + }, + { + "key": "dst_port", + "value": "10000", + "description": "目标端口" + }, + { + "key": "is_udp", + "value": "0", + "description": "是否为udp模式,否则为tcp模式" + } + ] + } + }, + "response": [] + }, + { + "name": "停止 发送rtp(stopSendRtp)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{ZLMediaKit_URL}}/index/api/stopSendRtp?secret={{ZLMediaKit_secret}}&vhost={{defaultVhost}}&app=live&stream=obs", + "host": [ + "{{ZLMediaKit_URL}}" + ], + "path": [ + "index", + "api", + "stopSendRtp" + ], + "query": [ + { + "key": "secret", + "value": "{{ZLMediaKit_secret}}", + "description": "api操作密钥(配置文件配置),如果操作ip是127.0.0.1,则不需要此参数" + }, + { + "key": "vhost", + "value": "{{defaultVhost}}", + "description": "虚拟主机,例如__defaultVhost__" + }, + { + "key": "app", + "value": "live", + "description": "应用名,例如 live" + }, + { + "key": "stream", + "value": "obs", + "description": "流id,例如 obs" + } + ] + } + }, + "response": [] } ], "event": [ @@ -1074,17 +1176,17 @@ ], "variable": [ { - "id": "0e272976-965b-4f25-8b9e-5916c59234d7", + "id": "ce426571-eb1e-4067-8901-01978c982fed", "key": "ZLMediaKit_URL", "value": "zlmediakit.com:8880" }, { - "id": "321374c3-3357-4405-915e-9cb524d95fbc", + "id": "2d3dfd4a-a39c-47d8-a3e9-37d80352ea5f", "key": "ZLMediaKit_secret", "value": "035c73f7-bb6b-4889-a715-d9eb2d1925cc" }, { - "id": "468ce1f6-ec79-44d2-819e-5cb9f42cd396", + "id": "0aacc473-3a2e-4ef9-b415-e86ce71e0c42", "key": "defaultVhost", "value": "__defaultVhost__" } diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index fdf9b7f2..cea3cdc2 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -14,6 +14,7 @@ #include "Util/File.h" #include "System.h" #include "Thread/WorkThreadPool.h" +#include "Network/sockutil.h" namespace FFmpeg { #define FFmpeg_FIELD "ffmpeg." @@ -45,6 +46,18 @@ FFmpegSource::~FFmpegSource() { DebugL; } +static bool is_local_ip(const string &ip){ + if (ip == "127.0.0.1" || ip == "localhost") { + return true; + } + auto ips = SockUtil::getInterfaceList(); + for (auto &obj : ips) { + if (ip == obj["ip"]) { + return true; + } + } + return false; +} void FFmpegSource::play(const string &src_url,const string &dst_url,int timeout_ms,const onPlay &cb) { GET_CONFIG(string,ffmpeg_bin,FFmpeg::kBin); @@ -60,7 +73,7 @@ void FFmpegSource::play(const string &src_url,const string &dst_url,int timeout_ _process.run(cmd,ffmpeg_log.empty() ? "" : File::absolutePath("",ffmpeg_log)); InfoL << cmd; - if(_media_info._host == "127.0.0.1"){ + if (is_local_ip(_media_info._host)) { //推流给自己的,通过判断流是否注册上来判断是否正常 if(_media_info._schema != RTSP_SCHEMA && _media_info._schema != RTMP_SCHEMA){ cb(SockException(Err_other,"本服务只支持rtmp/rtsp推流")); @@ -179,7 +192,7 @@ void FFmpegSource::startTimer(int timeout_ms) { //自身已经销毁 return false; } - if (strongSelf->_media_info._host == "127.0.0.1") { + if (is_local_ip(strongSelf->_media_info._host)) { //推流给自己的,我们通过检查是否已经注册来判断FFmpeg是否工作正常 strongSelf->findAsync(0, [&](const MediaSource::Ptr &src) { //同步查找流 @@ -232,33 +245,19 @@ bool FFmpegSource::close(MediaSource &sender, bool force) { return true; } -int FFmpegSource::totalReaderCount(MediaSource &sender) { - auto listener = _listener.lock(); - if(listener){ - return listener->totalReaderCount(sender); - } - return sender.readerCount(); -} - -void FFmpegSource::onNoneReader(MediaSource &sender){ - auto listener = _listener.lock(); - if(listener){ - listener->onNoneReader(sender); - return; - } - MediaSourceEvent::onNoneReader(sender); -} - -void FFmpegSource::onRegist(MediaSource &sender, bool regist){ - auto listener = _listener.lock(); - if(listener){ - listener->onRegist(sender, regist); - } -} - void FFmpegSource::onGetMediaSource(const MediaSource::Ptr &src) { - _listener = src->getListener(); - src->setListener(shared_from_this()); + auto listener = src->getListener(); + if (listener.lock().get() != this) { + //防止多次进入onGetMediaSource函数导致无效递归调用的bug + _listener = listener; + src->setListener(shared_from_this()); + } else { + WarnL << "多次触发onGetMediaSource事件:" + << src->getSchema() << "/" + << src->getVhost() << "/" + << src->getApp() << "/" + << src->getId(); + } } void FFmpegSnap::makeSnap(const string &play_url, const string &save_path, float timeout_sec, const function &cb) { diff --git a/server/FFmpegSource.h b/server/FFmpegSource.h index 00635ac5..02fc288c 100644 --- a/server/FFmpegSource.h +++ b/server/FFmpegSource.h @@ -40,7 +40,7 @@ private: ~FFmpegSnap() = delete; }; -class FFmpegSource : public std::enable_shared_from_this , public MediaSourceEvent{ +class FFmpegSource : public std::enable_shared_from_this , public MediaSourceEventInterceptor{ public: typedef shared_ptr Ptr; typedef function onPlay; @@ -60,9 +60,6 @@ private: //MediaSourceEvent override bool close(MediaSource &sender,bool force) override; - int totalReaderCount(MediaSource &sender) override; - void onNoneReader(MediaSource &sender) override; - void onRegist(MediaSource &sender, bool regist) override; private: Process _process; @@ -72,7 +69,6 @@ private: string _src_url; string _dst_url; function _onClose; - std::weak_ptr _listener; Ticker _replay_ticker; }; diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 7f2de674..be3db75b 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -144,7 +144,7 @@ static ApiArgsType getAllArgs(const Parser &parser) { for (auto &pr : parser.getUrlArgs()) { allArgs[pr.first] = pr.second; } - return std::move(allArgs); + return allArgs; } static inline void addHttpListener(){ @@ -596,8 +596,6 @@ void installWebApi() { const string &app, const string &stream, const string &url, - bool enable_rtsp, - bool enable_rtmp, bool enable_hls, bool enable_mp4, int rtp_type, @@ -610,7 +608,7 @@ void installWebApi() { return; } //添加拉流代理 - PlayerProxy::Ptr player(new PlayerProxy(vhost,app,stream,enable_rtsp,enable_rtmp,enable_hls,enable_mp4)); + PlayerProxy::Ptr player(new PlayerProxy(vhost, app, stream, enable_hls, enable_mp4)); s_proxyMap[key] = player; //指定RTP over TCP(播放rtsp时有效) @@ -636,13 +634,11 @@ void installWebApi() { //测试url http://127.0.0.1/index/api/addStreamProxy?vhost=__defaultVhost__&app=proxy&enable_rtsp=1&enable_rtmp=1&stream=0&url=rtmp://127.0.0.1/live/obs api_regist2("/index/api/addStreamProxy",[](API_ARGS2){ CHECK_SECRET(); - CHECK_ARGS("vhost","app","stream","url","enable_rtsp","enable_rtmp"); + CHECK_ARGS("vhost","app","stream","url"); addStreamProxy(allArgs["vhost"], allArgs["app"], allArgs["stream"], allArgs["url"], - allArgs["enable_rtsp"],/* 是否rtsp转发 */ - allArgs["enable_rtmp"],/* 是否rtmp转发 */ allArgs["enable_hls"],/* 是否hls转发 */ allArgs["enable_mp4"],/* 是否MP4录制 */ allArgs["rtp_type"], @@ -788,7 +784,14 @@ void installWebApi() { CHECK_ARGS("stream_id"); lock_guard lck(s_rtpServerMapMtx); - val["hit"] = (int) s_rtpServerMap.erase(allArgs["stream_id"]); + auto it = s_rtpServerMap.find(allArgs["stream_id"]); + if(it == s_rtpServerMap.end()){ + val["hit"] = 0; + return; + } + auto server = it->second; + s_rtpServerMap.erase(it); + val["hit"] = 1; }); api_regist1("/index/api/listRtpServer",[](API_ARGS1){ @@ -803,6 +806,39 @@ void installWebApi() { } }); + api_regist2("/index/api/startSendRtp",[](API_ARGS2){ + CHECK_SECRET(); + CHECK_ARGS("vhost", "app", "stream", "ssrc", "dst_url", "dst_port", "is_udp"); + + auto src = MediaSource::find(allArgs["vhost"], allArgs["app"], allArgs["stream"]); + if (!src) { + throw ApiRetException("该媒体流不存在", API::OtherFailed); + } + + src->startSendRtp(allArgs["dst_url"], allArgs["dst_port"], allArgs["ssrc"], allArgs["is_udp"], [val, headerOut, invoker](const SockException &ex){ + if (ex) { + const_cast(val)["code"] = API::OtherFailed; + const_cast(val)["msg"] = ex.what(); + } + invoker("200 OK", headerOut, val.toStyledString()); + }); + }); + + api_regist1("/index/api/stopSendRtp",[](API_ARGS1){ + CHECK_SECRET(); + CHECK_ARGS("vhost", "app", "stream"); + + auto src = MediaSource::find(allArgs["vhost"], allArgs["app"], allArgs["stream"]); + if (!src) { + throw ApiRetException("该媒体流不存在", API::OtherFailed); + } + + if (!src->stopSendRtp()) { + throw ApiRetException("尚未开始推流,停止失败", API::OtherFailed); + } + }); + + #endif//ENABLE_RTPPROXY // 开始录制hls或MP4 @@ -1031,8 +1067,6 @@ void installWebApi() { allArgs["stream"], /** 支持rtsp和rtmp方式拉流 ,rtsp支持h265/h264/aac,rtmp仅支持h264/aac **/ "rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov", - true,/* 开启rtsp转发 */ - true,/* 开启rtmp转发 */ true,/* 开启hls转发 */ false,/* 禁用MP4录制 */ 0,//rtp over tcp方式拉流 diff --git a/server/WebHook.cpp b/server/WebHook.cpp index 244b07a7..e2d1adf1 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -53,6 +53,7 @@ const string kOnRtspAuth = HOOK_FIELD"on_rtsp_auth"; const string kOnStreamChanged = HOOK_FIELD"on_stream_changed"; const string kOnStreamNotFound = HOOK_FIELD"on_stream_not_found"; const string kOnRecordMp4 = HOOK_FIELD"on_record_mp4"; +const string kOnRecordTs = HOOK_FIELD"on_record_ts"; const string kOnShellLogin = HOOK_FIELD"on_shell_login"; const string kOnStreamNoneReader = HOOK_FIELD"on_stream_none_reader"; const string kOnHttpAccess = HOOK_FIELD"on_http_access"; @@ -70,6 +71,7 @@ onceToken token([](){ mINI::Instance()[kOnStreamChanged] = "https://127.0.0.1/index/hook/on_stream_changed"; mINI::Instance()[kOnStreamNotFound] = "https://127.0.0.1/index/hook/on_stream_not_found"; mINI::Instance()[kOnRecordMp4] = "https://127.0.0.1/index/hook/on_record_mp4"; + mINI::Instance()[kOnRecordTs] = "https://127.0.0.1/index/hook/on_record_ts"; mINI::Instance()[kOnShellLogin] = "https://127.0.0.1/index/hook/on_shell_login"; mINI::Instance()[kOnStreamNoneReader] = "https://127.0.0.1/index/hook/on_stream_none_reader"; mINI::Instance()[kOnHttpAccess] = "https://127.0.0.1/index/hook/on_http_access"; @@ -161,7 +163,7 @@ static ArgsType make_json(const MediaInfo &args){ body["app"] = args._app; body["stream"] = args._streamid; body["params"] = args._param_strs; - return std::move(body); + return body; } static void reportServerStarted(){ @@ -190,16 +192,16 @@ void installWebHook(){ GET_CONFIG(string,hook_stream_chaned,Hook::kOnStreamChanged); GET_CONFIG(string,hook_stream_not_found,Hook::kOnStreamNotFound); GET_CONFIG(string,hook_record_mp4,Hook::kOnRecordMp4); + GET_CONFIG(string,hook_record_ts,Hook::kOnRecordTs); GET_CONFIG(string,hook_shell_login,Hook::kOnShellLogin); GET_CONFIG(string,hook_stream_none_reader,Hook::kOnStreamNoneReader); GET_CONFIG(string,hook_http_access,Hook::kOnHttpAccess); NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastMediaPublish,[](BroadcastMediaPublishArgs){ - GET_CONFIG(bool,toRtxp,General::kPublishToRtxp); GET_CONFIG(bool,toHls,General::kPublishToHls); GET_CONFIG(bool,toMP4,General::kPublishToMP4); if(!hook_enable || args._param_strs == hook_adminparams || hook_publish.empty() || sender.get_peer_ip() == "127.0.0.1"){ - invoker("",toRtxp,toHls,toMP4); + invoker("", toHls, toMP4); return; } //异步执行该hook api,防止阻塞NoticeCenter @@ -211,27 +213,20 @@ void installWebHook(){ do_http_hook(hook_publish,body,[invoker](const Value &obj,const string &err){ if(err.empty()){ //推流鉴权成功 - bool enableRtxp = toRtxp; bool enableHls = toHls; bool enableMP4 = toMP4; - //兼容用户不传递enableRtxp、enableHls、enableMP4参数 - if(obj.isMember("enableRtxp")){ - enableRtxp = obj["enableRtxp"].asBool(); - } - - if(obj.isMember("enableHls")){ + //兼容用户不传递enableHls、enableMP4参数 + if (obj.isMember("enableHls")) { enableHls = obj["enableHls"].asBool(); } - - if(obj.isMember("enableMP4")){ + if (obj.isMember("enableMP4")) { enableMP4 = obj["enableMP4"].asBool(); } - - invoker(err,enableRtxp,enableHls,enableMP4); - }else{ + invoker(err, enableHls, enableMP4); + } else { //推流鉴权失败 - invoker(err,false, false, false); + invoker(err, false, false); } }); @@ -336,7 +331,7 @@ void installWebHook(){ //监听播放失败(未找到特定的流)事件 NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastNotFoundStream,[](BroadcastNotFoundStreamArgs){ if(!hook_enable || hook_stream_not_found.empty()){ - closePlayer(); +// closePlayer(); return; } auto body = make_json(args); @@ -347,28 +342,40 @@ void installWebHook(){ do_http_hook(hook_stream_not_found,body, nullptr); }); + static auto getRecordInfo = [](const RecordInfo &info) { + ArgsType body; + body["start_time"] = (Json::UInt64) info.start_time; + body["file_size"] = (Json::UInt64) info.file_size; + body["time_len"] = info.time_len; + body["file_path"] = info.file_path; + body["file_name"] = info.file_name; + body["folder"] = info.folder; + body["url"] = info.url; + body["app"] = info.app; + body["stream"] = info.stream; + body["vhost"] = info.vhost; + return body; + }; + #ifdef ENABLE_MP4 //录制mp4文件成功后广播 NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastRecordMP4,[](BroadcastRecordMP4Args){ - if(!hook_enable || hook_record_mp4.empty()){ + if (!hook_enable || hook_record_mp4.empty()) { return; } - ArgsType body; - body["start_time"] = (Json::UInt64)info.ui64StartedTime; - body["time_len"] = (Json::UInt64)info.ui64TimeLen; - body["file_size"] = (Json::UInt64)info.ui64FileSize; - body["file_path"] = info.strFilePath; - body["file_name"] = info.strFileName; - body["folder"] = info.strFolder; - body["url"] = info.strUrl; - body["app"] = info.strAppName; - body["stream"] = info.strStreamId; - body["vhost"] = info.strVhost; //执行hook - do_http_hook(hook_record_mp4,body, nullptr); + do_http_hook(hook_record_mp4, getRecordInfo(info), nullptr); }); #endif //ENABLE_MP4 + NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastRecordTs, [](BroadcastRecordTsArgs) { + if (!hook_enable || hook_record_ts.empty()) { + return; + } + // 执行 hook + do_http_hook(hook_record_ts, getRecordInfo(info), nullptr); + }); + NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastShellLogin,[](BroadcastShellLoginArgs){ if(!hook_enable || hook_shell_login.empty() || sender.get_peer_ip() == "127.0.0.1"){ invoker(""); @@ -407,7 +414,6 @@ void installWebHook(){ } strongSrc->close(false); }); - }); /** diff --git a/src/Common/Device.cpp b/src/Common/Device.cpp index 37a0056b..0b08289d 100644 --- a/src/Common/Device.cpp +++ b/src/Common/Device.cpp @@ -12,23 +12,17 @@ #include "Util/logger.h" #include "Util/base64.h" #include "Extension/AAC.h" +#include "Extension/Opus.h" #include "Extension/G711.h" #include "Extension/H264.h" #include "Extension/H265.h" - using namespace toolkit; namespace mediakit { -DevChannel::DevChannel(const string &vhost, - const string &app, - const string &stream_id, - float duration, - bool enable_rtsp, - bool enable_rtmp, - bool enable_hls, - bool enable_mp4) : - MultiMediaSourceMuxer(vhost, app, stream_id, duration, enable_rtsp, enable_rtmp, enable_hls, enable_mp4) {} +DevChannel::DevChannel(const string &vhost, const string &app, const string &stream_id, + float duration, bool enable_hls, bool enable_mp4) : + MultiMediaSourceMuxer(vhost, app, stream_id, duration, true, true, enable_hls, enable_mp4) {} DevChannel::~DevChannel() {} @@ -109,11 +103,12 @@ void DevChannel::inputH265(const char *data, int len, uint32_t dts, uint32_t pts inputFrame(frame); } -class AACFrameCacheAble : public AACFrameNoCacheAble{ +class FrameAutoDelete : public FrameFromPtr{ public: template - AACFrameCacheAble(ARGS && ...args) : AACFrameNoCacheAble(std::forward(args)...){}; - virtual ~AACFrameCacheAble() { + FrameAutoDelete(ARGS && ...args) : FrameFromPtr(std::forward(args)...){} + + ~FrameAutoDelete() override { delete [] _ptr; }; @@ -123,31 +118,32 @@ public: }; void DevChannel::inputAAC(const char *data_without_adts, int len, uint32_t dts, const char *adts_header){ - if(dts == 0){ - dts = (uint32_t)_aTicker[1].elapsedTime(); + if (dts == 0) { + dts = (uint32_t) _aTicker[1].elapsedTime(); } - if(adts_header){ - if(adts_header + 7 == data_without_adts){ + if (adts_header) { + if (adts_header + ADTS_HEADER_LEN == data_without_adts) { //adts头和帧在一起 - inputFrame(std::make_shared((char *)data_without_adts - 7, len + 7, dts, 0, 7)); - }else{ + inputFrame(std::make_shared(_audio->codecId, (char *) data_without_adts - ADTS_HEADER_LEN, len + ADTS_HEADER_LEN, dts, 0, ADTS_HEADER_LEN)); + } else { //adts头和帧不在一起 - char *dataWithAdts = new char[len + 7]; - memcpy(dataWithAdts, adts_header, 7); - memcpy(dataWithAdts + 7 , data_without_adts , len); - inputFrame(std::make_shared(dataWithAdts, len + 7, dts, 0, 7)); + char *data_with_adts = new char[len + ADTS_HEADER_LEN]; + memcpy(data_with_adts, adts_header, ADTS_HEADER_LEN); + memcpy(data_with_adts + ADTS_HEADER_LEN, data_without_adts, len); + inputFrame(std::make_shared(_audio->codecId, data_with_adts, len + ADTS_HEADER_LEN, dts, 0, ADTS_HEADER_LEN)); } + } else { + //没有adts头 + inputFrame(std::make_shared(_audio->codecId, (char *) data_without_adts, len, dts, 0, 0)); } } -void DevChannel::inputG711(const char *data, int len, uint32_t dts){ +void DevChannel::inputAudio(const char *data, int len, uint32_t dts){ if (dts == 0) { - dts = (uint32_t)_aTicker[1].elapsedTime(); + dts = (uint32_t) _aTicker[1].elapsedTime(); } - auto frame = std::make_shared((char*)data, len, dts, 0); - frame->setCodec(_audio->codecId); - inputFrame(frame); + inputFrame(std::make_shared(_audio->codecId, (char *) data, len, dts, 0)); } void DevChannel::initVideo(const VideoInfo &info) { @@ -165,6 +161,7 @@ void DevChannel::initAudio(const AudioInfo &info) { case CodecAAC : addTrack(std::make_shared()); break; case CodecG711A : case CodecG711U : addTrack(std::make_shared(info.codecId, info.iSampleRate, info.iChannel, info.iSampleBit)); break; + case CodecOpus : addTrack(std::make_shared()); break; default: WarnL << "不支持该类型的音频编码类型:" << info.codecId; break; } } diff --git a/src/Common/Device.h b/src/Common/Device.h index a2129d98..5f726f79 100644 --- a/src/Common/Device.h +++ b/src/Common/Device.h @@ -17,11 +17,9 @@ #include "Util/util.h" #include "Util/TimeTicker.h" #include "Common/MultiMediaSourceMuxer.h" - using namespace std; using namespace toolkit; - #ifdef ENABLE_FAAC #include "Codec/AACEncoder.h" #endif //ENABLE_FAAC @@ -55,16 +53,10 @@ class DevChannel : public MultiMediaSourceMuxer{ public: typedef std::shared_ptr Ptr; //fDuration<=0为直播,否则为点播 - DevChannel(const string &vhost, - const string &app, - const string &stream_id, - float duration = 0, - bool enable_rtsp = true, - bool enable_rtmp = true, - bool enable_hls = true, - bool enable_mp4 = false); + DevChannel(const string &vhost, const string &app, const string &stream_id, + float duration = 0, bool enable_hls = true, bool enable_mp4 = false); - virtual ~DevChannel(); + ~DevChannel() override ; /** * 初始化视频Track @@ -108,12 +100,12 @@ public: void inputAAC(const char *data_without_adts, int len, uint32_t dts, const char *adts_header); /** - * G711音频帧 + * 输入OPUS/G711音频帧 * @param data 音频帧 * @param len 帧数据长度 * @param dts 时间戳,单位毫秒 */ - void inputG711(const char* data, int len, uint32_t dts); + void inputAudio(const char *data, int len, uint32_t dts); #ifdef ENABLE_X264 /** diff --git a/src/Common/MediaSink.cpp b/src/Common/MediaSink.cpp index acb5e7da..13b9ab40 100644 --- a/src/Common/MediaSink.cpp +++ b/src/Common/MediaSink.cpp @@ -161,7 +161,7 @@ vector MediaSink::getTracks(bool trackReady) const{ } ret.emplace_back(pr.second); } - return std::move(ret); + return ret; } diff --git a/src/Common/MediaSink.h b/src/Common/MediaSink.h index ea7e3408..45afe88d 100644 --- a/src/Common/MediaSink.h +++ b/src/Common/MediaSink.h @@ -36,6 +36,11 @@ public: */ virtual void addTrack(const Track::Ptr & track) = 0; + /** + * 添加所有Track完毕 + */ + virtual void addTrackCompleted() {} + /** * 重置track */ @@ -70,7 +75,7 @@ public: * 这样会增加生成流的延时,如果添加了音视频双Track,那么可以不调用此方法 * 否则为了降低流注册延时,请手动调用此方法 */ - void addTrackCompleted(); + void addTrackCompleted() override; /** * 重置track diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index b6a454a3..2b7b80bb 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -8,7 +8,6 @@ * may be found in the AUTHORS file in the root of the source tree. */ -#include #include "MediaSource.h" #include "Record/MP4Reader.h" #include "Util/util.h" @@ -17,16 +16,19 @@ using namespace toolkit; namespace mediakit { -recursive_mutex MediaSource::g_mtxMediaSrc; -MediaSource::SchemaVhostAppStreamMap MediaSource::g_mapMediaSrc; +recursive_mutex s_media_source_mtx; +MediaSource::SchemaVhostAppStreamMap s_media_source_map; -MediaSource::MediaSource(const string &strSchema, const string &strVhost, const string &strApp, const string &strId) : - _strSchema(strSchema), _strApp(strApp), _strId(strId) { - if (strVhost.empty()) { - _strVhost = DEFAULT_VHOST; +MediaSource::MediaSource(const string &schema, const string &vhost, const string &app, const string &stream_id){ + GET_CONFIG(bool, enableVhost, General::kEnableVhost); + if (!enableVhost) { + _vhost = DEFAULT_VHOST; } else { - _strVhost = strVhost; + _vhost = vhost.empty() ? DEFAULT_VHOST : vhost; } + _schema = schema; + _app = app; + _stream_id = stream_id; } MediaSource::~MediaSource() { @@ -34,32 +36,28 @@ MediaSource::~MediaSource() { } const string& MediaSource::getSchema() const { - return _strSchema; + return _schema; } const string& MediaSource::getVhost() const { - return _strVhost; + return _vhost; } const string& MediaSource::getApp() const { //获取该源的id - return _strApp; + return _app; } const string& MediaSource::getId() const { - return _strId; + return _stream_id; } -vector MediaSource::getTracks(bool trackReady) const { - auto strongPtr = _track_source.lock(); - if(strongPtr){ - return strongPtr->getTracks(trackReady); +vector MediaSource::getTracks(bool ready) const { + auto listener = _listener.lock(); + if(!listener){ + return vector(); } - return vector(); -} - -void MediaSource::setTrackSource(const std::weak_ptr &track_src) { - _track_source = track_src; + return listener->getTracks(const_cast(*this), ready); } void MediaSource::setListener(const std::weak_ptr &listener){ @@ -77,12 +75,13 @@ int MediaSource::totalReaderCount(){ } return listener->totalReaderCount(*this); } -bool MediaSource::seekTo(uint32_t ui32Stamp) { + +bool MediaSource::seekTo(uint32_t stamp) { auto listener = _listener.lock(); if(!listener){ return false; } - return listener->seekTo(*this,ui32Stamp); + return listener->seekTo(*this, stamp); } bool MediaSource::close(bool force) { @@ -93,19 +92,17 @@ bool MediaSource::close(bool force) { return listener->close(*this,force); } -void MediaSource::onNoneReader(){ +void MediaSource::onReaderChanged(int size) { auto listener = _listener.lock(); - if(!listener){ - return; - } - if (listener->totalReaderCount(*this) == 0) { - listener->onNoneReader(*this); + if (listener) { + listener->onReaderChanged(*this, size); } } bool MediaSource::setupRecord(Recorder::type type, bool start, const string &custom_path){ auto listener = _listener.lock(); if (!listener) { + WarnL << "未设置MediaSource的事件监听者,setupRecord失败:" << getSchema() << "/" << getVhost() << "/" << getApp() << "/" << getId(); return false; } return listener->setupRecord(*this, type, start, custom_path); @@ -119,13 +116,30 @@ bool MediaSource::isRecording(Recorder::type type){ return listener->isRecording(*this, type); } +void MediaSource::startSendRtp(const string &dst_url, uint16_t dst_port, uint32_t ssrc, bool is_udp, const function &cb){ + auto listener = _listener.lock(); + if (!listener) { + cb(SockException(Err_other, "尚未设置事件监听器")); + return; + } + return listener->startSendRtp(*this, dst_url, dst_port, ssrc, is_udp, cb); +} + +bool MediaSource::stopSendRtp() { + auto listener = _listener.lock(); + if (!listener) { + return false; + } + return listener->stopSendRtp(*this); +} + void MediaSource::for_each_media(const function &cb) { - decltype(g_mapMediaSrc) copy; + decltype(s_media_source_map) copy; { - //拷贝g_mapMediaSrc后再遍历,考虑到是高频使用的全局单例锁,并且在上锁时会执行回调代码 + //拷贝s_media_source_map后再遍历,考虑到是高频使用的全局单例锁,并且在上锁时会执行回调代码 //很容易导致多个锁交叉死锁的情况,而且该函数使用频率不高,拷贝开销相对来说是可以接受的 - lock_guard lock(g_mtxMediaSrc); - copy = g_mapMediaSrc; + lock_guard lock(s_media_source_mtx); + copy = s_media_source_map; } for (auto &pr0 : copy) { @@ -180,42 +194,76 @@ static void eraseIfEmpty(MAP &map, IT0 it0, IT1 it1, IT2 it2) { } } -void MediaSource::findAsync_l(const MediaInfo &info, const std::shared_ptr &session, bool retry, const function &cb){ - auto src = MediaSource::find_l(info._schema, info._vhost, info._app, info._streamid, true); +static MediaSource::Ptr find_l(const string &schema, const string &vhost_in, const string &app, const string &id, bool create_new) { + string vhost = vhost_in; + GET_CONFIG(bool,enableVhost,General::kEnableVhost); + if(vhost.empty() || !enableVhost){ + vhost = DEFAULT_VHOST; + } + + MediaSource::Ptr ret; + { + lock_guard lock(s_media_source_mtx); + //查找某一媒体源,找到后返回 + searchMedia(s_media_source_map, schema, vhost, app, id, + [&](MediaSource::SchemaVhostAppStreamMap::iterator &it0, MediaSource::VhostAppStreamMap::iterator &it1, + MediaSource::AppStreamMap::iterator &it2, MediaSource::StreamMap::iterator &it3) { + ret = it3->second.lock(); + if (!ret) { + //该对象已经销毁 + it2->second.erase(it3); + eraseIfEmpty(s_media_source_map, it0, it1, it2); + return false; + } + return true; + }); + } + + if(!ret && create_new && schema != HLS_SCHEMA){ + //未查找媒体源,则读取mp4创建一个 + //播放hls不触发mp4点播(因为HLS也可以用于录像,不是纯粹的直播) + ret = MediaSource::createFromMP4(schema, vhost, app, id); + } + return ret; +} + +static void findAsync_l(const MediaInfo &info, const std::shared_ptr &session, bool retry, + const function &cb){ + auto src = find_l(info._schema, info._vhost, info._app, info._streamid, true); if (src || !retry) { cb(src); return; } void *listener_tag = session.get(); - weak_ptr weakSession = session; + weak_ptr weak_session = session; GET_CONFIG(int, maxWaitMS, General::kMaxStreamWaitTimeMS); - auto onTimeout = session->getPoller()->doDelayTask(maxWaitMS, [cb, listener_tag]() { + auto on_timeout = session->getPoller()->doDelayTask(maxWaitMS, [cb, listener_tag]() { //最多等待一定时间,如果这个时间内,流未注册上,那么返回未找到流 NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged); cb(nullptr); return 0; }); - auto cancelAll = [onTimeout, listener_tag]() { + auto cancel_all = [on_timeout, listener_tag]() { //取消延时任务,防止多次回调 - onTimeout->cancel(); + on_timeout->cancel(); //取消媒体注册事件监听 NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged); }; - function closePlayer = [cb, cancelAll]() { - cancelAll(); + function close_player = [cb, cancel_all]() { + cancel_all(); //告诉播放器,流不存在,这样会立即断开播放器 cb(nullptr); }; - auto onRegist = [weakSession, info, cb, cancelAll](BroadcastMediaChangedArgs) { - auto strongSession = weakSession.lock(); - if (!strongSession) { + auto on_regist = [weak_session, info, cb, cancel_all](BroadcastMediaChangedArgs) { + auto strong_session = weak_session.lock(); + if (!strong_session) { //自己已经销毁 - cancelAll(); + cancel_all(); return; } @@ -228,11 +276,11 @@ void MediaSource::findAsync_l(const MediaInfo &info, const std::shared_ptrasync([weakSession, info, cb]() { - auto strongSession = weakSession.lock(); + strong_session->async([weak_session, info, cb]() { + auto strongSession = weak_session.lock(); if (!strongSession) { return; } @@ -243,9 +291,9 @@ void MediaSource::findAsync_l(const MediaInfo &info, const std::shared_ptr(*session), closePlayer); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastNotFoundStream, info, static_cast(*session), close_player); } void MediaSource::findAsync(const MediaInfo &info, const std::shared_ptr &session,const function &cb){ @@ -256,232 +304,129 @@ MediaSource::Ptr MediaSource::find(const string &schema, const string &vhost, co return find_l(schema, vhost, app, id, false); } -MediaSource::Ptr MediaSource::find_l(const string &schema, const string &vhost_tmp, const string &app, const string &id, bool bMake) { - string vhost = vhost_tmp; - if(vhost.empty()){ - vhost = DEFAULT_VHOST; +MediaSource::Ptr MediaSource::find(const string &vhost, const string &app, const string &stream_id){ + auto src = MediaSource::find(RTMP_SCHEMA, vhost, app, stream_id); + if (src) { + return src; } - - GET_CONFIG(bool,enableVhost,General::kEnableVhost); - if(!enableVhost){ - vhost = DEFAULT_VHOST; + src = MediaSource::find(RTSP_SCHEMA, vhost, app, stream_id); + if (src) { + return src; } - - MediaSource::Ptr ret; - { - lock_guard lock(g_mtxMediaSrc); - //查找某一媒体源,找到后返回 - searchMedia(g_mapMediaSrc, schema, vhost, app, id, [&](SchemaVhostAppStreamMap::iterator &it0, - VhostAppStreamMap::iterator &it1, - AppStreamMap::iterator &it2, - StreamMap::iterator &it3) { - ret = it3->second.lock(); - if (!ret) { - //该对象已经销毁 - it2->second.erase(it3); - eraseIfEmpty(g_mapMediaSrc, it0, it1, it2); - return false; - } - return true; - }); - } - - if(!ret && bMake){ - //未查找媒体源,则创建一个 - ret = createFromMP4(schema, vhost, app, id); - } - return ret; + return MediaSource::find(HLS_SCHEMA, vhost, app, stream_id); } -void MediaSource::regist() { - GET_CONFIG(bool,enableVhost,General::kEnableVhost); - if(!enableVhost){ - _strVhost = DEFAULT_VHOST; - } - //注册该源,注册后服务器才能找到该源 - { - lock_guard lock(g_mtxMediaSrc); - g_mapMediaSrc[_strSchema][_strVhost][_strApp][_strId] = shared_from_this(); - } - _StrPrinter codec_info; - auto tracks = getTracks(true); - for(auto &track : tracks) { - auto codec_type = track->getTrackType(); - codec_info << track->getCodecName(); - switch (codec_type) { - case TrackAudio : { - auto audio_track = dynamic_pointer_cast(track); - codec_info << "[" - << audio_track->getAudioSampleRate() << "/" - << audio_track->getAudioChannel() << "/" - << audio_track->getAudioSampleBit() << "] "; - break; - } - case TrackVideo : { - auto video_track = dynamic_pointer_cast(track); - codec_info << "[" - << video_track->getVideoWidth() << "/" - << video_track->getVideoHeight() << "/" - << round(video_track->getVideoFps()) << "] "; - break; - } - default: - break; - } - } - - InfoL << _strSchema << " " << _strVhost << " " << _strApp << " " << _strId << " " << codec_info; - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, true, *this); +void MediaSource::emitEvent(bool regist){ auto listener = _listener.lock(); if (listener) { - listener->onRegist(*this, true); + //触发回调 + listener->onRegist(*this, regist); } + //触发广播 + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, regist, *this); + InfoL << (regist ? "媒体注册:" : "媒体注销:") << _schema << " " << _vhost << " " << _app << " " << _stream_id; +} + +void MediaSource::regist() { + { + //减小互斥锁临界区 + lock_guard lock(s_media_source_mtx); + s_media_source_map[_schema][_vhost][_app][_stream_id] = shared_from_this(); + } + emitEvent(true); } //反注册该源 bool MediaSource::unregist() { bool ret; { - lock_guard lock(g_mtxMediaSrc); - ret = searchMedia(g_mapMediaSrc, _strSchema, _strVhost, _strApp, _strId, - [&](SchemaVhostAppStreamMap::iterator &it0, - VhostAppStreamMap::iterator &it1, - AppStreamMap::iterator &it2, - StreamMap::iterator &it3) { - auto strongMedia = it3->second.lock(); - if (strongMedia && this != strongMedia.get()) { - //不是自己,不允许反注册 - return false; - } - it2->second.erase(it3); - eraseIfEmpty(g_mapMediaSrc, it0, it1, it2); - return true; - }); + //减小互斥锁临界区 + lock_guard lock(s_media_source_mtx); + ret = searchMedia(s_media_source_map, _schema, _vhost, _app, _stream_id, + [&](SchemaVhostAppStreamMap::iterator &it0, VhostAppStreamMap::iterator &it1, + AppStreamMap::iterator &it2, StreamMap::iterator &it3) { + auto strong_self = it3->second.lock(); + if (strong_self && this != strong_self.get()) { + //不是自己,不允许反注册 + return false; + } + it2->second.erase(it3); + eraseIfEmpty(s_media_source_map, it0, it1, it2); + return true; + }); } - if(ret){ - InfoL << _strSchema << " " << _strVhost << " " << _strApp << " " << _strId; - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, false, *this); - - auto listener = _listener.lock(); - if (listener) { - listener->onRegist(*this, false); - } + if (ret) { + emitEvent(false); } return ret; } /////////////////////////////////////MediaInfo////////////////////////////////////// -void MediaInfo::parse(const string &url){ - //string url = "rtsp://127.0.0.1:8554/live/id?key=val&a=1&&b=2&vhost=vhost.com"; +void MediaInfo::parse(const string &url_in){ + string url = url_in; + auto pos = url.find("?"); + if (pos != string::npos) { + _param_strs = url.substr(pos + 1); + url.erase(pos); + } + auto schema_pos = url.find("://"); - if(schema_pos != string::npos){ - _schema = url.substr(0,schema_pos); - }else{ + if (schema_pos != string::npos) { + _schema = url.substr(0, schema_pos); + } else { schema_pos = -3; } - auto split_vec = split(url.substr(schema_pos + 3),"/"); - if(split_vec.size() > 0){ + auto split_vec = split(url.substr(schema_pos + 3), "/"); + if (split_vec.size() > 0) { auto vhost = split_vec[0]; auto pos = vhost.find(":"); - if(pos != string::npos){ - _host = _vhost = vhost.substr(0,pos); + if (pos != string::npos) { + _host = _vhost = vhost.substr(0, pos); _port = vhost.substr(pos + 1); - } else{ + } else { _host = _vhost = vhost; } - - if(_vhost == "localhost" || INADDR_NONE != inet_addr(_vhost.data())){ + if (_vhost == "localhost" || INADDR_NONE != inet_addr(_vhost.data())) { //如果访问的是localhost或ip,那么则为默认虚拟主机 _vhost = DEFAULT_VHOST; } - } - if(split_vec.size() > 1){ + if (split_vec.size() > 1) { _app = split_vec[1]; } - if(split_vec.size() > 2){ - string steamid; - for(int i = 2 ; i < split_vec.size() ; ++i){ - steamid.append(split_vec[i] + "/"); + if (split_vec.size() > 2) { + string stream_id; + for (int i = 2; i < split_vec.size(); ++i) { + stream_id.append(split_vec[i] + "/"); } - if(steamid.back() == '/'){ - steamid.pop_back(); - } - auto pos = steamid.find("?"); - if(pos != string::npos){ - _streamid = steamid.substr(0,pos); - _param_strs = steamid.substr(pos + 1); - auto params = Parser::parseArgs(_param_strs); - if(params.find(VHOST_KEY) != params.end()){ - _vhost = params[VHOST_KEY]; - } - } else{ - _streamid = steamid; + if (stream_id.back() == '/') { + stream_id.pop_back(); } + _streamid = stream_id; } - GET_CONFIG(bool,enableVhost,General::kEnableVhost); - if(!enableVhost || _vhost.empty()){ + auto params = Parser::parseArgs(_param_strs); + if (params.find(VHOST_KEY) != params.end()) { + _vhost = params[VHOST_KEY]; + } + + GET_CONFIG(bool, enableVhost, General::kEnableVhost); + if (!enableVhost || _vhost.empty()) { //如果关闭虚拟主机或者虚拟主机为空,则设置虚拟主机为默认 _vhost = DEFAULT_VHOST; } } -/////////////////////////////////////MediaSourceEvent////////////////////////////////////// - -void MediaSourceEvent::onNoneReader(MediaSource &sender){ - GET_CONFIG(string, recordApp, Record::kAppName); - GET_CONFIG(int, stream_none_reader_delay, General::kStreamNoneReaderDelayMS); - - //如果mp4点播, 无人观看时我们强制关闭点播 - bool is_mp4_vod = sender.getApp() == recordApp; - - //没有任何人观看该视频源,表明该源可以关闭了 - weak_ptr weakSender = sender.shared_from_this(); - _async_close_timer = std::make_shared(stream_none_reader_delay / 1000.0, [weakSender,is_mp4_vod]() { - auto strongSender = weakSender.lock(); - if (!strongSender) { - //对象已经销毁 - return false; - } - - if (strongSender->totalReaderCount() != 0) { - //还有人消费 - return false; - } - - if(!is_mp4_vod){ - //直播时触发无人观看事件,让开发者自行选择是否关闭 - WarnL << "无人观看事件:" - << strongSender->getSchema() << "/" - << strongSender->getVhost() << "/" - << strongSender->getApp() << "/" - << strongSender->getId(); - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastStreamNoneReader, *strongSender); - }else{ - //这个是mp4点播,我们自动关闭 - WarnL << "MP4点播无人观看,自动关闭:" - << strongSender->getSchema() << "/" - << strongSender->getVhost() << "/" - << strongSender->getApp() << "/" - << strongSender->getId(); - strongSender->close(false); - } - - return false; - }, nullptr); -} - -MediaSource::Ptr MediaSource::createFromMP4(const string &schema, const string &vhost, const string &app, const string &stream, const string &filePath , bool checkApp){ +MediaSource::Ptr MediaSource::createFromMP4(const string &schema, const string &vhost, const string &app, const string &stream, const string &file_path , bool check_app){ GET_CONFIG(string, appName, Record::kAppName); - if (checkApp && app != appName) { + if (check_app && app != appName) { return nullptr; } #ifdef ENABLE_MP4 try { - MP4Reader::Ptr pReader(new MP4Reader(vhost, app, stream, filePath)); + MP4Reader::Ptr pReader(new MP4Reader(vhost, app, stream, file_path)); pReader->startReadMP4(); return MediaSource::find(schema, vhost, app, stream); } catch (std::exception &ex) { @@ -494,6 +439,136 @@ MediaSource::Ptr MediaSource::createFromMP4(const string &schema, const string & #endif //ENABLE_MP4 } +/////////////////////////////////////MediaSourceEvent////////////////////////////////////// + +void MediaSourceEvent::onReaderChanged(MediaSource &sender, int size){ + if (size || totalReaderCount(sender)) { + //还有人观看该视频,不触发关闭事件 + return; + } + //没有任何人观看该视频源,表明该源可以关闭了 + GET_CONFIG(string, record_app, Record::kAppName); + GET_CONFIG(int, stream_none_reader_delay, General::kStreamNoneReaderDelayMS); + //如果mp4点播, 无人观看时我们强制关闭点播 + bool is_mp4_vod = sender.getApp() == record_app; + weak_ptr weak_sender = sender.shared_from_this(); + + _async_close_timer = std::make_shared(stream_none_reader_delay / 1000.0, [weak_sender, is_mp4_vod]() { + auto strong_sender = weak_sender.lock(); + if (!strong_sender) { + //对象已经销毁 + return false; + } + + if (strong_sender->totalReaderCount()) { + //还有人观看该视频,不触发关闭事件 + return false; + } + + if (!is_mp4_vod) { + //直播时触发无人观看事件,让开发者自行选择是否关闭 + WarnL << "无人观看事件:" + << strong_sender->getSchema() << "/" + << strong_sender->getVhost() << "/" + << strong_sender->getApp() << "/" + << strong_sender->getId(); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastStreamNoneReader, *strong_sender); + } else { + //这个是mp4点播,我们自动关闭 + WarnL << "MP4点播无人观看,自动关闭:" + << strong_sender->getSchema() << "/" + << strong_sender->getVhost() << "/" + << strong_sender->getApp() << "/" + << strong_sender->getId(); + strong_sender->close(false); + } + return false; + }, nullptr); +} + +bool MediaSourceEventInterceptor::seekTo(MediaSource &sender, uint32_t stamp) { + auto listener = _listener.lock(); + if (!listener) { + return false; + } + return listener->seekTo(sender, stamp); +} + +bool MediaSourceEventInterceptor::close(MediaSource &sender, bool force) { + auto listener = _listener.lock(); + if (!listener) { + return false; + } + return listener->close(sender, force); +} + +int MediaSourceEventInterceptor::totalReaderCount(MediaSource &sender) { + auto listener = _listener.lock(); + if (!listener) { + return sender.readerCount(); + } + return listener->totalReaderCount(sender); +} + +void MediaSourceEventInterceptor::onReaderChanged(MediaSource &sender, int size) { + auto listener = _listener.lock(); + if (!listener) { + MediaSourceEvent::onReaderChanged(sender, size); + } else { + listener->onReaderChanged(sender, size); + } +} + +void MediaSourceEventInterceptor::onRegist(MediaSource &sender, bool regist) { + auto listener = _listener.lock(); + if (listener) { + listener->onRegist(sender, regist); + } +} + +bool MediaSourceEventInterceptor::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path) { + auto listener = _listener.lock(); + if (!listener) { + return false; + } + return listener->setupRecord(sender, type, start, custom_path); +} + +bool MediaSourceEventInterceptor::isRecording(MediaSource &sender, Recorder::type type) { + auto listener = _listener.lock(); + if (!listener) { + return false; + } + return listener->isRecording(sender, type); +} + +vector MediaSourceEventInterceptor::getTracks(MediaSource &sender, bool trackReady) const { + auto listener = _listener.lock(); + if (!listener) { + return vector(); + } + return listener->getTracks(sender, trackReady); +} + +void MediaSourceEventInterceptor::startSendRtp(MediaSource &sender, const string &dst_url, uint16_t dst_port, uint32_t ssrc, bool is_udp, const function &cb){ + auto listener = _listener.lock(); + if (listener) { + listener->startSendRtp(sender, dst_url, dst_port, ssrc, is_udp, cb); + } else { + MediaSourceEvent::startSendRtp(sender, dst_url, dst_port, ssrc, is_udp, cb); + } +} + +bool MediaSourceEventInterceptor::stopSendRtp(MediaSource &sender){ + auto listener = _listener.lock(); + if (listener) { + return listener->stopSendRtp(sender); + } + return false; +} + +/////////////////////////////////////FlushPolicy////////////////////////////////////// + static bool isFlushAble_default(bool is_video, uint32_t last_stamp, uint32_t new_stamp, int cache_size) { if (new_stamp + 500 < last_stamp) { //时间戳回退比较大(可能seek中),由于rtp中时间戳是pts,是可能存在一定程度的回退的 diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index d4e44d4e..f92df7c2 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -44,33 +44,63 @@ public: virtual ~MediaSourceEvent(){}; // 通知拖动进度条 - virtual bool seekTo(MediaSource &sender,uint32_t ui32Stamp){ return false; } - // 通知其停止推流 - virtual bool close(MediaSource &sender,bool force) { return false;} - // 观看总人数 + virtual bool seekTo(MediaSource &sender, uint32_t stamp) { return false; } + // 通知其停止产生流 + virtual bool close(MediaSource &sender, bool force) { return false; } + // 获取观看总人数 virtual int totalReaderCount(MediaSource &sender) = 0; + // 通知观看人数变化 + virtual void onReaderChanged(MediaSource &sender, int size); + //流注册或注销事件 + virtual void onRegist(MediaSource &sender, bool regist) {}; + + ////////////////////////仅供MultiMediaSourceMuxer对象继承//////////////////////// // 开启或关闭录制 virtual bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path) { return false; }; // 获取录制状态 virtual bool isRecording(MediaSource &sender, Recorder::type type) { return false; }; - // 通知无人观看 - virtual void onNoneReader(MediaSource &sender); - //流注册或注销事件 - virtual void onRegist(MediaSource &sender, bool regist) {}; + // 获取所有track相关信息 + virtual vector getTracks(MediaSource &sender, bool trackReady = true) const { return vector(); }; + // 开始发送ps-rtp + virtual void startSendRtp(MediaSource &sender, const string &dst_url, uint16_t dst_port, uint32_t ssrc, bool is_udp, const function &cb) { cb(SockException(Err_other, "not implemented"));}; + // 停止发送ps-rtp + virtual bool stopSendRtp(MediaSource &sender) {return false; } private: Timer::Ptr _async_close_timer; }; +//该对象用于拦截感兴趣的MediaSourceEvent事件 +class MediaSourceEventInterceptor : public MediaSourceEvent{ +public: + MediaSourceEventInterceptor(){} + ~MediaSourceEventInterceptor() override {} + + bool seekTo(MediaSource &sender, uint32_t stamp) override; + bool close(MediaSource &sender, bool force) override; + int totalReaderCount(MediaSource &sender) override; + void onReaderChanged(MediaSource &sender, int size) override; + void onRegist(MediaSource &sender, bool regist) override; + bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path) override; + bool isRecording(MediaSource &sender, Recorder::type type) override; + vector getTracks(MediaSource &sender, bool trackReady = true) const override; + void startSendRtp(MediaSource &sender, const string &dst_url, uint16_t dst_port, uint32_t ssrc, bool is_udp, const function &cb) override; + bool stopSendRtp(MediaSource &sender) override; + +protected: + std::weak_ptr _listener; +}; + /** * 解析url获取媒体相关信息 */ class MediaInfo{ public: - MediaInfo(){} - ~MediaInfo(){} - MediaInfo(const string &url){ parse(url); } + ~MediaInfo() {} + MediaInfo() {} + MediaInfo(const string &url) { parse(url); } void parse(const string &url); + public: string _schema; string _host; @@ -92,9 +122,11 @@ public: typedef unordered_map VhostAppStreamMap; typedef unordered_map SchemaVhostAppStreamMap; - MediaSource(const string &strSchema, const string &strVhost, const string &strApp, const string &strId) ; + MediaSource(const string &schema, const string &vhost, const string &app, const string &stream_id) ; virtual ~MediaSource() ; + ////////////////获取MediaSource相关信息//////////////// + // 获取协议类型 const string& getSchema() const; // 虚拟主机 @@ -104,13 +136,18 @@ public: // 流id const string& getId() const; - // 设置TrackSource - void setTrackSource(const std::weak_ptr &track_src); // 获取所有Track - vector getTracks(bool trackReady = true) const override; + vector getTracks(bool ready = true) const override; + + // 获取流当前时间戳 + virtual uint32_t getTimeStamp(TrackType type) { return 0; }; + // 设置时间戳 + virtual void setTimeStamp(uint32_t stamp) {}; + + ////////////////MediaSourceEvent相关接口实现//////////////// // 设置监听者 - virtual void setListener(const std::weak_ptr &listener); + void setListener(const std::weak_ptr &listener); // 获取监听者 const std::weak_ptr& getListener() const; @@ -119,48 +156,52 @@ public: // 观看者个数,包括(hls/rtsp/rtmp) virtual int totalReaderCount(); - // 获取流当前时间戳 - virtual uint32_t getTimeStamp(TrackType trackType) { return 0; }; - // 设置时间戳 - virtual void setTimeStamp(uint32_t uiStamp) {}; - // 拖动进度条 - bool seekTo(uint32_t ui32Stamp); + bool seekTo(uint32_t stamp); // 关闭该流 bool close(bool force); - // 该流无人观看 - void onNoneReader(); + // 该流观看人数变化 + void onReaderChanged(int size); // 开启或关闭录制 - virtual bool setupRecord(Recorder::type type, bool start, const string &custom_path); + bool setupRecord(Recorder::type type, bool start, const string &custom_path); // 获取录制状态 - virtual bool isRecording(Recorder::type type); + bool isRecording(Recorder::type type); + // 开始发送ps-rtp + void startSendRtp(const string &dst_url, uint16_t dst_port, uint32_t ssrc, bool is_udp, const function &cb); + // 停止发送ps-rtp + bool stopSendRtp(); + + ////////////////static方法,查找或生成MediaSource//////////////// // 同步查找流 static Ptr find(const string &schema, const string &vhost, const string &app, const string &id); + + // 忽略类型,同步查找流,可能返回rtmp/rtsp/hls类型 + static Ptr find(const string &vhost, const string &app, const string &stream_id); + // 异步查找流 static void findAsync(const MediaInfo &info, const std::shared_ptr &session, const function &cb); // 遍历所有流 static void for_each_media(const function &cb); - // 从mp4文件生成MediaSource - static MediaSource::Ptr createFromMP4(const string &schema, const string &vhost, const string &app, const string &stream, const string &filePath = "", bool checkApp = true); + static MediaSource::Ptr createFromMP4(const string &schema, const string &vhost, const string &app, const string &stream, const string &file_path = "", bool check_app = true); protected: - void regist() ; - bool unregist(); + //媒体注册 + void regist(); private: - static Ptr find_l(const string &schema, const string &vhost, const string &app, const string &id, bool bMake); - static void findAsync_l(const MediaInfo &info, const std::shared_ptr &session, bool retry, const function &cb); + //媒体注销 + bool unregist(); + //触发媒体事件 + void emitEvent(bool regist); + private: - string _strSchema; - string _strVhost; - string _strApp; - string _strId; + string _schema; + string _vhost; + string _app; + string _stream_id; std::weak_ptr _listener; - weak_ptr _track_source; - static SchemaVhostAppStreamMap g_mapMediaSrc; - static recursive_mutex g_mtxMediaSrc; }; ///缓存刷新策略类 @@ -174,7 +215,7 @@ public: } uint32_t getStamp(const RtmpPacket::Ptr &packet) { - return packet->timeStamp; + return packet->time_stamp; } bool isFlushAble(bool is_video, bool is_key, uint32_t new_stamp, int cache_size); @@ -208,6 +249,10 @@ public: } } + virtual void clearCache() { + _cache->clear(); + } + virtual void onFlush(std::shared_ptr &, bool key_pos) = 0; private: @@ -221,9 +266,9 @@ private: } private: + bool _key_pos = false; policy _policy; std::shared_ptr _cache; - bool _key_pos = false; }; } /* namespace mediakit */ diff --git a/src/Common/MultiMediaSourceMuxer.cpp b/src/Common/MultiMediaSourceMuxer.cpp index aeaa84bc..9ef5a678 100644 --- a/src/Common/MultiMediaSourceMuxer.cpp +++ b/src/Common/MultiMediaSourceMuxer.cpp @@ -8,36 +8,33 @@ * may be found in the AUTHORS file in the root of the source tree. */ +#include #include "MultiMediaSourceMuxer.h" namespace mediakit { +///////////////////////////////MultiMuxerPrivate////////////////////////////////// + MultiMuxerPrivate::~MultiMuxerPrivate() {} -MultiMuxerPrivate::MultiMuxerPrivate(const string &vhost, - const string &app, - const string &stream, - float dur_sec, - bool enable_rtsp, - bool enable_rtmp, - bool enable_hls, - bool enable_mp4) { +MultiMuxerPrivate::MultiMuxerPrivate(const string &vhost, const string &app, const string &stream, float dur_sec, + bool enable_rtsp, bool enable_rtmp, bool enable_hls, bool enable_mp4) { + _stream_url = vhost + " " + app + " " + stream; if (enable_rtmp) { _rtmp = std::make_shared(vhost, app, stream, std::make_shared(dur_sec)); - _enable_rtxp = true; } if (enable_rtsp) { _rtsp = std::make_shared(vhost, app, stream, std::make_shared(dur_sec)); - _enable_rtxp = true; } if (enable_hls) { - _hls = Recorder::createRecorder(Recorder::type_hls, vhost, app, stream); - _enable_record = true; + _hls = dynamic_pointer_cast(Recorder::createRecorder(Recorder::type_hls, vhost, app, stream)); } if (enable_mp4) { _mp4 = Recorder::createRecorder(Recorder::type_mp4, vhost, app, stream); - _enable_record = true; } + + _ts = std::make_shared(vhost, app, stream); + _fmp4 = std::make_shared(vhost, app, stream); } void MultiMuxerPrivate::resetTracks() { @@ -47,6 +44,12 @@ void MultiMuxerPrivate::resetTracks() { if (_rtsp) { _rtsp->resetTracks(); } + if (_ts) { + _ts->resetTracks(); + } + if (_fmp4) { + _fmp4->resetTracks(); + } //拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题 auto hls = _hls; @@ -61,24 +64,32 @@ void MultiMuxerPrivate::resetTracks() { } void MultiMuxerPrivate::setMediaListener(const std::weak_ptr &listener) { + _listener = listener; if (_rtmp) { _rtmp->setListener(listener); } - if (_rtsp) { _rtsp->setListener(listener); } - - auto hls_src = getHlsMediaSource(); - if (hls_src) { - hls_src->setListener(listener); + if (_ts) { + _ts->setListener(listener); + } + if (_fmp4) { + _fmp4->setListener(listener); + } + auto hls = _hls; + if (hls) { + hls->setListener(listener); } - _meida_listener = listener; } int MultiMuxerPrivate::totalReaderCount() const { - auto hls_src = getHlsMediaSource(); - return (_rtsp ? _rtsp->readerCount() : 0) + (_rtmp ? _rtmp->readerCount() : 0) + (hls_src ? hls_src->readerCount() : 0); + auto hls = _hls; + return (_rtsp ? _rtsp->readerCount() : 0) + + (_rtmp ? _rtmp->readerCount() : 0) + + (_ts ? _ts->readerCount() : 0) + + (_fmp4 ? _fmp4->readerCount() : 0) + + (hls ? hls->readerCount() : 0); } static std::shared_ptr makeRecorder(const vector &tracks, Recorder::type type, const string &custom_path, MediaSource &sender){ @@ -95,18 +106,16 @@ bool MultiMuxerPrivate::setupRecord(MediaSource &sender, Recorder::type type, bo case Recorder::type_hls : { if (start && !_hls) { //开始录制 - _hls = makeRecorder(getTracks(true), type, custom_path, sender); - auto hls_src = getHlsMediaSource(); - if (hls_src) { + auto hls = dynamic_pointer_cast(makeRecorder(getTracks(true), type, custom_path, sender)); + if (hls) { //设置HlsMediaSource的事件监听器 - hls_src->setListener(_meida_listener); - hls_src->setTrackSource(shared_from_this()); + hls->setListener(_listener); } + _hls = hls; } else if (!start && _hls) { //停止录制 _hls = nullptr; } - _enable_record = _hls || _mp4; return true; } case Recorder::type_mp4 : { @@ -117,7 +126,6 @@ bool MultiMuxerPrivate::setupRecord(MediaSource &sender, Recorder::type type, bo //停止录制 _mp4 = nullptr; } - _enable_record = _hls || _mp4; return true; } default : return false; @@ -146,7 +154,7 @@ void MultiMuxerPrivate::setTimeStamp(uint32_t stamp) { } void MultiMuxerPrivate::setTrackListener(Listener *listener) { - _listener = listener; + _track_listener = listener; } void MultiMuxerPrivate::onTrackReady(const Track::Ptr &track) { @@ -156,6 +164,12 @@ void MultiMuxerPrivate::onTrackReady(const Track::Ptr &track) { if (_rtsp) { _rtsp->addTrack(track); } + if (_ts) { + _ts->addTrack(track); + } + if (_fmp4) { + _fmp4->addTrack(track); + } //拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题 auto hls = _hls; @@ -169,7 +183,12 @@ void MultiMuxerPrivate::onTrackReady(const Track::Ptr &track) { } bool MultiMuxerPrivate::isEnabled(){ - return _enable_rtxp || _enable_record; + auto hls = _hls; + return (_rtmp ? _rtmp->isEnabled() : false) || + (_rtsp ? _rtsp->isEnabled() : false) || + (_ts ? _ts->isEnabled() : false) || + (_fmp4 ? _fmp4->isEnabled() : false) || + (hls ? hls->isEnabled() : false) || _mp4; } void MultiMuxerPrivate::onTrackFrame(const Frame::Ptr &frame) { @@ -179,6 +198,13 @@ void MultiMuxerPrivate::onTrackFrame(const Frame::Ptr &frame) { if (_rtsp) { _rtsp->inputFrame(frame); } + if (_ts) { + _ts->inputFrame(frame); + } + if (_fmp4) { + _fmp4->inputFrame(frame); + } + //拷贝智能指针,目的是为了防止跨线程调用设置录像相关api导致的线程竞争问题 //此处使用智能指针拷贝来确保线程安全,比互斥锁性能更优 auto hls = _hls; @@ -191,51 +217,70 @@ void MultiMuxerPrivate::onTrackFrame(const Frame::Ptr &frame) { } } +static string getTrackInfoStr(const TrackSource *track_src){ + _StrPrinter codec_info; + auto tracks = track_src->getTracks(true); + for (auto &track : tracks) { + auto codec_type = track->getTrackType(); + codec_info << track->getCodecName(); + switch (codec_type) { + case TrackAudio : { + auto audio_track = dynamic_pointer_cast(track); + codec_info << "[" + << audio_track->getAudioSampleRate() << "/" + << audio_track->getAudioChannel() << "/" + << audio_track->getAudioSampleBit() << "] "; + break; + } + case TrackVideo : { + auto video_track = dynamic_pointer_cast(track); + codec_info << "[" + << video_track->getVideoWidth() << "/" + << video_track->getVideoHeight() << "/" + << round(video_track->getVideoFps()) << "] "; + break; + } + default: + break; + } + } + return codec_info; +} + void MultiMuxerPrivate::onAllTrackReady() { if (_rtmp) { - _rtmp->setTrackSource(shared_from_this()); _rtmp->onAllTrackReady(); } if (_rtsp) { - _rtsp->setTrackSource(shared_from_this()); _rtsp->onAllTrackReady(); } - - auto hls_src = getHlsMediaSource(); - if (hls_src) { - hls_src->setTrackSource(shared_from_this()); + if (_fmp4) { + _fmp4->onAllTrackReady(); } - - if (_listener) { - _listener->onAllTrackReady(); + if (_track_listener) { + _track_listener->onAllTrackReady(); } + InfoL << "stream: " << _stream_url << " , codec info: " << getTrackInfoStr(this); } -MediaSource::Ptr MultiMuxerPrivate::getHlsMediaSource() const { - auto recorder = dynamic_pointer_cast(_hls); - if (recorder) { - return recorder->getMediaSource(); - } - return nullptr; -} - -///////////////////////////////////////////////////////////////// +///////////////////////////////MultiMediaSourceMuxer////////////////////////////////// MultiMediaSourceMuxer::~MultiMediaSourceMuxer() {} -MultiMediaSourceMuxer::MultiMediaSourceMuxer(const string &vhost, - const string &app, - const string &stream, - float dur_sec, - bool enable_rtsp, - bool enable_rtmp, - bool enable_hls, - bool enable_mp4) { + +MultiMediaSourceMuxer::MultiMediaSourceMuxer(const string &vhost, const string &app, const string &stream, float dur_sec, + bool enable_rtsp, bool enable_rtmp, bool enable_hls, bool enable_mp4) { _muxer.reset(new MultiMuxerPrivate(vhost, app, stream, dur_sec, enable_rtsp, enable_rtmp, enable_hls, enable_mp4)); + _muxer->setTrackListener(this); } void MultiMediaSourceMuxer::setMediaListener(const std::weak_ptr &listener) { - _muxer->setMediaListener(shared_from_this()); _listener = listener; + //拦截事件 + _muxer->setMediaListener(shared_from_this()); +} + +void MultiMediaSourceMuxer::setTrackListener(const std::weak_ptr &listener) { + _track_listener = listener; } int MultiMediaSourceMuxer::totalReaderCount() const { @@ -246,62 +291,57 @@ void MultiMediaSourceMuxer::setTimeStamp(uint32_t stamp) { _muxer->setTimeStamp(stamp); } -void MultiMediaSourceMuxer::setTrackListener(Listener *listener) { - _muxer->setTrackListener(listener); -} - -vector MultiMediaSourceMuxer::getTracks(bool trackReady) const { +vector MultiMediaSourceMuxer::getTracks(MediaSource &sender, bool trackReady) const { return _muxer->getTracks(trackReady); } -bool MultiMediaSourceMuxer::seekTo(MediaSource &sender, uint32_t ui32Stamp) { - auto listener = _listener.lock(); - if (!listener) { - return false; - } - return listener->seekTo(sender, ui32Stamp); -} - -bool MultiMediaSourceMuxer::close(MediaSource &sender, bool force) { - auto listener = _listener.lock(); - if (!listener) { - return false; - } - return listener->close(sender, force); -} - int MultiMediaSourceMuxer::totalReaderCount(MediaSource &sender) { auto listener = _listener.lock(); if (!listener) { - return _muxer->totalReaderCount(); + return totalReaderCount(); } return listener->totalReaderCount(sender); } -void MultiMediaSourceMuxer::onNoneReader(MediaSource &sender){ - auto listener = _listener.lock(); - if (!listener) { - MediaSourceEvent::onNoneReader(sender); - return; - } - listener->onNoneReader(sender); -} - -void MultiMediaSourceMuxer::onRegist(MediaSource &sender, bool regist){ - auto listener = _listener.lock(); - if (listener) { - listener->onRegist(sender, regist); - } -} - bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path) { - return _muxer->setupRecord(sender,type,start,custom_path); + return _muxer->setupRecord(sender, type, start, custom_path); } bool MultiMediaSourceMuxer::isRecording(MediaSource &sender, Recorder::type type) { return _muxer->isRecording(sender,type); } +void MultiMediaSourceMuxer::startSendRtp(MediaSource &sender, const string &dst_url, uint16_t dst_port, uint32_t ssrc, bool is_udp, const function &cb){ +#if defined(ENABLE_RTPPROXY) + auto ps_rtp_sender = std::make_shared(ssrc); + weak_ptr weak_self = shared_from_this(); + ps_rtp_sender->startSend(dst_url, dst_port, is_udp, [weak_self, ps_rtp_sender, cb](const SockException &ex) { + cb(ex); + auto strong_self = weak_self.lock(); + if (!strong_self || ex) { + return; + } + for (auto &track : strong_self->_muxer->getTracks(false)) { + ps_rtp_sender->addTrack(track); + } + ps_rtp_sender->addTrackCompleted(); + strong_self->_ps_rtp_sender = ps_rtp_sender; + }); +#else + cb(SockException(Err_other, "该功能未启用,编译时请打开ENABLE_RTPPROXY宏")); +#endif//ENABLE_RTPPROXY +} + +bool MultiMediaSourceMuxer::stopSendRtp(MediaSource &sender){ +#if defined(ENABLE_RTPPROXY) + if (_ps_rtp_sender) { + _ps_rtp_sender = nullptr; + return true; + } +#endif//ENABLE_RTPPROXY + return false; +} + void MultiMediaSourceMuxer::addTrack(const Track::Ptr &track) { _muxer->addTrack(track); } @@ -310,6 +350,14 @@ void MultiMediaSourceMuxer::addTrackCompleted() { _muxer->addTrackCompleted(); } +void MultiMediaSourceMuxer::onAllTrackReady(){ + _muxer->setMediaListener(shared_from_this()); + auto listener = _track_listener.lock(); + if(listener){ + listener->onAllTrackReady(); + } +} + void MultiMediaSourceMuxer::resetTracks() { _muxer->resetTracks(); } @@ -361,27 +409,36 @@ public: return _frame->getCodecId(); } private: - Frame::Ptr _frame; int64_t _dts; int64_t _pts; + Frame::Ptr _frame; }; -void MultiMediaSourceMuxer::inputFrame(const Frame::Ptr &frame) { - GET_CONFIG(bool,modify_stamp,General::kModifyStamp); - if(!modify_stamp){ - //未开启时间戳覆盖 - _muxer->inputFrame(frame); - }else{ +void MultiMediaSourceMuxer::inputFrame(const Frame::Ptr &frame_in) { + GET_CONFIG(bool, modify_stamp, General::kModifyStamp); + auto frame = frame_in; + if (modify_stamp) { //开启了时间戳覆盖 - FrameModifyStamp::Ptr new_frame = std::make_shared(frame,_stamp[frame->getTrackType()]); - //输入时间戳覆盖后的帧 - _muxer->inputFrame(new_frame); + frame = std::make_shared(frame, _stamp[frame->getTrackType()]); } + _muxer->inputFrame(frame); + +#if defined(ENABLE_RTPPROXY) + auto ps_rtp_sender = _ps_rtp_sender; + if (ps_rtp_sender) { + ps_rtp_sender->inputFrame(frame); + } +#endif //ENABLE_RTPPROXY + } bool MultiMediaSourceMuxer::isEnabled(){ +#if defined(ENABLE_RTPPROXY) + return (_muxer->isEnabled() || _ps_rtp_sender); +#else return _muxer->isEnabled(); +#endif //ENABLE_RTPPROXY } -}//namespace mediakit \ No newline at end of file +}//namespace mediakit diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index 216ba443..04833b06 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -10,14 +10,20 @@ #ifndef ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H #define ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H + +#include "Common/Stamp.h" +#include "Rtp/PSRtpSender.h" +#include "Record/Recorder.h" +#include "Record/HlsRecorder.h" +#include "Record/HlsMediaSource.h" #include "Rtsp/RtspMediaSourceMuxer.h" #include "Rtmp/RtmpMediaSourceMuxer.h" -#include "Record/Recorder.h" -#include "Record/HlsMediaSource.h" -#include "Record/HlsRecorder.h" +#include "TS/TSMediaSourceMuxer.h" +#include "FMP4/FMP4MediaSourceMuxer.h" + namespace mediakit{ -class MultiMuxerPrivate : public MediaSink , public std::enable_shared_from_this{ +class MultiMuxerPrivate : public MediaSink, public std::enable_shared_from_this{ public: friend class MultiMediaSourceMuxer; typedef std::shared_ptr Ptr; @@ -27,17 +33,12 @@ public: virtual ~Listener() = default; virtual void onAllTrackReady() = 0; }; - ~MultiMuxerPrivate() override ; -private: - MultiMuxerPrivate(const string &vhost, - const string &app, - const string &stream, - float dur_sec, - bool enable_rtsp, - bool enable_rtmp, - bool enable_hls, - bool enable_mp4); + ~MultiMuxerPrivate() override; + +private: + MultiMuxerPrivate(const string &vhost,const string &app, const string &stream,float dur_sec, + bool enable_rtsp, bool enable_rtmp, bool enable_hls, bool enable_mp4); void resetTracks() override; void setMediaListener(const std::weak_ptr &listener); int totalReaderCount() const; @@ -46,83 +47,67 @@ private: bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path); bool isRecording(MediaSource &sender, Recorder::type type); bool isEnabled(); -private: void onTrackReady(const Track::Ptr & track) override; void onTrackFrame(const Frame::Ptr &frame) override; void onAllTrackReady() override; - MediaSource::Ptr getHlsMediaSource() const; + private: + string _stream_url; + Listener *_track_listener = nullptr; RtmpMediaSourceMuxer::Ptr _rtmp; RtspMediaSourceMuxer::Ptr _rtsp; - MediaSinkInterface::Ptr _hls; + HlsRecorder::Ptr _hls; MediaSinkInterface::Ptr _mp4; - Listener *_listener = nullptr; - std::weak_ptr _meida_listener; - bool _enable_rtxp = false; - bool _enable_record = false; + TSMediaSourceMuxer::Ptr _ts; + FMP4MediaSourceMuxer::Ptr _fmp4; + std::weak_ptr _listener; }; -class MultiMediaSourceMuxer : public MediaSourceEvent, public MediaSinkInterface, public TrackSource, public std::enable_shared_from_this{ +class MultiMediaSourceMuxer : public MediaSourceEventInterceptor, public MediaSinkInterface, public MultiMuxerPrivate::Listener, public std::enable_shared_from_this{ public: typedef MultiMuxerPrivate::Listener Listener; typedef std::shared_ptr Ptr; ~MultiMediaSourceMuxer() override; - MultiMediaSourceMuxer(const string &vhost, - const string &app, - const string &stream, - float dur_sec = 0.0, - bool enable_rtsp = true, - bool enable_rtmp = true, - bool enable_hls = true, - bool enable_mp4 = false); + MultiMediaSourceMuxer(const string &vhost, const string &app, const string &stream, float dur_sec = 0.0, + bool enable_rtsp = true, bool enable_rtmp = true, bool enable_hls = true, bool enable_mp4 = false); /** * 设置事件监听器 - * @param listener + * @param listener 监听器 */ void setMediaListener(const std::weak_ptr &listener); + /** + * 随着Track就绪事件监听器 + * @param listener 事件监听器 + */ + void setTrackListener(const std::weak_ptr &listener); + /** * 返回总的消费者个数 - * @return */ int totalReaderCount() const; + /** + * 判断是否生效(是否正在转其他协议) + */ + bool isEnabled(); + /** * 设置MediaSource时间戳 * @param stamp 时间戳 */ void setTimeStamp(uint32_t stamp); - /** - * 随着Track就绪事件监听器 - * @param listener 事件监听器 - */ - void setTrackListener(Listener *listener); + /////////////////////////////////MediaSourceEvent override///////////////////////////////// /** * 获取所有Track * @param trackReady 是否筛选过滤未就绪的track * @return 所有Track */ - vector getTracks(bool trackReady = true) const override; - - /** - * 通知拖动进度条 - * @param sender 事件发送者 - * @param ui32Stamp 目标时间戳 - * @return 是否成功 - */ - bool seekTo(MediaSource &sender,uint32_t ui32Stamp) override; - - /** - * 通知停止流生成 - * @param sender 事件发送者 - * @param force 是否强制关闭 - * @return 成功与否 - */ - bool close(MediaSource &sender,bool force) override; + vector getTracks(MediaSource &sender, bool trackReady = true) const override; /** * 观看总人数 @@ -131,19 +116,6 @@ public: */ int totalReaderCount(MediaSource &sender) override; - /** - * 触发无人观看事件 - * @param sender 触发者 - */ - void onNoneReader(MediaSource &sender) override; - - /** - * 媒体注册注销事件 - * @param sender 触发者 - * @param regist 是否为注册事件 - */ - void onRegist(MediaSource &sender, bool regist) override; - /** * 设置录制状态 * @param type 录制类型 @@ -160,18 +132,35 @@ public: */ bool isRecording(MediaSource &sender, Recorder::type type) override; + /** + * 开始发送ps-rtp流 + * @param dst_url 目标ip或域名 + * @param dst_port 目标端口 + * @param ssrc rtp的ssrc + * @param is_udp 是否为udp + * @param cb 启动成功或失败回调 + */ + void startSendRtp(MediaSource &sender, const string &dst_url, uint16_t dst_port, uint32_t ssrc, bool is_udp, const function &cb) override; + + /** + * 停止ps-rtp发送 + * @return 是否成功 + */ + bool stopSendRtp(MediaSource &sender) override; + + /////////////////////////////////MediaSinkInterface override///////////////////////////////// + /** * 添加track,内部会调用Track的clone方法 * 只会克隆sps pps这些信息 ,而不会克隆Delegate相关关系 - * @param track + * @param track 添加音频或视频轨道 */ - void addTrack(const Track::Ptr & track) override; + void addTrack(const Track::Ptr &track) override; /** * 添加track完毕 - * @param track */ - void addTrackCompleted(); + void addTrackCompleted() override; /** * 重置track @@ -184,14 +173,20 @@ public: */ void inputFrame(const Frame::Ptr &frame) override; + /////////////////////////////////MultiMuxerPrivate::Listener override///////////////////////////////// + /** - * 判断是否生效(是否正在转其他协议) + * 所有track全部就绪 */ - bool isEnabled(); + void onAllTrackReady() override; + private: - MultiMuxerPrivate::Ptr _muxer; - std::weak_ptr _listener; Stamp _stamp[2]; + MultiMuxerPrivate::Ptr _muxer; + std::weak_ptr _track_listener; +#if defined(ENABLE_RTPPROXY) + PSRtpSender::Ptr _ps_rtp_sender; +#endif //ENABLE_RTPPROXY }; }//namespace mediakit diff --git a/src/Common/Stamp.cpp b/src/Common/Stamp.cpp index d9ab1823..595d147d 100644 --- a/src/Common/Stamp.cpp +++ b/src/Common/Stamp.cpp @@ -44,77 +44,79 @@ void Stamp::setPlayBack(bool playback) { void Stamp::syncTo(Stamp &other){ _sync_master = &other; - _sync_finished = false; } +//限制dts回退 void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp) { - revise_l(dts,pts,dts_out,pts_out,modifyStamp); - if(_sync_finished || modifyStamp || _playback){ - //自动生成时间戳或回放或同步完毕 - if(dts_out < 0) { dts_out = 0; } - if(pts_out < 0) { pts_out = 0; } + revise_l(dts, pts, dts_out, pts_out, modifyStamp); + if (_playback) { + //回放允许时间戳回退 return; } - if(_sync_master && _sync_master->_last_dts){ + if (dts_out < _last_dts_out) { + WarnL << "dts回退:" << dts_out << " < " << _last_dts_out; + dts_out = _last_dts_out; + pts_out = _last_pts_out; + return; + } + _last_dts_out = dts_out; + _last_pts_out = pts_out; +} + +//音视频时间戳同步 +void Stamp::revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp) { + revise_l2(dts, pts, dts_out, pts_out, modifyStamp); + if (!_sync_master || modifyStamp || _playback) { + //自动生成时间戳或回放或同步完毕 + return; + } + + if (_sync_master && _sync_master->_last_dts_in) { //音视频dts当前时间差 - int64_t dts_diff = _last_dts - _sync_master->_last_dts; - if(ABS(dts_diff) < 5000){ + int64_t dts_diff = _last_dts_in - _sync_master->_last_dts_in; + if (ABS(dts_diff) < 5000) { //如果绝对时间戳小于5秒,那么说明他们的起始时间戳是一致的,那么强制同步 - _last_relativeStamp = _relativeStamp; - _relativeStamp = _sync_master->_relativeStamp + dts_diff; + _relative_stamp = _sync_master->_relative_stamp + dts_diff; } //下次不用再强制同步 _sync_master = nullptr; } - - if (dts_out < 0 || dts_out < _last_relativeStamp) { - //相对时间戳小于0,或者小于上次的时间戳, - //那么说明是同步时间戳导致的,在这个过渡期内,我们一直返回上次的结果(目的是为了防止时间戳回退) - pts_out = _last_relativeStamp + (pts_out - dts_out); - dts_out = _last_relativeStamp; - } else if(!_sync_master){ - //音视频同步过渡期完毕 - _sync_finished = true; - } - - if(pts_out < 0){ - pts_out = dts_out; - } } -void Stamp::revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp) { - if(!pts){ +//求取相对时间戳 +void Stamp::revise_l2(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp) { + if (!pts) { //没有播放时间戳,使其赋值为解码时间戳 pts = dts; } - if(_playback){ + if (_playback) { //这是点播 dts_out = dts; pts_out = pts; - _relativeStamp = dts_out; - _last_dts = dts; + _relative_stamp = dts_out; + _last_dts_in = dts; return; } //pts和dts的差值 int pts_dts_diff = pts - dts; - if(_last_dts != dts){ + if (_last_dts_in != dts) { //时间戳发生变更 - if(modifyStamp){ + if (modifyStamp) { //内部自己生产时间戳 - _relativeStamp = _ticker.elapsedTime(); - }else{ - _relativeStamp += deltaStamp(dts); + _relative_stamp = _ticker.elapsedTime(); + } else { + _relative_stamp += deltaStamp(dts); } - _last_dts = dts; + _last_dts_in = dts; } - dts_out = _relativeStamp; + dts_out = _relative_stamp; //////////////以下是播放时间戳的计算////////////////// - if(ABS(pts_dts_diff) > MAX_CTS){ + if (ABS(pts_dts_diff) > MAX_CTS) { //如果差值太大,则认为由于回环导致时间戳错乱了 pts_dts_diff = 0; } @@ -123,11 +125,11 @@ void Stamp::revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_ou } void Stamp::setRelativeStamp(int64_t relativeStamp) { - _relativeStamp = relativeStamp; + _relative_stamp = relativeStamp; } int64_t Stamp::getRelativeStamp() const { - return _relativeStamp; + return _relative_stamp; } bool DtsGenerator::getDts(uint32_t pts, uint32_t &dts){ diff --git a/src/Common/Stamp.h b/src/Common/Stamp.h index cf374277..30d41157 100644 --- a/src/Common/Stamp.h +++ b/src/Common/Stamp.h @@ -29,6 +29,7 @@ public: * @return 时间戳增量 */ int64_t deltaStamp(int64_t stamp); + private: int64_t _last_stamp = 0; }; @@ -41,7 +42,7 @@ public: ~Stamp() = default; /** - * 修正时间戳 + * 求取相对时间戳,同时实现了音视频同步、限制dts回退等功能 * @param dts 输入dts,如果为0则根据系统时间戳生成 * @param pts 输入pts,如果为0则等于dts * @param dts_out 输出dts @@ -75,15 +76,20 @@ public: void syncTo(Stamp &other); private: + //主要实现音视频时间戳同步功能 void revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp = false); + + //主要实现获取相对时间戳功能 + void revise_l2(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp = false); + private: - int64_t _relativeStamp = 0; - int64_t _last_relativeStamp = 0; - int64_t _last_dts = 0; + int64_t _relative_stamp = 0; + int64_t _last_dts_in = 0; + int64_t _last_dts_out = 0; + int64_t _last_pts_out = 0; SmoothTicker _ticker; bool _playback = false; Stamp *_sync_master = nullptr; - bool _sync_finished = true; }; //dts生成器, @@ -93,8 +99,10 @@ public: DtsGenerator() = default; ~DtsGenerator() = default; bool getDts(uint32_t pts, uint32_t &dts); + private: bool getDts_l(uint32_t pts, uint32_t &dts); + private: uint32_t _dts_pts_offset = 0; uint32_t _last_dts = 0; diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 90e171ab..f26c504d 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -40,6 +40,7 @@ bool loadIniConfig(const char *ini_path){ namespace Broadcast { const string kBroadcastMediaChanged = "kBroadcastMediaChanged"; const string kBroadcastRecordMP4 = "kBroadcastRecordMP4"; +const string kBroadcastRecordTs = "kBroadcastRecoredTs"; const string kBroadcastHttpRequest = "kBroadcastHttpRequest"; const string kBroadcastHttpAccess = "kBroadcastHttpAccess"; const string kBroadcastOnGetRtspRealm = "kBroadcastOnGetRtspRealm"; @@ -63,7 +64,6 @@ const string kMaxStreamWaitTimeMS = GENERAL_FIELD"maxStreamWaitMS"; const string kEnableVhost = GENERAL_FIELD"enableVhost"; const string kAddMuteAudio = GENERAL_FIELD"addMuteAudio"; const string kResetWhenRePlay = GENERAL_FIELD"resetWhenRePlay"; -const string kPublishToRtxp = GENERAL_FIELD"publishToRtxp"; const string kPublishToHls = GENERAL_FIELD"publishToHls"; const string kPublishToMP4 = GENERAL_FIELD"publishToMP4"; const string kMergeWriteMS = GENERAL_FIELD"mergeWriteMS"; @@ -76,7 +76,6 @@ onceToken token([](){ mINI::Instance()[kEnableVhost] = 0; mINI::Instance()[kAddMuteAudio] = 1; mINI::Instance()[kResetWhenRePlay] = 1; - mINI::Instance()[kPublishToRtxp] = 1; mINI::Instance()[kPublishToHls] = 1; mINI::Instance()[kPublishToMP4] = 0; mINI::Instance()[kMergeWriteMS] = 0; @@ -253,6 +252,8 @@ const string kSegmentRetain = HLS_FIELD"segRetain"; const string kFileBufSize = HLS_FIELD"fileBufSize"; //录制文件路径 const string kFilePath = HLS_FIELD"filePath"; +// 是否广播 ts 切片完成通知 +const string kBroadcastRecordTs = HLS_FIELD"broadcastRecordTs"; onceToken token([](){ mINI::Instance()[kSegmentDuration] = 2; @@ -260,6 +261,7 @@ onceToken token([](){ mINI::Instance()[kSegmentRetain] = 5; mINI::Instance()[kFileBufSize] = 64 * 1024; mINI::Instance()[kFilePath] = "./www"; + mINI::Instance()[kBroadcastRecordTs] = false; },nullptr); } //namespace Hls diff --git a/src/Common/config.h b/src/Common/config.h index 6832aec1..c352b1f7 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -47,6 +47,8 @@ bool loadIniConfig(const char *ini_path = nullptr); #define RTSP_SCHEMA "rtsp" #define RTMP_SCHEMA "rtmp" #define HLS_SCHEMA "hls" +#define TS_SCHEMA "ts" +#define FMP4_SCHEMA "fmp4" #define DEFAULT_VHOST "__defaultVhost__" ////////////广播名称/////////// @@ -58,7 +60,11 @@ extern const string kBroadcastMediaChanged; //录制mp4文件成功后广播 extern const string kBroadcastRecordMP4; -#define BroadcastRecordMP4Args const MP4Info &info +#define BroadcastRecordMP4Args const RecordInfo &info + +// 录制 ts 文件后广播 +extern const string kBroadcastRecordTs; +#define BroadcastRecordTsArgs const RecordInfo &info //收到http api请求广播 extern const string kBroadcastHttpRequest; @@ -86,8 +92,7 @@ extern const string kBroadcastOnRtspAuth; //如果errMessage为空则代表鉴权成功 //enableHls: 是否允许转换hls //enableMP4: 是否运行MP4录制 -//enableRtxp: rtmp推流时是否运行转rtsp;rtsp推流时,是否允许转rtmp -typedef std::function PublishAuthInvoker; +typedef std::function PublishAuthInvoker; //收到rtsp/rtmp推流事件广播,通过该事件控制推流鉴权 extern const string kBroadcastMediaPublish; @@ -165,8 +170,6 @@ extern const string kAddMuteAudio; //拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始, //如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写) extern const string kResetWhenRePlay; -//是否默认推流时转换成rtsp或rtmp,hook接口(on_publish)中可以覆盖该设置 -extern const string kPublishToRtxp ; //是否默认推流时转换成hls,hook接口(on_publish)中可以覆盖该设置 extern const string kPublishToHls ; //是否默认推流时mp4录像,hook接口(on_publish)中可以覆盖该设置 @@ -284,6 +287,8 @@ extern const string kSegmentRetain; extern const string kFileBufSize; //录制文件路径 extern const string kFilePath; +// 是否广播 ts 切片完成通知 +extern const string kBroadcastRecordTs; } //namespace Hls ////////////Rtp代理相关配置/////////// diff --git a/src/Extension/AAC.cpp b/src/Extension/AAC.cpp index b034a462..1a820e42 100644 --- a/src/Extension/AAC.cpp +++ b/src/Extension/AAC.cpp @@ -92,6 +92,16 @@ static void parseAacConfig(const string &config, AdtsHeader &adts) { adts.no_raw_data_blocks_in_frame = 0; } +int getAacFrameLength(const uint8_t *data, int bytes) { + uint16_t len; + if (bytes < 7) return -1; + if (0xFF != data[0] || 0xF0 != (data[1] & 0xF0)) { + return -1; + } + len = ((uint16_t) (data[3] & 0x03) << 11) | ((uint16_t) data[4] << 3) | ((uint16_t) (data[5] >> 5) & 0x07); + return len; +} + string makeAacConfig(const uint8_t *hex, int length){ #ifndef ENABLE_MP4 if (!(hex[0] == 0xFF && (hex[1] & 0xF0) == 0xF0)) { @@ -134,7 +144,7 @@ int dumpAacConfig(const string &config, int length, uint8_t *out, int out_size) #ifndef ENABLE_MP4 AdtsHeader header; parseAacConfig(config, header); - header.aac_frame_length = length; + header.aac_frame_length = ADTS_HEADER_LEN + length; dumpAdtsHeader(header, out); return ADTS_HEADER_LEN; #else diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index aa9dbb1e..243f919f 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -18,44 +18,10 @@ namespace mediakit{ string makeAacConfig(const uint8_t *hex, int length); +int getAacFrameLength(const uint8_t *hex, int length); int dumpAacConfig(const string &config, int length, uint8_t *out, int out_size); bool parseAacConfig(const string &config, int &samplerate, int &channels); -/** - * aac帧,包含adts头 - */ -class AACFrame : public FrameImp { -public: - typedef std::shared_ptr Ptr; - AACFrame(){ - _codecid = CodecAAC; - } -}; - -class AACFrameNoCacheAble : public FrameFromPtr { -public: - typedef std::shared_ptr Ptr; - - AACFrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts,uint32_t pts = 0,int prefix_size = ADTS_HEADER_LEN){ - _ptr = ptr; - _size = size; - _dts = dts; - _prefix_size = prefix_size; - } - - CodecId getCodecId() const override{ - return CodecAAC; - } - - bool keyFrame() const override { - return false; - } - - bool configFrame() const override{ - return false; - } -}; - /** * aac音频通道 */ @@ -135,6 +101,28 @@ public: * @param frame 数据帧 */ void inputFrame(const Frame::Ptr &frame) override{ + if (frame->prefixSize()) { + //有adts头,尝试分帧 + auto ptr = frame->data(); + auto end = frame->data() + frame->size(); + while (ptr < end) { + auto frame_len = getAacFrameLength((uint8_t *) ptr, end - ptr); + if (frame_len < ADTS_HEADER_LEN) { + break; + } + + auto sub_frame = std::make_shared >(frame, (char *) ptr, frame_len, ADTS_HEADER_LEN); + ptr += frame_len; + sub_frame->setCodecId(CodecAAC); + inputFrame_l(sub_frame); + } + } else { + inputFrame_l(frame); + } + } + +private: + void inputFrame_l(const Frame::Ptr &frame) { if (_cfg.empty()) { //未获取到aac_cfg信息 if (frame->prefixSize()) { @@ -151,7 +139,6 @@ public: AudioTrack::inputFrame(frame); } } -private: /** * 解析2个字节的aac配置 */ diff --git a/src/Extension/AACRtmp.cpp b/src/Extension/AACRtmp.cpp index c2e5de6c..759687d7 100644 --- a/src/Extension/AACRtmp.cpp +++ b/src/Extension/AACRtmp.cpp @@ -21,29 +21,30 @@ static string getAacCfg(const RtmpPacket &thiz) { if (!thiz.isCfgFrame()) { return ret; } - if (thiz.strBuf.size() < 4) { + if (thiz.buffer.size() < 4) { WarnL << "bad aac cfg!"; return ret; } - ret = thiz.strBuf.substr(2); + ret = thiz.buffer.substr(2); return ret; } -bool AACRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt, bool) { +void AACRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt) { if (pkt->isCfgFrame()) { _aac_cfg = getAacCfg(*pkt); onGetAAC(nullptr, 0, 0); - return false; + return; } if (!_aac_cfg.empty()) { - onGetAAC(pkt->strBuf.data() + 2, pkt->strBuf.size() - 2, pkt->timeStamp); + onGetAAC(pkt->buffer.data() + 2, pkt->buffer.size() - 2, pkt->time_stamp); } - return false; } void AACRtmpDecoder::onGetAAC(const char* data, int len, uint32_t stamp) { - auto frame = ResourcePoolHelper::obtainObj(); + auto frame = ResourcePoolHelper::obtainObj(); + frame->_codec_id = CodecAAC; + //生成adts头 char adts_header[32] = {0}; auto size = dumpAacConfig(_aac_cfg, len, (uint8_t *) adts_header, sizeof(adts_header)); @@ -95,43 +96,43 @@ void AACRtmpEncoder::inputFrame(const Frame::Ptr &frame) { if(!_aac_cfg.empty()){ RtmpPacket::Ptr rtmpPkt = ResourcePoolHelper::obtainObj(); - rtmpPkt->strBuf.clear(); + rtmpPkt->buffer.clear(); //header uint8_t is_config = false; - rtmpPkt->strBuf.push_back(_audio_flv_flags); - rtmpPkt->strBuf.push_back(!is_config); + rtmpPkt->buffer.push_back(_audio_flv_flags); + rtmpPkt->buffer.push_back(!is_config); //aac data - rtmpPkt->strBuf.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize()); + rtmpPkt->buffer.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize()); - rtmpPkt->bodySize = rtmpPkt->strBuf.size(); - rtmpPkt->chunkId = CHUNK_AUDIO; - rtmpPkt->streamId = STREAM_MEDIA; - rtmpPkt->timeStamp = frame->dts(); - rtmpPkt->typeId = MSG_AUDIO; - RtmpCodec::inputRtmp(rtmpPkt, false); + rtmpPkt->body_size = rtmpPkt->buffer.size(); + rtmpPkt->chunk_id = CHUNK_AUDIO; + rtmpPkt->stream_index = STREAM_MEDIA; + rtmpPkt->time_stamp = frame->dts(); + rtmpPkt->type_id = MSG_AUDIO; + RtmpCodec::inputRtmp(rtmpPkt); } } void AACRtmpEncoder::makeAudioConfigPkt() { _audio_flv_flags = getAudioRtmpFlags(std::make_shared(_aac_cfg)); RtmpPacket::Ptr rtmpPkt = ResourcePoolHelper::obtainObj(); - rtmpPkt->strBuf.clear(); + rtmpPkt->buffer.clear(); //header uint8_t is_config = true; - rtmpPkt->strBuf.push_back(_audio_flv_flags); - rtmpPkt->strBuf.push_back(!is_config); + rtmpPkt->buffer.push_back(_audio_flv_flags); + rtmpPkt->buffer.push_back(!is_config); //aac config - rtmpPkt->strBuf.append(_aac_cfg); + rtmpPkt->buffer.append(_aac_cfg); - rtmpPkt->bodySize = rtmpPkt->strBuf.size(); - rtmpPkt->chunkId = CHUNK_AUDIO; - rtmpPkt->streamId = STREAM_MEDIA; - rtmpPkt->timeStamp = 0; - rtmpPkt->typeId = MSG_AUDIO; - RtmpCodec::inputRtmp(rtmpPkt, false); + rtmpPkt->body_size = rtmpPkt->buffer.size(); + rtmpPkt->chunk_id = CHUNK_AUDIO; + rtmpPkt->stream_index = STREAM_MEDIA; + rtmpPkt->time_stamp = 0; + rtmpPkt->type_id = MSG_AUDIO; + RtmpCodec::inputRtmp(rtmpPkt); } }//namespace mediakit \ No newline at end of file diff --git a/src/Extension/AACRtmp.h b/src/Extension/AACRtmp.h index dc7f0d62..741da220 100644 --- a/src/Extension/AACRtmp.h +++ b/src/Extension/AACRtmp.h @@ -19,7 +19,7 @@ namespace mediakit{ /** * aac Rtmp转adts类 */ -class AACRtmpDecoder : public RtmpCodec , public ResourcePoolHelper { +class AACRtmpDecoder : public RtmpCodec , public ResourcePoolHelper { public: typedef std::shared_ptr Ptr; @@ -28,10 +28,9 @@ public: /** * 输入Rtmp并解码 - * @param Rtmp Rtmp数据包 - * @param key_pos 此参数内部强制转换为false,请忽略之 + * @param rtmp Rtmp数据包 */ - bool inputRtmp(const RtmpPacket::Ptr &Rtmp, bool key_pos = false) override; + void inputRtmp(const RtmpPacket::Ptr &rtmp) override; CodecId getCodecId() const override{ return CodecAAC; diff --git a/src/Extension/AACRtp.cpp b/src/Extension/AACRtp.cpp index 08f8307c..8790cfeb 100644 --- a/src/Extension/AACRtp.cpp +++ b/src/Extension/AACRtp.cpp @@ -9,7 +9,6 @@ */ #include "AACRtp.h" -#define AAC_MAX_FRAME_SIZE (2 * 1024) namespace mediakit{ @@ -68,60 +67,73 @@ AACRtpDecoder::AACRtpDecoder(const Track::Ptr &track) { } else { _aac_cfg = aacTrack->getAacCfg(); } - _frame = obtainFrame(); + obtainFrame(); } AACRtpDecoder::AACRtpDecoder() { - _frame = obtainFrame(); + obtainFrame(); } -AACFrame::Ptr AACRtpDecoder::obtainFrame() { +void AACRtpDecoder::obtainFrame() { //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 - auto frame = ResourcePoolHelper::obtainObj(); - frame->_prefix_size = 0; - frame->_buffer.clear(); - return frame; + _frame = ResourcePoolHelper::obtainObj(); + _frame->_prefix_size = 0; + _frame->_buffer.clear(); + _frame->_codec_id = CodecAAC; } bool AACRtpDecoder::inputRtp(const RtpPacket::Ptr &rtppack, bool key_pos) { //rtp数据开始部分 uint8_t *ptr = (uint8_t *) rtppack->data() + rtppack->offset; //rtp数据末尾 - const uint8_t *end = (uint8_t *) rtppack->data() + rtppack->size(); - + uint8_t *end = (uint8_t *) rtppack->data() + rtppack->size(); //首2字节表示Au-Header的个数,单位bit,所以除以16得到Au-Header个数 - const uint16_t au_header_count = ((ptr[0] << 8) | ptr[1]) >> 4; - //忽略Au-Header区 - ptr += 2 + au_header_count * 2; + uint16_t au_header_count = ((ptr[0] << 8) | ptr[1]) >> 4; + //记录au_header起始指针 + uint8_t *au_header_ptr = ptr + 2; + ptr = au_header_ptr + au_header_count * 2; - while (ptr < end) { - auto size = (uint32_t) (end - ptr); - if (size > AAC_MAX_FRAME_SIZE) { - size = AAC_MAX_FRAME_SIZE; + if (end < ptr) { + //数据不够 + return false; + } + + if (!_last_dts) { + //记录第一个时间戳 + _last_dts = rtppack->timeStamp; + } + + //每个audio unit时间戳增量 + auto dts_inc = (rtppack->timeStamp - _last_dts) / au_header_count; + if (dts_inc < 0 && dts_inc > 100) { + //时间戳增量异常,忽略 + dts_inc = 0; + } + + for (int i = 0; i < au_header_count; ++i) { + // 之后的2字节是AU_HEADER,其中高13位表示一帧AAC负载的字节长度,低3位无用 + uint16_t size = ((au_header_ptr[0] << 8) | au_header_ptr[1]) >> 3; + if (ptr + size > end) { + //数据不够 + break; } - if (_frame->size() + size > AAC_MAX_FRAME_SIZE) { - //数据太多了,先清空 + + if (size) { + //设置aac数据 + _frame->_buffer.assign((char *) ptr, size); + //设置当前audio unit时间戳 + _frame->_dts = _last_dts + i * dts_inc; + ptr += size; + au_header_ptr += 2; flushData(); } - //追加aac数据 - _frame->_buffer.append((char *) ptr, size); - _frame->_dts = rtppack->timeStamp; - ptr += size; - } - - if (rtppack->mark) { - //最后一个rtp分片 - flushData(); } + //记录上次时间戳 + _last_dts = rtppack->timeStamp; return false; } void AACRtpDecoder::flushData() { - if (_frame->_buffer.empty()) { - //没有有效数据 - return; - } - //插入adts头 char adts_header[32] = {0}; auto size = dumpAacConfig(_aac_cfg, _frame->_buffer.size(), (uint8_t *) adts_header, sizeof(adts_header)); @@ -131,11 +143,7 @@ void AACRtpDecoder::flushData() { _frame->_prefix_size = size; } RtpCodec::inputFrame(_frame); - _frame = obtainFrame(); + obtainFrame(); } - -}//namespace mediakit - - - +}//namespace mediakit \ No newline at end of file diff --git a/src/Extension/AACRtp.h b/src/Extension/AACRtp.h index 7b646204..7e284c8e 100644 --- a/src/Extension/AACRtp.h +++ b/src/Extension/AACRtp.h @@ -17,7 +17,7 @@ namespace mediakit{ /** * aac rtp转adts类 */ -class AACRtpDecoder : public RtpCodec , public ResourcePoolHelper { +class AACRtpDecoder : public RtpCodec , public ResourcePoolHelper { public: typedef std::shared_ptr Ptr; @@ -39,12 +39,13 @@ protected: AACRtpDecoder(); private: - AACFrame::Ptr obtainFrame(); + void obtainFrame(); void flushData(); private: - AACFrame::Ptr _frame; + FrameImp::Ptr _frame; string _aac_cfg; + uint32_t _last_dts = 0; }; diff --git a/src/Extension/CommonRtmp.cpp b/src/Extension/CommonRtmp.cpp new file mode 100644 index 00000000..41fec6ec --- /dev/null +++ b/src/Extension/CommonRtmp.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "CommonRtmp.h" + +namespace mediakit{ + +CommonRtmpDecoder::CommonRtmpDecoder(CodecId codec) { + _codec = codec; + obtainFrame(); +} + +CodecId CommonRtmpDecoder::getCodecId() const { + return _codec; +} + +void CommonRtmpDecoder::obtainFrame() { + //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 + _frame = ResourcePoolHelper::obtainObj(); + _frame->_buffer.clear(); + _frame->_codec_id = _codec; + _frame->_prefix_size = 0; +} + +void CommonRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &rtmp) { + //拷贝负载 + _frame->_buffer.assign(rtmp->buffer.data() + 1, rtmp->buffer.size() - 1); + _frame->_dts = rtmp->time_stamp; + //写入环形缓存 + RtmpCodec::inputFrame(_frame); + //创建下一帧 + obtainFrame(); +} + +///////////////////////////////////////////////////////////////////////////////////// + +CommonRtmpEncoder::CommonRtmpEncoder(const Track::Ptr &track) : CommonRtmpDecoder(track->getCodecId()) { + _audio_flv_flags = getAudioRtmpFlags(track); +} + +void CommonRtmpEncoder::inputFrame(const Frame::Ptr &frame) { + if (!_audio_flv_flags) { + return; + } + RtmpPacket::Ptr rtmp = ResourcePoolHelper::obtainObj(); + rtmp->buffer.clear(); + //header + rtmp->buffer.push_back(_audio_flv_flags); + //data + rtmp->buffer.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize()); + rtmp->body_size = rtmp->buffer.size(); + rtmp->chunk_id = CHUNK_AUDIO; + rtmp->stream_index = STREAM_MEDIA; + rtmp->time_stamp = frame->dts(); + rtmp->type_id = MSG_AUDIO; + RtmpCodec::inputRtmp(rtmp); +} + +}//namespace mediakit \ No newline at end of file diff --git a/src/Extension/CommonRtmp.h b/src/Extension/CommonRtmp.h new file mode 100644 index 00000000..1f676507 --- /dev/null +++ b/src/Extension/CommonRtmp.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_COMMONRTMP_H +#define ZLMEDIAKIT_COMMONRTMP_H + +#include "Frame.h" +#include "Rtmp/RtmpCodec.h" + +namespace mediakit{ + +/** + * 通用 rtmp解码类 + */ +class CommonRtmpDecoder : public RtmpCodec , public ResourcePoolHelper { +public: + typedef std::shared_ptr Ptr; + + ~CommonRtmpDecoder() override {} + + /** + * 构造函数 + * @param codec 编码id + */ + CommonRtmpDecoder(CodecId codec); + + /** + * 返回编码类型ID + */ + CodecId getCodecId() const override; + + /** + * 输入Rtmp并解码 + * @param rtmp Rtmp数据包 + */ + void inputRtmp(const RtmpPacket::Ptr &rtmp) override; + +private: + void obtainFrame(); + +private: + CodecId _codec; + FrameImp::Ptr _frame; +}; + +/** + * 通用 rtmp编码类 + */ +class CommonRtmpEncoder : public CommonRtmpDecoder , public ResourcePoolHelper { +public: + typedef std::shared_ptr Ptr; + + CommonRtmpEncoder(const Track::Ptr &track); + ~CommonRtmpEncoder() override{} + + /** + * 输入帧数据 + */ + void inputFrame(const Frame::Ptr &frame) override; + +private: + uint8_t _audio_flv_flags = 0; +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_COMMONRTMP_H diff --git a/src/Extension/CommonRtp.cpp b/src/Extension/CommonRtp.cpp new file mode 100644 index 00000000..14fd37f7 --- /dev/null +++ b/src/Extension/CommonRtp.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "CommonRtp.h" + +CommonRtpDecoder::CommonRtpDecoder(CodecId codec, int max_frame_size ){ + _codec = codec; + _max_frame_size = max_frame_size; + obtainFrame(); +} + +CodecId CommonRtpDecoder::getCodecId() const { + return _codec; +} + +void CommonRtpDecoder::obtainFrame() { + _frame = ResourcePoolHelper::obtainObj(); + _frame->_buffer.clear(); + _frame->_prefix_size = 0; + _frame->_dts = 0; + _frame->_codec_id = _codec; +} + +bool CommonRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool){ + auto payload = rtp->data() + rtp->offset; + auto size = rtp->size() - rtp->offset; + if (size <= 0) { + //无实际负载 + return false; + } + + if (_frame->_dts != rtp->timeStamp || _frame->_buffer.size() > _max_frame_size) { + //时间戳发生变化或者缓存超过MAX_FRAME_SIZE,则清空上帧数据 + if (!_frame->_buffer.empty()) { + //有有效帧,则输出 + RtpCodec::inputFrame(_frame); + } + + //新的一帧数据 + obtainFrame(); + _frame->_dts = rtp->timeStamp; + _drop_flag = false; + } else if (_last_seq != 0 && (uint16_t)(_last_seq + 1) != rtp->sequence) { + //时间戳未发生变化,但是seq却不连续,说明中间rtp丢包了,那么整帧应该废弃 + WarnL << "rtp丢包:" << _last_seq << " -> " << rtp->sequence; + _drop_flag = true; + _frame->_buffer.clear(); + } + + if (!_drop_flag) { + _frame->_buffer.append(payload, size); + } + + _last_seq = rtp->sequence; + return false; +} + +//////////////////////////////////////////////////////////////// + +CommonRtpEncoder::CommonRtpEncoder(CodecId codec, uint32_t ssrc, uint32_t mtu_size, + uint32_t sample_rate, uint8_t payload_type, uint8_t interleaved) + : CommonRtpDecoder(codec), RtpInfo(ssrc, mtu_size, sample_rate, payload_type, interleaved) { +} + +void CommonRtpEncoder::inputFrame(const Frame::Ptr &frame){ + GET_CONFIG(uint32_t, cycleMS, Rtp::kCycleMS); + auto stamp = frame->dts() % cycleMS; + auto ptr = frame->data() + frame->prefixSize(); + auto len = frame->size() - frame->prefixSize(); + auto remain_size = len; + const auto max_rtp_size = _ui32MtuSize - 20; + + while (remain_size > 0) { + auto rtp_size = remain_size > max_rtp_size ? max_rtp_size : remain_size; + RtpCodec::inputRtp(makeRtp(getTrackType(), ptr, rtp_size, false, stamp), false); + ptr += rtp_size; + remain_size -= rtp_size; + } +} \ No newline at end of file diff --git a/src/Extension/CommonRtp.h b/src/Extension/CommonRtp.h new file mode 100644 index 00000000..4345c641 --- /dev/null +++ b/src/Extension/CommonRtp.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_COMMONRTP_H +#define ZLMEDIAKIT_COMMONRTP_H + +#include "Frame.h" +#include "Rtsp/RtpCodec.h" + +namespace mediakit{ + +/** + * 通用 rtp解码类 + */ +class CommonRtpDecoder : public RtpCodec, public ResourcePoolHelper { +public: + typedef std::shared_ptr Ptr; + + ~CommonRtpDecoder() override {} + + /** + * 构造函数 + * @param codec 编码id + * @param max_frame_size 允许的最大帧大小 + */ + CommonRtpDecoder(CodecId codec, int max_frame_size = 2 * 1024); + + /** + * 返回编码类型ID + */ + CodecId getCodecId() const override; + + /** + * 输入rtp并解码 + * @param rtp rtp数据包 + * @param key_pos 此参数内部强制转换为false,请忽略之 + */ + bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = false) override; + +private: + void obtainFrame(); + +private: + bool _drop_flag = false; + uint16_t _last_seq = 0; + int _max_frame_size; + CodecId _codec; + FrameImp::Ptr _frame; +}; + +/** + * 通用 rtp编码类 + */ +class CommonRtpEncoder : public CommonRtpDecoder, public RtpInfo { +public: + typedef std::shared_ptr Ptr; + + ~CommonRtpEncoder() override {} + + /** + * 构造函数 + * @param codec 编码类型 + * @param ssrc ssrc + * @param mtu_size mtu 大小 + * @param sample_rate 采样率 + * @param payload_type pt类型 + * @param interleaved rtsp interleaved 值 + */ + CommonRtpEncoder(CodecId codec, uint32_t ssrc, uint32_t mtu_size, uint32_t sample_rate, uint8_t payload_type, uint8_t interleaved); + + /** + * 输入帧数据并编码成rtp + */ + void inputFrame(const Frame::Ptr &frame) override; +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_COMMONRTP_H diff --git a/src/Extension/Factory.cpp b/src/Extension/Factory.cpp index 92771f38..fba7109b 100644 --- a/src/Extension/Factory.cpp +++ b/src/Extension/Factory.cpp @@ -13,11 +13,13 @@ #include "H264Rtmp.h" #include "H265Rtmp.h" #include "AACRtmp.h" -#include "G711Rtmp.h" +#include "CommonRtmp.h" #include "H264Rtp.h" #include "AACRtp.h" -#include "G711Rtp.h" #include "H265Rtp.h" +#include "CommonRtp.h" +#include "Opus.h" +#include "G711.h" #include "Common/Parser.h" namespace mediakit{ @@ -42,6 +44,10 @@ Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) { return std::make_shared(aac_cfg); } + if (strcasecmp(track->_codec.data(), "opus") == 0) { + return std::make_shared(); + } + if (strcasecmp(track->_codec.data(), "PCMA") == 0) { return std::make_shared(CodecG711A, track->_samplerate, track->_channel, 16); } @@ -114,11 +120,12 @@ RtpCodec::Ptr Factory::getRtpEncoderBySdp(const Sdp::Ptr &sdp) { auto interleaved = sdp->getTrackType() * 2; auto codec_id = sdp->getCodecId(); switch (codec_id){ - case CodecH264 : return std::make_shared(ssrc,mtu,sample_rate,pt,interleaved); - case CodecH265 : return std::make_shared(ssrc,mtu,sample_rate,pt,interleaved); - case CodecAAC : return std::make_shared(ssrc,mtu,sample_rate,pt,interleaved); + case CodecH264 : return std::make_shared(ssrc, mtu, sample_rate, pt, interleaved); + case CodecH265 : return std::make_shared(ssrc, mtu, sample_rate, pt, interleaved); + case CodecAAC : return std::make_shared(ssrc, mtu, sample_rate, pt, interleaved); + case CodecOpus : case CodecG711A : - case CodecG711U : return std::make_shared(codec_id, ssrc, mtu, sample_rate, pt, interleaved); + case CodecG711U : return std::make_shared(codec_id, ssrc, mtu, sample_rate, pt, interleaved); default : WarnL << "暂不支持该CodecId:" << codec_id; return nullptr; } } @@ -128,8 +135,9 @@ RtpCodec::Ptr Factory::getRtpDecoderByTrack(const Track::Ptr &track) { case CodecH264 : return std::make_shared(); case CodecH265 : return std::make_shared(); case CodecAAC : return std::make_shared(track->clone()); + case CodecOpus : case CodecG711A : - case CodecG711U : return std::make_shared(track->getCodecId()); + case CodecG711U : return std::make_shared(track->getCodecId()); default : WarnL << "暂不支持该CodecId:" << track->getCodecName(); return nullptr; } } @@ -137,40 +145,35 @@ RtpCodec::Ptr Factory::getRtpDecoderByTrack(const Track::Ptr &track) { /////////////////////////////rtmp相关/////////////////////////////////////////// static CodecId getVideoCodecIdByAmf(const AMFValue &val){ - if (val.type() == AMF_STRING){ + if (val.type() == AMF_STRING) { auto str = val.as_string(); - if(str == "avc1"){ + if (str == "avc1") { return CodecH264; } - if(str == "mp4a"){ - return CodecAAC; - } - if(str == "hev1" || str == "hvc1"){ + if (str == "hev1" || str == "hvc1") { return CodecH265; } - WarnL << "暂不支持该Amf:" << str; + WarnL << "暂不支持该视频Amf:" << str; return CodecInvalid; } - if (val.type() != AMF_NULL){ + if (val.type() != AMF_NULL) { auto type_id = val.as_integer(); - switch (type_id){ - case FLV_CODEC_H264: return CodecH264; - case FLV_CODEC_AAC: return CodecAAC; - case FLV_CODEC_H265: return CodecH265; - default : WarnL << "暂不支持该Amf:" << type_id; return CodecInvalid; + switch (type_id) { + case FLV_CODEC_H264 : return CodecH264; + case FLV_CODEC_H265 : return CodecH265; + default : WarnL << "暂不支持该视频Amf:" << type_id; return CodecInvalid; } } - return CodecInvalid; } - Track::Ptr getTrackByCodecId(CodecId codecId, int sample_rate = 0, int channels = 0, int sample_bit = 0) { switch (codecId){ case CodecH264 : return std::make_shared(); case CodecH265 : return std::make_shared(); case CodecAAC : return std::make_shared(); + case CodecOpus: return std::make_shared(); case CodecG711A : case CodecG711U : return (sample_rate && channels && sample_bit) ? std::make_shared(codecId, sample_rate, channels, sample_bit) : nullptr; default : WarnL << "暂不支持该CodecId:" << codecId; return nullptr; @@ -191,7 +194,7 @@ static CodecId getAudioCodecIdByAmf(const AMFValue &val) { if (str == "mp4a") { return CodecAAC; } - WarnL << "暂不支持该Amf:" << str; + WarnL << "暂不支持该音频Amf:" << str; return CodecInvalid; } @@ -201,7 +204,8 @@ static CodecId getAudioCodecIdByAmf(const AMFValue &val) { case FLV_CODEC_AAC : return CodecAAC; case FLV_CODEC_G711A : return CodecG711A; case FLV_CODEC_G711U : return CodecG711U; - default : WarnL << "暂不支持该Amf:" << type_id; return CodecInvalid; + case FLV_CODEC_OPUS : return CodecOpus; + default : WarnL << "暂不支持该音频Amf:" << type_id; return CodecInvalid; } } @@ -221,6 +225,7 @@ RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track, bool is_enc case CodecH264 : return std::make_shared(track); case CodecAAC : return std::make_shared(track); case CodecH265 : return std::make_shared(track); + case CodecOpus : return std::make_shared(track); case CodecG711A : case CodecG711U : { auto audio_track = dynamic_pointer_cast(track); @@ -235,7 +240,7 @@ RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track, bool is_enc << ",该音频已被忽略"; return nullptr; } - return std::make_shared(track); + return std::make_shared(track); } default : WarnL << "暂不支持该CodecId:" << track->getCodecName(); return nullptr; } @@ -248,6 +253,7 @@ AMFValue Factory::getAmfByCodecId(CodecId codecId) { case CodecH265: return AMFValue(FLV_CODEC_H265); case CodecG711A: return AMFValue(FLV_CODEC_G711A); case CodecG711U: return AMFValue(FLV_CODEC_G711U); + case CodecOpus: return AMFValue(FLV_CODEC_OPUS); default: return AMFValue(AMF_NULL); } } diff --git a/src/Extension/Frame.cpp b/src/Extension/Frame.cpp index 1a5f8fd3..ec8a3608 100644 --- a/src/Extension/Frame.cpp +++ b/src/Extension/Frame.cpp @@ -35,12 +35,12 @@ public: _dts = frame->dts(); _pts = frame->pts(); _prefix_size = frame->prefixSize(); - _codecid = frame->getCodecId(); + _codec_id = frame->getCodecId(); _key = frame->keyFrame(); _config = frame->configFrame(); } - virtual ~FrameCacheAble() = default; + ~FrameCacheAble() override = default; /** * 可以被缓存 @@ -49,10 +49,6 @@ public: return true; } - CodecId getCodecId() const override{ - return _codecid; - } - bool keyFrame() const override{ return _key; } @@ -60,10 +56,10 @@ public: bool configFrame() const override{ return _config; } + private: Frame::Ptr _frame; BufferRaw::Ptr _buffer; - CodecId _codecid; bool _key; bool _config; }; diff --git a/src/Extension/Frame.h b/src/Extension/Frame.h index 5b34654b..8f2ab8a3 100644 --- a/src/Extension/Frame.h +++ b/src/Extension/Frame.h @@ -148,7 +148,7 @@ public: } CodecId getCodecId() const override{ - return _codecid; + return _codec_id; } bool keyFrame() const override { @@ -160,7 +160,7 @@ public: } public: - CodecId _codecid = CodecInvalid; + CodecId _codec_id = CodecInvalid; string _buffer; uint32_t _dts = 0; uint32_t _pts = 0; @@ -314,9 +314,24 @@ private: class FrameFromPtr : public Frame{ public: typedef std::shared_ptr Ptr; + + FrameFromPtr(CodecId codec_id, char *ptr, uint32_t size, uint32_t dts, uint32_t pts = 0, int prefix_size = 0) + : FrameFromPtr(ptr, size, dts, pts, prefix_size) { + _codec_id = codec_id; + } + + FrameFromPtr(char *ptr, uint32_t size, uint32_t dts, uint32_t pts = 0, int prefix_size = 0){ + _ptr = ptr; + _size = size; + _dts = dts; + _pts = pts; + _prefix_size = prefix_size; + } + char *data() const override{ return _ptr; } + uint32_t size() const override { return _size; } @@ -336,12 +351,80 @@ public: bool cacheAble() const override { return false; } + + CodecId getCodecId() const override { + if (_codec_id == CodecInvalid) { + throw std::invalid_argument("FrameFromPtr对象未设置codec类型"); + } + return _codec_id; + } + + void setCodecId(CodecId codec_id) { + _codec_id = codec_id; + } + + bool keyFrame() const override { + return false; + } + + bool configFrame() const override{ + return false; + } + +protected: + FrameFromPtr() {} + protected: char *_ptr; uint32_t _size; uint32_t _dts; uint32_t _pts = 0; uint32_t _prefix_size; + CodecId _codec_id = CodecInvalid; +}; + +/** + * 该对象可以把Buffer对象转换成可缓存的Frame对象 + */ +template +class FrameWrapper : public Parent{ +public: + ~FrameWrapper() = default; + + /** + * 构造frame + * @param buf 数据缓存 + * @param dts 解码时间戳 + * @param pts 显示时间戳 + * @param prefix 帧前缀长度 + * @param offset buffer有效数据偏移量 + */ + FrameWrapper(const Buffer::Ptr &buf, int64_t dts, int64_t pts, int prefix, int offset) : Parent(buf->data() + offset, buf->size() - offset, dts, pts, prefix){ + _buf = buf; + } + + /** + * 构造frame + * @param buf 数据缓存 + * @param dts 解码时间戳 + * @param pts 显示时间戳 + * @param prefix 帧前缀长度 + * @param offset buffer有效数据偏移量 + * @param codec 帧类型 + */ + FrameWrapper(const Buffer::Ptr &buf, int64_t dts, int64_t pts, int prefix, int offset, CodecId codec) : Parent(codec, buf->data() + offset, buf->size() - offset, dts, pts, prefix){ + _buf = buf; + } + + /** + * 该帧可缓存 + */ + bool cacheAble() const override { + return true; + } + +private: + Buffer::Ptr _buf; }; }//namespace mediakit diff --git a/src/Extension/G711.h b/src/Extension/G711.h index 04e11b6a..72273831 100644 --- a/src/Extension/G711.h +++ b/src/Extension/G711.h @@ -16,47 +16,6 @@ namespace mediakit{ -/** - * G711帧 - */ -class G711Frame : public FrameImp { -public: - G711Frame(){ - _codecid = CodecG711A; - } -}; - -class G711FrameNoCacheAble : public FrameFromPtr { -public: - typedef std::shared_ptr Ptr; - - G711FrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts, uint32_t pts = 0,int prefix_size = 0){ - _ptr = ptr; - _size = size; - _dts = dts; - _prefix_size = prefix_size; - } - - void setCodec(CodecId codecId){ - _codecId = codecId; - } - - CodecId getCodecId() const override{ - return _codecId; - } - - bool keyFrame() const override { - return false; - } - - bool configFrame() const override{ - return false; - } - -private: - CodecId _codecId; -}; - /** * G711音频通道 */ diff --git a/src/Extension/G711Rtmp.cpp b/src/Extension/G711Rtmp.cpp deleted file mode 100644 index 7ffca2f0..00000000 --- a/src/Extension/G711Rtmp.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Use of this source code is governed by MIT license that can be found in the - * LICENSE file in the root of the source tree. All contributing project authors - * may be found in the AUTHORS file in the root of the source tree. - */ - -#include "G711Rtmp.h" - -namespace mediakit{ - -G711RtmpDecoder::G711RtmpDecoder(CodecId codecId) { - _frame = obtainFrame(); - _codecId = codecId; -} - -G711Frame::Ptr G711RtmpDecoder::obtainFrame() { - //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 - auto frame = ResourcePoolHelper::obtainObj(); - frame->_buffer.clear(); - frame->_codecid = _codecId; - return frame; -} - -bool G711RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt, bool) { - //拷贝G711负载 - _frame->_buffer.assign(pkt->strBuf.data() + 1, pkt->strBuf.size() - 1); - _frame->_dts = pkt->timeStamp; - //写入环形缓存 - RtmpCodec::inputFrame(_frame); - _frame = obtainFrame(); - return false; -} - -///////////////////////////////////////////////////////////////////////////////////// - -G711RtmpEncoder::G711RtmpEncoder(const Track::Ptr &track) : G711RtmpDecoder(track->getCodecId()) { - _audio_flv_flags = getAudioRtmpFlags(track); -} - -void G711RtmpEncoder::inputFrame(const Frame::Ptr &frame) { - if(!_audio_flv_flags){ - return; - } - RtmpPacket::Ptr rtmpPkt = ResourcePoolHelper::obtainObj(); - rtmpPkt->strBuf.clear(); - //header - rtmpPkt->strBuf.push_back(_audio_flv_flags); - - //g711 data - rtmpPkt->strBuf.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize()); - rtmpPkt->bodySize = rtmpPkt->strBuf.size(); - rtmpPkt->chunkId = CHUNK_AUDIO; - rtmpPkt->streamId = STREAM_MEDIA; - rtmpPkt->timeStamp = frame->dts(); - rtmpPkt->typeId = MSG_AUDIO; - RtmpCodec::inputRtmp(rtmpPkt, false); -} - -}//namespace mediakit \ No newline at end of file diff --git a/src/Extension/G711Rtmp.h b/src/Extension/G711Rtmp.h deleted file mode 100644 index c2fea41b..00000000 --- a/src/Extension/G711Rtmp.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Use of this source code is governed by MIT license that can be found in the - * LICENSE file in the root of the source tree. All contributing project authors - * may be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ZLMEDIAKIT_G711RTMPCODEC_H -#define ZLMEDIAKIT_G711RTMPCODEC_H - -#include "Rtmp/RtmpCodec.h" -#include "Extension/Track.h" -#include "Extension/G711.h" - -namespace mediakit{ -/** - * G711 Rtmp转G711 Frame类 - */ -class G711RtmpDecoder : public RtmpCodec , public ResourcePoolHelper { -public: - typedef std::shared_ptr Ptr; - - G711RtmpDecoder(CodecId codecId); - ~G711RtmpDecoder() {} - - /** - * 输入Rtmp并解码 - * @param Rtmp Rtmp数据包 - * @param key_pos 此参数内部强制转换为false,请忽略之 - */ - bool inputRtmp(const RtmpPacket::Ptr &Rtmp, bool key_pos = false) override; - - CodecId getCodecId() const override{ - return _codecId; - } -private: - G711Frame::Ptr obtainFrame(); -private: - G711Frame::Ptr _frame; - CodecId _codecId; -}; - -/** - * G711 RTMP打包类 - */ -class G711RtmpEncoder : public G711RtmpDecoder , public ResourcePoolHelper { -public: - typedef std::shared_ptr Ptr; - - G711RtmpEncoder(const Track::Ptr &track); - ~G711RtmpEncoder() {} - - /** - * 输入G711 数据 - */ - void inputFrame(const Frame::Ptr &frame) override; -private: - uint8_t _audio_flv_flags = 0; -}; - -}//namespace mediakit - -#endif //ZLMEDIAKIT_G711RTMPCODEC_H diff --git a/src/Extension/G711Rtp.cpp b/src/Extension/G711Rtp.cpp deleted file mode 100644 index 48238db5..00000000 --- a/src/Extension/G711Rtp.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Use of this source code is governed by MIT license that can be found in the - * LICENSE file in the root of the source tree. All contributing project authors - * may be found in the AUTHORS file in the root of the source tree. - */ - -#include "G711Rtp.h" - -namespace mediakit{ - -G711RtpDecoder::G711RtpDecoder(CodecId codecid){ - _codecid = codecid; - _frame = obtainFrame(); -} - -G711Frame::Ptr G711RtpDecoder::obtainFrame() { - //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 - auto frame = ResourcePoolHelper::obtainObj(); - frame->_buffer.clear(); - frame->_codecid = _codecid; - frame->_dts = 0; - return frame; -} - -bool G711RtpDecoder::inputRtp(const RtpPacket::Ptr &rtppack, bool) { - // 获取rtp数据长度 - int length = rtppack->size() - rtppack->offset; - // 获取rtp数据 - const char *rtp_packet_buf = rtppack->data() + rtppack->offset; - - if (rtppack->timeStamp != _frame->_dts) { - //时间戳变更,清空上一帧 - onGetG711(_frame); - } - - //追加数据 - _frame->_buffer.append(rtp_packet_buf, length); - //赋值时间戳 - _frame->_dts = rtppack->timeStamp; - - if (rtppack->mark || _frame->_buffer.size() > 10 * 1024) { - //标记为mark时,或者内存快溢出时,我们认为这是该帧最后一个包 - onGetG711(_frame); - } - return false; -} - -void G711RtpDecoder::onGetG711(const G711Frame::Ptr &frame) { - if(!frame->_buffer.empty()){ - //写入环形缓存 - RtpCodec::inputFrame(frame); - _frame = obtainFrame(); - } -} - -///////////////////////////////////////////////////////////////////////////////////// - -G711RtpEncoder::G711RtpEncoder(CodecId codecid, uint32_t ui32Ssrc, uint32_t ui32MtuSize, - uint32_t ui32SampleRate, uint8_t ui8PayloadType, uint8_t ui8Interleaved) : - G711RtpDecoder(codecid), - RtpInfo(ui32Ssrc, ui32MtuSize, ui32SampleRate, ui8PayloadType, ui8Interleaved) { -} - -void G711RtpEncoder::inputFrame(const Frame::Ptr &frame) { - GET_CONFIG(uint32_t, cycleMS, Rtp::kCycleMS); - auto uiStamp = frame->dts(); - auto pcData = frame->data() + frame->prefixSize(); - auto iLen = frame->size() - frame->prefixSize(); - - uiStamp %= cycleMS; - char *ptr = (char *) pcData; - int iSize = iLen; - while (iSize > 0) { - if (iSize <= _ui32MtuSize - 20) { - makeG711Rtp(ptr, iSize, true, uiStamp); - break; - } - makeG711Rtp(ptr, _ui32MtuSize - 20, false, uiStamp); - ptr += (_ui32MtuSize - 20); - iSize -= (_ui32MtuSize - 20); - } -} - -void G711RtpEncoder::makeG711Rtp(const void *data, unsigned int len, bool mark, uint32_t uiStamp) { - RtpCodec::inputRtp(makeRtp(getTrackType(), data, len, mark, uiStamp), false); -} - -}//namespace mediakit diff --git a/src/Extension/G711Rtp.h b/src/Extension/G711Rtp.h deleted file mode 100644 index aca08e6d..00000000 --- a/src/Extension/G711Rtp.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Use of this source code is governed by MIT license that can be found in the - * LICENSE file in the root of the source tree. All contributing project authors - * may be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ZLMEDIAKIT_G711RTPCODEC_H -#define ZLMEDIAKIT_G711RTPCODEC_H -#include "Rtsp/RtpCodec.h" -#include "Extension/G711.h" -namespace mediakit{ - -/** - * rtp转G711类 - */ -class G711RtpDecoder : public RtpCodec , public ResourcePoolHelper { -public: - typedef std::shared_ptr Ptr; - - G711RtpDecoder(CodecId codecid); - ~G711RtpDecoder() {} - - /** - * 输入rtp并解码 - * @param rtp rtp数据包 - * @param key_pos 此参数内部强制转换为false,请忽略之 - */ - bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = false) override; - - CodecId getCodecId() const override{ - return _codecid; - } - -private: - void onGetG711(const G711Frame::Ptr &frame); - G711Frame::Ptr obtainFrame(); - -private: - G711Frame::Ptr _frame; - CodecId _codecid; -}; - -/** - * g711 转rtp类 - */ -class G711RtpEncoder : public G711RtpDecoder , public RtpInfo { -public: - typedef std::shared_ptr Ptr; - - /** - * @param ui32Ssrc ssrc - * @param ui32MtuSize mtu 大小 - * @param ui32SampleRate 采样率 - * @param ui8PayloadType pt类型 - * @param ui8Interleaved rtsp interleaved 值 - */ - G711RtpEncoder(CodecId codecid, - uint32_t ui32Ssrc, - uint32_t ui32MtuSize, - uint32_t ui32SampleRate, - uint8_t ui8PayloadType = 0, - uint8_t ui8Interleaved = TrackAudio * 2); - ~G711RtpEncoder() {} - - /** - * @param frame g711数据 - */ - void inputFrame(const Frame::Ptr &frame) override; -private: - void makeG711Rtp(const void *pData, unsigned int uiLen, bool bMark, uint32_t uiStamp); -}; - -}//namespace mediakit - -#endif //ZLMEDIAKIT_G711RTPCODEC_H diff --git a/src/Extension/H264.h b/src/Extension/H264.h index a7fca526..02eba813 100644 --- a/src/Extension/H264.h +++ b/src/Extension/H264.h @@ -30,14 +30,16 @@ public: typedef std::shared_ptr Ptr; typedef enum { - NAL_SPS = 7, - NAL_PPS = 8, NAL_IDR = 5, NAL_SEI = 6, + NAL_SPS = 7, + NAL_PPS = 8, + NAL_AUD = 9, + NAL_B_P = 1, } NalType; H264Frame(){ - _codecid = CodecH264; + _codec_id = CodecH264; } bool keyFrame() const override { @@ -68,10 +70,7 @@ public: _dts = dts; _pts = pts; _prefix_size = prefix_size; - } - - CodecId getCodecId() const override{ - return CodecH264; + _codec_id = CodecH264; } bool keyFrame() const override { @@ -182,8 +181,8 @@ public: */ void inputFrame(const Frame::Ptr &frame) override{ int type = H264_TYPE(*((uint8_t *)frame->data() + frame->prefixSize())); - if(type == H264Frame::NAL_SPS || type == H264Frame::NAL_SEI){ - //有些设备会把SPS PPS IDR帧当做一个帧打包,所以我们要split一下 + if(type != H264Frame::NAL_B_P && type != H264Frame::NAL_IDR){ + //非I/B/P帧情况下,split一下,防止多个帧粘合在一起 splitH264(frame->data(), frame->size(), frame->prefixSize(), [&](const char *ptr, int len, int prefix) { H264FrameInternal::Ptr sub_frame = std::make_shared(frame, (char *)ptr, len, prefix); inputFrame_l(sub_frame); @@ -227,6 +226,10 @@ private: VideoTrack::inputFrame(frame); } break; + case H264Frame::NAL_AUD:{ + //忽略AUD帧; + } + break; default: VideoTrack::inputFrame(frame); diff --git a/src/Extension/H264Rtmp.cpp b/src/Extension/H264Rtmp.cpp index 798de496..c1a94b96 100644 --- a/src/Extension/H264Rtmp.cpp +++ b/src/Extension/H264Rtmp.cpp @@ -23,10 +23,6 @@ H264Frame::Ptr H264RtmpDecoder::obtainFrame() { return frame; } -bool H264RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos) { - return decodeRtmp(rtmp); -} - /** * 返回不带0x00 00 00 01头的sps * @return @@ -39,18 +35,18 @@ static string getH264SPS(const RtmpPacket &thiz) { if (!thiz.isCfgFrame()) { return ret; } - if (thiz.strBuf.size() < 13) { + if (thiz.buffer.size() < 13) { WarnL << "bad H264 cfg!"; return ret; } uint16_t sps_size ; - memcpy(&sps_size, thiz.strBuf.data() + 11,2); + memcpy(&sps_size, thiz.buffer.data() + 11, 2); sps_size = ntohs(sps_size); - if ((int) thiz.strBuf.size() < 13 + sps_size) { + if ((int) thiz.buffer.size() < 13 + sps_size) { WarnL << "bad H264 cfg!"; return ret; } - ret.assign(thiz.strBuf.data() + 13, sps_size); + ret.assign(thiz.buffer.data() + 13, sps_size); return ret; } @@ -66,60 +62,59 @@ static string getH264PPS(const RtmpPacket &thiz) { if (!thiz.isCfgFrame()) { return ret; } - if (thiz.strBuf.size() < 13) { + if (thiz.buffer.size() < 13) { WarnL << "bad H264 cfg!"; return ret; } uint16_t sps_size ; - memcpy(&sps_size,thiz.strBuf.data() + 11,2); + memcpy(&sps_size, thiz.buffer.data() + 11, 2); sps_size = ntohs(sps_size); - if ((int) thiz.strBuf.size() < 13 + sps_size + 1 + 2) { + if ((int) thiz.buffer.size() < 13 + sps_size + 1 + 2) { WarnL << "bad H264 cfg!"; return ret; } uint16_t pps_size ; - memcpy(&pps_size, thiz.strBuf.data() + 13 + sps_size + 1,2); + memcpy(&pps_size, thiz.buffer.data() + 13 + sps_size + 1, 2); pps_size = ntohs(pps_size); - if ((int) thiz.strBuf.size() < 13 + sps_size + 1 + 2 + pps_size) { + if ((int) thiz.buffer.size() < 13 + sps_size + 1 + 2 + pps_size) { WarnL << "bad H264 cfg!"; return ret; } - ret.assign(thiz.strBuf.data() + 13 + sps_size + 1 + 2, pps_size); + ret.assign(thiz.buffer.data() + 13 + sps_size + 1 + 2, pps_size); return ret; } -bool H264RtmpDecoder::decodeRtmp(const RtmpPacket::Ptr &pkt) { +void H264RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt) { if (pkt->isCfgFrame()) { //缓存sps pps,后续插入到I帧之前 _sps = getH264SPS(*pkt); _pps = getH264PPS(*pkt); - onGetH264(_sps.data(), _sps.size(), pkt->timeStamp , pkt->timeStamp); - onGetH264(_pps.data(), _pps.size(), pkt->timeStamp , pkt->timeStamp); - return false; + onGetH264(_sps.data(), _sps.size(), pkt->time_stamp , pkt->time_stamp); + onGetH264(_pps.data(), _pps.size(), pkt->time_stamp , pkt->time_stamp); + return; } - if (pkt->strBuf.size() > 9) { - uint32_t iTotalLen = pkt->strBuf.size(); + if (pkt->buffer.size() > 9) { + uint32_t iTotalLen = pkt->buffer.size(); uint32_t iOffset = 5; - uint8_t *cts_ptr = (uint8_t *) (pkt->strBuf.data() + 2); + uint8_t *cts_ptr = (uint8_t *) (pkt->buffer.data() + 2); int32_t cts = (((cts_ptr[0] << 16) | (cts_ptr[1] << 8) | (cts_ptr[2])) + 0xff800000) ^ 0xff800000; - auto pts = pkt->timeStamp + cts; + auto pts = pkt->time_stamp + cts; while(iOffset + 4 < iTotalLen){ uint32_t iFrameLen; - memcpy(&iFrameLen, pkt->strBuf.data() + iOffset, 4); + memcpy(&iFrameLen, pkt->buffer.data() + iOffset, 4); iFrameLen = ntohl(iFrameLen); iOffset += 4; if(iFrameLen + iOffset > iTotalLen){ break; } - onGetH264(pkt->strBuf.data() + iOffset, iFrameLen, pkt->timeStamp , pts); + onGetH264(pkt->buffer.data() + iOffset, iFrameLen, pkt->time_stamp , pts); iOffset += iFrameLen; } } - return pkt->isVideoKeyFrame(); } inline void H264RtmpDecoder::onGetH264(const char* pcData, int iLen, uint32_t dts,uint32_t pts) { @@ -190,8 +185,8 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) { } } - if(_lastPacket && _lastPacket->timeStamp != frame->dts()) { - RtmpCodec::inputRtmp(_lastPacket, _lastPacket->isVideoKeyFrame()); + if(_lastPacket && _lastPacket->time_stamp != frame->dts()) { + RtmpCodec::inputRtmp(_lastPacket); _lastPacket = nullptr; } @@ -202,23 +197,23 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) { flags |= (((frame->configFrame() || frame->keyFrame()) ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4); _lastPacket = ResourcePoolHelper::obtainObj(); - _lastPacket->strBuf.clear(); - _lastPacket->strBuf.push_back(flags); - _lastPacket->strBuf.push_back(!is_config); + _lastPacket->buffer.clear(); + _lastPacket->buffer.push_back(flags); + _lastPacket->buffer.push_back(!is_config); auto cts = frame->pts() - frame->dts(); cts = htonl(cts); - _lastPacket->strBuf.append((char *)&cts + 1, 3); + _lastPacket->buffer.append((char *)&cts + 1, 3); - _lastPacket->chunkId = CHUNK_VIDEO; - _lastPacket->streamId = STREAM_MEDIA; - _lastPacket->timeStamp = frame->dts(); - _lastPacket->typeId = MSG_VIDEO; + _lastPacket->chunk_id = CHUNK_VIDEO; + _lastPacket->stream_index = STREAM_MEDIA; + _lastPacket->time_stamp = frame->dts(); + _lastPacket->type_id = MSG_VIDEO; } auto size = htonl(iLen); - _lastPacket->strBuf.append((char *) &size, 4); - _lastPacket->strBuf.append(pcData, iLen); - _lastPacket->bodySize = _lastPacket->strBuf.size(); + _lastPacket->buffer.append((char *) &size, 4); + _lastPacket->buffer.append(pcData, iLen); + _lastPacket->body_size = _lastPacket->buffer.size(); } void H264RtmpEncoder::makeVideoConfigPkt() { @@ -227,39 +222,39 @@ void H264RtmpEncoder::makeVideoConfigPkt() { bool is_config = true; RtmpPacket::Ptr rtmpPkt = ResourcePoolHelper::obtainObj(); - rtmpPkt->strBuf.clear(); + rtmpPkt->buffer.clear(); //header - rtmpPkt->strBuf.push_back(flags); - rtmpPkt->strBuf.push_back(!is_config); + rtmpPkt->buffer.push_back(flags); + rtmpPkt->buffer.push_back(!is_config); //cts - rtmpPkt->strBuf.append("\x0\x0\x0", 3); + rtmpPkt->buffer.append("\x0\x0\x0", 3); //AVCDecoderConfigurationRecord start - rtmpPkt->strBuf.push_back(1); // version - rtmpPkt->strBuf.push_back(_sps[1]); // profile - rtmpPkt->strBuf.push_back(_sps[2]); // compat - rtmpPkt->strBuf.push_back(_sps[3]); // level - rtmpPkt->strBuf.push_back(0xff); // 6 bits reserved + 2 bits nal size length - 1 (11) - rtmpPkt->strBuf.push_back(0xe1); // 3 bits reserved + 5 bits number of sps (00001) + rtmpPkt->buffer.push_back(1); // version + rtmpPkt->buffer.push_back(_sps[1]); // profile + rtmpPkt->buffer.push_back(_sps[2]); // compat + rtmpPkt->buffer.push_back(_sps[3]); // level + rtmpPkt->buffer.push_back(0xff); // 6 bits reserved + 2 bits nal size length - 1 (11) + rtmpPkt->buffer.push_back(0xe1); // 3 bits reserved + 5 bits number of sps (00001) //sps uint16_t size = _sps.size(); size = htons(size); - rtmpPkt->strBuf.append((char *) &size, 2); - rtmpPkt->strBuf.append(_sps); + rtmpPkt->buffer.append((char *) &size, 2); + rtmpPkt->buffer.append(_sps); //pps - rtmpPkt->strBuf.push_back(1); // version + rtmpPkt->buffer.push_back(1); // version size = _pps.size(); size = htons(size); - rtmpPkt->strBuf.append((char *) &size, 2); - rtmpPkt->strBuf.append(_pps); + rtmpPkt->buffer.append((char *) &size, 2); + rtmpPkt->buffer.append(_pps); - rtmpPkt->bodySize = rtmpPkt->strBuf.size(); - rtmpPkt->chunkId = CHUNK_VIDEO; - rtmpPkt->streamId = STREAM_MEDIA; - rtmpPkt->timeStamp = 0; - rtmpPkt->typeId = MSG_VIDEO; - RtmpCodec::inputRtmp(rtmpPkt, false); + rtmpPkt->body_size = rtmpPkt->buffer.size(); + rtmpPkt->chunk_id = CHUNK_VIDEO; + rtmpPkt->stream_index = STREAM_MEDIA; + rtmpPkt->time_stamp = 0; + rtmpPkt->type_id = MSG_VIDEO; + RtmpCodec::inputRtmp(rtmpPkt); } }//namespace mediakit diff --git a/src/Extension/H264Rtmp.h b/src/Extension/H264Rtmp.h index cf8f4dbe..3921fc79 100644 --- a/src/Extension/H264Rtmp.h +++ b/src/Extension/H264Rtmp.h @@ -32,17 +32,17 @@ public: /** * 输入264 Rtmp包 * @param rtmp Rtmp包 - * @param key_pos 此参数忽略之 */ - bool inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos = true) override; + void inputRtmp(const RtmpPacket::Ptr &rtmp) override; CodecId getCodecId() const override{ return CodecH264; } + protected: - bool decodeRtmp(const RtmpPacket::Ptr &Rtmp); void onGetH264(const char *pcData, int iLen, uint32_t dts,uint32_t pts); H264Frame::Ptr obtainFrame(); + protected: H264Frame::Ptr _h264frame; string _sps; diff --git a/src/Extension/H264Rtp.cpp b/src/Extension/H264Rtp.cpp index 76958147..8836385f 100644 --- a/src/Extension/H264Rtp.cpp +++ b/src/Extension/H264Rtp.cpp @@ -12,13 +12,6 @@ namespace mediakit{ - -typedef struct { - unsigned forbidden_zero_bit :1; - unsigned nal_ref_idc :2; - unsigned type :5; -} NALU; - typedef struct { unsigned S :1; unsigned E :1; @@ -26,15 +19,6 @@ typedef struct { unsigned type :5; } FU; -static bool MakeNalu(uint8_t in, NALU &nal) { - nal.forbidden_zero_bit = in >> 7; - if (nal.forbidden_zero_bit) { - return false; - } - nal.nal_ref_idc = (in & 0x60) >> 5; - nal.type = in & 0x1f; - return true; -} static bool MakeFU(uint8_t in, FU &fu) { fu.S = in >> 7; fu.E = (in >> 6) & 0x01; @@ -86,30 +70,28 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { 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; - MakeNalu(*frame, nal); + int nal_type = *frame & 0x1F; + int nal_suffix = *frame & (~0x1F); - if (nal.type >= 0 && nal.type < 24) { + if (nal_type >= 0 && nal_type < 24) { //a full frame _h264frame->_buffer.assign("\x0\x0\x0\x1", 4); - _h264frame->_buffer.append((char *)frame, length); + _h264frame->_buffer.append((char *) frame, length); _h264frame->_pts = rtppack->timeStamp; auto key = _h264frame->keyFrame(); onGetH264(_h264frame); return (key); //i frame } - switch (nal.type){ + switch (nal_type){ case 24:{ // 24 STAP-A 单一时间的组合包 bool haveIDR = false; auto ptr = frame + 1; - while(true){ + while (true) { int off = ptr - frame; if (off >= length) { break; @@ -121,14 +103,12 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { if (off + len > length) { break; } - if(len >= 10){ - //过小的帧丢弃 - NALU nal; - MakeNalu(ptr[0], nal); + if (len > 0) { + //有有效数据 _h264frame->_buffer.assign("\x0\x0\x0\x1", 4); - _h264frame->_buffer.append((char *)ptr, len); + _h264frame->_buffer.append((char *) ptr, len); _h264frame->_pts = rtppack->timeStamp; - if(nal.type == H264Frame::NAL_IDR){ + if ((ptr[0] & 0x1F) == H264Frame::NAL_IDR) { haveIDR = true; } onGetH264(_h264frame); @@ -144,10 +124,9 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { MakeFU(frame[1], fu); if (fu.S) { //该帧的第一个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); - _h264frame->_buffer.append((char *)frame + 2, length - 2); + _h264frame->_buffer.push_back(nal_suffix | fu.type); + _h264frame->_buffer.append((char *) frame + 2, length - 2); _h264frame->_pts = rtppack->timeStamp; //该函数return时,保存下当前sequence,以便下次对比seq是否连续 _lastSeq = rtppack->sequence; @@ -163,20 +142,20 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { if (!fu.E) { //该帧的中间rtp包 FU-A mid - _h264frame->_buffer.append((char *)frame + 2, length - 2); + _h264frame->_buffer.append((char *) frame + 2, length - 2); //该函数return时,保存下当前sequence,以便下次对比seq是否连续 _lastSeq = rtppack->sequence; return false; } //该帧最后一个rtp包 FU-A end - _h264frame->_buffer.append((char *)frame + 2, length - 2); + _h264frame->_buffer.append((char *) frame + 2, length - 2); _h264frame->_pts = rtppack->timeStamp; onGetH264(_h264frame); return false; } - default:{ + default: { // 29 FU-B 单NAL单元B模式 // 25 STAP-B 单一时间的组合包 // 26 MTAP16 多个时间的组合包 @@ -184,7 +163,7 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { // 0 udef // 30 udef // 31 udef - WarnL << "不支持的rtp类型:" << (int)nal.type << " " << rtppack->sequence; + WarnL << "不支持的rtp类型:" << (int) nal_type << " " << rtppack->sequence; return false; } } @@ -215,63 +194,62 @@ H264RtpEncoder::H264RtpEncoder(uint32_t ui32Ssrc, void H264RtpEncoder::inputFrame(const Frame::Ptr &frame) { GET_CONFIG(uint32_t,cycleMS,Rtp::kCycleMS); - auto pcData = frame->data() + frame->prefixSize(); - auto uiStamp = frame->pts(); - auto iLen = frame->size() - frame->prefixSize(); - //获取NALU的5bit 帧类型 - unsigned char naluType = H264_TYPE(pcData[0]); + auto ptr = frame->data() + frame->prefixSize(); + auto pts = frame->pts() % cycleMS; + auto len = frame->size() - frame->prefixSize(); + auto nal_type = H264_TYPE(ptr[0]); + auto max_rtp_size = _ui32MtuSize - 2; - uiStamp %= cycleMS; - int iSize = _ui32MtuSize - 2; //超过MTU则按照FU-A模式打包 - if (iLen > iSize) { + if (len > max_rtp_size) { //最高位bit为forbidden_zero_bit, //后面2bit为nal_ref_idc(帧重要程度),00:可以丢,11:不能丢 //末尾5bit为nalu type,固定为28(FU-A) - unsigned char f_nri_flags = (*((unsigned char *) pcData) & 0x60) | 28; + unsigned char nal_fu_a = (*((unsigned char *) ptr) & (~0x1F)) | 28; unsigned char s_e_r_flags; - bool bFirst = true; - bool mark = false; - int nOffset = 1; - while (!mark) { - if (iLen <= nOffset + iSize) { + bool fu_a_start = true; + bool mark_bit = false; + int offset = 1; + while (!mark_bit) { + if (len <= offset + max_rtp_size) { //已经拆分结束 - iSize = iLen - nOffset; - mark = true; + max_rtp_size = len - offset; + mark_bit = true; //FU-A end - s_e_r_flags = (1 << 6) | naluType; - } else if (bFirst) { + s_e_r_flags = (1 << 6) | nal_type; + } else if (fu_a_start) { //FU-A start - s_e_r_flags = (1 << 7) | naluType; + s_e_r_flags = (1 << 7) | nal_type; } else { //FU-A mid - s_e_r_flags = naluType; + s_e_r_flags = nal_type; } { //传入nullptr先不做payload的内存拷贝 - auto rtp = makeRtp(getTrackType(), nullptr, iSize + 2, mark, uiStamp); + auto rtp = makeRtp(getTrackType(), nullptr, max_rtp_size + 2, mark_bit, pts); //rtp payload 负载部分 uint8_t *payload = (uint8_t*)rtp->data() + rtp->offset; //FU-A 第1个字节 - payload[0] = f_nri_flags; + payload[0] = nal_fu_a; //FU-A 第2个字节 payload[1] = s_e_r_flags; //H264 数据 - memcpy(payload + 2, (unsigned char *) pcData + nOffset, iSize); + memcpy(payload + 2, (unsigned char *) ptr + offset, max_rtp_size); //输入到rtp环形缓存 - RtpCodec::inputRtp(rtp,bFirst && naluType == H264Frame::NAL_IDR); + RtpCodec::inputRtp(rtp, fu_a_start && nal_type == H264Frame::NAL_IDR); } - nOffset += iSize; - bFirst = false; + offset += max_rtp_size; + fu_a_start = false; } } else { - makeH264Rtp(naluType,pcData, iLen, true, true, uiStamp); + //如果帧长度不超过mtu, 则按照Single NAL unit packet per H.264 方式打包 + makeH264Rtp(ptr, len, false, false, pts); } } -void H264RtpEncoder::makeH264Rtp(int nal_type,const void* data, unsigned int len, bool mark, bool first_packet, uint32_t uiStamp) { - RtpCodec::inputRtp(makeRtp(getTrackType(),data,len,mark,uiStamp),first_packet && nal_type == H264Frame::NAL_IDR); +void H264RtpEncoder::makeH264Rtp(const void* data, unsigned int len, bool mark, bool gop_pos, uint32_t uiStamp) { + RtpCodec::inputRtp(makeRtp(getTrackType(), data, len, mark, uiStamp), gop_pos); } }//namespace mediakit diff --git a/src/Extension/H264Rtp.h b/src/Extension/H264Rtp.h index 2b5038b1..89e09c59 100644 --- a/src/Extension/H264Rtp.h +++ b/src/Extension/H264Rtp.h @@ -78,7 +78,7 @@ public: */ void inputFrame(const Frame::Ptr &frame) override; private: - void makeH264Rtp(int nal_type,const void *pData, unsigned int uiLen, bool bMark, bool first_packet, uint32_t uiStamp); + void makeH264Rtp(const void *pData, unsigned int uiLen, bool bMark, bool gop_pos, uint32_t uiStamp); }; }//namespace mediakit{ diff --git a/src/Extension/H265.h b/src/Extension/H265.h index 5279908b..eee14de2 100644 --- a/src/Extension/H265.h +++ b/src/Extension/H265.h @@ -61,7 +61,7 @@ public: } NaleType; H265Frame(){ - _codecid = CodecH265; + _codec_id = CodecH265; } bool keyFrame() const override { @@ -92,10 +92,7 @@ public: _dts = dts; _pts = pts; _prefix_size = prefix_size; - } - - CodecId getCodecId() const override { - return CodecH265; + _codec_id = CodecH265; } bool keyFrame() const override { diff --git a/src/Extension/H265Rtmp.cpp b/src/Extension/H265Rtmp.cpp index ff346582..dddf5379 100644 --- a/src/Extension/H265Rtmp.cpp +++ b/src/Extension/H265Rtmp.cpp @@ -27,10 +27,6 @@ H265Frame::Ptr H265RtmpDecoder::obtainFrame() { return frame; } -bool H265RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos) { - return decodeRtmp(rtmp); -} - #ifdef ENABLE_MP4 /** * 返回不带0x00 00 00 01头的sps @@ -43,61 +39,60 @@ static bool getH265ConfigFrame(const RtmpPacket &thiz,string &frame) { if (!thiz.isCfgFrame()) { return false; } - if (thiz.strBuf.size() < 6) { + if (thiz.buffer.size() < 6) { WarnL << "bad H265 cfg!"; return false; } - auto extra = thiz.strBuf.data() + 5; - auto bytes = thiz.strBuf.size() - 5; + auto extra = thiz.buffer.data() + 5; + auto bytes = thiz.buffer.size() - 5; struct mpeg4_hevc_t hevc = {0}; if (mpeg4_hevc_decoder_configuration_record_load((uint8_t *) extra, bytes, &hevc) > 0) { - uint8_t config[1024] = {0}; - int size = mpeg4_hevc_to_nalu(&hevc, config, sizeof(config)); + uint8_t *config = new uint8_t[bytes * 2]; + int size = mpeg4_hevc_to_nalu(&hevc, config, bytes * 2); if (size > 4) { frame.assign((char *) config + 4, size - 4); - return true; } + delete [] config; + return size > 4; } - return false; } #endif -bool H265RtmpDecoder::decodeRtmp(const RtmpPacket::Ptr &pkt) { +void H265RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt) { if (pkt->isCfgFrame()) { #ifdef ENABLE_MP4 string config; if(getH265ConfigFrame(*pkt,config)){ - onGetH265(config.data(), config.size(), pkt->timeStamp , pkt->timeStamp); + onGetH265(config.data(), config.size(), pkt->time_stamp , pkt->time_stamp); } #else WarnL << "请开启MP4相关功能并使能\"ENABLE_MP4\",否则对H265-RTMP支持不完善"; #endif - return false; + return; } - if (pkt->strBuf.size() > 9) { - uint32_t iTotalLen = pkt->strBuf.size(); + if (pkt->buffer.size() > 9) { + uint32_t iTotalLen = pkt->buffer.size(); uint32_t iOffset = 5; - uint8_t *cts_ptr = (uint8_t *) (pkt->strBuf.data() + 2); + uint8_t *cts_ptr = (uint8_t *) (pkt->buffer.data() + 2); int32_t cts = (((cts_ptr[0] << 16) | (cts_ptr[1] << 8) | (cts_ptr[2])) + 0xff800000) ^ 0xff800000; - auto pts = pkt->timeStamp + cts; + auto pts = pkt->time_stamp + cts; while(iOffset + 4 < iTotalLen){ uint32_t iFrameLen; - memcpy(&iFrameLen, pkt->strBuf.data() + iOffset, 4); + memcpy(&iFrameLen, pkt->buffer.data() + iOffset, 4); iFrameLen = ntohl(iFrameLen); iOffset += 4; if(iFrameLen + iOffset > iTotalLen){ break; } - onGetH265(pkt->strBuf.data() + iOffset, iFrameLen, pkt->timeStamp , pts); + onGetH265(pkt->buffer.data() + iOffset, iFrameLen, pkt->time_stamp , pts); iOffset += iFrameLen; } } - return pkt->isVideoKeyFrame(); } inline void H265RtmpDecoder::onGetH265(const char* pcData, int iLen, uint32_t dts,uint32_t pts) { @@ -176,8 +171,8 @@ void H265RtmpEncoder::inputFrame(const Frame::Ptr &frame) { return; } - if(_lastPacket && _lastPacket->timeStamp != frame->dts()) { - RtmpCodec::inputRtmp(_lastPacket, _lastPacket->isVideoKeyFrame()); + if(_lastPacket && _lastPacket->time_stamp != frame->dts()) { + RtmpCodec::inputRtmp(_lastPacket); _lastPacket = nullptr; } @@ -188,23 +183,23 @@ void H265RtmpEncoder::inputFrame(const Frame::Ptr &frame) { flags |= (((frame->configFrame() || frame->keyFrame()) ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4); _lastPacket = ResourcePoolHelper::obtainObj(); - _lastPacket->strBuf.clear(); - _lastPacket->strBuf.push_back(flags); - _lastPacket->strBuf.push_back(!is_config); + _lastPacket->buffer.clear(); + _lastPacket->buffer.push_back(flags); + _lastPacket->buffer.push_back(!is_config); auto cts = frame->pts() - frame->dts(); cts = htonl(cts); - _lastPacket->strBuf.append((char *)&cts + 1, 3); + _lastPacket->buffer.append((char *)&cts + 1, 3); - _lastPacket->chunkId = CHUNK_VIDEO; - _lastPacket->streamId = STREAM_MEDIA; - _lastPacket->timeStamp = frame->dts(); - _lastPacket->typeId = MSG_VIDEO; + _lastPacket->chunk_id = CHUNK_VIDEO; + _lastPacket->stream_index = STREAM_MEDIA; + _lastPacket->time_stamp = frame->dts(); + _lastPacket->type_id = MSG_VIDEO; } auto size = htonl(iLen); - _lastPacket->strBuf.append((char *) &size, 4); - _lastPacket->strBuf.append(pcData, iLen); - _lastPacket->bodySize = _lastPacket->strBuf.size(); + _lastPacket->buffer.append((char *) &size, 4); + _lastPacket->buffer.append(pcData, iLen); + _lastPacket->body_size = _lastPacket->buffer.size(); } void H265RtmpEncoder::makeVideoConfigPkt() { @@ -214,13 +209,13 @@ void H265RtmpEncoder::makeVideoConfigPkt() { bool is_config = true; RtmpPacket::Ptr rtmpPkt = ResourcePoolHelper::obtainObj(); - rtmpPkt->strBuf.clear(); + rtmpPkt->buffer.clear(); //header - rtmpPkt->strBuf.push_back(flags); - rtmpPkt->strBuf.push_back(!is_config); + rtmpPkt->buffer.push_back(flags); + rtmpPkt->buffer.push_back(!is_config); //cts - rtmpPkt->strBuf.append("\x0\x0\x0", 3); + rtmpPkt->buffer.append("\x0\x0\x0", 3); struct mpeg4_hevc_t hevc = {0}; string vps_sps_pps = string("\x00\x00\x00\x01", 4) + _vps + @@ -235,14 +230,14 @@ void H265RtmpEncoder::makeVideoConfigPkt() { } //HEVCDecoderConfigurationRecord - rtmpPkt->strBuf.append((char *)extra_data, extra_data_size); + rtmpPkt->buffer.append((char *)extra_data, extra_data_size); - rtmpPkt->bodySize = rtmpPkt->strBuf.size(); - rtmpPkt->chunkId = CHUNK_VIDEO; - rtmpPkt->streamId = STREAM_MEDIA; - rtmpPkt->timeStamp = 0; - rtmpPkt->typeId = MSG_VIDEO; - RtmpCodec::inputRtmp(rtmpPkt, false); + rtmpPkt->body_size = rtmpPkt->buffer.size(); + rtmpPkt->chunk_id = CHUNK_VIDEO; + rtmpPkt->stream_index = STREAM_MEDIA; + rtmpPkt->time_stamp = 0; + rtmpPkt->type_id = MSG_VIDEO; + RtmpCodec::inputRtmp(rtmpPkt); #else WarnL << "请开启MP4相关功能并使能\"ENABLE_MP4\",否则对H265-RTMP支持不完善"; #endif diff --git a/src/Extension/H265Rtmp.h b/src/Extension/H265Rtmp.h index 4a151251..c46396ff 100644 --- a/src/Extension/H265Rtmp.h +++ b/src/Extension/H265Rtmp.h @@ -32,17 +32,17 @@ public: /** * 输入265 Rtmp包 * @param rtmp Rtmp包 - * @param key_pos 此参数忽略之 */ - bool inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos = true) override; + void inputRtmp(const RtmpPacket::Ptr &rtmp) override; CodecId getCodecId() const override{ return CodecH265; } + protected: - bool decodeRtmp(const RtmpPacket::Ptr &Rtmp); void onGetH265(const char *pcData, int iLen, uint32_t dts,uint32_t pts); H265Frame::Ptr obtainFrame(); + protected: H265Frame::Ptr _h265frame; }; diff --git a/src/Extension/H265Rtp.cpp b/src/Extension/H265Rtp.cpp index 6af6c6ee..461d8ed0 100644 --- a/src/Extension/H265Rtp.cpp +++ b/src/Extension/H265Rtp.cpp @@ -200,7 +200,7 @@ void H265RtpEncoder::inputFrame(const Frame::Ptr &frame) { bFirst = false; } } else { - makeH265Rtp(naluType,pcData, iLen, true, true, uiStamp); + makeH265Rtp(naluType,pcData, iLen, false, true, uiStamp); } } diff --git a/src/Extension/Opus.h b/src/Extension/Opus.h index 0a6e56d3..144afb77 100644 --- a/src/Extension/Opus.h +++ b/src/Extension/Opus.h @@ -16,52 +16,13 @@ namespace mediakit{ -/** - * Opus帧 - */ -class OpusFrame : public FrameImp { -public: - typedef std::shared_ptr Ptr; - - OpusFrame(){ - _codecid = CodecOpus; - } -}; - -/** - * 不可缓存的Opus帧 - */ -class OpusFrameNoCacheAble : public FrameFromPtr { -public: - typedef std::shared_ptr Ptr; - - OpusFrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts, uint32_t pts = 0,int prefix_size = 0){ - _ptr = ptr; - _size = size; - _dts = dts; - _prefix_size = prefix_size; - } - - CodecId getCodecId() const override{ - return CodecOpus; - } - - bool keyFrame() const override { - return false; - } - - bool configFrame() const override{ - return false; - } -}; - /** * Opus帧音频通道 */ class OpusTrack : public AudioTrackImp{ public: typedef std::shared_ptr Ptr; - OpusTrack(int sample_rate, int channels, int sample_bit) : AudioTrackImp(CodecOpus,sample_rate,channels,sample_bit){} + OpusTrack() : AudioTrackImp(CodecOpus,48000,2,16){} private: //克隆该Track diff --git a/src/FMP4/FMP4MediaSource.h b/src/FMP4/FMP4MediaSource.h new file mode 100644 index 00000000..277d4bf8 --- /dev/null +++ b/src/FMP4/FMP4MediaSource.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_FMP4MEDIASOURCE_H +#define ZLMEDIAKIT_FMP4MEDIASOURCE_H + +#include "Common/MediaSource.h" +using namespace toolkit; +#define FMP4_GOP_SIZE 512 + +namespace mediakit { + +//FMP4直播数据包 +class FMP4Packet : public BufferString{ +public: + using Ptr = std::shared_ptr; + + template + FMP4Packet(ARGS && ...args) : BufferString(std::forward(args)...) {}; + ~FMP4Packet() override = default; + +public: + uint32_t time_stamp = 0; +}; + +//FMP4直播合并写策略类 +class FMP4FlushPolicy : public FlushPolicy{ +public: + FMP4FlushPolicy() = default; + ~FMP4FlushPolicy() = default; + + uint32_t getStamp(const FMP4Packet::Ptr &packet) { + return packet->time_stamp; + } +}; + +//FMP4直播源 +class FMP4MediaSource : public MediaSource, public RingDelegate, public PacketCache{ +public: + using Ptr = std::shared_ptr; + using RingDataType = std::shared_ptr >; + using RingType = RingBuffer; + + FMP4MediaSource(const string &vhost, + const string &app, + const string &stream_id, + int ring_size = FMP4_GOP_SIZE) : MediaSource(FMP4_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {} + + ~FMP4MediaSource() override = default; + + /** + * 获取媒体源的环形缓冲 + */ + const RingType::Ptr &getRing() const { + return _ring; + } + + /** + * 获取fmp4 init segment + */ + const string &getInitSegment() const{ + return _init_segment; + } + + /** + * 设置fmp4 init segment + * @param str init segment + */ + void setInitSegment(string str) { + _init_segment = std::move(str); + if (_ring) { + regist(); + } + } + + /** + * 获取播放器个数 + */ + int readerCount() override { + return _ring ? _ring->readerCount() : 0; + } + + /** + * 输入FMP4包 + * @param packet FMP4包 + * @param key 是否为关键帧第一个包 + */ + void onWrite(const FMP4Packet::Ptr &packet, bool key) override { + if (!_ring) { + createRing(); + } + if (key) { + _have_video = true; + } + PacketCache::inputPacket(true, packet, key); + } + + /** + * 情况GOP缓存 + */ + void clearCache() override { + PacketCache::clearCache(); + _ring->clearCache(); + } + +private: + void createRing(){ + weak_ptr weak_self = dynamic_pointer_cast(shared_from_this()); + _ring = std::make_shared(_ring_size, [weak_self](int size) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + strong_self->onReaderChanged(size); + }); + onReaderChanged(0); + if (!_init_segment.empty()) { + regist(); + } + } + + /** + * 合并写回调 + * @param packet_list 合并写缓存列队 + * @param key_pos 是否包含关键帧 + */ + void onFlush(std::shared_ptr > &packet_list, bool key_pos) override { + //如果不存在视频,那么就没有存在GOP缓存的意义,所以确保一直清空GOP缓存 + _ring->write(packet_list, _have_video ? key_pos : true); + } + +private: + bool _have_video = false; + int _ring_size; + string _init_segment; + RingType::Ptr _ring; +}; + + +}//namespace mediakit +#endif //ZLMEDIAKIT_FMP4MEDIASOURCE_H diff --git a/src/FMP4/FMP4MediaSourceMuxer.h b/src/FMP4/FMP4MediaSourceMuxer.h new file mode 100644 index 00000000..6922eef8 --- /dev/null +++ b/src/FMP4/FMP4MediaSourceMuxer.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H +#define ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H + +#include "FMP4MediaSource.h" +#include "Record/MP4Muxer.h" + +namespace mediakit { + +class FMP4MediaSourceMuxer : public MP4MuxerMemory, public MediaSourceEventInterceptor, + public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + + FMP4MediaSourceMuxer(const string &vhost, + const string &app, + const string &stream_id) { + _media_src = std::make_shared(vhost, app, stream_id); + } + + ~FMP4MediaSourceMuxer() override = default; + + void setListener(const std::weak_ptr &listener){ + _listener = listener; + _media_src->setListener(shared_from_this()); + } + + int readerCount() const{ + return _media_src->readerCount(); + } + + void onReaderChanged(MediaSource &sender, int size) override { + _enabled = size; + if (!size) { + _clear_cache = true; + } + MediaSourceEventInterceptor::onReaderChanged(sender, size); + } + + void inputFrame(const Frame::Ptr &frame) override { + if (_clear_cache) { + _clear_cache = false; + _media_src->clearCache(); + } + if (_enabled) { + MP4MuxerMemory::inputFrame(frame); + } + } + + bool isEnabled() { + //缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 + return _clear_cache ? true : _enabled; + } + + void onAllTrackReady() { + _media_src->setInitSegment(getInitSegment()); + } + +protected: + void onSegmentData(const string &string, uint32_t stamp, bool key_frame) override { + if (string.empty()) { + return; + } + FMP4Packet::Ptr packet = std::make_shared(std::move(string)); + packet->time_stamp = stamp; + _media_src->onWrite(packet, key_frame); + } + +private: + bool _enabled = true; + bool _clear_cache = false; + FMP4MediaSource::Ptr _media_src; +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H diff --git a/src/Http/HlsPlayer.cpp b/src/Http/HlsPlayer.cpp index 519297b6..232e3453 100644 --- a/src/Http/HlsPlayer.cpp +++ b/src/Http/HlsPlayer.cpp @@ -13,7 +13,7 @@ namespace mediakit { HlsPlayer::HlsPlayer(const EventPoller::Ptr &poller){ _segment.setOnSegment([this](const char *data, uint64_t len) { onPacket(data, len); }); - _poller = poller ? poller : EventPollerPool::Instance().getPoller(); + setPoller(poller ? poller : EventPollerPool::Instance().getPoller()); } HlsPlayer::~HlsPlayer() {} @@ -63,6 +63,15 @@ void HlsPlayer::playNextTs(bool force){ std::shared_ptr ticker(new Ticker); _http_ts_player = std::make_shared(getPoller(), false); + + _http_ts_player->setOnCreateSocket([weakSelf](const EventPoller::Ptr &poller) { + auto strongSelf = weakSelf.lock(); + if (strongSelf) { + return strongSelf->createSocket(); + } + return Socket::createSocket(poller, true); + }); + _http_ts_player->setOnDisconnect([weakSelf, ticker, ts_duration](const SockException &err) { auto strongSelf = weakSelf.lock(); if (!strongSelf) { @@ -84,6 +93,7 @@ void HlsPlayer::playNextTs(bool force){ }, strongSelf->getPoller())); } }); + _http_ts_player->setOnPacket([weakSelf](const char *data, uint64_t len) { auto strongSelf = weakSelf.lock(); if (!strongSelf) { @@ -94,9 +104,10 @@ void HlsPlayer::playNextTs(bool force){ }); _http_ts_player->setMethod("GET"); - if(!(*this)[kNetAdapter].empty()) { + if (!(*this)[kNetAdapter].empty()) { _http_ts_player->setNetAdapter((*this)[Client::kNetAdapter]); } + _http_ts_player->sendRequest(_ts_list.front().url, 2 * _ts_list.front().duration); _ts_list.pop_front(); } @@ -254,11 +265,15 @@ void HlsPlayerImp::onAllTrackReady() { } void HlsPlayerImp::onPlayResult(const SockException &ex) { - if(ex){ + if (ex) { PlayerImp::onPlayResult(ex); - }else{ + } else { + _frame_cache.clear(); + _stamp[TrackAudio].setRelativeStamp(0); + _stamp[TrackVideo].setRelativeStamp(0); _stamp[TrackAudio].syncTo(_stamp[TrackVideo]); - _ticker.resetTime(); + setPlayPosition(0); + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); //每50毫秒执行一次 _timer = std::make_shared(0.05, [weakSelf]() { @@ -288,25 +303,47 @@ void HlsPlayerImp::inputFrame(const Frame::Ptr &frame) { //根据时间戳缓存frame _frame_cache.emplace(dts, Frame::getCacheAbleFrame(frame)); - while (!_frame_cache.empty()) { - if (_frame_cache.rbegin()->first - _frame_cache.begin()->first > 30 * 1000) { - //缓存超过30秒,强制消费掉 + if (getBufferMS() > 30 * 1000) { + //缓存超过30秒,强制消费至15秒(减少延时或内存占用) + while (getBufferMS() > 15 * 1000) { MediaSink::inputFrame(_frame_cache.begin()->second); _frame_cache.erase(_frame_cache.begin()); - continue; } - //缓存小于30秒 - break; + //接着播放缓存中最早的帧 + setPlayPosition(_frame_cache.begin()->first); } } +int64_t HlsPlayerImp::getPlayPosition(){ + return _ticker.elapsedTime() + _ticker_offset; +} + +int64_t HlsPlayerImp::getBufferMS(){ + if(_frame_cache.empty()){ + return 0; + } + return _frame_cache.rbegin()->first - _frame_cache.begin()->first; +} + +void HlsPlayerImp::setPlayPosition(int64_t pos){ + _ticker.resetTime(); + _ticker_offset = pos; +} + void HlsPlayerImp::onTick() { auto it = _frame_cache.begin(); while (it != _frame_cache.end()) { - if (it->first > _ticker.elapsedTime()) { + if (it->first > getPlayPosition()) { //这些帧还未到时间播放 break; } + + if (getBufferMS() < 3 * 1000) { + //缓存小于3秒,那么降低定时器消费速度(让剩余的数据在3秒后消费完毕) + //目的是为了防止定时器长时间干等后,数据瞬间消费完毕 + setPlayPosition(_frame_cache.begin()->first); + } + //消费掉已经到期的帧 MediaSink::inputFrame(it->second); it = _frame_cache.erase(it); diff --git a/src/Http/HlsPlayer.h b/src/Http/HlsPlayer.h index bda64a77..0e99b47b 100644 --- a/src/Http/HlsPlayer.h +++ b/src/Http/HlsPlayer.h @@ -138,13 +138,19 @@ private: void inputFrame(const Frame::Ptr &frame) override; void onShutdown(const SockException &ex) override; void onTick(); + + int64_t getPlayPosition(); + void setPlayPosition(int64_t pos); + int64_t getBufferMS(); + private: - TSSegment::onSegment _on_ts; - DecoderImp::Ptr _decoder; - multimap _frame_cache; - Timer::Ptr _timer; + int64_t _ticker_offset = 0; Ticker _ticker; Stamp _stamp[2]; + Timer::Ptr _timer; + DecoderImp::Ptr _decoder; + TSSegment::onSegment _on_ts; + multimap _frame_cache; }; }//namespace mediakit diff --git a/src/Http/HttpBody.cpp b/src/Http/HttpBody.cpp index fb06e19b..9abf523f 100644 --- a/src/Http/HttpBody.cpp +++ b/src/Http/HttpBody.cpp @@ -135,7 +135,7 @@ Buffer::Ptr HttpFileBody::readData(uint32_t size) { //读到数据了 ret->setSize(iRead); _offset += iRead; - return std::move(ret); + return ret; } //读取文件异常,文件真实长度小于声明长度 _offset = _max_size; @@ -146,7 +146,7 @@ Buffer::Ptr HttpFileBody::readData(uint32_t size) { //mmap模式 auto ret = std::make_shared(_map_addr,_offset,size); _offset += size; - return std::move(ret); + return ret; } ////////////////////////////////////////////////////////////////// diff --git a/src/Http/HttpClient.cpp b/src/Http/HttpClient.cpp index c2c22745..27549e6a 100644 --- a/src/Http/HttpClient.cpp +++ b/src/Http/HttpClient.cpp @@ -100,7 +100,7 @@ void HttpClient::onConnect(const SockException &ex) { } //先假设http客户端只会接收一点点数据(只接受http头,节省内存) - _sock->setReadBuffer(std::make_shared(1 * 1024)); + getSock()->setReadBuffer(std::make_shared(1 * 1024)); _totalBodySize = 0; _recvedBodySize = 0; @@ -157,7 +157,7 @@ int64_t HttpClient::onRecvHeader(const char *data, uint64_t len) { if(_parser["Transfer-Encoding"] == "chunked"){ //我们认为这种情况下后面应该有大量的数据过来,加大接收缓存提高性能 - _sock->setReadBuffer(std::make_shared(256 * 1024)); + getSock()->setReadBuffer(std::make_shared(256 * 1024)); //如果Transfer-Encoding字段等于chunked,则认为后续的content是不限制长度的 _totalBodySize = -1; @@ -185,9 +185,9 @@ int64_t HttpClient::onRecvHeader(const char *data, uint64_t len) { _recvedBodySize = 0; if(_totalBodySize > 0){ //根据_totalBodySize设置接收缓存大小 - _sock->setReadBuffer(std::make_shared(MIN(_totalBodySize + 1,256 * 1024))); + getSock()->setReadBuffer(std::make_shared(MIN(_totalBodySize + 1,256 * 1024))); }else{ - _sock->setReadBuffer(std::make_shared(256 * 1024)); + getSock()->setReadBuffer(std::make_shared(256 * 1024)); } return -1; diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index 5d6464db..dd2647a1 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -27,7 +27,7 @@ static int kHlsCookieSecond = 60; static const string kCookieName = "ZL_COOKIE"; static const string kHlsSuffix = "/hls.m3u8"; -class HttpCookieAttachment{ +class HttpCookieAttachment { public: HttpCookieAttachment() {}; ~HttpCookieAttachment() {}; @@ -160,7 +160,7 @@ const string &HttpFileManager::getContentType(const char *name) { dot = strrchr(name, '.'); static StrCaseMap mapType; static onceToken token([&]() { - for (unsigned int i = 0; i < sizeof (s_mime_src) / sizeof (s_mime_src[0]); ++i) { + for (unsigned int i = 0; i < sizeof(s_mime_src) / sizeof(s_mime_src[0]); ++i) { mapType.emplace(s_mime_src[i][0], s_mime_src[i][1]); } }); @@ -183,8 +183,8 @@ static string searchIndexFile(const string &dir){ } set setFile; while ((pDirent = readdir(pDir)) != NULL) { - static set indexSet = {"index.html","index.htm","index"}; - if(indexSet.find(pDirent->d_name) != indexSet.end()){ + static set indexSet = {"index.html", "index.htm", "index"}; + if (indexSet.find(pDirent->d_name) != indexSet.end()) { string ret = pDirent->d_name; closedir(pDir); return ret; @@ -196,16 +196,16 @@ static string searchIndexFile(const string &dir){ static bool makeFolderMenu(const string &httpPath, const string &strFullPath, string &strRet) { GET_CONFIG(bool, dirMenu, Http::kDirMenu); - if(!dirMenu){ + if (!dirMenu) { //不允许浏览文件夹 return false; } string strPathPrefix(strFullPath); string last_dir_name; - if(strPathPrefix.back() == '/'){ + if (strPathPrefix.back() == '/') { strPathPrefix.pop_back(); - }else{ - last_dir_name = split(strPathPrefix,"/").back(); + } else { + last_dir_name = split(strPathPrefix, "/").back(); } if (!File::is_dir(strPathPrefix.data())) { @@ -249,24 +249,24 @@ static bool makeFolderMenu(const string &httpPath, const string &strFullPath, st if (File::is_special_dir(pDirent->d_name)) { continue; } - if(pDirent->d_name[0] == '.'){ + if (pDirent->d_name[0] == '.') { continue; } setFile.emplace(pDirent->d_name); } int i = 0; - for(auto &strFile :setFile ){ + for (auto &strFile :setFile) { string strAbsolutePath = strPathPrefix + "/" + strFile; bool isDir = File::is_dir(strAbsolutePath.data()); ss << "
  • " << i++ << "\t"; ss << ""; @@ -307,11 +307,16 @@ static bool end_of(const string &str, const string &substr){ //拦截hls的播放请求 static bool emitHlsPlayed(const Parser &parser, const MediaInfo &mediaInfo, const HttpSession::HttpAccessPathInvoker &invoker,TcpSession &sender){ //访问的hls.m3u8结尾,我们转换成kBroadcastMediaPlayed事件 - Broadcast::AuthInvoker mediaAuthInvoker = [invoker](const string &err){ + Broadcast::AuthInvoker auth_invoker = [invoker](const string &err) { //cookie有效期为kHlsCookieSecond - invoker(err,"",kHlsCookieSecond); + invoker(err, "", kHlsCookieSecond); }; - return NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed,mediaInfo,mediaAuthInvoker,static_cast(sender)); + bool flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed, mediaInfo, auth_invoker, static_cast(sender)); + if (!flag) { + //未开启鉴权,那么允许播放 + auth_invoker(""); + } + return flag; } class SockInfoImp : public SockInfo{ @@ -320,23 +325,23 @@ public: SockInfoImp() = default; ~SockInfoImp() override = default; - string get_local_ip() override{ + string get_local_ip() override { return _local_ip; } - uint16_t get_local_port() override{ + uint16_t get_local_port() override { return _local_port; } - string get_peer_ip() override{ + string get_peer_ip() override { return _peer_ip; } - uint16_t get_peer_port() override{ + uint16_t get_peer_port() override { return _peer_port; } - string getIdentifier() const override{ + string getIdentifier() const override { return _identifier; } @@ -379,7 +384,7 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI //上次cookie是限定本目录 if (attachment._err_msg.empty()) { //上次鉴权成功 - if(attachment._is_hls){ + if (attachment._is_hls) { //如果播放的是hls,那么刷新hls的cookie(获取ts文件也会刷新) cookie->updateTime(); cookie_from_header = false; @@ -429,7 +434,7 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI attachment._err_msg = errMsg; //记录访问的是否为hls attachment._is_hls = is_hls; - if(is_hls){ + if (is_hls) { //hls相关信息 attachment._hls_data = std::make_shared(mediaInfo, info); //hls未查找MediaSource @@ -437,13 +442,14 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI } (*cookie)[kCookieName].set(std::move(attachment)); callback(errMsg, cookie); - }else{ + } else { callback(errMsg, nullptr); } }; - if (is_hls && emitHlsPlayed(parser, mediaInfo, accessPathInvoker, sender)) { + if (is_hls) { //是hls的播放鉴权,拦截之 + emitHlsPlayed(parser, mediaInfo, accessPathInvoker, sender); return; } @@ -459,15 +465,15 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI * 发送404 Not Found */ static void sendNotFound(const HttpFileManager::invoker &cb) { - GET_CONFIG(string,notFound,Http::kNotFound); - cb("404 Not Found","text/html",StrCaseMap(),std::make_shared(notFound)); + GET_CONFIG(string, notFound, Http::kNotFound); + cb("404 Not Found", "text/html", StrCaseMap(), std::make_shared(notFound)); } /** * 拼接文件路径 */ static string pathCat(const string &a, const string &b){ - if(a.back() == '/'){ + if (a.back() == '/') { return a + b; } return a + '/' + b; @@ -490,7 +496,7 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo return; } - if(is_hls){ + if (is_hls) { //hls,那么移除掉后缀获取真实的stream_id并且修改协议为HLS const_cast(mediaInfo._schema) = HLS_SCHEMA; replace(const_cast(mediaInfo._streamid), kHlsSuffix, ""); @@ -500,7 +506,7 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo //判断是否有权限访问该文件 canAccessPath(sender, parser, mediaInfo, false, [cb, strFile, parser, is_hls, mediaInfo, weakSession , file_exist](const string &errMsg, const HttpServerCookie::Ptr &cookie) { auto strongSession = weakSession.lock(); - if(!strongSession){ + if (!strongSession) { //http客户端已经断开,不需要回复 return; } @@ -508,6 +514,7 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo //文件鉴权失败 StrCaseMap headerOut; if (cookie) { + auto lck = cookie->getLock(); headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookieName].get()._path); } cb("401 Unauthorized", "text/html", headerOut, std::make_shared(errMsg)); @@ -517,11 +524,12 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo auto response_file = [file_exist](const HttpServerCookie::Ptr &cookie, const HttpFileManager::invoker &cb, const string &strFile, const Parser &parser) { StrCaseMap httpHeader; if (cookie) { + auto lck = cookie->getLock(); httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookieName].get()._path); } HttpSession::HttpResponseInvoker invoker = [&](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) { if (cookie && file_exist) { - cookie->getLock(); + auto lck = cookie->getLock(); auto is_hls = (*cookie)[kCookieName].get()._is_hls; if (is_hls) { (*cookie)[kCookieName].get()._hls_data->addByteUsage(body->remainSize()); @@ -535,26 +543,47 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo if (!is_hls) { //不是hls,直接回复文件或404 response_file(cookie, cb, strFile, parser); - } else { - //是hls直播,判断是否存在 - bool have_find_media_src = false; - if(cookie){ - have_find_media_src = (*cookie)[kCookieName].get()._have_find_media_source; - if(!have_find_media_src){ - (*cookie)[kCookieName].get()._have_find_media_source = true; - } + return; + } + + //是hls直播,判断HLS直播流是否已经注册 + bool have_find_media_src = false; + if (cookie) { + auto lck = cookie->getLock(); + have_find_media_src = (*cookie)[kCookieName].get()._have_find_media_source; + if (!have_find_media_src) { + (*cookie)[kCookieName].get()._have_find_media_source = true; } - if(have_find_media_src){ - //之前该cookie已经通过MediaSource::findAsync查找过了,所以现在只以文件系统查找结果为准 + } + if (have_find_media_src) { + //之前该cookie已经通过MediaSource::findAsync查找过了,所以现在只以文件系统查找结果为准 + response_file(cookie, cb, strFile, parser); + return; + } + //hls文件不存在,我们等待其生成并延后回复 + MediaSource::findAsync(mediaInfo, strongSession, [response_file, cookie, cb, strFile, parser](const MediaSource::Ptr &src) { + if (cookie) { + auto lck = cookie->getLock(); + //尝试添加HlsMediaSource的观看人数(HLS是按需生成的,这样可以触发HLS文件的生成) + (*cookie)[kCookieName].get()._hls_data->addByteUsage(0); + } + if (src && File::is_file(strFile.data())) { + //流和m3u8文件都存在,那么直接返回文件 response_file(cookie, cb, strFile, parser); return; } - //hls文件不存在,我们等待其生成并延后回复 - MediaSource::findAsync(mediaInfo, strongSession, [response_file, cookie, cb, strFile, parser](const MediaSource::Ptr &src) { - //hls已经生成或者超时后仍未生成,那么不管怎么样都返回客户端 + auto hls = dynamic_pointer_cast(src); + if (!hls) { + //流不存在,那么直接返回文件(相当于纯粹的HLS文件服务器,但是会挂起播放器15秒左右(用于等待HLS流的注册)) + response_file(cookie, cb, strFile, parser); + return; + } + + //流存在,但是m3u8文件不存在,那么等待生成m3u8文件(HLS源注册后,并不会立即生成HLS文件,有人观看才会按需生成HLS文件) + hls->waitForFile([response_file, cookie, cb, strFile, parser]() { response_file(cookie, cb, strFile, parser); }); - } + }); }); } @@ -563,7 +592,7 @@ static string getFilePath(const Parser &parser,const MediaInfo &mediaInfo, TcpSe GET_CONFIG(string, rootPath, Http::kRootPath); auto ret = File::absolutePath(enableVhost ? mediaInfo._vhost + parser.Url() : parser.Url(), rootPath); NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpBeforeAccess, parser, ret, static_cast(sender)); - return std::move(ret); + return ret; } /** @@ -615,13 +644,13 @@ void HttpFileManager::onAccessPath(TcpSession &sender, Parser &parser, const Htt ////////////////////////////////////HttpResponseInvokerImp////////////////////////////////////// void HttpResponseInvokerImp::operator()(const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) const{ - if(_lambad){ - _lambad(codeOut,headerOut,body); + if (_lambad) { + _lambad(codeOut, headerOut, body); } } void HttpResponseInvokerImp::operator()(const string &codeOut, const StrCaseMap &headerOut, const string &body) const{ - this->operator()(codeOut,headerOut,std::make_shared(body)); + this->operator()(codeOut, headerOut, std::make_shared(body)); } HttpResponseInvokerImp::HttpResponseInvokerImp(const HttpResponseInvokerImp::HttpResponseInvokerLambda0 &lambda){ @@ -629,23 +658,23 @@ HttpResponseInvokerImp::HttpResponseInvokerImp(const HttpResponseInvokerImp::Htt } HttpResponseInvokerImp::HttpResponseInvokerImp(const HttpResponseInvokerImp::HttpResponseInvokerLambda1 &lambda){ - if(!lambda){ + if (!lambda) { _lambad = nullptr; return; } - _lambad = [lambda](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body){ + _lambad = [lambda](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) { string str; - if(body && body->remainSize()){ + if (body && body->remainSize()) { str = body->readData(body->remainSize())->toString(); } - lambda(codeOut,headerOut,str); + lambda(codeOut, headerOut, str); }; } void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader, const StrCaseMap &responseHeader, const string &filePath) const { - StrCaseMap &httpHeader = const_cast(responseHeader); + StrCaseMap &httpHeader = const_cast(responseHeader); std::shared_ptr fp(fopen(filePath.data(), "rb"), [](FILE *fp) { if (fp) { fclose(fp); @@ -654,8 +683,8 @@ void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader, if (!fp) { //打开文件失败 - GET_CONFIG(string,notFound,Http::kNotFound); - GET_CONFIG(string,charSet,Http::kCharSet); + GET_CONFIG(string, notFound, Http::kNotFound); + GET_CONFIG(string, charSet, Http::kCharSet); auto strContentType = StrPrinter << "text/html; charset=" << charSet << endl; httpHeader["Content-Type"] = strContentType; @@ -665,14 +694,14 @@ void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader, auto &strRange = const_cast(requestHeader)["Range"]; int64_t iRangeStart = 0; - int64_t iRangeEnd = 0 ; + int64_t iRangeEnd = 0; int64_t fileSize = HttpMultiFormBody::fileSize(fp.get()); const char *pcHttpResult = NULL; if (strRange.size() == 0) { //全部下载 pcHttpResult = "200 OK"; - iRangeEnd = fileSize - 1; + iRangeEnd = fileSize - 1; } else { //分节下载 pcHttpResult = "206 Partial Content"; diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index f161342f..ba996d93 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -8,15 +8,9 @@ * may be found in the AUTHORS file in the root of the source tree. */ -#if !defined(_WIN32) -#include -#endif //!defined(_WIN32) - #include #include #include -#include - #include "Common/config.h" #include "strCoding.h" #include "HttpSession.h" @@ -96,10 +90,10 @@ void HttpSession::onRecv(const Buffer::Ptr &pBuf) { } void HttpSession::onError(const SockException& err) { - if(_is_flv_stream){ + if(_is_live_stream){ uint64_t duration = _ticker.createdTime()/1000; - //flv播放器 - WarnP(this) << "FLV播放器(" + //flv/ts播放器 + WarnP(this) << "FLV/TS/FMP4播放器(" << _mediaInfo._vhost << "/" << _mediaInfo._app << "/" << _mediaInfo._streamid @@ -107,8 +101,8 @@ void HttpSession::onError(const SockException& err) { << ",耗时(s):" << duration; GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold); - if(_ui64TotalBytes > iFlowThreshold * 1024){ - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, duration , true, static_cast(*this)); + if(_total_bytes_usage > iFlowThreshold * 1024){ + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _total_bytes_usage, duration , true, static_cast(*this)); } return; } @@ -132,7 +126,7 @@ void HttpSession::onManager() { bool HttpSession::checkWebSocket(){ auto Sec_WebSocket_Key = _parser["Sec-WebSocket-Key"]; - if(Sec_WebSocket_Key.empty()){ + if (Sec_WebSocket_Key.empty()) { return false; } auto Sec_WebSocket_Accept = encodeBase64(SHA1::encode_bin(Sec_WebSocket_Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")); @@ -141,99 +135,95 @@ bool HttpSession::checkWebSocket(){ headerOut["Upgrade"] = "websocket"; headerOut["Connection"] = "Upgrade"; headerOut["Sec-WebSocket-Accept"] = Sec_WebSocket_Accept; - if(!_parser["Sec-WebSocket-Protocol"].empty()){ + if (!_parser["Sec-WebSocket-Protocol"].empty()) { headerOut["Sec-WebSocket-Protocol"] = _parser["Sec-WebSocket-Protocol"]; } - auto res_cb = [this,headerOut](){ - _flv_over_websocket = true; - sendResponse("101 Switching Protocols",false,nullptr,headerOut,nullptr, true); + auto res_cb = [this, headerOut]() { + _live_over_websocket = true; + sendResponse("101 Switching Protocols", false, nullptr, headerOut, nullptr, true); }; //判断是否为websocket-flv - if(checkLiveFlvStream(res_cb)){ + if (checkLiveStreamFlv(res_cb)) { //这里是websocket-flv直播请求 return true; } - //如果checkLiveFlvStream返回false,则代表不是websocket-flv,而是普通的websocket连接 - if(!onWebSocketConnect(_parser)){ - sendResponse("501 Not Implemented",true, nullptr, headerOut); + //判断是否为websocket-ts + if (checkLiveStreamTS(res_cb)) { + //这里是websocket-ts直播请求 return true; } - sendResponse("101 Switching Protocols",false, nullptr,headerOut); + + //判断是否为websocket-fmp4 + if (checkLiveStreamFMP4(res_cb)) { + //这里是websocket-fmp4直播请求 + return true; + } + + //这是普通的websocket连接 + if (!onWebSocketConnect(_parser)) { + sendResponse("501 Not Implemented", true, nullptr, headerOut); + return true; + } + sendResponse("101 Switching Protocols", false, nullptr, headerOut, nullptr, true); return true; } -//http-flv 链接格式:http://vhost-url:port/app/streamid.flv?key1=value1&key2=value2 -//如果url(除去?以及后面的参数)后缀是.flv,那么表明该url是一个http-flv直播。 -bool HttpSession::checkLiveFlvStream(const function &cb){ - auto pos = strrchr(_parser.Url().data(),'.'); - if(!pos){ - //未找到".flv"后缀 - return false; - } - if(strcasecmp(pos,".flv") != 0){ - //未找到".flv"后缀 +bool HttpSession::checkLiveStream(const string &schema, const string &url_suffix, const function &cb){ + auto pos = strcasestr(_parser.Url().data(), url_suffix.data()); + if (!pos || pos + url_suffix.size() != 1 + &_parser.Url().back()) { + //未找到后缀 return false; } - //这是个.flv的流 - _mediaInfo.parse(string(RTMP_SCHEMA) + "://" + _parser["Host"] + _parser.FullUrl()); - if(_mediaInfo._app.empty() || _mediaInfo._streamid.size() < 5){ + //这是个符合后缀的直播的流 + _mediaInfo.parse(schema + "://" + _parser["Host"] + _parser.FullUrl()); + if (_mediaInfo._app.empty() || _mediaInfo._streamid.size() < url_suffix.size() + 1) { //url不合法 return false; } - _mediaInfo._streamid.erase(_mediaInfo._streamid.size() - 4);//去除.flv后缀 - bool bClose = !strcasecmp(_parser["Connection"].data(),"close"); - - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + //去除后缀 + bool close_flag = !strcasecmp(_parser["Connection"].data(), "close"); + //流id去除后缀 + _mediaInfo._streamid.erase(_mediaInfo._streamid.size() - url_suffix.size()); + weak_ptr weak_self = dynamic_pointer_cast(shared_from_this()); //鉴权结果回调 - auto onRes = [cb, weakSelf, bClose](const string &err){ - auto strongSelf = weakSelf.lock(); - if (!strongSelf) { + auto onRes = [cb, weak_self, close_flag](const string &err) { + auto strong_self = weak_self.lock(); + if (!strong_self) { //本对象已经销毁 return; } - if(!err.empty()){ + if (!err.empty()) { //播放鉴权失败 - strongSelf->sendResponse("401 Unauthorized", bClose, nullptr, KeyValue(), std::make_shared(err)); + strong_self->sendResponse("401 Unauthorized", close_flag, nullptr, KeyValue(), std::make_shared(err)); return; } - //异步查找rtmp流 - MediaSource::findAsync(strongSelf->_mediaInfo, strongSelf, [weakSelf, bClose, cb](const MediaSource::Ptr &src) { - auto strongSelf = weakSelf.lock(); - if (!strongSelf) { + //异步查找直播流 + MediaSource::findAsync(strong_self->_mediaInfo, strong_self, [weak_self, close_flag, cb](const MediaSource::Ptr &src) { + auto strong_self = weak_self.lock(); + if (!strong_self) { //本对象已经销毁 return; } - auto rtmp_src = dynamic_pointer_cast(src); - if (!rtmp_src) { + if (!src) { //未找到该流 - strongSelf->sendNotFound(bClose); + strong_self->sendNotFound(close_flag); return; } - - if (!cb) { - //找到rtmp源,发送http头,负载后续发送 - strongSelf->sendResponse("200 OK", false, "video/x-flv", KeyValue(), nullptr, true); - } else { - //自定义发送http头 - cb(); - } - - //http-flv直播牺牲延时提升发送性能 - strongSelf->setSocketFlags(); - strongSelf->start(strongSelf->getPoller(), rtmp_src); - strongSelf->_is_flv_stream = true; + strong_self->_is_live_stream = true; + //触发回调 + cb(src); }); }; - Broadcast::AuthInvoker invoker = [weakSelf, onRes](const string &err) { - auto strongSelf = weakSelf.lock(); + Broadcast::AuthInvoker invoker = [weak_self, onRes](const string &err) { + auto strongSelf = weak_self.lock(); if (!strongSelf) { return; } @@ -250,34 +240,142 @@ bool HttpSession::checkLiveFlvStream(const function &cb){ return true; } +//http-fmp4 链接格式:http://vhost-url:port/app/streamid.live.mp4?key1=value1&key2=value2 +bool HttpSession::checkLiveStreamFMP4(const function &cb){ + return checkLiveStream(FMP4_SCHEMA, ".live.mp4", [this, cb](const MediaSource::Ptr &src) { + auto fmp4_src = dynamic_pointer_cast(src); + assert(fmp4_src); + if (!cb) { + //找到源,发送http头,负载后续发送 + sendResponse("200 OK", false, HttpFileManager::getContentType(".mp4").data(), KeyValue(), nullptr, true); + } else { + //自定义发送http头 + cb(); + } + + //直播牺牲延时提升发送性能 + setSocketFlags(); + onWrite(std::make_shared(fmp4_src->getInitSegment()), true); + weak_ptr weak_self = dynamic_pointer_cast(shared_from_this()); + _fmp4_reader = fmp4_src->getRing()->attach(getPoller()); + _fmp4_reader->setDetachCB([weak_self]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { + //本对象已经销毁 + return; + } + strong_self->shutdown(SockException(Err_shutdown, "fmp4 ring buffer detached")); + }); + _fmp4_reader->setReadCB([weak_self](const FMP4MediaSource::RingDataType &fmp4_list) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + //本对象已经销毁 + return; + } + int i = 0; + int size = fmp4_list->size(); + fmp4_list->for_each([&](const FMP4Packet::Ptr &ts) { + strong_self->onWrite(ts, ++i == size); + }); + }); + }); +} + +//http-ts 链接格式:http://vhost-url:port/app/streamid.live.ts?key1=value1&key2=value2 +bool HttpSession::checkLiveStreamTS(const function &cb){ + return checkLiveStream(TS_SCHEMA, ".live.ts", [this, cb](const MediaSource::Ptr &src) { + auto ts_src = dynamic_pointer_cast(src); + assert(ts_src); + if (!cb) { + //找到源,发送http头,负载后续发送 + sendResponse("200 OK", false, HttpFileManager::getContentType(".ts").data(), KeyValue(), nullptr, true); + } else { + //自定义发送http头 + cb(); + } + + //直播牺牲延时提升发送性能 + setSocketFlags(); + weak_ptr weak_self = dynamic_pointer_cast(shared_from_this()); + _ts_reader = ts_src->getRing()->attach(getPoller()); + _ts_reader->setDetachCB([weak_self](){ + auto strong_self = weak_self.lock(); + if (!strong_self) { + //本对象已经销毁 + return; + } + strong_self->shutdown(SockException(Err_shutdown,"ts ring buffer detached")); + }); + _ts_reader->setReadCB([weak_self](const TSMediaSource::RingDataType &ts_list) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + //本对象已经销毁 + return; + } + int i = 0; + int size = ts_list->size(); + ts_list->for_each([&](const TSPacket::Ptr &ts) { + strong_self->onWrite(ts, ++i == size); + }); + }); + }); +} + +//http-flv 链接格式:http://vhost-url:port/app/streamid.flv?key1=value1&key2=value2 +bool HttpSession::checkLiveStreamFlv(const function &cb){ + return checkLiveStream(RTMP_SCHEMA, ".flv", [this, cb](const MediaSource::Ptr &src) { + auto rtmp_src = dynamic_pointer_cast(src); + assert(rtmp_src); + if (!cb) { + //找到源,发送http头,负载后续发送 + sendResponse("200 OK", false, HttpFileManager::getContentType(".flv").data(), KeyValue(), nullptr, true); + } else { + //自定义发送http头 + cb(); + } + //直播牺牲延时提升发送性能 + setSocketFlags(); + start(getPoller(), rtmp_src); + }); +} + void HttpSession::Handle_Req_GET(int64_t &content_len) { Handle_Req_GET_l(content_len, true); } void HttpSession::Handle_Req_GET_l(int64_t &content_len, bool sendBody) { //先看看是否为WebSocket请求 - if(checkWebSocket()){ + if (checkWebSocket()) { content_len = -1; - _contentCallBack = [this](const char *data,uint64_t len){ - WebSocketSplitter::decode((uint8_t *)data,len); + _contentCallBack = [this](const char *data, uint64_t len) { + WebSocketSplitter::decode((uint8_t *) data, len); //_contentCallBack是可持续的,后面还要处理后续数据 return true; }; return; } - if(emitHttpEvent(false)){ + if (emitHttpEvent(false)) { //拦截http api事件 return; } - if(checkLiveFlvStream()){ + if (checkLiveStreamFlv()) { //拦截http-flv播放器 return; } - bool bClose = !strcasecmp(_parser["Connection"].data(),"close"); + if (checkLiveStreamTS()) { + //拦截http-ts播放器 + return; + } + if (checkLiveStreamFMP4()) { + //拦截http-fmp4播放器 + return; + } + + bool bClose = !strcasecmp(_parser["Connection"].data(),"close"); weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); HttpFileManager::onAccessPath(*this, _parser, [weakSelf, bClose](const string &status_code, const string &content_type, const StrCaseMap &responseHeader, const HttpBody::Ptr &body) { @@ -389,7 +487,7 @@ void HttpSession::sendResponse(const char *pcStatus, const char *pcContentType, const HttpSession::KeyValue &header, const HttpBody::Ptr &body, - bool is_http_flv ){ + bool no_content_length ){ GET_CONFIG(string,charSet,Http::kCharSet); GET_CONFIG(uint32_t,keepAliveSec,Http::kKeepAliveSecond); @@ -400,7 +498,7 @@ void HttpSession::sendResponse(const char *pcStatus, size = body->remainSize(); } - if(is_http_flv){ + if(no_content_length){ //http-flv直播是Keep-Alive类型 bClose = false; }else if(size >= INT64_MAX){ @@ -425,7 +523,7 @@ void HttpSession::sendResponse(const char *pcStatus, headerOut.emplace(kAccessControlAllowCredentials, "true"); } - if(!is_http_flv && size >= 0 && size < INT64_MAX){ + if(!no_content_length && size >= 0 && size < INT64_MAX){ //文件长度为固定值,且不是http-flv强制设置Content-Length headerOut[kContentLength] = to_string(size); } @@ -475,7 +573,7 @@ void HttpSession::sendResponse(const char *pcStatus, //发送http body AsyncSenderData::Ptr data = std::make_shared(shared_from_this(),body,bClose); - _sock->setOnFlush([data](){ + getSock()->setOnFlush([data](){ return AsyncSender::onSocketFlushed(data); }); AsyncSender::onSocketFlushed(data); @@ -542,10 +640,10 @@ void HttpSession::Handle_Req_POST(int64_t &content_len) { //根据Content-Length设置接收缓存大小 if(totalContentLen > 0){ - _sock->setReadBuffer(std::make_shared(MIN(totalContentLen + 1,256 * 1024))); + getSock()->setReadBuffer(std::make_shared(MIN(totalContentLen + 1,256 * 1024))); }else{ //不定长度的Content-Length - _sock->setReadBuffer(std::make_shared(256 * 1024)); + getSock()->setReadBuffer(std::make_shared(256 * 1024)); } if(totalContentLen > 0 && totalContentLen < maxReqSize ){ @@ -609,7 +707,7 @@ void HttpSession::setSocketFlags(){ GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS); if(mergeWriteMS > 0) { //推流模式下,关闭TCP_NODELAY会增加推流端的延时,但是服务器性能将提高 - SockUtil::setNoDelay(_sock->rawFD(), false); + SockUtil::setNoDelay(getSock()->rawFD(), false); //播放模式下,开启MSG_MORE会增加延时,但是能提高发送性能 setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); } @@ -622,29 +720,44 @@ void HttpSession::onWrite(const Buffer::Ptr &buffer, bool flush) { } _ticker.resetTime(); - if(!_flv_over_websocket){ - _ui64TotalBytes += buffer->size(); + if (!_live_over_websocket) { + _total_bytes_usage += buffer->size(); send(buffer); - }else{ + } else { WebSocketHeader header; header._fin = true; header._reserved = 0; header._opcode = WebSocketHeader::BINARY; header._mask_flag = false; - WebSocketSplitter::encode(header,buffer); + WebSocketSplitter::encode(header, buffer); } - if(flush){ + if (flush) { //本次刷新缓存后,下次不用刷新缓存 HttpSession::setSendFlushFlag(false); } } void HttpSession::onWebSocketEncodeData(const Buffer::Ptr &buffer){ - _ui64TotalBytes += buffer->size(); + _total_bytes_usage += buffer->size(); send(buffer); } +void HttpSession::onWebSocketDecodeComplete(const WebSocketHeader &header_in){ + WebSocketHeader& header = const_cast(header_in); + header._mask_flag = false; + + switch (header._opcode) { + case WebSocketHeader::CLOSE: { + encode(header, nullptr); + shutdown(SockException(Err_shutdown, "recv close request from client")); + break; + } + + default : break; + } +} + void HttpSession::onDetach() { shutdown(SockException(Err_shutdown,"rtmp ring buffer detached")); } diff --git a/src/Http/HttpSession.h b/src/Http/HttpSession.h index 09bcfca5..f1df5a8d 100644 --- a/src/Http/HttpSession.h +++ b/src/Http/HttpSession.h @@ -19,6 +19,8 @@ #include "WebSocketSplitter.h" #include "HttpCookieManager.h" #include "HttpFileManager.h" +#include "TS/TSMediaSource.h" +#include "FMP4/FMP4MediaSource.h" using namespace std; using namespace toolkit; @@ -47,6 +49,7 @@ public: void onError(const SockException &err) override; void onManager() override; static string urlDecode(const string &str); + protected: //FlvMuxer override void onWrite(const Buffer::Ptr &data, bool flush) override ; @@ -90,35 +93,49 @@ protected: * @param buffer websocket协议数据 */ void onWebSocketEncodeData(const Buffer::Ptr &buffer) override; + + /** + * 接收到完整的一个webSocket数据包后回调 + * @param header 数据包包头 + */ + void onWebSocketDecodeComplete(const WebSocketHeader &header_in) override; + private: void Handle_Req_GET(int64_t &content_len); void Handle_Req_GET_l(int64_t &content_len, bool sendBody); void Handle_Req_POST(int64_t &content_len); void Handle_Req_HEAD(int64_t &content_len); - bool checkLiveFlvStream(const function &cb = nullptr); + bool checkLiveStream(const string &schema, const string &url_suffix, const function &cb); + + bool checkLiveStreamFlv(const function &cb = nullptr); + bool checkLiveStreamTS(const function &cb = nullptr); + bool checkLiveStreamFMP4(const function &fmp4_list = nullptr); + bool checkWebSocket(); bool emitHttpEvent(bool doInvoke); void urlDecode(Parser &parser); void sendNotFound(bool bClose); void sendResponse(const char *pcStatus, bool bClose, const char *pcContentType = nullptr, const HttpSession::KeyValue &header = HttpSession::KeyValue(), - const HttpBody::Ptr &body = nullptr,bool is_http_flv = false); + const HttpBody::Ptr &body = nullptr, bool no_content_length = false); //设置socket标志 void setSocketFlags(); + private: + bool _is_live_stream = false; + bool _live_over_websocket = false; + //消耗的总流量 + uint64_t _total_bytes_usage = 0; string _origin; Parser _parser; Ticker _ticker; - //消耗的总流量 - uint64_t _ui64TotalBytes = 0; - //flv over http MediaInfo _mediaInfo; + TSMediaSource::RingType::RingReader::Ptr _ts_reader; + FMP4MediaSource::RingType::RingReader::Ptr _fmp4_reader; //处理content数据的callback function _contentCallBack; - bool _flv_over_websocket = false; - bool _is_flv_stream = false; }; diff --git a/src/Http/HttpTSPlayer.cpp b/src/Http/HttpTSPlayer.cpp index fb525f11..706e1df3 100644 --- a/src/Http/HttpTSPlayer.cpp +++ b/src/Http/HttpTSPlayer.cpp @@ -12,9 +12,9 @@ namespace mediakit { HttpTSPlayer::HttpTSPlayer(const EventPoller::Ptr &poller, bool split_ts){ - _segment.setOnSegment([this](const char *data, uint64_t len) { onPacket(data, len); }); - _poller = poller ? poller : EventPollerPool::Instance().getPoller(); _split_ts = split_ts; + _segment.setOnSegment([this](const char *data, uint64_t len) { onPacket(data, len); }); + setPoller(poller ? poller : EventPollerPool::Instance().getPoller()); } HttpTSPlayer::~HttpTSPlayer() {} @@ -25,8 +25,8 @@ int64_t HttpTSPlayer::onResponseHeader(const string &status, const HttpClient::H shutdown(SockException(Err_other, StrPrinter << "bad http status code:" + status)); return 0; } - auto contet_type = const_cast< HttpClient::HttpHeader &>(headers)["Content-Type"]; - if (contet_type.find("video/mp2t") == 0 || contet_type.find("video/mpeg") == 0) { + auto content_type = const_cast< HttpClient::HttpHeader &>(headers)["Content-Type"]; + if (content_type.find("video/mp2t") == 0 || content_type.find("video/mpeg") == 0) { _is_ts_content = true; } diff --git a/src/Http/WebSocketClient.h b/src/Http/WebSocketClient.h index 93f140be..4ecce5cb 100644 --- a/src/Http/WebSocketClient.h +++ b/src/Http/WebSocketClient.h @@ -38,11 +38,10 @@ public: template ClientTypeImp(ArgsType &&...args): ClientType(std::forward(args)...){} ~ClientTypeImp() override {}; + protected: /** * 发送前拦截并打包为websocket协议 - * @param buf - * @return */ int send(const Buffer::Ptr &buf) override{ if(_beforeSendCB){ @@ -50,6 +49,7 @@ protected: } return ClientType::send(buf); } + /** * 设置发送数据截取回调函数 * @param cb 截取回调函数 @@ -57,6 +57,7 @@ protected: void setOnBeforeSendCB(const onBeforeSendCB &cb){ _beforeSendCB = cb; } + private: onBeforeSendCB _beforeSendCB; }; @@ -73,7 +74,7 @@ public: HttpWsClient(ClientTypeImp &delegate) : _delegate(delegate){ _Sec_WebSocket_Key = encodeBase64(SHA1::encode_bin(makeRandStr(16, false))); - _poller = delegate.getPoller(); + setPoller(delegate.getPoller()); } ~HttpWsClient(){} @@ -108,6 +109,7 @@ public: header._mask_flag = true; WebSocketSplitter::encode(header, nullptr); } + protected: //HttpClientImp override @@ -124,6 +126,8 @@ protected: if(Sec_WebSocket_Accept == const_cast(headers)["Sec-WebSocket-Accept"]){ //success onWebSocketException(SockException()); + //防止ws服务器返回Content-Length + const_cast(headers).erase("Content-Length"); //后续全是websocket负载数据 return -1; } @@ -180,7 +184,6 @@ protected: /** * tcp连接结果 - * @param ex */ void onConnect(const SockException &ex) override{ if(ex){ @@ -194,7 +197,6 @@ protected: /** * tcp连接断开 - * @param ex */ void onErr(const SockException &ex) override{ //tcp断开或者shutdown导致的断开 @@ -208,7 +210,7 @@ protected: * @param header 数据包头 */ void onWebSocketDecodeHeader(const WebSocketHeader &header) override{ - _payload.clear(); + _payload_section.clear(); } /** @@ -219,10 +221,9 @@ protected: * @param recved 已接收数据长度(包含本次数据长度),等于header._payload_len时则接受完毕 */ void onWebSocketDecodePayload(const WebSocketHeader &header, const uint8_t *ptr, uint64_t len, uint64_t recved) override{ - _payload.append((char *)ptr,len); + _payload_section.append((char *)ptr,len); } - /** * 接收到完整的一个webSocket数据包后回调 * @param header 数据包包头 @@ -238,28 +239,46 @@ protected: //服务器主动关闭 WebSocketSplitter::encode(header,nullptr); shutdown(SockException(Err_eof,"websocket server close the connection")); - } break; + } + case WebSocketHeader::PING:{ //心跳包 header._opcode = WebSocketHeader::PONG; - WebSocketSplitter::encode(header,std::make_shared(std::move(_payload))); - } + WebSocketSplitter::encode(header,std::make_shared(std::move(_payload_section))); break; - case WebSocketHeader::CONTINUATION:{ + } - } - break; + case WebSocketHeader::CONTINUATION: case WebSocketHeader::TEXT: case WebSocketHeader::BINARY:{ - //接收完毕websocket数据包,触发onRecv事件 - _delegate.onRecv(std::make_shared(std::move(_payload))); + if (!header._fin) { + //还有后续分片数据, 我们先缓存数据,所有分片收集完成才一次性输出 + _payload_cache.append(std::move(_payload_section)); + if (_payload_cache.size() < MAX_WS_PACKET) { + //还有内存容量缓存分片数据 + break; + } + //分片缓存太大,需要清空 + } + + //最后一个包 + if (_payload_cache.empty()) { + //这个包是唯一个分片 + _delegate.onRecv(std::make_shared(header._opcode, header._fin, std::move(_payload_section))); + break; + } + + //这个包由多个分片组成 + _payload_cache.append(std::move(_payload_section)); + _delegate.onRecv(std::make_shared(header._opcode, header._fin, std::move(_payload_cache))); + _payload_cache.clear(); + break; } - break; - default: - break; + + default: break; } - _payload.clear(); + _payload_section.clear(); header._mask_flag = flag; } @@ -271,6 +290,7 @@ protected: void onWebSocketEncodeData(const Buffer::Ptr &buffer) override{ HttpClientImp::send(buffer); } + private: void onWebSocketException(const SockException &ex){ if(!ex){ @@ -292,7 +312,7 @@ private: }); //设置sock,否则shutdown等接口都无效 - _delegate.setSock(HttpClientImp::_sock); + _delegate.setSock(HttpClientImp::getSock()); //触发连接成功事件 _delegate.onConnect(ex); //拦截websocket数据接收 @@ -319,10 +339,10 @@ private: string _Sec_WebSocket_Key; function _onRecv; ClientTypeImp &_delegate; - string _payload; + string _payload_section; + string _payload_cache; }; - /** * Tcp客户端转WebSocket客户端模板, * 通过该模板,开发者再不修改TcpClient派生类任何代码的情况下快速实现WebSocket协议的包装 @@ -365,6 +385,7 @@ public: void startWebSocket(const string &ws_url,float fTimeOutSec = 3){ _wsClient->startWsClient(ws_url,fTimeOutSec); } + private: typename HttpWsClient::Ptr _wsClient; }; diff --git a/src/Http/WebSocketSession.h b/src/Http/WebSocketSession.h index ed9dd140..69b691f1 100644 --- a/src/Http/WebSocketSession.h +++ b/src/Http/WebSocketSession.h @@ -78,7 +78,6 @@ public: } }; - /** * 通过该模板类可以透明化WebSocket协议, * 用户只要实现WebSock协议下的具体业务协议,譬如基于WebSocket协议的Rtmp协议等 @@ -107,8 +106,9 @@ public: void attachServer(const TcpServer &server) override{ HttpSessionType::attachServer(server); - _weakServer = const_cast(server).shared_from_this(); + _weak_server = const_cast(server).shared_from_this(); } + protected: /** * websocket客户端连接上事件 @@ -117,12 +117,12 @@ protected: */ bool onWebSocketConnect(const Parser &header) override{ //创建websocket session类 - _session = _creator(header, *this,HttpSessionType::_sock); + _session = _creator(header, *this,HttpSessionType::getSock()); if(!_session){ //此url不允许创建websocket连接 return false; } - auto strongServer = _weakServer.lock(); + auto strongServer = _weak_server.lock(); if(strongServer){ _session->attachServer(*strongServer); } @@ -145,24 +145,20 @@ protected: //允许websocket客户端 return true; } + /** * 开始收到一个webSocket数据包 - * @param packet */ void onWebSocketDecodeHeader(const WebSocketHeader &packet) override{ //新包,原来的包残余数据清空掉 - _remian_data.clear(); + _payload_section.clear(); } /** * 收到websocket数据包负载 - * @param packet - * @param ptr - * @param len - * @param recved */ void onWebSocketDecodePayload(const WebSocketHeader &packet,const uint8_t *ptr,uint64_t len,uint64_t recved) override { - _remian_data.append((char *)ptr,len); + _payload_section.append((char *)ptr,len); } /** @@ -177,39 +173,60 @@ protected: switch (header._opcode){ case WebSocketHeader::CLOSE:{ HttpSessionType::encode(header,nullptr); - } + HttpSessionType::shutdown(SockException(Err_shutdown, "recv close request from client")); break; + } + case WebSocketHeader::PING:{ header._opcode = WebSocketHeader::PONG; - HttpSessionType::encode(header,std::make_shared(_remian_data)); - } + HttpSessionType::encode(header,std::make_shared(_payload_section)); break; - case WebSocketHeader::CONTINUATION:{ - } - break; + + case WebSocketHeader::CONTINUATION: case WebSocketHeader::TEXT: case WebSocketHeader::BINARY:{ - _session->onRecv(std::make_shared(_remian_data)); + if (!header._fin) { + //还有后续分片数据, 我们先缓存数据,所有分片收集完成才一次性输出 + _payload_cache.append(std::move(_payload_section)); + if (_payload_cache.size() < MAX_WS_PACKET) { + //还有内存容量缓存分片数据 + break; + } + //分片缓存太大,需要清空 + } + + //最后一个包 + if (_payload_cache.empty()) { + //这个包是唯一个分片 + _session->onRecv(std::make_shared(header._opcode, header._fin, std::move(_payload_section))); + break; + } + + //这个包由多个分片组成 + _payload_cache.append(std::move(_payload_section)); + _session->onRecv(std::make_shared(header._opcode, header._fin, std::move(_payload_cache))); + _payload_cache.clear(); + break; } - break; - default: - break; + + default: break; } - _remian_data.clear(); + _payload_section.clear(); header._mask_flag = flag; } /** - * 发送数据进行websocket协议打包后回调 - * @param buffer + * 发送数据进行websocket协议打包后回调 */ void onWebSocketEncodeData(const Buffer::Ptr &buffer) override{ HttpSessionType::send(buffer); } + private: - string _remian_data; - weak_ptr _weakServer; + string _payload_cache; + string _payload_section; + weak_ptr _weak_server; TcpSession::Ptr _session; Creator _creator; }; diff --git a/src/Http/WebSocketSplitter.h b/src/Http/WebSocketSplitter.h index 9b2bbea3..e241406c 100644 --- a/src/Http/WebSocketSplitter.h +++ b/src/Http/WebSocketSplitter.h @@ -16,10 +16,12 @@ #include #include #include "Network/Buffer.h" - using namespace std; using namespace toolkit; +//websocket组合包最大不得超过4MB(防止内存爆炸) +#define MAX_WS_PACKET (4 * 1024 * 1024) + namespace mediakit { class WebSocketHeader { @@ -44,6 +46,7 @@ public: CONTROL_RSVF = 0xF } Type; public: + WebSocketHeader() : _mask(4){ //获取_mask内部buffer的内存地址,该内存是malloc开辟的,地址为随机 uint64_t ptr = (uint64_t)(&_mask[0]); @@ -51,6 +54,7 @@ public: _mask.assign((uint8_t*)(&ptr), (uint8_t*)(&ptr) + 4); } virtual ~WebSocketHeader(){} + public: bool _fin; uint8_t _reserved; @@ -60,6 +64,26 @@ public: vector _mask; }; +//websocket协议收到的字符串类型缓存,用户协议层获取该数据传输的方式 +class WebSocketBuffer : public BufferString { +public: + typedef std::shared_ptr Ptr; + + template + WebSocketBuffer(WebSocketHeader::Type headType, bool fin, ARGS &&...args) + : _head_type(headType), _fin(fin), BufferString(std::forward(args)...) {} + + ~WebSocketBuffer() override {} + + WebSocketHeader::Type headType() const { return _head_type; } + + bool isFinished() const { return _fin; }; + +private: + WebSocketHeader::Type _head_type; + bool _fin; +}; + class WebSocketSplitter : public WebSocketHeader{ public: WebSocketSplitter(){} @@ -80,6 +104,7 @@ public: * @param buffer 负载数据 */ void encode(const WebSocketHeader &header,const Buffer::Ptr &buffer); + protected: /** * 收到一个webSocket数据包包头,后续将继续触发onWebSocketDecodePayload回调 @@ -96,7 +121,6 @@ protected: */ virtual void onWebSocketDecodePayload(const WebSocketHeader &header, const uint8_t *ptr, uint64_t len, uint64_t recved) {}; - /** * 接收到完整的一个webSocket数据包后回调 * @param header 数据包包头 @@ -109,8 +133,10 @@ protected: * @param len 数据指针长度 */ virtual void onWebSocketEncodeData(const Buffer::Ptr &buffer){}; + private: void onPayloadData(uint8_t *data, uint64_t len); + private: string _remain_data; int _mask_offset = 0; diff --git a/src/Player/MediaPlayer.cpp b/src/Player/MediaPlayer.cpp index 6b95a4eb..e77888f3 100644 --- a/src/Player/MediaPlayer.cpp +++ b/src/Player/MediaPlayer.cpp @@ -17,31 +17,43 @@ using namespace toolkit; namespace mediakit { MediaPlayer::MediaPlayer(const EventPoller::Ptr &poller) { - _poller = poller; - if(!_poller){ - _poller = EventPollerPool::Instance().getPoller(); - } + _poller = poller ? poller : EventPollerPool::Instance().getPoller(); } MediaPlayer::~MediaPlayer() { } -void MediaPlayer::play(const string &strUrl) { - _delegate = PlayerBase::createPlayer(_poller,strUrl); + +static void setOnCreateSocket_l(const std::shared_ptr &delegate, const Socket::onCreateSocket &cb){ + auto helper = dynamic_pointer_cast(delegate); + if (helper) { + helper->setOnCreateSocket(cb); + } +} + +void MediaPlayer::play(const string &url) { + _delegate = PlayerBase::createPlayer(_poller, url); + assert(_delegate); + setOnCreateSocket_l(_delegate, _on_create_socket); _delegate->setOnShutdown(_shutdownCB); _delegate->setOnPlayResult(_playResultCB); _delegate->setOnResume(_resumeCB); _delegate->setMediaSouce(_pMediaSrc); _delegate->mINI::operator=(*this); - _delegate->play(strUrl); + _delegate->play(url); } EventPoller::Ptr MediaPlayer::getPoller(){ return _poller; } -void MediaPlayer::pause(bool bPause) { +void MediaPlayer::setOnCreateSocket(Socket::onCreateSocket cb){ + setOnCreateSocket_l(_delegate, cb); + _on_create_socket = std::move(cb); +} + +void MediaPlayer::pause(bool pause) { if (_delegate) { - _delegate->pause(bPause); + _delegate->pause(pause); } } diff --git a/src/Player/MediaPlayer.h b/src/Player/MediaPlayer.h index 42b6eb61..0e41deb1 100644 --- a/src/Player/MediaPlayer.h +++ b/src/Player/MediaPlayer.h @@ -27,12 +27,15 @@ public: MediaPlayer(const EventPoller::Ptr &poller = nullptr); virtual ~MediaPlayer(); - void play(const string &strUrl) override; - void pause(bool bPause) override; + void play(const string &url) override; + void pause(bool pause) override; void teardown() override; EventPoller::Ptr getPoller(); + void setOnCreateSocket(Socket::onCreateSocket cb); + private: EventPoller::Ptr _poller; + Socket::onCreateSocket _on_create_socket; }; } /* namespace mediakit */ diff --git a/src/Player/PlayerBase.cpp b/src/Player/PlayerBase.cpp index ffe54f28..775f38d5 100644 --- a/src/Player/PlayerBase.cpp +++ b/src/Player/PlayerBase.cpp @@ -105,7 +105,7 @@ vector Demuxer::getTracks(bool trackReady) const { ret.emplace_back(_audioTrack); } } - return std::move(ret); + return ret; } float Demuxer::getDuration() const { diff --git a/src/Player/PlayerProxy.cpp b/src/Player/PlayerProxy.cpp index 6b726228..e63b5a6a 100644 --- a/src/Player/PlayerProxy.cpp +++ b/src/Player/PlayerProxy.cpp @@ -46,31 +46,23 @@ static uint8_t s_mute_adts[] = {0xff, 0xf1, 0x6c, 0x40, 0x2d, 0x3f, 0xfc, 0x00, #define MUTE_ADTS_DATA_LEN sizeof(s_mute_adts) #define MUTE_ADTS_DATA_MS 130 -PlayerProxy::PlayerProxy(const string &strVhost, - const string &strApp, - const string &strSrc, - bool bEnableRtsp, - bool bEnableRtmp, - bool bEnableHls, - bool bEnableMp4, - int iRetryCount, - const EventPoller::Ptr &poller) : MediaPlayer(poller){ - _strVhost = strVhost; - _strApp = strApp; - _strSrc = strSrc; - _bEnableRtsp = bEnableRtsp; - _bEnableRtmp = bEnableRtmp; - _bEnableHls = bEnableHls; - _bEnableMp4 = bEnableMp4; - _iRetryCount = iRetryCount; +PlayerProxy::PlayerProxy(const string &vhost, const string &app, const string &stream_id, + bool enable_hls, bool enable_mp4, int retry_count, const EventPoller::Ptr &poller) + : MediaPlayer(poller) { + _vhost = vhost; + _app = app; + _stream_id = stream_id; + _enable_hls = enable_hls; + _enable_mp4 = enable_mp4; + _retry_count = retry_count; } void PlayerProxy::setPlayCallbackOnce(const function &cb){ - _playCB = cb; + _on_play = cb; } void PlayerProxy::setOnClose(const function &cb){ - _onClose = cb; + _on_close = cb; } void PlayerProxy::play(const string &strUrlTmp) { @@ -82,16 +74,16 @@ void PlayerProxy::play(const string &strUrlTmp) { return; } - if(strongSelf->_playCB) { - strongSelf->_playCB(err); - strongSelf->_playCB = nullptr; + if(strongSelf->_on_play) { + strongSelf->_on_play(err); + strongSelf->_on_play = nullptr; } if(!err) { // 播放成功 *piFailedCnt = 0;//连续播放失败次数清0 strongSelf->onPlaySuccess(); - }else if(*piFailedCnt < strongSelf->_iRetryCount || strongSelf->_iRetryCount < 0) { + }else if(*piFailedCnt < strongSelf->_retry_count || strongSelf->_retry_count < 0) { // 播放失败,延时重试播放 strongSelf->rePlay(strUrlTmp,(*piFailedCnt)++); } @@ -101,21 +93,21 @@ void PlayerProxy::play(const string &strUrlTmp) { if(!strongSelf) { return; } - if(strongSelf->_mediaMuxer) { - auto tracks = strongSelf->getTracks(false); + if(strongSelf->_muxer) { + auto tracks = strongSelf->MediaPlayer::getTracks(false); for (auto & track : tracks){ - track->delDelegate(strongSelf->_mediaMuxer.get()); + track->delDelegate(strongSelf->_muxer.get()); } GET_CONFIG(bool,resetWhenRePlay,General::kResetWhenRePlay); if (resetWhenRePlay) { - strongSelf->_mediaMuxer.reset(); + strongSelf->_muxer.reset(); } else { - strongSelf->_mediaMuxer->resetTracks(); + strongSelf->_muxer->resetTracks(); } } //播放异常中断,延时重试播放 - if(*piFailedCnt < strongSelf->_iRetryCount || strongSelf->_iRetryCount < 0) { + if(*piFailedCnt < strongSelf->_retry_count || strongSelf->_retry_count < 0) { strongSelf->rePlay(strUrlTmp,(*piFailedCnt)++); } }); @@ -125,14 +117,12 @@ void PlayerProxy::play(const string &strUrlTmp) { if(dynamic_pointer_cast(_delegate)){ //rtsp拉流 GET_CONFIG(bool,directProxy,Rtsp::kDirectProxy); - if(directProxy && _bEnableRtsp){ - mediaSource = std::make_shared(_strVhost,_strApp,_strSrc); + if(directProxy){ + mediaSource = std::make_shared(_vhost, _app, _stream_id); } } else if(dynamic_pointer_cast(_delegate)){ - //rtmp拉流 - if(_bEnableRtmp){ - mediaSource = std::make_shared(_strVhost,_strApp,_strSrc); - } + //rtmp拉流,rtmp强制直接代理 + mediaSource = std::make_shared(_vhost, _app, _stream_id); } if(mediaSource){ setMediaSouce(mediaSource); @@ -143,6 +133,7 @@ void PlayerProxy::play(const string &strUrlTmp) { PlayerProxy::~PlayerProxy() { _timer.reset(); } + void PlayerProxy::rePlay(const string &strUrl,int iFailedCnt){ auto iDelay = MAX(2 * 1000, MIN(iFailedCnt * 3000, 60*1000)); weak_ptr weakSelf = shared_from_this(); @@ -164,16 +155,17 @@ bool PlayerProxy::close(MediaSource &sender,bool force) { } //通知其停止推流 - weak_ptr weakSlef = dynamic_pointer_cast(shared_from_this()); - getPoller()->async_first([weakSlef]() { - auto stronSelf = weakSlef.lock(); - if (stronSelf) { - stronSelf->_mediaMuxer.reset(); - stronSelf->setMediaSouce(nullptr); - stronSelf->teardown(); - if(stronSelf->_onClose){ - stronSelf->_onClose(); - } + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + getPoller()->async_first([weakSelf]() { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return; + } + strongSelf->_muxer.reset(); + strongSelf->setMediaSouce(nullptr); + strongSelf->teardown(); + if (strongSelf->_on_close) { + strongSelf->_on_close(); } }); WarnL << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force; @@ -181,7 +173,7 @@ bool PlayerProxy::close(MediaSource &sender,bool force) { } int PlayerProxy::totalReaderCount(){ - return (_mediaMuxer ? _mediaMuxer->totalReaderCount() : 0) + (_pMediaSrc ? _pMediaSrc->readerCount() : 0); + return (_muxer ? _muxer->totalReaderCount() : 0) + (_pMediaSrc ? _pMediaSrc->readerCount() : 0); } int PlayerProxy::totalReaderCount(MediaSource &sender) { @@ -193,88 +185,90 @@ public: typedef std::shared_ptr Ptr; MuteAudioMaker(){}; - virtual ~MuteAudioMaker(){} + ~MuteAudioMaker() override {} + void inputFrame(const Frame::Ptr &frame) override { if(frame->getTrackType() == TrackVideo){ - auto iAudioIndex = frame->dts() / MUTE_ADTS_DATA_MS; - if(_iAudioIndex != iAudioIndex){ - _iAudioIndex = iAudioIndex; - auto aacFrame = std::make_shared((char *)MUTE_ADTS_DATA, MUTE_ADTS_DATA_LEN, _iAudioIndex * MUTE_ADTS_DATA_MS, 0); + auto audio_idx = frame->dts() / MUTE_ADTS_DATA_MS; + if(_audio_idx != audio_idx){ + _audio_idx = audio_idx; + auto aacFrame = std::make_shared(CodecAAC, (char *)MUTE_ADTS_DATA, MUTE_ADTS_DATA_LEN, _audio_idx * MUTE_ADTS_DATA_MS, 0 ,ADTS_HEADER_LEN); FrameDispatcher::inputFrame(aacFrame); } } } private: - class AACFrameCacheAble : public AACFrameNoCacheAble{ + class FrameFromStaticPtr : public FrameFromPtr{ public: template - AACFrameCacheAble(ARGS && ...args) : AACFrameNoCacheAble(std::forward(args)...){}; - virtual ~AACFrameCacheAble() = default; + FrameFromStaticPtr(ARGS && ...args) : FrameFromPtr(std::forward(args)...) {}; + ~FrameFromStaticPtr() override = default; bool cacheAble() const override { return true; } }; + private: - int _iAudioIndex = 0; + int _audio_idx = 0; }; void PlayerProxy::onPlaySuccess() { GET_CONFIG(bool,resetWhenRePlay,General::kResetWhenRePlay); if (dynamic_pointer_cast(_pMediaSrc)) { //rtsp拉流代理 - if (resetWhenRePlay || !_mediaMuxer) { - _mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), false, _bEnableRtmp, _bEnableHls, _bEnableMp4)); + if (resetWhenRePlay || !_muxer) { + _muxer.reset(new MultiMediaSourceMuxer(_vhost, _app, _stream_id, getDuration(), false, true, _enable_hls, _enable_mp4)); } } else if (dynamic_pointer_cast(_pMediaSrc)) { //rtmp拉流代理 - if (resetWhenRePlay || !_mediaMuxer) { - _mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), _bEnableRtsp, false, _bEnableHls, _bEnableMp4)); + if (resetWhenRePlay || !_muxer) { + _muxer.reset(new MultiMediaSourceMuxer(_vhost, _app, _stream_id, getDuration(), true, false, _enable_hls, _enable_mp4)); } } else { //其他拉流代理 - if (resetWhenRePlay || !_mediaMuxer) { - _mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), _bEnableRtsp, _bEnableRtmp, _bEnableHls, _bEnableMp4)); + if (resetWhenRePlay || !_muxer) { + _muxer.reset(new MultiMediaSourceMuxer(_vhost, _app, _stream_id, getDuration(), true, true, _enable_hls, _enable_mp4)); } } - _mediaMuxer->setMediaListener(shared_from_this()); + _muxer->setMediaListener(shared_from_this()); - auto videoTrack = getTrack(TrackVideo,false); - if(videoTrack){ + auto videoTrack = getTrack(TrackVideo, false); + if (videoTrack) { //添加视频 - _mediaMuxer->addTrack(videoTrack); + _muxer->addTrack(videoTrack); //视频数据写入_mediaMuxer - videoTrack->addDelegate(_mediaMuxer); + videoTrack->addDelegate(_muxer); } //是否添加静音音频 - GET_CONFIG(bool,addMuteAudio,General::kAddMuteAudio); + GET_CONFIG(bool, addMuteAudio, General::kAddMuteAudio); auto audioTrack = getTrack(TrackAudio, false); - if(audioTrack){ + if (audioTrack) { //添加音频 - _mediaMuxer->addTrack(audioTrack); + _muxer->addTrack(audioTrack); //音频数据写入_mediaMuxer - audioTrack->addDelegate(_mediaMuxer); - }else if(addMuteAudio && videoTrack){ + audioTrack->addDelegate(_muxer); + } else if (addMuteAudio && videoTrack) { //没有音频信息,产生一个静音音频 MuteAudioMaker::Ptr audioMaker = std::make_shared(); //videoTrack把数据写入MuteAudioMaker videoTrack->addDelegate(audioMaker); //添加一个静音Track至_mediaMuxer - _mediaMuxer->addTrack(std::make_shared()); + _muxer->addTrack(std::make_shared()); //MuteAudioMaker生成静音音频然后写入_mediaMuxer; - audioMaker->addDelegate(_mediaMuxer); + audioMaker->addDelegate(_muxer); } //添加完毕所有track,防止单track情况下最大等待3秒 - _mediaMuxer->addTrackCompleted(); + _muxer->addTrackCompleted(); - if(_pMediaSrc){ - _pMediaSrc->setTrackSource(_mediaMuxer); + if (_pMediaSrc) { + //让_muxer对象拦截一部分事件(比如说录像相关事件) + _pMediaSrc->setListener(_muxer); } } - } /* namespace mediakit */ diff --git a/src/Player/PlayerProxy.h b/src/Player/PlayerProxy.h index 4437a68a..35089ee5 100644 --- a/src/Player/PlayerProxy.h +++ b/src/Player/PlayerProxy.h @@ -15,32 +15,22 @@ #include "Common/Device.h" #include "Player/MediaPlayer.h" #include "Util/TimeTicker.h" - using namespace std; using namespace toolkit; - namespace mediakit { -class PlayerProxy :public MediaPlayer, - public std::enable_shared_from_this , - public MediaSourceEvent{ +class PlayerProxy : public MediaPlayer, public MediaSourceEvent, public std::enable_shared_from_this { public: typedef std::shared_ptr Ptr; - //如果iRetryCount<0,则一直重试播放;否则重试iRetryCount次数 + //如果retry_count<0,则一直重试播放;否则重试retry_count次数 //默认一直重试 - PlayerProxy(const string &strVhost, - const string &strApp, - const string &strSrc, - bool bEnableRtsp = true, - bool bEnableRtmp = true, - bool bEnableHls = true, - bool bEnableMp4 = false, - int iRetryCount = -1, - const EventPoller::Ptr &poller = nullptr); + PlayerProxy(const string &vhost, const string &app, const string &stream_id, + bool enable_hls = true, bool enable_mp4 = false, + int retry_count = -1, const EventPoller::Ptr &poller = nullptr); - virtual ~PlayerProxy(); + ~PlayerProxy() override; /** * 设置play结果回调,只触发一次;在play执行之前有效 @@ -64,27 +54,26 @@ public: * 获取观看总人数 */ int totalReaderCount() ; + private: //MediaSourceEvent override bool close(MediaSource &sender,bool force) override; int totalReaderCount(MediaSource &sender) override; void rePlay(const string &strUrl,int iFailedCnt); void onPlaySuccess(); + private: - bool _bEnableRtsp; - bool _bEnableRtmp; - bool _bEnableHls; - bool _bEnableMp4; - int _iRetryCount; - MultiMediaSourceMuxer::Ptr _mediaMuxer; - string _strVhost; - string _strApp; - string _strSrc; + bool _enable_hls; + bool _enable_mp4; + int _retry_count; + string _vhost; + string _app; + string _stream_id; Timer::Ptr _timer; - function _playCB; - function _onClose; + function _on_close; + function _on_play; + MultiMediaSourceMuxer::Ptr _muxer; }; } /* namespace mediakit */ - #endif /* SRC_DEVICE_PLAYERPROXY_H_ */ diff --git a/src/Pusher/MediaPusher.cpp b/src/Pusher/MediaPusher.cpp index f7fca4ed..822f5103 100644 --- a/src/Pusher/MediaPusher.cpp +++ b/src/Pusher/MediaPusher.cpp @@ -19,34 +19,44 @@ namespace mediakit { MediaPusher::MediaPusher(const MediaSource::Ptr &src, const EventPoller::Ptr &poller) { _src = src; - _poller = poller; - if(!_poller){ - _poller = EventPollerPool::Instance().getPoller(); - } + _poller = poller ? poller : EventPollerPool::Instance().getPoller(); } MediaPusher::MediaPusher(const string &schema, - const string &strVhost, - const string &strApp, - const string &strStream, + const string &vhost, + const string &app, + const string &stream, const EventPoller::Ptr &poller) : - MediaPusher(MediaSource::find(schema,strVhost,strApp,strStream),poller){ + MediaPusher(MediaSource::find(schema, vhost, app, stream), poller){ } MediaPusher::~MediaPusher() { } -void MediaPusher::publish(const string &strUrl) { - _delegate = PusherBase::createPusher(_poller,_src.lock(),strUrl); + +static void setOnCreateSocket_l(const std::shared_ptr &delegate, const Socket::onCreateSocket &cb){ + auto helper = dynamic_pointer_cast(delegate); + if (helper) { + helper->setOnCreateSocket(cb); + } +} + +void MediaPusher::publish(const string &url) { + _delegate = PusherBase::createPusher(_poller, _src.lock(), url); + assert(_delegate); + setOnCreateSocket_l(_delegate, _on_create_socket); _delegate->setOnShutdown(_shutdownCB); _delegate->setOnPublished(_publishCB); _delegate->mINI::operator=(*this); - _delegate->publish(strUrl); + _delegate->publish(url); } EventPoller::Ptr MediaPusher::getPoller(){ return _poller; } - +void MediaPusher::setOnCreateSocket(Socket::onCreateSocket cb){ + setOnCreateSocket_l(_delegate, cb); + _on_create_socket = std::move(cb); +} } /* namespace mediakit */ diff --git a/src/Pusher/MediaPusher.h b/src/Pusher/MediaPusher.h index 5e4974fb..2e2f985e 100644 --- a/src/Pusher/MediaPusher.h +++ b/src/Pusher/MediaPusher.h @@ -24,20 +24,24 @@ public: typedef std::shared_ptr Ptr; MediaPusher(const string &schema, - const string &strVhost, - const string &strApp, - const string &strStream, + const string &vhost, + const string &app, + const string &stream, const EventPoller::Ptr &poller = nullptr); MediaPusher(const MediaSource::Ptr &src, const EventPoller::Ptr &poller = nullptr); virtual ~MediaPusher(); - void publish(const string &strUrl) override; + + void publish(const string &url) override; EventPoller::Ptr getPoller(); + void setOnCreateSocket(Socket::onCreateSocket cb); + private: std::weak_ptr _src; EventPoller::Ptr _poller; + Socket::onCreateSocket _on_create_socket; }; } /* namespace mediakit */ diff --git a/src/Record/HlsMaker.cpp b/src/Record/HlsMaker.cpp index 0ae69d16..a91b3e05 100644 --- a/src/Record/HlsMaker.cpp +++ b/src/Record/HlsMaker.cpp @@ -32,27 +32,27 @@ void HlsMaker::makeIndexFile(bool eof) { } } - auto sequence = _seg_number ? (_file_index > _seg_number ? _file_index - _seg_number : 0LL) : 0LL; + auto sequence = _seg_number ? (_file_index > _seg_number ? _file_index - _seg_number : 0LL) : 0LL; string m3u8; - snprintf(file_content,sizeof(file_content), - "#EXTM3U\n" - "#EXT-X-VERSION:3\n" - "#EXT-X-ALLOW-CACHE:NO\n" - "#EXT-X-TARGETDURATION:%u\n" - "#EXT-X-MEDIA-SEQUENCE:%llu\n", - (maxSegmentDuration + 999) / 1000, - sequence); + snprintf(file_content, sizeof(file_content), + "#EXTM3U\n" + "#EXT-X-VERSION:3\n" + "#EXT-X-ALLOW-CACHE:NO\n" + "#EXT-X-TARGETDURATION:%u\n" + "#EXT-X-MEDIA-SEQUENCE:%llu\n", + (maxSegmentDuration + 999) / 1000, + sequence); m3u8.assign(file_content); for (auto &tp : _seg_dur_list) { - snprintf(file_content,sizeof(file_content), "#EXTINF:%.3f,\n%s\n", std::get<0>(tp) / 1000.0, std::get<1>(tp).data()); + snprintf(file_content, sizeof(file_content), "#EXTINF:%.3f,\n%s\n", std::get<0>(tp) / 1000.0, std::get<1>(tp).data()); m3u8.append(file_content); } if (eof) { - snprintf(file_content,sizeof(file_content),"#EXT-X-ENDLIST\n"); + snprintf(file_content, sizeof(file_content), "#EXT-X-ENDLIST\n"); m3u8.append(file_content); } onWriteHls(m3u8.data(), m3u8.size()); @@ -61,12 +61,15 @@ void HlsMaker::makeIndexFile(bool eof) { void HlsMaker::inputData(void *data, uint32_t len, uint32_t timestamp, bool is_idr_fast_packet) { if (data && len) { - if(is_idr_fast_packet){ + if (is_idr_fast_packet) { + //尝试切片ts addNewSegment(timestamp); } - onWriteSegment((char *) data, len); - //记录上次写入数据时间 - _ticker_last_data.resetTime(); + if (!_last_file_name.empty()) { + //存在切片才写入ts数据 + onWriteSegment((char *) data, len); + _last_timestamp = timestamp; + } } else { //resetTracks时触发此逻辑 flushLastSegment(true); @@ -74,7 +77,7 @@ void HlsMaker::inputData(void *data, uint32_t len, uint32_t timestamp, bool is_i } void HlsMaker::delOldSegment() { - if(_seg_number == 0){ + if (_seg_number == 0) { //如果设置为保留0个切片,则认为是保存为点播 return; } @@ -83,15 +86,15 @@ void HlsMaker::delOldSegment() { _seg_dur_list.pop_front(); } - GET_CONFIG(uint32_t,segRetain,Hls::kSegmentRetain); + GET_CONFIG(uint32_t, segRetain, Hls::kSegmentRetain); //但是实际保存的切片个数比m3u8所述多若干个,这样做的目的是防止播放器在切片删除前能下载完毕 if (_file_index > _seg_number + segRetain) { onDelSegment(_file_index - _seg_number - segRetain - 1); } } -void HlsMaker::addNewSegment(uint32_t) { - if(!_last_file_name.empty() && _ticker.elapsedTime() < _seg_duration * 1000){ +void HlsMaker::addNewSegment(uint32_t stamp) { + if (!_last_file_name.empty() && stamp - _last_seg_timestamp < _seg_duration * 1000) { //存在上个切片,并且未到分片时间 return; } @@ -100,28 +103,36 @@ void HlsMaker::addNewSegment(uint32_t) { flushLastSegment(_seg_number == 0); //新增切片 _last_file_name = onOpenSegment(_file_index++); - //重置切片计时器 - _ticker.resetTime(); + //记录本次切片的起始时间戳 + _last_seg_timestamp = stamp; } void HlsMaker::flushLastSegment(bool eof){ - if(_last_file_name.empty()){ + if (_last_file_name.empty()) { //不存在上个切片 return; } //文件创建到最后一次数据写入的时间即为切片长度 - auto seg_dur = _ticker.elapsedTime() - _ticker_last_data.elapsedTime(); - if(seg_dur <= 0){ + auto seg_dur = _last_timestamp - _last_seg_timestamp; + if (seg_dur <= 0) { seg_dur = 100; } - _seg_dur_list.push_back(std::make_tuple(seg_dur, _last_file_name)); + _seg_dur_list.push_back(std::make_tuple(seg_dur, std::move(_last_file_name))); + _last_file_name.clear(); delOldSegment(); makeIndexFile(eof); - _last_file_name.clear(); + onFlushLastSegment(seg_dur); } bool HlsMaker::isLive() { return _seg_number != 0; } +void HlsMaker::clear() { + _file_index = 0; + _last_seg_timestamp = 0; + _seg_dur_list.clear(); + _last_file_name.clear(); +} + }//namespace mediakit \ No newline at end of file diff --git a/src/Record/HlsMaker.h b/src/Record/HlsMaker.h index 2a2324c3..7eff8e10 100644 --- a/src/Record/HlsMaker.h +++ b/src/Record/HlsMaker.h @@ -39,6 +39,17 @@ public: * @param is_idr_fast_packet 是否为关键帧第一个包 */ void inputData(void *data, uint32_t len, uint32_t timestamp, bool is_idr_fast_packet); + + /** + * 是否为直播 + */ + bool isLive(); + + /** + * 清空记录 + */ + void clear(); + protected: /** * 创建ts切片文件回调 @@ -68,15 +79,17 @@ protected: virtual void onWriteHls(const char *data, int len) = 0; /** - * 关闭上个ts切片并且写入m3u8索引 - * @param eof + * 上一个 ts 切片写入完成, 可在这里进行通知处理 + * @param duration_ms 上一个 ts 切片的时长, 单位为毫秒 */ - void flushLastSegment(bool eof = false); + virtual void onFlushLastSegment(uint32_t duration_ms) {}; /** - * 是否为直播 + * 关闭上个ts切片并且写入m3u8索引 + * @param eof HLS直播是否已结束 */ - bool isLive(); + void flushLastSegment(bool eof); + private: /** * 生成m3u8文件 @@ -94,12 +107,13 @@ private: * @param timestamp */ void addNewSegment(uint32_t timestamp); + private: - uint32_t _seg_number = 0; float _seg_duration = 0; + uint32_t _seg_number = 0; + uint32_t _last_timestamp = 0; + uint32_t _last_seg_timestamp = 0; uint64_t _file_index = 0; - Ticker _ticker; - Ticker _ticker_last_data; string _last_file_name; std::deque > _seg_dur_list; }; diff --git a/src/Record/HlsMakerImp.cpp b/src/Record/HlsMakerImp.cpp index 28f88714..93959c1b 100644 --- a/src/Record/HlsMakerImp.cpp +++ b/src/Record/HlsMakerImp.cpp @@ -8,9 +8,12 @@ * may be found in the AUTHORS file in the root of the source tree. */ +#include +#include #include "HlsMakerImp.h" #include "Util/util.h" #include "Util/uv_errno.h" + using namespace toolkit; namespace mediakit { @@ -24,45 +27,61 @@ HlsMakerImp::HlsMakerImp(const string &m3u8_file, _path_hls = m3u8_file; _params = params; _buf_size = bufSize; - _file_buf.reset(new char[bufSize],[](char *ptr){ + _file_buf.reset(new char[bufSize], [](char *ptr) { delete[] ptr; }); + + _info.folder = _path_prefix; } HlsMakerImp::~HlsMakerImp() { + clearCache(); +} + +void HlsMakerImp::clearCache() { //录制完了 flushLastSegment(true); - if(isLive()){ + if (isLive()) { //hls直播才删除文件 + clear(); + _file = nullptr; + _segment_file_paths.clear(); File::delete_file(_path_prefix.data()); } } string HlsMakerImp::onOpenSegment(int index) { - string segment_name , segment_path; + string segment_name, segment_path; { auto strDate = getTimeStr("%Y-%m-%d"); auto strHour = getTimeStr("%H"); auto strTime = getTimeStr("%M-%S"); segment_name = StrPrinter << strDate + "/" + strHour + "/" + strTime << "_" << index << ".ts"; - segment_path = _path_prefix + "/" + segment_name; - if(isLive()){ - _segment_file_paths.emplace(index,segment_path); + segment_path = _path_prefix + "/" + segment_name; + if (isLive()) { + _segment_file_paths.emplace(index, segment_path); } } _file = makeFile(segment_path, true); - if(!_file){ - WarnL << "create file falied," << segment_path << " " << get_uv_errmsg(); + + //保存本切片的元数据 + _info.start_time = ::time(NULL); + _info.file_name = segment_name; + _info.file_path = segment_path; + _info.url = _info.app + "/" + _info.stream + "/" + segment_name; + + if (!_file) { + WarnL << "create file failed," << segment_path << " " << get_uv_errmsg(); } - if(_params.empty()){ - return std::move(segment_name); + if (_params.empty()) { + return segment_name; } - return std::move(segment_name + "?" + _params); + return segment_name + "?" + _params; } void HlsMakerImp::onDelSegment(int index) { auto it = _segment_file_paths.find(index); - if(it == _segment_file_paths.end()){ + if (it == _segment_file_paths.end()) { return; } File::delete_file(it->second.data()); @@ -77,27 +96,39 @@ void HlsMakerImp::onWriteSegment(const char *data, int len) { void HlsMakerImp::onWriteHls(const char *data, int len) { auto hls = makeFile(_path_hls); - if(hls){ - fwrite(data,len,1,hls.get()); + if (hls) { + fwrite(data, len, 1, hls.get()); hls.reset(); - if(_media_src){ - _media_src->registHls(); + if (_media_src) { + _media_src->registHls(true); } - } else{ - WarnL << "create hls file falied," << _path_hls << " " << get_uv_errmsg(); + } else { + WarnL << "create hls file failed," << _path_hls << " " << get_uv_errmsg(); } //DebugL << "\r\n" << string(data,len); } +void HlsMakerImp::onFlushLastSegment(uint32_t duration_ms) { + GET_CONFIG(bool, broadcastRecordTs, Hls::kBroadcastRecordTs); + if (broadcastRecordTs) { + //关闭ts文件以便获取正确的文件大小 + _file = nullptr; + _info.time_len = duration_ms / 1000.0; + struct stat fileData; + stat(_info.file_path.data(), &fileData); + _info.file_size = fileData.st_size; + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordTs, _info); + } +} -std::shared_ptr HlsMakerImp::makeFile(const string &file,bool setbuf) { +std::shared_ptr HlsMakerImp::makeFile(const string &file, bool setbuf) { auto file_buf = _file_buf; - auto ret= shared_ptr(File::create_file(file.data(), "wb"), [file_buf](FILE *fp) { + auto ret = shared_ptr(File::create_file(file.data(), "wb"), [file_buf](FILE *fp) { if (fp) { fclose(fp); } }); - if(ret && setbuf){ + if (ret && setbuf) { setvbuf(ret.get(), _file_buf.get(), _IOFBF, _buf_size); } return ret; @@ -105,9 +136,12 @@ std::shared_ptr HlsMakerImp::makeFile(const string &file,bool setbuf) { void HlsMakerImp::setMediaSource(const string &vhost, const string &app, const string &stream_id) { _media_src = std::make_shared(vhost, app, stream_id); + _info.app = app; + _info.stream = stream_id; + _info.vhost = vhost; } -MediaSource::Ptr HlsMakerImp::getMediaSource() const{ +HlsMediaSource::Ptr HlsMakerImp::getMediaSource() const { return _media_src; } diff --git a/src/Record/HlsMakerImp.h b/src/Record/HlsMakerImp.h index 80ea54dd..155909b7 100644 --- a/src/Record/HlsMakerImp.h +++ b/src/Record/HlsMakerImp.h @@ -16,6 +16,7 @@ #include #include "HlsMaker.h" #include "HlsMediaSource.h" + using namespace std; namespace mediakit { @@ -27,7 +28,8 @@ public: uint32_t bufSize = 64 * 1024, float seg_duration = 5, uint32_t seg_number = 3); - virtual ~HlsMakerImp(); + + ~HlsMakerImp() override; /** * 设置媒体信息 @@ -41,23 +43,33 @@ public: * 获取MediaSource * @return */ - MediaSource::Ptr getMediaSource() const; + HlsMediaSource::Ptr getMediaSource() const; + + /** + * 清空缓存 + */ + void clearCache(); + protected: string onOpenSegment(int index) override ; void onDelSegment(int index) override; void onWriteSegment(const char *data, int len) override; void onWriteHls(const char *data, int len) override; + void onFlushLastSegment(uint32_t duration_ms) override; + private: std::shared_ptr makeFile(const string &file,bool setbuf = false); + private: - HlsMediaSource::Ptr _media_src; - map _segment_file_paths; + int _buf_size; + string _params; + string _path_hls; + string _path_prefix; + RecordInfo _info; std::shared_ptr _file; std::shared_ptr _file_buf; - string _path_prefix; - string _path_hls; - string _params; - int _buf_size; + HlsMediaSource::Ptr _media_src; + map _segment_file_paths; }; }//namespace mediakit diff --git a/src/Record/HlsMediaSource.cpp b/src/Record/HlsMediaSource.cpp index 4133a206..99f2022e 100644 --- a/src/Record/HlsMediaSource.cpp +++ b/src/Record/HlsMediaSource.cpp @@ -23,9 +23,7 @@ void HlsCookieData::addReaderCount(){ if(!*_added){ auto src = dynamic_pointer_cast(MediaSource::find(HLS_SCHEMA,_info._vhost,_info._app,_info._streamid)); if(src){ - src->modifyReaderCount(true); *_added = true; - _src = src; _ring_reader = src->getRing()->attach(EventPollerPool::Instance().getPoller()); auto added = _added; _ring_reader->setDetachCB([added](){ @@ -38,18 +36,15 @@ void HlsCookieData::addReaderCount(){ HlsCookieData::~HlsCookieData() { if (*_added) { - auto src = _src.lock(); - if (src) { - src->modifyReaderCount(false); - } uint64_t duration = (_ticker.createdTime() - _ticker.elapsedTime()) / 1000; WarnL << _sock_info->getIdentifier() << "(" << _sock_info->get_peer_ip() << ":" << _sock_info->get_peer_port() << ") " << "HLS播放器(" << _info._vhost << "/" << _info._app << "/" << _info._streamid << ")断开,耗时(s):" << duration; GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold); - if (_bytes > iFlowThreshold * 1024) { - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _info, _bytes, duration, true, static_cast(*_sock_info)); + uint64_t bytes = _bytes.load(); + if (bytes > iFlowThreshold * 1024) { + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _info, bytes, duration, true, static_cast(*_sock_info)); } } } diff --git a/src/Record/HlsMediaSource.h b/src/Record/HlsMediaSource.h index d6bcab5e..b5b8357f 100644 --- a/src/Record/HlsMediaSource.h +++ b/src/Record/HlsMediaSource.h @@ -21,12 +21,8 @@ public: friend class HlsCookieData; typedef RingBuffer RingType; typedef std::shared_ptr Ptr; - HlsMediaSource(const string &vhost, const string &app, const string &stream_id) : MediaSource(HLS_SCHEMA, vhost, app, stream_id){ - _readerCount = 0; - _ring = std::make_shared(); - } - - virtual ~HlsMediaSource() = default; + HlsMediaSource(const string &vhost, const string &app, const string &stream_id) : MediaSource(HLS_SCHEMA, vhost, app, stream_id){} + ~HlsMediaSource() override = default; /** * 获取媒体源的环形缓冲 @@ -37,41 +33,57 @@ public: /** * 获取播放器个数 - * @return */ int readerCount() override { - return _readerCount.load(); + return _ring ? _ring->readerCount() : 0; } /** - * 注册hls + * 生成m3u8文件时触发 + * @param file_created 是否产生了hls文件 */ - void registHls(){ - if(!_registed){ + void registHls(bool file_created){ + if (!_is_regist) { + _is_regist = true; + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + auto lam = [weakSelf](int size) { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return; + } + strongSelf->onReaderChanged(size); + }; + _ring = std::make_shared(0, std::move(lam)); + onReaderChanged(0); regist(); - _registed = true; } - } -private: - /** - * 修改观看者个数 - * @param add 添加海思删除 - */ - void modifyReaderCount(bool add) { - if (add) { - ++_readerCount; + if (!file_created) { + //没产生文件 return; } - - if (--_readerCount == 0) { - onNoneReader(); + //m3u8文件生成,发送给播放器 + decltype(_list_cb) copy; + { + lock_guard lck(_mtx_cb); + copy.swap(_list_cb); } + copy.for_each([](const function &cb) { + cb(); + }); } + + void waitForFile(function cb) { + //等待生成m3u8文件 + lock_guard lck(_mtx_cb); + _list_cb.emplace_back(std::move(cb)); + } + private: - atomic_int _readerCount; - bool _registed = false; + bool _is_regist = false; RingType::Ptr _ring; + mutex _mtx_cb; + List > _list_cb; }; class HlsCookieData{ @@ -80,13 +92,14 @@ public: HlsCookieData(const MediaInfo &info, const std::shared_ptr &sock_info); ~HlsCookieData(); void addByteUsage(uint64_t bytes); + private: void addReaderCount(); + private: - uint64_t _bytes = 0; + atomic _bytes {0}; MediaInfo _info; std::shared_ptr _added; - weak_ptr _src; Ticker _ticker; std::shared_ptr _sock_info; HlsMediaSource::RingType::RingReader::Ptr _ring_reader; diff --git a/src/Record/HlsRecorder.h b/src/Record/HlsRecorder.h index 20c3d68f..c753c8b4 100644 --- a/src/Record/HlsRecorder.h +++ b/src/Record/HlsRecorder.h @@ -15,37 +15,70 @@ #include "TsMuxer.h" namespace mediakit { -class HlsRecorder -#if defined(ENABLE_HLS) -: public TsMuxer -#endif - { +class HlsRecorder : public MediaSourceEventInterceptor, public TsMuxer, public std::enable_shared_from_this { public: typedef std::shared_ptr Ptr; HlsRecorder(const string &m3u8_file, const string ¶ms){ - GET_CONFIG(uint32_t,hlsNum,Hls::kSegmentNum); - GET_CONFIG(uint32_t,hlsBufSize,Hls::kFileBufSize); - GET_CONFIG(uint32_t,hlsDuration,Hls::kSegmentDuration); - _hls = new HlsMakerImp(m3u8_file,params,hlsBufSize,hlsDuration,hlsNum); + GET_CONFIG(uint32_t, hlsNum, Hls::kSegmentNum); + GET_CONFIG(uint32_t, hlsBufSize, Hls::kFileBufSize); + GET_CONFIG(uint32_t, hlsDuration, Hls::kSegmentDuration); + _hls = std::make_shared(m3u8_file, params, hlsBufSize, hlsDuration, hlsNum); + //清空上次的残余文件 + _hls->clearCache(); } - ~HlsRecorder(){ - delete _hls; - } - void setMediaSource(const string &vhost, const string &app, const string &stream_id){ + + ~HlsRecorder(){} + + void setMediaSource(const string &vhost, const string &app, const string &stream_id) { _hls->setMediaSource(vhost, app, stream_id); } - MediaSource::Ptr getMediaSource() const{ - return _hls->getMediaSource(); + void setListener(const std::weak_ptr &listener) { + _listener = listener; + _hls->getMediaSource()->setListener(shared_from_this()); + //先注册媒体流,后续可以按需生成 + _hls->getMediaSource()->registHls(false); } -#if defined(ENABLE_HLS) -protected: - void onTs(const void *packet, int bytes,uint32_t timestamp,bool is_idr_fast_packet) override { - _hls->inputData((char *)packet,bytes,timestamp, is_idr_fast_packet); - }; -#endif + + int readerCount() { + return _hls->getMediaSource()->readerCount(); + } + + void onReaderChanged(MediaSource &sender, int size) override { + //hls保留切片个数为0时代表为hls录制(不删除切片),那么不管有无观看者都一直生成hls + _enabled = _hls->isLive() ? size : true; + if (!size && _hls->isLive()) { + //hls直播时,如果无人观看就删除视频缓存,目的是为了防止视频跳跃 + _clear_cache = true; + } + MediaSourceEventInterceptor::onReaderChanged(sender, size); + } + + bool isEnabled() { + //缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 + return _clear_cache ? true : _enabled; + } + + void inputFrame(const Frame::Ptr &frame) override{ + if (_clear_cache) { + _clear_cache = false; + _hls->clearCache(); + } + if (_enabled) { + TsMuxer::inputFrame(frame); + } + } + private: - HlsMakerImp *_hls; + void onTs(const void *packet, int bytes, uint32_t timestamp, bool is_idr_fast_packet) override { + _hls->inputData((char *) packet, bytes, timestamp, is_idr_fast_packet); + } + +private: + //默认不生成hls文件,有播放器时再生成 + bool _enabled = false; + bool _clear_cache = false; + std::shared_ptr _hls; }; }//namespace mediakit #endif //HLSRECORDER_H diff --git a/src/Record/MP4.cpp b/src/Record/MP4.cpp index 0dd1756a..684e4be3 100644 --- a/src/Record/MP4.cpp +++ b/src/Record/MP4.cpp @@ -13,34 +13,125 @@ #include "Util/File.h" #include "Util/logger.h" #include "Common/config.h" +#include "fmp4-writer.h" + using namespace toolkit; namespace mediakit { +/////////////////////////////////////////////////mp4_writer_t///////////////////////////////////////////////// + +struct mp4_writer_t { + int is_fmp4; + union { + fmp4_writer_t *fmp4; + mov_writer_t *mov; + } u; +}; + +mp4_writer_t* mp4_writer_create(int is_fmp4, const struct mov_buffer_t *buffer, void* param, int flags){ + mp4_writer_t *mp4 = (mp4_writer_t *) malloc(sizeof(mp4_writer_t)); + mp4->is_fmp4 = is_fmp4; + if (is_fmp4) { + mp4->u.fmp4 = fmp4_writer_create(buffer, param, flags); + } else { + mp4->u.mov = mov_writer_create(buffer, param, flags); + } + return mp4; +} + +void mp4_writer_destroy(mp4_writer_t* mp4){ + if (mp4->is_fmp4) { + fmp4_writer_destroy(mp4->u.fmp4); + } else { + mov_writer_destroy(mp4->u.mov); + } + free(mp4); +} + +int mp4_writer_add_audio(mp4_writer_t* mp4, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size){ + if (mp4->is_fmp4) { + return fmp4_writer_add_audio(mp4->u.fmp4, object, channel_count, bits_per_sample, sample_rate, extra_data, extra_data_size); + } else { + return mov_writer_add_audio(mp4->u.mov, object, channel_count, bits_per_sample, sample_rate, extra_data, extra_data_size); + } +} + +int mp4_writer_add_video(mp4_writer_t* mp4, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size){ + if (mp4->is_fmp4) { + return fmp4_writer_add_video(mp4->u.fmp4, object, width, height, extra_data, extra_data_size); + } else { + return mov_writer_add_video(mp4->u.mov, object, width, height, extra_data, extra_data_size); + } +} + +int mp4_writer_add_subtitle(mp4_writer_t* mp4, uint8_t object, const void* extra_data, size_t extra_data_size){ + if (mp4->is_fmp4) { + return fmp4_writer_add_subtitle(mp4->u.fmp4, object, extra_data, extra_data_size); + } else { + return mov_writer_add_subtitle(mp4->u.mov, object, extra_data, extra_data_size); + } +} + +int mp4_writer_write(mp4_writer_t* mp4, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags){ + if (mp4->is_fmp4) { + return fmp4_writer_write(mp4->u.fmp4, track, data, bytes, pts, dts, flags); + } else { + return mov_writer_write(mp4->u.mov, track, data, bytes, pts, dts, flags); + } +} + +int mp4_writer_write_l(mp4_writer_t* mp4, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags, int add_nalu_size){ + if (mp4->is_fmp4) { + return fmp4_writer_write_l(mp4->u.fmp4, track, data, bytes, pts, dts, flags, add_nalu_size); + } else { + return mov_writer_write_l(mp4->u.mov, track, data, bytes, pts, dts, flags, add_nalu_size); + } +} + +int mp4_writer_save_segment(mp4_writer_t* mp4){ + if (mp4->is_fmp4) { + return fmp4_writer_save_segment(mp4->u.fmp4); + } else { + return -1; + } +} + +int mp4_writer_init_segment(mp4_writer_t* mp4){ + if (mp4->is_fmp4) { + return fmp4_writer_init_segment(mp4->u.fmp4); + } else { + return -1; + } +} + +/////////////////////////////////////////////////MP4FileIO///////////////////////////////////////////////// + static struct mov_buffer_t s_io = { - [](void* ctx, void* data, uint64_t bytes) { - MP4File *thiz = (MP4File *)ctx; - return thiz->onRead(data,bytes); + [](void *ctx, void *data, uint64_t bytes) { + MP4FileIO *thiz = (MP4FileIO *) ctx; + return thiz->onRead(data, bytes); }, - [](void* ctx, const void* data, uint64_t bytes){ - MP4File *thiz = (MP4File *)ctx; - return thiz->onWrite(data,bytes); + [](void *ctx, const void *data, uint64_t bytes) { + MP4FileIO *thiz = (MP4FileIO *) ctx; + return thiz->onWrite(data, bytes); }, - [](void* ctx, uint64_t offset) { - MP4File *thiz = (MP4File *)ctx; + [](void *ctx, uint64_t offset) { + MP4FileIO *thiz = (MP4FileIO *) ctx; return thiz->onSeek(offset); }, - [](void* ctx){ - MP4File *thiz = (MP4File *)ctx; + [](void *ctx) { + MP4FileIO *thiz = (MP4FileIO *) ctx; return thiz->onTell(); } }; -MP4File::Writer MP4File::createWriter(){ - GET_CONFIG(bool, mp4FastStart, Record::kFastStart); +MP4FileIO::Writer MP4FileIO::createWriter(int flags, bool is_fmp4){ Writer writer; - writer.reset(mov_writer_create(&s_io,this,mp4FastStart ? MOV_FLAG_FASTSTART : 0),[](mov_writer_t *ptr){ + Ptr self = shared_from_this(); + //保存自己的强引用,防止提前释放 + writer.reset(mp4_writer_create(is_fmp4, &s_io,this, flags),[self](mp4_writer_t *ptr){ if(ptr){ - mov_writer_destroy(ptr); + mp4_writer_destroy(ptr); } }); if(!writer){ @@ -49,9 +140,11 @@ MP4File::Writer MP4File::createWriter(){ return writer; } -MP4File::Reader MP4File::createReader(){ +MP4FileIO::Reader MP4FileIO::createReader(){ Reader reader; - reader.reset(mov_reader_create(&s_io,this),[](mov_reader_t *ptr){ + Ptr self = shared_from_this(); + //保存自己的强引用,防止提前释放 + reader.reset(mov_reader_create(&s_io,this),[self](mov_reader_t *ptr){ if(ptr){ mov_reader_destroy(ptr); } @@ -62,15 +155,17 @@ MP4File::Reader MP4File::createReader(){ return reader; } +/////////////////////////////////////////////////////MP4FileDisk///////////////////////////////////////////////////////// + #if defined(_WIN32) || defined(_WIN64) #define fseek64 _fseeki64 -#define ftell64 _ftelli64 + #define ftell64 _ftelli64 #else -#define fseek64 fseek -#define ftell64 ftell + #define fseek64 fseek + #define ftell64 ftell #endif -void MP4File::openFile(const char *file,const char *mode) { +void MP4FileDisk::openFile(const char *file, const char *mode) { //创建文件 auto fp = File::create_file(file, mode); if(!fp){ @@ -98,28 +193,74 @@ void MP4File::openFile(const char *file,const char *mode) { }); } -void MP4File::closeFile() { +void MP4FileDisk::closeFile() { _file = nullptr; } -int MP4File::onRead(void *data, uint64_t bytes) { +int MP4FileDisk::onRead(void *data, uint64_t bytes) { if (bytes == fread(data, 1, bytes, _file.get())){ return 0; } return 0 != ferror(_file.get()) ? ferror(_file.get()) : -1 /*EOF*/; } -int MP4File::onWrite(const void *data, uint64_t bytes) { +int MP4FileDisk::onWrite(const void *data, uint64_t bytes) { return bytes == fwrite(data, 1, bytes, _file.get()) ? 0 : ferror(_file.get()); } -int MP4File::onSeek(uint64_t offset) { +int MP4FileDisk::onSeek(uint64_t offset) { return fseek64(_file.get(), offset, SEEK_SET); } -uint64_t MP4File::onTell() { +uint64_t MP4FileDisk::onTell() { return ftell64(_file.get()); } +/////////////////////////////////////////////////////MP4FileMemory///////////////////////////////////////////////////////// + +string MP4FileMemory::getAndClearMemory(){ + string ret; + ret.swap(_memory); + _offset = 0; + return ret; +} + +uint64_t MP4FileMemory::fileSize() const{ + return _memory.size(); +} + +uint64_t MP4FileMemory::onTell(){ + return _offset; +} + +int MP4FileMemory::onSeek(uint64_t offset){ + if (offset > _memory.size()) { + return -1; + } + _offset = offset; + return 0; +} + +int MP4FileMemory::onRead(void *data, uint64_t bytes){ + if (_offset >= _memory.size()) { + //EOF + return -1; + } + bytes = MIN(bytes, _memory.size() - _offset); + memcpy(data, _memory.data(), bytes); + _offset += bytes; + return 0; +} + +int MP4FileMemory::onWrite(const void *data, uint64_t bytes){ + if (_offset + bytes > _memory.size()) { + //需要扩容 + _memory.resize(_offset + bytes); + } + memcpy((uint8_t *) _memory.data() + _offset, data, bytes); + _offset += bytes; + return 0; +} + }//namespace mediakit #endif //NABLE_MP4RECORD diff --git a/src/Record/MP4.h b/src/Record/MP4.h index b76362a9..3b7ae1c3 100644 --- a/src/Record/MP4.h +++ b/src/Record/MP4.h @@ -23,27 +23,127 @@ using namespace std; namespace mediakit { -class MP4File { -public: - friend struct mov_buffer_t; - typedef std::shared_ptr Writer; - typedef std::shared_ptr Reader; - MP4File() = default; - virtual ~MP4File() = default; +//以下是fmp4/mov的通用接口,简单包装了ireader/media-server的接口 +typedef struct mp4_writer_t mp4_writer_t; +mp4_writer_t* mp4_writer_create(int is_fmp4, const struct mov_buffer_t *buffer, void* param, int flags); +void mp4_writer_destroy(mp4_writer_t* mp4); +int mp4_writer_add_audio(mp4_writer_t* mp4, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size); +int mp4_writer_add_video(mp4_writer_t* mp4, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size); +int mp4_writer_add_subtitle(mp4_writer_t* mp4, uint8_t object, const void* extra_data, size_t extra_data_size); +int mp4_writer_write(mp4_writer_t* mp4, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags); +int mp4_writer_write_l(mp4_writer_t* mp4, int track, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags, int add_nalu_size); +int mp4_writer_save_segment(mp4_writer_t* mp4); +int mp4_writer_init_segment(mp4_writer_t* mp4); - Writer createWriter(); - Reader createReader(); - void openFile(const char *file,const char *mode); +//mp4文件IO的抽象接口类 +class MP4FileIO : public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + using Writer = std::shared_ptr; + using Reader = std::shared_ptr; + + MP4FileIO() = default; + virtual ~MP4FileIO() = default; + + /** + * 创建mp4复用器 + * @param flags 支持0、MOV_FLAG_FASTSTART、MOV_FLAG_SEGMENT + * @param is_fmp4 是否为fmp4还是普通mp4 + * @return mp4复用器 + */ + virtual Writer createWriter(int flags, bool is_fmp4 = false); + + /** + * 创建mp4解复用器 + * @return mp4解复用器 + */ + virtual Reader createReader(); + + /** + * 获取文件读写位置 + */ + virtual uint64_t onTell() = 0; + + /** + * seek至文件某处 + * @param offset 文件偏移量 + * @return 是否成功(0成功) + */ + virtual int onSeek(uint64_t offset) = 0; + + /** + * 从文件读取一定数据 + * @param data 数据存放指针 + * @param bytes 指针长度 + * @return 是否成功(0成功) + */ + virtual int onRead(void *data, uint64_t bytes) = 0; + + /** + * 写入文件一定数据 + * @param data 数据指针 + * @param bytes 数据长度 + * @return 是否成功(0成功) + */ + virtual int onWrite(const void *data, uint64_t bytes) = 0; +}; + +//磁盘MP4文件类 +class MP4FileDisk : public MP4FileIO { +public: + using Ptr = std::shared_ptr; + MP4FileDisk() = default; + ~MP4FileDisk() override = default; + + /** + * 打开磁盘文件 + * @param file 文件路径 + * @param mode fopen的方式 + */ + void openFile(const char *file, const char *mode); + + /** + * 关闭磁盘文件 + */ void closeFile(); - int onRead(void* data, uint64_t bytes); - int onWrite(const void* data, uint64_t bytes); - int onSeek( uint64_t offset); - uint64_t onTell(); +protected: + uint64_t onTell() override; + int onSeek(uint64_t offset) override; + int onRead(void *data, uint64_t bytes) override; + int onWrite(const void *data, uint64_t bytes) override; + private: std::shared_ptr _file; }; +class MP4FileMemory : public MP4FileIO{ +public: + using Ptr = std::shared_ptr; + MP4FileMemory() = default; + ~MP4FileMemory() override = default; + + /** + * 获取文件大小 + */ + uint64_t fileSize() const; + + /** + * 获取并清空文件缓存 + */ + string getAndClearMemory(); + +protected: + uint64_t onTell() override; + int onSeek(uint64_t offset) override; + int onRead(void *data, uint64_t bytes) override; + int onWrite(const void *data, uint64_t bytes) override; + +private: + uint64_t _offset = 0; + string _memory; +}; + }//namespace mediakit #endif //NABLE_MP4RECORD #endif //ZLMEDIAKIT_MP4_H diff --git a/src/Record/MP4Demuxer.cpp b/src/Record/MP4Demuxer.cpp index 7b0fadd4..f4fa5a13 100644 --- a/src/Record/MP4Demuxer.cpp +++ b/src/Record/MP4Demuxer.cpp @@ -15,21 +15,24 @@ #include "Extension/H264.h" #include "Extension/AAC.h" #include "Extension/G711.h" +#include "Extension/Opus.h" using namespace toolkit; namespace mediakit { -MP4Demuxer::MP4Demuxer(const char *file) { - openFile(file,"rb+"); - _mov_reader = createReader(); - getAllTracks(); - _duration_ms = mov_reader_getduration(_mov_reader.get()); -} +MP4Demuxer::MP4Demuxer() {} MP4Demuxer::~MP4Demuxer() { _mov_reader = nullptr; closeFile(); } +void MP4Demuxer::openMP4(const string &file){ + openFile(file.data(),"rb+"); + _mov_reader = createReader(); + getAllTracks(); + _duration_ms = mov_reader_getduration(_mov_reader.get()); +} + int MP4Demuxer::getAllTracks() { static mov_reader_trackinfo_t s_on_track = { [](void *param, uint32_t track, uint8_t object, int width, int height, const void *extra, size_t bytes) { @@ -119,14 +122,22 @@ void MP4Demuxer::onAudioTrack(uint32_t track_id, uint8_t object, int channel_cou case MOV_OBJECT_AAC:{ auto audio = std::make_shared(bytes > 0 ? string((char *)extra,bytes) : ""); _track_to_codec.emplace(track_id, audio); - } break; + } + case MOV_OBJECT_G711a: case MOV_OBJECT_G711u:{ auto audio = std::make_shared(object == MOV_OBJECT_G711a ? CodecG711A : CodecG711U, sample_rate, channel_count, bit_per_sample / channel_count ); _track_to_codec.emplace(track_id, audio); - } break; + } + + case MOV_OBJECT_OPUS: { + auto audio = std::make_shared(); + _track_to_codec.emplace(track_id, audio); + break; + } + default: WarnL << "不支持该编码类型的MP4,已忽略:" << getObjectName(object); break; @@ -149,6 +160,8 @@ struct Context{ BufferRaw::Ptr buffer; }; +#define DATA_OFFSET ADTS_HEADER_LEN + Frame::Ptr MP4Demuxer::readFrame(bool &keyFrame, bool &eof) { keyFrame = false; eof = false; @@ -163,9 +176,9 @@ Frame::Ptr MP4Demuxer::readFrame(bool &keyFrame, bool &eof) { static mov_onalloc mov_onalloc = [](void *param, int bytes) -> void * { Context *ctx = (Context *) param; ctx->buffer = ctx->thiz->_buffer_pool.obtain(); - ctx->buffer->setCapacity(bytes + 1); - ctx->buffer->setSize(bytes); - return ctx->buffer->data(); + ctx->buffer->setCapacity(bytes + DATA_OFFSET + 1); + ctx->buffer->setSize(bytes + DATA_OFFSET); + return ctx->buffer->data() + DATA_OFFSET; }; Context ctx = {this, 0}; @@ -189,59 +202,49 @@ Frame::Ptr MP4Demuxer::readFrame(bool &keyFrame, bool &eof) { } } -template -class FrameWrapper : public Parent{ -public: - ~FrameWrapper() = default; - FrameWrapper(const Buffer::Ptr &buf, int64_t pts, int64_t dts, int prefix) : Parent(buf->data(), buf->size(), dts, pts, prefix){ - _buf = buf; - } - bool cacheAble() const override { - return true; - } -private: - Buffer::Ptr _buf; -}; - Frame::Ptr MP4Demuxer::makeFrame(uint32_t track_id, const Buffer::Ptr &buf, int64_t pts, int64_t dts) { auto it = _track_to_codec.find(track_id); if (it == _track_to_codec.end()) { return nullptr; } - auto numBytes = buf->size(); - auto pBytes = buf->data(); + auto bytes = buf->size() - DATA_OFFSET; + auto data = buf->data() + DATA_OFFSET; auto codec = it->second->getCodecId(); switch (codec) { case CodecH264 : case CodecH265 : { - uint32_t iOffset = 0; - while (iOffset < numBytes) { - uint32_t iFrameLen; - memcpy(&iFrameLen, pBytes + iOffset, 4); - iFrameLen = ntohl(iFrameLen); - if (iFrameLen + iOffset + 4 > numBytes) { + uint32_t offset = 0; + while (offset < bytes) { + uint32_t frame_len; + memcpy(&frame_len, data + offset, 4); + frame_len = ntohl(frame_len); + if (frame_len + offset + 4 > bytes) { return nullptr; } - memcpy(pBytes + iOffset, "\x0\x0\x0\x1", 4); - iOffset += (iFrameLen + 4); + memcpy(data + offset, "\x0\x0\x0\x1", 4); + offset += (frame_len + 4); } if (codec == CodecH264) { - return std::make_shared >(buf, pts, dts, 4); + return std::make_shared >(buf, dts, pts, 4, DATA_OFFSET); } - return std::make_shared >(buf, pts, dts, 4); + return std::make_shared >(buf, dts, pts, 4, DATA_OFFSET); } - case CodecAAC : - return std::make_shared >(buf, pts, dts, 0); + case CodecAAC: { + AACTrack::Ptr track = dynamic_pointer_cast(it->second); + assert(track); + //加上adts头 + dumpAacConfig(track->getAacCfg(), buf->size() - DATA_OFFSET, (uint8_t *) buf->data() + (DATA_OFFSET - ADTS_HEADER_LEN), ADTS_HEADER_LEN); + return std::make_shared >(buf, dts, pts, ADTS_HEADER_LEN, DATA_OFFSET - ADTS_HEADER_LEN, codec); + } + case CodecOpus: case CodecG711A: case CodecG711U: { - auto frame = std::make_shared >(buf, pts, dts, 0); - frame->setCodec(codec); - return frame; + return std::make_shared >(buf, dts, pts, 0, DATA_OFFSET, codec); } - default: - return nullptr; + + default: return nullptr; } } @@ -253,7 +256,7 @@ vector MP4Demuxer::getTracks(bool trackReady) const { } ret.push_back(pr.second); } - return std::move(ret); + return ret; } uint64_t MP4Demuxer::getDurationMS() const { diff --git a/src/Record/MP4Demuxer.h b/src/Record/MP4Demuxer.h index 5565cc22..2512602c 100644 --- a/src/Record/MP4Demuxer.h +++ b/src/Record/MP4Demuxer.h @@ -16,24 +16,60 @@ #include "Util/ResourcePool.h" namespace mediakit { -class MP4Demuxer : public MP4File, public TrackSource{ +class MP4Demuxer : public MP4FileDisk, public TrackSource{ public: typedef std::shared_ptr Ptr; - MP4Demuxer(const char *file); + + /** + * 创建mp4解复用器 + */ + MP4Demuxer(); ~MP4Demuxer() override; + + /** + * 打开文件 + * @param file mp4文件路径 + */ + void openMP4(const string &file); + + /** + * 移动时间轴至某处 + * @param stamp_ms 预期的时间轴位置,单位毫秒 + * @return 时间轴位置 + */ int64_t seekTo(int64_t stamp_ms); + + /** + * 读取一帧数据 + * @param keyFrame 是否为关键帧 + * @param eof 是否文件读取完毕 + * @return 帧数据,可能为空 + */ Frame::Ptr readFrame(bool &keyFrame, bool &eof); - vector getTracks(bool trackReady) const override ; + + /** + * 获取所有Track信息 + * @param trackReady 是否要求track为就绪状态 + * @return 所有Track + */ + vector getTracks(bool trackReady) const override; + + /** + * 获取文件长度 + * @return 文件长度,单位毫秒 + */ uint64_t getDurationMS() const; + private: int getAllTracks(); - void onVideoTrack(uint32_t track_id, uint8_t object, int width, int height, const void* extra, size_t bytes); - void onAudioTrack(uint32_t track_id, uint8_t object, int channel_count, int bit_per_sample, int sample_rate, const void* extra, size_t bytes); - Frame::Ptr makeFrame(uint32_t track_id, const Buffer::Ptr &buf,int64_t pts, int64_t dts); + void onVideoTrack(uint32_t track_id, uint8_t object, int width, int height, const void *extra, size_t bytes); + void onAudioTrack(uint32_t track_id, uint8_t object, int channel_count, int bit_per_sample, int sample_rate, const void *extra, size_t bytes); + Frame::Ptr makeFrame(uint32_t track_id, const Buffer::Ptr &buf, int64_t pts, int64_t dts); + private: - MP4File::Reader _mov_reader; + Reader _mov_reader; uint64_t _duration_ms = 0; - map _track_to_codec; + map _track_to_codec; ResourcePool _buffer_pool; }; diff --git a/src/Record/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp index af350f67..471b6e09 100644 --- a/src/Record/MP4Muxer.cpp +++ b/src/Record/MP4Muxer.cpp @@ -14,33 +14,57 @@ #include "Extension/H264.h" namespace mediakit{ -MP4Muxer::MP4Muxer(const char *file) { - _file_name = file; - openMP4(); -} +MP4Muxer::MP4Muxer() {} MP4Muxer::~MP4Muxer() { closeMP4(); } -void MP4Muxer::openMP4(){ +void MP4Muxer::openMP4(const string &file){ closeMP4(); - openFile(_file_name.data(), "wb+"); - _mov_writter = createWriter(); + _file_name = file; + _mp4_file = std::make_shared(); + _mp4_file->openFile(_file_name.data(), "wb+"); } + +MP4FileIO::Writer MP4Muxer::createWriter(){ + GET_CONFIG(bool, mp4FastStart, Record::kFastStart); + return _mp4_file->createWriter(mp4FastStart ? MOV_FLAG_FASTSTART : 0, false); +} + void MP4Muxer::closeMP4(){ - _mov_writter = nullptr; - closeFile(); + MP4MuxerInterface::resetTracks(); + _mp4_file = nullptr; } void MP4Muxer::resetTracks() { - _codec_to_trackid.clear(); - _started = false; - _have_video = false; - openMP4(); + MP4MuxerInterface::resetTracks(); + openMP4(_file_name); } -void MP4Muxer::inputFrame(const Frame::Ptr &frame) { +/////////////////////////////////////////// MP4MuxerInterface ///////////////////////////////////////////// + +void MP4MuxerInterface::saveSegment(){ + mp4_writer_save_segment(_mov_writter.get()); +} + +void MP4MuxerInterface::initSegment(){ + mp4_writer_init_segment(_mov_writter.get()); +} + +bool MP4MuxerInterface::haveVideo() const{ + return _have_video; +} + +void MP4MuxerInterface::resetTracks() { + _started = false; + _have_video = false; + _mov_writter = nullptr; + _frameCached.clear(); + _codec_to_trackid.clear(); +} + +void MP4MuxerInterface::inputFrame(const Frame::Ptr &frame) { auto it = _codec_to_trackid.find(frame->getCodecId()); if(it == _codec_to_trackid.end()){ //该Track不存在或初始化失败 @@ -48,17 +72,13 @@ void MP4Muxer::inputFrame(const Frame::Ptr &frame) { } if (!_started) { - //还没开始 - if (!_have_video) { - _started = true; - } else { - if (frame->getTrackType() != TrackVideo || !frame->keyFrame()) { - //如果首帧是音频或者是视频但是不是i帧,那么不能开始写文件 - return; - } - //开始写文件 - _started = true; + //该逻辑确保含有视频时,第一帧为关键帧 + if (_have_video && !frame->keyFrame()) { + //含有视频,但是不是关键帧,那么前面的帧丢弃 + return; } + //开始写文件 + _started = true; } //mp4文件时间戳需要从0开始 @@ -88,24 +108,23 @@ void MP4Muxer::inputFrame(const Frame::Ptr &frame) { merged.append((char *) &nalu_size, 4); merged.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize()); }); - mov_writer_write_l(_mov_writter.get(), + mp4_writer_write(_mov_writter.get(), track_info.track_id, merged.data(), merged.size(), pts_out, dts_out, - back->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0, - 1/*我们合并时已经生成了4个字节的MP4格式start code*/); + back->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0); } else { //缓存中只有一帧视频 - mov_writer_write_l(_mov_writter.get(), + mp4_writer_write_l(_mov_writter.get(), track_info.track_id, back->data() + back->prefixSize(), back->size() - back->prefixSize(), pts_out, dts_out, back->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0, - 0/*需要生成头4个字节的MP4格式start code*/); + 1/*需要生成头4个字节的MP4格式start code*/); } _frameCached.clear(); } @@ -115,14 +134,13 @@ void MP4Muxer::inputFrame(const Frame::Ptr &frame) { break; default: { track_info.stamp.revise(frame->dts(), frame->pts(), dts_out, pts_out); - mov_writer_write_l(_mov_writter.get(), - track_info.track_id, - frame->data() + frame->prefixSize(), - frame->size() - frame->prefixSize(), - pts_out, - dts_out, - frame->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0, - 1/*aac或其他类型frame不用添加4个nalu_size的字节*/); + mp4_writer_write(_mov_writter.get(), + track_info.track_id, + frame->data() + frame->prefixSize(), + frame->size() - frame->prefixSize(), + pts_out, + dts_out, + frame->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0); } break; } @@ -140,7 +158,7 @@ static uint8_t getObject(CodecId codecId){ } } -void MP4Muxer::stampSync(){ +void MP4MuxerInterface::stampSync(){ if(_codec_to_trackid.size() < 2){ return; } @@ -160,7 +178,10 @@ void MP4Muxer::stampSync(){ } } -void MP4Muxer::addTrack(const Track::Ptr &track) { +void MP4MuxerInterface::addTrack(const Track::Ptr &track) { + if (!_mov_writter) { + _mov_writter = createWriter(); + } auto mp4_object = getObject(track->getCodecId()); if (!mp4_object) { WarnL << "MP4录制不支持该编码格式:" << track->getCodecName(); @@ -182,7 +203,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { return; } - auto track_id = mov_writer_add_audio(_mov_writter.get(), + auto track_id = mp4_writer_add_audio(_mov_writter.get(), mp4_object, audio_track->getAudioChannel(), audio_track->getAudioSampleBit() * audio_track->getAudioChannel(), @@ -203,7 +224,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { return; } - auto track_id = mov_writer_add_audio(_mov_writter.get(), + auto track_id = mp4_writer_add_audio(_mov_writter.get(), mp4_object, audio_track->getAudioChannel(), audio_track->getAudioSampleBit() * audio_track->getAudioChannel(), @@ -236,7 +257,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { return; } - auto track_id = mov_writer_add_video(_mov_writter.get(), + auto track_id = mp4_writer_add_video(_mov_writter.get(), mp4_object, h264_track->getVideoWidth(), h264_track->getVideoHeight(), @@ -271,7 +292,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { return; } - auto track_id = mov_writer_add_video(_mov_writter.get(), + auto track_id = mp4_writer_add_video(_mov_writter.get(), mp4_object, h265_track->getVideoWidth(), h265_track->getVideoHeight(), @@ -293,5 +314,54 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { stampSync(); } +/////////////////////////////////////////// MP4MuxerMemory ///////////////////////////////////////////// + +MP4MuxerMemory::MP4MuxerMemory() { + _memory_file = std::make_shared(); +} + +MP4FileIO::Writer MP4MuxerMemory::createWriter() { + return _memory_file->createWriter(MOV_FLAG_SEGMENT, true); +} + +const string &MP4MuxerMemory::getInitSegment(){ + if (_init_segment.empty()) { + initSegment(); + saveSegment(); + _init_segment = _memory_file->getAndClearMemory(); + } + return _init_segment; +} + +void MP4MuxerMemory::resetTracks(){ + MP4MuxerInterface::resetTracks(); + _memory_file = std::make_shared(); + _init_segment.clear(); +} + +void MP4MuxerMemory::inputFrame(const Frame::Ptr &frame){ + if (_init_segment.empty()) { + //尚未生成init segment + return; + } + + bool key_frame = frame->keyFrame(); + if (_ticker.elapsedTime() > 50 || key_frame) { + //遇到关键帧或者超过50ms则切片 + _ticker.resetTime(); + //flush切片 + saveSegment(); + //输出切片数据 + onSegmentData(_memory_file->getAndClearMemory(), frame->dts(), _key_frame); + _key_frame = false; + } + + if (key_frame) { + _key_frame = true; + } + MP4MuxerInterface::inputFrame(frame); +} + + }//namespace mediakit #endif//#ifdef ENABLE_MP4 diff --git a/src/Record/MP4Muxer.h b/src/Record/MP4Muxer.h index f2e3fbd2..479e98d5 100644 --- a/src/Record/MP4Muxer.h +++ b/src/Record/MP4Muxer.h @@ -23,17 +23,16 @@ namespace mediakit{ -class MP4Muxer : public MediaSinkInterface, public MP4File{ +class MP4MuxerInterface : public MediaSinkInterface { public: - typedef std::shared_ptr Ptr; - - MP4Muxer(const char *file); - ~MP4Muxer() override; + MP4MuxerInterface() = default; + ~MP4MuxerInterface() override = default; /** * 添加已经ready状态的track */ - void addTrack(const Track::Ptr & track) override; + void addTrack(const Track::Ptr &track) override; + /** * 输入帧 */ @@ -42,30 +41,112 @@ public: /** * 重置所有track */ - void resetTracks() override ; + void resetTracks() override; + + /** + * 是否包含视频 + */ + bool haveVideo() const; + + /** + * 保存fmp4分片 + */ + void saveSegment(); + + /** + * 创建新切片 + */ + void initSegment(); + +protected: + virtual MP4FileIO::Writer createWriter() = 0; + +private: + void stampSync(); + +private: + bool _started = false; + bool _have_video = false; + MP4FileIO::Writer _mov_writter; + struct track_info { + int track_id = -1; + Stamp stamp; + }; + List _frameCached; + unordered_map _codec_to_trackid; +}; + +class MP4Muxer : public MP4MuxerInterface{ +public: + typedef std::shared_ptr Ptr; + + MP4Muxer(); + ~MP4Muxer() override; + + /** + * 重置所有track + */ + void resetTracks() override; + + /** + * 打开mp4 + * @param file 文件完整路径 + */ + void openMP4(const string &file); /** * 手动关闭文件(对象析构时会自动关闭) */ void closeMP4(); -private: - void openMP4(); - void stampSync(); +protected: + MP4FileIO::Writer createWriter() override; private: - struct track_info { - int track_id = -1; - Stamp stamp; - }; - unordered_map _codec_to_trackid; - List _frameCached; - bool _started = false; - bool _have_video = false; - MP4File::Writer _mov_writter; string _file_name; + MP4FileDisk::Ptr _mp4_file; }; +class MP4MuxerMemory : public MP4MuxerInterface{ +public: + MP4MuxerMemory(); + ~MP4MuxerMemory() override = default; + + /** + * 重置所有track + */ + void resetTracks() override; + + /** + * 输入帧 + */ + void inputFrame(const Frame::Ptr &frame) override; + + /** + * 获取fmp4 init segment + */ + const string &getInitSegment(); + +protected: + /** + * 输出fmp4切片回调函数 + * @param string 切片内容 + * @param stamp 切片末尾时间戳 + * @param key_frame 是否有关键帧 + */ + virtual void onSegmentData(const string &string, uint32_t stamp, bool key_frame) = 0; + +protected: + MP4FileIO::Writer createWriter() override; + +private: + bool _key_frame = false; + Ticker _ticker; + string _init_segment; + MP4FileMemory::Ptr _memory_file; +}; + + }//namespace mediakit #endif//#ifdef ENABLE_MP4 #endif //ZLMEDIAKIT_MP4MUXER_H diff --git a/src/Record/MP4Reader.cpp b/src/Record/MP4Reader.cpp index 1e78c617..ea58e0a3 100644 --- a/src/Record/MP4Reader.cpp +++ b/src/Record/MP4Reader.cpp @@ -29,7 +29,8 @@ MP4Reader::MP4Reader(const string &strVhost,const string &strApp, const string & strFileName = File::absolutePath(strFileName,recordPath); } - _demuxer = std::make_shared(strFileName.data()); + _demuxer = std::make_shared(); + _demuxer->openMP4(strFileName); _mediaMuxer.reset(new MultiMediaSourceMuxer(strVhost, strApp, strId, _demuxer->getDurationMS() / 1000.0, true, true, false, false)); auto tracks = _demuxer->getTracks(false); if(tracks.empty()){ diff --git a/src/Record/MP4Reader.h b/src/Record/MP4Reader.h index bb7e365e..b8348752 100644 --- a/src/Record/MP4Reader.h +++ b/src/Record/MP4Reader.h @@ -35,6 +35,7 @@ public: * 意思是在文件流化结束之前或中断之前,MP4Reader对象是不会被销毁的(不管有没有被外部对象持有) */ void startReadMP4(); + private: //MediaSourceEvent override bool seekTo(MediaSource &sender,uint32_t ui32Stamp) override; @@ -45,15 +46,16 @@ private: uint32_t getCurrentStamp(); void setCurrentStamp(uint32_t ui32Stamp); bool seekTo(uint32_t ui32Stamp); + private: - recursive_mutex _mtx; - MultiMediaSourceMuxer::Ptr _mediaMuxer; + bool _have_video = false; uint32_t _seek_to; + recursive_mutex _mtx; Ticker _seek_ticker; Timer::Ptr _timer; EventPoller::Ptr _poller; MP4Demuxer::Ptr _demuxer; - bool _have_video = false; + MultiMediaSourceMuxer::Ptr _mediaMuxer; }; } /* namespace mediakit */ diff --git a/src/Record/MP4Recorder.cpp b/src/Record/MP4Recorder.cpp index 1f247354..b9922b17 100644 --- a/src/Record/MP4Recorder.cpp +++ b/src/Record/MP4Recorder.cpp @@ -25,10 +25,10 @@ MP4Recorder::MP4Recorder(const string& strPath, const string &strStreamId) { _strPath = strPath; /////record 业务逻辑////// - _info.strAppName = strApp; - _info.strStreamId = strStreamId; - _info.strVhost = strVhost; - _info.strFolder = strPath; + _info.app = strApp; + _info.stream = strStreamId; + _info.vhost = strVhost; + _info.folder = strPath; } MP4Recorder::~MP4Recorder() { closeFile(); @@ -42,26 +42,27 @@ void MP4Recorder::createFile() { auto strFile = _strPath + strDate + "/" + strTime + ".mp4"; /////record 业务逻辑////// - _info.ui64StartedTime = ::time(NULL); - _info.strFileName = strTime + ".mp4"; - _info.strFilePath = strFile; + _info.start_time = ::time(NULL); + _info.file_name = strTime + ".mp4"; + _info.file_path = strFile; GET_CONFIG(string,appName,Record::kAppName); - _info.strUrl = appName + "/" - + _info.strAppName + "/" - + _info.strStreamId + "/" - + strDate + "/" - + strTime + ".mp4"; + _info.url = appName + "/" + + _info.app + "/" + + _info.stream + "/" + + strDate + "/" + + strTime + ".mp4"; try { - _muxer = std::make_shared(strFileTmp.data()); - for(auto &track :_tracks){ + _muxer = std::make_shared(); + _muxer->openMP4(strFileTmp); + for (auto &track :_tracks) { //添加track _muxer->addTrack(track); } _strFileTmp = strFileTmp; _strFile = strFile; _createFileTicker.resetTime(); - }catch(std::exception &ex) { + } catch (std::exception &ex) { WarnL << ex.what(); } } @@ -73,7 +74,7 @@ void MP4Recorder::asyncClose() { auto info = _info; WorkThreadPool::Instance().getExecutor()->async([muxer,strFileTmp,strFile,info]() { //获取文件录制时间,放在关闭mp4之前是为了忽略关闭mp4执行时间 - const_cast(info).ui64TimeLen = ::time(NULL) - info.ui64StartedTime; + const_cast(info).time_len = ::time(NULL) - info.start_time; //关闭mp4非常耗时,所以要放在后台线程执行 muxer->closeMP4(); //临时文件名改成正式文件名,防止mp4未完成时被访问 @@ -81,7 +82,7 @@ void MP4Recorder::asyncClose() { //获取文件大小 struct stat fileData; stat(strFile.data(), &fileData); - const_cast(info).ui64FileSize = fileData.st_size; + const_cast(info).file_size = fileData.st_size; /////record 业务逻辑////// NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordMP4,info); }); diff --git a/src/Record/MP4Recorder.h b/src/Record/MP4Recorder.h index 60f3c561..0270ea62 100644 --- a/src/Record/MP4Recorder.h +++ b/src/Record/MP4Recorder.h @@ -20,24 +20,11 @@ #include "Util/TimeTicker.h" #include "Common/MediaSink.h" #include "MP4Muxer.h" + using namespace toolkit; namespace mediakit { -class MP4Info { -public: - time_t ui64StartedTime; //GMT标准时间,单位秒 - time_t ui64TimeLen;//录像长度,单位秒 - off_t ui64FileSize;//文件大小,单位BYTE - string strFilePath;//文件路径 - string strFileName;//文件名称 - string strFolder;//文件夹路径 - string strUrl;//播放路径 - string strAppName;//应用名称 - string strStreamId;//流ID - string strVhost;//vhost -}; - #ifdef ENABLE_MP4 class MP4Recorder : public MediaSinkInterface{ public: @@ -72,7 +59,7 @@ private: string _strFile; string _strFileTmp; Ticker _createFileTicker; - MP4Info _info; + RecordInfo _info; bool _haveVideo = false; MP4Muxer::Ptr _muxer; list _tracks; diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index 2f0314cb..100a2951 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -79,16 +79,8 @@ std::shared_ptr Recorder::createRecorder(type type, const st } } -static MediaSource::Ptr getMediaSource(const string &vhost, const string &app, const string &stream_id){ - auto src = MediaSource::find(RTMP_SCHEMA, vhost, app, stream_id); - if(src){ - return src; - } - return MediaSource::find(RTSP_SCHEMA, vhost, app, stream_id); -} - bool Recorder::isRecording(type type, const string &vhost, const string &app, const string &stream_id){ - auto src = getMediaSource(vhost, app, stream_id); + auto src = MediaSource::find(vhost, app, stream_id); if(!src){ return false; } @@ -96,15 +88,16 @@ bool Recorder::isRecording(type type, const string &vhost, const string &app, co } bool Recorder::startRecord(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path){ - auto src = getMediaSource(vhost, app, stream_id); - if(!src){ + auto src = MediaSource::find(vhost, app, stream_id); + if (!src) { + WarnL << "未找到相关的MediaSource,startRecord失败:" << vhost << "/" << app << "/" << stream_id; return false; } - return src->setupRecord(type,true,customized_path); + return src->setupRecord(type, true, customized_path); } bool Recorder::stopRecord(type type, const string &vhost, const string &app, const string &stream_id){ - auto src = getMediaSource(vhost, app, stream_id); + auto src = MediaSource::find(vhost, app, stream_id); if(!src){ return false; } diff --git a/src/Record/Recorder.h b/src/Record/Recorder.h index 670de9f8..fa6e2ac7 100644 --- a/src/Record/Recorder.h +++ b/src/Record/Recorder.h @@ -16,6 +16,20 @@ using namespace std; namespace mediakit { class MediaSinkInterface; +class RecordInfo { +public: + time_t start_time; // GMT 标准时间,单位秒 + float time_len; // 录像长度,单位秒 + off_t file_size; // 文件大小,单位 BYTE + string file_path; // 文件路径 + string file_name; // 文件名称 + string folder; // 文件夹路径 + string url; // 播放路径 + string app; // 应用名称 + string stream; // 流 ID + string vhost; // 虚拟主机 +}; + class Recorder{ public: typedef enum { diff --git a/src/Record/TsMuxer.cpp b/src/Record/TsMuxer.cpp index 40bee618..fa31388f 100644 --- a/src/Record/TsMuxer.cpp +++ b/src/Record/TsMuxer.cpp @@ -8,8 +8,8 @@ * may be found in the AUTHORS file in the root of the source tree. */ -#include "TsMuxer.h" #if defined(ENABLE_HLS) +#include "TsMuxer.h" #include "mpeg-ts-proto.h" #include "mpeg-ts.h" #include "Extension/H264.h" @@ -25,20 +25,20 @@ TsMuxer::~TsMuxer() { } void TsMuxer::stampSync(){ - if(_codec_to_trackid.size() < 2){ + if (_codec_to_trackid.size() < 2) { return; } Stamp *audio = nullptr, *video = nullptr; - for(auto &pr : _codec_to_trackid){ - switch (getTrackType((CodecId) pr.first)){ + for (auto &pr : _codec_to_trackid) { + switch (getTrackType((CodecId) pr.first)) { case TrackAudio : audio = &pr.second.stamp; break; case TrackVideo : video = &pr.second.stamp; break; default : break; } } - if(audio && video){ + if (audio && video) { //音频时间戳同步于视频,因为音频时间戳被修改后不影响播放 audio->syncTo(*video); } @@ -73,6 +73,11 @@ void TsMuxer::addTrack(const Track::Ptr &track) { break; } + case CodecOpus: { + _codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_AUDIO_OPUS, nullptr, 0); + break; + } + default: WarnL << "mpeg-ts 不支持该编码格式,已忽略:" << track->getCodecName(); break; } @@ -82,88 +87,100 @@ void TsMuxer::addTrack(const Track::Ptr &track) { void TsMuxer::inputFrame(const Frame::Ptr &frame) { auto it = _codec_to_trackid.find(frame->getCodecId()); - if(it == _codec_to_trackid.end()){ + if (it == _codec_to_trackid.end()) { return; } - //mp4文件时间戳需要从0开始 auto &track_info = it->second; int64_t dts_out, pts_out; _is_idr_fast_packet = !_have_video; switch (frame->getCodecId()){ case CodecH264: { - int type = H264_TYPE(*((uint8_t *)frame->data() + frame->prefixSize())); - if(type == H264Frame::NAL_SEI){ + int type = H264_TYPE(*((uint8_t *) frame->data() + frame->prefixSize())); + if (type == H264Frame::NAL_SEI) { break; } } + case CodecH265: { //这里的代码逻辑是让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){ + 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()); + _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()); } - if(frame->keyFrame()){ + if (frame->keyFrame()) { _is_idr_fast_packet = true; } }); merged_frame = std::make_shared(std::move(merged)); } - track_info.stamp.revise(back->dts(),back->pts(),dts_out,pts_out); + track_info.stamp.revise(back->dts(), back->pts(), dts_out, pts_out); + //取视频时间戳为TS的时间戳 _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()); + mpeg_ts_write(_context, track_info.track_id, back->keyFrame() ? 0x0001 : 0, pts_out * 90LL,dts_out * 90LL, merged_frame->data(), merged_frame->size()); _frameCached.clear(); } _frameCached.emplace_back(Frame::getCacheAbleFrame(frame)); - } break; + } + + case CodecAAC: { + if (frame->prefixSize() == 0) { + WarnL << "必须提供adts头才能mpeg-ts打包"; + break; + } + } + default: { - track_info.stamp.revise(frame->dts(),frame->pts(),dts_out,pts_out); - _timestamp = dts_out; + track_info.stamp.revise(frame->dts(), frame->pts(), dts_out, pts_out); + if(!_have_video){ + //没有视频时,才以音频时间戳为TS的时间戳 + _timestamp = dts_out; + } mpeg_ts_write(_context, track_info.track_id, frame->keyFrame() ? 0x0001 : 0, pts_out * 90LL, dts_out * 90LL, frame->data(), frame->size()); - } break; + } } } void TsMuxer::resetTracks() { _have_video = false; //通知片段中断 - onTs(nullptr, 0, 0, 0); + onTs(nullptr, 0, _timestamp, 0); uninit(); init(); } void TsMuxer::init() { - static mpeg_ts_func_t s_func= { - [](void* param, size_t bytes){ - TsMuxer *muxer = (TsMuxer *)param; + static mpeg_ts_func_t s_func = { + [](void *param, size_t bytes) { + TsMuxer *muxer = (TsMuxer *) param; assert(sizeof(TsMuxer::_tsbuf) >= bytes); - return (void *)muxer->_tsbuf; + return (void *) muxer->_tsbuf; }, - [](void* param, void* packet){ + [](void *param, void *packet) { //do nothing }, - [](void* param, const void* packet, size_t bytes){ - TsMuxer *muxer = (TsMuxer *)param; - muxer->onTs(packet, bytes,muxer->_timestamp,muxer->_is_idr_fast_packet); + [](void *param, const void *packet, size_t bytes) { + TsMuxer *muxer = (TsMuxer *) param; + muxer->onTs(packet, bytes, muxer->_timestamp, muxer->_is_idr_fast_packet); muxer->_is_idr_fast_packet = false; } }; - if(_context == nullptr){ - _context = mpeg_ts_create(&s_func,this); + if (_context == nullptr) { + _context = mpeg_ts_create(&s_func, this); } } void TsMuxer::uninit() { - if(_context){ + if (_context) { mpeg_ts_destroy(_context); _context = nullptr; } @@ -171,5 +188,4 @@ void TsMuxer::uninit() { } }//namespace mediakit - #endif// defined(ENABLE_HLS) \ No newline at end of file diff --git a/src/Record/TsMuxer.h b/src/Record/TsMuxer.h index 5a16f1cf..74c97da2 100644 --- a/src/Record/TsMuxer.h +++ b/src/Record/TsMuxer.h @@ -11,6 +11,7 @@ #ifndef TSMUXER_H #define TSMUXER_H +#if defined(ENABLE_HLS) #include #include "Extension/Frame.h" #include "Extension/Track.h" @@ -72,4 +73,25 @@ private: }; }//namespace mediakit + +#else + +#include "Common/MediaSink.h" + +namespace mediakit { +class TsMuxer : public MediaSinkInterface { +public: + TsMuxer() {} + ~TsMuxer() override {} + void addTrack(const Track::Ptr &track) override {} + void resetTracks() override {} + void inputFrame(const Frame::Ptr &frame) override {} + +protected: + virtual void onTs(const void *packet, int bytes,uint32_t timestamp,bool is_idr_fast_packet) = 0; +}; +}//namespace mediakit + +#endif// defined(ENABLE_HLS) + #endif //TSMUXER_H \ No newline at end of file diff --git a/src/Rtmp/FlvMuxer.cpp b/src/Rtmp/FlvMuxer.cpp index 35564867..7418a436 100644 --- a/src/Rtmp/FlvMuxer.cpp +++ b/src/Rtmp/FlvMuxer.cpp @@ -73,10 +73,10 @@ void FlvMuxer::onWriteFlvHeader(const RtmpMediaSource::Ptr &mediaSrc) { bool is_have_audio = false,is_have_video = false; mediaSrc->getConfigFrame([&](const RtmpPacket::Ptr &pkt){ - if(pkt->typeId == MSG_VIDEO){ + if(pkt->type_id == MSG_VIDEO){ is_have_video = true; } - if(pkt->typeId == MSG_AUDIO){ + if(pkt->type_id == MSG_AUDIO){ is_have_audio = true; } }); @@ -133,16 +133,16 @@ public: #pragma pack(pop) #endif // defined(_WIN32) -void FlvMuxer::onWriteFlvTag(const RtmpPacket::Ptr &pkt, uint32_t ui32TimeStamp , bool flush) { - onWriteFlvTag(pkt->typeId,pkt,ui32TimeStamp, flush); +void FlvMuxer::onWriteFlvTag(const RtmpPacket::Ptr &pkt, uint32_t time_stamp , bool flush) { + onWriteFlvTag(pkt->type_id, pkt, time_stamp, flush); } -void FlvMuxer::onWriteFlvTag(uint8_t ui8Type, const Buffer::Ptr &buffer, uint32_t ui32TimeStamp, bool flush) { +void FlvMuxer::onWriteFlvTag(uint8_t type, const Buffer::Ptr &buffer, uint32_t time_stamp, bool flush) { RtmpTagHeader header; - header.type = ui8Type; + header.type = type; set_be24(header.data_size, buffer->size()); - header.timestamp_ex = (uint8_t) ((ui32TimeStamp >> 24) & 0xff); - set_be24(header.timestamp,ui32TimeStamp & 0xFFFFFF); + header.timestamp_ex = (uint8_t) ((time_stamp >> 24) & 0xff); + set_be24(header.timestamp, time_stamp & 0xFFFFFF); //tag header onWrite(std::make_shared((char *)&header, sizeof(header)), false); //tag data @@ -154,7 +154,7 @@ void FlvMuxer::onWriteFlvTag(uint8_t ui8Type, const Buffer::Ptr &buffer, uint32_ void FlvMuxer::onWriteRtmp(const RtmpPacket::Ptr &pkt,bool flush) { int64_t dts_out; - _stamp[pkt->typeId % 2].revise(pkt->timeStamp, 0, dts_out, dts_out); + _stamp[pkt->type_id % 2].revise(pkt->time_stamp, 0, dts_out, dts_out); onWriteFlvTag(pkt, dts_out,flush); } diff --git a/src/Rtmp/FlvMuxer.h b/src/Rtmp/FlvMuxer.h index 86be5ee3..fa16a1c8 100644 --- a/src/Rtmp/FlvMuxer.h +++ b/src/Rtmp/FlvMuxer.h @@ -25,21 +25,23 @@ public: FlvMuxer(); virtual ~FlvMuxer(); void stop(); + protected: - void start(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &media); + void start(const EventPoller::Ptr &poller, const RtmpMediaSource::Ptr &media); virtual void onWrite(const Buffer::Ptr &data, bool flush) = 0; virtual void onDetach() = 0; virtual std::shared_ptr getSharedPtr() = 0; + private: void onWriteFlvHeader(const RtmpMediaSource::Ptr &media); - void onWriteRtmp(const RtmpPacket::Ptr &pkt,bool flush); - void onWriteFlvTag(const RtmpPacket::Ptr &pkt, uint32_t ui32TimeStamp, bool flush); - void onWriteFlvTag(uint8_t ui8Type, const Buffer::Ptr &buffer, uint32_t ui32TimeStamp, bool flush); + void onWriteRtmp(const RtmpPacket::Ptr &pkt, bool flush); + void onWriteFlvTag(const RtmpPacket::Ptr &pkt, uint32_t time_stamp, bool flush); + void onWriteFlvTag(uint8_t type, const Buffer::Ptr &buffer, uint32_t time_stamp, bool flush); + private: - RtmpMediaSource::RingType::RingReader::Ptr _ring_reader; //时间戳修整器 Stamp _stamp[2]; - + RtmpMediaSource::RingType::RingReader::Ptr _ring_reader; }; class FlvRecorder : public FlvMuxer , public std::enable_shared_from_this{ @@ -47,12 +49,14 @@ public: typedef std::shared_ptr Ptr; FlvRecorder(); virtual ~FlvRecorder(); - void startRecord(const EventPoller::Ptr &poller,const string &vhost,const string &app,const string &stream,const string &file_path); - void startRecord(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &media,const string &file_path); + void startRecord(const EventPoller::Ptr &poller, const RtmpMediaSource::Ptr &media, const string &file_path); + void startRecord(const EventPoller::Ptr &poller, const string &vhost, const string &app, const string &stream, const string &file_path); + private: virtual void onWrite(const Buffer::Ptr &data, bool flush) override ; virtual void onDetach() override; virtual std::shared_ptr getSharedPtr() override; + private: std::shared_ptr _file; recursive_mutex _file_mtx; @@ -60,5 +64,4 @@ private: }//namespace mediakit - #endif //ZLMEDIAKIT_FLVMUXER_H diff --git a/src/Rtmp/Rtmp.cpp b/src/Rtmp/Rtmp.cpp index db0078f4..b7046163 100644 --- a/src/Rtmp/Rtmp.cpp +++ b/src/Rtmp/Rtmp.cpp @@ -58,6 +58,14 @@ uint8_t getAudioRtmpFlags(const Track::Ptr &track){ switch (track->getCodecId()){ case CodecG711A : flvAudioType = FLV_CODEC_G711A; break; case CodecG711U : flvAudioType = FLV_CODEC_G711U; break; + case CodecOpus : { + flvAudioType = FLV_CODEC_OPUS; + //opus不通过flags获取音频相关信息 + iSampleRate = 44100; + iSampleBit = 16; + iChannel = 2; + break; + } case CodecAAC : { flvAudioType = FLV_CODEC_AAC; //aac不通过flags获取音频相关信息 diff --git a/src/Rtmp/Rtmp.h b/src/Rtmp/Rtmp.h index f9d3d8e6..0d2e5fb3 100644 --- a/src/Rtmp/Rtmp.h +++ b/src/Rtmp/Rtmp.h @@ -23,9 +23,6 @@ using namespace toolkit; -#define PORT 1935 -#define DEFAULT_CHUNK_LEN 128 - #if !defined(_WIN32) #define PACKED __attribute__((packed)) #else @@ -33,6 +30,7 @@ using namespace toolkit; #endif //!defined(_WIN32) +#define DEFAULT_CHUNK_LEN 128 #define HANDSHAKE_PLAINTEXT 0x03 #define RANDOM_LEN (1536 - 8) @@ -74,10 +72,12 @@ using namespace toolkit; #define FLV_CODEC_AAC 10 #define FLV_CODEC_H264 7 +//金山扩展: https://github.com/ksvc/FFmpeg/wiki #define FLV_CODEC_H265 12 #define FLV_CODEC_G711A 7 #define FLV_CODEC_G711U 8 - +//参考学而思网校: https://github.com/notedit/rtmp/commit/6e314ac5b29611431f8fb5468596b05815743c10 +#define FLV_CODEC_OPUS 13 namespace mediakit { @@ -89,22 +89,24 @@ class RtmpHandshake { public: RtmpHandshake(uint32_t _time, uint8_t *_random = nullptr) { _time = htonl(_time); - memcpy(timeStamp, &_time, 4); + memcpy(time_stamp, &_time, 4); if (!_random) { random_generate((char *) random, sizeof(random)); } else { memcpy(random, _random, sizeof(random)); } } - uint8_t timeStamp[4]; + + uint8_t time_stamp[4]; uint8_t zero[4] = {0}; uint8_t random[RANDOM_LEN]; - void random_generate(char* bytes, int size) { - static char cdata[] = { 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x2d, 0x72, - 0x74, 0x6d, 0x70, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2d, 0x77, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x2d, 0x77, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x40, 0x31, 0x32, 0x36, 0x2e, 0x63, 0x6f, 0x6d }; + + void random_generate(char *bytes, int size) { + static char cdata[] = {0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x2d, 0x72, + 0x74, 0x6d, 0x70, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2d, 0x77, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x2d, 0x77, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x40, 0x31, 0x32, 0x36, 0x2e, 0x63, 0x6f, 0x6d}; for (int i = 0; i < size; i++) { bytes[i] = cdata[rand() % (sizeof(cdata) - 1)]; } @@ -114,10 +116,10 @@ public: class RtmpHeader { public: uint8_t flags; - uint8_t timeStamp[3]; - uint8_t bodySize[3]; - uint8_t typeId; - uint8_t streamId[4]; /* Note, this is little-endian while others are BE */ + uint8_t time_stamp[3]; + uint8_t body_size[3]; + uint8_t type_id; + uint8_t stream_index[4]; /* Note, this is little-endian while others are BE */ }PACKED; #if defined(_WIN32) @@ -127,22 +129,23 @@ public: class RtmpPacket : public Buffer{ public: typedef std::shared_ptr Ptr; - uint8_t typeId; - uint32_t bodySize = 0; - uint32_t timeStamp = 0; - bool hasAbsStamp = false; - bool hasExtStamp = false; - uint32_t deltaStamp = 0; - uint32_t streamId; - uint32_t chunkId; - std::string strBuf; + uint8_t type_id; + uint32_t body_size = 0; + uint32_t time_stamp = 0; + bool is_abs_stamp = false; + uint32_t ts_field = 0; + uint32_t stream_index; + uint32_t chunk_id; + std::string buffer; + public: char *data() const override{ - return (char*)strBuf.data(); + return (char*)buffer.data(); } uint32_t size() const override { - return strBuf.size(); - }; + return buffer.size(); + } + public: RtmpPacket() = default; RtmpPacket(const RtmpPacket &that) = delete; @@ -150,59 +153,64 @@ public: RtmpPacket &operator=(RtmpPacket &&that) = delete; RtmpPacket(RtmpPacket &&that){ - typeId = that.typeId; - bodySize = that.bodySize; - timeStamp = that.timeStamp; - hasAbsStamp = that.hasAbsStamp; - hasExtStamp = that.hasExtStamp; - deltaStamp = that.deltaStamp; - streamId = that.streamId; - chunkId = that.chunkId; - strBuf = std::move(that.strBuf); + type_id = that.type_id; + body_size = that.body_size; + time_stamp = that.time_stamp; + is_abs_stamp = that.is_abs_stamp; + ts_field = that.ts_field; + stream_index = that.stream_index; + chunk_id = that.chunk_id; + buffer = std::move(that.buffer); } + bool isVideoKeyFrame() const { - return typeId == MSG_VIDEO && (uint8_t) strBuf[0] >> 4 == FLV_KEY_FRAME && (uint8_t) strBuf[1] == 1; + return type_id == MSG_VIDEO && (uint8_t) buffer[0] >> 4 == FLV_KEY_FRAME && (uint8_t) buffer[1] == 1; } + bool isCfgFrame() const { - switch (typeId){ - case MSG_VIDEO : return strBuf[1] == 0; + switch (type_id){ + case MSG_VIDEO : return buffer[1] == 0; case MSG_AUDIO : { switch (getMediaType()){ - case FLV_CODEC_AAC : return strBuf[1] == 0; + case FLV_CODEC_AAC : return buffer[1] == 0; default : return false; } } default : return false; } } + int getMediaType() const { - switch (typeId) { - case MSG_VIDEO : return (uint8_t) strBuf[0] & 0x0F; - case MSG_AUDIO : return (uint8_t) strBuf[0] >> 4; + switch (type_id) { + case MSG_VIDEO : return (uint8_t) buffer[0] & 0x0F; + case MSG_AUDIO : return (uint8_t) buffer[0] >> 4; default : return 0; } } + int getAudioSampleRate() const { - if (typeId != MSG_AUDIO) { + if (type_id != MSG_AUDIO) { return 0; } - int flvSampleRate = ((uint8_t) strBuf[0] & 0x0C) >> 2; + int flvSampleRate = ((uint8_t) buffer[0] & 0x0C) >> 2; const static int sampleRate[] = { 5512, 11025, 22050, 44100 }; return sampleRate[flvSampleRate]; } + int getAudioSampleBit() const { - if (typeId != MSG_AUDIO) { + if (type_id != MSG_AUDIO) { return 0; } - int flvSampleBit = ((uint8_t) strBuf[0] & 0x02) >> 1; + int flvSampleBit = ((uint8_t) buffer[0] & 0x02) >> 1; const static int sampleBit[] = { 8, 16 }; return sampleBit[flvSampleBit]; } + int getAudioChannel() const { - if (typeId != MSG_AUDIO) { + if (type_id != MSG_AUDIO) { return 0; } - int flvStereoOrMono = (uint8_t) strBuf[0] & 0x01; + int flvStereoOrMono = (uint8_t) buffer[0] & 0x01; const static int channel[] = { 1, 2 }; return channel[flvStereoOrMono]; } diff --git a/src/Rtmp/RtmpCodec.h b/src/Rtmp/RtmpCodec.h index bb74fb69..15a4496a 100644 --- a/src/Rtmp/RtmpCodec.h +++ b/src/Rtmp/RtmpCodec.h @@ -23,14 +23,14 @@ public: typedef std::shared_ptr Ptr; typedef RingBuffer RingType; - RtmpRing(){} - virtual ~RtmpRing(){} + RtmpRing() {} + virtual ~RtmpRing() {} /** * 获取rtmp环形缓存 * @return */ - virtual RingType::Ptr getRtmpRing() const{ + virtual RingType::Ptr getRtmpRing() const { return _rtmpRing; } @@ -38,27 +38,24 @@ public: * 设置rtmp环形缓存 * @param ring */ - virtual void setRtmpRing(const RingType::Ptr &ring){ + virtual void setRtmpRing(const RingType::Ptr &ring) { _rtmpRing = ring; } /** * 输入rtmp包 * @param rtmp rtmp包 - * @param key_pos 是否为关键帧 - * @return 是否为关键帧 */ - virtual bool inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos){ - if(_rtmpRing){ - _rtmpRing->write(rtmp,key_pos); + virtual void inputRtmp(const RtmpPacket::Ptr &rtmp) { + if (_rtmpRing) { + _rtmpRing->write(rtmp, rtmp->isVideoKeyFrame()); } - return key_pos; } + protected: RingType::Ptr _rtmpRing; }; - class RtmpCodec : public RtmpRing, public FrameDispatcher , public CodecInfo{ public: typedef std::shared_ptr Ptr; @@ -69,5 +66,4 @@ public: }//namespace mediakit - #endif //ZLMEDIAKIT_RTMPCODEC_H diff --git a/src/Rtmp/RtmpDemuxer.cpp b/src/Rtmp/RtmpDemuxer.cpp index 6e38b0d7..53ce4c87 100644 --- a/src/Rtmp/RtmpDemuxer.cpp +++ b/src/Rtmp/RtmpDemuxer.cpp @@ -65,34 +65,32 @@ bool RtmpDemuxer::loadMetaData(const AMFValue &val){ return ret; } -bool RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) { - switch (pkt->typeId) { +void RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) { + switch (pkt->type_id) { case MSG_VIDEO: { - if(!_tryedGetVideoTrack){ - _tryedGetVideoTrack = true; + if (!_try_get_video_track) { + _try_get_video_track = true; auto codec = AMFValue(pkt->getMediaType()); makeVideoTrack(codec); } - if(_videoRtmpDecoder){ - return _videoRtmpDecoder->inputRtmp(pkt, true); + if (_video_rtmp_decoder) { + _video_rtmp_decoder->inputRtmp(pkt); } - return false; + break; } case MSG_AUDIO: { - if(!_tryedGetAudioTrack) { - _tryedGetAudioTrack = true; + if (!_try_get_audio_track) { + _try_get_audio_track = true; auto codec = AMFValue(pkt->getMediaType()); makeAudioTrack(codec, pkt->getAudioSampleRate(), pkt->getAudioChannel(), pkt->getAudioSampleBit()); } - if(_audioRtmpDecoder){ - _audioRtmpDecoder->inputRtmp(pkt, false); - return false; + if (_audio_rtmp_decoder) { + _audio_rtmp_decoder->inputRtmp(pkt); } - return false; + break; } - default: - return false; + default : break; } } @@ -101,12 +99,12 @@ void RtmpDemuxer::makeVideoTrack(const AMFValue &videoCodec) { _videoTrack = dynamic_pointer_cast(Factory::getVideoTrackByAmf(videoCodec)); if (_videoTrack) { //生成rtmpCodec对象以便解码rtmp - _videoRtmpDecoder = Factory::getRtmpCodecByTrack(_videoTrack, false); - if (_videoRtmpDecoder) { + _video_rtmp_decoder = Factory::getRtmpCodecByTrack(_videoTrack, false); + if (_video_rtmp_decoder) { //设置rtmp解码器代理,生成的frame写入该Track - _videoRtmpDecoder->addDelegate(_videoTrack); + _video_rtmp_decoder->addDelegate(_videoTrack); onAddTrack(_videoTrack); - _tryedGetVideoTrack = true; + _try_get_video_track = true; } else { //找不到相应的rtmp解码器,该track无效 _videoTrack.reset(); @@ -119,12 +117,12 @@ void RtmpDemuxer::makeAudioTrack(const AMFValue &audioCodec,int sample_rate, int _audioTrack = dynamic_pointer_cast(Factory::getAudioTrackByAmf(audioCodec, sample_rate, channels, sample_bit)); if (_audioTrack) { //生成rtmpCodec对象以便解码rtmp - _audioRtmpDecoder = Factory::getRtmpCodecByTrack(_audioTrack, false); - if (_audioRtmpDecoder) { + _audio_rtmp_decoder = Factory::getRtmpCodecByTrack(_audioTrack, false); + if (_audio_rtmp_decoder) { //设置rtmp解码器代理,生成的frame写入该Track - _audioRtmpDecoder->addDelegate(_audioTrack); + _audio_rtmp_decoder->addDelegate(_audioTrack); onAddTrack(_audioTrack); - _tryedGetAudioTrack = true; + _try_get_audio_track = true; } else { //找不到相应的rtmp解码器,该track无效 _audioTrack.reset(); diff --git a/src/Rtmp/RtmpDemuxer.h b/src/Rtmp/RtmpDemuxer.h index 7c6bdfa7..253cad69 100644 --- a/src/Rtmp/RtmpDemuxer.h +++ b/src/Rtmp/RtmpDemuxer.h @@ -28,24 +28,25 @@ public: typedef std::shared_ptr Ptr; RtmpDemuxer() = default; - virtual ~RtmpDemuxer() = default; + ~RtmpDemuxer() override = default; bool loadMetaData(const AMFValue &metadata); /** * 开始解复用 * @param pkt rtmp包 - * @return true 代表是i帧 */ - bool inputRtmp(const RtmpPacket::Ptr &pkt); + void inputRtmp(const RtmpPacket::Ptr &pkt); + private: void makeVideoTrack(const AMFValue &val); void makeAudioTrack(const AMFValue &val, int sample_rate, int channels, int sample_bit); + private: - bool _tryedGetVideoTrack = false; - bool _tryedGetAudioTrack = false; - RtmpCodec::Ptr _audioRtmpDecoder; - RtmpCodec::Ptr _videoRtmpDecoder; + bool _try_get_video_track = false; + bool _try_get_audio_track = false; + RtmpCodec::Ptr _audio_rtmp_decoder; + RtmpCodec::Ptr _video_rtmp_decoder; }; } /* namespace mediakit */ diff --git a/src/Rtmp/RtmpMediaSource.h b/src/Rtmp/RtmpMediaSource.h index 5718c94f..cd3f6b5c 100644 --- a/src/Rtmp/RtmpMediaSource.h +++ b/src/Rtmp/RtmpMediaSource.h @@ -60,7 +60,7 @@ public: MediaSource(RTMP_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) { } - virtual ~RtmpMediaSource() {} + ~RtmpMediaSource() override{} /** * 获取媒体源的环形缓冲 @@ -100,9 +100,8 @@ public: * 设置metadata */ virtual void setMetaData(const AMFValue &metadata) { - lock_guard lock(_mtx); _metadata = metadata; - if(_ring){ + if (_ring) { regist(); } } @@ -118,25 +117,24 @@ public: /** * 输入rtmp包 * @param pkt rtmp包 - * @param key 是否为关键帧 */ - void onWrite(const RtmpPacket::Ptr &pkt, bool key = true) override { - lock_guard lock(_mtx); - if(pkt->typeId == MSG_VIDEO){ - //有视频,那么启用GOP缓存 - _have_video = true; + void onWrite(const RtmpPacket::Ptr &pkt, bool = true) override { + //保存当前时间戳 + switch (pkt->type_id) { + case MSG_VIDEO : _track_stamps[TrackVideo] = pkt->time_stamp, _have_video = true; break; + case MSG_AUDIO : _track_stamps[TrackAudio] = pkt->time_stamp; break; + default : break; } + if (pkt->isCfgFrame()) { - _config_frame_map[pkt->typeId] = pkt; + lock_guard lock(_mtx); + _config_frame_map[pkt->type_id] = pkt; return; } - //保存当前时间戳 - _track_stamps_map[pkt->typeId] = pkt->timeStamp; - if (!_ring) { weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - auto lam = [weakSelf](const EventPoller::Ptr &, int size, bool) { + auto lam = [weakSelf](int size) { auto strongSelf = weakSelf.lock(); if (!strongSelf) { return; @@ -144,9 +142,8 @@ public: strongSelf->onReaderChanged(size); }; - //rtmp包缓存最大允许512个,如果是纯视频(25fps)大概为20秒数据 - //但是这个是GOP缓存的上限值,真实的GOP缓存大小等于两个I帧之间的包数的两倍 - //而且每次遇到I帧,则会清空GOP缓存,所以真实的GOP缓存远小于此值 + //GOP默认缓冲512组RTMP包,每组RTMP包时间戳相同(如果开启合并写了,那么每组为合并写时间内的RTMP包), + //每次遇到关键帧第一个RTMP包,则会清空GOP缓存(因为有新的关键帧了,同样可以实现秒开) _ring = std::make_shared(_ring_size,std::move(lam)); onReaderChanged(0); @@ -154,26 +151,35 @@ public: regist(); } } - PacketCache::inputPacket(pkt->typeId == MSG_VIDEO, pkt, key); + PacketCache::inputPacket(pkt->type_id == MSG_VIDEO, pkt, pkt->isVideoKeyFrame()); } /** * 获取当前时间戳 */ uint32_t getTimeStamp(TrackType trackType) override { - lock_guard lock(_mtx); - switch (trackType) { - case TrackVideo: - return _track_stamps_map[MSG_VIDEO]; - case TrackAudio: - return _track_stamps_map[MSG_AUDIO]; - default: - return MAX(_track_stamps_map[MSG_VIDEO], _track_stamps_map[MSG_AUDIO]); + assert(trackType >= TrackInvalid && trackType < TrackMax); + if (trackType != TrackInvalid) { + //获取某track的时间戳 + return _track_stamps[trackType]; } + + //获取所有track的最小时间戳 + uint32_t ret = UINT32_MAX; + for (auto &stamp : _track_stamps) { + if (stamp > 0 && stamp < ret) { + ret = stamp; + } + } + return ret; + } + + void clearCache() override{ + PacketCache::clearCache(); + _ring->clearCache(); } private: - /** * 批量flush rtmp包时触发该函数 * @param rtmp_list rtmp包列表 @@ -184,22 +190,14 @@ private: _ring->write(rtmp_list, _have_video ? key_pos : true); } - /** - * 每次增减消费者都会触发该函数 - */ - void onReaderChanged(int size) { - if (size == 0) { - onNoneReader(); - } - } - private: - int _ring_size; bool _have_video = false; - mutable recursive_mutex _mtx; + int _ring_size; + uint32_t _track_stamps[TrackMax] = {0}; AMFValue _metadata; RingType::Ptr _ring; - unordered_map _track_stamps_map; + + mutable recursive_mutex _mtx; unordered_map _config_frame_map; }; diff --git a/src/Rtmp/RtmpMediaSourceImp.h b/src/Rtmp/RtmpMediaSourceImp.h index a779f30c..3b3de9d7 100644 --- a/src/Rtmp/RtmpMediaSourceImp.h +++ b/src/Rtmp/RtmpMediaSourceImp.h @@ -60,27 +60,12 @@ public: /** * 输入rtmp并解析 */ - void onWrite(const RtmpPacket::Ptr &pkt,bool key_pos = true) override { - if(_all_track_ready && !_muxer->isEnabled()){ - //获取到所有Track后,并且未开启转协议,那么不需要解复用rtmp - key_pos = pkt->isVideoKeyFrame(); - }else{ - //需要解复用rtmp - key_pos = _demuxer->inputRtmp(pkt); - } - - RtmpMediaSource::onWrite(pkt,key_pos); - } - - /** - * 设置监听器 - * @param listener - */ - void setListener(const std::weak_ptr &listener) override { - RtmpMediaSource::setListener(listener); - if(_muxer){ - _muxer->setMediaListener(listener); + void onWrite(const RtmpPacket::Ptr &pkt, bool = true) override { + if (!_all_track_ready || _muxer->isEnabled()) { + //未获取到所有Track后,或者开启转协议,那么需要解复用rtmp + _demuxer->inputRtmp(pkt); } + RtmpMediaSource::onWrite(pkt); } /** @@ -90,44 +75,19 @@ public: return readerCount() + (_muxer ? _muxer->totalReaderCount() : 0); } - /** - * 设置录制状态 - * @param type 录制类型 - * @param start 开始或停止 - * @param custom_path 开启录制时,指定自定义路径 - * @return 是否设置成功 - */ - bool setupRecord(Recorder::type type, bool start, const string &custom_path) override{ - if(_muxer){ - return _muxer->setupRecord(*this,type, start, custom_path); - } - return RtmpMediaSource::setupRecord(type, start, custom_path); - } - - /** - * 获取录制状态 - * @param type 录制类型 - * @return 录制状态 - */ - bool isRecording(Recorder::type type) override{ - if(_muxer){ - return _muxer->isRecording(*this,type); - } - return RtmpMediaSource::isRecording(type); - } - - /** * 设置协议转换 - * @param enableRtsp 是否转换成rtsp * @param enableHls 是否转换成hls * @param enableMP4 是否mp4录制 */ - void setProtocolTranslation(bool enableRtsp, bool enableHls, bool enableMP4) { + void setProtocolTranslation(bool enableHls, bool enableMP4) { //不重复生成rtmp - _muxer = std::make_shared(getVhost(), getApp(), getId(), _demuxer->getDuration(), enableRtsp, false, enableHls, enableMP4); + _muxer = std::make_shared(getVhost(), getApp(), getId(), _demuxer->getDuration(), true, false, enableHls, enableMP4); _muxer->setMediaListener(getListener()); - _muxer->setTrackListener(this); + _muxer->setTrackListener(static_pointer_cast(shared_from_this())); + //让_muxer对象拦截一部分事件(比如说录像相关事件) + setListener(_muxer); + for(auto &track : _demuxer->getTracks(false)){ _muxer->addTrack(track); track->addDelegate(_muxer); @@ -148,12 +108,11 @@ public: * _muxer触发的所有Track就绪的事件 */ void onAllTrackReady() override{ - setTrackSource(_muxer); _all_track_ready = true; if (_recreate_metadata) { //更新metadata - for (auto &track : _muxer->getTracks()) { + for (auto &track : _muxer->getTracks(*this)) { Metadata::addTrack(_metadata, track); } RtmpMediaSource::updateMetaData(_metadata); @@ -161,11 +120,12 @@ public: } private: - RtmpDemuxer::Ptr _demuxer; - MultiMediaSourceMuxer::Ptr _muxer; - AMFValue _metadata; bool _all_track_ready = false; bool _recreate_metadata = false; + AMFValue _metadata; + RtmpDemuxer::Ptr _demuxer; + MultiMediaSourceMuxer::Ptr _muxer; + }; } /* namespace mediakit */ diff --git a/src/Rtmp/RtmpMediaSourceMuxer.h b/src/Rtmp/RtmpMediaSourceMuxer.h index c1ab506c..e176beab 100644 --- a/src/Rtmp/RtmpMediaSourceMuxer.h +++ b/src/Rtmp/RtmpMediaSourceMuxer.h @@ -16,7 +16,8 @@ namespace mediakit { -class RtmpMediaSourceMuxer : public RtmpMuxer { +class RtmpMediaSourceMuxer : public RtmpMuxer, public MediaSourceEventInterceptor, + public std::enable_shared_from_this { public: typedef std::shared_ptr Ptr; @@ -24,34 +25,57 @@ public: const string &strApp, const string &strId, const TitleMeta::Ptr &title = nullptr) : RtmpMuxer(title){ - _mediaSouce = std::make_shared(vhost,strApp,strId); - getRtmpRing()->setDelegate(_mediaSouce); + _media_src = std::make_shared(vhost, strApp, strId); + getRtmpRing()->setDelegate(_media_src); } - virtual ~RtmpMediaSourceMuxer(){} + + ~RtmpMediaSourceMuxer() override{} void setListener(const std::weak_ptr &listener){ - _mediaSouce->setListener(listener); + _listener = listener; + _media_src->setListener(shared_from_this()); } void setTimeStamp(uint32_t stamp){ - _mediaSouce->setTimeStamp(stamp); + _media_src->setTimeStamp(stamp); } int readerCount() const{ - return _mediaSouce->readerCount(); + return _media_src->readerCount(); } void onAllTrackReady(){ makeConfigPacket(); - _mediaSouce->setMetaData(getMetadata()); + _media_src->setMetaData(getMetadata()); } - // 设置TrackSource - void setTrackSource(const std::weak_ptr &track_src){ - _mediaSouce->setTrackSource(track_src); + void onReaderChanged(MediaSource &sender, int size) override { + _enabled = size; + if (!size) { + _clear_cache = true; + } + MediaSourceEventInterceptor::onReaderChanged(sender, size); } + + void inputFrame(const Frame::Ptr &frame) override { + if (_clear_cache) { + _clear_cache = false; + _media_src->clearCache(); + } + if (_enabled) { + RtmpMuxer::inputFrame(frame); + } + } + + bool isEnabled() { + //缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 + return _clear_cache ? true : _enabled; + } + private: - RtmpMediaSource::Ptr _mediaSouce; + bool _enabled = true; + bool _clear_cache = false; + RtmpMediaSource::Ptr _media_src; }; diff --git a/src/Rtmp/RtmpMuxer.cpp b/src/Rtmp/RtmpMuxer.cpp index 50686d01..a44266c2 100644 --- a/src/Rtmp/RtmpMuxer.cpp +++ b/src/Rtmp/RtmpMuxer.cpp @@ -19,7 +19,7 @@ RtmpMuxer::RtmpMuxer(const TitleMeta::Ptr &title) { }else{ _metadata = title->getMetadata(); } - _rtmpRing = std::make_shared(); + _rtmp_ring = std::make_shared(); } void RtmpMuxer::addTrack(const Track::Ptr &track) { @@ -31,7 +31,7 @@ void RtmpMuxer::addTrack(const Track::Ptr &track) { } //设置rtmp输出环形缓存 - encoder->setRtmpRing(_rtmpRing); + encoder->setRtmpRing(_rtmp_ring); //添加metadata Metadata::addTrack(_metadata,track); @@ -57,7 +57,7 @@ const AMFValue &RtmpMuxer::getMetadata() const { } RtmpRing::RingType::Ptr RtmpMuxer::getRtmpRing() const { - return _rtmpRing; + return _rtmp_ring; } void RtmpMuxer::resetTracks() { diff --git a/src/Rtmp/RtmpMuxer.h b/src/Rtmp/RtmpMuxer.h index ad6f27e6..6533e9a4 100644 --- a/src/Rtmp/RtmpMuxer.h +++ b/src/Rtmp/RtmpMuxer.h @@ -61,7 +61,7 @@ public: */ void makeConfigPacket(); private: - RtmpRing::RingType::Ptr _rtmpRing; + RtmpRing::RingType::Ptr _rtmp_ring; AMFValue _metadata; RtmpCodec::Ptr _encoder[TrackMax]; }; diff --git a/src/Rtmp/RtmpPlayer.cpp b/src/Rtmp/RtmpPlayer.cpp index 09556454..247a6d19 100644 --- a/src/Rtmp/RtmpPlayer.cpp +++ b/src/Rtmp/RtmpPlayer.cpp @@ -18,8 +18,7 @@ using namespace mediakit::Client; namespace mediakit { -RtmpPlayer::RtmpPlayer(const EventPoller::Ptr &poller) : TcpClient(poller) { -} +RtmpPlayer::RtmpPlayer(const EventPoller::Ptr &poller) : TcpClient(poller) {} RtmpPlayer::~RtmpPlayer() { DebugL << endl; @@ -29,97 +28,82 @@ void RtmpPlayer::teardown() { if (alive()) { shutdown(SockException(Err_shutdown,"teardown")); } - _strApp.clear(); - _strStream.clear(); - _strTcUrl.clear(); - _pBeatTimer.reset(); - _pPlayTimer.reset(); - _pMediaTimer.reset(); - _iSeekTo = 0; + _app.clear(); + _stream_id.clear(); + _tc_url.clear(); + _beat_timer.reset(); + _play_timer.reset(); + _rtmp_recv_timer.reset(); + _seek_ms = 0; RtmpProtocol::reset(); - CLEAR_ARR(_aiFistStamp); - CLEAR_ARR(_aiNowStamp); + CLEAR_ARR(_fist_stamp); + CLEAR_ARR(_now_stamp); - lock_guard lck(_mtxOnResultCB); - _mapOnResultCB.clear(); - lock_guard lck2(_mtxOnStatusCB); - _dqOnStatusCB.clear(); + lock_guard lck(_mtx_on_result); + _map_on_result.clear(); + lock_guard lck2(_mtx_on_status); + _deque_on_status.clear(); } void RtmpPlayer::play(const string &strUrl) { teardown(); - string strHost = FindField(strUrl.data(), "://", "/"); - _strApp = FindField(strUrl.data(), (strHost + "/").data(), "/"); - _strStream = FindField(strUrl.data(), (strHost + "/" + _strApp + "/").data(), NULL); - _strTcUrl = string("rtmp://") + strHost + "/" + _strApp; + string host_url = FindField(strUrl.data(), "://", "/"); + _app = FindField(strUrl.data(), (host_url + "/").data(), "/"); + _stream_id = FindField(strUrl.data(), (host_url + "/" + _app + "/").data(), NULL); + _tc_url = string("rtmp://") + host_url + "/" + _app; - if (!_strApp.size() || !_strStream.size()) { - onPlayResult_l(SockException(Err_other,"rtmp url非法"),false); + if (!_app.size() || !_stream_id.size()) { + onPlayResult_l(SockException(Err_other, "rtmp url非法"), false); return; } - DebugL << strHost << " " << _strApp << " " << _strStream; + DebugL << host_url << " " << _app << " " << _stream_id; - auto iPort = atoi(FindField(strHost.data(), ":", NULL).data()); + auto iPort = atoi(FindField(host_url.data(), ":", NULL).data()); if (iPort <= 0) { //rtmp 默认端口1935 iPort = 1935; } else { //服务器域名 - strHost = FindField(strHost.data(), NULL, ":"); + host_url = FindField(host_url.data(), NULL, ":"); } - if(!(*this)[kNetAdapter].empty()){ + if (!(*this)[kNetAdapter].empty()) { setNetAdapter((*this)[kNetAdapter]); } - weak_ptr weakSelf= dynamic_pointer_cast(shared_from_this()); - float playTimeOutSec = (*this)[kTimeoutMS].as() / 1000.0; - _pPlayTimer.reset( new Timer(playTimeOutSec, [weakSelf]() { - auto strongSelf=weakSelf.lock(); - if(!strongSelf) { + weak_ptr weak_self = dynamic_pointer_cast(shared_from_this()); + float play_timeout_sec = (*this)[kTimeoutMS].as() / 1000.0; + _play_timer.reset(new Timer(play_timeout_sec, [weak_self]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { return false; } - strongSelf->onPlayResult_l(SockException(Err_timeout,"play rtmp timeout"),false); + strong_self->onPlayResult_l(SockException(Err_timeout, "play rtmp timeout"), false); return false; - },getPoller())); + }, getPoller())); _metadata_got = false; - startConnect(strHost, iPort , playTimeOutSec); + startConnect(host_url, iPort, play_timeout_sec); } + void RtmpPlayer::onErr(const SockException &ex){ //定时器_pPlayTimer为空后表明握手结束了 - onPlayResult_l(ex, !_pPlayTimer); + onPlayResult_l(ex, !_play_timer); } -void RtmpPlayer::onPlayResult_l(const SockException &ex , bool handshakeCompleted) { - WarnL << ex.getErrCode() << " " << ex.what(); - - if(!ex){ - //播放成功,恢复rtmp接收超时定时器 - _mediaTicker.resetTime(); - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - int timeoutMS = (*this)[kMediaTimeoutMS].as(); - //创建rtmp数据接收超时检测定时器 - _pMediaTimer.reset( new Timer(timeoutMS / 2000.0, [weakSelf,timeoutMS]() { - auto strongSelf=weakSelf.lock(); - if(!strongSelf) { - return false; - } - if(strongSelf->_mediaTicker.elapsedTime()> timeoutMS) { - //接收rtmp媒体数据超时 - strongSelf->onPlayResult_l(SockException(Err_timeout,"receive rtmp timeout"),true); - return false; - } - return true; - },getPoller())); +void RtmpPlayer::onPlayResult_l(const SockException &ex, bool handshake_done) { + if (ex.getErrCode() == Err_shutdown) { + //主动shutdown的,不触发回调 + return; } - if (!handshakeCompleted) { + WarnL << ex.getErrCode() << " " << ex.what(); + if (!handshake_done) { //开始播放阶段 - _pPlayTimer.reset(); - onPlayResult(ex); + _play_timer.reset(); //是否为性能测试模式 _benchmark_mode = (*this)[Client::kBenchmarkMode].as(); + onPlayResult(ex); } else if (ex) { //播放成功后异常断开回调 onShutdown(ex); @@ -128,36 +112,58 @@ void RtmpPlayer::onPlayResult_l(const SockException &ex , bool handshakeComplete onResume(); } - if(ex){ + if (!ex) { + //播放成功,恢复rtmp接收超时定时器 + _rtmp_recv_ticker.resetTime(); + int timeout_ms = (*this)[kMediaTimeoutMS].as(); + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + auto lam = [weakSelf, timeout_ms]() { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return false; + } + if (strongSelf->_rtmp_recv_ticker.elapsedTime() > timeout_ms) { + //接收rtmp媒体数据超时 + SockException ex(Err_timeout, "receive rtmp timeout"); + strongSelf->onPlayResult_l(ex, true); + return false; + } + return true; + }; + //创建rtmp数据接收超时检测定时器 + _rtmp_recv_timer = std::make_shared(timeout_ms / 2000.0, lam, getPoller()); + } else { teardown(); } } + void RtmpPlayer::onConnect(const SockException &err){ - if(err.getErrCode() != Err_success) { + if (err.getErrCode() != Err_success) { onPlayResult_l(err, false); return; } - weak_ptr weakSelf= dynamic_pointer_cast(shared_from_this()); - startClientSession([weakSelf](){ - auto strongSelf=weakSelf.lock(); - if(!strongSelf) { + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + startClientSession([weakSelf]() { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { return; } strongSelf->send_connect(); }); } -void RtmpPlayer::onRecv(const Buffer::Ptr &pBuf){ + +void RtmpPlayer::onRecv(const Buffer::Ptr &buf){ try { - if(_benchmark_mode && !_pPlayTimer){ + if (_benchmark_mode && !_play_timer) { //在性能测试模式下,如果rtmp握手完毕后,不再解析rtmp包 - _mediaTicker.resetTime(); + _rtmp_recv_ticker.resetTime(); return; } - onParseRtmp(pBuf->data(), pBuf->size()); + onParseRtmp(buf->data(), buf->size()); } catch (exception &e) { SockException ex(Err_other, e.what()); //定时器_pPlayTimer为空后表明握手结束了 - onPlayResult_l(ex, !_pPlayTimer); + onPlayResult_l(ex, !_play_timer); } } @@ -167,8 +173,8 @@ void RtmpPlayer::pause(bool bPause) { inline void RtmpPlayer::send_connect() { AMFValue obj(AMF_OBJECT); - obj.set("app", _strApp); - obj.set("tcUrl", _strTcUrl); + obj.set("app", _app); + obj.set("tcUrl", _tc_url); //未使用代理 obj.set("fpad", false); //参考librtmp,什么作用? @@ -176,18 +182,18 @@ inline void RtmpPlayer::send_connect() { //SUPPORT_VID_CLIENT_SEEK 支持seek obj.set("videoFunction", 1); //只支持aac - obj.set("audioCodecs", (double)(0x0400)); + obj.set("audioCodecs", (double) (0x0400)); //只支持H264 - obj.set("videoCodecs", (double)(0x0080)); + obj.set("videoCodecs", (double) (0x0080)); sendInvoke("connect", obj); - addOnResultCB([this](AMFDecoder &dec){ + addOnResultCB([this](AMFDecoder &dec) { //TraceL << "connect result"; dec.load(); auto val = dec.load(); auto level = val["level"].as_string(); auto code = val["code"].as_string(); - if(level != "status"){ - throw std::runtime_error(StrPrinter <<"connect 失败:" << level << " " << code << endl); + if (level != "status") { + throw std::runtime_error(StrPrinter << "connect 失败:" << level << " " << code << endl); } send_createStream(); }); @@ -196,102 +202,103 @@ inline void RtmpPlayer::send_connect() { inline void RtmpPlayer::send_createStream() { AMFValue obj(AMF_NULL); sendInvoke("createStream", obj); - addOnResultCB([this](AMFDecoder &dec){ + addOnResultCB([this](AMFDecoder &dec) { //TraceL << "createStream result"; dec.load(); - _ui32StreamId = dec.load(); + _stream_index = dec.load(); send_play(); }); } inline void RtmpPlayer::send_play() { AMFEncoder enc; - enc << "play" << ++_iReqID << nullptr << _strStream << (double)_ui32StreamId; + enc << "play" << ++_send_req_id << nullptr << _stream_id << (double) _stream_index; sendRequest(MSG_CMD, enc.data()); - auto fun = [this](AMFValue &val){ + auto fun = [this](AMFValue &val) { //TraceL << "play onStatus"; auto level = val["level"].as_string(); auto code = val["code"].as_string(); - if(level != "status"){ - throw std::runtime_error(StrPrinter <<"play 失败:" << level << " " << code << endl); + if (level != "status") { + throw std::runtime_error(StrPrinter << "play 失败:" << level << " " << code << endl); } }; addOnStatusCB(fun); addOnStatusCB(fun); } -inline void RtmpPlayer::send_pause(bool bPause) { +inline void RtmpPlayer::send_pause(bool pause) { AMFEncoder enc; - enc << "pause" << ++_iReqID << nullptr << bPause; + enc << "pause" << ++_send_req_id << nullptr << pause; sendRequest(MSG_CMD, enc.data()); - auto fun = [this,bPause](AMFValue &val){ + auto fun = [this, pause](AMFValue &val) { //TraceL << "pause onStatus"; auto level = val["level"].as_string(); auto code = val["code"].as_string(); - if(level != "status") { - if(!bPause){ - throw std::runtime_error(StrPrinter <<"pause 恢复播放失败:" << level << " " << code << endl); + if (level != "status") { + if (!pause) { + throw std::runtime_error(StrPrinter << "pause 恢复播放失败:" << level << " " << code << endl); } - }else{ - _bPaused = bPause; - if(!bPause){ + } else { + _paused = pause; + if (!pause) { onPlayResult_l(SockException(Err_success, "resum rtmp success"), true); - }else{ + } else { //暂停播放 - _pMediaTimer.reset(); + _rtmp_recv_timer.reset(); } } }; addOnStatusCB(fun); - _pBeatTimer.reset(); - if(bPause){ + _beat_timer.reset(); + if (pause) { weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - _pBeatTimer.reset(new Timer((*this)[kBeatIntervalMS].as() / 1000.0,[weakSelf](){ + _beat_timer.reset(new Timer((*this)[kBeatIntervalMS].as() / 1000.0, [weakSelf]() { auto strongSelf = weakSelf.lock(); - if (!strongSelf){ + if (!strongSelf) { return false; } uint32_t timeStamp = ::time(NULL); strongSelf->sendUserControl(CONTROL_PING_REQUEST, timeStamp); return true; - },getPoller())); + }, getPoller())); } } void RtmpPlayer::onCmd_result(AMFDecoder &dec){ - auto iReqId = dec.load(); - lock_guard lck(_mtxOnResultCB); - auto it = _mapOnResultCB.find(iReqId); - if(it != _mapOnResultCB.end()){ + auto req_id = dec.load(); + lock_guard lck(_mtx_on_result); + auto it = _map_on_result.find(req_id); + if (it != _map_on_result.end()) { it->second(dec); - _mapOnResultCB.erase(it); - }else{ + _map_on_result.erase(it); + } else { WarnL << "unhandled _result"; } } + void RtmpPlayer::onCmd_onStatus(AMFDecoder &dec) { AMFValue val; - while(true){ + while (true) { val = dec.load(); - if(val.type() == AMF_OBJECT){ + if (val.type() == AMF_OBJECT) { break; } } - if(val.type() != AMF_OBJECT){ + if (val.type() != AMF_OBJECT) { throw std::runtime_error("onStatus:the result object was not found"); } - - lock_guard lck(_mtxOnStatusCB); - if(_dqOnStatusCB.size()){ - _dqOnStatusCB.front()(val); - _dqOnStatusCB.pop_front(); - }else{ + + lock_guard lck(_mtx_on_status); + if (_deque_on_status.size()) { + _deque_on_status.front()(val); + _deque_on_status.pop_front(); + } else { auto level = val["level"]; auto code = val["code"].as_string(); - if(level.type() == AMF_STRING){ - if(level.as_string() != "status"){ - throw std::runtime_error(StrPrinter <<"onStatus 失败:" << level.as_string() << " " << code << endl); + if (level.type() == AMF_STRING) { + if (level.as_string() != "status") { + throw std::runtime_error(StrPrinter << "onStatus 失败:" << level.as_string() << " " << code << endl); } } //WarnL << "unhandled onStatus:" << code; @@ -301,112 +308,111 @@ void RtmpPlayer::onCmd_onStatus(AMFDecoder &dec) { void RtmpPlayer::onCmd_onMetaData(AMFDecoder &dec) { //TraceL; auto val = dec.load(); - if(!onCheckMeta(val)){ + if (!onCheckMeta(val)) { throw std::runtime_error("onCheckMeta failed"); } _metadata_got = true; } -void RtmpPlayer::onStreamDry(uint32_t ui32StreamId) { - //TraceL << ui32StreamId; - onPlayResult_l(SockException(Err_other,"rtmp stream dry"), true); +void RtmpPlayer::onStreamDry(uint32_t stream_index) { + //TraceL << stream_index; + onPlayResult_l(SockException(Err_other, "rtmp stream dry"), true); } -void RtmpPlayer::onMediaData_l(const RtmpPacket::Ptr &packet) { - _mediaTicker.resetTime(); - if(!_pPlayTimer){ +void RtmpPlayer::onMediaData_l(const RtmpPacket::Ptr &chunk_data) { + _rtmp_recv_ticker.resetTime(); + if (!_play_timer) { //已经触发了onPlayResult事件,直接触发onMediaData事件 - onMediaData(packet); + onMediaData(chunk_data); return; } - if(packet->isCfgFrame()){ + if (chunk_data->isCfgFrame()) { //输入配置帧以便初始化完成各个track - onMediaData(packet); - }else{ + onMediaData(chunk_data); + } else { //先触发onPlayResult事件,这个时候解码器才能初始化完毕 - onPlayResult_l(SockException(Err_success,"play rtmp success"), false); + onPlayResult_l(SockException(Err_success, "play rtmp success"), false); //触发onPlayResult事件后,再把帧数据输入到解码器 - onMediaData(packet); + onMediaData(chunk_data); } } -void RtmpPlayer::onRtmpChunk(RtmpPacket &chunkData) { +void RtmpPlayer::onRtmpChunk(RtmpPacket &chunk_data) { typedef void (RtmpPlayer::*rtmp_func_ptr)(AMFDecoder &dec); static unordered_map s_func_map; static onceToken token([]() { - s_func_map.emplace("_error",&RtmpPlayer::onCmd_result); - s_func_map.emplace("_result",&RtmpPlayer::onCmd_result); - s_func_map.emplace("onStatus",&RtmpPlayer::onCmd_onStatus); - s_func_map.emplace("onMetaData",&RtmpPlayer::onCmd_onMetaData); - }, []() {}); + s_func_map.emplace("_error", &RtmpPlayer::onCmd_result); + s_func_map.emplace("_result", &RtmpPlayer::onCmd_result); + s_func_map.emplace("onStatus", &RtmpPlayer::onCmd_onStatus); + s_func_map.emplace("onMetaData", &RtmpPlayer::onCmd_onMetaData); + }); - switch (chunkData.typeId) { + switch (chunk_data.type_id) { case MSG_CMD: case MSG_CMD3: case MSG_DATA: case MSG_DATA3: { - AMFDecoder dec(chunkData.strBuf, 0); + AMFDecoder dec(chunk_data.buffer, 0); std::string type = dec.load(); auto it = s_func_map.find(type); - if(it != s_func_map.end()){ + if (it != s_func_map.end()) { auto fun = it->second; (this->*fun)(dec); - }else{ + } else { WarnL << "can not support cmd:" << type; } - } break; + } + case MSG_AUDIO: case MSG_VIDEO: { - auto idx = chunkData.typeId%2; - if (_aNowStampTicker[idx].elapsedTime() > 500) { + auto idx = chunk_data.type_id % 2; + if (_now_stamp_ticker[idx].elapsedTime() > 500) { //计算播放进度时间轴用 - _aiNowStamp[idx] = chunkData.timeStamp; + _now_stamp[idx] = chunk_data.time_stamp; } - if(!_metadata_got){ - if(!onCheckMeta(TitleMeta().getMetadata())){ + if (!_metadata_got) { + if (!onCheckMeta(TitleMeta().getMetadata())) { throw std::runtime_error("onCheckMeta failed"); } _metadata_got = true; } - onMediaData_l(std::make_shared(std::move(chunkData))); - } - break; - default: - //WarnL << "unhandled message:" << (int) chunkData.typeId << hexdump(chunkData.strBuf.data(), chunkData.strBuf.size()); + onMediaData_l(std::make_shared(std::move(chunk_data))); break; } + + default: break; + } } uint32_t RtmpPlayer::getProgressMilliSecond() const{ - uint32_t iTime[2] = {0,0}; - for(auto i = 0 ;i < 2 ;i++){ - iTime[i] = _aiNowStamp[i] - _aiFistStamp[i]; + uint32_t stamp[2] = {0, 0}; + for (auto i = 0; i < 2; i++) { + stamp[i] = _now_stamp[i] - _fist_stamp[i]; } - return _iSeekTo + MAX(iTime[0],iTime[1]); + return _seek_ms + MAX(stamp[0], stamp[1]); } + void RtmpPlayer::seekToMilliSecond(uint32_t seekMS){ - if (_bPaused) { + if (_paused) { pause(false); } AMFEncoder enc; - enc << "seek" << ++_iReqID << nullptr << seekMS * 1.0; + enc << "seek" << ++_send_req_id << nullptr << seekMS * 1.0; sendRequest(MSG_CMD, enc.data()); - addOnStatusCB([this,seekMS](AMFValue &val) { + addOnStatusCB([this, seekMS](AMFValue &val) { //TraceL << "seek result"; - _aNowStampTicker[0].resetTime(); - _aNowStampTicker[1].resetTime(); + _now_stamp_ticker[0].resetTime(); + _now_stamp_ticker[1].resetTime(); int iTimeInc = seekMS - getProgressMilliSecond(); - for(auto i = 0 ;i < 2 ;i++){ - _aiFistStamp[i] = _aiNowStamp[i] + iTimeInc; - _aiNowStamp[i] = _aiFistStamp[i]; + for (auto i = 0; i < 2; i++) { + _fist_stamp[i] = _now_stamp[i] + iTimeInc; + _now_stamp[i] = _fist_stamp[i]; } - _iSeekTo = seekMS; + _seek_ms = seekMS; }); - } } /* namespace mediakit */ - diff --git a/src/Rtmp/RtmpPlayer.h b/src/Rtmp/RtmpPlayer.h index 9b7f1b66..08c0bde0 100644 --- a/src/Rtmp/RtmpPlayer.h +++ b/src/Rtmp/RtmpPlayer.h @@ -8,8 +8,8 @@ * may be found in the AUTHORS file in the root of the source tree. */ -#ifndef SRC_RTMP_RtmpPlayer2_H_ -#define SRC_RTMP_RtmpPlayer2_H_ +#ifndef SRC_RTMP_RtmpPlayer_H_ +#define SRC_RTMP_RtmpPlayer_H_ #include #include @@ -28,84 +28,90 @@ using namespace toolkit; using namespace mediakit::Client; namespace mediakit { + //实现了rtmp播放器协议部分的功能,及数据接收功能 -class RtmpPlayer:public PlayerBase, public TcpClient, public RtmpProtocol{ +class RtmpPlayer : public PlayerBase, public TcpClient, public RtmpProtocol { public: typedef std::shared_ptr Ptr; RtmpPlayer(const EventPoller::Ptr &poller); - virtual ~RtmpPlayer(); + ~RtmpPlayer() override; void play(const string &strUrl) override; void pause(bool bPause) override; void teardown() override; + protected: - virtual bool onCheckMeta(const AMFValue &val) =0; - virtual void onMediaData(const RtmpPacket::Ptr &chunkData) =0; + virtual bool onCheckMeta(const AMFValue &val) = 0; + virtual void onMediaData(const RtmpPacket::Ptr &chunk_data) = 0; uint32_t getProgressMilliSecond() const; void seekToMilliSecond(uint32_t ms); + protected: - void onMediaData_l(const RtmpPacket::Ptr &chunkData); + void onMediaData_l(const RtmpPacket::Ptr &chunk_data); //在获取config帧后才触发onPlayResult_l(而不是收到play命令回复),所以此时所有track都初始化完毕了 - void onPlayResult_l(const SockException &ex, bool handshakeCompleted); + void onPlayResult_l(const SockException &ex, bool handshake_done); //form Tcpclient - void onRecv(const Buffer::Ptr &pBuf) override; + void onRecv(const Buffer::Ptr &buf) override; void onConnect(const SockException &err) override; void onErr(const SockException &ex) override; //from RtmpProtocol - void onRtmpChunk(RtmpPacket &chunkData) override; - void onStreamDry(uint32_t ui32StreamId) override; - void onSendRawData(const Buffer::Ptr &buffer) override{ + void onRtmpChunk(RtmpPacket &chunk_data) override; + void onStreamDry(uint32_t stream_index) override; + void onSendRawData(const Buffer::Ptr &buffer) override { send(buffer); } - template - inline void addOnResultCB(const FUN &fun) { - lock_guard lck(_mtxOnResultCB); - _mapOnResultCB.emplace(_iReqID, fun); + template + void addOnResultCB(const FUNC &func) { + lock_guard lck(_mtx_on_result); + _map_on_result.emplace(_send_req_id, func); } - template - inline void addOnStatusCB(const FUN &fun) { - lock_guard lck(_mtxOnStatusCB); - _dqOnStatusCB.emplace_back(fun); + template + void addOnStatusCB(const FUNC &func) { + lock_guard lck(_mtx_on_status); + _deque_on_status.emplace_back(func); } void onCmd_result(AMFDecoder &dec); void onCmd_onStatus(AMFDecoder &dec); void onCmd_onMetaData(AMFDecoder &dec); - inline void send_connect(); - inline void send_createStream(); - inline void send_play(); - inline void send_pause(bool bPause); + void send_connect(); + void send_createStream(); + void send_play(); + void send_pause(bool pause); + private: - string _strApp; - string _strStream; - string _strTcUrl; - bool _bPaused = false; + string _app; + string _stream_id; + string _tc_url; - unordered_map > _mapOnResultCB; - recursive_mutex _mtxOnResultCB; - deque > _dqOnStatusCB; - recursive_mutex _mtxOnStatusCB; - - //超时功能实现 - Ticker _mediaTicker; - std::shared_ptr _pMediaTimer; - std::shared_ptr _pPlayTimer; - //心跳定时器 - std::shared_ptr _pBeatTimer; - - //播放进度控制 - uint32_t _iSeekTo = 0; - uint32_t _aiFistStamp[2] = { 0, 0 }; - uint32_t _aiNowStamp[2] = { 0, 0 }; - Ticker _aNowStampTicker[2]; + bool _paused = false; bool _metadata_got = false; //是否为性能测试模式 bool _benchmark_mode = false; + + //播放进度控制 + uint32_t _seek_ms = 0; + uint32_t _fist_stamp[2] = {0, 0}; + uint32_t _now_stamp[2] = {0, 0}; + Ticker _now_stamp_ticker[2]; + + recursive_mutex _mtx_on_result; + recursive_mutex _mtx_on_status; + deque > _deque_on_status; + unordered_map > _map_on_result; + + //rtmp接收超时计时器 + Ticker _rtmp_recv_ticker; + //心跳发送定时器 + std::shared_ptr _beat_timer; + //播放超时定时器 + std::shared_ptr _play_timer; + //rtmp接收超时定时器 + std::shared_ptr _rtmp_recv_timer; }; } /* namespace mediakit */ - -#endif /* SRC_RTMP_RtmpPlayer2_H_ */ +#endif /* SRC_RTMP_RtmpPlayer_H_ */ diff --git a/src/Rtmp/RtmpPlayerImp.h b/src/Rtmp/RtmpPlayerImp.h index f7678dbe..4ac573c6 100644 --- a/src/Rtmp/RtmpPlayerImp.h +++ b/src/Rtmp/RtmpPlayerImp.h @@ -27,51 +27,59 @@ namespace mediakit { class RtmpPlayerImp: public PlayerImp { public: typedef std::shared_ptr Ptr; - RtmpPlayerImp(const EventPoller::Ptr &poller) : PlayerImp(poller){}; - virtual ~RtmpPlayerImp(){ - DebugL< 0){ + + RtmpPlayerImp(const EventPoller::Ptr &poller) : PlayerImp(poller) {}; + + ~RtmpPlayerImp() override { + DebugL << endl; + } + + float getProgress() const override { + if (getDuration() > 0) { return getProgressMilliSecond() / (getDuration() * 1000); } return PlayerBase::getProgress(); - }; - void seekTo(float fProgress) override{ - fProgress = MAX(float(0),MIN(fProgress,float(1.0))); - seekToMilliSecond(fProgress * getDuration() * 1000); - }; - void play(const string &strUrl) override { - PlayerImp::play(strUrl); } + + void seekTo(float fProgress) override { + fProgress = MAX(float(0), MIN(fProgress, float(1.0))); + seekToMilliSecond(fProgress * getDuration() * 1000); + } + + void play(const string &strUrl) override { + PlayerImp::play(strUrl); + } + private: //派生类回调函数 bool onCheckMeta(const AMFValue &val) override { - _pRtmpMediaSrc = dynamic_pointer_cast(_pMediaSrc); - if(_pRtmpMediaSrc){ - _pRtmpMediaSrc->setMetaData(val); + _rtmp_src = dynamic_pointer_cast(_pMediaSrc); + if (_rtmp_src) { + _rtmp_src->setMetaData(val); _set_meta_data = true; } _delegate.reset(new RtmpDemuxer); _delegate->loadMetaData(val); return true; } + void onMediaData(const RtmpPacket::Ptr &chunkData) override { - if(_pRtmpMediaSrc){ - if(!_set_meta_data && !chunkData->isCfgFrame()){ + if (_rtmp_src) { + if (!_set_meta_data && !chunkData->isCfgFrame()) { _set_meta_data = true; - _pRtmpMediaSrc->setMetaData(TitleMeta().getMetadata()); + _rtmp_src->setMetaData(TitleMeta().getMetadata()); } - _pRtmpMediaSrc->onWrite(chunkData); + _rtmp_src->onWrite(chunkData); } - if(!_delegate){ + if (!_delegate) { //这个流没有metadata _delegate.reset(new RtmpDemuxer()); } _delegate->inputRtmp(chunkData); } + private: - RtmpMediaSource::Ptr _pRtmpMediaSrc; + RtmpMediaSource::Ptr _rtmp_src; bool _set_meta_data = false; }; diff --git a/src/Rtmp/RtmpProtocol.cpp b/src/Rtmp/RtmpProtocol.cpp index 3789b3c3..8c658bc1 100644 --- a/src/Rtmp/RtmpProtocol.cpp +++ b/src/Rtmp/RtmpProtocol.cpp @@ -15,14 +15,22 @@ #include "Thread/ThreadPool.h" using namespace toolkit; +#define C1_DIGEST_SIZE 32 +#define C1_KEY_SIZE 128 +#define C1_SCHEMA_SIZE 764 +#define C1_HANDSHARK_SIZE (RANDOM_LEN + 8) +#define C1_FPKEY_SIZE 30 +#define S1_FMS_KEY_SIZE 36 +#define S2_FMS_KEY_SIZE 68 +#define C1_OFFSET_SIZE 4 + #ifdef ENABLE_OPENSSL #include "Util/SSLBox.h" #include #include -static string openssl_HMACsha256(const void *key,unsigned int key_len, - const void *data,unsigned int data_len){ - std::shared_ptr out(new char[32],[](char *ptr){delete [] ptr;}); +static string openssl_HMACsha256(const void *key, unsigned int key_len, const void *data,unsigned int data_len){ + std::shared_ptr out(new char[32], [](char *ptr) { delete[] ptr; }); unsigned int out_len; #if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER > 0x10100000L) @@ -46,273 +54,271 @@ static string openssl_HMACsha256(const void *key,unsigned int key_len, } #endif //ENABLE_OPENSSL - -#define C1_DIGEST_SIZE 32 -#define C1_KEY_SIZE 128 -#define C1_SCHEMA_SIZE 764 -#define C1_HANDSHARK_SIZE (RANDOM_LEN + 8) -#define C1_FPKEY_SIZE 30 -#define S1_FMS_KEY_SIZE 36 -#define S2_FMS_KEY_SIZE 68 -#define C1_OFFSET_SIZE 4 - namespace mediakit { RtmpProtocol::RtmpProtocol() { - _nextHandle = [this](){ + _next_step_func = [this]() { handle_C0C1(); }; } + RtmpProtocol::~RtmpProtocol() { reset(); } + void RtmpProtocol::reset() { ////////////ChunkSize//////////// - _iChunkLenIn = DEFAULT_CHUNK_LEN; - _iChunkLenOut = DEFAULT_CHUNK_LEN; + _chunk_size_in = DEFAULT_CHUNK_LEN; + _chunk_size_out = DEFAULT_CHUNK_LEN; ////////////Acknowledgement//////////// - _ui32ByteSent = 0; - _ui32LastSent = 0; - _ui32WinSize = 0; + _bytes_sent = 0; + _bytes_sent_last = 0; + _windows_size = 0; ///////////PeerBandwidth/////////// - _ui32Bandwidth = 2500000; - _ui8LimitType = 2; + _bandwidth = 2500000; + _band_limit_type = 2; ////////////Chunk//////////// - _mapChunkData.clear(); - _iNowStreamID = 0; - _iNowChunkID = 0; + _map_chunk_data.clear(); + _now_stream_index = 0; + _now_chunk_id = 0; //////////Invoke Request////////// - _iReqID = 0; + _send_req_id = 0; //////////Rtmp parser////////// - _strRcvBuf.clear(); - _ui32StreamId = STREAM_CONTROL; - _nextHandle = [this]() { + _recv_data_buf.clear(); + _stream_index = STREAM_CONTROL; + _next_step_func = [this]() { handle_C0C1(); }; } -void RtmpProtocol::sendAcknowledgement(uint32_t ui32Size) { - std::string control; - uint32_t stream = htonl(ui32Size); - control.append((char *) &stream, 4); - sendRequest(MSG_ACK, control); +void RtmpProtocol::sendAcknowledgement(uint32_t size) { + size = htonl(size); + std::string acknowledgement((char *) &size, 4); + sendRequest(MSG_ACK, acknowledgement); } -void RtmpProtocol::sendAcknowledgementSize(uint32_t ui32Size) { - uint32_t windowSize = htonl(ui32Size); - std::string set_windowSize((char *) &windowSize, 4); +void RtmpProtocol::sendAcknowledgementSize(uint32_t size) { + size = htonl(size); + std::string set_windowSize((char *) &size, 4); sendRequest(MSG_WIN_SIZE, set_windowSize); } -void RtmpProtocol::sendPeerBandwidth(uint32_t ui32Size) { - uint32_t peerBandwidth = htonl(ui32Size); - std::string set_peerBandwidth((char *) &peerBandwidth, 4); +void RtmpProtocol::sendPeerBandwidth(uint32_t size) { + size = htonl(size); + std::string set_peerBandwidth((char *) &size, 4); set_peerBandwidth.push_back((char) 0x02); sendRequest(MSG_SET_PEER_BW, set_peerBandwidth); } -void RtmpProtocol::sendChunkSize(uint32_t ui32Size) { - uint32_t len = htonl(ui32Size); +void RtmpProtocol::sendChunkSize(uint32_t size) { + uint32_t len = htonl(size); std::string set_chunk((char *) &len, 4); sendRequest(MSG_SET_CHUNK, set_chunk); - _iChunkLenOut = ui32Size; + _chunk_size_out = size; } -void RtmpProtocol::sendPingRequest(uint32_t ui32TimeStamp) { - sendUserControl(CONTROL_PING_REQUEST, ui32TimeStamp); +void RtmpProtocol::sendPingRequest(uint32_t stamp) { + sendUserControl(CONTROL_PING_REQUEST, stamp); } -void RtmpProtocol::sendPingResponse(uint32_t ui32TimeStamp) { - sendUserControl(CONTROL_PING_RESPONSE, ui32TimeStamp); +void RtmpProtocol::sendPingResponse(uint32_t time_stamp) { + sendUserControl(CONTROL_PING_RESPONSE, time_stamp); } -void RtmpProtocol::sendSetBufferLength(uint32_t ui32StreamId, - uint32_t ui32Length) { +void RtmpProtocol::sendSetBufferLength(uint32_t stream_index, uint32_t len) { std::string control; - ui32StreamId = htonl(ui32StreamId); - control.append((char *) &ui32StreamId, 4); - ui32Length = htonl(ui32Length); - control.append((char *) &ui32Length, 4); + stream_index = htonl(stream_index); + control.append((char *) &stream_index, 4); + + len = htonl(len); + control.append((char *) &len, 4); sendUserControl(CONTROL_SETBUFFER, control); } -void RtmpProtocol::sendUserControl(uint16_t ui16EventType, - uint32_t ui32EventData) { +void RtmpProtocol::sendUserControl(uint16_t event_type, uint32_t event_data) { std::string control; - uint16_t type = htons(ui16EventType); - control.append((char *) &type, 2); - uint32_t stream = htonl(ui32EventData); - control.append((char *) &stream, 4); + event_type = htons(event_type); + control.append((char *) &event_type, 2); + + event_data = htonl(event_data); + control.append((char *) &event_data, 4); sendRequest(MSG_USER_CONTROL, control); } -void RtmpProtocol::sendUserControl(uint16_t ui16EventType, - const string& strEventData) { +void RtmpProtocol::sendUserControl(uint16_t event_type, const string &event_data) { std::string control; - uint16_t type = htons(ui16EventType); - control.append((char *) &type, 2); - control.append(strEventData); + event_type = htons(event_type); + control.append((char *) &event_type, 2); + control.append(event_data); sendRequest(MSG_USER_CONTROL, control); } -void RtmpProtocol::sendResponse(int iType, const string& str) { - if(!_bDataStarted && (iType == MSG_DATA)){ - _bDataStarted = true; +void RtmpProtocol::sendResponse(int type, const string &str) { + if(!_data_started && (type == MSG_DATA)){ + _data_started = true; } - sendRtmp(iType, _iNowStreamID, str, 0, _bDataStarted ? CHUNK_CLIENT_REQUEST_AFTER : CHUNK_CLIENT_REQUEST_BEFORE); + sendRtmp(type, _now_stream_index, str, 0, _data_started ? CHUNK_CLIENT_REQUEST_AFTER : CHUNK_CLIENT_REQUEST_BEFORE); } -void RtmpProtocol::sendInvoke(const string& strCmd, const AMFValue& val) { +void RtmpProtocol::sendInvoke(const string &cmd, const AMFValue &val) { AMFEncoder enc; - enc << strCmd << ++_iReqID << val; + enc << cmd << ++_send_req_id << val; sendRequest(MSG_CMD, enc.data()); } -void RtmpProtocol::sendRequest(int iCmd, const string& str) { - sendRtmp(iCmd, _ui32StreamId, str, 0, CHUNK_SERVER_REQUEST); +void RtmpProtocol::sendRequest(int cmd, const string& str) { + sendRtmp(cmd, _stream_index, str, 0, CHUNK_SERVER_REQUEST); } class BufferPartial : public Buffer { public: - BufferPartial(const Buffer::Ptr &buffer,uint32_t offset,uint32_t size){ + BufferPartial(const Buffer::Ptr &buffer, uint32_t offset, uint32_t size){ _buffer = buffer; _data = buffer->data() + offset; _size = size; } - ~BufferPartial(){} + ~BufferPartial() override{} char *data() const override { return _data; } + uint32_t size() const override{ return _size; } + private: - Buffer::Ptr _buffer; char *_data; uint32_t _size; + Buffer::Ptr _buffer; }; -void RtmpProtocol::sendRtmp(uint8_t ui8Type, uint32_t ui32StreamId, - const std::string& strBuf, uint32_t ui32TimeStamp, int iChunkId) { - sendRtmp(ui8Type,ui32StreamId,std::make_shared(strBuf),ui32TimeStamp,iChunkId); +void RtmpProtocol::sendRtmp(uint8_t type, uint32_t stream_index, const std::string &buffer, uint32_t stamp, int chunk_id) { + sendRtmp(type, stream_index, std::make_shared(buffer), stamp, chunk_id); } -void RtmpProtocol::sendRtmp(uint8_t ui8Type, uint32_t ui32StreamId, - const Buffer::Ptr &buf, uint32_t ui32TimeStamp, int iChunkId){ - if (iChunkId < 2 || iChunkId > 63) { - auto strErr = StrPrinter << "不支持发送该类型的块流 ID:" << iChunkId << endl; +void RtmpProtocol::sendRtmp(uint8_t type, uint32_t stream_index, const Buffer::Ptr &buf, uint32_t stamp, int chunk_id){ + if (chunk_id < 2 || chunk_id > 63) { + auto strErr = StrPrinter << "不支持发送该类型的块流 ID:" << chunk_id << endl; throw std::runtime_error(strErr); } //是否有扩展时间戳 - bool bExtStamp = ui32TimeStamp >= 0xFFFFFF; + bool ext_stamp = stamp >= 0xFFFFFF; //rtmp头 - BufferRaw::Ptr bufferHeader = obtainBuffer(); - bufferHeader->setCapacity(sizeof(RtmpHeader)); - bufferHeader->setSize(sizeof(RtmpHeader)); + BufferRaw::Ptr buffer_header = obtainBuffer(); + buffer_header->setCapacity(sizeof(RtmpHeader)); + buffer_header->setSize(sizeof(RtmpHeader)); //对rtmp头赋值,如果使用整形赋值,在arm android上可能由于数据对齐导致总线错误的问题 - RtmpHeader *header = (RtmpHeader*) bufferHeader->data(); - header->flags = (iChunkId & 0x3f) | (0 << 6); - header->typeId = ui8Type; - set_be24(header->timeStamp, bExtStamp ? 0xFFFFFF : ui32TimeStamp); - set_be24(header->bodySize, buf->size()); - set_le32(header->streamId, ui32StreamId); + RtmpHeader *header = (RtmpHeader *) buffer_header->data(); + header->flags = (chunk_id & 0x3f) | (0 << 6); + header->type_id = type; + set_be24(header->time_stamp, ext_stamp ? 0xFFFFFF : stamp); + set_be24(header->body_size, buf->size()); + set_le32(header->stream_index, stream_index); //发送rtmp头 - onSendRawData(bufferHeader); + onSendRawData(buffer_header); //扩展时间戳字段 - BufferRaw::Ptr bufferExtStamp; - if (bExtStamp) { + BufferRaw::Ptr buffer_ext_stamp; + if (ext_stamp) { //生成扩展时间戳 - bufferExtStamp = obtainBuffer(); - bufferExtStamp->setCapacity(4); - bufferExtStamp->setSize(4); - set_be32(bufferExtStamp->data(), ui32TimeStamp); + buffer_ext_stamp = obtainBuffer(); + buffer_ext_stamp->setCapacity(4); + buffer_ext_stamp->setSize(4); + set_be32(buffer_ext_stamp->data(), stamp); } //生成一个字节的flag,标明是什么chunkId - BufferRaw::Ptr bufferFlags = obtainBuffer(); - bufferFlags->setCapacity(1); - bufferFlags->setSize(1); - bufferFlags->data()[0] = (iChunkId & 0x3f) | (3 << 6); - + BufferRaw::Ptr buffer_flags = obtainBuffer(); + buffer_flags->setCapacity(1); + buffer_flags->setSize(1); + buffer_flags->data()[0] = (chunk_id & 0x3f) | (3 << 6); + size_t offset = 0; uint32_t totalSize = sizeof(RtmpHeader); while (offset < buf->size()) { if (offset) { - onSendRawData(bufferFlags); + onSendRawData(buffer_flags); totalSize += 1; } - if (bExtStamp) { + if (ext_stamp) { //扩展时间戳 - onSendRawData(bufferExtStamp); + onSendRawData(buffer_ext_stamp); totalSize += 4; } - size_t chunk = min(_iChunkLenOut, buf->size() - offset); - onSendRawData(std::make_shared(buf,offset,chunk)); + size_t chunk = min(_chunk_size_out, buf->size() - offset); + onSendRawData(std::make_shared(buf, offset, chunk)); totalSize += chunk; offset += chunk; } - _ui32ByteSent += totalSize; - if (_ui32WinSize > 0 && _ui32ByteSent - _ui32LastSent >= _ui32WinSize) { - _ui32LastSent = _ui32ByteSent; - sendAcknowledgement(_ui32ByteSent); + _bytes_sent += totalSize; + if (_windows_size > 0 && _bytes_sent - _bytes_sent_last >= _windows_size) { + _bytes_sent_last = _bytes_sent; + sendAcknowledgement(_bytes_sent); } } +void RtmpProtocol::onParseRtmp(const char *data, int size) { + _recv_data_buf.append(data, size); + //移动拷贝提高性能 + function next_step_func(std::move(_next_step_func)); + //执行下一步 + next_step_func(); -void RtmpProtocol::onParseRtmp(const char *pcRawData, int iSize) { - _strRcvBuf.append(pcRawData, iSize); - auto cb = _nextHandle; - cb(); + if (!_next_step_func) { + //为设置下一步,恢复之 + next_step_func.swap(_next_step_func); + } } ////for client//// -void RtmpProtocol::startClientSession(const function &callBack) { +void RtmpProtocol::startClientSession(const function &func) { //发送 C0C1 char handshake_head = HANDSHAKE_PLAINTEXT; onSendRawData(obtainBuffer(&handshake_head, 1)); RtmpHandshake c1(0); onSendRawData(obtainBuffer((char *) (&c1), sizeof(c1))); - _nextHandle = [this,callBack]() { + _next_step_func = [this, func]() { //等待 S0+S1+S2 - handle_S0S1S2(callBack); + handle_S0S1S2(func); }; } -void RtmpProtocol::handle_S0S1S2(const function &callBack) { - if (_strRcvBuf.size() < 1 + 2 * C1_HANDSHARK_SIZE) { + +void RtmpProtocol::handle_S0S1S2(const function &func) { + if (_recv_data_buf.size() < 1 + 2 * C1_HANDSHARK_SIZE) { //数据不够 return; } - if (_strRcvBuf[0] != HANDSHAKE_PLAINTEXT) { + if (_recv_data_buf[0] != HANDSHAKE_PLAINTEXT) { throw std::runtime_error("only plaintext[0x03] handshake supported"); } //发送 C2 - const char *pcC2 = _strRcvBuf.data() + 1; + const char *pcC2 = _recv_data_buf.data() + 1; onSendRawData(obtainBuffer(pcC2, C1_HANDSHARK_SIZE)); - _strRcvBuf.erase(0, 1 + 2 * C1_HANDSHARK_SIZE); + _recv_data_buf.erase(0, 1 + 2 * C1_HANDSHARK_SIZE); //握手结束 - _nextHandle = [this]() { + _next_step_func = [this]() { //握手结束并且开始进入解析命令模式 handle_rtmp(); }; - callBack(); + func(); } + ////for server //// void RtmpProtocol::handle_C0C1() { - if (_strRcvBuf.size() < 1 + C1_HANDSHARK_SIZE) { + if (_recv_data_buf.size() < 1 + C1_HANDSHARK_SIZE) { //need more data! return; } - if (_strRcvBuf[0] != HANDSHAKE_PLAINTEXT) { + if (_recv_data_buf[0] != HANDSHAKE_PLAINTEXT) { throw std::runtime_error("only plaintext[0x03] handshake supported"); } - if(memcmp(_strRcvBuf.data() + 5,"\x00\x00\x00\x00",4) ==0 ){ + if (memcmp(_recv_data_buf.data() + 5, "\x00\x00\x00\x00", 4) == 0) { //simple handsharke handle_C1_simple(); - }else{ + } else { #ifdef ENABLE_OPENSSL //complex handsharke handle_C1_complex(); @@ -321,8 +327,9 @@ void RtmpProtocol::handle_C0C1() { handle_C1_simple(); #endif//ENABLE_OPENSSL } - _strRcvBuf.erase(0, 1 + C1_HANDSHARK_SIZE); + _recv_data_buf.erase(0, 1 + C1_HANDSHARK_SIZE); } + void RtmpProtocol::handle_C1_simple(){ //发送S0 char handshake_head = HANDSHAKE_PLAINTEXT; @@ -331,51 +338,52 @@ void RtmpProtocol::handle_C1_simple(){ RtmpHandshake s1(0); onSendRawData(obtainBuffer((char *) &s1, C1_HANDSHARK_SIZE)); //发送S2 - onSendRawData(obtainBuffer(_strRcvBuf.data() + 1, C1_HANDSHARK_SIZE)); + onSendRawData(obtainBuffer(_recv_data_buf.data() + 1, C1_HANDSHARK_SIZE)); //等待C2 - _nextHandle = [this]() { + _next_step_func = [this]() { handle_C2(); }; } + #ifdef ENABLE_OPENSSL void RtmpProtocol::handle_C1_complex(){ //参考自:http://blog.csdn.net/win_lin/article/details/13006803 //skip c0,time,version - const char *c1_start = _strRcvBuf.data() + 1; + const char *c1_start = _recv_data_buf.data() + 1; const char *schema_start = c1_start + 8; char *digest_start; - try{ + try { /* c1s1 schema0 time: 4bytes version: 4bytes key: 764bytes digest: 764bytes */ - auto digest = get_C1_digest((uint8_t *)schema_start + C1_SCHEMA_SIZE,&digest_start); - string c1_joined(c1_start,C1_HANDSHARK_SIZE); - c1_joined.erase(digest_start - c1_start , C1_DIGEST_SIZE ); - check_C1_Digest(digest,c1_joined); + auto digest = get_C1_digest((uint8_t *) schema_start + C1_SCHEMA_SIZE, &digest_start); + string c1_joined(c1_start, C1_HANDSHARK_SIZE); + c1_joined.erase(digest_start - c1_start, C1_DIGEST_SIZE); + check_C1_Digest(digest, c1_joined); - send_complex_S0S1S2(0,digest); + send_complex_S0S1S2(0, digest); // InfoL << "schema0"; - }catch(std::exception &ex){ + } catch (std::exception &ex) { //貌似flash从来都不用schema1 // WarnL << "try rtmp complex schema0 failed:" << ex.what(); - try{ + try { /* c1s1 schema1 time: 4bytes version: 4bytes digest: 764bytes key: 764bytes */ - auto digest = get_C1_digest((uint8_t *)schema_start,&digest_start); - string c1_joined(c1_start,C1_HANDSHARK_SIZE); - c1_joined.erase(digest_start - c1_start , C1_DIGEST_SIZE ); - check_C1_Digest(digest,c1_joined); + auto digest = get_C1_digest((uint8_t *) schema_start, &digest_start); + string c1_joined(c1_start, C1_HANDSHARK_SIZE); + c1_joined.erase(digest_start - c1_start, C1_DIGEST_SIZE); + check_C1_Digest(digest, c1_joined); - send_complex_S0S1S2(1,digest); + send_complex_S0S1S2(1, digest); // InfoL << "schema1"; - }catch(std::exception &ex){ + } catch (std::exception &ex) { // WarnL << "try rtmp complex schema1 failed:" << ex.what(); handle_C1_simple(); } @@ -408,14 +416,16 @@ static u_int8_t FPKey[] = { 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE }; // 62 + void RtmpProtocol::check_C1_Digest(const string &digest,const string &data){ - auto sha256 = openssl_HMACsha256(FPKey,C1_FPKEY_SIZE,data.data(),data.size()); - if(sha256 != digest){ + auto sha256 = openssl_HMACsha256(FPKey, C1_FPKEY_SIZE, data.data(), data.size()); + if (sha256 != digest) { throw std::runtime_error("digest mismatched"); - }else{ + } else { InfoL << "check rtmp complex handshark success!"; } } + string RtmpProtocol::get_C1_digest(const uint8_t *ptr,char **digestPos){ /* 764bytes digest结构 offset: 4bytes @@ -424,15 +434,16 @@ string RtmpProtocol::get_C1_digest(const uint8_t *ptr,char **digestPos){ random-data: (764-4-offset-32)bytes */ int offset = 0; - for(int i=0;i> 6]; - _iNowChunkID = flags & 0x3f; - switch (_iNowChunkID) { - case 0: { - //0 值表示二字节形式,并且 ID 范围 64 - 319 - //(第二个字节 + 64)。 - if (_strRcvBuf.size() < 2) { - //need more data - return; + while (!_recv_data_buf.empty()) { + int offset = 0; + uint8_t flags = _recv_data_buf[0]; + size_t header_len = HEADER_LENGTH[flags >> 6]; + _now_chunk_id = flags & 0x3f; + switch (_now_chunk_id) { + case 0: { + //0 值表示二字节形式,并且 ID 范围 64 - 319 + //(第二个字节 + 64)。 + if (_recv_data_buf.size() < 2) { + //need more data + return; + } + _now_chunk_id = 64 + (uint8_t) (_recv_data_buf[1]); + offset = 1; + break; } - _iNowChunkID = 64 + (uint8_t) (_strRcvBuf[1]); - iOffset = 1; - } - break; - case 1: { - //1 值表示三字节形式,并且 ID 范围为 64 - 65599 - //((第三个字节) * 256 + 第二个字节 + 64)。 - if (_strRcvBuf.size() < 3) { - //need more data - return; + + case 1: { + //1 值表示三字节形式,并且 ID 范围为 64 - 65599 + //((第三个字节) * 256 + 第二个字节 + 64)。 + if (_recv_data_buf.size() < 3) { + //need more data + return; + } + _now_chunk_id = 64 + ((uint8_t) (_recv_data_buf[2]) << 8) + (uint8_t) (_recv_data_buf[1]); + offset = 2; + break; } - _iNowChunkID = 64 + ((uint8_t) (_strRcvBuf[2]) << 8) + (uint8_t) (_strRcvBuf[1]); - iOffset = 2; - } - break; - default: + //带有 2 值的块流 ID 被保留,用于下层协议控制消息和命令。 - break; + default : break; } - if (_strRcvBuf.size() < iHeaderLen + iOffset) { + if (_recv_data_buf.size() < header_len + offset) { //need more data return; } - RtmpHeader &header = *((RtmpHeader *) (_strRcvBuf.data() + iOffset)); - auto &chunkData = _mapChunkData[_iNowChunkID]; - chunkData.chunkId = _iNowChunkID; - switch (iHeaderLen) { - case 12: - chunkData.hasAbsStamp = true; - chunkData.streamId = load_le32(header.streamId); - case 8: - chunkData.bodySize = load_be24(header.bodySize); - chunkData.typeId = header.typeId; - case 4: - chunkData.deltaStamp = load_be24(header.timeStamp); - chunkData.hasExtStamp = chunkData.deltaStamp == 0xFFFFFF; + + RtmpHeader &header = *((RtmpHeader *) (_recv_data_buf.data() + offset)); + auto &chunk_data = _map_chunk_data[_now_chunk_id]; + chunk_data.chunk_id = _now_chunk_id; + switch (header_len) { + case 12: + chunk_data.is_abs_stamp = true; + chunk_data.stream_index = load_le32(header.stream_index); + case 8: + chunk_data.body_size = load_be24(header.body_size); + chunk_data.type_id = header.type_id; + case 4: + chunk_data.ts_field = load_be24(header.time_stamp); } - - if (chunkData.hasExtStamp) { - if (_strRcvBuf.size() < iHeaderLen + iOffset + 4) { + + auto time_stamp = chunk_data.ts_field; + if (chunk_data.ts_field == 0xFFFFFF) { + if (_recv_data_buf.size() < header_len + offset + 4) { //need more data return; } - chunkData.deltaStamp = load_be32(_strRcvBuf.data() + iOffset + iHeaderLen); - iOffset += 4; + time_stamp = load_be32(_recv_data_buf.data() + offset + header_len); + offset += 4; } - - if (chunkData.bodySize < chunkData.strBuf.size()) { + + if (chunk_data.body_size < chunk_data.buffer.size()) { throw std::runtime_error("非法的bodySize"); } - - auto iMore = min(_iChunkLenIn, chunkData.bodySize - chunkData.strBuf.size()); - if (_strRcvBuf.size() < iHeaderLen + iOffset + iMore) { + + auto iMore = min(_chunk_size_in, chunk_data.body_size - chunk_data.buffer.size()); + if (_recv_data_buf.size() < header_len + offset + iMore) { //need more data return; } - - chunkData.strBuf.append(_strRcvBuf, iHeaderLen + iOffset, iMore); - _strRcvBuf.erase(0, iHeaderLen + iOffset + iMore); - - if (chunkData.strBuf.size() == chunkData.bodySize) { + chunk_data.buffer.append(_recv_data_buf, header_len + offset, iMore); + _recv_data_buf.erase(0, header_len + offset + iMore); + if (chunk_data.buffer.size() == chunk_data.body_size) { //frame is ready - _iNowStreamID = chunkData.streamId; - chunkData.timeStamp = chunkData.deltaStamp + (chunkData.hasAbsStamp ? 0 : chunkData.timeStamp); - - if(chunkData.bodySize){ - handle_rtmpChunk(chunkData); + _now_stream_index = chunk_data.stream_index; + chunk_data.time_stamp = time_stamp + (chunk_data.is_abs_stamp ? 0 : chunk_data.time_stamp); + if (chunk_data.body_size) { + handle_rtmpChunk(chunk_data); } - chunkData.strBuf.clear(); - chunkData.hasAbsStamp = false; - chunkData.hasExtStamp = false; - chunkData.deltaStamp = 0; + chunk_data.buffer.clear(); + chunk_data.is_abs_stamp = false; } } } -void RtmpProtocol::handle_rtmpChunk(RtmpPacket& chunkData) { - switch (chunkData.typeId) { +void RtmpProtocol::handle_rtmpChunk(RtmpPacket& chunk_data) { + switch (chunk_data.type_id) { case MSG_ACK: { - if (chunkData.strBuf.size() < 4) { + if (chunk_data.buffer.size() < 4) { throw std::runtime_error("MSG_ACK: Not enough data"); } - //auto bytePeerRecv = load_be32(&chunkData.strBuf[0]); + //auto bytePeerRecv = load_be32(&chunk_data.buffer[0]); //TraceL << "MSG_ACK:" << bytePeerRecv; - } break; + } + case MSG_SET_CHUNK: { - if (chunkData.strBuf.size() < 4) { + if (chunk_data.buffer.size() < 4) { throw std::runtime_error("MSG_SET_CHUNK :Not enough data"); } - _iChunkLenIn = load_be32(&chunkData.strBuf[0]); - TraceL << "MSG_SET_CHUNK:" << _iChunkLenIn; - } + _chunk_size_in = load_be32(&chunk_data.buffer[0]); + TraceL << "MSG_SET_CHUNK:" << _chunk_size_in; break; + } + case MSG_USER_CONTROL: { //user control message - if (chunkData.strBuf.size() < 2) { + if (chunk_data.buffer.size() < 2) { throw std::runtime_error("MSG_USER_CONTROL: Not enough data."); } - uint16_t event_type = load_be16(&chunkData.strBuf[0]); - chunkData.strBuf.erase(0, 2); + uint16_t event_type = load_be16(&chunk_data.buffer[0]); + chunk_data.buffer.erase(0, 2); switch (event_type) { - case CONTROL_PING_REQUEST: { - if (chunkData.strBuf.size() < 4) { + case CONTROL_PING_REQUEST: { + if (chunk_data.buffer.size() < 4) { throw std::runtime_error("CONTROL_PING_REQUEST: Not enough data."); } - uint32_t timeStamp = load_be32(&chunkData.strBuf[0]); - //TraceL << "CONTROL_PING_REQUEST:" << timeStamp; + uint32_t timeStamp = load_be32(&chunk_data.buffer[0]); + //TraceL << "CONTROL_PING_REQUEST:" << time_stamp; sendUserControl(CONTROL_PING_RESPONSE, timeStamp); - } break; - case CONTROL_PING_RESPONSE: { - if (chunkData.strBuf.size() < 4) { - throw std::runtime_error("CONTROL_PING_RESPONSE: Not enough data."); } - //uint32_t timeStamp = load_be32(&chunkData.strBuf[0]); - //TraceL << "CONTROL_PING_RESPONSE:" << timeStamp; - } - break; - case CONTROL_STREAM_BEGIN: { - //开始播放 - if (chunkData.strBuf.size() < 4) { - throw std::runtime_error("CONTROL_STREAM_BEGIN: Not enough data."); - } - uint32_t stramId = load_be32(&chunkData.strBuf[0]); - onStreamBegin(stramId); - TraceL << "CONTROL_STREAM_BEGIN:" << stramId; - } - break; - case CONTROL_STREAM_EOF: { - //暂停 - if (chunkData.strBuf.size() < 4) { - throw std::runtime_error("CONTROL_STREAM_EOF: Not enough data."); + case CONTROL_PING_RESPONSE: { + if (chunk_data.buffer.size() < 4) { + throw std::runtime_error("CONTROL_PING_RESPONSE: Not enough data."); + } + //uint32_t time_stamp = load_be32(&chunk_data.buffer[0]); + //TraceL << "CONTROL_PING_RESPONSE:" << time_stamp; + break; } - uint32_t stramId = load_be32(&chunkData.strBuf[0]); - onStreamEof(stramId); - TraceL << "CONTROL_STREAM_EOF:" << stramId; - } - break; - case CONTROL_STREAM_DRY: { - //停止播放 - if (chunkData.strBuf.size() < 4) { - throw std::runtime_error("CONTROL_STREAM_DRY: Not enough data."); + + case CONTROL_STREAM_BEGIN: { + //开始播放 + if (chunk_data.buffer.size() < 4) { + throw std::runtime_error("CONTROL_STREAM_BEGIN: Not enough data."); + } + uint32_t stream_index = load_be32(&chunk_data.buffer[0]); + onStreamBegin(stream_index); + TraceL << "CONTROL_STREAM_BEGIN:" << stream_index; + break; } - uint32_t stramId = load_be32(&chunkData.strBuf[0]); - onStreamDry(stramId); - TraceL << "CONTROL_STREAM_DRY:" << stramId; + + case CONTROL_STREAM_EOF: { + //暂停 + if (chunk_data.buffer.size() < 4) { + throw std::runtime_error("CONTROL_STREAM_EOF: Not enough data."); + } + uint32_t stream_index = load_be32(&chunk_data.buffer[0]); + onStreamEof(stream_index); + TraceL << "CONTROL_STREAM_EOF:" << stream_index; + break; + } + + case CONTROL_STREAM_DRY: { + //停止播放 + if (chunk_data.buffer.size() < 4) { + throw std::runtime_error("CONTROL_STREAM_DRY: Not enough data."); + } + uint32_t stream_index = load_be32(&chunk_data.buffer[0]); + onStreamDry(stream_index); + TraceL << "CONTROL_STREAM_DRY:" << stream_index; + break; + } + + default: /*WarnL << "unhandled user control:" << event_type; */ break; } - break; - default: - //WarnL << "unhandled user control:" << event_type; - break; - } - } break; + } case MSG_WIN_SIZE: { - _ui32WinSize = load_be32(&chunkData.strBuf[0]); - TraceL << "MSG_WIN_SIZE:" << _ui32WinSize; - } + _windows_size = load_be32(&chunk_data.buffer[0]); + TraceL << "MSG_WIN_SIZE:" << _windows_size; break; + } + case MSG_SET_PEER_BW: { - _ui32Bandwidth = load_be32(&chunkData.strBuf[0]); - _ui8LimitType = chunkData.strBuf[4]; - TraceL << "MSG_SET_PEER_BW:" << _ui32WinSize; - } + _bandwidth = load_be32(&chunk_data.buffer[0]); + _band_limit_type = chunk_data.buffer[4]; + TraceL << "MSG_SET_PEER_BW:" << _windows_size; break; + } + case MSG_AGGREGATE: { - auto ptr = (uint8_t*)chunkData.strBuf.data(); - auto ptr_tail = ptr + chunkData.strBuf.length() ; - while(ptr + 8 + 3 < ptr_tail){ + auto ptr = (uint8_t *) chunk_data.buffer.data(); + auto ptr_tail = ptr + chunk_data.buffer.length(); + while (ptr + 8 + 3 < ptr_tail) { auto type = *ptr; ptr += 1; auto size = load_be24(ptr); @@ -706,56 +723,28 @@ void RtmpProtocol::handle_rtmpChunk(RtmpPacket& chunkData) { ptr += 3; ts |= (*ptr << 24); ptr += 1; - - //参考ffmpeg忽略了3个字节 - /** - * while (next - pkt->data < pkt->size - RTMP_HEADER) { - type = bytestream_get_byte(&next); - size = bytestream_get_be24(&next); - cts = bytestream_get_be24(&next); - cts |= bytestream_get_byte(&next) << 24; - if (!pts) - pts = cts; - ts += cts - pts; - pts = cts; - if (size + 3 + 4 > pkt->data + pkt->size - next) - break; - bytestream_put_byte(&p, type); - bytestream_put_be24(&p, size); - bytestream_put_be24(&p, ts); - bytestream_put_byte(&p, ts >> 24); - memcpy(p, next, size + 3 + 4); - p += size + 3; - bytestream_put_be32(&p, size + RTMP_HEADER); - next += size + 3 + 4; - } - */ ptr += 3; //参考FFmpeg多拷贝了4个字节 size += 4; - if(ptr + size > ptr_tail){ -// ErrorL << ptr + size << " " << ptr_tail << " " << ptr_tail - ptr - size; + if (ptr + size > ptr_tail) { break; } -// DebugL << (int)type << " " << size << " " << ts << " " << chunkData.timeStamp << " " << ptr_tail - ptr; - RtmpPacket sub_packet ; - sub_packet.strBuf.resize(size); - memcpy((char *)sub_packet.strBuf.data(),ptr,size); - sub_packet.typeId = type; - sub_packet.bodySize = size; - sub_packet.timeStamp = ts; - sub_packet.streamId = chunkData.streamId; - sub_packet.chunkId = chunkData.chunkId; + RtmpPacket sub_packet; + sub_packet.buffer.resize(size); + memcpy((char *) sub_packet.buffer.data(), ptr, size); + sub_packet.type_id = type; + sub_packet.body_size = size; + sub_packet.time_stamp = ts; + sub_packet.stream_index = chunk_data.stream_index; + sub_packet.chunk_id = chunk_data.chunk_id; handle_rtmpChunk(sub_packet); ptr += size; } -// InfoL << ptr_tail - ptr; - } - break; - default: - onRtmpChunk(chunkData); break; } + + default: onRtmpChunk(chunk_data); break; + } } BufferRaw::Ptr RtmpProtocol::obtainBuffer() { @@ -764,7 +753,7 @@ BufferRaw::Ptr RtmpProtocol::obtainBuffer() { BufferRaw::Ptr RtmpProtocol::obtainBuffer(const void *data, int len) { auto buffer = obtainBuffer(); - buffer->assign((const char *)data,len); + buffer->assign((const char *) data, len); return buffer; } diff --git a/src/Rtmp/RtmpProtocol.h b/src/Rtmp/RtmpProtocol.h index 3eb2d07f..6f2df9dd 100644 --- a/src/Rtmp/RtmpProtocol.h +++ b/src/Rtmp/RtmpProtocol.h @@ -31,45 +31,42 @@ class RtmpProtocol { public: RtmpProtocol(); virtual ~RtmpProtocol(); + + void onParseRtmp(const char *data, int size); //作为客户端发送c0c1,等待s0s1s2并且回调 void startClientSession(const function &cb); - void onParseRtmp(const char *pcRawData,int iSize); - void reset(); + protected: virtual void onSendRawData(const Buffer::Ptr &buffer) = 0; - virtual void onRtmpChunk(RtmpPacket &chunkData) = 0; - virtual void onStreamBegin(uint32_t ui32StreamId){ - _ui32StreamId = ui32StreamId; + virtual void onRtmpChunk(RtmpPacket &chunk_data) = 0; + virtual void onStreamBegin(uint32_t stream_index){ + _stream_index = stream_index; } - virtual void onStreamEof(uint32_t ui32StreamId){}; - virtual void onStreamDry(uint32_t ui32StreamId){}; -protected: - void sendAcknowledgement(uint32_t ui32Size); - void sendAcknowledgementSize(uint32_t ui32Size); - void sendPeerBandwidth(uint32_t ui32Size); - void sendChunkSize(uint32_t ui32Size); - void sendPingRequest(uint32_t ui32TimeStamp = ::time(NULL)); - void sendPingResponse(uint32_t ui32TimeStamp = ::time(NULL)); - void sendSetBufferLength(uint32_t ui32StreamId, uint32_t ui32Length); - void sendUserControl(uint16_t ui16EventType, uint32_t ui32EventData); - void sendUserControl(uint16_t ui16EventType, const string &strEventData); + virtual void onStreamEof(uint32_t stream_index){}; + virtual void onStreamDry(uint32_t stream_index){}; - void sendInvoke(const string &strCmd, const AMFValue &val); - void sendRequest(int iCmd, const string &str); - void sendResponse(int iType, const string &str); - void sendRtmp(uint8_t ui8Type, uint32_t ui32StreamId, const std::string &strBuf, uint32_t ui32TimeStamp, int iChunkID); - void sendRtmp(uint8_t ui8Type, uint32_t ui32StreamId, const Buffer::Ptr &buffer, uint32_t ui32TimeStamp, int iChunkID); protected: - int _iReqID = 0; - uint32_t _ui32StreamId = STREAM_CONTROL; - int _iNowStreamID = 0; - int _iNowChunkID = 0; - bool _bDataStarted = false; - inline BufferRaw::Ptr obtainBuffer(); - inline BufferRaw::Ptr obtainBuffer(const void *data, int len); - //ResourcePool _bufferPool; + void reset(); + BufferRaw::Ptr obtainBuffer(); + BufferRaw::Ptr obtainBuffer(const void *data, int len); + + void sendAcknowledgement(uint32_t size); + void sendAcknowledgementSize(uint32_t size); + void sendPeerBandwidth(uint32_t size); + void sendChunkSize(uint32_t size); + void sendPingRequest(uint32_t ti = ::time(NULL)); + void sendPingResponse(uint32_t time_stamp = ::time(NULL)); + void sendSetBufferLength(uint32_t stream_index, uint32_t len); + void sendUserControl(uint16_t event_type, uint32_t event_data); + void sendUserControl(uint16_t event_type, const string &event_data); + void sendInvoke(const string &cmd, const AMFValue &val); + void sendRequest(int cmd, const string &str); + void sendResponse(int type, const string &str); + void sendRtmp(uint8_t type, uint32_t stream_index, const std::string &buffer, uint32_t stamp, int chunk_id); + void sendRtmp(uint8_t type, uint32_t stream_index, const Buffer::Ptr &buffer, uint32_t stamp, int chunk_id); + private: - void handle_S0S1S2(const function &cb); + void handle_S0S1S2(const function &func); void handle_C0C1(); void handle_C1_simple(); #ifdef ENABLE_OPENSSL @@ -82,26 +79,32 @@ private: void handle_C2(); void handle_rtmp(); - void handle_rtmpChunk(RtmpPacket &chunkData); + void handle_rtmpChunk(RtmpPacket &chunk_data); + +protected: + int _send_req_id = 0; + uint32_t _stream_index = STREAM_CONTROL; private: + int _now_stream_index = 0; + int _now_chunk_id = 0; + bool _data_started = false; ////////////ChunkSize//////////// - size_t _iChunkLenIn = DEFAULT_CHUNK_LEN; - size_t _iChunkLenOut = DEFAULT_CHUNK_LEN; + size_t _chunk_size_in = DEFAULT_CHUNK_LEN; + size_t _chunk_size_out = DEFAULT_CHUNK_LEN; ////////////Acknowledgement//////////// - uint32_t _ui32ByteSent = 0; - uint32_t _ui32LastSent = 0; - uint32_t _ui32WinSize = 0; + uint32_t _bytes_sent = 0; + uint32_t _bytes_sent_last = 0; + uint32_t _windows_size = 0; ///////////PeerBandwidth/////////// - uint32_t _ui32Bandwidth = 2500000; - uint8_t _ui8LimitType = 2; - ////////////Chunk//////////// - unordered_map _mapChunkData; + uint32_t _bandwidth = 2500000; + uint8_t _band_limit_type = 2; //////////Rtmp parser////////// - string _strRcvBuf; - function _nextHandle; + string _recv_data_buf; + function _next_step_func; + ////////////Chunk//////////// + unordered_map _map_chunk_data; }; } /* namespace mediakit */ - #endif /* SRC_RTMP_RTMPPROTOCOL_H_ */ diff --git a/src/Rtmp/RtmpPusher.cpp b/src/Rtmp/RtmpPusher.cpp index 725868b5..201d6f9e 100644 --- a/src/Rtmp/RtmpPusher.cpp +++ b/src/Rtmp/RtmpPusher.cpp @@ -18,141 +18,147 @@ using namespace mediakit::Client; namespace mediakit { -RtmpPusher::RtmpPusher(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &src) : TcpClient(poller){ - _pMediaSrc=src; +RtmpPusher::RtmpPusher(const EventPoller::Ptr &poller, const RtmpMediaSource::Ptr &src) : TcpClient(poller){ + _publish_src = src; } RtmpPusher::~RtmpPusher() { teardown(); DebugL << endl; } + void RtmpPusher::teardown() { if (alive()) { - _strApp.clear(); - _strStream.clear(); - _strTcUrl.clear(); + _app.clear(); + _stream_id.clear(); + _tc_url.clear(); { - lock_guard lck(_mtxOnResultCB); - _mapOnResultCB.clear(); + lock_guard lck(_mtx_on_result); + _map_on_result.clear(); } { - lock_guard lck(_mtxOnStatusCB); - _dqOnStatusCB.clear(); + lock_guard lck(_mtx_on_status); + _deque_on_status.clear(); } - _pPublishTimer.reset(); + _publish_timer.reset(); reset(); - shutdown(SockException(Err_shutdown,"teardown")); + shutdown(SockException(Err_shutdown, "teardown")); } } -void RtmpPusher::onPublishResult(const SockException &ex,bool handshakeCompleted) { - if(!handshakeCompleted){ +void RtmpPusher::onPublishResult(const SockException &ex, bool handshake_done) { + if (ex.getErrCode() == Err_shutdown) { + //主动shutdown的,不触发回调 + return; + } + if (!handshake_done) { //播放结果回调 - _pPublishTimer.reset(); - if(_onPublished){ - _onPublished(ex); + _publish_timer.reset(); + if (_on_published) { + _on_published(ex); } } else { //播放成功后异常断开回调 - if(_onShutdown){ - _onShutdown(ex); + if (_on_shutdown) { + _on_shutdown(ex); } } - if(ex){ + if (ex) { teardown(); } } -void RtmpPusher::publish(const string &strUrl) { +void RtmpPusher::publish(const string &url) { teardown(); - string strHost = FindField(strUrl.data(), "://", "/"); - _strApp = FindField(strUrl.data(), (strHost + "/").data(), "/"); - _strStream = FindField(strUrl.data(), (strHost + "/" + _strApp + "/").data(), NULL); - _strTcUrl = string("rtmp://") + strHost + "/" + _strApp; + string host_url = FindField(url.data(), "://", "/"); + _app = FindField(url.data(), (host_url + "/").data(), "/"); + _stream_id = FindField(url.data(), (host_url + "/" + _app + "/").data(), NULL); + _tc_url = string("rtmp://") + host_url + "/" + _app; - if (!_strApp.size() || !_strStream.size()) { - onPublishResult(SockException(Err_other,"rtmp url非法"),false); + if (!_app.size() || !_stream_id.size()) { + onPublishResult(SockException(Err_other, "rtmp url非法"), false); return; } - DebugL << strHost << " " << _strApp << " " << _strStream; + DebugL << host_url << " " << _app << " " << _stream_id; - auto iPort = atoi(FindField(strHost.data(), ":", NULL).data()); + auto iPort = atoi(FindField(host_url.data(), ":", NULL).data()); if (iPort <= 0) { //rtmp 默认端口1935 iPort = 1935; } else { //服务器域名 - strHost = FindField(strHost.data(), NULL, ":"); + host_url = FindField(host_url.data(), NULL, ":"); } weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); float publishTimeOutSec = (*this)[kTimeoutMS].as() / 1000.0; - _pPublishTimer.reset( new Timer(publishTimeOutSec, [weakSelf]() { - auto strongSelf=weakSelf.lock(); - if(!strongSelf) { + _publish_timer.reset(new Timer(publishTimeOutSec, [weakSelf]() { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { return false; } - strongSelf->onPublishResult(SockException(Err_timeout,"publish rtmp timeout"), false); + strongSelf->onPublishResult(SockException(Err_timeout, "publish rtmp timeout"), false); return false; - },getPoller())); + }, getPoller())); - if(!(*this)[kNetAdapter].empty()){ + if (!(*this)[kNetAdapter].empty()) { setNetAdapter((*this)[kNetAdapter]); } - startConnect(strHost, iPort); + startConnect(host_url, iPort); } void RtmpPusher::onErr(const SockException &ex){ //定时器_pPublishTimer为空后表明握手结束了 - onPublishResult(ex,!_pPublishTimer); + onPublishResult(ex, !_publish_timer); } + void RtmpPusher::onConnect(const SockException &err){ - if(err) { - onPublishResult(err,false); + if (err) { + onPublishResult(err, false); return; } //推流器不需要多大的接收缓存,节省内存占用 - _sock->setReadBuffer(std::make_shared(1 * 1024)); + getSock()->setReadBuffer(std::make_shared(1 * 1024)); - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - startClientSession([weakSelf](){ - auto strongSelf=weakSelf.lock(); - if(!strongSelf) { + weak_ptr weak_self = dynamic_pointer_cast(shared_from_this()); + startClientSession([weak_self]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { return; } - strongSelf->sendChunkSize(60000); - strongSelf->send_connect(); + strong_self->sendChunkSize(60000); + strong_self->send_connect(); }); } -void RtmpPusher::onRecv(const Buffer::Ptr &pBuf){ + +void RtmpPusher::onRecv(const Buffer::Ptr &buf){ try { - onParseRtmp(pBuf->data(), pBuf->size()); + onParseRtmp(buf->data(), buf->size()); } catch (exception &e) { SockException ex(Err_other, e.what()); //定时器_pPublishTimer为空后表明握手结束了 - onPublishResult(ex,!_pPublishTimer); + onPublishResult(ex, !_publish_timer); } } - inline void RtmpPusher::send_connect() { AMFValue obj(AMF_OBJECT); - obj.set("app", _strApp); + obj.set("app", _app); obj.set("type", "nonprivate"); - obj.set("tcUrl", _strTcUrl); - obj.set("swfUrl", _strTcUrl); + obj.set("tcUrl", _tc_url); + obj.set("swfUrl", _tc_url); sendInvoke("connect", obj); - addOnResultCB([this](AMFDecoder &dec){ + addOnResultCB([this](AMFDecoder &dec) { //TraceL << "connect result"; dec.load(); auto val = dec.load(); auto level = val["level"].as_string(); auto code = val["code"].as_string(); - if(level != "status"){ - throw std::runtime_error(StrPrinter <<"connect 失败:" << level << " " << code << endl); + if (level != "status") { + throw std::runtime_error(StrPrinter << "connect 失败:" << level << " " << code << endl); } send_createStream(); }); @@ -161,23 +167,24 @@ inline void RtmpPusher::send_connect() { inline void RtmpPusher::send_createStream() { AMFValue obj(AMF_NULL); sendInvoke("createStream", obj); - addOnResultCB([this](AMFDecoder &dec){ + addOnResultCB([this](AMFDecoder &dec) { //TraceL << "createStream result"; dec.load(); - _ui32StreamId = dec.load(); + _stream_index = dec.load(); send_publish(); }); } + inline void RtmpPusher::send_publish() { AMFEncoder enc; - enc << "publish" << ++_iReqID << nullptr << _strStream << _strApp ; + enc << "publish" << ++_send_req_id << nullptr << _stream_id << _app; sendRequest(MSG_CMD, enc.data()); addOnStatusCB([this](AMFValue &val) { auto level = val["level"].as_string(); auto code = val["code"].as_string(); - if(level != "status") { - throw std::runtime_error(StrPrinter <<"publish 失败:" << level << " " << code << endl); + if (level != "status") { + throw std::runtime_error(StrPrinter << "publish 失败:" << level << " " << code << endl); } //start send media send_metaData(); @@ -185,122 +192,124 @@ inline void RtmpPusher::send_publish() { } inline void RtmpPusher::send_metaData(){ - auto src = _pMediaSrc.lock(); + auto src = _publish_src.lock(); if (!src) { throw std::runtime_error("the media source was released"); } AMFEncoder enc; - enc << "@setDataFrame" << "onMetaData" << src->getMetaData(); + enc << "@setDataFrame" << "onMetaData" << src->getMetaData(); sendRequest(MSG_DATA, enc.data()); - - src->getConfigFrame([&](const RtmpPacket::Ptr &pkt){ - sendRtmp(pkt->typeId, _ui32StreamId, pkt, pkt->timeStamp, pkt->chunkId ); + + src->getConfigFrame([&](const RtmpPacket::Ptr &pkt) { + sendRtmp(pkt->type_id, _stream_index, pkt, pkt->time_stamp, pkt->chunk_id); }); - - _pRtmpReader = src->getRing()->attach(getPoller()); - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - _pRtmpReader->setReadCB([weakSelf](const RtmpMediaSource::RingDataType &pkt){ - auto strongSelf = weakSelf.lock(); - if(!strongSelf) { + + _rtmp_reader = src->getRing()->attach(getPoller()); + weak_ptr weak_self = dynamic_pointer_cast(shared_from_this()); + _rtmp_reader->setReadCB([weak_self](const RtmpMediaSource::RingDataType &pkt) { + auto strong_self = weak_self.lock(); + if (!strong_self) { return; } int i = 0; int size = pkt->size(); - strongSelf->setSendFlushFlag(false); - pkt->for_each([&](const RtmpPacket::Ptr &rtmp){ - if(++i == size){ - strongSelf->setSendFlushFlag(true); + strong_self->setSendFlushFlag(false); + pkt->for_each([&](const RtmpPacket::Ptr &rtmp) { + if (++i == size) { + strong_self->setSendFlushFlag(true); } - strongSelf->sendRtmp(rtmp->typeId, strongSelf->_ui32StreamId, rtmp, rtmp->timeStamp, rtmp->chunkId); + strong_self->sendRtmp(rtmp->type_id, strong_self->_stream_index, rtmp, rtmp->time_stamp, rtmp->chunk_id); }); }); - _pRtmpReader->setDetachCB([weakSelf](){ - auto strongSelf = weakSelf.lock(); - if(strongSelf){ - strongSelf->onPublishResult(SockException(Err_other,"媒体源被释放"), !strongSelf->_pPublishTimer); + _rtmp_reader->setDetachCB([weak_self]() { + auto strong_self = weak_self.lock(); + if (strong_self) { + strong_self->onPublishResult(SockException(Err_other, "媒体源被释放"), !strong_self->_publish_timer); } }); - onPublishResult(SockException(Err_success,"success"), false); + onPublishResult(SockException(Err_success, "success"), false); //提升发送性能 setSocketFlags(); } void RtmpPusher::setSocketFlags(){ GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS); - if(mergeWriteMS > 0) { + if (mergeWriteMS > 0) { //提高发送性能 setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); - SockUtil::setNoDelay(_sock->rawFD(), false); + SockUtil::setNoDelay(getSock()->rawFD(), false); } } void RtmpPusher::onCmd_result(AMFDecoder &dec){ - auto iReqId = dec.load(); - lock_guard lck(_mtxOnResultCB); - auto it = _mapOnResultCB.find(iReqId); - if(it != _mapOnResultCB.end()){ + auto req_id = dec.load(); + lock_guard lck(_mtx_on_result); + auto it = _map_on_result.find(req_id); + if (it != _map_on_result.end()) { it->second(dec); - _mapOnResultCB.erase(it); - }else{ + _map_on_result.erase(it); + } else { WarnL << "unhandled _result"; } } + void RtmpPusher::onCmd_onStatus(AMFDecoder &dec) { AMFValue val; - while(true){ + while (true) { val = dec.load(); - if(val.type() == AMF_OBJECT){ + if (val.type() == AMF_OBJECT) { break; } } - if(val.type() != AMF_OBJECT){ + if (val.type() != AMF_OBJECT) { throw std::runtime_error("onStatus:the result object was not found"); } - lock_guard lck(_mtxOnStatusCB); - if(_dqOnStatusCB.size()){ - _dqOnStatusCB.front()(val); - _dqOnStatusCB.pop_front(); - }else{ + lock_guard lck(_mtx_on_status); + if (_deque_on_status.size()) { + _deque_on_status.front()(val); + _deque_on_status.pop_front(); + } else { auto level = val["level"]; auto code = val["code"].as_string(); - if(level.type() == AMF_STRING){ - if(level.as_string() != "status"){ - throw std::runtime_error(StrPrinter <<"onStatus 失败:" << level.as_string() << " " << code << endl); + if (level.type() == AMF_STRING) { + if (level.as_string() != "status") { + throw std::runtime_error(StrPrinter << "onStatus 失败:" << level.as_string() << " " << code << endl); } } } } -void RtmpPusher::onRtmpChunk(RtmpPacket &chunkData) { - switch (chunkData.typeId) { +void RtmpPusher::onRtmpChunk(RtmpPacket &chunk_data) { + switch (chunk_data.type_id) { case MSG_CMD: case MSG_CMD3: { typedef void (RtmpPusher::*rtmpCMDHandle)(AMFDecoder &dec); static unordered_map g_mapCmd; static onceToken token([]() { - g_mapCmd.emplace("_error",&RtmpPusher::onCmd_result); - g_mapCmd.emplace("_result",&RtmpPusher::onCmd_result); - g_mapCmd.emplace("onStatus",&RtmpPusher::onCmd_onStatus); - }, []() {}); + g_mapCmd.emplace("_error", &RtmpPusher::onCmd_result); + g_mapCmd.emplace("_result", &RtmpPusher::onCmd_result); + g_mapCmd.emplace("onStatus", &RtmpPusher::onCmd_onStatus); + }); - AMFDecoder dec(chunkData.strBuf, 0); + AMFDecoder dec(chunk_data.buffer, 0); std::string type = dec.load(); auto it = g_mapCmd.find(type); - if(it != g_mapCmd.end()){ + if (it != g_mapCmd.end()) { auto fun = it->second; (this->*fun)(dec); - }else{ + } else { WarnL << "can not support cmd:" << type; } - } break; + } + default: - //WarnL << "unhandled message:" << (int) chunkData.typeId << hexdump(chunkData.strBuf.data(), chunkData.strBuf.size()); + //WarnL << "unhandled message:" << (int) chunk_data.type_id << hexdump(chunk_data.buffer.data(), chunk_data.buffer.size()); break; - } + } } diff --git a/src/Rtmp/RtmpPusher.h b/src/Rtmp/RtmpPusher.h index af825968..3d7e7044 100644 --- a/src/Rtmp/RtmpPusher.h +++ b/src/Rtmp/RtmpPusher.h @@ -18,46 +18,47 @@ namespace mediakit { -class RtmpPusher: public RtmpProtocol , public TcpClient , public PusherBase{ +class RtmpPusher : public RtmpProtocol, public TcpClient, public PusherBase { public: typedef std::shared_ptr Ptr; RtmpPusher(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &src); - virtual ~RtmpPusher(); - - void publish(const string &strUrl) override ; + ~RtmpPusher() override; + void publish(const string &url) override ; void teardown() override; void setOnPublished(const Event &cb) override { - _onPublished = cb; + _on_published = cb; } void setOnShutdown(const Event &cb) override{ - _onShutdown = cb; + _on_shutdown = cb; } + protected: //for Tcpclient override - void onRecv(const Buffer::Ptr &pBuf) override; + void onRecv(const Buffer::Ptr &buf) override; void onConnect(const SockException &err) override; void onErr(const SockException &ex) override; //for RtmpProtocol override - void onRtmpChunk(RtmpPacket &chunkData) override; + void onRtmpChunk(RtmpPacket &chunk_data) override; void onSendRawData(const Buffer::Ptr &buffer) override{ send(buffer); } + private: - void onPublishResult(const SockException &ex,bool handshakeCompleted); + void onPublishResult(const SockException &ex, bool handshake_done); template inline void addOnResultCB(const FUN &fun) { - lock_guard lck(_mtxOnResultCB); - _mapOnResultCB.emplace(_iReqID, fun); + lock_guard lck(_mtx_on_result); + _map_on_result.emplace(_send_req_id, fun); } template inline void addOnStatusCB(const FUN &fun) { - lock_guard lck(_mtxOnStatusCB); - _dqOnStatusCB.emplace_back(fun); + lock_guard lck(_mtx_on_status); + _deque_on_status.emplace_back(fun); } void onCmd_result(AMFDecoder &dec); @@ -69,23 +70,25 @@ private: inline void send_publish(); inline void send_metaData(); void setSocketFlags(); -private: - string _strApp; - string _strStream; - string _strTcUrl; - unordered_map > _mapOnResultCB; - recursive_mutex _mtxOnResultCB; - deque > _dqOnStatusCB; - recursive_mutex _mtxOnStatusCB; - //超时功能实现 - std::shared_ptr _pPublishTimer; - //源 - std::weak_ptr _pMediaSrc; - RtmpMediaSource::RingType::RingReader::Ptr _pRtmpReader; +private: + string _app; + string _stream_id; + string _tc_url; + + recursive_mutex _mtx_on_result; + recursive_mutex _mtx_on_status; + deque > _deque_on_status; + unordered_map > _map_on_result; + //事件监听 - Event _onShutdown; - Event _onPublished; + Event _on_shutdown; + Event _on_published; + + //推流超时定时器 + std::shared_ptr _publish_timer; + std::weak_ptr _publish_src; + RtmpMediaSource::RingType::RingReader::Ptr _rtmp_reader; }; } /* namespace mediakit */ diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index 777f3a13..91ea29e2 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -13,12 +13,12 @@ #include "Util/onceToken.h" namespace mediakit { -RtmpSession::RtmpSession(const Socket::Ptr &pSock) : TcpSession(pSock) { +RtmpSession::RtmpSession(const Socket::Ptr &sock) : TcpSession(sock) { DebugP(this); GET_CONFIG(uint32_t,keep_alive_sec,Rtmp::kKeepAliveSecond); - pSock->setSendTimeOutSecond(keep_alive_sec); + sock->setSendTimeOutSecond(keep_alive_sec); //起始接收buffer缓存设置为4K,节省内存 - pSock->setReadBuffer(std::make_shared(4 * 1024)); + sock->setReadBuffer(std::make_shared(4 * 1024)); } RtmpSession::~RtmpSession() { @@ -26,20 +26,20 @@ RtmpSession::~RtmpSession() { } void RtmpSession::onError(const SockException& err) { - bool isPlayer = !_pPublisherSrc; + bool isPlayer = !_publisher_src; uint64_t duration = _ticker.createdTime()/1000; WarnP(this) << (isPlayer ? "RTMP播放器(" : "RTMP推流器(") - << _mediaInfo._vhost << "/" - << _mediaInfo._app << "/" - << _mediaInfo._streamid + << _media_info._vhost << "/" + << _media_info._app << "/" + << _media_info._streamid << ")断开:" << err.what() << ",耗时(s):" << duration; //流量统计事件广播 GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold); - if(_ui64TotalBytes > iFlowThreshold * 1024){ - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, duration, isPlayer, static_cast(*this)); + if(_total_bytes > iFlowThreshold * 1024){ + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _media_info, _total_bytes, duration, isPlayer, static_cast(*this)); } } @@ -48,11 +48,11 @@ void RtmpSession::onManager() { GET_CONFIG(uint32_t,keep_alive_sec,Rtmp::kKeepAliveSecond); if (_ticker.createdTime() > handshake_sec * 1000) { - if (!_pRingReader && !_pPublisherSrc) { + if (!_ring_reader && !_publisher_src) { shutdown(SockException(Err_timeout,"illegal connection")); } } - if (_pPublisherSrc) { + if (_publisher_src) { //publisher if (_ticker.elapsedTime() > keep_alive_sec * 1000) { shutdown(SockException(Err_timeout,"recv data from rtmp pusher timeout")); @@ -60,22 +60,22 @@ void RtmpSession::onManager() { } } -void RtmpSession::onRecv(const Buffer::Ptr &pBuf) { +void RtmpSession::onRecv(const Buffer::Ptr &buf) { _ticker.resetTime(); try { - _ui64TotalBytes += pBuf->size(); - onParseRtmp(pBuf->data(), pBuf->size()); - } catch (exception &e) { - shutdown(SockException(Err_shutdown, e.what())); + _total_bytes += buf->size(); + onParseRtmp(buf->data(), buf->size()); + } catch (exception &ex) { + shutdown(SockException(Err_shutdown, ex.what())); } } void RtmpSession::onCmd_connect(AMFDecoder &dec) { auto params = dec.load(); - double amfVer = 0; + double amf_ver = 0; AMFValue objectEncoding = params["objectEncoding"]; if(objectEncoding){ - amfVer = objectEncoding.as_number(); + amf_ver = objectEncoding.as_number(); } ///////////set chunk size//////////////// sendChunkSize(60000); @@ -84,11 +84,11 @@ void RtmpSession::onCmd_connect(AMFDecoder &dec) { ///////////set peerBandwidth//////////////// sendPeerBandwidth(5000000); - _mediaInfo._app = params["app"].as_string(); - _strTcUrl = params["tcUrl"].as_string(); - if(_strTcUrl.empty()){ + _media_info._app = params["app"].as_string(); + _tc_url = params["tcUrl"].as_string(); + if(_tc_url.empty()){ //defaultVhost:默认vhost - _strTcUrl = string(RTMP_SCHEMA) + "://" + DEFAULT_VHOST + "/" + _mediaInfo._app; + _tc_url = string(RTMP_SCHEMA) + "://" + DEFAULT_VHOST + "/" + _media_info._app; } bool ok = true; //(app == APP_NAME); AMFValue version(AMF_OBJECT); @@ -98,10 +98,10 @@ void RtmpSession::onCmd_connect(AMFDecoder &dec) { status.set("level", ok ? "status" : "error"); status.set("code", ok ? "NetConnection.Connect.Success" : "NetConnection.Connect.InvalidApp"); status.set("description", ok ? "Connection succeeded." : "InvalidApp."); - status.set("objectEncoding", amfVer); + status.set("objectEncoding", amf_ver); sendReply(ok ? "_result" : "_error", version, status); if (!ok) { - throw std::runtime_error("Unsupported application: " + _mediaInfo._app); + throw std::runtime_error("Unsupported application: " + _media_info._app); } AMFEncoder invoke; @@ -114,75 +114,74 @@ void RtmpSession::onCmd_createStream(AMFDecoder &dec) { } void RtmpSession::onCmd_publish(AMFDecoder &dec) { - std::shared_ptr pTicker(new Ticker); - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - std::shared_ptr pToken(new onceToken(nullptr,[pTicker,weakSelf](){ - auto strongSelf = weakSelf.lock(); - if(strongSelf){ - DebugP(strongSelf.get()) << "publish 回复时间:" << pTicker->elapsedTime() << "ms"; + std::shared_ptr ticker(new Ticker); + weak_ptr weak_self = dynamic_pointer_cast(shared_from_this()); + std::shared_ptr pToken(new onceToken(nullptr,[ticker,weak_self](){ + auto strong_self = weak_self.lock(); + if(strong_self){ + DebugP(strong_self.get()) << "publish 回复时间:" << ticker->elapsedTime() << "ms"; } })); dec.load();/* NULL */ - _mediaInfo.parse(_strTcUrl + "/" + getStreamId(dec.load())); - _mediaInfo._schema = RTMP_SCHEMA; + _media_info.parse(_tc_url + "/" + getStreamId(dec.load())); + _media_info._schema = RTMP_SCHEMA; - auto onRes = [this,pToken](const string &err,bool enableRtxp,bool enableHls,bool enableMP4){ + auto on_res = [this,pToken](const string &err, bool enableHls, bool enableMP4){ auto src = dynamic_pointer_cast(MediaSource::find(RTMP_SCHEMA, - _mediaInfo._vhost, - _mediaInfo._app, - _mediaInfo._streamid)); - bool authSuccess = err.empty(); - bool ok = (!src && !_pPublisherSrc && authSuccess); + _media_info._vhost, + _media_info._app, + _media_info._streamid)); + bool auth_success = err.empty(); + bool ok = (!src && !_publisher_src && auth_success); AMFValue status(AMF_OBJECT); status.set("level", ok ? "status" : "error"); - status.set("code", ok ? "NetStream.Publish.Start" : (authSuccess ? "NetStream.Publish.BadName" : "NetStream.Publish.BadAuth")); - status.set("description", ok ? "Started publishing stream." : (authSuccess ? "Already publishing." : err.data())); + status.set("code", ok ? "NetStream.Publish.Start" : (auth_success ? "NetStream.Publish.BadName" : "NetStream.Publish.BadAuth")); + status.set("description", ok ? "Started publishing stream." : (auth_success ? "Already publishing." : err.data())); status.set("clientid", "0"); sendReply("onStatus", nullptr, status); if (!ok) { - string errMsg = StrPrinter << (authSuccess ? "already publishing:" : err.data()) << " " - << _mediaInfo._vhost << " " - << _mediaInfo._app << " " - << _mediaInfo._streamid; + string errMsg = StrPrinter << (auth_success ? "already publishing:" : err.data()) << " " + << _media_info._vhost << " " + << _media_info._app << " " + << _media_info._streamid; shutdown(SockException(Err_shutdown,errMsg)); return; } - _pPublisherSrc.reset(new RtmpMediaSourceImp(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid)); - _pPublisherSrc->setListener(dynamic_pointer_cast(shared_from_this())); + _publisher_src.reset(new RtmpMediaSourceImp(_media_info._vhost, _media_info._app, _media_info._streamid)); + _publisher_src->setListener(dynamic_pointer_cast(shared_from_this())); //设置转协议 - _pPublisherSrc->setProtocolTranslation(enableRtxp,enableHls,enableMP4); + _publisher_src->setProtocolTranslation(enableHls, enableMP4); //如果是rtmp推流客户端,那么加大TCP接收缓存,这样能提升接收性能 - _sock->setReadBuffer(std::make_shared(256 * 1024)); + getSock()->setReadBuffer(std::make_shared(256 * 1024)); setSocketFlags(); }; - if(_mediaInfo._app.empty() || _mediaInfo._streamid.empty()){ + if(_media_info._app.empty() || _media_info._streamid.empty()){ //不允许莫名其妙的推流url - onRes("rtmp推流url非法", false, false, false); + on_res("rtmp推流url非法", false, false); return; } - Broadcast::PublishAuthInvoker invoker = [weakSelf,onRes,pToken](const string &err,bool enableRtxp,bool enableHls,bool enableMP4){ - auto strongSelf = weakSelf.lock(); - if(!strongSelf){ + Broadcast::PublishAuthInvoker invoker = [weak_self, on_res, pToken](const string &err, bool enableHls, bool enableMP4) { + auto strongSelf = weak_self.lock(); + if (!strongSelf) { return; } - strongSelf->async([weakSelf,onRes,err,pToken,enableRtxp,enableHls,enableMP4](){ - auto strongSelf = weakSelf.lock(); - if(!strongSelf){ + strongSelf->async([weak_self, on_res, err, pToken, enableHls, enableMP4]() { + auto strongSelf = weak_self.lock(); + if (!strongSelf) { return; } - onRes(err,enableRtxp,enableHls,enableMP4); + on_res(err, enableHls, enableMP4); }); }; - auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPublish,_mediaInfo,invoker,static_cast(*this)); + auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPublish, _media_info, invoker, static_cast(*this)); if(!flag){ //该事件无人监听,默认鉴权成功 - GET_CONFIG(bool,toRtxp,General::kPublishToRtxp); - GET_CONFIG(bool,toHls,General::kPublishToHls); - GET_CONFIG(bool,toMP4,General::kPublishToMP4); - onRes("",toRtxp,toHls,toMP4); + GET_CONFIG(bool,to_hls,General::kPublishToHls); + GET_CONFIG(bool,to_mp4,General::kPublishToMP4); + on_res("", to_hls, to_mp4); } } @@ -196,8 +195,8 @@ void RtmpSession::onCmd_deleteStream(AMFDecoder &dec) { } void RtmpSession::sendPlayResponse(const string &err,const RtmpMediaSource::Ptr &src){ - bool authSuccess = err.empty(); - bool ok = (src.operator bool() && authSuccess); + bool auth_success = err.empty(); + bool ok = (src.operator bool() && auth_success); if (ok) { //stream begin sendUserControl(CONTROL_STREAM_BEGIN, STREAM_MEDIA); @@ -205,17 +204,17 @@ void RtmpSession::sendPlayResponse(const string &err,const RtmpMediaSource::Ptr // onStatus(NetStream.Play.Reset) AMFValue status(AMF_OBJECT); status.set("level", ok ? "status" : "error"); - status.set("code", ok ? "NetStream.Play.Reset" : (authSuccess ? "NetStream.Play.StreamNotFound" : "NetStream.Play.BadAuth")); - status.set("description", ok ? "Resetting and playing." : (authSuccess ? "No such stream." : err.data())); - status.set("details", _mediaInfo._streamid); + status.set("code", ok ? "NetStream.Play.Reset" : (auth_success ? "NetStream.Play.StreamNotFound" : "NetStream.Play.BadAuth")); + status.set("description", ok ? "Resetting and playing." : (auth_success ? "No such stream." : err.data())); + status.set("details", _media_info._streamid); status.set("clientid", "0"); sendReply("onStatus", nullptr, status); if (!ok) { - string errMsg = StrPrinter << (authSuccess ? "no such stream:" : err.data()) << " " - << _mediaInfo._vhost << " " - << _mediaInfo._app << " " - << _mediaInfo._streamid; - shutdown(SockException(Err_shutdown,errMsg)); + string err_msg = StrPrinter << (auth_success ? "no such stream:" : err.data()) << " " + << _media_info._vhost << " " + << _media_info._app << " " + << _media_info._streamid; + shutdown(SockException(Err_shutdown, err_msg)); return; } @@ -224,7 +223,7 @@ void RtmpSession::sendPlayResponse(const string &err,const RtmpMediaSource::Ptr status.set("level", "status"); status.set("code", "NetStream.Play.Start"); status.set("description", "Started playing."); - status.set("details", _mediaInfo._streamid); + status.set("details", _media_info._streamid); status.set("clientid", "0"); sendReply("onStatus", nullptr, status); @@ -245,7 +244,7 @@ void RtmpSession::sendPlayResponse(const string &err,const RtmpMediaSource::Ptr status.set("level", "status"); status.set("code", "NetStream.Play.PublishNotify"); status.set("description", "Now published."); - status.set("details", _mediaInfo._streamid); + status.set("details", _media_info._streamid); status.set("clientid", "0"); sendReply("onStatus", nullptr, status); @@ -273,9 +272,9 @@ void RtmpSession::sendPlayResponse(const string &err,const RtmpMediaSource::Ptr //音频同步于视频 _stamp[0].syncTo(_stamp[1]); - _pRingReader = src->getRing()->attach(getPoller()); + _ring_reader = src->getRing()->attach(getPoller()); weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - _pRingReader->setReadCB([weakSelf](const RtmpMediaSource::RingDataType &pkt) { + _ring_reader->setReadCB([weakSelf](const RtmpMediaSource::RingDataType &pkt) { auto strongSelf = weakSelf.lock(); if (!strongSelf) { return; @@ -293,14 +292,14 @@ void RtmpSession::sendPlayResponse(const string &err,const RtmpMediaSource::Ptr strongSelf->onSendMedia(rtmp); }); }); - _pRingReader->setDetachCB([weakSelf]() { + _ring_reader->setDetachCB([weakSelf]() { auto strongSelf = weakSelf.lock(); if (!strongSelf) { return; } strongSelf->shutdown(SockException(Err_shutdown,"rtmp ring buffer detached")); }); - _pPlayerSrc = src; + _player_src = src; if (src->totalReaderCount() == 1) { src->seekTo(0); } @@ -317,46 +316,47 @@ void RtmpSession::doPlayResponse(const string &err,const std::function weakSelf = dynamic_pointer_cast(shared_from_this()); - MediaSource::findAsync(_mediaInfo,weakSelf.lock(),[weakSelf,cb](const MediaSource::Ptr &src){ + weak_ptr weak_self = dynamic_pointer_cast(shared_from_this()); + MediaSource::findAsync(_media_info, weak_self.lock(), [weak_self,cb](const MediaSource::Ptr &src){ auto rtmp_src = dynamic_pointer_cast(src); - auto strongSelf = weakSelf.lock(); - if(strongSelf){ - strongSelf->sendPlayResponse("", rtmp_src); + auto strong_self = weak_self.lock(); + if(strong_self){ + strong_self->sendPlayResponse("", rtmp_src); } cb(rtmp_src.operator bool()); }); } void RtmpSession::doPlay(AMFDecoder &dec){ - std::shared_ptr pTicker(new Ticker); - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - std::shared_ptr pToken(new onceToken(nullptr,[pTicker,weakSelf](){ - auto strongSelf = weakSelf.lock(); - if(strongSelf) { - DebugP(strongSelf.get()) << "play 回复时间:" << pTicker->elapsedTime() << "ms"; + std::shared_ptr ticker(new Ticker); + weak_ptr weak_self = dynamic_pointer_cast(shared_from_this()); + std::shared_ptr token(new onceToken(nullptr, [ticker,weak_self](){ + auto strongSelf = weak_self.lock(); + if (strongSelf) { + DebugP(strongSelf.get()) << "play 回复时间:" << ticker->elapsedTime() << "ms"; } })); - Broadcast::AuthInvoker invoker = [weakSelf,pToken](const string &err){ - auto strongSelf = weakSelf.lock(); - if(!strongSelf){ + Broadcast::AuthInvoker invoker = [weak_self,token](const string &err){ + auto strong_self = weak_self.lock(); + if (!strong_self) { return; } - strongSelf->async([weakSelf,err,pToken](){ - auto strongSelf = weakSelf.lock(); - if(!strongSelf){ + strong_self->async([weak_self, err, token]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { return; } - strongSelf->doPlayResponse(err,[pToken](bool){}); + strong_self->doPlayResponse(err, [token](bool) {}); }); }; - auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed,_mediaInfo,invoker,static_cast(*this)); + auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed, _media_info, invoker, static_cast(*this)); if(!flag){ //该事件无人监听,默认不鉴权 - doPlayResponse("",[pToken](bool){}); + doPlayResponse("",[token](bool){}); } } + void RtmpSession::onCmd_play2(AMFDecoder &dec) { doPlay(dec); } @@ -365,30 +365,30 @@ string RtmpSession::getStreamId(const string &str){ string stream_id; string params; auto pos = str.find('?'); - if(pos != string::npos){ + if (pos != string::npos) { //有url参数 - stream_id = str.substr(0,pos); + stream_id = str.substr(0, pos); //获取url参数 params = str.substr(pos + 1); - }else{ + } else { //没有url参数 stream_id = str; } pos = stream_id.find(":"); - if(pos != string::npos){ + if (pos != string::npos) { //vlc和ffplay在播放 rtmp://127.0.0.1/record/0.mp4时, //传过来的url会是rtmp://127.0.0.1/record/mp4:0, //我们在这里还原成0.mp4 //实际使用时发现vlc,mpv等会传过来rtmp://127.0.0.1/record/mp4:0.mp4,这里做个判断 - auto ext = stream_id.substr(0,pos); + auto ext = stream_id.substr(0, pos); stream_id = stream_id.substr(pos + 1); - if(stream_id.find(ext) == string::npos){ + if (stream_id.find(ext) == string::npos) { stream_id = stream_id + "." + ext; } } - if(params.empty()){ + if (params.empty()) { //没有url参数 return stream_id; } @@ -399,8 +399,8 @@ string RtmpSession::getStreamId(const string &str){ void RtmpSession::onCmd_play(AMFDecoder &dec) { dec.load();/* NULL */ - _mediaInfo.parse(_strTcUrl + "/" + getStreamId(dec.load())); - _mediaInfo._schema = RTMP_SCHEMA; + _media_info.parse(_tc_url + "/" + getStreamId(dec.load())); + _media_info._schema = RTMP_SCHEMA; doPlay(dec); } @@ -419,7 +419,7 @@ void RtmpSession::onCmd_pause(AMFDecoder &dec) { } void RtmpSession::setMetaData(AMFDecoder &dec) { - if (!_pPublisherSrc) { + if (!_publisher_src) { throw std::runtime_error("not a publisher"); } std::string type = dec.load(); @@ -428,22 +428,23 @@ void RtmpSession::setMetaData(AMFDecoder &dec) { } auto metadata = dec.load(); // dumpMetadata(metadata); - _pPublisherSrc->setMetaData(metadata); + _publisher_src->setMetaData(metadata); _set_meta_data = true; } void RtmpSession::onProcessCmd(AMFDecoder &dec) { - typedef void (RtmpSession::*rtmpCMDHandle)(AMFDecoder &dec); - static unordered_map s_cmd_functions; + typedef void (RtmpSession::*cmd_function)(AMFDecoder &dec); + static unordered_map s_cmd_functions; static onceToken token([]() { - 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);}, []() {}); + 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 = s_cmd_functions.find(method); @@ -451,52 +452,54 @@ void RtmpSession::onProcessCmd(AMFDecoder &dec) { // TraceP(this) << "can not support cmd:" << method; return; } - _dNowReqID = dec.load(); + _recv_req_id = dec.load(); auto fun = it->second; (this->*fun)(dec); } -void RtmpSession::onRtmpChunk(RtmpPacket &chunkData) { - switch (chunkData.typeId) { +void RtmpSession::onRtmpChunk(RtmpPacket &chunk_data) { + switch (chunk_data.type_id) { case MSG_CMD: case MSG_CMD3: { - AMFDecoder dec(chunkData.strBuf, chunkData.typeId == MSG_CMD3 ? 1 : 0); + AMFDecoder dec(chunk_data.buffer, chunk_data.type_id == MSG_CMD3 ? 1 : 0); onProcessCmd(dec); - } break; + } case MSG_DATA: case MSG_DATA3: { - AMFDecoder dec(chunkData.strBuf, chunkData.typeId == MSG_CMD3 ? 1 : 0); + AMFDecoder dec(chunk_data.buffer, chunk_data.type_id == MSG_CMD3 ? 1 : 0); std::string type = dec.load(); if (type == "@setDataFrame") { setMetaData(dec); - }else{ + } else { TraceP(this) << "unknown notify:" << type; } - } break; + } + case MSG_AUDIO: case MSG_VIDEO: { - if (!_pPublisherSrc) { + if (!_publisher_src) { throw std::runtime_error("Not a rtmp publisher!"); } - GET_CONFIG(bool,rtmp_modify_stamp,Rtmp::kModifyStamp); - if(rtmp_modify_stamp){ + GET_CONFIG(bool, rtmp_modify_stamp, Rtmp::kModifyStamp); + if (rtmp_modify_stamp) { int64_t dts_out; - _stamp[chunkData.typeId % 2].revise(chunkData.timeStamp, chunkData.timeStamp, dts_out, dts_out, true); - chunkData.timeStamp = dts_out; + _stamp[chunk_data.type_id % 2].revise(chunk_data.time_stamp, chunk_data.time_stamp, dts_out, dts_out, true); + chunk_data.time_stamp = dts_out; } - if(!_set_meta_data && !chunkData.isCfgFrame()){ + if (!_set_meta_data && !chunk_data.isCfgFrame()) { _set_meta_data = true; - _pPublisherSrc->setMetaData(TitleMeta().getMetadata()); + _publisher_src->setMetaData(TitleMeta().getMetadata()); } - _pPublisherSrc->onWrite(std::make_shared(std::move(chunkData))); - } + _publisher_src->onWrite(std::make_shared(std::move(chunk_data))); break; + } + default: - WarnP(this) << "unhandled message:" << (int) chunkData.typeId << hexdump(chunkData.strBuf.data(), chunkData.strBuf.size()); + WarnP(this) << "unhandled message:" << (int) chunk_data.type_id << hexdump(chunk_data.buffer.data(), chunk_data.buffer.size()); break; } } @@ -512,23 +515,23 @@ void RtmpSession::onCmd_seek(AMFDecoder &dec) { auto milliSeconds = dec.load().as_number(); InfoP(this) << "rtmp seekTo(ms):" << milliSeconds; - auto stongSrc = _pPlayerSrc.lock(); - if (stongSrc) { - stongSrc->seekTo(milliSeconds); + auto strong_src = _player_src.lock(); + if (strong_src) { + strong_src->seekTo(milliSeconds); } } void RtmpSession::onSendMedia(const RtmpPacket::Ptr &pkt) { //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); + _stamp[pkt->type_id % 2].revise(pkt->time_stamp, 0, dts_out, dts_out); + sendRtmp(pkt->type_id, pkt->stream_index, pkt, dts_out, pkt->chunk_id); } bool RtmpSession::close(MediaSource &sender,bool force) { //此回调在其他线程触发 - if(!_pPublisherSrc || (!force && _pPublisherSrc->totalReaderCount())){ + if(!_publisher_src || (!force && _publisher_src->totalReaderCount())){ return false; } string err = StrPrinter << "close media:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force; @@ -537,28 +540,28 @@ bool RtmpSession::close(MediaSource &sender,bool force) { } int RtmpSession::totalReaderCount(MediaSource &sender) { - return _pPublisherSrc ? _pPublisherSrc->totalReaderCount() : sender.readerCount(); + return _publisher_src ? _publisher_src->totalReaderCount() : sender.readerCount(); } void RtmpSession::setSocketFlags(){ - GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS); - if(mergeWriteMS > 0) { + GET_CONFIG(int, merge_write_ms, General::kMergeWriteMS); + if (merge_write_ms > 0) { //推流模式下,关闭TCP_NODELAY会增加推流端的延时,但是服务器性能将提高 - SockUtil::setNoDelay(_sock->rawFD(), false); + SockUtil::setNoDelay(getSock()->rawFD(), false); //播放模式下,开启MSG_MORE会增加延时,但是能提高发送性能 setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); } } void RtmpSession::dumpMetadata(const AMFValue &metadata) { - if(metadata.type() != AMF_OBJECT && metadata.type() != AMF_ECMA_ARRAY){ + if (metadata.type() != AMF_OBJECT && metadata.type() != AMF_ECMA_ARRAY) { WarnL << "invalid metadata type:" << metadata.type(); - return ; + return; } _StrPrinter printer; - metadata.object_for_each([&](const string &key, const AMFValue &val){ - printer << "\r\n" << key << "\t:" << val.to_string() ; + metadata.object_for_each([&](const string &key, const AMFValue &val) { + printer << "\r\n" << key << "\t:" << val.to_string(); }); - InfoL << _mediaInfo._vhost << " " << _mediaInfo._app << " " << _mediaInfo._streamid << (string)printer; + InfoL << _media_info._vhost << " " << _media_info._app << " " << _media_info._streamid << (string) printer; } } /* namespace mediakit */ diff --git a/src/Rtmp/RtmpSession.h b/src/Rtmp/RtmpSession.h index f88d2eb6..860422f3 100644 --- a/src/Rtmp/RtmpSession.h +++ b/src/Rtmp/RtmpSession.h @@ -30,11 +30,13 @@ namespace mediakit { class RtmpSession: public TcpSession ,public RtmpProtocol , public MediaSourceEvent{ public: typedef std::shared_ptr Ptr; - RtmpSession(const Socket::Ptr &_sock); - virtual ~RtmpSession(); - void onRecv(const Buffer::Ptr &pBuf) override; + RtmpSession(const Socket::Ptr &sock); + ~RtmpSession() override; + + void onRecv(const Buffer::Ptr &buf) override; void onError(const SockException &err) override; void onManager() override; + private: void onProcessCmd(AMFDecoder &dec); void onCmd_connect(AMFDecoder &dec); @@ -55,15 +57,15 @@ private: void onSendMedia(const RtmpPacket::Ptr &pkt); void onSendRawData(const Buffer::Ptr &buffer) override{ - _ui64TotalBytes += buffer->size(); + _total_bytes += buffer->size(); send(buffer); } - void onRtmpChunk(RtmpPacket &chunkData) override; + void onRtmpChunk(RtmpPacket &chunk_data) override; template inline void sendReply(const char *str, const first &reply, const second &status) { AMFEncoder invoke; - invoke << str << _dNowReqID << reply << status; + invoke << str << _recv_req_id << reply << status; sendResponse(MSG_CMD, invoke.data()); } @@ -74,29 +76,30 @@ private: void setSocketFlags(); string getStreamId(const string &str); void dumpMetadata(const AMFValue &metadata); + private: - std::string _strTcUrl; - MediaInfo _mediaInfo; - double _dNowReqID = 0; + bool _paused = false; bool _set_meta_data = false; - Ticker _ticker;//数据接收时间 - RtmpMediaSource::RingType::RingReader::Ptr _pRingReader; - std::shared_ptr _pPublisherSrc; - std::weak_ptr _pPlayerSrc; + double _recv_req_id = 0; + //消耗的总流量 + uint64_t _total_bytes = 0; + + std::string _tc_url; //时间戳修整器 Stamp _stamp[2]; - //消耗的总流量 - uint64_t _ui64TotalBytes = 0; - bool _paused = false; + //数据接收超时计时器 + Ticker _ticker; + MediaInfo _media_info; + std::weak_ptr _player_src; + std::shared_ptr _publisher_src; + RtmpMediaSource::RingType::RingReader::Ptr _ring_reader; }; - /** * 支持ssl加密的rtmp服务器 */ typedef TcpSessionWithSSL RtmpSessionWithSSL; } /* namespace mediakit */ - #endif /* SRC_RTMP_RTMPSESSION_H_ */ diff --git a/src/Rtp/Decoder.cpp b/src/Rtp/Decoder.cpp index 92402a95..09090d76 100644 --- a/src/Rtp/Decoder.cpp +++ b/src/Rtp/Decoder.cpp @@ -15,6 +15,7 @@ #include "Extension/H265.h" #include "Extension/AAC.h" #include "Extension/G711.h" +#include "Extension/Opus.h" #if defined(ENABLE_RTPPROXY) || defined(ENABLE_HLS) #include "mpeg-ts-proto.h" @@ -91,6 +92,7 @@ static const char *getCodecName(int codec_id) { SWITCH_CASE(PSI_STREAM_AUDIO_G722); SWITCH_CASE(PSI_STREAM_AUDIO_G723); SWITCH_CASE(PSI_STREAM_AUDIO_G729); + SWITCH_CASE(PSI_STREAM_AUDIO_OPUS); default : return "unknown codec"; } } @@ -133,7 +135,7 @@ void DecoderImp::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t d auto frame = std::make_shared((char *) data, bytes, dts, pts,0); _merger.inputFrame(frame,[this](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer) { - onFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts, prefixSize(buffer->data(), buffer->size()))); + onFrame(std::make_shared >(buffer, dts, pts, prefixSize(buffer->data(), buffer->size()), 0)); }); break; } @@ -152,7 +154,7 @@ void DecoderImp::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t d } auto frame = std::make_shared((char *) data, bytes, dts, pts, 0); _merger.inputFrame(frame,[this](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer) { - onFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts, prefixSize(buffer->data(), buffer->size()))); + onFrame(std::make_shared >(buffer, dts, pts, prefixSize(buffer->data(), buffer->size()), 0)); }); break; } @@ -175,7 +177,7 @@ void DecoderImp::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t d WarnL<< "audio track change to AAC from codecid:" << getCodecName(_codecid_audio); return; } - onFrame(std::make_shared((char *) data, bytes, dts, 0, 7)); + onFrame(std::make_shared(CodecAAC, (char *) data, bytes, dts, 0, ADTS_HEADER_LEN)); break; } @@ -195,11 +197,27 @@ void DecoderImp::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t d WarnL<< "audio track change to G711 from codecid:" << getCodecName(_codecid_audio); return; } - auto frame = std::make_shared((char *) data, bytes, dts); - frame->setCodec(codec); - onFrame(frame); + onFrame(std::make_shared(codec, (char *) data, bytes, dts)); break; } + + case PSI_STREAM_AUDIO_OPUS: { + if (!_codecid_audio) { + //获取到音频 + _codecid_audio = codecid; + InfoL << "got audio track: opus"; + auto track = std::make_shared(); + onTrack(track); + } + + if (codecid != _codecid_audio) { + WarnL << "audio track change to opus from codecid:" << getCodecName(_codecid_audio); + return; + } + onFrame(std::make_shared(CodecOpus, (char *) data, bytes, dts)); + break; + } + default: if(codecid != 0){ WarnL<< "unsupported codec type:" << getCodecName(codecid) << " " << (int)codecid; diff --git a/src/Rtp/PSEncoder.cpp b/src/Rtp/PSEncoder.cpp new file mode 100644 index 00000000..dcba1755 --- /dev/null +++ b/src/Rtp/PSEncoder.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(ENABLE_RTPPROXY) +#include "PSEncoder.h" +#include "Extension/H264.h" +namespace mediakit{ + +PSEncoder::PSEncoder() { + _buffer = std::make_shared(); + init(); +} + +PSEncoder::~PSEncoder() { + +} + +void PSEncoder::init() { + static struct ps_muxer_func_t func = { + /*alloc*/ + [](void *param, size_t bytes) { + PSEncoder *thiz = (PSEncoder *) param; + thiz->_buffer->setCapacity(bytes + 1); + return (void *) thiz->_buffer->data(); + }, + /*free*/ + [](void *param, void *packet) { + //什么也不做 + }, + /*wtite*/ + [](void *param, int stream, void *packet, size_t bytes) { + PSEncoder *thiz = (PSEncoder *) param; + thiz->onPS(thiz->_timestamp, packet, bytes); + } + }; + + _muxer.reset(ps_muxer_create(&func, this), [](struct ps_muxer_t *ptr) { + ps_muxer_destroy(ptr); + }); +} + +void PSEncoder::addTrack(const Track::Ptr &track) { + switch (track->getCodecId()) { + case CodecH264: { + _codec_to_trackid[track->getCodecId()].track_id = ps_muxer_add_stream(_muxer.get(), STREAM_VIDEO_H264, nullptr, 0); + break; + } + + case CodecH265: { + _codec_to_trackid[track->getCodecId()].track_id = ps_muxer_add_stream(_muxer.get(), STREAM_VIDEO_H265, nullptr, 0); + break; + } + + case CodecAAC: { + _codec_to_trackid[track->getCodecId()].track_id = ps_muxer_add_stream(_muxer.get(), STREAM_AUDIO_AAC, nullptr, 0); + break; + } + + case CodecG711A: { + _codec_to_trackid[track->getCodecId()].track_id = ps_muxer_add_stream(_muxer.get(), STREAM_AUDIO_G711A, nullptr, 0); + break; + } + + case CodecG711U: { + _codec_to_trackid[track->getCodecId()].track_id = ps_muxer_add_stream(_muxer.get(), STREAM_AUDIO_G711U, nullptr, 0); + break; + } + + case CodecOpus: { + _codec_to_trackid[track->getCodecId()].track_id = ps_muxer_add_stream(_muxer.get(), STREAM_AUDIO_OPUS, nullptr, 0); + break; + } + + default: WarnL << "mpeg-ps 不支持该编码格式,已忽略:" << track->getCodecName(); break; + } + //尝试音视频同步 + stampSync(); +} + +void PSEncoder::stampSync(){ + if(_codec_to_trackid.size() < 2){ + return; + } + + Stamp *audio = nullptr, *video = nullptr; + for(auto &pr : _codec_to_trackid){ + switch (getTrackType((CodecId) pr.first)){ + case TrackAudio : audio = &pr.second.stamp; break; + case TrackVideo : video = &pr.second.stamp; break; + default : break; + } + } + + if(audio && video){ + //音频时间戳同步于视频,因为音频时间戳被修改后不影响播放 + audio->syncTo(*video); + } +} + +void PSEncoder::resetTracks() { + init(); +} + +void PSEncoder::inputFrame(const Frame::Ptr &frame) { + auto it = _codec_to_trackid.find(frame->getCodecId()); + if (it == _codec_to_trackid.end()) { + return; + } + auto &track_info = it->second; + int64_t dts_out, pts_out; + switch (frame->getCodecId()) { + case CodecH264: { + int type = H264_TYPE(*((uint8_t *) frame->data() + frame->prefixSize())); + if (type == H264Frame::NAL_SEI) { + break; + } + } + case CodecH265: { + //这里的代码逻辑是让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)); + } + track_info.stamp.revise(back->dts(), back->pts(), dts_out, pts_out); + _timestamp = dts_out; + ps_muxer_input(_muxer.get(), track_info.track_id, back->keyFrame() ? 0x0001 : 0, pts_out * 90LL, + dts_out * 90LL, merged_frame->data(), merged_frame->size()); + _frameCached.clear(); + } + _frameCached.emplace_back(Frame::getCacheAbleFrame(frame)); + } + break; + + case CodecAAC: { + if (frame->prefixSize() == 0) { + WarnL << "必须提供adts头才能mpeg-ps打包"; + break; + } + } + + default: { + track_info.stamp.revise(frame->dts(), frame->pts(), dts_out, pts_out); + _timestamp = dts_out; + ps_muxer_input(_muxer.get(), track_info.track_id, frame->keyFrame() ? 0x0001 : 0, pts_out * 90LL, + dts_out * 90LL, frame->data(), frame->size()); + } + break; + } +} + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) diff --git a/src/Rtp/PSEncoder.h b/src/Rtp/PSEncoder.h new file mode 100644 index 00000000..5b63d3fc --- /dev/null +++ b/src/Rtp/PSEncoder.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_PSENCODER_H +#define ZLMEDIAKIT_PSENCODER_H +#if defined(ENABLE_RTPPROXY) +#include "mpeg-ps.h" +#include "Common/MediaSink.h" +#include "Common/Stamp.h" +namespace mediakit{ + +//该类实现mpeg-ps容器格式的打包 +class PSEncoder : public MediaSinkInterface { +public: + PSEncoder(); + ~PSEncoder() override; + + /** + * 添加音视频轨道 + */ + void addTrack(const Track::Ptr &track) override; + + /** + * 重置音视频轨道 + */ + void resetTracks() override; + + /** + * 输入帧数据 + */ + void inputFrame(const Frame::Ptr &frame) override; + +protected: + /** + * 输出mpeg-ps的回调函数 + * @param stamp 时间戳,毫秒 + * @param packet 数据指针 + * @param bytes 数据长度 + */ + virtual void onPS(uint32_t stamp, void *packet, size_t bytes) = 0; + +private: + void init(); + //音视频时间戳同步用 + void stampSync(); + +private: + struct track_info { + int track_id = -1; + Stamp stamp; + }; + +private: + uint32_t _timestamp = 0; + BufferRaw::Ptr _buffer; + List _frameCached; + std::shared_ptr _muxer; + unordered_map _codec_to_trackid; +}; + +}//namespace mediakit +#endif //ENABLE_RTPPROXY +#endif //ZLMEDIAKIT_PSENCODER_H diff --git a/src/Rtp/PSRtpSender.cpp b/src/Rtp/PSRtpSender.cpp new file mode 100644 index 00000000..5f80efdf --- /dev/null +++ b/src/Rtp/PSRtpSender.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(ENABLE_RTPPROXY) +#include "PSRtpSender.h" +#include "Rtsp/RtspSession.h" +#include "Thread/WorkThreadPool.h" + +namespace mediakit{ + +PSRtpSender::PSRtpSender(uint32_t ssrc, uint8_t payload_type) { + GET_CONFIG(uint32_t,video_mtu,Rtp::kVideoMtuSize); + _rtp_encoder = std::make_shared(CodecInvalid, ssrc, video_mtu, 90000, payload_type, 0); + _rtp_encoder->setRtpRing(std::make_shared()); + _rtp_encoder->getRtpRing()->setDelegate(std::make_shared([this](const RtpPacket::Ptr &rtp, bool is_key){ + onRtp(rtp, is_key); + })); + _poller = EventPollerPool::Instance().getPoller(); + InfoL << this << " " << printSSRC(_rtp_encoder->getSsrc()); +} + +PSRtpSender::~PSRtpSender() { + InfoL << this << " " << printSSRC(_rtp_encoder->getSsrc()); +} + +void PSRtpSender::startSend(const string &dst_url, uint16_t dst_port, bool is_udp, const function &cb){ + _is_udp = is_udp; + _socket = Socket::createSocket(_poller, false); + _dst_url = dst_url; + _dst_port = dst_port; + weak_ptr weak_self = shared_from_this(); + if (is_udp) { + _socket->bindUdpSock(0); + auto poller = _poller; + WorkThreadPool::Instance().getPoller()->async([cb, dst_url, dst_port, weak_self, poller]() { + struct sockaddr addr; + //切换线程目的是为了dns解析放在后台线程执行 + if (!SockUtil::getDomainIP(dst_url.data(), dst_port, addr)) { + poller->async([dst_url, cb]() { + //切回自己的线程 + cb(SockException(Err_dns, StrPrinter << "dns解析域名失败:" << dst_url)); + }); + return; + } + + //dns解析成功 + poller->async([addr, weak_self, cb]() { + //切回自己的线程 + cb(SockException()); + auto strong_self = weak_self.lock(); + if (strong_self) { + strong_self->_socket->setSendPeerAddr(&addr); + strong_self->onConnect(); + } + }); + }); + } else { + _socket->connect(dst_url, dst_port, [cb, weak_self](const SockException &err) { + cb(err); + auto strong_self = weak_self.lock(); + if (strong_self && !err) { + //tcp连接成功 + strong_self->onConnect(); + } + }); + } +} + +void PSRtpSender::onConnect(){ + _is_connect = true; + //加大发送缓存,防止udp丢包之类的问题 + SockUtil::setSendBuf(_socket->rawFD(), 4 * 1024 * 1024); + if (!_is_udp) { + //关闭tcp no_delay并开启MSG_MORE, 提高发送性能 + SockUtil::setNoDelay(_socket->rawFD(), false); + _socket->setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); + } + //连接建立成功事件 + weak_ptr weak_self = shared_from_this(); + _socket->setOnErr([weak_self](const SockException &err) { + auto strong_self = weak_self.lock(); + if (strong_self) { + strong_self->onErr(err); + } + }); + InfoL << "开始发送 ps rtp:" << _socket->get_peer_ip() << ":" << _socket->get_peer_port() << ", 是否为udp方式:" << _is_udp; +} + +//此函数在其他线程执行 +void PSRtpSender::inputFrame(const Frame::Ptr &frame) { + if (_is_connect) { + //连接成功后才做实质操作(节省cpu资源) + PSEncoder::inputFrame(frame); + } +} + +//此函数在其他线程执行 +void PSRtpSender::onPS(uint32_t stamp, void *packet, size_t bytes) { + _rtp_encoder->inputFrame(std::make_shared((char *) packet, bytes, stamp)); +} + +//此函数在其他线程执行 +void PSRtpSender::onRtp(const RtpPacket::Ptr &rtp, bool) { + //开启合并写提高发送性能 + PacketCache::inputPacket(true, rtp, false); +} + +//此函数在其他线程执行 +void PSRtpSender::onFlush(shared_ptr> &rtp_list, bool) { + if(!_is_connect){ + //连接成功后才能发送数据 + return; + } + + auto is_udp = _is_udp; + auto socket = _socket; + _poller->async([rtp_list, is_udp, socket]() { + int i = 0; + int size = rtp_list->size(); + rtp_list->for_each([&](const RtpPacket::Ptr &packet) { + if (is_udp) { + //udp模式,rtp over tcp前4个字节可以忽略 + socket->send(std::make_shared(packet, 4), nullptr, 0, ++i == size); + } else { + //tcp模式, rtp over tcp前2个字节可以忽略,只保留后续rtp长度的2个字节 + socket->send(std::make_shared(packet, 2), nullptr, 0, ++i == size); + } + }); + }); +} + +void PSRtpSender::onErr(const SockException &ex, bool is_connect) { + _is_connect = false; + + //监听socket断开事件,方便重连 + if (is_connect) { + WarnL << "重连" << _dst_url << ":" << _dst_port << "失败, 原因为:" << ex.what(); + } else { + WarnL << "停止发送 ps rtp:" << _dst_url << ":" << _dst_port << ", 原因为:" << ex.what(); + } + + weak_ptr weak_self = shared_from_this(); + _connect_timer = std::make_shared(10.0, [weak_self]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return false; + } + strong_self->startSend(strong_self->_dst_url, strong_self->_dst_port, strong_self->_is_udp, [weak_self](const SockException &ex){ + auto strong_self = weak_self.lock(); + if (strong_self && ex) { + //连接失败且本对象未销毁,那么重试连接 + strong_self->onErr(ex, true); + } + }); + return false; + }, _poller); +} + +}//namespace mediakit +#endif// defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/PSRtpSender.h b/src/Rtp/PSRtpSender.h new file mode 100644 index 00000000..9cb9a653 --- /dev/null +++ b/src/Rtp/PSRtpSender.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_PSRTPSENDER_H +#define ZLMEDIAKIT_PSRTPSENDER_H +#if defined(ENABLE_RTPPROXY) +#include "PSEncoder.h" +#include "Extension/CommonRtp.h" + +namespace mediakit{ + +class RingDelegateHelper : public RingDelegate { +public: + typedef function onRtp; + + ~RingDelegateHelper() override{} + RingDelegateHelper(onRtp on_rtp){ + _on_rtp = std::move(on_rtp); + } + void onWrite(const RtpPacket::Ptr &in, bool is_key) override{ + _on_rtp(in, is_key); + } + +private: + onRtp _on_rtp; +}; + +//该类在PSEncoder的基础上,实现了mpeg-ps的rtp打包以及发送 +class PSRtpSender : public PSEncoder, public std::enable_shared_from_this, public PacketCache{ +public: + typedef std::shared_ptr Ptr; + + /** + * 构造函数 + * @param ssrc rtp的ssrc + * @param payload_type 国标中ps-rtp的pt一般为96 + */ + PSRtpSender(uint32_t ssrc, uint8_t payload_type = 96); + ~PSRtpSender() override; + + /** + * 开始发送ps-rtp包 + * @param dst_url 目标ip或域名 + * @param dst_port 目标端口 + * @param is_udp 是否采用udp方式发送rtp + * @param cb 连接目标端口是否成功的回调 + */ + void startSend(const string &dst_url, uint16_t dst_port, bool is_udp, const function &cb); + + /** + * 输入帧数据 + */ + void inputFrame(const Frame::Ptr &frame) override; + +protected: + //mpeg-ps回调 + void onPS(uint32_t stamp, void *packet, size_t bytes) override; + + /** + * 批量flush rtp包时触发该函数 + * @param rtp_list rtp包列表 + * @param key_pos 是否包含关键帧 + */ + void onFlush(std::shared_ptr > &rtp_list, bool key_pos) override; + +private: + //rtp打包后回调 + void onRtp(const RtpPacket::Ptr &in, bool is_key); + //udp/tcp连接成功回调 + void onConnect(); + //异常断开socket事件 + void onErr(const SockException &ex, bool is_connect = false); + +private: + bool _is_udp; + bool _is_connect = false; + string _dst_url; + uint16_t _dst_port; + Socket::Ptr _socket; + EventPoller::Ptr _poller; + Timer::Ptr _connect_timer; + std::shared_ptr _rtp_encoder; +}; + +}//namespace mediakit +#endif// defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_PSRTPSENDER_H diff --git a/src/Rtp/RtpDecoder.cpp b/src/Rtp/RtpDecoder.cpp deleted file mode 100644 index a576c8fe..00000000 --- a/src/Rtp/RtpDecoder.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Use of this source code is governed by MIT license that can be found in the - * LICENSE file in the root of the source tree. All contributing project authors - * may be found in the AUTHORS file in the root of the source tree. - */ - -#if defined(ENABLE_RTPPROXY) -#include "Util/logger.h" -#include "RtpDecoder.h" -#include "rtp-payload.h" -using namespace toolkit; - -namespace mediakit{ - -RtpDecoder::RtpDecoder(const char *codec) { - _buffer = std::make_shared(); - _codec = codec; -} - -RtpDecoder::~RtpDecoder() { - if(_rtp_decoder){ - rtp_payload_decode_destroy(_rtp_decoder); - _rtp_decoder = nullptr; - } -} - -void RtpDecoder::decodeRtp(const void *data, int bytes) { - if(!_rtp_decoder){ - static rtp_payload_t s_func= { - [](void* param, int bytes){ - RtpDecoder *obj = (RtpDecoder *)param; - obj->_buffer->setCapacity(bytes); - return (void *)obj->_buffer->data(); - }, - [](void* param, void* packet){ - //do nothing - }, - [](void* param, const void *packet, int bytes, uint32_t timestamp, int flags){ - RtpDecoder *obj = (RtpDecoder *)param; - obj->onRtpDecode((uint8_t *)packet, bytes, timestamp, flags); - } - }; - - uint8_t rtp_type = 0x7F & ((uint8_t *) data)[1]; - InfoL << "rtp type:" << (int) rtp_type; - _rtp_decoder = rtp_payload_decode_create(rtp_type, _codec.data(), &s_func, this); - if (!_rtp_decoder) { - WarnL << "unsupported rtp type:" << (int) rtp_type << ",size:" << bytes << ",hexdump" << hexdump(data, bytes > 16 ? 16 : bytes); - } - } - - if(_rtp_decoder){ - rtp_payload_decode_input(_rtp_decoder,data,bytes); - } -} - -}//namespace mediakit -#endif//defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/RtpDecoder.h b/src/Rtp/RtpDecoder.h deleted file mode 100644 index 78a5b634..00000000 --- a/src/Rtp/RtpDecoder.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Use of this source code is governed by MIT license that can be found in the - * LICENSE file in the root of the source tree. All contributing project authors - * may be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ZLMEDIAKIT_RTPDECODER_H -#define ZLMEDIAKIT_RTPDECODER_H - -#if defined(ENABLE_RTPPROXY) -#include "Network/Buffer.h" -using namespace toolkit; - -namespace mediakit{ - -class RtpDecoder { -public: - RtpDecoder(const char *codec = "MP2P"); - virtual ~RtpDecoder(); - void decodeRtp(const void *data, int bytes); -protected: - virtual void onRtpDecode(const uint8_t *packet, int bytes, uint32_t timestamp, int flags) = 0; -private: - void *_rtp_decoder = nullptr; - BufferRaw::Ptr _buffer; - string _codec; -}; - -}//namespace mediakit -#endif//defined(ENABLE_RTPPROXY) -#endif //ZLMEDIAKIT_RTPDECODER_H diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index 3b9a689d..23b4c754 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -53,6 +53,10 @@ RtpProcess::RtpProcess(const string &stream_id) { }); } } + _rtp_decoder = std::make_shared(CodecInvalid, 256 * 1024); + _rtp_decoder->addDelegate(std::make_shared([this](const Frame::Ptr &frame){ + onRtpDecode((uint8_t *) frame->data(), frame->size(), frame->dts()); + })); } RtpProcess::~RtpProcess() { @@ -111,7 +115,7 @@ static inline bool checkTS(const uint8_t *packet, int bytes){ } void RtpProcess::onRtpSorted(const RtpPacket::Ptr &rtp, int) { - if(rtp->sequence != _sequence + (uint16_t)1 && _sequence != 0){ + if(rtp->sequence != (uint16_t)(_sequence + 1) && _sequence != 0){ WarnP(this) << "rtp丢包:" << rtp->sequence << " != " << _sequence << "+1" << ",公网环境下请使用tcp方式推流"; } _sequence = rtp->sequence; @@ -121,10 +125,25 @@ void RtpProcess::onRtpSorted(const RtpPacket::Ptr &rtp, int) { fwrite((uint8_t *) &size, 2, 1, _save_file_rtp.get()); fwrite((uint8_t *) rtp->data() + 4, rtp->size() - 4, 1, _save_file_rtp.get()); } - decodeRtp(rtp->data() + 4 ,rtp->size() - 4); + _rtp_decoder->inputRtp(rtp); } -void RtpProcess::onRtpDecode(const uint8_t *packet, int bytes, uint32_t timestamp, int flags) { +const char *RtpProcess::onSearchPacketTail(const char *packet,int bytes){ + try { + auto ret = _decoder->input((uint8_t *) packet, bytes); + if (ret > 0) { + return packet + ret; + } + return nullptr; + } catch (std::exception &ex) { + InfoL << "解析ps或ts异常: bytes=" << bytes + << " ,exception=" << ex.what() + << " ,hex=" << hexdump((uint8_t *) packet, bytes); + return nullptr; + } +} + +void RtpProcess::onRtpDecode(const uint8_t *packet, int bytes, uint32_t timestamp) { if(_save_file_ps){ fwrite((uint8_t *)packet,bytes, 1, _save_file_ps.get()); } @@ -143,10 +162,7 @@ void RtpProcess::onRtpDecode(const uint8_t *packet, int bytes, uint32_t timestam } if (_decoder) { - auto ret = _decoder->input((uint8_t *) packet, bytes); - if (ret != bytes) { - WarnP(this) << ret << " != " << bytes << " " << flags; - } + HttpRequestSplitter::input((char *) packet, bytes); } } @@ -227,7 +243,7 @@ void RtpProcess::setListener(const std::weak_ptr &listener){ void RtpProcess::emitOnPublish() { weak_ptr weak_self = shared_from_this(); - Broadcast::PublishAuthInvoker invoker = [weak_self](const string &err, bool enableRtxp, bool enableHls, bool enableMP4) { + Broadcast::PublishAuthInvoker invoker = [weak_self](const string &err, bool enableHls, bool enableMP4) { auto strongSelf = weak_self.lock(); if (!strongSelf) { return; @@ -236,7 +252,7 @@ void RtpProcess::emitOnPublish() { strongSelf->_muxer = std::make_shared(strongSelf->_media_info._vhost, strongSelf->_media_info._app, strongSelf->_media_info._streamid, 0, - enableRtxp, enableRtxp, enableHls, enableMP4); + true, true, enableHls, enableMP4); strongSelf->_muxer->setMediaListener(strongSelf->_listener); InfoP(strongSelf) << "允许RTP推流"; } else { @@ -248,10 +264,9 @@ void RtpProcess::emitOnPublish() { auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPublish, _media_info, invoker, static_cast(*this)); if(!flag){ //该事件无人监听,默认不鉴权 - GET_CONFIG(bool, toRtxp, General::kPublishToRtxp); GET_CONFIG(bool, toHls, General::kPublishToHls); GET_CONFIG(bool, toMP4, General::kPublishToMP4); - invoker("", toRtxp, toHls, toMP4); + invoker("", toHls, toMP4); } } diff --git a/src/Rtp/RtpProcess.h b/src/Rtp/RtpProcess.h index d2d9c5ee..b9530021 100644 --- a/src/Rtp/RtpProcess.h +++ b/src/Rtp/RtpProcess.h @@ -14,15 +14,16 @@ #if defined(ENABLE_RTPPROXY) #include "Rtsp/RtpReceiver.h" -#include "RtpDecoder.h" #include "Decoder.h" #include "Common/Device.h" #include "Common/Stamp.h" +#include "Http/HttpRequestSplitter.h" +#include "Extension/CommonRtp.h" using namespace mediakit; namespace mediakit{ -class RtpProcess : public RtpReceiver , public RtpDecoder, public SockInfo, public MediaSinkInterface, public std::enable_shared_from_this{ +class RtpProcess : public HttpRequestSplitter, public RtpReceiver, public SockInfo, public MediaSinkInterface, public std::enable_shared_from_this{ public: typedef std::shared_ptr Ptr; RtpProcess(const string &stream_id); @@ -66,15 +67,19 @@ public: protected: void onRtpSorted(const RtpPacket::Ptr &rtp, int track_index) override ; - void onRtpDecode(const uint8_t *packet, int bytes, uint32_t timestamp, int flags) override; void inputFrame(const Frame::Ptr &frame) override; void addTrack(const Track::Ptr & track) override; void resetTracks() override {}; -private: - void emitOnPublish(); + const char *onSearchPacketTail(const char *data,int len) override; + int64_t onRecvHeader(const char *data,uint64_t len) override { return 0; }; private: + void emitOnPublish(); + void onRtpDecode(const uint8_t *packet, int bytes, uint32_t timestamp); + +private: + std::shared_ptr _rtp_decoder; std::shared_ptr _save_file_rtp; std::shared_ptr _save_file_ps; std::shared_ptr _save_file_video; diff --git a/src/Rtp/RtpSelector.cpp b/src/Rtp/RtpSelector.cpp index 8f74e8ce..283dd98f 100644 --- a/src/Rtp/RtpSelector.cpp +++ b/src/Rtp/RtpSelector.cpp @@ -72,31 +72,40 @@ void RtpSelector::createTimer() { } void RtpSelector::delProcess(const string &stream_id,const RtpProcess *ptr) { - lock_guard lck(_mtx_map); - auto it = _map_rtp_process.find(stream_id); - if (it == _map_rtp_process.end()) { - return; + RtpProcess::Ptr process; + { + lock_guard lck(_mtx_map); + auto it = _map_rtp_process.find(stream_id); + if (it == _map_rtp_process.end()) { + return; + } + if (it->second->getProcess().get() != ptr) { + return; + } + process = it->second->getProcess(); + _map_rtp_process.erase(it); } - if (it->second->getProcess().get() != ptr) { - return; - } - auto process = it->second->getProcess(); - _map_rtp_process.erase(it); process->onDetach(); } void RtpSelector::onManager() { - lock_guard lck(_mtx_map); - for (auto it = _map_rtp_process.begin(); it != _map_rtp_process.end();) { - if (it->second->getProcess()->alive()) { - ++it; - continue; + List clear_list; + { + lock_guard lck(_mtx_map); + for (auto it = _map_rtp_process.begin(); it != _map_rtp_process.end();) { + if (it->second->getProcess()->alive()) { + ++it; + continue; + } + WarnL << "RtpProcess timeout:" << it->first; + clear_list.emplace_back(it->second->getProcess()); + it = _map_rtp_process.erase(it); } - WarnL << "RtpProcess timeout:" << it->first; - auto process = it->second->getProcess(); - it = _map_rtp_process.erase(it); - process->onDetach(); } + + clear_list.for_each([](const RtpProcess::Ptr &process) { + process->onDetach(); + }); } RtpSelector::RtpSelector() { diff --git a/src/Rtp/RtpServer.cpp b/src/Rtp/RtpServer.cpp index f7a4c9eb..5b92226b 100644 --- a/src/Rtp/RtpServer.cpp +++ b/src/Rtp/RtpServer.cpp @@ -24,7 +24,7 @@ RtpServer::~RtpServer() { void RtpServer::start(uint16_t local_port, const string &stream_id, bool enable_tcp, const char *local_ip) { //创建udp服务器 - Socket::Ptr udp_server = std::make_shared(nullptr, false); + Socket::Ptr udp_server = Socket::createSocket(nullptr, false); if (!udp_server->bindUdpSock(local_port, local_ip)) { throw std::runtime_error(StrPrinter << "bindUdpSock on " << local_ip << ":" << local_port << " failed:" << get_uv_errmsg(true)); } @@ -33,14 +33,10 @@ void RtpServer::start(uint16_t local_port, const string &stream_id, bool enable TcpServer::Ptr tcp_server; if (enable_tcp) { - try { - //创建tcp服务器 - tcp_server = std::make_shared(udp_server->getPoller()); - (*tcp_server)[RtpSession::kStreamID] = stream_id; - tcp_server->start(udp_server->get_local_port(), local_ip); - } catch (...) { - throw; - } + //创建tcp服务器 + tcp_server = std::make_shared(udp_server->getPoller()); + (*tcp_server)[RtpSession::kStreamID] = stream_id; + tcp_server->start(udp_server->get_local_port(), local_ip); } RtpProcess::Ptr process; diff --git a/src/Rtp/RtpSession.cpp b/src/Rtp/RtpSession.cpp index 2e3f63fe..72037b5e 100644 --- a/src/Rtp/RtpSession.cpp +++ b/src/Rtp/RtpSession.cpp @@ -70,7 +70,7 @@ void RtpSession::onRtpPacket(const char *data, uint64_t len) { _process = RtpSelector::Instance().getProcess(_stream_id, true); _process->setListener(dynamic_pointer_cast(shared_from_this())); } - _process->inputRtp(_sock, data + 2, len - 2, &addr); + _process->inputRtp(getSock(), data + 2, len - 2, &addr); _ticker.resetTime(); } diff --git a/src/Rtp/RtpSplitter.cpp b/src/Rtp/RtpSplitter.cpp index 01414f20..7c90b3dc 100644 --- a/src/Rtp/RtpSplitter.cpp +++ b/src/Rtp/RtpSplitter.cpp @@ -12,20 +12,27 @@ #include "RtpSplitter.h" namespace mediakit{ -RtpSplitter::RtpSplitter() { -} +RtpSplitter::RtpSplitter() {} -RtpSplitter::~RtpSplitter() { -} +RtpSplitter::~RtpSplitter() {} const char *RtpSplitter::onSearchPacketTail(const char *data, int len) { + if (data[0] == '$') { + //可能是4个字节的rtp头 + return onSearchPacketTail_l(data + 2, len - 2); + } + //两个字节的rtp头 + return onSearchPacketTail_l(data, len); +} + +const char *RtpSplitter::onSearchPacketTail_l(const char *data, int len) { //这是rtp包 - if(len < 2){ + if (len < 2) { //数据不够 return nullptr; } - uint16_t length = (((uint8_t *)data)[0] << 8) | ((uint8_t *)data)[1]; - if(len < length + 2){ + uint16_t length = (((uint8_t *) data)[0] << 8) | ((uint8_t *) data)[1]; + if (len < length + 2) { //数据不够 return nullptr; } diff --git a/src/Rtp/RtpSplitter.h b/src/Rtp/RtpSplitter.h index 45776b01..7e8d61de 100644 --- a/src/Rtp/RtpSplitter.h +++ b/src/Rtp/RtpSplitter.h @@ -20,15 +20,16 @@ class RtpSplitter : public HttpRequestSplitter{ public: RtpSplitter(); virtual ~RtpSplitter(); + protected: /** * 收到rtp包回调 - * @param data - * @param len */ virtual void onRtpPacket(const char *data,uint64_t len) = 0; + protected: const char *onSearchPacketTail(const char *data,int len) override ; + const char *onSearchPacketTail_l(const char *data,int len); int64_t onRecvHeader(const char *data,uint64_t len) override; }; diff --git a/src/Rtsp/RtpCodec.h b/src/Rtsp/RtpCodec.h index 16a6f88a..ef4bed35 100644 --- a/src/Rtsp/RtpCodec.h +++ b/src/Rtsp/RtpCodec.h @@ -106,6 +106,7 @@ public: return _ui32MtuSize; } RtpPacket::Ptr makeRtp(TrackType type,const void *pData, unsigned int uiLen, bool bMark, uint32_t uiStamp); + protected: uint32_t _ui32Ssrc; uint32_t _ui32SampleRate; diff --git a/src/Rtsp/RtpMultiCaster.cpp b/src/Rtsp/RtpMultiCaster.cpp index cf3a12b0..27226540 100644 --- a/src/Rtsp/RtpMultiCaster.cpp +++ b/src/Rtsp/RtpMultiCaster.cpp @@ -25,154 +25,177 @@ MultiCastAddressMaker &MultiCastAddressMaker::Instance() { return instance; } -static uint32_t addressToInt(const string &ip){ - struct in_addr addr; - bzero(&addr,sizeof(addr)); - addr.s_addr = inet_addr(ip.data()); - return (uint32_t)ntohl((uint32_t &)addr.s_addr); +bool MultiCastAddressMaker::isMultiCastAddress(uint32_t addr) { + static uint32_t addrMin = mINI::Instance()[MultiCast::kAddrMin].as(); + static uint32_t addrMax = mINI::Instance()[MultiCast::kAddrMax].as(); + return addr >= addrMin && addr <= addrMax; } -std::shared_ptr MultiCastAddressMaker::obtain(uint32_t iTry) { +string MultiCastAddressMaker::toString(uint32_t addr) { + addr = htonl(addr); + return SockUtil::inet_ntoa((struct in_addr &) (addr)); +} + +static uint32_t addressToInt(const string &ip){ + struct in_addr addr; + bzero(&addr, sizeof(addr)); + addr.s_addr = inet_addr(ip.data()); + return (uint32_t) ntohl((uint32_t &) addr.s_addr); +} + +std::shared_ptr MultiCastAddressMaker::obtain(uint32_t max_try) { lock_guard lck(_mtx); - GET_CONFIG(string,addrMinStr,MultiCast::kAddrMin); - GET_CONFIG(string,addrMaxStr,MultiCast::kAddrMax); + GET_CONFIG(string, addrMinStr, MultiCast::kAddrMin); + GET_CONFIG(string, addrMaxStr, MultiCast::kAddrMax); uint32_t addrMin = addressToInt(addrMinStr); uint32_t addrMax = addressToInt(addrMaxStr); - if(_iAddr > addrMax || _iAddr == 0){ - _iAddr = addrMin; + if (_addr > addrMax || _addr == 0) { + _addr = addrMin; } - auto iGotAddr = _iAddr++; - if(_setBadAddr.find(iGotAddr) != _setBadAddr.end()){ + auto iGotAddr = _addr++; + if (_used_addr.find(iGotAddr) != _used_addr.end()) { //已经分配过了 - if(iTry){ - return obtain(--iTry); + if (max_try) { + return obtain(--max_try); } //分配完了,应该不可能到这里 ErrorL; return nullptr; } - _setBadAddr.emplace(iGotAddr); - std::shared_ptr ret(new uint32_t(iGotAddr),[](uint32_t *ptr){ + _used_addr.emplace(iGotAddr); + std::shared_ptr ret(new uint32_t(iGotAddr), [](uint32_t *ptr) { MultiCastAddressMaker::Instance().release(*ptr); delete ptr; }); return ret; } -void MultiCastAddressMaker::release(uint32_t iAddr){ + +void MultiCastAddressMaker::release(uint32_t addr){ lock_guard lck(_mtx); - _setBadAddr.erase(iAddr); + _used_addr.erase(addr); } +//////////////////////////////////////////////////////////////////////////////////// -recursive_mutex RtpMultiCaster::g_mtx; -unordered_map > RtpMultiCaster::g_mapBroadCaster; +recursive_mutex g_mtx; +unordered_map > g_multi_caster_map; void RtpMultiCaster::setDetachCB(void* listener, const onDetach& cb) { lock_guard lck(_mtx); - if(cb){ - _mapDetach.emplace(listener,cb); - }else{ - _mapDetach.erase(listener); + if (cb) { + _detach_map.emplace(listener, cb); + } else { + _detach_map.erase(listener); } } + RtpMultiCaster::~RtpMultiCaster() { - _pReader->setReadCB(nullptr); - _pReader->setDetachCB(nullptr); + _rtp_reader->setReadCB(nullptr); + _rtp_reader->setDetachCB(nullptr); DebugL; } -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; - throw std::runtime_error(strErr); +RtpMultiCaster::RtpMultiCaster(SocketHelper &helper, const string &local_ip, const string &vhost, const string &app, const string &stream) { + auto src = dynamic_pointer_cast(MediaSource::find(RTSP_SCHEMA, vhost, app, stream)); + if (!src) { + auto err = StrPrinter << "未找到媒体源:" << vhost << " " << app << " " << stream << endl; + throw std::runtime_error(err); + } + _multicast_ip = MultiCastAddressMaker::Instance().obtain(); + if (!_multicast_ip) { + throw std::runtime_error("获取组播地址失败"); } - _multiAddr = MultiCastAddressMaker::Instance().obtain(); - for(auto i = 0; i < 2; i++){ - _apUdpSock[i].reset(new Socket(poller)); - if(!_apUdpSock[i]->bindUdpSock(0, strLocalIp.data())){ - auto strErr = StrPrinter << "绑定UDP端口失败:" << strLocalIp << endl; - throw std::runtime_error(strErr); - } - auto fd = _apUdpSock[i]->rawFD(); - GET_CONFIG(uint32_t,udpTTL,MultiCast::kUdpTTL); + for (auto i = 0; i < 2; ++i) { + //创建udp socket, 数组下标为TrackType + _udp_sock[i] = helper.createSocket(); + if (!_udp_sock[i]->bindUdpSock(0, local_ip.data())) { + auto err = StrPrinter << "绑定UDP端口失败:" << local_ip << endl; + throw std::runtime_error(err); + } + auto fd = _udp_sock[i]->rawFD(); + GET_CONFIG(uint32_t, udpTTL, MultiCast::kUdpTTL); SockUtil::setMultiTTL(fd, udpTTL); SockUtil::setMultiLOOP(fd, false); - SockUtil::setMultiIF(fd, strLocalIp.data()); + SockUtil::setMultiIF(fd, local_ip.data()); - struct sockaddr_in &peerAddr = _aPeerUdpAddr[i]; - peerAddr.sin_family = AF_INET; - peerAddr.sin_port = htons(_apUdpSock[i]->get_local_port()); - peerAddr.sin_addr.s_addr = htonl(*_multiAddr); - bzero(&(peerAddr.sin_zero), sizeof peerAddr.sin_zero); - _apUdpSock[i]->setSendPeerAddr((struct sockaddr *)&peerAddr); + struct sockaddr_in peer; + peer.sin_family = AF_INET; + //组播目标端口为本地发送端口 + peer.sin_port = htons(_udp_sock[i]->get_local_port()); + //组播目标地址 + peer.sin_addr.s_addr = htonl(*_multicast_ip); + bzero(&(peer.sin_zero), sizeof peer.sin_zero); + _udp_sock[i]->setSendPeerAddr((struct sockaddr *) &peer); } - _pReader = src->getRing()->attach(poller); - _pReader->setReadCB([this](const RtspMediaSource::RingDataType &pkt){ + + _rtp_reader = src->getRing()->attach(helper.getPoller()); + _rtp_reader->setReadCB([this](const RtspMediaSource::RingDataType &pkt) { int i = 0; int size = pkt->size(); pkt->for_each([&](const RtpPacket::Ptr &rtp) { - int i = (int) (rtp->type); - auto &pSock = _apUdpSock[i]; - auto &peerAddr = _aPeerUdpAddr[i]; - BufferRtp::Ptr buffer(new BufferRtp(rtp, 4)); - pSock->send(buffer, nullptr, 0, ++i == size); + auto &sock = _udp_sock[rtp->type]; + sock->send(std::make_shared(rtp, 4), nullptr, 0, ++i == size); }); }); - _pReader->setDetachCB([this](){ - unordered_map _mapDetach_copy; + _rtp_reader->setDetachCB([this]() { + unordered_map _detach_map_copy; { lock_guard lck(_mtx); - _mapDetach_copy = std::move(_mapDetach); + _detach_map_copy = std::move(_detach_map); } - for(auto &pr : _mapDetach_copy){ + for (auto &pr : _detach_map_copy) { pr.second(); } }); - DebugL << MultiCastAddressMaker::toString(*_multiAddr) << " " - << _apUdpSock[0]->get_local_port() << " " - << _apUdpSock[1]->get_local_port() << " " - << strVhost << " " - << strApp << " " << strStream; -} -uint16_t RtpMultiCaster::getPort(TrackType trackType){ - return _apUdpSock[trackType]->get_local_port(); -} -string RtpMultiCaster::getIP(){ - return SockUtil::inet_ntoa(_aPeerUdpAddr[0].sin_addr); -} -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 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; - g_mapBroadCaster.emplace(strKey,weakPtr); - return ret; - }catch (std::exception &ex) { - WarnL << ex.what(); - return nullptr; - } + + DebugL << MultiCastAddressMaker::toString(*_multicast_ip) << " " + << _udp_sock[0]->get_local_port() << " " + << _udp_sock[1]->get_local_port() << " " + << vhost << " " << app << " " << stream; } -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; +uint16_t RtpMultiCaster::getMultiCasterPort(TrackType trackType) { + return _udp_sock[trackType]->get_local_port(); +} + +string RtpMultiCaster::getMultiCasterIP() { + struct in_addr addr; + addr.s_addr = htonl(*_multicast_ip); + return SockUtil::inet_ntoa(addr); +} + +RtpMultiCaster::Ptr RtpMultiCaster::get(SocketHelper &helper, const string &local_ip, const string &vhost, const string &app, const string &stream) { + static auto on_create = [](SocketHelper &helper, const string &local_ip, const string &vhost, const string &app, const string &stream){ + try { + auto poller = helper.getPoller(); + auto ret = RtpMultiCaster::Ptr(new RtpMultiCaster(helper, local_ip, vhost, app, stream), [poller](RtpMultiCaster *ptr) { + poller->async([ptr]() { + delete ptr; + }); + }); + lock_guard lck(g_mtx); + string strKey = StrPrinter << local_ip << " " << vhost << " " << app << " " << stream << endl; + g_multi_caster_map.emplace(strKey, ret); + return ret; + } catch (std::exception &ex) { + WarnL << ex.what(); + return RtpMultiCaster::Ptr(); + } + }; + + string strKey = StrPrinter << local_ip << " " << vhost << " " << app << " " << stream << endl; lock_guard lck(g_mtx); - auto it = g_mapBroadCaster.find(strKey); - if (it == g_mapBroadCaster.end()) { - return make(poller,strLocalIp,strVhost,strApp, strStream); + auto it = g_multi_caster_map.find(strKey); + if (it == g_multi_caster_map.end()) { + return on_create(helper, local_ip, vhost, app, stream); } auto ret = it->second.lock(); if (!ret) { - g_mapBroadCaster.erase(it); - return make(poller,strLocalIp,strVhost,strApp, strStream); + g_multi_caster_map.erase(it); + return on_create(helper, local_ip, vhost, app, stream); } return ret; } diff --git a/src/Rtsp/RtpMultiCaster.h b/src/Rtsp/RtpMultiCaster.h index d3893ad7..8f8e76a6 100644 --- a/src/Rtsp/RtpMultiCaster.h +++ b/src/Rtsp/RtpMultiCaster.h @@ -11,7 +11,6 @@ #ifndef SRC_RTSP_RTPBROADCASTER_H_ #define SRC_RTSP_RTPBROADCASTER_H_ - #include #include #include @@ -20,60 +19,52 @@ #include "RtspMediaSource.h" #include "Util/mini.h" #include "Network/Socket.h" - using namespace std; using namespace toolkit; namespace mediakit{ -class MultiCastAddressMaker -{ +class MultiCastAddressMaker { public: - static MultiCastAddressMaker &Instance(); + ~MultiCastAddressMaker() {} + static MultiCastAddressMaker& Instance(); + static bool isMultiCastAddress(uint32_t addr); + static string toString(uint32_t addr); + + std::shared_ptr obtain(uint32_t max_try = 10); - static bool isMultiCastAddress(uint32_t iAddr){ - static uint32_t addrMin = mINI::Instance()[MultiCast::kAddrMin].as(); - static uint32_t addrMax = mINI::Instance()[MultiCast::kAddrMax].as(); - return iAddr >= addrMin && iAddr <= addrMax; - } - static string toString(uint32_t iAddr){ - iAddr = htonl(iAddr); - return SockUtil::inet_ntoa((struct in_addr &)(iAddr)); - } - virtual ~MultiCastAddressMaker(){} - std::shared_ptr obtain(uint32_t iTry = 10); private: - MultiCastAddressMaker(){}; - void release(uint32_t iAddr); - uint32_t _iAddr = 0; + MultiCastAddressMaker() {}; + void release(uint32_t addr); + +private: + uint32_t _addr = 0; recursive_mutex _mtx; - unordered_set _setBadAddr; + unordered_set _used_addr; }; + class RtpMultiCaster { public: typedef std::shared_ptr Ptr; typedef function onDetach; - virtual ~RtpMultiCaster(); - static Ptr get(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream); + ~RtpMultiCaster(); + + static Ptr get(SocketHelper &helper, const string &local_ip, const string &vhost, const string &app, const string &stream); void setDetachCB(void *listener,const onDetach &cb); - uint16_t getPort(TrackType trackType); - string getIP(); + + string getMultiCasterIP(); + uint16_t getMultiCasterPort(TrackType trackType); + private: - static recursive_mutex g_mtx; - static unordered_map > g_mapBroadCaster; - static Ptr make(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream); + RtpMultiCaster(SocketHelper &helper, const string &local_ip, const string &vhost, const string &app, const string &stream); - std::shared_ptr _multiAddr; +private: recursive_mutex _mtx; - unordered_map _mapDetach; - RtspMediaSource::RingType::RingReader::Ptr _pReader; - Socket::Ptr _apUdpSock[2]; - struct sockaddr_in _aPeerUdpAddr[2]; - - RtpMultiCaster(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream); - + Socket::Ptr _udp_sock[2]; + std::shared_ptr _multicast_ip; + unordered_map _detach_map; + RtspMediaSource::RingType::RingReader::Ptr _rtp_reader; }; }//namespace mediakit - #endif /* SRC_RTSP_RTPBROADCASTER_H_ */ diff --git a/src/Rtsp/Rtsp.cpp b/src/Rtsp/Rtsp.cpp index c93331d6..f63bd30a 100644 --- a/src/Rtsp/Rtsp.cpp +++ b/src/Rtsp/Rtsp.cpp @@ -286,7 +286,7 @@ vector SdpParser::getAvailableTrack() const { continue; } } - return std::move(ret); + return ret; } string SdpParser::toString() const { @@ -365,8 +365,10 @@ bool RtspUrl::setup(bool isSSL, const string &strUrl, const string &strUser, con return true; } -std::pair makeSockPair_l(const EventPoller::Ptr &poller, const string &local_ip){ - auto pSockRtp = std::make_shared(poller); +static void makeSockPair_l(std::pair &pair, const string &local_ip){ + auto &pSockRtp = pair.first; + auto &pSockRtcp = pair.second; + if (!pSockRtp->bindUdpSock(0, local_ip.data())) { //分配端口失败 throw runtime_error("open udp socket failed"); @@ -374,7 +376,6 @@ std::pair makeSockPair_l(const EventPoller::Ptr &polle //是否是偶数 bool even_numbers = pSockRtp->get_local_port() % 2 == 0; - auto pSockRtcp = std::make_shared(poller); if (!pSockRtcp->bindUdpSock(pSockRtp->get_local_port() + (even_numbers ? 1 : -1), local_ip.data())) { //分配端口失败 throw runtime_error("open udp socket failed"); @@ -386,22 +387,21 @@ std::pair makeSockPair_l(const EventPoller::Ptr &polle pSockRtp = pSockRtcp; pSockRtcp = tmp; } - - return std::make_pair(pSockRtp, pSockRtcp); } -std::pair makeSockPair(const EventPoller::Ptr &poller, const string &local_ip){ - int try_count = 0; - while (true) { - try { - return makeSockPair_l(poller, local_ip); - } catch (...) { - if (++try_count == 3) { - throw; - } - WarnL << "open udp socket failed, retry: " << try_count; - } - } + void makeSockPair(std::pair &pair, const string &local_ip){ + int try_count = 0; + while (true) { + try { + makeSockPair_l(pair, local_ip); + break; + } catch (...) { + if (++try_count == 3) { + throw; + } + WarnL << "open udp socket failed, retry: " << try_count; + } + } } string printSSRC(uint32_t ui32Ssrc) { diff --git a/src/Rtsp/Rtsp.h b/src/Rtsp/Rtsp.h index ff31295c..eb27510b 100644 --- a/src/Rtsp/Rtsp.h +++ b/src/Rtsp/Rtsp.h @@ -271,7 +271,7 @@ private: _StrPrinter _printer; }; -std::pair makeSockPair(const EventPoller::Ptr &poller, const string &local_ip); +void makeSockPair(std::pair &pair, const string &local_ip); string printSSRC(uint32_t ui32Ssrc); } //namespace mediakit diff --git a/src/Rtsp/RtspMediaSource.h b/src/Rtsp/RtspMediaSource.h index 4e2657e0..a67a4b34 100644 --- a/src/Rtsp/RtspMediaSource.h +++ b/src/Rtsp/RtspMediaSource.h @@ -56,7 +56,7 @@ public: int ring_size = RTP_GOP_SIZE) : MediaSource(RTSP_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {} - virtual ~RtspMediaSource() {} + ~RtspMediaSource() override{} /** * 获取媒体源的环形缓冲 @@ -83,7 +83,8 @@ public: * 获取相应轨道的ssrc */ virtual uint32_t getSsrc(TrackType trackType) { - auto track = _sdp_parser.getTrack(trackType); + assert(trackType >= 0 && trackType < TrackMax); + auto track = _tracks[trackType]; if (!track) { return 0; } @@ -94,7 +95,8 @@ public: * 获取相应轨道的seqence */ virtual uint16_t getSeqence(TrackType trackType) { - auto track = _sdp_parser.getTrack(trackType); + assert(trackType >= 0 && trackType < TrackMax); + auto track = _tracks[trackType]; if (!track) { return 0; } @@ -105,28 +107,33 @@ public: * 获取相应轨道的时间戳,单位毫秒 */ uint32_t getTimeStamp(TrackType trackType) override { - auto track = _sdp_parser.getTrack(trackType); - if (track) { - return track->_time_stamp; + assert(trackType >= TrackInvalid && trackType < TrackMax); + if (trackType != TrackInvalid) { + //获取某track的时间戳 + auto track = _tracks[trackType]; + if (track) { + return track->_time_stamp; + } } - auto tracks = _sdp_parser.getAvailableTrack(); - switch (tracks.size()) { - case 0: - return 0; - case 1: - return tracks[0]->_time_stamp; - default: - return MIN(tracks[0]->_time_stamp, tracks[1]->_time_stamp); + + //获取所有track的最小时间戳 + uint32_t ret = UINT32_MAX; + for (auto &track : _tracks) { + if (track && track->_time_stamp < ret) { + ret = track->_time_stamp; + } } + return ret; } /** * 更新时间戳 */ - void setTimeStamp(uint32_t uiStamp) override { - auto tracks = _sdp_parser.getAvailableTrack(); - for (auto &track : tracks) { - track->_time_stamp = uiStamp; + void setTimeStamp(uint32_t stamp) override { + for (auto &track : _tracks) { + if (track) { + track->_time_stamp = stamp; + } } } @@ -135,8 +142,10 @@ public: */ virtual void setSdp(const string &sdp) { _sdp = sdp; - _sdp_parser.load(sdp); - _have_video = (bool)_sdp_parser.getTrack(TrackVideo); + SdpParser sdp_parser(sdp); + _tracks[TrackVideo] = sdp_parser.getTrack(TrackVideo); + _tracks[TrackAudio] = sdp_parser.getTrack(TrackAudio); + _have_video = (bool) _tracks[TrackVideo]; if (_ring) { regist(); } @@ -148,7 +157,8 @@ public: * @param keyPos 该包是否为关键帧的第一个包 */ void onWrite(const RtpPacket::Ptr &rtp, bool keyPos) override { - auto track = _sdp_parser.getTrack(rtp->type); + assert(rtp->type >= 0 && rtp->type < TrackMax); + auto track = _tracks[rtp->type]; if (track) { track->_seq = rtp->sequence; track->_time_stamp = rtp->timeStamp; @@ -156,16 +166,15 @@ public: } if (!_ring) { weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - auto lam = [weakSelf](const EventPoller::Ptr &, int size, bool) { + auto lam = [weakSelf](int size) { auto strongSelf = weakSelf.lock(); if (!strongSelf) { return; } strongSelf->onReaderChanged(size); }; - //rtp包缓存最大允许2048个,大概最多3MB数据 - //但是这个是GOP缓存的上限值,真实的GOP缓存大小等于两个I帧之间的包数的两倍 - //而且每次遇到I帧,则会清空GOP缓存,所以真实的GOP缓存远小于此值 + //GOP默认缓冲512组RTP包,每组RTP包时间戳相同(如果开启合并写了,那么每组为合并写时间内的RTP包), + //每次遇到关键帧第一个RTP包,则会清空GOP缓存(因为有新的关键帧了,同样可以实现秒开) _ring = std::make_shared(_ring_size, std::move(lam)); onReaderChanged(0); if (!_sdp.empty()) { @@ -175,8 +184,12 @@ public: PacketCache::inputPacket(rtp->type == TrackVideo, rtp, keyPos); } -private: + void clearCache() override{ + PacketCache::clearCache(); + _ring->clearCache(); + } +private: /** * 批量flush rtp包时触发该函数 * @param rtp_list rtp包列表 @@ -187,20 +200,12 @@ private: _ring->write(rtp_list, _have_video ? key_pos : true); } - /** - * 每次增减消费者都会触发该函数 - */ - void onReaderChanged(int size) { - if (size == 0) { - onNoneReader(); - } - } private: - int _ring_size; bool _have_video = false; - SdpParser _sdp_parser; + int _ring_size; string _sdp; RingType::Ptr _ring; + SdpTrack::Ptr _tracks[TrackMax]; }; } /* namespace mediakit */ diff --git a/src/Rtsp/RtspMediaSourceImp.h b/src/Rtsp/RtspMediaSourceImp.h index 91a97992..13c4fea4 100644 --- a/src/Rtsp/RtspMediaSourceImp.h +++ b/src/Rtsp/RtspMediaSourceImp.h @@ -48,28 +48,17 @@ public: * 输入rtp并解析 */ void onWrite(const RtpPacket::Ptr &rtp, bool key_pos) override { - if(_all_track_ready && !_muxer->isEnabled()){ + if (_all_track_ready && !_muxer->isEnabled()) { //获取到所有Track后,并且未开启转协议,那么不需要解复用rtp //在关闭rtp解复用后,无法知道是否为关键帧,这样会导致无法秒开,或者开播花屏 key_pos = rtp->type == TrackVideo; - }else{ + } else { //需要解复用rtp key_pos = _demuxer->inputRtp(rtp); } RtspMediaSource::onWrite(rtp, key_pos); } - /** - * 设置监听器 - * @param listener - */ - void setListener(const std::weak_ptr &listener) override { - RtspMediaSource::setListener(listener); - if(_muxer){ - _muxer->setMediaListener(listener); - } - } - /** * 获取观看总人数,包括(hls/rtsp/rtmp) */ @@ -77,44 +66,19 @@ public: return readerCount() + (_muxer ? _muxer->totalReaderCount() : 0); } - /** - * 设置录制状态 - * @param type 录制类型 - * @param start 开始或停止 - * @param custom_path 开启录制时,指定自定义路径 - * @return 是否设置成功 - */ - bool setupRecord(Recorder::type type, bool start, const string &custom_path) override{ - if(_muxer){ - return _muxer->setupRecord(*this,type, start, custom_path); - } - return RtspMediaSource::setupRecord(type, start, custom_path); - } - - /** - * 获取录制状态 - * @param type 录制类型 - * @return 录制状态 - */ - bool isRecording(Recorder::type type) override{ - if(_muxer){ - return _muxer->isRecording(*this,type); - } - return RtspMediaSource::isRecording(type); - } - - /** * 设置协议转换 - * @param enableRtmp 是否转换成rtmp * @param enableHls 是否转换成hls * @param enableMP4 是否mp4录制 */ - void setProtocolTranslation(bool enableRtmp,bool enableHls,bool enableMP4){ + void setProtocolTranslation(bool enableHls,bool enableMP4){ //不重复生成rtsp - _muxer = std::make_shared(getVhost(), getApp(), getId(), _demuxer->getDuration(), false, enableRtmp, enableHls, enableMP4); + _muxer = std::make_shared(getVhost(), getApp(), getId(), _demuxer->getDuration(), false, true, enableHls, enableMP4); _muxer->setMediaListener(getListener()); - _muxer->setTrackListener(this); + _muxer->setTrackListener(static_pointer_cast(shared_from_this())); + //让_muxer对象拦截一部分事件(比如说录像相关事件) + setListener(_muxer); + for(auto &track : _demuxer->getTracks(false)){ _muxer->addTrack(track); track->addDelegate(_muxer); @@ -135,9 +99,9 @@ public: * _muxer触发的所有Track就绪的事件 */ void onAllTrackReady() override{ - setTrackSource(_muxer); _all_track_ready = true; } + private: RtspDemuxer::Ptr _demuxer; MultiMediaSourceMuxer::Ptr _muxer; diff --git a/src/Rtsp/RtspMediaSourceMuxer.h b/src/Rtsp/RtspMediaSourceMuxer.h index 4d2683ab..10bd920c 100644 --- a/src/Rtsp/RtspMediaSourceMuxer.h +++ b/src/Rtsp/RtspMediaSourceMuxer.h @@ -16,7 +16,8 @@ namespace mediakit { -class RtspMediaSourceMuxer : public RtspMuxer { +class RtspMediaSourceMuxer : public RtspMuxer, public MediaSourceEventInterceptor, + public std::enable_shared_from_this { public: typedef std::shared_ptr Ptr; @@ -24,33 +25,56 @@ public: const string &strApp, const string &strId, const TitleSdp::Ptr &title = nullptr) : RtspMuxer(title){ - _mediaSouce = std::make_shared(vhost,strApp,strId); - getRtpRing()->setDelegate(_mediaSouce); + _media_src = std::make_shared(vhost,strApp,strId); + getRtpRing()->setDelegate(_media_src); } - virtual ~RtspMediaSourceMuxer(){} + + ~RtspMediaSourceMuxer() override{} void setListener(const std::weak_ptr &listener){ - _mediaSouce->setListener(listener); + _listener = listener; + _media_src->setListener(shared_from_this()); } int readerCount() const{ - return _mediaSouce->readerCount(); + return _media_src->readerCount(); } void setTimeStamp(uint32_t stamp){ - _mediaSouce->setTimeStamp(stamp); + _media_src->setTimeStamp(stamp); } void onAllTrackReady(){ - _mediaSouce->setSdp(getSdp()); + _media_src->setSdp(getSdp()); } - // 设置TrackSource - void setTrackSource(const std::weak_ptr &track_src){ - _mediaSouce->setTrackSource(track_src); + void onReaderChanged(MediaSource &sender, int size) override { + _enabled = size; + if (!size) { + _clear_cache = true; + } + MediaSourceEventInterceptor::onReaderChanged(sender, size); } + + void inputFrame(const Frame::Ptr &frame) override { + if (_clear_cache) { + _clear_cache = false; + _media_src->clearCache(); + } + if (_enabled) { + RtspMuxer::inputFrame(frame); + } + } + + bool isEnabled() { + //缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 + return _clear_cache ? true : _enabled; + } + private: - RtspMediaSource::Ptr _mediaSouce; + bool _enabled = true; + bool _clear_cache = false; + RtspMediaSource::Ptr _media_src; }; diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index a2ce2b24..dc899a5d 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -107,13 +107,13 @@ void RtspPlayer::onConnect(const SockException &err){ sendOptions(); } -void RtspPlayer::onRecv(const Buffer::Ptr& pBuf) { +void RtspPlayer::onRecv(const Buffer::Ptr& buf) { if(_benchmark_mode && !_play_check_timer){ //在性能测试模式下,如果rtsp握手完毕后,不再解析rtp包 _rtp_recv_ticker.resetTime(); return; } - input(pBuf->data(),pBuf->size()); + input(buf->data(), buf->size()); } void RtspPlayer::onErr(const SockException &ex) { @@ -211,16 +211,17 @@ void RtspPlayer::createUdpSockIfNecessary(int track_idx){ auto &rtpSockRef = _rtp_sock[track_idx]; auto &rtcpSockRef = _rtcp_sock[track_idx]; if (!rtpSockRef || !rtcpSockRef) { - auto pr = makeSockPair(getPoller(), get_local_ip()); + std::pair pr = std::make_pair(createSocket(), createSocket()); + makeSockPair(pr, get_local_ip()); rtpSockRef = pr.first; rtcpSockRef = pr.second; } } //发送SETUP命令 -void RtspPlayer::sendSetup(unsigned int trackIndex) { - _on_response = std::bind(&RtspPlayer::handleResSETUP, this, placeholders::_1, trackIndex); - auto &track = _sdp_track[trackIndex]; +void RtspPlayer::sendSetup(unsigned int track_idx) { + _on_response = std::bind(&RtspPlayer::handleResSETUP, this, placeholders::_1, track_idx); + auto &track = _sdp_track[track_idx]; auto baseUrl = _content_base + "/" + track->_control_surffix; switch (_rtp_type) { case Rtsp::RTP_TCP: { @@ -232,11 +233,11 @@ void RtspPlayer::sendSetup(unsigned int trackIndex) { } break; case Rtsp::RTP_UDP: { - createUdpSockIfNecessary(trackIndex); + createUdpSockIfNecessary(track_idx); sendRtspRequest("SETUP", baseUrl, {"Transport", StrPrinter << "RTP/AVP;unicast;client_port=" - << _rtp_sock[trackIndex]->get_local_port() << "-" - << _rtcp_sock[trackIndex]->get_local_port()}); + << _rtp_sock[track_idx]->get_local_port() << "-" + << _rtcp_sock[track_idx]->get_local_port()}); } break; default: @@ -244,12 +245,12 @@ void RtspPlayer::sendSetup(unsigned int trackIndex) { } } -void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) { +void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int track_idx) { if (parser.Url() != "200") { throw std::runtime_error( StrPrinter << "SETUP:" << parser.Url() << " " << parser.Tail() << endl); } - if (uiTrackIndex == 0) { + if (track_idx == 0) { _session_id = parser["Session"]; _session_id.append(";"); _session_id = FindField(_session_id.data(), nullptr, ";"); @@ -268,19 +269,19 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) if(_rtp_type == Rtsp::RTP_TCP) { string interleaved = FindField( FindField((strTransport + ";").data(), "interleaved=", ";").data(), NULL, "-"); - _sdp_track[uiTrackIndex]->_interleaved = atoi(interleaved.data()); + _sdp_track[track_idx]->_interleaved = atoi(interleaved.data()); }else{ const char *strPos = (_rtp_type == Rtsp::RTP_MULTICAST ? "port=" : "server_port=") ; auto port_str = FindField((strTransport + ";").data(), strPos, ";"); uint16_t rtp_port = atoi(FindField(port_str.data(), NULL, "-").data()); uint16_t rtcp_port = atoi(FindField(port_str.data(), "-",NULL).data()); - auto &pRtpSockRef = _rtp_sock[uiTrackIndex]; - auto &pRtcpSockRef = _rtcp_sock[uiTrackIndex]; + auto &pRtpSockRef = _rtp_sock[track_idx]; + auto &pRtcpSockRef = _rtcp_sock[track_idx]; if (_rtp_type == Rtsp::RTP_MULTICAST) { //udp组播 auto multiAddr = FindField((strTransport + ";").data(), "destination=", ";"); - pRtpSockRef.reset(new Socket(getPoller())); + pRtpSockRef = createSocket(); if (!pRtpSockRef->bindUdpSock(rtp_port, multiAddr.data())) { pRtpSockRef.reset(); throw std::runtime_error("open udp sock err"); @@ -290,7 +291,7 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) SockUtil::joinMultiAddr(fd, multiAddr.data(),get_local_ip().data()); } } else { - createUdpSockIfNecessary(uiTrackIndex); + createUdpSockIfNecessary(track_idx); //udp单播 struct sockaddr_in rtpto; rtpto.sin_port = ntohs(rtp_port); @@ -310,7 +311,7 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) auto srcIP = inet_addr(get_peer_ip().data()); weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); //设置rtp over udp接收回调处理函数 - pRtpSockRef->setOnRead([srcIP, uiTrackIndex, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr , int addr_len) { + pRtpSockRef->setOnRead([srcIP, track_idx, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr , int addr_len) { auto strongSelf = weakSelf.lock(); if (!strongSelf) { return; @@ -319,13 +320,13 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) WarnL << "收到其他地址的rtp数据:" << SockUtil::inet_ntoa(((struct sockaddr_in *) addr)->sin_addr); return; } - strongSelf->handleOneRtp(uiTrackIndex, strongSelf->_sdp_track[uiTrackIndex]->_type, - strongSelf->_sdp_track[uiTrackIndex]->_samplerate, (unsigned char *) buf->data(), buf->size()); + strongSelf->handleOneRtp(track_idx, strongSelf->_sdp_track[track_idx]->_type, + strongSelf->_sdp_track[track_idx]->_samplerate, (unsigned char *) buf->data(), buf->size()); }); if(pRtcpSockRef) { //设置rtcp over udp接收回调处理函数 - pRtcpSockRef->setOnRead([srcIP, uiTrackIndex, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr , int addr_len) { + pRtcpSockRef->setOnRead([srcIP, track_idx, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr , int addr_len) { auto strongSelf = weakSelf.lock(); if (!strongSelf) { return; @@ -334,14 +335,14 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) WarnL << "收到其他地址的rtcp数据:" << SockUtil::inet_ntoa(((struct sockaddr_in *) addr)->sin_addr); return; } - strongSelf->onRtcpPacket(uiTrackIndex, strongSelf->_sdp_track[uiTrackIndex], (unsigned char *) buf->data(), buf->size()); + strongSelf->onRtcpPacket(track_idx, strongSelf->_sdp_track[track_idx], (unsigned char *) buf->data(), buf->size()); }); } } - if (uiTrackIndex < _sdp_track.size() - 1) { + if (track_idx < _sdp_track.size() - 1) { //需要继续发送SETUP命令 - sendSetup(uiTrackIndex + 1); + sendSetup(track_idx + 1); return; } //所有setup命令发送完毕 @@ -404,8 +405,8 @@ void RtspPlayer::sendPause(int type , uint32_t seekMS){ } } -void RtspPlayer::pause(bool bPause) { - sendPause(bPause ? type_pause : type_seek, getProgressMilliSecond()); +void RtspPlayer::pause(bool pause_flag) { + sendPause(pause_flag ? type_pause : type_seek, getProgressMilliSecond()); } void RtspPlayer::handleResPAUSE(const Parser& parser,int type) { @@ -416,6 +417,7 @@ void RtspPlayer::handleResPAUSE(const Parser& parser,int type) { break; case type_play: WarnL << "Play failed:" << parser.Url() << " " << parser.Tail() << endl; + onPlayResult_l(SockException(Err_shutdown, StrPrinter << "rtsp play failed:" << parser.Url() << " " << parser.Tail() ), !_play_check_timer); break; case type_seek: WarnL << "Seek failed:" << parser.Url() << " " << parser.Tail() << endl; @@ -442,8 +444,8 @@ void RtspPlayer::handleResPAUSE(const Parser& parser,int type) { iSeekTo = 1000 * atof(strStart.data()); DebugL << "seekTo(ms):" << iSeekTo; } - //设置相对时间戳 - onPlayResult_l(SockException(Err_success, type == type_seek ? "resum rtsp success" : "rtsp play success"), type == type_seek); + + onPlayResult_l(SockException(Err_success, type == type_seek ? "resum rtsp success" : "rtsp play success"), !_play_check_timer); } void RtspPlayer::onWholeRtspPacket(Parser &parser) { @@ -473,7 +475,7 @@ void RtspPlayer::onRtpPacket(const char *data, uint64_t len) { } //此处预留rtcp处理函数 -void RtspPlayer::onRtcpPacket(int iTrackidx, SdpTrack::Ptr &track, unsigned char *pucData, unsigned int uiLen){} +void RtspPlayer::onRtcpPacket(int track_idx, SdpTrack::Ptr &track, unsigned char *data, unsigned int len){} #if 0 //改代码提取自FFmpeg,参考之 @@ -533,12 +535,12 @@ void RtspPlayer::onRtcpPacket(int iTrackidx, SdpTrack::Ptr &track, unsigned char avio_w8(pb, 0); #endif -void RtspPlayer::sendReceiverReport(bool overTcp,int iTrackIndex){ +void RtspPlayer::sendReceiverReport(bool over_tcp, int track_idx){ static const char s_cname[] = "ZLMediaKitRtsp"; uint8_t aui8Rtcp[4 + 32 + 10 + sizeof(s_cname) + 1] = {0}; uint8_t *pui8Rtcp_RR = aui8Rtcp + 4, *pui8Rtcp_SDES = pui8Rtcp_RR + 32; - auto &track = _sdp_track[iTrackIndex]; - auto &counter = _rtcp_counter[iTrackIndex]; + auto &track = _sdp_track[track_idx]; + auto &counter = _rtcp_counter[track_idx]; aui8Rtcp[0] = '$'; aui8Rtcp[1] = track->_interleaved + 1; @@ -564,13 +566,13 @@ void RtspPlayer::sendReceiverReport(bool overTcp,int iTrackIndex){ pui8Rtcp_RR[15] = 0x00; //FIXME: max sequence received - int cycleCount = getCycleCount(iTrackIndex); + int cycleCount = getCycleCount(track_idx); pui8Rtcp_RR[16] = cycleCount >> 8; pui8Rtcp_RR[17] = cycleCount & 0xFF; pui8Rtcp_RR[18] = counter.pktCnt >> 8; pui8Rtcp_RR[19] = counter.pktCnt & 0xFF; - uint32_t jitter = htonl(getJitterSize(iTrackIndex)); + uint32_t jitter = htonl(getJitterSize(track_idx)); //FIXME: jitter memcpy(pui8Rtcp_RR + 20, &jitter , 4); /* last SR timestamp */ @@ -592,10 +594,10 @@ void RtspPlayer::sendReceiverReport(bool overTcp,int iTrackIndex){ memcpy(&pui8Rtcp_SDES[10], s_cname, sizeof(s_cname)); pui8Rtcp_SDES[10 + sizeof(s_cname)] = 0x00; - if(overTcp){ + if (over_tcp) { send(obtainBuffer((char *) aui8Rtcp, sizeof(aui8Rtcp))); - }else if(_rtcp_sock[iTrackIndex]) { - _rtcp_sock[iTrackIndex]->send((char *) aui8Rtcp + 4, sizeof(aui8Rtcp) - 4); + } else if (_rtcp_sock[track_idx]) { + _rtcp_sock[track_idx]->send((char *) aui8Rtcp + 4, sizeof(aui8Rtcp) - 4); } } @@ -613,24 +615,24 @@ void RtspPlayer::onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx){ } float RtspPlayer::getPacketLossRate(TrackType type) const{ - int iTrackIdx = getTrackIndexByTrackType(type); - if(iTrackIdx == -1){ + try { + auto track_idx = getTrackIndexByTrackType(type); + if (_rtp_seq_now[track_idx] - _rtp_seq_start[track_idx] + 1 == 0) { + return 0; + } + return 1.0 - (double) _rtp_recv_count[track_idx] / (_rtp_seq_now[track_idx] - _rtp_seq_start[track_idx] + 1); + } catch (...) { uint64_t totalRecv = 0; uint64_t totalSend = 0; for (unsigned int i = 0; i < _sdp_track.size(); i++) { totalRecv += _rtp_recv_count[i]; totalSend += (_rtp_seq_now[i] - _rtp_seq_start[i] + 1); } - if(totalSend == 0){ + if (totalSend == 0) { return 0; } - return 1.0 - (double)totalRecv / totalSend; + return 1.0 - (double) totalRecv / totalSend; } - - if(_rtp_seq_now[iTrackIdx] - _rtp_seq_start[iTrackIdx] + 1 == 0){ - return 0; - } - return 1.0 - (double)_rtp_recv_count[iTrackIdx] / (_rtp_seq_now[iTrackIdx] - _rtp_seq_start[iTrackIdx] + 1); } uint32_t RtspPlayer::getProgressMilliSecond() const{ @@ -705,56 +707,40 @@ void RtspPlayer::sendRtspRequest(const string &cmd, const string &url,const StrC SockSender::send(printer << "\r\n"); } -void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &pkt, const SdpTrack::Ptr &track) { +void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track) { _rtp_recv_ticker.resetTime(); - onRecvRTP(pkt, track); + onRecvRTP(rtp, track); - int iTrackIndex = getTrackIndexByTrackType(pkt->type); - RtcpCounter &counter = _rtcp_counter[iTrackIndex]; - counter.pktCnt = pkt->sequence; - auto &ticker = _rtcp_send_ticker[iTrackIndex]; + int track_idx = getTrackIndexByTrackType(rtp->type); + RtcpCounter &counter = _rtcp_counter[track_idx]; + counter.pktCnt = rtp->sequence; + auto &ticker = _rtcp_send_ticker[track_idx]; if (ticker.elapsedTime() > 5 * 1000) { //send rtcp every 5 second counter.lastTimeStamp = counter.timeStamp; //直接保存网络字节序 - memcpy(&counter.timeStamp, pkt->data() + 8, 4); + memcpy(&counter.timeStamp, rtp->data() + 8, 4); if (counter.lastTimeStamp != 0) { - sendReceiverReport(_rtp_type == Rtsp::RTP_TCP, iTrackIndex); + sendReceiverReport(_rtp_type == Rtsp::RTP_TCP, track_idx); ticker.resetTime(); } //有些rtsp服务器需要rtcp保活,有些需要发送信令保活 - if (iTrackIndex == 0) { + if (track_idx == 0) { //只需要发送一次心跳信令包 sendKeepAlive(); } } } -void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshakeCompleted) { - WarnL << ex.getErrCode() << " " << ex.what(); - - if(!ex){ - //播放成功,恢复rtp接收超时定时器 - _rtp_recv_ticker.resetTime(); - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - int timeoutMS = (*this)[kMediaTimeoutMS].as(); - //创建rtp数据接收超时检测定时器 - _rtp_check_timer.reset(new Timer(timeoutMS / 2000.0, [weakSelf,timeoutMS]() { - auto strongSelf=weakSelf.lock(); - if(!strongSelf) { - return false; - } - if(strongSelf->_rtp_recv_ticker.elapsedTime() > timeoutMS) { - //接收rtp媒体数据包超时 - strongSelf->onPlayResult_l(SockException(Err_timeout,"receive rtp timeout"), true); - return false; - } - return true; - }, getPoller())); +void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshake_done) { + if (ex.getErrCode() == Err_shutdown) { + //主动shutdown的,不触发回调 + return; } - if (!handshakeCompleted) { + WarnL << ex.getErrCode() << " " << ex.what(); + if (!handshake_done) { //开始播放阶段 _play_check_timer.reset(); onPlayResult(ex); @@ -768,7 +754,26 @@ void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshakeComplete onResume(); } - if(ex){ + if (!ex) { + //播放成功,恢复rtp接收超时定时器 + _rtp_recv_ticker.resetTime(); + int timeoutMS = (*this)[kMediaTimeoutMS].as(); + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + auto lam = [weakSelf, timeoutMS]() { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return false; + } + if (strongSelf->_rtp_recv_ticker.elapsedTime() > timeoutMS) { + //接收rtp媒体数据包超时 + strongSelf->onPlayResult_l(SockException(Err_timeout, "receive rtp timeout"), true); + return false; + } + return true; + }; + //创建rtp数据接收超时检测定时器 + _rtp_check_timer = std::make_shared(timeoutMS / 2000.0, lam, getPoller()); + } else { teardown(); } } @@ -785,16 +790,16 @@ int RtspPlayer::getTrackIndexByInterleaved(int interleaved) const { throw SockException(Err_shutdown, StrPrinter << "no such track with interleaved:" << interleaved); } -int RtspPlayer::getTrackIndexByTrackType(TrackType trackType) const { +int RtspPlayer::getTrackIndexByTrackType(TrackType track_type) const { for (unsigned int i = 0; i < _sdp_track.size(); i++) { - if (_sdp_track[i]->_type == trackType) { + if (_sdp_track[i]->_type == track_type) { return i; } } if (_sdp_track.size() == 1) { return 0; } - throw SockException(Err_shutdown, StrPrinter << "no such track with type:" << (int) trackType); + throw SockException(Err_shutdown, StrPrinter << "no such track with type:" << (int) track_type); } } /* namespace mediakit */ diff --git a/src/Rtsp/RtspPlayer.h b/src/Rtsp/RtspPlayer.h index 5a7b8efc..e6529a23 100644 --- a/src/Rtsp/RtspPlayer.h +++ b/src/Rtsp/RtspPlayer.h @@ -36,16 +36,18 @@ class RtspPlayer: public PlayerBase,public TcpClient, public RtspSplitter, publi public: typedef std::shared_ptr Ptr; - RtspPlayer(const EventPoller::Ptr &poller) ; - virtual ~RtspPlayer(void); + RtspPlayer(const EventPoller::Ptr &poller); + ~RtspPlayer() override; + void play(const string &strUrl) override; - void pause(bool bPause) override; + void pause(bool pause_flag) override; void teardown() override; float getPacketLossRate(TrackType type) const override; + protected: //派生类回调函数 - virtual bool onCheckSDP(const string &strSdp) = 0; - virtual void onRecvRTP(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track) = 0; + virtual bool onCheckSDP(const string &sdp) = 0; + virtual void onRecvRTP(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track) = 0; uint32_t getProgressMilliSecond() const; void seekToMilliSecond(uint32_t ms); @@ -64,47 +66,48 @@ protected: /** * rtp数据包排序后输出 - * @param rtppt rtp数据包 - * @param trackidx track索引 + * @param rtp rtp数据包 + * @param track_idx track索引 */ - void onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx) override; - + void onRtpSorted(const RtpPacket::Ptr &rtp, int track_idx) override; /** * 收到RTCP包回调 - * @param iTrackidx - * @param track - * @param pucData - * @param uiLen + * @param track_idx track索引 + * @param track sdp相关信息 + * @param data rtcp内容 + * @param len rtcp内容长度 */ - virtual void onRtcpPacket(int iTrackidx, SdpTrack::Ptr &track, unsigned char *pucData, unsigned int uiLen); + virtual void onRtcpPacket(int track_idx, SdpTrack::Ptr &track, unsigned char *data, unsigned int len); /////////////TcpClient override///////////// void onConnect(const SockException &err) override; - void onRecv(const Buffer::Ptr &pBuf) override; + void onRecv(const Buffer::Ptr &buf) override; void onErr(const SockException &ex) override; + private: - void onRecvRTP_l(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track); - void onPlayResult_l(const SockException &ex , bool handshakeCompleted); + void onRecvRTP_l(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track); + void onPlayResult_l(const SockException &ex , bool handshake_done); int getTrackIndexByInterleaved(int interleaved) const; - int getTrackIndexByTrackType(TrackType trackType) const; + int getTrackIndexByTrackType(TrackType track_type) const; - void handleResSETUP(const Parser &parser, unsigned int uiTrackIndex); + void handleResSETUP(const Parser &parser, unsigned int track_idx); void handleResDESCRIBE(const Parser &parser); bool handleAuthenticationFailure(const string &wwwAuthenticateParamsStr); void handleResPAUSE(const Parser &parser, int type); bool handleResponse(const string &cmd, const Parser &parser); void sendOptions(); - void sendSetup(unsigned int uiTrackIndex); + void sendSetup(unsigned int track_idx); void sendPause(int type , uint32_t ms); void sendDescribe(); void sendKeepAlive(); void sendRtspRequest(const string &cmd, const string &url ,const StrCaseMap &header = StrCaseMap()); void sendRtspRequest(const string &cmd, const string &url ,const std::initializer_list &header); - void sendReceiverReport(bool overTcp,int iTrackIndex); + void sendReceiverReport(bool over_tcp, int track_idx); void createUdpSockIfNecessary(int track_idx); + private: string _play_url; vector _sdp_track; @@ -148,5 +151,4 @@ private: }; } /* namespace mediakit */ - #endif /* SRC_RTSPPLAYER_RTSPPLAYER_H_TXT_ */ diff --git a/src/Rtsp/RtspPlayerImp.h b/src/Rtsp/RtspPlayerImp.h index 2c9ea972..814a4299 100644 --- a/src/Rtsp/RtspPlayerImp.h +++ b/src/Rtsp/RtspPlayerImp.h @@ -25,46 +25,51 @@ using namespace toolkit; namespace mediakit { -class RtspPlayerImp: public PlayerImp { +class RtspPlayerImp : public PlayerImp { public: typedef std::shared_ptr Ptr; - RtspPlayerImp(const EventPoller::Ptr &poller) : PlayerImp(poller){} - virtual ~RtspPlayerImp(){ - DebugL< 0){ + + RtspPlayerImp(const EventPoller::Ptr &poller) : PlayerImp(poller) {} + ~RtspPlayerImp() override{ + DebugL << endl; + } + + float getProgress() const override { + if (getDuration() > 0) { return getProgressMilliSecond() / (getDuration() * 1000); } return PlayerBase::getProgress(); - - }; - void seekTo(float fProgress) override{ - fProgress = MAX(float(0),MIN(fProgress,float(1.0))); + + } + + void seekTo(float fProgress) override { + fProgress = MAX(float(0), MIN(fProgress, float(1.0))); seekToMilliSecond(fProgress * getDuration() * 1000); - }; + } + private: //派生类回调函数 bool onCheckSDP(const string &sdp) override { - _pRtspMediaSrc = dynamic_pointer_cast(_pMediaSrc); - if(_pRtspMediaSrc){ - _pRtspMediaSrc->setSdp(sdp); + _rtsp_media_src = dynamic_pointer_cast(_pMediaSrc); + if (_rtsp_media_src) { + _rtsp_media_src->setSdp(sdp); } _delegate.reset(new RtspDemuxer); _delegate->loadSdp(sdp); return true; } + void onRecvRTP(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track) override { - if(_pRtspMediaSrc){ + if (_rtsp_media_src) { // rtsp直接代理是无法判断该rtp是否是I帧,所以GOP缓存基本是无效的 // 为了减少内存使用,那么我们设置为一直关键帧以便清空GOP缓存 - _pRtspMediaSrc->onWrite(rtp,true); + _rtsp_media_src->onWrite(rtp, true); } _delegate->inputRtp(rtp); - if(_maxAnalysisMS && _delegate->isInited(_maxAnalysisMS)){ - PlayerImp::onPlayResult(SockException(Err_success,"play rtsp success")); - _maxAnalysisMS = 0; + if (_max_analysis_ms && _delegate->isInited(_max_analysis_ms)) { + PlayerImp::onPlayResult(SockException(Err_success, "play rtsp success")); + _max_analysis_ms = 0; } } @@ -74,17 +79,18 @@ private: //如果超过这个时间还未获取成功,那么会强制触发onPlayResult事件(虽然此时有些track还未初始化成功) void onPlayResult(const SockException &ex) override { //isInited判断条件:无超时 - if(ex || _delegate->isInited(0)){ + if (ex || _delegate->isInited(0)) { //已经初始化成功,说明sdp里面有完善的信息 - PlayerImp::onPlayResult(ex); - }else{ + PlayerImp::onPlayResult(ex); + } else { //还没初始化成功,说明sdp里面信息不完善,还有一些track未初始化成功 - _maxAnalysisMS = (*this)[Client::kMaxAnalysisMS]; + _max_analysis_ms = (*this)[Client::kMaxAnalysisMS]; } } + private: - RtspMediaSource::Ptr _pRtspMediaSrc; - int _maxAnalysisMS = 0; + int _max_analysis_ms = 0; + RtspMediaSource::Ptr _rtsp_media_src; }; } /* namespace mediakit */ diff --git a/src/Rtsp/RtspPusher.cpp b/src/Rtsp/RtspPusher.cpp index 16dd12b7..4728c8b4 100644 --- a/src/Rtsp/RtspPusher.cpp +++ b/src/Rtsp/RtspPusher.cpp @@ -17,8 +17,8 @@ using namespace mediakit::Client; namespace mediakit { -RtspPusher::RtspPusher(const EventPoller::Ptr &poller,const RtspMediaSource::Ptr &src) : TcpClient(poller){ - _pMediaSrc = src; +RtspPusher::RtspPusher(const EventPoller::Ptr &poller, const RtspMediaSource::Ptr &src) : TcpClient(poller) { + _push_src = src; } RtspPusher::~RtspPusher() { @@ -28,30 +28,30 @@ RtspPusher::~RtspPusher() { void RtspPusher::teardown() { if (alive()) { - sendRtspRequest("TEARDOWN" ,_strContentBase); - shutdown(SockException(Err_shutdown,"teardown")); + sendRtspRequest("TEARDOWN", _content_base); + shutdown(SockException(Err_shutdown, "teardown")); } reset(); - CLEAR_ARR(_apUdpSock); - _rtspMd5Nonce.clear(); - _rtspRealm.clear(); - _aTrackInfo.clear(); - _strSession.clear(); - _strContentBase.clear(); - _strSession.clear(); - _uiCseq = 1; - _pPublishTimer.reset(); - _pBeatTimer.reset(); - _pRtspReader.reset(); - _aTrackInfo.clear(); - _onHandshake = nullptr; + CLEAR_ARR(_udp_socks); + _nonce.clear(); + _realm.clear(); + _track_vec.clear(); + _session_id.clear(); + _content_base.clear(); + _session_id.clear(); + _cseq = 1; + _publish_timer.reset(); + _beat_timer.reset(); + _rtsp_reader.reset(); + _track_vec.clear(); + _on_res_func = nullptr; } -void RtspPusher::publish(const string &strUrl) { +void RtspPusher::publish(const string &url_str) { RtspUrl url; - if(!url.parse(strUrl)){ - onPublishResult(SockException(Err_other,StrPrinter << "illegal rtsp url:" << strUrl),false); + if (!url.parse(url_str)) { + onPublishResult(SockException(Err_other, StrPrinter << "illegal rtsp url:" << url_str), false); return; } @@ -65,97 +65,101 @@ void RtspPusher::publish(const string &strUrl) { (*this)[kRtspPwdIsMD5] = false; } - _strUrl = strUrl; - _eType = (Rtsp::eRtpType)(int)(*this)[kRtpType]; - DebugL << url._url << " " << (url._user.size() ? url._user : "null") << " " << (url._passwd.size() ? url._passwd : "null") << " " << _eType; + _url = url_str; + _rtp_type = (Rtsp::eRtpType) (int) (*this)[kRtpType]; + DebugL << url._url << " " << (url._user.size() ? url._user : "null") << " " + << (url._passwd.size() ? url._passwd : "null") << " " << _rtp_type; - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - float publishTimeOutSec = (*this)[kTimeoutMS].as() / 1000.0; - _pPublishTimer.reset( new Timer(publishTimeOutSec, [weakSelf]() { - auto strongSelf=weakSelf.lock(); - if(!strongSelf) { + weak_ptr weak_self = dynamic_pointer_cast(shared_from_this()); + float publish_timeout_sec = (*this)[kTimeoutMS].as() / 1000.0; + _publish_timer.reset(new Timer(publish_timeout_sec, [weak_self]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { return false; } - strongSelf->onPublishResult(SockException(Err_timeout,"publish rtsp timeout"),false); + strong_self->onPublishResult(SockException(Err_timeout, "publish rtsp timeout"), false); return false; - },getPoller())); + }, getPoller())); - if(!(*this)[kNetAdapter].empty()){ + if (!(*this)[kNetAdapter].empty()) { setNetAdapter((*this)[kNetAdapter]); } - startConnect(url._host, url._port, publishTimeOutSec); + startConnect(url._host, url._port, publish_timeout_sec); } -void RtspPusher::onPublishResult(const SockException &ex, bool handshakeCompleted) { - if(!handshakeCompleted){ +void RtspPusher::onPublishResult(const SockException &ex, bool handshake_done) { + if (ex.getErrCode() == Err_shutdown) { + //主动shutdown的,不触发回调 + return; + } + if (!handshake_done) { //播放结果回调 - _pPublishTimer.reset(); - if(_onPublished){ - _onPublished(ex); + _publish_timer.reset(); + if (_on_published) { + _on_published(ex); } } else { //播放成功后异常断开回调 - if(_onShutdown){ - _onShutdown(ex); + if (_on_shutdown) { + _on_shutdown(ex); } } - if(ex){ + if (ex) { teardown(); } } void RtspPusher::onErr(const SockException &ex) { //定时器_pPublishTimer为空后表明握手结束了 - onPublishResult(ex,!_pPublishTimer); + onPublishResult(ex, !_publish_timer); } void RtspPusher::onConnect(const SockException &err) { - if(err) { - onPublishResult(err,false); + if (err) { + onPublishResult(err, false); return; } //推流器不需要多大的接收缓存,节省内存占用 - _sock->setReadBuffer(std::make_shared(1 * 1024)); + getSock()->setReadBuffer(std::make_shared(1 * 1024)); sendAnnounce(); } -void RtspPusher::onRecv(const Buffer::Ptr &pBuf){ +void RtspPusher::onRecv(const Buffer::Ptr &buf){ try { - input(pBuf->data(), pBuf->size()); + input(buf->data(), buf->size()); } catch (exception &e) { SockException ex(Err_other, e.what()); //定时器_pPublishTimer为空后表明握手结束了 - onPublishResult(ex,!_pPublishTimer); + onPublishResult(ex, !_publish_timer); } } void RtspPusher::onWholeRtspPacket(Parser &parser) { - decltype(_onHandshake) fun; - _onHandshake.swap(fun); - if(fun){ - fun(parser); + decltype(_on_res_func) func; + _on_res_func.swap(func); + if (func) { + func(parser); } parser.Clear(); } - void RtspPusher::sendAnnounce() { - auto src = _pMediaSrc.lock(); + auto src = _push_src.lock(); if (!src) { throw std::runtime_error("the media source was released"); } //解析sdp - _sdpParser.load(src->getSdp()); - _aTrackInfo = _sdpParser.getAvailableTrack(); + _sdp_parser.load(src->getSdp()); + _track_vec = _sdp_parser.getAvailableTrack(); - if (_aTrackInfo.empty()) { + if (_track_vec.empty()) { throw std::runtime_error("无有效的Sdp Track"); } - _onHandshake = std::bind(&RtspPusher::handleResAnnounce,this, placeholders::_1); - sendRtspRequest("ANNOUNCE",_strUrl,{},src->getSdp()); + _on_res_func = std::bind(&RtspPusher::handleResAnnounce, this, placeholders::_1); + sendRtspRequest("ANNOUNCE", _url, {}, src->getSdp()); } void RtspPusher::handleResAnnounce(const Parser &parser) { @@ -165,9 +169,9 @@ void RtspPusher::handleResAnnounce(const Parser &parser) { sendAnnounce(); return; } - if(parser.Url() == "302"){ + if (parser.Url() == "302") { auto newUrl = parser["Location"]; - if(newUrl.empty()){ + if (newUrl.empty()) { throw std::runtime_error("未找到Location字段(跳转url)"); } publish(newUrl); @@ -176,45 +180,45 @@ void RtspPusher::handleResAnnounce(const Parser &parser) { if (parser.Url() != "200") { throw std::runtime_error(StrPrinter << "ANNOUNCE:" << parser.Url() << " " << parser.Tail()); } - _strContentBase = parser["Content-Base"]; + _content_base = parser["Content-Base"]; - if(_strContentBase.empty()){ - _strContentBase = _strUrl; + if (_content_base.empty()) { + _content_base = _url; } - if (_strContentBase.back() == '/') { - _strContentBase.pop_back(); + if (_content_base.back() == '/') { + _content_base.pop_back(); } sendSetup(0); } -bool RtspPusher::handleAuthenticationFailure(const string ¶msStr) { - if(!_rtspRealm.empty()){ +bool RtspPusher::handleAuthenticationFailure(const string ¶ms_str) { + if (!_realm.empty()) { //已经认证过了 return false; } - char *realm = new char[paramsStr.size()]; - char *nonce = new char[paramsStr.size()]; - char *stale = new char[paramsStr.size()]; - onceToken token(nullptr,[&](){ + char *realm = new char[params_str.size()]; + char *nonce = new char[params_str.size()]; + char *stale = new char[params_str.size()]; + onceToken token(nullptr, [&]() { delete[] realm; delete[] nonce; delete[] stale; }); - if (sscanf(paramsStr.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\", stale=%[a-zA-Z]", realm, nonce, stale) == 3) { - _rtspRealm = (const char *)realm; - _rtspMd5Nonce = (const char *)nonce; + if (sscanf(params_str.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\", stale=%[a-zA-Z]", realm, nonce, stale) == 3) { + _realm = (const char *) realm; + _nonce = (const char *) nonce; return true; } - if (sscanf(paramsStr.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2) { - _rtspRealm = (const char *)realm; - _rtspMd5Nonce = (const char *)nonce; + if (sscanf(params_str.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2) { + _realm = (const char *) realm; + _nonce = (const char *) nonce; return true; } - if (sscanf(paramsStr.data(), "Basic realm=\"%[^\"]\"", realm) == 1) { - _rtspRealm = (const char *)realm; + if (sscanf(params_str.data(), "Basic realm=\"%[^\"]\"", realm) == 1) { + _realm = (const char *) realm; return true; } return false; @@ -222,30 +226,33 @@ bool RtspPusher::handleAuthenticationFailure(const string ¶msStr) { //有必要的情况下创建udp端口 void RtspPusher::createUdpSockIfNecessary(int track_idx){ - auto &rtpSockRef = _apUdpSock[track_idx]; - if(!rtpSockRef){ - rtpSockRef.reset(new Socket(getPoller())); + auto &rtp_sock = _udp_socks[track_idx]; + if (!rtp_sock) { + rtp_sock = createSocket(); //rtp随机端口 - if (!rtpSockRef->bindUdpSock(0, get_local_ip().data())) { - rtpSockRef.reset(); + if (!rtp_sock->bindUdpSock(0, get_local_ip().data())) { + rtp_sock.reset(); throw std::runtime_error("open rtp sock failed"); } } } -void RtspPusher::sendSetup(unsigned int trackIndex) { - _onHandshake = std::bind(&RtspPusher::handleResSetup,this, placeholders::_1,trackIndex); - auto &track = _aTrackInfo[trackIndex]; - auto baseUrl = _strContentBase + "/" + track->_control_surffix; - switch (_eType) { +void RtspPusher::sendSetup(unsigned int track_idx) { + _on_res_func = std::bind(&RtspPusher::handleResSetup, this, placeholders::_1, track_idx); + auto &track = _track_vec[track_idx]; + auto base_url = _content_base + "/" + track->_control_surffix; + switch (_rtp_type) { case Rtsp::RTP_TCP: { - sendRtspRequest("SETUP",baseUrl,{"Transport",StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track->_type * 2 << "-" << track->_type * 2 + 1}); + sendRtspRequest("SETUP", base_url, {"Transport", + StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track->_type * 2 + << "-" << track->_type * 2 + 1}); } break; case Rtsp::RTP_UDP: { - createUdpSockIfNecessary(trackIndex); - int port = _apUdpSock[trackIndex]->get_local_port(); - sendRtspRequest("SETUP",baseUrl,{"Transport",StrPrinter << "RTP/AVP;unicast;client_port=" << port << "-" << port + 1}); + createUdpSockIfNecessary(track_idx); + int port = _udp_socks[track_idx]->get_local_port(); + sendRtspRequest("SETUP", base_url, + {"Transport", StrPrinter << "RTP/AVP;unicast;client_port=" << port << "-" << port + 1}); } break; default: @@ -254,42 +261,41 @@ void RtspPusher::sendSetup(unsigned int trackIndex) { } -void RtspPusher::handleResSetup(const Parser &parser, unsigned int uiTrackIndex) { +void RtspPusher::handleResSetup(const Parser &parser, unsigned int track_idx) { if (parser.Url() != "200") { - throw std::runtime_error( - StrPrinter << "SETUP:" << parser.Url() << " " << parser.Tail() << endl); + throw std::runtime_error(StrPrinter << "SETUP:" << parser.Url() << " " << parser.Tail() << endl); } - if (uiTrackIndex == 0) { - _strSession = parser["Session"]; - _strSession.append(";"); - _strSession = FindField(_strSession.data(), nullptr, ";"); + if (track_idx == 0) { + _session_id = parser["Session"]; + _session_id.append(";"); + _session_id = FindField(_session_id.data(), nullptr, ";"); } - auto strTransport = parser["Transport"]; - if(strTransport.find("TCP") != string::npos || strTransport.find("interleaved") != string::npos){ - _eType = Rtsp::RTP_TCP; - string interleaved = FindField( FindField((strTransport + ";").data(), "interleaved=", ";").data(), NULL, "-"); - _aTrackInfo[uiTrackIndex]->_interleaved = atoi(interleaved.data()); - }else if(strTransport.find("multicast") != string::npos){ + auto transport = parser["Transport"]; + if (transport.find("TCP") != string::npos || transport.find("interleaved") != string::npos) { + _rtp_type = Rtsp::RTP_TCP; + string interleaved = FindField(FindField((transport + ";").data(), "interleaved=", ";").data(), NULL, "-"); + _track_vec[track_idx]->_interleaved = atoi(interleaved.data()); + } else if (transport.find("multicast") != string::npos) { throw std::runtime_error("SETUP rtsp pusher can not support multicast!"); - }else{ - _eType = Rtsp::RTP_UDP; - createUdpSockIfNecessary(uiTrackIndex); - const char *strPos = "server_port=" ; - auto port_str = FindField((strTransport + ";").data(), strPos, ";"); + } else { + _rtp_type = Rtsp::RTP_UDP; + createUdpSockIfNecessary(track_idx); + const char *strPos = "server_port="; + auto port_str = FindField((transport + ";").data(), strPos, ";"); uint16_t port = atoi(FindField(port_str.data(), NULL, "-").data()); struct sockaddr_in rtpto; rtpto.sin_port = ntohs(port); rtpto.sin_family = AF_INET; rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data()); - _apUdpSock[uiTrackIndex]->setSendPeerAddr((struct sockaddr *)&(rtpto)); + _udp_socks[track_idx]->setSendPeerAddr((struct sockaddr *) &(rtpto)); } - RtspSplitter::enableRecvRtp(_eType == Rtsp::RTP_TCP); + RtspSplitter::enableRecvRtp(_rtp_type == Rtsp::RTP_TCP); - if (uiTrackIndex < _aTrackInfo.size() - 1) { + if (track_idx < _track_vec.size() - 1) { //需要继续发送SETUP命令 - sendSetup(uiTrackIndex + 1); + sendSetup(track_idx + 1); return; } @@ -297,13 +303,12 @@ void RtspPusher::handleResSetup(const Parser &parser, unsigned int uiTrackIndex) } void RtspPusher::sendOptions() { - _onHandshake = [this](const Parser& parser){}; - sendRtspRequest("OPTIONS",_strContentBase); + _on_res_func = [this](const Parser &parser) {}; + sendRtspRequest("OPTIONS", _content_base); } inline void RtspPusher::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) { - //InfoL<<(int)pkt.Interleaved; - switch (_eType) { + switch (_rtp_type) { case Rtsp::RTP_TCP: { int i = 0; int size = pkt->size(); @@ -315,88 +320,87 @@ inline void RtspPusher::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) BufferRtp::Ptr buffer(new BufferRtp(rtp)); send(buffer); }); - } break; + } + case Rtsp::RTP_UDP: { int i = 0; int size = pkt->size(); pkt->for_each([&](const RtpPacket::Ptr &rtp) { int iTrackIndex = getTrackIndexByTrackType(rtp->type); - auto &pSock = _apUdpSock[iTrackIndex]; + auto &pSock = _udp_socks[iTrackIndex]; if (!pSock) { - shutdown(SockException(Err_shutdown,"udp sock not opened yet")); + shutdown(SockException(Err_shutdown, "udp sock not opened yet")); return; } - BufferRtp::Ptr buffer(new BufferRtp(rtp,4)); + BufferRtp::Ptr buffer(new BufferRtp(rtp, 4)); pSock->send(buffer, nullptr, 0, ++i == size); }); + break; } - break; - default: - break; + default : break; } } inline int RtspPusher::getTrackIndexByTrackType(TrackType type) { - for (unsigned int i = 0; i < _aTrackInfo.size(); i++) { - if (type == _aTrackInfo[i]->_type) { + for (unsigned int i = 0; i < _track_vec.size(); i++) { + if (type == _track_vec[i]->_type) { return i; } } - if(_aTrackInfo.size() == 1){ + if (_track_vec.size() == 1) { return 0; } throw SockException(Err_shutdown, StrPrinter << "no such track with type:" << (int) type); } void RtspPusher::sendRecord() { - _onHandshake = [this](const Parser& parser){ - auto src = _pMediaSrc.lock(); + _on_res_func = [this](const Parser &parser) { + auto src = _push_src.lock(); if (!src) { throw std::runtime_error("the media source was released"); } - _pRtspReader = src->getRing()->attach(getPoller()); - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - _pRtspReader->setReadCB([weakSelf](const RtspMediaSource::RingDataType &pkt){ - auto strongSelf = weakSelf.lock(); - if(!strongSelf) { + _rtsp_reader = src->getRing()->attach(getPoller()); + weak_ptr weak_self = dynamic_pointer_cast(shared_from_this()); + _rtsp_reader->setReadCB([weak_self](const RtspMediaSource::RingDataType &pkt) { + auto strong_self = weak_self.lock(); + if (!strong_self) { return; } - strongSelf->sendRtpPacket(pkt); + strong_self->sendRtpPacket(pkt); }); - _pRtspReader->setDetachCB([weakSelf](){ - auto strongSelf = weakSelf.lock(); - if(strongSelf){ - strongSelf->onPublishResult(SockException(Err_other,"媒体源被释放"), !strongSelf->_pPublishTimer); + _rtsp_reader->setDetachCB([weak_self]() { + auto strong_self = weak_self.lock(); + if (strong_self) { + strong_self->onPublishResult(SockException(Err_other, "媒体源被释放"), !strong_self->_publish_timer); } }); - if(_eType != Rtsp::RTP_TCP){ + if (_rtp_type != Rtsp::RTP_TCP) { /////////////////////////心跳///////////////////////////////// - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - _pBeatTimer.reset(new Timer((*this)[kBeatIntervalMS].as() / 1000.0, [weakSelf](){ - auto strongSelf = weakSelf.lock(); - if (!strongSelf){ + _beat_timer.reset(new Timer((*this)[kBeatIntervalMS].as() / 1000.0, [weak_self]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { return false; } - strongSelf->sendOptions(); + strong_self->sendOptions(); return true; - },getPoller())); + }, getPoller())); } - onPublishResult(SockException(Err_success,"success"), false); + onPublishResult(SockException(Err_success, "success"), false); //提升发送性能 setSocketFlags(); }; - sendRtspRequest("RECORD",_strContentBase,{"Range","npt=0.000-"}); + sendRtspRequest("RECORD", _content_base, {"Range", "npt=0.000-"}); } void RtspPusher::setSocketFlags(){ - GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS); - if(mergeWriteMS > 0) { + GET_CONFIG(int, merge_write_ms, General::kMergeWriteMS); + if (merge_write_ms > 0) { //提高发送性能 setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); - SockUtil::setNoDelay(_sock->rawFD(), false); + SockUtil::setNoDelay(getSock()->rawFD(), false); } } @@ -404,26 +408,26 @@ void RtspPusher::sendRtspRequest(const string &cmd, const string &url, const std string key; StrCaseMap header_map; int i = 0; - for(auto &val : header){ - if(++i % 2 == 0){ - header_map.emplace(key,val); - }else{ + for (auto &val : header) { + if (++i % 2 == 0) { + header_map.emplace(key, val); + } else { key = val; } } - sendRtspRequest(cmd,url,header_map,sdp); + sendRtspRequest(cmd, url, header_map, sdp); } void RtspPusher::sendRtspRequest(const string &cmd, const string &url,const StrCaseMap &header_const,const string &sdp ) { auto header = header_const; - header.emplace("CSeq",StrPrinter << _uiCseq++); - header.emplace("User-Agent",SERVER_NAME); + header.emplace("CSeq", StrPrinter << _cseq++); + header.emplace("User-Agent", SERVER_NAME); - if(!_strSession.empty()){ - header.emplace("Session",_strSession); + if (!_session_id.empty()) { + header.emplace("Session", _session_id); } - if(!_rtspRealm.empty() && !(*this)[kRtspUser].empty()){ - if(!_rtspMd5Nonce.empty()){ + if (!_realm.empty() && !(*this)[kRtspUser].empty()) { + if (!_nonce.empty()) { //MD5认证 /* response计算方法如下: @@ -434,41 +438,41 @@ void RtspPusher::sendRtspRequest(const string &cmd, const string &url,const StrC response= md5( md5(username:realm:password):nonce:md5(public_method:url) ); */ string encrypted_pwd = (*this)[kRtspPwd]; - if(!(*this)[kRtspPwdIsMD5].as()){ - encrypted_pwd = MD5((*this)[kRtspUser]+ ":" + _rtspRealm + ":" + encrypted_pwd).hexdigest(); + if (!(*this)[kRtspPwdIsMD5].as()) { + encrypted_pwd = MD5((*this)[kRtspUser] + ":" + _realm + ":" + encrypted_pwd).hexdigest(); } - auto response = MD5( encrypted_pwd + ":" + _rtspMd5Nonce + ":" + MD5(cmd + ":" + url).hexdigest()).hexdigest(); + auto response = MD5(encrypted_pwd + ":" + _nonce + ":" + MD5(cmd + ":" + url).hexdigest()).hexdigest(); _StrPrinter printer; printer << "Digest "; printer << "username=\"" << (*this)[kRtspUser] << "\", "; - printer << "realm=\"" << _rtspRealm << "\", "; - printer << "nonce=\"" << _rtspMd5Nonce << "\", "; + printer << "realm=\"" << _realm << "\", "; + printer << "nonce=\"" << _nonce << "\", "; printer << "uri=\"" << url << "\", "; printer << "response=\"" << response << "\""; - header.emplace("Authorization",printer); - }else if(!(*this)[kRtspPwdIsMD5].as()){ + header.emplace("Authorization", printer); + } else if (!(*this)[kRtspPwdIsMD5].as()) { //base64认证 string authStr = StrPrinter << (*this)[kRtspUser] << ":" << (*this)[kRtspPwd]; char authStrBase64[1024] = {0}; - av_base64_encode(authStrBase64,sizeof(authStrBase64),(uint8_t *)authStr.data(),authStr.size()); - header.emplace("Authorization",StrPrinter << "Basic " << authStrBase64 ); + av_base64_encode(authStrBase64, sizeof(authStrBase64), (uint8_t *) authStr.data(), authStr.size()); + header.emplace("Authorization", StrPrinter << "Basic " << authStrBase64); } } - if(!sdp.empty()){ - header.emplace("Content-Length",StrPrinter << sdp.size()); - header.emplace("Content-Type","application/sdp"); + if (!sdp.empty()) { + header.emplace("Content-Length", StrPrinter << sdp.size()); + header.emplace("Content-Type", "application/sdp"); } _StrPrinter printer; printer << cmd << " " << url << " RTSP/1.0\r\n"; - for (auto &pr : header){ + for (auto &pr : header) { printer << pr.first << ": " << pr.second << "\r\n"; } printer << "\r\n"; - if(!sdp.empty()){ + if (!sdp.empty()) { printer << sdp; } SockSender::send(printer); diff --git a/src/Rtsp/RtspPusher.h b/src/Rtsp/RtspPusher.h index ccc4c76f..5f521763 100644 --- a/src/Rtsp/RtspPusher.h +++ b/src/Rtsp/RtspPusher.h @@ -31,39 +31,39 @@ class RtspPusher : public TcpClient, public RtspSplitter, public PusherBase { public: typedef std::shared_ptr Ptr; RtspPusher(const EventPoller::Ptr &poller,const RtspMediaSource::Ptr &src); - virtual ~RtspPusher(); - - void publish(const string &strUrl) override; - + ~RtspPusher() override; + void publish(const string &url) override; void teardown() override; void setOnPublished(const Event &cb) override { - _onPublished = cb; + _on_published = cb; } void setOnShutdown(const Event & cb) override{ - _onShutdown = cb; + _on_shutdown = cb; } + protected: //for Tcpclient override - void onRecv(const Buffer::Ptr &pBuf) override; + void onRecv(const Buffer::Ptr &buf) override; void onConnect(const SockException &err) override; void onErr(const SockException &ex) override; //RtspSplitter override void onWholeRtspPacket(Parser &parser) override ; void onRtpPacket(const char *data,uint64_t len) override {}; + private: - void onPublishResult(const SockException &ex, bool handshakeCompleted); + void onPublishResult(const SockException &ex, bool handshake_done); void sendAnnounce(); - void sendSetup(unsigned int uiTrackIndex); + void sendSetup(unsigned int track_idx); void sendRecord(); void sendOptions(); void handleResAnnounce(const Parser &parser); - void handleResSetup(const Parser &parser, unsigned int uiTrackIndex); - bool handleAuthenticationFailure(const string ¶msStr); + void handleResSetup(const Parser &parser, unsigned int track_idx); + bool handleAuthenticationFailure(const string ¶ms_str); inline int getTrackIndexByTrackType(TrackType type); @@ -73,33 +73,30 @@ private: void createUdpSockIfNecessary(int track_idx); void setSocketFlags(); + private: + unsigned int _cseq = 1; + Rtsp::eRtpType _rtp_type = Rtsp::RTP_TCP; + //rtsp鉴权相关 - string _rtspMd5Nonce; - string _rtspRealm; - + string _nonce; + string _realm; + string _url; + string _session_id; + string _content_base; + SdpParser _sdp_parser; + vector _track_vec; + Socket::Ptr _udp_socks[2]; //超时功能实现 - std::shared_ptr _pPublishTimer; - //源 - std::weak_ptr _pMediaSrc; - RtspMediaSource::RingType::RingReader::Ptr _pRtspReader; - //事件监听 - Event _onShutdown; - Event _onPublished; - - string _strUrl; - SdpParser _sdpParser; - vector _aTrackInfo; - string _strSession; - unsigned int _uiCseq = 1; - string _strContentBase; - Rtsp::eRtpType _eType = Rtsp::RTP_TCP; - Socket::Ptr _apUdpSock[2]; - function _onHandshake; + std::shared_ptr _publish_timer; //心跳定时器 - std::shared_ptr _pBeatTimer; - - + std::shared_ptr _beat_timer; + std::weak_ptr _push_src; + RtspMediaSource::RingType::RingReader::Ptr _rtsp_reader; + //事件监听 + Event _on_shutdown; + Event _on_published; + function _on_res_func; }; } /* namespace mediakit */ diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index c0b49472..6f7a6a65 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -252,7 +252,7 @@ void RtspSession::handleReq_RECORD(const Parser &parser){ send_SessionNotFound(); throw SockException(Err_shutdown, _sdp_track.empty() ? "can not find any availabe track when record" : "session not found when record"); } - auto onRes = [this](const string &err,bool enableRtxp,bool enableHls,bool enableMP4){ + auto onRes = [this](const string &err, bool enableHls, bool enableMP4){ bool authSuccess = err.empty(); if(!authSuccess){ sendRtspResponse("401 Unauthorized", {"Content-Type", "text/plain"}, err); @@ -261,7 +261,7 @@ void RtspSession::handleReq_RECORD(const Parser &parser){ } //设置转协议 - _push_src->setProtocolTranslation(enableRtxp, enableHls, enableMP4); + _push_src->setProtocolTranslation(enableHls, enableMP4); _StrPrinter rtp_info; for(auto &track : _sdp_track){ @@ -277,23 +277,23 @@ void RtspSession::handleReq_RECORD(const Parser &parser){ sendRtspResponse("200 OK", {"RTP-Info",rtp_info}); if(_rtp_type == Rtsp::RTP_TCP){ //如果是rtsp推流服务器,并且是TCP推流,那么加大TCP接收缓存,这样能提升接收性能 - _sock->setReadBuffer(std::make_shared(256 * 1024)); + getSock()->setReadBuffer(std::make_shared(256 * 1024)); setSocketFlags(); } }; weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - Broadcast::PublishAuthInvoker invoker = [weakSelf,onRes](const string &err,bool enableRtxp,bool enableHls,bool enableMP4){ + Broadcast::PublishAuthInvoker invoker = [weakSelf, onRes](const string &err, bool enableHls, bool enableMP4) { auto strongSelf = weakSelf.lock(); - if(!strongSelf){ + if (!strongSelf) { return; } - strongSelf->async([weakSelf,onRes,err,enableRtxp,enableHls,enableMP4](){ + strongSelf->async([weakSelf, onRes, err, enableHls, enableMP4]() { auto strongSelf = weakSelf.lock(); - if(!strongSelf){ + if (!strongSelf) { return; } - onRes(err,enableRtxp,enableHls,enableMP4); + onRes(err, enableHls, enableMP4); }); }; @@ -301,10 +301,9 @@ void RtspSession::handleReq_RECORD(const Parser &parser){ auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPublish, _media_info, invoker, static_cast(*this)); if(!flag){ //该事件无人监听,默认不鉴权 - GET_CONFIG(bool,toRtxp,General::kPublishToRtxp); GET_CONFIG(bool,toHls,General::kPublishToHls); GET_CONFIG(bool,toMP4,General::kPublishToMP4); - onRes("",toRtxp,toHls,toMP4); + onRes("",toHls,toMP4); } } @@ -667,10 +666,10 @@ void RtspSession::handleReq_Setup(const Parser &parser) { break; case Rtsp::RTP_UDP: { - std::pair pr; - try{ - pr = makeSockPair(_sock->getPoller(), get_local_ip()); - }catch(std::exception &ex) { + std::pair pr = std::make_pair(createSocket(),createSocket()); + try { + makeSockPair(pr, get_local_ip()); + } catch (std::exception &ex) { //分配端口失败 send_NotAcceptable(); throw SockException(Err_shutdown, ex.what()); @@ -681,8 +680,8 @@ void RtspSession::handleReq_Setup(const Parser &parser) { //设置客户端内网端口信息 string strClientPort = FindField(parser["Transport"].data(), "client_port=", NULL); - uint16_t ui16RtpPort = atoi( FindField(strClientPort.data(), NULL, "-").data()); - uint16_t ui16RtcpPort = atoi( FindField(strClientPort.data(), "-" , NULL).data()); + uint16_t ui16RtpPort = atoi(FindField(strClientPort.data(), NULL, "-").data()); + uint16_t ui16RtcpPort = atoi(FindField(strClientPort.data(), "-", NULL).data()); struct sockaddr_in peerAddr; //设置rtp发送目标地址 @@ -690,14 +689,14 @@ void RtspSession::handleReq_Setup(const Parser &parser) { peerAddr.sin_port = htons(ui16RtpPort); peerAddr.sin_addr.s_addr = inet_addr(get_peer_ip().data()); bzero(&(peerAddr.sin_zero), sizeof peerAddr.sin_zero); - pr.first->setSendPeerAddr((struct sockaddr *)(&peerAddr)); + pr.first->setSendPeerAddr((struct sockaddr *) (&peerAddr)); //设置rtcp发送目标地址 peerAddr.sin_family = AF_INET; peerAddr.sin_port = htons(ui16RtcpPort); peerAddr.sin_addr.s_addr = inet_addr(get_peer_ip().data()); bzero(&(peerAddr.sin_zero), sizeof peerAddr.sin_zero); - pr.second->setSendPeerAddr((struct sockaddr *)(&peerAddr)); + pr.second->setSendPeerAddr((struct sockaddr *) (&peerAddr)); //尝试获取客户端nat映射地址 startListenPeerUdpData(trackIdx); @@ -714,7 +713,7 @@ void RtspSession::handleReq_Setup(const Parser &parser) { break; case Rtsp::RTP_MULTICAST: { if(!_multicaster){ - _multicaster = RtpMultiCaster::get(getPoller(), get_local_ip(), _media_info._vhost, _media_info._app, _media_info._streamid); + _multicaster = RtpMultiCaster::get(*this, get_local_ip(), _media_info._vhost, _media_info._app, _media_info._streamid); if (!_multicaster) { send_NotAcceptable(); throw SockException(Err_shutdown, "can not get a available udp multicast socket"); @@ -728,10 +727,10 @@ void RtspSession::handleReq_Setup(const Parser &parser) { strongSelf->safeShutdown(SockException(Err_shutdown,"ring buffer detached")); }); } - int iSrvPort = _multicaster->getPort(trackRef->_type); + int iSrvPort = _multicaster->getMultiCasterPort(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); + auto pSockRtcp = UDPServer::Instance().getSock(*this, get_local_ip().data(), 2 * trackIdx + 1, iSrvPort + 1); if (!pSockRtcp) { //分配端口失败 send_NotAcceptable(); @@ -742,7 +741,7 @@ void RtspSession::handleReq_Setup(const Parser &parser) { sendRtspResponse("200 OK", {"Transport", StrPrinter << "RTP/AVP;multicast;" - << "destination=" << _multicaster->getIP() << ";" + << "destination=" << _multicaster->getMultiCasterIP() << ";" << "source=" << get_local_ip() << ";" << "port=" << iSrvPort << "-" << pSockRtcp->get_local_port() << ";" << "ttl=" << udpTTL << ";" @@ -1123,7 +1122,7 @@ inline void RtspSession::onSendRtpPacket(const RtpPacket::Ptr &pkt){ //send rtcp every 5 second ticker.resetTime(); //直接保存网络字节序 - memcpy(&counter.timeStamp, pkt->data() + 8, 4); + memcpy(&counter.time_stamp, pkt->data() + 8, 4); sendSenderReport(_rtp_type == Rtsp::RTP_TCP, track_index); } #endif @@ -1230,7 +1229,7 @@ void RtspSession::setSocketFlags(){ GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS); if(mergeWriteMS > 0) { //推流模式下,关闭TCP_NODELAY会增加推流端的延时,但是服务器性能将提高 - SockUtil::setNoDelay(_sock->rawFD(), false); + SockUtil::setNoDelay(getSock()->rawFD(), false); //播放模式下,开启MSG_MORE会增加延时,但是能提高发送性能 setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); } diff --git a/src/Rtsp/UDPServer.cpp b/src/Rtsp/UDPServer.cpp index a31b3815..ce69652e 100644 --- a/src/Rtsp/UDPServer.cpp +++ b/src/Rtsp/UDPServer.cpp @@ -25,72 +25,71 @@ UDPServer::~UDPServer() { InfoL; } -Socket::Ptr UDPServer::getSock(const EventPoller::Ptr &poller,const char* strLocalIp, int intervaled,uint16_t iLocalPort) { - lock_guard lck(_mtxUpdSock); - string strKey = StrPrinter << strLocalIp << ":" << intervaled << endl; - auto it = _mapUpdSock.find(strKey); - if (it == _mapUpdSock.end()) { - Socket::Ptr pSock(new Socket(poller)); - //InfoL<bindUdpSock(iLocalPort, strLocalIp)) { +Socket::Ptr UDPServer::getSock(SocketHelper &helper, const char* local_ip, int interleaved, uint16_t local_port) { + lock_guard lck(_mtx_udp_sock); + string key = StrPrinter << local_ip << ":" << interleaved << endl; + auto it = _udp_sock_map.find(key); + if (it == _udp_sock_map.end()) { + Socket::Ptr sock = helper.createSocket(); + if (!sock->bindUdpSock(local_port, local_ip)) { //分配失败 return nullptr; } - pSock->setOnRead(bind(&UDPServer::onRcvData, this, intervaled, placeholders::_1,placeholders::_2)); - pSock->setOnErr(bind(&UDPServer::onErr, this, strKey, placeholders::_1)); - _mapUpdSock[strKey] = pSock; - DebugL << strLocalIp << " " << pSock->get_local_port() << " " << intervaled; - return pSock; + sock->setOnErr(bind(&UDPServer::onErr, this, key, placeholders::_1)); + sock->setOnRead(bind(&UDPServer::onRecv, this, interleaved, placeholders::_1, placeholders::_2)); + _udp_sock_map[key] = sock; + DebugL << local_ip << " " << sock->get_local_port() << " " << interleaved; + return sock; } return it->second; } -void UDPServer::listenPeer(const char* strPeerIp, void* pSelf, const onRecvData& cb) { - lock_guard lck(_mtxDataHandler); - auto &mapRef = _mapDataHandler[strPeerIp]; - mapRef.emplace(pSelf, cb); +void UDPServer::listenPeer(const char* peer_ip, void* obj, const onRecvData &cb) { + lock_guard lck(_mtx_on_recv); + auto &ref = _on_recv_map[peer_ip]; + ref.emplace(obj, cb); } -void UDPServer::stopListenPeer(const char* strPeerIp, void* pSelf) { - lock_guard lck(_mtxDataHandler); - auto it0 = _mapDataHandler.find(strPeerIp); - if (it0 == _mapDataHandler.end()) { +void UDPServer::stopListenPeer(const char* peer_ip, void* obj) { + lock_guard lck(_mtx_on_recv); + auto it0 = _on_recv_map.find(peer_ip); + if (it0 == _on_recv_map.end()) { return; } - auto &mapRef = it0->second; - auto it1 = mapRef.find(pSelf); - if (it1 != mapRef.end()) { - mapRef.erase(it1); + auto &ref = it0->second; + auto it1 = ref.find(obj); + if (it1 != ref.end()) { + ref.erase(it1); } - if (mapRef.size() == 0) { - _mapDataHandler.erase(it0); + if (ref.size() == 0) { + _on_recv_map.erase(it0); } - } -void UDPServer::onErr(const string& strKey, const SockException& err) { + +void UDPServer::onErr(const string &key, const SockException &err) { WarnL << err.what(); - lock_guard lck(_mtxUpdSock); - _mapUpdSock.erase(strKey); + lock_guard lck(_mtx_udp_sock); + _udp_sock_map.erase(key); } -void UDPServer::onRcvData(int intervaled, const Buffer::Ptr &pBuf, struct sockaddr* pPeerAddr) { - //TraceL << trackIndex; - struct sockaddr_in *in = (struct sockaddr_in *) pPeerAddr; - string peerIp = SockUtil::inet_ntoa(in->sin_addr); - lock_guard lck(_mtxDataHandler); - auto it0 = _mapDataHandler.find(peerIp); - if (it0 == _mapDataHandler.end()) { + +void UDPServer::onRecv(int interleaved, const Buffer::Ptr &buf, struct sockaddr* peer_addr) { + struct sockaddr_in *in = (struct sockaddr_in *) peer_addr; + string peer_ip = SockUtil::inet_ntoa(in->sin_addr); + lock_guard lck(_mtx_on_recv); + auto it0 = _on_recv_map.find(peer_ip); + if (it0 == _on_recv_map.end()) { return; } - auto &mapRef = it0->second; - for (auto it1 = mapRef.begin(); it1 != mapRef.end(); ++it1) { - onRecvData &funRef = it1->second; - if (!funRef(intervaled, pBuf, pPeerAddr)) { - it1 = mapRef.erase(it1); + auto &ref = it0->second; + for (auto it1 = ref.begin(); it1 != ref.end(); ++it1) { + auto &func = it1->second; + if (!func(interleaved, buf, peer_addr)) { + it1 = ref.erase(it1); } } - if (mapRef.size() == 0) { - _mapDataHandler.erase(it0); + if (ref.size() == 0) { + _on_recv_map.erase(it0); } } diff --git a/src/Rtsp/UDPServer.h b/src/Rtsp/UDPServer.h index d7c0e487..137eb576 100644 --- a/src/Rtsp/UDPServer.h +++ b/src/Rtsp/UDPServer.h @@ -30,18 +30,20 @@ public: typedef function< bool(int intervaled, const Buffer::Ptr &buffer, struct sockaddr *peer_addr)> onRecvData; ~UDPServer(); static UDPServer &Instance(); - Socket::Ptr getSock(const EventPoller::Ptr &poller,const char *strLocalIp, int intervaled,uint16_t iLocalPort = 0); - void listenPeer(const char *strPeerIp, void *pSelf, const onRecvData &cb); - void stopListenPeer(const char *strPeerIp, void *pSelf); + Socket::Ptr getSock(SocketHelper &helper, const char *local_ip, int interleaved, uint16_t local_port = 0); + void listenPeer(const char *peer_ip, void *obj, const onRecvData &cb); + void stopListenPeer(const char *peer_ip, void *obj); + private: UDPServer(); - void onRcvData(int intervaled, const Buffer::Ptr &pBuf,struct sockaddr *pPeerAddr); + void onRecv(int interleaved, const Buffer::Ptr &buf, struct sockaddr *peer_addr); void onErr(const string &strKey,const SockException &err); - unordered_map _mapUpdSock; - mutex _mtxUpdSock; - unordered_map > _mapDataHandler; - mutex _mtxDataHandler; +private: + mutex _mtx_udp_sock; + mutex _mtx_on_recv; + unordered_map _udp_sock_map; + unordered_map > _on_recv_map; }; } /* namespace mediakit */ diff --git a/src/TS/TSMediaSource.h b/src/TS/TSMediaSource.h new file mode 100644 index 00000000..c7b05f53 --- /dev/null +++ b/src/TS/TSMediaSource.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_TSMEDIASOURCE_H +#define ZLMEDIAKIT_TSMEDIASOURCE_H + +#include "Common/MediaSource.h" +using namespace toolkit; +#define TS_GOP_SIZE 512 + +namespace mediakit { + +//TS直播数据包 +class TSPacket : public BufferRaw{ +public: + using Ptr = std::shared_ptr; + + template + TSPacket(ARGS && ...args) : BufferRaw(std::forward(args)...) {}; + ~TSPacket() override = default; + +public: + uint32_t time_stamp = 0; +}; + +//TS直播合并写策略类 +class TSFlushPolicy : public FlushPolicy{ +public: + TSFlushPolicy() = default; + ~TSFlushPolicy() = default; + + uint32_t getStamp(const TSPacket::Ptr &packet) { + return packet->time_stamp; + } +}; + +//TS直播源 +class TSMediaSource : public MediaSource, public RingDelegate, public PacketCache{ +public: + using PoolType = ResourcePool; + using Ptr = std::shared_ptr; + using RingDataType = std::shared_ptr >; + using RingType = RingBuffer; + + TSMediaSource(const string &vhost, + const string &app, + const string &stream_id, + int ring_size = TS_GOP_SIZE) : MediaSource(TS_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {} + + ~TSMediaSource() override = default; + + /** + * 获取媒体源的环形缓冲 + */ + const RingType::Ptr &getRing() const { + return _ring; + } + + /** + * 获取播放器个数 + */ + int readerCount() override { + return _ring ? _ring->readerCount() : 0; + } + + /** + * 输入TS包 + * @param packet TS包 + * @param key 是否为关键帧第一个包 + */ + void onWrite(const TSPacket::Ptr &packet, bool key) override { + if (!_ring) { + createRing(); + } + if (key) { + _have_video = true; + } + PacketCache::inputPacket(true, packet, key); + } + + /** + * 情况GOP缓存 + */ + void clearCache() override { + PacketCache::clearCache(); + _ring->clearCache(); + } + +private: + void createRing(){ + weak_ptr weak_self = dynamic_pointer_cast(shared_from_this()); + _ring = std::make_shared(_ring_size, [weak_self](int size) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + strong_self->onReaderChanged(size); + }); + onReaderChanged(0); + //注册媒体源 + regist(); + } + + /** + * 合并写回调 + * @param packet_list 合并写缓存列队 + * @param key_pos 是否包含关键帧 + */ + void onFlush(std::shared_ptr > &packet_list, bool key_pos) override { + //如果不存在视频,那么就没有存在GOP缓存的意义,所以确保一直清空GOP缓存 + _ring->write(packet_list, _have_video ? key_pos : true); + } + +private: + bool _have_video = false; + int _ring_size; + RingType::Ptr _ring; +}; + + +}//namespace mediakit +#endif //ZLMEDIAKIT_TSMEDIASOURCE_H diff --git a/src/TS/TSMediaSourceMuxer.h b/src/TS/TSMediaSourceMuxer.h new file mode 100644 index 00000000..553e18f4 --- /dev/null +++ b/src/TS/TSMediaSourceMuxer.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_TSMEDIASOURCEMUXER_H +#define ZLMEDIAKIT_TSMEDIASOURCEMUXER_H + +#include "TSMediaSource.h" +#include "Record/TsMuxer.h" + +namespace mediakit { + +class TSMediaSourceMuxer : public TsMuxer, public MediaSourceEventInterceptor, + public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + + TSMediaSourceMuxer(const string &vhost, + const string &app, + const string &stream_id) { + _media_src = std::make_shared(vhost, app, stream_id); + _pool.setSize(256); + } + + ~TSMediaSourceMuxer() override = default; + + void setListener(const std::weak_ptr &listener){ + _listener = listener; + _media_src->setListener(shared_from_this()); + } + + int readerCount() const{ + return _media_src->readerCount(); + } + + void onReaderChanged(MediaSource &sender, int size) override { + _enabled = size; + if (!size) { + _clear_cache = true; + } + MediaSourceEventInterceptor::onReaderChanged(sender, size); + } + + void inputFrame(const Frame::Ptr &frame) override { + if (_clear_cache) { + _clear_cache = false; + _media_src->clearCache(); + } + if (_enabled) { + TsMuxer::inputFrame(frame); + } + } + + bool isEnabled() { + //缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 + return _clear_cache ? true : _enabled; + } + +protected: + void onTs(const void *data, int len,uint32_t timestamp,bool is_idr_fast_packet) override{ + if(!data || !len){ + return; + } + TSPacket::Ptr packet = _pool.obtain(); + packet->assign((char *) data, len); + packet->time_stamp = timestamp; + _media_src->onWrite(packet, is_idr_fast_packet); + } + +private: + bool _enabled = true; + bool _clear_cache = false; + TSMediaSource::PoolType _pool; + TSMediaSource::Ptr _media_src; +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_TSMEDIASOURCEMUXER_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4458b8d1..64ec28a1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,8 +14,10 @@ find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_check_modules(AVUTIL QUIET IMPORTED_TARGET libavutil) if(AVUTIL_FOUND) - list(APPEND LINK_LIB_LIST PkgConfig::AVUTIL) - message(STATUS "found library:${AVUTIL_LIBRARY}") + include_directories(${AVUTIL_INCLUDE_DIRS}) + link_directories(${AVUTIL_LIBRARY_DIRS}) + list(APPEND LINK_LIB_LIST ${AVUTIL_LIBRARIES}) + message(STATUS "found library:${AVUTIL_LIBRARIES}") endif() else() find_package(AVUTIL QUIET) @@ -30,8 +32,10 @@ endif() if(PKG_CONFIG_FOUND) pkg_check_modules(AVCODEC QUIET IMPORTED_TARGET libavcodec) if(AVCODEC_FOUND) - list(APPEND LINK_LIB_LIST PkgConfig::AVCODEC) - message(STATUS "found library:${AVCODEC_LIBRARY}") + include_directories(${AVCODEC_INCLUDE_DIRS}) + link_directories(${AVCODEC_LIBRARY_DIRS}) + list(APPEND LINK_LIB_LIST ${AVCODEC_LIBRARIES}) + message(STATUS "found library:${AVCODEC_LIBRARIES}") endif() else() find_package(AVCODEC QUIET) @@ -58,7 +62,6 @@ foreach(TEST_SRC ${TEST_SRC_LIST}) if(WIN32) set_target_properties(${TEST_EXE_NAME} PROPERTIES COMPILE_FLAGS ${VS_FALGS} ) endif(WIN32) - target_link_libraries(${TEST_EXE_NAME} ${LINK_LIB_LIST}) endforeach() diff --git a/tests/test_player.cpp b/tests/test_player.cpp index 5417030d..f6ff3e78 100644 --- a/tests/test_player.cpp +++ b/tests/test_player.cpp @@ -109,7 +109,7 @@ int main(int argc, char *argv[]) { return; } - auto viedoTrack = strongPlayer->getTrack(TrackVideo); + auto viedoTrack = strongPlayer->getTrack(TrackVideo, false); if (!viedoTrack) { WarnL << "没有视频!"; return; diff --git a/tests/test_pusher.cpp b/tests/test_pusher.cpp index d64c84ad..fe7f1b0b 100644 --- a/tests/test_pusher.cpp +++ b/tests/test_pusher.cpp @@ -75,14 +75,7 @@ int domain(const string &playUrl, const string &pushUrl) { //拉一个流,生成一个RtmpMediaSource,源的名称是"app/stream" //你也可以以其他方式生成RtmpMediaSource,比如说MP4文件(请查看test_rtmpPusherMp4.cpp代码) MediaInfo info(pushUrl); - bool enable_rtsp = true; - bool enable_rtmp = true; - if(info._schema == RTSP_SCHEMA){ - enable_rtmp = false; - }else if(info._schema == RTMP_SCHEMA){ - enable_rtsp = false; - } - PlayerProxy::Ptr player(new PlayerProxy(DEFAULT_VHOST, "app", "stream",enable_rtsp,enable_rtmp,false,false,-1 , poller)); + PlayerProxy::Ptr player(new PlayerProxy(DEFAULT_VHOST, "app", "stream",false,false,-1 , poller)); //可以指定rtsp拉流方式,支持tcp和udp方式,默认tcp // (*player)[Client::kRtpType] = Rtsp::RTP_UDP; player->play(playUrl.data()); diff --git a/tests/test_server.cpp b/tests/test_server.cpp index aa4b8780..4e37b447 100644 --- a/tests/test_server.cpp +++ b/tests/test_server.cpp @@ -141,7 +141,7 @@ void initEventListener() { NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastMediaPublish, [](BroadcastMediaPublishArgs) { DebugL << "推流鉴权:" << args._schema << " " << args._vhost << " " << args._app << " " << args._streamid << " " << args._param_strs; - invoker("", true, true, false);//鉴权成功 + invoker("", true, false);//鉴权成功 //invoker("this is auth failed message");//鉴权失败 });