From 77928452388c60ba20facbadd02343b3725f5133 Mon Sep 17 00:00:00 2001 From: Marco van Dijk Date: Wed, 28 Sep 2022 14:59:07 +0200 Subject: [PATCH] Fix refreshing from DTSH in HLS input Co-authored-by: Thulinma --- src/input/input_hls.cpp | 148 +++++++++++++++++++++++++--------------- src/input/input_hls.h | 12 ++++ 2 files changed, 104 insertions(+), 56 deletions(-) diff --git a/src/input/input_hls.cpp b/src/input/input_hls.cpp index 1529d653..9f079e81 100644 --- a/src/input/input_hls.cpp +++ b/src/input/input_hls.cpp @@ -701,63 +701,61 @@ namespace Mist{ } bool inputHLS::readExistingHeader(){ - if (!Input::readExistingHeader()){return false;} - if (!M.inputLocalVars.isMember("version") || M.inputLocalVars["version"].asInt() < 2){ + if (!Input::readExistingHeader()){ + INFO_MSG("Could not read existing header, regenerating"); + return false; + } + if (!M.inputLocalVars.isMember("version") || M.inputLocalVars["version"].asInt() < 3){ INFO_MSG("Header needs update, regenerating"); return false; } - // Vars for parsing TS packets - TS::Packet packet; - bool hasPacket; - - // Set internal variables based on existing header file - tthread::lock_guard guard(entryMutex); - for (std::map >::iterator pListIt = listEntries.begin(); - pListIt != listEntries.end(); pListIt++){ - tsStream.clear(); - uint32_t entId = 0; - // For each entry in the playlist, we need to parse the earliest packet in order to set the segment offset - for (std::deque::iterator entryIt = pListIt->second.begin(); - entryIt != pListIt->second.end(); entryIt++){ - tsStream.partialClear(); - - if (!segDowner.loadSegment(*entryIt)){ - FAIL_MSG("Failed to load segment - skipping to next"); - continue; - } - // Flag to allow getPacketTime to set the offset - entId++; - allowRemap = true; - while (!segDowner.atEnd()){ - hasPacket = tsStream.hasPacketOnEachTrack() || (segDowner.atEnd() && tsStream.hasPacket()); - if (hasPacket){ - DTSC::Packet headerPack; - tsStream.getEarliestPacket(headerPack); - while (headerPack){ - size_t tmpTrackId = headerPack.getTrackId(); - // Call getPacketID in order to set pidmapping - uint64_t packetId = getPacketID(pListIt->first, tmpTrackId); - // Call getPacketTime in order to set segment offset - uint64_t packetTime = getPacketTime(headerPack.getTime(), tmpTrackId, pListIt->first, entryIt->mUTC); - VERYHIGH_MSG("Parsed earliest TS packet with id '%lu' @ '%lu ms' for TS segment with index '%u'", packetId, packetTime, entId - 1); - // Keep parsing until we have called getPacketID for each track - tsStream.getEarliestPacket(headerPack); - } - // If we do not have a packet on each track, read the next TS packet - }else if (segDowner.readNext()){ - packet.FromPointer(segDowner.packetPtr); - tsStream.parse(packet, entId); - } - } - // Finally save the offset as part of the TS segment. This is required for bufferframe - // to work correctly, since not every segment might have an UTC timestamp tag - std::deque &curList = listEntries[pListIt->first]; - VERYHIGH_MSG("Saving offset of '%" PRId64 "' to current TS segment", plsTimeOffset[pListIt->first]); - curList.at(entId-1).timeOffset = plsTimeOffset[pListIt->first]; - } + // Check if the DTSH file contains all expected data + if (!M.inputLocalVars.isMember("streamoffset")){ + INFO_MSG("Header needs update as it contains no streamoffset, regenerating"); + return false; } - tsStream.clear(); - // set bootMsOffset in order to display the program time correctly in the player + if (!M.inputLocalVars.isMember("playlistEntries")){ + INFO_MSG("Header needs update as it contains no playlist entries, regenerating"); + return false; + } + if (!M.inputLocalVars.isMember("pidMappingR")){ + INFO_MSG("Header needs update as it contains no packet id mappings, regenerating"); + return false; + } + // Recover playlist entries + tthread::lock_guard guard(entryMutex); + jsonForEachConst(M.inputLocalVars["playlistEntries"], i){ + std::deque newList; + jsonForEachConst(*i, j){ + const JSON::Value & thisEntry = *j; + playListEntries newEntry; + newEntry.filename = thisEntry[0u].asString(); + newEntry.bytePos = thisEntry[1u].asInt(); + newEntry.mUTC = thisEntry[2u].asInt(); + newEntry.duration = thisEntry[3u].asDouble(); + newEntry.timestamp = thisEntry[4u].asInt(); + newEntry.timeOffset = thisEntry[5u].asInt(); + newEntry.wait = thisEntry[6u].asInt(); + if (thisEntry[7u].asString().size() && thisEntry[8u].asString().size()){ + memcpy(newEntry.ivec, thisEntry[7u].asString().data(), 16); + memcpy(newEntry.keyAES, thisEntry[8u].asString().data(), 16); + }else{ + memset(newEntry.ivec, 0, 16); + memset(newEntry.keyAES, 0, 16); + } + newList.push_back(newEntry); + } + listEntries[JSON::Value(i.key()).asInt()] = newList; + } + // Recover pidMappings + jsonForEachConst(M.inputLocalVars["pidMappingR"], i){ + uint64_t key = JSON::Value(i.key()).asInt(); + uint64_t val = i->asInt(); + pidMappingR[key] = val; + pidMapping[val] = key; + } + // Set bootMsOffset in order to display the program time correctly in the player + streamOffset = M.inputLocalVars["streamoffset"].asInt(); if (meta.getLive()){meta.setUTCOffset(streamOffset + (Util::unixMS() - Util::bootMS()));} meta.setBootMsOffset(streamOffset); return true; @@ -765,6 +763,7 @@ namespace Mist{ bool inputHLS::readHeader(){ if (streamIsLive && !isLiveDVR){return true;} + if (readExistingHeader()){return true;} // to analyse and extract data TS::Packet packet; char *data; @@ -843,8 +842,13 @@ namespace Mist{ } // Finally save the offset as part of the TS segment. This is required for bufferframe // to work correctly, since not every segment might have an UTC timestamp tag - std::deque &curList = listEntries[pListIt->first]; - curList.at(entId-1).timeOffset = plsTimeOffset[pListIt->first]; + if (plsTimeOffset.count(pListIt->first)){ + std::deque &curList = listEntries[pListIt->first]; + curList.at(entId-1).timeOffset = plsTimeOffset[pListIt->first]; + }else{ + std::deque &curList = listEntries[pListIt->first]; + curList.at(entId-1).timeOffset = 0; + } } } @@ -854,7 +858,39 @@ namespace Mist{ if (streamIsLive || isLiveDVR){return true;} // Set local vars used for parsing existing headers - meta.inputLocalVars["version"] = 2; + meta.inputLocalVars["version"] = 3; + + // Write playlist entry info + JSON::Value allEntries; + for (std::map >::iterator pListIt = listEntries.begin(); + pListIt != listEntries.end(); pListIt++){ + JSON::Value thisPlaylist; + for (std::deque::iterator entryIt = pListIt->second.begin(); + entryIt != pListIt->second.end(); entryIt++){ + JSON::Value thisEntries; + thisEntries.append(entryIt->filename); + thisEntries.append(entryIt->bytePos); + thisEntries.append(entryIt->mUTC); + thisEntries.append(entryIt->duration); + thisEntries.append(entryIt->timestamp); + thisEntries.append(entryIt->timeOffset); + thisEntries.append(entryIt->wait); + thisEntries.append(entryIt->ivec); + thisEntries.append(entryIt->keyAES); + thisPlaylist.append(thisEntries); + } + allEntries[JSON::Value(pListIt->first).asString()] = thisPlaylist; + } + meta.inputLocalVars["playlistEntries"] = allEntries; + meta.inputLocalVars["streamoffset"] = streamOffset; + + // Write packet ID mappings + JSON::Value thisMappingsR; + for (std::map::iterator pidIt = pidMappingR.begin(); + pidIt != pidMappingR.end(); pidIt++){ + thisMappingsR[JSON::Value(pidIt->first).asString()] = pidIt->second; + } + meta.inputLocalVars["pidMappingR"] = thisMappingsR; INFO_MSG("write header file..."); M.toFile((config->getString("input") + ".dtsh").c_str()); diff --git a/src/input/input_hls.h b/src/input/input_hls.h index 5dc89080..59f4d817 100644 --- a/src/input/input_hls.h +++ b/src/input/input_hls.h @@ -32,6 +32,18 @@ namespace Mist{ uint64_t wait; char ivec[16]; char keyAES[16]; + playListEntries(){ + bytePos = 0; + mUTC = 0; + duration = 0; + timestamp = 0; + timeOffset = 0; + wait = 0; + for (size_t i = 0; i < 16; ++i){ + ivec[i] = 0; + keyAES[i] = 0; + } + } }; /// Keeps the segment entry list by playlist ID