Shared memory rewrite

This commit is contained in:
Thulinma 2014-04-04 19:50:40 +02:00
parent afcddbfca6
commit cd2fe225c5
81 changed files with 7775 additions and 5411 deletions

338
src/input/input.cpp Normal file
View file

@ -0,0 +1,338 @@
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mist/defines.h>
#include "input.h"
#include <sstream>
#include <fstream>
#include <iterator>
namespace Mist {
Input * Input::singleton = NULL;
void Input::userCallback(char * data, size_t len, unsigned int id){
long tid = ((long)(data[0]) << 24) | ((long)(data[1]) << 16) | ((long)(data[2]) << 8) | ((long)(data[3]));
long keyNum = ((long)(data[4]) << 8) | ((long)(data[5]));
bufferFrame(tid, keyNum + 1);//Try buffer next frame
}
void Input::doNothing(char * data, size_t len, unsigned int id){
DEBUG_MSG(DLVL_DONTEVEN, "Doing 'nothing'");
for (int i = 0; i < 5; i++){
int tmp = ((long)(data[i*6]) << 24) | ((long)(data[i*6 + 1]) << 16) | ((long)(data[i*6 + 2]) << 8) | data[i*6 + 3];
if (tmp){
singleton->userCallback(data + (i*6), 6, id);//call the userCallback for this input
}
}
}
Input::Input(Util::Config * cfg) {
config = cfg;
JSON::Value option;
option["long"] = "json";
option["short"] = "j";
option["help"] = "Output MistIn info in JSON format, then exit.";
option["value"].append(0ll);
config->addOption("json", option);
option.null();
option["arg_num"] = 1ll;
option["arg"] = "string";
option["help"] = "Name of the input file or - for stdin";
option["value"].append("-");
config->addOption("input", option);
option.null();
option["arg_num"] = 2ll;
option["arg"] = "string";
option["help"] = "Name of the output file or - for stdout";
option["value"].append("-");
config->addOption("output", option);
option.null();
option["arg"] = "string";
option["short"] = "s";
option["long"] = "stream";
option["help"] = "The name of the stream that this connector will transmit.";
config->addOption("streamname", option);
option.null();
option["short"] = "p";
option["long"] = "player";
option["help"] = "Makes this connector into a player";
config->addOption("player", option);
packTime = 0;
lastActive = Util::epoch();
playing = 0;
playUntil = 0;
singleton = this;
isBuffer = false;
}
int Input::run() {
if (config->getBool("json")) {
std::cerr << capa.toString() << std::endl;
return 0;
}
if (!setup()) {
std::cerr << config->getString("cmd") << " setup failed." << std::endl;
return 0;
}
if (!readHeader()) {
std::cerr << "Reading header for " << config->getString("input") << " failed." << std::endl;
return 0;
}
parseHeader();
if (!config->getBool("player")){
//check filename for no -
if (config->getString("output") != "-"){
//output to dtsc
DTSC::Meta newMeta = myMeta;
newMeta.reset();
JSON::Value tempVal;
std::ofstream file(config->getString("output").c_str());
long long int bpos = 0;
seek(0);
getNext();
while (lastPack){
tempVal = lastPack.toJSON();
tempVal["bpos"] = bpos;
newMeta.update(tempVal);
file << std::string(lastPack.getData(), lastPack.getDataLen());
bpos += lastPack.getDataLen();
getNext();
}
//close file
file.close();
//create header
file.open((config->getString("output")+".dtsh").c_str());
file << newMeta.toJSON().toNetPacked();
file.close();
}else{
DEBUG_MSG(DLVL_FAIL,"No filename specified, exiting");
}
}else{
//after this player functionality
metaPage.init(config->getString("streamname"), (isBuffer ? 8388608 : myMeta.getSendLen()), true);
myMeta.writeTo(metaPage.mapped);
userPage.init(config->getString("streamname") + "_users", 30, true);
if (!isBuffer){
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
bufferFrame(it->first, 0);
}
}
sem_t * waiting = sem_open(std::string("/wait_" + config->getString("streamname")).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 0);
if (waiting == SEM_FAILED){
DEBUG_MSG(DLVL_FAIL, "Failed to open semaphore - cancelling");
return -1;
}
sem_post(waiting);
sem_close(waiting);
DEBUG_MSG(DLVL_HIGH,"Pre-While");
long long int activityCounter = Util::getMS();
while ((Util::getMS() - activityCounter) < 10000){//1minute timeout
DEBUG_MSG(DLVL_HIGH, "Timer running");
Util::sleep(1000);
removeUnused();
userPage.parseEach(doNothing);
if (userPage.amount){
activityCounter = Util::getMS();
DEBUG_MSG(DLVL_HIGH, "Connected users: %d", userPage.amount);
}
}
DEBUG_MSG(DLVL_DEVEL,"Closing clean");
//end player functionality
}
return 0;
}
void Input::removeUnused(){
for (std::map<unsigned int, std::map<unsigned int, unsigned int> >::iterator it = pageCounter.begin(); it != pageCounter.end(); it++){
for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){
it2->second--;
}
bool change = true;
while (change){
change = false;
for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){
if (!it2->second){
DEBUG_MSG(DLVL_DEVEL, "Erasing page %u:%u", it->first, it2->first);
pagesByTrack[it->first].erase(it2->first);
pageCounter[it->first].erase(it2->first);
change = true;
break;
}
}
}
}
}
void Input::parseHeader(){
DEBUG_MSG(DLVL_DEVEL,"Parsing the header");
//Select all tracks for parsing 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);
//selectedTracks.insert(it->first);
if (trackSpec.str() != ""){
trackSpec << " ";
}
trackSpec << it->first;
DEBUG_MSG(DLVL_VERYHIGH, "Trackspec now %s", trackSpec.str().c_str());
for (std::deque<DTSC::Key>::iterator it2 = it->second.keys.begin(); it2 != it->second.keys.end(); it2++){
keyTimes[it->first].insert(it2->getTime());
}
}
trackSelect(trackSpec.str());
std::map<int, DTSCPageData> curData;
std::map<int, booking> bookKeeping;
seek(0);
getNext();
while(lastPack){//loop through all
int tid = lastPack.getTrackId();
if (!bookKeeping.count(tid)){
bookKeeping[tid].first = 0;
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, 8192, 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 > 8388608) {
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 ++;
getNext(false);
}
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 (!pagesByTrack.count(it->first)){
DEBUG_MSG(DLVL_WARN, "No pages for track %d found", it->first);
}else{
DEBUG_MSG(DLVL_HIGH, "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++){
}
}
}
}
bool Input::bufferFrame(int track, int keyNum){
DEBUG_MSG(DLVL_DONTEVEN, "Attempting to buffer %d:%d", track, keyNum);
if (!pagesByTrack.count(track)){
return false;
}
std::map<int, DTSCPageData> ::iterator it = pagesByTrack[track].upper_bound(keyNum);
if (it == pagesByTrack[track].begin()){
return false;
}
it --;
int pageNum = it->first;
pageCounter[track][pageNum] = 15;///Keep page 15seconds in memory after last use
if (!dataPages[track].count(pageNum)){
char pageId[100];
int pageIdLen = sprintf(pageId, "%s%d_%d", config->getString("streamname").c_str(), track, pageNum);
std::string tmpString(pageId, pageIdLen);
dataPages[track][pageNum].init(tmpString, it->second.dataSize, true);
DEBUG_MSG(DLVL_HIGH, "Buffering page %d through %d / %lu", pageNum, pageNum + it->second.keyNum, myMeta.tracks[track].keys.size());
std::stringstream trackSpec;
trackSpec << track;
trackSelect(trackSpec.str());
}else{
return true;
}
seek(myMeta.tracks[track].keys[pageNum].getTime());
long long unsigned int stopTime = myMeta.tracks[track].lastms + 1;
if ((int)myMeta.tracks[track].keys.size() > pageNum + it->second.keyNum){
stopTime = myMeta.tracks[track].keys[pageNum + it->second.keyNum].getTime();
}
DEBUG_MSG(DLVL_HIGH, "Playing from %ld to %llu", myMeta.tracks[track].keys[pageNum].getTime(), stopTime);
getNext();
while (lastPack && lastPack.getTime() < stopTime){
if (it->second.curOffset + lastPack.getDataLen() > pagesByTrack[track][pageNum].dataSize){
DEBUG_MSG(DLVL_WARN, "Trying to write %u bytes past the end of page %u/%u", lastPack.getDataLen(), track, pageNum);
return true;
}else{
memcpy(dataPages[track][pageNum].mapped + it->second.curOffset, lastPack.getData(), lastPack.getDataLen());
it->second.curOffset += lastPack.getDataLen();
}
getNext();
}
for (int i = 0; i < indexPages[track].len / 8; i++){
if (((long long int*)indexPages[track].mapped)[i] == 0){
((long long int*)indexPages[track].mapped)[i] = (((long long int)htonl(pageNum)) << 32) | htonl(it->second.keyNum);
break;
}
}
return true;
}
bool Input::atKeyFrame(){
static std::map<int, int> lastSeen;
//not in keyTimes? We're not at a keyframe.
unsigned int c = keyTimes[lastPack.getTrackId()].count(lastPack.getTime());
if (!c){
return false;
}
//skip double times
if (lastSeen.count(lastPack.getTrackId()) && lastSeen[lastPack.getTrackId()] == lastPack.getTime()){
return false;
}
//set last seen, and return true
lastSeen[lastPack.getTrackId()] = lastPack.getTime();
return true;
}
void Input::play(int until) {
playing = -1;
playUntil = until;
initialTime = 0;
benchMark = Util::getMS();
}
void Input::playOnce() {
if (playing <= 0) {
playing = 1;
}
++playing;
benchMark = Util::getMS();
}
void Input::quitPlay() {
playing = 0;
}
}

