Ubuntu接收客户端

//编译:gcc -o recv_dec_play recv_dec_play.c -lspeex -logg -lasound -lortp
/*************************************************************************
 * Filename: recv_dec_play.c
 * Author:  Gongguan
 * Description: 通过RTP协议接收来自网络的ogg音频数据,解码后播放出来
 * Version: Trial
 * Date: 2012-03-16
 * History: none
 *************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <ortp/ortp.h>
#include <signal.h>

#include <speex/speex.h>
#include <ogg/ogg.h>
#include <math.h>
#include <string.h>
#include <speex/speex_header.h>
#include <speex/speex_stereo.h>
#include <speex/speex_callbacks.h>

#include <alsa/asoundlib.h>
#include <assert.h>
#include <termios.h>
#include <sys/poll.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <endian.h>

#define DEC_FRAME_SIZE 640
static const SpeexMode *mode;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t recv_over = PTHREAD_COND_INITIALIZER;
static pthread_cond_t play_over = PTHREAD_COND_INITIALIZER;
static pthread_cond_t ready_recv = PTHREAD_COND_INITIALIZER;
static pthread_cond_t ready_play = PTHREAD_COND_INITIALIZER;

typedef void* (*fun)(void*);
void* receive(void*);
void* decode(void*);
void* play(void*);

pthread_t tdec, tplay, trecv;

void stop_handler(int signum)
{
	exit(1);
}

void ssrc_cb(RtpSession *session)
{
	printf("hey, the ssrc has changed !\n");
}

static u_char *recv_buf = NULL;
static int recv_size = 0;

#define MULAW 0
#define ALAW 1
#define PORT 5000
#define RECV_SIZE 160

/*
 * Now, for player
 */
#define DEFAULT_FORMAT		SND_PCM_FORMAT_S16_LE
#define DEFAULT_SPEED 		8000
#define CHANNELS 2
#define PLAYBACK_TIME 3
#define PERIOD_FRAMES 160
static snd_pcm_sframes_t (*writei_func)(snd_pcm_t *handle, const void *buffer, snd_pcm_uframes_t size);
static snd_pcm_t *handle;
static struct {
	snd_pcm_format_t format;
	unsigned int channels;
	unsigned int rate;
} hwparams;
static int quiet_mode = 0;//printf information
static snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
static int nonblock = 0;
static int can_pause = 0;
static short *audio_buf = NULL;
static snd_pcm_uframes_t chunk_size = PERIOD_FRAMES;
static unsigned period_time = 0;
static unsigned buffer_time = 0;
static snd_pcm_uframes_t period_frames = PERIOD_FRAMES;
static snd_pcm_uframes_t buffer_frames = 0;
static int avail_min = -1;
static int start_delay = 0;
static int stop_delay = 0;
static int monotonic = 0;
static size_t bits_per_sample, bits_per_frame;
static size_t chunk_bytes;
static int test_nowait = 0;
volatile static int recycle_capture_file = 0;
static void playback(char *filename);
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
#define error(...) do {\
	fprintf(stderr, "%s:%d: ", __FUNCTION__, __LINE__); \
	fprintf(stderr, __VA_ARGS__); \
	putc('\n', stderr); \
} while (0)
#else
#define error(args...) do {\
	fprintf(stderr, "%s:%d: ", __FUNCTION__, __LINE__); \
	fprintf(stderr, ##args); \
	putc('\n', stderr); \
} while (0)
#endif
/*
 * End player
 */

/***************************************************************************
 * receive the ogg audio stream from network by RTP protocal
 ***************************************************************************/
