void snd_ioctl (struct despotify_session* ds, int cmd, void *data, int length) { switch (cmd) { case SND_CMD_CHANNEL_END: /* end of substream */ if (ds->dlabort) { DSFYDEBUG("ds->dlstate = DL_DRAINING\n"); ds->dlstate = DL_DRAINING; } else if (ds->dlstate != DL_END_OF_LIST) { DSFYDEBUG("ds->dlstate = DL_FILLING\n"); ds->dlstate = DL_FILLING; /* step down from DL_FILLING_BUSY */ } return; case SND_CMD_END: /* end of track. end of playlist? */ if (!ds->track) { DSFYDEBUG("ds->dlstate = DL_END_OF_LIST\n"); ds->dlstate = DL_END_OF_LIST; } break; } if (ds->dlabort) { if (data) free(data); return; } struct ds_snd_buffer* buff = malloc(sizeof(struct ds_snd_buffer)); if (!buff) { perror ("malloc failed"); exit (-1); } buff->length = length; buff->cmd = cmd; buff->consumed = 0; buff->ptr = data; buff->next = NULL; pthread_mutex_lock (&ds->fifo->lock); DSFYDEBUG_SNDQUEUE("Current FIFO totbytes=%d, pushed data length is %d\n", ds->fifo->totbytes, length); /* Drop the first ogg page if it is Spotify's spec-violating single-page same-serial-number stream */ if (ds->fifo->lastcmd == SND_CMD_START && (buff->ptr[5] == 6)) { int offset = 28; /* size of ogg header */ /* calculate size of page */ for (int i=0; i < buff->ptr[26]; i++) offset += buff->ptr[i+27]; if (offset < buff->length) { memmove(buff->ptr, buff->ptr + offset, length - offset); buff->length -= offset; DSFYDEBUG("Dropping the first %d bytes of data in this stream, new length is %d\n", offset, buff->length); } else { DSFYDEBUG("Corrupt first OGG packet gave offset %d. Skipping.", offset); } } /* Hook in entry in linked list */ if (ds->fifo->end) ds->fifo->end->next = buff; ds->fifo->end = buff; /* If this is the first entry */ if (ds->fifo->start == NULL) ds->fifo->start = buff; ds->fifo->totbytes += buff->length; /* Signal receiver */ pthread_cond_signal (&ds->fifo->cs); pthread_mutex_unlock (&ds->fifo->lock); ds->fifo->lastcmd = cmd; }
/* * Ogg-Vorbis read() callback * Called by both ov_info() and ov_read() * * This functions dequeues buffers from the fifo * */ size_t snd_ov_read_callback(void *ptr, size_t size, size_t nmemb, void* session) { struct despotify_session* ds = session; pthread_mutex_lock(&ds->fifo->lock); int totlength = 0; bool loop = true; /* process data */ while (loop) { /* Check queue status */ if (ds->fifo->start == NULL) { _DSFYDEBUG ("Waiting for data (%d bytes)\n", ds->fifo->totbytes); pthread_cond_wait (&ds->fifo->cs, &ds->fifo->lock); _DSFYDEBUG ("Got data\n"); } DSFYDEBUG_SNDQUEUE("Processing one buffer at ds->fifo->start." " %zd items of size %zd requested. Totbytes: %d\n", size, nmemb, ds->fifo->totbytes ); struct ds_snd_buffer* b = ds->fifo->start; if (!b) break; _DSFYDEBUG("loop cmd:%d bytes:%d\n", b->cmd, ds->fifo->totbytes); switch (b->cmd) { case SND_CMD_START: /* first packet of a track */ DSFYDEBUG ("Got SND_CMD_START\n"); /* Increment by one */ ds->fifo->start = ds->fifo->start->next; /* notify client */ if (ds->client_callback) ds->client_callback(ds, DESPOTIFY_NEW_TRACK, b->ptr, ds->client_callback_data); /* If this was the last entry */ if (b == ds->fifo->end) ds->fifo->end = NULL; if (b->ptr) DSFYfree (b->ptr); DSFYfree (b); break; case SND_CMD_DATA: { /* data packet */ int remaining = b->length - b->consumed; int ptrsize = size * nmemb; int length; if (totlength + remaining < ptrsize) length = remaining; /* The entire buffer will fit */ else { length = ptrsize - totlength; /* Don't overrun ptrsize */ } memcpy (ptr + totlength, b->ptr + b->consumed, length); b->consumed += length; totlength += length; /* If we have used the entire buffer, free it */ if (b->consumed == b->length) { ds->fifo->start = ds->fifo->start->next; ds->fifo->totbytes -= b->length; /* If this was the last entry */ if (b == ds->fifo->end) ds->fifo->end = NULL; DSFYfree (b->ptr); DSFYfree (b); } /* exit if input is empty or output is full */ if (!ds->fifo->start || totlength == (int)(size*nmemb)) loop = false; break; } case SND_CMD_END: /* last packet of a track, return 0 bytes to signal EOF */ DSFYDEBUG ("Got SND_CMD_END\n"); /* if there already are bytes to return, send them first and then come back here empty */ if (totlength) { loop = false; break; } /* Increment by one */ ds->fifo->start = ds->fifo->start->next; /* If this was the last entry */ if (b == ds->fifo->end) ds->fifo->end = NULL; /* If this was the last entry */ if (b == ds->fifo->end) ds->fifo->end = NULL; if (b->ptr) DSFYfree (b->ptr); DSFYfree (b); _DSFYDEBUG("Calling despotify_end_of_track\n"); if (!ds->fifo->start) { /* (snd_stop locks the mutex internally) */ pthread_mutex_unlock(&ds->fifo->lock); snd_stop(ds); pthread_mutex_lock(&ds->fifo->lock); if (ds->client_callback) ds->client_callback(ds, DESPOTIFY_END_OF_PLAYLIST, NULL, ds->client_callback_data); } /* return 0 bytes as EOF marker to decoder */ loop = false; break; } } pthread_mutex_unlock(&ds->fifo->lock); /* Return number of bytes read to ogg-layer */ _DSFYDEBUG("Returning %d bytes. %d left.\n", totlength, ds->fifo->totbytes); return totlength; }