Ogg support fixed and re-added. Squash of various commits made by Wouter Spruit.
This commit is contained in:
parent
142ef73f6c
commit
a47504b5cb
17 changed files with 944 additions and 456 deletions
23
Makefile
23
Makefile
|
@ -57,9 +57,9 @@ analysers: MistAnalyserMP4
|
|||
MistAnalyserMP4: src/analysers/mp4_analyser.cpp
|
||||
$(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@
|
||||
|
||||
#analysers: MistAnalyserOGG
|
||||
#MistAnalyserOGG: src/analysers/ogg_analyser.cpp
|
||||
# $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@
|
||||
analysers: MistAnalyserOGG
|
||||
MistAnalyserOGG: src/analysers/ogg_analyser.cpp
|
||||
$(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@
|
||||
|
||||
analysers: MistInfo
|
||||
MistInfo: src/analysers/info.cpp
|
||||
|
@ -77,11 +77,11 @@ MistInFLV: override CPPFLAGS += "-DINPUTTYPE=\"input_flv.h\""
|
|||
MistInFLV: src/input/mist_in.cpp src/input/input.cpp src/input/input_flv.cpp
|
||||
$(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@
|
||||
|
||||
#inputs: MistInOGG
|
||||
#MistInOGG: override LDLIBS += $(THREADLIB)
|
||||
#MistInOGG: override CPPFLAGS += "-DINPUTTYPE=\"input_ogg.h\""
|
||||
#MistInOGG: src/input/mist_in.cpp src/input/input.cpp src/input/input_ogg.cpp
|
||||
# $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@
|
||||
inputs: MistInOGG
|
||||
MistInOGG: override LDLIBS += $(THREADLIB)
|
||||
MistInOGG: override CPPFLAGS += "-DINPUTTYPE=\"input_ogg.h\""
|
||||
MistInOGG: src/input/mist_in.cpp src/input/input.cpp src/input/input_ogg.cpp
|
||||
$(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@
|
||||
|
||||
inputs: MistInBuffer
|
||||
MistInBuffer: override LDLIBS += $(THREADLIB)
|
||||
|
@ -95,6 +95,13 @@ MistOutFLV: override CPPFLAGS += "-DOUTPUTTYPE=\"output_progressive_flv.h\""
|
|||
MistOutFLV: src/output/mist_out.cpp src/output/output_http.cpp src/output/output.cpp src/output/output_progressive_flv.cpp
|
||||
$(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@
|
||||
|
||||
outputs: MistOutOGG
|
||||
MistOutOGG: override LDLIBS += $(THREADLIB)
|
||||
MistOutOGG: override LDLIBS += $(GEOIP)
|
||||
MistOutOGG: override CPPFLAGS += "-DOUTPUTTYPE=\"output_progressive_ogg.h\""
|
||||
MistOutOGG: src/output/mist_out.cpp src/output/output_http.cpp src/output/output.cpp src/output/output_progressive_ogg.cpp
|
||||
$(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@
|
||||
|
||||
outputs: MistOutMP4
|
||||
MistOutMP4: override LDLIBS += $(THREADLIB)
|
||||
MistOutMP4: override CPPFLAGS += "-DOUTPUTTYPE=\"output_progressive_mp4.h\""
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace Analysers {
|
|||
break;
|
||||
}
|
||||
case DTSC::DTSC_V2: {
|
||||
std::cout << "DTSCv2 packet: " << F.getPacket().getScan().toPrettyString() << std::endl;
|
||||
std::cout << "DTSCv2 packet (Track " << F.getPacket().getTrackId() << ", time " << F.getPacket().getTime() << "): " << F.getPacket().getScan().toPrettyString() << std::endl;
|
||||
break;
|
||||
}
|
||||
case DTSC::DTSC_HEAD: {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
@ -8,41 +8,83 @@
|
|||
#include <mist/ogg.h>
|
||||
#include <mist/config.h>
|
||||
#include <mist/theora.h>
|
||||
|
||||
///\todo rewrite this analyser.
|
||||
namespace Analysers {
|
||||
std::string Opus_prettyPacket(char * part,int len){
|
||||
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;
|
||||
if ((part[0] & 4) == 4){r << "Stereo, ";}else{r << "Mono, ";}
|
||||
if ((part[0] & 4) == 4){
|
||||
r << "Stereo, ";
|
||||
} else {
|
||||
r << "Mono, ";
|
||||
}
|
||||
if (config < 14){
|
||||
r << "SILK, ";
|
||||
if (config < 4){r << "NB, ";}
|
||||
if (config < 8 && config > 3){r << "MB, ";}
|
||||
if (config < 14 && config > 7){r << "WB, ";}
|
||||
if (config % 4 == 0){r << "10ms";}
|
||||
if (config % 4 == 1){r << "20ms";}
|
||||
if (config % 4 == 2){r << "40ms";}
|
||||
if (config % 4 == 3){r << "60ms";}
|
||||
if (config < 4){
|
||||
r << "NB, ";
|
||||
}
|
||||
if (config < 8 && config > 3){
|
||||
r << "MB, ";
|
||||
}
|
||||
if (config < 14 && config > 7){
|
||||
r << "WB, ";
|
||||
}
|
||||
if (config % 4 == 0){
|
||||
r << "10ms";
|
||||
}
|
||||
if (config % 4 == 1){
|
||||
r << "20ms";
|
||||
}
|
||||
if (config % 4 == 2){
|
||||
r << "40ms";
|
||||
}
|
||||
if (config % 4 == 3){
|
||||
r << "60ms";
|
||||
}
|
||||
}
|
||||
if (config < 16 && config > 13){
|
||||
r << "Hybrid, ";
|
||||
if (config < 14){r << "SWB, ";}else{r << "FB, ";}
|
||||
if (config % 2 == 0){r << "10ms";}else{r << "20ms";}
|
||||
if (config < 14){
|
||||
r << "SWB, ";
|
||||
} else {
|
||||
r << "FB, ";
|
||||
}
|
||||
if (config % 2 == 0){
|
||||
r << "10ms";
|
||||
} else {
|
||||
r << "20ms";
|
||||
}
|
||||
}
|
||||
if (config > 15){
|
||||
r << "CELT, ";
|
||||
if (config < 20){r << "NB, ";}
|
||||
if (config < 24 && config > 19){r << "WB, ";}
|
||||
if (config < 28 && config > 23){r << "SWB, ";}
|
||||
if (config > 27){r << "FB, ";}
|
||||
if (config % 4 == 0){r << "2.5ms";}
|
||||
if (config % 4 == 1){r << "5ms";}
|
||||
if (config % 4 == 2){r << "10ms";}
|
||||
if (config % 4 == 3){r << "20ms";}
|
||||
if (config < 20){
|
||||
r << "NB, ";
|
||||
}
|
||||
if (config < 24 && config > 19){
|
||||
r << "WB, ";
|
||||
}
|
||||
if (config < 28 && config > 23){
|
||||
r << "SWB, ";
|
||||
}
|
||||
if (config > 27){
|
||||
r << "FB, ";
|
||||
}
|
||||
if (config % 4 == 0){
|
||||
r << "2.5ms";
|
||||
}
|
||||
if (config % 4 == 1){
|
||||
r << "5ms";
|
||||
}
|
||||
if (config % 4 == 2){
|
||||
r << "10ms";
|
||||
}
|
||||
if (config % 4 == 3){
|
||||
r << "20ms";
|
||||
}
|
||||
}
|
||||
if (code == 0){
|
||||
r << ": 1 packet (" << (len - 1) << "b)";
|
||||
|
@ -80,28 +122,24 @@ namespace Analysers{
|
|||
std::map<int, std::string> sn2Codec;
|
||||
std::string oggBuffer;
|
||||
OGG::Page oggPage;
|
||||
int kfgshift;
|
||||
//Read all of std::cin to oggBuffer
|
||||
//while stream busy
|
||||
while (std::cin.good()){
|
||||
for (unsigned int i = 0; (i < 1024) && (std::cin.good()); i++){
|
||||
oggBuffer += std::cin.get();
|
||||
}
|
||||
//while OGG::page check function read
|
||||
while (oggPage.read(oggBuffer)){//reading ogg to string
|
||||
while (oggPage.read(stdin)){ //reading ogg to string
|
||||
//print the Ogg page details, if requested
|
||||
if (conf.getBool("pages")){
|
||||
std::cout << oggPage.toPrettyString() << std::endl;
|
||||
printf("%s", oggPage.toPrettyString().c_str());
|
||||
}
|
||||
|
||||
//attempt to detect codec if this is the first page of a stream
|
||||
if (oggPage.getHeaderType() & OGG::BeginOfStream){
|
||||
if (memcmp("theora",oggPage.getFullPayload() + 1,6) == 0){
|
||||
if (memcmp("theora", oggPage.getSegment(0) + 1, 6) == 0){
|
||||
sn2Codec[oggPage.getBitstreamSerialNumber()] = "Theora";
|
||||
}
|
||||
if (memcmp("vorbis",oggPage.getFullPayload() + 1,6) == 0){
|
||||
if (memcmp("vorbis", oggPage.getSegment(0) + 1, 6) == 0){
|
||||
sn2Codec[oggPage.getBitstreamSerialNumber()] = "Vorbis";
|
||||
}
|
||||
if (memcmp("OpusHead",oggPage.getFullPayload(),8) == 0){
|
||||
if (memcmp("OpusHead", oggPage.getSegment(0), 8) == 0){
|
||||
sn2Codec[oggPage.getBitstreamSerialNumber()] = "Opus";
|
||||
}
|
||||
if (sn2Codec[oggPage.getBitstreamSerialNumber()] != ""){
|
||||
|
@ -114,36 +152,43 @@ namespace Analysers{
|
|||
|
||||
if (sn2Codec[oggPage.getBitstreamSerialNumber()] == "Theora"){
|
||||
std::cout << " Theora data" << std::endl;
|
||||
int offset = 0;
|
||||
for (unsigned int i = 0; i < oggPage.getSegmentTableDeque().size(); i++){
|
||||
theora::header tmpHeader;
|
||||
int len = oggPage.getSegmentTableDeque()[i];
|
||||
if (tmpHeader.read(oggPage.getFullPayload()+offset,len)){
|
||||
std::cout << tmpHeader.toPrettyString(2);
|
||||
static unsigned int numParts = 0;
|
||||
static unsigned int keyCount = 0;
|
||||
for (unsigned int i = 0; i < oggPage.getAllSegments().size(); i++){
|
||||
theora::header tmpHeader((char *)oggPage.getSegment(i), oggPage.getAllSegments()[i].size());
|
||||
if (tmpHeader.isHeader()){
|
||||
if (tmpHeader.getHeaderType() == 0){
|
||||
kfgshift = tmpHeader.getKFGShift();
|
||||
}
|
||||
theora::frame tmpFrame;
|
||||
if (tmpFrame.read(oggPage.getFullPayload()+offset,len)){
|
||||
std::cout << tmpFrame.toPrettyString(2);
|
||||
} else {
|
||||
if (!(oggPage.getHeaderType() == OGG::Continued) && tmpHeader.getFTYPE() == 0){ //if keyframe
|
||||
std::cout << "keyframe granule: " << (oggPage.getGranulePosition() >> kfgshift) << std::endl;
|
||||
std::cout << "keyframe " << keyCount << " has " << numParts << " parts, yo." << std::endl;
|
||||
numParts = 0;
|
||||
keyCount++;
|
||||
}
|
||||
offset += len;
|
||||
if (oggPage.getHeaderType() != OGG::Continued || i){
|
||||
numParts++;
|
||||
}
|
||||
}
|
||||
std::cout << tmpHeader.toPrettyString(4);
|
||||
|
||||
}
|
||||
} else if (sn2Codec[oggPage.getBitstreamSerialNumber()] == "Vorbis"){
|
||||
std::cout << " Vorbis data" << std::endl;
|
||||
int offset = 0;
|
||||
for (unsigned int i = 0; i < oggPage.getSegmentTableDeque().size(); i++){
|
||||
vorbis::header tmpHeader;
|
||||
int len = oggPage.getSegmentTableDeque()[i];
|
||||
if (tmpHeader.read(oggPage.getFullPayload()+offset,len)){
|
||||
std::cout << tmpHeader.toPrettyString(2);
|
||||
for (unsigned int i = 0; i < oggPage.getAllSegments().size(); i++){
|
||||
int len = oggPage.getAllSegments()[i].size();
|
||||
vorbis::header tmpHeader((char*)oggPage.getSegment(i), len);
|
||||
if (tmpHeader.isHeader()){
|
||||
std::cout << tmpHeader.toPrettyString(4);
|
||||
}
|
||||
offset += len;
|
||||
}
|
||||
} else if (sn2Codec[oggPage.getBitstreamSerialNumber()] == "Opus"){
|
||||
std::cout << " Opus data" << std::endl;
|
||||
int offset = 0;
|
||||
for (unsigned int i = 0; i < oggPage.getSegmentTableDeque().size(); i++){
|
||||
int len = oggPage.getSegmentTableDeque()[i];
|
||||
char * part = oggPage.getFullPayload() + offset;
|
||||
for (unsigned int i = 0; i < oggPage.getAllSegments().size(); i++){
|
||||
int len = oggPage.getAllSegments()[i].size();
|
||||
const char * part = oggPage.getSegment(i);
|
||||
if (len >= 8 && memcmp(part, "Opus", 4) == 0){
|
||||
if (memcmp(part, "OpusHead", 8) == 0){
|
||||
std::cout << " Version: " << (int)(part[8]) << std::endl;
|
||||
|
@ -159,14 +204,14 @@ namespace Analysers{
|
|||
if (memcmp(part, "OpusTags", 8) == 0){
|
||||
unsigned int vendor_len = part[8] + (part[9] << 8) + (part[10] << 16) + (part[11] << 24);
|
||||
std::cout << " Vendor: " << std::string(part + 12, vendor_len) << std::endl;
|
||||
char * str_data = part+12+vendor_len;
|
||||
const char * str_data = part + 12 + vendor_len;
|
||||
unsigned int strings = str_data[0] + (str_data[1] << 8) + (str_data[2] << 16) + (str_data[3] << 24);
|
||||
std::cout << " Tags: (" << strings << ")" << std::endl;
|
||||
str_data += 4;
|
||||
for (unsigned int j = 0; j < strings; j++){
|
||||
unsigned int strlen = str_data[0] + (str_data[1] << 8) + (str_data[2] << 16) + (str_data[3] << 24);
|
||||
str_data += 4;
|
||||
std::cout << " [" << j << "] " << std::string(str_data, strlen) << std::endl;
|
||||
std::cout << " [" << j << "] " << std::string((char *) str_data, strlen) << std::endl;
|
||||
str_data += strlen;
|
||||
}
|
||||
}
|
||||
|
@ -176,7 +221,7 @@ namespace Analysers{
|
|||
offset += len;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -186,3 +231,4 @@ int main(int argc, char ** argv){
|
|||
return Analysers::analyseOGG(argc, argv);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ namespace Controller {
|
|||
data["online"] = 0;
|
||||
return;
|
||||
}else{
|
||||
if (!getMeta && data["meta"].packedSize() > 1*1024 * data["meta"]["tracks"].size()){
|
||||
if (!getMeta && data["meta"].packedSize() > 10*1024 * data["meta"]["tracks"].size()){
|
||||
DEBUG_MSG(DLVL_WARN, "Metadata for stream %s is quite big (%u b) - assuming corruption and forcing reload", name.c_str(), data["meta"].packedSize());
|
||||
getMeta = true;
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ namespace Mist {
|
|||
|
||||
void Input::userCallback(char * data, size_t len, unsigned int id){
|
||||
for (int i = 0; i < 5; i++){
|
||||
long tid = ((long)(data[i*6]) << 24) | ((long)(data[i*6+1]) << 16) | ((long)(data[i*6+2]) << 8) | ((long)(data[i*6+3]));
|
||||
unsigned long tid = ((unsigned long)(data[i*6]) << 24) | ((unsigned long)(data[i*6+1]) << 16) | ((unsigned long)(data[i*6+2]) << 8) | ((unsigned long)(data[i*6+3]));
|
||||
if (tid){
|
||||
long keyNum = ((long)(data[i*6+4]) << 8) | ((long)(data[i*6+5]));
|
||||
unsigned long keyNum = ((unsigned long)(data[i*6+4]) << 8) | ((unsigned long)(data[i*6+5]));
|
||||
bufferFrame(tid, keyNum + 1);//Try buffer next frame
|
||||
}
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ namespace Mist {
|
|||
|
||||
|
||||
if (!isBuffer){
|
||||
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
bufferFrame(it->first, 1);
|
||||
}
|
||||
}
|
||||
|
@ -204,8 +204,8 @@ namespace Mist {
|
|||
DEBUG_MSG(DLVL_DONTEVEN,"Parsing the header");
|
||||
selectedTracks.clear();
|
||||
std::stringstream trackSpec;
|
||||
for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
DEBUG_MSG(DLVL_VERYHIGH, "Track %d encountered", it->first);
|
||||
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
|
||||
DEBUG_MSG(DLVL_VERYHIGH, "Track %u encountered", it->first);
|
||||
if (trackSpec.str() != ""){
|
||||
trackSpec << " ";
|
||||
}
|
||||
|
@ -218,16 +218,18 @@ namespace Mist {
|
|||
trackSelect(trackSpec.str());
|
||||
|
||||
bool hasKeySizes = true;
|
||||
for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
if (!it->second.keySizes.size()){
|
||||
hasKeySizes = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
INFO_MSG("%s", (hasKeySizes ? "hasKeysizes" : "noHasKeysizes"));
|
||||
if (hasKeySizes){
|
||||
for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
char tmpId[20];
|
||||
sprintf(tmpId, "%d", it->first);
|
||||
sprintf(tmpId, "%u", it->first);
|
||||
INFO_MSG("Making page %s", std::string(config->getString("streamname") + tmpId).c_str());
|
||||
indexPages[it->first].init(config->getString("streamname") + tmpId, 8 * 1024, true);//Pages of 8kb in size, room for 512 parts.
|
||||
bool newData = true;
|
||||
for (int i = 0; i < it->second.keys.size(); i++){
|
||||
|
@ -252,7 +254,7 @@ namespace Mist {
|
|||
getNext();
|
||||
|
||||
while(lastPack){//loop through all
|
||||
int tid = lastPack.getTrackId();
|
||||
unsigned int tid = lastPack.getTrackId();
|
||||
if (!tid){
|
||||
getNext(false);
|
||||
continue;
|
||||
|
@ -269,11 +271,11 @@ namespace Mist {
|
|||
curData[tid].curOffset = 0;
|
||||
curData[tid].firstTime = myMeta.tracks[tid].keys[0].getTime();
|
||||
|
||||
char tmpId[20];
|
||||
sprintf(tmpId, "%d", tid);
|
||||
indexPages[tid].init(config->getString("streamname") + tmpId, 8 * 1024, true);//Pages of 8kb in size, room for 512 parts.
|
||||
char tmpId[80];
|
||||
snprintf(tmpId, 80, "%s%u", config->getString("streamname").c_str(), tid);
|
||||
indexPages[tid].init(tmpId, 8 * 1024, true);//Pages of 8kb in size, room for 512 parts.
|
||||
}
|
||||
if (myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getParts() == curData[tid].partNum){
|
||||
if (myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getParts() + 1 == curData[tid].partNum){
|
||||
if (curData[tid].dataSize > 8 * 1024 * 1024) {
|
||||
pagesByTrack[tid][bookKeeping[tid].first] = curData[tid];
|
||||
bookKeeping[tid].first += curData[tid].keyNum;
|
||||
|
@ -288,25 +290,29 @@ namespace Mist {
|
|||
curData[tid].dataSize += lastPack.getDataLen();
|
||||
curData[tid].partNum ++;
|
||||
bookKeeping[tid].curPart ++;
|
||||
DEBUG_MSG(DLVL_INSANE, "Track %ld:%llu (%db) on page %d, being part %d of key %d", lastPack.getTrackId(), lastPack.getTime(), lastPack.getDataLen(), bookKeeping[tid].first, curData[tid].partNum, curData[tid].keyNum);
|
||||
DEBUG_MSG(DLVL_DONTEVEN, "Track %ld:%llu on page %d@%llu (len:%d), being part %d of key %d", lastPack.getTrackId(), lastPack.getTime(), bookKeeping[tid].first, curData[tid].dataSize, lastPack.getDataLen(), curData[tid].partNum, bookKeeping[tid].first+curData[tid].keyNum);
|
||||
getNext(false);
|
||||
}
|
||||
for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
|
||||
if (curData.count(it->first) && !pagesByTrack[it->first].count(bookKeeping[it->first].first)){
|
||||
pagesByTrack[it->first][bookKeeping[it->first].first] = curData[it->first];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
if (!pagesByTrack.count(it->first)){
|
||||
DEBUG_MSG(DLVL_WARN, "No pages for track %d found", it->first);
|
||||
}else{
|
||||
DEBUG_MSG(DLVL_MEDIUM, "Track %d (%s) split into %lu pages", it->first, myMeta.tracks[it->first].codec.c_str(), pagesByTrack[it->first].size());
|
||||
for (std::map<int, DTSCPageData>::iterator it2 = pagesByTrack[it->first].begin(); it2 != pagesByTrack[it->first].end(); it2++){
|
||||
DEBUG_MSG(DLVL_VERYHIGH, "Page %u-%u, (%llu bytes)", it2->first, it2->first + it2->second.keyNum - 1, it2->second.dataSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Input::bufferFrame(int track, int keyNum){
|
||||
bool Input::bufferFrame(unsigned int track, unsigned int keyNum){
|
||||
if (keyNum < 1){keyNum = 1;}
|
||||
if (!pagesByTrack.count(track)){
|
||||
return false;
|
||||
|
@ -315,21 +321,22 @@ namespace Mist {
|
|||
if (it != pagesByTrack[track].begin()){
|
||||
it--;
|
||||
}
|
||||
int pageNum = it->first;
|
||||
unsigned int pageNum = it->first;
|
||||
pageCounter[track][pageNum] = 15;///Keep page 15seconds in memory after last use
|
||||
|
||||
DEBUG_MSG(DLVL_DONTEVEN, "Attempting to buffer %d:%d on page %d", track, keyNum, pageNum);
|
||||
DEBUG_MSG(DLVL_DONTEVEN, "Attempting to buffer page %u key %d->%d", track, keyNum, pageNum);
|
||||
if (dataPages[track].count(pageNum)){
|
||||
return true;
|
||||
}
|
||||
char pageId[100];
|
||||
int pageIdLen = sprintf(pageId, "%s%d_%d", config->getString("streamname").c_str(), track, pageNum);
|
||||
int pageIdLen = snprintf(pageId, 100, "%s%u_%u", config->getString("streamname").c_str(), track, pageNum);
|
||||
std::string tmpString(pageId, pageIdLen);
|
||||
#ifdef __CYGWIN__
|
||||
dataPages[track][pageNum].init(tmpString, 26 * 1024 * 1024, true);
|
||||
#else
|
||||
dataPages[track][pageNum].init(tmpString, it->second.dataSize, true);
|
||||
#endif
|
||||
DEBUG_MSG(DLVL_HIGH, "Buffering track %u page %u through %u datasize: %llu", track, pageNum, pageNum-1 + it->second.keyNum, it->second.dataSize);
|
||||
|
||||
std::stringstream trackSpec;
|
||||
trackSpec << track;
|
||||
|
@ -354,6 +361,7 @@ namespace Mist {
|
|||
DEBUG_MSG(DLVL_WARN, "Trying to write %u bytes on pos %llu where size is %llu (time: %llu / %llu, track %u page %u)", lastPack.getDataLen(), it->second.curOffset, pagesByTrack[track][pageNum].dataSize, lastPack.getTime(), stopTime, track, pageNum);
|
||||
break;
|
||||
}else{
|
||||
// DEBUG_MSG(DLVL_WARN, "Writing %u bytes on pos %llu where size is %llu (time: %llu / %llu, track %u page %u)", lastPack.getDataLen(), it->second.curOffset, pagesByTrack[track][pageNum].dataSize, lastPack.getTime(), stopTime, track, pageNum);
|
||||
memcpy(dataPages[track][pageNum].mapped + it->second.curOffset, lastPack.getData(), lastPack.getDataLen());
|
||||
it->second.curOffset += lastPack.getDataLen();
|
||||
}
|
||||
|
@ -365,7 +373,7 @@ namespace Mist {
|
|||
break;
|
||||
}
|
||||
}
|
||||
DEBUG_MSG(DLVL_HIGH, "Done buffering page %d for track %d", pageNum, track);
|
||||
DEBUG_MSG(DLVL_DEVEL, "Done buffering page %u for track %u", pageNum, track);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace Mist {
|
|||
virtual void userCallback(char * data, size_t len, unsigned int id);
|
||||
|
||||
void parseHeader();
|
||||
bool bufferFrame(int track, int keyNum);
|
||||
bool bufferFrame(unsigned int track, unsigned int keyNum);
|
||||
|
||||
unsigned int packTime;///Media-timestamp of the last packet.
|
||||
int lastActive;///Timestamp of the last time we received or sent something.
|
||||
|
@ -53,7 +53,7 @@ namespace Mist {
|
|||
int playing;
|
||||
unsigned int playUntil;
|
||||
unsigned int benchMark;
|
||||
std::set<int> selectedTracks;
|
||||
std::set<unsigned int> selectedTracks;
|
||||
|
||||
bool isBuffer;
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace Mist {
|
|||
inputBuffer::~inputBuffer(){
|
||||
if (myMeta.tracks.size()){
|
||||
DEBUG_MSG(DLVL_DEVEL, "Cleaning up, removing last keyframes");
|
||||
for(std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
for(std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
while (removeKey(it->first)){}
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ namespace Mist {
|
|||
void inputBuffer::updateMeta(){
|
||||
long long unsigned int firstms = 0xFFFFFFFFFFFFFFFFull;
|
||||
long long unsigned int lastms = 0;
|
||||
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
if (it->second.firstms < firstms){
|
||||
firstms = it->second.firstms;
|
||||
}
|
||||
|
@ -116,14 +116,14 @@ namespace Mist {
|
|||
void inputBuffer::removeUnused(){
|
||||
//find the earliest video keyframe stored
|
||||
unsigned int firstVideo = 1;
|
||||
for(std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
for(std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
if (it->second.type == "video"){
|
||||
if (it->second.firstms < firstVideo || firstVideo == 1){
|
||||
firstVideo = it->second.firstms;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
for(std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
//non-video tracks need to have a second keyframe that is <= firstVideo
|
||||
if (it->second.type != "video"){
|
||||
if (it->second.keys.size() < 2 || it->second.keys[1].getTime() > firstVideo){
|
||||
|
|
|
@ -9,33 +9,52 @@
|
|||
#include <mist/ogg.h>
|
||||
#include <mist/defines.h>
|
||||
#include <mist/bitstream.h>
|
||||
|
||||
#include "input_ogg.h"
|
||||
|
||||
///\todo Whar be Opus support?
|
||||
|
||||
namespace Mist {
|
||||
|
||||
JSON::Value segment::toJSON(OGG::oggCodec myCodec){
|
||||
JSON::Value retval;
|
||||
retval["time"] = (long long int)time;
|
||||
retval["trackid"] = (long long int)tid;
|
||||
std::string tmpString = "";
|
||||
for (unsigned int i = 0; i < parts.size(); i++){
|
||||
tmpString += parts[i];
|
||||
}
|
||||
retval["data"] = tmpString;
|
||||
// INFO_MSG("Setting bpos for packet on track %llu, time %llu, to %llu", tid, time, bytepos);
|
||||
retval["bpos"] = (long long int)bytepos;
|
||||
if (myCodec == OGG::THEORA){
|
||||
if (!theora::isHeader(tmpString.data(), tmpString.size())){
|
||||
theora::header tmpHeader((char*)tmpString.data(), tmpString.size());
|
||||
if (tmpHeader.getFTYPE() == 0){
|
||||
retval["keyframe"] = 1LL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
unsigned long oggTrack::getBlockSize(unsigned int vModeIndex){ //WTF!!?
|
||||
return blockSize[vModes[vModeIndex].blockFlag];
|
||||
|
||||
}
|
||||
*/
|
||||
inputOGG::inputOGG(Util::Config * cfg) : Input(cfg){
|
||||
capa["name"] = "OGG";
|
||||
capa["decs"] = "Enables OGG Input";
|
||||
capa["desc"] = "Enables OGG input";
|
||||
capa["codecs"][0u][0u].append("theora");
|
||||
capa["codecs"][0u][1u].append("vorbis");
|
||||
}
|
||||
|
||||
bool inputOGG::setup(){
|
||||
if (config->getString("input") == "-"){
|
||||
std::cerr << "Input from stdin not yet supported" << std::endl;
|
||||
std::cerr << "Input from stream not yet supported" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!config->getString("streamname").size()){
|
||||
if (config->getString("output") == "-") {
|
||||
std::cerr << "Output to stdout not yet supported" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
if (config->getString("output") != "-") {
|
||||
std::cerr << "File output in player mode not supported" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//open File
|
||||
inFile = fopen(config->getString("input").c_str(), "r");
|
||||
|
@ -45,223 +64,362 @@ namespace Mist {
|
|||
return true;
|
||||
}
|
||||
|
||||
///\todo check if all trackID (tid) instances are replaced with bitstream serial numbers
|
||||
void inputOGG::parseBeginOfStream(OGG::Page & bosPage){
|
||||
long long int tid = snum2tid.size() + 1;
|
||||
snum2tid[bosPage.getBitstreamSerialNumber()] = tid;
|
||||
if (!memcmp(bosPage.getFullPayload() + 1, "theora", 6)) {
|
||||
oggTracks[tid].codec = THEORA;
|
||||
theora::header tmpHead(bosPage.getFullPayload(), bosPage.getPayloadSize());
|
||||
oggTracks[tid].msPerFrame = (double)(tmpHead.getFRD() * 1000) / tmpHead.getFRN();
|
||||
//long long int tid = snum2tid.size() + 1;
|
||||
unsigned int tid = bosPage.getBitstreamSerialNumber();
|
||||
if (memcmp(bosPage.getSegment(0) + 1, "theora", 6) == 0){
|
||||
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;
|
||||
myMeta.tracks[tid].fpks = (tmpHead.getFRN() * 1000) / tmpHead.getFRD();
|
||||
myMeta.tracks[tid].height = tmpHead.getPICH();
|
||||
myMeta.tracks[tid].width = tmpHead.getPICW();
|
||||
if (!myMeta.tracks[tid].init.size()){
|
||||
myMeta.tracks[tid].init = (char)((bosPage.getPayloadSize() >> 8) & 0xFF);
|
||||
myMeta.tracks[tid].init += (char)(bosPage.getPayloadSize() & 0xFF);
|
||||
myMeta.tracks[tid].init.append(bosPage.getSegment(0), bosPage.getSegmentLen(0));
|
||||
}
|
||||
if (!memcmp(bosPage.getFullPayload() + 1, "vorbis", 6)) {
|
||||
oggTracks[tid].codec = VORBIS;
|
||||
vorbis::header tmpHead(bosPage.getFullPayload(), bosPage.getPayloadSize());
|
||||
oggTracks[tid].msPerFrame = (double)1000 / ntohl(tmpHead.getAudioSampleRate());
|
||||
}
|
||||
if (memcmp(bosPage.getSegment(0) + 1, "vorbis", 6) == 0){
|
||||
vorbis::header tmpHead((char*)bosPage.getSegment(0), bosPage.getSegmentLen(0));
|
||||
|
||||
oggTracks[tid].codec = OGG::VORBIS;
|
||||
oggTracks[tid].msPerFrame = (double)1000.0f / tmpHead.getAudioSampleRate();
|
||||
DEBUG_MSG(DLVL_DEVEL, "vorbis trackID: %d msperFrame %f ", tid, oggTracks[tid].msPerFrame);
|
||||
oggTracks[tid].channels = tmpHead.getAudioChannels();
|
||||
oggTracks[tid].blockSize[0] = 1 << tmpHead.getBlockSize0();
|
||||
oggTracks[tid].blockSize[1] = 1 << tmpHead.getBlockSize1();
|
||||
//Abusing .contBuffer for temporarily storing the idHeader
|
||||
bosPage.getSegment(0, oggTracks[tid].contBuffer);
|
||||
|
||||
myMeta.tracks[tid].type = "audio";
|
||||
myMeta.tracks[tid].codec = "vorbis";
|
||||
myMeta.tracks[tid].rate = tmpHead.getAudioSampleRate();
|
||||
myMeta.tracks[tid].trackID = tid;
|
||||
myMeta.tracks[tid].channels = tmpHead.getAudioChannels();
|
||||
}
|
||||
}
|
||||
|
||||
bool inputOGG::readHeader(){
|
||||
JSON::Value lastPack;
|
||||
if (!inFile) {
|
||||
return false;
|
||||
}
|
||||
//See whether a separate header file exists.
|
||||
DTSC::File tmp(config->getString("input") + ".dtsh");
|
||||
if (tmp) {
|
||||
myMeta = tmp.getMeta();
|
||||
return true;
|
||||
}
|
||||
//Create header file from OGG data
|
||||
OGG::Page myPage;
|
||||
fseek(inFile, 0, SEEK_SET);
|
||||
OGG::Page tmpPage;
|
||||
long long int lastBytePos = 0;
|
||||
while (tmpPage.read(inFile)) {
|
||||
DEBUG_MSG(DLVL_WARN,"Read a page");
|
||||
if (tmpPage.getHeaderType() & OGG::BeginOfStream){
|
||||
parseBeginOfStream(tmpPage);
|
||||
DEBUG_MSG(DLVL_WARN,"Read BOS page for stream %lu, now track %lld", tmpPage.getBitstreamSerialNumber(), snum2tid[tmpPage.getBitstreamSerialNumber()]);
|
||||
while (myPage.read(inFile)){ //assumes all headers are sent before any data
|
||||
unsigned int tid = myPage.getBitstreamSerialNumber();
|
||||
if (myPage.getHeaderType() & OGG::BeginOfStream){
|
||||
parseBeginOfStream(myPage);
|
||||
INFO_MSG("Read BeginOfStream for track %d", tid);
|
||||
continue; //Continue reading next pages
|
||||
}
|
||||
int offset = 0;
|
||||
long long int tid = snum2tid[tmpPage.getBitstreamSerialNumber()];
|
||||
for (std::deque<unsigned int>::iterator it = tmpPage.getSegmentTableDeque().begin(); it != tmpPage.getSegmentTableDeque().end(); it++) {
|
||||
if (oggTracks[tid].parsedHeaders) {
|
||||
DEBUG_MSG(DLVL_WARN,"Parsing a page segment on track %lld", tid);
|
||||
if ((it == (tmpPage.getSegmentTableDeque().end() - 1)) && (int)(tmpPage.getPageSegments()) == 255 && (int)(tmpPage.getSegmentTable()[254]) == 255) {
|
||||
oggTracks[tid].contBuffer.append(tmpPage.getFullPayload() + offset, (*it));
|
||||
} else {
|
||||
lastPack["trackid"] = tid;
|
||||
lastPack["time"] = (long long)oggTracks[tid].lastTime;
|
||||
if (oggTracks[tid].contBuffer.size()) {
|
||||
lastPack["data"] = oggTracks[tid].contBuffer + std::string(tmpPage.getFullPayload() + offset, (*it));
|
||||
oggTracks[tid].contBuffer.clear();
|
||||
} else {
|
||||
lastPack["data"] = std::string(tmpPage.getFullPayload() + offset, (*it));
|
||||
}
|
||||
if (oggTracks[tid].codec == VORBIS) {
|
||||
unsigned int blockSize = 0;
|
||||
Utils::bitstreamLSBF packet;
|
||||
packet.append(lastPack["data"].asString());
|
||||
if (!packet.get(1)) {
|
||||
blockSize = oggTracks[tid].blockSize[oggTracks[tid].vModes[packet.get(vorbis::ilog(oggTracks[tid].vModes.size() - 1))].blockFlag];
|
||||
} else {
|
||||
DEBUG_MSG(DLVL_WARN, "Packet type != 0");
|
||||
}
|
||||
oggTracks[tid].lastTime += oggTracks[tid].msPerFrame * (blockSize / oggTracks[tid].channels);
|
||||
}
|
||||
if (oggTracks[tid].codec == THEORA) {
|
||||
oggTracks[tid].lastTime += oggTracks[tid].msPerFrame;
|
||||
if (it == (tmpPage.getSegmentTableDeque().end() - 1)) {
|
||||
if (oggTracks[tid].idHeader.parseGranuleUpper(oggTracks[tid].lastGran) != oggTracks[tid].idHeader.parseGranuleUpper(tmpPage.getGranulePosition())) {
|
||||
lastPack["keyframe"] = 1ll;
|
||||
oggTracks[tid].lastGran = tmpPage.getGranulePosition();
|
||||
} else {
|
||||
lastPack["interframe"] = 1ll;
|
||||
}
|
||||
}
|
||||
}
|
||||
lastPack["bpos"] = 0ll;
|
||||
DEBUG_MSG(DLVL_WARN,"Parsed a packet of track %lld, new timestamp %f", tid, oggTracks[tid].lastTime);
|
||||
myMeta.update(lastPack);
|
||||
}
|
||||
} else {
|
||||
//Parsing headers
|
||||
switch (oggTracks[tid].codec) {
|
||||
case THEORA: {
|
||||
theora::header tmpHead(tmpPage.getFullPayload() + offset, (*it));
|
||||
DEBUG_MSG(DLVL_WARN,"Theora header, type %d", tmpHead.getHeaderType());
|
||||
switch (tmpHead.getHeaderType()) {
|
||||
case 0: {
|
||||
oggTracks[tid].idHeader = tmpHead;
|
||||
myMeta.tracks[tid].height = tmpHead.getPICH();
|
||||
myMeta.tracks[tid].width = tmpHead.getPICW();
|
||||
myMeta.tracks[tid].idHeader = std::string(tmpPage.getFullPayload() + offset, (*it));
|
||||
|
||||
bool readAllHeaders = true;
|
||||
for (std::map<long unsigned int, OGG::oggTrack>::iterator it = oggTracks.begin(); it != oggTracks.end(); it++){
|
||||
if (!it->second.parsedHeaders){
|
||||
readAllHeaders = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (readAllHeaders){
|
||||
break;
|
||||
}
|
||||
|
||||
// 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 (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: {
|
||||
myMeta.tracks[tid].commentHeader = std::string(tmpPage.getFullPayload() + offset, (*it));
|
||||
myMeta.tracks[tid].init += (char)((len >> 8) & 0xFF);
|
||||
myMeta.tracks[tid].init += (char)(len & 0xFF);
|
||||
myMeta.tracks[tid].init.append(myPage.getSegment(i), len);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
myMeta.tracks[tid].codec = "theora";
|
||||
myMeta.tracks[tid].trackID = tid;
|
||||
myMeta.tracks[tid].type = "video";
|
||||
myMeta.tracks[tid].init = std::string(tmpPage.getFullPayload() + offset, (*it));
|
||||
oggTracks[tid].parsedHeaders = true;
|
||||
myMeta.tracks[tid].init += (char)((len >> 8) & 0xFF);
|
||||
myMeta.tracks[tid].init += (char)(len & 0xFF);
|
||||
myMeta.tracks[tid].init.append(myPage.getSegment(i), len);
|
||||
oggTracks[tid].lastGran = 0;
|
||||
oggTracks[tid].parsedHeaders = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VORBIS: {
|
||||
vorbis::header tmpHead(tmpPage.getFullPayload() + offset, (*it));
|
||||
}
|
||||
|
||||
if (myMeta.tracks[tid].codec == "vorbis"){
|
||||
for (int i = 0; i < myPage.getAllSegments().size(); i++){
|
||||
unsigned long len = myPage.getSegmentLen(i);
|
||||
vorbis::header tmpHead((char*)myPage.getSegment(i), len);
|
||||
if (!tmpHead.isHeader()){
|
||||
DEBUG_MSG(DLVL_FAIL, "Header read failed!");
|
||||
return false;
|
||||
}
|
||||
DEBUG_MSG(DLVL_WARN, "Vorbis header, type %d", tmpHead.getHeaderType());
|
||||
switch (tmpHead.getHeaderType()){
|
||||
case 1: {
|
||||
myMeta.tracks[tid].channels = tmpHead.getAudioChannels();
|
||||
myMeta.tracks[tid].idHeader = std::string(tmpPage.getFullPayload() + offset, (*it));
|
||||
oggTracks[tid].channels = tmpHead.getAudioChannels();
|
||||
oggTracks[tid].blockSize[0] = 1 << tmpHead.getBlockSize0();
|
||||
oggTracks[tid].blockSize[1] = 1 << tmpHead.getBlockSize1();
|
||||
break;
|
||||
}
|
||||
//Case 1 is being handled by parseBeginOfStream
|
||||
case 3: {
|
||||
myMeta.tracks[tid].commentHeader = std::string(tmpPage.getFullPayload() + offset, (*it));
|
||||
//we have the first header stored in contBuffer
|
||||
myMeta.tracks[tid].init += (char)0x02;
|
||||
//ID header size
|
||||
for (int i = 0; i < (oggTracks[tid].contBuffer.size() / 255); i++){
|
||||
myMeta.tracks[tid].init += (char)0xFF;
|
||||
}
|
||||
myMeta.tracks[tid].init += (char)(oggTracks[tid].contBuffer.size() % 255);
|
||||
//Comment header size
|
||||
for (int i = 0; i < (len / 255); i++){
|
||||
myMeta.tracks[tid].init += (char)0xFF;
|
||||
}
|
||||
myMeta.tracks[tid].init += (char)(len % 255);
|
||||
myMeta.tracks[tid].init += oggTracks[tid].contBuffer;
|
||||
oggTracks[tid].contBuffer.clear();
|
||||
myMeta.tracks[tid].init.append(myPage.getSegment(i), len);
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
myMeta.tracks[tid].codec = "vorbis";
|
||||
myMeta.tracks[tid].trackID = tid;
|
||||
myMeta.tracks[tid].type = "audio";
|
||||
DEBUG_MSG(DLVL_WARN,"Set default values");
|
||||
myMeta.tracks[tid].init = std::string(tmpPage.getFullPayload() + offset, (*it));
|
||||
DEBUG_MSG(DLVL_WARN,"Set init values");
|
||||
myMeta.tracks[tid].init.append(myPage.getSegment(i), len);
|
||||
oggTracks[tid].vModes = tmpHead.readModeDeque(oggTracks[tid].channels);
|
||||
DEBUG_MSG(DLVL_WARN,"Set vmodevalues");
|
||||
oggTracks[tid].parsedHeaders = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
offset += (*it);
|
||||
}
|
||||
|
||||
for (std::map<long unsigned int, OGG::oggTrack>::iterator it = oggTracks.begin(); it != oggTracks.end(); it++){
|
||||
fseek(inFile, 0, SEEK_SET);
|
||||
position tmp = seekFirstData(it->first);
|
||||
if (tmp.trackID){
|
||||
currentPositions.insert(tmp);
|
||||
} else {
|
||||
INFO_MSG("missing track: %lu", it->first);
|
||||
}
|
||||
}
|
||||
lastBytePos = ftell(inFile);
|
||||
DEBUG_MSG(DLVL_WARN,"End of Loop, @ filepos %lld", lastBytePos);
|
||||
getNext();
|
||||
while (lastPack){
|
||||
myMeta.update(lastPack);
|
||||
getNext();
|
||||
}
|
||||
DEBUG_MSG(DLVL_WARN,"Exited while loop");
|
||||
|
||||
std::ofstream oFile(std::string(config->getString("input") + ".dtsh").c_str());
|
||||
oFile << myMeta.toJSON().toNetPacked();
|
||||
oFile.close();
|
||||
|
||||
//myMeta.toPrettyString(std::cout);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool inputOGG::seekNextPage(int tid){
|
||||
fseek(inFile, oggTracks[tid].lastPageOffset, SEEK_SET);
|
||||
bool res = true;
|
||||
do {
|
||||
res = oggTracks[tid].myPage.read(inFile);
|
||||
} while(res && snum2tid[oggTracks[tid].myPage.getBitstreamSerialNumber()] != tid);
|
||||
oggTracks[tid].lastPageOffset = ftell(inFile);
|
||||
oggTracks[tid].nxtSegment = 0;
|
||||
position inputOGG::seekFirstData(long long unsigned int tid){
|
||||
fseek(inFile, 0, SEEK_SET);
|
||||
position res;
|
||||
res.time = 0;
|
||||
res.trackID = tid;
|
||||
res.segmentNo = 0;
|
||||
bool readSuccesfull = true;
|
||||
bool quitloop = false;
|
||||
while (!quitloop){
|
||||
quitloop = true;
|
||||
res.bytepos = ftell(inFile);
|
||||
readSuccesfull = oggTracks[tid].myPage.read(inFile);
|
||||
if (!readSuccesfull){
|
||||
quitloop = true; //break :(
|
||||
break;
|
||||
}
|
||||
if (oggTracks[tid].myPage.getBitstreamSerialNumber() != tid){
|
||||
quitloop = false;
|
||||
continue;
|
||||
}
|
||||
if (oggTracks[tid].myPage.getHeaderType() != OGG::Plain){
|
||||
quitloop = false;
|
||||
continue;
|
||||
}
|
||||
if (oggTracks[tid].codec == OGG::VORBIS){
|
||||
vorbis::header tmpHead((char*)oggTracks[tid].myPage.getSegment(0), oggTracks[tid].myPage.getSegmentLen(0));
|
||||
if (tmpHead.isHeader()){
|
||||
quitloop = false;
|
||||
}
|
||||
}
|
||||
if (oggTracks[tid].codec == OGG::THEORA){
|
||||
theora::header tmpHead((char*)oggTracks[tid].myPage.getSegment(0), oggTracks[tid].myPage.getSegmentLen(0));
|
||||
if (tmpHead.isHeader()){
|
||||
quitloop = false;
|
||||
}
|
||||
}
|
||||
}// while ( oggTracks[tid].myPage.getHeaderType() != OGG::Plain && readSuccesfull && oggTracks[tid].myPage.getBitstreamSerialNumber() != tid);
|
||||
INFO_MSG("seek first bytepos: %llu tid: %llu oggTracks[tid].myPage.getHeaderType(): %d ", res.bytepos, tid, oggTracks[tid].myPage.getHeaderType());
|
||||
if (!readSuccesfull){
|
||||
res.trackID = 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void inputOGG::getNext(bool smart){
|
||||
if (!sortedSegments.size()){
|
||||
for (std::set<int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
|
||||
seekNextPage((*it));
|
||||
if (!currentPositions.size()){
|
||||
lastPack.null();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (sortedSegments.size()){
|
||||
int tid = (*(sortedSegments.begin())).tid;
|
||||
bool addedPacket = false;
|
||||
while (!addedPacket){
|
||||
segPart tmpPart;
|
||||
if (oggTracks[tid].myPage.getSegment(oggTracks[tid].nxtSegment, tmpPart.segData, tmpPart.len)){
|
||||
if (oggTracks[tid].nxtSegment == 0 && oggTracks[tid].myPage.getHeaderType() && OGG::Continued){
|
||||
segment tmpSeg = *(sortedSegments.begin());
|
||||
tmpSeg.parts.push_back(tmpPart);
|
||||
sortedSegments.erase(sortedSegments.begin());
|
||||
sortedSegments.insert(tmpSeg);
|
||||
}else{
|
||||
segment tmpSeg;
|
||||
tmpSeg.parts.push_back(tmpPart);
|
||||
tmpSeg.tid = tid;
|
||||
tmpSeg.time = oggTracks[tid].lastTime;
|
||||
if (oggTracks[tid].codec == VORBIS) {
|
||||
std::string data;
|
||||
data.append(tmpPart.segData, tmpPart.len);
|
||||
unsigned int blockSize = 0;
|
||||
Utils::bitstreamLSBF packet;
|
||||
packet.append(data);
|
||||
if (!packet.get(1)) {
|
||||
blockSize = oggTracks[tid].blockSize[oggTracks[tid].vModes[packet.get(vorbis::ilog(oggTracks[tid].vModes.size() - 1))].blockFlag];
|
||||
}
|
||||
oggTracks[tid].lastTime += oggTracks[tid].msPerFrame * (blockSize / oggTracks[tid].channels);
|
||||
}
|
||||
if (oggTracks[tid].codec == THEORA) {
|
||||
oggTracks[tid].lastTime += oggTracks[tid].msPerFrame;
|
||||
}
|
||||
sortedSegments.insert(tmpSeg);
|
||||
addedPacket = true;
|
||||
}
|
||||
oggTracks[tid].nxtSegment ++;
|
||||
}else{
|
||||
if (!seekNextPage(tid)){
|
||||
bool lastCompleteSegment = false;
|
||||
position curPos = *currentPositions.begin();
|
||||
currentPositions.erase(currentPositions.begin());
|
||||
segment thisSegment;
|
||||
thisSegment.tid = curPos.trackID;
|
||||
thisSegment.time = curPos.time;
|
||||
thisSegment.bytepos = curPos.bytepos + curPos.segmentNo;
|
||||
unsigned int oldSegNo = curPos.segmentNo;
|
||||
fseek(inFile, curPos.bytepos, SEEK_SET);
|
||||
OGG::Page curPage;
|
||||
curPage.read(inFile);
|
||||
thisSegment.parts.push_back(std::string(curPage.getSegment(curPos.segmentNo), curPage.getSegmentLen(curPos.segmentNo)));
|
||||
bool readFullPacket = false;
|
||||
if (curPos.segmentNo == curPage.getAllSegments().size() - 1){
|
||||
OGG::Page tmpPage;
|
||||
unsigned int bPos;
|
||||
while (!readFullPacket){
|
||||
bPos = ftell(inFile);//<-- :(
|
||||
if (!tmpPage.read(inFile)){
|
||||
break;
|
||||
}
|
||||
if (tmpPage.getBitstreamSerialNumber() != thisSegment.tid){
|
||||
continue;
|
||||
}
|
||||
curPos.bytepos = bPos;
|
||||
curPos.segmentNo = 0;
|
||||
if (tmpPage.getHeaderType() == OGG::Continued){
|
||||
thisSegment.parts.push_back(std::string(tmpPage.getSegment(0), tmpPage.getSegmentLen(0)));
|
||||
curPos.segmentNo = 1;
|
||||
if (tmpPage.getAllSegments().size() == 1){
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
lastCompleteSegment = true; //if this segment ends on the page, use granule to sync video time
|
||||
}
|
||||
readFullPacket = true;
|
||||
}
|
||||
} else {
|
||||
curPos.segmentNo++;
|
||||
|
||||
// if (oggTracks[thisSegment.tid].codec == OGG::THEORA && curPage.getGranulePosition() != (0xFFFFFFFFFFFFFFFFull) && curPos.segmentNo == curPage.getAllSegments().size() - 1){ //if the next segment is the last one on the page, the (theora) granule should be used to sync the time for the current segment
|
||||
if ((oggTracks[thisSegment.tid].codec == OGG::THEORA ||oggTracks[thisSegment.tid].codec == OGG::VORBIS)&& curPage.getGranulePosition() != (0xFFFFFFFFFFFFFFFFull) && curPos.segmentNo == curPage.getAllSegments().size() - 1){ //if the next segment is the last one on the page, the (theora) granule should be used to sync the time for the current segment
|
||||
OGG::Page tmpPage;
|
||||
while (tmpPage.read(inFile) && tmpPage.getBitstreamSerialNumber() != thisSegment.tid){}
|
||||
if ((tmpPage.getBitstreamSerialNumber() == thisSegment.tid) && tmpPage.getHeaderType() == OGG::Continued){
|
||||
lastCompleteSegment = true; //this segment should be used to sync time using granule
|
||||
}
|
||||
}
|
||||
std::string data;
|
||||
readFullPacket = true;
|
||||
}
|
||||
std::string tmpStr = thisSegment.toJSON(oggTracks[thisSegment.tid].codec).toNetPacked();
|
||||
lastPack.reInit(tmpStr.data(), tmpStr.size());
|
||||
|
||||
if (oggTracks[thisSegment.tid].codec == OGG::VORBIS){
|
||||
unsigned long blockSize = 0;
|
||||
Utils::bitstreamLSBF packet;
|
||||
packet.append((char*)curPage.getSegment(oldSegNo), curPage.getSegmentLen(oldSegNo));
|
||||
if (!packet.get(1)){
|
||||
//Read index first
|
||||
unsigned long vModeIndex = packet.get(vorbis::ilog(oggTracks[thisSegment.tid].vModes.size() - 1));
|
||||
blockSize= oggTracks[thisSegment.tid].blockSize[oggTracks[thisSegment.tid].vModes[vModeIndex].blockFlag]; //almost readable.
|
||||
} else {
|
||||
DEBUG_MSG(DLVL_WARN, "Packet type != 0");
|
||||
}
|
||||
curPos.time += oggTracks[thisSegment.tid].msPerFrame * (blockSize / oggTracks[thisSegment.tid].channels);
|
||||
} else if (oggTracks[thisSegment.tid].codec == OGG::THEORA){
|
||||
if (lastCompleteSegment == true && curPage.getGranulePosition() != (0xFFFFFFFFFFFFFFFFull)){ //this segment should be used to sync time using granule
|
||||
long long unsigned int parseGranuleUpper = curPage.getGranulePosition() >> oggTracks[thisSegment.tid].KFGShift ;
|
||||
long long unsigned int parseGranuleLower(curPage.getGranulePosition() & ((1 << oggTracks[thisSegment.tid].KFGShift) - 1));
|
||||
thisSegment.time = oggTracks[thisSegment.tid].msPerFrame * (parseGranuleUpper + parseGranuleLower - 1);
|
||||
curPos.time = thisSegment.time;
|
||||
std::string tmpStr = thisSegment.toJSON(oggTracks[thisSegment.tid].codec).toNetPacked();
|
||||
lastPack.reInit(tmpStr.data(), tmpStr.size());
|
||||
// INFO_MSG("thisTime: %d", lastPack.getTime());
|
||||
}
|
||||
curPos.time += oggTracks[thisSegment.tid].msPerFrame;
|
||||
}
|
||||
if (readFullPacket){
|
||||
currentPositions.insert(curPos);
|
||||
}
|
||||
}//getnext()
|
||||
|
||||
long long unsigned int inputOGG::calcGranuleTime(unsigned long tid, long long unsigned int granule){
|
||||
switch (oggTracks[tid].codec){
|
||||
case OGG::VORBIS:
|
||||
return granule * oggTracks[tid].msPerFrame ; //= samples * samples per second
|
||||
break;
|
||||
case OGG::THEORA:{
|
||||
long long unsigned int parseGranuleUpper = granule >> oggTracks[tid].KFGShift ;
|
||||
long long unsigned int parseGranuleLower = (granule & ((1 << oggTracks[tid].KFGShift) - 1));
|
||||
return (parseGranuleUpper + parseGranuleLower) * oggTracks[tid].msPerFrame ; //= frames * msPerFrame
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DEBUG_MSG(DLVL_WARN, "Unknown codec, can not calculate time from granule");
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void inputOGG::seek(int seekTime){
|
||||
DEBUG_MSG(DLVL_WARN,"Seeking is not yet supported for ogg files");
|
||||
//Do nothing, seeking is not yet implemented for ogg
|
||||
currentPositions.clear();
|
||||
DEBUG_MSG(DLVL_MEDIUM, "Seeking to %dms", seekTime);
|
||||
|
||||
//for every track
|
||||
for (std::set<unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
|
||||
//find first keyframe before keyframe with ms > seektime
|
||||
position tmpPos;
|
||||
tmpPos.trackID = *it;
|
||||
tmpPos.time = myMeta.tracks[*it].keys.begin()->getTime();
|
||||
tmpPos.bytepos = myMeta.tracks[*it].keys.begin()->getBpos();
|
||||
for (std::deque<DTSC::Key>::iterator ot = myMeta.tracks[*it].keys.begin(); ot != myMeta.tracks[*it].keys.end(); ot++){
|
||||
if (ot->getTime() > seekTime){
|
||||
break;
|
||||
} else {
|
||||
tmpPos.time = ot->getTime();
|
||||
tmpPos.bytepos = ot->getBpos();
|
||||
}
|
||||
}
|
||||
INFO_MSG("Found %dms for track %u at %llu bytepos %llu", seekTime, *it, tmpPos.time, tmpPos.bytepos);
|
||||
int backChrs=std::min(280ull, tmpPos.bytepos - 1);
|
||||
fseek(inFile, tmpPos.bytepos - backChrs, SEEK_SET);
|
||||
char buffer[300];
|
||||
fread(buffer, 300, 1, inFile);
|
||||
char * loc = buffer + backChrs + 2; //start at tmpPos.bytepos+2
|
||||
while (loc && !(loc[0] == 'O' && loc[1] == 'g' && loc[2] == 'g' && loc[3] == 'S')){
|
||||
loc = (char *)memrchr(buffer, 'O', (loc-buffer) -1 );//seek reverse
|
||||
}
|
||||
if (!loc){
|
||||
INFO_MSG("Unable to find a page boundary starting @ %llu, track %u", tmpPos.bytepos, *it);
|
||||
for (int i = 0; i < 300; i++){
|
||||
fprintf(stderr, "%0.2X ", buffer[i]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
tmpPos.segmentNo = backChrs - (loc - buffer);
|
||||
tmpPos.bytepos -= tmpPos.segmentNo;
|
||||
INFO_MSG("Track %u, segment %llu found at bytepos %llu", *it, tmpPos.segmentNo, tmpPos.bytepos);
|
||||
|
||||
currentPositions.insert(tmpPos);
|
||||
}
|
||||
}
|
||||
|
||||
void inputOGG::trackSelect(std::string trackSpec){
|
||||
|
@ -269,8 +427,7 @@ namespace Mist {
|
|||
size_t index;
|
||||
while (trackSpec != ""){
|
||||
index = trackSpec.find(' ');
|
||||
selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str()));
|
||||
DEBUG_MSG(DLVL_WARN, "Added track %d, index = %lu, (index == npos) = %d", atoi(trackSpec.substr(0, index).c_str()), index, index == std::string::npos);
|
||||
selectedTracks.insert(atoll(trackSpec.substr(0, index).c_str()));
|
||||
if (index != std::string::npos){
|
||||
trackSpec.erase(0, index + 1);
|
||||
} else {
|
||||
|
@ -281,3 +438,9 @@ namespace Mist {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -3,27 +3,51 @@
|
|||
#include <mist/ogg.h>
|
||||
|
||||
namespace Mist {
|
||||
enum codecType {THEORA, VORBIS};
|
||||
|
||||
struct segPart {
|
||||
char * segData;
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
struct segment{
|
||||
class segment {
|
||||
public:
|
||||
segment() : time(0), tid(0), bytepos(0), keyframe(0){}
|
||||
bool operator < (const segment & rhs) const {
|
||||
return time < rhs.time || (time == rhs.time && tid < rhs.tid);
|
||||
}
|
||||
std::vector<segPart> parts;
|
||||
unsigned int time;
|
||||
unsigned int tid;
|
||||
std::vector<std::string> parts;
|
||||
unsigned long long int time;
|
||||
unsigned long long int tid;
|
||||
long long unsigned int bytepos;
|
||||
bool keyframe;
|
||||
JSON::Value toJSON(OGG::oggCodec myCodec);
|
||||
};
|
||||
|
||||
struct position {
|
||||
bool operator < (const position & rhs) const {
|
||||
if (time < rhs.time){
|
||||
return true;
|
||||
} else {
|
||||
if (time == rhs.time){
|
||||
if (trackID < rhs.trackID){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
long unsigned int trackID;
|
||||
long long unsigned int time;
|
||||
long long unsigned int bytepos;
|
||||
long long unsigned int segmentNo;
|
||||
};
|
||||
/*
|
||||
class oggTrack {
|
||||
public:
|
||||
oggTrack() : lastTime(0), parsedHeaders(false), lastPageOffset(0), nxtSegment(0){ }
|
||||
codecType codec;
|
||||
std::string contBuffer;//buffer for continuing pages
|
||||
segment myBuffer;
|
||||
double lastTime;
|
||||
long long unsigned int lastGran;
|
||||
bool parsedHeaders;
|
||||
|
@ -37,8 +61,9 @@ namespace Mist {
|
|||
//vorbis
|
||||
std::deque<vorbis::mode> vModes;
|
||||
char channels;
|
||||
long long unsigned int blockSize[2];
|
||||
};
|
||||
unsigned long blockSize[2];
|
||||
unsigned long getBlockSize(unsigned int vModeIndex);
|
||||
};*/
|
||||
|
||||
class inputOGG : public Input {
|
||||
public:
|
||||
|
@ -47,19 +72,22 @@ namespace Mist {
|
|||
//Private Functions
|
||||
bool setup();
|
||||
bool readHeader();
|
||||
bool seekNextPage(int tid);
|
||||
position seekFirstData(long long unsigned int tid);
|
||||
void getNext(bool smart = true);
|
||||
void seek(int seekTime);
|
||||
void trackSelect(std::string trackSpec);
|
||||
|
||||
void parseBeginOfStream(OGG::Page & bosPage);
|
||||
|
||||
std::set<position> currentPositions;
|
||||
FILE * inFile;
|
||||
std::map<long long int, long long int> snum2tid;
|
||||
std::map<long long int, oggTrack> oggTracks;
|
||||
std::set<segment> sortedSegments;
|
||||
std::map<long unsigned int, OGG::oggTrack> oggTracks;//this remembers all metadata for every track
|
||||
std::set<segment> sortedSegments;//probably not needing this
|
||||
long long unsigned int calcGranuleTime(unsigned long tid, long long unsigned int granule);
|
||||
long long unsigned int calcSegmentDuration(unsigned long tid , std::string & segment);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
typedef Mist::inputOGG mistIn;
|
||||
|
||||
|
||||
|
|
|
@ -163,7 +163,7 @@ namespace Mist {
|
|||
|
||||
void Output::negotiatePushTracks() {
|
||||
int i = 0;
|
||||
for (std::map<int, DTSC::Track>::iterator it = meta_out.tracks.begin(); it != meta_out.tracks.end() && i < 5; it++){
|
||||
for (std::map<unsigned int, DTSC::Track>::iterator it = meta_out.tracks.begin(); it != meta_out.tracks.end() && i < 5; it++){
|
||||
negotiateWithBuffer(it->first);
|
||||
i++;
|
||||
}
|
||||
|
@ -286,7 +286,7 @@ namespace Mist {
|
|||
}
|
||||
}
|
||||
if (!found){
|
||||
for (std::map<int,DTSC::Track>::iterator trit = myMeta.tracks.begin(); trit != myMeta.tracks.end(); trit++){
|
||||
for (std::map<unsigned int,DTSC::Track>::iterator trit = myMeta.tracks.begin(); trit != myMeta.tracks.end(); trit++){
|
||||
if (trit->second.codec == (*itc).asStringRef()){
|
||||
genCounter++;
|
||||
found = true;
|
||||
|
@ -326,7 +326,7 @@ namespace Mist {
|
|||
}
|
||||
}
|
||||
if (!found){
|
||||
for (std::map<int,DTSC::Track>::iterator trit = myMeta.tracks.begin(); trit != myMeta.tracks.end(); trit++){
|
||||
for (std::map<unsigned int,DTSC::Track>::iterator trit = myMeta.tracks.begin(); trit != myMeta.tracks.end(); trit++){
|
||||
if (trit->second.codec == (*itc).asStringRef()){
|
||||
selectedTracks.insert(trit->first);
|
||||
found = true;
|
||||
|
@ -438,8 +438,8 @@ namespace Mist {
|
|||
return;
|
||||
}
|
||||
char id[100];
|
||||
sprintf(id, "%s%lu_%d", streamName.c_str(), trackId, pageNum);
|
||||
curPages[trackId].init(std::string(id),26 * 1024 * 1024);
|
||||
snprintf(id, 100, "%s%lu_%d", streamName.c_str(), trackId, pageNum);
|
||||
curPages[trackId].init(id, 26 * 1024 * 1024);
|
||||
if (!(curPages[trackId].mapped)){
|
||||
DEBUG_MSG(DLVL_FAIL, "Initializing page %s failed", curPages[trackId].name.c_str());
|
||||
return;
|
||||
|
@ -448,7 +448,7 @@ namespace Mist {
|
|||
}
|
||||
|
||||
/// Prepares all tracks from selectedTracks for seeking to the specified ms position.
|
||||
void Output::seek(long long pos){
|
||||
void Output::seek(unsigned long long pos){
|
||||
sought = true;
|
||||
firstTime = Util::getMS() - pos;
|
||||
if (!isInitialized){
|
||||
|
@ -457,16 +457,16 @@ namespace Mist {
|
|||
buffer.clear();
|
||||
currentPacket.null();
|
||||
updateMeta();
|
||||
DEBUG_MSG(DLVL_MEDIUM, "Seeking to %llims", pos);
|
||||
DEBUG_MSG(DLVL_MEDIUM, "Seeking to %llums", pos);
|
||||
for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
|
||||
seek(*it, pos);
|
||||
}
|
||||
}
|
||||
|
||||
bool Output::seek(int tid, long long pos, bool getNextKey){
|
||||
bool Output::seek(unsigned int tid, unsigned long long pos, bool getNextKey){
|
||||
loadPageForKey(tid, getKeyForTime(tid, pos) + (getNextKey?1:0));
|
||||
if (!curPages.count(tid) || !curPages[tid].mapped){
|
||||
DEBUG_MSG(DLVL_DEVEL, "Aborting seek to %llims in track %d, not available.", pos, tid);
|
||||
DEBUG_MSG(DLVL_DEVEL, "Aborting seek to %llums in track %u, not available.", pos, tid);
|
||||
return false;
|
||||
}
|
||||
sortedPageInfo tmp;
|
||||
|
@ -724,7 +724,7 @@ namespace Mist {
|
|||
}
|
||||
if (trackMap.size()){
|
||||
for (std::map<int, int>::iterator it = trackMap.begin(); it != trackMap.end() && tNum < 5; it++){
|
||||
int tId = it->second;
|
||||
unsigned int tId = it->second;
|
||||
char * thisData = playerConn.getData() + (6 * tNum);
|
||||
thisData[0] = ((tId >> 24) & 0xFF);
|
||||
thisData[1] = ((tId >> 16) & 0xFF);
|
||||
|
@ -736,7 +736,7 @@ namespace Mist {
|
|||
}
|
||||
}else{
|
||||
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end() && tNum < 5; it++){
|
||||
int tId = *it;
|
||||
unsigned int tId = *it;
|
||||
char * thisData = playerConn.getData() + (6 * tNum);
|
||||
thisData[0] = ((tId >> 24) & 0xFF);
|
||||
thisData[1] = ((tId >> 16) & 0xFF);
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace Mist {
|
|||
}
|
||||
return (time == rhs.time && tid < rhs.tid);
|
||||
}
|
||||
int tid;
|
||||
unsigned int tid;
|
||||
long long unsigned int time;
|
||||
unsigned int offset;
|
||||
};
|
||||
|
@ -53,8 +53,8 @@ namespace Mist {
|
|||
//non-virtual generic functions
|
||||
int run();
|
||||
void stats();
|
||||
void seek(long long pos);
|
||||
bool seek(int tid, long long pos, bool getNextKey = false);
|
||||
void seek(unsigned long long pos);
|
||||
bool seek(unsigned int tid, unsigned long long pos, bool getNextKey = false);
|
||||
void stop();
|
||||
void setBlocking(bool blocking);
|
||||
long unsigned int getMainSelectedTrack();
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace Mist {
|
|||
audioTrack = 0;
|
||||
JSON::Value & vidCapa = capa["codecs"][0u][0u];
|
||||
JSON::Value & audCapa = capa["codecs"][0u][1u];
|
||||
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
for (JSON::ArrIter itb = vidCapa.ArrBegin(); itb != vidCapa.ArrEnd(); itb++){
|
||||
if (it->second.codec == (*itb).asStringRef()){
|
||||
videoTracks.insert(it->first);
|
||||
|
|
|
@ -10,14 +10,14 @@ namespace Mist {
|
|||
result << "#EXTM3U\r\n";
|
||||
int audioId = -1;
|
||||
std::string audioName;
|
||||
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
if (it->second.codec == "AAC"){
|
||||
audioId = it->first;
|
||||
audioName = it->second.getIdentifier();
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
if (it->second.codec == "H264"){
|
||||
int bWidth = it->second.bps * 2;
|
||||
if (audioId != -1){
|
||||
|
@ -266,7 +266,7 @@ namespace Mist {
|
|||
return 1;
|
||||
}
|
||||
//loop trough all the tracks
|
||||
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
//return "too late" if one track is past this point
|
||||
if (ms < it->second.firstms){
|
||||
return -1;
|
||||
|
|
|
@ -295,13 +295,13 @@ namespace Mist {
|
|||
"MajorVersion=\"2\" "
|
||||
"MinorVersion=\"0\" "
|
||||
"TimeScale=\"10000000\" ";
|
||||
std::deque<std::map<int, DTSC::Track>::iterator> audioIters;
|
||||
std::deque<std::map<int, DTSC::Track>::iterator> videoIters;
|
||||
std::deque<std::map<unsigned int, DTSC::Track>::iterator> audioIters;
|
||||
std::deque<std::map<unsigned int, DTSC::Track>::iterator> videoIters;
|
||||
long long int maxWidth = 0;
|
||||
long long int maxHeight = 0;
|
||||
long long int minWidth = 99999999;
|
||||
long long int minHeight = 99999999;
|
||||
for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
|
||||
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
|
||||
if (it->second.codec == "AAC") {
|
||||
audioIters.push_back(it);
|
||||
}
|
||||
|
@ -343,7 +343,7 @@ namespace Mist {
|
|||
"Chunks=\"" << (*audioIters.begin())->second.keys.size() << "\" "
|
||||
"Url=\"Q({bitrate},{CustomAttributes})/A({start time})\">\n";
|
||||
int index = 0;
|
||||
for (std::deque<std::map<int, DTSC::Track>::iterator>::iterator it = audioIters.begin(); it != audioIters.end(); it++) {
|
||||
for (std::deque<std::map<unsigned int, DTSC::Track>::iterator>::iterator it = audioIters.begin(); it != audioIters.end(); it++) {
|
||||
Result << "<QualityLevel "
|
||||
"Index=\"" << index << "\" "
|
||||
"Bitrate=\"" << (*it)->second.bps * 8 << "\" "
|
||||
|
@ -388,7 +388,7 @@ namespace Mist {
|
|||
"DisplayWidth=\"" << maxWidth << "\" "
|
||||
"DisplayHeight=\"" << maxHeight << "\">\n";
|
||||
int index = 0;
|
||||
for (std::deque<std::map<int, DTSC::Track>::iterator>::iterator it = videoIters.begin(); it != videoIters.end(); it++) {
|
||||
for (std::deque<std::map<unsigned int, DTSC::Track>::iterator>::iterator it = videoIters.begin(); it != videoIters.end(); it++) {
|
||||
//Add video qualities
|
||||
Result << "<QualityLevel "
|
||||
"Index=\"" << index << "\" "
|
||||
|
@ -450,7 +450,7 @@ namespace Mist {
|
|||
|
||||
void OutHSS::initialize() {
|
||||
Output::initialize();
|
||||
for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
|
||||
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
|
||||
for (std::deque<DTSC::Key>::iterator it2 = it->second.keys.begin(); it2 != it->second.keys.end(); it2++) {
|
||||
keyTimes[it->first].insert(it2->getTime());
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ namespace Mist {
|
|||
if (H.GetVar("callback") != ""){jsonp = H.GetVar("callback");}
|
||||
if (H.GetVar("jsonp") != ""){jsonp = H.GetVar("jsonp");}
|
||||
initialize();
|
||||
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
for (std::map<unsigned int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
|
||||
if (it->second.type == "meta" ){
|
||||
selectedTracks.insert(it->first);
|
||||
}
|
||||
|
|
208
src/output/output_progressive_ogg.cpp
Normal file
208
src/output/output_progressive_ogg.cpp
Normal file
|
@ -0,0 +1,208 @@
|
|||
#include "output_progressive_ogg.h"
|
||||
#include <mist/bitstream.h>
|
||||
#include <mist/defines.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Mist {
|
||||
OutProgressiveOGG::OutProgressiveOGG(Socket::Connection & conn) : HTTPOutput(conn){
|
||||
realTime = 0;
|
||||
}
|
||||
|
||||
OutProgressiveOGG::~OutProgressiveOGG(){}
|
||||
|
||||
void OutProgressiveOGG::init(Util::Config * cfg){
|
||||
HTTPOutput::init(cfg);
|
||||
capa["name"] = "OGG";
|
||||
capa["desc"] = "Enables HTTP protocol progressive streaming.";
|
||||
capa["deps"] = "HTTP";
|
||||
capa["url_rel"] = "/$.ogg";
|
||||
capa["url_match"] = "/$.ogg";
|
||||
capa["codecs"][0u][0u].append("theora");
|
||||
capa["codecs"][0u][1u].append("vorbis");
|
||||
capa["codecs"][0u][1u].append("opus");
|
||||
capa["methods"][0u]["handler"] = "http";
|
||||
capa["methods"][0u]["type"] = "html5/video/ogg";
|
||||
capa["methods"][0u]["priority"] = 8ll;
|
||||
capa["methods"][0u]["nolive"] = 1;
|
||||
}
|
||||
|
||||
void OutProgressiveOGG::sendNext(){
|
||||
unsigned int track = currentPacket.getTrackId();
|
||||
|
||||
|
||||
OGG::oggSegment newSegment;
|
||||
currentPacket.getString("data", newSegment.dataString);
|
||||
// if (currentPacket.getTime() > 315800){// && currentPacket.getTime() < 316200){
|
||||
//INFO_MSG("Found a packet of time %llu, size: %d", currentPacket.getTime(), newSegment.dataString.size());
|
||||
//}
|
||||
pageBuffer[track].totalFrames = ((double)currentPacket.getTime() / (1000000.0f / myMeta.tracks[track].fpks)) + 1.5; //should start at 1. added .5 for rounding.
|
||||
// INFO_MSG("track: %u totalFrames %llu timestamp: %llu totalframe value: %f", track, pageBuffer[track].totalFrames, currentPacket.getTime(), ((double)currentPacket.getTime() / (1000000.0f / myMeta.tracks[track].fpks)) + 1);
|
||||
|
||||
if (pageBuffer[track].codec == OGG::THEORA){
|
||||
newSegment.isKeyframe = currentPacket.getFlag("keyframe");
|
||||
if (newSegment.isKeyframe == true){
|
||||
pageBuffer[track].sendTo(myConn);//send data remaining in buffer (expected to fit on a page), keyframe will allways start on new page
|
||||
// INFO_MSG("segments left in buffer: %d", pageBuffer[track].oggSegments.size());
|
||||
pageBuffer[track].lastKeyFrame = pageBuffer[track].totalFrames;
|
||||
}
|
||||
newSegment.framesSinceKeyFrame = pageBuffer[track].totalFrames - pageBuffer[track].lastKeyFrame;
|
||||
newSegment.lastKeyFrameSeen = pageBuffer[track].lastKeyFrame;
|
||||
// theora::frame tmpFrame;
|
||||
// tmpFrame.read(newSegment.dataString.data(),newSegment.dataString.size());
|
||||
// INFO_MSG("FTYPE: %d ISKEYFRAME: %d",tmpFrame.getFTYPE(),newSegment.isKeyframe );
|
||||
}
|
||||
|
||||
newSegment.frameNumber = pageBuffer[track].totalFrames;
|
||||
newSegment.timeStamp = currentPacket.getTime();
|
||||
|
||||
pageBuffer[track].oggSegments.push_back(newSegment);
|
||||
|
||||
if (pageBuffer[track].codec == OGG::VORBIS){
|
||||
pageBuffer[track].vorbisStuff();//this updates lastKeyFrame
|
||||
}
|
||||
|
||||
// while (pageBuffer[track].oggSegments.size()){
|
||||
//pageBuffer[track].sendTo(myConn);
|
||||
//}
|
||||
while (pageBuffer[track].shouldSend()){
|
||||
pageBuffer[track].sendTo(myConn);
|
||||
}
|
||||
}
|
||||
|
||||
bool OutProgressiveOGG::onFinish(){
|
||||
for (std::map<long long unsigned int, OGG::Page>::iterator it = pageBuffer.begin(); it != pageBuffer.end(); it++){
|
||||
it->second.setHeaderType(OGG::EndOfStream);
|
||||
it->second.sendTo(myConn);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool OutProgressiveOGG::parseInit(std::string & initData, std::deque<std::string> & output){
|
||||
std::string temp;
|
||||
unsigned int index = 0;
|
||||
if (initData[0] == 0x02){ //"special" case, requires interpretation similar to table
|
||||
if (initData.size() < 7){
|
||||
FAIL_MSG("initData size too tiny (size: %lu)", initData.size());
|
||||
return false;
|
||||
}
|
||||
unsigned int len1 = 0 ;
|
||||
unsigned int len2 = 0 ;
|
||||
index = 1;
|
||||
while (initData[index] == 255){ //get len 1
|
||||
len1 += initData[index++];
|
||||
}
|
||||
len1 += initData[index++];
|
||||
|
||||
while (initData[index] == 255){ //get len 1
|
||||
len2 += initData[index++];
|
||||
}
|
||||
len2 += initData[index++];
|
||||
|
||||
if (initData.size() < (len1 + len2 + 4)){
|
||||
FAIL_MSG("initData size too tiny (size: %lu)", initData.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
temp = initData.substr(index, len1);
|
||||
output.push_back(temp);
|
||||
index += len1;
|
||||
temp = initData.substr(index, len2);
|
||||
output.push_back(temp);
|
||||
index += len2;
|
||||
temp = initData.substr(index); //remainder of string:
|
||||
output.push_back(temp); //add data to output deque
|
||||
} else {
|
||||
if (initData.size() < 7){
|
||||
FAIL_MSG("initData size too tiny (size: %lu)", initData.size());
|
||||
return false;
|
||||
}
|
||||
unsigned int len = 0;
|
||||
for (unsigned int i = 0; i < 3; i++){
|
||||
temp = initData.substr(index, 2);
|
||||
len = (((unsigned int)temp[0]) << 8) | (temp[1]); //2 bytes len
|
||||
index += 2; //start of data
|
||||
if (index + len > initData.size()){
|
||||
FAIL_MSG("index+len > initData size");
|
||||
return false;
|
||||
}
|
||||
temp = initData.substr(index, len);
|
||||
output.push_back(temp); //add data to output deque
|
||||
index += len;
|
||||
INFO_MSG("init data len[%d]: %d ", i, len);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OutProgressiveOGG::sendHeader(){
|
||||
HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers
|
||||
HTTP_S.SetHeader("Content-Type", "video/ogg");
|
||||
HTTP_S.protocol = "HTTP/1.0";
|
||||
myConn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file
|
||||
|
||||
std::map<int, std::deque<std::string> > initData;
|
||||
|
||||
OGG::oggSegment newSegment;
|
||||
for (std::set<long unsigned int>::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.
|
||||
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];
|
||||
theora::header tempHead((char *)tempStr.c_str(), 42);
|
||||
pageBuffer[*it].split = tempHead.getKFGShift();
|
||||
INFO_MSG("got theora KFG shift: %d", pageBuffer[*it].split); //looks OK.
|
||||
} else if (myMeta.tracks[*it].codec == "vorbis"){
|
||||
pageBuffer[*it].codec = OGG::VORBIS;
|
||||
pageBuffer[*it].totalFrames = 0;
|
||||
pageBuffer[*it].sampleRate = myMeta.tracks[*it].rate;
|
||||
pageBuffer[*it].prevBlockFlag = -1;
|
||||
vorbis::header tempHead((char *)initData[*it][0].data(), initData[*it][0].size());
|
||||
pageBuffer[*it].blockSize[0] = std::min(tempHead.getBlockSize0(), tempHead.getBlockSize1());
|
||||
pageBuffer[*it].blockSize[1] = std::max(tempHead.getBlockSize0(), tempHead.getBlockSize1());
|
||||
char audioChannels = tempHead.getAudioChannels(); //?
|
||||
vorbis::header tempHead2((char *)initData[*it][2].data(), initData[*it][2].size());
|
||||
pageBuffer[*it].vorbisModes = tempHead2.readModeDeque(audioChannels);//getting modes
|
||||
} else if (myMeta.tracks[*it].codec == "opus"){
|
||||
pageBuffer[*it].totalFrames = 0; //?
|
||||
pageBuffer[*it].codec = OGG::OPUS;
|
||||
}
|
||||
pageBuffer[*it].clear(OGG::BeginOfStream, 0, *it, 0); //CREATES a (map)pageBuffer object, *it = id, pagetype=BOS
|
||||
newSegment.dataString = initData[*it][0];
|
||||
pageBuffer[*it].oggSegments.push_back(newSegment);
|
||||
pageBuffer[*it].sendTo(myConn, 0); //granule position of 0
|
||||
}
|
||||
for (std::set<long unsigned int>::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 (pageBuffer[*it].oggSegments.size()){
|
||||
pageBuffer[*it].sendTo(myConn, 0); //granule position of 0
|
||||
}
|
||||
}
|
||||
sentHeader = true;
|
||||
}
|
||||
|
||||
void OutProgressiveOGG::onRequest(){
|
||||
if (HTTP_R.Read(myConn)){
|
||||
DEBUG_MSG(DLVL_DEVEL, "Received request %s", HTTP_R.getUrl().c_str());
|
||||
if (HTTP_R.GetVar("audio") != ""){
|
||||
selectedTracks.insert(JSON::Value(HTTP_R.GetVar("audio")).asInt());
|
||||
}
|
||||
if (HTTP_R.GetVar("video") != ""){
|
||||
selectedTracks.insert(JSON::Value(HTTP_R.GetVar("video")).asInt());
|
||||
}
|
||||
parseData = true;
|
||||
wantRequest = false;
|
||||
HTTP_R.Clean();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
28
src/output/output_progressive_ogg.h
Normal file
28
src/output/output_progressive_ogg.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "output_http.h"
|
||||
#include <mist/ogg.h>
|
||||
#include <mist/http_parser.h>
|
||||
|
||||
namespace Mist {
|
||||
class OutProgressiveOGG : public HTTPOutput {
|
||||
public:
|
||||
OutProgressiveOGG(Socket::Connection & conn);
|
||||
~OutProgressiveOGG();
|
||||
static void init(Util::Config * cfg);
|
||||
void onRequest();
|
||||
void sendNext();
|
||||
void sendHeader();
|
||||
bool onFinish();
|
||||
bool parseInit(std::string & initData, std::deque<std::string> & output);
|
||||
protected:
|
||||
HTTP::Parser HTTP_R;//Received HTTP
|
||||
HTTP::Parser HTTP_S;//Sent HTTP
|
||||
std::map <long long unsigned int, OGG::Page > pageBuffer; //OGG specific variables
|
||||
};
|
||||
}
|
||||
|
||||
typedef Mist::OutProgressiveOGG mistOut;
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Add table
Reference in a new issue