/* * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2008 Apple Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ * */ #include "MP3Broadcaster.h" #include "MP3MetaInfoUpdater.h" #include "StringTranslator.h" #include "../defaultPaths.h" #include #include #include "SafeStdLib.h" #include #include #ifndef __Win32__ #include #endif #ifndef __Win32__ #include #if defined (__solaris__) || defined (__osf__) || defined (__hpux__) #include "daemon.h" #else #ifndef __FreeBSD__ #include #endif #endif #endif #include #include "OSHeaders.h" #include "OS.h" #include "OSMemory.h" #include "Task.h" #include "TimeoutTask.h" #include "OSArrayObjectDeleter.h" #include "ConfParser.h" #include "MP3FileBroadcaster.h" // must now inlcude this from the project level using the -i switch in the compiler #ifndef __MacOSX__ #include "../revision.h" #endif MP3Broadcaster* MP3Broadcaster::sBroadcaster = NULL; //#include "MyAssert.h" MP3Broadcaster::MP3Broadcaster(char* ipaddr, int port, char* config, char* playList, char* workingDir, Bool16 useICY) : mValid(true), mPort(8000), mBitRate(0), mFrequency(0), mUpcomingSongsListSize(7), mRecentSongsListSize(0), mLogging(0), mShowCurrent(true), mShowUpcoming(true), mNumErrors(0), mNumWarnings(0), mPreflight(false), mCleanupDone(false), mTempPicker(NULL), mSocket(NULL, 0), mLog(NULL), mUseICY(useICY) { sBroadcaster = this; strcpy(mIPAddr, "128.0.0.1"); strcpy(mPlayListPath, DEFAULTPATHS_ETC_DIR "mp3playlist.ply"); strcpy(mWorkingDirPath, DEFAULTPATHS_ETC_DIR); strcpy(mPlayMode, "sequential"); strcpy(mMountPoint, "/"); strcpy(mGenre, "Pop"); strcpy(mURL, ""); strcpy(mPIDFile, ""); //see if there is a defaults File. //if there is load it and over-ride the other defaults int len = ::strlen(config) + 10; char *defaultFileName = new char[len]; qtss_snprintf(defaultFileName, len, "%s%s", config, ".def"); (void) ::ParseConfigFile( false, defaultFileName, ConfigSetter, this ); //ignore if no defaults file delete [] defaultFileName; int err = ::ParseConfigFile( false, config, ConfigSetter, this ); if (err) { mValid = false; return; } if (ipaddr != NULL) { // override config file // size limit for any IP addr is 255 strncpy(mIPAddr, ipaddr, 255); } if (port != 0) { // override config file mPort = port; } if (playList) { // override config file // size limit for any playlist string is PATH_MAX - 1 strncpy(mPlayListPath, playList, PATH_MAX-1); } if (workingDir) { // override config file // size limit for any working path is PATH_MAX - extension - 1 strncpy(mWorkingDirPath, playList, PATH_MAX-12); } CreateWorkingFilePath(".current", mCurrentFile); CreateWorkingFilePath(".upcoming", mUpcomingFile); CreateWorkingFilePath(".replacelist", mReplaceFile); CreateWorkingFilePath(".stoplist", mStopFile); CreateWorkingFilePath(".insertlist", mInsertFile); CreateWorkingFilePath("mp3_broadcast.log", mLogFile); } Bool16 MP3Broadcaster::ConfigSetter( const char* paramName, const char* paramValue[], void* userData ) { // return true if set fails MP3Broadcaster* thisPtr = (MP3Broadcaster*)userData; if (!::strcmp( "destination_ip_address", paramName) ) { if (strlen(paramValue[0]) >= sizeof(thisPtr->mIPAddr)) return true; strcpy(thisPtr->mIPAddr, paramValue[0]); return false; } else if (!::strcmp( "destination_base_port", paramName) ) { thisPtr->mPort = atoi(paramValue[0]); return false; } else if (!::strcmp( "max_upcoming_list_size", paramName) ) { if ( ::atoi( paramValue[0] ) < 0 ) return true; thisPtr->mUpcomingSongsListSize = ::atoi( paramValue[0] ); return false; } else if (!::strcmp( "play_mode", paramName) ) { if (strlen(paramValue[0]) >= sizeof(thisPtr->mPlayMode)) return true; strcpy(thisPtr->mPlayMode, paramValue[0]); return false; } else if (!::strcmp( "recent_songs_list_size", paramName) ) { if ( ::atoi( paramValue[0] ) < 0 ) return true; thisPtr->mRecentSongsListSize = ::atoi( paramValue[0] ); return false; } else if (!::strcmp( "playlist_file", paramName) ) { if (strlen(paramValue[0]) >= sizeof(thisPtr->mPlayListPath)) return true; strcpy(thisPtr->mPlayListPath, paramValue[0]); return false; } else if (!::strcmp( "working_dir", paramName) ) { if (strlen(paramValue[0]) >= sizeof(thisPtr->mWorkingDirPath)) return true; strcpy(thisPtr->mWorkingDirPath, paramValue[0]); return false; } else if (!::strcmp( "logging", paramName) ) { return SetEnabled(paramValue[0], &thisPtr->mLogging); } else if (!::strcmp( "show_current", paramName) ) { return SetEnabled(paramValue[0], &thisPtr->mShowCurrent); } else if (!::strcmp( "show_upcoming", paramName) ) { return SetEnabled(paramValue[0], &thisPtr->mShowUpcoming); } else if (!::strcmp( "use_icy", paramName) ) { return SetEnabled(paramValue[0], &thisPtr->mUseICY); } else if (!::strcmp( "broadcast_name", paramName) ) { if (strlen(paramValue[0]) >= sizeof(thisPtr->mName)) return true; strcpy(thisPtr->mName, paramValue[0]); return false; } else if (!::strcmp( "broadcast_password", paramName) ) { if (strlen(paramValue[0]) >= sizeof(thisPtr->mPassword)) return true; strcpy(thisPtr->mPassword, paramValue[0]); return false; } else if (!::strcmp( "broadcast_genre", paramName) ) { if (strlen(paramValue[0]) >= sizeof(thisPtr->mGenre)) return true; strcpy(thisPtr->mGenre, paramValue[0]); return false; } else if (!::strcmp( "broadcast_url", paramName) ) { if (strlen(paramValue[0]) >= sizeof(thisPtr->mURL)) return true; strcpy(thisPtr->mURL, paramValue[0]); return false; } else if (!::strcmp( "broadcast_bitrate", paramName) ) { if ( ::atoi( paramValue[0] ) < 0 ) return true; thisPtr->mBitRate = ::atoi( paramValue[0] ); return false; } else if (!::strcmp( "broadcast_sample_rate", paramName) ) { if ( ::atoi( paramValue[0] ) < -1 ) return true; thisPtr->mFrequency = ::atoi( paramValue[0] ); return false; } else if (!::strcmp( "broadcast_mount_point", paramName) ) { if (strlen(paramValue[0]) >= sizeof(thisPtr->mMountPoint)) return true; // Make sure the mountpoint always begins with a '/' character. // If its missing prepend it to the mountpoint name for them. thisPtr->mMountPoint[0] = '\0'; if (*paramValue[0] != '/') strcpy(thisPtr->mMountPoint, "/"); strcat(thisPtr->mMountPoint, paramValue[0]); return false; } else if (!::strcmp( "pid_file", paramName) ) { if (strlen(paramValue[0]) >= sizeof(thisPtr->mPIDFile)) return true; strcpy(thisPtr->mPIDFile, paramValue[0]); return false; } else if (!::strcmp( "max_err_file_k_size", paramName) ) { if ( !paramValue[0] || !::strlen(paramValue[0]) ) return true; UInt32 setvalue = kSInt32_Max; int maxValue = ::atoi( paramValue[0] ); if (maxValue >= 0) setvalue = (UInt32) maxValue; qtss_setmaxprintfcharsinK( (UInt32) setvalue); return false; } return true; } Bool16 MP3Broadcaster::SetEnabled( const char* value, Bool16* field) { if ( ::strcmp( "enabled", value) && ::strcmp( "disabled", value) ) return true; *field = !strcmp( "enabled", value ); return false; } void MP3Broadcaster::CreateWorkingFilePath(char* extension, char* result) { if (strlen(mWorkingDirPath) + strlen(extension) >= PATH_MAX) result[0] = 0; else { strcpy(result, mWorkingDirPath); strcat(result, extension); } } void MP3Broadcaster::CreateCurrentAndUpcomingFiles() { if (mShowCurrent) { if(FileCreateAndCheckAccess(mCurrentFile)) { /* error */ mLog->LogInfo( "MP3Broadcaster Error: Failed to create current broadcast file" ); } } if (mShowUpcoming) { if(FileCreateAndCheckAccess(mUpcomingFile)) { /* error */ mLog->LogInfo( "MP3Broadcaster Error: Failed to create upcoming broadcast file" ); } } } void MP3Broadcaster::UpdatePlaylistFiles(PlaylistPicker *picker,PlaylistPicker *insertPicker) { if ( (NULL == picker) || (NULL == insertPicker) ) return; /* if .stoplist file exists - prepare to stop broadcast */ if(!access(mStopFile, R_OK)) { picker->CleanList(); PopulatePickerFromFile(picker, mStopFile, "", NULL); mTempPicker->CleanList(); remove(mStopFile); picker->mStopFlag = true; } /* if .replacelist file exists - replace current playlist */ if(!access(mReplaceFile, R_OK)) { picker->CleanList(); PopulatePickerFromFile(picker, mReplaceFile, "", NULL); mTempPicker->CleanList(); remove(mReplaceFile); picker->mStopFlag = false; } /* if .insertlist file exists - insert into current playlist */ if(!access(mInsertFile, R_OK)) { insertPicker->CleanList(); mTempPicker->CleanList(); PopulatePickerFromFile(insertPicker, mInsertFile, "", NULL); remove(mInsertFile); picker->mStopFlag = false; } // write upcoming playlist to .upcoming file if (mShowUpcoming) { FILE *upcomingFile = fopen(mUpcomingFile, "w"); if(upcomingFile) { mElementCount = 0; if (!::strcmp(mPlayMode, "weighted_random")) qtss_fprintf(upcomingFile,"#random play - upcoming list not supported\n"); else { qtss_fprintf(upcomingFile,"*PLAY-LIST*\n"); ShowPlaylistElements(insertPicker,upcomingFile); ShowPlaylistElements(picker,upcomingFile); if ( picker->GetNumMovies() == 0 && !picker->mStopFlag && 0 != ::strcmp(mPlayMode, "sequential") ) { picker->CleanList(); PopulatePickerFromFile(picker,mPlayListPath,"",NULL); ShowPlaylistElements(picker,upcomingFile); mTempPicker->CleanList(); PopulatePickerFromFile(mTempPicker,mPlayListPath,"",NULL); } if ( mElementCount <= mUpcomingSongsListSize && 0 == ::strcmp(mPlayMode, "sequential_looped") ) { if (mTempPicker->GetNumMovies() == 0) { mTempPicker->CleanList(); PopulatePickerFromFile(mTempPicker,mPlayListPath,"",NULL); } //sElementCount can be zero if the playlist contains no paths to valid files while ( (mElementCount != 0) && mElementCount <= mUpcomingSongsListSize ) ShowPlaylistElements(mTempPicker,upcomingFile); } } fclose(upcomingFile); } } else { if ( picker->GetNumMovies() == 0 && !picker->mStopFlag && ::strcmp(mPlayMode, "sequential") ) { picker->CleanList(); PopulatePickerFromFile(picker,mPlayListPath,"",NULL); } } } void MP3Broadcaster::UpdateCurrentFile(char *thePick) { if (NULL == thePick) return; // save currently playing song to .current file if (mShowCurrent) { FILE *currentFile = fopen(mCurrentFile, "w"); if(currentFile) { qtss_fprintf(currentFile,"u=%s\n",thePick); fclose(currentFile); } } } void MP3Broadcaster::PrintPlaylistElement(PLDoubleLinkedListNode *node,void *file) { sBroadcaster->mElementCount ++; if (sBroadcaster->mElementCount <= sBroadcaster->mUpcomingSongsListSize) { char* thePick = node->fElement->mElementName; qtss_fprintf((FILE*)file,"%s\n", thePick); } } void MP3Broadcaster::ShowPlaylistElements(PlaylistPicker *picker,FILE *file) { if (mElementCount > mUpcomingSongsListSize) return; UInt32 x; for (x= 0;xGetNumBuckets();x++) { picker->GetBucket(x)->ForEach(PrintPlaylistElement,file); } } void MP3Broadcaster::PreFlightOrBroadcast( bool preflight, bool daemonize, bool showMovieList, bool currentMovie, bool checkMP3s, const char* errorlog) { PlaylistPicker* picker = NULL; PlaylistPicker* insertPicker = NULL; MP3FileBroadcaster fileBroadcaster(&mSocket, mBitRate, mFrequency); MP3MetaInfoUpdater* metaInfoUpdater = NULL; SInt32 moviePlayCount; char* thePick = NULL; SInt32 numMovieErrors; bool didAtLeastOneMoviePlay = false; int err; mPreflight = preflight; if ( preflight ) ShowSetupParams(); if (preflight) picker = new PlaylistPicker(false); // make sequential picker, no looping else { picker = MakePickerFromConfig(); // make picker according to parms mTempPicker = new PlaylistPicker(false); insertPicker = new PlaylistPicker(false); insertPicker->mRemoveFlag = true; } // initial call uses empty string for path, NULL for loop detection list (void)PopulatePickerFromFile( picker, mPlayListPath, "", NULL ); if ( preflight ) { if ( picker->mNumToPickFrom == 1 ) qtss_printf( "\nThere is one movie in the Playlist.\n\n" ); else qtss_printf( "\nThere are (%li) movies in the Playlist.\n\n", (SInt32) picker->mNumToPickFrom ); } if ( picker->mNumToPickFrom == 0 ) { qtss_printf( "- MP3Broadcaster setup failed: There are no movies to play.\n" ); mNumErrors++; goto bail; } // check that we have enough movies to cover the recent movies list. if ( preflight ) { if ( !strcmp( mPlayMode, "weighted_random" ) ) // this implies "random" play { if ( mRecentSongsListSize >= picker->mNumToPickFrom ) { mRecentSongsListSize = picker->mNumToPickFrom - 1; qtss_printf("- PlaylistBroadcaster Warning:\n The recent_movies_list_size setting is greater than \n"); qtss_printf(" or equal to the number of movies in the playlist.\n" ); mNumWarnings++; } } } // create the log file mLog = new MP3BroadcasterLog( mWorkingDirPath, mLogFile, mLogging ); // if ( !PreflightTrackerFileAccess( R_OK | W_OK ) ) // goto bail; if (!preflight) { err = ConnectToServer(); if (err) { if (err == EACCES) qtss_printf("- MP3Broadcaster: Disconnected from Server. Bad password or mount point\n Exiting.\n" ); else qtss_printf("- MP3Broadcaster: Couldn't connect to server\n Exiting.\n" ); mNumErrors++; goto bail; } } //Unless the Debug command line option is set, daemonize the process at this point if (daemonize) { #ifndef __Win32__ qtss_printf("- MP3Broadcaster: Started in background.\n"); // keep the same working directory.. #ifdef __sgi__ if (::_daemonize(_DF_NOCHDIR, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO) != 0) #else if (::daemon( 1, 0 ) != 0) #endif { qtss_printf("- MP3Broadcaster: System error (%i).\n", errno); mNumErrors++; goto bail; } #endif } if (daemonize && (errorlog != NULL)) { freopen(errorlog, "a", stdout); ::setvbuf(stdout, (char *)NULL, _IONBF, 0); } if (!preflight) { metaInfoUpdater = new MP3MetaInfoUpdater(mPassword, mMountPoint, mSocket.GetRemoteAddr(), mPort); metaInfoUpdater->Start(); fileBroadcaster.SetInfoUpdater(metaInfoUpdater); } // ^ daemon must be called before we Open the log and tracker since we'll // get a new pid, our files close, ( does SIGTERM get sent? ) if (( mLog ) && ( mLogging )) mLog->EnableLog( false ); // don't append ".log" to name for PLB if ( mLogging && !mLog->IsLogEnabled() ) { if ( mLog->LogDirName() && *mLog->LogDirName() ) qtss_printf("- MP3Broadcaster: The log file failed to open.\n ( path: %s/%s )\n Exiting.\n", mLog->LogDirName(), mLog->LogFileName() ); else qtss_printf("- MP3Broadcaster: The log file failed to open.\n ( path: %s )\n Exiting.\n", mLog->LogFileName() ); mNumErrors++; goto bail; } if (mPIDFile[0] != 0) { if(!FileCreateAndCheckAccess(mPIDFile)) { FILE *pidFile = fopen(mPIDFile, "w"); if(pidFile) { qtss_fprintf(pidFile,"%d\n",getpid()); fclose(pidFile); } } } // AddOurPIDToTracker( bcastSetupFilePath ); // <-- exits on failure if ( !preflight ) mLog->LogInfo( "MP3Broadcaster started." ); else mLog->LogInfo( "MP3Broadcaster preflight started." ); if(!preflight) { CreateCurrentAndUpcomingFiles(); SendXAudioCastHeaders(); } if (!preflight) { // check the frequency of the first song fileBroadcaster.PlaySong( picker->GetFirstFile(), NULL, true, true ); } moviePlayCount = 0; numMovieErrors = 0; didAtLeastOneMoviePlay = false; while (true) { if (!showMovieList && !preflight) UpdatePlaylistFiles(picker, insertPicker); if (NULL != insertPicker) thePick = insertPicker->PickOne(); if (NULL == thePick && (NULL != picker)) thePick = picker->PickOne(); if ( (thePick != NULL) && (!preflight && showMovieList) ) { // display the picks in debug mode, but not preflight qtss_printf( "[%li] %s picked\n", moviePlayCount, thePick ); } if ( !showMovieList ) { int playError; if(!preflight) { UpdateCurrentFile(thePick); /* if playlist is about to run out repopulate it */ if ( !::strcmp(mPlayMode, "sequential_looped") ) { if(NULL == thePick &&!picker->mStopFlag) { if (picker->GetNumMovies() == 0) break; else continue; } } } if (thePick == NULL) break; ++moviePlayCount; if(!preflight) { SInt64 startTime = OS::Milliseconds(); SInt64 endTime = 0; playError = fileBroadcaster.PlaySong( thePick, mCurrentFile ); endTime = OS::Milliseconds(); //were we able to actually play the movie? didAtLeastOneMoviePlay = didAtLeastOneMoviePlay || (playError == 0); //ok, we've reached the end of the current playlist if (picker->GetNumMovies() == 0) { //If we determine that every one of the movies resulted in an error, then bail if (!didAtLeastOneMoviePlay) { qtss_printf("Quitting: Playlist contains no valid files.\n"); mLog->LogInfo( "Quitting: Playlist contains no valid files.\n" ); goto bail; } else { didAtLeastOneMoviePlay = false; } } mLog->LogMediaData(thePick, fileBroadcaster.GetTitle(), fileBroadcaster.GetArtist(), fileBroadcaster.GetAlbum(), (UInt32) ((endTime - startTime)/1000L), playError); } else { playError = fileBroadcaster.PlaySong( thePick, NULL, preflight, !checkMP3s ); } if (playError == MP3FileBroadcaster::kConnectionError) { // do something mNumErrors++; goto bail; } if ( !preflight && (playError != 0)) { qtss_printf("File %s : ", thePick); mLog->LogMediaError( thePick, MapErrorToString(playError), NULL ); } else if (playError != 0) { qtss_printf("File %s : ", thePick); MapErrorToString(playError); numMovieErrors++; mNumWarnings++; } } delete [] thePick; thePick = NULL; } //while (true) remove(mCurrentFile); remove(mUpcomingFile); if ( preflight ) { char str[256]; qtss_printf( " - " ); if (numMovieErrors == 1) strcpy(str, "MP3Broadcaster found one problem MP3 file."); else qtss_sprintf( str, "MP3Broadcaster found %li problem MP3 files." , numMovieErrors ); qtss_printf( "%s\n", str ); if (mLog) mLog->LogInfo( str ); if (numMovieErrors == moviePlayCount) { qtss_printf("There are no valid MP3s to play\n"); mNumErrors++; } } bail: delete picker; if (metaInfoUpdater) delete metaInfoUpdater; Cleanup(); #ifndef __Win32__ /* if ( sgTrackingSucceeded ) { // remove ourselves from the tracker // this is the "normal" remove, also signal handlers // may remove us. BCasterTracker tracker( sgTrackerFilePath ); tracker.RemoveByProcessID( getpid() ); tracker.Save(); } */ #endif } PlaylistPicker* MP3Broadcaster::MakePickerFromConfig() { // construct a PlaylistPicker object using options set PlaylistPicker *picker = NULL; if ( !::strcmp( mPlayMode, "weighted_random" ) ) { picker = new PlaylistPicker( 10, mRecentSongsListSize ); } else if ( !::strcmp( mPlayMode, "sequential_looped" ) ) { picker = new PlaylistPicker(true); picker->mRemoveFlag = true; } else if ( !::strcmp( mPlayMode, "sequential" ) ) { picker = new PlaylistPicker(false); picker->mRemoveFlag = true; } return picker; } bool MP3Broadcaster::FileCreateAndCheckAccess(char *theFileName) { FILE *theFile; #ifndef __Win32__ if(access(theFileName, F_OK)){ /* file does not exist - create and set rights */ theFile = ::fopen(theFileName, "w+"); if(theFile){ /* make sure everybody has r/w access to file */ (void)::chmod(theFileName, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); (void)::fclose(theFile); }else return 1; }else{ /* file exists - check rights */ if(access(theFileName, R_OK|W_OK))return 2; } #else theFile = ::fopen(theFileName,"a"); if (theFile) ::fclose(theFile); #endif return 0; } int MP3Broadcaster::ConnectToServer() { UInt32 addr; addr = SocketUtils::ConvertStringToAddr(mIPAddr); if (addr == INADDR_NONE) { struct hostent* theHostent = ::gethostbyname(mIPAddr); if (theHostent != NULL) addr = ntohl(*(UInt32*)(theHostent->h_addr_list[0])); else qtss_printf("Couldn't resolve address %s\n", mIPAddr); } OS_Error err = mSocket.Open(); err = mSocket.Connect(addr, mPort); if (err == 0) { char buffer1[512]; char buffer2[512]; UInt32 len; StringTranslator::EncodeURL(mMountPoint, strlen(mMountPoint) + 1, buffer1, sizeof(buffer1)); if (strlen(buffer1) + strlen(mPassword) + 12 <= 512) { if (mUseICY) { // in the ICY protocol there is no mountpoint // the reflector assumes a default mountpoint of "/" qtss_sprintf(buffer2, "%s\n", mPassword); } else { // if the mountpoint does not start with a "/" we prepend one for the // reflector before we send the broadcast request. if (buffer1[0] == '/') qtss_sprintf(buffer2, "SOURCE %s %s\n\n", mPassword, buffer1); else qtss_sprintf(buffer2, "SOURCE %s /%s\n\n", mPassword, buffer1); } mSocket.Send(buffer2, strlen(buffer2), &len); } else return -1; char buffer3[3]; len = 0; mSocket.Read(buffer3, 2, &len); buffer3[2] = '\0'; if (::strcmp(buffer3, "OK") != 0) err = EACCES; } return err; } int MP3Broadcaster::SendXAudioCastHeaders() { char buffer[1024]; char temp[256]; UInt32 len; if (mUseICY) { qtss_sprintf(buffer, "icy-name:%s\n", mName); qtss_sprintf(temp, "icy-genre:%s\n", mGenre); strcat(buffer, temp); qtss_sprintf(temp, "icy-pub:%s\n", "0"); strcat(buffer, temp); qtss_sprintf(temp, "icy-url:%s\n", mURL); strcat(buffer, temp); } else { qtss_sprintf(buffer, "x-audiocast-name:%s\n", mName); qtss_sprintf(temp, "x-audiocast-genre:%s\n", mGenre); strcat(buffer, temp); qtss_sprintf(temp, "x-audiocast-public:%s\n", "0"); strcat(buffer, temp); qtss_sprintf(temp, "x-audiocast-description:%s\n", ""); strcat(buffer, temp); } mSocket.Send(buffer, strlen(buffer), &len); return 0; } void MP3Broadcaster::ShowSetupParams() { qtss_printf( "\n" ); qtss_printf( "Configuration Settings\n" ); qtss_printf( "----------------------------\n" ); qtss_printf( "Destination address %s:%d\n", mIPAddr, mPort ); qtss_printf( "MP3 bitrate %d\n", mBitRate ); qtss_printf( "play_mode %s\n", mPlayMode ); qtss_printf( "recent_movies_list_size %d\n", mRecentSongsListSize ); qtss_printf( "playlist_file %s\n", mPlayListPath ); qtss_printf( "working_dir %s\n", mWorkingDirPath ); qtss_printf( "logging %d\n", mLogging ); qtss_printf( "log_file %s\n", mLogFile ); qtss_printf( "max_upcoming_list_size %d\n", mUpcomingSongsListSize ); qtss_printf( "show_current %d\n", mShowCurrent ); qtss_printf( "show_upcoming %d\n", mShowUpcoming ); qtss_printf( "use_icy %d\n", mUseICY ); qtss_printf( "broadcast_name \"%s\"\n", mName); qtss_printf( "broadcast_genre \"%s\"\n", mGenre); qtss_printf( "broadcast_mount_point \"%s\"\n", mMountPoint); qtss_printf( "broadcast_password \"XXXXX\"\n"); qtss_printf( "max_err_file_k_size %"_U32BITARG_"\n", qtss_getmaxprintfcharsinK()); qtss_printf( "\n" ); } void MP3Broadcaster::RemoveFiles() { if (mPIDFile[0] != 0) { remove(mPIDFile); } remove(mStopFile); remove(mReplaceFile); remove(mInsertFile); remove(mCurrentFile); remove(mUpcomingFile); } char* MP3Broadcaster::MapErrorToString(int error) { char* result = NULL; if (error == MP3FileBroadcaster::kBadFileFormat) result = "Bad file format."; else if (error == MP3FileBroadcaster::kWrongFrequency) result = "Encoded at wrong frequency."; else if (error == MP3FileBroadcaster::kWrongBitRate) result = "Doesn't use desired bit rate."; else if (error == MP3FileBroadcaster::kCouldntOpenFile) result = "Couldn't open file."; if (result != NULL) qtss_printf("%s\n", result); return result; } void MP3Broadcaster::Cleanup(bool signalHandler) { if (mCleanupDone) return; mCleanupDone = true; if (signalHandler) { mNumErrors++; qtss_printf("- MP3Broadcaster: Disconnected from Server while playing. Exiting.\n"); } if (mPreflight) { qtss_printf("Warnings: %"_S32BITARG_"\n", mNumWarnings); qtss_printf("Errors: %"_S32BITARG_"\n", mNumErrors); } else { qtss_printf("Broadcast Warnings: %"_S32BITARG_"\n", mNumWarnings); qtss_printf("Broadcast Errors: %"_S32BITARG_"\n", mNumErrors); } RemoveFiles(); if (mLog) { if ( mPreflight ) mLog->LogInfo( "MP3Broadcaster preflight finished." ); else mLog->LogInfo( "MP3Broadcaster finished." ); } // mLog = NULL; // protect the interrupt handler and just let it die don't delete because it is a task thread }