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

Revision 392, 22.5 kB (checked in by pieterpalmers, 16 years ago)

- document TimestampedBuffer? class
- partially ported timestamp handling to TimestampedBuffer?
- introduced test for TimestampedBuffer? class

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 FreebobUtil {
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), m_buffer_tail_timestamp(0),
45       m_buffer_next_tail_timestamp(0),
46       m_dll_e2(0.0), m_dll_b(0.877), m_dll_c(0.384),
47       m_nominal_rate(0.0), m_update_period(0)
48 {
49     pthread_mutex_init(&m_framecounter_lock, NULL);
50
51 }
52
53 TimestampedBuffer::~TimestampedBuffer() {
54     freebob_ringbuffer_free(m_event_buffer);
55     free(m_cluster_buffer);
56 }
57
58 /**
59  * \brief Set the nominal rate in frames/timeunit
60  *
61  * Sets the nominal rate in frames per time unit. This rate is used
62  * to initialize the DLL that will extract the effective rate based
63  * upon the timestamps it gets fed.
64  *
65  * @param r rate
66  * @return true if successful
67  */
68 bool TimestampedBuffer::setNominalRate(float r) {
69     m_nominal_rate=r;
70     debugOutput(DEBUG_LEVEL_VERBOSE," nominal rate=%e set to %e\n",
71                                     m_nominal_rate, r);   
72     return true;
73 }
74
75 /**
76  * \brief Set the nominal update period (in frames)
77  *
78  * Sets the nominal update period. This period is the number of frames
79  * between two timestamp updates (hence buffer writes)
80  *
81  * @param n period in frames
82  * @return true if successful
83  */
84 bool TimestampedBuffer::setUpdatePeriod(unsigned int n) {
85     m_update_period=n;
86     return true;
87 }
88
89 /**
90  * \brief set the value at which timestamps should wrap around
91  * @param w value to wrap at
92  * @return true if successful
93  */
94 bool TimestampedBuffer::setWrapValue(uint64_t w) {
95     m_wrap_at=w;
96     return true;
97 }
98
99 /**
100  * \brief return the effective rate
101  *
102  * Returns the effective rate calculated by the DLL.
103  *
104  * @return rate (in frames/timeunit)
105  */
106 float TimestampedBuffer::getRate() {
107     return ((float) m_update_period)/m_dll_e2;
108 }
109
110 /**
111  * \brief Sets the size of the events
112  * @param s event size in bytes
113  * @return true if successful
114  */
115 bool TimestampedBuffer::setEventSize(unsigned int s) {
116     m_event_size=s;
117    
118     m_bytes_per_frame=m_event_size*m_events_per_frame;
119     m_bytes_per_buffer=m_bytes_per_frame*m_buffer_size;
120    
121     return true;
122 }
123
124 /**
125  * \brief Sets the number of events per frame
126  * @param n number of events per frame
127  * @return true if successful
128  */
129 bool TimestampedBuffer::setEventsPerFrame(unsigned int n) {
130     m_events_per_frame=n;
131    
132     m_bytes_per_frame=m_event_size*m_events_per_frame;
133     m_bytes_per_buffer=m_bytes_per_frame*m_buffer_size;
134    
135     return true;
136 }
137 /**
138  * \brief Sets the buffer size in frames
139  * @param n number frames
140  * @return true if successful
141  */
142 bool TimestampedBuffer::setBufferSize(unsigned int n) {
143     m_buffer_size=n;
144
145     m_bytes_per_frame=m_event_size*m_events_per_frame;
146     m_bytes_per_buffer=m_bytes_per_frame*m_buffer_size;
147
148     return true;
149 }
150
151 /**
152  * \brief Returns the current fill of the buffer
153  *
154  * This returns the buffer fill of the internal ringbuffer. This
155  * can only be used as an indication because it's state is not
156  * guaranteed to be consistent at all times due to threading issues.
157  *
158  * In order to get the number of frames in the buffer, use the
159  * getFrameCounter, getBufferHeadTimestamp, getBufferTailTimestamp
160  * functions
161  *
162  * @return the internal buffer fill in frames
163  */
164 unsigned int TimestampedBuffer::getBufferFill() {
165     return freebob_ringbuffer_read_space(m_event_buffer)/(m_bytes_per_frame);
166 }
167
168 /**
169  * \brief Initializes the TimestampedBuffer
170  *
171  * Initializes the TimestampedBuffer, should be called before anything else
172  * is done.
173  *
174  * @return true if successful
175  */
176 bool TimestampedBuffer::init() {
177     return true;
178 }
179
180 /**
181  * \brief Resets the TimestampedBuffer
182  *
183  * Resets the TimestampedBuffer, clearing the buffers and counters.
184  * (not true yet: Also resets the DLL to the nominal values.)
185  *
186  * \note when this is called, you should make sure that the buffer
187  *       tail timestamp gets set before continuing
188  *
189  * @return true if successful
190  */
191 bool TimestampedBuffer::reset() {
192     freebob_ringbuffer_reset(m_event_buffer);
193    
194     resetFrameCounter();
195    
196     return true;
197 }
198
199 /**
200  * \brief Perpares the TimestampedBuffer
201  *
202  * Prepare the TimestampedBuffer. This allocates all internal buffers and
203  * initializes all data structures.
204  *
205  * This should be called after parameters such as buffer size, event size etc.. are set,
206  * and before any read/write operations are performed.
207  *
208  * @return true if successful
209  */
210 bool TimestampedBuffer::prepare() {
211     debugOutput(DEBUG_LEVEL_VERBOSE,"Preparing buffer (%p)\n",this);
212     debugOutput(DEBUG_LEVEL_VERBOSE," Size=%u events, events/frame=%u, event size=%ubytes\n",
213                                         m_buffer_size,m_events_per_frame,m_event_size);
214                                        
215     debugOutput(DEBUG_LEVEL_VERBOSE," update period %u\n",
216                                     m_update_period);
217     debugOutput(DEBUG_LEVEL_VERBOSE," nominal rate=%f\n",
218                                     m_nominal_rate);
219                                    
220     debugOutput(DEBUG_LEVEL_VERBOSE," wrapping at %llu\n",m_wrap_at);
221
222     assert(m_buffer_size);
223     assert(m_events_per_frame);
224     assert(m_event_size);
225
226     assert(m_nominal_rate != 0.0L);
227     assert(m_update_period != 0);
228    
229     if( !(m_event_buffer=freebob_ringbuffer_create(
230             (m_events_per_frame * m_buffer_size) * m_event_size))) {
231         debugFatal("Could not allocate memory event ringbuffer\n");
232         return false;
233     }
234    
235     // allocate the temporary cluster buffer
236     if( !(m_cluster_buffer=(char *)calloc(m_events_per_frame,m_event_size))) {
237             debugFatal("Could not allocate temporary cluster buffer\n");
238         freebob_ringbuffer_free(m_event_buffer);
239         return false;
240     }
241    
242     // init the DLL
243     m_dll_e2=m_nominal_rate * (double)m_update_period;
244    
245     m_dll_b=((double)(0.877));
246     m_dll_c=((double)(0.384));
247
248     return true;
249 }
250
251 /**
252  * @brief Write frames to the buffer
253  *
254  * Copies \ref nframes of frames from the buffer pointed to by \ref data to the
255  * internal ringbuffer. The time of the last frame in the buffer is set to \ref ts.
256  *
257  * @param nframes number of frames to copy
258  * @param data pointer to the frame buffer
259  * @param ts timestamp of the last frame copied
260  * @return true if successful
261  */
262 bool TimestampedBuffer::writeFrames(unsigned int nframes, char *data, uint64_t ts) {
263
264     unsigned int write_size=nframes*m_event_size*m_events_per_frame;
265
266     // add the data payload to the ringbuffer
267     if (freebob_ringbuffer_write(m_event_buffer,data,write_size) < write_size)
268     {
269         debugWarning("writeFrames buffer overrun\n");
270         return false;
271     }
272    
273     incrementFrameCounter(nframes,ts);
274    
275     return true;
276
277 }
278 /**
279  * @brief Read frames from the buffer
280  *
281  * Copies \ref nframes of frames from the internal buffer to the data buffer pointed
282  * to by \ref data.
283  *
284  * @param nframes number of frames to copy
285  * @param data pointer to the frame buffer
286  * @return true if successful
287  */
288 bool TimestampedBuffer::readFrames(unsigned int nframes, char *data) {
289
290     unsigned int read_size=nframes*m_event_size*m_events_per_frame;
291
292     // get the data payload to the ringbuffer
293     if ((freebob_ringbuffer_read(m_event_buffer,data,read_size)) < read_size)
294     {
295         debugWarning("readFrames buffer underrun\n");
296         return false;
297     }
298    
299     decrementFrameCounter(nframes);
300    
301     return true;
302
303 }
304
305 /**
306  * @brief Performs block processing write of frames
307  *
308  * This function allows for zero-copy writing into the ringbuffer.
309  * It calls the client's processWriteBlock function to write frames
310  * into the internal buffer's data area, in a thread safe fashion.
311  *
312  * It also updates the timestamp.
313  *
314  * @param nbframes number of frames to process
315  * @param ts timestamp of the last frame written to the buffer
316  * @return true if successful
317  */
318 bool TimestampedBuffer::blockProcessWriteFrames(unsigned int nbframes, int64_t ts) {
319
320     debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Transferring period...\n");
321     int xrun;
322     unsigned int offset=0;
323    
324     freebob_ringbuffer_data_t vec[2];
325     // we received one period of frames
326     // this is period_size*dimension of events
327     unsigned int events2write=nbframes*m_events_per_frame;
328     unsigned int bytes2write=events2write*m_event_size;
329
330     /* write events2write bytes to the ringbuffer
331     *  first see if it can be done in one read.
332     *  if so, ok.
333     *  otherwise write up to a multiple of clusters directly to the buffer
334     *  then do the buffer wrap around using ringbuffer_write
335     *  then write the remaining data directly to the buffer in a third pass
336     *  Make sure that we cannot end up on a non-cluster aligned position!
337     */
338     unsigned int cluster_size=m_events_per_frame*m_event_size;
339
340     while(bytes2write>0) {
341         int byteswritten=0;
342        
343         unsigned int frameswritten=(nbframes*cluster_size-bytes2write)/cluster_size;
344         offset=frameswritten;
345        
346         freebob_ringbuffer_get_write_vector(m_event_buffer, vec);
347            
348         if(vec[0].len==0) { // this indicates a full event buffer
349             debugError("Event buffer overrun in buffer %p\n",this);
350             break;
351         }
352            
353         /* if we don't take care we will get stuck in an infinite loop
354         * because we align to a cluster boundary later
355         * the remaining nb of bytes in one write operation can be
356         * smaller than one cluster
357         * this can happen because the ringbuffer size is always a power of 2
358         */
359         if(vec[0].len<cluster_size) {
360            
361             // encode to the temporary buffer
362             xrun = m_Client->processWriteBlock(m_cluster_buffer, 1, offset);
363            
364             if(xrun<0) {
365                 // xrun detected
366                 debugError("Frame buffer underrun in buffer %p\n",this);
367                 return false;
368             }
369                
370             // use the ringbuffer function to write one cluster
371             // the write function handles the wrap around.
372             freebob_ringbuffer_write(m_event_buffer,
373                          m_cluster_buffer,
374                          cluster_size);
375                
376             // we advanced one cluster_size
377             bytes2write-=cluster_size;
378                
379         } else { //
380            
381             if(bytes2write>vec[0].len) {
382                 // align to a cluster boundary
383                 byteswritten=vec[0].len-(vec[0].len%cluster_size);
384             } else {
385                 byteswritten=bytes2write;
386             }
387                
388             xrun = m_Client->processWriteBlock(vec[0].buf,
389                          byteswritten/cluster_size,
390                          offset);
391            
392             if(xrun<0) {
393                     // xrun detected
394                 debugError("Frame buffer underrun in buffer %p\n",this);
395                 return false; // FIXME: return false ?
396             }
397
398             freebob_ringbuffer_write_advance(m_event_buffer, byteswritten);
399             bytes2write -= byteswritten;
400         }
401
402         // the bytes2write should always be cluster aligned
403         assert(bytes2write%cluster_size==0);
404
405     }
406    
407     incrementFrameCounter(nbframes,ts);
408    
409     return true;
410    
411 }
412
413 /**
414  * @brief Performs block processing read of frames
415  *
416  * This function allows for zero-copy reading from the ringbuffer.
417  * It calls the client's processReadBlock function to read frames
418  * directly from the internal buffer's data area, in a thread safe
419  * fashion.
420  *
421  * @param nbframes number of frames to process
422  * @return true if successful
423  */
424 bool TimestampedBuffer::blockProcessReadFrames(unsigned int nbframes) {
425
426     debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Reading %u from buffer (%p)...\n", nbframes, this);
427    
428     int xrun;
429     unsigned int offset=0;
430    
431     freebob_ringbuffer_data_t vec[2];
432     // we received one period of frames on each connection
433     // this is period_size*dimension of events
434
435     unsigned int events2read=nbframes*m_events_per_frame;
436     unsigned int bytes2read=events2read*m_event_size;
437     /* read events2read bytes from the ringbuffer
438     *  first see if it can be done in one read.
439     *  if so, ok.
440     *  otherwise read up to a multiple of clusters directly from the buffer
441     *  then do the buffer wrap around using ringbuffer_read
442     *  then read the remaining data directly from the buffer in a third pass
443     *  Make sure that we cannot end up on a non-cluster aligned position!
444     */
445     unsigned int cluster_size=m_events_per_frame*m_event_size;
446    
447     while(bytes2read>0) {
448         unsigned int framesread=(nbframes*cluster_size-bytes2read)/cluster_size;
449         offset=framesread;
450
451         int bytesread=0;
452
453         freebob_ringbuffer_get_read_vector(m_event_buffer, vec);
454
455         if(vec[0].len==0) { // this indicates an empty event buffer
456             debugError("Event buffer underrun in buffer %p\n",this);
457             return false;
458         }
459
460         /* if we don't take care we will get stuck in an infinite loop
461         * because we align to a cluster boundary later
462         * the remaining nb of bytes in one read operation can be smaller than one cluster
463         * this can happen because the ringbuffer size is always a power of 2
464                 */
465         if(vec[0].len<cluster_size) {
466             // use the ringbuffer function to read one cluster
467             // the read function handles wrap around
468             freebob_ringbuffer_read(m_event_buffer,m_cluster_buffer,cluster_size);
469
470             assert(m_Client);
471             xrun = m_Client->processReadBlock(m_cluster_buffer, 1, offset);
472
473             if(xrun<0) {
474                 // xrun detected
475                 debugError("Frame buffer overrun in buffer %p\n",this);
476                     return false;
477             }
478
479             // we advanced one cluster_size
480             bytes2read-=cluster_size;
481
482         } else { //
483
484             if(bytes2read>vec[0].len) {
485                 // align to a cluster boundary
486                 bytesread=vec[0].len-(vec[0].len%cluster_size);
487             } else {
488                 bytesread=bytes2read;
489             }
490            
491             assert(m_Client);
492             xrun = m_Client->processReadBlock(vec[0].buf, bytesread/cluster_size, 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             freebob_ringbuffer_read_advance(m_event_buffer, bytesread);
501             bytes2read -= bytesread;
502         }
503
504         // the bytes2read should always be cluster aligned
505         assert(bytes2read%cluster_size==0);
506     }
507
508     decrementFrameCounter(nbframes);
509    
510     return true;
511 }
512
513 /**
514  * @brief Sets the buffer tail timestamp.
515  *
516  * Set the buffer tail timestamp to \ref new_timestamp. This will recalculate
517  * the internal state such that the buffer's timeframe starts at
518  * \ref new_timestamp.
519  *
520  * This is thread safe.
521  *
522  * @param new_timestamp
523  */
524 void TimestampedBuffer::setBufferTailTimestamp(uint64_t new_timestamp) {
525    
526     pthread_mutex_lock(&m_framecounter_lock);
527    
528     m_buffer_tail_timestamp = new_timestamp;
529    
530     m_dll_e2=m_update_period * m_nominal_rate;
531     m_buffer_next_tail_timestamp = (uint64_t)((double)m_buffer_tail_timestamp + m_dll_e2);
532    
533     pthread_mutex_unlock(&m_framecounter_lock);   
534    
535     debugOutput(DEBUG_LEVEL_VERBOSE, "Set buffer tail timestamp for (%p) to %11llu, NTS=%llu, DLL2=%f\n",
536                 this, new_timestamp, m_buffer_next_tail_timestamp, m_dll_e2);
537
538 }
539
540 /**
541  * \brief return the timestamp of the first frame in the buffer
542  *
543  * This function returns the timestamp of the very first sample in
544  * the StreamProcessor's buffer. It also returns the framecounter value
545  * for which this timestamp is valid.
546  *
547  * @param ts address to store the timestamp in
548  * @param fc address to store the associated framecounter in
549  */
550 void TimestampedBuffer::getBufferHeadTimestamp(uint64_t *ts, uint64_t *fc) {
551     double rate=(double)m_buffer_next_tail_timestamp - (double)m_buffer_tail_timestamp;
552     rate /= (double)m_update_period;
553    
554     int64_t timestamp;
555    
556     pthread_mutex_lock(&m_framecounter_lock);
557     *fc = m_framecounter;
558
559     // ts(x) = m_buffer_tail_timestamp -
560     //         (m_buffer_next_tail_timestamp - m_buffer_tail_timestamp)/(samples_between_updates)*(x)
561    
562     // buffer head is the frame that is framecounter-1 away from the tail
563     timestamp=(int64_t)m_buffer_tail_timestamp - (int64_t)((m_framecounter) * rate);
564
565     pthread_mutex_unlock(&m_framecounter_lock);
566    
567     debugOutput(DEBUG_LEVEL_VERBOSE, "(%p): HTS = %011lld, TTS=%011llu, FC=%05u, RATE=%f\n",
568                 this, timestamp, m_buffer_tail_timestamp, *fc, rate);
569    
570     if(timestamp >= (int64_t)m_wrap_at) {
571         timestamp -= m_wrap_at;
572     } else if(timestamp < 0) {
573         timestamp += m_wrap_at;
574     }
575    
576     *ts=timestamp;
577    
578     debugOutput(DEBUG_LEVEL_VERBOSE, "    HTS = %011lld, FC=%05u\n",
579                 *ts, *fc);
580
581 }
582
583 /**
584  * \brief return the timestamp of the last frame in the buffer
585  *
586  * This function returns the timestamp of the last frame in
587  * the StreamProcessor's buffer. It also returns the framecounter
588  * value for which this timestamp is valid.
589  *
590  * @param ts address to store the timestamp in
591  * @param fc address to store the associated framecounter in
592  */
593 void TimestampedBuffer::getBufferTailTimestamp(uint64_t *ts, uint64_t *fc) {
594     pthread_mutex_lock(&m_framecounter_lock);
595     *fc = m_framecounter;
596     *ts = m_buffer_tail_timestamp;
597     pthread_mutex_unlock(&m_framecounter_lock);
598 }
599
600 /**
601  * Resets the frame counter, in a atomic way. This
602  * is thread safe.
603  */
604 void TimestampedBuffer::resetFrameCounter() {
605     pthread_mutex_lock(&m_framecounter_lock);
606     m_framecounter = 0;
607     pthread_mutex_unlock(&m_framecounter_lock);
608 }
609
610 /**
611  * Decrements the frame counter in a thread safe way.
612  *
613  * @param nbframes number of frames to decrement
614  */
615 void TimestampedBuffer::decrementFrameCounter(int nbframes) {
616     pthread_mutex_lock(&m_framecounter_lock);
617     m_framecounter -= nbframes;
618     pthread_mutex_unlock(&m_framecounter_lock);
619 }
620
621 /**
622  * Increments the frame counter in a thread safe way.
623  * Also updates the timestamp.
624  *
625  * @param nbframes the number of frames to add
626  * @param new_timestamp the new timestamp
627  */
628 void TimestampedBuffer::incrementFrameCounter(int nbframes, uint64_t new_timestamp) {
629     debugOutput(DEBUG_LEVEL_VERBOSE, "Setting buffer tail timestamp for (%p) to %11llu\n",
630                 this, new_timestamp);
631    
632     pthread_mutex_lock(&m_framecounter_lock);
633     m_framecounter += nbframes;
634    
635     // update the DLL
636     int64_t diff = (int64_t)new_timestamp-(int64_t)m_buffer_next_tail_timestamp;
637    
638     // idea to implement it for nbframes values that differ from m_update_period:
639     // diff = diff * nbframes/m_update_period
640     // m_buffer_next_tail_timestamp = m_buffer_tail_timestamp + diff
641    
642     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "(%p): diff=%lld ",
643                 this, diff);
644                    
645     // the maximal difference we can allow (64secs)
646     const int64_t max=m_wrap_at/2;
647    
648     if(diff > max) {
649         diff -= m_wrap_at;
650     } else if (diff < -max) {
651         diff += m_wrap_at;
652     }
653
654     double err=diff;
655    
656     debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, "diff2=%lld err=%f\n",
657                     diff, err);
658     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "B: FC=%10u, TS=%011llu, NTS=%011llu\n",
659                     m_framecounter, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp);
660    
661     m_buffer_tail_timestamp=m_buffer_next_tail_timestamp;
662     m_buffer_next_tail_timestamp += (uint64_t)(m_dll_b * err + m_dll_e2);
663    
664     if (m_buffer_next_tail_timestamp >= m_wrap_at) {
665         m_buffer_next_tail_timestamp -= m_wrap_at;
666        
667         debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Unwrapping next tail timestamp: %11llu\n",
668                 m_buffer_next_tail_timestamp);
669     }
670    
671     m_dll_e2 += m_dll_c*err;
672    
673     debugOutput(DEBUG_LEVEL_VERBOSE, "A: TS=%011llu, NTS=%011llu, DLLe2=%f\n",
674                 m_buffer_tail_timestamp, m_buffer_next_tail_timestamp, m_dll_e2);
675    
676     pthread_mutex_unlock(&m_framecounter_lock);
677    
678     if(m_buffer_tail_timestamp>=m_wrap_at) {
679         debugError("Wrapping failed for m_buffer_tail_timestamp!\n");
680     }
681     if(m_buffer_next_tail_timestamp>=m_wrap_at) {
682         debugError("Wrapping failed for m_buffer_next_tail_timestamp!\n");
683     }
684    
685     // this DLL allows the calculation of any sample timestamp relative to the buffer tail,
686     // to the next period and beyond (through extrapolation)
687     //
688     // ts(x) = m_buffer_tail_timestamp +
689     //         (m_buffer_next_tail_timestamp - m_buffer_tail_timestamp)/(samples_between_updates)*x
690    
691 }
692
693 /**
694  * @brief Print status info.
695  */
696 void TimestampedBuffer::dumpInfo() {
697    
698     uint64_t ts_head, fc;
699     getBufferHeadTimestamp(&ts_head,&fc);
700    
701     int64_t diff=(int64_t)ts_head - (int64_t)m_buffer_tail_timestamp;
702
703     debugOutputShort( DEBUG_LEVEL_NORMAL, "  TimestampedBuffer (%p) info:\n",this);
704     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Frame counter         : %d\n", m_framecounter);
705     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Buffer head timestamp : %011llu\n",ts_head);
706     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Buffer tail timestamp : %011llu\n",m_buffer_tail_timestamp);
707     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Head - Tail           : %011lld\n",diff);
708     debugOutputShort( DEBUG_LEVEL_NORMAL, "  rate                  : %lf (%f)\n",m_dll_e2,m_dll_e2/m_update_period);
709 }
710
711 } // end of namespace FreebobUtil
Note: See TracBrowser for help on using the browser.