void* receive(void* arg)
{
	RtpSession *session;
	u_char buffer[RECV_SIZE];
	int err;
	uint32_t ts=0;
	int stream_received=0;
	int local_port = PORT;
	int have_more;
	int i;
	int format=0;
	int jittcomp=40;
	bool_t adapt=TRUE;
	
	ortp_init();
	ortp_scheduler_init();
	ortp_set_log_level_mask(ORTP_DEBUG|ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR);
	signal(SIGINT,stop_handler);
	session=rtp_session_new(RTP_SESSION_RECVONLY);	
	rtp_session_set_scheduling_mode(session,1);
	rtp_session_set_blocking_mode(session,1);
	rtp_session_set_local_addr(session,"0.0.0.0",PORT);
	rtp_session_set_connected_mode(session,TRUE);
	rtp_session_set_symmetric_rtp(session,TRUE);
	rtp_session_enable_adaptive_jitter_compensation(session,adapt);
	rtp_session_set_jitter_compensation(session,jittcomp);
	rtp_session_set_payload_type(session,0);	//mulaw
	rtp_session_signal_connect(session,"ssrc_changed",(RtpCallback)ssrc_cb,0);
	rtp_session_signal_connect(session,"ssrc_changed",(RtpCallback)rtp_session_reset,0);	

	sleep(1); //sleep for a while to make sure decode thread can receive the signal
	pthread_mutex_lock(&mutex);	
	while(1)
	{
		have_more=1;
		while (have_more)
		{
			recv_size = rtp_session_recv_with_ts(session,buffer,RECV_SIZE,ts,&have_more);
			if (recv_size > 0) 
				stream_received = 1;
			/* this is to avoid to write to disk some silence before the first RTP packet is returned */	
			if ((stream_received) && (recv_size > 0)) 
			{
				for (i = 0; i < recv_size; i++)
					recv_buf[i] = buffer[i];
				
				pthread_cond_signal(&recv_over); //send signal to decoder to ready for decode
				//printf("Receiver : Sending signal to decoder ...\n");
				pthread_cond_wait(&ready_recv, &mutex); //waitting the signal from decoder to receive again
				//printf("Receiver : Roger that!\n");
			}
		}
		ts += RECV_SIZE;
	}

	pthread_mutex_unlock(&mutex);
	pthread_exit(0);
}//end of receive()


/*
 * process the ogg audio header
 */ 
static void *process_header(ogg_packet *op, spx_int32_t enh_enabled, spx_int32_t *frame_size, int *granule_frame_size, spx_int32_t *rate, int *nframes, int forceMode, int *channels, SpeexStereoState *stereo, int *extra_headers, int quiet)
{
   void *st;
   SpeexHeader *header;
   int modeID;
   SpeexCallback callback;
      
   header = speex_packet_to_header((char*)op->packet, op->bytes);
   if (!header)
   {
      fprintf (stderr, "Cannot read header\n");
      return NULL;
   }
   
   
   if (header->mode >= SPEEX_NB_MODES || header->mode<0)
   {
      fprintf (stderr, "Mode number %d does not (yet/any longer) exist in this version\n", 
               header->mode);
      free(header);
      return NULL;
   }
      
   modeID = header->mode;
   if (forceMode!=-1)
      modeID = forceMode;

   mode = speex_lib_get_mode (modeID);
   
   if (header->speex_version_id > 1)
   {
      fprintf (stderr, "This file was encoded with Speex bit-stream version %d, which I don't know how to decode\n", header->speex_version_id);
      free(header);
      return NULL;
   }

   if (mode->bitstream_version < header->mode_bitstream_version)
   {
      fprintf (stderr, "The file was encoded with a newer version of Speex. You need to upgrade in order to play it.\n");
      free(header);
      return NULL;
   }
   if (mode->bitstream_version > header->mode_bitstream_version) 
   {
      fprintf (stderr, "The file was encoded with an older version of Speex. You would need to downgrade the version in order to play it.\n");
      free(header);
      return NULL;
   }
 
   st = speex_decoder_init(mode);
   if (!st)
   {
      fprintf (stderr, "Decoder initialization failed.\n");
      free(header);
      return NULL;
   }
   speex_decoder_ctl(st, SPEEX_SET_ENH, &enh_enabled);
   speex_decoder_ctl(st, SPEEX_GET_FRAME_SIZE, frame_size);
   *granule_frame_size = *frame_size;

   if (!*rate)
      *rate = header->rate;
   /* Adjust rate if --force-* options are used */
   if (forceMode!=-1)
   {
      if (header->mode < forceMode)
      {
         *rate <<= (forceMode - header->mode);
         *granule_frame_size >>= (forceMode - header->mode);
      }
      if (header->mode > forceMode)
      {
         *rate >>= (header->mode - forceMode);
         *granule_frame_size <<= (header->mode - forceMode);
      }
   }


   speex_decoder_ctl(st, SPEEX_SET_SAMPLING_RATE, rate);

   *nframes = header->frames_per_packet;

   if (*channels==-1)
      *channels = header->nb_channels;

   if (!(*channels==1))
   {
      *channels = 2;
      callback.callback_id = SPEEX_INBAND_STEREO;
      callback.func = speex_std_stereo_request_handler;
      callback.data = stereo;
      speex_decoder_ctl(st, SPEEX_SET_HANDLER, &callback);
   }
  
   if (!quiet)
   {
      fprintf (stderr, "Decoding %d Hz audio using %s mode", 
               *rate, mode->modeName);

      if (*channels==1)
         fprintf (stderr, " (mono");
      else
         fprintf (stderr, " (stereo");
      
      if (header->vbr)
         fprintf (stderr, ", VBR)\n");
      else
         fprintf(stderr, ")\n");
      /*fprintf (stderr, "Decoding %d Hz audio at %d bps using %s mode\n", 
       *rate, mode->bitrate, mode->modeName);*/
   }

	*extra_headers = header->extra_headers;

   free(header);
   return st;
}//end of process_header()

