예제 #1
0
파일: Player.cpp 프로젝트: dkulp/fpp
long long Player::StartSequence(std::string seqName, int priority, int startSeconds,
	int startChannel, int blockSize, int autoRepeat)
{
	LogDebug(VB_PLAYER, "Player::StartSequence(%s, %d, %d, %d, %d, %d)\n",
		seqName.c_str(), priority, startSeconds, startChannel, blockSize,
		autoRepeat);
	pthread_mutex_lock(&m_sequenceLock);

	Sequence *newSequence = new Sequence(priority, startChannel, blockSize);

	if ((!m_nextSequenceID) ||
		(m_nextSequenceID < 0))
		m_nextSequenceID = 1;

	newSequence->SetSequenceID(m_nextSequenceID++);

	if (autoRepeat)
		newSequence->SetAutoRepeat();

	newSequence->OpenSequenceFile(seqName, startSeconds);

	int inserted = 0;
	if (m_sequence.size())
	{
		// Order the vector from lowest to highest priority.  In the event
		// of a tie, the newest item is put at the high end.
		for (int i = 0; i < m_sequence.size(); i++)
		{
			if (m_sequence[i]->GetPriority() > priority)
			{
				m_sequence.insert(m_sequence.begin() + i, newSequence);
				inserted = 1;
				break;
			}
		}
	}

	if (!inserted)
	{
		m_sequence.push_back(newSequence);
	    SetChannelOutputRefreshRate(newSequence->GetRefreshRate());
	}

	pthread_mutex_unlock(&m_sequenceLock);

	if (!m_outputThreadIsRunning)
	    StartChannelOutputThread();

	return newSequence->GetSequenceID();
}
예제 #2
0
void Bridge_Initialize(int &eSock, int &dSock)
{
	LogExcess(VB_E131BRIDGE, "Bridge_Initialize()\n");

    // prepare the msg receive buffers
    memset(msgs, 0, sizeof(msgs));
    for (int i = 0; i < MAX_MSG; i++) {
        iovecs[i].iov_base         = buffers[i];
        iovecs[i].iov_len          = BUFSIZE;
        msgs[i].msg_hdr.msg_iov    = &iovecs[i];
        msgs[i].msg_hdr.msg_iovlen = 1;
    }
    
	/* Initialize our Universe Index lookup cache */
	for (int i = 0; i < 65536; i++)
		UniverseCache[i] = BRIDGE_INVALID_UNIVERSE_INDEX;

	LoadInputUniversesFromFile();
	LogInfo(VB_E131BRIDGE, "Universe Count = %d\n",InputUniverseCount);
	InputUniversesPrint();
    
    
    // FIXME FIXME FIXME FIXME
    // This is a total hack to get a file descriptor greater than 2
    // because otherwise, the bind() call later will fail for some reason.
    // FIXME FIXME FIXME FIXME
    int i1 = socket(AF_INET, SOCK_DGRAM, 0);
    int i2 = socket(AF_INET, SOCK_DGRAM, 0);
    int i3 = socket(AF_INET, SOCK_DGRAM, 0);
    
    ddpSock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
    if (ddpSock < 0) {
        LogDebug(VB_E131BRIDGE, "e131bridge DDP socket failed: %s", strerror(errno));
        exit(1);
    }
    memset((char *)&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(DDP_PORT);
    addrlen = sizeof(addr);
    // Bind the socket to address/port
    if (bind(ddpSock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
        LogDebug(VB_E131BRIDGE, "e131bridge DDP bind failed: %s", strerror(errno));
        exit(1);
    }

	int            UniverseOctet[2];
	struct ip_mreq mreq;
	char           strMulticastGroup[16];

	/* set up socket */
	bridgeSock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
	if (bridgeSock < 0) {
		LogDebug(VB_E131BRIDGE, "e131bridge socket failed: %s", strerror(errno));
		exit(1);
	}

	// FIXME, move this to /etc/sysctl.conf or our startup script
	system("sudo sysctl net/ipv4/igmp_max_memberships=512");

	memset((char *)&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(E131_DEST_PORT);
	addrlen = sizeof(addr);
	// Bind the socket to address/port
	if (bind(bridgeSock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
		LogDebug(VB_E131BRIDGE, "e131bridge bind failed: %s", strerror(errno));
		exit(1);
	}
    
    //get all the addresses
    struct ifaddrs *interfaces,*tmp;
    getifaddrs(&interfaces);
    
	char address[16];
    address[0] = 0;
	// Join the multicast groups
	for(int i = 0; i < InputUniverseCount; i++)  {
		if (InputUniverses[i].type == E131_TYPE_MULTICAST) {
			UniverseOctet[0] = InputUniverses[i].universe/256;
			UniverseOctet[1] = InputUniverses[i].universe%256;
			sprintf(strMulticastGroup, "239.255.%d.%d", UniverseOctet[0],UniverseOctet[1]);
			mreq.imr_multiaddr.s_addr = inet_addr(strMulticastGroup);

			LogInfo(VB_E131BRIDGE, "Adding group %s\n", strMulticastGroup);

            // add group to groups to listen for on eth0 and wlan0 if it exists
            int multicastJoined = 0;
            tmp = interfaces;
            //loop through all the interfaces and subscribe to the group
            while (tmp) {
                //struct sockaddr_in *sin = (struct sockaddr_in *)tmp->ifa_addr;
                //strcpy(address, inet_ntoa(sin->sin_addr));
                if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET) {
                    GetInterfaceAddress(tmp->ifa_name, address, NULL, NULL);
                    if (strcmp(address, "127.0.0.1")) {
                        LogDebug(VB_E131BRIDGE, "   Adding interface %s - %s\n", tmp->ifa_name, address);
                        mreq.imr_interface.s_addr = inet_addr(address);
                        if (setsockopt(bridgeSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
                            LogWarn(VB_E131BRIDGE, "   Could not setup Multicast Group for interface %s\n", tmp->ifa_name);
                        }
                        multicastJoined = 1;
                    }
                } else if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET6) {
                    //FIXME for ipv6 multicast
                    //LogDebug(VB_E131BRIDGE, "   Inet6 interface %s\n", tmp->ifa_name);
                }
                tmp = tmp->ifa_next;
            }

			if (!multicastJoined) {
				LogDebug(VB_E131BRIDGE, "  Binding to default interface\n");
				mreq.imr_interface.s_addr = htonl(INADDR_ANY);
				if (setsockopt(bridgeSock, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq)) < 0) {
                    LogWarn(VB_E131BRIDGE, "   Could not setup Multicast Group\n");
				}
			}
		}
	}
    freeifaddrs(interfaces);

	StartChannelOutputThread();
    
    if (i1 >= 0) close(i1);
    if (i2 >= 0) close(i2);
    if (i3 >= 0) close(i3);

    eSock = bridgeSock;
    dSock = ddpSock;
}
예제 #3
0
파일: fppd.c 프로젝트: bagumondigi/fpp
void MainLoop(void)
{
	int            commandSock = 0;
	int            controlSock = 0;
	int            bridgeSock = 0;
	int            prevFPPstatus = FPPstatus;
	int            sleepms = 50000;
	fd_set         active_fd_set;
	fd_set         read_fd_set;
	struct timeval timeout;
	int            selectResult;

	LogDebug(VB_GENERAL, "MainLoop()\n");

	FD_ZERO (&active_fd_set);

	CheckExistanceOfDirectoriesAndFiles();

	piFaceSetup(200); // PiFace inputs 1-8 == wiringPi 200-207

	if (getFPPmode() == BRIDGE_MODE)
	{
		bridgeSock = Bridge_Initialize();
		if (bridgeSock)
			FD_SET (bridgeSock, &active_fd_set);
	}
	else
	{
		InitMediaOutput();
	}

	pluginCallbackManager.init();

	InitializeChannelOutputs();
	sequence->SendBlankingData();

	InitEffects();
	InitializeChannelDataMemoryMap();

	commandSock = Command_Initialize();
	if (commandSock)
		FD_SET (commandSock, &active_fd_set);

#ifdef USEHTTPAPI
	APIServer apiServer;
	apiServer.Init();
#endif

	controlSock = InitControlSocket();
	FD_SET (controlSock, &active_fd_set);

	SetupGPIOInput();

	if (getFPPmode() & PLAYER_MODE)
	{
		if (getFPPmode() == MASTER_MODE)
			InitSyncMaster();

		scheduler->CheckIfShouldBePlayingNow();

		if (getAlwaysTransmit())
			StartChannelOutputThread();
	}

	LogInfo(VB_GENERAL, "Starting main processing loop\n");

	while (runMainFPPDLoop)
	{
		timeout.tv_sec  = 0;
		timeout.tv_usec = sleepms;

		read_fd_set = active_fd_set;


		selectResult = select(FD_SETSIZE, &read_fd_set, NULL, NULL, &timeout);
		if (selectResult < 0)
		{
			if (errno == EINTR)
			{
				// We get interrupted when media players finish
				continue;
			}
			else
			{
				LogErr(VB_GENERAL, "Main select() failed: %s\n",
					strerror(errno));
				runMainFPPDLoop = 0;
				continue;
			}
		}

		if (commandSock && FD_ISSET(commandSock, &read_fd_set))
			CommandProc();

		if (bridgeSock && FD_ISSET(bridgeSock, &read_fd_set))
			Bridge_ReceiveData();

		if (controlSock && FD_ISSET(controlSock, &read_fd_set))
			ProcessControlPacket();

		// Check to see if we need to start up the output thread.
		// FIXME, possibly trigger this via a fpp command to fppd
		if ((!ChannelOutputThreadIsRunning()) &&
			(getFPPmode() != BRIDGE_MODE) &&
			((UsingMemoryMapInput()) ||
			 (channelTester->Testing()) ||
			 (getAlwaysTransmit()))) {
			int E131BridgingInterval = getSettingInt("E131BridgingInterval");
			if (!E131BridgingInterval)
				E131BridgingInterval = 50;
			SetChannelOutputRefreshRate(1000 / E131BridgingInterval);
			StartChannelOutputThread();
		}

		if (getFPPmode() & PLAYER_MODE)
		{
			if ((FPPstatus == FPP_STATUS_PLAYLIST_PLAYING) ||
				(FPPstatus == FPP_STATUS_STOPPING_GRACEFULLY))
			{
				if (prevFPPstatus == FPP_STATUS_IDLE)
				{
					playlist->PlayListPlayingInit();
					sleepms = 10000;
				}

				// Check again here in case PlayListPlayingInit
				// didn't find anything and put us back to IDLE
				if ((FPPstatus == FPP_STATUS_PLAYLIST_PLAYING) ||
					(FPPstatus == FPP_STATUS_STOPPING_GRACEFULLY))
				{
					playlist->PlayListPlayingProcess();
				}
			}

			int reactivated = 0;
			if (FPPstatus == FPP_STATUS_IDLE)
			{
				if ((prevFPPstatus == FPP_STATUS_PLAYLIST_PLAYING) ||
					(prevFPPstatus == FPP_STATUS_STOPPING_GRACEFULLY))
				{
					playlist->PlayListPlayingCleanup();

					if (FPPstatus != FPP_STATUS_IDLE)
						reactivated = 1;
					else
						sleepms = 50000;
				}
			}

			if (reactivated)
				prevFPPstatus = FPP_STATUS_IDLE;
			else
				prevFPPstatus = FPPstatus;

			scheduler->ScheduleProc();
		}
		else if (getFPPmode() == REMOTE_MODE)
		{
			if(mediaOutputStatus.status == MEDIAOUTPUTSTATUS_PLAYING)
			{
				playlist->PlaylistProcessMediaData();
			}
		}

		CheckGPIOInputs();
	}

	StopChannelOutputThread();
	ShutdownControlSocket();

	if (getFPPmode() == BRIDGE_MODE)
		Bridge_Shutdown();

	LogInfo(VB_GENERAL, "Main Loop complete, shutting down.\n");
}
예제 #4
0
파일: Sequence.cpp 프로젝트: jcochran/fpp
int Sequence::OpenSequenceFile(const char *filename, int startSeconds) {
	LogDebug(VB_SEQUENCE, "OpenSequenceFile(%s, %d)\n", filename, startSeconds);

	if (!filename || !filename[0])
	{
		LogErr(VB_SEQUENCE, "Empty Sequence Filename!\n", filename);
		return 0;
	}

	size_t bytesRead = 0;

	m_seqFileSize = 0;

	if (IsSequenceRunning())
		CloseSequenceFile();

	m_seqStarting = 1;
	m_seqPaused   = 0;
	m_seqDuration = 0;
	m_seqSecondsElapsed = 0;
	m_seqSecondsRemaining = 0;

	strcpy(m_seqFilename, filename);

	char tmpFilename[2048];
	unsigned char tmpData[2048];
	strcpy(tmpFilename,(const char *)getSequenceDirectory());
	strcat(tmpFilename,"/");
	strcat(tmpFilename, filename);

	if (getFPPmode() == REMOTE_MODE)
		CheckForHostSpecificFile(getSetting("HostName"), tmpFilename);

	if (!FileExists(tmpFilename))
	{
		LogErr(VB_SEQUENCE, "Sequence file %s does not exist\n", tmpFilename);
		m_seqStarting = 0;
		return 0;
	}

	m_seqFile = fopen((const char *)tmpFilename, "r");
	if (m_seqFile == NULL) 
	{
		LogErr(VB_SEQUENCE, "Error opening sequence file: %s. fopen returned NULL\n",
			tmpFilename);
		m_seqStarting = 0;
		return 0;
	}

	if (getFPPmode() == MASTER_MODE)
	{
		SendSeqSyncStartPacket(filename);

		// Give the remotes a head start spining up so they are ready
		usleep(100000);
	}

	///////////////////////////////////////////////////////////////////////
	// Check 4-byte File format identifier
	char seqFormatID[5];
	strcpy(seqFormatID, "    ");
	bytesRead = fread(seqFormatID, 1, 4, m_seqFile);
	seqFormatID[4] = 0;
	if ((bytesRead != 4) || (strcmp(seqFormatID, "PSEQ") && strcmp(seqFormatID, "FSEQ")))
	{
		LogErr(VB_SEQUENCE, "Error opening sequence file: %s. Incorrect File Format header: '%s', bytesRead: %d\n",
			filename, seqFormatID, bytesRead);

		fseek(m_seqFile, 0L, SEEK_SET);
		bytesRead = fread(tmpData, 1, DATA_DUMP_SIZE, m_seqFile);
		HexDump("Sequence File head:", tmpData, bytesRead);

		fclose(m_seqFile);
		m_seqFile = NULL;
		m_seqStarting = 0;
		return 0;
	}

	///////////////////////////////////////////////////////////////////////
	// Get Channel Data Offset
	bytesRead = fread(tmpData, 1, 2, m_seqFile);
	if (bytesRead != 2)
	{
		LogErr(VB_SEQUENCE, "Sequence file %s too short, unable to read channel data offset value\n", filename);

		fseek(m_seqFile, 0L, SEEK_SET);
		bytesRead = fread(tmpData, 1, DATA_DUMP_SIZE, m_seqFile);
		HexDump("Sequence File head:", tmpData, bytesRead);

		fclose(m_seqFile);
		m_seqFile = NULL;
		m_seqStarting = 0;
		return 0;
	}
	m_seqChanDataOffset = tmpData[0] + (tmpData[1] << 8);

	///////////////////////////////////////////////////////////////////////
	// Now that we know the header size, read the whole header in one shot
	fseek(m_seqFile, 0L, SEEK_SET);
	bytesRead = fread(tmpData, 1, m_seqChanDataOffset, m_seqFile);
	if (bytesRead != m_seqChanDataOffset)
	{
		LogErr(VB_SEQUENCE, "Sequence file %s too short, unable to read fixed header size value\n", filename);

		fseek(m_seqFile, 0L, SEEK_SET);
		bytesRead = fread(tmpData, 1, DATA_DUMP_SIZE, m_seqFile);
		HexDump("Sequence File head:", tmpData, bytesRead);

		fclose(m_seqFile);
		m_seqFile = NULL;
		m_seqStarting = 0;
		return 0;
	}

	m_seqVersionMinor = tmpData[6];
	m_seqVersionMajor = tmpData[7];
	m_seqVersion      = (m_seqVersionMajor * 256) + m_seqVersionMinor;

	m_seqFixedHeaderSize =
		(tmpData[8])        + (tmpData[9] << 8);

	m_seqStepSize =
		(tmpData[10])       + (tmpData[11] << 8) +
		(tmpData[12] << 16) + (tmpData[13] << 24);

	m_seqNumPeriods =
		(tmpData[14])       + (tmpData[15] << 8) +
		(tmpData[16] << 16) + (tmpData[17] << 24);

	m_seqStepTime =
		(tmpData[18])       + (tmpData[19] << 8);

	m_seqNumUniverses = 
		(tmpData[20])       + (tmpData[21] << 8);

	m_seqUniverseSize = 
		(tmpData[22])       + (tmpData[23] << 8);

	m_seqGamma         = tmpData[24];
	m_seqColorEncoding = tmpData[25];

	// End of v1.0 fields
	if (m_seqVersion > 0x0100)
	{
	}

	m_seqRefreshRate = 1000 / m_seqStepTime;

	fseek(m_seqFile, 0L, SEEK_END);
	m_seqFileSize = ftell(m_seqFile);
	m_seqDuration = (int)((float)(m_seqFileSize - m_seqChanDataOffset)
		/ ((float)m_seqStepSize * (float)m_seqRefreshRate));
	m_seqSecondsRemaining = m_seqDuration;
	fseek(m_seqFile, m_seqChanDataOffset, SEEK_SET);
	m_seqFilePosition = m_seqChanDataOffset;

	LogDebug(VB_SEQUENCE, "Sequence File Information\n");
	LogDebug(VB_SEQUENCE, "seqFilename           : %s\n", m_seqFilename);
	LogDebug(VB_SEQUENCE, "seqVersion            : %d.%d\n",
		m_seqVersionMajor, m_seqVersionMinor);
	LogDebug(VB_SEQUENCE, "seqFormatID           : %s\n", seqFormatID);
	LogDebug(VB_SEQUENCE, "seqChanDataOffset     : %d\n", m_seqChanDataOffset);
	LogDebug(VB_SEQUENCE, "seqFixedHeaderSize    : %d\n", m_seqFixedHeaderSize);
	LogDebug(VB_SEQUENCE, "seqStepSize           : %d\n", m_seqStepSize);
	LogDebug(VB_SEQUENCE, "seqNumPeriods         : %d\n", m_seqNumPeriods);
	LogDebug(VB_SEQUENCE, "seqStepTime           : %dms\n", m_seqStepTime);
	LogDebug(VB_SEQUENCE, "seqNumUniverses       : %d *\n", m_seqNumUniverses);
	LogDebug(VB_SEQUENCE, "seqUniverseSize       : %d *\n", m_seqUniverseSize);
	LogDebug(VB_SEQUENCE, "seqGamma              : %d *\n", m_seqGamma);
	LogDebug(VB_SEQUENCE, "seqColorEncoding      : %d *\n", m_seqColorEncoding);
	LogDebug(VB_SEQUENCE, "seqRefreshRate        : %d\n", m_seqRefreshRate);
	LogDebug(VB_SEQUENCE, "seqFileSize           : %lu\n", m_seqFileSize);
	LogDebug(VB_SEQUENCE, "seqDuration           : %d\n", m_seqDuration);
	LogDebug(VB_SEQUENCE, "'*' denotes field is currently ignored by FPP\n");

	m_seqPaused = 0;
	m_seqSingleStep = 0;
	m_seqSingleStepBack = 0;

	int frameNumber = 0;

	if (startSeconds)
	{
		int frameNumber = startSeconds * m_seqRefreshRate;
		int newPos = m_seqChanDataOffset + (frameNumber * m_seqStepSize);
		LogDebug(VB_SEQUENCE, "Seeking to byte %d in %s\n", newPos, m_seqFilename);

		fseek(m_seqFile, newPos, SEEK_SET);
	}

	ReadSequenceData();

	SetChannelOutputFrameNumber(frameNumber);

	SetChannelOutputRefreshRate(m_seqRefreshRate);
	StartChannelOutputThread();

	m_seqStarting = 0;

	return m_seqFileSize;
}
예제 #5
0
파일: effects.cpp 프로젝트: ihbar/fpp
/*
 * Start a new effect offset at the specified channel number
 */
int StartEffect(const std::string &effectName, int startChannel, int loop)
{
	int   effectID = -1;
    int   frameTime = 50;
	LogInfo(VB_EFFECT, "Starting effect %s at channel %d\n", effectName.c_str(), startChannel);

    std::unique_lock<std::mutex> lock(effectsLock);
	if (effectCount >= MAX_EFFECTS) {
		LogErr(VB_EFFECT, "Unable to start effect %s, maximum number of effects already running\n", effectName.c_str());
		return effectID;
	}

    std::string filename = getEffectDirectory();
    filename += "/";
    filename += effectName;
    filename += ".eseq";

    FSEQFile *fseq = FSEQFile::openFSEQFile(filename);
	if (!fseq) {
		LogErr(VB_EFFECT, "Unable to open effect: %s\n", filename.c_str());
		return effectID;
	}
    V2FSEQFile *v2fseq = dynamic_cast<V2FSEQFile*>(fseq);
    if (!v2fseq) {
        delete fseq;
        LogErr(VB_EFFECT, "Effect file not a correct eseq file: %s\n", filename.c_str());
        return effectID;
    }

	if (v2fseq->m_sparseRanges.size() == 0){
		LogErr(VB_EFFECT, "eseq file must have at least one model range.");
        delete fseq;
		return effectID;
	}

	if (startChannel != 0) {
		// This will need to change if/when we support multiple models per file
        v2fseq->m_sparseRanges[0].first = startChannel - 1;
	}
    frameTime = v2fseq->getStepTime();
	effectID = GetNextEffectID();

	if (effectID < 0) {
		LogErr(VB_EFFECT, "Unable to start effect %s, unable to determine next effect ID\n", effectName.c_str());
        delete fseq;
		return effectID;
	}

	effects[effectID] = new FPPeffect;
	effects[effectID]->name = effectName;
	effects[effectID]->fp = v2fseq;
	effects[effectID]->loop = loop;
	effects[effectID]->background = 0;

	if (effectName == "background") {
		effects[effectID]->background = 1;
	} else if ((getFPPmode() == REMOTE_MODE) &&
			 (effectName.find("background_") == 0)) {
        std::string localFilename = "background_";
		localFilename += getSetting("HostName");

        if (localFilename == effectName) {
			effects[effectID]->background = 1;
        }
	}
	effectCount++;
    int tmpec = effectCount;
    lock.unlock();

	StartChannelOutputThread();
    
    if (!sequence->IsSequenceRunning()
        && tmpec == 1) {
        //first effect running, no sequence running, set the refresh rate
        //to the rate of the effect
        SetChannelOutputRefreshRate(1000 / frameTime);
    }


	return effectID;
}
예제 #6
0
/*
 * Create and initialize the channel data and control memory map files
 */
int InitializeChannelDataMemoryMap(void) {
	int   i  = 0;
	char  ch = '\0';
	long long pixelLocation = 0;
	char  tmpData[FPPD_MAX_CHANNELS];
	char  tmpCtrlData[FPPCHANNELMEMORYMAPSIZE];
	long long tmpPixelMapData[FPPD_MAX_CHANNELS];

	LogDebug(VB_CHANNELOUT, "InitializeChannelDataMemoryMap()\n");

	bzero((void *)tmpData, sizeof(tmpData));
	bzero((void *)tmpCtrlData, sizeof(tmpCtrlData));
	bzero((void *)tmpPixelMapData, sizeof(tmpPixelMapData));

	// Block of of raw channel data used to overlay data
	chanDataMapFD =
		open(FPPCHANNELMEMORYMAPDATAFILE, O_CREAT | O_TRUNC | O_RDWR, 0666);

	if (chanDataMapFD < 0) {
		LogErr(VB_CHANNELOUT, "Error opening %s memory map file: %s\n",
			FPPCHANNELMEMORYMAPDATAFILE, strerror(errno));
		return -1;
	}

	chmod(FPPCHANNELMEMORYMAPDATAFILE, 0666);

	if (write(chanDataMapFD, (void *)tmpData, sizeof(tmpData)) != sizeof(tmpData)) {
		LogErr(VB_CHANNELOUT, "Error populating %s memory map file: %s\n",
			FPPCHANNELMEMORYMAPDATAFILE, strerror(errno));
		CloseChannelDataMemoryMap();
		return -1;
	}

	chanDataMap = (char *)mmap(0, FPPD_MAX_CHANNELS, PROT_READ, MAP_SHARED, chanDataMapFD, 0);

	if (!chanDataMap) {
		LogErr(VB_CHANNELOUT, "Error mapping %s memory map file: %s\n",
			FPPCHANNELMEMORYMAPDATAFILE, strerror(errno));
		CloseChannelDataMemoryMap();
		return -1;
	}

	// Control file to turn on/off blocks and get block info
	ctrlFD = open(FPPCHANNELMEMORYMAPCTRLFILE, O_CREAT | O_TRUNC | O_RDWR, 0666);

	if (ctrlFD < 0) {
		LogErr(VB_CHANNELOUT, "Error opening %s memory map file: %s\n",
			FPPCHANNELMEMORYMAPCTRLFILE, strerror(errno));
		CloseChannelDataMemoryMap();
		return -1;
	}

	chmod(FPPCHANNELMEMORYMAPCTRLFILE, 0666);

	if (write(ctrlFD, (void *)tmpCtrlData, sizeof(tmpCtrlData)) != sizeof(tmpCtrlData)) {
		LogErr(VB_CHANNELOUT, "Error populating %s memory map file: %s\n",
			FPPCHANNELMEMORYMAPCTRLFILE, strerror(errno));
		CloseChannelDataMemoryMap();
		return -1;
	}

	ctrlMap = (char *)mmap(0, FPPCHANNELMEMORYMAPSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, ctrlFD, 0);

	if (!ctrlMap) {
		LogErr(VB_CHANNELOUT, "Error mapping %s memory map file: %s\n",
			FPPCHANNELMEMORYMAPCTRLFILE, strerror(errno));
		CloseChannelDataMemoryMap();
		return -1;
	}

	ctrlHeader = (FPPChannelMemoryMapControlHeader *)ctrlMap;

	// Pixel Map to map channels to matrix positions
	pixelFD =
		open(FPPCHANNELMEMORYMAPPIXELFILE, O_CREAT | O_TRUNC | O_RDWR, 0666);

	if (pixelFD < 0) {
		LogErr(VB_CHANNELOUT, "Error opening %s memory map file: %s\n",
			FPPCHANNELMEMORYMAPPIXELFILE, strerror(errno));
		CloseChannelDataMemoryMap();
		return -1;
	}

	chmod(FPPCHANNELMEMORYMAPPIXELFILE, 0666);

	if (write(pixelFD, (void *)tmpPixelMapData, sizeof(tmpPixelMapData)) != sizeof(tmpPixelMapData)) {
		LogErr(VB_CHANNELOUT, "Error populating %s memory map file: %s\n",
			FPPCHANNELMEMORYMAPPIXELFILE, strerror(errno));
		CloseChannelDataMemoryMap();
		return -1;
	}

	pixelMap = (long long *)mmap(0, FPPD_MAX_CHANNELS * sizeof(long long), PROT_READ|PROT_WRITE, MAP_SHARED, pixelFD, 0);

	if (!pixelMap) {
		LogErr(VB_CHANNELOUT, "Error mapping %s memory map file: %s\n",
			FPPCHANNELMEMORYMAPPIXELFILE, strerror(errno));
		CloseChannelDataMemoryMap();
		return -1;
	}

	for (i = 0; i < FPPD_MAX_CHANNELS; i++) {
		pixelMap[i] = i;
	}

	// Load the config
	LoadChannelMemoryMapData();

	if (ctrlHeader->totalBlocks)
		StartChannelOutputThread();

	return 1;
}