Commit bc1d31e1 by xiongguangjie Committed by GitHub

Push force replace (#2899)

Co-authored-by: Alex <liyu7352@gmail.com>
Co-authored-by: xia-chu <771730766@qq.com>
Co-authored-by: Johnny <hellojinqiang@gmail.com>
Co-authored-by: BackT0TheFuture <10088733+BackT0TheFuture@users.noreply.github.com>
Co-authored-by: ljx0305 <ljx0305@gmail.com>
Co-authored-by: Per-Arne Andersen <per@sysx.no>
Co-authored-by: codeRATny <60806889+codeRATny@users.noreply.github.com>
Co-authored-by: 老衲不出家 <tannzh2018@outlook.com>
Co-authored-by: Kiki <haijuanchen.sun@gmail.com>
Co-authored-by: PioLing <964472638@qq.com>
Co-authored-by: dengjfzh <76604422+dengjfzh@users.noreply.github.com>
Co-authored-by: a-ucontrol <55526028+a-ucontrol@users.noreply.github.com>
Co-authored-by: 百鸣 <94030128+ixingqiao@users.noreply.github.com>
Co-authored-by: fruit Juice <2317232721@qq.com>
Co-authored-by: Luosh <fjlshyyyy@qq.com>
Co-authored-by: tbago <moonzalor@gmail.com>
Co-authored-by: Talus <xiaoxiaochenjian@gmail.com>
Co-authored-by: 朱如洪 <zhu410289616@163.com>
Co-authored-by: pedoc <pedoc@qq.com>
Co-authored-by: XiaoYan Lin <linxiaoyan87@foxmail.com>
Co-authored-by: xiangshengjye <46069012+xiangshengjye@users.noreply.github.com>
Co-authored-by: tjpgt <602950305@qq.com>
Co-authored-by: Nick <joyouswind@gmail.com>
Co-authored-by: yogo-zhangyingzhe <100331270+yogo-zhangyingzhe@users.noreply.github.com>
Co-authored-by: Xiaofeng Wang <wasphin@gmail.com>
Co-authored-by: Dw9 <xweimvp@gmail.com>
Co-authored-by: waken <33921191+mc373906408@users.noreply.github.com>
Co-authored-by: Deepslient <1154547394@qq.com>
parent b8729dc2
name: Docker
on: [push, pull_request]
on:
push:
branches:
- "master"
- "feature/*"
- "release/*"
pull_request:
branches:
- "master"
- "feature/*"
- "release/*"
env:
# Use docker.io for Docker Hub if empty
......
......@@ -24,7 +24,11 @@
##############################################################################
# jsoncpp
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/jsoncpp/src/lib_json JSONCPP_SRC_LIST)
file(GLOB JSONCPP_SRC_LIST
${CMAKE_CURRENT_SOURCE_DIR}/jsoncpp/include/json/*.h
${CMAKE_CURRENT_SOURCE_DIR}/jsoncpp/src/lib_json/*.cpp
${CMAKE_CURRENT_SOURCE_DIR}/jsoncpp/src/lib_json/*.h)
add_library(jsoncpp STATIC ${JSONCPP_SRC_LIST})
target_compile_options(jsoncpp
PRIVATE ${COMPILE_OPTIONS_DEFAULT})
......@@ -43,44 +47,44 @@ set(MediaServer_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/media-server")
# TODO: 补一个函数处理各种库
# 添加 mov、flv 库用于 MP4 录制
if(ENABLE_MP4)
message(STATUS "ENABLE_MP4 defined")
if (ENABLE_MP4 OR ENABLE_HLS_FMP4)
# MOV
set(MediaServer_MOV_ROOT ${MediaServer_ROOT}/libmov)
aux_source_directory(${MediaServer_MOV_ROOT}/include MOV_SRC_LIST)
aux_source_directory(${MediaServer_MOV_ROOT}/source MOV_SRC_LIST)
aux_source_directory(${MediaServer_MOV_ROOT}/source MOV_SRC_LIST)
add_library(mov STATIC ${MOV_SRC_LIST})
add_library(MediaServer::mov ALIAS mov)
target_compile_definitions(mov
PUBLIC -DENABLE_MP4)
target_compile_options(mov
PRIVATE ${COMPILE_OPTIONS_DEFAULT})
target_compile_options(mov PRIVATE ${COMPILE_OPTIONS_DEFAULT})
target_include_directories(mov
PRIVATE
"$<BUILD_INTERFACE:${MediaServer_MOV_ROOT}/include>"
PUBLIC
"$<BUILD_INTERFACE:${MediaServer_MOV_ROOT}/include>")
PRIVATE
"$<BUILD_INTERFACE:${MediaServer_MOV_ROOT}/include>"
PUBLIC
"$<BUILD_INTERFACE:${MediaServer_MOV_ROOT}/include>")
# FLV
set(MediaServer_FLV_ROOT ${MediaServer_ROOT}/libflv)
aux_source_directory(${MediaServer_FLV_ROOT}/include FLV_SRC_LIST)
aux_source_directory(${MediaServer_FLV_ROOT}/source FLV_SRC_LIST)
aux_source_directory(${MediaServer_FLV_ROOT}/source FLV_SRC_LIST)
add_library(flv STATIC ${FLV_SRC_LIST})
add_library(MediaServer::flv ALIAS flv)
target_compile_options(flv
PRIVATE ${COMPILE_OPTIONS_DEFAULT})
target_compile_options(flv PRIVATE ${COMPILE_OPTIONS_DEFAULT})
target_include_directories(flv
PRIVATE
"$<BUILD_INTERFACE:${MediaServer_FLV_ROOT}/include>"
PUBLIC
"$<BUILD_INTERFACE:${MediaServer_FLV_ROOT}/include>")
update_cached_list(MK_LINK_LIBRARIES
MediaServer::flv MediaServer::mov)
update_cached_list(MK_COMPILE_DEFINITIONS
ENABLE_MP4)
endif()
PRIVATE
"$<BUILD_INTERFACE:${MediaServer_FLV_ROOT}/include>"
PUBLIC
"$<BUILD_INTERFACE:${MediaServer_FLV_ROOT}/include>")
update_cached_list(MK_LINK_LIBRARIES MediaServer::flv MediaServer::mov)
if (ENABLE_MP4)
message(STATUS "ENABLE_MP4 defined")
update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_MP4)
endif ()
if (ENABLE_HLS_FMP4)
message(STATUS "ENABLE_HLS_FMP4 defined")
update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_HLS_FMP4)
endif ()
endif ()
# 添加 mpeg 用于支持 ts 生成
if(ENABLE_RTPPROXY OR ENABLE_HLS)
......@@ -104,9 +108,11 @@ if(ENABLE_RTPPROXY OR ENABLE_HLS)
update_cached_list(MK_LINK_LIBRARIES MediaServer::mpeg)
if(ENABLE_RTPPROXY)
message(STATUS "ENABLE_RTPPROXY defined")
update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_RTPPROXY)
endif()
if(ENABLE_HLS)
message(STATUS "ENABLE_HLS defined")
update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_HLS)
endif()
endif()
......
ZLToolKit @ b11582c3
Subproject commit 7e40c751659d5c1ec623699732284c12e0a4feb8
Subproject commit b11582c38e8dbbb8d93ca9ce33c9a0b0cd58f59a
......@@ -70,4 +70,18 @@ WuPeng <wp@zafu.edu.cn>
[ahaooahaz](https://github.com/AHAOAHA)
[TempoTian](https://github.com/TempoTian)
[Derek Liu](https://github.com/yjkhtddx)
[ljx0305](https://github.com/ljx0305)
\ No newline at end of file
[ljx0305](https://github.com/ljx0305)
[朱如洪 ](https://github.com/zhu410289616)
[lijin](https://github.com/1461521844lijin)
[PioLing](https://github.com/PioLing)
[BackT0TheFuture](https://github.com/BackT0TheFuture)
[perara](https://github.com/perara)
[codeRATny](https://github.com/codeRATny)
[dengjfzh](https://github.com/dengjfzh)
[百鸣](https://github.com/ixingqiao)
[fruit Juice](https://github.com/xuandu)
[tbago](https://github.com/tbago)
[Luosh](https://github.com/Luosh)
[linxiaoyan87](https://github.com/linxiaoyan)
[waken](https://github.com/mc373906408)
[Deepslient](https://github.com/Deepslient)
\ No newline at end of file
......@@ -39,8 +39,10 @@ option(ENABLE_FAAC "Enable FAAC" OFF)
option(ENABLE_FFMPEG "Enable FFmpeg" OFF)
option(ENABLE_HLS "Enable HLS" ON)
option(ENABLE_JEMALLOC_STATIC "Enable static linking to the jemalloc library" OFF)
option(ENABLE_JEMALLOC_DUMP "Enable jemalloc to dump malloc statistics" OFF)
option(ENABLE_MEM_DEBUG "Enable Memory Debug" OFF)
option(ENABLE_MP4 "Enable MP4" ON)
option(ENABLE_HLS_FMP4 "Enable HLS-FMP4" ON)
option(ENABLE_MSVC_MT "Enable MSVC Mt/Mtd lib" ON)
option(ENABLE_MYSQL "Enable MySQL" OFF)
option(ENABLE_OPENSSL "Enable OpenSSL" ON)
......@@ -200,8 +202,8 @@ if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
endif()
# mediakit 以及各个 runtime 依赖
update_cached_list(MK_LINK_LIBRARIES "")
update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_VERSION)
update_cached(MK_LINK_LIBRARIES "")
update_cached(MK_COMPILE_DEFINITIONS ENABLE_VERSION)
if (DISABLE_REPORT)
update_cached_list(MK_COMPILE_DEFINITIONS DISABLE_REPORT)
......@@ -334,7 +336,11 @@ if(ENABLE_JEMALLOC_STATIC)
if(NOT EXISTS ${DEP_ROOT_DIR})
file(MAKE_DIRECTORY ${DEP_ROOT_DIR})
endif()
if (ENABLE_JEMALLOC_DUMP)
set(ENABLE_JEMALLOC_STAT ON)
else ()
set(ENABLE_JEMALLOC_STAT OFF)
endif ()
include(Jemalloc)
include_directories(SYSTEM ${DEP_ROOT_DIR}/${JEMALLOC_NAME}/include/jemalloc)
link_directories(${DEP_ROOT_DIR}/${JEMALLOC_NAME}/lib)
......@@ -348,6 +354,12 @@ if(JEMALLOC_FOUND)
message(STATUS "found library: ${JEMALLOC_LIBRARIES}")
include_directories(${JEMALLOC_INCLUDE_DIR})
update_cached_list(MK_LINK_LIBRARIES ${JEMALLOC_LIBRARIES})
add_definitions(-DUSE_JEMALLOC)
message(STATUS "jemalloc will be used to avoid memory fragmentation")
if (ENABLE_JEMALLOC_DUMP)
add_definitions(-DENABLE_JEMALLOC_DUMP)
message(STATUS "jemalloc will save memory usage statistics when the program exits")
endif ()
endif()
# 查找 openssl 是否安装
......@@ -449,11 +461,6 @@ if(ENABLE_API)
add_subdirectory(api)
endif()
# IOS 不编译可执行程序
if(IOS)
return()
endif()
##############################################################################
if(ENABLE_PLAYER AND ENABLE_FFMPEG)
......@@ -461,13 +468,20 @@ if(ENABLE_PLAYER AND ENABLE_FFMPEG)
endif()
#MediaServer主程序
add_subdirectory(server)
if(ENABLE_SERVER)
add_subdirectory(server)
endif()
# Android 会 add_subdirectory 并依赖该变量
if(ENABLE_SERVER_LIB)
if(ENABLE_SERVER_LIB AND NOT CMAKE_PARENT_LIST_FILE STREQUAL CMAKE_CURRENT_LIST_FILE)
set(MK_LINK_LIBRARIES ${MK_LINK_LIBRARIES} PARENT_SCOPE)
endif()
# IOS 不编译可执行程序
if(IOS)
return()
endif()
#cpp测试demo程序
if (ENABLE_TESTS)
add_subdirectory(tests)
......
![logo](https://raw.githubusercontent.com/ZLMediaKit/ZLMediaKit/master/www/logo.png)
简体中文 | [English](./README_en.md)
# 一个基于C++11的高性能运营级流媒体服务框架
[![](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/ZLMediaKit/ZLMediaKit/blob/master/LICENSE)
......@@ -44,7 +46,7 @@
## 功能清单
### 功能一览
<img width="800" alt="功能一览" src="https://user-images.githubusercontent.com/11495632/190864440-91c45f8f-480f-43db-8110-5bb44e6300ff.png">
<img width="800" alt="功能一览" src="https://github.com/ZLMediaKit/ZLMediaKit/assets/11495632/481ea769-5b27-495e-bf7d-31191e6af9d2">
- RTSP[S]
- RTSP[S] 服务器,支持RTMP/MP4/HLS转RTSP[S],支持亚马逊echo show这样的设备
......@@ -61,14 +63,16 @@
- RTMP[S] 发布服务器,支持录制发布流
- RTMP[S] 播放器,支持RTMP代理,支持生成静音音频
- RTMP[S] 推流客户端
- 支持http[s]-flv直播
- 支持http[s]-flv直播服务器
- 支持http[s]-flv直播播放器
- 支持websocket-flv直播
- 支持H264/H265/AAC/G711/OPUS编码,其他编码能转发但不能转协议
- 支持[RTMP-H265](https://github.com/ksvc/FFmpeg/wiki)
- 支持[RTMP-OPUS](https://github.com/ZLMediaKit/ZLMediaKit/wiki/RTMP%E5%AF%B9H265%E5%92%8COPUS%E7%9A%84%E6%94%AF%E6%8C%81)
- 支持[enhanced-rtmp(H265)](https://github.com/veovera/enhanced-rtmp)
- HLS
- 支持HLS文件生成,自带HTTP文件服务器
- 支持HLS文件(mpegts/fmp4)生成,自带HTTP文件服务器
- 通过cookie追踪技术,可以模拟HLS播放为长连接,可以实现HLS按需拉流、播放统计等业务
- 支持HLS播发器,支持拉流HLS转rtsp/rtmp/mp4
- 支持H264/H265/AAC/G711/OPUS编码
......@@ -99,6 +103,7 @@
- 支持es/ps/ts/ehome rtp推流
- 支持es/ps rtp转推
- 支持GB28181主动拉流模式
- 支持双向语音对讲
- MP4点播与录制
- 支持录制为FLV/HLS/MP4
......@@ -119,6 +124,7 @@
- 支持datachannel
- 支持webrtc over tcp模式
- 优秀的nack、jitter buffer算法, 抗丢包能力卓越
- 支持whip/whep协议
- [SRT支持](./srt/srt.md)
- 其他
- 支持丰富的restful api以及web hook事件
......@@ -163,30 +169,36 @@ bash build_docker_images.sh
```
## 合作项目
- 视频管理平台
- [wvp-GB28181-pro](https://github.com/648540858/wvp-GB28181-pro) java实现的开箱即用的GB28181协议视频平台
- [AKStream](https://github.com/chatop2020/AKStream) c#实现的全功能的软NVR接口/GB28181平台
- [BXC_SipServer](https://github.com/any12345com/BXC_SipServer) c++实现的国标GB28181流媒体信令服务器
- [gosip](https://github.com/panjjo/gosip) golang实现的GB28181服务器
- [FreeEhome](https://github.com/tsingeye/FreeEhome) golang实现的海康ehome服务器
- 播放器
- [h265web.js](https://github.com/numberwolf/h265web.js) 基于wasm支持H265的播放器,支持本项目多种专属协议
- [jessibuca](https://github.com/langhuihui/jessibuca) 基于wasm支持H265的播放器
- [wsPlayer](https://github.com/v354412101/wsPlayer) 基于MSE的websocket-fmp4播放器
- [BXC_gb28181Player](https://github.com/any12345com/BXC_gb28181Player) C++开发的支持国标GB28181协议的视频流播放器
- 可视化管理网站
- [最新的前后端分离web项目,支持webrtc播放](https://github.com/langmansh/AKStreamNVR)
- [基于ZLMediaKit主线的管理WEB网站](https://gitee.com/kkkkk5G/MediaServerUI)
- [基于ZLMediaKit分支的管理WEB网站](https://github.com/chenxiaolei/ZLMediaKit_NVR_UI)
- [一个非常漂亮的可视化后台管理系统](https://github.com/MingZhuLiu/ZLMediaServerManagent)
- 流媒体管理平台
- [GB28181完整解决方案,自带web管理网站,支持webrtc、h265播放](https://github.com/648540858/wvp-GB28181-pro)
- [功能强大的流媒体控制管理接口平台,支持GB28181](https://github.com/chatop2020/AKStream)
- [Go实现的GB28181服务器](https://github.com/panjjo/gosip)
- [node-js版本的GB28181平台](https://gitee.com/hfwudao/GB28181_Node_Http)
- [Go实现的海康ehome服务器](https://github.com/tsingeye/FreeEhome)
- 客户端
- [c sdk完整c#包装库](https://github.com/malegend/ZLMediaKit.Autogen)
- WEB管理网站
- [AKStreamNVR](https://github.com/langmansh/AKStreamNVR) 前后端分离web项目,支持webrtc播放
- SDK
- [c# sdk](https://github.com/malegend/ZLMediaKit.Autogen) 本项目c sdk完整c#包装库
- [metaRTC](https://github.com/metartc/metaRTC) 全国产纯c webrtc sdk
- 其他项目(已停止更新)
- [NodeJS实现的GB28181平台](https://gitee.com/hfwudao/GB28181_Node_Http)
- [基于ZLMediaKit主线的管理WEB网站 ](https://gitee.com/kkkkk5G/MediaServerUI)
- [基于ZLMediaKit分支的管理WEB网站](https://github.com/chenxiaolei/ZLMediaKit_NVR_UI)
- [一个非常漂亮的可视化后台管理系统](https://github.com/MingZhuLiu/ZLMediaServerManagent)
- [基于C SDK实现的推流客户端](https://github.com/hctym1995/ZLM_ApiDemo)
- [C#版本的Http API与Hook](https://github.com/chengxiaosheng/ZLMediaKit.HttpApi)
- [DotNetCore的RESTful客户端](https://github.com/MingZhuLiu/ZLMediaKit.DotNetCore.Sdk)
- 播放器
- [基于wasm支持H265的播放器](https://github.com/numberwolf/h265web.js)
- [基于MSE的websocket-fmp4播放器](https://github.com/v354412101/wsPlayer)
- [全国产webrtc sdk(metaRTC)](https://github.com/metartc/metaRTC)
## 授权协议
......@@ -198,9 +210,12 @@ bash build_docker_images.sh
## 联系方式
- 邮箱:<1213642868@qq.com>(本项目相关或流媒体相关问题请走issue流程,否则恕不邮件答复)
- QQ群:两个qq群已满员(共4000人),后续将不再新建qq群,用户可加入[知识星球](https://github.com/ZLMediaKit/ZLMediaKit/issues/2364)提问以支持本项目。
- 关注微信公众号:
- 请关注微信公众号获取最新消息推送:
<img src=https://user-images.githubusercontent.com/11495632/232451702-4c50bc72-84d8-4c94-af2b-57290088ba7a.png width=15% />
- 也可以自愿有偿加入知识星球咨询和获取资料:
<img src= https://user-images.githubusercontent.com/11495632/231946329-aa8517b0-3cf5-49cf-8c75-a93ed58cb9d2.png width=30% />
## 怎么提问?
......@@ -208,9 +223,7 @@ bash build_docker_images.sh
- 1、仔细看下readme、wiki,如果有必要可以查看下issue.
- 2、如果您的问题还没解决,可以提issue.
- 3、有些问题,如果不具备参考性的,无需在issue提的,可以在qq群提.
- 4、QQ私聊一般不接受无偿技术咨询和支持([为什么不提倡QQ私聊](https://github.com/ZLMediaKit/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)).
- 5、如果需要获取更及时贴心的技术支持,可以有偿加入[知识星球](https://github.com/ZLMediaKit/ZLMediaKit/issues/2364).
- 3、如果需要获取更及时贴心的技术支持,可以有偿加入[知识星球](https://github.com/ZLMediaKit/ZLMediaKit/issues/2364).
## 特别感谢
......@@ -302,6 +315,24 @@ bash build_docker_images.sh
[TempoTian](https://github.com/TempoTian)
[Derek Liu](https://github.com/yjkhtddx)
[ljx0305](https://github.com/ljx0305)
[朱如洪 ](https://github.com/zhu410289616)
[lijin](https://github.com/1461521844lijin)
[PioLing](https://github.com/PioLing)
[BackT0TheFuture](https://github.com/BackT0TheFuture)
[perara](https://github.com/perara)
[codeRATny](https://github.com/codeRATny)
[dengjfzh](https://github.com/dengjfzh)
[百鸣](https://github.com/ixingqiao)
[fruit Juice](https://github.com/xuandu)
[tbago](https://github.com/tbago)
[Luosh](https://github.com/Luosh)
[linxiaoyan87](https://github.com/linxiaoyan)
[waken](https://github.com/mc373906408)
[Deepslient](https://github.com/Deepslient)
同时感谢JetBrains对开源项目的支持,本项目使用CLion开发与调试:
[![JetBrains](https://resources.jetbrains.com/storage/products/company/brand/logos/CLion.svg)](https://jb.gg/OpenSourceSupport)
## 使用案例
......
......@@ -30,13 +30,6 @@ file(GLOB API_SRC_LIST
set(LINK_LIBRARIES ${MK_LINK_LIBRARIES})
if(IOS)
add_library(mk_api STATIC ${API_SRC_LIST})
target_link_libraries(mk_api
PRIVATE ${LINK_LIBRARIES})
return()
endif ()
set(COMPILE_DEFINITIONS ${MK_COMPILE_DEFINITIONS})
if (MSVC)
......@@ -46,6 +39,8 @@ endif ()
if(ENABLE_API_STATIC_LIB)
add_library(mk_api STATIC ${API_SRC_LIST})
list(APPEND COMPILE_DEFINITIONS MediaKitApi_STATIC)
elseif(IOS)
add_library(mk_api STATIC ${API_SRC_LIST})
else()
add_library(mk_api SHARED ${API_SRC_LIST})
endif()
......@@ -74,8 +69,6 @@ generate_export_header(mk_api
STATIC_DEFINE MediaKitApi_STATIC
EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/mk_export.h")
add_subdirectory(tests)
file(GLOB API_HEADER_LIST include/*.h ${CMAKE_CURRENT_BINARY_DIR}/*.h)
install(FILES ${API_HEADER_LIST}
DESTINATION ${INSTALL_PATH_INCLUDE})
......@@ -83,3 +76,12 @@ install(TARGETS mk_api
ARCHIVE DESTINATION ${INSTALL_PATH_LIB}
LIBRARY DESTINATION ${INSTALL_PATH_LIB}
RUNTIME DESTINATION ${INSTALL_PATH_RUNTIME})
# IOS 跳过测试代码
if(IOS)
return()
endif()
if (ENABLE_TESTS)
add_subdirectory(tests)
endif()
......@@ -166,6 +166,17 @@ typedef struct {
*/
void (API_CALL *on_mk_log)(int level, const char *file, int line, const char *function, const char *message);
/**
* 发送rtp流失败回调,适用于mk_media_source_start_send_rtp/mk_media_start_send_rtp接口触发的rtp发送
* @param vhost 虚拟主机
* @param app 应用名
* @param stream 流id
* @param ssrc ssrc的10进制打印,通过atoi转换为整型
* @param err 错误代码
* @param msg 错误提示
*/
void(API_CALL *on_mk_media_send_rtp_stop)(const char *vhost, const char *app, const char *stream, const char *ssrc, int err, const char *msg);
} mk_events;
......
......@@ -12,6 +12,7 @@
#define MK_EVENT_OBJECTS_H
#include "mk_common.h"
#include "mk_tcp.h"
#include "mk_track.h"
#ifdef __cplusplus
extern "C" {
#endif
......@@ -61,19 +62,19 @@ API_EXPORT const char* API_CALL mk_parser_get_content(const mk_parser ctx, size_
///////////////////////////////////////////MediaInfo/////////////////////////////////////////////
//MediaInfo对象的C映射
typedef struct mk_media_info_t *mk_media_info;
//MediaInfo::_param_strs
//MediaInfo::param_strs
API_EXPORT const char* API_CALL mk_media_info_get_params(const mk_media_info ctx);
//MediaInfo::_schema
//MediaInfo::schema
API_EXPORT const char* API_CALL mk_media_info_get_schema(const mk_media_info ctx);
//MediaInfo::_vhost
//MediaInfo::vhost
API_EXPORT const char* API_CALL mk_media_info_get_vhost(const mk_media_info ctx);
//MediaInfo::_app
//MediaInfo::app
API_EXPORT const char* API_CALL mk_media_info_get_app(const mk_media_info ctx);
//MediaInfo::_streamid
//MediaInfo::stream
API_EXPORT const char* API_CALL mk_media_info_get_stream(const mk_media_info ctx);
//MediaInfo::_host
//MediaInfo::host
API_EXPORT const char* API_CALL mk_media_info_get_host(const mk_media_info ctx);
//MediaInfo::_port
//MediaInfo::port
API_EXPORT uint16_t API_CALL mk_media_info_get_port(const mk_media_info ctx);
......@@ -95,6 +96,13 @@ API_EXPORT const char* API_CALL mk_media_source_get_stream(const mk_media_source
API_EXPORT int API_CALL mk_media_source_get_reader_count(const mk_media_source ctx);
//MediaSource::totalReaderCount()
API_EXPORT int API_CALL mk_media_source_get_total_reader_count(const mk_media_source ctx);
// get track count from MediaSource
API_EXPORT int API_CALL mk_media_source_get_track_count(const mk_media_source ctx);
// copy track reference by index from MediaSource, please use mk_track_unref to release it
API_EXPORT mk_track API_CALL mk_media_source_get_track(const mk_media_source ctx, int index);
// MediaSource::broadcastMessage
API_EXPORT int API_CALL mk_media_source_broadcast_msg(const mk_media_source ctx, const char *msg, size_t len);
/**
* 直播源在ZLMediaKit中被称作为MediaSource,
* 目前支持3种,分别是RtmpMediaSource、RtspMediaSource、HlsMediaSource
......@@ -132,6 +140,12 @@ API_EXPORT void API_CALL mk_media_source_find(const char *schema,
int from_mp4,
void *user_data,
on_mk_media_source_find_cb cb);
API_EXPORT const mk_media_source API_CALL mk_media_source_find2(const char *schema,
const char *vhost,
const char *app,
const char *stream,
int from_mp4);
//MediaSource::for_each_media()
API_EXPORT void API_CALL mk_media_source_for_each(void *user_data, on_mk_media_source_find_cb cb, const char *schema,
const char *vhost, const char *app, const char *stream);
......
......@@ -117,6 +117,108 @@ API_EXPORT uint64_t API_CALL mk_frame_get_pts(mk_frame frame);
*/
API_EXPORT uint32_t API_CALL mk_frame_get_flags(mk_frame frame);
//////////////////////////////////////////////////////////////////////
typedef struct mk_buffer_t *mk_buffer;
typedef struct mk_frame_merger_t *mk_frame_merger;
/**
* 创建帧合并器
* @param type 起始头类型,0: none, 1: h264_prefix/AnnexB(0x 00 00 00 01), 2: mp4_nal_size(avcC)
* @return 帧合并器
*/
API_EXPORT mk_frame_merger API_CALL mk_frame_merger_create(int type);
/**
* 销毁帧合并器
* @param ctx 对象指针
*/
API_EXPORT void API_CALL mk_frame_merger_release(mk_frame_merger ctx);
/**
* 清空merger对象缓冲,方便复用
* @param ctx 对象指针
*/
API_EXPORT void API_CALL mk_frame_merger_clear(mk_frame_merger ctx);
/**
* 合并帧回调函数
* @param user_data 用户数据指针
* @param dts 解码时间戳
* @param pts 显示时间戳
* @param buffer 合并后数据buffer对象
* @param have_key_frame 合并后数据中是否包含关键帧
*/
typedef void(API_CALL *on_mk_frame_merger)(void *user_data, uint64_t dts, uint64_t pts, mk_buffer buffer, int have_key_frame);
/**
* 输入frame到merger对象并合并
* @param ctx 对象指针
* @param frame 帧数据
* @param cb 帧合并回调函数
* @param user_data 帧合并回调函数用户数据指针
*/
API_EXPORT void API_CALL mk_frame_merger_input(mk_frame_merger ctx, mk_frame frame, on_mk_frame_merger cb, void *user_data);
/**
* 强制flush merger对象缓冲,调用此api前需要确保先调用mk_frame_merger_input函数并且回调参数有效
* @param ctx 对象指针
*/
API_EXPORT void API_CALL mk_frame_merger_flush(mk_frame_merger ctx);
//////////////////////////////////////////////////////////////////////
typedef struct mk_mpeg_muxer_t *mk_mpeg_muxer;
/**
* mpeg-ps/ts 打包器输出回调函数
* @param user_data 设置回调时的用户数据指针
* @param muxer 对象
* @param frame 帧数据
* @param size 帧数据长度
* @param timestamp 时间戳
* @param key_pos 是否关键帧
*/
typedef void(API_CALL *on_mk_mpeg_muxer_frame)(void *user_data, mk_mpeg_muxer muxer, const char *frame, size_t size, uint64_t timestamp, int key_pos);
/**
* mpeg-ps/ts 打包器
* @param cb 打包回调函数
* @param user_data 回调用户数据指针
* @param is_ps 是否是ps
* @return 打包器对象
*/
API_EXPORT mk_mpeg_muxer API_CALL mk_mpeg_muxer_create(on_mk_mpeg_muxer_frame cb, void *user_data, int is_ps);
/**
* 删除mpeg-ps/ts 打包器
* @param ctx 打包器
*/
API_EXPORT void API_CALL mk_mpeg_muxer_release(mk_mpeg_muxer ctx);
/**
* 添加音视频track
* @param ctx mk_mpeg_muxer对象
* @param track mk_track对象,音视频轨道
*/
API_EXPORT void API_CALL mk_mpeg_muxer_init_track(mk_mpeg_muxer ctx, void* track);
/**
* 初始化track完毕后调用此函数,
* 在单track(只有音频或视频)时,因为ZLMediaKit不知道后续是否还要添加track,所以会多等待3秒钟
* 如果产生的流是单Track类型,请调用此函数以便加快流生成速度,当然不调用该函数,影响也不大(会多等待3秒)
* @param ctx 对象指针
*/
API_EXPORT void API_CALL mk_mpeg_muxer_init_complete(mk_mpeg_muxer ctx);
/**
* 输入frame对象
* @param ctx mk_mpeg_muxer对象
* @param frame 帧对象
* @return 1代表成功,0失败
*/
API_EXPORT int API_CALL mk_mpeg_muxer_input_frame(mk_mpeg_muxer ctx, mk_frame frame);
#ifdef __cplusplus
}
#endif
......
......@@ -58,7 +58,7 @@ API_EXPORT int API_CALL mk_recorder_is_recording(int type, const char *vhost, co
/**
* 开始录制
* @param type 0:hls,1:MP4
* @param type 0:hls-ts,1:MP4,2:hls-fmp4,3:http-fmp4,4:http-ts
* @param vhost 虚拟主机
* @param app 应用名
* @param stream 流id
......@@ -70,7 +70,7 @@ API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const cha
/**
* 停止录制
* @param type 0:hls,1:MP4
* @param type 0:hls-ts,1:MP4,2:hls-fmp4,3:http-fmp4,4:http-ts
* @param vhost 虚拟主机
* @param app 应用名
* @param stream 流id
......
......@@ -82,11 +82,11 @@ API_EXPORT void API_CALL mk_tcp_session_send_buffer(const mk_tcp_session ctx, mk
API_EXPORT void API_CALL mk_tcp_session_send_safe(const mk_tcp_session ctx, const char *data, size_t len);
API_EXPORT void API_CALL mk_tcp_session_send_buffer_safe(const mk_tcp_session ctx, mk_buffer buffer);
//创建mk_tcp_session的引用
//创建mk_tcp_session的引用
API_EXPORT mk_tcp_session_ref API_CALL mk_tcp_session_ref_from(const mk_tcp_session ctx);
//删除mk_tcp_session的引用
//删除mk_tcp_session的引用
API_EXPORT void mk_tcp_session_ref_release(const mk_tcp_session_ref ref);
//根据弱引用获取mk_tcp_session,如果mk_tcp_session已经销毁,那么返回NULL
//根据强引用获取mk_tcp_session
API_EXPORT mk_tcp_session mk_tcp_session_from_ref(const mk_tcp_session_ref ref);
///////////////////////////////////////////自定义tcp服务/////////////////////////////////////////////
......
......@@ -159,7 +159,7 @@ API_EXPORT void API_CALL mk_set_option(const char *key, const char *val) {
}
mINI::Instance()[key] = val;
//广播配置文件热加载
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastReloadConfig);
NOTICE_EMIT(BroadcastReloadConfigArgs, Broadcast::kBroadcastReloadConfig);
}
API_EXPORT const char * API_CALL mk_get_option(const char *key)
......
......@@ -160,6 +160,13 @@ API_EXPORT void API_CALL mk_events_listen(const mk_events *events){
s_events.on_mk_log((int) ctx->_level, ctx->_file.data(), ctx->_line, ctx->_function.data(), log.data());
}
});
NoticeCenter::Instance().addListener(&s_tag, Broadcast::kBroadcastSendRtpStopped,[](BroadcastSendRtpStoppedArgs){
if (s_events.on_mk_media_send_rtp_stop) {
s_events.on_mk_media_send_rtp_stop(sender.getMediaTuple().vhost.c_str(), sender.getMediaTuple().app.c_str(),
sender.getMediaTuple().stream.c_str(), ssrc.c_str(), ex.getErrCode(), ex.what());
}
});
});
}
......@@ -86,17 +86,17 @@ API_EXPORT const char* API_CALL mk_mp4_info_get_stream(const mk_mp4_info ctx){
API_EXPORT const char* API_CALL mk_parser_get_method(const mk_parser ctx){
assert(ctx);
Parser *parser = (Parser *)ctx;
return parser->Method().c_str();
return parser->method().c_str();
}
API_EXPORT const char* API_CALL mk_parser_get_url(const mk_parser ctx){
assert(ctx);
Parser *parser = (Parser *)ctx;
return parser->Url().c_str();
return parser->url().c_str();
}
API_EXPORT const char* API_CALL mk_parser_get_url_params(const mk_parser ctx){
assert(ctx);
Parser *parser = (Parser *)ctx;
return parser->Params().c_str();
return parser->params().c_str();
}
API_EXPORT const char* API_CALL mk_parser_get_url_param(const mk_parser ctx,const char *key){
assert(ctx && key);
......@@ -106,7 +106,7 @@ API_EXPORT const char* API_CALL mk_parser_get_url_param(const mk_parser ctx,cons
API_EXPORT const char* API_CALL mk_parser_get_tail(const mk_parser ctx){
assert(ctx);
Parser *parser = (Parser *)ctx;
return parser->Tail().c_str();
return parser->protocol().c_str();
}
API_EXPORT const char* API_CALL mk_parser_get_header(const mk_parser ctx,const char *key){
assert(ctx && key);
......@@ -117,52 +117,52 @@ API_EXPORT const char* API_CALL mk_parser_get_content(const mk_parser ctx, size_
assert(ctx);
Parser *parser = (Parser *)ctx;
if(length){
*length = parser->Content().size();
*length = parser->content().size();
}
return parser->Content().c_str();
return parser->content().c_str();
}
///////////////////////////////////////////MediaInfo/////////////////////////////////////////////
API_EXPORT const char* API_CALL mk_media_info_get_params(const mk_media_info ctx){
assert(ctx);
MediaInfo *info = (MediaInfo *)ctx;
return info->_param_strs.c_str();
return info->param_strs.c_str();
}
API_EXPORT const char* API_CALL mk_media_info_get_schema(const mk_media_info ctx){
assert(ctx);
MediaInfo *info = (MediaInfo *)ctx;
return info->_schema.c_str();
return info->schema.c_str();
}
API_EXPORT const char* API_CALL mk_media_info_get_vhost(const mk_media_info ctx){
assert(ctx);
MediaInfo *info = (MediaInfo *)ctx;
return info->_vhost.c_str();
return info->vhost.c_str();
}
API_EXPORT const char* API_CALL mk_media_info_get_host(const mk_media_info ctx){
assert(ctx);
MediaInfo *info = (MediaInfo *)ctx;
return info->_host.c_str();
return info->host.c_str();
}
API_EXPORT uint16_t API_CALL mk_media_info_get_port(const mk_media_info ctx){
assert(ctx);
MediaInfo *info = (MediaInfo *)ctx;
return info->_port;
return info->port;
}
API_EXPORT const char* API_CALL mk_media_info_get_app(const mk_media_info ctx){
assert(ctx);
MediaInfo *info = (MediaInfo *)ctx;
return info->_app.c_str();
return info->app.c_str();
}
API_EXPORT const char* API_CALL mk_media_info_get_stream(const mk_media_info ctx){
assert(ctx);
MediaInfo *info = (MediaInfo *)ctx;
return info->_streamid.c_str();
return info->stream.c_str();
}
///////////////////////////////////////////MediaSource/////////////////////////////////////////////
......@@ -174,17 +174,17 @@ API_EXPORT const char* API_CALL mk_media_source_get_schema(const mk_media_source
API_EXPORT const char* API_CALL mk_media_source_get_vhost(const mk_media_source ctx){
assert(ctx);
MediaSource *src = (MediaSource *)ctx;
return src->getVhost().c_str();
return src->getMediaTuple().vhost.c_str();
}
API_EXPORT const char* API_CALL mk_media_source_get_app(const mk_media_source ctx){
assert(ctx);
MediaSource *src = (MediaSource *)ctx;
return src->getApp().c_str();
return src->getMediaTuple().app.c_str();
}
API_EXPORT const char* API_CALL mk_media_source_get_stream(const mk_media_source ctx){
assert(ctx);
MediaSource *src = (MediaSource *)ctx;
return src->getId().c_str();
return src->getMediaTuple().stream.c_str();
}
API_EXPORT int API_CALL mk_media_source_get_reader_count(const mk_media_source ctx){
assert(ctx);
......@@ -198,6 +198,32 @@ API_EXPORT int API_CALL mk_media_source_get_total_reader_count(const mk_media_so
return src->totalReaderCount();
}
API_EXPORT int API_CALL mk_media_source_get_track_count(const mk_media_source ctx) {
assert(ctx);
MediaSource *src = (MediaSource *)ctx;
return src->getTracks(false).size();
}
API_EXPORT mk_track API_CALL mk_media_source_get_track(const mk_media_source ctx, int index) {
assert(ctx);
MediaSource *src = (MediaSource *)ctx;
auto tracks = src->getTracks(false);
if (index < 0 && index >= tracks.size()) {
return nullptr;
}
return (mk_track) new Track::Ptr(std::move(tracks[index]));
}
API_EXPORT int API_CALL mk_media_source_broadcast_msg(const mk_media_source ctx, const char *msg, size_t len) {
assert(ctx && msg && len);
MediaSource *src = (MediaSource *)ctx;
Any any;
Buffer::Ptr buffer = std::make_shared<BufferLikeString>(std::string(msg, len));
any.set(std::move(buffer));
return src->broadcastMessage(any);
}
API_EXPORT int API_CALL mk_media_source_close(const mk_media_source ctx,int force){
assert(ctx);
MediaSource *src = (MediaSource *)ctx;
......@@ -248,6 +274,16 @@ API_EXPORT void API_CALL mk_media_source_find(const char *schema,
cb(user_data, (mk_media_source)src.get());
}
API_EXPORT const mk_media_source API_CALL mk_media_source_find2(const char *schema,
const char *vhost,
const char *app,
const char *stream,
int from_mp4) {
assert(schema && vhost && app && stream);
auto src = MediaSource::find(schema, vhost, app, stream, from_mp4);
return (mk_media_source)src.get();
}
API_EXPORT void API_CALL mk_media_source_for_each(void *user_data, on_mk_media_source_find_cb cb, const char *schema,
const char *vhost, const char *app, const char *stream) {
assert(cb);
......
......@@ -9,10 +9,12 @@
*/
#include "mk_frame.h"
#include "mk_track.h"
#include "Extension/Frame.h"
#include "Extension/H264.h"
#include "Extension/H265.h"
#include "Extension/AAC.h"
#include "Record/MPEG.h"
using namespace mediakit;
......@@ -178,3 +180,92 @@ API_EXPORT uint32_t API_CALL mk_frame_get_flags(mk_frame frame) {
}
return ret;
}
API_EXPORT mk_frame_merger API_CALL mk_frame_merger_create(int type) {
return reinterpret_cast<mk_frame_merger>(new FrameMerger(type));
}
API_EXPORT void API_CALL mk_frame_merger_release(mk_frame_merger ctx) {
assert(ctx);
delete reinterpret_cast<FrameMerger *>(ctx);
}
API_EXPORT void API_CALL mk_frame_merger_clear(mk_frame_merger ctx) {
assert(ctx);
reinterpret_cast<FrameMerger *>(ctx)->clear();
}
API_EXPORT void API_CALL mk_frame_merger_flush(mk_frame_merger ctx) {
assert(ctx);
reinterpret_cast<FrameMerger *>(ctx)->flush();
}
API_EXPORT void API_CALL mk_frame_merger_input(mk_frame_merger ctx, mk_frame frame, on_mk_frame_merger cb, void *user_data) {
assert(ctx && frame && cb);
reinterpret_cast<FrameMerger *>(ctx)->inputFrame(*((Frame::Ptr *) frame), [cb, user_data](uint64_t dts, uint64_t pts, const toolkit::Buffer::Ptr &buffer, bool have_key_frame) {
cb(user_data, dts, pts, (mk_buffer)(&buffer), have_key_frame);
});
}
//////////////////////////////////////////////////////////////////////
class MpegMuxerForC : public MpegMuxer {
public:
using onMuxer = std::function<void(const char *frame, size_t size, uint64_t timestamp, int key_pos)>;
MpegMuxerForC(bool is_ps) : MpegMuxer(is_ps) {
_cb = nullptr;
}
~MpegMuxerForC() { MpegMuxer::flush(); };
void setOnMuxer(onMuxer cb) {
_cb = std::move(cb);
}
private:
void onWrite(std::shared_ptr<toolkit::Buffer> buffer, uint64_t timestamp, bool key_pos) override {
if (_cb) {
if (!buffer) {
_cb(nullptr, 0, timestamp, key_pos);
} else {
_cb(buffer->data(), buffer->size(), timestamp, key_pos);
}
}
}
private:
onMuxer _cb;
};
API_EXPORT mk_mpeg_muxer API_CALL mk_mpeg_muxer_create(on_mk_mpeg_muxer_frame cb, void *user_data, int is_ps){
assert(cb);
auto ret = new MpegMuxerForC(is_ps);
std::shared_ptr<void> ptr(user_data, [](void *) {});
ret->setOnMuxer([cb, ptr, ret](const char *frame, size_t size, uint64_t timestamp, int key_pos) {
cb(ptr.get(), reinterpret_cast<mk_mpeg_muxer>(ret), frame, size, timestamp, key_pos);
});
return reinterpret_cast<mk_mpeg_muxer>(ret);
}
API_EXPORT void API_CALL mk_mpeg_muxer_release(mk_mpeg_muxer ctx){
assert(ctx);
auto ptr = reinterpret_cast<MpegMuxerForC *>(ctx);
delete ptr;
}
API_EXPORT void API_CALL mk_mpeg_muxer_init_track(mk_mpeg_muxer ctx, void* track) {
assert(ctx && track);
auto ptr = reinterpret_cast<MpegMuxerForC *>(ctx);
ptr->addTrack(*((Track::Ptr *) track));
}
API_EXPORT void API_CALL mk_mpeg_muxer_init_complete(mk_mpeg_muxer ctx) {
assert(ctx);
auto ptr = reinterpret_cast<MpegMuxerForC *>(ctx);
ptr->addTrackCompleted();
}
API_EXPORT int API_CALL mk_mpeg_muxer_input_frame(mk_mpeg_muxer ctx, mk_frame frame){
assert(ctx && frame);
auto ptr = reinterpret_cast<MpegMuxerForC *>(ctx);
return ptr->inputFrame(*((Frame::Ptr *) frame));
}
\ No newline at end of file
......@@ -108,7 +108,7 @@ API_EXPORT void API_CALL mk_http_requester_add_header(mk_http_requester ctx,cons
API_EXPORT const char* API_CALL mk_http_requester_get_response_status(mk_http_requester ctx){
assert(ctx);
HttpRequester::Ptr *obj = (HttpRequester::Ptr *)ctx;
return (*obj)->response().Url().c_str();
return (*obj)->response().status().c_str();
}
API_EXPORT const char* API_CALL mk_http_requester_get_response_header(mk_http_requester ctx,const char *key){
......@@ -121,9 +121,9 @@ API_EXPORT const char* API_CALL mk_http_requester_get_response_body(mk_http_requ
assert(ctx);
HttpRequester::Ptr *obj = (HttpRequester::Ptr *)ctx;
if(length){
*length = (*obj)->response().Content().size();
*length = (*obj)->response().content().size();
}
return (*obj)->response().Content().c_str();
return (*obj)->response().content().c_str();
}
API_EXPORT mk_parser API_CALL mk_http_requester_get_response(mk_http_requester ctx){
......
......@@ -22,7 +22,8 @@ public:
MediaHelper(const char *vhost, const char *app, const char *stream, float duration, const ProtocolOption &option) {
_poller = EventPollerPool::Instance().getPoller();
// 在poller线程中创建DevChannel(MultiMediaSourceMuxer)对象,确保严格的线程安全限制
_poller->sync([&]() { _channel = std::make_shared<DevChannel>(vhost, app, stream, duration, option); });
auto tuple = MediaTuple{vhost, app, stream};
_poller->sync([&]() { _channel = std::make_shared<DevChannel>(tuple, duration, option); });
}
~MediaHelper() = default;
......@@ -263,7 +264,7 @@ API_EXPORT void API_CALL mk_media_input_yuv(mk_media ctx, const char *yuv[3], in
}
API_EXPORT int API_CALL mk_media_input_aac(mk_media ctx, const void *data, int len, uint64_t dts, void *adts) {
assert(ctx && data && len > 0 && adts);
assert(ctx && data && len > 0);
MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx;
return (*obj)->getChannel()->inputAAC((const char *) data, len, dts, (char *) adts);
}
......
......@@ -47,21 +47,25 @@ static inline bool isRecording(Recorder::type type, const string &vhost, const s
return src->isRecording(type);
}
static inline bool startRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path, size_t max_second){
static inline bool startRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path, size_t max_second) {
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, max_second);
bool ret;
src->getOwnerPoller()->sync([&]() { ret = src->setupRecord(type, true, customized_path, max_second); });
return ret;
}
static inline bool stopRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id){
static inline bool stopRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id) {
auto src = MediaSource::find(vhost, app, stream_id);
if(!src){
if (!src) {
return false;
}
return src->setupRecord(type, false, "", 0);
bool ret;
src->getOwnerPoller()->sync([&]() { ret = src->setupRecord(type, false, "", 0); });
return ret;
}
API_EXPORT int API_CALL mk_recorder_is_recording(int type, const char *vhost, const char *app, const char *stream){
......
......@@ -139,7 +139,7 @@ API_EXPORT void API_CALL mk_tcp_session_send(const mk_tcp_session ctx, const cha
API_EXPORT void API_CALL mk_tcp_session_send_buffer_safe(const mk_tcp_session ctx, mk_buffer buffer) {
assert(ctx && buffer);
try {
std::weak_ptr<Session> weak_session = ((SessionForC *) ctx)->shared_from_this();
std::weak_ptr<SocketHelper> weak_session = ((SessionForC *) ctx)->shared_from_this();
auto ref = mk_buffer_ref(buffer);
((SessionForC *) ctx)->async([weak_session, ref]() {
auto session_session = weak_session.lock();
......@@ -149,13 +149,13 @@ API_EXPORT void API_CALL mk_tcp_session_send_buffer_safe(const mk_tcp_session ct
mk_buffer_unref(ref);
});
} catch (std::exception &ex) {
WarnL << "can not got the strong pionter of this mk_tcp_session:" << ex.what();
WarnL << "can not got the strong pointer of this mk_tcp_session:" << ex.what();
}
}
API_EXPORT mk_tcp_session_ref API_CALL mk_tcp_session_ref_from(const mk_tcp_session ctx) {
auto ref = ((SessionForC *) ctx)->shared_from_this();
return (mk_tcp_session_ref)new std::shared_ptr<SessionForC>(std::dynamic_pointer_cast<SessionForC>(ref));
return (mk_tcp_session_ref)new std::shared_ptr<SessionForC>(std::static_pointer_cast<SessionForC>(ref));
}
API_EXPORT void mk_tcp_session_ref_release(const mk_tcp_session_ref ref) {
......@@ -271,7 +271,7 @@ void TcpClientForC::onRecv(const Buffer::Ptr &pBuf) {
}
}
void TcpClientForC::onErr(const SockException &ex) {
void TcpClientForC::onError(const SockException &ex) {
if(_events.on_mk_tcp_client_disconnect){
_events.on_mk_tcp_client_disconnect(_client,ex.getErrCode(),ex.what());
}
......
......@@ -21,7 +21,7 @@ public:
TcpClientForC(mk_tcp_client_events *events) ;
~TcpClientForC() override ;
void onRecv(const toolkit::Buffer::Ptr &pBuf) override;
void onErr(const toolkit::SockException &ex) override;
void onError(const toolkit::SockException &ex) override;
void onManager() override;
void onConnect(const toolkit::SockException &ex) override;
void setClient(mk_tcp_client client);
......
......@@ -143,7 +143,7 @@ public:
}
EventPoller::Ptr getPoller() {
return dynamic_pointer_cast<EventPoller>(getExecutor());
return static_pointer_cast<EventPoller>(getExecutor());
}
};
......
......@@ -65,7 +65,7 @@ API_EXPORT mk_ini API_CALL mk_ini_default() {
static void emit_ini_file_reload(mk_ini ini) {
if (ini == mk_ini_default()) {
// 广播配置文件热加载
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastReloadConfig);
NOTICE_EMIT(BroadcastReloadConfigArgs, Broadcast::kBroadcastReloadConfig);
}
}
......
......@@ -43,15 +43,3 @@ foreach(TEST_SRC ${TEST_SRC_LIST})
target_link_libraries(${exe_name} mk_api)
target_compile_options(${exe_name} PRIVATE ${COMPILE_OPTIONS_DEFAULT})
endforeach()
......@@ -9,6 +9,7 @@
*/
#include <string.h>
#include <stdio.h>
#include "mk_mediakit.h"
typedef struct {
......
......@@ -9,6 +9,7 @@
*/
#include <string.h>
#include <stdio.h>
#include "mk_mediakit.h"
#define LOG_LEV 4
......
# Download and build Jemalloc
set(JEMALLOC_VERSION 5.2.1)
set(JEMALLOC_VERSION 5.3.0)
set(JEMALLOC_NAME jemalloc-${JEMALLOC_VERSION})
set(JEMALLOC_TAR_PATH ${DEP_ROOT_DIR}/${JEMALLOC_NAME}.tar.bz2)
list(APPEND jemalloc_CONFIG_ARGS --disable-initial-exec-tls)
list(APPEND jemalloc_CONFIG_ARGS --without-export)
#list(APPEND jemalloc_CONFIG_ARGS --without-export)
if (ENABLE_JEMALLOC_STAT)
list(APPEND jemalloc_CONFIG_ARGS --enable-stats)
message(STATUS "Jemalloc stats enabled")
else ()
list(APPEND jemalloc_CONFIG_ARGS --disable-stats)
message(STATUS "Jemalloc stats disabled")
endif ()
list(APPEND jemalloc_CONFIG_ARGS --disable-libdl)
#list(APPEND jemalloc_CONFIG_ARGS --disable-cxx)
#list(APPEND jemalloc_CONFIG_ARGS --with-jemalloc-prefix=je_)
#list(APPEND jemalloc_CONFIG_ARGS --enable-debug)
if(NOT EXISTS ${JEMALLOC_TAR_PATH})
message(STATUS "Downloading ${JEMALLOC_NAME}...")
file(DOWNLOAD https://github.com/jemalloc/jemalloc/releases/download/${JEMALLOC_VERSION}/${JEMALLOC_NAME}.tar.bz2
${JEMALLOC_TAR_PATH})
set(JEMALLOC_URL https://github.com/jemalloc/jemalloc/releases/download/${JEMALLOC_VERSION}/${JEMALLOC_NAME}.tar.bz2)
message(STATUS "Downloading ${JEMALLOC_NAME} from ${JEMALLOC_URL}")
file(DOWNLOAD ${JEMALLOC_URL} ${JEMALLOC_TAR_PATH} SHOW_PROGRESS STATUS JEMALLOC_DOWNLOAD_STATUS LOG JEMALLOC_DOWNLOAD_LOG)
list(GET JEMALLOC_DOWNLOAD_STATUS 0 JEMALLOC_DOWNLOAD_STATUS_CODE)
if(NOT JEMALLOC_DOWNLOAD_STATUS_CODE EQUAL 0)
file(REMOVE ${JEMALLOC_TAR_PATH})
message(STATUS "${JEMALLOC_DOWNLOAD_LOG}")
message(FATAL_ERROR "${JEMALLOC_NAME} download failed! error is ${JEMALLOC_DOWNLOAD_STATUS}")
return()
endif ()
endif()
SET( DIR_CONTAINING_JEMALLOC ${DEP_ROOT_DIR}/${JEMALLOC_NAME} )
......
......@@ -22,7 +22,7 @@ bin=/usr/bin/ffmpeg
#FFmpeg拉流再推流的命令模板,通过该模板可以设置再编码的一些参数
cmd=%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s
#FFmpeg生成截图的命令,可以通过修改该配置改变截图分辨率或质量
snap=%s -i %s -y -f mjpeg -t 0.001 %s
snap=%s -i %s -y -f mjpeg -frames:v 1 %s
#FFmpeg日志的路径,如果置空则不生成FFmpeg日志
#可以为相对(相对于本可执行程序目录)或绝对路径
log=./ffmpeg/ffmpeg.log
......@@ -32,18 +32,28 @@ restart_sec=0
#转协议相关开关;如果addStreamProxy api和on_publish hook回复未指定转协议参数,则采用这些配置项
[protocol]
#转协议时,是否开启帧级时间戳覆盖
modify_stamp=0
# 0:采用源视频流绝对时间戳,不做任何改变
# 1:采用zlmediakit接收数据时的系统时间戳(有平滑处理)
# 2:采用源视频流时间戳相对时间戳(增长量),有做时间戳跳跃和回退矫正
modify_stamp=2
#转协议是否开启音频
enable_audio=1
#添加acc静音音频,在关闭音频时,此开关无效
add_mute_audio=1
#无人观看时,是否直接关闭(而不是通过on_none_reader hook返回close)
#此配置置1时,此流如果无人观看,将不触发on_none_reader hook回调,
#而是将直接关闭流
auto_close=0
#推流断开后可以在超时时间内重新连接上继续推流,这样播放器会接着播放。
#置0关闭此特性(推流断开会导致立即断开播放器)
#此参数不应大于播放器超时时间;单位毫秒
continue_push_ms=15000
#是否开启转换为hls
#是否开启转换为hls(mpegts)
enable_hls=1
#是否开启转换为hls(fmp4)
enable_hls_fmp4=0
#是否开启MP4录制
enable_mp4=0
#是否开启转换为rtsp/webrtc
......@@ -121,7 +131,7 @@ segDur=2
segNum=3
#HLS切片从m3u8文件中移除后,继续保留在磁盘上的个数
segRetain=5
#是否广播 ts 切片完成通知
#是否广播 hls切片(ts/fmp4)完成通知(on_record_ts)
broadcastRecordTs=0
#直播hls文件删除延时,单位秒,issue: #913
deleteDelaySec=10
......@@ -132,9 +142,6 @@ deleteDelaySec=10
segKeep=0
[hook]
#在推流时,如果url参数匹对admin_params,那么可以不经过hook鉴权直接推流成功,播放时亦然
#该配置项的目的是为了开发者自己调试测试,该参数暴露后会有泄露隐私的安全隐患
admin_params=secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc
#是否启用hook事件,启用后,推拉流都将进行鉴权
enable=0
#播放器或推流器使用流量事件,置空则关闭
......@@ -147,7 +154,7 @@ 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 切片完成事件
# 录制 hls ts(或fmp4) 切片完成事件
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
......@@ -159,12 +166,16 @@ on_rtsp_realm=https://127.0.0.1/index/hook/on_rtsp_realm
on_shell_login=https://127.0.0.1/index/hook/on_shell_login
#直播流注册或注销事件
on_stream_changed=https://127.0.0.1/index/hook/on_stream_changed
#过滤on_stream_changed hook的协议类型,可以选择只监听某些感兴趣的协议;置空则不过滤协议
stream_changed_schemas=rtsp/rtmp/fmp4/ts/hls/hls.fmp4
#无人观看流事件,通过该事件,可以选择是否关闭无人观看的流。配合general.streamNoneReaderDelayMS选项一起使用
on_stream_none_reader=https://127.0.0.1/index/hook/on_stream_none_reader
#播放时,未找到流事件,通过配合hook.on_stream_none_reader事件可以完成按需拉流
on_stream_not_found=https://127.0.0.1/index/hook/on_stream_not_found
#服务器启动报告,可以用于服务器的崩溃重启事件监听
on_server_started=https://127.0.0.1/index/hook/on_server_started
#服务器退出报告,当服务器正常退出时触发
on_server_exited=https://127.0.0.1/index/hook/on_server_exited
#server保活上报
on_server_keepalive=https://127.0.0.1/index/hook/on_server_keepalive
#发送rtp(startSendRtp)被动关闭时回调
......@@ -230,6 +241,10 @@ forbidCacheSuffix=
#可以把http代理前真实客户端ip放在http头中:https://github.com/ZLMediaKit/ZLMediaKit/issues/1388
#切勿暴露此key,否则可能导致伪造客户端ip
forwarded_ip_header=
#默认允许所有跨域请求
allow_cross_domains=1
#允许访问http api和http文件索引的ip地址范围白名单,置空情况下不做限制
allow_ip_range=::1,127.0.0.1,172.16.0.0-172.31.255.255,192.168.0.0-192.168.255.255,10.0.0.0-10.255.255.255
[multicast]
#rtp组播截止组播ip地址
......@@ -259,8 +274,6 @@ handshakeSecond=15
#rtmp超时时间,如果该时间内未收到客户端的数据,
#或者tcp发送缓存超过这个时间,则会断开连接,单位秒
keepAliveSecond=15
#在接收rtmp推流时,是否重新生成时间戳(很多推流器的时间戳着实很烂)
modifyStamp=0
#rtmp服务器监听端口
port=1935
#rtmps服务器监听地址
......@@ -276,6 +289,9 @@ videoMtuSize=1400
rtpMaxSize=10
# rtp 打包时,低延迟开关,默认关闭(为0),h264存在一帧多个slice(NAL)的情况,在这种情况下,如果开启可能会导致画面花屏
lowLatency=0
# H264 rtp打包模式是否采用stap-a模式(为了在老版本浏览器上兼容webrtc)还是采用Single NAL unit packet per H.264 模式
# 有些老的rtsp设备不支持stap-a rtp,设置此配置为0可提高兼容性
h264_stap_a=1
[rtp_proxy]
#导出调试数据(包括rtp/ps/h264)至该目录,置空则关闭数据导出
......@@ -357,6 +373,10 @@ port=554
sslport=0
#rtsp 转发是否使用低延迟模式,当开启时,不会缓存rtp包,来提高并发,可以降低一帧的延迟
lowLatency=0
#强制协商rtp传输方式 (0:TCP,1:UDP,2:MULTICAST,-1:不限制)
#当客户端发起RTSP SETUP的时候如果传输类型和此配置不一致则返回461 Unsupported transport
#迫使客户端重新SETUP并切换到对应协议。目前支持FFMPEG和VLC
rtpTransportType=-1
[shell]
#调试telnet服务器接受最大bufffer大小
maxReqSize=1024
......
......@@ -128,4 +128,4 @@ WORKDIR /opt/zlm
VOLUME [ "/opt/zlm/conf/","/opt/zlm/log/","opt/zlm/ffmpeg/"]
COPY --from=build /opt/build /
ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH TZ=Asia/Shanghai
CMD ./MediaServer -c ./conf/config.ini
\ No newline at end of file
CMD ["./MediaServer", "-c" , "./conf/config.ini"]
\ No newline at end of file
......@@ -41,4 +41,4 @@ RUN cmake -DCMAKE_BUILD_TYPE=Release .. && \
make
ENV PATH /opt/media/ZLMediaKit/release/linux/Release/:$PATH
CMD MediaServer
CMD ["MediaServer"]
......@@ -60,4 +60,4 @@ RUN apt-get update && \
WORKDIR /opt/media/bin/
COPY --from=build /opt/media/ZLMediaKit/release/linux/Release/MediaServer /opt/media/bin/MediaServer
ENV PATH /opt/media/bin:$PATH
CMD MediaServer
CMD ["MediaServer"]
......@@ -42,4 +42,4 @@ RUN cmake -DCMAKE_BUILD_TYPE=Release .. && \
make
ENV PATH /opt/media/ZLMediaKit/release/linux/Release:$PATH
CMD MediaServer
CMD ["MediaServer"]
......@@ -60,4 +60,4 @@ RUN apt-get update && \
WORKDIR /opt/media/bin/
COPY --from=build /opt/media/ZLMediaKit/release/linux/Release/MediaServer /opt/media/bin/MediaServer
ENV PATH /opt/media/bin:$PATH
CMD MediaServer
CMD ["MediaServer"]
......@@ -83,4 +83,4 @@ COPY --from=build /opt/media/ZLMediaKit/release/linux/${MODEL}/MediaServer /opt/
COPY --from=build /opt/media/ZLMediaKit/release/linux/${MODEL}/config.ini /opt/media/conf/
COPY --from=build /opt/media/ZLMediaKit/www/ /opt/media/bin/www/
ENV PATH /opt/media/bin:$PATH
CMD ["sh","-c","./MediaServer -s default.pem -c ../conf/config.ini -l 0"]
CMD ["./MediaServer","-s", "default.pem", "-c", "../conf/config.ini", "-l","0"]
......@@ -33,9 +33,9 @@ SDLAudioDevice::SDLAudioDevice() {
SDLAudioDevice *_this = (SDLAudioDevice *) userdata;
_this->onReqPCM((char *) stream, len);
};
if (SDL_OpenAudio(&wanted_spec, &_audio_config) < 0) {
throw std::runtime_error("SDL_OpenAudio failed");
}
if (SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &_audio_config, SDL_AUDIO_ALLOW_ANY_CHANGE) < 0) {
throw std::runtime_error("SDL_OpenAudioDevice failed");
}
InfoL << "actual audioSpec, " << "freq:" << _audio_config.freq
<< ", format:" << hex << _audio_config.format << dec
......
......@@ -56,8 +56,7 @@ int main(int argc, char *argv[]) {
if (argc < 3) {
ErrorL << "\r\n测试方法:./test_player rtxp_url rtp_type\r\n"
<< "例如:./test_player rtsp://admin:123456@127.0.0.1/live/0 0\r\n"
<< endl;
<< "例如:./test_player rtsp://admin:123456@127.0.0.1/live/0 0\r\n";
return 0;
}
......
......@@ -34,11 +34,17 @@ if(ENABLE_SERVER_LIB)
PRIVATE ${COMPILE_OPTIONS_DEFAULT})
target_link_libraries(MediaServer
PRIVATE ${MK_LINK_LIBRARIES})
update_cached(MK_LINK_LIBRARIES MediaServer)
update_cached_list(MK_LINK_LIBRARIES MediaServer)
return()
endif()
add_executable(MediaServer ${MediaServer_SRC_LIST})
# IOS 不编译可执行程序,只做依赖库
if(IOS)
add_library(MediaServer STATIC ${MediaServer_SRC_LIST})
else()
add_executable(MediaServer ${MediaServer_SRC_LIST})
endif()
target_compile_definitions(MediaServer
PRIVATE ${COMPILE_DEFINITIONS})
target_compile_options(MediaServer
......
......@@ -20,6 +20,7 @@
namespace FFmpeg {
extern const std::string kSnap;
extern const std::string kBin;
}
class FFmpegSnap {
......@@ -79,8 +80,6 @@ private:
mediakit::MediaOriginType getOriginType(mediakit::MediaSource &sender) const override;
//获取媒体源url或者文件路径
std::string getOriginUrl(mediakit::MediaSource &sender) const override;
// 获取媒体源客户端相关信息
std::shared_ptr<toolkit::SockInfo> getOriginSock(mediakit::MediaSource &sender) const override;
private:
bool _enable_hls = false;
......
......@@ -108,7 +108,7 @@ static int cloneFunc(void *ptr) {
#endif
void Process::run(const string &cmd, string &log_file) {
void Process::run(const string &cmd, string log_file) {
kill(2000);
#ifdef _WIN32
STARTUPINFO si = { 0 };
......
......@@ -26,7 +26,7 @@ class Process {
public:
Process();
~Process();
void run(const std::string &cmd, std::string &log_file);
void run(const std::string &cmd, std::string log_file);
void kill(int max_delay,bool force = false);
bool wait(bool block = true);
int exit_code();
......
......@@ -22,10 +22,11 @@
#include <map>
#include <iostream>
#include "Common/JemallocUtil.h"
#include "Common/macros.h"
#include "System.h"
#include "Util/logger.h"
#include "Util/uv_errno.h"
#include "System.h"
#include "Common/macros.h"
using namespace std;
using namespace toolkit;
......@@ -55,6 +56,16 @@ string System::execute(const string &cmd) {
static constexpr int MAX_STACK_FRAMES = 128;
static void save_jemalloc_stats() {
string jemalloc_status = JemallocUtil::get_malloc_stats();
if (jemalloc_status.empty()) {
return;
}
ofstream out(StrPrinter << exeDir() << "/jemalloc.json", ios::out | ios::binary | ios::trunc);
out << jemalloc_status;
out.flush();
}
static void sig_crash(int sig) {
signal(sig, SIG_DFL);
void *array[MAX_STACK_FRAMES];
......@@ -126,6 +137,12 @@ void System::startDaemon(bool &kill_parent_if_failed) {
exit(0);
});
signal(SIGTERM,[](int) {
WarnL << "收到主动退出信号,关闭父进程与子进程";
kill(pid, SIGINT);
exit(0);
});
do {
int status = 0;
if (waitpid(pid, &status, 0) >= 0) {
......@@ -143,6 +160,12 @@ void System::startDaemon(bool &kill_parent_if_failed) {
}
void System::systemSetup(){
#ifdef ENABLE_JEMALLOC_DUMP
//Save memory report when program exits
atexit(save_jemalloc_stats);
#endif //ENABLE_JEMALLOC_DUMP
#if !defined(_WIN32)
struct rlimit rlim,rlim_new;
if (getrlimit(RLIMIT_CORE, &rlim)==0) {
......
......@@ -44,6 +44,8 @@ typedef enum {
OtherFailed = -1,//业务代码执行失败,
Success = 0//执行成功
} ApiErr;
extern const std::string kSecret;
}//namespace API
class ApiRetException: public std::runtime_error {
......@@ -219,21 +221,29 @@ bool checkArgs(Args &args, const First &first, const KeyTypes &...keys) {
throw InvalidArgsException("缺少必要参数:" #__VA_ARGS__); \
}
//检查http参数中是否附带secret密钥的宏,127.0.0.1的ip不检查密钥
// 检查http参数中是否附带secret密钥的宏,127.0.0.1的ip不检查密钥
// 同时检测是否在ip白名单内
#define CHECK_SECRET() \
if(sender.get_peer_ip() != "127.0.0.1"){ \
do { \
auto ip = sender.get_peer_ip(); \
if (!HttpFileManager::isIPAllowed(ip)) { \
throw AuthException("Your ip is not allowed to access the service."); \
} \
CHECK_ARGS("secret"); \
if(api_secret != allArgs["secret"]){ \
if (api_secret != allArgs["secret"]) { \
throw AuthException("secret错误"); \
} \
}
} while(false);
void installWebApi();
void unInstallWebApi();
uint16_t openRtpServer(uint16_t local_port, const std::string &stream_id, int tcp_mode, const std::string &local_ip, bool re_use_port, uint32_t ssrc);
#if defined(ENABLE_RTPPROXY)
uint16_t openRtpServer(uint16_t local_port, const std::string &stream_id, int tcp_mode, const std::string &local_ip, bool re_use_port, uint32_t ssrc, bool only_audio);
void connectRtpServer(const std::string &stream_id, const std::string &dst_url, uint16_t dst_port, const std::function<void(const toolkit::SockException &ex)> &cb);
bool closeRtpServer(const std::string &stream_id);
#endif
Json::Value makeMediaSourceJson(mediakit::MediaSource &media);
void getStatisticJson(const std::function<void(Json::Value &val)> &cb);
void addStreamProxy(const std::string &vhost, const std::string &app, const std::string &stream, const std::string &url, int retry_count,
......
......@@ -31,6 +31,7 @@ extern const std::string kTimeoutSec;
void installWebHook();
void unInstallWebHook();
void onProcessExited();
/**
* 触发http hook请求
* @param url 请求地址
......
......@@ -179,6 +179,29 @@ public:
throw ExitException();
});
#endif
(*_parser) << Option(0,/*该选项简称,如果是\x00则说明无简称*/
"log-slice",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
Option::ArgRequired,/*该选项后面必须跟值*/
"100",/*该选项默认值*/
true,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
"最大保存日志切片个数",/*该选项说明文字*/
nullptr);
(*_parser) << Option(0,/*该选项简称,如果是\x00则说明无简称*/
"log-size",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
Option::ArgRequired,/*该选项后面必须跟值*/
"256",/*该选项默认值*/
true,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
"单个日志切片最大容量,单位MB",/*该选项说明文字*/
nullptr);
(*_parser) << Option(0,/*该选项简称,如果是\x00则说明无简称*/
"log-dir",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
Option::ArgRequired,/*该选项后面必须跟值*/
(exeDir() + "log/").data(),/*该选项默认值*/
true,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
"日志保存文件夹路径",/*该选项说明文字*/
nullptr);
}
~CMD_main() override{}
......@@ -213,9 +236,11 @@ int start_main(int argc,char *argv[]) {
//设置日志
Logger::Instance().add(std::make_shared<ConsoleChannel>("ConsoleChannel", logLevel));
#if !defined(ANDROID)
auto fileChannel = std::make_shared<FileChannel>("FileChannel", exeDir() + "log/", logLevel);
auto fileChannel = std::make_shared<FileChannel>("FileChannel", cmd_main["log-dir"], logLevel);
// 日志最多保存天数
fileChannel->setMaxDay(cmd_main["max_day"]);
fileChannel->setFileMaxCount(cmd_main["log-slice"]);
fileChannel->setFileMaxSize(cmd_main["log-size"]);
Logger::Instance().add(fileChannel);
#endif // !defined(ANDROID)
......@@ -326,6 +351,14 @@ int start_main(int argc,char *argv[]) {
#endif //defined(ENABLE_SRT)
try {
auto &secret = mINI::Instance()[API::kSecret];
if (secret == "035c73f7-bb6b-4889-a715-d9eb2d1925cc" || secret.empty()) {
// 使用默认secret被禁止启动
secret = makeRandStr(32, true);
mINI::Instance().dumpFile(g_ini_file);
WarnL << "The " << API::kSecret << " is invalid, modified it to: " << secret
<< ", saved config file: " << g_ini_file;
}
//rtsp服务器,端口默认554
if (rtspPort) { rtspSrv->start<RtspSession>(rtspPort); }
//rtsps服务器,端口默认322
......@@ -363,8 +396,7 @@ int start_main(int argc,char *argv[]) {
#endif//defined(ENABLE_SRT)
} catch (std::exception &ex) {
WarnL << "端口占用或无权限:" << ex.what() << endl;
ErrorL << "程序启动失败,请修改配置文件中端口号后重试!" << endl;
ErrorL << "Start server failed: " << ex.what();
sleep(1);
#if !defined(_WIN32)
if (pid != getpid() && kill_parent_if_failed) {
......@@ -384,9 +416,15 @@ int start_main(int argc,char *argv[]) {
static semaphore sem;
signal(SIGINT, [](int) {
InfoL << "SIGINT:exit";
signal(SIGINT, SIG_IGN);// 设置退出信号
signal(SIGINT, SIG_IGN); // 设置退出信号
sem.post();
});// 设置退出信号
}); // 设置退出信号
signal(SIGTERM,[](int) {
WarnL << "SIGTERM:exit";
signal(SIGTERM, SIG_IGN);
sem.post();
});
#if !defined(_WIN32)
signal(SIGHUP, [](int) { mediakit::loadIniConfig(g_ini_file.data()); });
......@@ -395,6 +433,8 @@ int start_main(int argc,char *argv[]) {
}
unInstallWebApi();
unInstallWebHook();
onProcessExited();
//休眠1秒再退出,防止资源释放顺序错误
InfoL << "程序退出中,请等待...";
sleep(1);
......
......@@ -436,6 +436,7 @@ FFmpegDecoder::FFmpegDecoder(const Track::Ptr &track, int thread_num, const std:
av_dict_set(&dict, "zerolatency", "1", 0);
av_dict_set(&dict, "strict", "-2", 0);
#ifdef AV_CODEC_CAP_TRUNCATED
if (codec->capabilities & AV_CODEC_CAP_TRUNCATED) {
/* we do not send complete frames */
_context->flags |= AV_CODEC_FLAG_TRUNCATED;
......@@ -443,6 +444,7 @@ FFmpegDecoder::FFmpegDecoder(const Track::Ptr &track, int thread_num, const std:
// 此时业务层应该需要合帧
_do_merger = true;
}
#endif
int ret = avcodec_open2(_context.get(), codec, &dict);
av_dict_free(&dict);
......
......@@ -47,10 +47,8 @@ public:
using Ptr = std::shared_ptr<DevChannel>;
//fDuration<=0为直播,否则为点播
DevChannel(
const std::string &vhost, const std::string &app, const std::string &stream_id, float duration = 0,
const ProtocolOption &option = ProtocolOption())
: MultiMediaSourceMuxer(vhost, app, stream_id, duration, option) {}
DevChannel(const MediaTuple& tuple, float duration = 0, const ProtocolOption &option = ProtocolOption())
: MultiMediaSourceMuxer(tuple, duration, option) {}
~DevChannel() override = default;
/**
......
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/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 "JemallocUtil.h"
#include "Util/logger.h"
#ifdef USE_JEMALLOC
#include <iostream>
#include <jemalloc/jemalloc.h>
#endif
namespace mediakit {
void set_profile_active(bool active) {
#ifdef USE_JEMALLOC
int err = mallctl("prof.active", nullptr, nullptr, (void *)&active, sizeof(active));
if (err != 0) {
WarnL << "mallctl failed with: " << err;
}
#endif
}
void JemallocUtil::enable_profiling() {
set_profile_active(true);
}
void JemallocUtil::disable_profiling() {
set_profile_active(false);
}
void JemallocUtil::dump(const std::string &file_name) {
#ifdef USE_JEMALLOC
auto *c_str = file_name.c_str();
int err = mallctl("prof.dump", nullptr, nullptr, &c_str, sizeof(const char *));
if (err != 0) {
std::cerr << "mallctl failed with: " << err << std::endl;
}
#endif
}
std::string JemallocUtil::get_malloc_stats() {
#ifdef USE_JEMALLOC
std::string res;
malloc_stats_print([](void *opaque, const char *s) { ((std::string *)opaque)->append(s); }, &res, "J");
return res;
#else
return "";
#endif
}
void JemallocUtil::some_malloc_stats(const std::function<void(const char *, uint64_t)> &fn) {
#ifdef USE_JEMALLOC
constexpr std::array<const char *, 8> STATS = {
"stats.allocated", "stats.active", "stats.metadata", "stats.metadata_thp",
"stats.resident", "stats.mapped", "stats.retained", "stats.zero_reallocs",
};
for (const char *stat : STATS) {
size_t value;
size_t len = sizeof(value);
auto err = mallctl(stat, &value, &len, nullptr, 0);
if (err != 0) {
ErrorL << "Failed reading " << stat << ": " << err;
continue;
}
fn(stat, value);
}
#endif
}
} // namespace mediakit
\ No newline at end of file
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/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_JEMALLOCUTIL_H
#define ZLMEDIAKIT_JEMALLOCUTIL_H
#include <functional>
#include <string>
namespace mediakit {
class JemallocUtil {
public:
JemallocUtil() = default;
~JemallocUtil() = default;
static void enable_profiling();
static void disable_profiling();
static void dump(const std::string &file_name);
static std::string get_malloc_stats();
static void some_malloc_stats(const std::function<void(const char *, uint64_t)> &fn);
};
} // namespace mediakit
#endif // ZLMEDIAKIT_JEMALLOCUTIL_H
......@@ -41,6 +41,7 @@ enum class MediaOriginType : uint8_t {
std::string getOriginTypeString(MediaOriginType type);
class MediaSource;
class MultiMediaSourceMuxer;
class MediaSourceEvent {
public:
friend class MediaSource;
......@@ -88,6 +89,8 @@ public:
virtual bool isRecording(MediaSource &sender, Recorder::type type) { return false; }
// 获取所有track相关信息
virtual std::vector<Track::Ptr> getMediaTracks(MediaSource &sender, bool trackReady = true) const { return std::vector<Track::Ptr>(); };
// 获取MultiMediaSourceMuxer对象
virtual std::shared_ptr<MultiMediaSourceMuxer> getMuxer(MediaSource &sender) { return nullptr; }
class SendRtpArgs {
public:
......@@ -136,17 +139,30 @@ class ProtocolOption {
public:
ProtocolOption();
//时间戳修复这一路流标志位
bool modify_stamp;
enum {
kModifyStampOff = 0, // 采用源视频流绝对时间戳,不做任何改变
kModifyStampSystem = 1, // 采用zlmediakit接收数据时的系统时间戳(有平滑处理)
kModifyStampRelative = 2 // 采用源视频流时间戳相对时间戳(增长量),有做时间戳跳跃和回退矫正
};
// 时间戳类型
int modify_stamp;
//转协议是否开启音频
bool enable_audio;
//添加静音音频,在关闭音频时,此开关无效
bool add_mute_audio;
// 无人观看时,是否直接关闭(而不是通过on_none_reader hook返回close)
// 此配置置1时,此流如果无人观看,将不触发on_none_reader hook回调,
// 而是将直接关闭流
bool auto_close;
//断连续推延时,单位毫秒,默认采用配置文件
uint32_t continue_push_ms;
//是否开启转换为hls
//是否开启转换为hls(mpegts)
bool enable_hls;
//是否开启转换为hls(fmp4)
bool enable_hls_fmp4;
//是否开启MP4录制
bool enable_mp4;
//是否开启转换为rtsp/webrtc
......@@ -179,15 +195,20 @@ public:
//hls录制保存路径
std::string hls_save_path;
// 支持通过on_publish返回值替换stream_id
std::string stream_replace;
template <typename MAP>
ProtocolOption(const MAP &allArgs) : ProtocolOption() {
#define GET_OPT_VALUE(key) getArgsValue(allArgs, #key, key)
GET_OPT_VALUE(modify_stamp);
GET_OPT_VALUE(enable_audio);
GET_OPT_VALUE(add_mute_audio);
GET_OPT_VALUE(auto_close);
GET_OPT_VALUE(continue_push_ms);
GET_OPT_VALUE(enable_hls);
GET_OPT_VALUE(enable_hls_fmp4);
GET_OPT_VALUE(enable_mp4);
GET_OPT_VALUE(enable_rtsp);
GET_OPT_VALUE(enable_rtmp);
......@@ -205,6 +226,7 @@ public:
GET_OPT_VALUE(mp4_save_path);
GET_OPT_VALUE(hls_save_path);
GET_OPT_VALUE(stream_replace);
}
private:
......@@ -244,6 +266,7 @@ public:
bool stopSendRtp(MediaSource &sender, const std::string &ssrc) override;
float getLossRate(MediaSource &sender, TrackType type) override;
toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override;
std::shared_ptr<MultiMediaSourceMuxer> getMuxer(MediaSource &sender) override;
private:
std::weak_ptr<MediaSourceEvent> _listener;
......@@ -252,26 +275,24 @@ private:
/**
* 解析url获取媒体相关信息
*/
class MediaInfo {
class MediaInfo: public MediaTuple {
public:
~MediaInfo() = default;
MediaInfo() = default;
MediaInfo(const std::string &url) { parse(url); }
void parse(const std::string &url);
std::string shortUrl() const { return _vhost + "/" + _app + "/" + _streamid; }
std::string getUrl() const { return _schema + "://" + shortUrl(); }
std::string getUrl() const { return schema + "://" + shortUrl(); }
public:
uint16_t _port = 0;
std::string _full_url;
std::string _schema;
std::string _host;
std::string _vhost;
std::string _app;
std::string _streamid;
std::string _param_strs;
uint16_t port = 0;
std::string full_url;
std::string schema;
std::string host;
std::string param_strs;
};
bool equalMediaTuple(const MediaTuple& a, const MediaTuple& b);
/**
* 媒体源,任何rtsp/rtmp的直播流都源自该对象
*/
......@@ -280,23 +301,21 @@ public:
static MediaSource& NullMediaSource();
using Ptr = std::shared_ptr<MediaSource>;
MediaSource(const std::string &schema, const std::string &vhost, const std::string &app, const std::string &stream_id);
MediaSource(const std::string &schema, const MediaTuple& tuple);
virtual ~MediaSource();
////////////////获取MediaSource相关信息////////////////
// 获取协议类型
const std::string& getSchema() const;
// 虚拟主机
const std::string& getVhost() const;
// 应用名
const std::string& getApp() const;
// 流id
const std::string& getId() const;
const std::string& getSchema() const {
return _schema;
}
std::string shortUrl() const { return _vhost + "/" + _app + "/" + _stream_id; }
const MediaTuple& getMediaTuple() const {
return _tuple;
}
std::string getUrl() const { return _schema + "://" + shortUrl(); }
std::string getUrl() const { return _schema + "://" + _tuple.shortUrl(); }
//获取对象所有权
std::shared_ptr<void> getOwnership();
......@@ -321,19 +340,21 @@ public:
// 设置监听者
virtual void setListener(const std::weak_ptr<MediaSourceEvent> &listener);
// 获取监听者
std::weak_ptr<MediaSourceEvent> getListener(bool next = false) const;
std::weak_ptr<MediaSourceEvent> getListener() const;
// 本协议获取观看者个数,可能返回本协议的观看人数,也可能返回总人数
virtual int readerCount() = 0;
// 观看者个数,包括(hls/rtsp/rtmp)
virtual int totalReaderCount();
// 获取播放器列表
virtual void getPlayerList(const std::function<void(const std::list<std::shared_ptr<void>> &info_list)> &cb,
const std::function<std::shared_ptr<void>(std::shared_ptr<void> &&info)> &on_change) {
virtual void getPlayerList(const std::function<void(const std::list<toolkit::Any> &info_list)> &cb,
const std::function<toolkit::Any(toolkit::Any &&info)> &on_change) {
assert(cb);
cb(std::list<std::shared_ptr<void>>());
cb(std::list<toolkit::Any>());
}
virtual bool broadcastMessage(const toolkit::Any &data) { return false; }
// 获取媒体源类型
MediaOriginType getOriginType() const;
// 获取媒体源url或者文件路径
......@@ -363,13 +384,15 @@ public:
float getLossRate(mediakit::TrackType type);
// 获取所在线程
toolkit::EventPoller::Ptr getOwnerPoller();
// 获取MultiMediaSourceMuxer对象
std::shared_ptr<MultiMediaSourceMuxer> getMuxer();
////////////////static方法,查找或生成MediaSource////////////////
// 同步查找流
static Ptr find(const std::string &schema, const std::string &vhost, const std::string &app, const std::string &id, bool from_mp4 = false);
static Ptr find(const MediaInfo &info, bool from_mp4 = false) {
return find(info._schema, info._vhost, info._app, info._streamid, from_mp4);
return find(info.schema, info.vhost, info.app, info.stream, from_mp4);
}
// 忽略schema,同步查找流,可能返回rtmp/rtsp/hls类型
......@@ -394,15 +417,13 @@ private:
protected:
toolkit::BytesSpeed _speed[TrackMax];
MediaTuple _tuple;
private:
std::atomic_flag _owned { false };
time_t _create_stamp;
toolkit::Ticker _ticker;
std::string _schema;
std::string _vhost;
std::string _app;
std::string _stream_id;
std::weak_ptr<MediaSourceEvent> _listener;
// 对象个数统计
toolkit::ObjectStatistic<MediaSource> _statistic;
......
......@@ -37,7 +37,7 @@ public:
virtual void onAllTrackReady() = 0;
};
MultiMediaSourceMuxer(const std::string &vhost, const std::string &app, const std::string &stream, float dur_sec = 0.0,const ProtocolOption &option = ProtocolOption());
MultiMediaSourceMuxer(const MediaTuple& tuple, float dur_sec = 0.0,const ProtocolOption &option = ProtocolOption());
~MultiMediaSourceMuxer() override = default;
/**
......@@ -131,9 +131,13 @@ public:
*/
toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override;
const std::string& getVhost() const;
const std::string& getApp() const;
const std::string& getStreamId() const;
/**
* 获取本对象
*/
std::shared_ptr<MultiMediaSourceMuxer> getMuxer(MediaSource &sender) override;
const ProtocolOption &getOption() const;
const MediaTuple &getMediaTuple() const;
std::string shortUrl() const;
protected:
......@@ -164,25 +168,19 @@ private:
bool _is_enable = false;
bool _create_in_poller = false;
bool _video_key_pos = false;
std::string _vhost;
std::string _app;
std::string _stream_id;
MediaTuple _tuple;
ProtocolOption _option;
toolkit::Ticker _last_check;
Stamp _stamp[2];
std::weak_ptr<Listener> _track_listener;
#if defined(ENABLE_RTPPROXY)
std::unordered_map<std::string, RingType::RingReader::Ptr> _rtp_sender;
#endif //ENABLE_RTPPROXY
#if defined(ENABLE_MP4)
FMP4MediaSourceMuxer::Ptr _fmp4;
#endif
RtmpMediaSourceMuxer::Ptr _rtmp;
RtspMediaSourceMuxer::Ptr _rtsp;
TSMediaSourceMuxer::Ptr _ts;
MediaSinkInterface::Ptr _mp4;
HlsRecorder::Ptr _hls;
HlsFMP4Recorder::Ptr _hls_fmp4;
toolkit::EventPoller::Ptr _poller;
RingType::Ptr _ring;
......
......@@ -17,15 +17,13 @@
namespace mediakit {
//从字符串中提取子字符串
std::string FindField(const char *buf, const char *start, const char *end, size_t bufSize = 0);
//把url解析为主机地址和端口号,兼容ipv4/ipv6/dns
void splitUrl(const std::string &url, std::string &host, uint16_t& port);
// 从字符串中提取子字符串
std::string findSubString(const char *buf, const char *start, const char *end, size_t buf_size = 0);
// 把url解析为主机地址和端口号,兼容ipv4/ipv6/dns
void splitUrl(const std::string &url, std::string &host, uint16_t &port);
struct StrCaseCompare {
bool operator()(const std::string &__x, const std::string &__y) const {
return strcasecmp(__x.data(), __y.data()) < 0;
}
bool operator()(const std::string &__x, const std::string &__y) const { return strcasecmp(__x.data(), __y.data()) < 0; }
};
class StrCaseMap : public std::multimap<std::string, std::string, StrCaseCompare> {
......@@ -42,84 +40,87 @@ public:
return it->second;
}
template<typename V>
void emplace(const std::string &k, V &&v) {
template <typename K, typename V>
void emplace(K &&k, V &&v) {
auto it = find(k);
if (it != end()) {
return;
}
Super::emplace(k, std::forward<V>(v));
Super::emplace(std::forward<K>(k), std::forward<V>(v));
}
template<typename V>
void emplace_force(const std::string k, V &&v) {
Super::emplace(k, std::forward<V>(v));
template <typename K, typename V>
void emplace_force(K &&k, V &&v) {
Super::emplace(std::forward<K>(k), std::forward<V>(v));
}
};
//rtsp/http/sip解析类
// rtsp/http/sip解析类
class Parser {
public:
Parser() = default;
~Parser() = default;
//解析信令
void Parse(const char *buf);
// 解析http/rtsp/sip请求,需要确保buf以\0结尾
void parse(const char *buf, size_t size);
//获取命令字
const std::string &Method() const;
// 获取命令字,如GET/POST
const std::string &method() const;
//获取中间url,不包含?后面的参数
const std::string &Url() const;
// 请求时,获取中间url,不包含?后面的参数
const std::string &url() const;
// 回复时,获取状态码,如200/404
const std::string &status() const;
//获取中间url,包含?后面的参数
std::string FullUrl() const;
// 获取中间url,包含?后面的参数
std::string fullUrl() const;
//获取命令协议名
const std::string &Tail() const;
// 请求时,获取协议名,如HTTP/1.1
const std::string &protocol() const;
// 回复时,获取状态字符串,如 OK/Not Found
const std::string &statusStr() const;
//根据header key名,获取请求header value值
// 根据header key名,获取请求header value值
const std::string &operator[](const char *name) const;
//获取http body或sdp
const std::string &Content() const;
// 获取http body或sdp
const std::string &content() const;
//清空,为了重用
void Clear();
// 清空,为了重用
void clear();
//获取?后面的参数
const std::string &Params() const;
// 获取?后面的参数
const std::string &params() const;
//重新设置url
// 重新设置url
void setUrl(std::string url);
//重新设置content
// 重新设置content
void setContent(std::string content);
//获取header列表
// 获取header列表
StrCaseMap &getHeader() const;
//获取url参数列表
// 获取url参数列表
StrCaseMap &getUrlArgs() const;
//解析?后面的参数
// 解析?后面的参数
static StrCaseMap parseArgs(const std::string &str, const char *pair_delim = "&", const char *key_delim = "=");
static std::string merge_url(const std::string &base_url, const std::string &path);
static std::string mergeUrl(const std::string &base_url, const std::string &path);
private:
std::string _strMethod;
std::string _strUrl;
std::string _strTail;
std::string _strContent;
std::string _strNull;
std::string _method;
std::string _url;
std::string _protocol;
std::string _content;
std::string _params;
mutable StrCaseMap _mapHeaders;
mutable StrCaseMap _mapUrlArgs;
mutable StrCaseMap _headers;
mutable StrCaseMap _url_args;
};
//解析rtsp url的工具类
class RtspUrl{
// 解析rtsp url的工具类
class RtspUrl {
public:
bool _is_ssl;
uint16_t _port;
......@@ -134,9 +135,9 @@ public:
void parse(const std::string &url);
private:
void setup(bool,const std::string &, const std::string &, const std::string &);
void setup(bool, const std::string &, const std::string &, const std::string &);
};
}//namespace mediakit
} // namespace mediakit
#endif //ZLMEDIAKIT_PARSER_H
#endif // ZLMEDIAKIT_PARSER_H
......@@ -125,12 +125,12 @@ public:
uint64_t getNtpStamp(uint32_t rtp_stamp, uint32_t sample_rate);
private:
void update(uint32_t rtp_stamp, uint64_t ntp_stamp_ms);
uint64_t getNtpStamp_l(uint32_t rtp_stamp, uint32_t sample_rate);
void update(uint32_t rtp_stamp, uint64_t ntp_stamp_us);
uint64_t getNtpStampUS(uint32_t rtp_stamp, uint32_t sample_rate);
private:
uint32_t _last_rtp_stamp = 0;
uint64_t _last_ntp_stamp_ms = 0;
uint64_t _last_ntp_stamp_us = 0;
};
}//namespace mediakit
......
......@@ -9,6 +9,7 @@
*/
#include "Common/config.h"
#include "MediaSource.h"
#include "Util/NoticeCenter.h"
#include "Util/logger.h"
#include "Util/onceToken.h"
......@@ -30,7 +31,7 @@ bool loadIniConfig(const char *ini_path) {
}
try {
mINI::Instance().parseFile(ini);
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastReloadConfig);
NOTICE_EMIT(BroadcastReloadConfigArgs, Broadcast::kBroadcastReloadConfig);
return true;
} catch (std::exception &) {
InfoL << "dump ini file to:" << ini;
......@@ -98,9 +99,11 @@ namespace Protocol {
const string kModifyStamp = PROTOCOL_FIELD "modify_stamp";
const string kEnableAudio = PROTOCOL_FIELD "enable_audio";
const string kAddMuteAudio = PROTOCOL_FIELD "add_mute_audio";
const string kAutoClose = PROTOCOL_FIELD "auto_close";
const string kContinuePushMS = PROTOCOL_FIELD "continue_push_ms";
const string kEnableHls = PROTOCOL_FIELD "enable_hls";
const string kEnableHlsFmp4 = PROTOCOL_FIELD "enable_hls_fmp4";
const string kEnableMP4 = PROTOCOL_FIELD "enable_mp4";
const string kEnableRtsp = PROTOCOL_FIELD "enable_rtsp";
const string kEnableRtmp = PROTOCOL_FIELD "enable_rtmp";
......@@ -120,12 +123,14 @@ const string kTSDemand = PROTOCOL_FIELD "ts_demand";
const string kFMP4Demand = PROTOCOL_FIELD "fmp4_demand";
static onceToken token([]() {
mINI::Instance()[kModifyStamp] = 0;
mINI::Instance()[kModifyStamp] = (int)ProtocolOption::kModifyStampRelative;
mINI::Instance()[kEnableAudio] = 1;
mINI::Instance()[kAddMuteAudio] = 1;
mINI::Instance()[kContinuePushMS] = 15000;
mINI::Instance()[kAutoClose] = 0;
mINI::Instance()[kEnableHls] = 1;
mINI::Instance()[kEnableHlsFmp4] = 0;
mINI::Instance()[kEnableMP4] = 0;
mINI::Instance()[kEnableRtsp] = 1;
mINI::Instance()[kEnableRtmp] = 1;
......@@ -159,6 +164,8 @@ const string kNotFound = HTTP_FIELD "notFound";
const string kDirMenu = HTTP_FIELD "dirMenu";
const string kForbidCacheSuffix = HTTP_FIELD "forbidCacheSuffix";
const string kForwardedIpHeader = HTTP_FIELD "forwarded_ip_header";
const string kAllowCrossDomains = HTTP_FIELD "allow_cross_domains";
const string kAllowIPRange = HTTP_FIELD "allow_ip_range";
static onceToken token([]() {
mINI::Instance()[kSendBufSize] = 64 * 1024;
......@@ -186,6 +193,8 @@ static onceToken token([]() {
<< endl;
mINI::Instance()[kForbidCacheSuffix] = "";
mINI::Instance()[kForwardedIpHeader] = "";
mINI::Instance()[kAllowCrossDomains] = 1;
mINI::Instance()[kAllowIPRange] = "::1,127.0.0.1,172.16.0.0-172.31.255.255,192.168.0.0-192.168.255.255,10.0.0.0-10.255.255.255";
});
} // namespace Http
......@@ -206,6 +215,7 @@ const string kHandshakeSecond = RTSP_FIELD "handshakeSecond";
const string kKeepAliveSecond = RTSP_FIELD "keepAliveSecond";
const string kDirectProxy = RTSP_FIELD "directProxy";
const string kLowLatency = RTSP_FIELD"lowLatency";
const string kRtpTransportType = RTSP_FIELD"rtpTransportType";
static onceToken token([]() {
// 默认Md5方式认证
......@@ -214,18 +224,17 @@ static onceToken token([]() {
mINI::Instance()[kKeepAliveSecond] = 15;
mINI::Instance()[kDirectProxy] = 1;
mINI::Instance()[kLowLatency] = 0;
mINI::Instance()[kRtpTransportType] = -1;
});
} // namespace Rtsp
////////////RTMP服务器配置///////////
namespace Rtmp {
#define RTMP_FIELD "rtmp."
const string kModifyStamp = RTMP_FIELD "modifyStamp";
const string kHandshakeSecond = RTMP_FIELD "handshakeSecond";
const string kKeepAliveSecond = RTMP_FIELD "keepAliveSecond";
static onceToken token([]() {
mINI::Instance()[kModifyStamp] = false;
mINI::Instance()[kHandshakeSecond] = 15;
mINI::Instance()[kKeepAliveSecond] = 15;
});
......@@ -239,15 +248,15 @@ const string kVideoMtuSize = RTP_FIELD "videoMtuSize";
const string kAudioMtuSize = RTP_FIELD "audioMtuSize";
// rtp包最大长度限制,单位是KB
const string kRtpMaxSize = RTP_FIELD "rtpMaxSize";
const string kLowLatency = RTP_FIELD "lowLatency";
const string kH264StapA = RTP_FIELD "h264_stap_a";
static onceToken token([]() {
mINI::Instance()[kVideoMtuSize] = 1400;
mINI::Instance()[kAudioMtuSize] = 600;
mINI::Instance()[kRtpMaxSize] = 10;
mINI::Instance()[kLowLatency] = 0;
mINI::Instance()[kH264StapA] = 1;
});
} // namespace Rtp
......
......@@ -99,7 +99,7 @@ extern const std::string kBroadcastStreamNoneReader;
// rtp推流被动停止时触发
extern const std::string kBroadcastSendRtpStopped;
#define BroadcastSendRtpStopped MultiMediaSourceMuxer &sender, const std::string &ssrc, const SockException &ex
#define BroadcastSendRtpStoppedArgs MultiMediaSourceMuxer &sender, const std::string &ssrc, const SockException &ex
// 更新配置文件事件广播,执行loadIniConfig函数加载配置文件成功后会触发该广播
extern const std::string kBroadcastReloadConfig;
......@@ -107,7 +107,7 @@ extern const std::string kBroadcastReloadConfig;
// rtp server 超时
extern const std::string KBroadcastRtpServerTimeout;
#define BroadcastRtpServerTimeout uint16_t &local_port, const string &stream_id,int &tcp_mode, bool &re_use_port, uint32_t &ssrc
#define BroadcastRtpServerTimeoutArgs uint16_t &local_port, const string &stream_id,int &tcp_mode, bool &re_use_port, uint32_t &ssrc
#define ReloadConfigTag ((void *)(0xFF))
#define RELOAD_KEY(arg, key) \
......@@ -190,11 +190,17 @@ extern const std::string kModifyStamp;
extern const std::string kEnableAudio;
//添加静音音频,在关闭音频时,此开关无效
extern const std::string kAddMuteAudio;
// 无人观看时,是否直接关闭(而不是通过on_none_reader hook返回close)
// 此配置置1时,此流如果无人观看,将不触发on_none_reader hook回调,
// 而是将直接关闭流
extern const std::string kAutoClose;
//断连续推延时,单位毫秒,默认采用配置文件
extern const std::string kContinuePushMS;
//是否开启转换为hls
//是否开启转换为hls(mpegts)
extern const std::string kEnableHls;
//是否开启转换为hls(fmp4)
extern const std::string kEnableHlsFmp4;
//是否开启MP4录制
extern const std::string kEnableMP4;
//是否开启转换为rtsp/webrtc
......@@ -246,6 +252,10 @@ extern const std::string kDirMenu;
extern const std::string kForbidCacheSuffix;
// 可以把http代理前真实客户端ip放在http头中:https://github.com/ZLMediaKit/ZLMediaKit/issues/1388
extern const std::string kForwardedIpHeader;
// 是否允许所有跨域请求
extern const std::string kAllowCrossDomains;
// 允许访问http api和http文件索引的ip地址范围白名单,置空情况下不做限制
extern const std::string kAllowIPRange;
} // namespace Http
////////////SHELL配置///////////
......@@ -271,6 +281,11 @@ extern const std::string kDirectProxy;
// rtsp 转发是否使用低延迟模式,当开启时,不会缓存rtp包,来提高并发,可以降低一帧的延迟
extern const std::string kLowLatency;
//强制协商rtp传输方式 (0:TCP,1:UDP,2:MULTICAST,-1:不限制)
//当客户端发起RTSP SETUP的时候如果传输类型和此配置不一致则返回461 Unsupport Transport
//迫使客户端重新SETUP并切换到对应协议。目前支持FFMPEG和VLC
extern const std::string kRtpTransportType;
} // namespace Rtsp
////////////RTMP服务器配置///////////
......@@ -291,6 +306,8 @@ extern const std::string kAudioMtuSize;
extern const std::string kRtpMaxSize;
// rtp 打包时,低延迟开关,默认关闭(为0),h264存在一帧多个slice(NAL)的情况,在这种情况下,如果开启可能会导致画面花屏
extern const std::string kLowLatency;
//H264 rtp打包模式是否采用stap-a模式(为了在老版本浏览器上兼容webrtc)还是采用Single NAL unit packet per H.264 模式
extern const std::string kH264StapA;
} // namespace Rtp
////////////组播配置///////////
......
......@@ -32,14 +32,6 @@
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#endif
#ifndef PACKED
#if !defined(_WIN32)
#define PACKED __attribute__((packed))
#else
#define PACKED
#endif //! defined(_WIN32)
#endif
#ifndef CHECK
#define CHECK(exp, ...) ::mediakit::Assert_ThrowCpp(!(exp), #exp, __FUNCTION__, __FILE__, __LINE__, ##__VA_ARGS__)
#endif // CHECK
......@@ -67,6 +59,7 @@
#define HLS_SCHEMA "hls"
#define TS_SCHEMA "ts"
#define FMP4_SCHEMA "fmp4"
#define HLS_FMP4_SCHEMA "hls.fmp4"
#define SRT_SCHEMA "srt"
#define DEFAULT_VHOST "__defaultVhost__"
......
......@@ -13,7 +13,7 @@
#include <iostream>
#include <string>
#include <cstdint>
namespace mediakit {
class strCoding {
......
......@@ -246,7 +246,7 @@ AACTrack::AACTrack(const string &aac_cfg) {
onReady();
}
const string &AACTrack::getAacCfg() const {
const string &AACTrack::getConfig() const {
return _cfg;
}
......@@ -342,7 +342,7 @@ Sdp::Ptr AACTrack::getSdp() {
WarnL << getCodecName() << " Track未准备好";
return nullptr;
}
return std::make_shared<AACSdp>(getAacCfg(), getAudioSampleRate(), getAudioChannel(), getBitRate() / 1024);
return std::make_shared<AACSdp>(getConfig(), getAudioSampleRate(), getAudioChannel(), getBitRate() / 1024);
}
}//namespace mediakit
\ No newline at end of file
......@@ -44,7 +44,7 @@ public:
/**
* 获取aac 配置信息
*/
const std::string &getAacCfg() const;
const std::string &getConfig() const;
bool ready() override;
CodecId getCodecId() const override;
......
......@@ -16,12 +16,9 @@ using namespace toolkit;
namespace mediakit {
static string getAacCfg(const RtmpPacket &thiz) {
static string getConfig(const RtmpPacket &thiz) {
string ret;
if (thiz.getMediaType() != FLV_CODEC_AAC) {
return ret;
}
if (!thiz.isCfgFrame()) {
if ((RtmpAudioCodec)thiz.getRtmpCodecId() != RtmpAudioCodec::aac) {
return ret;
}
if (thiz.buffer.size() < 4) {
......@@ -33,8 +30,8 @@ static string getAacCfg(const RtmpPacket &thiz) {
}
void AACRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt) {
if (pkt->isCfgFrame()) {
_aac_cfg = getAacCfg(*pkt);
if (pkt->isConfigFrame()) {
_aac_cfg = getConfig(*pkt);
if (!_aac_cfg.empty()) {
onGetAAC(nullptr, 0, 0);
}
......@@ -82,7 +79,7 @@ AACRtmpEncoder::AACRtmpEncoder(const Track::Ptr &track) {
void AACRtmpEncoder::makeConfigPacket() {
if (_track && _track->ready()) {
//从track中和获取aac配置信息
_aac_cfg = _track->getAacCfg();
_aac_cfg = _track->getConfig();
}
if (!_aac_cfg.empty()) {
......@@ -93,51 +90,45 @@ void AACRtmpEncoder::makeConfigPacket() {
bool AACRtmpEncoder::inputFrame(const Frame::Ptr &frame) {
if (_aac_cfg.empty()) {
if (frame->prefixSize()) {
//包含adts头,从adts头获取aac配置信息
_aac_cfg = makeAacConfig((uint8_t *) (frame->data()), frame->prefixSize());
// 包含adts头,从adts头获取aac配置信息
_aac_cfg = makeAacConfig((uint8_t *)(frame->data()), frame->prefixSize());
}
makeConfigPacket();
}
if(_aac_cfg.empty()){
if (_aac_cfg.empty()) {
return false;
}
auto rtmpPkt = RtmpPacket::create();
//header
uint8_t is_config = false;
rtmpPkt->buffer.push_back(_audio_flv_flags);
rtmpPkt->buffer.push_back(!is_config);
//aac data
rtmpPkt->buffer.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
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);
auto pkt = RtmpPacket::create();
// header
pkt->buffer.push_back(_audio_flv_flags);
pkt->buffer.push_back((uint8_t)RtmpAACPacketType::aac_raw);
// aac data
pkt->buffer.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize());
pkt->body_size = pkt->buffer.size();
pkt->chunk_id = CHUNK_AUDIO;
pkt->stream_index = STREAM_MEDIA;
pkt->time_stamp = frame->dts();
pkt->type_id = MSG_AUDIO;
RtmpCodec::inputRtmp(pkt);
return true;
}
void AACRtmpEncoder::makeAudioConfigPkt() {
_audio_flv_flags = getAudioRtmpFlags(std::make_shared<AACTrack>(_aac_cfg));
auto rtmpPkt = RtmpPacket::create();
//header
uint8_t is_config = true;
rtmpPkt->buffer.push_back(_audio_flv_flags);
rtmpPkt->buffer.push_back(!is_config);
//aac config
rtmpPkt->buffer.append(_aac_cfg);
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);
auto pkt = RtmpPacket::create();
// header
pkt->buffer.push_back(_audio_flv_flags);
pkt->buffer.push_back((uint8_t)RtmpAACPacketType::aac_config_header);
// aac config
pkt->buffer.append(_aac_cfg);
pkt->body_size = pkt->buffer.size();
pkt->chunk_id = CHUNK_AUDIO;
pkt->stream_index = STREAM_MEDIA;
pkt->time_stamp = 0;
pkt->type_id = MSG_AUDIO;
RtmpCodec::inputRtmp(pkt);
}
}//namespace mediakit
\ No newline at end of file
......@@ -64,7 +64,7 @@ AACRtpDecoder::AACRtpDecoder(const Track::Ptr &track) {
if (!aacTrack || !aacTrack->ready()) {
WarnL << "该aac track无效!";
} else {
_aac_cfg = aacTrack->getAacCfg();
_aac_cfg = aacTrack->getConfig();
}
obtainFrame();
}
......
......@@ -34,10 +34,10 @@ bool CommonRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool){
return false;
}
auto payload = rtp->getPayload();
auto stamp = rtp->getStampMS();
auto stamp = rtp->getStamp();
auto seq = rtp->getSeq();
if (_frame->_dts != stamp || _frame->_buffer.size() > _max_frame_size) {
if (_last_stamp != stamp || _frame->_buffer.size() > _max_frame_size) {
//时间戳发生变化或者缓存超过MAX_FRAME_SIZE,则清空上帧数据
if (!_frame->_buffer.empty()) {
//有有效帧,则输出
......@@ -46,7 +46,8 @@ bool CommonRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool){
//新的一帧数据
obtainFrame();
_frame->_dts = stamp;
_frame->_dts = rtp->getStampMS();
_last_stamp = stamp;
_drop_flag = false;
} else if (_last_seq != 0 && (uint16_t)(_last_seq + 1) != seq) {
//时间戳未发生变化,但是seq却不连续,说明中间rtp丢包了,那么整帧应该废弃
......
......@@ -50,6 +50,7 @@ private:
private:
bool _drop_flag = false;
uint16_t _last_seq = 0;
uint64_t _last_stamp = 0;
size_t _max_frame_size;
CodecId _codec;
FrameImp::Ptr _frame;
......
......@@ -45,9 +45,9 @@ Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) {
case CodecOpus : return std::make_shared<OpusTrack>();
case CodecAAC : {
string aac_cfg_str = FindField(track->_fmtp.data(), "config=", ";");
string aac_cfg_str = findSubString(track->_fmtp.data(), "config=", ";");
if (aac_cfg_str.empty()) {
aac_cfg_str = FindField(track->_fmtp.data(), "config=", nullptr);
aac_cfg_str = findSubString(track->_fmtp.data(), "config=", nullptr);
}
if (aac_cfg_str.empty()) {
//如果sdp中获取不到aac config信息,那么在rtp也无法获取,那么忽略该Track
......@@ -67,8 +67,8 @@ Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) {
//a=fmtp:96 packetization-mode=1;profile-level-id=42C01F;sprop-parameter-sets=Z0LAH9oBQBboQAAAAwBAAAAPI8YMqA==,aM48gA==
auto map = Parser::parseArgs(track->_fmtp, ";", "=");
auto sps_pps = map["sprop-parameter-sets"];
string base64_SPS = FindField(sps_pps.data(), NULL, ",");
string base64_PPS = FindField(sps_pps.data(), ",", NULL);
string base64_SPS = findSubString(sps_pps.data(), NULL, ",");
string base64_PPS = findSubString(sps_pps.data(), ",", NULL);
auto sps = decodeBase64(base64_SPS);
auto pps = decodeBase64(base64_PPS);
if (sps.empty() || pps.empty()) {
......@@ -201,17 +201,20 @@ static CodecId getVideoCodecIdByAmf(const AMFValue &val){
}
if (val.type() != AMF_NULL) {
auto type_id = val.as_integer();
auto type_id = (RtmpVideoCodec)val.as_integer();
switch (type_id) {
case FLV_CODEC_H264 : return CodecH264;
case FLV_CODEC_H265 : return CodecH265;
default : WarnL << "暂不支持该视频Amf:" << type_id; return CodecInvalid;
case RtmpVideoCodec::h264: return CodecH264;
case RtmpVideoCodec::fourcc_hevc:
case RtmpVideoCodec::h265: return CodecH265;
case RtmpVideoCodec::fourcc_av1: return CodecAV1;
case RtmpVideoCodec::fourcc_vp9: return CodecVP9;
default: WarnL << "暂不支持该视频Amf:" << (int)type_id; return CodecInvalid;
}
}
return CodecInvalid;
}
Track::Ptr getTrackByCodecId(CodecId codecId, int sample_rate = 0, int channels = 0, int sample_bit = 0) {
Track::Ptr Factory::getTrackByCodecId(CodecId codecId, int sample_rate, int channels, int sample_bit) {
switch (codecId){
case CodecH264 : return std::make_shared<H264Track>();
case CodecH265 : return std::make_shared<H265Track>();
......@@ -243,13 +246,13 @@ static CodecId getAudioCodecIdByAmf(const AMFValue &val) {
}
if (val.type() != AMF_NULL) {
auto type_id = val.as_integer();
auto type_id = (RtmpAudioCodec)val.as_integer();
switch (type_id) {
case FLV_CODEC_AAC : return CodecAAC;
case FLV_CODEC_G711A : return CodecG711A;
case FLV_CODEC_G711U : return CodecG711U;
case FLV_CODEC_OPUS : return CodecOpus;
default : WarnL << "暂不支持该音频Amf:" << type_id; return CodecInvalid;
case RtmpAudioCodec::aac : return CodecAAC;
case RtmpAudioCodec::g711a : return CodecG711A;
case RtmpAudioCodec::g711u : return CodecG711U;
case RtmpAudioCodec::opus : return CodecOpus;
default : WarnL << "暂不支持该音频Amf:" << (int)type_id; return CodecInvalid;
}
}
......@@ -291,13 +294,13 @@ RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track, bool is_enc
}
AMFValue Factory::getAmfByCodecId(CodecId codecId) {
switch (codecId){
case CodecAAC: return AMFValue(FLV_CODEC_AAC);
case CodecH264: return AMFValue(FLV_CODEC_H264);
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);
switch (codecId) {
case CodecAAC: return AMFValue((int)RtmpAudioCodec::aac);
case CodecH264: return AMFValue((int)RtmpVideoCodec::h264);
case CodecH265: return AMFValue((int)RtmpVideoCodec::h265);
case CodecG711A: return AMFValue((int)RtmpAudioCodec::g711a);
case CodecG711U: return AMFValue((int)RtmpAudioCodec::g711u);
case CodecOpus: return AMFValue((int)RtmpAudioCodec::opus);
default: return AMFValue(AMF_NULL);
}
}
......
......@@ -21,6 +21,16 @@ namespace mediakit{
class Factory {
public:
/**
* 根据codec_id 获取track
* @param codecId 编码id
* @param sample_rate 采样率,视频固定为90000
* @param channels 音频通道数
* @param sample_bit 音频采样位数
*/
static Track::Ptr getTrackByCodecId(CodecId codecId, int sample_rate = 0, int channels = 0, int sample_bit = 0);
////////////////////////////////rtsp相关//////////////////////////////////
/**
* 根据sdp生成Track对象
......
......@@ -13,6 +13,7 @@
#include "H265.h"
#include "Common/Parser.h"
#include "Common/Stamp.h"
#include "Common/MediaSource.h"
using namespace std;
using namespace toolkit;
......@@ -31,11 +32,11 @@ Frame::Ptr Frame::getCacheAbleFrame(const Frame::Ptr &frame){
return std::make_shared<FrameCacheAble>(frame);
}
FrameStamp::FrameStamp(Frame::Ptr frame, Stamp &stamp, bool modify_stamp)
FrameStamp::FrameStamp(Frame::Ptr frame, Stamp &stamp, int modify_stamp)
{
_frame = std::move(frame);
//覆盖时间戳
stamp.revise(_frame->dts(), _frame->pts(), _dts, _pts, modify_stamp);
// kModifyStampSystem时采用系统时间戳,kModifyStampRelative采用相对时间戳
stamp.revise(_frame->dts(), _frame->pts(), _dts, _pts, modify_stamp == ProtocolOption::kModifyStampSystem);
}
TrackType getTrackType(CodecId codecId) {
......
......@@ -40,7 +40,7 @@ typedef enum {
XX(CodecVP8, TrackVideo, 7, "VP8", PSI_STREAM_VP8) \
XX(CodecVP9, TrackVideo, 8, "VP9", PSI_STREAM_VP9) \
XX(CodecAV1, TrackVideo, 9, "AV1", PSI_STREAM_AV1) \
XX(CodecJPEG, TrackVideo, 10, "JPEG", PSI_STREAM_JPEG_2000)
XX(CodecJPEG, TrackVideo, 10, "JPEG", PSI_STREAM_RESERVED)
typedef enum {
CodecInvalid = -1,
......@@ -492,7 +492,7 @@ private:
class FrameStamp : public Frame {
public:
using Ptr = std::shared_ptr<FrameStamp>;
FrameStamp(Frame::Ptr frame, Stamp &stamp, bool modify_stamp);
FrameStamp(Frame::Ptr frame, Stamp &stamp, int modify_stamp);
~FrameStamp() override {}
uint64_t dts() const override { return (uint64_t)_dts; }
......
......@@ -12,9 +12,10 @@
#include "SPSParser.h"
#include "Util/logger.h"
#include "Util/base64.h"
#include "Common/config.h"
using namespace toolkit;
using namespace std;
using namespace toolkit;
namespace mediakit {
......@@ -248,7 +249,14 @@ public:
_printer << "b=AS:" << bitrate << "\r\n";
}
_printer << "a=rtpmap:" << payload_type << " " << getCodecName() << "/" << 90000 << "\r\n";
_printer << "a=fmtp:" << payload_type << " packetization-mode=1; profile-level-id=";
/**
Single NAI Unit Mode = 0. // Single NAI mode (Only nals from 1-23 are allowed)
Non Interleaved Mode = 1,// Non-interleaved Mode: 1-23,24 (STAP-A),28 (FU-A) are allowed
Interleaved Mode = 2, // 25 (STAP-B),26 (MTAP16),27 (MTAP24),28 (EU-A),and 29 (EU-B) are allowed.
**/
GET_CONFIG(bool, h264_stap_a, Rtp::kH264StapA);
_printer << "a=fmtp:" << payload_type << " packetization-mode=" << h264_stap_a << "; profile-level-id=";
uint32_t profile_level_id = 0;
if (strSPS.length() >= 4) { // sanity check
......
......@@ -30,10 +30,7 @@ H264Frame::Ptr H264RtmpDecoder::obtainFrame() {
* 返回不带0x00 00 00 01头的sps pps
*/
static bool getH264Config(const RtmpPacket &thiz, string &sps, string &pps) {
if (thiz.getMediaType() != FLV_CODEC_H264) {
return false;
}
if (!thiz.isCfgFrame()) {
if ((RtmpVideoCodec)thiz.getRtmpCodecId() != RtmpVideoCodec::h264) {
return false;
}
if (thiz.buffer.size() < 13) {
......@@ -59,7 +56,7 @@ static bool getH264Config(const RtmpPacket &thiz, string &sps, string &pps) {
}
void H264RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt) {
if (pkt->isCfgFrame()) {
if (pkt->isConfigFrame()) {
//缓存sps pps,后续插入到I帧之前
if (!getH264Config(*pkt, _sps, _pps)) {
WarnL << "get h264 sps/pps failed, rtmp packet is: " << hexdump(pkt->data(), pkt->size());
......@@ -159,26 +156,21 @@ bool H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
}
return _merger.inputFrame(frame, [this](uint64_t dts, uint64_t pts, const Buffer::Ptr &, bool have_key_frame) {
//flags
_rtmp_packet->buffer[0] = FLV_CODEC_H264 | ((have_key_frame ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4);
//not config
_rtmp_packet->buffer[1] = true;
int32_t cts = pts - dts;
if (cts < 0) {
cts = 0;
}
//cts
set_be24(&_rtmp_packet->buffer[2], cts);
_rtmp_packet->time_stamp = dts;
_rtmp_packet->body_size = _rtmp_packet->buffer.size();
_rtmp_packet->chunk_id = CHUNK_VIDEO;
_rtmp_packet->stream_index = STREAM_MEDIA;
_rtmp_packet->type_id = MSG_VIDEO;
//输出rtmp packet
RtmpCodec::inputRtmp(_rtmp_packet);
_rtmp_packet = nullptr;
}, &_rtmp_packet->buffer);
// flags
_rtmp_packet->buffer[0] = (uint8_t)RtmpVideoCodec::h264 | ((uint8_t)(have_key_frame ? RtmpFrameType::key_frame : RtmpFrameType::inter_frame) << 4);
_rtmp_packet->buffer[1] = (uint8_t)RtmpH264PacketType::h264_nalu;
int32_t cts = pts - dts;
// cts
set_be24(&_rtmp_packet->buffer[2], cts);
_rtmp_packet->time_stamp = dts;
_rtmp_packet->body_size = _rtmp_packet->buffer.size();
_rtmp_packet->chunk_id = CHUNK_VIDEO;
_rtmp_packet->stream_index = STREAM_MEDIA;
_rtmp_packet->type_id = MSG_VIDEO;
// 输出rtmp packet
RtmpCodec::inputRtmp(_rtmp_packet);
_rtmp_packet = nullptr;
}, &_rtmp_packet->buffer);
}
void H264RtmpEncoder::makeVideoConfigPkt() {
......@@ -186,42 +178,39 @@ void H264RtmpEncoder::makeVideoConfigPkt() {
WarnL << "sps长度不足4字节";
return;
}
int8_t flags = FLV_CODEC_H264;
flags |= (FLV_KEY_FRAME << 4);
bool is_config = true;
auto rtmpPkt = RtmpPacket::create();
//header
rtmpPkt->buffer.push_back(flags);
rtmpPkt->buffer.push_back(!is_config);
//cts
rtmpPkt->buffer.append("\x0\x0\x0", 3);
//AVCDecoderConfigurationRecord start
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((char)0xff); // 6 bits reserved + 2 bits nal size length - 1 (11)
rtmpPkt->buffer.push_back((char)0xe1); // 3 bits reserved + 5 bits number of sps (00001)
//sps
auto flags = (uint8_t)RtmpVideoCodec::h264;
flags |= ((uint8_t)RtmpFrameType::key_frame << 4);
auto pkt = RtmpPacket::create();
// header
pkt->buffer.push_back(flags);
pkt->buffer.push_back((uint8_t)RtmpH264PacketType::h264_config_header);
// cts
pkt->buffer.append("\x0\x0\x0", 3);
// AVCDecoderConfigurationRecord start
pkt->buffer.push_back(1); // version
pkt->buffer.push_back(_sps[1]); // profile
pkt->buffer.push_back(_sps[2]); // compat
pkt->buffer.push_back(_sps[3]); // level
pkt->buffer.push_back((char)0xff); // 6 bits reserved + 2 bits nal size length - 1 (11)
pkt->buffer.push_back((char)0xe1); // 3 bits reserved + 5 bits number of sps (00001)
// sps
uint16_t size = (uint16_t)_sps.size();
size = htons(size);
rtmpPkt->buffer.append((char *) &size, 2);
rtmpPkt->buffer.append(_sps);
//pps
rtmpPkt->buffer.push_back(1); // version
pkt->buffer.append((char *)&size, 2);
pkt->buffer.append(_sps);
// pps
pkt->buffer.push_back(1); // version
size = (uint16_t)_pps.size();
size = htons(size);
rtmpPkt->buffer.append((char *) &size, 2);
rtmpPkt->buffer.append(_pps);
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);
pkt->buffer.append((char *)&size, 2);
pkt->buffer.append(_pps);
pkt->body_size = pkt->buffer.size();
pkt->chunk_id = CHUNK_VIDEO;
pkt->stream_index = STREAM_MEDIA;
pkt->time_stamp = 0;
pkt->type_id = MSG_VIDEO;
RtmpCodec::inputRtmp(pkt);
}
}//namespace mediakit
......@@ -13,9 +13,7 @@
namespace mediakit{
#if defined(_WIN32)
#pragma pack(push, 1)
#endif // defined(_WIN32)
class FuFlags {
public:
......@@ -30,11 +28,9 @@ public:
unsigned end_bit: 1;
unsigned start_bit: 1;
#endif
} PACKED;
};
#if defined(_WIN32)
#pragma pack(pop)
#endif // defined(_WIN32)
H264RtpDecoder::H264RtpDecoder() {
_frame = obtainFrame();
......@@ -209,8 +205,8 @@ void H264RtpEncoder::insertConfigFrame(uint64_t pts){
void H264RtpEncoder::packRtp(const char *ptr, size_t len, uint64_t pts, bool is_mark, bool gop_pos){
if (len + 3 <= getMaxSize()) {
//STAP-A模式打包小于MTU
packRtpStapA(ptr, len, pts, is_mark, gop_pos);
// 采用STAP-A/Single NAL unit packet per H.264 模式
packRtpSmallFrame(ptr, len, pts, is_mark, gop_pos);
} else {
//STAP-A模式打包会大于MTU,所以采用FU-A模式
packRtpFu(ptr, len, pts, is_mark, gop_pos);
......@@ -220,8 +216,8 @@ void H264RtpEncoder::packRtp(const char *ptr, size_t len, uint64_t pts, bool is_
void H264RtpEncoder::packRtpFu(const char *ptr, size_t len, uint64_t pts, bool is_mark, bool gop_pos){
auto packet_size = getMaxSize() - 2;
if (len <= packet_size + 1) {
//小于FU-A打包最小字节长度要求,采用STAP-A模式
packRtpStapA(ptr, len, pts, is_mark, gop_pos);
// 小于FU-A打包最小字节长度要求,采用STAP-A/Single NAL unit packet per H.264 模式
packRtpSmallFrame(ptr, len, pts, is_mark, gop_pos);
return;
}
......@@ -257,8 +253,17 @@ void H264RtpEncoder::packRtpFu(const char *ptr, size_t len, uint64_t pts, bool i
}
}
void H264RtpEncoder::packRtpSmallFrame(const char *data, size_t len, uint64_t pts, bool is_mark, bool gop_pos) {
GET_CONFIG(bool, h264_stap_a, Rtp::kH264StapA);
if (h264_stap_a) {
packRtpStapA(data, len, pts, is_mark, gop_pos);
} else {
packRtpSingleNalu(data, len, pts, is_mark, gop_pos);
}
}
void H264RtpEncoder::packRtpStapA(const char *ptr, size_t len, uint64_t pts, bool is_mark, bool gop_pos){
//如果帧长度不超过mtu,为了兼容性 webrtc,采用STAP-A模式打包
// 如果帧长度不超过mtu,为了兼容性 webrtc,采用STAP-A模式打包
auto rtp = makeRtp(getTrackType(), nullptr, len + 3, is_mark, pts);
uint8_t *payload = rtp->getPayload();
//STAP-A
......@@ -270,6 +275,11 @@ void H264RtpEncoder::packRtpStapA(const char *ptr, size_t len, uint64_t pts, boo
RtpCodec::inputRtp(rtp, gop_pos);
}
void H264RtpEncoder::packRtpSingleNalu(const char *data, size_t len, uint64_t pts, bool is_mark, bool gop_pos) {
// Single NAL unit packet per H.264 模式
RtpCodec::inputRtp(makeRtp(getTrackType(), data, len, is_mark, pts), gop_pos);
}
bool H264RtpEncoder::inputFrame(const Frame::Ptr &frame) {
auto ptr = frame->data() + frame->prefixSize();
switch (H264_TYPE(ptr[0])) {
......
......@@ -28,7 +28,7 @@ public:
using Ptr = std::shared_ptr<H264RtpDecoder>;
H264RtpDecoder();
~H264RtpDecoder() {}
~H264RtpDecoder() override = default;
/**
* 输入264 rtp包
......@@ -77,9 +77,10 @@ public:
uint32_t sample_rate = 90000,
uint8_t pt = 96,
uint8_t interleaved = TrackVideo * 2);
~H264RtpEncoder() {}
/**
~H264RtpEncoder() override = default;
/**
* 输入264帧
* @param frame 帧数据,必须
*/
......@@ -96,6 +97,8 @@ private:
void packRtp(const char *data, size_t len, uint64_t pts, bool is_mark, bool gop_pos);
void packRtpFu(const char *data, size_t len, uint64_t pts, bool is_mark, bool gop_pos);
void packRtpStapA(const char *data, size_t len, uint64_t pts, bool is_mark, bool gop_pos);
void packRtpSingleNalu(const char *data, size_t len, uint64_t pts, bool is_mark, bool gop_pos);
void packRtpSmallFrame(const char *data, size_t len, uint64_t pts, bool is_mark, bool gop_pos);
private:
Frame::Ptr _sps;
......
......@@ -15,7 +15,7 @@
#include "Extension/Track.h"
#include "Extension/H265.h"
namespace mediakit{
namespace mediakit {
/**
* h265 Rtmp解码类
* 将 h265 over rtmp 解复用出 h265-Frame
......@@ -25,7 +25,7 @@ public:
using Ptr = std::shared_ptr<H265RtmpDecoder>;
H265RtmpDecoder();
~H265RtmpDecoder() {}
~H265RtmpDecoder() = default;
/**
* 输入265 Rtmp包
......@@ -33,22 +33,23 @@ public:
*/
void inputRtmp(const RtmpPacket::Ptr &rtmp) override;
CodecId getCodecId() const override{
return CodecH265;
}
CodecId getCodecId() const override { return CodecH265; }
protected:
void onGetH265(const char *pcData, size_t iLen, uint32_t dts,uint32_t pts);
H265Frame::Ptr obtainFrame();
void onGetH265(const char *data, size_t size, uint32_t dts, uint32_t pts);
void splitFrame(const uint8_t *data, size_t size, uint32_t dts, uint32_t pts);
protected:
RtmpPacketInfo _info;
H265Frame::Ptr _h265frame;
};
/**
* 265 Rtmp打包类
*/
class H265RtmpEncoder : public H265RtmpDecoder{
class H265RtmpEncoder : public H265RtmpDecoder {
public:
using Ptr = std::shared_ptr<H265RtmpEncoder>;
......@@ -87,9 +88,9 @@ private:
std::string _pps;
H265Track::Ptr _track;
RtmpPacket::Ptr _rtmp_packet;
FrameMerger _merger{FrameMerger::mp4_nal_size};
FrameMerger _merger { FrameMerger::mp4_nal_size };
};
}//namespace mediakit
} // namespace mediakit
#endif //ZLMEDIAKIT_H265RTMPCODEC_H
#endif // ZLMEDIAKIT_H265RTMPCODEC_H
......@@ -302,7 +302,7 @@ void H265RtpEncoder::packRtpFu(const char *ptr, size_t len, uint64_t pts, bool i
}
void H265RtpEncoder::packRtp(const char *ptr, size_t len, uint64_t pts, bool is_mark, bool gop_pos){
if (len + 3 <= getMaxSize()) {
if (len <= getMaxSize()) {
//signal-nalu
RtpCodec::inputRtp(makeRtp(getTrackType(), ptr, len, is_mark, pts), gop_pos);
} else {
......
......@@ -39,10 +39,8 @@ public:
using RingDataType = std::shared_ptr<toolkit::List<FMP4Packet::Ptr> >;
using RingType = toolkit::RingBuffer<RingDataType>;
FMP4MediaSource(const std::string &vhost,
const std::string &app,
const std::string &stream_id,
int ring_size = FMP4_GOP_SIZE) : MediaSource(FMP4_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {}
FMP4MediaSource(const MediaTuple& tuple,
int ring_size = FMP4_GOP_SIZE) : MediaSource(FMP4_SCHEMA, tuple), _ring_size(ring_size) {}
~FMP4MediaSource() override { flush(); }
......@@ -53,8 +51,8 @@ public:
return _ring;
}
void getPlayerList(const std::function<void(const std::list<std::shared_ptr<void>> &info_list)> &cb,
const std::function<std::shared_ptr<void>(std::shared_ptr<void> &&info)> &on_change) override {
void getPlayerList(const std::function<void(const std::list<toolkit::Any> &info_list)> &cb,
const std::function<toolkit::Any(toolkit::Any &&info)> &on_change) override {
_ring->getInfoList(cb, on_change);
}
......@@ -108,7 +106,7 @@ public:
private:
void createRing(){
std::weak_ptr<FMP4MediaSource> weak_self = std::dynamic_pointer_cast<FMP4MediaSource>(shared_from_this());
std::weak_ptr<FMP4MediaSource> weak_self = std::static_pointer_cast<FMP4MediaSource>(shared_from_this());
_ring = std::make_shared<RingType>(_ring_size, [weak_self](int size) {
auto strong_self = weak_self.lock();
if (!strong_self) {
......
......@@ -11,8 +11,6 @@
#ifndef ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H
#define ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H
#if defined(ENABLE_MP4)
#include "FMP4MediaSource.h"
#include "Record/MP4Muxer.h"
......@@ -23,12 +21,9 @@ class FMP4MediaSourceMuxer final : public MP4MuxerMemory, public MediaSourceEven
public:
using Ptr = std::shared_ptr<FMP4MediaSourceMuxer>;
FMP4MediaSourceMuxer(const std::string &vhost,
const std::string &app,
const std::string &stream_id,
const ProtocolOption &option) {
FMP4MediaSourceMuxer(const MediaTuple& tuple, const ProtocolOption &option) {
_option = option;
_media_src = std::make_shared<FMP4MediaSource>(vhost, app, stream_id);
_media_src = std::make_shared<FMP4MediaSource>(tuple);
}
~FMP4MediaSourceMuxer() override { MP4MuxerMemory::flush(); };
......@@ -66,7 +61,8 @@ public:
return _option.fmp4_demand ? (_clear_cache ? true : _enabled) : true;
}
void onAllTrackReady() {
void addTrackCompleted() override {
MP4MuxerMemory::addTrackCompleted();
_media_src->setInitSegment(getInitSegment());
}
......@@ -89,5 +85,4 @@ private:
}//namespace mediakit
#endif// defined(ENABLE_MP4)
#endif //ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H
......@@ -37,7 +37,7 @@ bool HlsParser::parse(const string &http_url, const string &m3u8) {
if ((_is_m3u8_inner || extinf_dur != 0) && line[0] != '#') {
segment.duration = extinf_dur;
segment.url = Parser::merge_url(http_url, line);
segment.url = Parser::mergeUrl(http_url, line);
if (!_is_m3u8_inner) {
//ts按照先后顺序排序
ts_map.emplace(index++, segment);
......
......@@ -71,6 +71,11 @@ void HlsPlayer::teardown() {
void HlsPlayer::fetchSegment() {
if (_ts_list.empty()) {
// 如果是点播文件,播放列表为空代表文件播放结束,关闭播放器: #2628
if(!HlsParser::isLive()){
teardown();
return;
}
//播放列表为空,那么立即重新下载m3u8文件
_timer.reset();
fetchIndexFile();
......@@ -80,7 +85,7 @@ void HlsPlayer::fetchSegment() {
//播放器目前还存活,正在下载中
return;
}
weak_ptr<HlsPlayer> weak_self = dynamic_pointer_cast<HlsPlayer>(shared_from_this());
weak_ptr<HlsPlayer> weak_self = static_pointer_cast<HlsPlayer>(shared_from_this());
if (!_http_ts_player) {
_http_ts_player = std::make_shared<HttpTSPlayer>(getPoller());
_http_ts_player->setOnCreateSocket([weak_self](const EventPoller::Ptr &poller) {
......@@ -121,18 +126,21 @@ void HlsPlayer::fetchSegment() {
WarnL << "Download ts segment " << url << " failed:" << err;
if (err.getErrCode() == Err_timeout) {
strong_self->_timeout_multiple = MAX(strong_self->_timeout_multiple + 1, MAX_TIMEOUT_MULTIPLE);
}else{
strong_self->_timeout_multiple = MAX(strong_self->_timeout_multiple -1 , MIN_TIMEOUT_MULTIPLE);
} else {
strong_self->_timeout_multiple = MAX(strong_self->_timeout_multiple - 1, MIN_TIMEOUT_MULTIPLE);
}
}
//提前半秒下载好
auto delay = duration - ticker.elapsedTime() / 1000.0f - 0.5;
if (delay <= 0) {
//延时最小10ms
delay = 10;
// 提前0.5秒下载好,支持点播文件控制下载速度: #2628
auto delay = duration - 0.5 - ticker.elapsedTime() / 1000.0f;
if (delay > 2.0) {
// 提前1秒下载
delay -= 1.0;
} else if (delay <= 0) {
// 延时最小10ms
delay = 0.01;
}
//延时下载下一个切片
strong_self->_timer_ts.reset(new Timer(delay / 1000.0f, [weak_self]() {
// 延时下载下一个切片
strong_self->_timer_ts.reset(new Timer(delay, [weak_self]() {
auto strong_self = weak_self.lock();
if (strong_self) {
strong_self->fetchSegment();
......@@ -186,7 +194,7 @@ bool HlsPlayer::onParsed(bool is_m3u8_inner, int64_t sequence, const map<int, ts
throw invalid_argument("empty sub hls list:" + getUrl());
}
_timer.reset();
weak_ptr<HlsPlayer> weak_self = dynamic_pointer_cast<HlsPlayer>(shared_from_this());
weak_ptr<HlsPlayer> weak_self = static_pointer_cast<HlsPlayer>(shared_from_this());
auto url = ts_map.rbegin()->second.url;
getPoller()->async([weak_self, url]() {
auto strong_self = weak_self.lock();
......@@ -259,7 +267,7 @@ bool HlsPlayer::onRedirectUrl(const string &url, bool temporary) {
}
void HlsPlayer::playDelay() {
weak_ptr<HlsPlayer> weak_self = dynamic_pointer_cast<HlsPlayer>(shared_from_this());
weak_ptr<HlsPlayer> weak_self = static_pointer_cast<HlsPlayer>(shared_from_this());
_timer.reset(new Timer(delaySecond(), [weak_self]() {
auto strong_self = weak_self.lock();
if (strong_self) {
......
......@@ -21,7 +21,7 @@ namespace mediakit {
void HttpClient::sendRequest(const string &url) {
clearResponse();
_url = url;
auto protocol = FindField(url.data(), NULL, "://");
auto protocol = findSubString(url.data(), NULL, "://");
uint16_t port;
bool is_https;
if (strcasecmp(protocol.data(), "http") == 0) {
......@@ -35,11 +35,11 @@ void HttpClient::sendRequest(const string &url) {
throw std::invalid_argument(strErr);
}
auto host = FindField(url.data(), "://", "/");
auto host = findSubString(url.data(), "://", "/");
if (host.empty()) {
host = FindField(url.data(), "://", NULL);
host = findSubString(url.data(), "://", NULL);
}
_path = FindField(url.data(), host.data(), NULL);
_path = findSubString(url.data(), host.data(), NULL);
if (_path.empty()) {
_path = "/";
}
......@@ -100,7 +100,7 @@ void HttpClient::clearResponse() {
_header_recved = false;
_recved_body_size = 0;
_total_body_size = 0;
_parser.Clear();
_parser.clear();
_chunked_splitter = nullptr;
_wait_header.resetTime();
_wait_body.resetTime();
......@@ -176,25 +176,25 @@ void HttpClient::onRecv(const Buffer::Ptr &pBuf) {
HttpRequestSplitter::input(pBuf->data(), pBuf->size());
}
void HttpClient::onErr(const SockException &ex) {
void HttpClient::onError(const SockException &ex) {
onResponseCompleted_l(ex);
}
ssize_t HttpClient::onRecvHeader(const char *data, size_t len) {
_parser.Parse(data);
if (_parser.Url() == "302" || _parser.Url() == "301" || _parser.Url() == "303") {
auto new_url = Parser::merge_url(_url, _parser["Location"]);
_parser.parse(data, len);
if (_parser.status() == "302" || _parser.status() == "301" || _parser.status() == "303") {
auto new_url = Parser::mergeUrl(_url, _parser["Location"]);
if (new_url.empty()) {
throw invalid_argument("未找到Location字段(跳转url)");
}
if (onRedirectUrl(new_url, _parser.Url() == "302")) {
if (onRedirectUrl(new_url, _parser.status() == "302")) {
HttpClient::sendRequest(new_url);
return 0;
}
}
checkCookie(_parser.getHeader());
onResponseHeader(_parser.Url(), _parser.getHeader());
onResponseHeader(_parser.status(), _parser.getHeader());
_header_recved = true;
if (_parser["Transfer-Encoding"] == "chunked") {
......@@ -226,7 +226,7 @@ ssize_t HttpClient::onRecvHeader(const char *data, size_t len) {
if (_total_body_size == 0) {
//后续没content,本次http请求结束
onResponseCompleted_l(SockException(Err_success, "success"));
onResponseCompleted_l(SockException(Err_success, "The request is successful but has no body"));
return 0;
}
......@@ -260,7 +260,7 @@ void HttpClient::onRecvContent(const char *data, size_t len) {
if (_recved_body_size == (size_t)_total_body_size) {
//content接收完毕
onResponseBody(data, len);
onResponseCompleted_l(SockException(Err_success, "success"));
onResponseCompleted_l(SockException(Err_success, "completed"));
return;
}
......@@ -329,7 +329,7 @@ void HttpClient::onResponseCompleted_l(const SockException &ex) {
if (_total_body_size > 0 && _recved_body_size >= (size_t)_total_body_size) {
//回复header中有content-length信息,那么收到的body大于等于声明值则认为成功
onResponseCompleted(SockException(Err_success, "success"));
onResponseCompleted(SockException(Err_success, "read body completed"));
return;
}
......@@ -361,8 +361,8 @@ void HttpClient::checkCookie(HttpClient::HttpHeader &headers) {
int index = 0;
auto arg_vec = split(it_set_cookie->second, ";");
for (string &key_val : arg_vec) {
auto key = FindField(key_val.data(), NULL, "=");
auto val = FindField(key_val.data(), "=", NULL);
auto key = findSubString(key_val.data(), NULL, "=");
auto val = findSubString(key_val.data(), "=", NULL);
if (index++ == 0) {
cookie->setKeyVal(key, val);
......
......@@ -22,7 +22,7 @@
#include "HttpRequestSplitter.h"
#include "HttpCookie.h"
#include "HttpChunkedSplitter.h"
#include "strCoding.h"
#include "Common/strCoding.h"
#include "HttpBody.h"
namespace mediakit {
......@@ -177,7 +177,7 @@ protected:
//// TcpClient override ////
void onConnect(const toolkit::SockException &ex) override;
void onRecv(const toolkit::Buffer::Ptr &pBuf) override;
void onErr(const toolkit::SockException &ex) override;
void onError(const toolkit::SockException &ex) override;
void onFlush() override;
void onManager() override;
......
......@@ -18,7 +18,7 @@ using namespace toolkit;
namespace mediakit{
const char *getHttpStatusMessage(int status) {
const char *HttpConst::getHttpStatusMessage(int status) {
switch (status) {
case 100: return "Continue";
case 101: return "Switching Protocol";
......@@ -196,7 +196,7 @@ static const char *s_mime_src[][2] = {
{"avi", "video/x-msvideo"},
};
const string &getHttpContentType(const char *name) {
const string& HttpConst::getHttpContentType(const char *name) {
const char *dot;
dot = strrchr(name, '.');
static StrCaseMap mapType;
......
......@@ -15,19 +15,25 @@
namespace mediakit{
/**
* 根据http错误代码获取字符说明
* @param status 譬如404
* @return 错误代码字符说明,譬如Not Found
*/
const char *getHttpStatusMessage(int status);
class HttpConst {
public:
HttpConst() = delete;
~HttpConst() = delete;
/**
* 根据文件后缀返回http mime
* @param name 文件后缀,譬如html
* @return mime值,譬如text/html
*/
const std::string &getHttpContentType(const char *name);
/**
* 根据http错误代码获取字符说明
* @param status 譬如404
* @return 错误代码字符说明,譬如Not Found
*/
static const char *getHttpStatusMessage(int status);
/**
* 根据文件后缀返回http mime
* @param name 文件后缀,譬如html
* @return mime值,譬如text/html
*/
static const std::string &getHttpContentType(const char *name);
};
}//mediakit
......
......@@ -61,7 +61,7 @@ bool HttpServerCookie::isExpired() {
return _ticker.elapsedTime() > _max_elapsed * 1000;
}
void HttpServerCookie::setAttach(std::shared_ptr<void> attach) {
void HttpServerCookie::setAttach(toolkit::Any attach) {
_attach = std::move(attach);
}
......@@ -114,8 +114,7 @@ void HttpCookieManager::onManager() {
}
}
HttpServerCookie::Ptr HttpCookieManager::addCookie(const string &cookie_name, const string &uid_in,
uint64_t max_elapsed, std::shared_ptr<void> attach, int max_client) {
HttpServerCookie::Ptr HttpCookieManager::addCookie(const string &cookie_name, const string &uid_in, uint64_t max_elapsed, toolkit::Any attach, int max_client) {
lock_guard<recursive_mutex> lck(_mtx_cookie);
auto cookie = _generator.obtain();
auto uid = uid_in.empty() ? cookie : uid_in;
......@@ -158,9 +157,9 @@ HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name, co
if (it == http_header.end()) {
return nullptr;
}
auto cookie = FindField(it->second.data(), (cookie_name + "=").data(), ";");
auto cookie = findSubString(it->second.data(), (cookie_name + "=").data(), ";");
if (cookie.empty()) {
cookie = FindField(it->second.data(), (cookie_name + "=").data(), nullptr);
cookie = findSubString(it->second.data(), (cookie_name + "=").data(), nullptr);
}
if (cookie.empty()) {
return nullptr;
......
......@@ -85,14 +85,14 @@ public:
/**
* 设置附加数据
*/
void setAttach(std::shared_ptr<void> attach);
void setAttach(toolkit::Any attach);
/*
* 获取附加数据
*/
template <class T>
T& getAttach() {
return *static_cast<T *>(_attach.get());
return _attach.get<T>();
}
private:
......@@ -104,7 +104,7 @@ private:
std::string _cookie_uuid;
uint64_t _max_elapsed;
toolkit::Ticker _ticker;
std::shared_ptr<void> _attach;
toolkit::Any _attach;
std::weak_ptr<HttpCookieManager> _manager;
};
......@@ -163,7 +163,7 @@ public:
*/
HttpServerCookie::Ptr addCookie(
const std::string &cookie_name, const std::string &uid, uint64_t max_elapsed = COOKIE_DEFAULT_LIFE,
std::shared_ptr<void> attach = nullptr,
toolkit::Any = toolkit::Any{},
int max_client = 1);
/**
......
......@@ -62,6 +62,13 @@ public:
* @return mime值
*/
static const std::string &getContentType(const char *name);
/**
* 该ip是否再白名单中
* @param ip 支持ipv4和ipv6
*/
static bool isIPAllowed(const std::string &ip);
private:
HttpFileManager() = delete;
~HttpFileManager() = delete;
......
......@@ -91,7 +91,7 @@ void HttpRequestSplitter::input(const char *data,size_t len) {
_remain_data.assign(ptr, _remain_data_size);
return;
}
//收到content数据,并且接content完毕
//收到content数据,并且接content完毕
onRecvContent(ptr,_content_len);
_remain_data_size -= _content_len;
......
......@@ -27,7 +27,7 @@ void HttpRequester::onResponseBody(const char *buf, size_t size) {
void HttpRequester::onResponseCompleted(const SockException &ex) {
if (ex && _retry++ < _max_retry) {
std::weak_ptr<HttpRequester> weak_self = std::dynamic_pointer_cast<HttpRequester>(shared_from_this());
std::weak_ptr<HttpRequester> weak_self = std::static_pointer_cast<HttpRequester>(shared_from_this());
getPoller()->doDelayTask(_retry_delay, [weak_self](){
if (auto self = weak_self.lock()) {
InfoL << "resend request " << self->getUrl() << " with retry " << self->getRetry();
......@@ -271,7 +271,7 @@ static void sendReport() {
}
static toolkit::onceToken s_token([]() {
NoticeCenter::Instance().addListener(nullptr, "kBroadcastEventPollerPoolStarted", [](EventPollerPool &pool, size_t &size) {
NoticeCenter::Instance().addListener(nullptr, "kBroadcastEventPollerPoolStarted", [](EventPollerPoolOnStartedArgs) {
// 第一次汇报在程序启动后5分钟
pool.getPoller()->doDelayTask(5 * 60 * 1000, []() {
sendReport();
......
......@@ -28,15 +28,16 @@ class HttpSession: public toolkit::Session,
public HttpRequestSplitter,
public WebSocketSplitter {
public:
typedef StrCaseMap KeyValue;
typedef HttpResponseInvokerImp HttpResponseInvoker;
using Ptr = std::shared_ptr<HttpSession>;
using KeyValue = StrCaseMap;
using HttpResponseInvoker = HttpResponseInvokerImp ;
friend class AsyncSender;
/**
* @param errMsg 如果为空,则代表鉴权通过,否则为错误提示
* @param accessPath 运行或禁止访问的根目录
* @param cookieLifeSecond 鉴权cookie有效期
**/
typedef std::function<void(const std::string &errMsg,const std::string &accessPath, int cookieLifeSecond)> HttpAccessPathInvoker;
using HttpAccessPathInvoker = std::function<void(const std::string &errMsg,const std::string &accessPath, int cookieLifeSecond)>;
HttpSession(const toolkit::Socket::Ptr &pSock);
~HttpSession() override;
......@@ -100,11 +101,10 @@ protected:
std::string get_peer_ip() override;
private:
void Handle_Req_GET(ssize_t &content_len);
void Handle_Req_GET_l(ssize_t &content_len, bool sendBody);
void Handle_Req_POST(ssize_t &content_len);
void Handle_Req_HEAD(ssize_t &content_len);
void Handle_Req_OPTIONS(ssize_t &content_len);
void onHttpRequest_GET();
void onHttpRequest_POST();
void onHttpRequest_HEAD();
void onHttpRequest_OPTIONS();
bool checkLiveStream(const std::string &schema, const std::string &url_suffix, const std::function<void(const MediaSource::Ptr &src)> &cb);
......@@ -123,19 +123,20 @@ private:
//设置socket标志
void setSocketFlags();
protected:
MediaInfo _mediaInfo;
private:
bool _is_live_stream = false;
bool _live_over_websocket = false;
//消耗的总流量
uint64_t _total_bytes_usage = 0;
std::string _origin;
Parser _parser;
toolkit::Ticker _ticker;
MediaInfo _mediaInfo;
TSMediaSource::RingType::RingReader::Ptr _ts_reader;
FMP4MediaSource::RingType::RingReader::Ptr _fmp4_reader;
//处理content数据的callback
std::function<bool (const char *data,size_t len) > _contentCallBack;
std::function<bool (const char *data,size_t len) > _on_recv_body;
};
using HttpsSession = toolkit::SessionWithSSL<HttpSession>;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论