HlsMaker.cpp 4.03 KB
Newer Older
xiongziliang committed
1
/*
xiongziliang committed
2
 * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
xiongziliang committed
3 4 5
 *
 * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
 *
xiongziliang committed
6 7 8
 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
xzl committed
9 10
 */

11
#include "HlsMaker.h"
xiongziliang committed
12
namespace mediakit {
xzl committed
13

14
HlsMaker::HlsMaker(float seg_duration, uint32_t seg_number) {
xiongziliang committed
15
    //最小允许设置为0,0个切片代表点播
16 17
    _seg_number = seg_number;
    _seg_duration = seg_duration;
xzl committed
18 19
}

20
HlsMaker::~HlsMaker() {
xzl committed
21 22
}

23 24

void HlsMaker::makeIndexFile(bool eof) {
xiongziliang committed
25
    char file_content[1024];
xiongziliang committed
26
    int maxSegmentDuration = 0;
27

28 29 30
    for (auto &tp : _seg_dur_list) {
        int dur = std::get<0>(tp);
        if (dur > maxSegmentDuration) {
xiongziliang committed
31 32 33
            maxSegmentDuration = dur;
        }
    }
xiongziliang committed
34

35
    auto sequence = _seg_number ? (_file_index > _seg_number ? _file_index - _seg_number : 0LL) : 0LL;
36

xiongziliang committed
37
    string m3u8;
38 39 40 41 42 43 44 45
    snprintf(file_content, sizeof(file_content),
             "#EXTM3U\n"
             "#EXT-X-VERSION:3\n"
             "#EXT-X-ALLOW-CACHE:NO\n"
             "#EXT-X-TARGETDURATION:%u\n"
             "#EXT-X-MEDIA-SEQUENCE:%llu\n",
             (maxSegmentDuration + 999) / 1000,
             sequence);
46

xiongziliang committed
47 48
    m3u8.assign(file_content);

49
    for (auto &tp : _seg_dur_list) {
50
        snprintf(file_content, sizeof(file_content), "#EXTINF:%.3f,\n%s\n", std::get<0>(tp) / 1000.0, std::get<1>(tp).data());
xiongziliang committed
51
        m3u8.append(file_content);
xiongziliang committed
52
    }
53

54
    if (eof) {
55
        snprintf(file_content, sizeof(file_content), "#EXT-X-ENDLIST\n");
xiongziliang committed
56
        m3u8.append(file_content);
57
    }
xiongziliang committed
58
    onWriteHls(m3u8.data(), m3u8.size());
xzl committed
59 60 61
}


62
void HlsMaker::inputData(void *data, uint32_t len, uint32_t timestamp, bool is_idr_fast_packet) {
63
    if (data && len) {
64 65
        if (is_idr_fast_packet) {
            //尝试切片ts
66 67
            addNewSegment(timestamp);
        }
68 69 70
        if (!_last_file_name.empty()) {
            //存在切片才写入ts数据
            onWriteSegment((char *) data, len);
71
            _last_timestamp = timestamp;
72
        }
xiongziliang committed
73
    } else {
74
        //resetTracks时触发此逻辑
75
        flushLastSegment(true);
76
    }
xzl committed
77 78
}

xiongziliang committed
79
void HlsMaker::delOldSegment() {
80
    if (_seg_number == 0) {
xiongziliang committed
81 82 83
        //如果设置为保留0个切片,则认为是保存为点播
        return;
    }
84
    //在hls m3u8索引文件中,我们保存的切片个数跟_seg_number相关设置一致
85
    if (_file_index > _seg_number) {
86 87
        _seg_dur_list.pop_front();
    }
88

89
    GET_CONFIG(uint32_t, segRetain, Hls::kSegmentRetain);
90 91 92
    //但是实际保存的切片个数比m3u8所述多若干个,这样做的目的是防止播放器在切片删除前能下载完毕
    if (_file_index > _seg_number + segRetain) {
        onDelSegment(_file_index - _seg_number - segRetain - 1);
93
    }
94 95
}

96 97
void HlsMaker::addNewSegment(uint32_t stamp) {
    if (!_last_file_name.empty() && stamp - _last_seg_timestamp < _seg_duration * 1000) {
xiongziliang committed
98 99
        //存在上个切片,并且未到分片时间
        return;
100
    }
xiongziliang committed
101

102
    //关闭并保存上一个切片,如果_seg_number==0,那么是点播。
103
    flushLastSegment(_seg_number == 0);
xiongziliang committed
104 105
    //新增切片
    _last_file_name = onOpenSegment(_file_index++);
106 107
    //记录本次切片的起始时间戳
    _last_seg_timestamp = stamp;
xiongziliang committed
108 109
}

110
void HlsMaker::flushLastSegment(bool eof){
111
    if (_last_file_name.empty()) {
xiongziliang committed
112 113 114 115
        //不存在上个切片
        return;
    }
    //文件创建到最后一次数据写入的时间即为切片长度
116
    auto seg_dur = _last_timestamp - _last_seg_timestamp;
117
    if (seg_dur <= 0) {
xiongziliang committed
118
        seg_dur = 100;
119
    }
120
    _seg_dur_list.push_back(std::make_tuple(seg_dur, std::move(_last_file_name)));
121
    _last_file_name.clear();
xiongziliang committed
122 123
    delOldSegment();
    makeIndexFile(eof);
124 125 126
    onFlushLastSegment(seg_dur);
}

xiongziliang committed
127 128 129 130
bool HlsMaker::isLive() {
    return _seg_number != 0;
}

131 132 133
void HlsMaker::clear() {
    _file_index = 0;
    _last_seg_timestamp = 0;
134 135 136 137
    _seg_dur_list.clear();
    _last_file_name.clear();
}

138
}//namespace mediakit