BlinkenArea - GitList
Repositories
Blog
Wiki
flaneth
Code
Commits
Branches
Tags
Search
Tree:
63afb08
Branches
Tags
master
flaneth
firmware
app_bbm.c
do not try to catch up with time wehen playing BBMs, it could harm synchronous playback
Stefan Schuermans
commited
63afb08
at 2012-05-06 18:10:51
app_bbm.c
Blame
History
Raw
/* flaneth - flash and ethernet Copyright (C) 2007-2012 Stefan Schuermans <stefan@schuermans.info> Copyleft: GNU public license V2 - http://www.gnu.org/copyleft/gpl.html a BlinkenArea project - http://www.blinkenarea.org/ */ #include <stdio.h> #include <string.h> #include "apps_tools.h" #include "app_bbm.h" #include "debug.h" #include "dosfs.h" #include "nethelp.h" #include "udp.h" #include "timing.h" ///< BBM file header typedef struct app_bbm_header { uint32_t magic; uint16_t height; uint16_t width; uint16_t channels; uint16_t maxval; uint32_t framecnt; uint32_t duration; uint32_t frameptr; } AppBbmHeader; /** * @brief get name of next file from playlist directory * @param[in,out] state internal state of application * @param[out] filename name of file * @param[in] filename_sz length of filename buffer * @return 0 if success ful, -1 if not */ static char AppBbmNextName(AppBbmState *state, char *filename, unsigned char filename_sz) { // assemble filename of playlist directory char dirname[10]; sprintf(dirname, "bbm%hhu/pl", state->no); // open playlist directory DIRINFO di; di.scratch = *state->sectorBuf; if (DFS_OpenDir(state->vi, (uint8_t *)dirname, &di) != DFS_OK) { // no playlist dir -> deactivate application state->isInit = 0; debug_app_bbm_printf("%hhu: cannot open directory %s", state->no, dirname); return -1; } // get file with index idxFile DIRENT de; unsigned short idx; char firstName[11], haveFirst; haveFirst = 0; for (idx = 0; idx <= /* file 0 -> loop once */ state->idxFile; ++idx) { // get next file do { if (DFS_GetNext(state->vi, &di, &de) != 0) { // no files at all -> deactivate application if (!haveFirst) { state->isInit = 0; debug_app_bbm_printf("%hhu: no files in playlist directory", state->no); return -1; } // end of (non-empty) playlist reached -> restart playlist // -> return first file name, next file index 1 snprintf(filename, filename_sz, "bbm%hhu/pl/%-11.11s", state->no, firstName); filename[filename_sz - 1] = 0; state->idxFile = 1; return 0; } } while (de.name[0] == 0 || // ignore deleted de.attr & (ATTR_VOLUME_ID | ATTR_DIRECTORY)); // ignore volumeIDs, // dirs, long names // first file found -> save name (for restarting playlist when end reached) if (!haveFirst) { memcpy(firstName, de.name, sizeof(firstName)); haveFirst = 1; } } // for idx // found name of file with index idxFile // -> return file name, increment file index for next file snprintf(filename, filename_sz, "bbm%hhu/pl/%-11.11s", state->no, de.name); filename[filename_sz - 1] = 0; state->idxFile++; return 0; } /** * @brief parse header of BBM file * @param[in,out] state internal state of application */ static void AppBbmParseHeader(AppBbmState *state) { // read BBM header AppBbmHeader hdr; uint32_t len; if (DFS_ReadFile(&state->fi, *state->sectorBuf, (uint8_t *)&hdr, &len, sizeof(hdr)) != DFS_OK) len = 0; if (len < sizeof(hdr)) { debug_app_bbm_printf("%hhu: truncated BBM file", state->no); return; // retry with next file in next iteration } // check header if (ntohl(hdr.magic) != 0x23542666) { debug_app_bbm_printf("%hhu: invalid magic %lX in BBM file", state->no, hdr.magic); return; // retry with next file in next iteration } state->height = ntohs(hdr.height); state->width = ntohs(hdr.width); state->channels = ntohs(hdr.channels); state->maxval = ntohs(hdr.maxval); if (state->height < 1 || state->height > 256 || state->width < 1 || state->width > 256 || state->channels < 1 || state->channels > 3 || state->maxval < 1 || state->maxval > 255) { debug_app_bbm_printf("%hhu: invalid dimensions in BBM file", state->no); return; // retry with next file in next iteration } state->dataSz = state->height * state->width * state->channels; if (state->dataSz > 768) { debug_app_bbm_printf("%hhu: BBM frame size too large", state->no); return; // retry with next file in next iteration } uint32_t frameptr = ntohl(hdr.frameptr); if (frameptr < sizeof(hdr)) { debug_app_bbm_printf("%hhu: invalid framepointer in BBM", state->no); return; // retry with next file in next iteration } // seek to start of first frame DFS_Seek(&state->fi, frameptr, *state->sectorBuf); if (state->fi.pointer != frameptr) { debug_app_bbm_printf("%hhu: seek to first frame failed", state->no); return; // retry with next file in next iteration } // read frame start mrker uint8_t framestart[4]; if (DFS_ReadFile(&state->fi, *state->sectorBuf, framestart, &len, sizeof(framestart)) != DFS_OK) len = 0; if (len < sizeof(framestart)) { debug_app_bbm_printf("%hhu: truncated BBM file", state->no); return; // retry with next file in next iteration } if (memcmp(framestart, "frms", 4) != 0) { debug_app_bbm_printf("%hhu: invalid frame start marker in BBM file", state->no); return; // retry with next file in next iteration } // a file has been found and opened, header is parsed state->haveFile = 1; debug_app_bbm_printf("%hhu: header parsed: %hux%hu-%hu/%hu", state->no, state->width, state->height, state->channels, state->maxval + 1); } /** * @brief open next file from playlist directory * @param[in,out] state internal state of application */ static void AppBbmNextFile(AppBbmState *state) { // get name of next file to open char filename[22]; if (AppBbmNextName(state, filename, sizeof(filename)) != 0) return; // open file if (DFS_OpenFile(state->vi, (uint8_t *)filename, DFS_READ, *state->sectorBuf, &state->fi) != 0) { debug_app_bbm_printf("%hhu: cannot open file %s", state->no, filename); return; // retry with next file in next iteration } debug_app_bbm_printf("%hhu: file %s", state->no, filename); // parse header AppBbmParseHeader(state); } /** * @brief process next frame * @param[in,out] state internal state of application */ static void AppBbmProcFrame(AppBbmState *state) { struct packet { struct UdpPacket udp; uint8_t mcuf[12 + state->dataSz]; } pack; // read next frame from file // - read to mcuf pos 10, so data is placed at pos 12 for MCUF frame uint32_t len; if (DFS_ReadFile(&state->fi, *state->sectorBuf, pack.mcuf + 10, &len, state->dataSz + 2) != DFS_OK) len = 0; if (len < state->dataSz + 2) { // end of file reached -> use next file in next iteration state->haveFile = 0; debug_app_bbm_printf("%hhu: end of file", state->no); return; } // get frame duration uint16_t duration = ntohs(*(uint16_t *)(pack.mcuf + 10)); debug_app_bbm_printf("%hhu: frame duration %u", state->no, duration); // fill in MCUF header *(uint32_t *)(pack.mcuf + 0) = htonl(0x23542666); *(uint16_t *)(pack.mcuf + 4) = htons(state->height); *(uint16_t *)(pack.mcuf + 6) = htons(state->width); *(uint16_t *)(pack.mcuf + 8) = htons(state->channels); *(uint16_t *)(pack.mcuf + 10) = htons(state->maxval); // send MCUF frame if (state->haveAddr) { memcpy(pack.udp.IpHdr.Dest, state->addr, sizeof(pack.udp.IpHdr.Dest)); pack.udp.UdpHdr.SrcPort = htons(2323); pack.udp.UdpHdr.DestPort = htons(state->port); UdpSend((unsigned char *)&pack, sizeof(pack)); } // wait for frame duration state->nextActMs += duration; } /** * @brief initialize BBM play application * @param[in,out] state internal state of application * * state.sectorBuf, state.vi, state.no have to be initialized before calling */ void AppBbmInit(AppBbmState *state) // (extern) { // default: not initialized state->isInit = 0; // get destination address for MCUF frames char filename[12]; sprintf(filename, "bbm%hhu/addr", state->no); if (AppsToolsReadIp(*state->sectorBuf, state->vi, filename, state->addr) == 0) { state->haveAddr = 1; debug_app_bbm_printf("%hhu: address %hhu.%hhu.%hhu.%hhu", state->no, state->addr[0], state->addr[1], state->addr[2], state->addr[3]); } else { state->haveAddr = 0; debug_app_bbm_printf("%hhu: no address", state->no); } // get destination port for MCUF frames if (state->haveAddr) { char filename[12]; sprintf(filename, "bbm%hhu/port", state->no); if (AppsToolsReadPort(*state->sectorBuf, state->vi, filename, &state->port) != 0) state->port = 2323; // default MCUF destination port debug_app_bbm_printf("%hhu: port %hu", state->no, state->port); } // initialize internal state state->idxFile = 0; state->haveFile = 0; // initialized, start with first action now state->isInit = 1; TimingGetMs(&state->nextActMs); } /** * @brief run BBM play application * @param[in,out] state internal state of application */ void AppBbmRun(AppBbmState *state) // (extern) { // not initialized -> leave if (!state->isInit) return; // time for next action not yet reached -> leave unsigned long ms; TimingGetMs(&ms); long delta = state->nextActMs - ms; if (delta > 0) return; /* do not catch up - this can harm synchronous playback // if lagging behind more than 100ms, do not try to catch up -> advance time if (delta < -100) state->nextActMs = ms; */ // no file -> open next one if (!state->haveFile) { AppBbmNextFile(state); return; // do not work too much in one step / app might be deactivated now } // process next frame AppBbmProcFrame(state); }