Mercurial > mplayer.hg
comparison libao2/ao_macosx.c @ 14769:e701873aa8ca
rids ao_macosx of the buffer mutex by using the same buffering scheme as ao_sdl - Patch by Reimar Doffinger
author | nplourde |
---|---|
date | Tue, 22 Feb 2005 20:54:31 +0000 |
parents | 38572280e8e7 |
children | 21f44596f356 |
comparison
equal
deleted
inserted
replaced
14768:662a391e4901 | 14769:e701873aa8ca |
---|---|
72 /* CoreAudio */ | 72 /* CoreAudio */ |
73 AudioDeviceID outputDeviceID; | 73 AudioDeviceID outputDeviceID; |
74 AudioStreamBasicDescription outputStreamBasicDescription; | 74 AudioStreamBasicDescription outputStreamBasicDescription; |
75 | 75 |
76 /* Ring-buffer */ | 76 /* Ring-buffer */ |
77 pthread_mutex_t buffer_mutex; /* mutex covering buffer variables */ | 77 /* does not need explicit synchronization, but needs to allocate |
78 | 78 * (num_chunks + 1) * chunk_size memory to store num_chunks * chunk_size |
79 unsigned char *buffer[NUM_BUFS]; | 79 * data */ |
80 unsigned int buffer_len; | 80 unsigned char *buffer; |
81 unsigned int buffer_len; ///< must always be (num_chunks + 1) * chunk_size | |
82 unsigned int num_chunks; | |
83 unsigned int chunk_size; | |
81 | 84 |
82 unsigned int buf_read; | |
83 unsigned int buf_write; | |
84 unsigned int buf_read_pos; | 85 unsigned int buf_read_pos; |
85 unsigned int buf_write_pos; | 86 unsigned int buf_write_pos; |
86 int full_buffers; | |
87 int buffered_bytes; | |
88 } ao_macosx_t; | 87 } ao_macosx_t; |
89 | 88 |
90 static ao_macosx_t *ao; | 89 static ao_macosx_t *ao; |
91 | 90 |
92 /* General purpose Ring-buffering routines */ | 91 /** |
93 static int write_buffer(unsigned char* data,int len){ | 92 * \brief return number of free bytes in the buffer |
94 int len2=0; | 93 * may only be called by mplayer's thread |
95 int x; | 94 * \return minimum number of free bytes in buffer, value may change between |
96 | 95 * two immediately following calls, and the real number of free bytes |
97 while(len>0){ | 96 * might actually be larger! |
98 if(ao->full_buffers==NUM_BUFS) { | 97 */ |
99 ao_msg(MSGT_AO,MSGL_V, "Buffer overrun\n"); | 98 static int buf_free() { |
100 break; | 99 int free = ao->buf_read_pos - ao->buf_write_pos - ao->chunk_size; |
101 } | 100 if (free < 0) free += ao->buffer_len; |
102 | 101 return free; |
103 x=ao->buffer_len-ao->buf_write_pos; | 102 } |
104 if(x>len) x=len; | 103 |
105 memcpy(ao->buffer[ao->buf_write]+ao->buf_write_pos,data+len2,x); | 104 /** |
106 | 105 * \brief return number of buffered bytes |
107 /* accessing common variables, locking mutex */ | 106 * may only be called by playback thread |
108 pthread_mutex_lock(&ao->buffer_mutex); | 107 * \return minimum number of buffered bytes, value may change between |
109 len2+=x; len-=x; | 108 * two immediately following calls, and the real number of buffered bytes |
110 ao->buffered_bytes+=x; ao->buf_write_pos+=x; | 109 * might actually be larger! |
111 if(ao->buf_write_pos>=ao->buffer_len) { | 110 */ |
112 /* block is full, find next! */ | 111 static int buf_used() { |
113 ao->buf_write=(ao->buf_write+1)%NUM_BUFS; | 112 int used = ao->buf_write_pos - ao->buf_read_pos; |
114 ++ao->full_buffers; | 113 if (used < 0) used += ao->buffer_len; |
115 ao->buf_write_pos=0; | 114 return used; |
116 } | 115 } |
117 pthread_mutex_unlock(&ao->buffer_mutex); | 116 |
117 /** | |
118 * \brief add data to ringbuffer | |
119 */ | |
120 static int write_buffer(unsigned char* data, int len){ | |
121 int first_len = ao->buffer_len - ao->buf_write_pos; | |
122 int free = buf_free(); | |
123 if (len > free) len = free; | |
124 if (first_len > len) first_len = len; | |
125 // till end of buffer | |
126 memcpy (&ao->buffer[ao->buf_write_pos], data, first_len); | |
127 if (len > first_len) { // we have to wrap around | |
128 // remaining part from beginning of buffer | |
129 memcpy (ao->buffer, &data[first_len], len - first_len); | |
118 } | 130 } |
119 | 131 ao->buf_write_pos = (ao->buf_write_pos + len) % ao->buffer_len; |
120 return len2; | 132 return len; |
121 } | 133 } |
122 | 134 |
135 /** | |
136 * \brief remove data from ringbuffer | |
137 */ | |
123 static int read_buffer(unsigned char* data,int len){ | 138 static int read_buffer(unsigned char* data,int len){ |
124 int len2=0; | 139 int first_len = ao->buffer_len - ao->buf_read_pos; |
125 int x; | 140 int buffered = buf_used(); |
126 | 141 if (len > buffered) len = buffered; |
127 while(len>0){ | 142 if (first_len > len) first_len = len; |
128 if(ao->full_buffers==0) { | 143 // till end of buffer |
129 ao_msg(MSGT_AO,MSGL_V, "Buffer underrun\n"); | 144 memcpy (data, &ao->buffer[ao->buf_read_pos], first_len); |
130 break; | 145 if (len > first_len) { // we have to wrap around |
131 } | 146 // remaining part from beginning of buffer |
132 | 147 memcpy (&data[first_len], ao->buffer, len - first_len); |
133 x=ao->buffer_len-ao->buf_read_pos; | |
134 if(x>len) x=len; | |
135 memcpy(data+len2,ao->buffer[ao->buf_read]+ao->buf_read_pos,x); | |
136 len2+=x; len-=x; | |
137 | |
138 /* accessing common variables, locking mutex */ | |
139 pthread_mutex_lock(&ao->buffer_mutex); | |
140 ao->buffered_bytes-=x; ao->buf_read_pos+=x; | |
141 if(ao->buf_read_pos>=ao->buffer_len){ | |
142 /* block is empty, find next! */ | |
143 ao->buf_read=(ao->buf_read+1)%NUM_BUFS; | |
144 --ao->full_buffers; | |
145 ao->buf_read_pos=0; | |
146 } | |
147 pthread_mutex_unlock(&ao->buffer_mutex); | |
148 } | 148 } |
149 | 149 ao->buf_read_pos = (ao->buf_read_pos + len) % ao->buffer_len; |
150 | 150 return len; |
151 return len2; | |
152 } | 151 } |
153 | 152 |
154 /* end ring buffer stuff */ | 153 /* end ring buffer stuff */ |
155 | 154 |
156 /* The function that the CoreAudio thread calls when it wants more data */ | 155 /* The function that the CoreAudio thread calls when it wants more data */ |
157 static OSStatus audioDeviceIOProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inClientData) | 156 static OSStatus audioDeviceIOProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inClientData) |
158 { | 157 { |
159 outOutputData->mBuffers[0].mDataByteSize = | 158 outOutputData->mBuffers[0].mDataByteSize = |
160 read_buffer((char *)outOutputData->mBuffers[0].mData, ao->buffer_len); | 159 read_buffer((char *)outOutputData->mBuffers[0].mData, ao->chunk_size); |
161 | 160 |
162 return 0; | 161 return 0; |
163 } | 162 } |
164 | 163 |
165 | 164 |
244 int rc; | 243 int rc; |
245 int i; | 244 int i; |
246 | 245 |
247 ao = (ao_macosx_t *)malloc(sizeof(ao_macosx_t)); | 246 ao = (ao_macosx_t *)malloc(sizeof(ao_macosx_t)); |
248 | 247 |
249 /* initialise mutex */ | |
250 pthread_mutex_init(&ao->buffer_mutex, NULL); | |
251 pthread_mutex_unlock(&ao->buffer_mutex); | |
252 | |
253 /* get default output device */ | 248 /* get default output device */ |
254 propertySize = sizeof(ao->outputDeviceID); | 249 propertySize = sizeof(ao->outputDeviceID); |
255 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &(ao->outputDeviceID)); | 250 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &(ao->outputDeviceID)); |
256 if (status) { | 251 if (status) { |
257 ao_msg(MSGT_AO,MSGL_WARN, | 252 ao_msg(MSGT_AO,MSGL_WARN, |
376 status = AudioDeviceGetProperty(ao->outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &ao->outputStreamBasicDescription); | 371 status = AudioDeviceGetProperty(ao->outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &ao->outputStreamBasicDescription); |
377 print_format("final: ",&ao->outputStreamBasicDescription); | 372 print_format("final: ",&ao->outputStreamBasicDescription); |
378 | 373 |
379 /* get requested buffer length */ | 374 /* get requested buffer length */ |
380 // TODO: set NUM_BUFS dinamically, based on buffer size! | 375 // TODO: set NUM_BUFS dinamically, based on buffer size! |
381 propertySize = sizeof(ao->buffer_len); | 376 propertySize = sizeof(ao->chunk_size); |
382 status = AudioDeviceGetProperty(ao->outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &ao->buffer_len); | 377 status = AudioDeviceGetProperty(ao->outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &ao->chunk_size); |
383 if (status) { | 378 if (status) { |
384 ao_msg(MSGT_AO,MSGL_WARN, "AudioDeviceGetProperty returned %d when getting kAudioDevicePropertyBufferSize\n", (int)status); | 379 ao_msg(MSGT_AO,MSGL_WARN, "AudioDeviceGetProperty returned %d when getting kAudioDevicePropertyBufferSize\n", (int)status); |
385 return CONTROL_FALSE; | 380 return CONTROL_FALSE; |
386 } | 381 } |
387 ao_msg(MSGT_AO,MSGL_V, "%5d ao->buffer_len\n", (int)ao->buffer_len); | 382 ao_msg(MSGT_AO,MSGL_V, "%5d chunk size\n", (int)ao->chunk_size); |
388 | 383 |
389 ao_data.samplerate = ao->outputStreamBasicDescription.mSampleRate; | 384 ao_data.samplerate = ao->outputStreamBasicDescription.mSampleRate; |
390 ao_data.channels = channels; | 385 ao_data.channels = channels; |
391 ao_data.outburst = ao_data.buffersize = ao->buffer_len; | 386 ao_data.outburst = ao_data.buffersize = ao->chunk_size; |
392 ao_data.bps = | 387 ao_data.bps = |
393 ao_data.samplerate * ao->outputStreamBasicDescription.mBytesPerFrame; | 388 ao_data.samplerate * ao->outputStreamBasicDescription.mBytesPerFrame; |
394 | 389 |
395 if (ao->outputStreamBasicDescription.mFormatID == kAudioFormatLinearPCM) { | 390 if (ao->outputStreamBasicDescription.mFormatID == kAudioFormatLinearPCM) { |
396 uint32_t flags = ao->outputStreamBasicDescription.mFormatFlags; | 391 uint32_t flags = ao->outputStreamBasicDescription.mFormatFlags; |
408 "support Linear PCM!\n"); | 403 "support Linear PCM!\n"); |
409 return CONTROL_FALSE; | 404 return CONTROL_FALSE; |
410 } | 405 } |
411 | 406 |
412 /* Allocate ring-buffer memory */ | 407 /* Allocate ring-buffer memory */ |
413 for(i=0;i<NUM_BUFS;i++) | 408 ao->num_chunks = NUM_BUFS; |
414 ao->buffer[i]=(unsigned char *) malloc(ao->buffer_len); | 409 ao->buffer_len = (ao->num_chunks + 1) * ao->chunk_size; |
410 ao->buffer = (unsigned char *)malloc(ao->buffer_len); | |
415 | 411 |
416 | 412 |
417 /* Prepare for playback */ | 413 /* Prepare for playback */ |
418 | 414 |
419 reset(); | |
420 | |
421 /* Set the IO proc that CoreAudio will call when it needs data */ | 415 /* Set the IO proc that CoreAudio will call when it needs data */ |
422 status = AudioDeviceAddIOProc(ao->outputDeviceID, audioDeviceIOProc, NULL); | 416 status = AudioDeviceAddIOProc(ao->outputDeviceID, audioDeviceIOProc, NULL); |
423 if (status) { | 417 if (status) { |
424 ao_msg(MSGT_AO,MSGL_WARN, "AudioDeviceAddIOProc returned %d\n", (int)status); | 418 ao_msg(MSGT_AO,MSGL_WARN, "AudioDeviceAddIOProc returned %d\n", (int)status); |
425 return CONTROL_FALSE; | 419 return CONTROL_FALSE; |
426 } | 420 } |
427 | 421 |
428 /* Start callback */ | 422 /* Start callback */ |
429 status = AudioDeviceStart(ao->outputDeviceID, audioDeviceIOProc); | 423 reset(); |
430 if (status) { | |
431 ao_msg(MSGT_AO,MSGL_WARN, "AudioDeviceStart returned %d\n", | |
432 (int)status); | |
433 return CONTROL_FALSE; | |
434 } | |
435 | 424 |
436 return CONTROL_OK; | 425 return CONTROL_OK; |
437 } | 426 } |
438 | 427 |
439 | 428 |
443 } | 432 } |
444 | 433 |
445 /* set variables and buffer to initial state */ | 434 /* set variables and buffer to initial state */ |
446 static void reset() | 435 static void reset() |
447 { | 436 { |
448 int i; | 437 audio_pause(); |
449 | |
450 pthread_mutex_lock(&ao->buffer_mutex); | |
451 | |
452 /* reset ring-buffer state */ | 438 /* reset ring-buffer state */ |
453 ao->buf_read=0; | |
454 ao->buf_write=0; | |
455 ao->buf_read_pos=0; | 439 ao->buf_read_pos=0; |
456 ao->buf_write_pos=0; | 440 ao->buf_write_pos=0; |
441 audio_resume(); | |
457 | 442 |
458 ao->full_buffers=0; | |
459 ao->buffered_bytes=0; | |
460 | |
461 /* zero output buffer */ | |
462 for (i = 0; i < NUM_BUFS; i++) | |
463 memset(ao->buffer[i], 0, ao->buffer_len); | |
464 | |
465 pthread_mutex_unlock(&ao->buffer_mutex); | |
466 | |
467 return; | 443 return; |
468 } | 444 } |
469 | 445 |
470 | 446 |
471 /* return available space */ | 447 /* return available space */ |
472 static int get_space() | 448 static int get_space() |
473 { | 449 { |
474 return (NUM_BUFS-ao->full_buffers)*ao_data.buffersize - ao->buf_write_pos; | 450 return buf_free(); |
475 } | 451 } |
476 | 452 |
477 | 453 |
478 /* return delay until audio is played */ | 454 /* return delay until audio is played */ |
479 static float get_delay() | 455 static float get_delay() |
480 { | 456 { |
481 return (float)(ao->buffered_bytes)/(float)ao_data.bps; | 457 int buffered = ao->buffer_len - ao->chunk_size - buf_free(); // could be less |
458 // inaccurate, should also contain the data buffered e.g. by the OS | |
459 return (float)(buffered)/(float)ao_data.bps; | |
482 } | 460 } |
483 | 461 |
484 | 462 |
485 /* unload plugin and deregister from coreaudio */ | 463 /* unload plugin and deregister from coreaudio */ |
486 static void uninit(int immed) | 464 static void uninit(int immed) |
493 status = AudioDeviceRemoveIOProc(ao->outputDeviceID, audioDeviceIOProc); | 471 status = AudioDeviceRemoveIOProc(ao->outputDeviceID, audioDeviceIOProc); |
494 if (status) | 472 if (status) |
495 ao_msg(MSGT_AO,MSGL_WARN, "AudioDeviceRemoveIOProc " | 473 ao_msg(MSGT_AO,MSGL_WARN, "AudioDeviceRemoveIOProc " |
496 "returned %d\n", (int)status); | 474 "returned %d\n", (int)status); |
497 | 475 |
498 for(i=0;i<NUM_BUFS;i++) free(ao->buffer[i]); | 476 free(ao->buffer); |
499 free(ao); | 477 free(ao); |
500 } | 478 } |
501 | 479 |
502 | 480 |
503 /* stop playing, keep buffers (for pause) */ | 481 /* stop playing, keep buffers (for pause) */ |