/************************************************************************************
 * decode the received audio from the receiver and give it to player after decode it
 *************************************************************************************/
void* decode(void* arg)
{
   int c;
   int option_index = 0;
   short out[DEC_FRAME_SIZE];
   short output[DEC_FRAME_SIZE];
   int frame_size=0, granule_frame_size=0;
   void *st=NULL;
   SpeexBits bits;
   int packet_count=0;
   int stream_init = 0;
   int quiet = 0;
   ogg_int64_t page_granule=0, last_granule=0;
   int skip_samples=0, page_nb_packets;

   ogg_sync_state oy;
   ogg_page       og;
   ogg_packet     op;
   ogg_stream_state os;

   int enh_enabled = 1;
   int nframes=2;
   int print_bitrate=0;
   int close_in=0;
   int eos=0;
   int forceMode=-1;
   float loss_percent=-1;
   SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT;
   int channels=-1;
   int rate=0;
   int extra_headers=0;
   int lookahead;
   int speex_serialno = -1;
   int i, j, recv_loop,dec_loop;

   /* Init Ogg data struct */
   ogg_sync_init(&oy);
   
   speex_bits_init(&bits);

   /* Main decoding loop */
	while (1)
	{
		u_char *data;

		/*Get the ogg buffer for writing*/
		data = ogg_sync_buffer(&oy, 160);

		pthread_cond_wait(&recv_over, &mutex); //wait the signal from decoder
		//printf("Decoder : Roger that!\n");

		/*Read bitstream from input file*/
		for (recv_loop = 0; recv_loop < recv_size; recv_loop++)
		{
			data[recv_loop] = recv_buf[recv_loop];
		}

		ogg_sync_wrote(&oy, recv_size);

		/*Loop for all complete pages we got (most likely only one)*/
		while (ogg_sync_pageout(&oy, &og) == 1)
		{
			int packet_no;
			if (stream_init == 0) 
			{
				ogg_stream_init(&os, ogg_page_serialno(&og));
				stream_init = 1;
			}
			
			if (ogg_page_serialno(&og) != os.serialno) 
				ogg_stream_reset_serialno(&os, ogg_page_serialno(&og));	/* so all streams are read. */

			/*Add page to the bitstream*/
			ogg_stream_pagein(&os, &og);
			page_granule = ogg_page_granulepos(&og);
			page_nb_packets = ogg_page_packets(&og);
			if (page_granule>0 && frame_size)
			{
				/* FIXME: shift the granule values if --force-* is specified */
				skip_samples = frame_size*(page_nb_packets*granule_frame_size*nframes - (page_granule-last_granule))/granule_frame_size;
				
				if (ogg_page_eos(&og))
					skip_samples = -skip_samples;
            } else /*else if (!ogg_page_bos(&og))  skip_samples = 0;*/
			{
				skip_samples = 0;
			}
			//printf ("page granulepos: %d %d %d\n", skip_samples, page_nb_packets, (int)page_granule);

			last_granule = page_granule;

			/*Extract all available packets*/
			packet_no=0;

			while (!eos && ogg_stream_packetout(&os, &op) == 1)
			{
				if (op.bytes>=5 && !memcmp(op.packet, "Speex", 5)) 
				{
					speex_serialno = os.serialno;
				}

				if (speex_serialno == -1 || os.serialno != speex_serialno)
					break;
			
				if (packet_count==0)	/*If first packet, process as Speex header*/
				{
					st = process_header(&op, enh_enabled, &frame_size, &granule_frame_size, &rate, &nframes, forceMode, &channels, &stereo, &extra_headers, quiet);
					if (!st)
						exit(1);
					speex_decoder_ctl(st, SPEEX_GET_LOOKAHEAD, &lookahead);
					if (!nframes)
						nframes=1;
				}else if(packet_count<=1+extra_headers)
            	{
               		/* Ignore extra headers.Don't delete it, or you will find a notification! */
            	} 
				else
				{
					int lost=0;
					packet_no++;
					if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
					lost=1;

					/*End of stream condition*/
					if (op.e_o_s && os.serialno == speex_serialno) /* don't care for anything except speex eos */
					eos=1;

					/*Copy Ogg packet to Speex bitstream*/
					speex_bits_read_from(&bits, (char*)op.packet, op.bytes);

 					for (j = 0; j != nframes; j++)
					{
						int ret;

						/*Decode frame*/
						if (!lost)
							ret = speex_decode_int(st, &bits, output);
						else
							ret = speex_decode_int(st, NULL, output);
						if (ret==-1)
							break;
						if (ret==-2)
						{
							fprintf (stderr, "Decoding error: corrupted stream?\n");
							break;
						}
						
						if (speex_bits_remaining(&bits)<0)
						{
							fprintf (stderr, "Decoding overflow: corrupted stream?\n");
							break;
						}
					
						if (channels==2)
							speex_decode_stereo_int(output, frame_size, &stereo);
	
						if (print_bitrate)
						{
							spx_int32_t tmp;
							char ch=13;
							speex_decoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
							fputc (ch, stderr);
							fprintf (stderr, "Bitrate is use: %d bps     ", tmp);
						}
	
						for (i = 0; i < frame_size*channels; i++)
							out[i]=output[i];
	
						int frame_offset = 0;
						int new_frame_size = frame_size;

						//printf ("packet %d %d\n", packet_no, skip_samples);
						//fprintf (stderr, "packet %d %d %d\n", packet_no, skip_samples, lookahead);
						if (packet_no==1 && j==0 && skip_samples > 0)
						{
							//printf ("chopping first packet\n");
							new_frame_size -= skip_samples+lookahead;
							frame_offset = skip_samples+lookahead;
						}

						if (packet_no == page_nb_packets && skip_samples < 0)
						{
							int packet_length = nframes*frame_size+skip_samples+lookahead;
							new_frame_size = packet_length - j*frame_size;
							if (new_frame_size<0)
								new_frame_size = 0;
							if (new_frame_size>frame_size)
								new_frame_size = frame_size;
							//printf ("chopping end: %d %d %d\n", new_frame_size, packet_length, packet_no);
						}
	
						if (new_frame_size > 0)
						{  
							for (j = 0,dec_loop=0;(dec_loop<DEC_FRAME_SIZE/sizeof(short))&&(j<new_frame_size*channels);dec_loop++,j++)
								audio_buf[dec_loop] = out[j + frame_offset*channels];

							pthread_cond_signal(&ready_play);	//inform player ready to play
							//printf("Decoder : Sending signal to player ...\n");
							pthread_cond_wait(&play_over, &mutex); //wait the signal from player
							//audio_size += sizeof(short)*new_frame_size*channels;
						}
						
					}//end of for (j = 0; j != nframes; j++)
				}//end of else
				packet_count++;
			}//end of while (!eos && ogg_stream_packetout(&os, &op) == 1)
		}//end of while (ogg_sync_pageout(&oy, &og) == 1)
	pthread_cond_signal(&ready_recv);
	//printf("Decoder : Sending signal to receiver ... \n");
	}//end of while (1)

	if (st)
		speex_decoder_destroy(st);
	else 
		fprintf (stderr, "This doesn't look like a Speex file\n");
	
	speex_bits_destroy(&bits);
	if (stream_init)
		ogg_stream_clear(&os);
	ogg_sync_clear(&oy);

	pthread_exit(0);
}//end of decode()