84
src/input/input.h Normal file
View file

@ -0,0 +1,84 @@
#include <set>
#include <map>
#include <cstdlib>
#include <mist/config.h>
#include <mist/json.h>
#include <mist/timing.h>
#include <mist/dtsc.h>
#include <mist/shared_memory.h>
namespace Mist {
struct DTSCPageData {
DTSCPageData() : keyNum(0), partNum(0), dataSize(0), curOffset(0), firstTime(0){}
int keyNum;///<The number of keyframes in this page.
int partNum;///<The number of parts in this page.
unsigned long long int dataSize;///<The full size this page should be.
unsigned long long int curOffset;///<The current write offset in the page.
unsigned long long int firstTime;///<The first timestamp of the page.
unsigned long lastKeyTime;///<The last key time encountered on this track.
};
struct booking {
int first;
int curKey;
int curPart;
};
class Input {
public:
Input(Util::Config * cfg);
int run();
virtual ~Input() {};
protected:
static void doNothing(char * data, size_t len, unsigned int id);
virtual bool setup() = 0;
virtual bool readHeader() = 0;
virtual bool atKeyFrame();
virtual void getNext(bool smart = true) {};
virtual void seek(int seekTime){};
void play(int until = 0);
void playOnce();
void quitPlay();
virtual void removeUnused();
virtual void trackSelect(std::string trackSpec){};
virtual void userCallback(char * data, size_t len, unsigned int id);
void parseHeader();
bool bufferFrame(int track, int keyNum);
unsigned int packTime;///Media-timestamp of the last packet.
int lastActive;///Timestamp of the last time we received or sent something.
int initialTime;
int playing;
unsigned int playUntil;
unsigned int benchMark;
std::set<int> selectedTracks;
bool isBuffer;
Util::Config * config;
JSON::Value capa;
Socket::Connection StatsSocket;
DTSC::Meta myMeta;
DTSC::Packet lastPack;
std::map<int,std::set<int> > keyTimes;
IPC::sharedPage metaPage;
//Create server for user pages
IPC::sharedServer userPage;
//TrackIndex pages
std::map<int, IPC::sharedPage> indexPages;
std::map<int, std::map<int, IPC::sharedPage> > dataPages;
//Page Overview
std::map<int, std::map<int, DTSCPageData> > pagesByTrack;
std::map<unsigned int, std::map<unsigned int, unsigned int> > pageCounter;
static Input * singleton;
};
}

274
src/input/input_buffer.cpp Normal file
View file

@ -0,0 +1,274 @@
#include <iostream>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <mist/stream.h>
#include <mist/defines.h>
#include "input_buffer.h"
namespace Mist {
inputBuffer::inputBuffer(Util::Config * cfg) : Input(cfg) {
JSON::Value option;
option["arg"] = "integer";
option["long"] = "buffer";
option["short"] = "b";
option["help"] = "Buffertime for this stream.";
option["value"].append(30000LL);
config->addOption("bufferTime", option);
capa["desc"] = "Enables buffered live input";
capa["codecs"][0u][0u].append("*");
capa["codecs"][0u][1u].append("*");
capa["codecs"][0u][2u].append("*");
capa["codecs"][0u][3u].append("*");
capa["codecs"][0u][4u].append("*");
capa["codecs"][0u][5u].append("*");
capa["codecs"][0u][6u].append("*");
capa["codecs"][0u][7u].append("*");
capa["codecs"][0u][8u].append("*");
capa["codecs"][0u][9u].append("*");
DEBUG_MSG(DLVL_DEVEL, "Started MistInBuffer");
isBuffer = true;
singleton = this;
bufferTime = 0;
cutTime = 0;
}
void inputBuffer::updateMeta(){
long long unsigned int firstms = 0xFFFFFFFFFFFFFFFF;
long long unsigned int lastms = 0;
for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
if (it->second.firstms < firstms){
firstms = it->second.firstms;
}
if (it->second.firstms > lastms){
lastms = it->second.lastms;
}
}
myMeta.bufferWindow = lastms - firstms;
myMeta.writeTo(metaPage.mapped);
}
bool inputBuffer::removeKey(unsigned int tid){
if (myMeta.tracks[tid].keys.size() < 2 || myMeta.tracks[tid].fragments.size() < 2){
return false;
}
DEBUG_MSG(DLVL_HIGH, "Erasing key %d:%d", tid, myMeta.tracks[tid].keys[0].getNumber());
//remove all parts of this key
for (int i = 0; i < myMeta.tracks[tid].keys[0].getParts(); i++){
myMeta.tracks[tid].parts.pop_front();
}
//remove the key itself
myMeta.tracks[tid].keys.pop_front();
//re-calculate firstms
myMeta.tracks[tid].firstms = myMeta.tracks[tid].keys[0].getTime();
//delete the fragment if it's no longer fully buffered
if (myMeta.tracks[tid].fragments[0].getNumber() < myMeta.tracks[tid].keys[0].getNumber()){
myMeta.tracks[tid].fragments.pop_front();
myMeta.tracks[tid].missedFrags ++;
}
//if there is more than one page buffered for this track...
if (inputLoc[tid].size() > 1){
//Check if the first key starts on the second page or higher
if (myMeta.tracks[tid].keys[0].getNumber() >= (++(inputLoc[tid].begin()))->first){
//Find page in indexpage and null it
for (int i = 0; i < 8192; i += 8){
int thisKeyNum = ((((long long int *)(indexPages[tid].mapped + i))[0]) >> 32) & 0xFFFFFFFF;
if (thisKeyNum == htonl(pagesByTrack[tid].begin()->first) && ((((long long int *)(indexPages[tid].mapped + i))[0]) != 0)){
(((long long int *)(indexPages[tid].mapped + i))[0]) = 0;
}
}
DEBUG_MSG(DLVL_DEVEL, "Erasing track %d, keys %lu-%lu from buffer", tid, inputLoc[tid].begin()->first, inputLoc[tid].begin()->first + inputLoc[tid].begin()->second.keyNum - 1);
inputLoc[tid].erase(inputLoc[tid].begin());
dataPages[tid].erase(dataPages[tid].begin());
}else{
DEBUG_MSG(DLVL_HIGH, "%d still on first page (%lu - %lu)", myMeta.tracks[tid].keys[0].getNumber(), inputLoc[tid].begin()->first, inputLoc[tid].begin()->first + inputLoc[tid].begin()->second.keyNum - 1);
}
}
return true;
}
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++){
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++){
//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){
continue;
}
}
//Buffer cutting
while(it->second.keys.size() > 1 && it->second.keys[0].getTime() < cutTime){
if (!removeKey(it->first)){break;}
}
//Buffer size management
while(it->second.keys.size() > 1 && (it->second.lastms - it->second.keys[1].getTime()) > bufferTime){
if (!removeKey(it->first)){break;}
}
}
updateMeta();
}
void inputBuffer::userCallback(char * data, size_t len, unsigned int id) {
unsigned long tmp = ((long)(data[0]) << 24) | ((long)(data[1]) << 16) | ((long)(data[2]) << 8) | ((long)(data[3]));
if (tmp & 0x80000000) {
//Track is set to "New track request", assign new track id and create shared memory page
unsigned long tNum = (givenTracks.size() ? (*givenTracks.rbegin()) : 0) + 1;
///\todo Neatify this
data[0] = (tNum >> 24) & 0xFF;
data[1] = (tNum >> 16) & 0xFF;
data[2] = (tNum >> 8) & 0xFF;
data[3] = (tNum) & 0xFF;
givenTracks.insert(tNum);
char tmpChr[100];
long tmpLen = sprintf(tmpChr, "liveStream_%s%lu", config->getString("streamname").c_str(), tNum);
metaPages[tNum].init(std::string(tmpChr, tmpLen), 8388608, true);
} else {
unsigned long tNum = ((long)(data[0]) << 24) | ((long)(data[1]) << 16) | ((long)(data[2]) << 8) | ((long)(data[3]));
if (!myMeta.tracks.count(tNum)) {
DEBUG_MSG(DLVL_DEVEL, "Tracknum not in meta: %lu, from user %u", tNum, id);
if (metaPages[tNum].mapped) {
if (metaPages[tNum].mapped[0] == 'D' && metaPages[tNum].mapped[1] == 'T') {
unsigned int len = ntohl(((int *)metaPages[tNum].mapped)[1]);
unsigned int i = 0;
JSON::Value tmpMeta;
JSON::fromDTMI((const unsigned char *)metaPages[tNum].mapped + 8, len, i, tmpMeta);
DTSC::Meta tmpTrack(tmpMeta);
int oldTNum = tmpTrack.tracks.begin()->first;
bool collision = false;
for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
if (it->first == tNum) {
continue;
}
if (it->second.getIdentifier() == tmpTrack.tracks[oldTNum].getIdentifier()) {
collision = true;
break;
}
}
if (collision) {
/// \todo Erasing page for now, should do more here
DEBUG_MSG(DLVL_DEVEL, "Collision detected! Erasing page for now, should do more here");
metaPages.erase(tNum);
data[0] = 0xFF;
data[1] = 0xFF;
data[2] = 0xFF;
data[3] = 0xFF;
} else {
if (!myMeta.tracks.count(tNum)) {
myMeta.tracks[tNum] = tmpTrack.tracks[oldTNum];
data[4] = 0x00;
data[5] = 0x00;
updateMeta();
char firstPage[100];
sprintf(firstPage, "%s%lu", config->getString("streamname").c_str(), tNum);
indexPages[tNum].init(firstPage, 8192, true);
((long long int *)indexPages[tNum].mapped)[0] = htonl(1000);
///\todo Fix for non-first-key-pushing
sprintf(firstPage, "%s%lu_0", config->getString("streamname").c_str(), tNum);
///\todo Make size dynamic / other solution. 25mb is too much.
dataPages[tNum][0].init(firstPage, 26214400, true);
}
}
}
}
} else {
//First check if the previous page has been finished:
if (!inputLoc[tNum].count(dataPages[tNum].rbegin()->first) || !inputLoc[tNum][dataPages[tNum].rbegin()->first].curOffset){
if (dataPages[tNum].size() > 1){
int prevPage = (++dataPages[tNum].rbegin())->first;
//update previous page.
updateMetaFromPage(tNum, prevPage);
}
}
//update current page
int curPage = dataPages[tNum].rbegin()->first;
updateMetaFromPage(tNum, curPage);
if (inputLoc[tNum][curPage].curOffset > 8388608) {
//create new page is > 8MB
int nxtPage = curPage + inputLoc[tNum][curPage].keyNum;
char nextPageName[100];
sprintf(nextPageName, "%s%lu_%d", config->getString("streamname").c_str(), tNum, nxtPage);
dataPages[tNum][nxtPage].init(nextPageName, 20971520, true);
bool createdNew = false;
for (int i = 0; i < 8192; i += 8){
int thisKeyNum = ((((long long int *)(indexPages[tNum].mapped + i))[0]) >> 32) & 0xFFFFFFFF;
if (thisKeyNum == htonl(curPage)){
if((ntohl((((long long int*)(indexPages[tNum].mapped + i))[0]) & 0xFFFFFFFF) == 1000)){
((long long int *)(indexPages[tNum].mapped + i))[0] &= 0xFFFFFFFF00000000;
((long long int *)(indexPages[tNum].mapped + i))[0] |= htonl(inputLoc[tNum][curPage].keyNum);
}
}
if (!createdNew && (((long long int*)(indexPages[tNum].mapped + i))[0]) == 0){
createdNew = true;
((long long int *)(indexPages[tNum].mapped + i))[0] = (((long long int)htonl(nxtPage)) << 32) | htonl(1000);
}
}
}
}
}
}
void inputBuffer::updateMetaFromPage(int tNum, int pageNum){
DTSC::Packet tmpPack;
tmpPack.reInit(dataPages[tNum][pageNum].mapped + inputLoc[tNum][pageNum].curOffset, 0);
while (tmpPack) {
myMeta.update(tmpPack);
if (inputLoc[tNum][pageNum].firstTime == 0){
inputLoc[tNum][pageNum].firstTime = tmpPack.getTime();
}
//Overloaded use of .firstTime to indicate last Keytime on non-video streams;
if (myMeta.tracks[tNum].type == "video"){
inputLoc[tNum][pageNum].keyNum += tmpPack.getFlag("keyframe");
}else{
if ((tmpPack.getTime() > 5000) && ((tmpPack.getTime() - 5000) > inputLoc[tNum][pageNum].firstTime)){
inputLoc[tNum][pageNum].keyNum ++;
}
}
inputLoc[tNum][pageNum].curOffset += tmpPack.getDataLen();
tmpPack.reInit(dataPages[tNum][pageNum].mapped + inputLoc[tNum][pageNum].curOffset, 0);
}
updateMeta();
}
bool inputBuffer::setup() {
if (!bufferTime){
bufferTime = config->getInteger("bufferTime");
}
JSON::Value servConf = JSON::fromFile(Util::getTmpFolder() + "streamlist");
if (servConf.isMember("streams") && servConf["streams"].isMember(config->getString("streamname"))){
JSON::Value & streamConfig = servConf["streams"][config->getString("streamname")];
if (streamConfig.isMember("DVR") && streamConfig["DVR"].asInt()){
if (bufferTime != streamConfig["DVR"].asInt()){
DEBUG_MSG(DLVL_DEVEL, "Setting bufferTime from %u to new value of %lli", bufferTime, streamConfig["DVR"].asInt());
bufferTime = streamConfig["DVR"].asInt();
}
}
}
return true;
}
bool inputBuffer::readHeader() {
return true;
}
void inputBuffer::getNext(bool smart) {}
void inputBuffer::seek(int seekTime) {}
void inputBuffer::trackSelect(std::string trackSpec) {}
}

33
src/input/input_buffer.h Normal file
View file

@ -0,0 +1,33 @@
#include "input.h"
#include <mist/dtsc.h>
#include <mist/shared_memory.h>
namespace Mist {
class inputBuffer : public Input {
public:
inputBuffer(Util::Config * cfg);
private:
unsigned int bufferTime;
unsigned int cutTime;
protected:
//Private Functions
bool setup();
void updateMeta();
bool readHeader();
void getNext(bool smart = true);
void updateMetaFromPage(int tNum, int pageNum);
void seek(int seekTime);
void trackSelect(std::string trackSpec);
bool removeKey(unsigned int tid);
void removeUnused();
void userCallback(char * data, size_t len, unsigned int id);
std::set<unsigned long> givenTracks;
std::map<unsigned long, IPC::sharedPage> metaPages;
std::map<unsigned long, std::map<unsigned long, DTSCPageData> > inputLoc;
inputBuffer * singleton;
};
}
typedef Mist::inputBuffer mistIn;

90
src/input/input_dtsc.cpp Normal file
View file

@ -0,0 +1,90 @@
#include <iostream>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <mist/stream.h>
#include <mist/defines.h>
#include "input_dtsc.h"
namespace Mist {
inputDTSC::inputDTSC(Util::Config * cfg) : Input(cfg) {
capa["decs"] = "Enables DTSC Input";
capa["codecs"][0u][0u].append("H264");
capa["codecs"][0u][0u].append("H263");
capa["codecs"][0u][0u].append("VP6");
capa["codecs"][0u][0u].append("theora");
capa["codecs"][0u][1u].append("AAC");
capa["codecs"][0u][1u].append("MP3");
capa["codecs"][0u][1u].append("vorbis");
}
bool inputDTSC::setup() {
if (config->getString("input") == "-") {
std::cerr << "Input from stream not yet supported" << std::endl;
return false;
}
if (config->getString("output") != "-") {
std::cerr << "Output to non-stdout not yet supported" << std::endl;
}
//open File
inFile = DTSC::File(config->getString("input"));
if (!inFile) {
return false;
}
return true;
}
bool inputDTSC::readHeader() {
if (!inFile) {
return false;
}
DTSC::File tmp(config->getString("input") + ".dtsh");
if (tmp) {
myMeta = tmp.getMeta();
DEBUG_MSG(DLVL_DEVEL,"Meta read in with %lu tracks", myMeta.tracks.size());
return true;
}
if (inFile.getMeta().moreheader < 0 || inFile.getMeta().tracks.size() == 0) {
DEBUG_MSG(DLVL_FAIL,"Missing external header file");
return false;
}
myMeta = DTSC::Meta(inFile.getMeta());
DEBUG_MSG(DLVL_DEVEL,"Meta read in with %lu tracks", myMeta.tracks.size());
return true;
}
void inputDTSC::getNext(bool smart) {
if (smart){
inFile.seekNext();
}else{
inFile.parseNext();
}
lastPack = inFile.getPacket();
}
void inputDTSC::seek(int seekTime) {
inFile.seek_time(seekTime);
initialTime = 0;
playUntil = 0;
}
void inputDTSC::trackSelect(std::string trackSpec) {
selectedTracks.clear();
long long unsigned int index;
while (trackSpec != "") {
index = trackSpec.find(' ');
selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str()));
if (index != std::string::npos) {
trackSpec.erase(0, index + 1);
} else {
trackSpec = "";
}
}
inFile.selectTracks(selectedTracks);
}
}

22
src/input/input_dtsc.h Normal file
View file

@ -0,0 +1,22 @@
#include "input.h"
#include <mist/dtsc.h>
namespace Mist {
class inputDTSC : public Input {
public:
inputDTSC(Util::Config * cfg);
protected:
//Private Functions
bool setup();
bool readHeader();
void getNext(bool smart = true);
void seek(int seekTime);
void trackSelect(std::string trackSpec);
DTSC::File inFile;
};
}
typedef Mist::inputDTSC mistIn;

130
src/input/input_flv.cpp Normal file
View file

@ -0,0 +1,130 @@
#include <iostream>
#include <fstream>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <mist/stream.h>
#include <mist/flv_tag.h>
#include <mist/defines.h>
#include "input_flv.h"
namespace Mist {
inputFLV::inputFLV(Util::Config * cfg) : Input(cfg) {
capa["decs"] = "Enables FLV Input";
capa["codecs"][0u][0u].append("H264");
capa["codecs"][0u][0u].append("H263");
capa["codecs"][0u][0u].append("VP6");
capa["codecs"][0u][1u].append("AAC");
capa["codecs"][0u][1u].append("MP3");
}
bool inputFLV::setup() {
if (config->getString("input") == "-") {
std::cerr << "Input from stream not yet supported" << std::endl;
return false;
}
if (config->getString("output") != "-") {
std::cerr << "Output to non-stdout not yet supported" << std::endl;
}
//open File
inFile = fopen(config->getString("input").c_str(), "r");
if (!inFile) {
return false;
}
return true;
}
bool inputFLV::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 FLV data
fseek(inFile, 13, SEEK_SET);
FLV::Tag tmpTag;
long long int lastBytePos = 13;
while (!feof(inFile) && !FLV::Parse_Error){
if (tmpTag.FileLoader(inFile)){
lastPack.null();
lastPack = tmpTag.toJSON(myMeta);
lastPack["bpos"] = lastBytePos;
myMeta.update(lastPack);
lastBytePos = ftell(inFile);
}
}
if (FLV::Parse_Error){
std::cerr << FLV::Error_Str << std::endl;
return false;
}
std::ofstream oFile(std::string(config->getString("input") + ".dtsh").c_str());
oFile << myMeta.toJSON().toNetPacked();
oFile.close();
return true;
}
void inputFLV::getNext(bool smart) {
static JSON::Value thisPack;
thisPack.null();
long long int lastBytePos = ftell(inFile);
FLV::Tag tmpTag;
while (!feof(inFile) && !FLV::Parse_Error){
if (tmpTag.FileLoader(inFile)){
thisPack = tmpTag.toJSON(myMeta);
thisPack["bpos"] = lastBytePos;
if ( !selectedTracks.count(thisPack["trackid"].asInt())){
getNext();
}
break;
}
}
if (FLV::Parse_Error){
std::cerr << FLV::Error_Str << std::endl;
thisPack.null();
lastPack.null();
return;
}
std::string tmpStr = thisPack.toNetPacked();
lastPack.reInit(tmpStr.data(), tmpStr.size());
}
void inputFLV::seek(int seekTime) {
//We will seek to the corresponding keyframe of the video track if selected, otherwise audio keyframe.
//Flv files are never multi-track, so track 1 is video, track 2 is audio.
int trackSeek = (selectedTracks.count(1) ? 1 : 2);
size_t seekPos = myMeta.tracks[trackSeek].keys[0].getBpos();
for (int i = 0; i < myMeta.tracks[trackSeek].keys.size(); i++){
if (myMeta.tracks[trackSeek].keys[i].getTime() > seekTime){
DEBUG_MSG(DLVL_WARN, "Seeking to keyframe %d on track %d, timestamp %ld, bytepos %lu", i, trackSeek, myMeta.tracks[trackSeek].keys[i].getTime(), seekPos);
break;
}
seekPos = myMeta.tracks[trackSeek].keys[i].getBpos();
}
fseek(inFile, seekPos, SEEK_SET);
}
void inputFLV::trackSelect(std::string trackSpec) {
selectedTracks.clear();
long long int index;
while (trackSpec != "") {
index = trackSpec.find(' ');
selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str()));
DEBUG_MSG(DLVL_WARN, "Added track %d, index = %lld, (index == npos) = %d", atoi(trackSpec.substr(0, index).c_str()), index, index == std::string::npos);
if (index != std::string::npos) {
trackSpec.erase(0, index + 1);
} else {
trackSpec = "";
}
}
}
}

21
src/input/input_flv.h Normal file
View file

@ -0,0 +1,21 @@
#include "input.h"
#include <mist/dtsc.h>
namespace Mist {
class inputFLV : public Input {
public:
inputFLV(Util::Config * cfg);
protected:
//Private Functions
bool setup();
bool readHeader();
void getNext(bool smart = true);
void seek(int seekTime);
void trackSelect(std::string trackSpec);
FILE * inFile;
};
}
typedef Mist::inputFLV mistIn;

274
src/input/input_ogg.cpp Normal file
View file

@ -0,0 +1,274 @@
#include <iostream>
#include <fstream>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <mist/stream.h>
#include <mist/ogg.h>
#include <mist/defines.h>
#include <mist/bitstream.h>
#include "input_ogg.h"
namespace Mist {
inputOGG::inputOGG(Util::Config * cfg) : Input(cfg) {
capa["decs"] = "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 stream not yet supported" << std::endl;
return false;
}
if (config->getString("output") != "-") {
std::cerr << "Output to non-stdout not yet supported" << std::endl;
}
//open File
inFile = fopen(config->getString("input").c_str(), "r");
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();
}
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());
}
}
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
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()]);
}
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);
}
}
lastBytePos = ftell(inFile);
DEBUG_MSG(DLVL_WARN,"End of Loop, @ filepos %lld", lastBytePos);
}
DEBUG_MSG(DLVL_WARN,"Exited while loop");
std::ofstream oFile(std::string(config->getString("input") + ".dtsh").c_str());
oFile << myMeta.toJSON().toNetPacked();
oFile.close();
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;
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 (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;
}
}
}
std::string data;
}
}
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) {
selectedTracks.clear();
long long int index;
while (trackSpec != "") {
index = trackSpec.find(' ');
selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str()));
DEBUG_MSG(DLVL_WARN, "Added track %d, index = %lld, (index == npos) = %d", atoi(trackSpec.substr(0, index).c_str()), index, index == std::string::npos);
if (index != std::string::npos) {
trackSpec.erase(0, index + 1);
} else {
trackSpec = "";
}
}
}
}

