Merge branch 'master' into dev

This commit is contained in:
xiongguangjie 2024-07-23 09:46:35 +08:00
commit a153d99f6d
85 changed files with 3778 additions and 750 deletions

View File

@ -46,12 +46,14 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
script: | script: |
github.rest.issues.createComment({ github.rest.issues.createComment({
issue_number: 483, issue_number: ${{vars.VERSION_ISSUE_NO}},
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n' body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n'
+ '- 分支: ${{ env.BRANCH2 }}\n' + '- 分支: ${{ env.BRANCH2 }}\n'
+ '- git hash: ${{ github.sha }} \n' + '- git hash: ${{ github.sha }} \n'
+ '- 编译日期: ${{ env.DATE }}\n' + '- 编译日期: ${{ env.DATE }}\n'
+ '- 编译记录: [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})\n'
+ '- 开启特性: 未开启openssl/webrtc/datachannel等功能\n'
+ '- 打包ci名: ${{ github.workflow }}\n' + '- 打包ci名: ${{ github.workflow }}\n'
}) })

View File

@ -21,33 +21,82 @@ jobs:
ref: v2.3.0 ref: v2.3.0
path: 3rdpart/libsrtp path: 3rdpart/libsrtp
- name: 下载 openssl
uses: actions/checkout@v2
with:
repository: openssl/openssl
fetch-depth: 1
ref: OpenSSL_1_1_1
path: 3rdpart/openssl
- name: 下载 usrsctp
uses: actions/checkout@v2
with:
repository: sctplab/usrsctp
fetch-depth: 1
ref: 0.9.5.0
path: 3rdpart/usrsctp
- name: 启动 Docker 容器, 在Docker 容器中执行脚本 - name: 启动 Docker 容器, 在Docker 容器中执行脚本
run: | run: |
docker pull centos:7 docker pull centos:7
docker run -v $(pwd):/root -w /root --rm centos:7 sh -c " docker run -v $(pwd):/root -w /root --rm centos:7 sh -c "
#!/bin/bash
set -x set -x
yum install -y git wget gcc gcc-c++ make unzip ffmpeg-devel libavutil-devel libswscale-devel libresample-devel usrsctp-devel
wget https://github.com/openssl/openssl/archive/refs/heads/OpenSSL_1_1_1-stable.zip # Backup original CentOS-Base.repo file
unzip OpenSSL_1_1_1-stable.zip cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
cd openssl-OpenSSL_1_1_1-stable
./config no-shared --prefix=/root/release # Define new repository configuration
cat <<EOF > /etc/yum.repos.d/CentOS-Base.repo
[base]
name=CentOS-7 - Base - mirrors.aliyun.com
baseurl=http://mirrors.aliyun.com/centos/7/os/x86_64/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
[updates]
name=CentOS-7 - Updates - mirrors.aliyun.com
baseurl=http://mirrors.aliyun.com/centos/7/updates/x86_64/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
EOF
# Clean yum cache and recreate it
yum clean all
yum makecache
echo \"CentOS 7 软件源已成功切换\"
yum install -y git wget gcc gcc-c++ make
mkdir -p /root/install
cd 3rdpart/openssl
./config no-shared --prefix=/root/install
make -j $(nproc) make -j $(nproc)
make install make install
cd ..
wget https://github.com/Kitware/CMake/releases/download/v3.29.5/cmake-3.29.5.tar.gz
tar -xvf cmake-3.29.5.tar.gz
cd cmake-3.29.5
OPENSSL_ROOT_DIR=/root/release ./configure
make -j $(nproc)
make install
cd ..
cd 3rdpart/libsrtp && ./configure --enable-openssl --with-openssl-dir=/root/release && make -j $(nproc) && make install
cd ../../ cd ../../
mkdir -p linux_build && cd linux_build && cmake .. -DOPENSSL_ROOT_DIR=/root/release -DCMAKE_BUILD_TYPE=Release -DENABLE_FFMPEG=true && make -j $(nproc) wget https://github.com/Kitware/CMake/releases/download/v3.29.5/cmake-3.29.5.tar.gz
tar -xf cmake-3.29.5.tar.gz
cd cmake-3.29.5
OPENSSL_ROOT_DIR=/root/install ./configure
make -j $(nproc)
make install
cd ..
cd 3rdpart/usrsctp
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON ..
make -j $(nproc)
make install
cd ../../../
cd 3rdpart/libsrtp && ./configure --enable-openssl --with-openssl-dir=/root/install && make -j $(nproc) && make install
cd ../../
mkdir -p linux_build && cd linux_build && cmake .. -DOPENSSL_ROOT_DIR=/root/install -DCMAKE_BUILD_TYPE=Release && make -j $(nproc)
" "
- name: 设置环境变量 - name: 设置环境变量
@ -72,13 +121,15 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
script: | script: |
github.rest.issues.createComment({ github.rest.issues.createComment({
issue_number: 483, issue_number: ${{vars.VERSION_ISSUE_NO}},
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n' body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n'
+ '- 分支: ${{ env.BRANCH2 }}\n' + '- 分支: ${{ env.BRANCH2 }}\n'
+ '- git hash: ${{ github.sha }} \n' + '- git hash: ${{ github.sha }} \n'
+ '- 编译日期: ${{ env.DATE }}\n' + '- 编译日期: ${{ env.DATE }}\n'
+ '- 编译记录: [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})\n'
+ '- 打包ci名: ${{ github.workflow }}\n' + '- 打包ci名: ${{ github.workflow }}\n'
+ '- 说明: 本二进制在centos7(x64)上编译,请确保您的机器系统不低于此版本,并提前`sudo yum check-update && sudo yum install -y openssl-devel ffmpeg-devel libavutil-devel libswscale-devel libresample-devel usrsctp-devel`安装依赖项\n' + '- 开启特性: openssl/webrtc/datachannel\n'
+ '- 说明: 本二进制在centos7(x64)上编译,请确保您的机器系统不低于此版本\n'
}) })

View File

@ -20,7 +20,7 @@ jobs:
vcpkgTriplet: arm64-osx vcpkgTriplet: arm64-osx
# 2024.06.01 # 2024.06.01
vcpkgGitCommitId: '47364fbc300756f64f7876b549d9422d5f3ec0d3' vcpkgGitCommitId: '47364fbc300756f64f7876b549d9422d5f3ec0d3'
vcpkgArguments: 'openssl libsrtp[openssl]' vcpkgArguments: 'openssl libsrtp[openssl] usrsctp'
- name: 编译 - name: 编译
uses: lukka/run-cmake@v3 uses: lukka/run-cmake@v3
@ -52,13 +52,15 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
script: | script: |
github.rest.issues.createComment({ github.rest.issues.createComment({
issue_number: 483, issue_number: ${{vars.VERSION_ISSUE_NO}},
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n' body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n'
+ '- 分支: ${{ env.BRANCH2 }}\n' + '- 分支: ${{ env.BRANCH2 }}\n'
+ '- git hash: ${{ github.sha }} \n' + '- git hash: ${{ github.sha }} \n'
+ '- 编译日期: ${{ env.DATE }}\n' + '- 编译日期: ${{ env.DATE }}\n'
+ '- 编译记录: [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})\n'
+ '- 打包ci名: ${{ github.workflow }}\n' + '- 打包ci名: ${{ github.workflow }}\n'
+ '- 开启特性: openssl/webrtc/datachannel\n'
+ '- 说明: 此二进制为arm64版本\n' + '- 说明: 此二进制为arm64版本\n'
}) })

View File

@ -19,7 +19,7 @@ jobs:
vcpkgTriplet: x64-windows-static vcpkgTriplet: x64-windows-static
# 2024.06.01 # 2024.06.01
vcpkgGitCommitId: '47364fbc300756f64f7876b549d9422d5f3ec0d3' vcpkgGitCommitId: '47364fbc300756f64f7876b549d9422d5f3ec0d3'
vcpkgArguments: 'openssl libsrtp[openssl]' vcpkgArguments: 'openssl libsrtp[openssl] usrsctp'
- name: 编译 - name: 编译
uses: lukka/run-cmake@v3 uses: lukka/run-cmake@v3
@ -54,13 +54,15 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
script: | script: |
github.rest.issues.createComment({ github.rest.issues.createComment({
issue_number: 483, issue_number: ${{vars.VERSION_ISSUE_NO}},
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n' body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n'
+ '- 分支: ${{ env.BRANCH2 }}\n' + '- 分支: ${{ env.BRANCH2 }}\n'
+ '- git hash: ${{ github.sha }} \n' + '- git hash: ${{ github.sha }} \n'
+ '- 编译日期: ${{ env.DATE }}\n' + '- 编译日期: ${{ env.DATE }}\n'
+ '- 编译记录: [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})\n'
+ '- 打包ci名: ${{ github.workflow }}\n' + '- 打包ci名: ${{ github.workflow }}\n'
+ '- 开启特性: openssl/webrtc/datachannel\n'
+ '- 说明: 此二进制为x64版本\n' + '- 说明: 此二进制为x64版本\n'
}) })

@ -1 +1 @@
Subproject commit 5144e2aa521df6d473308bfb31172054772a634f Subproject commit fb695d203421d906c473018022a736fa4a7a47e4

@ -1 +1 @@
Subproject commit 527c0f5117b489fda78fcd123d446370ddd9ec9a Subproject commit cf83ebc62e65ae6f3b73bc5ebd06cb0b2da49fa5

View File

@ -448,10 +448,21 @@ endif()
if(WIN32) if(WIN32)
update_cached_list(MK_LINK_LIBRARIES WS2_32 Iphlpapi shlwapi) update_cached_list(MK_LINK_LIBRARIES WS2_32 Iphlpapi shlwapi)
elseif(ANDROID)
update_cached_list(MK_LINK_LIBRARIES log)
elseif(NOT ANDROID OR IOS) elseif(NOT ANDROID OR IOS)
update_cached_list(MK_LINK_LIBRARIES pthread) update_cached_list(MK_LINK_LIBRARIES pthread)
endif() endif()
if(ENABLE_VIDEOSTACK)
if(ENABLE_FFMPEG AND ENABLE_X264)
message(STATUS "ENABLE_VIDEOSTACK defined")
update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_VIDEOSTACK)
else()
message(WARNING "ENABLE_VIDEOSTACK requires ENABLE_FFMPEG and ENABLE_X264")
endif ()
endif ()
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
# Solution folders: # Solution folders:
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------

View File

@ -58,6 +58,8 @@ API_EXPORT const char *API_CALL mk_record_info_get_stream(const mk_record_info c
///////////////////////////////////////////Parser///////////////////////////////////////////// ///////////////////////////////////////////Parser/////////////////////////////////////////////
//Parser对象的C映射 //Parser对象的C映射
typedef struct mk_parser_t *mk_parser; typedef struct mk_parser_t *mk_parser;
//Parser对象中Headers foreach回调
typedef void(API_CALL *on_mk_parser_header_cb)(void *user_data, const char *key, const char *val);
//Parser::Method(),获取命令字譬如GET/POST //Parser::Method(),获取命令字譬如GET/POST
API_EXPORT const char* API_CALL mk_parser_get_method(const mk_parser ctx); API_EXPORT const char* API_CALL mk_parser_get_method(const mk_parser ctx);
//Parser::Url(),获取HTTP的访问url(不包括?后面的参数) //Parser::Url(),获取HTTP的访问url(不包括?后面的参数)
@ -72,6 +74,8 @@ API_EXPORT const char* API_CALL mk_parser_get_tail(const mk_parser ctx);
API_EXPORT const char* API_CALL mk_parser_get_header(const mk_parser ctx,const char *key); API_EXPORT const char* API_CALL mk_parser_get_header(const mk_parser ctx,const char *key);
//Parser::Content(),获取HTTP body //Parser::Content(),获取HTTP body
API_EXPORT const char* API_CALL mk_parser_get_content(const mk_parser ctx, size_t *length); API_EXPORT const char* API_CALL mk_parser_get_content(const mk_parser ctx, size_t *length);
//循环获取所有header
API_EXPORT void API_CALL mk_parser_headers_for_each(const mk_parser ctx, on_mk_parser_header_cb cb, void *user_data);
///////////////////////////////////////////MediaInfo///////////////////////////////////////////// ///////////////////////////////////////////MediaInfo/////////////////////////////////////////////
//MediaInfo对象的C映射 //MediaInfo对象的C映射
@ -114,19 +118,24 @@ API_EXPORT int API_CALL mk_media_source_get_total_reader_count(const mk_media_so
API_EXPORT int API_CALL mk_media_source_get_track_count(const mk_media_source ctx); 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 // 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); API_EXPORT mk_track API_CALL mk_media_source_get_track(const mk_media_source ctx, int index);
// MediaSource::Track:loss
API_EXPORT float API_CALL mk_media_source_get_track_loss(const mk_media_source ctx, const mk_track track);
// MediaSource::broadcastMessage // MediaSource::broadcastMessage
API_EXPORT int API_CALL mk_media_source_broadcast_msg(const mk_media_source ctx, const char *msg, size_t len); API_EXPORT int API_CALL mk_media_source_broadcast_msg(const mk_media_source ctx, const char *msg, size_t len);
// MediaSource::getOriginUrl() // MediaSource::getOriginUrl()
API_EXPORT const char* API_CALL mk_media_source_get_origin_url(const mk_media_source ctx); API_EXPORT const char* API_CALL mk_media_source_get_origin_url(const mk_media_source ctx);
// MediaSource::getOriginType() // MediaSource::getOriginType()
API_EXPORT int API_CALL mk_media_source_get_origin_type(const mk_media_source ctx); API_EXPORT int API_CALL mk_media_source_get_origin_type(const mk_media_source ctx);
// MediaSource::getOriginTypeStr(), 使用后请用mk_free释放返回值
API_EXPORT const char *API_CALL mk_media_source_get_origin_type_str(const mk_media_source ctx);
// MediaSource::getCreateStamp() // MediaSource::getCreateStamp()
API_EXPORT uint64_t API_CALL mk_media_source_get_create_stamp(const mk_media_source ctx); API_EXPORT uint64_t API_CALL mk_media_source_get_create_stamp(const mk_media_source ctx);
// MediaSource::isRecording() 0:hls,1:MP4 // MediaSource::isRecording() 0:hls,1:MP4
API_EXPORT int API_CALL mk_media_source_is_recording(const mk_media_source ctx, int type); API_EXPORT int API_CALL mk_media_source_is_recording(const mk_media_source ctx, int type);
// MediaSource::getBytesSpeed()
API_EXPORT int API_CALL mk_media_source_get_bytes_speed(const mk_media_source ctx);
// MediaSource::getAliveSecond()
API_EXPORT uint64_t API_CALL mk_media_source_get_alive_second(const mk_media_source ctx);
/** /**
* ZLMediaKit中被称作为MediaSource * ZLMediaKit中被称作为MediaSource
* 3RtmpMediaSourceRtspMediaSourceHlsMediaSource * 3RtmpMediaSourceRtspMediaSourceHlsMediaSource

View File

@ -79,7 +79,7 @@ API_EXPORT void API_CALL mk_proxy_player_release(mk_proxy_player ctx);
/** /**
* *
* @param ctx * @param ctx
* @param key , net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms * @param key , net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms/rtsp_speed
* @param val ,string * @param val ,string
*/ */
API_EXPORT void API_CALL mk_proxy_player_set_option(mk_proxy_player ctx, const char *key, const char *val); API_EXPORT void API_CALL mk_proxy_player_set_option(mk_proxy_player ctx, const char *key, const char *val);

View File

@ -24,6 +24,7 @@ typedef struct mk_rtp_server_t *mk_rtp_server;
* @return * @return
*/ */
API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create(uint16_t port, int tcp_mode, const char *stream_id); API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create(uint16_t port, int tcp_mode, const char *stream_id);
API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create2(uint16_t port, int tcp_mode, const char *vhost, const char *app, const char *stream_id);
/** /**
* TCP * TCP

View File

@ -73,6 +73,21 @@ API_EXPORT const char* API_CALL mk_track_codec_name(mk_track track);
*/ */
API_EXPORT int API_CALL mk_track_bit_rate(mk_track track); API_EXPORT int API_CALL mk_track_bit_rate(mk_track track);
/**
* 1: 0
*/
API_EXPORT int API_CALL mk_track_ready(mk_track track);
/**
*
*/
API_EXPORT uint64_t API_CALL mk_track_frames(mk_track track);
/**
*
*/
API_EXPORT uint64_t API_CALL mk_track_duration(mk_track track);
/** /**
* frame输出事件 * frame输出事件
* @param track track对象 * @param track track对象
@ -114,6 +129,21 @@ API_EXPORT int API_CALL mk_track_video_height(mk_track track);
*/ */
API_EXPORT int API_CALL mk_track_video_fps(mk_track track); API_EXPORT int API_CALL mk_track_video_fps(mk_track track);
/**
*
*/
API_EXPORT uint64_t API_CALL mk_track_video_key_frames(mk_track track);
/**
* GOP关键帧间隔
*/
API_EXPORT int API_CALL mk_track_video_gop_size(mk_track track);
/**
* ()
*/
API_EXPORT int API_CALL mk_track_video_gop_interval_ms(mk_track track);
/** /**
* *
*/ */

View File

@ -125,6 +125,13 @@ API_EXPORT const char* API_CALL mk_parser_get_content(const mk_parser ctx, size_
} }
return parser->content().c_str(); return parser->content().c_str();
} }
API_EXPORT void API_CALL mk_parser_headers_for_each(const mk_parser ctx, on_mk_parser_header_cb cb, void *user_data){
assert(ctx && cb);
Parser *parser = (Parser *)ctx;
for (auto it = parser->getHeader().begin(); it != parser->getHeader().end(); ++it) {
cb(user_data, it->first.c_str(), it->second.c_str());
}
}
///////////////////////////////////////////MediaInfo///////////////////////////////////////////// ///////////////////////////////////////////MediaInfo/////////////////////////////////////////////
API_EXPORT const char* API_CALL mk_media_info_get_params(const mk_media_info ctx){ API_EXPORT const char* API_CALL mk_media_info_get_params(const mk_media_info ctx){
@ -218,6 +225,13 @@ API_EXPORT mk_track API_CALL mk_media_source_get_track(const mk_media_source ctx
return (mk_track) new Track::Ptr(std::move(tracks[index])); return (mk_track) new Track::Ptr(std::move(tracks[index]));
} }
API_EXPORT float API_CALL mk_media_source_get_track_loss(const mk_media_source ctx, const mk_track track) {
assert(ctx);
MediaSource *src = (MediaSource *)ctx;
// rtp推流只有一个统计器但是可能有多个track如果短时间多次获取间隔丢包率第二次会获取为-1
return src->getLossRate((*((Track::Ptr *)track))->getTrackType());
}
API_EXPORT int API_CALL mk_media_source_broadcast_msg(const mk_media_source ctx, const char *msg, size_t len) { 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); assert(ctx && msg && len);
MediaSource *src = (MediaSource *)ctx; MediaSource *src = (MediaSource *)ctx;
@ -240,6 +254,12 @@ API_EXPORT int API_CALL mk_media_source_get_origin_type(const mk_media_source c
return static_cast<int>(src->getOriginType()); return static_cast<int>(src->getOriginType());
} }
API_EXPORT const char* API_CALL mk_media_source_get_origin_type_str(const mk_media_source ctx) {
assert(ctx);
MediaSource *src = (MediaSource *)ctx;
return _strdup(getOriginTypeString(src->getOriginType()).c_str());
}
API_EXPORT uint64_t API_CALL mk_media_source_get_create_stamp(const mk_media_source ctx) { API_EXPORT uint64_t API_CALL mk_media_source_get_create_stamp(const mk_media_source ctx) {
assert(ctx); assert(ctx);
MediaSource *src = (MediaSource *)ctx; MediaSource *src = (MediaSource *)ctx;
@ -252,6 +272,19 @@ API_EXPORT int API_CALL mk_media_source_is_recording(const mk_media_source ctx,i
return src->isRecording((Recorder::type)type); return src->isRecording((Recorder::type)type);
} }
API_EXPORT int API_CALL mk_media_source_get_bytes_speed(const mk_media_source ctx) {
assert(ctx);
MediaSource *src = (MediaSource *)ctx;
return src->getBytesSpeed();
}
API_EXPORT uint64_t API_CALL mk_media_source_get_alive_second(const mk_media_source ctx) {
assert(ctx);
MediaSource *src = (MediaSource *)ctx;
return src->getAliveSecond();
}
API_EXPORT int API_CALL mk_media_source_close(const mk_media_source ctx,int force){ API_EXPORT int API_CALL mk_media_source_close(const mk_media_source ctx,int force){
assert(ctx); assert(ctx);
MediaSource *src = (MediaSource *)ctx; MediaSource *src = (MediaSource *)ctx;

View File

@ -15,7 +15,7 @@
using namespace mediakit; using namespace mediakit;
extern "C" { extern "C" {
#define XX(name, type, value, str, mpeg_id, mp4_id) API_EXPORT const int MK##name = value; #define XX(name, type, value, str, mpeg_id, mp4_id) const int MK##name = value;
CODEC_MAP(XX) CODEC_MAP(XX)
#undef XX #undef XX
} }

View File

@ -28,7 +28,8 @@ API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create3(const char *vhost, c
ProtocolOption option; ProtocolOption option;
option.enable_hls = hls_enabled; option.enable_hls = hls_enabled;
option.enable_mp4 = mp4_enabled; option.enable_mp4 = mp4_enabled;
PlayerProxy::Ptr *obj(new PlayerProxy::Ptr(new PlayerProxy(vhost, app, stream, option, retry_count))); MediaTuple tuple = {vhost, app, stream, ""};
PlayerProxy::Ptr *obj(new PlayerProxy::Ptr(new PlayerProxy(tuple, option, retry_count)));
return (mk_proxy_player)obj; return (mk_proxy_player)obj;
} }
@ -36,7 +37,8 @@ API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create3(const char *vhost, c
API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create4(const char *vhost, const char *app, const char *stream, mk_ini ini, int retry_count) { API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create4(const char *vhost, const char *app, const char *stream, mk_ini ini, int retry_count) {
assert(vhost && app && stream); assert(vhost && app && stream);
ProtocolOption option(*((mINI *)ini)); ProtocolOption option(*((mINI *)ini));
PlayerProxy::Ptr *obj(new PlayerProxy::Ptr(new PlayerProxy(vhost, app, stream, option, retry_count))); MediaTuple tuple = {vhost, app, stream, ""};
PlayerProxy::Ptr *obj(new PlayerProxy::Ptr(new PlayerProxy(tuple, option, retry_count)));
return (mk_proxy_player)obj; return (mk_proxy_player)obj;
} }

View File

@ -18,7 +18,13 @@ using namespace mediakit;
API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create(uint16_t port, int tcp_mode, const char *stream_id) { API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create(uint16_t port, int tcp_mode, const char *stream_id) {
RtpServer::Ptr *server = new RtpServer::Ptr(new RtpServer); RtpServer::Ptr *server = new RtpServer::Ptr(new RtpServer);
(*server)->start(port, stream_id, (RtpServer::TcpMode)tcp_mode); (*server)->start(port, MediaTuple { DEFAULT_VHOST, kRtpAppName, stream_id, "" }, (RtpServer::TcpMode)tcp_mode);
return (mk_rtp_server)server;
}
API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create2(uint16_t port, int tcp_mode, const char *vhost, const char *app, const char *stream_id) {
RtpServer::Ptr *server = new RtpServer::Ptr(new RtpServer);
(*server)->start(port, MediaTuple { vhost, app, stream_id, "" }, (RtpServer::TcpMode)tcp_mode);
return (mk_rtp_server)server; return (mk_rtp_server)server;
} }
@ -71,6 +77,11 @@ API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create(uint16_t port, int enable
return nullptr; return nullptr;
} }
API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create2(uint16_t port, int tcp_mode, const char *vhost, const char *app, const char *stream_id) {
WarnL << "请打开ENABLE_RTPPROXY后再编译";
return nullptr;
}
API_EXPORT void API_CALL mk_rtp_server_release(mk_rtp_server ctx) { API_EXPORT void API_CALL mk_rtp_server_release(mk_rtp_server ctx) {
WarnL << "请打开ENABLE_RTPPROXY后再编译"; WarnL << "请打开ENABLE_RTPPROXY后再编译";
} }

View File

@ -109,6 +109,21 @@ API_EXPORT int API_CALL mk_track_bit_rate(mk_track track) {
return (*((Track::Ptr *) track))->getBitRate(); return (*((Track::Ptr *) track))->getBitRate();
} }
API_EXPORT int API_CALL mk_track_ready(mk_track track) {
assert(track);
return (*((Track::Ptr *)track))->ready();
}
API_EXPORT uint64_t API_CALL mk_track_frames(mk_track track) {
assert(track);
return (*((Track::Ptr *)track))->getFrames();
}
API_EXPORT uint64_t API_CALL mk_track_duration(mk_track track) {
assert(track);
return (*((Track::Ptr *)track))->getDuration();
}
API_EXPORT void *API_CALL mk_track_add_delegate(mk_track track, on_mk_frame_out cb, void *user_data) { API_EXPORT void *API_CALL mk_track_add_delegate(mk_track track, on_mk_frame_out cb, void *user_data) {
return mk_track_add_delegate2(track, cb, user_data, nullptr); return mk_track_add_delegate2(track, cb, user_data, nullptr);
} }
@ -167,6 +182,36 @@ API_EXPORT int API_CALL mk_track_video_fps(mk_track track) {
return 0; return 0;
} }
API_EXPORT uint64_t API_CALL mk_track_video_key_frames(mk_track track) {
assert(track);
auto video = dynamic_pointer_cast<VideoTrack>((*((Track::Ptr *)track)));
if (video) {
return video->getVideoFps();
}
WarnL << "not video track";
return 0;
}
API_EXPORT int API_CALL mk_track_video_gop_size(mk_track track) {
assert(track);
auto video = dynamic_pointer_cast<VideoTrack>((*((Track::Ptr *)track)));
if (video) {
return video->getVideoGopSize();
}
WarnL << "not video track";
return 0;
}
API_EXPORT int API_CALL mk_track_video_gop_interval_ms(mk_track track) {
assert(track);
auto video = dynamic_pointer_cast<VideoTrack>((*((Track::Ptr *)track)));
if (video) {
return video->getVideoGopInterval();
}
WarnL << "not video track";
return 0;
}
API_EXPORT int API_CALL mk_track_audio_sample_rate(mk_track track) { API_EXPORT int API_CALL mk_track_audio_sample_rate(mk_track track) {
assert(track); assert(track);
auto audio = dynamic_pointer_cast<AudioTrack>((*((Track::Ptr *) track))); auto audio = dynamic_pointer_cast<AudioTrack>((*((Track::Ptr *) track)));

View File

@ -29,9 +29,8 @@ static void on_h264_frame(void *user_data, mk_h264_splitter splitter, const char
#else #else
usleep(40 * 1000); usleep(40 * 1000);
#endif #endif
static int dts = 0; uint64_t dts = mk_util_get_current_millisecond();
mk_frame frame = mk_frame_create(MKCodecH264, dts, dts, data, size, NULL, NULL); mk_frame frame = mk_frame_create(MKCodecH264, dts, dts, data, size, NULL, NULL);
dts += 40;
mk_media_input_frame((mk_media) user_data, frame); mk_media_input_frame((mk_media) user_data, frame);
mk_frame_unref(frame); mk_frame_unref(frame);
} }

View File

@ -369,13 +369,13 @@ start_bitrate=0
max_bitrate=0 max_bitrate=0
min_bitrate=0 min_bitrate=0
#nack接收端 #nack接收端, rtp发送端zlm发送rtc流
#Nack缓存包最早时间间隔 #rtp重发缓存列队最大长度单位毫秒
maxNackMS=5000 maxRtpCacheMS=5000
#Nack包检查间隔(包数量) #rtp重发缓存列队最大长度单位个数
rtpCacheCheckInterval=100 maxRtpCacheSize=2048
#nack发送端 #nack发送端rtp接收端zlm接收rtc推流
#最大保留的rtp丢包状态个数 #最大保留的rtp丢包状态个数
nackMaxSize=2048 nackMaxSize=2048
#rtp丢包状态最长保留时间 #rtp丢包状态最长保留时间

View File

@ -55,62 +55,61 @@ void AACRtpDecoder::obtainFrame() {
bool AACRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) { bool AACRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) {
auto payload_size = rtp->getPayloadSize(); auto payload_size = rtp->getPayloadSize();
if (payload_size <= 0) { if (payload_size <= 0) {
//无实际负载 // 无实际负载
return false; return false;
} }
auto stamp = rtp->getStampMS(); auto stamp = rtp->getStampMS();
//rtp数据开始部分 // rtp数据开始部分
auto ptr = rtp->getPayload(); auto ptr = rtp->getPayload();
//rtp数据末尾 // rtp数据末尾
auto end = ptr + payload_size; auto end = ptr + payload_size;
//首2字节表示Au-Header的个数单位bit所以除以16得到Au-Header个数 // 首2字节表示Au-Header的个数单位bit所以除以16得到Au-Header个数
auto au_header_count = ((ptr[0] << 8) | ptr[1]) >> 4; auto au_header_count = ((ptr[0] << 8) | ptr[1]) >> 4;
if (!au_header_count) { if (!au_header_count) {
//问题issue: https://github.com/ZLMediaKit/ZLMediaKit/issues/1869 // 问题issue: https://github.com/ZLMediaKit/ZLMediaKit/issues/1869
WarnL << "invalid aac rtp au_header_count"; WarnL << "invalid aac rtp au_header_count";
return false; return false;
} }
//记录au_header起始指针 // 记录au_header起始指针
auto au_header_ptr = ptr + 2; auto au_header_ptr = ptr + 2;
ptr = au_header_ptr + au_header_count * 2; ptr = au_header_ptr + au_header_count * 2;
if (end < ptr) { if (end < ptr) {
//数据不够 // 数据不够
return false; return false;
} }
if (!_last_dts) { if (!_last_dts) {
//记录第一个时间戳 // 记录第一个时间戳
_last_dts = stamp; _last_dts = stamp;
} }
//每个audio unit时间戳增量 // 每个audio unit时间戳增量
auto dts_inc = (stamp - _last_dts) / au_header_count; auto dts_inc = static_cast<int64_t>(stamp - _last_dts) / au_header_count;
if (dts_inc < 0 && dts_inc > 100) { if (dts_inc < 0 || dts_inc > 100) {
//时间戳增量异常,忽略 // 时间戳增量异常,忽略
dts_inc = 0; dts_inc = 0;
} }
for (int i = 0; i < au_header_count; ++i) { for (auto i = 0u; i < (size_t)au_header_count; ++i) {
// 之后的2字节是AU_HEADER,其中高13位表示一帧AAC负载的字节长度低3位无用 // 之后的2字节是AU_HEADER,其中高13位表示一帧AAC负载的字节长度低3位无用
uint16_t size = ((au_header_ptr[0] << 8) | au_header_ptr[1]) >> 3; auto size = ((au_header_ptr[0] << 8) | au_header_ptr[1]) >> 3;
if (ptr + size > end) { auto len = std::min<int>(size, end - ptr);
//数据不够 if (len <= 0) {
break; break;
} }
_frame->_buffer.append((char *)ptr, len);
ptr += len;
au_header_ptr += 2;
if (size) { if (_frame->size() >= (size_t)size) {
//设置aac数据 // 设置当前audio unit时间戳
_frame->_buffer.assign((char *) ptr, size);
//设置当前audio unit时间戳
_frame->_dts = _last_dts + i * dts_inc; _frame->_dts = _last_dts + i * dts_inc;
ptr += size;
au_header_ptr += 2;
flushData(); flushData();
} }
} }
//记录上次时间戳 // 记录上次时间戳
_last_dts = stamp; _last_dts = stamp;
return false; return false;
} }

