From 2a721f5e7fdcd091882d0e643cd957275a44e880 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Fri, 28 Jul 2017 11:59:30 +0200 Subject: [PATCH] TS MPEG2/MP2 support --- lib/ts_packet.cpp | 14 ++- lib/ts_stream.cpp | 86 +++++++++++++++- lib/ts_stream.h | 5 +- src/input/input_ts.cpp | 2 + src/output/output_httpts.cpp | 2 + src/output/output_ts.cpp | 2 + src/output/output_ts_base.cpp | 186 ++++++++++++++++++---------------- 7 files changed, 200 insertions(+), 97 deletions(-) diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index e9b1899d..e3a650e7 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -527,9 +527,13 @@ namespace TS { /// \param len The length of this frame. /// \param PTS The timestamp of the frame. std::string & Packet::getPESVideoLeadIn(unsigned int len, unsigned long long PTS, unsigned long long offset, bool isAligned, uint64_t bps) { - len += (offset ? 13 : 8); + if (len){ + len += (offset ? 13 : 8); + } if (bps >= 50){ - len += 3; + if (len){ + len += 3; + } }else{ bps = 0; } @@ -826,7 +830,7 @@ namespace TS { case 0x15: return "meta PES"; case 0x16: return "meta section"; case 0x1B: return "H264"; - case 0x24: return "H265"; + case 0x24: return "HEVC"; case 0x81: return "AC3"; default: return "unknown"; } @@ -1219,6 +1223,8 @@ namespace TS { entry.setStreamType(0x1B); }else if (myMeta.tracks[*it].codec == "HEVC"){ entry.setStreamType(0x24); + }else if (myMeta.tracks[*it].codec == "MPEG2"){ + entry.setStreamType(0x02); }else if (myMeta.tracks[*it].codec == "AAC"){ entry.setStreamType(0x0F); std::string aac_info("\174\002\121\000", 4);//AAC descriptor: AAC Level 2. Hardcoded, because... what are AAC levels, anyway? @@ -1229,7 +1235,7 @@ namespace TS { aac_info.append("\000", 1); } entry.setESInfo(aac_info); - }else if (myMeta.tracks[*it].codec == "MP3"){ + }else if (myMeta.tracks[*it].codec == "MP3" || myMeta.tracks[*it].codec == "MP2"){ entry.setStreamType(0x03); }else if (myMeta.tracks[*it].codec == "AC3"){ entry.setStreamType(0x81); diff --git a/lib/ts_stream.cpp b/lib/ts_stream.cpp index bc729941..df1451bb 100644 --- a/lib/ts_stream.cpp +++ b/lib/ts_stream.cpp @@ -6,6 +6,8 @@ #include "nal.h" #include #include +#include "mpeg.h" + namespace TS{ @@ -187,6 +189,8 @@ namespace TS{ case H265: case AC3: case ID3: + case MP2: + case MPEG2: pidToCodec[pid] = sType; if (sType == ID3){ metaInit[pid] = std::string(entry.getESInfo(), entry.getESInfoLength()); @@ -517,10 +521,13 @@ namespace TS{ } } } - if (thisCodec == ID3 || thisCodec == AC3){ + if (thisCodec == ID3 || thisCodec == AC3 || thisCodec == MP2){ out.push_back(DTSC::Packet()); out.back().genericFill(timeStamp, timeOffset, tid, pesPayload, realPayloadSize, bPos, 0); + if (thisCodec == MP2 && !mp2Hdr.count(tid)){ + mp2Hdr[tid] = std::string(pesPayload, realPayloadSize); + } } @@ -626,6 +633,37 @@ namespace TS{ nextPtr = nalu::scanAnnexB(pesPayload, realPayloadSize); } } + if (thisCodec == MPEG2){ + const char *origBegin = pesPayload; + size_t origSize = realPayloadSize; + const char *nextPtr; + const char *pesEnd = pesPayload+realPayloadSize; + uint32_t nalSize = 0; + + bool isKeyFrame = false; + + nextPtr = nalu::scanAnnexB(pesPayload, realPayloadSize); + if (!nextPtr){ + WARN_MSG("No start code found in entire PES packet!"); + return; + } + + while (nextPtr < pesEnd){ + if (!nextPtr){nextPtr = pesEnd;} + //Calculate size of NAL unit, removing null bytes from the end + nalSize = nalu::nalEndPosition(pesPayload, nextPtr - pesPayload) - pesPayload; + + // Check if this is a keyframe + parseNal(tid, pesPayload, nextPtr, isKeyFrame); + + if (((nextPtr - pesPayload) + 3) >= realPayloadSize){break;}//end of the loop + realPayloadSize -= ((nextPtr - pesPayload) + 3); // decrease the total size + pesPayload = nextPtr + 3; + nextPtr = nalu::scanAnnexB(pesPayload, realPayloadSize); + } + out.push_back(DTSC::Packet()); + out.back().genericFill(timeStamp, timeOffset, tid, origBegin, origSize, bPos, isKeyFrame); + } } void Stream::getPacket(unsigned long tid, DTSC::Packet &pack){ @@ -660,11 +698,27 @@ namespace TS{ bool firstSlice = true; char typeNal; - isKeyFrame = false; if (pidToCodec[tid] == MPEG2){ + typeNal = pesPayload[0]; + switch (typeNal){ + case 0xB3: + if (!mpeg2SeqHdr.count(tid)){ + mpeg2SeqHdr[tid] = std::string(pesPayload, (nextPtr - pesPayload)); + } + break; + case 0xB5: + if (!mpeg2SeqExt.count(tid)){ + mpeg2SeqExt[tid] = std::string(pesPayload, (nextPtr - pesPayload)); + } + break; + case 0xB8: + isKeyFrame = true; + break; + } return; } + isKeyFrame = false; if (pidToCodec[tid] == H264){ typeNal = pesPayload[0] & 0x1F; switch (typeNal){ @@ -816,6 +870,17 @@ namespace TS{ } } }break; + case MPEG2:{ + meta.tracks[mId].type = "video"; + meta.tracks[mId].codec = "MPEG2"; + meta.tracks[mId].trackID = mId; + meta.tracks[mId].init = std::string("\000\000\001", 3) + mpeg2SeqHdr[it->first] + std::string("\000\000\001", 3) + mpeg2SeqExt[it->first]; + + Mpeg::MPEG2Info info = Mpeg::parseMPEG2Header(meta.tracks[mId].init); + meta.tracks[mId].width = info.width; + meta.tracks[mId].height = info.height; + meta.tracks[mId].fpks = info.fps * 1000; + }break; case ID3:{ meta.tracks[mId].type = "meta"; meta.tracks[mId].codec = "ID3"; @@ -831,6 +896,18 @@ namespace TS{ meta.tracks[mId].rate = 0; meta.tracks[mId].channels = 0; }break; + case MP2:{ + meta.tracks[mId].type = "audio"; + meta.tracks[mId].codec = "MP2"; + meta.tracks[mId].trackID = mId; + + Mpeg::MP2Info info = Mpeg::parseMP2Header(mp2Hdr[it->first]); + meta.tracks[mId].rate = info.sampleRate; + meta.tracks[mId].channels = info.channels; + + ///\todo Fix this value + meta.tracks[mId].size = 0; + }break; case AAC:{ meta.tracks[mId].type = "audio"; meta.tracks[mId].codec = "AAC"; @@ -887,7 +964,10 @@ namespace TS{ case AAC: case H265: case AC3: - case ID3: result.insert(entry.getElementaryPid()); break; + case ID3: + case MP2: + case MPEG2: + result.insert(entry.getElementaryPid()); break; default: break; } entry.advance(); diff --git a/lib/ts_stream.h b/lib/ts_stream.h index 0620206c..4402ed35 100644 --- a/lib/ts_stream.h +++ b/lib/ts_stream.h @@ -9,7 +9,7 @@ #include "shared_memory.h" namespace TS{ - enum codecType{H264 = 0x1B, AAC = 0x0F, AC3 = 0x81, MP3 = 0x03, H265 = 0x24, ID3 = 0x15}; + enum codecType{H264 = 0x1B, AAC = 0x0F, AC3 = 0x81, MP3 = 0x03, H265 = 0x24, ID3 = 0x15, MPEG2 = 0x02, MP2 = 0x03}; class ADTSRemainder{ private: @@ -80,6 +80,9 @@ namespace TS{ std::map metaInit; std::map descriptors; std::map seenUnitStart; + std::map mpeg2SeqHdr; + std::map mpeg2SeqExt; + std::map mp2Hdr; mutable tthread::recursive_mutex tMutex; bool threaded; diff --git a/src/input/input_ts.cpp b/src/input/input_ts.cpp index c6c0f756..2cce0346 100755 --- a/src/input/input_ts.cpp +++ b/src/input/input_ts.cpp @@ -123,8 +123,10 @@ namespace Mist { capa["priority"] = 9ll; capa["codecs"][0u][0u].append("H264"); capa["codecs"][0u][0u].append("HEVC"); + capa["codecs"][0u][0u].append("MPEG2"); capa["codecs"][0u][1u].append("AAC"); capa["codecs"][0u][1u].append("AC3"); + capa["codecs"][0u][1u].append("MP2"); inFile = NULL; inputProcess = 0; } diff --git a/src/output/output_httpts.cpp b/src/output/output_httpts.cpp index c5bbbe53..ddc26d2f 100644 --- a/src/output/output_httpts.cpp +++ b/src/output/output_httpts.cpp @@ -40,9 +40,11 @@ namespace Mist { capa["socket"] = "http_ts"; capa["codecs"][0u][0u].append("H264"); capa["codecs"][0u][0u].append("HEVC"); + capa["codecs"][0u][0u].append("MPEG2"); capa["codecs"][0u][1u].append("AAC"); capa["codecs"][0u][1u].append("MP3"); capa["codecs"][0u][1u].append("AC3"); + capa["codecs"][0u][1u].append("MP2"); capa["methods"][0u]["handler"] = "http"; capa["methods"][0u]["type"] = "html5/video/mpeg"; capa["methods"][0u]["priority"] = 1ll; diff --git a/src/output/output_ts.cpp b/src/output/output_ts.cpp index a97d31b7..9fc594d8 100644 --- a/src/output/output_ts.cpp +++ b/src/output/output_ts.cpp @@ -80,9 +80,11 @@ namespace Mist { capa["optional"]["tracks"]["default"] = ""; capa["codecs"][0u][0u].append("HEVC"); capa["codecs"][0u][0u].append("H264"); + capa["codecs"][0u][0u].append("MPEG2"); capa["codecs"][0u][1u].append("AAC"); capa["codecs"][0u][1u].append("MP3"); capa["codecs"][0u][1u].append("AC3"); + capa["codecs"][0u][1u].append("MP2"); cfg->addConnectorOptions(8888, capa); config = cfg; capa["push_urls"].append("tsudp://*"); diff --git a/src/output/output_ts_base.cpp b/src/output/output_ts_base.cpp index 6c172c5f..3e16063e 100644 --- a/src/output/output_ts_base.cpp +++ b/src/output/output_ts_base.cpp @@ -81,115 +81,123 @@ namespace Mist { std::string bs; //prepare bufferstring if (video){ - unsigned int extraSize = 0; - //dataPointer[4] & 0x1f is used to check if this should be done later: fillPacket("\000\000\000\001\011\360", 6); - if (Trk.codec == "H264" && (dataPointer[4] & 0x1f) != 0x09){ - extraSize += 6; - } - if (keyframe){ - if (Trk.codec == "H264"){ - if (!haveAvcc){ - avccbox.setPayload(Trk.init); - haveAvcc = true; - } - bs = avccbox.asAnnexB(); - extraSize += bs.size(); + if (Trk.codec == "H264" || Trk.codec == "HEVC"){ + unsigned int extraSize = 0; + //dataPointer[4] & 0x1f is used to check if this should be done later: fillPacket("\000\000\000\001\011\360", 6); + if (Trk.codec == "H264" && (dataPointer[4] & 0x1f) != 0x09){ + extraSize += 6; } - /*LTS-START*/ - if (Trk.codec == "HEVC"){ - if (!haveHvcc){ - hvccbox.setPayload(Trk.init); - haveHvcc = true; + if (keyframe){ + if (Trk.codec == "H264"){ + if (!haveAvcc){ + avccbox.setPayload(Trk.init); + haveAvcc = true; + } + bs = avccbox.asAnnexB(); + extraSize += bs.size(); } - bs = hvccbox.asAnnexB(); - extraSize += bs.size(); + /*LTS-START*/ + if (Trk.codec == "HEVC"){ + if (!haveHvcc){ + hvccbox.setPayload(Trk.init); + haveHvcc = true; + } + bs = hvccbox.asAnnexB(); + extraSize += bs.size(); + } + /*LTS-END*/ } - /*LTS-END*/ - } - - unsigned int watKunnenWeIn1Ding = 65490-13; - unsigned int splitCount = (dataLen+extraSize) / watKunnenWeIn1Ding; - unsigned int currPack = 0; - unsigned int ThisNaluSize = 0; - unsigned int i = 0; - unsigned int nalLead = 0; - uint64_t offset = thisPacket.getInt("offset") * 90; + + unsigned int watKunnenWeIn1Ding = 65490-13; + unsigned int splitCount = (dataLen+extraSize) / watKunnenWeIn1Ding; + unsigned int currPack = 0; + unsigned int ThisNaluSize = 0; + unsigned int i = 0; + unsigned int nalLead = 0; + uint64_t offset = thisPacket.getInt("offset") * 90; - while (currPack <= splitCount){ - unsigned int alreadySent = 0; - bs = TS::Packet::getPESVideoLeadIn((currPack != splitCount ? watKunnenWeIn1Ding : dataLen+extraSize - currPack*watKunnenWeIn1Ding), packTime, offset, !currPack, Trk.bps); - fillPacket(bs.data(), bs.size(), firstPack, video, keyframe, pkgPid, contPkg); - if (!currPack){ - if (Trk.codec == "H264" && (dataPointer[4] & 0x1f) != 0x09){ - //End of previous nal unit, if not already present - fillPacket("\000\000\000\001\011\360", 6, firstPack, video, keyframe, pkgPid, contPkg); - alreadySent += 6; - } - if (keyframe){ - if (Trk.codec == "H264"){ - bs = avccbox.asAnnexB(); - fillPacket(bs.data(), bs.size(), firstPack, video, keyframe, pkgPid, contPkg); - alreadySent += bs.size(); + while (currPack <= splitCount){ + unsigned int alreadySent = 0; + bs = TS::Packet::getPESVideoLeadIn((currPack != splitCount ? watKunnenWeIn1Ding : dataLen+extraSize - currPack*watKunnenWeIn1Ding), packTime, offset, !currPack, Trk.bps); + fillPacket(bs.data(), bs.size(), firstPack, video, keyframe, pkgPid, contPkg); + if (!currPack){ + if (Trk.codec == "H264" && (dataPointer[4] & 0x1f) != 0x09){ + //End of previous nal unit, if not already present + fillPacket("\000\000\000\001\011\360", 6, firstPack, video, keyframe, pkgPid, contPkg); + alreadySent += 6; } - /*LTS-START*/ - if (Trk.codec == "HEVC"){ - bs = hvccbox.asAnnexB(); - fillPacket(bs.data(), bs.size(), firstPack, video, keyframe, pkgPid, contPkg); - alreadySent += bs.size(); + if (keyframe){ + if (Trk.codec == "H264"){ + bs = avccbox.asAnnexB(); + fillPacket(bs.data(), bs.size(), firstPack, video, keyframe, pkgPid, contPkg); + alreadySent += bs.size(); + } + /*LTS-START*/ + if (Trk.codec == "HEVC"){ + bs = hvccbox.asAnnexB(); + fillPacket(bs.data(), bs.size(), firstPack, video, keyframe, pkgPid, contPkg); + alreadySent += bs.size(); + } + /*LTS-END*/ } - /*LTS-END*/ } - } - while (i + 4 < (unsigned int)dataLen){ - if (nalLead){ - fillPacket("\000\000\000\001"+4-nalLead,nalLead, firstPack, video, keyframe, pkgPid, contPkg); - i += nalLead; - alreadySent += nalLead; - nalLead = 0; - } - if (!ThisNaluSize){ - ThisNaluSize = (dataPointer[i] << 24) + (dataPointer[i+1] << 16) + (dataPointer[i+2] << 8) + dataPointer[i+3]; - if (ThisNaluSize + i + 4 > (unsigned int)dataLen){ - DEBUG_MSG(DLVL_WARN, "Too big NALU detected (%u > %d) - skipping!", ThisNaluSize + i + 4, dataLen); - break; + while (i + 4 < (unsigned int)dataLen){ + if (nalLead){ + fillPacket("\000\000\000\001"+4-nalLead,nalLead, firstPack, video, keyframe, pkgPid, contPkg); + i += nalLead; + alreadySent += nalLead; + nalLead = 0; } - if (alreadySent + 4 > watKunnenWeIn1Ding){ - nalLead = 4 - (watKunnenWeIn1Ding-alreadySent); - fillPacket("\000\000\000\001",watKunnenWeIn1Ding-alreadySent, firstPack, video, keyframe, pkgPid, contPkg); + if (!ThisNaluSize){ + ThisNaluSize = (dataPointer[i] << 24) + (dataPointer[i+1] << 16) + (dataPointer[i+2] << 8) + dataPointer[i+3]; + if (ThisNaluSize + i + 4 > (unsigned int)dataLen){ + DEBUG_MSG(DLVL_WARN, "Too big NALU detected (%u > %d) - skipping!", ThisNaluSize + i + 4, dataLen); + break; + } + if (alreadySent + 4 > watKunnenWeIn1Ding){ + nalLead = 4 - (watKunnenWeIn1Ding-alreadySent); + fillPacket("\000\000\000\001",watKunnenWeIn1Ding-alreadySent, firstPack, video, keyframe, pkgPid, contPkg); + i += watKunnenWeIn1Ding-alreadySent; + alreadySent += watKunnenWeIn1Ding-alreadySent; + }else{ + fillPacket("\000\000\000\001",4, firstPack, video, keyframe, pkgPid, contPkg); + alreadySent += 4; + i += 4; + } + } + if (alreadySent + ThisNaluSize > watKunnenWeIn1Ding){ + fillPacket(dataPointer+i,watKunnenWeIn1Ding-alreadySent, firstPack, video, keyframe, pkgPid, contPkg); i += watKunnenWeIn1Ding-alreadySent; + ThisNaluSize -= watKunnenWeIn1Ding-alreadySent; alreadySent += watKunnenWeIn1Ding-alreadySent; }else{ - fillPacket("\000\000\000\001",4, firstPack, video, keyframe, pkgPid, contPkg); - alreadySent += 4; - i += 4; + fillPacket(dataPointer+i,ThisNaluSize, firstPack, video, keyframe, pkgPid, contPkg); + alreadySent += ThisNaluSize; + i += ThisNaluSize; + ThisNaluSize = 0; + } + if (alreadySent == watKunnenWeIn1Ding){ + packData.addStuffing(); + fillPacket(0, 0, firstPack, video, keyframe, pkgPid, contPkg); + firstPack = true; + break; } } - if (alreadySent + ThisNaluSize > watKunnenWeIn1Ding){ - fillPacket(dataPointer+i,watKunnenWeIn1Ding-alreadySent, firstPack, video, keyframe, pkgPid, contPkg); - i += watKunnenWeIn1Ding-alreadySent; - ThisNaluSize -= watKunnenWeIn1Ding-alreadySent; - alreadySent += watKunnenWeIn1Ding-alreadySent; - }else{ - fillPacket(dataPointer+i,ThisNaluSize, firstPack, video, keyframe, pkgPid, contPkg); - alreadySent += ThisNaluSize; - i += ThisNaluSize; - ThisNaluSize = 0; - } - if (alreadySent == watKunnenWeIn1Ding){ - packData.addStuffing(); - fillPacket(0, 0, firstPack, video, keyframe, pkgPid, contPkg); - firstPack = true; - break; - } + currPack++; } - currPack++; + }else{ + uint64_t offset = thisPacket.getInt("offset") * 90; + bs = TS::Packet::getPESVideoLeadIn(0, packTime, offset, true, Trk.bps); + fillPacket(bs.data(), bs.size(), firstPack, video, keyframe, pkgPid, contPkg); + + fillPacket(dataPointer, dataLen, firstPack, video, keyframe, pkgPid, contPkg); } }else if (Trk.type == "audio"){ long unsigned int tempLen = dataLen; if (Trk.codec == "AAC"){ tempLen += 7; } - bs = TS::Packet::getPESAudioLeadIn(tempLen, packTime, Trk.bps);// myMeta.tracks[thisPacket.getTrackId()].rate / 1000 ); + bs = TS::Packet::getPESAudioLeadIn(tempLen, packTime, Trk.bps); fillPacket(bs.data(), bs.size(), firstPack, video, keyframe, pkgPid, contPkg); if (Trk.codec == "AAC"){ bs = TS::getAudioHeader(dataLen, Trk.init);