/* * Callback for 'strm' command. */ static int strm_callback(slimproto_t *proto, const unsigned char *buf, int buf_len, void *user_data) { slimproto_msg_t msg; slimaudio_t *audio = (slimaudio_t *) user_data; slimproto_parse_command(buf, buf_len, &msg); DEBUGF("strm cmd %c\n", msg.strm.command); switch (msg.strm.command) { case 's': /* start */ slimaudio_http_connect(audio, &msg); slimaudio_decoder_connect(audio, &msg); slimaudio_output_connect(audio, &msg); break; case 'p': /* pause */ slimaudio_output_pause(audio); break; case 'u': /* unpause */ slimaudio_output_unpause(audio); break; case 'q': /* stop */ audio_stop(audio); break; case 't': /* status */ break; } slimaudio_stat(audio, (char *)&msg.strm.cmd); DEBUGF("DONE strm cmd %c\n", msg.strm.command); return 0; }
/* Wrapper to call slimaudio_stat from output_thread. Because the ** output thread keeps the output mutex locked, this wrapper unlocks ** the mutex for the duration of the slimaudio_stat call. The reason ** is that slimaudio_stat may find that the socket has been closed and ** will then try to stop the audio output by calling ** slimaudio_output_disconnect and this requires the mutex to be unlocked. */ static void output_thread_stat(slimaudio_t* audio, char* code) { pthread_mutex_unlock(&audio->output_mutex); slimaudio_stat(audio, code, (u32_t) 0); pthread_mutex_lock(&audio->output_mutex); }
static void http_recv(slimaudio_t *audio) { char buf[AUDIO_CHUNK_SIZE]; struct timeval timeOut; int n; fd_set fdread; u32_t decode_num_tracks_started; u32_t autostart_threshold; timeOut.tv_sec = 0; timeOut.tv_usec = 100*1000; /* wait for up to 100ms */ FD_ZERO(&fdread); FD_SET(audio->streamfd, &fdread); if (select(audio->streamfd + 1, &fdread, NULL, &fdread, &timeOut) == 0) { return; } while (slimaudio_buffer_available(audio->output_buffer) < AUDIO_CHUNK_SIZE * 2 && slimaudio_buffer_available(audio->decoder_buffer) >= AUDIO_CHUNK_SIZE * 8) { DEBUGF("http_recv: output_buffer %i below %i\n", slimaudio_buffer_available(audio->output_buffer), AUDIO_CHUNK_SIZE * 2); DEBUGF("http_recv: output_decoder_available %i above %i\n", slimaudio_buffer_available(audio->decoder_buffer), AUDIO_CHUNK_SIZE * 8); sched_yield(); } n = recv(audio->streamfd, buf, AUDIO_CHUNK_SIZE, 0); /* n == 0 http stream closed by server */ if (n <= 0) { DEBUGF("http_recv: (2) n=%i msg=%s(%i)\n", n, strerror(SOCKETERROR), SOCKETERROR); http_close(audio); return; } VDEBUGF("http_recv: audio n=%i\n", n); slimaudio_buffer_write(audio->decoder_buffer, buf, n); pthread_mutex_lock(&audio->output_mutex); decode_num_tracks_started = audio->decode_num_tracks_started; pthread_mutex_unlock(&audio->output_mutex); pthread_mutex_lock(&audio->http_mutex); audio->http_total_bytes += n; audio->http_stream_bytes += n; autostart_threshold = audio->autostart_threshold; if ( !decode_num_tracks_started ) { switch ( audio->autostart_mode ) { case '1': /* Modify threshold for autostart modes, and not sync modes */ case '3': switch (audio->decoder_mode) { case 'o': case 'm': if (threshold_override) autostart_threshold = 40000L; break; default: break; } break; default: break; } } VDEBUGF("http_recv: decode_num_tracks_started %u decode_bytes_available %u\n", decode_num_tracks_started, audio->http_stream_bytes ); if ( ( !audio->autostart_threshold_reached ) && ( audio->http_stream_bytes >= autostart_threshold ) ) { audio->autostart_threshold_reached = true; switch ( audio->autostart_mode ) { case '0': case '2': DEBUGF("http_recv: AUTOSTART mode %c at %u threshold %u\n", audio->autostart_mode, audio->http_stream_bytes, autostart_threshold); slimaudio_stat(audio, "STMl", (u32_t) 0); pthread_mutex_unlock(&audio->http_mutex); pthread_cond_broadcast(&audio->http_cond); break; case '1': case '3': DEBUGF("http_recv: AUTOSTART mode %c at %u threshold %u\n", audio->autostart_mode, audio->http_stream_bytes, autostart_threshold); pthread_mutex_unlock(&audio->http_mutex); pthread_cond_broadcast(&audio->http_cond); slimaudio_output_unpause(audio); break; default: break; } } else { pthread_mutex_unlock(&audio->http_mutex); pthread_cond_broadcast(&audio->http_cond); } }
void slimaudio_http_connect(slimaudio_t *audio, slimproto_msg_t *msg) { int n; struct sockaddr_in serv_addr = audio->proto->serv_addr; const socket_t fd = socket(AF_INET, SOCK_STREAM, 0); char http_hdr[HTTP_HEADER_LENGTH]; int pos = 0; int crlf = 0; slimaudio_http_disconnect(audio); if (msg->strm.server_ip != 0) { serv_addr.sin_addr.s_addr = htonl(msg->strm.server_ip); } if (msg->strm.server_port != 0) { serv_addr.sin_port = htons(msg->strm.server_port); } DEBUGF("slimaudio_http_connect: http connect %s:%i\n", inet_ntoa(serv_addr.sin_addr), msg->strm.server_port); if (fd < 0) { perror("slimaudio_http_connect: Error opening socket"); return; } if ( slimproto_configure_socket (fd, 0) != 0 ) { perror("slimaudio_http_connect: error configuring socket"); CLOSESOCKET(fd); return; } if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0) { perror("slimaudio_http_connect: error connecting to server"); CLOSESOCKET(fd); return; } slimaudio_stat(audio, "STMe", (u32_t) 0); /* Stream connection established */ /* send http request to server */ DEBUGF("slimaudio_http_connect: http request %s\n", msg->strm.http_hdr); n = send_message(fd, msg->strm.http_hdr, strlen((const char *)msg->strm.http_hdr), slimproto_get_socketsendflags()); if (n < 0) { DEBUGF("http_send: (1) n=%i msg=%s(%i)\n", n, strerror(SOCKETERROR), SOCKETERROR); CLOSESOCKET(fd); return; } /* read http header */ do { n = recv(fd, http_hdr+pos, 1, 0); if (n < 0) { DEBUGF("http_recv: (1) n=%i msg=%s(%i)\n", n, strerror(SOCKETERROR), SOCKETERROR); CLOSESOCKET(fd); return; } switch (crlf) { case 0: case 2: if (http_hdr[pos] == 13) { crlf++; } else { crlf = 0; } break; case 1: case 3: if (http_hdr[pos] == 10) { crlf++; } else { crlf = 0; } break; default: crlf = 0; break; } pos++; } while (crlf < 4 && pos < HTTP_HEADER_LENGTH -1); http_hdr[pos+1] = '\0'; DEBUGF("slimaudio_http_connect: http connected hdr %s\n", http_hdr); pthread_mutex_lock(&audio->http_mutex); slimaudio_stat(audio, "STMh", (u32_t) 0); /* acknowledge HTTP headers have been received */ slimaudio_buffer_open(audio->decoder_buffer, NULL); audio->streamfd = fd; audio->http_stream_bytes = 0; audio->autostart_mode = msg->strm.autostart ; audio->autostart_threshold_reached = false; audio->autostart_threshold = (msg->strm.threshold & 0xFF) * 1024; #ifdef AAC_DECODER /* AAC container type and bitstream format */ audio->aac_format = msg->strm.pcm_sample_size ; #endif #ifdef WMA_DECODER /* WMA stream details */ audio->wma_chunking = msg->strm.pcm_sample_size ; audio->wma_playstream = msg->strm.pcm_sample_rate + 48 ; /* Squeezebox.pm doesn't use char for this field */ audio->wma_metadatastream = msg->strm.pcm_channels ; #endif DEBUGF("slimaudio_http_connect: pcm_sample_size:%d '%c' pcm_sample_rate:%d '%c' pcm_channels:%d '%c'\n", msg->strm.pcm_sample_size, msg->strm.pcm_sample_size, msg->strm.pcm_sample_rate, msg->strm.pcm_sample_rate, msg->strm.pcm_channels, msg->strm.pcm_channels); /* XXX FIXME Hard coded sample rate calculation */ /* (Sample Rate * Sample Size * Channels / 8 bits/byte) / tenths of a second) */ /* If the server sends 0 for strm.output_threshold, we use OUTPUT_THRESHOLD, stored in bytes. */ if ( msg->strm.output_threshold > 0 ) { /* Stored in bytes */ audio->output_threshold = (((44100*16*2)/8)/10) * msg->strm.output_threshold; } else { audio->output_threshold = OUTPUT_THRESHOLD; } /* Over ride Output threshold */ if ( output_threshold != OUTPUT_THRESHOLD ) audio->output_threshold = output_threshold; DEBUGF("slimaudio_http_connect: autostart_mode=%c autostart_threshold=%i output_threshold=%i replay_gain=%f\n", audio->autostart_mode, audio->autostart_threshold, audio->output_threshold, audio->replay_gain); audio->http_state = STREAM_PLAYING; pthread_mutex_unlock(&audio->http_mutex); pthread_cond_broadcast(&audio->http_cond); }
/* * Callback for 'strm' command. */ static int strm_callback(slimproto_t *proto, const unsigned char *buf, int buf_len, void *user_data) { slimproto_msg_t msg; float replay_gain; slimaudio_t *audio = (slimaudio_t *) user_data; slimproto_parse_command(buf, buf_len, &msg); DEBUGF("strm cmd %c strm.replay_gain:%u ", msg.strm.command, msg.strm.replay_gain); switch (msg.strm.command) { case 's': /* start */ replay_gain = (float) (msg.strm.replay_gain) / 65536.0; audio->start_replay_gain = replay_gain == 0.0 ? 1.0 : replay_gain; if (audio->replay_gain == -1.0) audio->replay_gain = audio->start_replay_gain; DEBUGF("start_replay_gain:%f\n", audio->start_replay_gain); slimaudio_stat(audio, "STMc", (u32_t) 0); /* connect, acknowledge strm-s */ slimaudio_http_connect(audio, &msg); slimaudio_decoder_connect(audio, &msg); slimaudio_output_connect(audio, &msg); break; case 'p': /* pause */ DEBUGF("\n"); slimaudio_output_pause(audio); /* Only send STMp if interval is zero */ if (! msg.strm.replay_gain) slimaudio_stat(audio, "STMp", (u32_t) 0); /* pause */ break; case 'u': /* unpause */ DEBUGF("\n"); slimaudio_output_unpause(audio); slimaudio_stat(audio, "STMr", (u32_t) 0); /* resume */ break; case 'q': /* stop */ DEBUGF("\n"); audio_stop(audio); slimaudio_stat(audio, "STMf", (u32_t) 0); /* acknowledge stop cmd */ break; case 'f': /* flush */ DEBUGF("\n"); slimaudio_buffer_flush(audio->decoder_buffer); slimaudio_buffer_flush(audio->output_buffer); slimaudio_stat(audio, "STMf", (u32_t) 0); /* acknowledge flush cmd */ break; case 'a': /* skip ahead */ DEBUGF("\n"); break; case 't': /* status */ DEBUGF("\n"); slimaudio_stat(audio, "STMt", msg.strm.replay_gain); break; } DEBUGF("DONE strm cmd %c\n", msg.strm.command); return 0; }
void slimaudio_http_connect(slimaudio_t *audio, slimproto_msg_t *msg) { slimaudio_http_disconnect(audio); struct sockaddr_in serv_addr = audio->proto->serv_addr; if (msg->strm.server_ip != 0) { serv_addr.sin_addr.s_addr = htonl(msg->strm.server_ip); } if (msg->strm.server_port != 0) { serv_addr.sin_port = htons(msg->strm.server_port); } DEBUGF("slimaudio_http_connect: http connect %s:%i\n", inet_ntoa(serv_addr.sin_addr), msg->strm.server_port); const socket_t fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("slimaudio_http_connect: Error opening socket"); return; } if ( slimproto_configure_socket (fd, 0) != 0 ) { perror("slimaudio_http_connect: error configuring socket"); CLOSESOCKET(fd); return; } if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0) { perror("slimaudio_http_connect: error connecting to server"); CLOSESOCKET(fd); return; } slimaudio_stat(audio, "STMe", (u32_t) 0); /* Stream connection established */ /* send http request to server */ DEBUGF("slimaudio_http_connect: http request %s\n", msg->strm.http_hdr); int n = send(fd, msg->strm.http_hdr, strlen(msg->strm.http_hdr), slimproto_get_socketsendflags()); if (n < 0) { DEBUGF("http_send: (1) n=%i msg=%s(%i)\n", n, strerror(SOCKETERROR), SOCKETERROR); CLOSESOCKET(fd); return; } /* read http header */ char http_hdr[HTTP_HEADER_LENGTH]; int pos = 0; int crlf = 0; do { n = recv(fd, http_hdr+pos, 1, 0); if (n < 0) { DEBUGF("http_recv: (1) n=%i msg=%s(%i)\n", n, strerror(SOCKETERROR), SOCKETERROR); CLOSESOCKET(fd); return; } switch (crlf) { case 0: case 2: if (http_hdr[pos] == 13) { crlf++; } else { crlf = 0; } break; case 1: case 3: if (http_hdr[pos] == 10) { crlf++; } else { crlf = 0; } break; default: crlf = 0; break; } pos++; } while (crlf < 4 && pos < HTTP_HEADER_LENGTH -1); http_hdr[pos+1] = '\0'; DEBUGF("slimaudio_http_connect: http connected hdr %s\n", http_hdr); pthread_mutex_lock(&audio->http_mutex); slimaudio_stat(audio, "STMh", (u32_t) 0); /* acknowledge HTTP headers have been received */ slimaudio_buffer_open(audio->decoder_buffer, NULL); audio->streamfd = fd; audio->http_stream_bytes = 0; audio->autostart = msg->strm.autostart == '1' || msg->strm.autostart == '3'; audio->autostart_threshold = (msg->strm.threshold & 0xFF) * 1024; /* XXX FIXME Hard coded sample rate calculation */ /* (Sample Rate * Sample Size * Channels / 8 bits/byte) / tenths of a second) */ audio->output_threshold = (((44100*16*2)/8)/10) * msg->strm.output_threshold; /* Stored in bytes */ DEBUGF("slimaudio_http_connect: autostart=%i autostart_threshold=%i output_threshold=%i replay_gain=%f\n", audio->autostart, audio->autostart_threshold, audio->output_threshold, audio->replay_gain); audio->http_state = STREAM_PLAYING; pthread_mutex_unlock(&audio->http_mutex); pthread_cond_broadcast(&audio->http_cond); }
static void *decoder_thread(void *ptr) { bool decoder_failed = false; unsigned char first_time = 1; slimaudio_t *audio = (slimaudio_t *) ptr; #ifdef BSD_THREAD_LOCKING pthread_mutex_lock(&audio->decoder_mutex); #endif audio->decoder_state = STREAM_STOPPED; while ( audio->decoder_state != STREAM_QUIT ) { switch (audio->decoder_state) { case STREAM_STOPPED: DEBUGF("decoder_thread: STREAM_STOPPED first_time:%d\n", first_time); if (first_time == 1) { /* * The first time in, the mutex has already been * acquired in the function that starts the thread. */ first_time = 0; } else { pthread_mutex_lock(&audio->decoder_mutex); } if ( decoder_failed ) { decoder_failed = false; slimaudio_stat(audio, "STMn", (u32_t) 0); // decoder does not support format DEBUGF("decoder_thread: decoder %c failed\n", audio->decoder_mode); } pthread_cond_wait(&audio->decoder_cond, &audio->decoder_mutex); pthread_mutex_unlock(&audio->decoder_mutex); break; case STREAM_PLAYING: DEBUGF("decoder_thread: STREAM_PLAYING type %c\n", audio->decoder_mode); switch (audio->decoder_mode) { case 'm': // mp3 slimaudio_decoder_mad_process(audio); break; case 'f': // flac slimaudio_decoder_flac_process(audio); break; case 'o': // ogg vorbis slimaudio_decoder_vorbis_process(audio); break; case 'p': // wav slimaudio_decoder_pcm_process(audio); break; #ifdef AAC_DECODER case 'a': // aac if ( slimaudio_decoder_aac_process(audio) < 0 ) { decoder_failed = true ; } break; #endif #ifdef WMA_DECODER case 'w': // wma if ( slimaudio_decoder_wma_process(audio) < 0 ) { decoder_failed = true ; } break; #endif default: fprintf(stderr, "Cannot decode unknown format: %c\n", audio->decoder_mode); slimaudio_stat(audio, "STMn", (u32_t) 0); // decoder does not support format break; } DEBUGF("decoder_thread: STREAM_PLAY (before STMd) previous state: %i\n", audio->decoder_state); if ( audio->decoder_state == STREAM_PLAYING ) { slimaudio_stat(audio, "STMd", (u32_t) 0); DEBUGF("decoder_thread: STREAM_PLAY (after STMd) previous state: %i\n", audio->decoder_state); } case STREAM_STOP: DEBUGF("decoder_thread: STREAM_STOP previous state: %i\n", audio->decoder_state); pthread_mutex_lock(&audio->decoder_mutex); audio->decoder_state = STREAM_STOPPED; slimaudio_buffer_close(audio->output_buffer); pthread_mutex_unlock(&audio->decoder_mutex); pthread_cond_broadcast(&audio->decoder_cond); break; case STREAM_QUIT: DEBUGF("decoder_thread: STREAM_QUIT\n"); break; } } return 0; }