2020-01-07 15:10:59 +08:00
/*
2023-12-09 16:23:51 +08:00
* Copyright ( c ) 2016 - present The ZLMediaKit project authors . All Rights Reserved .
2019-06-11 09:25:54 +08:00
*
2023-12-09 16:23:51 +08:00
* This file is part of ZLMediaKit ( https : //github.com/ZLMediaKit/ZLMediaKit).
2019-06-11 09:25:54 +08:00
*
2023-12-09 16:23:51 +08:00
* Use of this source code is governed by MIT - like license that can be found in the
2020-04-04 20:30:09 +08:00
* LICENSE file in the root of the source tree . All contributing project authors
* may be found in the AUTHORS file in the root of the source tree .
2019-06-11 09:25:54 +08:00
*/
2019-06-06 18:28:33 +08:00
# include "FFmpegSource.h"
# include "Common/config.h"
# include "Common/MediaSource.h"
2023-07-08 21:35:09 +08:00
# include "Common/MultiMediaSourceMuxer.h"
2019-06-06 18:28:33 +08:00
# include "Util/File.h"
# include "System.h"
2020-05-09 00:06:36 +08:00
# include "Thread/WorkThreadPool.h"
2020-08-15 09:49:56 +08:00
# include "Network/sockutil.h"
2019-06-06 18:28:33 +08:00
2022-02-02 20:34:50 +08:00
using namespace std ;
using namespace toolkit ;
using namespace mediakit ;
2019-06-06 18:28:33 +08:00
namespace FFmpeg {
# define FFmpeg_FIELD "ffmpeg."
2020-01-07 14:37:18 +08:00
const string kBin = FFmpeg_FIELD " bin " ;
const string kCmd = FFmpeg_FIELD " cmd " ;
const string kLog = FFmpeg_FIELD " log " ;
2020-05-09 00:06:36 +08:00
const string kSnap = FFmpeg_FIELD " snap " ;
2022-01-09 14:51:54 +08:00
const string kRestartSec = FFmpeg_FIELD " restart_sec " ;
2019-06-06 18:28:33 +08:00
onceToken token ( [ ] ( ) {
2020-01-07 14:37:18 +08:00
# ifdef _WIN32
2020-06-08 14:56:17 +08:00
string ffmpeg_bin = trim ( System : : execute ( " where ffmpeg " ) ) ;
2020-01-07 14:37:18 +08:00
# else
2020-06-08 14:56:17 +08:00
string ffmpeg_bin = trim ( System : : execute ( " which ffmpeg " ) ) ;
2020-01-07 14:37:18 +08:00
# endif
2024-09-19 14:53:50 +08:00
// 默认ffmpeg命令路径为环境变量中路径 [AUTO-TRANSLATED:40c35597]
// Default ffmpeg command path is the path in the environment variable
2020-01-07 14:37:18 +08:00
mINI : : Instance ( ) [ kBin ] = ffmpeg_bin . empty ( ) ? " ffmpeg " : ffmpeg_bin ;
2024-09-19 14:53:50 +08:00
// ffmpeg日志保存路径 [AUTO-TRANSLATED:e455732d]
// ffmpeg log save path
2019-10-18 09:51:20 +08:00
mINI : : Instance ( ) [ kLog ] = " ./ffmpeg/ffmpeg.log " ;
2020-06-08 14:56:17 +08:00
mINI : : Instance ( ) [ kCmd ] = " %s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s " ;
2024-01-26 10:19:19 +08:00
mINI : : Instance ( ) [ kSnap ] = " %s -i %s -y -f mjpeg -frames:v 1 -an %s " ;
2022-01-09 14:51:54 +08:00
mINI : : Instance ( ) [ kRestartSec ] = 0 ;
2019-06-06 18:28:33 +08:00
} ) ;
}
FFmpegSource : : FFmpegSource ( ) {
_poller = EventPollerPool : : Instance ( ) . getPoller ( ) ;
}
FFmpegSource : : ~ FFmpegSource ( ) {
DebugL ;
}
2020-08-15 09:49:56 +08:00
static bool is_local_ip ( const string & ip ) {
if ( ip = = " 127.0.0.1 " | | ip = = " localhost " ) {
return true ;
}
auto ips = SockUtil : : getInterfaceList ( ) ;
for ( auto & obj : ips ) {
if ( ip = = obj [ " ip " ] ) {
return true ;
}
}
return false ;
}
2019-06-06 18:28:33 +08:00
2021-01-17 20:15:08 +08:00
void FFmpegSource : : setupRecordFlag ( bool enable_hls , bool enable_mp4 ) {
2020-12-20 20:25:44 +08:00
_enable_hls = enable_hls ;
_enable_mp4 = enable_mp4 ;
}
2023-07-08 21:35:09 +08:00
void FFmpegSource : : play ( const string & ffmpeg_cmd_key , const string & src_url , const string & dst_url , int timeout_ms , const onPlay & cb ) {
GET_CONFIG ( string , ffmpeg_bin , FFmpeg : : kBin ) ;
GET_CONFIG ( string , ffmpeg_cmd_default , FFmpeg : : kCmd ) ;
GET_CONFIG ( string , ffmpeg_log , FFmpeg : : kLog ) ;
2019-06-06 18:28:33 +08:00
_src_url = src_url ;
_dst_url = dst_url ;
2021-01-23 09:42:15 +08:00
_ffmpeg_cmd_key = ffmpeg_cmd_key ;
2023-03-14 19:38:24 +08:00
try {
_media_info . parse ( dst_url ) ;
} catch ( std : : exception & ex ) {
cb ( SockException ( Err_other , ex . what ( ) ) ) ;
return ;
}
2019-06-06 18:28:33 +08:00
2021-01-23 09:42:15 +08:00
auto ffmpeg_cmd = ffmpeg_cmd_default ;
if ( ! ffmpeg_cmd_key . empty ( ) ) {
auto cmd_it = mINI : : Instance ( ) . find ( ffmpeg_cmd_key ) ;
if ( cmd_it ! = mINI : : Instance ( ) . end ( ) ) {
ffmpeg_cmd = cmd_it - > second ;
2023-07-08 21:35:09 +08:00
} else {
2021-01-23 09:42:15 +08:00
WarnL < < " 配置文件中,ffmpeg命令模板( " < < ffmpeg_cmd_key < < " )不存在,已采用默认模板( " < < ffmpeg_cmd_default < < " ) " ;
}
}
2023-07-08 21:35:09 +08:00
char cmd [ 2048 ] = { 0 } ;
2022-02-24 11:28:48 +08:00
snprintf ( cmd , sizeof ( cmd ) , ffmpeg_cmd . data ( ) , File : : absolutePath ( " " , ffmpeg_bin ) . data ( ) , src_url . data ( ) , dst_url . data ( ) ) ;
auto log_file = ffmpeg_log . empty ( ) ? " " : File : : absolutePath ( " " , ffmpeg_log ) ;
_process . run ( cmd , log_file ) ;
2024-10-30 09:45:26 +08:00
_cmd = cmd ;
2019-06-06 18:28:33 +08:00
InfoL < < cmd ;
2023-05-25 16:23:24 +08:00
if ( is_local_ip ( _media_info . host ) ) {
2024-09-19 14:53:50 +08:00
// 推流给自己的,通过判断流是否注册上来判断是否正常 [AUTO-TRANSLATED:423f2be6]
// Push stream to yourself, judge whether the stream is registered to determine whether it is normal
2023-05-25 16:23:24 +08:00
if ( _media_info . schema ! = RTSP_SCHEMA & & _media_info . schema ! = RTMP_SCHEMA ) {
2023-07-08 21:35:09 +08:00
cb ( SockException ( Err_other , " 本服务只支持rtmp/rtsp推流 " ) ) ;
2019-06-06 18:28:33 +08:00
return ;
}
weak_ptr < FFmpegSource > weakSelf = shared_from_this ( ) ;
2023-07-08 21:35:09 +08:00
findAsync ( timeout_ms , [ cb , weakSelf , timeout_ms ] ( const MediaSource : : Ptr & src ) {
2019-06-06 18:28:33 +08:00
auto strongSelf = weakSelf . lock ( ) ;
2023-07-08 21:35:09 +08:00
if ( ! strongSelf ) {
2024-09-19 14:53:50 +08:00
// 自己已经销毁 [AUTO-TRANSLATED:3d45c3b0]
// Self has been destroyed
2019-06-06 18:28:33 +08:00
return ;
}
2023-07-08 21:35:09 +08:00
if ( src ) {
2024-09-19 14:53:50 +08:00
// 推流给自己成功 [AUTO-TRANSLATED:65dba71b]
// Push stream to yourself successfully
2019-06-06 18:28:33 +08:00
cb ( SockException ( ) ) ;
2019-11-18 12:07:11 +08:00
strongSelf - > onGetMediaSource ( src ) ;
2019-06-06 18:28:33 +08:00
strongSelf - > startTimer ( timeout_ms ) ;
return ;
}
2024-09-19 14:53:50 +08:00
// 推流失败 [AUTO-TRANSLATED:4d8d226a]
// Push stream failed
2023-07-08 21:35:09 +08:00
if ( ! strongSelf - > _process . wait ( false ) ) {
2024-09-19 14:53:50 +08:00
// ffmpeg进程已经退出 [AUTO-TRANSLATED:04193893]
// ffmpeg process has exited
2023-07-08 21:35:09 +08:00
cb ( SockException ( Err_other , StrPrinter < < " ffmpeg已经退出,exit code = " < < strongSelf - > _process . exit_code ( ) ) ) ;
2019-06-06 18:28:33 +08:00
return ;
}
2024-09-19 14:53:50 +08:00
// ffmpeg进程还在线, 但是等待推流超时 [AUTO-TRANSLATED:9f71f17b]
// ffmpeg process is still online, but waiting for the stream to timeout
2023-07-08 21:35:09 +08:00
cb ( SockException ( Err_other , " 等待超时 " ) ) ;
2019-06-06 18:28:33 +08:00
} ) ;
} else {
2024-09-19 14:53:50 +08:00
// 推流给其他服务器的, 通过判断FFmpeg进程是否在线判断是否成功 [AUTO-TRANSLATED:9b963da5]
// Push stream to other servers, judge whether it is successful by judging whether the FFmpeg process is online
2019-06-06 18:28:33 +08:00
weak_ptr < FFmpegSource > weakSelf = shared_from_this ( ) ;
2023-07-08 21:35:09 +08:00
_timer = std : : make_shared < Timer > ( timeout_ms / 1000.0f , [ weakSelf , cb , timeout_ms ] ( ) {
2019-06-06 18:28:33 +08:00
auto strongSelf = weakSelf . lock ( ) ;
2023-07-08 21:35:09 +08:00
if ( ! strongSelf ) {
2024-09-19 14:53:50 +08:00
// 自身已经销毁 [AUTO-TRANSLATED:5f954f8a]
// Self has been destroyed
2019-06-06 18:28:33 +08:00
return false ;
}
2024-09-19 14:53:50 +08:00
// FFmpeg还在线, 那么我们认为推流成功 [AUTO-TRANSLATED:4330df49]
// FFmpeg is still online, so we think the push stream is successful
2023-07-08 21:35:09 +08:00
if ( strongSelf - > _process . wait ( false ) ) {
2019-06-06 18:28:33 +08:00
cb ( SockException ( ) ) ;
strongSelf - > startTimer ( timeout_ms ) ;
return false ;
}
2024-09-19 14:53:50 +08:00
// ffmpeg进程已经退出 [AUTO-TRANSLATED:04193893]
// ffmpeg process has exited
2023-07-08 21:35:09 +08:00
cb ( SockException ( Err_other , StrPrinter < < " ffmpeg已经退出,exit code = " < < strongSelf - > _process . exit_code ( ) ) ) ;
2019-06-06 18:28:33 +08:00
return false ;
2023-07-08 21:35:09 +08:00
} , _poller ) ;
2019-06-06 18:28:33 +08:00
}
}
void FFmpegSource : : findAsync ( int maxWaitMS , const function < void ( const MediaSource : : Ptr & src ) > & cb ) {
2023-07-08 21:35:09 +08:00
auto src = MediaSource : : find ( _media_info . schema , _media_info . vhost , _media_info . app , _media_info . stream ) ;
if ( src | | ! maxWaitMS ) {
2019-06-06 18:28:33 +08:00
cb ( src ) ;
return ;
}
void * listener_tag = this ;
2024-09-19 14:53:50 +08:00
// 若干秒后执行等待媒体注册超时回调 [AUTO-TRANSLATED:71010a04]
// Execute the media registration timeout callback after a few seconds
2023-07-08 21:35:09 +08:00
auto onRegistTimeout = _poller - > doDelayTask ( maxWaitMS , [ cb , listener_tag ] ( ) {
2024-09-19 14:53:50 +08:00
// 取消监听该事件 [AUTO-TRANSLATED:31297323]
// Cancel listening to this event
2023-07-08 21:35:09 +08:00
NoticeCenter : : Instance ( ) . delListener ( listener_tag , Broadcast : : kBroadcastMediaChanged ) ;
2019-06-06 18:28:33 +08:00
cb ( nullptr ) ;
return 0 ;
} ) ;
weak_ptr < FFmpegSource > weakSelf = shared_from_this ( ) ;
2023-07-08 21:35:09 +08:00
auto onRegist = [ listener_tag , weakSelf , cb , onRegistTimeout ] ( BroadcastMediaChangedArgs ) {
2019-06-06 18:28:33 +08:00
auto strongSelf = weakSelf . lock ( ) ;
2023-07-08 21:35:09 +08:00
if ( ! strongSelf ) {
2024-09-19 14:53:50 +08:00
// 本身已经销毁,取消延时任务 [AUTO-TRANSLATED:cc2e420f]
// Self has been destroyed, cancel the delayed task
2019-06-06 18:28:33 +08:00
onRegistTimeout - > cancel ( ) ;
2023-07-08 21:35:09 +08:00
NoticeCenter : : Instance ( ) . delListener ( listener_tag , Broadcast : : kBroadcastMediaChanged ) ;
2019-06-06 18:28:33 +08:00
return ;
}
2023-07-08 21:35:09 +08:00
if ( ! bRegist | | sender . getSchema ( ) ! = strongSelf - > _media_info . schema | |
2023-06-17 10:28:01 +08:00
! equalMediaTuple ( sender . getMediaTuple ( ) , strongSelf - > _media_info ) ) {
2024-09-19 14:53:50 +08:00
// 不是自己感兴趣的事件,忽略之 [AUTO-TRANSLATED:f61f5668]
// Not an event of interest, ignore it
2019-06-06 18:28:33 +08:00
return ;
}
2024-09-19 14:53:50 +08:00
// 查找的流终于注册上了;取消延时任务,防止多次回调 [AUTO-TRANSLATED:66fc5abf]
// The stream you are looking for is finally registered; cancel the delayed task to prevent multiple callbacks
2019-06-06 18:28:33 +08:00
onRegistTimeout - > cancel ( ) ;
2024-09-19 14:53:50 +08:00
// 取消事件监听 [AUTO-TRANSLATED:c722acb6]
// Cancel event listening
2023-07-08 21:35:09 +08:00
NoticeCenter : : Instance ( ) . delListener ( listener_tag , Broadcast : : kBroadcastMediaChanged ) ;
2024-09-19 14:53:50 +08:00
// 切换到自己的线程再回复 [AUTO-TRANSLATED:3b630c64]
// Switch to your own thread and then reply
2023-07-08 21:35:09 +08:00
strongSelf - > _poller - > async ( [ weakSelf , cb ] ( ) {
if ( auto strongSelf = weakSelf . lock ( ) ) {
2024-09-19 14:53:50 +08:00
// 再找一遍媒体源,一般能找到 [AUTO-TRANSLATED:f0b81977]
// Find the media source again, usually you can find it
2023-07-08 21:35:09 +08:00
strongSelf - > findAsync ( 0 , cb ) ;
2019-06-06 18:28:33 +08:00
}
} , false ) ;
} ;
2024-09-19 14:53:50 +08:00
// 监听媒体注册事件 [AUTO-TRANSLATED:ea3e763b]
// Listen to media registration events
2019-06-06 18:28:33 +08:00
NoticeCenter : : Instance ( ) . addListener ( listener_tag , Broadcast : : kBroadcastMediaChanged , onRegist ) ;
}
/**
* 定 时 检 查 媒 体 是 否 在 线
2024-09-19 14:53:50 +08:00
* Check if the media is online regularly
* [ AUTO - TRANSLATED : 11 bae8ab ]
2019-06-06 18:28:33 +08:00
*/
void FFmpegSource : : startTimer ( int timeout_ms ) {
weak_ptr < FFmpegSource > weakSelf = shared_from_this ( ) ;
2022-01-09 14:51:54 +08:00
GET_CONFIG ( uint64_t , ffmpeg_restart_sec , FFmpeg : : kRestartSec ) ;
2021-01-17 18:31:50 +08:00
_timer = std : : make_shared < Timer > ( 1.0f , [ weakSelf , timeout_ms ] ( ) {
2019-06-06 18:28:33 +08:00
auto strongSelf = weakSelf . lock ( ) ;
if ( ! strongSelf ) {
2024-09-19 14:53:50 +08:00
// 自身已经销毁 [AUTO-TRANSLATED:5a02ef8b]
// Self has been destroyed
2019-06-06 18:28:33 +08:00
return false ;
}
2022-01-09 14:51:54 +08:00
bool needRestart = ffmpeg_restart_sec > 0 & & strongSelf - > _replay_ticker . elapsedTime ( ) > ffmpeg_restart_sec * 1000 ;
2023-05-25 16:23:24 +08:00
if ( is_local_ip ( strongSelf - > _media_info . host ) ) {
2024-09-19 14:53:50 +08:00
// 推流给自己的, 我们通过检查是否已经注册来判断FFmpeg是否工作正常 [AUTO-TRANSLATED:9a441d38]
// Push stream to yourself, we judge whether FFmpeg is working properly by checking whether it has been registered
2019-06-06 18:28:33 +08:00
strongSelf - > findAsync ( 0 , [ & ] ( const MediaSource : : Ptr & src ) {
2024-09-19 14:53:50 +08:00
// 同步查找流 [AUTO-TRANSLATED:97048f1e]
// Synchronously find the stream
2022-01-09 14:51:54 +08:00
if ( ! src | | needRestart ) {
2023-07-08 21:35:09 +08:00
if ( needRestart ) {
2022-01-09 14:51:54 +08:00
strongSelf - > _replay_ticker . resetTime ( ) ;
2023-07-08 21:35:09 +08:00
if ( strongSelf - > _process . wait ( false ) ) {
2024-09-19 14:53:50 +08:00
// FFmpeg进程还在运行, 超时就关闭它 [AUTO-TRANSLATED:bd907d0c]
// The FFmpeg process is still running, timeout and close it
2022-01-09 14:51:54 +08:00
strongSelf - > _process . kill ( 2000 ) ;
}
InfoL < < " FFmpeg即将重启, 将会继续拉流 " < < strongSelf - > _src_url ;
}
2024-09-19 14:53:50 +08:00
// 流不在线,重新拉流, 这里原先是10秒超时, 实际发现10秒不够, 改成20秒了 [AUTO-TRANSLATED:10e8c704]
// The stream is not online, re-pull the stream, here the original timeout was 10 seconds, but it was found that 10 seconds was not enough, so it was changed to 20 seconds
2023-07-08 21:35:09 +08:00
if ( strongSelf - > _replay_ticker . elapsedTime ( ) > 20 * 1000 ) {
2024-09-19 14:53:50 +08:00
// 上次重试时间超过10秒, 那么再重试FFmpeg拉流 [AUTO-TRANSLATED:b308095a]
// The last retry time exceeds 10 seconds, then retry FFmpeg to pull the stream
2019-12-29 17:14:23 +08:00
strongSelf - > _replay_ticker . resetTime ( ) ;
2021-01-23 09:42:15 +08:00
strongSelf - > play ( strongSelf - > _ffmpeg_cmd_key , strongSelf - > _src_url , strongSelf - > _dst_url , timeout_ms , [ ] ( const SockException & ) { } ) ;
2019-12-29 17:14:23 +08:00
}
2019-06-06 18:28:33 +08:00
}
} ) ;
} else {
2024-09-19 14:53:50 +08:00
// 推流给其他服务器的, 我们通过判断FFmpeg进程是否在线, 如果FFmpeg推流中断, 那么它应该会自动退出 [AUTO-TRANSLATED:82da3ea5]
// Push stream to other servers, we judge whether the FFmpeg process is online, if FFmpeg push stream is interrupted, then it should exit automatically
2022-01-09 14:51:54 +08:00
if ( ! strongSelf - > _process . wait ( false ) | | needRestart ) {
2023-07-08 21:35:09 +08:00
if ( needRestart ) {
2022-01-09 14:51:54 +08:00
strongSelf - > _replay_ticker . resetTime ( ) ;
2023-07-08 21:35:09 +08:00
if ( strongSelf - > _process . wait ( false ) ) {
2024-09-19 14:53:50 +08:00
// FFmpeg进程还在运行, 超时就关闭它 [AUTO-TRANSLATED:bd907d0c]
// The FFmpeg process is still running, timeout and close it
2022-01-09 14:51:54 +08:00
strongSelf - > _process . kill ( 2000 ) ;
}
InfoL < < " FFmpeg即将重启, 将会继续拉流 " < < strongSelf - > _src_url ;
}
2024-09-19 14:53:50 +08:00
// ffmpeg不在线, 重新拉流 [AUTO-TRANSLATED:aa958c43]
// ffmpeg is not online, re-pull the stream
2021-01-23 09:42:15 +08:00
strongSelf - > play ( strongSelf - > _ffmpeg_cmd_key , strongSelf - > _src_url , strongSelf - > _dst_url , timeout_ms , [ weakSelf ] ( const SockException & ex ) {
2023-07-08 21:35:09 +08:00
if ( ! ex ) {
2024-09-19 14:53:50 +08:00
// 没有错误 [AUTO-TRANSLATED:037ae0ca]
// No error
2020-06-08 15:51:12 +08:00
return ;
}
auto strongSelf = weakSelf . lock ( ) ;
if ( ! strongSelf ) {
2024-09-19 14:53:50 +08:00
// 自身已经销毁 [AUTO-TRANSLATED:5f954f8a]
// Self has been destroyed
2020-06-08 15:51:12 +08:00
return ;
}
2024-09-19 14:53:50 +08:00
// 上次重试时间超过10秒, 那么再重试FFmpeg拉流 [AUTO-TRANSLATED:b308095a]
// Retry FFmpeg stream pulling if the last retry time is over 10 seconds
2020-06-08 15:51:12 +08:00
strongSelf - > startTimer ( 10 * 1000 ) ;
} ) ;
2019-06-06 18:28:33 +08:00
}
}
return true ;
} , _poller ) ;
2019-11-18 12:07:11 +08:00
}
2019-06-06 18:28:33 +08:00
2019-11-18 12:07:11 +08:00
void FFmpegSource : : setOnClose ( const function < void ( ) > & cb ) {
_onClose = cb ;
}
2019-06-06 18:28:33 +08:00
2022-09-18 20:36:47 +08:00
bool FFmpegSource : : close ( MediaSource & sender ) {
2020-10-24 23:31:58 +08:00
auto listener = getDelegate ( ) ;
2022-09-18 20:36:47 +08:00
if ( listener & & ! listener - > close ( sender ) ) {
2024-09-19 14:53:50 +08:00
// 关闭失败 [AUTO-TRANSLATED:83f07dba]
// Close failed
2019-11-18 12:07:11 +08:00
return false ;
}
2024-09-19 14:53:50 +08:00
// 该流无人观看,我们停止吧 [AUTO-TRANSLATED:43999b39]
// No one is watching this stream, let's stop it
2022-09-18 20:36:47 +08:00
if ( _onClose ) {
2019-11-18 12:07:11 +08:00
_onClose ( ) ;
}
return true ;
}
2019-06-06 18:28:33 +08:00
2020-09-27 11:32:49 +08:00
MediaOriginType FFmpegSource : : getOriginType ( MediaSource & sender ) const {
return MediaOriginType : : ffmpeg_pull ;
}
2023-07-08 21:35:09 +08:00
string FFmpegSource : : getOriginUrl ( MediaSource & sender ) const {
2020-09-27 11:32:49 +08:00
return _src_url ;
}
2019-11-18 12:07:11 +08:00
void FFmpegSource : : onGetMediaSource ( const MediaSource : : Ptr & src ) {
2023-07-08 21:35:09 +08:00
auto muxer = src - > getMuxer ( ) ;
auto listener = muxer ? muxer - > getDelegate ( ) : nullptr ;
if ( listener & & listener . get ( ) ! = this ) {
2024-09-19 14:53:50 +08:00
// 防止多次进入onGetMediaSource函数导致无限递归调用的bug [AUTO-TRANSLATED:ceadb9c7]
// Prevent the bug of infinite recursive calls caused by entering the onGetMediaSource function multiple times
2020-10-24 23:31:58 +08:00
setDelegate ( listener ) ;
2023-07-08 21:35:09 +08:00
muxer - > setDelegate ( shared_from_this ( ) ) ;
2020-12-20 20:25:44 +08:00
if ( _enable_hls ) {
2021-03-07 10:41:57 +08:00
src - > setupRecord ( Recorder : : type_hls , true , " " , 0 ) ;
2020-12-20 20:25:44 +08:00
}
if ( _enable_mp4 ) {
2021-03-07 10:41:57 +08:00
src - > setupRecord ( Recorder : : type_mp4 , true , " " , 0 ) ;
2020-12-20 20:25:44 +08:00
}
2020-08-30 17:03:58 +08:00
}
2019-11-18 12:07:11 +08:00
}
2020-05-09 00:06:36 +08:00
2022-02-24 11:28:48 +08:00
void FFmpegSnap : : makeSnap ( const string & play_url , const string & save_path , float timeout_sec , const onSnap & cb ) {
2023-07-08 21:35:09 +08:00
GET_CONFIG ( string , ffmpeg_bin , FFmpeg : : kBin ) ;
GET_CONFIG ( string , ffmpeg_snap , FFmpeg : : kSnap ) ;
GET_CONFIG ( string , ffmpeg_log , FFmpeg : : kLog ) ;
2020-10-01 19:01:48 +08:00
Ticker ticker ;
2023-07-08 21:35:09 +08:00
WorkThreadPool : : Instance ( ) . getPoller ( ) - > async ( [ timeout_sec , play_url , save_path , cb , ticker ] ( ) {
2020-10-01 19:01:48 +08:00
auto elapsed_ms = ticker . elapsedTime ( ) ;
if ( elapsed_ms > timeout_sec * 1000 ) {
2024-09-19 14:53:50 +08:00
// 超时,后台线程负载太高,当代太久才启动该任务 [AUTO-TRANSLATED:815606d6]
// Timeout, the background thread load is too high, it takes too long to start this task
2022-02-24 11:28:48 +08:00
cb ( false , " wait work poller schedule snap task timeout " ) ;
2020-10-01 19:01:48 +08:00
return ;
2020-05-09 00:06:36 +08:00
}
2022-02-24 11:28:48 +08:00
char cmd [ 2048 ] = { 0 } ;
snprintf ( cmd , sizeof ( cmd ) , ffmpeg_snap . data ( ) , File : : absolutePath ( " " , ffmpeg_bin ) . data ( ) , play_url . data ( ) , save_path . data ( ) ) ;
2020-10-01 19:01:48 +08:00
std : : shared_ptr < Process > process = std : : make_shared < Process > ( ) ;
2022-02-24 11:28:48 +08:00
auto log_file = ffmpeg_log . empty ( ) ? ffmpeg_log : File : : absolutePath ( " " , ffmpeg_log ) ;
process - > run ( cmd , log_file ) ;
2024-09-19 14:53:50 +08:00
// 定时器延时应该减去后台任务启动的延时 [AUTO-TRANSLATED:7d224687]
// The timer delay should be reduced by the delay of the background task startup
2022-02-24 11:28:48 +08:00
auto delayTask = EventPollerPool : : Instance ( ) . getPoller ( ) - > doDelayTask (
( uint64_t ) ( timeout_sec * 1000 - elapsed_ms ) , [ process , cb , log_file , save_path ] ( ) {
if ( process - > wait ( false ) ) {
2024-09-19 14:53:50 +08:00
// FFmpeg进程还在运行, 超时就关闭它 [AUTO-TRANSLATED:bd907d0c]
// The FFmpeg process is still running, close it if it times out
2022-02-24 11:28:48 +08:00
process - > kill ( 2000 ) ;
}
return 0 ;
} ) ;
2020-10-01 19:01:48 +08:00
2024-09-19 14:53:50 +08:00
// 等待FFmpeg进程退出 [AUTO-TRANSLATED:0a179187]
// Wait for the FFmpeg process to exit
2020-05-09 00:06:36 +08:00
process - > wait ( true ) ;
2024-09-19 14:53:50 +08:00
// FFmpeg进程退出了可以取消定时器了 [AUTO-TRANSLATED:c8a4b513]
// The FFmpeg process has exited, the timer can be canceled
2020-05-09 00:06:36 +08:00
delayTask - > cancel ( ) ;
2024-09-19 14:53:50 +08:00
// 执行回调函数 [AUTO-TRANSLATED:7309a900]
// Execute the callback function
2023-12-02 19:49:28 +08:00
bool success = process - > exit_code ( ) = = 0 & & File : : fileSize ( save_path ) ;
cb ( success , ( ! success & & ! log_file . empty ( ) ) ? File : : loadFile ( log_file ) : " " ) ;
2020-05-09 00:06:36 +08:00
} ) ;
}