From 41842227fa7e47ae3eb8144b8f273a1c01d549a2 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 13 May 2017 23:41:19 +0200 Subject: [PATCH 1/2] Added support for Opus in Ogg input --- lib/opus.cpp | 35 +++++++++++++++++++++++++++++++++-- lib/opus.h | 1 + src/input/input_ogg.cpp | 41 +++++++++++++++++++++++++++++++---------- 3 files changed, 65 insertions(+), 12 deletions(-) diff --git a/lib/opus.cpp b/lib/opus.cpp index d1ad58f5..122a21d4 100644 --- a/lib/opus.cpp +++ b/lib/opus.cpp @@ -2,11 +2,42 @@ #include namespace Opus{ + + unsigned int Opus_getDuration(const char *part){ + const char config = part[0] >> 3; + const char code = part[0] & 3; + double dur = 0; + if (config < 14){ + switch (config % 4){ + case 0: dur = 10; break; + case 1: dur = 20; break; + case 2: dur = 40; break; + case 3: dur = 60; break; + } + } else if (config < 16){ + if (config % 2 == 0){ + dur = 10; + }else{ + dur = 20; + } + } else { + switch (config % 4){ + case 0: dur = 2.5; break; + case 1: dur = 5; break; + case 2: dur = 10; break; + case 3: dur = 20; break; + } + } + if (code == 0){return (unsigned int)dur;} + if (code < 3){return (unsigned int)(dur*2);} + return (unsigned int)(dur*(part[1] & 63)); + } + std::string Opus_prettyPacket(const char *part, int len){ if (len < 1){return "Invalid packet (0 byte length)";} std::stringstream r; - char config = part[0] >> 3; - char code = part[0] & 3; + const char config = part[0] >> 3; + const char code = part[0] & 3; if ((part[0] & 4) == 4){ r << "Stereo, "; }else{ diff --git a/lib/opus.h b/lib/opus.h index 0557225f..4c756728 100644 --- a/lib/opus.h +++ b/lib/opus.h @@ -1,6 +1,7 @@ #include namespace Opus{ + unsigned int Opus_getDuration(const char *part); std::string Opus_prettyPacket(const char *part, int len); } diff --git a/src/input/input_ogg.cpp b/src/input/input_ogg.cpp index c5a95237..56a364ce 100644 --- a/src/input/input_ogg.cpp +++ b/src/input/input_ogg.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "input_ogg.h" ///\todo Whar be Opus support? @@ -49,6 +50,7 @@ namespace Mist { capa["source_match"] = "/*.ogg"; capa["codecs"][0u][0u].append("theora"); capa["codecs"][0u][1u].append("vorbis"); + capa["codecs"][0u][1u].append("opus"); } bool inputOGG::setup(){ @@ -73,10 +75,7 @@ namespace Mist { theora::header tmpHead((char*)bosPage.getSegment(0), bosPage.getSegmentLen(0)); oggTracks[tid].codec = OGG::THEORA; oggTracks[tid].msPerFrame = (double)(tmpHead.getFRD() * 1000) / (double)tmpHead.getFRN(); //this should be: 1000/( tmpHead.getFRN()/ tmpHead.getFRD() ) - DEBUG_MSG(DLVL_DEVEL, "theora trackID: %u msperFrame %f ", tid, oggTracks[tid].msPerFrame); oggTracks[tid].KFGShift = tmpHead.getKFGShift(); //store KFGShift for granule calculations - DEVEL_MSG("read theora header size: %d, tid: %u", tmpHead.datasize, tid); - myMeta.tracks[tid].type = "video"; myMeta.tracks[tid].codec = "theora"; myMeta.tracks[tid].trackID = tid; @@ -88,6 +87,7 @@ namespace Mist { myMeta.tracks[tid].init += (char)(bosPage.getPayloadSize() & 0xFF); myMeta.tracks[tid].init.append(bosPage.getSegment(0), bosPage.getSegmentLen(0)); } + INFO_MSG("Track %lu is %s", tid, myMeta.tracks[tid].codec.c_str()); } if (memcmp(bosPage.getSegment(0) + 1, "vorbis", 6) == 0){ vorbis::header tmpHead((char*)bosPage.getSegment(0), bosPage.getSegmentLen(0)); @@ -106,6 +106,17 @@ namespace Mist { myMeta.tracks[tid].rate = tmpHead.getAudioSampleRate(); myMeta.tracks[tid].trackID = tid; myMeta.tracks[tid].channels = tmpHead.getAudioChannels(); + INFO_MSG("Track %lu is %s", tid, myMeta.tracks[tid].codec.c_str()); + } + if (memcmp(bosPage.getSegment(0), "OpusHead", 8) == 0){ + oggTracks[tid].codec = OGG::OPUS; + myMeta.tracks[tid].type = "audio"; + myMeta.tracks[tid].codec = "opus"; + myMeta.tracks[tid].rate = 48000; + myMeta.tracks[tid].trackID = tid; + myMeta.tracks[tid].init.assign(bosPage.getSegment(0), bosPage.getSegmentLen(0)); + myMeta.tracks[tid].channels = myMeta.tracks[tid].init[9]; + INFO_MSG("Track %lu is %s", tid, myMeta.tracks[tid].codec.c_str()); } } @@ -134,19 +145,14 @@ namespace Mist { // INFO_MSG("tid: %d",tid); //Parsing headers -// INFO_MSG("parsing headers for tid: %d",tid); - ///\todo make sure the header is ffmpeg compatible if (myMeta.tracks[tid].codec == "theora"){ - // INFO_MSG("theora"); for (unsigned int i = 0; i < myPage.getAllSegments().size(); i++){ unsigned long len = myPage.getSegmentLen(i); - INFO_MSG("Length for segment %d: %lu", i, len); theora::header tmpHead((char*)myPage.getSegment(i),len); if (!tmpHead.isHeader()){ //not copying the header anymore, should this check isHeader? DEBUG_MSG(DLVL_FAIL, "Theora Header read failed!"); return false; } - DEBUG_MSG(DLVL_WARN, "Theora header, type %d", tmpHead.getHeaderType()); switch (tmpHead.getHeaderType()){ //Case 0 is being handled by parseBeginOfStream case 1: { @@ -167,7 +173,7 @@ namespace Mist { } } - if (myMeta.tracks[tid].codec == "vorbis"){ + if (myMeta.tracks[tid].codec == "vorbis"){ for (unsigned int i = 0; i < myPage.getAllSegments().size(); i++){ unsigned long len = myPage.getSegmentLen(i); vorbis::header tmpHead((char*)myPage.getSegment(i), len); @@ -175,7 +181,6 @@ namespace Mist { DEBUG_MSG(DLVL_FAIL, "Header read failed!"); return false; } - DEBUG_MSG(DLVL_WARN, "Vorbis header, type %d", tmpHead.getHeaderType()); switch (tmpHead.getHeaderType()){ //Case 1 is being handled by parseBeginOfStream case 3: { @@ -205,10 +210,14 @@ namespace Mist { } } } + if (myMeta.tracks[tid].codec == "opus"){ + oggTracks[tid].parsedHeaders = true; + } } for (std::map::iterator it = oggTracks.begin(); it != oggTracks.end(); it++){ fseek(inFile, 0, SEEK_SET); + INFO_MSG("Finding first data for track %lu", it->first); position tmp = seekFirstData(it->first); if (tmp.trackID){ currentPositions.insert(tmp); @@ -250,6 +259,11 @@ namespace Mist { quitloop = false; continue; } + if (oggTracks[tid].codec == OGG::OPUS){ + if (std::string(oggTracks[tid].myPage.getSegment(0), 2) == "Op"){ + quitloop = false; + } + } if (oggTracks[tid].codec == OGG::VORBIS){ vorbis::header tmpHead((char*)oggTracks[tid].myPage.getSegment(0), oggTracks[tid].myPage.getSegmentLen(0)); if (tmpHead.isHeader()){ @@ -351,6 +365,10 @@ namespace Mist { // INFO_MSG("thisTime: %d", thisPacket.getTime()); } curPos.time += oggTracks[thisSegment.tid].msPerFrame; + } else if (oggTracks[thisSegment.tid].codec == OGG::OPUS){ + if (thisSegment.parts.size()){ + curPos.time += Opus::Opus_getDuration(thisSegment.parts.front().data()); + } } if (readFullPacket){ currentPositions.insert(curPos); @@ -362,6 +380,9 @@ namespace Mist { case OGG::VORBIS: return granule * oggTracks[tid].msPerFrame ; //= samples * samples per second break; + case OGG::OPUS: + return granule / 48; //always 48kHz + break; case OGG::THEORA:{ long long unsigned int parseGranuleUpper = granule >> oggTracks[tid].KFGShift ; long long unsigned int parseGranuleLower = (granule & ((1 << oggTracks[tid].KFGShift) - 1)); From b072ffb1394d8cc9c79c299bcd591bbc21b65926 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 13 May 2017 23:04:42 +0200 Subject: [PATCH 2/2] Fixed Ogg output Opus support --- lib/ogg.cpp | 2 ++ src/output/output_progressive_ogg.cpp | 17 +++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index 24bd4180..925e4a51 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -492,6 +492,8 @@ namespace OGG { tempGranule = (currentSegment.lastKeyFrameSeen << split) | currentSegment.framesSinceKeyFrame; } else if (codec == OGG::VORBIS){ tempGranule = currentSegment.lastKeyFrameSeen; + } else if (codec == OGG::OPUS){ + tempGranule = currentSegment.timeStamp*48; } return tempGranule; } diff --git a/src/output/output_progressive_ogg.cpp b/src/output/output_progressive_ogg.cpp index c081bd36..028875b1 100644 --- a/src/output/output_progressive_ogg.cpp +++ b/src/output/output_progressive_ogg.cpp @@ -133,8 +133,8 @@ namespace Mist { OGG::oggSegment newSegment; for (std::set::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ - parseInit(myMeta.tracks[*it].init, initData[*it]); if (myMeta.tracks[*it].codec == "theora"){ //get size and position of init data for this page. + parseInit(myMeta.tracks[*it].init, initData[*it]); pageBuffer[*it].codec = OGG::THEORA; pageBuffer[*it].totalFrames = 1; //starts at frame number 1, according to weird offDetectMeta function. std::string tempStr = initData[*it][0]; @@ -142,6 +142,7 @@ namespace Mist { pageBuffer[*it].split = tempHead.getKFGShift(); INFO_MSG("got theora KFG shift: %d", pageBuffer[*it].split); //looks OK. } else if (myMeta.tracks[*it].codec == "vorbis"){ + parseInit(myMeta.tracks[*it].init, initData[*it]); pageBuffer[*it].codec = OGG::VORBIS; pageBuffer[*it].totalFrames = 0; pageBuffer[*it].sampleRate = myMeta.tracks[*it].rate; @@ -155,17 +156,21 @@ namespace Mist { } else if (myMeta.tracks[*it].codec == "opus"){ pageBuffer[*it].totalFrames = 0; //? pageBuffer[*it].codec = OGG::OPUS; + initData[*it].push_back(myMeta.tracks[*it].init); + initData[*it].push_back(std::string("OpusTags\000\000\000\012MistServer\000\000\000\000", 26)); } pageBuffer[*it].clear(OGG::BeginOfStream, 0, *it, 0); //CREATES a (map)pageBuffer object, *it = id, pagetype=BOS - newSegment.dataString = initData[*it][0]; + newSegment.dataString = initData[*it].front(); + initData[*it].pop_front(); pageBuffer[*it].oggSegments.push_back(newSegment); pageBuffer[*it].sendTo(myConn, 0); //granule position of 0 } for (std::set::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ - newSegment.dataString = initData[*it][1]; - pageBuffer[*it].oggSegments.push_back(newSegment); - newSegment.dataString = initData[*it][2]; - pageBuffer[*it].oggSegments.push_back(newSegment); + while (initData[*it].size()){ + newSegment.dataString = initData[*it].front(); + initData[*it].pop_front(); + pageBuffer[*it].oggSegments.push_back(newSegment); + } while (pageBuffer[*it].oggSegments.size()){ pageBuffer[*it].sendTo(myConn, 0); //granule position of 0 }