From 4b1b7bc4c73b384da8a0e0e93dbd86881d087367 Mon Sep 17 00:00:00 2001 From: pingkai Date: Tue, 4 Feb 2020 19:45:55 +0800 Subject: [PATCH] feat(HLSStream): support parse webvtt pts Signed-off-by: pingkai --- framework/demuxer/play_list/HLSStream.cpp | 90 ++++++++++++++++++++++- framework/demuxer/play_list/HLSStream.h | 21 ++++++ mediaPlayer/SuperMediaPlayer.cpp | 2 +- 3 files changed, 111 insertions(+), 2 deletions(-) diff --git a/framework/demuxer/play_list/HLSStream.cpp b/framework/demuxer/play_list/HLSStream.cpp index b99a9b8ac..eb0d4cdd4 100644 --- a/framework/demuxer/play_list/HLSStream.cpp +++ b/framework/demuxer/play_list/HLSStream.cpp @@ -89,6 +89,12 @@ namespace Cicada { } ret = pHandle->readSegment(buffer, size); + if (pHandle->getStreamType() == STREAM_TYPE_SUB && pHandle->mVttPtsOffSet == INT64_MIN && ret > 0) { + pHandle->mVttPtsOffSet = pHandle->mWVTTParser.addBuffer(buffer, ret); + if (pHandle->mVttPtsOffSet != INT64_MIN) { + AF_LOGD("WVTTParser pts is %lld\n", pHandle->mVttPtsOffSet); + } + } return ret; } @@ -352,7 +358,7 @@ namespace Cicada { } uint64_t ps = getSize(buf, 8, 8); pts = ps * 1000 / 90; - // AF_LOGD("ps is %u\n", ps); + // AF_LOGD("ps is %u\n", ps); } } meta1 = meta1->next; @@ -372,6 +378,10 @@ namespace Cicada { if (ret < 0) { return ret; } + if (getStreamType() == STREAM_TYPE_SUB) { + mWVTTParser.rest(); + mVttPtsOffSet = INT64_MIN; + } { std::lock_guard lock(mHLSMutex); @@ -895,6 +905,10 @@ namespace Cicada { packet->getInfo().pts += mPacketFirstPts; packet->getInfo().dts += mPacketFirstPts; } + if (mVttPtsOffSet != INT64_MIN && packet->getInfo().pts != INT64_MIN) { + packet->getInfo().pts += mVttPtsOffSet; + packet->getInfo().dts += mVttPtsOffSet; + } if (mCurSeg) { // mark the seg start time to first seg frame AF_LOGD("stream (%d) mark startTime %llu\n", mPTracker->getStreamType(), @@ -1313,4 +1327,78 @@ namespace Cicada { return ""; } + HLSStream::WebVttParser::WebVttParser() = default; + + HLSStream::WebVttParser::~WebVttParser() + { + if (mBuffer) { + free(mBuffer); + } + } + + static int64_t read_ts(const char *s) + { + int hh, mm, ss, ms; + if (sscanf(s, "%u:%u:%u.%u", &hh, &mm, &ss, &ms) == 4) { + return (hh * 3600LL + mm * 60LL + ss) * 1000LL + ms; + } + if (sscanf(s, "%u:%u.%u", &mm, &ss, &ms) == 3) { + return (mm * 60LL + ss) * 1000LL + ms; + } + return INT64_MIN; + } + + int64_t HLSStream::WebVttParser::addBuffer(uint8_t *buffer, int size) + { + if (bFinished) + return mMapPTS; + + char *lend; + + if ((lend = strnstr((const char *) buffer, "\n\n", size)) != nullptr + || (lend = strnstr((const char *) buffer, "\r\n\r\n", size)) != nullptr) { + bFinished = true; + } + size_t append_size = size; + if (lend != nullptr) { + append_size = lend - (const char *) buffer; + } + mBuffer = static_cast(realloc(mBuffer, mSize + append_size)); + memcpy(mBuffer + mSize, buffer, append_size); + mSize += append_size; + + if (!bFinished) + return INT64_MIN; + + mBuffer[mSize - 1] = 0; + /* + * WebVTT files in HLS streams contain a timestamp offset for + * syncing with the main stream: + * + * X-TIMESTAMP-MAP=LOCAL:00:00:00.000,MPEGTS:900000 + * (LOCAL and MPEGTS can be reversed even though HLS spec + * does not say so) + */ + char *hls_timestamp_map = strstr(reinterpret_cast(mBuffer), "\nX-TIMESTAMP-MAP="); + char *native_str = strstr(hls_timestamp_map, "LOCAL:"); + char *mpegts_str = strstr(hls_timestamp_map, "MPEGTS:"); + if (native_str && mpegts_str) { + + int64_t native_ts = read_ts(native_str + 6); + int64_t mpegts_ts = strtoll(mpegts_str + 7, nullptr, 10); + + if (native_ts != INT64_MIN) { + mMapPTS = mpegts_ts * 100 / 9 - native_ts * 1000; + } + } + + return mMapPTS; + } + + void HLSStream::WebVttParser::rest() + { + mSize = 0; + mMapPTS = INT64_MIN; + bFinished = false; + } } diff --git a/framework/demuxer/play_list/HLSStream.h b/framework/demuxer/play_list/HLSStream.h index 6197b28e5..498c85193 100644 --- a/framework/demuxer/play_list/HLSStream.h +++ b/framework/demuxer/play_list/HLSStream.h @@ -21,6 +21,24 @@ namespace Cicada { class HLSStream : public AbstractStream { + class WebVttParser { + + public: + WebVttParser(); + + ~WebVttParser(); + + int64_t addBuffer(uint8_t *buffer, int size); + + void rest(); + + private: + uint8_t *mBuffer = nullptr; + int mSize = 0; + int64_t mMapPTS = INT64_MIN; + bool bFinished = false; + }; + public: HLSStream(SegmentTracker *pTracker, int id); @@ -188,6 +206,9 @@ namespace Cicada { std::map mStreamStartTimeMap; int64_t mPacketFirstPts = INT64_MAX; + + WebVttParser mWVTTParser; + int64_t mVttPtsOffSet = INT64_MIN; }; } diff --git a/mediaPlayer/SuperMediaPlayer.cpp b/mediaPlayer/SuperMediaPlayer.cpp index 492d3acc0..85cc48b9d 100644 --- a/mediaPlayer/SuperMediaPlayer.cpp +++ b/mediaPlayer/SuperMediaPlayer.cpp @@ -1820,7 +1820,7 @@ namespace Cicada { } if ((HAVE_SUBTITLE || mSubPlayer) && !mSeekFlag) { - RenderSubtitle(mCurrentPos); + RenderSubtitle(mCurVideoPts); } return audioRendered || videoRendered;