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
|
@ -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++){
|
||||
|
@ -245,68 +247,72 @@ namespace Mist {
|
|||
}
|
||||
}
|
||||
}else{
|
||||
std::map<int, DTSCPageData> curData;
|
||||
std::map<int, booking> bookKeeping;
|
||||
|
||||
seek(0);
|
||||
getNext();
|
||||
std::map<int, DTSCPageData> curData;
|
||||
std::map<int, booking> bookKeeping;
|
||||
|
||||
seek(0);
|
||||
getNext();
|
||||
|
||||
while(lastPack){//loop through all
|
||||
int tid = lastPack.getTrackId();
|
||||
if (!tid){
|
||||
getNext(false);
|
||||
continue;
|
||||
}
|
||||
if (!bookKeeping.count(tid)){
|
||||
bookKeeping[tid].first = 1;
|
||||
bookKeeping[tid].curPart = 0;
|
||||
bookKeeping[tid].curKey = 0;
|
||||
|
||||
curData[tid].lastKeyTime = 0xFFFFFFFF;
|
||||
curData[tid].keyNum = 1;
|
||||
curData[tid].partNum = 0;
|
||||
curData[tid].dataSize = 0;
|
||||
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.
|
||||
}
|
||||
if (myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getParts() == curData[tid].partNum){
|
||||
if (curData[tid].dataSize > 8 * 1024 * 1024){
|
||||
pagesByTrack[tid][bookKeeping[tid].first] = curData[tid];
|
||||
bookKeeping[tid].first += curData[tid].keyNum;
|
||||
curData[tid].keyNum = 0;
|
||||
curData[tid].dataSize = 0;
|
||||
curData[tid].firstTime = myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getTime();
|
||||
}
|
||||
bookKeeping[tid].curKey++;
|
||||
curData[tid].keyNum++;
|
||||
curData[tid].partNum = 0;
|
||||
}
|
||||
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);
|
||||
while(lastPack){//loop through all
|
||||
unsigned int tid = lastPack.getTrackId();
|
||||
if (!tid){
|
||||
getNext(false);
|
||||
continue;
|
||||
}
|
||||
for (std::map<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];
|
||||
if (!bookKeeping.count(tid)){
|
||||
bookKeeping[tid].first = 1;
|
||||
bookKeeping[tid].curPart = 0;
|
||||
bookKeeping[tid].curKey = 0;
|
||||
|
||||
curData[tid].lastKeyTime = 0xFFFFFFFF;
|
||||
curData[tid].keyNum = 1;
|
||||
curData[tid].partNum = 0;
|
||||
curData[tid].dataSize = 0;
|
||||
curData[tid].curOffset = 0;
|
||||
curData[tid].firstTime = myMeta.tracks[tid].keys[0].getTime();
|
||||
|
||||
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() + 1 == curData[tid].partNum){
|
||||
if (curData[tid].dataSize > 8 * 1024 * 1024) {
|
||||
pagesByTrack[tid][bookKeeping[tid].first] = curData[tid];
|
||||
bookKeeping[tid].first += curData[tid].keyNum;
|
||||
curData[tid].keyNum = 0;
|
||||
curData[tid].dataSize = 0;
|
||||
curData[tid].firstTime = myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getTime();
|
||||
}
|
||||
bookKeeping[tid].curKey++;
|
||||
curData[tid].keyNum++;
|
||||
curData[tid].partNum = 0;
|
||||
}
|
||||
curData[tid].dataSize += lastPack.getDataLen();
|
||||
curData[tid].partNum ++;
|
||||
bookKeeping[tid].curPart ++;
|
||||
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<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++){
|
||||
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);
|
||||
}
|
||||
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,269 +9,426 @@
|
|||
#include <mist/ogg.h>
|
||||
#include <mist/defines.h>
|
||||
#include <mist/bitstream.h>
|
||||
|
||||
#include "input_ogg.h"
|
||||
|
||||
///\todo Whar be Opus support?
|
||||
|
||||
namespace Mist {
|
||||
inputOGG::inputOGG(Util::Config * cfg) : Input(cfg) {
|
||||
|
||||
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;
|
||||
bool inputOGG::setup(){
|
||||
if (config->getString("input") == "-"){
|
||||
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");
|
||||
if (!inFile) {
|
||||
if (!inFile){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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();
|
||||
///\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;
|
||||
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
|
||||
bool inputOGG::readHeader(){
|
||||
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));
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
myMeta.tracks[tid].commentHeader = std::string(tmpPage.getFullPayload() + offset, (*it));
|
||||
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;
|
||||
oggTracks[tid].lastGran = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VORBIS: {
|
||||
vorbis::header tmpHead(tmpPage.getFullPayload() + offset, (*it));
|
||||
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 3: {
|
||||
myMeta.tracks[tid].commentHeader = std::string(tmpPage.getFullPayload() + offset, (*it));
|
||||
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");
|
||||
oggTracks[tid].vModes = tmpHead.readModeDeque(oggTracks[tid].channels);
|
||||
DEBUG_MSG(DLVL_WARN,"Set vmodevalues");
|
||||
oggTracks[tid].parsedHeaders = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
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].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].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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 is being handled by parseBeginOfStream
|
||||
case 3: {
|
||||
//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].init.append(myPage.getSegment(i), len);
|
||||
oggTracks[tid].vModes = tmpHead.readModeDeque(oggTracks[tid].channels);
|
||||
oggTracks[tid].parsedHeaders = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lastBytePos = ftell(inFile);
|
||||
DEBUG_MSG(DLVL_WARN,"End of Loop, @ filepos %lld", lastBytePos);
|
||||
}
|
||||
DEBUG_MSG(DLVL_WARN,"Exited while loop");
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
getNext();
|
||||
while (lastPack){
|
||||
myMeta.update(lastPack);
|
||||
getNext();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
void inputOGG::getNext(bool smart){
|
||||
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)){
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
std::string data;
|
||||
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
|
||||
}
|
||||
}
|
||||
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){
|
||||
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::seek(int seekTime) {
|
||||
DEBUG_MSG(DLVL_WARN,"Seeking is not yet supported for ogg files");
|
||||
//Do nothing, seeking is not yet implemented for ogg
|
||||
}
|
||||
|
||||
void inputOGG::trackSelect(std::string trackSpec) {
|
||||
void inputOGG::trackSelect(std::string trackSpec){
|
||||
selectedTracks.clear();
|
||||
size_t index;
|
||||
while (trackSpec != "") {
|
||||
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);
|
||||
if (index != std::string::npos) {
|
||||
selectedTracks.insert(atoll(trackSpec.substr(0, index).c_str()));
|
||||
if (index != std::string::npos){
|
||||
trackSpec.erase(0, index + 1);
|
||||
} else {
|
||||
trackSpec = "";
|
||||
|
@ -281,3 +438,9 @@ namespace Mist {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -3,27 +3,51 @@
|
|||
#include <mist/ogg.h>
|
||||
|
||||
namespace Mist {
|
||||
enum codecType {THEORA, VORBIS};
|
||||
|
||||
struct segPart{
|
||||
struct segPart {
|
||||
char * segData;
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
struct segment{
|
||||
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;
|
||||
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<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);
|
||||
};
|
||||
|
||||
class oggTrack{
|
||||
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) { }
|
||||
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,9 +61,10 @@ 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:
|
||||
inputOGG(Util::Config * cfg);
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue