#include "Manager.h" ListItem::ListItem(string passType, string *Path, unsigned long *passUID) { type = pType_unknown; if(!strcmp(passType.c_str(), pType_audio_file)) type = pType_audio_file; if(!strcmp(passType.c_str(), pType_input_device)) type = pType_input_device; if(!strcmp(passType.c_str(), pType_realtime_stream)) type = pType_realtime_stream; if(!strcmp(passType.c_str(), pType_list_stop)) type = pType_list_stop; if(!strcmp(passType.c_str(), pType_db_item)) type = pType_db_item; if(!strcmp(passType.c_str(), pType_iax_line)) type = pType_iax_line; connected = false; disconnect = false; outputSource = NULL; status = status_empty; UID = 0; position = 0.0; fade = 0.0; next = -1; segout = 0.0; bufftime = 0; pNum = -1; if(passUID == NULL){ UID = createMetaRecord(*Path); dMutex->readLock(true); metaDataPtr rec = metaList[UID]; dMutex->writeLock(true); rec->metaData["Type"] = passType; // Type value set dMutex->writeUnlock(); dMutex->readUnlock(); }else UID = *passUID; registerMetaUser(UID); } ListItem::~ListItem() { deleteMetaRecord(UID); // see if the metaData record can be deleted from the metaData List DeleteFromLists(this); } void ListItem::start() { } void ListItem::stop() { } void ListItem::volChange(float vol) { } void ListItem::setPosition(float time) { } void ListItem::setStatus(unsigned long newStat) { } void DeleteItem(int pos) { if(pos < 0) return; // remove from the playlist lMutex->readLock(true); vector::iterator lp = PlayList.begin(); advance(lp, pos); if (lp < PlayList.end()){ lMutex->writeLock(true); if(PlayList.at(pos)->Managed) delete PlayList.at(pos); // delete (totally) the ARPlayer pointed to at this position else PlayList.erase(lp); // delete just from the playlist lMutex->writeUnlock(); plRev++; } lMutex->readUnlock(); } unsigned long AddItem(int pos, string URLstr) { ARPlayer *instance; vector::iterator lp; unsigned long newID; CFURLRef url; CFStringRef type_str; char ctype[8]; if(!URLfromString(&url, URLstr.c_str())) return 0; type_str = CFURLCopyScheme(url); if(type_str == NULL){ CFRelease(url); return 0; } // make lower case and c-string CFStringGetCString(type_str, ctype, sizeof(ctype), CFStringGetSystemEncoding()); for(unsigned char idx = 0; idx < strlen(ctype); idx = idx + 1) ctype[idx] = tolower(ctype[idx]); CFRelease(type_str); CFRelease(url); string Type(ctype); lMutex->readLock(true); if(pos < 0){ // negative position indicates end of the list lp = PlayList.end(); }else{ lp = PlayList.begin(); advance(lp, pos); } if (lp <= PlayList.end()){ //create item instance instance = new ListItem(Type, &URLstr, NULL); // insert into the list lMutex->writeLock(true); PlayList.insert(lp, instance); lMutex->writeUnlock(); newID = instance->UID; //load the meta data into the item UID meta data. GetURLMetaData(newID, URLstr.c_str()); if(GetMetaData(newID, "Type") == "task"){ // try running the task dbTaskRunner(newID, false); } // add handling below for missing file items and task items if(atoi(GetMetaData(newID, "Missing").c_str())){ // the file is missing... delete this item lMutex->writeLock(true); delete instance; lMutex->writeUnlock(); newID = 0; }else{ //check for valid segout time float segoutT, dur; char buf[32]; dur = atof(GetMetaData(newID, "Duration").c_str()); if(dur){ segoutT = atof(GetMetaData(newID, "SegOut").c_str()); if(segoutT == 0.0){ // no segout time specified, use default segout time from the end of the item segoutT = dur - atof(GetMetaData(0, "def_segout").c_str()); snprintf(buf, sizeof buf, "%0.1f", segoutT); SetMetaData(newID, "SegOut", string(buf)); } } } }else{ newID = 0; } if(newID){ plRev++; // create log entry ProgramLoger->UIDEntry(newID, true, false); } lMutex->readUnlock(); return newID; } void AddPlayer(int pos, int pNum) { ARPlayer *instance; ARPlayer *check; vector::iterator lp; if(!checkPnumber(pNum)) return; lMutex->readLock(true); if(pos < 0){ // negative position indicates end of the list lp = PlayList.end(); }else{ lp = PlayList.begin(); advance(lp, pos); } if (lp <= PlayList.end()){ //get item instance pMutex->readLock(true); instance = pList[pNum]; if(instance == NULL){ pMutex->readUnlock(); lMutex->readUnlock(); return; } // check to make sure this item (by UID) is not already in the playlist for(unsigned int idx=0; idxUID == instance->UID){ // a dup has been found... bail out! pMutex->readUnlock(); lMutex->readUnlock(); return; } } // still going; No dup, so insert into the list lMutex->writeLock(true); PlayList.insert(lp, instance); plRev++; lMutex->writeUnlock(); // clear needed status flags instance->status = instance->status & ~status_hasPlayed; instance->status = instance->status & ~status_logged; //check for valid segout time float segoutT, dur; char buf[32]; dur = atof(GetMetaData(instance->UID, "Duration").c_str()); if(dur){ segoutT = atof(GetMetaData(instance->UID, "SegOut").c_str()); if(segoutT == 0.0){ // no segout time specified, use default segout time from the end of the item segoutT = dur - atof(GetMetaData(0, "def_segout").c_str()); snprintf(buf, sizeof buf, "%0.1f", segoutT); SetMetaData(instance->UID, "SegOut", string(buf)); } } } pMutex->readUnlock(); lMutex->readUnlock(); ProgramLoger->UIDEntry(instance->UID, true, false); } void MoveItem(int sourcePos, int destPos) { ARPlayer *instance; vector::iterator lp; if (sourcePos < 0) return; if (sourcePos == destPos) return; lMutex->readLock(true); if (destPos > (signed int)(PlayList.size() - 1)) return; //get source item instance instance = PlayList.at(sourcePos); if(!instance){ lMutex->readUnlock(); return; } // delete from old position lp = PlayList.begin(); advance(lp, sourcePos); lMutex->writeLock(true); PlayList.erase(lp); // insert into the playlist at new position if(destPos < 0){ // negative position indicates end of the list lp = PlayList.end(); }else{ lp = PlayList.begin(); advance(lp, destPos); } // re-insert into the list PlayList.insert(lp, instance); plRev++; // new rev number since the list changed lMutex->writeUnlock(); lMutex->readUnlock(); } int LoadItem(int pos) { // assumes lMutex and pMutex is alread locked! int i; ARPlayer *instance, *newPlayer; vector::iterator lp; unsigned char result; if(pos < 0) return -2; lMutex->readLock(true); lp = PlayList.begin(); advance(lp, pos); if (lp >= PlayList.end()){ // pos out of range lMutex->readUnlock(); return -2; } instance = PlayList.at(pos); if (instance == NULL){ lMutex->readUnlock(); return -2; } if(!strcmp(instance->type, pType_list_stop)){ // it's a playlist stop item... stop the playlist! plRunning = false; lMutex->readUnlock(); return -2; } if (instance->pNum > -1){ // already in a player! lMutex->readUnlock(); return -2; } if (instance->UID == 0){ // No meta Data to base load on... lMutex->readUnlock(); return -2; } // get meta data dMutex->readLock(true); metaDataPtr rec = metaList[instance->UID]; string url = rec->metaData["URL"]; dMutex->readUnlock(); for(i = 0; i < inBusNum; i++){ if(pList[i] == NULL) break; } if(i == inBusNum){ //no available players lMutex->readUnlock(); return -1; } // i is the player number to load result = LoadPlayer(&i, url.c_str(), instance->UID); newPlayer = pList[i]; if(newPlayer != NULL){ // load suceesed! // replace the old instance with the new player in the playlist lMutex->writeLock(true); PlayList[pos] = newPlayer; // flag the palyer as being managed (since we loaded it and not a user) newPlayer->Managed = true; plRev++; // new rev number since the list changed lMutex->writeUnlock(); delete instance; // get rid of the old instance lMutex->readUnlock(); return i; }else{ lMutex->readUnlock(); if(result) return -3; // -3 indicated to keep in playlist... AR will handle it else return -2; } } void UnloadItem(int pos) { // assumes lMutex and pMutex is alread locked! ARPlayer *instance, *newItem; vector::iterator lp; if(pos < 0) return; lMutex->readLock(true); lp = PlayList.begin(); advance(lp, pos); if (lp >= PlayList.end()){ // pos out of range lMutex->readUnlock(); return; } instance = PlayList.at(pos); if (instance == NULL){ lMutex->readUnlock(); return; } if (!checkPnumber(instance->pNum)){ // not a valid pNum! lMutex->readUnlock(); return; } newItem = new ListItem(instance->type, NULL, &(instance->UID)); if(newItem != NULL){ // replace the old instance with the new player in the playlist lMutex->writeLock(true); PlayList[pos] = newItem; plRev++; // new rev number since the list changed lMutex->writeUnlock(); delete instance; // get rid of the old instance } lMutex->readUnlock(); return; } void setSegTimes(ARPlayer *thisp, ARPlayer *nextp) { double seginT, segoutT, dur; if(thisp == NULL) return; if(nextp == NULL) return; dur = atof(GetMetaData(thisp->UID, "Duration").c_str()); if(dur == 0) // no duration... realtime items do not do automatic segue return; if(checkPnumber(nextp->pNum)){ //get segout time of the source item segoutT = atof(GetMetaData(thisp->UID, "SegOut").c_str()); if(segoutT == 0.0) // no segue for items with segout set to zero. return; //get segin time of the next item seginT = atof(GetMetaData(nextp->UID, "SegIn").c_str()); thisp->next = nextp->pNum; thisp->segout = segoutT - seginT; } } void NextListItem(unsigned long lastStat, int index, int *firstp, double *sbtime, unsigned char *isPlaying) { // assumes lMutex and pMutex is alread read locked! ARPlayer *instance, *next; int result, nextp; unsigned long status; *firstp = -1; if((signed int)PlayList.size() > index){ instance = PlayList[index]; if(instance != NULL){ if(!checkPnumber(instance->pNum)){ // not in a player... // check the next one to see if it is playing if((signed int)PlayList.size() > index+1){ next = PlayList[index+1]; if(next != NULL){ if(checkPnumber(next->pNum)){ if(next->Managed && (next->status & status_playing)){ // next item IS playing... and it was not put there manually - delete this item DeleteItem(index); return; } } } } // next, load the item if needed if(lastStat & status_standby){ // previous item is loaded in a player. if(*sbtime < 60){ // less than 60 seconds cued up in players... load this items too if(instance->type != pType_unknown){ result = LoadItem(index); if(result < 0){ if(result == -1){ // waiting for a player instance->status = status_waiting; }else if(result == -2){ // can't load it... delete DeleteItem(index); plRev++; } } } } } } if((signed int)PlayList.size() > index){ status = status_empty; instance = PlayList[index]; if(instance != NULL){ *firstp = instance->pNum; status = instance->status; if(status & status_standby){ if(status & status_hasPlayed){ if(status & status_finished){ if((status & status_playing) == 0){ // has played but is now stopped... Finished! DeleteItem(index); *firstp = -1; }else{ *isPlaying = true; } }else{ *isPlaying = true; } } if(status == status_standby){ if(*sbtime > 60){ if(instance->Managed){ // already have more than 60 seconds already loaded in standby... unload this one UnloadItem(index); *firstp = -1; } }else{ // less than 60 seconds loaded in standby befor this one... add this one to the count float dur; dur = atof(GetMetaData(instance->UID, "Duration").c_str()); *sbtime = *sbtime + dur; } } } NextListItem(status, index + 1, &nextp, sbtime, isPlaying); // set seg times into next item if(checkPnumber(nextp)){ if(checkPnumber(*firstp)){ next = pList[nextp]; if(next != NULL){ if(next->status & status_standby){ if(!(next->status & status_playing)){ if(plRunning){ if(instance->next != next->pNum){ setSegTimes(instance, next); } }else{ if(instance->next != -1){ // unhook any segue times that have been set if the list is not running instance->next = -1; } } } } } } } } } } } } void* ListManagerTask(void *refCon) { unsigned char isPlaying, lastState; int firstp; double sbtime; ARPlayer *instance; struct timespec abstime; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); lastState = false; while(1){ isPlaying = false; firstp = -1; sbtime = 0; pMutex->readLock(true); lMutex->readLock(true); // recursively iterate through each play list item doing what need to be done with each NextListItem(status_standby, 0, &firstp, &sbtime, &isPlaying); if(plRunning){ // stuff to do while the play list is running if(PlayList.size() < 1){ plRunning = false; }else{ if(!lastState){ // playlist has started - log it. ProgramLogStruct *entryRec; entryRec = new ProgramLogStruct; entryRec->name = "--- Play List Started ---"; entryRec->ID = 0; entryRec->location = atol(GetMetaData(0, "db_loc").c_str()); entryRec->albumID = 0; entryRec->artistID = 0; entryRec->added = false; entryRec->played = true; ProgramLoger->MakeEntry(*entryRec); delete entryRec; lastState = true; } if(!isPlaying){ // nothing is playing! Get going... if(checkPnumber(firstp)){ instance = pList[firstp]; if(instance != NULL){ if(instance->status & status_standby){ instance->start(); } } } } } }else{ if(!isPlaying && lastState){ // playlist has stoped - log it. ProgramLogStruct *entryRec; entryRec = new ProgramLogStruct; entryRec->name = "--- Play List Stoped ---"; entryRec->ID = 0; entryRec->location = atol(GetMetaData(0, "db_loc").c_str()); entryRec->albumID = 0; entryRec->artistID = 0; entryRec->added = false; entryRec->played = true; ProgramLoger->MakeEntry(*entryRec); delete entryRec; lastState = false; } } lMutex->readUnlock(); pMutex->readUnlock(); // check the task list for finished or timed out tasks tMutex->readLock(true); TaskItem *instance; for(unsigned int x=0; xfinished){ delete instance; }else{ if(instance->timeOut > 0){ instance->timeOut = instance->timeOut - 1; if(instance->timeOut < 1){ delete instance; if(msg){ char buf[96]; snprintf(buf, sizeof buf, "Task- %08lxthread sig# %lu: timed out.\n", instance->UID, instance->thread->sig); ServerLoger->MakeEntry(buf); } } } } } tMutex->readUnlock(); // Wait for a signal to check the list again or a time out abstime.tv_nsec = 0; abstime.tv_sec = time(NULL) + 10; // set time-out for 10 seconds from now pthread_mutex_lock( &mgrMutex ); pthread_cond_timedwait(&mgrSemaphore, &mgrMutex, &abstime); pthread_mutex_unlock( &mgrMutex ); } return NULL; } void wakeManager(void) { pthread_mutex_lock( &mgrMutex ); pthread_cond_signal( &mgrSemaphore ); pthread_mutex_unlock( &mgrMutex ); }