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

Revision 397, 25.9 kB (checked in by pieterpalmers, 16 years ago)

- make timestampedbuffer use floats instead of doubles
- change iso receive back to the efficient case

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