View File

@ -40,7 +40,7 @@ bool G711RtpEncoder::inputFrame(const Frame::Ptr &frame) {
auto remain_size = len; auto remain_size = len;
size_t max_size = 160 * _channels * _pkt_dur_ms / 20; // 20 ms per 160 byte size_t max_size = 160 * _channels * _pkt_dur_ms / 20; // 20 ms per 160 byte
size_t n = 0; size_t n = 0;
bool mark = true; bool mark = false;
while (remain_size >= max_size) { while (remain_size >= max_size) {
assert(remain_size >= max_size); assert(remain_size >= max_size);
const size_t rtp_size = max_size; const size_t rtp_size = max_size;

View File

@ -117,7 +117,7 @@ size_t prefixSize(const char *ptr, size_t len) {
H264Track::H264Track(const string &sps, const string &pps, int sps_prefix_len, int pps_prefix_len) { H264Track::H264Track(const string &sps, const string &pps, int sps_prefix_len, int pps_prefix_len) {
_sps = sps.substr(sps_prefix_len); _sps = sps.substr(sps_prefix_len);
_pps = pps.substr(pps_prefix_len); _pps = pps.substr(pps_prefix_len);
update(); H264Track::update();
} }
CodecId H264Track::getCodecId() const { CodecId H264Track::getCodecId() const {
@ -238,6 +238,14 @@ bool H264Track::update() {
return getAVCInfo(_sps, _width, _height, _fps); return getAVCInfo(_sps, _width, _height, _fps);
} }
std::vector<Frame::Ptr> H264Track::getConfigFrames() const {
if (!ready()) {
return {};
}
return { createConfigFrame<H264Frame>(_sps, 0, getIndex()),
createConfigFrame<H264Frame>(_pps, 0, getIndex()) };
}
Track::Ptr H264Track::clone() const { Track::Ptr H264Track::clone() const {
return std::make_shared<H264Track>(*this); return std::make_shared<H264Track>(*this);
} }
@ -284,23 +292,11 @@ bool H264Track::inputFrame_l(const Frame::Ptr &frame) {
void H264Track::insertConfigFrame(const Frame::Ptr &frame) { void H264Track::insertConfigFrame(const Frame::Ptr &frame) {
if (!_sps.empty()) { if (!_sps.empty()) {
auto spsFrame = FrameImp::create<H264Frame>(); VideoTrack::inputFrame(createConfigFrame<H264Frame>(_sps, frame->dts(), frame->getIndex()));
spsFrame->_prefix_size = 4;
spsFrame->_buffer.assign("\x00\x00\x00\x01", 4);
spsFrame->_buffer.append(_sps);
spsFrame->_dts = frame->dts();
spsFrame->setIndex(frame->getIndex());
VideoTrack::inputFrame(spsFrame);
} }
if (!_pps.empty()) { if (!_pps.empty()) {
auto ppsFrame = FrameImp::create<H264Frame>(); VideoTrack::inputFrame(createConfigFrame<H264Frame>(_pps, frame->dts(), frame->getIndex()));
ppsFrame->_prefix_size = 4;
ppsFrame->_buffer.assign("\x00\x00\x00\x01", 4);
ppsFrame->_buffer.append(_pps);
ppsFrame->_dts = frame->dts();
ppsFrame->setIndex(frame->getIndex());
VideoTrack::inputFrame(ppsFrame);
} }
} }

View File

@ -115,6 +115,7 @@ public:
toolkit::Buffer::Ptr getExtraData() const override; toolkit::Buffer::Ptr getExtraData() const override;
void setExtraData(const uint8_t *data, size_t size) override; void setExtraData(const uint8_t *data, size_t size) override;
bool update() override; bool update() override;
std::vector<Frame::Ptr> getConfigFrames() const override;
private: private:
Sdp::Ptr getSdp(uint8_t payload_type) const override; Sdp::Ptr getSdp(uint8_t payload_type) const override;
@ -131,5 +132,17 @@ private:
std::string _pps; std::string _pps;
}; };
template <typename FrameType>
Frame::Ptr createConfigFrame(const std::string &data, uint64_t dts, int index) {
auto frame = FrameImp::create<FrameType>();
frame->_prefix_size = 4;
frame->_buffer.assign("\x00\x00\x00\x01", 4);
frame->_buffer.append(data);
frame->_dts = dts;
frame->setIndex(index);
return frame;
}
}//namespace mediakit }//namespace mediakit
#endif //ZLMEDIAKIT_H264_H #endif //ZLMEDIAKIT_H264_H

View File

@ -66,7 +66,7 @@ H265Track::H265Track(const string &vps,const string &sps, const string &pps,int
_vps = vps.substr(vps_prefix_len); _vps = vps.substr(vps_prefix_len);
_sps = sps.substr(sps_prefix_len); _sps = sps.substr(sps_prefix_len);
_pps = pps.substr(pps_prefix_len); _pps = pps.substr(pps_prefix_len);
update(); H265Track::update();
} }
CodecId H265Track::getCodecId() const { CodecId H265Track::getCodecId() const {
@ -185,6 +185,15 @@ bool H265Track::update() {
return getHEVCInfo(_vps, _sps, _width, _height, _fps); return getHEVCInfo(_vps, _sps, _width, _height, _fps);
} }
std::vector<Frame::Ptr> H265Track::getConfigFrames() const {
if (!ready()) {
return {};
}
return { createConfigFrame<H265Frame>(_vps, 0, getIndex()),
createConfigFrame<H265Frame>(_sps, 0, getIndex()),
createConfigFrame<H265Frame>(_pps, 0, getIndex()) };
}
Track::Ptr H265Track::clone() const { Track::Ptr H265Track::clone() const {
return std::make_shared<H265Track>(*this); return std::make_shared<H265Track>(*this);
} }
@ -194,32 +203,13 @@ void H265Track::insertConfigFrame(const Frame::Ptr &frame) {
return; return;
} }
if (!_vps.empty()) { if (!_vps.empty()) {
auto vpsFrame = FrameImp::create<H265Frame>(); VideoTrack::inputFrame(createConfigFrame<H265Frame>(_vps, frame->dts(), frame->getIndex()));
vpsFrame->_prefix_size = 4;
vpsFrame->_buffer.assign("\x00\x00\x00\x01", 4);
vpsFrame->_buffer.append(_vps);
vpsFrame->_dts = frame->dts();
vpsFrame->setIndex(frame->getIndex());
VideoTrack::inputFrame(vpsFrame);
} }
if (!_sps.empty()) { if (!_sps.empty()) {
auto spsFrame = FrameImp::create<H265Frame>(); VideoTrack::inputFrame(createConfigFrame<H265Frame>(_sps, frame->dts(), frame->getIndex()));
spsFrame->_prefix_size = 4;
spsFrame->_buffer.assign("\x00\x00\x00\x01", 4);
spsFrame->_buffer.append(_sps);
spsFrame->_dts = frame->dts();
spsFrame->setIndex(frame->getIndex());
VideoTrack::inputFrame(spsFrame);
} }
if (!_pps.empty()) { if (!_pps.empty()) {
auto ppsFrame = FrameImp::create<H265Frame>(); VideoTrack::inputFrame(createConfigFrame<H265Frame>(_pps, frame->dts(), frame->getIndex()));
ppsFrame->_prefix_size = 4;
ppsFrame->_buffer.assign("\x00\x00\x00\x01", 4);
ppsFrame->_buffer.append(_pps);
ppsFrame->_dts = frame->dts();
ppsFrame->setIndex(frame->getIndex());
VideoTrack::inputFrame(ppsFrame);
} }
} }

View File

@ -142,6 +142,7 @@ public:
toolkit::Buffer::Ptr getExtraData() const override; toolkit::Buffer::Ptr getExtraData() const override;
void setExtraData(const uint8_t *data, size_t size) override; void setExtraData(const uint8_t *data, size_t size) override;
bool update() override; bool update() override;
std::vector<Frame::Ptr> getConfigFrames() const override;
private: private:
Sdp::Ptr getSdp(uint8_t payload_type) const override; Sdp::Ptr getSdp(uint8_t payload_type) const override;

View File

