예제 #1
0
/*
 * 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;
}
예제 #2
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);
}
예제 #3
0
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);
	}
}
예제 #4
0
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);
}
예제 #5
0
/*
 * 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;
}
예제 #6
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);
}
예제 #7
0
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;
}