demux: mp4: avoid audio cuts on seek
[vlc.git] / modules / audio_output / TPCircularBuffer.c
blob1f7f05fa8c8b9acd735ddf6222927fc385208dfd
1 //
2 // TPCircularBuffer.c
3 // Circular/Ring buffer implementation
4 //
5 // Created by Michael Tyson on 10/12/2011.
6 // Copyright 2011-2012 A Tasty Pixel. All rights reserved.
9 #include "audio_output/TPCircularBuffer.h"
10 #include <mach/mach.h>
11 #include <stdio.h>
13 #define reportResult(result,operation) (_reportResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__))
14 static inline bool _reportResult(kern_return_t result, const char *operation, const char* file, int line) {
15 if ( result != ERR_SUCCESS ) {
16 printf("%s:%d: %s: %s\n", file, line, operation, mach_error_string(result));
17 return false;
19 return true;
22 bool TPCircularBufferInit(TPCircularBuffer *buffer, int length) {
24 // Keep trying until we get our buffer, needed to handle race conditions
25 int retries = 3;
26 while ( true ) {
28 buffer->length = round_page(length); // We need whole page sizes
30 // Temporarily allocate twice the length, so we have the contiguous address space to
31 // support a second instance of the buffer directly after
32 vm_address_t bufferAddress;
33 kern_return_t result = vm_allocate(mach_task_self(),
34 &bufferAddress,
35 buffer->length * 2,
36 VM_FLAGS_ANYWHERE); // allocate anywhere it'll fit
37 if ( result != ERR_SUCCESS ) {
38 if ( retries-- == 0 ) {
39 reportResult(result, "Buffer allocation");
40 return false;
42 // Try again if we fail
43 continue;
46 // Now replace the second half of the allocation with a virtual copy of the first half. Deallocate the second half...
47 result = vm_deallocate(mach_task_self(),
48 bufferAddress + buffer->length,
49 buffer->length);
50 if ( result != ERR_SUCCESS ) {
51 if ( retries-- == 0 ) {
52 reportResult(result, "Buffer deallocation");
53 return false;
55 // If this fails somehow, deallocate the whole region and try again
56 vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
57 continue;
60 // Re-map the buffer to the address space immediately after the buffer
61 vm_address_t virtualAddress = bufferAddress + buffer->length;
62 vm_prot_t cur_prot, max_prot;
63 result = vm_remap(mach_task_self(),
64 &virtualAddress, // mirror target
65 buffer->length, // size of mirror
66 0, // auto alignment
67 0, // force remapping to virtualAddress
68 mach_task_self(), // same task
69 bufferAddress, // mirror source
70 0, // MAP READ-WRITE, NOT COPY
71 &cur_prot, // unused protection struct
72 &max_prot, // unused protection struct
73 VM_INHERIT_DEFAULT);
74 if ( result != ERR_SUCCESS ) {
75 if ( retries-- == 0 ) {
76 reportResult(result, "Remap buffer memory");
77 return false;
79 // If this remap failed, we hit a race condition, so deallocate and try again
80 vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
81 continue;
84 if ( virtualAddress != bufferAddress+buffer->length ) {
85 // If the memory is not contiguous, clean up both allocated buffers and try again
86 if ( retries-- == 0 ) {
87 printf("Couldn't map buffer memory to end of buffer\n");
88 return false;
91 vm_deallocate(mach_task_self(), virtualAddress, buffer->length);
92 vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
93 continue;
96 buffer->buffer = (void*)bufferAddress;
97 buffer->fillCount = 0;
98 buffer->head = buffer->tail = 0;
100 return true;
102 return false;
105 void TPCircularBufferCleanup(TPCircularBuffer *buffer) {
106 vm_deallocate(mach_task_self(), (vm_address_t)buffer->buffer, buffer->length * 2);
107 memset(buffer, 0, sizeof(TPCircularBuffer));
110 void TPCircularBufferClear(TPCircularBuffer *buffer) {
111 int32_t fillCount;
112 if ( TPCircularBufferTail(buffer, &fillCount) ) {
113 TPCircularBufferConsume(buffer, fillCount);