ao_nas.c

来自「君正早期ucos系统(只有早期的才不没有打包成库),MPLAYER,文件系统,图」· C语言 代码 · 共 614 行 · 第 1/2 页

C
614
字号
/* * NAS output plugin for mplayer * * based on the libaudiooss parts rewritten by me, which were * originally based on the NAS output plugin for xmms. * * xmms plugin by Willem Monsuwe * adapted for libaudiooss by Jon Trulson * further modified by Erik Inge Bolsø * largely rewritten and used for this * plugin by Tobias Diedrich * * Theory of operation: * * The NAS consists of two parts, a server daemon and a client. * We setup the server to use a buffer of size bytes_per_second * with a low watermark of buffer_size - NAS_FRAG_SIZE. * Upon starting the flow the server will generate a buffer underrun * event and the event handler will fill the buffer for the first time. * Now the server will generate a lowwater event when the server buffer * falls below the low watermark value. The event handler gets called * again and refills the buffer by the number of bytes requested by the * server (usually a multiple of 4096). To prevent stuttering on * startup (start of playing, seeks, unpausing) the client buffer should * be bigger than the server buffer. (For debugging we also do some * accounting of what we think how much of the server buffer is filled) */#include <mplaylib.h>#include "mplaylib.h"#include <pthread.h>#include <audio/audiolib.h>#include "config.h"#include "mp_msg.h"#include "audio_out.h"#include "audio_out_internal.h"#include "libaf/af_format.h"#define NAS_FRAG_SIZE 4096static char *nas_event_types[] = {	"Undefined",	"Undefined",	"ElementNotify",	"GrabNotify",	"MonitorNotify",	"BucketNotify",	"DeviceNotify"};static char *nas_elementnotify_kinds[] = {	"LowWater",	"HighWater",	"State",	"Unknown"};static char *nas_states[] = {	"Stop",	"Start",	"Pause",	"Any"};static char *nas_reasons[] = {	"User",	"Underrun",	"Overrun",	"EOF",	"Watermark",	"Hardware",	"Any"};static char* nas_reason(unsigned int reason){	if (reason > 6) reason = 6;	return nas_reasons[reason];}static char* nas_elementnotify_kind(unsigned int kind){	if (kind > 2) kind = 3;	return nas_elementnotify_kinds[kind];}static char* nas_event_type(unsigned int type) {	if (type > 6) type = 0;	return nas_event_types[type];}static char* nas_state(unsigned int state) {	if (state>3) state = 3;	return nas_states[state];}static ao_info_t info = {	"NAS audio output",	"nas",	"Tobias Diedrich <ranma+mplayer@tdiedrich.de>",	""};struct ao_nas_data {	AuServer	*aud;	AuFlowID	flow;	AuDeviceID	dev;	AuFixedPoint	gain;	unsigned int state;	int expect_underrun;	void *client_buffer;	void *server_buffer;	unsigned int client_buffer_size;	unsigned int client_buffer_used;	unsigned int server_buffer_size;	unsigned int server_buffer_used;	pthread_mutex_t buffer_mutex;	pthread_t event_thread;	int stop_thread;};static struct ao_nas_data *nas_data;LIBAO_EXTERN(nas)static void nas_print_error(AuServer *aud, const char *prefix, AuStatus as){	char s[100];	AuGetErrorText(aud, as, s, 100);	mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: %s: returned status %d (%s)\n", prefix, as, s);}static int nas_readBuffer(struct ao_nas_data *nas_data, int num){	AuStatus as;	pthread_mutex_lock(&nas_data->buffer_mutex);	mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: nas_readBuffer(): num=%d client=%d/%d server=%d/%d\n",			num,			nas_data->client_buffer_used, nas_data->client_buffer_size,			nas_data->server_buffer_used, nas_data->server_buffer_size);	if (nas_data->client_buffer_used == 0) {		mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: buffer is empty, nothing read.\n");		pthread_mutex_unlock(&nas_data->buffer_mutex);		return 0;	}	if (num > nas_data->client_buffer_used)		num = nas_data->client_buffer_used;	/*	 * It is not appropriate to call AuWriteElement() here because the	 * buffer is locked and delays writing to the network will cause	 * other threads to block waiting for buffer_mutex.  Instead the	 * data is copied to "server_buffer" and written to the network	 * outside of the locked section of code.	 *	 * (Note: Rather than these two buffers, a single circular buffer	 *  could eliminate the memcpy/memmove steps.)	 */	/* make sure we don't overflow the buffer */	if (num > nas_data->server_buffer_size)		num = nas_data->server_buffer_size;	memcpy(nas_data->server_buffer, nas_data->client_buffer, num);	nas_data->client_buffer_used -= num;	nas_data->server_buffer_used += num;	memmove(nas_data->client_buffer, nas_data->client_buffer + num, nas_data->client_buffer_used);	pthread_mutex_unlock(&nas_data->buffer_mutex);	/*	 * Now write the new buffer to the network.	 */	AuWriteElement(nas_data->aud, nas_data->flow, 0, num, nas_data->server_buffer, AuFalse, &as);	if (as != AuSuccess) 		nas_print_error(nas_data->aud, "nas_readBuffer(): AuWriteElement", as);	return num;}static int nas_writeBuffer(struct ao_nas_data *nas_data, void *data, int len){	pthread_mutex_lock(&nas_data->buffer_mutex);	mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: nas_writeBuffer(): len=%d client=%d/%d server=%d/%d\n",			len, nas_data->client_buffer_used, nas_data->client_buffer_size,			nas_data->server_buffer_used, nas_data->server_buffer_size);	/* make sure we don't overflow the buffer */	if (len > nas_data->client_buffer_size - nas_data->client_buffer_used)		len = nas_data->client_buffer_size - nas_data->client_buffer_used;	memcpy(nas_data->client_buffer + nas_data->client_buffer_used, data, len);	nas_data->client_buffer_used += len;	pthread_mutex_unlock(&nas_data->buffer_mutex);	return len;}static int nas_empty_event_queue(struct ao_nas_data *nas_data){	AuEvent ev;	int result = 0;		while (AuScanForTypedEvent(nas_data->aud, AuEventsQueuedAfterFlush,				   AuTrue, AuEventTypeElementNotify, &ev)) {		AuDispatchEvent(nas_data->aud, &ev);		result = 1;	}	return result;}static void *nas_event_thread_start(void *data){	struct ao_nas_data *nas_data = data;	do {		mp_msg(MSGT_AO, MSGL_DBG2,		       "ao_nas: event thread heartbeat (state=%s)\n",		       nas_state(nas_data->state));		nas_empty_event_queue(nas_data);		usleep(1000);	} while (!nas_data->stop_thread);	return NULL;}static AuBool nas_error_handler(AuServer* aud, AuErrorEvent* ev){	char s[100];	AuGetErrorText(aud, ev->error_code, s, 100);	mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: error [%s]\n"		"error_code: %d\n"		"request_code: %d\n"		"minor_code: %d\n",		s,		ev->error_code,		ev->request_code,		ev->minor_code);	return AuTrue;}static AuBool nas_event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd){	AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;	struct ao_nas_data *nas_data = hnd->data;	mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: event_handler(): type %s kind %s state %s->%s reason %s numbytes %d expect_underrun %d\n",		nas_event_type(event->type),		nas_elementnotify_kind(event->kind),		nas_state(event->prev_state),		nas_state(event->cur_state),		nas_reason(event->reason),		event->num_bytes,		nas_data->expect_underrun);	if (event->num_bytes > INT_MAX) {		mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: num_bytes > 2GB, server buggy?\n");	}	if (event->num_bytes > nas_data->server_buffer_used)		event->num_bytes = nas_data->server_buffer_used;	nas_data->server_buffer_used -= event->num_bytes;	switch (event->reason) {	case AuReasonWatermark:		nas_readBuffer(nas_data, event->num_bytes);		break;	case AuReasonUnderrun:		// buffer underrun -> refill buffer		nas_data->server_buffer_used = 0;		if (nas_data->expect_underrun) {			nas_data->expect_underrun = 0;		} else {			static int hint = 1;			mp_msg(MSGT_AO, MSGL_WARN,			       "ao_nas: Buffer underrun.\n");			if (hint) {				hint = 0;				mp_msg(MSGT_AO, MSGL_HINT,				       "Possible reasons are:\n"				       "1) Network congestion.\n"				       "2) Your NAS server is too slow.\n"				       "Try renicing your nasd to e.g. -15.\n");			}		}		if (nas_readBuffer(nas_data,		                   nas_data->server_buffer_size -		                   nas_data->server_buffer_used) != 0) {			event->cur_state = AuStateStart;			break;		}		mp_msg(MSGT_AO, MSGL_DBG2,			"ao_nas: Can't refill buffer, stopping flow.\n");		AuStopFlow(nas_data->aud, nas_data->flow, NULL);		break;	default:		break;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?