root/branches/streaming-rework/src/libutil/TimestampedBuffer.cpp

Revision 419, 27.1 kB (checked in by pieterpalmers, 16 years ago)

namespace simplification

Line 
1 /* $Id$ */
2
3 /*
4  *   FreeBob Streaming API
5  *   FreeBob = Firewire (pro-)audio for linux
6  *
7  *   http://freebob.sf.net
8  *
9  *   Copyright (C) 2005,2006,2007 Pieter Palmers <pieterpalmers@users.sourceforge.net>
10  *
11  *   This program is free software {} you can redistribute it and/or modify
12  *   it under the terms of the GNU General Public License as published by
13  *   the Free Software Foundation {} either version 2 of the License, or
14  *   (at your option) any later version.
15  *
16  *   This program is distributed in the hope that it will be useful,
17  *   but WITHOUT ANY WARRANTY {} without even the implied warranty of
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *   GNU General Public License for more details.
20  *
21  *   You should have received a copy of the GNU General Public License
22  *   along with this program {} if not, write to the Free Software
23  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  *
25  *
26  *
27  */
28  
29 #include "libutil/Atomic.h"
30 #include "libstreaming/cycletimer.h"
31
32 #include "TimestampedBuffer.h"
33 #include "assert.h"
34
35 namespace Util {
36
37 IMPL_DEBUG_MODULE( TimestampedBuffer, TimestampedBuffer, DEBUG_LEVEL_VERBOSE );
38
39 TimestampedBuffer::TimestampedBuffer(TimestampedBufferClient *c)
40     : m_event_buffer(NULL), m_cluster_buffer(NULL),
41       m_event_size(0), m_events_per_frame(0), m_buffer_size(0),
42       m_bytes_per_frame(0), m_bytes_per_buffer(0),
43       m_wrap_at(0xFFFFFFFFFFFFFFFFLLU),
44       m_Client(c), m_framecounter(0),
45       m_tick_offset(0),
46       m_buffer_tail_timestamp(0),
47       m_buffer_next_tail_timestamp(0),
48       m_dll_e2(0.0), m_dll_b(0.877), m_dll_c(0.384),
49       m_nominal_rate(0.0), m_update_period(0)
50 {
51     pthread_mutex_init(&m_framecounter_lock, NULL);
52
53 }
54
55 TimestampedBuffer::~TimestampedBuffer() {
56     freebob_ringbuffer_free(m_event_buffer);
57     free(m_cluster_buffer);
58 }
59
60 /**
61  * \brief Set the nominal rate in frames/timeunit
62  *
63  * Sets the nominal rate in frames per time unit. This rate is used
64  * to initialize the DLL that will extract the effective rate based
65  * upon the timestamps it gets fed.
66  *
67  * @param r rate
68  * @return true if successful
69  */
70 bool TimestampedBuffer::setNominalRate(float r) {
71     m_nominal_rate=r;
72     debugOutput(DEBUG_LEVEL_VERBOSE," nominal rate=%e set to %e\n",
73                                     m_nominal_rate, r);   
74     return true;
75 }
76
77 /**
78  * \brief Set the nominal update period (in frames)
79  *
80  * Sets the nominal update period. This period is the number of frames
81  * between two timestamp updates (hence buffer writes)
82  *
83  * @param n period in frames
84  * @return true if successful
85  */
86 bool TimestampedBuffer::setUpdatePeriod(unsigned int n) {
87     m_update_period=n;
88     return true;
89 }
90
91 /**
92  * \brief set the value at which timestamps should wrap around
93  * @param w value to wrap at
94  * @return true if successful
95  */
96 bool TimestampedBuffer::setWrapValue(uint64_t w) {
97     m_wrap_at=w;
98     return true;
99 }
100
101 /**
102  * \brief return the effective rate
103  *
104  * Returns the effective rate calculated by the DLL.
105  *
106  * @return rate (in timeunits/frame)
107  */
108 float TimestampedBuffer::getRate() {
109     debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"getRate: %f/%f=%f\n",
110         m_dll_e2,(float)m_update_period, m_dll_e2/((float) m_update_period));
111    
112     return m_dll_e2/((float) m_update_period);
113 }
114
115 /**
116  * \brief Sets the size of the events
117  * @param s event size in bytes
118  * @return true if successful
119  */
120 bool TimestampedBuffer::setEventSize(unsigned int s) {
121     m_event_size=s;
122    
123     m_bytes_per_frame=m_event_size*m_events_per_frame;
124     m_bytes_per_buffer=m_bytes_per_frame*m_buffer_size;
125    
126     return true;
127 }
128
129 /**
130  * \brief Sets the number of events per frame
131  * @param n number of events per frame
132  * @return true if successful
133  */
134 bool TimestampedBuffer::setEventsPerFrame(unsigned int n) {
135     m_events_per_frame=n;
136    
137     m_bytes_per_frame=m_event_size*m_events_per_frame;
138     m_bytes_per_buffer=m_bytes_per_frame*m_buffer_size;
139    
140     return true;
141 }
142 /**
143  * \brief Sets the buffer size in frames
144  * @param n number frames
145  * @return true if successful
146  */
147 bool TimestampedBuffer::setBufferSize(unsigned int n) {
148     m_buffer_size=n;
149
150     m_bytes_per_frame=m_event_size*m_events_per_frame;
151     m_bytes_per_buffer=m_bytes_per_frame*m_buffer_size;
152
153     return true;
154 }
155
156 /**
157  * Sets the buffer offset in ticks.
158  *
159  * A positive value means that the buffer is 'delayed' for nticks ticks.
160  *
161  * @note These offsets are only used when reading timestamps. Any function
162  *       that returns a timestamp will incorporate this offset.
163  * @param nframes the number of ticks (positive = delay buffer)
164  * @return true if successful
165  */
166 bool TimestampedBuffer::setTickOffset(int nticks) {
167     debugOutput(DEBUG_LEVEL_VERBOSE,"Setting ticks offset to %d\n",nticks);
168     m_tick_offset=nticks;
169     return true;
170 }
171
172 /**
173  * \brief Returns the current fill of the buffer
174  *
175  * This returns the buffer fill of the internal ringbuffer. This
176  * can only be used as an indication because it's state is not
177  * guaranteed to be consistent at all times due to threading issues.
178  *
179  * In order to get the number of frames in the buffer, use the
180  * getFrameCounter, getBufferHeadTimestamp, getBufferTailTimestamp
181  * functions
182  *
183  * @return the internal buffer fill in frames
184  */
185 unsigned int TimestampedBuffer::getBufferFill() {
186     return freebob_ringbuffer_read_space(m_event_buffer)/(m_bytes_per_frame);
187 }
188
189 /**
190  * \brief Initializes the TimestampedBuffer
191  *
192  * Initializes the TimestampedBuffer, should be called before anything else
193  * is done.
194  *
195  * @return true if successful
196  */
197 bool TimestampedBuffer::init() {
198     return true;
199 }
200
201 /**
202  * \brief Resets the TimestampedBuffer
203  *
204  * Resets the TimestampedBuffer, clearing the buffers and counters.
205  * (not true yet: Also resets the DLL to the nominal values.)
206  *
207  * \note when this is called, you should make sure that the buffer
208  *       tail timestamp gets set before continuing
209  *
210  * @return true if successful
211  */
212 bool TimestampedBuffer::reset() {
213     freebob_ringbuffer_reset(m_event_buffer);
214    
215     resetFrameCounter();
216    
217     return true;
218 }
219
220 /**
221  * \brief Perpares the TimestampedBuffer
222  *
223  * Prepare the TimestampedBuffer. This allocates all internal buffers and
224  * initializes all data structures.
225  *
226  * This should be called after parameters such as buffer size, event size etc.. are set,
227  * and before any read/write operations are performed.
228  *
229  * @return true if successful
230  */
231 bool TimestampedBuffer::prepare() {
232     debugOutput(DEBUG_LEVEL_VERBOSE,"Preparing buffer (%p)\n",this);
233     debugOutput(DEBUG_LEVEL_VERBOSE," Size=%u events, events/frame=%u, event size=%ubytes\n",
234                                         m_buffer_size,m_events_per_frame,m_event_size);
235                                        
236     debugOutput(DEBUG_LEVEL_VERBOSE," update period %u\n",
237                                     m_update_period);
238     debugOutput(DEBUG_LEVEL_VERBOSE," nominal rate=%f\n",
239                                     m_nominal_rate);
240                                    
241     debugOutput(DEBUG_LEVEL_VERBOSE," wrapping at %llu\n",m_wrap_at);
242
243     assert(m_buffer_size);
244     assert(m_events_per_frame);
245     assert(m_event_size);
246
247     assert(m_nominal_rate != 0.0L);
248     assert(m_update_period != 0);
249    
250     if( !(m_event_buffer=freebob_ringbuffer_create(
251             (m_events_per_frame * m_buffer_size) * m_event_size))) {
252         debugFatal("Could not allocate memory event ringbuffer\n");
253         return false;
254     }
255    
256     // allocate the temporary cluster buffer
257     if( !(m_cluster_buffer=(char *)calloc(m_events_per_frame,m_event_size))) {
258             debugFatal("Could not allocate temporary cluster buffer\n");
259         freebob_ringbuffer_free(m_event_buffer);
260         return false;
261     }
262    
263     // init the DLL
264     m_dll_e2=m_nominal_rate * (float)m_update_period;
265    
266     m_dll_b=((float)(0.877));
267     m_dll_c=((float)(0.384));
268
269     return true;
270 }
271
272 /**
273  * @brief Write frames to the buffer
274  *
275  * Copies \ref nframes of frames from the buffer pointed to by \ref data to the
276  * internal ringbuffer. The time of the last frame in the buffer is set to \ref ts.
277  *
278  * @param nframes number of frames to copy
279  * @param data pointer to the frame buffer
280  * @param ts timestamp of the last frame copied
281  * @return true if successful
282  */
283 bool TimestampedBuffer::writeFrames(unsigned int nframes, char *data, uint64_t ts) {
284
285     unsigned int write_size=nframes*m_event_size*m_events_per_frame;
286
287     // add the data payload to the ringbuffer
288     if (freebob_ringbuffer_write(m_event_buffer,data,write_size) < write_size)
289     {
290 //         debugWarning("writeFrames buffer overrun\n");
291         return false;
292     }
293    
294     incrementFrameCounter(nframes,ts);
295    
296     return true;
297
298 }
299 /**
300  * @brief Read frames from the buffer
301  *
302  * Copies \ref nframes of frames from the internal buffer to the data buffer pointed
303  * to by \ref data.
304  *
305  * @param nframes number of frames to copy
306  * @param data pointer to the frame buffer
307  * @return true if successful
308  */
309 bool TimestampedBuffer::readFrames(unsigned int nframes, char *data) {
310
311     unsigned int read_size=nframes*m_event_size*m_events_per_frame;
312
313     // get the data payload to the ringbuffer
314     if ((freebob_ringbuffer_read(m_event_buffer,data,read_size)) < read_size)
315     {
316 //         debugWarning("readFrames buffer underrun\n");
317         return false;
318     }
319    
320     decrementFrameCounter(nframes);
321    
322     return true;
323
324 }
325
326 /**
327  * @brief Performs block processing write of frames
328  *
329  * This function allows for zero-copy writing into the ringbuffer.
330  * It calls the client's processWriteBlock function to write frames
331  * into the internal buffer's data area, in a thread safe fashion.
332  *
333  * It also updates the timestamp.
334  *
335  * @param nbframes number of frames to process
336  * @param ts timestamp of the last frame written to the buffer
337  * @return true if successful
338  */
339 bool TimestampedBuffer::blockProcessWriteFrames(unsigned int nbframes, int64_t ts) {
340
341     debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Transferring period...\n");
342     int xrun;
343     unsigned int offset=0;
344    
345     freebob_ringbuffer_data_t vec[2];
346     // we received one period of frames
347     // this is period_size*dimension of events
348     unsigned int events2write=nbframes*m_events_per_frame;
349     unsigned int bytes2write=events2write*m_event_size;
350
351     /* write events2write bytes to the ringbuffer
352     *  first see if it can be done in one read.
353     *  if so, ok.
354     *  otherwise write up to a multiple of clusters directly to the buffer
355     *  then do the buffer wrap around using ringbuffer_write
356     *  then write the remaining data directly to the buffer in a third pass
357     *  Make sure that we cannot end up on a non-cluster aligned position!
358     */
359     unsigned int cluster_size=m_events_per_frame*m_event_size;
360
361     while(bytes2write>0) {
362         int byteswritten=0;
363        
364         unsigned int frameswritten=(nbframes*cluster_size-bytes2write)/cluster_size;
365         offset=frameswritten;
366        
367         freebob_ringbuffer_get_write_vector(m_event_buffer, vec);
368            
369         if(vec[0].len==0) { // this indicates a full event buffer
370             debugError("Event buffer overrun in buffer %p\n",this);
371             break;
372         }
373            
374         /* if we don't take care we will get stuck in an infinite loop
375         * because we align to a cluster boundary later
376         * the remaining nb of bytes in one write operation can be
377         * smaller than one cluster
378         * this can happen because the ringbuffer size is always a power of 2
379         */
380         if(vec[0].len<cluster_size) {
381            
382             // encode to the temporary buffer
383             xrun = m_Client->processWriteBlock(m_cluster_buffer, 1, offset);
384            
385             if(xrun<0) {
386                 // xrun detected
387                 debugError("Frame buffer underrun in buffer %p\n",this);
388                 return false;
389             }
390                
391             // use the ringbuffer function to write one cluster
392             // the write function handles the wrap around.
393             freebob_ringbuffer_write(m_event_buffer,
394                          m_cluster_buffer,
395                          cluster_size);
396                
397             // we advanced one cluster_size
398             bytes2write-=cluster_size;
399                
400         } else { //
401            
402             if(bytes2write>vec[0].len) {
403                 // align to a cluster boundary
404                 byteswritten=vec[0].len-(vec[0].len%cluster_size);
405             } else {
406                 byteswritten=bytes2write;
407             }
408                
409             xrun = m_Client->processWriteBlock(vec[0].buf,
410                          byteswritten/cluster_size,
411                          offset);
412            
413             if(xrun<0) {
414                     // xrun detected
415                 debugError("Frame buffer underrun in buffer %p\n",this);
416                 return false; // FIXME: return false ?
417             }
418
419             freebob_ringbuffer_write_advance(m_event_buffer, byteswritten);
420             bytes2write -= byteswritten;
421         }
422
423         // the bytes2write should always be cluster aligned
424         assert(bytes2write%cluster_size==0);
425
426     }
427    
428     incrementFrameCounter(nbframes,ts);
429    
430     return true;
431    
432 }
433
434 /**
435  * @brief Performs block processing read of frames
436  *
437  * This function allows for zero-copy reading from the ringbuffer.
438  * It calls the client's processReadBlock function to read frames
439  * directly from the internal buffer's data area, in a thread safe
440  * fashion.
441  *
442  * @param nbframes number of frames to process
443  * @return true if successful
444  */
445 bool TimestampedBuffer::blockProcessReadFrames(unsigned int nbframes) {
446
447     debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Reading %u from buffer (%p)...\n", nbframes, this);
448    
449     int xrun;
450     unsigned int offset=0;
451    
452     freebob_ringbuffer_data_t vec[2];
453     // we received one period of frames on each connection
454     // this is period_size*dimension of events
455
456     unsigned int events2read=nbframes*m_events_per_frame;
457     unsigned int bytes2read=events2read*m_event_size;
458     /* read events2read bytes from the ringbuffer
459     *  first see if it can be done in one read.
460     *  if so, ok.
461     *  otherwise read up to a multiple of clusters directly from the buffer
462     *  then do the buffer wrap around using ringbuffer_read
463     *  then read the remaining data directly from the buffer in a third pass
464     *  Make sure that we cannot end up on a non-cluster aligned position!
465     */
466     unsigned int cluster_size=m_events_per_frame*m_event_size;
467    
468     while(bytes2read>0) {
469         unsigned int framesread=(nbframes*cluster_size-bytes2read)/cluster_size;
470         offset=framesread;
471
472         int bytesread=0;
473
474         freebob_ringbuffer_get_read_vector(m_event_buffer, vec);
475
476         if(vec[0].len==0) { // this indicates an empty event buffer
477             debugError("Event buffer underrun in buffer %p\n",this);
478             return false;
479         }
480
481         /* if we don't take care we will get stuck in an infinite loop
482         * because we align to a cluster boundary later
483         * the remaining nb of bytes in one read operation can be smaller than one cluster
484         * this can happen because the ringbuffer size is always a power of 2
485                 */
486         if(vec[0].len<cluster_size) {
487             // use the ringbuffer function to read one cluster
488             // the read function handles wrap around
489             freebob_ringbuffer_read(m_event_buffer,m_cluster_buffer,cluster_size);
490
491             assert(m_Client);
492             xrun = m_Client->processReadBlock(m_cluster_buffer, 1, offset);
493
494             if(xrun<0) {
495                 // xrun detected
496                 debugError("Frame buffer overrun in buffer %p\n",this);
497                     return false;
498             }
499
500             // we advanced one cluster_size
501             bytes2read-=cluster_size;
502
503         } else { //
504
505             if(bytes2read>vec[0].len) {
506                 // align to a cluster boundary
507                 bytesread=vec[0].len-(vec[0].len%cluster_size);
508             } else {
509                 bytesread=bytes2read;
510             }
511            
512             assert(m_Client);
513             xrun = m_Client->processReadBlock(vec[0].buf, bytesread/cluster_size, offset);
514
515             if(xrun<0) {
516                 // xrun detected
517                 debugError("Frame buffer overrun in buffer %p\n",this);
518                 return false;
519             }
520
521             freebob_ringbuffer_read_advance(m_event_buffer, bytesread);
522             bytes2read -= bytesread;
523         }
524
525         // the bytes2read should always be cluster aligned
526         assert(bytes2read%cluster_size==0);
527     }
528
529     decrementFrameCounter(nbframes);
530    
531     return true;
532 }
533
534 /**
535  * @brief Sets the buffer tail timestamp.
536  *
537  * Set the buffer tail timestamp to \ref new_timestamp. This will recalculate
538  * the internal state such that the buffer's timeframe starts at
539  * \ref new_timestamp.
540  *
541  * This is thread safe.
542  *
543  * @note considers offsets
544  *
545  * @param new_timestamp
546  */
547 void TimestampedBuffer::setBufferTailTimestamp(uint64_t new_timestamp) {
548
549     // add the offsets
550     int64_t ts=new_timestamp;
551     ts += m_tick_offset;
552    
553     if (ts >= (int64_t)m_wrap_at) {
554         ts -= m_wrap_at;
555     } else if (ts < 0) {
556         ts += m_wrap_at;
557     }
558    
559 #ifdef DEBUG
560     if (new_timestamp >= m_wrap_at) {
561         debugWarning("timestamp not wrapped: %llu\n",new_timestamp);
562     }   
563     if ((ts >= (int64_t)m_wrap_at) || (ts < 0 )) {
564         debugWarning("ts not wrapped correctly: %lld\n",ts);
565     }
566 #endif
567
568     pthread_mutex_lock(&m_framecounter_lock);
569    
570     m_buffer_tail_timestamp = ts;
571    
572     m_dll_e2=m_update_period * m_nominal_rate;
573     m_buffer_next_tail_timestamp = (uint64_t)((float)m_buffer_tail_timestamp + m_dll_e2);
574    
575     pthread_mutex_unlock(&m_framecounter_lock);   
576    
577     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Set buffer tail timestamp for (%p) to %11llu => %11lld, NTS=%llu, DLL2=%f, RATE=%f\n",
578                 this, new_timestamp, ts, m_buffer_next_tail_timestamp, m_dll_e2, m_nominal_rate);
579
580 }
581
582 /**
583  * @brief Sets the buffer head timestamp.
584  *
585  * Set the buffer tail timestamp such that the buffer head timestamp becomes
586  * \ref new_timestamp. This does not consider offsets, because it's use is to
587  * make sure the following is true after setBufferHeadTimestamp(x):
588  *   x == getBufferHeadTimestamp()
589  *
590  * This is thread safe.
591  *
592  * @param new_timestamp
593  */
594 void TimestampedBuffer::setBufferHeadTimestamp(uint64_t new_timestamp) {
595    
596 #ifdef DEBUG
597     if (new_timestamp >= m_wrap_at) {
598         debugWarning("timestamp not wrapped: %llu\n",new_timestamp);
599     }   
600 #endif
601
602     int64_t ts=new_timestamp;
603
604     pthread_mutex_lock(&m_framecounter_lock);
605    
606     // add the time
607     ts += (int64_t)(m_nominal_rate * (float)m_framecounter);
608    
609     if (ts >= (int64_t)m_wrap_at) {
610         ts -= m_wrap_at;
611     } else if (ts < 0) {
612         ts += m_wrap_at;
613     }
614    
615     m_buffer_tail_timestamp = ts;
616    
617     m_dll_e2=m_update_period * m_nominal_rate;
618     m_buffer_next_tail_timestamp = (uint64_t)((float)m_buffer_tail_timestamp + m_dll_e2);
619    
620     pthread_mutex_unlock(&m_framecounter_lock);   
621    
622     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Set buffer head timestamp for (%p) to %11llu => %11lld, NTS=%llu, DLL2=%f, RATE=%f\n",
623                 this, new_timestamp, ts, m_buffer_next_tail_timestamp, m_dll_e2, m_nominal_rate);
624
625 }
626
627 /**
628  * \brief return the timestamp of the first frame in the buffer
629  *
630  * This function returns the timestamp of the very first sample in
631  * the StreamProcessor's buffer. It also returns the framecounter value
632  * for which this timestamp is valid.
633  *
634  * @param ts address to store the timestamp in
635  * @param fc address to store the associated framecounter in
636  */
637 void TimestampedBuffer::getBufferHeadTimestamp(uint64_t *ts, uint64_t *fc) {
638     // NOTE: this is still ok with threads, because we use *fc to compute
639     //       the timestamp
640     *fc = m_framecounter;
641     *ts=getTimestampFromTail(*fc);
642 }
643
644 /**
645  * \brief return the timestamp of the last frame in the buffer
646  *
647  * This function returns the timestamp of the last frame in
648  * the StreamProcessor's buffer. It also returns the framecounter
649  * value for which this timestamp is valid.
650  *
651  * @param ts address to store the timestamp in
652  * @param fc address to store the associated framecounter in
653  */
654 void TimestampedBuffer::getBufferTailTimestamp(uint64_t *ts, uint64_t *fc) {
655     pthread_mutex_lock(&m_framecounter_lock);
656     *fc = (uint64_t)m_framecounter;
657     *ts = m_buffer_tail_timestamp;
658     pthread_mutex_unlock(&m_framecounter_lock);
659 }
660
661 /**
662  * @brief Get timestamp for a specific position from the buffer tail
663  *
664  * Returns the timestamp for a position that is nframes earlier than the
665  * buffer tail
666  *
667  * @param nframes number of frames
668  * @return timestamp value
669  */
670 uint64_t TimestampedBuffer::getTimestampFromTail(int nframes)
671 {
672     // ts(x) = m_buffer_tail_timestamp -
673     //         (m_buffer_next_tail_timestamp - m_buffer_tail_timestamp)/(samples_between_updates)*(x)
674    
675     int64_t diff=m_buffer_next_tail_timestamp - m_buffer_tail_timestamp;
676     if (diff < 0) diff += m_wrap_at;
677    
678     float rate=(float)diff / (float)m_update_period;
679    
680     int64_t timestamp;
681    
682     pthread_mutex_lock(&m_framecounter_lock);
683
684     timestamp=(int64_t)m_buffer_tail_timestamp - (int64_t)((nframes) * rate);
685
686     pthread_mutex_unlock(&m_framecounter_lock);
687    
688     if(timestamp >= (int64_t)m_wrap_at) {
689         timestamp -= m_wrap_at;
690     } else if(timestamp < 0) {
691         timestamp += m_wrap_at;
692     }
693    
694     return (uint64_t)timestamp;
695 }
696
697 /**
698  * @brief Get timestamp for a specific position from the buffer head
699  *
700  * Returns the timestamp for a position that is nframes later than the
701  * buffer head
702  *
703  * @param nframes number of frames
704  * @return timestamp value
705  */
706 uint64_t TimestampedBuffer::getTimestampFromHead(int nframes)
707 {
708     return getTimestampFromTail(m_framecounter-nframes);
709 }
710
711 /**
712  * Resets the frame counter, in a atomic way. This
713  * is thread safe.
714  */
715 void TimestampedBuffer::resetFrameCounter() {
716     pthread_mutex_lock(&m_framecounter_lock);
717     m_framecounter = 0;
718     pthread_mutex_unlock(&m_framecounter_lock);
719 }
720
721 /**
722  * Decrements the frame counter in a thread safe way.
723  *
724  * @param nbframes number of frames to decrement
725  */
726 void TimestampedBuffer::decrementFrameCounter(int nbframes) {
727     pthread_mutex_lock(&m_framecounter_lock);
728     m_framecounter -= nbframes;
729     pthread_mutex_unlock(&m_framecounter_lock);
730 }
731
732 /**
733  * Increments the frame counter in a thread safe way.
734  * Also updates the timestamp.
735  *
736  * @note the offsets defined by setTicksOffset and setFrameOffset
737  *       are added here.
738  *
739  * @param nbframes the number of frames to add
740  * @param new_timestamp the new timestamp
741  */
742 void TimestampedBuffer::incrementFrameCounter(int nbframes, uint64_t new_timestamp) {
743    
744     // add the offsets
745     int64_t diff=m_buffer_next_tail_timestamp - m_buffer_tail_timestamp;
746     if (diff < 0) diff += m_wrap_at;
747    
748     float rate=(float)diff / (float)m_update_period;
749        
750     int64_t ts=new_timestamp;
751     ts += (int64_t)m_tick_offset;
752    
753     if (ts >= (int64_t)m_wrap_at) {
754         ts -= m_wrap_at;
755     } else if (ts < 0) {
756         ts += m_wrap_at;
757     }
758    
759     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Setting buffer tail timestamp for (%p) to %11llu => %11lld\n",
760                 this, new_timestamp, ts);
761    
762 #ifdef DEBUG
763     if (new_timestamp >= m_wrap_at) {
764         debugWarning("timestamp not wrapped: %llu\n",new_timestamp);
765     }   
766     if ((ts >= (int64_t)m_wrap_at) || (ts < 0 )) {
767         debugWarning("ts not wrapped correctly: %lld\n",ts);
768     }
769 #endif
770
771     // update the DLL
772     diff = ts-(int64_t)m_buffer_next_tail_timestamp;
773    
774 #ifdef DEBUG
775     if ((diff > 1000) || (diff < -1000)) {
776         debugWarning("(%p) difference rather large: %lld, %011lld, %011lld\n",
777             this, diff, ts, m_buffer_next_tail_timestamp);
778     }
779 #endif
780
781     // idea to implement it for nbframes values that differ from m_update_period:
782     // diff = diff * nbframes/m_update_period
783     // m_buffer_next_tail_timestamp = m_buffer_tail_timestamp + diff
784    
785     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "(%p): diff=%lld ",
786                 this, diff);
787                    
788     // the maximal difference we can allow (64secs)
789     const int64_t max=m_wrap_at/2;
790    
791     if(diff > max) {
792         diff -= m_wrap_at;
793     } else if (diff < -max) {
794         diff += m_wrap_at;
795     }
796    
797     float err=diff;
798    
799     debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, "diff2=%lld err=%f\n",
800                     diff, err);
801     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "B: FC=%10u, TS=%011llu, NTS=%011llu\n",
802                     m_framecounter, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp);
803    
804     pthread_mutex_lock(&m_framecounter_lock);
805     m_framecounter += nbframes;
806    
807     m_buffer_tail_timestamp=m_buffer_next_tail_timestamp;
808     m_buffer_next_tail_timestamp += (int64_t)(m_dll_b * err + m_dll_e2);
809    
810     m_dll_e2 += m_dll_c*err;
811    
812     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "U: FC=%10u, TS=%011llu, NTS=%011llu\n",
813                     m_framecounter, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp);
814    
815     if (m_buffer_next_tail_timestamp >= m_wrap_at) {
816         debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Unwrapping next tail timestamp: %011llu",
817                 m_buffer_next_tail_timestamp);
818                
819         m_buffer_next_tail_timestamp -= m_wrap_at;
820        
821         debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, " => %011llu\n",
822                 m_buffer_next_tail_timestamp);
823
824     }
825    
826     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "A: TS=%011llu, NTS=%011llu, DLLe2=%f, RATE=%f\n",
827                 m_buffer_tail_timestamp, m_buffer_next_tail_timestamp, m_dll_e2, rate);
828    
829     pthread_mutex_unlock(&m_framecounter_lock);
830    
831     if(m_buffer_tail_timestamp>=m_wrap_at) {
832         debugError("Wrapping failed for m_buffer_tail_timestamp! %011llu\n",m_buffer_tail_timestamp);
833         debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " IN=%011lld, TS=%011llu, NTS=%011llu\n",
834                     ts, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp);
835        
836     }
837     if(m_buffer_next_tail_timestamp>=m_wrap_at) {
838         debugError("Wrapping failed for m_buffer_next_tail_timestamp! %011llu\n",m_buffer_next_tail_timestamp);
839         debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " IN=%011lld, TS=%011llu, NTS=%011llu\n",
840                     ts, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp);
841     }
842    
843     // this DLL allows the calculation of any sample timestamp relative to the buffer tail,
844     // to the next period and beyond (through extrapolation)
845     //
846     // ts(x) = m_buffer_tail_timestamp +
847     //         (m_buffer_next_tail_timestamp - m_buffer_tail_timestamp)/(samples_between_updates)*x
848    
849 }
850
851 /**
852  * @brief Print status info.
853  */
854 void TimestampedBuffer::dumpInfo() {
855    
856     uint64_t ts_head, fc;
857     getBufferHeadTimestamp(&ts_head,&fc);
858    
859     int64_t diff=(int64_t)ts_head - (int64_t)m_buffer_tail_timestamp;
860
861     debugOutputShort( DEBUG_LEVEL_NORMAL, "  TimestampedBuffer (%p) info:\n",this);
862     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Frame counter         : %d\n", m_framecounter);
863     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Buffer head timestamp : %011llu\n",ts_head);
864     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Buffer tail timestamp : %011llu\n",m_buffer_tail_timestamp);
865     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Head - Tail           : %011lld\n",diff);
866     debugOutputShort( DEBUG_LEVEL_NORMAL, "  rate                  : %f (%f)\n",m_dll_e2,m_dll_e2/m_update_period);
867 }
868
869 } // end of namespace Util
Note: See TracBrowser for help on using the browser.