From 41842227fa7e47ae3eb8144b8f273a1c01d549a2 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 13 May 2017 23:41:19 +0200 Subject: [PATCH] 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));