2019-08-08 19:01:45 +08:00
/*
2023-12-09 16:23:51 +08:00
* Copyright ( c ) 2016 - present The ZLMediaKit project authors . All Rights Reserved .
2017-09-27 16:20:30 +08:00
*
2023-12-09 16:23:51 +08:00
* This file is part of ZLMediaKit ( https : //github.com/ZLMediaKit/ZLMediaKit).
2017-09-27 16:20:30 +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 .
2017-04-01 16:35:56 +08:00
*/
2023-07-02 12:02:33 +08:00
# include <iomanip>
2019-04-01 12:57:33 +08:00
# include "HlsMaker.h"
2022-11-29 11:07:13 +08:00
# include "Common/config.h"
2022-02-02 20:34:50 +08:00
using namespace std ;
2018-10-24 17:17:55 +08:00
namespace mediakit {
2017-04-01 16:35:56 +08:00
2023-07-02 12:02:33 +08:00
HlsMaker : : HlsMaker ( bool is_fmp4 , float seg_duration , uint32_t seg_number , bool seg_keep ) {
_is_fmp4 = is_fmp4 ;
2024-09-19 14:53:50 +08:00
// 最小允许设置为0, 0个切片代表点播 [AUTO-TRANSLATED:19235e8e]
// Minimum allowed setting is 0, 0 slices represent on-demand
2019-04-01 12:57:33 +08:00
_seg_number = seg_number ;
_seg_duration = seg_duration ;
2022-05-10 17:32:50 +08:00
_seg_keep = seg_keep ;
2017-04-01 16:35:56 +08:00
}
2024-01-03 09:05:15 +08:00
void HlsMaker : : makeIndexFile ( bool include_delay , bool eof ) {
GET_CONFIG ( uint32_t , segDelay , Hls : : kSegmentDelay ) ;
GET_CONFIG ( uint32_t , segRetain , Hls : : kSegmentRetain ) ;
std : : deque < std : : tuple < int , std : : string > > temp ( _seg_dur_list ) ;
2024-04-20 20:00:25 +08:00
if ( ! include_delay & & _seg_number ) {
2024-01-03 09:05:15 +08:00
while ( temp . size ( ) > _seg_number ) {
temp . pop_front ( ) ;
}
}
2018-03-28 16:06:51 +08:00
int maxSegmentDuration = 0 ;
2024-01-03 09:05:15 +08:00
for ( auto & tp : temp ) {
2019-04-01 12:57:33 +08:00
int dur = std : : get < 0 > ( tp ) ;
if ( dur > maxSegmentDuration ) {
2017-11-09 18:14:16 +08:00
maxSegmentDuration = dur ;
}
}
2024-01-03 09:05:15 +08:00
uint64_t index_seq ;
if ( _seg_number ) {
if ( include_delay ) {
if ( _file_index > _seg_number + segDelay ) {
index_seq = _file_index - _seg_number - segDelay ;
} else {
index_seq = 0LL ;
}
} else {
if ( _file_index > _seg_number ) {
index_seq = _file_index - _seg_number ;
} else {
index_seq = 0LL ;
}
}
} else {
index_seq = 0LL ;
}
2019-09-26 14:21:20 +08:00
2023-07-02 12:02:33 +08:00
string index_str ;
index_str . reserve ( 2048 ) ;
index_str + = " #EXTM3U \n " ;
index_str + = ( _is_fmp4 ? " #EXT-X-VERSION:7 \n " : " #EXT-X-VERSION:4 \n " ) ;
if ( _seg_number = = 0 ) {
index_str + = " #EXT-X-PLAYLIST-TYPE:EVENT \n " ;
2021-12-15 16:43:49 +08:00
} else {
2023-07-02 12:02:33 +08:00
index_str + = " #EXT-X-ALLOW-CACHE:NO \n " ;
}
index_str + = " #EXT-X-TARGETDURATION: " + std : : to_string ( ( maxSegmentDuration + 999 ) / 1000 ) + " \n " ;
index_str + = " #EXT-X-MEDIA-SEQUENCE: " + std : : to_string ( index_seq ) + " \n " ;
if ( _is_fmp4 ) {
index_str + = " #EXT-X-MAP:URI= \" init.mp4 \" \n " ;
2021-12-15 16:43:49 +08:00
}
2019-09-26 14:21:20 +08:00
2023-07-02 12:02:33 +08:00
stringstream ss ;
2024-01-03 09:05:15 +08:00
for ( auto & tp : temp ) {
2023-07-02 12:02:33 +08:00
ss < < " #EXTINF: " < < std : : setprecision ( 3 ) < < std : : get < 0 > ( tp ) / 1000.0 < < " , \n " < < std : : get < 1 > ( tp ) < < " \n " ;
2018-03-28 15:56:47 +08:00
}
2023-07-02 12:02:33 +08:00
index_str + = ss . str ( ) ;
2018-11-17 16:34:31 +08:00
2019-04-01 12:57:33 +08:00
if ( eof ) {
2023-07-02 12:02:33 +08:00
index_str + = " #EXT-X-ENDLIST \n " ;
2019-04-01 12:57:33 +08:00
}
2024-01-03 09:05:15 +08:00
onWriteHls ( index_str , include_delay ) ;
2017-04-01 16:35:56 +08:00
}
2023-07-02 12:02:33 +08:00
void HlsMaker : : inputInitSegment ( const char * data , size_t len ) {
if ( ! _is_fmp4 ) {
throw std : : invalid_argument ( " Only fmp4-hls can input init segment " ) ;
}
onWriteInitSegment ( data , len ) ;
}
2017-04-01 16:35:56 +08:00
2023-07-02 12:02:33 +08:00
void HlsMaker : : inputData ( const char * data , size_t len , uint64_t timestamp , bool is_idr_fast_packet ) {
2019-10-11 16:51:10 +08:00
if ( data & & len ) {
2022-01-21 18:00:12 +08:00
if ( timestamp < _last_timestamp ) {
2024-09-19 14:53:50 +08:00
// 时间戳回退了,切片时长重新计时 [AUTO-TRANSLATED:fe91bd7f]
// Timestamp has been rolled back, slice duration is recalculated
2023-07-02 12:02:33 +08:00
WarnL < < " Timestamp reduce: " < < _last_timestamp < < " -> " < < timestamp ;
2022-01-21 18:00:12 +08:00
_last_seg_timestamp = _last_timestamp = timestamp ;
}
2020-09-12 20:49:00 +08:00
if ( is_idr_fast_packet ) {
2024-09-19 14:53:50 +08:00
// 尝试切片ts [AUTO-TRANSLATED:62264109]
// Attempt to slice ts
2020-01-24 22:00:55 +08:00
addNewSegment ( timestamp ) ;
}
2020-09-12 20:49:00 +08:00
if ( ! _last_file_name . empty ( ) ) {
2024-09-19 14:53:50 +08:00
// 存在切片才写入ts数据 [AUTO-TRANSLATED:ddd46115]
// Write ts data only if there are slices
2023-07-02 12:02:33 +08:00
onWriteSegment ( data , len ) ;
2020-09-20 10:56:54 +08:00
_last_timestamp = timestamp ;
2020-09-12 20:49:00 +08:00
}
2019-10-11 17:51:46 +08:00
} else {
2024-09-19 14:53:50 +08:00
// resetTracks时触发此逻辑 [AUTO-TRANSLATED:0ba915ed]
// This logic is triggered when resetTracks is called
2022-02-13 21:06:32 +08:00
flushLastSegment ( false ) ;
2019-10-11 16:51:10 +08:00
}
2017-04-01 16:35:56 +08:00
}
2019-10-12 10:29:40 +08:00
void HlsMaker : : delOldSegment ( ) {
2024-01-03 09:05:15 +08:00
GET_CONFIG ( uint32_t , segDelay , Hls : : kSegmentDelay ) ;
2020-09-12 20:49:00 +08:00
if ( _seg_number = = 0 ) {
2024-09-19 14:53:50 +08:00
// 如果设置为保留0个切片, 则认为是保存为点播 [AUTO-TRANSLATED:5bf20108]
// If set to keep 0 slices, it is considered to be saved as on-demand
2019-09-26 14:21:20 +08:00
return ;
}
2024-09-19 14:53:50 +08:00
// 在hls m3u8索引文件中,我们保存的切片个数跟_seg_number相关设置一致 [AUTO-TRANSLATED:b14b5b98]
// In the hls m3u8 index file, the number of slices we save is consistent with the _seg_number setting
2024-01-03 09:05:15 +08:00
if ( _file_index > _seg_number + segDelay ) {
2019-04-01 12:57:33 +08:00
_seg_dur_list . pop_front ( ) ;
}
2024-09-19 14:53:50 +08:00
// 如果设置为一直保存,就不删除 [AUTO-TRANSLATED:7c622e24]
// If set to always save, it will not be deleted
2022-05-10 17:32:50 +08:00
if ( _seg_keep ) {
return ;
}
2020-09-12 20:49:00 +08:00
GET_CONFIG ( uint32_t , segRetain , Hls : : kSegmentRetain ) ;
2024-09-19 14:53:50 +08:00
// 但是实际保存的切片个数比m3u8所述多若干个,这样做的目的是防止播放器在切片删除前能下载完毕 [AUTO-TRANSLATED:1688f857]
// However, the actual number of slices saved is a few more than what is stated in the m3u8, this is done to prevent the player from downloading the slices before they are deleted
2024-01-03 09:05:15 +08:00
if ( _file_index > _seg_number + segDelay + segRetain ) {
onDelSegment ( _file_index - _seg_number - segDelay - segRetain - 1 ) ;
2019-04-01 12:57:33 +08:00
}
2018-10-28 00:15:27 +08:00
}
2022-08-08 17:13:39 +08:00
void HlsMaker : : addNewSegment ( uint64_t stamp ) {
2023-11-30 17:47:22 +08:00
GET_CONFIG ( bool , fastRegister , Hls : : kFastRegister ) ;
if ( _file_index > fastRegister & & stamp - _last_seg_timestamp < _seg_duration * 1000 ) {
2024-09-19 14:53:50 +08:00
// 确保序号为0的切片立即open, 如果开启快速注册功能, 序号为1的切片也应该遇到关键帧立即生成; 否则需要等切片时长够长 [AUTO-TRANSLATED:d81d1a1c]
// Ensure that the slice with sequence number 0 is opened immediately, if the fast registration function is enabled, the slice with sequence number 1 should also be generated immediately when it encounters a keyframe; otherwise, it needs to wait until the slice duration is long enough
2019-10-12 10:29:40 +08:00
return ;
2019-10-11 19:01:34 +08:00
}
2024-09-19 14:53:50 +08:00
// 关闭并保存上一个切片, 如果_seg_number==0,那么是点播。 [AUTO-TRANSLATED:14076b61]
// Close and save the previous slice, if _seg_number==0, then it is on-demand.
2021-12-15 16:43:49 +08:00
flushLastSegment ( false ) ;
2024-09-19 14:53:50 +08:00
// 新增切片 [AUTO-TRANSLATED:b8623419]
// Add a new slice
2019-10-12 10:29:40 +08:00
_last_file_name = onOpenSegment ( _file_index + + ) ;
2024-09-19 14:53:50 +08:00
// 记录本次切片的起始时间戳 [AUTO-TRANSLATED:8eb776e9]
// Record the starting timestamp of this slice
2021-07-07 11:51:44 +08:00
_last_seg_timestamp = _last_timestamp ? _last_timestamp : stamp ;
2019-10-12 10:29:40 +08:00
}
2020-09-20 10:56:54 +08:00
void HlsMaker : : flushLastSegment ( bool eof ) {
2024-01-03 09:05:15 +08:00
GET_CONFIG ( uint32_t , segDelay , Hls : : kSegmentDelay ) ;
2020-09-12 20:49:00 +08:00
if ( _last_file_name . empty ( ) ) {
2024-09-19 14:53:50 +08:00
// 不存在上个切片 [AUTO-TRANSLATED:d81fe08e]
// There is no previous slice
2019-10-12 10:29:40 +08:00
return ;
}
2024-09-19 14:53:50 +08:00
// 文件创建到最后一次数据写入的时间即为切片长度 [AUTO-TRANSLATED:1f85739c]
// The time from file creation to the last data write is the slice length
2020-09-20 10:56:54 +08:00
auto seg_dur = _last_timestamp - _last_seg_timestamp ;
2020-09-12 20:49:00 +08:00
if ( seg_dur < = 0 ) {
2019-10-12 10:29:40 +08:00
seg_dur = 100 ;
2019-04-01 12:57:33 +08:00
}
2022-04-05 19:32:02 +08:00
_seg_dur_list . emplace_back ( seg_dur , std : : move ( _last_file_name ) ) ;
2019-10-12 10:29:40 +08:00
delOldSegment ( ) ;
2024-09-19 14:53:50 +08:00
// 先flush ts切片, 否则可能存在ts文件未写入完毕就被访问的情况 [AUTO-TRANSLATED:f8d6dc87]
// Flush the ts slice first, otherwise there may be a situation where the ts file is not written completely before it is accessed
2020-09-13 14:08:05 +08:00
onFlushLastSegment ( seg_dur ) ;
2024-09-19 14:53:50 +08:00
// 然后写m3u8文件 [AUTO-TRANSLATED:67200ce1]
// Then write the m3u8 file
2024-01-03 09:05:15 +08:00
makeIndexFile ( false , eof ) ;
2024-09-19 14:53:50 +08:00
// 写入切片延迟的m3u8文件 [AUTO-TRANSLATED:b1f12e43]
// Write the m3u8 file with slice delay
2024-01-03 09:05:15 +08:00
if ( segDelay ) {
makeIndexFile ( true , eof ) ;
}
2020-09-13 14:08:05 +08:00
}
2023-07-02 12:02:33 +08:00
bool HlsMaker : : isLive ( ) const {
2019-12-17 09:18:11 +08:00
return _seg_number ! = 0 ;
}
2023-07-02 12:02:33 +08:00
bool HlsMaker : : isKeep ( ) const {
2022-05-10 17:32:50 +08:00
return _seg_keep ;
}
2023-07-02 12:02:33 +08:00
bool HlsMaker : : isFmp4 ( ) const {
return _is_fmp4 ;
}
2020-09-12 20:49:00 +08:00
void HlsMaker : : clear ( ) {
_file_index = 0 ;
2022-02-13 21:06:32 +08:00
_last_timestamp = 0 ;
2020-09-12 20:49:00 +08:00
_last_seg_timestamp = 0 ;
2020-09-12 19:20:18 +08:00
_seg_dur_list . clear ( ) ;
_last_file_name . clear ( ) ;
}
2021-12-15 16:43:49 +08:00
} //namespace mediakit