65
src/input/input_ogg.h Normal file
View file

@ -0,0 +1,65 @@
#include "input.h"
#include <mist/dtsc.h>
#include <mist/ogg.h>
namespace Mist {
enum codecType {THEORA, VORBIS};
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 oggTrack{
public:
oggTrack() : lastTime(0), parsedHeaders(false), lastPageOffset(0), nxtSegment(0) { }
codecType codec;
std::string contBuffer;//buffer for continuing pages
double lastTime;
long long unsigned int lastGran;
bool parsedHeaders;
double msPerFrame;
long long unsigned int lastPageOffset;
OGG::Page myPage;
unsigned int nxtSegment;
//Codec specific elements
//theora
theora::header idHeader;
//vorbis
std::deque<vorbis::mode> vModes;
char channels;
long long unsigned int blockSize[2];
};
class inputOGG : public Input {
public:
inputOGG(Util::Config * cfg);
protected:
//Private Functions
bool setup();
bool readHeader();
bool seekNextPage(int tid);
void getNext(bool smart = true);
void seek(int seekTime);
void trackSelect(std::string trackSpec);
void parseBeginOfStream(OGG::Page & bosPage);
FILE * inFile;
std::map<long long int, long long int> snum2tid;
std::map<long long int, oggTrack> oggTracks;
std::set<segment> sortedSegments;
};
}
typedef Mist::inputOGG mistIn;

