1 /*
2  * Copyright (C) 2000-2019 the xine project
3  *
4  * This file is part of xine, a free video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  *
21  * contents:
22  *
23  * buffer_entry structure - serves as a transport encapsulation
24  *   of the mpeg audio/video data through xine
25  *
26  * free buffer pool management routines
27  *
28  * FIFO buffer structures/routines
29  */
30 
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <pthread.h>
38 
39 /********** logging **********/
40 #define LOG_MODULE "buffer"
41 #define LOG_VERBOSE
42 /*
43 #define LOG
44 */
45 
46 #include <xine/buffer.h>
47 #include <xine/xineutils.h>
48 #include <xine/xine_internal.h>
49 
50 /* The large buffer feature.
51  * If we have enough contigous memory, and if we can afford to hand if out,
52  * provide an oversize item there. The buffers covering that extra space will
53  * hide inside our buffer array, and buffer_pool_free () will reappear them
54  * mysteriously later.
55  * Small bufs are requested frequently, so we dont do a straightforward
56  * heap manager. Instead, we keep bufs in pool sorted by address, and
57  * be_ei_t.nbufs holds the count of contigous bufs when this is the
58  * first of such a group.
59  * API permits using bufs in a different fifo than their pool origin
60  * (see demux_mpeg_block). Thats why we test buf->source.
61  * It is also possible to supply fully custom bufs. We detect these by
62  * buf->free_buffer != buffer_pool_free.
63  */
64 
65 typedef struct {
66   buf_element_t elem; /* needs to be first */
67   int nbufs;          /* # of contigous bufs */
68   extra_info_t  ei;
69 } be_ei_t;
70 
71 #define LARGE_NUM 0x7fffffff
72 
73 /*
74  * put a previously allocated buffer element back into the buffer pool
75  */
buffer_pool_free(buf_element_t * element)76 static void buffer_pool_free (buf_element_t *element) {
77   fifo_buffer_t *this = (fifo_buffer_t *) element->source;
78   be_ei_t *newhead, *newtail, *nexthead;
79   int n;
80 
81   pthread_mutex_lock (&this->buffer_pool_mutex);
82 
83   newhead = (be_ei_t *)element;
84   n = newhead->nbufs;
85   this->buffer_pool_num_free += n;
86   if (this->buffer_pool_num_free > this->buffer_pool_capacity) {
87     fprintf(stderr, _("xine-lib: buffer.c: There has been a fatal error: TOO MANY FREE's\n"));
88     _x_abort();
89   }
90 
91   /* we might be a new chunk */
92   newtail = newhead + 1;
93   while (--n > 0) {
94     newtail[-1].elem.next = &newtail[0].elem;
95     newtail++;
96   }
97 
98   nexthead = (be_ei_t *)this->buffer_pool_top;
99   if (!nexthead || (nexthead >= newtail)) {
100     /* add head */
101     this->buffer_pool_top = &newhead->elem;
102     newtail[-1].elem.next = &nexthead->elem;
103     /* merge with next chunk if no gap */
104     if (newtail == nexthead)
105       newhead->nbufs += nexthead->nbufs;
106   } else {
107     /* Keep the pool sorted, elem1 > elem2 implies elem1->mem > elem2->mem. */
108     be_ei_t *prevhead, *prevtail;
109     while (1) {
110       prevhead = nexthead;
111       prevtail = prevhead + prevhead->nbufs;
112       nexthead = (be_ei_t *)prevtail[-1].elem.next;
113       if (!nexthead || (nexthead >= newtail))
114         break;
115     }
116     prevtail[-1].elem.next = &newhead->elem;
117     newtail[-1].elem.next = &nexthead->elem;
118     /* merge with next chunk if no gap */
119     if (newtail == nexthead)
120       newhead->nbufs += nexthead->nbufs;
121     /* merge with prev chunk if no gap */
122     if (prevtail == newhead)
123       prevhead->nbufs += newhead->nbufs;
124   }
125 
126   /* dont provoke useless wakeups */
127   if (this->buffer_pool_num_waiters ||
128     (this->buffer_pool_large_wait <= this->buffer_pool_num_free))
129     pthread_cond_signal (&this->buffer_pool_cond_not_empty);
130 
131   pthread_mutex_unlock (&this->buffer_pool_mutex);
132 }
133 
134 /*
135  * allocate a buffer from buffer pool
136  */
137 
buffer_pool_size_alloc_int(fifo_buffer_t * this,int n)138 static buf_element_t *buffer_pool_size_alloc_int (fifo_buffer_t *this, int n) {
139 
140   int i;
141   be_ei_t *buf;
142 
143   for (i = 0; this->alloc_cb[i]; i++)
144     this->alloc_cb[i] (this, this->alloc_cb_data[i]);
145 
146   if (n < 1)
147     n = 1;
148   /* we always keep one free buffer for emergency situations like
149    * decoder flushes that would need a buffer in buffer_pool_try_alloc() */
150   n += 2;
151   if (this->buffer_pool_num_free < n) {
152     /* Paranoia: someone else than demux calling this in parallel ?? */
153     if (this->buffer_pool_large_wait != LARGE_NUM) {
154       this->buffer_pool_num_waiters++;
155       do {
156         pthread_cond_wait (&this->buffer_pool_cond_not_empty, &this->buffer_pool_mutex);
157       } while (this->buffer_pool_num_free < n);
158       this->buffer_pool_num_waiters--;
159     } else {
160       this->buffer_pool_large_wait = n;
161       do {
162         pthread_cond_wait (&this->buffer_pool_cond_not_empty, &this->buffer_pool_mutex);
163       } while (this->buffer_pool_num_free < n);
164       this->buffer_pool_large_wait = LARGE_NUM;
165     }
166   }
167   n -= 2;
168 
169   buf = (be_ei_t *)this->buffer_pool_top;
170   if (n == 1) {
171 
172     this->buffer_pool_top = buf->elem.next;
173     i = buf->nbufs - 1;
174     if (i > 0)
175       buf[1].nbufs = i;
176     this->buffer_pool_num_free--;
177 
178   } else {
179 
180     buf_element_t **link = &this->buffer_pool_top, **bestlink = link;
181     int bestsize = 0;
182     while (1) {
183       int l = buf->nbufs;
184       if (l > n) {
185         be_ei_t *next = buf + n;
186         next->nbufs = l - n;
187         *link = &next->elem;
188         break;
189       } else if (l == n) {
190         *link = buf[l - 1].elem.next;
191         break;
192       }
193       if (l > bestsize) {
194         bestsize = l;
195         bestlink = link;
196       }
197       buf += l - 1;
198       link = &buf->elem.next;
199       buf = (be_ei_t *)(*link);
200       if (!buf) {
201         buf = (be_ei_t *)(*bestlink);
202         n = bestsize;
203         *bestlink = buf[n - 1].elem.next;
204         break;
205       }
206     }
207     this->buffer_pool_num_free -= n;
208 
209   }
210 
211   pthread_mutex_unlock (&this->buffer_pool_mutex);
212 
213   /* set sane values to the newly allocated buffer */
214   buf->elem.content = buf->elem.mem; /* 99% of demuxers will want this */
215   buf->elem.pts = 0;
216   buf->elem.size = 0;
217   buf->elem.max_size = n * this->buffer_pool_buf_size;
218   buf->elem.decoder_flags = 0;
219   buf->nbufs = n;
220   memset (buf->elem.decoder_info, 0, sizeof (buf->elem.decoder_info));
221   memset (buf->elem.decoder_info_ptr, 0, sizeof (buf->elem.decoder_info_ptr));
222   _x_extra_info_reset (buf->elem.extra_info);
223 
224   return &buf->elem;
225 }
226 
buffer_pool_size_alloc(fifo_buffer_t * this,size_t size)227 static buf_element_t *buffer_pool_size_alloc (fifo_buffer_t *this, size_t size) {
228   int n = size ? ((int)size + this->buffer_pool_buf_size - 1) / this->buffer_pool_buf_size : 1;
229   if (n > (this->buffer_pool_capacity >> 2))
230     n = this->buffer_pool_capacity >> 2;
231   pthread_mutex_lock (&this->buffer_pool_mutex);
232   return buffer_pool_size_alloc_int (this, n);
233 }
234 
235 
buffer_pool_alloc(fifo_buffer_t * this)236 static buf_element_t *buffer_pool_alloc (fifo_buffer_t *this) {
237   be_ei_t *buf;
238   int i;
239 
240   pthread_mutex_lock (&this->buffer_pool_mutex);
241 
242   for(i = 0; this->alloc_cb[i]; i++)
243     this->alloc_cb[i](this, this->alloc_cb_data[i]);
244 
245   /* we always keep one free buffer for emergency situations like
246    * decoder flushes that would need a buffer in buffer_pool_try_alloc() */
247   if (this->buffer_pool_num_free < 2) {
248     this->buffer_pool_num_waiters++;
249     do {
250       pthread_cond_wait (&this->buffer_pool_cond_not_empty, &this->buffer_pool_mutex);
251     } while (this->buffer_pool_num_free < 2);
252     this->buffer_pool_num_waiters--;
253   }
254 
255   buf = (be_ei_t *)this->buffer_pool_top;
256   this->buffer_pool_top = buf->elem.next;
257   i = buf->nbufs - 1;
258   if (i > 0)
259     buf[1].nbufs = i;
260   this->buffer_pool_num_free--;
261 
262   pthread_mutex_unlock (&this->buffer_pool_mutex);
263 
264   /* set sane values to the newly allocated buffer */
265   buf->elem.content = buf->elem.mem; /* 99% of demuxers will want this */
266   buf->elem.pts = 0;
267   buf->elem.size = 0;
268   buf->elem.max_size = this->buffer_pool_buf_size;
269   buf->elem.decoder_flags = 0;
270   buf->nbufs = 1;
271   memset (buf->elem.decoder_info, 0, sizeof (buf->elem.decoder_info));
272   memset (buf->elem.decoder_info_ptr, 0, sizeof (buf->elem.decoder_info_ptr));
273   _x_extra_info_reset (buf->elem.extra_info);
274 
275   return &buf->elem;
276 }
277 
buffer_pool_realloc(buf_element_t * buf,size_t new_size)278 static buf_element_t *buffer_pool_realloc (buf_element_t *buf, size_t new_size) {
279   fifo_buffer_t *this;
280   buf_element_t **last_buf;
281   be_ei_t *old_buf = (be_ei_t *)buf, *new_buf, *want_buf;
282   int n;
283 
284   if (!old_buf)
285     return NULL;
286   if ((int)new_size <= old_buf->elem.max_size)
287     return NULL;
288   if (old_buf->elem.free_buffer != buffer_pool_free)
289     return NULL;
290   this = (fifo_buffer_t *)old_buf->elem.source;
291   if (!this)
292     return NULL;
293 
294   n = ((int)new_size + this->buffer_pool_buf_size - 1) / this->buffer_pool_buf_size;
295   /* limit size to keep pool fluent */
296   if (n > (this->buffer_pool_capacity >> 3))
297     n = this->buffer_pool_capacity >> 3;
298   n -= old_buf->nbufs;
299 
300   want_buf = old_buf + old_buf->nbufs;
301   last_buf = &this->buffer_pool_top;
302   pthread_mutex_lock (&this->buffer_pool_mutex);
303   while (1) {
304     new_buf = (be_ei_t *)(*last_buf);
305     if (!new_buf)
306       break;
307     if (new_buf == want_buf)
308       break;
309     if (new_buf > want_buf) {
310       new_buf = NULL;
311       break;
312     }
313     new_buf += new_buf->nbufs;
314     last_buf = &(new_buf[-1].elem.next);
315   }
316 
317   if (new_buf) do {
318     int s;
319     /* save emergecy buf */
320     if (n > this->buffer_pool_num_free - 1)
321       n = this->buffer_pool_num_free - 1;
322     if (n < 1)
323       break;
324     s = new_buf->nbufs - n;
325     if (s > 0) {
326       new_buf += n;
327       new_buf->nbufs = s;
328       *last_buf = &new_buf->elem;
329     } else {
330       n = new_buf->nbufs;
331       new_buf += n;
332       *last_buf = new_buf[-1].elem.next;
333     }
334     this->buffer_pool_num_free -= n;
335     pthread_mutex_unlock (&this->buffer_pool_mutex);
336     old_buf->nbufs += n;
337     old_buf->elem.max_size = old_buf->nbufs * this->buffer_pool_buf_size;
338     return NULL;
339   } while (0);
340 
341   return buffer_pool_size_alloc_int (this, n);
342 }
343 
344 /*
345  * allocate a buffer from buffer pool - may fail if none is available
346  */
347 
buffer_pool_try_alloc(fifo_buffer_t * this)348 static buf_element_t *buffer_pool_try_alloc (fifo_buffer_t *this) {
349   be_ei_t *buf;
350   int i;
351 
352   pthread_mutex_lock (&this->buffer_pool_mutex);
353   buf = (be_ei_t *)this->buffer_pool_top;
354   if (!buf) {
355     pthread_mutex_unlock (&this->buffer_pool_mutex);
356     return NULL;
357   }
358 
359   this->buffer_pool_top = buf->elem.next;
360   i = buf->nbufs - 1;
361   if (i > 0)
362     buf[1].nbufs = i;
363   this->buffer_pool_num_free--;
364   pthread_mutex_unlock (&this->buffer_pool_mutex);
365 
366   /* set sane values to the newly allocated buffer */
367   buf->elem.content = buf->elem.mem; /* 99% of demuxers will want this */
368   buf->elem.pts = 0;
369   buf->elem.size = 0;
370   buf->elem.max_size = this->buffer_pool_buf_size;
371   buf->elem.decoder_flags = 0;
372   buf->nbufs = 1;
373   memset (buf->elem.decoder_info, 0, sizeof (buf->elem.decoder_info));
374   memset (buf->elem.decoder_info_ptr, 0, sizeof (buf->elem.decoder_info_ptr));
375   _x_extra_info_reset (buf->elem.extra_info);
376 
377   return &buf->elem;
378 }
379 
380 
381 /*
382  * append buffer element to fifo buffer
383  */
fifo_buffer_put(fifo_buffer_t * fifo,buf_element_t * element)384 static void fifo_buffer_put (fifo_buffer_t *fifo, buf_element_t *element) {
385   int i;
386 
387   pthread_mutex_lock (&fifo->mutex);
388 
389   if (element->decoder_flags & BUF_FLAG_MERGE) {
390     be_ei_t *new = (be_ei_t *)element, *prev = (be_ei_t *)fifo->last;
391     new->elem.decoder_flags &= ~BUF_FLAG_MERGE;
392     if (prev && (prev + prev->nbufs == new)
393       && (prev->elem.type == new->elem.type)
394       && (prev->nbufs < (fifo->buffer_pool_capacity >> 3))) {
395       fifo->fifo_size += new->nbufs;
396       fifo->fifo_data_size += new->elem.size;
397       prev->nbufs += new->nbufs;
398       prev->elem.max_size += new->elem.max_size;
399       prev->elem.size += new->elem.size;
400       prev->elem.decoder_flags |= new->elem.decoder_flags;
401       pthread_mutex_unlock (&fifo->mutex);
402       return;
403     }
404   }
405 
406   for(i = 0; fifo->put_cb[i]; i++)
407     fifo->put_cb[i](fifo, element, fifo->put_cb_data[i]);
408 
409   if (fifo->last)
410     fifo->last->next = element;
411   else
412     fifo->first = element;
413 
414   fifo->last = element;
415   element->next = NULL;
416 
417   if (element->free_buffer == buffer_pool_free) {
418     be_ei_t *beei = (be_ei_t *)element;
419     fifo->fifo_size += beei->nbufs;
420   } else {
421     fifo->fifo_size += 1;
422   }
423   fifo->fifo_data_size += element->size;
424 
425   if (fifo->fifo_num_waiters)
426     pthread_cond_signal (&fifo->not_empty);
427 
428   pthread_mutex_unlock (&fifo->mutex);
429 }
430 
431 /*
432  * simulate append buffer element to fifo buffer
433  */
dummy_fifo_buffer_put(fifo_buffer_t * fifo,buf_element_t * element)434 static void dummy_fifo_buffer_put (fifo_buffer_t *fifo, buf_element_t *element) {
435   int i;
436 
437   pthread_mutex_lock (&fifo->mutex);
438 
439   for(i = 0; fifo->put_cb[i]; i++)
440     fifo->put_cb[i](fifo, element, fifo->put_cb_data[i]);
441 
442   pthread_mutex_unlock (&fifo->mutex);
443 
444   element->free_buffer(element);
445 }
446 
447 /*
448  * insert buffer element to fifo buffer (demuxers MUST NOT call this one)
449  */
fifo_buffer_insert(fifo_buffer_t * fifo,buf_element_t * element)450 static void fifo_buffer_insert (fifo_buffer_t *fifo, buf_element_t *element) {
451   pthread_mutex_lock (&fifo->mutex);
452 
453   element->next = fifo->first;
454   fifo->first = element;
455 
456   if( !fifo->last )
457     fifo->last = element;
458 
459   if (element->free_buffer == buffer_pool_free) {
460     be_ei_t *beei = (be_ei_t *)element;
461     fifo->fifo_size += beei->nbufs;
462   } else {
463     fifo->fifo_size += 1;
464   }
465   fifo->fifo_data_size += element->size;
466 
467   if (fifo->fifo_num_waiters)
468     pthread_cond_signal (&fifo->not_empty);
469 
470   pthread_mutex_unlock (&fifo->mutex);
471 }
472 
473 /*
474  * insert buffer element to fifo buffer (demuxers MUST NOT call this one)
475  */
dummy_fifo_buffer_insert(fifo_buffer_t * fifo,buf_element_t * element)476 static void dummy_fifo_buffer_insert (fifo_buffer_t *fifo, buf_element_t *element) {
477   (void)fifo;
478   element->free_buffer(element);
479 }
480 
481 /*
482  * get element from fifo buffer
483  */
fifo_buffer_get(fifo_buffer_t * fifo)484 static buf_element_t *fifo_buffer_get (fifo_buffer_t *fifo) {
485   buf_element_t *buf;
486   int i;
487 
488   pthread_mutex_lock (&fifo->mutex);
489 
490   if (!fifo->first) {
491     fifo->fifo_num_waiters++;
492     do {
493       pthread_cond_wait (&fifo->not_empty, &fifo->mutex);
494     } while (!fifo->first);
495     fifo->fifo_num_waiters--;
496   }
497 
498   buf = fifo->first;
499 
500   fifo->first = fifo->first->next;
501   if (fifo->first==NULL)
502     fifo->last = NULL;
503 
504   if (buf->free_buffer == buffer_pool_free) {
505     be_ei_t *beei = (be_ei_t *)buf;
506     fifo->fifo_size -= beei->nbufs;
507   } else {
508     fifo->fifo_size -= 1;
509   }
510   fifo->fifo_data_size -= buf->size;
511 
512   for(i = 0; fifo->get_cb[i]; i++)
513     fifo->get_cb[i](fifo, buf, fifo->get_cb_data[i]);
514 
515   pthread_mutex_unlock (&fifo->mutex);
516 
517   return buf;
518 }
519 
fifo_buffer_tget(fifo_buffer_t * fifo,xine_ticket_t * ticket)520 static buf_element_t *fifo_buffer_tget (fifo_buffer_t *fifo, xine_ticket_t *ticket) {
521   /* Optimization: let decoders hold port ticket by default.
522    * Unfortunately, fifo callbacks are 1 big freezer, as they run with fifo locked,
523    * and may try to revoke ticket for pauseing or other stuff.
524    * Always releasing ticket when there are callbacks is safe but inefficient.
525    * Instead, we release ticket when we are going to wait for fifo or a buffer,
526    * and of course, when the ticket has been revoked.
527    * This should melt the "put" side. We could still freeze ourselves directly
528    * at the "get" side, what ticket->revoke () self grant hack shall fix.
529    */
530   buf_element_t *buf;
531   int mode = ticket ? 2 : 0, i;
532 
533   if (pthread_mutex_trylock (&fifo->mutex)) {
534     if (mode & 2) {
535       ticket->release (ticket, 0);
536       mode = 1;
537     }
538     pthread_mutex_lock (&fifo->mutex);
539   }
540 
541   if (!fifo->first) {
542     if (mode & 2) {
543       ticket->release (ticket, 0);
544       mode = 1;
545     }
546     fifo->fifo_num_waiters++;
547     do {
548       pthread_cond_wait (&fifo->not_empty, &fifo->mutex);
549     } while (!fifo->first);
550     fifo->fifo_num_waiters--;
551   }
552 
553   buf = fifo->first;
554 
555   fifo->first = fifo->first->next;
556   if (fifo->first==NULL)
557     fifo->last = NULL;
558 
559   if (buf->free_buffer == buffer_pool_free) {
560     be_ei_t *beei = (be_ei_t *)buf;
561     fifo->fifo_size -= beei->nbufs;
562   } else {
563     fifo->fifo_size -= 1;
564   }
565   fifo->fifo_data_size -= buf->size;
566 
567   if ((mode & 2) && ticket->ticket_revoked) {
568     ticket->release (ticket, 0);
569     mode = 1;
570   }
571 
572   for(i = 0; fifo->get_cb[i]; i++)
573     fifo->get_cb[i](fifo, buf, fifo->get_cb_data[i]);
574 
575   pthread_mutex_unlock (&fifo->mutex);
576 
577   if (mode & 1)
578     ticket->acquire (ticket, 0);
579 
580   return buf;
581 }
582 
583 
584 /*
585  * clear buffer (put all contained buffer elements back into buffer pool)
586  */
fifo_buffer_clear(fifo_buffer_t * fifo)587 static void fifo_buffer_clear (fifo_buffer_t *fifo) {
588   be_ei_t *start;
589 
590   pthread_mutex_lock (&fifo->mutex);
591 
592   /* take out all at once */
593   start = (be_ei_t *)fifo->first;
594   fifo->first = fifo->last = NULL;
595   fifo->fifo_size = 0;
596   fifo->fifo_data_size = 0;
597 
598   while (start) {
599     be_ei_t *buf, *next;
600     int n;
601 
602     /* keep control bufs (flush, ...) */
603     if ((start->elem.type & BUF_MAJOR_MASK) == BUF_CONTROL_BASE) {
604       if (!fifo->first)
605         fifo->first = &start->elem;
606       else
607         fifo->last->next = &start->elem;
608       fifo->last = &start->elem;
609       fifo->fifo_size += 1;
610       fifo->fifo_data_size += start->elem.size;
611       buf = (be_ei_t *)start->elem.next;
612       start->elem.next = NULL;
613       start = buf;
614       continue;
615     }
616 
617     /* free custom buf */
618     if (start->elem.free_buffer != buffer_pool_free) {
619       buf = (be_ei_t *)start->elem.next;
620       start->elem.next = NULL;
621       start->elem.free_buffer (&start->elem);
622       start = buf;
623       continue;
624     }
625 
626     /* optimize: get contiguous chunk */
627     buf = start;
628     n = 0;
629     while (1) {
630       int i = buf->nbufs;
631       next = (be_ei_t *)buf->elem.next;
632       n += i;
633       if (buf + i != next) /* includes next == NULL et al ;-) */
634         break;
635       if ((next->elem.type & BUF_MAJOR_MASK) == BUF_CONTROL_BASE)
636         break;
637       buf = next;
638     }
639     start->nbufs = n;
640     start->elem.free_buffer (&start->elem);
641     start = next;
642   }
643 
644   /* printf("Free buffers after clear: %d\n", fifo->buffer_pool_num_free); */
645   pthread_mutex_unlock (&fifo->mutex);
646 }
647 
fifo_buffer_all_clear(fifo_buffer_t * fifo)648 static void fifo_buffer_all_clear (fifo_buffer_t *fifo) {
649   be_ei_t *start;
650 
651   pthread_mutex_lock (&fifo->mutex);
652 
653   /* take out all at once */
654   start = (be_ei_t *)fifo->first;
655   fifo->first = fifo->last = NULL;
656   fifo->fifo_size = 0;
657   fifo->fifo_data_size = 0;
658 
659   while (start) {
660     be_ei_t *buf, *next;
661     int n;
662 
663     /* free custom buf */
664     if (start->elem.free_buffer != buffer_pool_free) {
665       buf = (be_ei_t *)start->elem.next;
666       start->elem.next = NULL;
667       start->elem.free_buffer (&start->elem);
668       start = buf;
669       continue;
670     }
671 
672     /* optimize: get contiguous chunk */
673     buf = start;
674     n = 0;
675     while (1) {
676       int i = buf->nbufs;
677       next = (be_ei_t *)buf->elem.next;
678       n += i;
679       if (buf + i != next) /* includes next == NULL ;-) */
680         break;
681       buf = next;
682     }
683     /* free just sibling bufs */
684     if (start->elem.source != (void *)fifo) {
685       start->nbufs = n;
686       start->elem.free_buffer (&start->elem);
687     }
688     start = next;
689   }
690 
691   pthread_mutex_unlock (&fifo->mutex);
692 }
693 
694 /*
695  * Return the number of elements in the fifo buffer
696  */
fifo_buffer_size(fifo_buffer_t * this)697 static int fifo_buffer_size (fifo_buffer_t *this) {
698   int size;
699 
700   pthread_mutex_lock(&this->mutex);
701   size = this->fifo_size;
702   pthread_mutex_unlock(&this->mutex);
703 
704   return size;
705 }
706 
707 /*
708  * Return the amount of the data in the fifo buffer
709  */
fifo_buffer_data_size(fifo_buffer_t * this)710 static uint32_t fifo_buffer_data_size (fifo_buffer_t *this) {
711   uint32_t data_size;
712 
713   pthread_mutex_lock(&this->mutex);
714   data_size = this->fifo_data_size;
715   pthread_mutex_unlock(&this->mutex);
716 
717   return data_size;
718 }
719 
720 /*
721  * Return the number of free elements in the pool
722  */
fifo_buffer_num_free(fifo_buffer_t * this)723 static int fifo_buffer_num_free (fifo_buffer_t *this) {
724   int buffer_pool_num_free;
725 
726   pthread_mutex_lock (&this->buffer_pool_mutex);
727   buffer_pool_num_free = this->buffer_pool_num_free;
728   pthread_mutex_unlock (&this->buffer_pool_mutex);
729 
730   return buffer_pool_num_free;
731 }
732 
733 /*
734  * Destroy the buffer
735  */
fifo_buffer_dispose(fifo_buffer_t * this)736 static void fifo_buffer_dispose (fifo_buffer_t *this) {
737   fifo_buffer_all_clear (this);
738   xine_free_aligned (this->buffer_pool_base);
739   pthread_mutex_destroy(&this->mutex);
740   pthread_cond_destroy(&this->not_empty);
741   pthread_mutex_destroy(&this->buffer_pool_mutex);
742   pthread_cond_destroy(&this->buffer_pool_cond_not_empty);
743   free (this);
744 }
745 
746 /*
747  * Register an "alloc" callback
748  */
fifo_register_alloc_cb(fifo_buffer_t * this,void (* cb)(fifo_buffer_t * this,void * data_cb),void * data_cb)749 static void fifo_register_alloc_cb (fifo_buffer_t *this,
750                                     void (*cb)(fifo_buffer_t *this,
751                                                void *data_cb),
752                                     void *data_cb) {
753   int i;
754 
755   pthread_mutex_lock(&this->mutex);
756   for(i = 0; this->alloc_cb[i]; i++)
757     ;
758   if( i != BUF_MAX_CALLBACKS-1 ) {
759     this->alloc_cb[i] = cb;
760     this->alloc_cb_data[i] = data_cb;
761     this->alloc_cb[i+1] = NULL;
762   }
763   pthread_mutex_unlock(&this->mutex);
764 }
765 
766 /*
767  * Register a "put" callback
768  */
fifo_register_put_cb(fifo_buffer_t * this,void (* cb)(fifo_buffer_t * this,buf_element_t * buf,void * data_cb),void * data_cb)769 static void fifo_register_put_cb (fifo_buffer_t *this,
770                                   void (*cb)(fifo_buffer_t *this,
771                                              buf_element_t *buf,
772                                              void *data_cb),
773                                   void *data_cb) {
774   int i;
775 
776   pthread_mutex_lock(&this->mutex);
777   for(i = 0; this->put_cb[i]; i++)
778     ;
779   if( i != BUF_MAX_CALLBACKS-1 ) {
780     this->put_cb[i] = cb;
781     this->put_cb_data[i] = data_cb;
782     this->put_cb[i+1] = NULL;
783   }
784   pthread_mutex_unlock(&this->mutex);
785 }
786 
787 /*
788  * Register a "get" callback
789  */
fifo_register_get_cb(fifo_buffer_t * this,void (* cb)(fifo_buffer_t * this,buf_element_t * buf,void * data_cb),void * data_cb)790 static void fifo_register_get_cb (fifo_buffer_t *this,
791                                   void (*cb)(fifo_buffer_t *this,
792                                              buf_element_t *buf,
793                                              void *data_cb),
794                                   void *data_cb) {
795   int i;
796 
797   pthread_mutex_lock(&this->mutex);
798   for(i = 0; this->get_cb[i]; i++)
799     ;
800   if( i != BUF_MAX_CALLBACKS-1 ) {
801     this->get_cb[i] = cb;
802     this->get_cb_data[i] = data_cb;
803     this->get_cb[i+1] = NULL;
804   }
805   pthread_mutex_unlock(&this->mutex);
806 }
807 
808 /*
809  * Unregister an "alloc" callback
810  */
fifo_unregister_alloc_cb(fifo_buffer_t * this,void (* cb)(fifo_buffer_t * this,void * data_cb))811 static void fifo_unregister_alloc_cb (fifo_buffer_t *this,
812                                       void (*cb)(fifo_buffer_t *this,
813                                                  void *data_cb) ) {
814   int i,j;
815 
816   pthread_mutex_lock(&this->mutex);
817   for(i = 0; this->alloc_cb[i]; i++) {
818     if( this->alloc_cb[i] == cb ) {
819       for(j = i; this->alloc_cb[j]; j++) {
820         this->alloc_cb[j] = this->alloc_cb[j+1];
821         this->alloc_cb_data[j] = this->alloc_cb_data[j+1];
822       }
823     }
824   }
825   pthread_mutex_unlock(&this->mutex);
826 }
827 
828 /*
829  * Unregister a "put" callback
830  */
fifo_unregister_put_cb(fifo_buffer_t * this,void (* cb)(fifo_buffer_t * this,buf_element_t * buf,void * data_cb))831 static void fifo_unregister_put_cb (fifo_buffer_t *this,
832                                   void (*cb)(fifo_buffer_t *this,
833                                              buf_element_t *buf,
834                                              void *data_cb) ) {
835   int i,j;
836 
837   pthread_mutex_lock(&this->mutex);
838   for(i = 0; this->put_cb[i]; i++) {
839     if( this->put_cb[i] == cb ) {
840       for(j = i; this->put_cb[j]; j++) {
841         this->put_cb[j] = this->put_cb[j+1];
842         this->put_cb_data[j] = this->put_cb_data[j+1];
843       }
844     }
845   }
846   pthread_mutex_unlock(&this->mutex);
847 }
848 
849 /*
850  * Unregister a "get" callback
851  */
fifo_unregister_get_cb(fifo_buffer_t * this,void (* cb)(fifo_buffer_t * this,buf_element_t * buf,void * data_cb))852 static void fifo_unregister_get_cb (fifo_buffer_t *this,
853                                   void (*cb)(fifo_buffer_t *this,
854                                              buf_element_t *buf,
855                                              void *data_cb) ) {
856   int i,j;
857 
858   pthread_mutex_lock(&this->mutex);
859   for(i = 0; this->get_cb[i]; i++) {
860     if( this->get_cb[i] == cb ) {
861       for(j = i; this->get_cb[j]; j++) {
862         this->get_cb[j] = this->get_cb[j+1];
863         this->get_cb_data[j] = this->get_cb_data[j+1];
864       }
865     }
866   }
867   pthread_mutex_unlock(&this->mutex);
868 }
869 
870 /*
871  * allocate and initialize new (empty) fifo buffer
872  */
_x_fifo_buffer_new(int num_buffers,uint32_t buf_size)873 fifo_buffer_t *_x_fifo_buffer_new (int num_buffers, uint32_t buf_size) {
874 
875   fifo_buffer_t *this;
876   int            i;
877   unsigned char *multi_buffer;
878   be_ei_t       *beei;
879 
880   this = calloc(1, sizeof(fifo_buffer_t));
881   if (!this)
882     return NULL;
883 #ifndef HAVE_ZERO_SAFE_MEM
884   /* Do these first, when compiler still knows "this" is all zeroed.
885    * Let it optimize away this on most systems where clear mem
886    * interpretes as 0, 0f or NULL safely.
887    */
888   this->first                   = NULL;
889   this->last                    = NULL;
890   this->fifo_size               = 0;
891   this->fifo_num_waiters        = 0;
892   this->buffer_pool_num_waiters = 0;
893   this->alloc_cb[0]             = NULL;
894   this->get_cb[0]               = NULL;
895   this->put_cb[0]               = NULL;
896   this->alloc_cb_data[0]        = NULL;
897   this->get_cb_data[0]          = NULL;
898   this->put_cb_data[0]          = NULL;
899 #endif
900 
901   /* printf ("Allocating %d buffers of %ld bytes in one chunk\n", num_buffers, (long int) buf_size); */
902   multi_buffer = xine_mallocz_aligned (num_buffers * (buf_size + sizeof (be_ei_t)));
903   if (!multi_buffer) {
904     free (this);
905     return NULL;
906   }
907 
908   this->put                 = fifo_buffer_put;
909   this->insert              = fifo_buffer_insert;
910   this->get                 = fifo_buffer_get;
911   this->tget                = fifo_buffer_tget;
912   this->clear               = fifo_buffer_clear;
913   this->size                = fifo_buffer_size;
914   this->num_free            = fifo_buffer_num_free;
915   this->data_size           = fifo_buffer_data_size;
916   this->dispose             = fifo_buffer_dispose;
917   this->register_alloc_cb   = fifo_register_alloc_cb;
918   this->register_get_cb     = fifo_register_get_cb;
919   this->register_put_cb     = fifo_register_put_cb;
920   this->unregister_alloc_cb = fifo_unregister_alloc_cb;
921   this->unregister_get_cb   = fifo_unregister_get_cb;
922   this->unregister_put_cb   = fifo_unregister_put_cb;
923   pthread_mutex_init (&this->mutex, NULL);
924   pthread_cond_init (&this->not_empty, NULL);
925 
926   /* init buffer pool */
927 
928   pthread_mutex_init (&this->buffer_pool_mutex, NULL);
929   pthread_cond_init (&this->buffer_pool_cond_not_empty, NULL);
930 
931   this->buffer_pool_num_free   =
932   this->buffer_pool_capacity   = num_buffers;
933   this->buffer_pool_buf_size   = buf_size;
934   this->buffer_pool_alloc      = buffer_pool_alloc;
935   this->buffer_pool_try_alloc  = buffer_pool_try_alloc;
936   this->buffer_pool_size_alloc = buffer_pool_size_alloc;
937   this->buffer_pool_realloc    = buffer_pool_realloc;
938 
939   this->buffer_pool_large_wait  = LARGE_NUM;
940 
941   this->buffer_pool_base = multi_buffer;
942   beei = (be_ei_t *)(multi_buffer + num_buffers * buf_size);
943   this->buffer_pool_top  = &beei->elem;
944   beei->nbufs = num_buffers;
945 
946   for (i = 0; i < num_buffers; i++) {
947     beei->elem.mem         = multi_buffer;
948     multi_buffer          += buf_size;
949     beei->elem.max_size    = buf_size;
950     beei->elem.free_buffer = buffer_pool_free;
951     beei->elem.source      = this;
952     beei->elem.extra_info  = &beei->ei;
953     beei->elem.next        = &(beei + 1)->elem;
954     beei++;
955   }
956 
957   (beei - 1)->elem.next = NULL;
958 
959   return this;
960 }
961 
962 /*
963  * allocate and initialize new (empty) fifo buffer
964  */
_x_dummy_fifo_buffer_new(int num_buffers,uint32_t buf_size)965 fifo_buffer_t *_x_dummy_fifo_buffer_new (int num_buffers, uint32_t buf_size) {
966   fifo_buffer_t *this = _x_fifo_buffer_new (num_buffers, buf_size);
967   if (this) {
968     this->put    = dummy_fifo_buffer_put;
969     this->insert = dummy_fifo_buffer_insert;
970   }
971   return this;
972 }
973 
_x_free_buf_elements(buf_element_t * head)974 void _x_free_buf_elements(buf_element_t *head) {
975 
976   if (head) {
977     buf_element_t  *cur, *next;
978 
979     cur = head;
980     while (cur) {
981       next = cur->next;
982       cur->free_buffer(cur);
983       cur = next;
984     }
985   }
986 }
987 
988