/*
 * Subroutine to clean up before exit.
 */
static void prg_exit(int code) 
{
	if (handle)
		snd_pcm_close(handle);
	exit(code);
}

/*
 * deal with signal
 */
static void signal_handler(int sig)
{
	static int in_aborting;

	if (in_aborting)
		return;

	in_aborting = 1;

	if (!quiet_mode)
		fprintf(stderr, "Aborted by signal %s...\n", strsignal(sig));

	if (handle && sig != SIGABRT) {
		snd_pcm_close(handle);
		handle = NULL;
	}
	prg_exit(EXIT_FAILURE);
}//end of signal_handler()

/* 
 * call on SIGUSR1 signal
 */
static void signal_handler_recycle (int sig)
{
	/* flag the capture loop to start a new output file */
	recycle_capture_file = 1;
}

/*
 * set hardware & software parameters
 */
static void set_params(void)
{
	snd_pcm_hw_params_t *params;
	snd_pcm_sw_params_t *swparams;
	snd_pcm_uframes_t buffer_size;
	int err;
	size_t n;
	unsigned int rate;
	snd_pcm_uframes_t start_threshold, stop_threshold;
	snd_pcm_hw_params_alloca(¶ms);
	snd_pcm_sw_params_alloca(&swparams);

	err = snd_pcm_hw_params_any(handle, params);
	if (err < 0) {
		error("Broken configuration for this PCM: no configurations available");
		prg_exit(EXIT_FAILURE);
	}

	err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
	if (err < 0) {
		error("Access type not available");
		prg_exit(EXIT_FAILURE);
	}

	err = snd_pcm_hw_params_set_format(handle, params, hwparams.format);
	if (err < 0) {
		error("Sample format non available");
		prg_exit(EXIT_FAILURE);
	}

	err = snd_pcm_hw_params_set_channels(handle, params, hwparams.channels);
	if (err < 0) {
		error("Channels count non available");
		prg_exit(EXIT_FAILURE);
	}

	rate = hwparams.rate;
	err = snd_pcm_hw_params_set_rate_near(handle, params, &hwparams.rate, 0);
	assert(err >= 0);
	if ((float)rate * 1.05 < hwparams.rate || (float)rate * 0.95 > hwparams.rate) {
		if (!quiet_mode) {
			char plugex[64];
			const char *pcmname = snd_pcm_name(handle);
			fprintf(stderr, "Warning: rate is not accurate (requested = %iHz, got = %iHz)\n", rate, hwparams.rate);
			if (! pcmname || strchr(snd_pcm_name(handle), ':'))
				*plugex = 0;
			else
				snprintf(plugex, sizeof(plugex), "(-Dplug:%s)",
					 snd_pcm_name(handle));
			fprintf(stderr,"         please, try the plug plugin %s\n",
				plugex);
		}
	}
	rate = hwparams.rate;

	if (buffer_time == 0 && buffer_frames == 0) {
		err = snd_pcm_hw_params_get_buffer_time_max(params, &buffer_time, 0);
		assert(err >= 0);
		if (buffer_time > 500000)
			buffer_time = 500000;
	}

	if (period_time == 0 && period_frames == 0) {
		if (buffer_time > 0)
			period_time = buffer_time / 4;
		else
			period_frames = buffer_frames / 4;
	}

	if (period_time > 0)
		err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, 0);
	else
		err = snd_pcm_hw_params_set_period_size_near(handle, params, &period_frames, 0);
	assert(err >= 0);
	if (buffer_time > 0) {
		err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, 0);
	} else {
		err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_frames);
	}
	assert(err >= 0);
	monotonic = snd_pcm_hw_params_is_monotonic(params);
	can_pause = snd_pcm_hw_params_can_pause(params);

	err = snd_pcm_hw_params(handle, params);
	if (err < 0) {
		error("Unable to install hw params:");
		prg_exit(EXIT_FAILURE);
	}

	snd_pcm_hw_params_get_period_size(params, &chunk_size, 0);
	snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
	if (chunk_size == buffer_size) {
		error("Can't use period equal to buffer size (%lu == %lu)",
		      chunk_size, buffer_size);
		prg_exit(EXIT_FAILURE);
	}

	snd_pcm_sw_params_current(handle, swparams);
	if (avail_min < 0)
		n = chunk_size;
	else
		n = (double) rate * avail_min / 1000000;
	err = snd_pcm_sw_params_set_avail_min(handle, swparams, n);

	/* round up to closest transfer boundary */
	n = buffer_size;
	if (start_delay <= 0) {
		start_threshold = n + (double) rate * start_delay / 1000000;
	} else
		start_threshold = (double) rate * start_delay / 1000000;
	if (start_threshold < 1)
		start_threshold = 1;
	if (start_threshold > n)
		start_threshold = n;
	err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold);
	assert(err >= 0);
	if (stop_delay <= 0) 
		stop_threshold = buffer_size + (double) rate * stop_delay / 1000000;
	else
		stop_threshold = (double) rate * stop_delay / 1000000;
	err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold);
	assert(err >= 0);

	if (snd_pcm_sw_params(handle, swparams) < 0) {
		error("unable to install sw params:");
		prg_exit(EXIT_FAILURE);
	}

	bits_per_sample = snd_pcm_format_physical_width(hwparams.format);
	bits_per_frame = bits_per_sample * hwparams.channels;
	chunk_bytes = chunk_size * bits_per_frame / 8;

}//end of set_params()