62
src/input/mist_in.cpp Normal file
View file

@ -0,0 +1,62 @@
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <unistd.h>
#include <semaphore.h>
#include INPUTTYPE
#include <mist/config.h>
#include <mist/defines.h>
int main(int argc, char * argv[]) {
Util::Config conf(argv[0], PACKAGE_VERSION);
mistIn conv(&conf);
if (conf.parseArgs(argc, argv)) {
sem_t * playerLock = sem_open(std::string("/lock_" + conf.getString("streamname")).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 1);
if (sem_trywait(playerLock) == -1){
DEBUG_MSG(DLVL_DEVEL, "A player for stream %s is already running", conf.getString("streamname").c_str());
return 1;
}
conf.activate();
while (conf.is_active){
int pid = fork();
if (pid == 0){
sem_close(playerLock);
return conv.run();
}
if (pid == -1){
DEBUG_MSG(DLVL_FAIL, "Unable to spawn player process");
sem_post(playerLock);
return 2;
}
//wait for the process to exit
int status;
while (waitpid(pid, &status, 0) != pid && errno == EINTR) continue;
//clean up the semaphore by waiting for it, if it's non-zero
sem_t * waiting = sem_open(std::string("/wait_" + conf.getString("streamname")).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 0);
if (waiting == SEM_FAILED){
DEBUG_MSG(DLVL_FAIL, "Failed to open semaphore - cancelling");
return -1;
}
int sem_val = 0;
sem_getvalue(waiting, &sem_val);
while (sem_val){
while (sem_wait(waiting) == -1 && errno == EINTR) continue;
sem_getvalue(waiting, &sem_val);
}
sem_close(waiting);
//if the exit was clean, don't restart it
if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)){
DEBUG_MSG(DLVL_DEVEL, "Finished player succesfully");
break;
}
}
sem_post(playerLock);
sem_close(playerLock);
}
return 0;
}