@ -598,7 +598,7 @@ void JPEGRtpEncoder::rtpSendJpeg(const uint8_t *buf, int size, uint64_t pts, uin
{ {
const uint8_t *qtables[4] = { NULL }; const uint8_t *qtables[4] = { NULL };
int nb_qtables = 0; int nb_qtables = 0;
uint8_t w, h; uint8_t w { 0 }, h { 0 };
uint8_t *p; uint8_t *p;
int off = 0; /* fragment offset of the current JPEG frame */ int off = 0; /* fragment offset of the current JPEG frame */
int len; int len;

View File

@ -1476,6 +1476,16 @@
"value": "{{ZLMediaKit_secret}}", "value": "{{ZLMediaKit_secret}}",
"description": "api操作密钥(配置文件配置)" "description": "api操作密钥(配置文件配置)"
}, },
{
"key": "vhost",
"value": "{{defaultVhost}}",
"description": "虚拟主机例如__defaultVhost__"
},
{
"key": "app",
"value": "rtp",
"description": "应用名,例如 rtp"
},
{ {
"key": "stream_id", "key": "stream_id",
"value": "test", "value": "test",
@ -1517,6 +1527,16 @@
"value": "1", "value": "1",
"description": "tcp模式0时为不启用tcp监听1时为启用tcp监听2时为tcp主动连接模式" "description": "tcp模式0时为不启用tcp监听1时为启用tcp监听2时为tcp主动连接模式"
}, },
{
"key": "vhost",
"value": "{{defaultVhost}}",
"description": "虚拟主机例如__defaultVhost__"
},
{
"key": "app",
"value": "rtp",
"description": "应用名,例如 rtp"
},
{ {
"key": "stream_id", "key": "stream_id",
"value": "test", "value": "test",
@ -1582,6 +1602,16 @@
"value": "1", "value": "1",
"description": "tcp模式0时为不启用tcp监听1时为启用tcp监听" "description": "tcp模式0时为不启用tcp监听1时为启用tcp监听"
}, },
{
"key": "vhost",
"value": "{{defaultVhost}}",
"description": "虚拟主机例如__defaultVhost__"
},
{
"key": "app",
"value": "rtp",
"description": "应用名,例如 rtp"
},
{ {
"key": "stream_id", "key": "stream_id",
"value": "test", "value": "test",
@ -1635,6 +1665,16 @@
"value": "1", "value": "1",
"description": "tcp主动模式时服务端端口" "description": "tcp主动模式时服务端端口"
}, },
{
"key": "vhost",
"value": "{{defaultVhost}}",
"description": "虚拟主机例如__defaultVhost__"
},
{
"key": "app",
"value": "rtp",
"description": "应用名,例如 rtp"
},
{ {
"key": "stream_id", "key": "stream_id",
"value": "test", "value": "test",
@ -1666,6 +1706,16 @@
"value": "{{ZLMediaKit_secret}}", "value": "{{ZLMediaKit_secret}}",
"description": "api操作密钥(配置文件配置)" "description": "api操作密钥(配置文件配置)"
}, },
{
"key": "vhost",
"value": "{{defaultVhost}}",
"description": "虚拟主机例如__defaultVhost__"
},
{
"key": "app",
"value": "rtp",
"description": "应用名,例如 rtp"
},
{ {
"key": "stream_id", "key": "stream_id",
"value": "test", "value": "test",
@ -1697,6 +1747,16 @@
"value": "{{ZLMediaKit_secret}}", "value": "{{ZLMediaKit_secret}}",
"description": "api操作密钥(配置文件配置)" "description": "api操作密钥(配置文件配置)"
}, },
{
"key": "vhost",
"value": "{{defaultVhost}}",
"description": "虚拟主机例如__defaultVhost__"
},
{
"key": "app",
"value": "rtp",
"description": "应用名,例如 rtp"
},
{ {
"key": "stream_id", "key": "stream_id",
"value": "test", "value": "test",
@ -1733,6 +1793,16 @@
"value": "{{ZLMediaKit_secret}}", "value": "{{ZLMediaKit_secret}}",
"description": "api操作密钥(配置文件配置)" "description": "api操作密钥(配置文件配置)"
}, },
{
"key": "vhost",
"value": "{{defaultVhost}}",
"description": "虚拟主机例如__defaultVhost__"
},
{
"key": "app",
"value": "rtp",
"description": "应用名,例如 rtp"
},
{ {
"key": "stream_id", "key": "stream_id",
"value": "test", "value": "test",
@ -1764,6 +1834,16 @@
"value": "{{ZLMediaKit_secret}}", "value": "{{ZLMediaKit_secret}}",
"description": "api操作密钥(配置文件配置)" "description": "api操作密钥(配置文件配置)"
}, },
{
"key": "vhost",
"value": "{{defaultVhost}}",
"description": "虚拟主机例如__defaultVhost__"
},
{
"key": "app",
"value": "rtp",
"description": "应用名,例如 rtp"
},
{ {
"key": "stream_id", "key": "stream_id",
"value": "test", "value": "test",

View File

@ -18,23 +18,15 @@
// ITU-R BT.709 // ITU-R BT.709
#define RGB_TO_Y(R, G, B) (((47 * (R) + 157 * (G) + 16 * (B) + 128) >> 8) + 16) #define RGB_TO_Y(R, G, B) (((47 * (R) + 157 * (G) + 16 * (B) + 128) >> 8) + 16)
#define RGB_TO_U(R, G, B) (((-26 * (R)-87 * (G) + 112 * (B) + 128) >> 8) + 128) #define RGB_TO_U(R, G, B) (((-26 * (R) - 87 * (G) + 112 * (B) + 128) >> 8) + 128)
#define RGB_TO_V(R, G, B) (((112 * (R)-102 * (G)-10 * (B) + 128) >> 8) + 128) #define RGB_TO_V(R, G, B) (((112 * (R) - 102 * (G) - 10 * (B) + 128) >> 8) + 128)
INSTANCE_IMP(VideoStackManager) INSTANCE_IMP(VideoStackManager)
Param::~Param() Param::~Param() { VideoStackManager::Instance().unrefChannel(id, width, height, pixfmt); }
{
VideoStackManager::Instance().unrefChannel(
id, width, height, pixfmt);
}
Channel::Channel(const std::string& id, int width, int height, AVPixelFormat pixfmt) Channel::Channel(const std::string& id, int width, int height, AVPixelFormat pixfmt)
: _id(id) : _id(id), _width(width), _height(height), _pixfmt(pixfmt) {
, _width(width)
, _height(height)
, _pixfmt(pixfmt)
{
_tmp = std::make_shared<mediakit::FFmpegFrame>(); _tmp = std::make_shared<mediakit::FFmpegFrame>();
_tmp->get()->width = _width; _tmp->get()->width = _width;
@ -53,88 +45,72 @@ Channel::Channel(const std::string& id, int width, int height, AVPixelFormat pix
_tmp = _sws->inputFrame(frame); _tmp = _sws->inputFrame(frame);
} }
void Channel::addParam(const std::weak_ptr<Param>& p) void Channel::addParam(const std::weak_ptr<Param>& p) {
{
std::lock_guard<std::recursive_mutex> lock(_mx); std::lock_guard<std::recursive_mutex> lock(_mx);
_params.push_back(p); _params.push_back(p);
} }
void Channel::onFrame(const mediakit::FFmpegFrame::Ptr& frame) void Channel::onFrame(const mediakit::FFmpegFrame::Ptr& frame) {
{
std::weak_ptr<Channel> weakSelf = shared_from_this(); std::weak_ptr<Channel> weakSelf = shared_from_this();
_poller = _poller ? _poller : toolkit::WorkThreadPool::Instance().getPoller(); _poller = _poller ? _poller : toolkit::WorkThreadPool::Instance().getPoller();
_poller->async([weakSelf, frame]() { _poller->async([weakSelf, frame]() {
auto self = weakSelf.lock(); auto self = weakSelf.lock();
if (!self) { if (!self) { return; }
return;
}
self->_tmp = self->_sws->inputFrame(frame); self->_tmp = self->_sws->inputFrame(frame);
self->forEachParam([self](const Param::Ptr& p) { self->fillBuffer(p); }); self->forEachParam([self](const Param::Ptr& p) { self->fillBuffer(p); });
}); });
} }
void Channel::forEachParam(const std::function<void(const Param::Ptr&)>& func) void Channel::forEachParam(const std::function<void(const Param::Ptr&)>& func) {
{
for (auto& wp : _params) { for (auto& wp : _params) {
if (auto sp = wp.lock()) { if (auto sp = wp.lock()) { func(sp); }
func(sp);
}
} }
} }
void Channel::fillBuffer(const Param::Ptr& p) void Channel::fillBuffer(const Param::Ptr& p) {
{ if (auto buf = p->weak_buf.lock()) { copyData(buf, p); }
if (auto buf = p->weak_buf.lock()) {
copyData(buf, p);
}
} }
void Channel::copyData(const mediakit::FFmpegFrame::Ptr& buf, const Param::Ptr& p) void Channel::copyData(const mediakit::FFmpegFrame::Ptr& buf, const Param::Ptr& p) {
{
switch (p->pixfmt) { switch (p->pixfmt) {
case AV_PIX_FMT_YUV420P: { case AV_PIX_FMT_YUV420P: {
for (int i = 0; i < p->height; i++) { for (int i = 0; i < p->height; i++) {
memcpy(buf->get()->data[0] + buf->get()->linesize[0] * (i + p->posY) + p->posX, memcpy(buf->get()->data[0] + buf->get()->linesize[0] * (i + p->posY) + p->posX,
_tmp->get()->data[0] + _tmp->get()->linesize[0] * i, _tmp->get()->data[0] + _tmp->get()->linesize[0] * i, _tmp->get()->width);
_tmp->get()->width); }
} // 确保height为奇数时也能正确的复制到最后一行uv数据
//确保height为奇数时也能正确的复制到最后一行uv数据 for (int i = 0; i < (p->height + 1) / 2; i++) {
for (int i = 0; i < (p->height + 1) / 2; i++) { // U平面
// U平面 memcpy(buf->get()->data[1] + buf->get()->linesize[1] * (i + p->posY / 2) +
memcpy(buf->get()->data[1] + buf->get()->linesize[1] * (i + p->posY / 2) + p->posX / 2, p->posX / 2,
_tmp->get()->data[1] + _tmp->get()->linesize[1] * i, _tmp->get()->data[1] + _tmp->get()->linesize[1] * i, _tmp->get()->width / 2);
_tmp->get()->width / 2);
// V平面 // V平面
memcpy(buf->get()->data[2] + buf->get()->linesize[2] * (i + p->posY / 2) + p->posX / 2, memcpy(buf->get()->data[2] + buf->get()->linesize[2] * (i + p->posY / 2) +
_tmp->get()->data[2] + _tmp->get()->linesize[2] * i, p->posX / 2,
_tmp->get()->width / 2); _tmp->get()->data[2] + _tmp->get()->linesize[2] * i, _tmp->get()->width / 2);
}
break;
}
case AV_PIX_FMT_NV12: {
// TODO: 待实现
break;
} }
break;
}
case AV_PIX_FMT_NV12: {
//TODO: 待实现
break;
}
default: default: WarnL << "No support pixformat: " << av_get_pix_fmt_name(p->pixfmt); break;
WarnL << "No support pixformat: " << av_get_pix_fmt_name(p->pixfmt);
break;
} }
} }
void StackPlayer::addChannel(const std::weak_ptr<Channel>& chn) void StackPlayer::addChannel(const std::weak_ptr<Channel>& chn) {
{
std::lock_guard<std::recursive_mutex> lock(_mx); std::lock_guard<std::recursive_mutex> lock(_mx);
_channels.push_back(chn); _channels.push_back(chn);
} }
void StackPlayer::play() void StackPlayer::play() {
{
auto url = _url; auto url = _url;
//创建拉流 解码对象 // 创建拉流 解码对象
_player = std::make_shared<mediakit::MediaPlayer>(); _player = std::make_shared<mediakit::MediaPlayer>();
std::weak_ptr<mediakit::MediaPlayer> weakPlayer = _player; std::weak_ptr<mediakit::MediaPlayer> weakPlayer = _player;
@ -146,13 +122,9 @@ void StackPlayer::play()
_player->setOnPlayResult([weakPlayer, weakSelf, url](const toolkit::SockException& ex) mutable { _player->setOnPlayResult([weakPlayer, weakSelf, url](const toolkit::SockException& ex) mutable {
TraceL << "StackPlayer: " << url << " OnPlayResult: " << ex.what(); TraceL << "StackPlayer: " << url << " OnPlayResult: " << ex.what();
auto strongPlayer = weakPlayer.lock(); auto strongPlayer = weakPlayer.lock();
if (!strongPlayer) { if (!strongPlayer) { return; }
return;
}
auto self = weakSelf.lock(); auto self = weakSelf.lock();
if (!self) { if (!self) { return; }
return;
}
if (!ex) { if (!ex) {
// 取消定时器 // 取消定时器
@ -164,19 +136,18 @@ void StackPlayer::play()
self->rePlay(url); self->rePlay(url);
} }
auto videoTrack = std::dynamic_pointer_cast<mediakit::VideoTrack>(strongPlayer->getTrack(mediakit::TrackVideo, false)); auto videoTrack = std::dynamic_pointer_cast<mediakit::VideoTrack>(
//auto audioTrack = std::dynamic_pointer_cast<mediakit::AudioTrack>(strongPlayer->getTrack(mediakit::TrackAudio, false)); strongPlayer->getTrack(mediakit::TrackVideo, false));
// auto audioTrack = std::dynamic_pointer_cast<mediakit::AudioTrack>(strongPlayer->getTrack(mediakit::TrackAudio, false));
if (videoTrack) { if (videoTrack) {
//TODO:添加使用显卡还是cpu解码的判断逻辑 // TODO:添加使用显卡还是cpu解码的判断逻辑
//auto decoder = std::make_shared<FFmpegDecoder>(videoTrack, 1, std::vector<std::string>{ "hevc_cuvid", "h264_cuvid"}); auto decoder = std::make_shared<mediakit::FFmpegDecoder>(
auto decoder = std::make_shared<mediakit::FFmpegDecoder>(videoTrack, 0, std::vector<std::string> { "h264", "hevc" }); videoTrack, 0, std::vector<std::string>{"h264", "hevc"});
decoder->setOnDecode([weakSelf](const mediakit::FFmpegFrame::Ptr& frame) mutable { decoder->setOnDecode([weakSelf](const mediakit::FFmpegFrame::Ptr& frame) mutable {
auto self = weakSelf.lock(); auto self = weakSelf.lock();
if (!self) { if (!self) { return; }
return;
}
self->onFrame(frame); self->onFrame(frame);
}); });
@ -190,14 +161,10 @@ void StackPlayer::play()
_player->setOnShutdown([weakPlayer, url, weakSelf](const toolkit::SockException& ex) { _player->setOnShutdown([weakPlayer, url, weakSelf](const toolkit::SockException& ex) {
TraceL << "StackPlayer: " << url << " OnShutdown: " << ex.what(); TraceL << "StackPlayer: " << url << " OnShutdown: " << ex.what();
auto strongPlayer = weakPlayer.lock(); auto strongPlayer = weakPlayer.lock();
if (!strongPlayer) { if (!strongPlayer) { return; }
return;
}
auto self = weakSelf.lock(); auto self = weakSelf.lock();
if (!self) { if (!self) { return; }
return;
}
self->onDisconnect(); self->onDisconnect();
@ -207,18 +174,14 @@ void StackPlayer::play()
_player->play(url); _player->play(url);
} }
void StackPlayer::onFrame(const mediakit::FFmpegFrame::Ptr& frame) void StackPlayer::onFrame(const mediakit::FFmpegFrame::Ptr& frame) {
{
std::lock_guard<std::recursive_mutex> lock(_mx); std::lock_guard<std::recursive_mutex> lock(_mx);
for (auto& weak_chn : _channels) { for (auto& weak_chn : _channels) {
if (auto chn = weak_chn.lock()) { if (auto chn = weak_chn.lock()) { chn->onFrame(frame); }
chn->onFrame(frame);
}
} }
} }
void StackPlayer::onDisconnect() void StackPlayer::onDisconnect() {
{
std::lock_guard<std::recursive_mutex> lock(_mx); std::lock_guard<std::recursive_mutex> lock(_mx);
for (auto& weak_chn : _channels) { for (auto& weak_chn : _channels) {
if (auto chn = weak_chn.lock()) { if (auto chn = weak_chn.lock()) {
@ -228,31 +191,22 @@ void StackPlayer::onDisconnect()
} }
} }
void StackPlayer::rePlay(const std::string& url) void StackPlayer::rePlay(const std::string& url) {
{
_failedCount++; _failedCount++;
auto delay = MAX(2 * 1000, MIN(_failedCount * 3 * 1000, 60 * 1000)); //步进延迟 重试间隔 auto delay = MAX(2 * 1000, MIN(_failedCount * 3 * 1000, 60 * 1000));// 步进延迟 重试间隔
std::weak_ptr<StackPlayer> weakSelf = shared_from_this(); std::weak_ptr<StackPlayer> weakSelf = shared_from_this();
_timer = std::make_shared<toolkit::Timer>( _timer = std::make_shared<toolkit::Timer>(delay / 1000.0f, [weakSelf, url]() {
delay / 1000.0f, [weakSelf, url]() { auto self = weakSelf.lock();
auto self = weakSelf.lock(); if (!self) {}
if (!self) { WarnL << "replay [" << self->_failedCount << "]:" << url;
} self->_player->play(url);
WarnL << "replay [" << self->_failedCount << "]:" << url; return false;
self->_player->play(url); }, nullptr);
return false;
},
nullptr);
} }
VideoStack::VideoStack(const std::string& id, int width, int height, AVPixelFormat pixfmt, float fps, int bitRate) VideoStack::VideoStack(const std::string& id, int width, int height, AVPixelFormat pixfmt,
: _id(id) float fps, int bitRate)
, _width(width) : _id(id), _width(width), _height(height), _pixfmt(pixfmt), _fps(fps), _bitRate(bitRate) {
, _height(height)
, _pixfmt(pixfmt)
, _fps(fps)
, _bitRate(bitRate)
{
_buffer = std::make_shared<mediakit::FFmpegFrame>(); _buffer = std::make_shared<mediakit::FFmpegFrame>();
@ -262,7 +216,8 @@ VideoStack::VideoStack(const std::string& id, int width, int height, AVPixelForm
av_frame_get_buffer(_buffer->get(), 32); av_frame_get_buffer(_buffer->get(), 32);
_dev = std::make_shared<mediakit::DevChannel>(mediakit::MediaTuple { DEFAULT_VHOST, "live", _id }); _dev = std::make_shared<mediakit::DevChannel>(
mediakit::MediaTuple{DEFAULT_VHOST, "live", _id, ""});
mediakit::VideoInfo info; mediakit::VideoInfo info;
info.codecId = mediakit::CodecH264; info.codecId = mediakit::CodecH264;
@ -272,34 +227,28 @@ VideoStack::VideoStack(const std::string& id, int width, int height, AVPixelForm
info.iBitRate = _bitRate; info.iBitRate = _bitRate;
_dev->initVideo(info); _dev->initVideo(info);
//dev->initAudio(); //TODO:音频 // dev->initAudio(); //TODO:音频
_dev->addTrackCompleted(); _dev->addTrackCompleted();
_isExit = false; _isExit = false;
} }
VideoStack::~VideoStack() VideoStack::~VideoStack() {
{
_isExit = true; _isExit = true;
if (_thread.joinable()) { if (_thread.joinable()) { _thread.join(); }
_thread.join();
}
} }
void VideoStack::setParam(const Params& params) void VideoStack::setParam(const Params& params) {
{
if (_params) { if (_params) {
for (auto& p : (*_params)) { for (auto& p : (*_params)) {
if (!p) if (!p) continue;
continue;
p->weak_buf.reset(); p->weak_buf.reset();
} }
} }
initBgColor(); initBgColor();
for (auto& p : (*params)) { for (auto& p : (*params)) {
if (!p) if (!p) continue;
continue;
p->weak_buf = _buffer; p->weak_buf = _buffer;
if (auto chn = p->weak_chn.lock()) { if (auto chn = p->weak_chn.lock()) {
chn->addParam(p); chn->addParam(p);
@ -309,14 +258,14 @@ void VideoStack::setParam(const Params& params)
_params = params; _params = params;
} }
void VideoStack::start() void VideoStack::start() {
{
_thread = std::thread([&]() { _thread = std::thread([&]() {
uint64_t pts = 0; uint64_t pts = 0;
int frameInterval = 1000 / _fps; int frameInterval = 1000 / _fps;
auto lastEncTP = std::chrono::steady_clock::now(); auto lastEncTP = std::chrono::steady_clock::now();
while (!_isExit) { while (!_isExit) {
if (std::chrono::steady_clock::now() - lastEncTP > std::chrono::milliseconds(frameInterval)) { if (std::chrono::steady_clock::now() - lastEncTP >
std::chrono::milliseconds(frameInterval)) {
lastEncTP = std::chrono::steady_clock::now(); lastEncTP = std::chrono::steady_clock::now();
_dev->inputYUV((char**)_buffer->get()->data, _buffer->get()->linesize, pts); _dev->inputYUV((char**)_buffer->get()->data, _buffer->get()->linesize, pts);
@ -326,9 +275,8 @@ void VideoStack::start()
}); });
} }
void VideoStack::initBgColor() void VideoStack::initBgColor() {
{ // 填充底色
//填充底色
auto R = 20; auto R = 20;
auto G = 20; auto G = 20;
auto B = 20; auto B = 20;
@ -342,27 +290,19 @@ void VideoStack::initBgColor()
memset(_buffer->get()->data[2], V, _buffer->get()->linesize[2] * _height / 2); memset(_buffer->get()->data[2], V, _buffer->get()->linesize[2] * _height / 2);
} }
Channel::Ptr VideoStackManager::getChannel(const std::string& id, Channel::Ptr VideoStackManager::getChannel(const std::string& id, int width, int height,
int width, AVPixelFormat pixfmt) {
int height,
AVPixelFormat pixfmt)
{
std::lock_guard<std::recursive_mutex> lock(_mx); std::lock_guard<std::recursive_mutex> lock(_mx);
auto key = id + std::to_string(width) + std::to_string(height) + std::to_string(pixfmt); auto key = id + std::to_string(width) + std::to_string(height) + std::to_string(pixfmt);
auto it = _channelMap.find(key); auto it = _channelMap.find(key);
if (it != _channelMap.end()) { if (it != _channelMap.end()) { return it->second->acquire(); }
return it->second->acquire();
}
return createChannel(id, width, height, pixfmt); return createChannel(id, width, height, pixfmt);
} }
void VideoStackManager::unrefChannel(const std::string& id, void VideoStackManager::unrefChannel(const std::string& id, int width, int height,
int width, AVPixelFormat pixfmt) {
int height,
AVPixelFormat pixfmt)
{
std::lock_guard<std::recursive_mutex> lock(_mx); std::lock_guard<std::recursive_mutex> lock(_mx);
auto key = id + std::to_string(width) + std::to_string(height) + std::to_string(pixfmt); auto key = id + std::to_string(width) + std::to_string(height) + std::to_string(pixfmt);
@ -377,8 +317,7 @@ void VideoStackManager::unrefChannel(const std::string& id,
} }
} }
int VideoStackManager::startVideoStack(const Json::Value& json) int VideoStackManager::startVideoStack(const Json::Value& json) {
{
std::string id; std::string id;
int width, height; int width, height;
@ -392,8 +331,7 @@ int VideoStackManager::startVideoStack(const Json::Value& json)
auto stack = std::make_shared<VideoStack>(id, width, height); auto stack = std::make_shared<VideoStack>(id, width, height);
for (auto& p : (*params)) { for (auto& p : (*params)) {
if (!p) if (!p) continue;
continue;
p->weak_chn = getChannel(p->id, p->width, p->height, p->pixfmt); p->weak_chn = getChannel(p->id, p->width, p->height, p->pixfmt);
} }
@ -405,13 +343,13 @@ int VideoStackManager::startVideoStack(const Json::Value& json)
return 0; return 0;
} }
int VideoStackManager::resetVideoStack(const Json::Value& json) int VideoStackManager::resetVideoStack(const Json::Value& json) {
{
std::string id; std::string id;
int width, height; int width, height;
auto params = parseParams(json, id, width, height); auto params = parseParams(json, id, width, height);
if (!params) { if (!params) {
ErrorL << "Videostack parse params failed!";
return -1; return -1;
} }
@ -419,15 +357,12 @@ int VideoStackManager::resetVideoStack(const Json::Value& json)
{ {
std::lock_guard<std::recursive_mutex> lock(_mx); std::lock_guard<std::recursive_mutex> lock(_mx);
auto it = _stackMap.find(id); auto it = _stackMap.find(id);
if (it == _stackMap.end()) { if (it == _stackMap.end()) { return -2; }
return -2;
}
stack = it->second; stack = it->second;
} }
for (auto& p : (*params)) { for (auto& p : (*params)) {
if (!p) if (!p) continue;
continue;
p->weak_chn = getChannel(p->id, p->width, p->height, p->pixfmt); p->weak_chn = getChannel(p->id, p->width, p->height, p->pixfmt);
} }
@ -435,8 +370,7 @@ int VideoStackManager::resetVideoStack(const Json::Value& json)
return 0; return 0;
} }
int VideoStackManager::stopVideoStack(const std::string& id) int VideoStackManager::stopVideoStack(const std::string& id) {
{
std::lock_guard<std::recursive_mutex> lock(_mx); std::lock_guard<std::recursive_mutex> lock(_mx);
auto it = _stackMap.find(id); auto it = _stackMap.find(id);
if (it != _stackMap.end()) { if (it != _stackMap.end()) {
@ -447,93 +381,90 @@ int VideoStackManager::stopVideoStack(const std::string& id)
return -1; return -1;
} }
mediakit::FFmpegFrame::Ptr VideoStackManager::getBgImg() mediakit::FFmpegFrame::Ptr VideoStackManager::getBgImg() { return _bgImg; }
{
return _bgImg;
}
Params VideoStackManager::parseParams(const Json::Value& json, template<typename T> T getJsonValue(const Json::Value& json, const std::string& key) {
std::string& id, if (!json.isMember(key)) {
int& width, throw Json::LogicError("VideoStack parseParams missing required field: " + key);
int& height)
{
try {
id = json["id"].asString();
width = json["width"].asInt();
height = json["height"].asInt();
int rows = json["row"].asInt(); //堆叠行数
int cols = json["col"].asInt(); //堆叠列数
float gapv = json["gapv"].asFloat(); //垂直间距
float gaph = json["gaph"].asFloat(); //水平间距
//单个间距
int gaphPix = static_cast<int>(round(width * gaph));
int gapvPix = static_cast<int>(round(height * gapv));
// 根据间距计算格子宽高
int gridWidth = cols > 1 ? (width - gaphPix * (cols - 1)) / cols : width;
int gridHeight = rows > 1 ? (height - gapvPix * (rows - 1)) / rows : height;
auto params = std::make_shared<std::vector<Param::Ptr>>(rows * cols);
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
std::string url = json["url"][row][col].asString();
auto param = std::make_shared<Param>();
param->posX = gridWidth * col + col * gaphPix;
param->posY = gridHeight * row + row * gapvPix;
param->width = gridWidth;
param->height = gridHeight;
param->id = url;
(*params)[row * cols + col] = param;
}
}
//判断是否需要合并格子 (焦点屏)
if (!json["span"].empty() && json.isMember("span")) {
for (const auto& subArray : json["span"]) {
if (!subArray.isArray() || subArray.size() != 2) {
throw Json::LogicError("Incorrect 'span' sub-array format in JSON");
}
std::array<int, 4> mergePos;
int index = 0;
for (const auto& innerArray : subArray) {
if (!innerArray.isArray() || innerArray.size() != 2) {
throw Json::LogicError("Incorrect 'span' inner-array format in JSON");
}
for (const auto& number : innerArray) {
if (index < mergePos.size()) {
mergePos[index++] = number.asInt();
}
}
}
for (int i = mergePos[0]; i <= mergePos[2]; i++) {
for (int j = mergePos[1]; j <= mergePos[3]; j++) {
if (i == mergePos[0] && j == mergePos[1]) {
(*params)[i * cols + j]->width = (mergePos[3] - mergePos[1] + 1) * gridWidth + (mergePos[3] - mergePos[1]) * gapvPix;
(*params)[i * cols + j]->height = (mergePos[2] - mergePos[0] + 1) * gridHeight + (mergePos[2] - mergePos[0]) * gaphPix;
} else {
(*params)[i * cols + j] = nullptr;
}
}
}
}
}
return params;
} catch (const std::exception& e) {
ErrorL << "Videostack parse params failed! " << e.what();
return nullptr;
} }
return json[key].as<T>();
} }
bool VideoStackManager::loadBgImg(const std::string& path) Params VideoStackManager::parseParams(const Json::Value& json, std::string& id, int& width,
{ int& height) {
id = getJsonValue<std::string>(json, "id");
width = getJsonValue<int>(json, "width");
height = getJsonValue<int>(json, "height");
int rows = getJsonValue<int>(json, "row");// 行数
int cols = getJsonValue<int>(json, "col");// 列数
float gapv = json["gapv"].asFloat();// 垂直间距
float gaph = json["gaph"].asFloat();// 水平间距
// 单个间距
int gaphPix = static_cast<int>(round(width * gaph));
int gapvPix = static_cast<int>(round(height * gapv));
// 根据间距计算格子宽高
int gridWidth = cols > 1 ? (width - gaphPix * (cols - 1)) / cols : width;
int gridHeight = rows > 1 ? (height - gapvPix * (rows - 1)) / rows : height;
auto params = std::make_shared<std::vector<Param::Ptr>>(rows * cols);
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
std::string url = json["url"][row][col].asString();
auto param = std::make_shared<Param>();
param->posX = gridWidth * col + col * gaphPix;
param->posY = gridHeight * row + row * gapvPix;
param->width = gridWidth;
param->height = gridHeight;
param->id = url;
(*params)[row * cols + col] = param;
}
}
// 判断是否需要合并格子 (焦点屏)
if (json.isMember("span") && json["span"].isArray() && json["span"].size() > 0) {
for (const auto& subArray : json["span"]) {
if (!subArray.isArray() || subArray.size() != 2) {
throw Json::LogicError("Incorrect 'span' sub-array format in JSON");
}
std::array<int, 4> mergePos;
unsigned int index = 0;
for (const auto& innerArray : subArray) {
if (!innerArray.isArray() || innerArray.size() != 2) {
throw Json::LogicError("Incorrect 'span' inner-array format in JSON");
}
for (const auto& number : innerArray) {
if (index < mergePos.size()) { mergePos[index++] = number.asInt(); }
}
}
for (int i = mergePos[0]; i <= mergePos[2]; i++) {
for (int j = mergePos[1]; j <= mergePos[3]; j++) {
if (i == mergePos[0] && j == mergePos[1]) {
(*params)[i * cols + j]->width =
(mergePos[3] - mergePos[1] + 1) * gridWidth +
(mergePos[3] - mergePos[1]) * gapvPix;
(*params)[i * cols + j]->height =
(mergePos[2] - mergePos[0] + 1) * gridHeight +
(mergePos[2] - mergePos[0]) * gaphPix;
} else {
(*params)[i * cols + j] = nullptr;
}
}
}
}
}
return params;
}
bool VideoStackManager::loadBgImg(const std::string& path) {
_bgImg = std::make_shared<mediakit::FFmpegFrame>(); _bgImg = std::make_shared<mediakit::FFmpegFrame>();
_bgImg->get()->width = 1280; _bgImg->get()->width = 1280;
@ -543,21 +474,21 @@ bool VideoStackManager::loadBgImg(const std::string& path)
av_frame_get_buffer(_bgImg->get(), 32); av_frame_get_buffer(_bgImg->get(), 32);
std::ifstream file(path, std::ios::binary); std::ifstream file(path, std::ios::binary);
if (!file.is_open()) { if (!file.is_open()) { return false; }
return false;
}
file.read((char*)_bgImg->get()->data[0], _bgImg->get()->linesize[0] * _bgImg->get()->height); // Y file.read((char*)_bgImg->get()->data[0],
file.read((char*)_bgImg->get()->data[1], _bgImg->get()->linesize[1] * _bgImg->get()->height / 2); // U _bgImg->get()->linesize[0] * _bgImg->get()->height);// Y
file.read((char*)_bgImg->get()->data[2], _bgImg->get()->linesize[2] * _bgImg->get()->height / 2); // V file.read((char*)_bgImg->get()->data[1],
_bgImg->get()->linesize[1] * _bgImg->get()->height / 2);// U
file.read((char*)_bgImg->get()->data[2],
_bgImg->get()->linesize[2] * _bgImg->get()->height / 2);// V
return true; return true;
} }
Channel::Ptr VideoStackManager::createChannel(const std::string& id, void VideoStackManager::clear() { _stackMap.clear(); }
int width,
int height, Channel::Ptr VideoStackManager::createChannel(const std::string& id, int width, int height,
AVPixelFormat pixfmt) AVPixelFormat pixfmt) {
{
std::lock_guard<std::recursive_mutex> lock(_mx); std::lock_guard<std::recursive_mutex> lock(_mx);
StackPlayer::Ptr player; StackPlayer::Ptr player;
@ -568,24 +499,24 @@ Channel::Ptr VideoStackManager::createChannel(const std::string& id,
player = createPlayer(id); player = createPlayer(id);
} }
auto refChn = std::make_shared<RefWrapper<Channel::Ptr>>(std::make_shared<Channel>(id, width, height, pixfmt)); auto refChn = std::make_shared<RefWrapper<Channel::Ptr>>(
std::make_shared<Channel>(id, width, height, pixfmt));
auto chn = refChn->acquire(); auto chn = refChn->acquire();
player->addChannel(chn); player->addChannel(chn);
_channelMap[id + std::to_string(width) + std::to_string(height) + std::to_string(pixfmt)] = refChn; _channelMap[id + std::to_string(width) + std::to_string(height) + std::to_string(pixfmt)] =
refChn;
return chn; return chn;
} }
StackPlayer::Ptr VideoStackManager::createPlayer(const std::string& id) StackPlayer::Ptr VideoStackManager::createPlayer(const std::string& id) {
{
std::lock_guard<std::recursive_mutex> lock(_mx); std::lock_guard<std::recursive_mutex> lock(_mx);
auto refPlayer = std::make_shared<RefWrapper<StackPlayer::Ptr>>(std::make_shared<StackPlayer>(id)); auto refPlayer =
std::make_shared<RefWrapper<StackPlayer::Ptr>>(std::make_shared<StackPlayer>(id));
_playerMap[id] = refPlayer; _playerMap[id] = refPlayer;
auto player = refPlayer->acquire(); auto player = refPlayer->acquire();
if (!id.empty()) { if (!id.empty()) { player->play(); }
player->play();
}
return player; return player;
} }

View File

@ -5,29 +5,23 @@
#include "Player/MediaPlayer.h" #include "Player/MediaPlayer.h"
#include "json/json.h" #include "json/json.h"
#include <mutex> #include <mutex>
template <typename T> template<typename T> class RefWrapper {
class RefWrapper { public:
public:
using Ptr = std::shared_ptr<RefWrapper<T>>; using Ptr = std::shared_ptr<RefWrapper<T>>;
template <typename... Args> template<typename... Args>
explicit RefWrapper(Args&&... args) explicit RefWrapper(Args&&... args) : _rc(0), _entity(std::forward<Args>(args)...) {}
: _rc(0)
, _entity(std::forward<Args>(args)...)
{
}
T acquire() T acquire() {
{
++_rc; ++_rc;
return _entity; return _entity;
} }
bool dispose() { return --_rc <= 0; } bool dispose() { return --_rc <= 0; }
private: private:
T _entity;
std::atomic<int> _rc; std::atomic<int> _rc;
T _entity;
}; };
class Channel; class Channel;
@ -40,7 +34,7 @@ struct Param {
int width = 0; int width = 0;
int height = 0; int height = 0;
AVPixelFormat pixfmt = AV_PIX_FMT_YUV420P; AVPixelFormat pixfmt = AV_PIX_FMT_YUV420P;
std::string id {}; std::string id{};
// runtime // runtime
std::weak_ptr<Channel> weak_chn; std::weak_ptr<Channel> weak_chn;
@ -52,7 +46,7 @@ struct Param {
using Params = std::shared_ptr<std::vector<Param::Ptr>>; using Params = std::shared_ptr<std::vector<Param::Ptr>>;
class Channel : public std::enable_shared_from_this<Channel> { class Channel : public std::enable_shared_from_this<Channel> {
public: public:
using Ptr = std::shared_ptr<Channel>; using Ptr = std::shared_ptr<Channel>;
Channel(const std::string& id, int width, int height, AVPixelFormat pixfmt); Channel(const std::string& id, int width, int height, AVPixelFormat pixfmt);
@ -63,12 +57,12 @@ class Channel : public std::enable_shared_from_this<Channel> {
void fillBuffer(const Param::Ptr& p); void fillBuffer(const Param::Ptr& p);
protected: protected:
void forEachParam(const std::function<void(const Param::Ptr&)>& func); void forEachParam(const std::function<void(const Param::Ptr&)>& func);
void copyData(const mediakit::FFmpegFrame::Ptr& buf, const Param::Ptr& p); void copyData(const mediakit::FFmpegFrame::Ptr& buf, const Param::Ptr& p);
private: private:
std::string _id; std::string _id;
int _width; int _width;
int _height; int _height;
@ -84,13 +78,10 @@ class Channel : public std::enable_shared_from_this<Channel> {
}; };
class StackPlayer : public std::enable_shared_from_this<StackPlayer> { class StackPlayer : public std::enable_shared_from_this<StackPlayer> {
public: public:
using Ptr = std::shared_ptr<StackPlayer>; using Ptr = std::shared_ptr<StackPlayer>;
StackPlayer(const std::string& url) StackPlayer(const std::string& url) : _url(url) {}
: _url(url)
{
}
void addChannel(const std::weak_ptr<Channel>& chn); void addChannel(const std::weak_ptr<Channel>& chn);
@ -100,14 +91,14 @@ class StackPlayer : public std::enable_shared_from_this<StackPlayer> {
void onDisconnect(); void onDisconnect();
protected: protected:
void rePlay(const std::string& url); void rePlay(const std::string& url);
private: private:
std::string _url; std::string _url;
mediakit::MediaPlayer::Ptr _player; mediakit::MediaPlayer::Ptr _player;
//用于断线重连 // 用于断线重连
toolkit::Timer::Ptr _timer; toolkit::Timer::Ptr _timer;
int _failedCount = 0; int _failedCount = 0;
@ -116,15 +107,12 @@ class StackPlayer : public std::enable_shared_from_this<StackPlayer> {
}; };
class VideoStack { class VideoStack {
public: public:
using Ptr = std::shared_ptr<VideoStack>; using Ptr = std::shared_ptr<VideoStack>;
VideoStack(const std::string& url, VideoStack(const std::string& url, int width = 1920, int height = 1080,
int width = 1920, AVPixelFormat pixfmt = AV_PIX_FMT_YUV420P, float fps = 25.0,
int height = 1080, int bitRate = 2 * 1024 * 1024);
AVPixelFormat pixfmt = AV_PIX_FMT_YUV420P,
float fps = 25.0,
int bitRate = 2 * 1024 * 1024);
~VideoStack(); ~VideoStack();
@ -132,15 +120,15 @@ class VideoStack {
void start(); void start();
protected: protected:
void initBgColor(); void initBgColor();
public: public:
Params _params; Params _params;
mediakit::FFmpegFrame::Ptr _buffer; mediakit::FFmpegFrame::Ptr _buffer;
private: private:
std::string _id; std::string _id;
int _width; int _width;
int _height; int _height;
@ -156,47 +144,41 @@ class VideoStack {
}; };
class VideoStackManager { class VideoStackManager {
public: public:
static VideoStackManager& Instance(); // 创建拼接流
Channel::Ptr getChannel(const std::string& id,
int width,
int height,
AVPixelFormat pixfmt);
void unrefChannel(const std::string& id,
int width,
int height,
AVPixelFormat pixfmt);
int startVideoStack(const Json::Value& json); int startVideoStack(const Json::Value& json);
// 停止拼接流
int stopVideoStack(const std::string& id);
// 可以在不断流的情况下,修改拼接流的配置(实现切换拼接屏内容)
int resetVideoStack(const Json::Value& json); int resetVideoStack(const Json::Value& json);
int stopVideoStack(const std::string& id); public:
static VideoStackManager& Instance();
Channel::Ptr getChannel(const std::string& id, int width, int height, AVPixelFormat pixfmt);
void unrefChannel(const std::string& id, int width, int height, AVPixelFormat pixfmt);
bool loadBgImg(const std::string& path); bool loadBgImg(const std::string& path);
void clear();
mediakit::FFmpegFrame::Ptr getBgImg(); mediakit::FFmpegFrame::Ptr getBgImg();
protected: protected:
Params parseParams(const Json::Value& json, Params parseParams(const Json::Value& json, std::string& id, int& width, int& height);
std::string& id,
int& width,
int& height);
protected: protected:
Channel::Ptr createChannel(const std::string& id, Channel::Ptr createChannel(const std::string& id, int width, int height, AVPixelFormat pixfmt);
int width,
int height,
AVPixelFormat pixfmt);
StackPlayer::Ptr createPlayer(const std::string& id); StackPlayer::Ptr createPlayer(const std::string& id);
private: private:
mediakit::FFmpegFrame::Ptr _bgImg; mediakit::FFmpegFrame::Ptr _bgImg;
private: private:
std::recursive_mutex _mx; std::recursive_mutex _mx;
std::unordered_map<std::string, VideoStack::Ptr> _stackMap; std::unordered_map<std::string, VideoStack::Ptr> _stackMap;

View File

@ -8,6 +8,7 @@
* may be found in the AUTHORS file in the root of the source tree. * may be found in the AUTHORS file in the root of the source tree.
*/ */
#include <exception>
#include <sys/stat.h> #include <sys/stat.h>
#include <math.h> #include <math.h>
#include <signal.h> #include <signal.h>
@ -375,9 +376,6 @@ static ServiceController<FFmpegSource> s_ffmpeg_src;
static ServiceController<RtpServer> s_rtp_server; static ServiceController<RtpServer> s_rtp_server;
#endif #endif
static inline string getProxyKey(const string &vhost, const string &app, const string &stream) {
return vhost + "/" + app + "/" + stream;
}
static inline string getPusherKey(const string &schema, const string &vhost, const string &app, const string &stream, static inline string getPusherKey(const string &schema, const string &vhost, const string &app, const string &stream,
const string &dst_url) { const string &dst_url) {
@ -476,18 +474,19 @@ Value makeMediaSourceJson(MediaSource &media){
} }
#if defined(ENABLE_RTPPROXY) #if defined(ENABLE_RTPPROXY)
uint16_t openRtpServer(uint16_t local_port, const string &stream_id, int tcp_mode, const string &local_ip, bool re_use_port, uint32_t ssrc, int only_track, bool multiplex) { uint16_t openRtpServer(uint16_t local_port, const mediakit::MediaTuple &tuple, int tcp_mode, const string &local_ip, bool re_use_port, uint32_t ssrc, int only_track, bool multiplex) {
if (s_rtp_server.find(stream_id)) { auto key = tuple.shortUrl();
//为了防止RtpProcess所有权限混乱的问题不允许重复添加相同的stream_id if (s_rtp_server.find(key)) {
//为了防止RtpProcess所有权限混乱的问题不允许重复添加相同的key
return 0; return 0;
} }
auto server = s_rtp_server.makeWithAction(stream_id, [&](RtpServer::Ptr server) { auto server = s_rtp_server.makeWithAction(key, [&](RtpServer::Ptr server) {
server->start(local_port, stream_id, (RtpServer::TcpMode)tcp_mode, local_ip.c_str(), re_use_port, ssrc, only_track, multiplex); server->start(local_port, tuple, (RtpServer::TcpMode)tcp_mode, local_ip.c_str(), re_use_port, ssrc, only_track, multiplex);
}); });
server->setOnDetach([stream_id](const SockException &ex) { server->setOnDetach([key](const SockException &ex) {
//设置rtp超时移除事件 //设置rtp超时移除事件
s_rtp_server.erase(stream_id); s_rtp_server.erase(key);
}); });
//回复json //回复json
@ -580,17 +579,17 @@ void getStatisticJson(const function<void(Value &val)> &cb) {
#endif #endif
} }
void addStreamProxy(const string &vhost, const string &app, const string &stream, const string &url, int retry_count, void addStreamProxy(const MediaTuple &tuple, const string &url, int retry_count,
const ProtocolOption &option, int rtp_type, float timeout_sec, const mINI &args, const ProtocolOption &option, int rtp_type, float timeout_sec, const mINI &args,
const function<void(const SockException &ex, const string &key)> &cb) { const function<void(const SockException &ex, const string &key)> &cb) {
auto key = getProxyKey(vhost, app, stream); auto key = tuple.shortUrl();
if (s_player_proxy.find(key)) { if (s_player_proxy.find(key)) {
//已经在拉流了 //已经在拉流了
cb(SockException(Err_other, "This stream already exists"), key); cb(SockException(Err_other, "This stream already exists"), key);
return; return;
} }
//添加拉流代理 //添加拉流代理
auto player = s_player_proxy.make(key, vhost, app, stream, option, retry_count); auto player = s_player_proxy.make(key, tuple, option, retry_count);
// 先透传拷贝参数 // 先透传拷贝参数
for (auto &pr : args) { for (auto &pr : args) {
@ -1100,9 +1099,13 @@ void installWebApi() {
ProtocolOption option(allArgs); ProtocolOption option(allArgs);
auto retry_count = allArgs["retry_count"].empty()? -1: allArgs["retry_count"].as<int>(); auto retry_count = allArgs["retry_count"].empty()? -1: allArgs["retry_count"].as<int>();
addStreamProxy(allArgs["vhost"],
allArgs["app"], std::string vhost = DEFAULT_VHOST;
allArgs["stream"], if (!allArgs["vhost"].empty()) {
vhost = allArgs["vhost"];
}
auto tuple = MediaTuple { vhost, allArgs["app"], allArgs["stream"], "" };
addStreamProxy(tuple,
allArgs["url"], allArgs["url"],
retry_count, retry_count,
option, option,
@ -1198,7 +1201,15 @@ void installWebApi() {
api_regist("/index/api/getRtpInfo",[](API_ARGS_MAP){ api_regist("/index/api/getRtpInfo",[](API_ARGS_MAP){
CHECK_SECRET(); CHECK_SECRET();
CHECK_ARGS("stream_id"); CHECK_ARGS("stream_id");
auto src = MediaSource::find(DEFAULT_VHOST, kRtpAppName, allArgs["stream_id"]); std::string vhost = DEFAULT_VHOST;
if (!allArgs["vhost"].empty()) {
vhost = allArgs["vhost"];
}
std::string app = kRtpAppName;
if (!allArgs["app"].empty()) {
app = allArgs["app"];
}
auto src = MediaSource::find(vhost, app, allArgs["stream_id"]);
auto process = src ? src->getRtpProcess() : nullptr; auto process = src ? src->getRtpProcess() : nullptr;
if (!process) { if (!process) {
val["exist"] = false; val["exist"] = false;
@ -1211,7 +1222,16 @@ void installWebApi() {
api_regist("/index/api/openRtpServer",[](API_ARGS_MAP){ api_regist("/index/api/openRtpServer",[](API_ARGS_MAP){
CHECK_SECRET(); CHECK_SECRET();
CHECK_ARGS("port", "stream_id"); CHECK_ARGS("port", "stream_id");
std::string vhost = DEFAULT_VHOST;
if (!allArgs["vhost"].empty()) {
vhost = allArgs["vhost"];
}
std::string app = kRtpAppName;
if (!allArgs["app"].empty()) {
app = allArgs["app"];
}
auto stream_id = allArgs["stream_id"]; auto stream_id = allArgs["stream_id"];
auto tuple = MediaTuple { vhost, app, stream_id, "" };
auto tcp_mode = allArgs["tcp_mode"].as<int>(); auto tcp_mode = allArgs["tcp_mode"].as<int>();
if (allArgs["enable_tcp"].as<int>() && !tcp_mode) { if (allArgs["enable_tcp"].as<int>() && !tcp_mode) {
//兼容老版本请求新版本去除enable_tcp参数并新增tcp_mode参数 //兼容老版本请求新版本去除enable_tcp参数并新增tcp_mode参数
@ -1226,40 +1246,50 @@ void installWebApi() {
if (!allArgs["local_ip"].empty()) { if (!allArgs["local_ip"].empty()) {
local_ip = allArgs["local_ip"]; local_ip = allArgs["local_ip"];
} }
auto port = openRtpServer(allArgs["port"], stream_id, tcp_mode, local_ip, allArgs["re_use_port"].as<bool>(), auto port = openRtpServer(allArgs["port"], tuple, tcp_mode, local_ip, allArgs["re_use_port"].as<bool>(),
allArgs["ssrc"].as<uint32_t>(), only_track); allArgs["ssrc"].as<uint32_t>(), only_track);
if (port == 0) { if (port == 0) {
throw InvalidArgsException("该stream_id已存在"); throw InvalidArgsException("This stream already exists");
} }
//回复json //回复json
val["port"] = port; val["port"] = port;
}); });
api_regist("/index/api/openRtpServerMultiplex", [](API_ARGS_MAP) { api_regist("/index/api/openRtpServerMultiplex", [](API_ARGS_MAP) {
CHECK_SECRET(); CHECK_SECRET();
CHECK_ARGS("port", "stream_id"); CHECK_ARGS("port", "stream_id");
auto stream_id = allArgs["stream_id"]; std::string vhost = DEFAULT_VHOST;
auto tcp_mode = allArgs["tcp_mode"].as<int>(); if (!allArgs["vhost"].empty()) {
if (allArgs["enable_tcp"].as<int>() && !tcp_mode) { vhost = allArgs["vhost"];
// 兼容老版本请求新版本去除enable_tcp参数并新增tcp_mode参数 }
tcp_mode = 1; std::string app = kRtpAppName;
} if (!allArgs["app"].empty()) {
auto only_track = allArgs["only_track"].as<int>(); app = allArgs["app"];
if (allArgs["only_audio"].as<bool>()) { }
// 兼容老版本请求新版本去除only_audio参数并新增only_track参数 auto stream_id = allArgs["stream_id"];
only_track = 1; auto tuple = MediaTuple { vhost, app, stream_id, "" };
} auto tcp_mode = allArgs["tcp_mode"].as<int>();
std::string local_ip = "::"; if (allArgs["enable_tcp"].as<int>() && !tcp_mode) {
if (!allArgs["local_ip"].empty()) { // 兼容老版本请求新版本去除enable_tcp参数并新增tcp_mode参数
local_ip = allArgs["local_ip"]; tcp_mode = 1;
} }
auto port = openRtpServer(allArgs["port"], stream_id, tcp_mode, local_ip, true, 0, only_track,true); auto only_track = allArgs["only_track"].as<int>();
if (port == 0) { if (allArgs["only_audio"].as<bool>()) {
throw InvalidArgsException("该stream_id已存在"); // 兼容老版本请求新版本去除only_audio参数并新增only_track参数
} only_track = 1;
// 回复json }
val["port"] = port; std::string local_ip = "::";
}); if (!allArgs["local_ip"].empty()) {
local_ip = allArgs["local_ip"];
}
auto port = openRtpServer(allArgs["port"], tuple, tcp_mode, local_ip, true, 0, only_track, true);
if (port == 0) {
throw InvalidArgsException("This stream already exists");
}
// 回复json
val["port"] = port;
});
api_regist("/index/api/connectRtpServer", [](API_ARGS_MAP_ASYNC) { api_regist("/index/api/connectRtpServer", [](API_ARGS_MAP_ASYNC) {
CHECK_SECRET(); CHECK_SECRET();
@ -1272,9 +1302,19 @@ void installWebApi() {
invoker(200, headerOut, val.toStyledString()); invoker(200, headerOut, val.toStyledString());
}; };
auto server = s_rtp_server.find(allArgs["stream_id"]); std::string vhost = DEFAULT_VHOST;
if (!allArgs["vhost"].empty()) {
vhost = allArgs["vhost"];
}
std::string app = kRtpAppName;
if (!allArgs["app"].empty()) {
app = allArgs["app"];
}
auto stream_id = allArgs["stream_id"];
auto tuple = MediaTuple { vhost, app, stream_id, "" };
auto server = s_rtp_server.find(tuple.shortUrl());
if (!server) { if (!server) {
cb(SockException(Err_other, "未找到rtp服务")); cb(SockException(Err_other, "can not find the stream"));
return; return;
} }
server->connectToServer(allArgs["dst_url"], allArgs["dst_port"], cb); server->connectToServer(allArgs["dst_url"], allArgs["dst_port"], cb);
@ -1284,7 +1324,17 @@ void installWebApi() {
CHECK_SECRET(); CHECK_SECRET();
CHECK_ARGS("stream_id"); CHECK_ARGS("stream_id");
if(s_rtp_server.erase(allArgs["stream_id"]) == 0){ std::string vhost = DEFAULT_VHOST;
if (!allArgs["vhost"].empty()) {
vhost = allArgs["vhost"];
}
std::string app = kRtpAppName;
if (!allArgs["app"].empty()) {
app = allArgs["app"];
}
auto stream_id = allArgs["stream_id"];
auto tuple = MediaTuple { vhost, app, stream_id, "" };
if (s_rtp_server.erase(tuple.shortUrl()) == 0) {
val["hit"] = 0; val["hit"] = 0;
return; return;
} }
@ -1295,7 +1345,17 @@ void installWebApi() {
CHECK_SECRET(); CHECK_SECRET();
CHECK_ARGS("stream_id", "ssrc"); CHECK_ARGS("stream_id", "ssrc");
auto server = s_rtp_server.find(allArgs["stream_id"]); std::string vhost = DEFAULT_VHOST;
if (!allArgs["vhost"].empty()) {
vhost = allArgs["vhost"];
}
std::string app = kRtpAppName;
if (!allArgs["app"].empty()) {
app = allArgs["app"];
}
auto stream_id = allArgs["stream_id"];
auto tuple = MediaTuple { vhost, app, stream_id, "" };
auto server = s_rtp_server.find(tuple.shortUrl());
if (!server) { if (!server) {
throw ApiRetException("RtpServer not found by stream_id", API::NotFound); throw ApiRetException("RtpServer not found by stream_id", API::NotFound);
} }
@ -1307,8 +1367,11 @@ void installWebApi() {
std::lock_guard<std::recursive_mutex> lck(s_rtp_server._mtx); std::lock_guard<std::recursive_mutex> lck(s_rtp_server._mtx);
for (auto &pr : s_rtp_server._map) { for (auto &pr : s_rtp_server._map) {
auto vec = split(pr.first, "/");
Value obj; Value obj;
obj["stream_id"] = pr.first; obj["vhost"] = vec[0];
obj["app"] = vec[1];
obj["stream_id"] = vec[2];
obj["port"] = pr.second->getPort(); obj["port"] = pr.second->getPort();
val["data"].append(obj); val["data"].append(obj);
} }
@ -1437,8 +1500,16 @@ void installWebApi() {
api_regist("/index/api/pauseRtpCheck", [](API_ARGS_MAP) { api_regist("/index/api/pauseRtpCheck", [](API_ARGS_MAP) {
CHECK_SECRET(); CHECK_SECRET();
CHECK_ARGS("stream_id"); CHECK_ARGS("stream_id");
std::string vhost = DEFAULT_VHOST;
if (!allArgs["vhost"].empty()) {
vhost = allArgs["vhost"];
}
std::string app = kRtpAppName;
if (!allArgs["app"].empty()) {
app = allArgs["app"];
}
//只是暂停流的检查流媒体服务器做为流负载服务收流就转发RTSP/RTMP有自己暂停协议 //只是暂停流的检查流媒体服务器做为流负载服务收流就转发RTSP/RTMP有自己暂停协议
auto src = MediaSource::find(DEFAULT_VHOST, kRtpAppName, allArgs["stream_id"]); auto src = MediaSource::find(vhost, app, allArgs["stream_id"]);
auto process = src ? src->getRtpProcess() : nullptr; auto process = src ? src->getRtpProcess() : nullptr;
if (process) { if (process) {
process->setStopCheckRtp(true); process->setStopCheckRtp(true);
@ -1450,7 +1521,15 @@ void installWebApi() {
api_regist("/index/api/resumeRtpCheck", [](API_ARGS_MAP) { api_regist("/index/api/resumeRtpCheck", [](API_ARGS_MAP) {
CHECK_SECRET(); CHECK_SECRET();
CHECK_ARGS("stream_id"); CHECK_ARGS("stream_id");
auto src = MediaSource::find(DEFAULT_VHOST, kRtpAppName, allArgs["stream_id"]); std::string vhost = DEFAULT_VHOST;
if (!allArgs["vhost"].empty()) {
vhost = allArgs["vhost"];
}
std::string app = kRtpAppName;
if (!allArgs["app"].empty()) {
app = allArgs["app"];
}
auto src = MediaSource::find(vhost, app, allArgs["stream_id"]);
auto process = src ? src->getRtpProcess() : nullptr; auto process = src ? src->getRtpProcess() : nullptr;
if (process) { if (process) {
process->setStopCheckRtp(false); process->setStopCheckRtp(false);
@ -1871,6 +1950,7 @@ void installWebApi() {
}); });
#endif #endif
#if ENABLE_MP4
api_regist("/index/api/loadMP4File", [](API_ARGS_MAP) { api_regist("/index/api/loadMP4File", [](API_ARGS_MAP) {
CHECK_SECRET(); CHECK_SECRET();
CHECK_ARGS("vhost", "app", "stream", "file_path"); CHECK_ARGS("vhost", "app", "stream", "file_path");
@ -1884,11 +1964,12 @@ void installWebApi() {
option.load(allArgs); option.load(allArgs);
// 强制无人观看时自动关闭 // 强制无人观看时自动关闭
option.auto_close = true; option.auto_close = true;
auto tuple = MediaTuple{allArgs["vhost"], allArgs["app"], allArgs["stream"], ""};
auto reader = std::make_shared<MP4Reader>(allArgs["vhost"], allArgs["app"], allArgs["stream"], allArgs["file_path"], option); auto reader = std::make_shared<MP4Reader>(tuple, allArgs["file_path"], option);
// sample_ms设置为0从配置文件加载file_repeat可以指定如果配置文件也指定循环解复用那么强制开启 // sample_ms设置为0从配置文件加载file_repeat可以指定如果配置文件也指定循环解复用那么强制开启
reader->startReadMP4(0, true, allArgs["file_repeat"]); reader->startReadMP4(0, true, allArgs["file_repeat"]);
}); });
#endif
GET_CONFIG_FUNC(std::set<std::string>, download_roots, API::kDownloadRoot, [](const string &str) -> std::set<std::string> { GET_CONFIG_FUNC(std::set<std::string>, download_roots, API::kDownloadRoot, [](const string &str) -> std::set<std::string> {
std::set<std::string> ret; std::set<std::string> ret;
@ -1950,9 +2031,29 @@ void installWebApi() {
api_regist("/index/api/stack/start", [](API_ARGS_JSON_ASYNC) { api_regist("/index/api/stack/start", [](API_ARGS_JSON_ASYNC) {
CHECK_SECRET(); CHECK_SECRET();
auto ret = VideoStackManager::Instance().startVideoStack(allArgs.args); int ret = 0;
val["code"] = ret; try {
val["msg"] = ret ? "failed" : "success"; ret = VideoStackManager::Instance().startVideoStack(allArgs.args);
val["code"] = ret;
val["msg"] = ret ? "failed" : "success";
} catch (const std::exception &e) {
val["code"] = -1;
val["msg"] = e.what();
}
invoker(200, headerOut, val.toStyledString());
});
api_regist("/index/api/stack/reset", [](API_ARGS_JSON_ASYNC) {
CHECK_SECRET();
int ret = 0;
try {
auto ret = VideoStackManager::Instance().resetVideoStack(allArgs.args);
val["code"] = ret;
val["msg"] = ret ? "failed" : "success";
} catch (const std::exception &e) {
val["code"] = -1;
val["msg"] = e.what();
}
invoker(200, headerOut, val.toStyledString()); invoker(200, headerOut, val.toStyledString());
}); });
@ -1974,6 +2075,9 @@ void unInstallWebApi(){
#if defined(ENABLE_RTPPROXY) #if defined(ENABLE_RTPPROXY)
s_rtp_server.clear(); s_rtp_server.clear();
#endif #endif
#if defined(ENABLE_VIDEOSTACK) && defined(ENABLE_FFMPEG) && defined(ENABLE_X264)
VideoStackManager::Instance().clear();
#endif
NoticeCenter::Instance().delListener(&web_api_tag); NoticeCenter::Instance().delListener(&web_api_tag);
} }

View File

@ -202,12 +202,12 @@ void installWebApi();
void unInstallWebApi(); void unInstallWebApi();
#if defined(ENABLE_RTPPROXY) #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, int only_track, bool multiplex=false); uint16_t openRtpServer(uint16_t local_port, const mediakit::MediaTuple &tuple, int tcp_mode, const std::string &local_ip, bool re_use_port, uint32_t ssrc, int only_track, bool multiplex=false);
#endif #endif
Json::Value makeMediaSourceJson(mediakit::MediaSource &media); Json::Value makeMediaSourceJson(mediakit::MediaSource &media);
void getStatisticJson(const std::function<void(Json::Value &val)> &cb); 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, void addStreamProxy(const mediakit::MediaTuple &tuple, const std::string &url, int retry_count,
const mediakit::ProtocolOption &option, int rtp_type, float timeout_sec, const toolkit::mINI &args, const mediakit::ProtocolOption &option, int rtp_type, float timeout_sec, const toolkit::mINI &args,
const std::function<void(const toolkit::SockException &ex, const std::string &key)> &cb); const std::function<void(const toolkit::SockException &ex, const std::string &key)> &cb);
#endif //ZLMEDIAKIT_WEBAPI_H #endif //ZLMEDIAKIT_WEBAPI_H

View File

@ -301,7 +301,7 @@ static void pullStreamFromOrigin(const vector<string> &urls, size_t index, size_
option.enable_hls = option.enable_hls || (args.schema == HLS_SCHEMA); option.enable_hls = option.enable_hls || (args.schema == HLS_SCHEMA);
option.enable_mp4 = false; option.enable_mp4 = false;
addStreamProxy(args.vhost, args.app, args.stream, url, retry_count, option, Rtsp::RTP_TCP, timeout_sec, mINI{}, [=](const SockException &ex, const string &key) mutable { addStreamProxy(args, url, retry_count, option, Rtsp::RTP_TCP, timeout_sec, mINI{}, [=](const SockException &ex, const string &key) mutable {
if (!ex) { if (!ex) {
return; return;
} }
@ -682,7 +682,9 @@ void installWebHook() {
ArgsType body; ArgsType body;
body["local_port"] = local_port; body["local_port"] = local_port;
body["stream_id"] = stream_id; body[VHOST_KEY] = tuple.vhost;
body["app"] = tuple.app;
body["stream_id"] = tuple.stream;
body["tcp_mode"] = tcp_mode; body["tcp_mode"] = tcp_mode;
body["re_use_port"] = re_use_port; body["re_use_port"] = re_use_port;
body["ssrc"] = ssrc; body["ssrc"] = ssrc;

View File

@ -55,59 +55,13 @@ string getOriginTypeString(MediaOriginType type){
////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////
ProtocolOption::ProtocolOption() { ProtocolOption::ProtocolOption() {
GET_CONFIG(int, s_modify_stamp, Protocol::kModifyStamp); mINI ini;
GET_CONFIG(bool, s_enabel_audio, Protocol::kEnableAudio); auto &config = mINI::Instance();
GET_CONFIG(bool, s_add_mute_audio, Protocol::kAddMuteAudio); static auto sz = strlen(Protocol::kFieldName);
GET_CONFIG(bool, s_auto_close, Protocol::kAutoClose); for (auto it = config.lower_bound(Protocol::kFieldName); it != config.end() && start_with(it->first, Protocol::kFieldName); ++it) {
GET_CONFIG(uint32_t, s_continue_push_ms, Protocol::kContinuePushMS); ini.emplace(it->first.substr(sz), it->second);
GET_CONFIG(uint32_t, s_paced_sender_ms, Protocol::kPacedSenderMS); }
load(ini);
GET_CONFIG(bool, s_enable_hls, Protocol::kEnableHls);
GET_CONFIG(bool, s_enable_hls_fmp4, Protocol::kEnableHlsFmp4);
GET_CONFIG(bool, s_enable_mp4, Protocol::kEnableMP4);
GET_CONFIG(bool, s_enable_rtsp, Protocol::kEnableRtsp);
GET_CONFIG(bool, s_enable_rtmp, Protocol::kEnableRtmp);
GET_CONFIG(bool, s_enable_ts, Protocol::kEnableTS);
GET_CONFIG(bool, s_enable_fmp4, Protocol::kEnableFMP4);
GET_CONFIG(bool, s_hls_demand, Protocol::kHlsDemand);
GET_CONFIG(bool, s_rtsp_demand, Protocol::kRtspDemand);
GET_CONFIG(bool, s_rtmp_demand, Protocol::kRtmpDemand);
GET_CONFIG(bool, s_ts_demand, Protocol::kTSDemand);
GET_CONFIG(bool, s_fmp4_demand, Protocol::kFMP4Demand);
GET_CONFIG(bool, s_mp4_as_player, Protocol::kMP4AsPlayer);
GET_CONFIG(uint32_t, s_mp4_max_second, Protocol::kMP4MaxSecond);
GET_CONFIG(string, s_mp4_save_path, Protocol::kMP4SavePath);
GET_CONFIG(string, s_hls_save_path, Protocol::kHlsSavePath);
modify_stamp = s_modify_stamp;
enable_audio = s_enabel_audio;
add_mute_audio = s_add_mute_audio;
auto_close = s_auto_close;
continue_push_ms = s_continue_push_ms;
paced_sender_ms = s_paced_sender_ms;
enable_hls = s_enable_hls;
enable_hls_fmp4 = s_enable_hls_fmp4;
enable_mp4 = s_enable_mp4;
enable_rtsp = s_enable_rtsp;
enable_rtmp = s_enable_rtmp;
enable_ts = s_enable_ts;
enable_fmp4 = s_enable_fmp4;
hls_demand = s_hls_demand;
rtsp_demand = s_rtsp_demand;
rtmp_demand = s_rtmp_demand;
ts_demand = s_ts_demand;
fmp4_demand = s_fmp4_demand;
mp4_as_player = s_mp4_as_player;
mp4_max_second = s_mp4_max_second;
mp4_save_path = s_mp4_save_path;
hls_save_path = s_hls_save_path;
} }
////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -641,7 +595,8 @@ MediaSource::Ptr MediaSource::createFromMP4(const string &schema, const string &
} }
#ifdef ENABLE_MP4 #ifdef ENABLE_MP4
try { try {
auto reader = std::make_shared<MP4Reader>(vhost, app, stream, file_path); MediaTuple tuple = {vhost, app, stream, ""};
auto reader = std::make_shared<MP4Reader>(tuple, file_path);
reader->startReadMP4(); reader->startReadMP4();
return MediaSource::find(schema, vhost, app, stream); return MediaSource::find(schema, vhost, app, stream);
} catch (std::exception &ex) { } catch (std::exception &ex) {
@ -711,7 +666,7 @@ string MediaSourceEvent::getOriginUrl(MediaSource &sender) const {
MediaOriginType MediaSourceEventInterceptor::getOriginType(MediaSource &sender) const { MediaOriginType MediaSourceEventInterceptor::getOriginType(MediaSource &sender) const {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (!listener) { if (!listener) {
return MediaOriginType::unknown; return MediaSourceEvent::getOriginType(sender);
} }
return listener->getOriginType(sender); return listener->getOriginType(sender);
} }
@ -731,7 +686,7 @@ string MediaSourceEventInterceptor::getOriginUrl(MediaSource &sender) const {
std::shared_ptr<SockInfo> MediaSourceEventInterceptor::getOriginSock(MediaSource &sender) const { std::shared_ptr<SockInfo> MediaSourceEventInterceptor::getOriginSock(MediaSource &sender) const {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (!listener) { if (!listener) {
return nullptr; return MediaSourceEvent::getOriginSock(sender);
} }
return listener->getOriginSock(sender); return listener->getOriginSock(sender);
} }
@ -739,7 +694,7 @@ std::shared_ptr<SockInfo> MediaSourceEventInterceptor::getOriginSock(MediaSource
bool MediaSourceEventInterceptor::seekTo(MediaSource &sender, uint32_t stamp) { bool MediaSourceEventInterceptor::seekTo(MediaSource &sender, uint32_t stamp) {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (!listener) { if (!listener) {
return false; return MediaSourceEvent::seekTo(sender, stamp);
} }
return listener->seekTo(sender, stamp); return listener->seekTo(sender, stamp);
} }
@ -747,7 +702,7 @@ bool MediaSourceEventInterceptor::seekTo(MediaSource &sender, uint32_t stamp) {
bool MediaSourceEventInterceptor::pause(MediaSource &sender, bool pause) { bool MediaSourceEventInterceptor::pause(MediaSource &sender, bool pause) {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (!listener) { if (!listener) {
return false; return MediaSourceEvent::pause(sender, pause);
} }
return listener->pause(sender, pause); return listener->pause(sender, pause);
} }
@ -755,7 +710,7 @@ bool MediaSourceEventInterceptor::pause(MediaSource &sender, bool pause) {
bool MediaSourceEventInterceptor::speed(MediaSource &sender, float speed) { bool MediaSourceEventInterceptor::speed(MediaSource &sender, float speed) {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (!listener) { if (!listener) {
return false; return MediaSourceEvent::speed(sender, speed);
} }
return listener->speed(sender, speed); return listener->speed(sender, speed);
} }
@ -763,7 +718,7 @@ bool MediaSourceEventInterceptor::speed(MediaSource &sender, float speed) {
bool MediaSourceEventInterceptor::close(MediaSource &sender) { bool MediaSourceEventInterceptor::close(MediaSource &sender) {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (!listener) { if (!listener) {
return false; return MediaSourceEvent::close(sender);
} }
return listener->close(sender); return listener->close(sender);
} }
@ -771,7 +726,7 @@ bool MediaSourceEventInterceptor::close(MediaSource &sender) {
int MediaSourceEventInterceptor::totalReaderCount(MediaSource &sender) { int MediaSourceEventInterceptor::totalReaderCount(MediaSource &sender) {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (!listener) { if (!listener) {
return sender.readerCount(); return MediaSourceEvent::totalReaderCount(sender);
} }
return listener->totalReaderCount(sender); return listener->totalReaderCount(sender);
} }
@ -779,49 +734,55 @@ int MediaSourceEventInterceptor::totalReaderCount(MediaSource &sender) {
void MediaSourceEventInterceptor::onReaderChanged(MediaSource &sender, int size) { void MediaSourceEventInterceptor::onReaderChanged(MediaSource &sender, int size) {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (!listener) { if (!listener) {
MediaSourceEvent::onReaderChanged(sender, size); return MediaSourceEvent::onReaderChanged(sender, size);
} else {
listener->onReaderChanged(sender, size);
} }
listener->onReaderChanged(sender, size);
} }
void MediaSourceEventInterceptor::onRegist(MediaSource &sender, bool regist) { void MediaSourceEventInterceptor::onRegist(MediaSource &sender, bool regist) {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (listener) { if (!listener) {
listener->onRegist(sender, regist); return MediaSourceEvent::onRegist(sender, regist);
} }
listener->onRegist(sender, regist);
} }
float MediaSourceEventInterceptor::getLossRate(MediaSource &sender, TrackType type){ float MediaSourceEventInterceptor::getLossRate(MediaSource &sender, TrackType type) {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (listener) { if (!listener) {
return listener->getLossRate(sender, type); return MediaSourceEvent::getLossRate(sender, type);
} }
return -1; //异常返回-1 return listener->getLossRate(sender, type);
} }
toolkit::EventPoller::Ptr MediaSourceEventInterceptor::getOwnerPoller(MediaSource &sender) { toolkit::EventPoller::Ptr MediaSourceEventInterceptor::getOwnerPoller(MediaSource &sender) {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (listener) { if (!listener) {
return listener->getOwnerPoller(sender); return MediaSourceEvent::getOwnerPoller(sender);
} }
throw std::runtime_error(toolkit::demangle(typeid(*this).name()) + "::getOwnerPoller failed"); return listener->getOwnerPoller(sender);
} }
std::shared_ptr<MultiMediaSourceMuxer> MediaSourceEventInterceptor::getMuxer(MediaSource &sender) const { std::shared_ptr<MultiMediaSourceMuxer> MediaSourceEventInterceptor::getMuxer(MediaSource &sender) const {
auto listener = _listener.lock(); auto listener = _listener.lock();
return listener ? listener->getMuxer(sender) : nullptr; if (!listener) {
return MediaSourceEvent::getMuxer(sender);
}
return listener->getMuxer(sender);
} }
std::shared_ptr<RtpProcess> MediaSourceEventInterceptor::getRtpProcess(MediaSource &sender) const { std::shared_ptr<RtpProcess> MediaSourceEventInterceptor::getRtpProcess(MediaSource &sender) const {
auto listener = _listener.lock(); auto listener = _listener.lock();
return listener ? listener->getRtpProcess(sender) : nullptr; if (!listener) {
return MediaSourceEvent::getRtpProcess(sender);
}
return listener->getRtpProcess(sender);
} }
bool MediaSourceEventInterceptor::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path, size_t max_second) { bool MediaSourceEventInterceptor::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path, size_t max_second) {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (!listener) { if (!listener) {
return false; return MediaSourceEvent::setupRecord(sender, type, start, custom_path, max_second);
} }
return listener->setupRecord(sender, type, start, custom_path, max_second); return listener->setupRecord(sender, type, start, custom_path, max_second);
} }
@ -829,7 +790,7 @@ bool MediaSourceEventInterceptor::setupRecord(MediaSource &sender, Recorder::typ
bool MediaSourceEventInterceptor::isRecording(MediaSource &sender, Recorder::type type) { bool MediaSourceEventInterceptor::isRecording(MediaSource &sender, Recorder::type type) {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (!listener) { if (!listener) {
return false; return MediaSourceEvent::isRecording(sender, type);
} }
return listener->isRecording(sender, type); return listener->isRecording(sender, type);
} }
@ -837,26 +798,25 @@ bool MediaSourceEventInterceptor::isRecording(MediaSource &sender, Recorder::typ
vector<Track::Ptr> MediaSourceEventInterceptor::getMediaTracks(MediaSource &sender, bool trackReady) const { vector<Track::Ptr> MediaSourceEventInterceptor::getMediaTracks(MediaSource &sender, bool trackReady) const {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (!listener) { if (!listener) {
return vector<Track::Ptr>(); return MediaSourceEvent::getMediaTracks(sender, trackReady);
} }
return listener->getMediaTracks(sender, trackReady); return listener->getMediaTracks(sender, trackReady);
} }
void MediaSourceEventInterceptor::startSendRtp(MediaSource &sender, const MediaSourceEvent::SendRtpArgs &args, const std::function<void(uint16_t, const toolkit::SockException &)> cb) { void MediaSourceEventInterceptor::startSendRtp(MediaSource &sender, const MediaSourceEvent::SendRtpArgs &args, const std::function<void(uint16_t, const toolkit::SockException &)> cb) {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (listener) { if (!listener) {
listener->startSendRtp(sender, args, cb); return MediaSourceEvent::startSendRtp(sender, args, cb);
} else {
MediaSourceEvent::startSendRtp(sender, args, cb);
} }
listener->startSendRtp(sender, args, cb);
} }
bool MediaSourceEventInterceptor::stopSendRtp(MediaSource &sender, const string &ssrc){ bool MediaSourceEventInterceptor::stopSendRtp(MediaSource &sender, const string &ssrc) {
auto listener = _listener.lock(); auto listener = _listener.lock();
if (listener) { if (!listener) {
return listener->stopSendRtp(sender, ssrc); return MediaSourceEvent::stopSendRtp(sender, ssrc);
} }
return false; return listener->stopSendRtp(sender, ssrc);
} }
void MediaSourceEventInterceptor::setDelegate(const std::weak_ptr<MediaSourceEvent> &listener) { void MediaSourceEventInterceptor::setDelegate(const std::weak_ptr<MediaSourceEvent> &listener) {
@ -866,7 +826,7 @@ void MediaSourceEventInterceptor::setDelegate(const std::weak_ptr<MediaSourceEve
_listener = listener; _listener = listener;
} }
std::shared_ptr<MediaSourceEvent> MediaSourceEventInterceptor::getDelegate() const{ std::shared_ptr<MediaSourceEvent> MediaSourceEventInterceptor::getDelegate() const {
return _listener.lock(); return _listener.lock();
} }

View File

@ -15,6 +15,7 @@
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <functional> #include <functional>
#include "Util/mini.h"
#include "Network/Socket.h" #include "Network/Socket.h"
#include "Extension/Track.h" #include "Extension/Track.h"
#include "Record/Recorder.h" #include "Record/Recorder.h"
@ -148,6 +149,14 @@ static void getArgsValue(const MAP &allArgs, const KEY &key, TYPE &value) {
} }
} }
template <typename KEY, typename TYPE>
static void getArgsValue(const toolkit::mINI &allArgs, const KEY &key, TYPE &value) {
auto it = allArgs.find(key);
if (it != allArgs.end()) {
value = (TYPE)it->second;
}
}
class ProtocolOption { class ProtocolOption {
public: public:
ProtocolOption(); ProtocolOption();

View File

@ -104,33 +104,32 @@ static onceToken token([]() {
} // namespace General } // namespace General
namespace Protocol { namespace Protocol {
#define PROTOCOL_FIELD "protocol." const string kModifyStamp = string(kFieldName) + "modify_stamp";
const string kModifyStamp = PROTOCOL_FIELD "modify_stamp"; const string kEnableAudio = string(kFieldName) + "enable_audio";
const string kEnableAudio = PROTOCOL_FIELD "enable_audio"; const string kAddMuteAudio = string(kFieldName) + "add_mute_audio";
const string kAddMuteAudio = PROTOCOL_FIELD "add_mute_audio"; const string kAutoClose = string(kFieldName) + "auto_close";
const string kAutoClose = PROTOCOL_FIELD "auto_close"; const string kContinuePushMS = string(kFieldName) + "continue_push_ms";
const string kContinuePushMS = PROTOCOL_FIELD "continue_push_ms"; const string kPacedSenderMS = string(kFieldName) + "paced_sender_ms";
const string kPacedSenderMS = PROTOCOL_FIELD "paced_sender_ms";
const string kEnableHls = PROTOCOL_FIELD "enable_hls"; const string kEnableHls = string(kFieldName) + "enable_hls";
const string kEnableHlsFmp4 = PROTOCOL_FIELD "enable_hls_fmp4"; const string kEnableHlsFmp4 = string(kFieldName) + "enable_hls_fmp4";
const string kEnableMP4 = PROTOCOL_FIELD "enable_mp4"; const string kEnableMP4 = string(kFieldName) + "enable_mp4";
const string kEnableRtsp = PROTOCOL_FIELD "enable_rtsp"; const string kEnableRtsp = string(kFieldName) + "enable_rtsp";
const string kEnableRtmp = PROTOCOL_FIELD "enable_rtmp"; const string kEnableRtmp = string(kFieldName) + "enable_rtmp";
const string kEnableTS = PROTOCOL_FIELD "enable_ts"; const string kEnableTS = string(kFieldName) + "enable_ts";
const string kEnableFMP4 = PROTOCOL_FIELD "enable_fmp4"; const string kEnableFMP4 = string(kFieldName) + "enable_fmp4";
const string kMP4AsPlayer = PROTOCOL_FIELD "mp4_as_player"; const string kMP4AsPlayer = string(kFieldName) + "mp4_as_player";
const string kMP4MaxSecond = PROTOCOL_FIELD "mp4_max_second"; const string kMP4MaxSecond = string(kFieldName) + "mp4_max_second";
const string kMP4SavePath = PROTOCOL_FIELD "mp4_save_path"; const string kMP4SavePath = string(kFieldName) + "mp4_save_path";
const string kHlsSavePath = PROTOCOL_FIELD "hls_save_path"; const string kHlsSavePath = string(kFieldName) + "hls_save_path";
const string kHlsDemand = PROTOCOL_FIELD "hls_demand"; const string kHlsDemand = string(kFieldName) + "hls_demand";
const string kRtspDemand = PROTOCOL_FIELD "rtsp_demand"; const string kRtspDemand = string(kFieldName) + "rtsp_demand";
const string kRtmpDemand = PROTOCOL_FIELD "rtmp_demand"; const string kRtmpDemand = string(kFieldName) + "rtmp_demand";
const string kTSDemand = PROTOCOL_FIELD "ts_demand"; const string kTSDemand = string(kFieldName) + "ts_demand";
const string kFMP4Demand = PROTOCOL_FIELD "fmp4_demand"; const string kFMP4Demand = string(kFieldName) + "fmp4_demand";
static onceToken token([]() { static onceToken token([]() {
mINI::Instance()[kModifyStamp] = (int)ProtocolOption::kModifyStampRelative; mINI::Instance()[kModifyStamp] = (int)ProtocolOption::kModifyStampRelative;
@ -375,6 +374,7 @@ const string kBenchmarkMode = "benchmark_mode";
const string kWaitTrackReady = "wait_track_ready"; const string kWaitTrackReady = "wait_track_ready";
const string kPlayTrack = "play_track"; const string kPlayTrack = "play_track";
const string kProxyUrl = "proxy_url"; const string kProxyUrl = "proxy_url";
const string kRtspSpeed = "rtsp_speed";
} // namespace Client } // namespace Client
} // namespace mediakit } // namespace mediakit

View File

@ -107,7 +107,7 @@ extern const std::string kBroadcastReloadConfig;
// rtp server 超时 // rtp server 超时
extern const std::string kBroadcastRtpServerTimeout; extern const std::string kBroadcastRtpServerTimeout;
#define BroadcastRtpServerTimeoutArgs 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 MediaTuple &tuple, int &tcp_mode, bool &re_use_port, uint32_t &ssrc
// rtc transport sctp 连接状态 // rtc transport sctp 连接状态
extern const std::string kBroadcastRtcSctpConnecting; extern const std::string kBroadcastRtcSctpConnecting;
@ -205,6 +205,7 @@ extern const std::string kBroadcastPlayerCountChanged;
} // namespace General } // namespace General
namespace Protocol { namespace Protocol {
static constexpr char kFieldName[] = "protocol.";
//时间戳修复这一路流标志位 //时间戳修复这一路流标志位
extern const std::string kModifyStamp; extern const std::string kModifyStamp;
//转协议是否开启音频 //转协议是否开启音频
@ -447,6 +448,8 @@ extern const std::string kWaitTrackReady;
extern const std::string kPlayTrack; extern const std::string kPlayTrack;
//设置代理url目前只支持http协议 //设置代理url目前只支持http协议
extern const std::string kProxyUrl; extern const std::string kProxyUrl;
//设置开始rtsp倍速播放
extern const std::string kRtspSpeed;
} // namespace Client } // namespace Client
} // namespace mediakit } // namespace mediakit

View File

@ -109,6 +109,11 @@ public:
* fps * fps
*/ */
virtual float getVideoFps() const { return 0; } virtual float getVideoFps() const { return 0; }
/**
* sps/pps
*/
virtual std::vector<Frame::Ptr> getConfigFrames() const { return std::vector<Frame::Ptr>{}; }
}; };
class VideoTrackImp : public VideoTrack { class VideoTrackImp : public VideoTrack {

View File

@ -41,7 +41,13 @@ public:
FMP4MediaSource(const MediaTuple& tuple, FMP4MediaSource(const MediaTuple& tuple,
int ring_size = FMP4_GOP_SIZE) : MediaSource(FMP4_SCHEMA, tuple), _ring_size(ring_size) {} int ring_size = FMP4_GOP_SIZE) : MediaSource(FMP4_SCHEMA, tuple), _ring_size(ring_size) {}
~FMP4MediaSource() override { flush(); } ~FMP4MediaSource() override {
try {
flush();
} catch (std::exception &ex) {
WarnL << ex.what();
}
}
/** /**
* *

View File

@ -26,7 +26,13 @@ public:
_media_src = std::make_shared<FMP4MediaSource>(tuple); _media_src = std::make_shared<FMP4MediaSource>(tuple);
} }
~FMP4MediaSourceMuxer() override { MP4MuxerMemory::flush(); }; ~FMP4MediaSourceMuxer() override {
try {
MP4MuxerMemory::flush();
} catch (std::exception &ex) {
WarnL << ex.what();
}
}
void setListener(const std::weak_ptr<MediaSourceEvent> &listener){ void setListener(const std::weak_ptr<MediaSourceEvent> &listener){
setDelegate(listener); setDelegate(listener);

View File

@ -141,7 +141,9 @@ static std::shared_ptr<char> getSharedMmap(const string &file_path, int64_t &fil
return nullptr; return nullptr;
} }
file_size = ::GetFileSize(hfile, NULL); LARGE_INTEGER FileSize;
GetFileSizeEx(hfile, &FileSize); //GetFileSize函数的拓展可用于获取大于4G的文件大小
file_size = FileSize.QuadPart;
auto hmapping = ::CreateFileMapping(hfile, NULL, PAGE_READONLY, 0, 0, NULL); auto hmapping = ::CreateFileMapping(hfile, NULL, PAGE_READONLY, 0, 0, NULL);

View File

@ -24,7 +24,7 @@ namespace mediakit {
PlayerBase::Ptr PlayerBase::createPlayer(const EventPoller::Ptr &in_poller, const string &url_in) { PlayerBase::Ptr PlayerBase::createPlayer(const EventPoller::Ptr &in_poller, const string &url_in) {
auto poller = in_poller ? in_poller : EventPollerPool::Instance().getPoller(); auto poller = in_poller ? in_poller : EventPollerPool::Instance().getPoller();
std::weak_ptr<EventPoller> weak_poller = poller; std::weak_ptr<EventPoller> weak_poller = poller;
static auto release_func = [weak_poller](PlayerBase *ptr) { auto release_func = [weak_poller](PlayerBase *ptr) {
if (auto poller = weak_poller.lock()) { if (auto poller = weak_poller.lock()) {
poller->async([ptr]() { poller->async([ptr]() {
onceToken token(nullptr, [&]() { delete ptr; }); onceToken token(nullptr, [&]() { delete ptr; });

View File

@ -24,13 +24,9 @@ using namespace std;
namespace mediakit { namespace mediakit {
PlayerProxy::PlayerProxy( PlayerProxy::PlayerProxy(
const string &vhost, const string &app, const string &stream_id, const ProtocolOption &option, int retry_count, const MediaTuple &tuple, const ProtocolOption &option, int retry_count,
const EventPoller::Ptr &poller, int reconnect_delay_min, int reconnect_delay_max, int reconnect_delay_step) const EventPoller::Ptr &poller, int reconnect_delay_min, int reconnect_delay_max, int reconnect_delay_step)
: MediaPlayer(poller) : MediaPlayer(poller), _tuple(tuple), _option(option) {
, _option(option) {
_tuple.vhost = vhost;
_tuple.app = app;
_tuple.stream = stream_id;
_retry_count = retry_count; _retry_count = retry_count;
setOnClose(nullptr); setOnClose(nullptr);

View File

@ -66,9 +66,9 @@ public:
// 如果retry_count<0,则一直重试播放否则重试retry_count次数 // 如果retry_count<0,则一直重试播放否则重试retry_count次数
// 默认一直重试 // 默认一直重试
PlayerProxy( PlayerProxy(const MediaTuple &tuple, const ProtocolOption &option, int retry_count = -1,
const std::string &vhost, const std::string &app, const std::string &stream_id, const ProtocolOption &option, int retry_count = -1, const toolkit::EventPoller::Ptr &poller = nullptr,
const toolkit::EventPoller::Ptr &poller = nullptr, int reconnect_delay_min = 2, int reconnect_delay_max = 60, int reconnect_delay_step = 3); int reconnect_delay_min = 2, int reconnect_delay_max = 60, int reconnect_delay_step = 3);
~PlayerProxy() override; ~PlayerProxy() override;
@ -129,12 +129,12 @@ private:
void setTranslationInfo(); void setTranslationInfo();
private: private:
ProtocolOption _option;
int _retry_count; int _retry_count;
int _reconnect_delay_min; int _reconnect_delay_min;
int _reconnect_delay_max; int _reconnect_delay_max;
int _reconnect_delay_step; int _reconnect_delay_step;
MediaTuple _tuple; MediaTuple _tuple;
ProtocolOption _option;
std::string _pull_url; std::string _pull_url;
toolkit::Timer::Ptr _timer; toolkit::Timer::Ptr _timer;
std::function<void()> _on_disconnect; std::function<void()> _on_disconnect;

View File

@ -84,7 +84,13 @@ public:
using Ptr = std::shared_ptr<HlsRecorder>; using Ptr = std::shared_ptr<HlsRecorder>;
template <typename ...ARGS> template <typename ...ARGS>
HlsRecorder(ARGS && ...args) : HlsRecorderBase<MpegMuxer>(false, std::forward<ARGS>(args)...) {} HlsRecorder(ARGS && ...args) : HlsRecorderBase<MpegMuxer>(false, std::forward<ARGS>(args)...) {}
~HlsRecorder() override { this->flush(); } ~HlsRecorder() override {
try {
this->flush();
} catch (std::exception &ex) {
WarnL << ex.what();
}
}
private: private:
void onWrite(std::shared_ptr<toolkit::Buffer> buffer, uint64_t timestamp, bool key_pos) override { void onWrite(std::shared_ptr<toolkit::Buffer> buffer, uint64_t timestamp, bool key_pos) override {
@ -102,7 +108,13 @@ public:
using Ptr = std::shared_ptr<HlsFMP4Recorder>; using Ptr = std::shared_ptr<HlsFMP4Recorder>;
template <typename ...ARGS> template <typename ...ARGS>
HlsFMP4Recorder(ARGS && ...args) : HlsRecorderBase<MP4MuxerMemory>(true, std::forward<ARGS>(args)...) {} HlsFMP4Recorder(ARGS && ...args) : HlsRecorderBase<MP4MuxerMemory>(true, std::forward<ARGS>(args)...) {}
~HlsFMP4Recorder() override { this->flush(); } ~HlsFMP4Recorder() override {
try {
this->flush();
} catch (std::exception &ex) {
WarnL << ex.what();
}
}
void addTrackCompleted() override { void addTrackCompleted() override {
HlsRecorderBase<MP4MuxerMemory>::addTrackCompleted(); HlsRecorderBase<MP4MuxerMemory>::addTrackCompleted();

View File

@ -99,6 +99,20 @@ bool MP4MuxerInterface::inputFrame(const Frame::Ptr &frame) {
_started = true; _started = true;
} }
// fmp4封装超过一定I帧间隔强制刷新segment防止内存上涨
if (frame->getTrackType() == TrackVideo && _mov_writter->fmp4) {
if (frame->keyFrame()) {
_non_iframe_video_count = 0;
} else {
_non_iframe_video_count++;
}
if (_non_iframe_video_count > 200) {
saveSegment();
_non_iframe_video_count = 0;
}
}
// mp4文件时间戳需要从0开始 // mp4文件时间戳需要从0开始
auto &track = it->second; auto &track = it->second;
switch (frame->getCodecId()) { switch (frame->getCodecId()) {
@ -164,6 +178,7 @@ bool MP4MuxerInterface::addTrack(const Track::Ptr &track) {
} }
_tracks[track->getIndex()].track_id = track_id; _tracks[track->getIndex()].track_id = track_id;
_have_video = true; _have_video = true;
_non_iframe_video_count = 0;
} else if (track->getTrackType() == TrackAudio) { } else if (track->getTrackType() == TrackAudio) {
auto audio_track = dynamic_pointer_cast<AudioTrack>(track); auto audio_track = dynamic_pointer_cast<AudioTrack>(track);
CHECK(audio_track); CHECK(audio_track);

View File

@ -72,6 +72,7 @@ private:
bool _started = false; bool _started = false;
bool _have_video = false; bool _have_video = false;
MP4FileIO::Writer _mov_writter; MP4FileIO::Writer _mov_writter;
int _non_iframe_video_count; // 非I帧个数
class FrameMergerImp : public FrameMerger { class FrameMergerImp : public FrameMerger {
public: public:

View File

@ -20,7 +20,7 @@ using namespace toolkit;
namespace mediakit { namespace mediakit {
MP4Reader::MP4Reader(const std::string &vhost, const std::string &app, const std::string &stream_id, const string &file_path, MP4Reader::MP4Reader(const MediaTuple &tuple, const string &file_path,
toolkit::EventPoller::Ptr poller) { toolkit::EventPoller::Ptr poller) {
ProtocolOption option; ProtocolOption option;
// 读取mp4文件并流化时不重复生成mp4/hls文件 // 读取mp4文件并流化时不重复生成mp4/hls文件
@ -29,16 +29,15 @@ MP4Reader::MP4Reader(const std::string &vhost, const std::string &app, const std
option.enable_hls_fmp4 = false; option.enable_hls_fmp4 = false;
// mp4支持多track // mp4支持多track
option.max_track = 16; option.max_track = 16;
setup(vhost, app, stream_id, file_path, option, std::move(poller)); setup(tuple, file_path, option, std::move(poller));
} }
MP4Reader::MP4Reader(const std::string &vhost, const std::string &app, const std::string &stream_id, const string &file_path, const ProtocolOption &option, toolkit::EventPoller::Ptr poller) { MP4Reader::MP4Reader(const MediaTuple &tuple, const string &file_path, const ProtocolOption &option, toolkit::EventPoller::Ptr poller) {
setup(vhost, app, stream_id, file_path, option, std::move(poller)); setup(tuple, file_path, option, std::move(poller));
} }
void MP4Reader::setup(const std::string &vhost, const std::string &app, const std::string &stream_id, const std::string &file_path, const ProtocolOption &option, toolkit::EventPoller::Ptr poller) { void MP4Reader::setup(const MediaTuple &tuple, const std::string &file_path, const ProtocolOption &option, toolkit::EventPoller::Ptr poller) {
//读写文件建议放在后台线程 //读写文件建议放在后台线程
auto tuple = MediaTuple{vhost, app, stream_id, ""};
_poller = poller ? std::move(poller) : WorkThreadPool::Instance().getPoller(); _poller = poller ? std::move(poller) : WorkThreadPool::Instance().getPoller();
_file_path = file_path; _file_path = file_path;
if (_file_path.empty()) { if (_file_path.empty()) {

View File

@ -28,11 +28,9 @@ public:
* @param stream_id id,,mp4,MediaSource * @param stream_id id,,mp4,MediaSource
* @param file_path 使 * @param file_path 使
*/ */
MP4Reader(const std::string &vhost, const std::string &app, const std::string &stream_id, MP4Reader(const MediaTuple &tuple, const std::string &file_path = "", toolkit::EventPoller::Ptr poller = nullptr);
const std::string &file_path = "", toolkit::EventPoller::Ptr poller = nullptr);
MP4Reader(const std::string &vhost, const std::string &app, const std::string &stream_id, MP4Reader(const MediaTuple &tuple, const std::string &file_path, const ProtocolOption &option, toolkit::EventPoller::Ptr poller = nullptr);
const std::string &file_path, const ProtocolOption &option, toolkit::EventPoller::Ptr poller = nullptr);
/** /**
* MP4文件 * MP4文件
@ -69,7 +67,7 @@ private:
void setCurrentStamp(uint32_t stamp); void setCurrentStamp(uint32_t stamp);
bool seekTo(uint32_t stamp_seek); bool seekTo(uint32_t stamp_seek);
void setup(const std::string &vhost, const std::string &app, const std::string &stream_id, const std::string &file_path, const ProtocolOption &option, toolkit::EventPoller::Ptr poller); void setup(const MediaTuple &tuple, const std::string &file_path, const ProtocolOption &option, toolkit::EventPoller::Ptr poller);
private: private:
bool _file_repeat = false; bool _file_repeat = false;

View File

@ -48,7 +48,13 @@ public:
*/ */
RtmpMediaSource(const MediaTuple& tuple, int ring_size = RTMP_GOP_SIZE): MediaSource(RTMP_SCHEMA, tuple), _ring_size(ring_size) {} RtmpMediaSource(const MediaTuple& tuple, int ring_size = RTMP_GOP_SIZE): MediaSource(RTMP_SCHEMA, tuple), _ring_size(ring_size) {}
~RtmpMediaSource() override { flush(); } ~RtmpMediaSource() override {
try {
flush();
} catch (std::exception &ex) {
WarnL << ex.what();
}
}
/** /**
* *

View File

@ -85,14 +85,17 @@ DecoderImp::DecoderImp(const Decoder::Ptr &decoder, MediaSinkInterface *sink){
#if defined(ENABLE_RTPPROXY) || defined(ENABLE_HLS) #if defined(ENABLE_RTPPROXY) || defined(ENABLE_HLS)
void DecoderImp::onStream(int stream, int codecid, const void *extra, size_t bytes, int finish) { void DecoderImp::onStream(int stream, int codecid, const void *extra, size_t bytes, int finish) {
// G711传统只支持 8000/1/16的规格FFmpeg貌似做了扩展但是这里不管它了 if (_finished) {
auto track = Factory::getTrackByCodecId(getCodecByMpegId(codecid), 8000, 1, 16);
if (!track) {
return; return;
} }
onTrack(stream, std::move(track)); // G711传统只支持 8000/1/16的规格FFmpeg貌似做了扩展但是这里不管它了
auto track = Factory::getTrackByCodecId(getCodecByMpegId(codecid), 8000, 1, 16);
if (track) {
onTrack(stream, std::move(track));
}
// 防止未获取视频track提前complete导致忽略后续视频的问题用于兼容一些不太规范的ps流 // 防止未获取视频track提前complete导致忽略后续视频的问题用于兼容一些不太规范的ps流
if (finish && _have_video) { if (finish && _have_video) {
_finished = true;
_sink->addTrackCompleted(); _sink->addTrackCompleted();
InfoL << "Add track finished"; InfoL << "Add track finished";
} }

View File

@ -57,6 +57,7 @@ private:
void onStream(int stream, int codecid, const void *extra, size_t bytes, int finish); void onStream(int stream, int codecid, const void *extra, size_t bytes, int finish);
private: private:
bool _finished = false;
bool _have_video = false; bool _have_video = false;
Decoder::Ptr _decoder; Decoder::Ptr _decoder;
MediaSinkInterface *_sink; MediaSinkInterface *_sink;

View File

@ -41,7 +41,7 @@ private:
class RtpCachePS : public RtpCache, public PSEncoderImp { class RtpCachePS : public RtpCache, public PSEncoderImp {
public: public:
RtpCachePS(onFlushed cb, uint32_t ssrc, uint8_t payload_type = 96, bool ps_or_ts = true) : RtpCachePS(onFlushed cb, uint32_t ssrc, uint8_t payload_type = 96, bool ps_or_ts = true) :
RtpCache(std::move(cb)), PSEncoderImp(ssrc, ps_or_ts ? payload_type : Rtsp::PT_MP2T, ps_or_ts) {}; RtpCache(std::move(cb)), PSEncoderImp(ssrc, ps_or_ts ? payload_type : static_cast<int>(Rtsp::PT_MP2T), ps_or_ts) {};
void flush() override; void flush() override;

View File

@ -18,22 +18,20 @@ using namespace std;
using namespace toolkit; using namespace toolkit;
//在创建_muxer对象前(也就是推流鉴权成功前)需要先缓存frame这样可以防止丢包提高体验 //在创建_muxer对象前(也就是推流鉴权成功前)需要先缓存frame这样可以防止丢包提高体验
//但是同时需要控制缓冲长度,防止内存溢出。200帧数据大概有10秒数据应该足矣等待鉴权hook返回 //但是同时需要控制缓冲长度,防止内存溢出。最多缓存10秒数据应该足矣等待鉴权hook返回
static constexpr size_t kMaxCachedFrame = 200; static constexpr size_t kMaxCachedFrameMS = 10 * 1000;
namespace mediakit { namespace mediakit {
RtpProcess::Ptr RtpProcess::createProcess(std::string stream_id) { RtpProcess::Ptr RtpProcess::createProcess(const MediaTuple &tuple) {
RtpProcess::Ptr ret(new RtpProcess(std::move(stream_id))); RtpProcess::Ptr ret(new RtpProcess(tuple));
ret->createTimer(); ret->createTimer();
return ret; return ret;
} }
RtpProcess::RtpProcess(string stream_id) { RtpProcess::RtpProcess(const MediaTuple &tuple) {
_media_info.schema = kRtpAppName; _media_info.schema = "rtp";
_media_info.vhost = DEFAULT_VHOST; static_cast<MediaTuple &>(_media_info) = tuple;
_media_info.app = kRtpAppName;
_media_info.stream = std::move(stream_id);
GET_CONFIG(string, dump_dir, RtpProxy::kDumpDir); GET_CONFIG(string, dump_dir, RtpProxy::kDumpDir);
{ {
@ -112,6 +110,7 @@ bool RtpProcess::inputRtp(bool is_udp, const Socket::Ptr &sock, const char *data
_addr.reset(new sockaddr_storage(*((sockaddr_storage *)addr))); _addr.reset(new sockaddr_storage(*((sockaddr_storage *)addr)));
if (first) { if (first) {
emitOnPublish(); emitOnPublish();
_cache_ticker.resetTime();
} }
} }
@ -152,8 +151,8 @@ bool RtpProcess::inputFrame(const Frame::Ptr &frame) {
_last_frame_time.resetTime(); _last_frame_time.resetTime();
return _muxer->inputFrame(frame); return _muxer->inputFrame(frame);
} }
if (_cached_func.size() > kMaxCachedFrame) { if (_cache_ticker.elapsedTime() > kMaxCachedFrameMS) {
WarnL << "cached frame of track(" << frame->getCodecName() << ") is too much, now dropped, please check your on_publish hook url in config.ini file"; WarnL << "Cached frame of stream(" << _media_info.stream << ") is too much, your on_publish hook responded too late!";
return false; return false;
} }
auto frame_cached = Frame::getCacheAbleFrame(frame); auto frame_cached = Frame::getCacheAbleFrame(frame);

View File

@ -25,7 +25,7 @@ public:
using Ptr = std::shared_ptr<RtpProcess>; using Ptr = std::shared_ptr<RtpProcess>;
using onDetachCB = std::function<void(const toolkit::SockException &ex)>; using onDetachCB = std::function<void(const toolkit::SockException &ex)>;
static Ptr createProcess(std::string stream_id); static Ptr createProcess(const MediaTuple &tuple);
~RtpProcess(); ~RtpProcess();
enum OnlyTrack { kAll = 0, kOnlyAudio = 1, kOnlyVideo = 2 }; enum OnlyTrack { kAll = 0, kOnlyAudio = 1, kOnlyVideo = 2 };
@ -91,7 +91,7 @@ protected:
bool close(mediakit::MediaSource &sender) override; bool close(mediakit::MediaSource &sender) override;
private: private:
RtpProcess(std::string stream_id); RtpProcess(const MediaTuple &tuple);
void emitOnPublish(); void emitOnPublish();
void doCachedFunc(); void doCachedFunc();
@ -117,6 +117,7 @@ private:
toolkit::Timer::Ptr _timer; toolkit::Timer::Ptr _timer;
toolkit::Ticker _last_check_alive; toolkit::Ticker _last_check_alive;
std::recursive_mutex _func_mtx; std::recursive_mutex _func_mtx;
toolkit::Ticker _cache_ticker;
std::deque<std::function<void()> > _cached_func; std::deque<std::function<void()> > _cached_func;
}; };

View File

@ -30,18 +30,18 @@ class RtcpHelper: public std::enable_shared_from_this<RtcpHelper> {
public: public:
using Ptr = std::shared_ptr<RtcpHelper>; using Ptr = std::shared_ptr<RtcpHelper>;
RtcpHelper(Socket::Ptr rtcp_sock, std::string stream_id) { RtcpHelper(Socket::Ptr rtcp_sock, MediaTuple tuple) {
_rtcp_sock = std::move(rtcp_sock); _rtcp_sock = std::move(rtcp_sock);
_stream_id = std::move(stream_id); _tuple = std::move(tuple);
} }
void setRtpServerInfo(uint16_t local_port, RtpServer::TcpMode mode, bool re_use_port, uint32_t ssrc, int only_track) { void setRtpServerInfo(uint16_t local_port, RtpServer::TcpMode mode, bool re_use_port, uint32_t ssrc, int only_track) {
_ssrc = ssrc; _ssrc = ssrc;
_process = RtpProcess::createProcess(_stream_id); _process = RtpProcess::createProcess(_tuple);
_process->setOnlyTrack((RtpProcess::OnlyTrack)only_track); _process->setOnlyTrack((RtpProcess::OnlyTrack)only_track);
_timeout_cb = [=]() mutable { _timeout_cb = [=]() mutable {
NOTICE_EMIT(BroadcastRtpServerTimeoutArgs, Broadcast::kBroadcastRtpServerTimeout, local_port, _stream_id, (int)mode, re_use_port, ssrc); NOTICE_EMIT(BroadcastRtpServerTimeoutArgs, Broadcast::kBroadcastRtpServerTimeout, local_port, _tuple, (int)mode, re_use_port, ssrc);
}; };
weak_ptr<RtcpHelper> weak_self = shared_from_this(); weak_ptr<RtcpHelper> weak_self = shared_from_this();
@ -117,15 +117,16 @@ private:
Ticker _ticker; Ticker _ticker;
Socket::Ptr _rtcp_sock; Socket::Ptr _rtcp_sock;
RtpProcess::Ptr _process; RtpProcess::Ptr _process;
std::string _stream_id; MediaTuple _tuple;
RtpProcess::onDetachCB _on_detach; RtpProcess::onDetachCB _on_detach;
std::shared_ptr<struct sockaddr_storage> _rtcp_addr; std::shared_ptr<struct sockaddr_storage> _rtcp_addr;
}; };
void RtpServer::start(uint16_t local_port, const string &stream_id, TcpMode tcp_mode, const char *local_ip, bool re_use_port, uint32_t ssrc, int only_track, bool multiplex) { void RtpServer::start(uint16_t local_port, const MediaTuple &tuple, TcpMode tcp_mode, const char *local_ip, bool re_use_port, uint32_t ssrc, int only_track, bool multiplex) {
//创建udp服务器 //创建udp服务器
Socket::Ptr rtp_socket = Socket::createSocket(nullptr, true); auto poller = EventPollerPool::Instance().getPoller();
Socket::Ptr rtcp_socket = Socket::createSocket(nullptr, true); Socket::Ptr rtp_socket = Socket::createSocket(poller, true);
Socket::Ptr rtcp_socket = Socket::createSocket(poller, true);
if (local_port == 0) { if (local_port == 0) {
//随机端口rtp端口采用偶数 //随机端口rtp端口采用偶数
auto pair = std::make_pair(rtp_socket, rtcp_socket); auto pair = std::make_pair(rtp_socket, rtcp_socket);
@ -147,9 +148,9 @@ void RtpServer::start(uint16_t local_port, const string &stream_id, TcpMode tcp_
UdpServer::Ptr udp_server; UdpServer::Ptr udp_server;
RtcpHelper::Ptr helper; RtcpHelper::Ptr helper;
//增加了多路复用判断如果多路复用为true就走else逻辑同时保留了原来stream_id为空走else逻辑 //增加了多路复用判断如果多路复用为true就走else逻辑同时保留了原来stream_id为空走else逻辑
if (!stream_id.empty() && !multiplex) { if (!tuple.stream.empty() && !multiplex) {
//指定了流id那么一个端口一个流(不管是否包含多个ssrc的多个流绑定rtp源后会筛选掉ip端口不匹配的流) //指定了流id那么一个端口一个流(不管是否包含多个ssrc的多个流绑定rtp源后会筛选掉ip端口不匹配的流)
helper = std::make_shared<RtcpHelper>(std::move(rtcp_socket), stream_id); helper = std::make_shared<RtcpHelper>(std::move(rtcp_socket), tuple);
helper->startRtcp(); helper->startRtcp();
helper->setRtpServerInfo(local_port, tcp_mode, re_use_port, ssrc, only_track); helper->setRtpServerInfo(local_port, tcp_mode, re_use_port, ssrc, only_track);
bool bind_peer_addr = false; bool bind_peer_addr = false;
@ -175,30 +176,34 @@ void RtpServer::start(uint16_t local_port, const string &stream_id, TcpMode tcp_
udp_server = std::make_shared<UdpServer>(); udp_server = std::make_shared<UdpServer>();
(*udp_server)[RtpSession::kOnlyTrack] = only_track; (*udp_server)[RtpSession::kOnlyTrack] = only_track;
(*udp_server)[RtpSession::kUdpRecvBuffer] = udpRecvSocketBuffer; (*udp_server)[RtpSession::kUdpRecvBuffer] = udpRecvSocketBuffer;
(*udp_server)[RtpSession::kVhost] = tuple.vhost;
(*udp_server)[RtpSession::kApp] = tuple.app;
udp_server->start<RtpSession>(local_port, local_ip); udp_server->start<RtpSession>(local_port, local_ip);
rtp_socket = nullptr; rtp_socket = nullptr;
} }
TcpServer::Ptr tcp_server; TcpServer::Ptr tcp_server;
if (tcp_mode == PASSIVE || tcp_mode == ACTIVE) { if (tcp_mode == PASSIVE || tcp_mode == ACTIVE) {
//创建tcp服务器 auto processor = helper ? helper->getProcess() : nullptr;
tcp_server = std::make_shared<TcpServer>(); // 如果共享同一个processor对象那么tcp server深圳为单线程模式确保线程安全
(*tcp_server)[RtpSession::kStreamID] = stream_id; tcp_server = std::make_shared<TcpServer>(processor ? poller : nullptr);
(*tcp_server)[RtpSession::kVhost] = tuple.vhost;
(*tcp_server)[RtpSession::kApp] = tuple.app;
(*tcp_server)[RtpSession::kStreamID] = tuple.stream;
(*tcp_server)[RtpSession::kSSRC] = ssrc; (*tcp_server)[RtpSession::kSSRC] = ssrc;
(*tcp_server)[RtpSession::kOnlyTrack] = only_track; (*tcp_server)[RtpSession::kOnlyTrack] = only_track;
if (tcp_mode == PASSIVE) { if (tcp_mode == PASSIVE) {
weak_ptr<RtpServer> weak_self = shared_from_this(); weak_ptr<RtpServer> weak_self = shared_from_this();
auto processor = helper ? helper->getProcess() : nullptr;
tcp_server->start<RtpSession>(local_port, local_ip, 1024, [weak_self, processor](std::shared_ptr<RtpSession> &session) { tcp_server->start<RtpSession>(local_port, local_ip, 1024, [weak_self, processor](std::shared_ptr<RtpSession> &session) {
session->setRtpProcess(processor); session->setRtpProcess(processor);
}); });
} else if (stream_id.empty()) { } else if (tuple.stream.empty()) {
// tcp主动模式时只能一个端口一个流必须指定流id; 创建TcpServer对象也仅用于传参 // tcp主动模式时只能一个端口一个流必须指定流id; 创建TcpServer对象也仅用于传参
throw std::runtime_error(StrPrinter << "tcp主动模式时必需指定流id"); throw std::runtime_error(StrPrinter << "tcp主动模式时必需指定流id");
} }
} }
_on_cleanup = [rtp_socket, stream_id]() { _on_cleanup = [rtp_socket]() {
if (rtp_socket) { if (rtp_socket) {
//去除循环引用 //去除循环引用
rtp_socket->setOnRead(nullptr); rtp_socket->setOnRead(nullptr);

View File

@ -43,7 +43,7 @@ public:
* @param ssrc ssrc * @param ssrc ssrc
* @param multiplex * @param multiplex
*/ */
void start(uint16_t local_port, const std::string &stream_id = "", TcpMode tcp_mode = PASSIVE, void start(uint16_t local_port, const MediaTuple &tuple = MediaTuple{DEFAULT_VHOST, kRtpAppName, "", ""}, TcpMode tcp_mode = PASSIVE,
const char *local_ip = "::", bool re_use_port = true, uint32_t ssrc = 0, int only_track = 0, bool multiplex = false); const char *local_ip = "::", bool re_use_port = true, uint32_t ssrc = 0, int only_track = 0, bool multiplex = false);
/** /**

View File

@ -21,6 +21,8 @@ using namespace toolkit;
namespace mediakit{ namespace mediakit{
const string RtpSession::kVhost = "vhost";
const string RtpSession::kApp = "app";
const string RtpSession::kStreamID = "stream_id"; const string RtpSession::kStreamID = "stream_id";
const string RtpSession::kSSRC = "ssrc"; const string RtpSession::kSSRC = "ssrc";
const string RtpSession::kOnlyTrack = "only_track"; const string RtpSession::kOnlyTrack = "only_track";
@ -31,7 +33,9 @@ void RtpSession::attachServer(const Server &server) {
} }
void RtpSession::setParams(mINI &ini) { void RtpSession::setParams(mINI &ini) {
_stream_id = ini[kStreamID]; _tuple.vhost = ini[kVhost];
_tuple.app = ini[kApp];
_tuple.stream = ini[kStreamID];
_ssrc = ini[kSSRC]; _ssrc = ini[kSSRC];
_only_track = ini[kOnlyTrack]; _only_track = ini[kOnlyTrack];
int udp_socket_buffer = ini[kUdpRecvBuffer]; int udp_socket_buffer = ini[kUdpRecvBuffer];
@ -63,7 +67,7 @@ void RtpSession::onError(const SockException &err) {
if (_emit_detach) { if (_emit_detach) {
_process->onDetach(err); _process->onDetach(err);
} }
WarnP(this) << _stream_id << " " << err; WarnP(this) << _tuple.shortUrl() << " " << err;
} }
void RtpSession::onManager() { void RtpSession::onManager() {
@ -107,12 +111,12 @@ void RtpSession::onRtpPacket(const char *data, size_t len) {
} }
// 未指定流id就使用ssrc为流id // 未指定流id就使用ssrc为流id
if (_stream_id.empty()) { if (_tuple.stream.empty()) {
_stream_id = printSSRC(_ssrc); _tuple.stream = printSSRC(_ssrc);
} }
if (!_process) { if (!_process) {
_process = RtpProcess::createProcess(_stream_id); _process = RtpProcess::createProcess(_tuple);
_process->setOnlyTrack((RtpProcess::OnlyTrack)_only_track); _process->setOnlyTrack((RtpProcess::OnlyTrack)_only_track);
weak_ptr<RtpSession> weak_self = static_pointer_cast<RtpSession>(shared_from_this()); weak_ptr<RtpSession> weak_self = static_pointer_cast<RtpSession>(shared_from_this());
_process->setOnDetach([weak_self](const SockException &ex) { _process->setOnDetach([weak_self](const SockException &ex) {

View File

@ -22,6 +22,8 @@ namespace mediakit{
class RtpSession : public toolkit::Session, public RtpSplitter { class RtpSession : public toolkit::Session, public RtpSplitter {
public: public:
static const std::string kVhost;
static const std::string kApp;
static const std::string kStreamID; static const std::string kStreamID;
static const std::string kSSRC; static const std::string kSSRC;
static const std::string kOnlyTrack; static const std::string kOnlyTrack;
@ -54,7 +56,7 @@ private:
int _only_track = 0; int _only_track = 0;
uint32_t _ssrc = 0; uint32_t _ssrc = 0;
toolkit::Ticker _ticker; toolkit::Ticker _ticker;
std::string _stream_id; MediaTuple _tuple;
struct sockaddr_storage _addr; struct sockaddr_storage _addr;
RtpProcess::Ptr _process; RtpProcess::Ptr _process;
}; };

View File

@ -46,7 +46,7 @@ const char *RtpSplitter::onSearchPacketTail(const char *data, size_t len) {
return nullptr; return nullptr;
} }
if ( _is_ehome ) { if (_check_ehome_count) {
if (isEhome(data, len)) { if (isEhome(data, len)) {
//是ehome协议 //是ehome协议
if (len < kEHOME_OFFSET + 4) { if (len < kEHOME_OFFSET + 4) {
@ -59,7 +59,7 @@ const char *RtpSplitter::onSearchPacketTail(const char *data, size_t len) {
//忽略ehome私有头 //忽略ehome私有头
return onSearchPacketTail_l(data + kEHOME_OFFSET + 2, len - kEHOME_OFFSET - 2); return onSearchPacketTail_l(data + kEHOME_OFFSET + 2, len - kEHOME_OFFSET - 2);
} }
_is_ehome = false; _check_ehome_count--;
} }
if ( _is_rtsp_interleaved ) { if ( _is_rtsp_interleaved ) {

View File

@ -31,7 +31,8 @@ protected:
const char *onSearchPacketTail_l(const char *data, size_t len); const char *onSearchPacketTail_l(const char *data, size_t len);
private: private:
bool _is_ehome = true; bool _is_ehome = false;
int _check_ehome_count = 3;
bool _is_rtsp_interleaved = true; bool _is_rtsp_interleaved = true;
size_t _offset = 0; size_t _offset = 0;
}; };

View File

@ -99,10 +99,10 @@ RtpMultiCaster::~RtpMultiCaster() {
DebugL; DebugL;
} }
RtpMultiCaster::RtpMultiCaster(SocketHelper &helper, const string &local_ip, const string &vhost, const string &app, const string &stream, uint32_t multicast_ip, uint16_t video_port, uint16_t audio_port) { RtpMultiCaster::RtpMultiCaster(SocketHelper &helper, const string &local_ip, const MediaTuple &tuple, uint32_t multicast_ip, uint16_t video_port, uint16_t audio_port) {
auto src = dynamic_pointer_cast<RtspMediaSource>(MediaSource::find(RTSP_SCHEMA, vhost, app, stream)); auto src = dynamic_pointer_cast<RtspMediaSource>(MediaSource::find(RTSP_SCHEMA, tuple.vhost, tuple.app, tuple.stream));
if (!src) { if (!src) {
auto err = StrPrinter << "未找到媒体源:" << vhost << " " << app << " " << stream << endl; auto err = StrPrinter << "未找到媒体源:" << tuple.shortUrl() << endl;
throw std::runtime_error(err); throw std::runtime_error(err);
} }
_multicast_ip = (multicast_ip) ? make_shared<uint32_t>(multicast_ip) : MultiCastAddressMaker::Instance().obtain(); _multicast_ip = (multicast_ip) ? make_shared<uint32_t>(multicast_ip) : MultiCastAddressMaker::Instance().obtain();
@ -144,7 +144,7 @@ RtpMultiCaster::RtpMultiCaster(SocketHelper &helper, const string &local_ip, con
}); });
}); });
string strKey = StrPrinter << local_ip << " " << vhost << " " << app << " " << stream << endl; string strKey = StrPrinter << local_ip << " " << tuple.vhost << " " << tuple.app << " " << tuple.stream << endl;
_rtp_reader->setDetachCB([this, strKey]() { _rtp_reader->setDetachCB([this, strKey]() {
{ {
lock_guard<recursive_mutex> lck(g_mtx); lock_guard<recursive_mutex> lck(g_mtx);
@ -167,7 +167,7 @@ RtpMultiCaster::RtpMultiCaster(SocketHelper &helper, const string &local_ip, con
DebugL << MultiCastAddressMaker::toString(*_multicast_ip) << " " DebugL << MultiCastAddressMaker::toString(*_multicast_ip) << " "
<< _udp_sock[0]->get_local_port() << " " << _udp_sock[0]->get_local_port() << " "
<< _udp_sock[1]->get_local_port() << " " << _udp_sock[1]->get_local_port() << " "
<< vhost << " " << app << " " << stream; << tuple.shortUrl();
} }
uint16_t RtpMultiCaster::getMultiCasterPort(TrackType trackType) { uint16_t RtpMultiCaster::getMultiCasterPort(TrackType trackType) {
@ -180,17 +180,17 @@ string RtpMultiCaster::getMultiCasterIP() {
return SockUtil::inet_ntoa(addr); return SockUtil::inet_ntoa(addr);
} }
RtpMultiCaster::Ptr RtpMultiCaster::get(SocketHelper &helper, const string &local_ip, const string &vhost, const string &app, const string &stream, uint32_t multicast_ip, uint16_t video_port, uint16_t audio_port) { RtpMultiCaster::Ptr RtpMultiCaster::get(SocketHelper &helper, const string &local_ip, const MediaTuple &tuple, uint32_t multicast_ip, uint16_t video_port, uint16_t audio_port) {
static auto on_create = [](SocketHelper &helper, const string &local_ip, const string &vhost, const string &app, const string &stream, uint32_t multicast_ip, uint16_t video_port, uint16_t audio_port){ static auto on_create = [](SocketHelper &helper, const string &local_ip, const MediaTuple &tuple, uint32_t multicast_ip, uint16_t video_port, uint16_t audio_port){
try { try {
auto poller = helper.getPoller(); auto poller = helper.getPoller();
auto ret = RtpMultiCaster::Ptr(new RtpMultiCaster(helper, local_ip, vhost, app, stream, multicast_ip, video_port, audio_port), [poller](RtpMultiCaster *ptr) { auto ret = RtpMultiCaster::Ptr(new RtpMultiCaster(helper, local_ip, tuple, multicast_ip, video_port, audio_port), [poller](RtpMultiCaster *ptr) {
poller->async([ptr]() { poller->async([ptr]() {
delete ptr; delete ptr;
}); });
}); });
lock_guard<recursive_mutex> lck(g_mtx); lock_guard<recursive_mutex> lck(g_mtx);
string strKey = StrPrinter << local_ip << " " << vhost << " " << app << " " << stream << endl; string strKey = StrPrinter << local_ip << " " << tuple.vhost << " " << tuple.app << " " << tuple.stream << endl;
g_multi_caster_map.emplace(strKey, ret); g_multi_caster_map.emplace(strKey, ret);
return ret; return ret;
} catch (std::exception &ex) { } catch (std::exception &ex) {
@ -199,16 +199,16 @@ RtpMultiCaster::Ptr RtpMultiCaster::get(SocketHelper &helper, const string &loca
} }
}; };
string strKey = StrPrinter << local_ip << " " << vhost << " " << app << " " << stream << endl; string strKey = StrPrinter << local_ip << " " << tuple.vhost << " " << tuple.app << " " << tuple.stream << endl;
lock_guard<recursive_mutex> lck(g_mtx); lock_guard<recursive_mutex> lck(g_mtx);
auto it = g_multi_caster_map.find(strKey); auto it = g_multi_caster_map.find(strKey);
if (it == g_multi_caster_map.end()) { if (it == g_multi_caster_map.end()) {
return on_create(helper, local_ip, vhost, app, stream, multicast_ip, video_port, audio_port); return on_create(helper, local_ip, tuple, multicast_ip, video_port, audio_port);
} }
auto ret = it->second.lock(); auto ret = it->second.lock();
if (!ret) { if (!ret) {
g_multi_caster_map.erase(it); g_multi_caster_map.erase(it);
return on_create(helper, local_ip, vhost, app, stream, multicast_ip, video_port, audio_port); return on_create(helper, local_ip, tuple, multicast_ip, video_port, audio_port);
} }
return ret; return ret;
} }

View File

@ -45,14 +45,14 @@ public:
~RtpMultiCaster(); ~RtpMultiCaster();
static Ptr get(toolkit::SocketHelper &helper, const std::string &local_ip, const std::string &vhost, const std::string &app, const std::string &stream, uint32_t multicast_ip = 0, uint16_t video_port = 0, uint16_t audio_port = 0); static Ptr get(toolkit::SocketHelper &helper, const std::string &local_ip, const MediaTuple &tuple, uint32_t multicast_ip = 0, uint16_t video_port = 0, uint16_t audio_port = 0);
void setDetachCB(void *listener,const onDetach &cb); void setDetachCB(void *listener,const onDetach &cb);
std::string getMultiCasterIP(); std::string getMultiCasterIP();
uint16_t getMultiCasterPort(TrackType trackType); uint16_t getMultiCasterPort(TrackType trackType);
private: private:
RtpMultiCaster(toolkit::SocketHelper &helper, const std::string &local_ip, const std::string &vhost, const std::string &app, const std::string &stream, uint32_t multicast_ip, uint16_t video_port, uint16_t audio_port); RtpMultiCaster(toolkit::SocketHelper &helper, const std::string &local_ip, const MediaTuple &tuple, uint32_t multicast_ip, uint16_t video_port, uint16_t audio_port);
private: private:
std::recursive_mutex _mtx; std::recursive_mutex _mtx;

View File

@ -15,6 +15,7 @@
#include "Common/Parser.h" #include "Common/Parser.h"
#include "Common/config.h" #include "Common/config.h"
#include "Network/Socket.h" #include "Network/Socket.h"
#include "Extension/Factory.h"
using namespace std; using namespace std;
using namespace toolkit; using namespace toolkit;
@ -236,10 +237,6 @@ void SdpParser::load(const string &sdp) {
track._codec = codec; track._codec = codec;
track._samplerate = samplerate; track._samplerate = samplerate;
} }
if (!track._samplerate && track._type == TrackVideo) {
// 未设置视频采样率时赋值为90000
track._samplerate = 90000;
}
++it; ++it;
} }
@ -260,6 +257,17 @@ void SdpParser::load(const string &sdp) {
if (it != track._attr.end()) { if (it != track._attr.end()) {
track._control = it->second; track._control = it->second;
} }
if (!track._samplerate && track._type == TrackVideo) {
// 未设置视频采样率时赋值为90000
track._samplerate = 90000;
} else if (!track._samplerate && track._type == TrackAudio) {
// some rtsp sdp no sample rate but has fmt config to parser get sample rate
auto t = Factory::getTrackBySdp(track_ptr);
if (t) {
track._samplerate = std::static_pointer_cast<AudioTrack>(t)->getAudioSampleRate();
}
}
} }
} }

View File

@ -44,7 +44,13 @@ public:
*/ */
RtspMediaSource(const MediaTuple& tuple, int ring_size = RTP_GOP_SIZE): MediaSource(RTSP_SCHEMA, tuple), _ring_size(ring_size) {} RtspMediaSource(const MediaTuple& tuple, int ring_size = RTP_GOP_SIZE): MediaSource(RTSP_SCHEMA, tuple), _ring_size(ring_size) {}
~RtspMediaSource() override { flush(); } ~RtspMediaSource() override {
try {
flush();
} catch (std::exception &ex) {
WarnL << ex.what();
}
}
/** /**
* *

View File

@ -29,7 +29,13 @@ public:
getRtpRing()->setDelegate(_media_src); getRtpRing()->setDelegate(_media_src);
} }
~RtspMediaSourceMuxer() override { RtspMuxer::flush(); } ~RtspMediaSourceMuxer() override {
try {
RtspMuxer::flush();
} catch (std::exception &ex) {
WarnL << ex.what();
}
}
void setListener(const std::weak_ptr<MediaSourceEvent> &listener){ void setListener(const std::weak_ptr<MediaSourceEvent> &listener){
setDelegate(listener); setDelegate(listener);

View File

@ -88,6 +88,7 @@ void RtspPlayer::play(const string &strUrl) {
_rtp_type = (Rtsp::eRtpType)(int)(*this)[Client::kRtpType]; _rtp_type = (Rtsp::eRtpType)(int)(*this)[Client::kRtpType];
_beat_type = (*this)[Client::kRtspBeatType].as<int>(); _beat_type = (*this)[Client::kRtspBeatType].as<int>();
_beat_interval_ms = (*this)[Client::kBeatIntervalMS].as<int>(); _beat_interval_ms = (*this)[Client::kBeatIntervalMS].as<int>();
_speed = (*this)[Client::kRtspSpeed].as<float>();
DebugL << url._url << " " << (url._user.size() ? url._user : "null") << " " << (url._passwd.size() ? url._passwd : "null") << " " << _rtp_type; DebugL << url._url << " " << (url._user.size() ? url._user : "null") << " " << (url._passwd.size() ? url._passwd : "null") << " " << _rtp_type;
weak_ptr<RtspPlayer> weakSelf = static_pointer_cast<RtspPlayer>(shared_from_this()); weak_ptr<RtspPlayer> weakSelf = static_pointer_cast<RtspPlayer>(shared_from_this());
@ -256,17 +257,19 @@ void RtspPlayer::sendSetup(unsigned int track_idx) {
switch (_rtp_type) { switch (_rtp_type) {
case Rtsp::RTP_TCP: { case Rtsp::RTP_TCP: {
sendRtspRequest( sendRtspRequest(
"SETUP", control_url, { "Transport", StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track->_type * 2 << "-" << track->_type * 2 + 1 }); "SETUP", control_url,
{ "Transport", StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track->_type * 2 << "-" << track->_type * 2 + 1 << ";mode=play" });
} break; } break;
case Rtsp::RTP_MULTICAST: { case Rtsp::RTP_MULTICAST: {
sendRtspRequest("SETUP", control_url, { "Transport", "RTP/AVP;multicast" }); sendRtspRequest("SETUP", control_url, { "Transport", "RTP/AVP;multicast;mode=play" });
} break; } break;
case Rtsp::RTP_UDP: { case Rtsp::RTP_UDP: {
createUdpSockIfNecessary(track_idx); createUdpSockIfNecessary(track_idx);
sendRtspRequest( sendRtspRequest(
"SETUP", control_url, "SETUP", control_url,
{ "Transport", { "Transport",
StrPrinter << "RTP/AVP;unicast;client_port=" << _rtp_sock[track_idx]->get_local_port() << "-" << _rtcp_sock[track_idx]->get_local_port() }); StrPrinter << "RTP/AVP;unicast;client_port=" << _rtp_sock[track_idx]->get_local_port() << "-" << _rtcp_sock[track_idx]->get_local_port()
<< ";mode=play" });
} break; } break;
default: break; default: break;
} }
@ -387,7 +390,12 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int track_idx) {
} }
// 所有setup命令发送完毕 // 所有setup命令发送完毕
// 发送play命令 // 发送play命令
sendPause(type_play, 0); if (_speed==0.0f) {
sendPause(type_play, 0);
} else {
sendPause(type_speed, 0);
}
} }
void RtspPlayer::sendDescribe() { void RtspPlayer::sendDescribe() {
@ -436,6 +444,9 @@ void RtspPlayer::sendPause(int type, uint32_t seekMS) {
case type_seek: case type_seek:
sendRtspRequest("PLAY", _control_url, { "Range", StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << seekMS / 1000.0 << "-" }); sendRtspRequest("PLAY", _control_url, { "Range", StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << seekMS / 1000.0 << "-" });
break; break;
case type_speed:
speed(_speed);
break;
default: default:
WarnL << "unknown type : " << type; WarnL << "unknown type : " << type;
_on_response = nullptr; _on_response = nullptr;

View File

@ -120,6 +120,8 @@ private:
uint32_t _beat_interval_ms = 0; uint32_t _beat_interval_ms = 0;
std::string _play_url; std::string _play_url;
// rtsp开始倍速
float _speed= 0.0f;
std::vector<SdpTrack::Ptr> _sdp_track; std::vector<SdpTrack::Ptr> _sdp_track;
std::function<void(const Parser&)> _on_response; std::function<void(const Parser&)> _on_response;
//RTP端口,trackid idx 为数组下标 //RTP端口,trackid idx 为数组下标

View File

@ -796,7 +796,7 @@ void RtspSession::handleReq_Setup(const Parser &parser) {
break; break;
case Rtsp::RTP_MULTICAST: { case Rtsp::RTP_MULTICAST: {
if(!_multicaster){ if(!_multicaster){
_multicaster = RtpMultiCaster::get(*this, get_local_ip(), _media_info.vhost, _media_info.app, _media_info.stream, _multicast_ip, _multicast_video_port, _multicast_audio_port); _multicaster = RtpMultiCaster::get(*this, get_local_ip(), _media_info, _multicast_ip, _multicast_video_port, _multicast_audio_port);
if (!_multicaster) { if (!_multicaster) {
send_NotAcceptable(); send_NotAcceptable();
throw SockException(Err_shutdown, "can not get a available udp multicast socket"); throw SockException(Err_shutdown, "can not get a available udp multicast socket");

View File

@ -40,7 +40,13 @@ public:
TSMediaSource(const MediaTuple& tuple, int ring_size = TS_GOP_SIZE): MediaSource(TS_SCHEMA, tuple), _ring_size(ring_size) {} TSMediaSource(const MediaTuple& tuple, int ring_size = TS_GOP_SIZE): MediaSource(TS_SCHEMA, tuple), _ring_size(ring_size) {}
~TSMediaSource() override { flush(); } ~TSMediaSource() override {
try {
flush();
} catch (std::exception &ex) {
WarnL << ex.what();
}
}
/** /**
* *

View File

@ -224,7 +224,8 @@ int main(int argc, char *argv[]) {
option.enable_mp4 = false; option.enable_mp4 = false;
option.modify_stamp = (int)ProtocolOption::kModifyStampRelative; option.modify_stamp = (int)ProtocolOption::kModifyStampRelative;
//添加拉流代理 //添加拉流代理
auto proxy = std::make_shared<PlayerProxy>(DEFAULT_VHOST, "app", std::to_string(i), option, -1, nullptr, 1); auto tuple = MediaTuple { DEFAULT_VHOST, "app", std::to_string(i), "" };
auto proxy = std::make_shared<PlayerProxy>(tuple, option, -1, nullptr, 1);
//开始拉流代理 //开始拉流代理
proxy->play(input_urls[i]); proxy->play(input_urls[i]);
proxy_map.emplace(i, std::move(proxy)); proxy_map.emplace(i, std::move(proxy));

View File

@ -137,7 +137,8 @@ int main(int argc, char *argv[]) {
option.enable_mp4 = false; option.enable_mp4 = false;
for (auto i = 0; i < proxy_count; ++i) { for (auto i = 0; i < proxy_count; ++i) {
auto stream = to_string(i); auto stream = to_string(i);
PlayerProxy::Ptr player(new PlayerProxy(DEFAULT_VHOST, "live", stream, option)); auto tuple = MediaTuple{DEFAULT_VHOST, "live", stream, ""};
PlayerProxy::Ptr player(new PlayerProxy(tuple, option));
(*player)[Client::kRtpType] = rtp_type; (*player)[Client::kRtpType] = rtp_type;
player->play(in_url); player->play(in_url);
proxyMap.emplace(stream, player); proxyMap.emplace(stream, player);

View File

@ -148,9 +148,10 @@ int main(int argc, char *argv[]) {
MediaSource::Ptr src = nullptr; MediaSource::Ptr src = nullptr;
PlayerProxy::Ptr proxy = nullptr;; PlayerProxy::Ptr proxy = nullptr;;
auto tuple = MediaTuple { DEFAULT_VHOST, app, stream, "" };
if (end_with(in_url, ".mp4")) { if (end_with(in_url, ".mp4")) {
// create MediaSource from mp4file // create MediaSource from mp4file
auto reader = std::make_shared<MP4Reader>(DEFAULT_VHOST, app, stream, in_url); auto reader = std::make_shared<MP4Reader>(tuple, in_url);
//mp4 repeat //mp4 repeat
reader->startReadMP4(0, true, true); reader->startReadMP4(0, true, true);
src = MediaSource::find(schema, DEFAULT_VHOST, app, stream, false); src = MediaSource::find(schema, DEFAULT_VHOST, app, stream, false);
@ -161,7 +162,7 @@ int main(int argc, char *argv[]) {
} }
} else { } else {
//添加拉流代理 //添加拉流代理
proxy = std::make_shared<PlayerProxy>(DEFAULT_VHOST, app, stream, option); proxy = std::make_shared<PlayerProxy>(tuple, option);
//rtsp拉流代理方式 //rtsp拉流代理方式
(*proxy)[Client::kRtpType] = rtp_type; (*proxy)[Client::kRtpType] = rtp_type;
//开始拉流代理 //开始拉流代理

View File

@ -79,7 +79,8 @@ int domain(const string &playUrl, const string &pushUrl) {
ProtocolOption option; ProtocolOption option;
option.enable_hls = false; option.enable_hls = false;
option.enable_mp4 = false; option.enable_mp4 = false;
PlayerProxy::Ptr player(new PlayerProxy(DEFAULT_VHOST, "app", "stream", option, -1, poller)); auto tuple = MediaTuple{DEFAULT_VHOST, "app", "stream", ""};
PlayerProxy::Ptr player(new PlayerProxy(tuple, option, -1, poller));
//可以指定rtsp拉流方式支持tcp和udp方式默认tcp //可以指定rtsp拉流方式支持tcp和udp方式默认tcp
// (*player)[Client::kRtpType] = Rtsp::RTP_UDP; // (*player)[Client::kRtpType] = Rtsp::RTP_UDP;
player->play(playUrl.data()); player->play(playUrl.data());

View File

@ -43,7 +43,8 @@ int domain(const string &file, const string &url) {
mINI::Instance()["protocol.enable_" + schema] = 1; mINI::Instance()["protocol.enable_" + schema] = 1;
// 从mp4文件加载生成MediaSource对象 // 从mp4文件加载生成MediaSource对象
auto reader = std::make_shared<MP4Reader>(DEFAULT_VHOST, "live", "stream", file); auto tuple = MediaTuple {DEFAULT_VHOST, "live", "stream", ""};
auto reader = std::make_shared<MP4Reader>(tuple, file);
// 开始加载mp4ref_self设置为false这样reader对象设置为nullptr就能注销了file_repeat可以设置为空这样文件读完了就停止推流了 // 开始加载mp4ref_self设置为false这样reader对象设置为nullptr就能注销了file_repeat可以设置为空这样文件读完了就停止推流了
reader->startReadMP4(100, false, true); reader->startReadMP4(100, false, true);
auto src = MediaSource::find(schema, DEFAULT_VHOST, "live", "stream", false); auto src = MediaSource::find(schema, DEFAULT_VHOST, "live", "stream", false);

View File

@ -42,7 +42,7 @@ static bool loadFile(const char *path, const EventPoller::Ptr &poller) {
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
addr.ss_family = AF_INET; addr.ss_family = AF_INET;
auto sock = Socket::createSocket(poller); auto sock = Socket::createSocket(poller);
auto process = RtpProcess::createProcess("test"); auto process = RtpProcess::createProcess(MediaTuple { DEFAULT_VHOST, kRtpAppName, "test", "" });
uint64_t stamp_last = 0; uint64_t stamp_last = 0;
auto total_size = std::make_shared<size_t>(0); auto total_size = std::make_shared<size_t>(0);
@ -75,7 +75,7 @@ static bool loadFile(const char *path, const EventPoller::Ptr &poller) {
return 0; return 0;
} }
auto diff = stamp - stamp_last; auto diff = static_cast<int64_t>(stamp - stamp_last);
if (diff < 0 || diff > 500) { if (diff < 0 || diff > 500) {
diff = 1; diff = 1;
} }

View File

@ -230,8 +230,8 @@ int main(int argc,char *argv[]) {
//http://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 //http://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4
//rtsp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 //rtsp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4
//rtmp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 //rtmp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4
auto tuple = MediaTuple{DEFAULT_VHOST, "live", std::string("chn") + to_string(i).data(), ""};
PlayerProxy::Ptr player(new PlayerProxy(DEFAULT_VHOST, "live", std::string("chn") + to_string(i).data(), ProtocolOption())); PlayerProxy::Ptr player(new PlayerProxy(tuple, ProtocolOption()));
//指定RTP over TCP(播放rtsp时有效) //指定RTP over TCP(播放rtsp时有效)
(*player)[Client::kRtpType] = Rtsp::RTP_TCP; (*player)[Client::kRtpType] = Rtsp::RTP_TCP;
//开始播放,如果播放失败或者播放中止,将会自动重试若干次,重试次数在配置文件中配置,默认一直重试 //开始播放,如果播放失败或者播放中止,将会自动重试若干次,重试次数在配置文件中配置,默认一直重试

View File

@ -19,12 +19,13 @@ namespace mediakit {
// RTC配置项目 // RTC配置项目
namespace Rtc { namespace Rtc {
#define RTC_FIELD "rtc." #define RTC_FIELD "rtc."
//~ nack接收端 //~ nack接收端, rtp发送端
// Nack缓存包最早时间间隔 // rtp重发缓存列队最大长度单位毫秒
const string kMaxNackMS = RTC_FIELD "maxNackMS"; const string kMaxRtpCacheMS = RTC_FIELD "maxRtpCacheMS";
// Nack包检查间隔(包数量) // rtp重发缓存列队最大长度单位个数
const string kRtpCacheCheckInterval = RTC_FIELD "rtpCacheCheckInterval"; const string kMaxRtpCacheSize = RTC_FIELD "maxRtpCacheSize";
//~ nack发送端
//~ nack发送端rtp接收端
//最大保留的rtp丢包状态个数 //最大保留的rtp丢包状态个数
const string kNackMaxSize = RTC_FIELD "nackMaxSize"; const string kNackMaxSize = RTC_FIELD "nackMaxSize";
// rtp丢包状态最长保留时间 // rtp丢包状态最长保留时间
@ -37,8 +38,8 @@ const string kNackIntervalRatio = RTC_FIELD "nackIntervalRatio";
const string kNackRtpSize = RTC_FIELD "nackRtpSize"; const string kNackRtpSize = RTC_FIELD "nackRtpSize";
static onceToken token([]() { static onceToken token([]() {
mINI::Instance()[kMaxNackMS] = 5 * 1000; mINI::Instance()[kMaxRtpCacheMS] = 5 * 1000;
mINI::Instance()[kRtpCacheCheckInterval] = 100; mINI::Instance()[kMaxRtpCacheSize] = 2048;
mINI::Instance()[kNackMaxSize] = 2048; mINI::Instance()[kNackMaxSize] = 2048;
mINI::Instance()[kNackMaxMS] = 3 * 1000; mINI::Instance()[kNackMaxMS] = 3 * 1000;
mINI::Instance()[kNackMaxCount] = 15; mINI::Instance()[kNackMaxCount] = 15;
@ -49,17 +50,26 @@ static onceToken token([]() {
} // namespace Rtc } // namespace Rtc
void NackList::pushBack(RtpPacket::Ptr rtp) { void NackList::pushBack(RtpPacket::Ptr rtp) {
GET_CONFIG(uint32_t, max_rtp_cache_ms, Rtc::kMaxRtpCacheMS);
GET_CONFIG(uint32_t, max_rtp_cache_size, Rtc::kMaxRtpCacheSize);
// 记录rtp
auto seq = rtp->getSeq(); auto seq = rtp->getSeq();
_nack_cache_seq.emplace_back(seq); _nack_cache_seq.emplace_back(seq);
_nack_cache_pkt.emplace(seq, std::move(rtp)); _nack_cache_pkt.emplace(seq, std::move(rtp));
GET_CONFIG(uint32_t, rtpcache_checkinterval, Rtc::kRtpCacheCheckInterval);
if (++_cache_ms_check < rtpcache_checkinterval) { // 限制rtp缓存最大个数
if (_nack_cache_seq.size() > max_rtp_cache_size) {
popFront();
}
if (++_cache_ms_check < 100) {
// 每100个rtp包检测下缓存长度节省cpu资源
return; return;
} }
_cache_ms_check = 0; _cache_ms_check = 0;
GET_CONFIG(uint32_t, maxnackms, Rtc::kMaxNackMS); // 限制rtp缓存最大时长
while (getCacheMS() >= maxnackms) { while (getCacheMS() >= max_rtp_cache_ms) {
// 需要清除部分nack缓存
popFront(); popFront();
} }
} }
@ -96,13 +106,13 @@ RtpPacket::Ptr *NackList::getRtp(uint16_t seq) {
uint32_t NackList::getCacheMS() { uint32_t NackList::getCacheMS() {
while (_nack_cache_seq.size() > 2) { while (_nack_cache_seq.size() > 2) {
auto back_stamp = getRtpStamp(_nack_cache_seq.back()); auto back_stamp = getNtpStamp(_nack_cache_seq.back());
if (back_stamp == -1) { if (back_stamp == -1) {
_nack_cache_seq.pop_back(); _nack_cache_seq.pop_back();
continue; continue;
} }
auto front_stamp = getRtpStamp(_nack_cache_seq.front()); auto front_stamp = getNtpStamp(_nack_cache_seq.front());
if (front_stamp == -1) { if (front_stamp == -1) {
_nack_cache_seq.pop_front(); _nack_cache_seq.pop_front();
continue; continue;
@ -111,18 +121,19 @@ uint32_t NackList::getCacheMS() {
if (back_stamp >= front_stamp) { if (back_stamp >= front_stamp) {
return back_stamp - front_stamp; return back_stamp - front_stamp;
} }
// 很有可能回环了 // ntp时间戳回退了非法数据丢掉
return back_stamp + (UINT32_MAX - front_stamp); _nack_cache_seq.pop_front();
} }
return 0; return 0;
} }
int64_t NackList::getRtpStamp(uint16_t seq) { int64_t NackList::getNtpStamp(uint16_t seq) {
auto it = _nack_cache_pkt.find(seq); auto it = _nack_cache_pkt.find(seq);
if (it == _nack_cache_pkt.end()) { if (it == _nack_cache_pkt.end()) {
return -1; return -1;
} }
return it->second->getStampMS(false); // 使用ntp时间戳不会回退
return it->second->getStampMS(true);
} }
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -20,6 +20,15 @@
namespace mediakit { namespace mediakit {
// RTC配置项目
namespace Rtc {
//~ nack发送端rtp接收端
// 最大保留的rtp丢包状态个数
extern const std::string kNackMaxSize;
// rtp丢包状态最长保留时间
extern const std::string kNackMaxMS;
} // namespace Rtc
class NackList { class NackList {
public: public:
void pushBack(RtpPacket::Ptr rtp); void pushBack(RtpPacket::Ptr rtp);
@ -28,7 +37,7 @@ public:
private: private:
void popFront(); void popFront();
uint32_t getCacheMS(); uint32_t getCacheMS();
int64_t getRtpStamp(uint16_t seq); int64_t getNtpStamp(uint16_t seq);
RtpPacket::Ptr *getRtp(uint16_t seq); RtpPacket::Ptr *getRtp(uint16_t seq);
private: private:
@ -66,7 +75,7 @@ private:
struct NackStatus { struct NackStatus {
uint64_t first_stamp; uint64_t first_stamp;
uint64_t update_stamp; uint64_t update_stamp;
int nack_count = 0; uint32_t nack_count = 0;
}; };
std::map<uint16_t /*seq*/, NackStatus> _nack_send_status; std::map<uint16_t /*seq*/, NackStatus> _nack_send_status;
}; };

View File

@ -9,7 +9,10 @@
*/ */
#include "WebRtcPlayer.h" #include "WebRtcPlayer.h"
#include "Common/config.h" #include "Common/config.h"
#include "Extension/Factory.h"
#include "Util/base64.h"
using namespace std; using namespace std;
@ -32,6 +35,9 @@ WebRtcPlayer::WebRtcPlayer(const EventPoller::Ptr &poller,
_media_info = info; _media_info = info;
_play_src = src; _play_src = src;
CHECK(src); CHECK(src);
GET_CONFIG(bool, direct_proxy, Rtsp::kDirectProxy);
_send_config_frames_once = direct_proxy;
} }
void WebRtcPlayer::onStartWebRTC() { void WebRtcPlayer::onStartWebRTC() {
@ -56,6 +62,13 @@ void WebRtcPlayer::onStartWebRTC() {
if (!strong_self) { if (!strong_self) {
return; return;
} }
if (strong_self->_send_config_frames_once && !pkt->empty()) {
const auto &first_rtp = pkt->front();
strong_self->sendConfigFrames(first_rtp->getSeq(), first_rtp->sample_rate, first_rtp->getStamp(), first_rtp->ntp_stamp);
strong_self->_send_config_frames_once = false;
}
size_t i = 0; size_t i = 0;
pkt->for_each([&](const RtpPacket::Ptr &rtp) { pkt->for_each([&](const RtpPacket::Ptr &rtp) {
//TraceL<<"send track type:"<<rtp->type<<" ts:"<<rtp->getStamp()<<" ntp:"<<rtp->ntp_stamp<<" size:"<<rtp->getPayloadSize()<<" i:"<<i; //TraceL<<"send track type:"<<rtp->type<<" ts:"<<rtp->getStamp()<<" ntp:"<<rtp->ntp_stamp<<" size:"<<rtp->getPayloadSize()<<" i:"<<i;
@ -111,4 +124,41 @@ void WebRtcPlayer::onRtcConfigure(RtcConfigure &configure) const {
configure.setPlayRtspInfo(playSrc->getSdp()); configure.setPlayRtspInfo(playSrc->getSdp());
} }
void WebRtcPlayer::sendConfigFrames(uint32_t before_seq, uint32_t sample_rate, uint32_t timestamp, uint64_t ntp_timestamp) {
auto play_src = _play_src.lock();
if (!play_src) {
return;
}
SdpParser parser(play_src->getSdp());
auto video_sdp = parser.getTrack(TrackVideo);
if (!video_sdp) {
return;
}
auto video_track = dynamic_pointer_cast<VideoTrack>(Factory::getTrackBySdp(video_sdp));
if (!video_track) {
return;
}
auto frames = video_track->getConfigFrames();
if (frames.empty()) {
return;
}
auto encoder = mediakit::Factory::getRtpEncoderByCodecId(video_track->getCodecId(), 0);
if (!encoder) {
return;
}
GET_CONFIG(uint32_t, video_mtu, Rtp::kVideoMtuSize);
encoder->setRtpInfo(0, video_mtu, sample_rate, 0, 0, 0);
auto seq = before_seq - frames.size();
for (const auto &frame : frames) {
auto rtp = encoder->getRtpInfo().makeRtp(TrackVideo, frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize(), false, 0);
auto header = rtp->getHeader();
header->seq = htons(seq++);
header->stamp = htonl(timestamp);
rtp->ntp_stamp = ntp_timestamp;
onSendRtp(rtp, false);
}
}
}// namespace mediakit }// namespace mediakit

View File

@ -31,11 +31,17 @@ protected:
private: private:
WebRtcPlayer(const EventPoller::Ptr &poller, const RtspMediaSource::Ptr &src, const MediaInfo &info); WebRtcPlayer(const EventPoller::Ptr &poller, const RtspMediaSource::Ptr &src, const MediaInfo &info);
void sendConfigFrames(uint32_t before_seq, uint32_t sample_rate, uint32_t timestamp, uint64_t ntp_timestamp);
private: private:
//媒体相关元数据 //媒体相关元数据
MediaInfo _media_info; MediaInfo _media_info;
//播放的rtsp源 //播放的rtsp源
std::weak_ptr<RtspMediaSource> _play_src; std::weak_ptr<RtspMediaSource> _play_src;
// rtp 直接转发情况下通常会缺少 sps/pps, 在转发 rtp 前, 先发送一次相关帧信息, 部分情况下是可以播放的
bool _send_config_frames_once { false };
//播放rtsp源的reader对象 //播放rtsp源的reader对象
RtspMediaSource::RingType::RingReader::Ptr _reader; RtspMediaSource::RingType::RingReader::Ptr _reader;
}; };

View File

@ -13,6 +13,7 @@
#include "Util/base64.h" #include "Util/base64.h"
#include "Network/sockutil.h" #include "Network/sockutil.h"
#include "Common/config.h" #include "Common/config.h"
#include "Nack.h"
#include "RtpExt.h" #include "RtpExt.h"
#include "Rtcp/Rtcp.h" #include "Rtcp/Rtcp.h"
#include "Rtcp/RtcpFCI.h" #include "Rtcp/RtcpFCI.h"
@ -57,9 +58,6 @@ const string kMinBitrate = RTC_FIELD "min_bitrate";
// 数据通道设置 // 数据通道设置
const string kDataChannelEcho = RTC_FIELD "datachannel_echo"; const string kDataChannelEcho = RTC_FIELD "datachannel_echo";
// rtp丢包状态最长保留时间
const string kNackMaxMS = RTC_FIELD "nackMaxMS";
static onceToken token([]() { static onceToken token([]() {
mINI::Instance()[kTimeOutSec] = 15; mINI::Instance()[kTimeOutSec] = 15;
mINI::Instance()[kExternIP] = ""; mINI::Instance()[kExternIP] = "";
@ -72,8 +70,6 @@ static onceToken token([]() {
mINI::Instance()[kMinBitrate] = 0; mINI::Instance()[kMinBitrate] = 0;
mINI::Instance()[kDataChannelEcho] = true; mINI::Instance()[kDataChannelEcho] = true;
mINI::Instance()[kNackMaxMS] = 3 * 1000;
}); });
} // namespace RTC } // namespace RTC
@ -806,7 +802,8 @@ public:
setOnSorted(std::move(cb)); setOnSorted(std::move(cb));
//设置jitter buffer参数 //设置jitter buffer参数
GET_CONFIG(uint32_t, nack_maxms, Rtc::kNackMaxMS); GET_CONFIG(uint32_t, nack_maxms, Rtc::kNackMaxMS);
RtpTrackImp::setParams(1024, nack_maxms, 512); GET_CONFIG(uint32_t, nack_max_rtp, Rtc::kNackMaxSize);
RtpTrackImp::setParams(nack_max_rtp, nack_maxms, nack_max_rtp / 2);
_nack_ctx.setOnNack([this](const FCI_NACK &nack) { onNack(nack); }); _nack_ctx.setOnNack([this](const FCI_NACK &nack) { onNack(nack); });
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -3,17 +3,30 @@
<head> <head>
<title>ZLM RTC demo</title> <title>ZLM RTC demo</title>
<script src="./ZLMRTCClient.js"></script> <script src="./ZLMRTCClient.js"></script>
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script>
// VConsole will be exported to `window.VConsole` by default.
var vConsole = new window.VConsole();
</script>
<style>
video {
width: 40vw;
max-height: 50vh;
height: 22.5vw; /* 默认和宽:高为 16:9 */
object-fit: contain;
background-color: grey;
}
</style>
</head> </head>
<body> <body>
<div style="text-align: center;"> <div style="text-align: center;">
<div> <div>
<video id='video' controls autoplay style="text-align:left;"> <video id='video' controls autoplay>
Your browser is too old which doesn't support HTML5 video. Your browser is too old which doesn't support HTML5 video.
</video> </video>
<video id='selfVideo' controls autoplay style="text-align:right;"> <video id='selfVideo' controls autoplay>
Your browser is too old which doesn't support HTML5 video. Your browser is too old which doesn't support HTML5 video.
</video> </video>
</div> </div>
@ -27,7 +40,6 @@
</div> </div>
<div style="float: right; width: 70%"> <div style="float: right; width: 70%">
<p> <p>
<label for="streamUrl">url:</label> <label for="streamUrl">url:</label>
<input type="text" style="co; width:70%" id='streamUrl' value="http://192.168.1.101/index/api/webrtc?app=live&stream=xiong&type=play"> <input type="text" style="co; width:70%" id='streamUrl' value="http://192.168.1.101/index/api/webrtc?app=live&stream=xiong&type=play">
@ -82,9 +94,24 @@
<button onclick="send()">发送(send by datachannel)</button> <button onclick="send()">发送(send by datachannel)</button>
<button onclick="close()">关闭(close datachannel)</button> <button onclick="close()">关闭(close datachannel)</button>
<p>
<label for="videoDevice">videodevice:</label>
<select id="videoDevice">
</select>
</p>
<p>
<label for="audioDevice">audiodevice:</label>
<select id="audioDevice">
</select>
</p>
<p>
<label for="switchDevice">switchDevice:</label>
<input type="checkbox" id='switchDevice' checked="checked">
</p>
<button onclick="switchVideo()">切换视频(switch video)</button>
<button onclick="switchAudio()">切换音频(switch audio)</button>
</div> </div>
</div> </div>
@ -132,6 +159,24 @@
document.getElementById("resolution").add(opt,null); document.getElementById("resolution").add(opt,null);
}); });
ZLMRTCClient.GetAllMediaDevice().then(devices=>{
devices.forEach(device=>{
opt = document.createElement('option');
opt.text = device.label + ":"+device.deviceId
opt.value = JSON.stringify(device)
if(device.kind == 'videoinput'){
document.getElementById("videoDevice").add(opt,null)
}else if(device.kind == 'audioinput'){
document.getElementById("audioDevice").add(opt,null)
}else if(device.kind == 'audiooutput'){
// useless
//console.error('not support device')
}
})
}).catch(e=>{
console.error(e);
})
function start_play(){ function start_play(){
let elr = document.getElementById("resolution"); let elr = document.getElementById("resolution");
let res = elr.options[elr.selectedIndex].text.match(/\d+/g); let res = elr.options[elr.selectedIndex].text.match(/\d+/g);
@ -157,6 +202,21 @@
window.history.pushState(null, null, newUrl); window.history.pushState(null, null, newUrl);
} }
let elv = document.getElementById("videoDevice");
let ela = document.getElementById("audioDevice");
let vdevid = ''
let adevid = ''
if (!recvOnly) {
if (elv.selectedIndex !== -1) {
vdevid = JSON.parse(elv.options[elv.selectedIndex].value).deviceId
}
if (ela.selectedIndex !== -1) {
adevid = JSON.parse(ela.options[ela.selectedIndex].value).deviceId
}
}
player = new ZLMRTCClient.Endpoint( player = new ZLMRTCClient.Endpoint(
{ {
element: document.getElementById('video'),// video 标签 element: document.getElementById('video'),// video 标签
@ -169,6 +229,8 @@
recvOnly:recvOnly, recvOnly:recvOnly,
resolution:{w,h}, resolution:{w,h},
usedatachannel:document.getElementById('datachannel').checked, usedatachannel:document.getElementById('datachannel').checked,
videoId:vdevid, // 不填选择默认的
audioId:adevid, // 不填选择默认的
} }
); );
@ -344,6 +406,39 @@
// get_media_list(); // get_media_list();
}, 5000); }, 5000);
function switchVideo(){
if(player){
// first arg bool false mean switch to screen video , second ignore
// true mean switch to video , second is camera deviceid
let elv = document.getElementById("videoDevice");
let vdevid = JSON.parse(elv.options[elv.selectedIndex].value).deviceId
player.switchVideo(document.getElementById('switchDevice').checked,vdevid).then(()=>{
// switch video successful
}).catch(e=>{
// switch video failed
console.error(e);
})
}
}
function switchAudio(){
if(player){
// first arg bool false mean switch to screen audio , second ignore
// true mean switch to mic , second is mic deviceid
let ela = document.getElementById("audioDevice");
let adevid = JSON.parse(ela.options[ela.selectedIndex].value).deviceId
player.switcAudio(document.getElementById('switchDevice').checked,adevid).then(()=>{
// switch audio successful
}).catch(e=>{
// switch audio failed
console.error(e);
})
}
}
</script> </script>
</body> </body>