static void do_pause(void)
{
	int err;
	unsigned char b;

	if (!can_pause) {
		fprintf(stderr, "\rPAUSE command ignored (no hw support)\n");
		return;
	}
	err = snd_pcm_pause(handle, 1);
	if (err < 0) {
		error("pause push error: %s", snd_strerror(err));
		return;
	}
	while (1) {
		while (read(fileno(stdin), &b, 1) != 1);
		if (b == ' ' || b == '\r') {
			while (read(fileno(stdin), &b, 1) == 1);
			err = snd_pcm_pause(handle, 0);
			if (err < 0)
				error("pause release error: %s", snd_strerror(err));
			return;
		}
	}
}

/* It's for xrun() */
#ifndef timersub
#define	timersub(a, b, result) \
do { \
	(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
	(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
	if ((result)->tv_usec < 0) { \
		--(result)->tv_sec; \
		(result)->tv_usec += 1000000; \
	} \
} while (0)
#endif

/*
 * I/O error handler
 */
static void xrun(void)
{
	snd_pcm_status_t *status;
	int res;
	
	snd_pcm_status_alloca(&status);
	if ((res = snd_pcm_status(handle, status))<0) {
		error("status error: %s", snd_strerror(res));
		prg_exit(EXIT_FAILURE);
	}

	if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
		if (monotonic) {
			fprintf(stderr, "%s !!!\n", "underrun");
		} else {
			struct timeval now, diff, tstamp;
			gettimeofday(&now, 0);
			snd_pcm_status_get_trigger_tstamp(status, &tstamp);
			timersub(&now, &tstamp, &diff);
			fprintf(stderr, "%s!!! (at least %.3f ms long)\n",
				stream == SND_PCM_STREAM_PLAYBACK ? "underrun" : "overrun",
				diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
		}

		if ((res = snd_pcm_prepare(handle))<0) {
			error("xrun: prepare error: %s", snd_strerror(res));
			prg_exit(EXIT_FAILURE);
		}
		return;		/* ok, data should be accepted again */
	} if (snd_pcm_status_get_state(status) == SND_PCM_STATE_DRAINING) {
		if (stream == SND_PCM_STREAM_CAPTURE) {
			fprintf(stderr, "capture stream format change? attempting recover...\n");
			if ((res = snd_pcm_prepare(handle))<0) {
				error("xrun(DRAINING): prepare error: %s", snd_strerror(res));
				prg_exit(EXIT_FAILURE);
			}
			return;
		}
	}

	error("read/write error, state = %s", snd_pcm_state_name(snd_pcm_status_get_state(status)));
	prg_exit(EXIT_FAILURE);
}//end of xrun()

/* 
 * I/O suspend handler
 */
static void suspend(void)
{
	int res;

	if (!quiet_mode)
		fprintf(stderr, "Suspended. Trying resume. "); fflush(stderr);
	while ((res = snd_pcm_resume(handle)) == -EAGAIN)
		sleep(1);	/* wait until suspend flag is released */
	if (res < 0) {
		if (!quiet_mode)
			fprintf(stderr, "Failed. Restarting stream. "); fflush(stderr);
		if ((res = snd_pcm_prepare(handle)) < 0) {
			error("suspend: prepare error: %s", snd_strerror(res));
			prg_exit(EXIT_FAILURE);
		}
	}
	if (!quiet_mode)
		fprintf(stderr, "Done.\n");
}

/*
 * write function
 */
static ssize_t pcm_write(short *data, size_t count)
{
	ssize_t r;
	ssize_t result = 0;
	int i = 1;

	while (count > 0) 
	{
		r = writei_func(handle, data, count);
		if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) 
		{
			if (!test_nowait)
				snd_pcm_wait(handle, 100);
		} else if (r == -EPIPE) {
			xrun();
		} else if (r == -ESTRPIPE) {
			suspend();
		} else if (r < 0) {
			error("write error: %s", snd_strerror(r));
			prg_exit(EXIT_FAILURE);
		}

		if (r > 0) 
		{
			result += r;
			count -= r;
			data += r * bits_per_frame / 8;
		}
	}
	return result;
}//end of pcm_write()

/***********************************************************************************
 * play the audio stream after decode it 
 ***********************************************************************************/
void* play(void* arg)
{
	char *pcm_name = "default";
	unsigned int err;

	stream = SND_PCM_STREAM_PLAYBACK;
	hwparams.format = DEFAULT_FORMAT;
	hwparams.rate = DEFAULT_SPEED;
	hwparams.channels = CHANNELS;

	/* open sound device */	
	err = snd_pcm_open(&handle, pcm_name, stream, 0);
	if (err < 0) 
	{
		perror("Audio open error");
		exit(1);
	}

	writei_func = snd_pcm_writei;

	signal(SIGINT, signal_handler);
	signal(SIGTERM, signal_handler);
	signal(SIGABRT, signal_handler);
	signal(SIGUSR1, signal_handler_recycle);

	set_params();

	while(1)
	{
		pthread_cond_wait(&ready_play, &mutex); //wait the signal from decoder
		//printf(" Player : Roger that!\n");
		pcm_write(audio_buf, PERIOD_FRAMES);
		pthread_cond_signal(&play_over);
	}
	
	pthread_mutex_unlock(&mutex);
	snd_pcm_nonblock(handle, 0);
	snd_pcm_drain(handle);
	snd_pcm_nonblock(handle, nonblock);
	
	snd_pcm_close(handle);
	pthread_exit(0);
}//end of play()

/*************************************************************************
 *	receive-->decode-->playback
 *************************************************************************/
int main(int argc, char** argv)
{
	/* receive the audio stream from network and give it to decoder */
	recv_buf = (u_char *)malloc(160);
	if (NULL == recv_buf)
		printf("Malloc recv_buf\n");

	/* play the audio stream after the decoder deal with it, it's just one period size  */
	audio_buf = (short *)malloc(PERIOD_FRAMES*4);
	if (NULL == audio_buf)
		printf("Malloc audio_buf\n");

	int t1 = 0, t2 = 0, t3 = 0;
	/* First, receive the audio stream from network */
	t1 = pthread_create(&trecv, NULL, receive, NULL);
	if(t1 != 0)
		printf("Create receive thread error!\n");

	/* Second, decode the audio stream */	
	t2 = pthread_create(&tdec, NULL, decode, NULL);
	if(t2 != 0)
		printf("Create decode thread error!\n");

	/* Third, playback the audio stream */
	t3 = pthread_create(&tplay, NULL, play, NULL);
	if(t3 != 0)
		printf("Create play thread error!\n");

	/* waitting for all the threads quit */
	pthread_join(trecv, NULL);
	pthread_join(tdec, NULL);
	pthread_join(tplay, NULL);

	free(recv_buf);
	free(audio_buf);
	return 0;	
}//end of main()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值