From 1e1b3794da5dc55ca32873e7e3568995a6050be9 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 12 Sep 2020 20:49:00 +0800 Subject: [PATCH] =?UTF-8?q?HLS=E5=88=87=E7=89=87=E9=97=B4=E9=9A=94?= =?UTF-8?q?=E4=BB=A5=E6=95=B0=E6=8D=AE=E6=97=B6=E9=97=B4=E6=88=B3=E4=B8=BA?= =?UTF-8?q?=E5=87=86:#463?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/HlsMaker.cpp | 64 ++++++++++++++++++++--------------------- src/Record/HlsMaker.h | 8 +++--- src/Record/TsMuxer.cpp | 53 ++++++++++++++++++---------------- 3 files changed, 65 insertions(+), 60 deletions(-) diff --git a/src/Record/HlsMaker.cpp b/src/Record/HlsMaker.cpp index ac8645d9..ff8ead52 100644 --- a/src/Record/HlsMaker.cpp +++ b/src/Record/HlsMaker.cpp @@ -32,27 +32,27 @@ void HlsMaker::makeIndexFile(bool eof) { } } - auto sequence = _seg_number ? (_file_index > _seg_number ? _file_index - _seg_number : 0LL) : 0LL; + auto sequence = _seg_number ? (_file_index > _seg_number ? _file_index - _seg_number : 0LL) : 0LL; string m3u8; - 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); + 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); m3u8.assign(file_content); for (auto &tp : _seg_dur_list) { - snprintf(file_content,sizeof(file_content), "#EXTINF:%.3f,\n%s\n", std::get<0>(tp) / 1000.0, std::get<1>(tp).data()); + snprintf(file_content, sizeof(file_content), "#EXTINF:%.3f,\n%s\n", std::get<0>(tp) / 1000.0, std::get<1>(tp).data()); m3u8.append(file_content); } if (eof) { - snprintf(file_content,sizeof(file_content),"#EXT-X-ENDLIST\n"); + snprintf(file_content, sizeof(file_content), "#EXT-X-ENDLIST\n"); m3u8.append(file_content); } onWriteHls(m3u8.data(), m3u8.size()); @@ -61,20 +61,22 @@ void HlsMaker::makeIndexFile(bool eof) { void HlsMaker::inputData(void *data, uint32_t len, uint32_t timestamp, bool is_idr_fast_packet) { if (data && len) { - if(is_idr_fast_packet){ + if (is_idr_fast_packet) { + //尝试切片ts addNewSegment(timestamp); } - onWriteSegment((char *) data, len); - //记录上次写入数据时间 - _ticker_last_data.resetTime(); + if (!_last_file_name.empty()) { + //存在切片才写入ts数据 + onWriteSegment((char *) data, len); + } } else { //resetTracks时触发此逻辑 - flushLastSegment(true); + flushLastSegment(timestamp, true); } } void HlsMaker::delOldSegment() { - if(_seg_number == 0){ + if (_seg_number == 0) { //如果设置为保留0个切片,则认为是保存为点播 return; } @@ -83,35 +85,35 @@ void HlsMaker::delOldSegment() { _seg_dur_list.pop_front(); } - GET_CONFIG(uint32_t,segRetain,Hls::kSegmentRetain); + GET_CONFIG(uint32_t, segRetain, Hls::kSegmentRetain); //但是实际保存的切片个数比m3u8所述多若干个,这样做的目的是防止播放器在切片删除前能下载完毕 if (_file_index > _seg_number + segRetain) { onDelSegment(_file_index - _seg_number - segRetain - 1); } } -void HlsMaker::addNewSegment(uint32_t) { - if(!_last_file_name.empty() && _ticker.elapsedTime() < _seg_duration * 1000){ +void HlsMaker::addNewSegment(uint32_t stamp) { + if (!_last_file_name.empty() && stamp - _last_seg_timestamp < _seg_duration * 1000) { //存在上个切片,并且未到分片时间 return; } //关闭并保存上一个切片,如果_seg_number==0,那么是点播。 - flushLastSegment(_seg_number == 0); + flushLastSegment(stamp, _seg_number == 0); //新增切片 _last_file_name = onOpenSegment(_file_index++); - //重置切片计时器 - _ticker.resetTime(); + //记录本次切片的起始时间戳 + _last_seg_timestamp = stamp; } -void HlsMaker::flushLastSegment(bool eof){ - if(_last_file_name.empty()){ +void HlsMaker::flushLastSegment(uint32_t timestamp, bool eof){ + if (_last_file_name.empty()) { //不存在上个切片 return; } //文件创建到最后一次数据写入的时间即为切片长度 - auto seg_dur = _ticker.elapsedTime() - _ticker_last_data.elapsedTime(); - if(seg_dur <= 0){ + auto seg_dur = timestamp - _last_seg_timestamp; + if (seg_dur <= 0) { seg_dur = 100; } _seg_dur_list.push_back(std::make_tuple(seg_dur, _last_file_name)); @@ -124,13 +126,11 @@ bool HlsMaker::isLive() { return _seg_number != 0; } -void HlsMaker::clear(){ +void HlsMaker::clear() { + _file_index = 0; + _last_seg_timestamp = 0; _seg_dur_list.clear(); _last_file_name.clear(); - _ticker_last_data.resetTime(); - _ticker.resetTime(); - _file_index = 0; } - }//namespace mediakit \ No newline at end of file diff --git a/src/Record/HlsMaker.h b/src/Record/HlsMaker.h index a03acb51..37666b93 100644 --- a/src/Record/HlsMaker.h +++ b/src/Record/HlsMaker.h @@ -80,9 +80,10 @@ protected: /** * 关闭上个ts切片并且写入m3u8索引 + * @param timestamp 毫秒时间戳 * @param eof */ - void flushLastSegment(bool eof = false); + void flushLastSegment(uint32_t timestamp, bool eof = false); private: /** @@ -103,11 +104,10 @@ private: void addNewSegment(uint32_t timestamp); private: - uint32_t _seg_number = 0; float _seg_duration = 0; + uint32_t _seg_number = 0; + uint32_t _last_seg_timestamp = 0; uint64_t _file_index = 0; - Ticker _ticker; - Ticker _ticker_last_data; string _last_file_name; std::deque > _seg_dur_list; }; diff --git a/src/Record/TsMuxer.cpp b/src/Record/TsMuxer.cpp index e71fa9c7..fa31388f 100644 --- a/src/Record/TsMuxer.cpp +++ b/src/Record/TsMuxer.cpp @@ -25,20 +25,20 @@ TsMuxer::~TsMuxer() { } void TsMuxer::stampSync(){ - if(_codec_to_trackid.size() < 2){ + if (_codec_to_trackid.size() < 2) { return; } Stamp *audio = nullptr, *video = nullptr; - for(auto &pr : _codec_to_trackid){ - switch (getTrackType((CodecId) pr.first)){ + for (auto &pr : _codec_to_trackid) { + switch (getTrackType((CodecId) pr.first)) { case TrackAudio : audio = &pr.second.stamp; break; case TrackVideo : video = &pr.second.stamp; break; default : break; } } - if(audio && video){ + if (audio && video) { //音频时间戳同步于视频,因为音频时间戳被修改后不影响播放 audio->syncTo(*video); } @@ -87,7 +87,7 @@ void TsMuxer::addTrack(const Track::Ptr &track) { void TsMuxer::inputFrame(const Frame::Ptr &frame) { auto it = _codec_to_trackid.find(frame->getCodecId()); - if(it == _codec_to_trackid.end()){ + if (it == _codec_to_trackid.end()) { return; } auto &track_info = it->second; @@ -95,39 +95,41 @@ void TsMuxer::inputFrame(const Frame::Ptr &frame) { _is_idr_fast_packet = !_have_video; switch (frame->getCodecId()){ case CodecH264: { - int type = H264_TYPE(*((uint8_t *)frame->data() + frame->prefixSize())); - if(type == H264Frame::NAL_SEI){ + int type = H264_TYPE(*((uint8_t *) frame->data() + frame->prefixSize())); + if (type == H264Frame::NAL_SEI) { break; } } + case CodecH265: { //这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理, if (!_frameCached.empty() && _frameCached.back()->dts() != frame->dts()) { Frame::Ptr back = _frameCached.back(); Buffer::Ptr merged_frame = back; - if(_frameCached.size() != 1){ + if (_frameCached.size() != 1) { string merged; - _frameCached.for_each([&](const Frame::Ptr &frame){ - if(frame->prefixSize()){ - merged.append(frame->data(),frame->size()); - } else{ - merged.append("\x00\x00\x00\x01",4); - merged.append(frame->data(),frame->size()); + _frameCached.for_each([&](const Frame::Ptr &frame) { + if (frame->prefixSize()) { + merged.append(frame->data(), frame->size()); + } else { + merged.append("\x00\x00\x00\x01", 4); + merged.append(frame->data(), frame->size()); } - if(frame->keyFrame()){ + if (frame->keyFrame()) { _is_idr_fast_packet = true; } }); merged_frame = std::make_shared(std::move(merged)); } - track_info.stamp.revise(back->dts(),back->pts(),dts_out,pts_out); + track_info.stamp.revise(back->dts(), back->pts(), dts_out, pts_out); + //取视频时间戳为TS的时间戳 _timestamp = dts_out; - mpeg_ts_write(_context, track_info.track_id, back->keyFrame() ? 0x0001 : 0, pts_out * 90LL, dts_out * 90LL, merged_frame->data(), merged_frame->size()); + mpeg_ts_write(_context, track_info.track_id, back->keyFrame() ? 0x0001 : 0, pts_out * 90LL,dts_out * 90LL, merged_frame->data(), merged_frame->size()); _frameCached.clear(); } _frameCached.emplace_back(Frame::getCacheAbleFrame(frame)); - } break; + } case CodecAAC: { if (frame->prefixSize() == 0) { @@ -137,18 +139,21 @@ void TsMuxer::inputFrame(const Frame::Ptr &frame) { } default: { - track_info.stamp.revise(frame->dts(),frame->pts(),dts_out,pts_out); - _timestamp = dts_out; + track_info.stamp.revise(frame->dts(), frame->pts(), dts_out, pts_out); + if(!_have_video){ + //没有视频时,才以音频时间戳为TS的时间戳 + _timestamp = dts_out; + } mpeg_ts_write(_context, track_info.track_id, frame->keyFrame() ? 0x0001 : 0, pts_out * 90LL, dts_out * 90LL, frame->data(), frame->size()); - } break; + } } } void TsMuxer::resetTracks() { _have_video = false; //通知片段中断 - onTs(nullptr, 0, 0, 0); + onTs(nullptr, 0, _timestamp, 0); uninit(); init(); } @@ -169,8 +174,8 @@ void TsMuxer::init() { muxer->_is_idr_fast_packet = false; } }; - if(_context == nullptr){ - _context = mpeg_ts_create(&s_func,this); + if (_context == nullptr) { + _context = mpeg_ts_create(&s_func, this); } }