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

Revision 407, 27.4 kB (checked in by pieterpalmers, 16 years ago)

- Changed the way the device class configure options are handled. Now they are handled in the makefiles instead of the source files. The only source file that still contains the #ifdef's is devicemanager.cpp, to conditionally include the device class include files and to conditionally probe the classes that might be supported.
- added a configure option to disable the compilation of the test programs in tests/
- cleaned up the ADMTP transmit streamprocessor. Now it sends silenced packets when in the disabled state, instead of no-data packets
- added a getNodeID() to ieee1394service
- made comments in ieee1394service.h doxygen compliant

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 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     int64_t diff=m_buffer_next_tail_timestamp - m_buffer_tail_timestamp;
569     if (diff < 0) diff += m_wrap_at;
570
571     pthread_mutex_lock(&m_framecounter_lock);
572    
573     m_buffer_tail_timestamp = ts;
574    
575     m_dll_e2=m_update_period * m_nominal_rate;
576     m_buffer_next_tail_timestamp = (uint64_t)((float)m_buffer_tail_timestamp + m_dll_e2);
577    
578     pthread_mutex_unlock(&m_framecounter_lock);   
579    
580     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Set buffer tail timestamp for (%p) to %11llu => %11lld, NTS=%llu, DLL2=%f, RATE=%f\n",
581                 this, new_timestamp, ts, m_buffer_next_tail_timestamp, m_dll_e2, m_nominal_rate);
582
583 }
584
585 /**
586  * @brief Sets the buffer head timestamp.
587  *
588  * Set the buffer tail timestamp such that the buffer head timestamp becomes
589  * \ref new_timestamp. This does not consider offsets, because it's use is to
590  * make sure the following is true after setBufferHeadTimestamp(x):
591  *   x == getBufferHeadTimestamp()
592  *
593  * This is thread safe.
594  *
595  * @param new_timestamp
596  */
597 void TimestampedBuffer::setBufferHeadTimestamp(uint64_t new_timestamp) {
598    
599 #ifdef DEBUG
600     if (new_timestamp >= m_wrap_at) {
601         debugWarning("timestamp not wrapped: %llu\n",new_timestamp);
602     }   
603 #endif
604
605     int64_t ts=new_timestamp;
606
607     int64_t diff=m_buffer_next_tail_timestamp - m_buffer_tail_timestamp;
608     if (diff < 0) diff += m_wrap_at;
609    
610     pthread_mutex_lock(&m_framecounter_lock);
611    
612     // add the time
613     ts += (int64_t)(m_nominal_rate * (float)m_framecounter);
614    
615     if (ts >= (int64_t)m_wrap_at) {
616         ts -= m_wrap_at;
617     } else if (ts < 0) {
618         ts += m_wrap_at;
619     }
620    
621     m_buffer_tail_timestamp = ts;
622    
623     m_dll_e2=m_update_period * m_nominal_rate;
624     m_buffer_next_tail_timestamp = (uint64_t)((float)m_buffer_tail_timestamp + m_dll_e2);
625    
626     pthread_mutex_unlock(&m_framecounter_lock);   
627    
628     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Set buffer head timestamp for (%p) to %11llu => %11lld, NTS=%llu, DLL2=%f, RATE=%f\n",
629                 this, new_timestamp, ts, m_buffer_next_tail_timestamp, m_dll_e2, m_nominal_rate);
630
631 }
632
633 /**
634  * \brief return the timestamp of the first frame in the buffer
635  *
636  * This function returns the timestamp of the very first sample in
637  * the StreamProcessor's buffer. It also returns the framecounter value
638  * for which this timestamp is valid.
639  *
640  * @param ts address to store the timestamp in
641  * @param fc address to store the associated framecounter in
642  */
643 void TimestampedBuffer::getBufferHeadTimestamp(uint64_t *ts, uint64_t *fc) {
644     // NOTE: this is still ok with threads, because we use *fc to compute
645     //       the timestamp
646     *fc = m_framecounter;
647     *ts=getTimestampFromTail(*fc);
648 }
649
650 /**
651  * \brief return the timestamp of the last frame in the buffer
652  *
653  * This function returns the timestamp of the last frame in
654  * the StreamProcessor's buffer. It also returns the framecounter
655  * value for which this timestamp is valid.
656  *
657  * @param ts address to store the timestamp in
658  * @param fc address to store the associated framecounter in
659  */
660 void TimestampedBuffer::getBufferTailTimestamp(uint64_t *ts, uint64_t *fc) {
661     pthread_mutex_lock(&m_framecounter_lock);
662     *fc = (uint64_t)m_framecounter;
663     *ts = m_buffer_tail_timestamp;
664     pthread_mutex_unlock(&m_framecounter_lock);
665 }
666
667 /**
668  * @brief Get timestamp for a specific position from the buffer tail
669  *
670  * Returns the timestamp for a position that is nframes earlier than the
671  * buffer tail
672  *
673  * @param nframes number of frames
674  * @return timestamp value
675  */
676 uint64_t TimestampedBuffer::getTimestampFromTail(int nframes)
677 {
678     // ts(x) = m_buffer_tail_timestamp -
679     //         (m_buffer_next_tail_timestamp - m_buffer_tail_timestamp)/(samples_between_updates)*(x)
680    
681     int64_t diff=m_buffer_next_tail_timestamp - m_buffer_tail_timestamp;
682     if (diff < 0) diff += m_wrap_at;
683    
684     float rate=(float)diff / (float)m_update_period;
685    
686     int64_t timestamp;
687    
688     pthread_mutex_lock(&m_framecounter_lock);
689
690     timestamp=(int64_t)m_buffer_tail_timestamp - (int64_t)((nframes) * rate);
691
692     pthread_mutex_unlock(&m_framecounter_lock);
693    
694     if(timestamp >= (int64_t)m_wrap_at) {
695         timestamp -= m_wrap_at;
696     } else if(timestamp < 0) {
697         timestamp += m_wrap_at;
698     }
699    
700     return (uint64_t)timestamp;
701 }
702
703 /**
704  * @brief Get timestamp for a specific position from the buffer head
705  *
706  * Returns the timestamp for a position that is nframes later than the
707  * buffer head
708  *
709  * @param nframes number of frames
710  * @return timestamp value
711  */
712 uint64_t TimestampedBuffer::getTimestampFromHead(int nframes)
713 {
714     return getTimestampFromTail(m_framecounter-nframes);
715 }
716
717 /**
718  * Resets the frame counter, in a atomic way. This
719  * is thread safe.
720  */
721 void TimestampedBuffer::resetFrameCounter() {
722     pthread_mutex_lock(&m_framecounter_lock);
723     m_framecounter = 0;
724     pthread_mutex_unlock(&m_framecounter_lock);
725 }
726
727 /**
728  * Decrements the frame counter in a thread safe way.
729  *
730  * @param nbframes number of frames to decrement
731  */
732 void TimestampedBuffer::decrementFrameCounter(int nbframes) {
733     pthread_mutex_lock(&m_framecounter_lock);
734     m_framecounter -= nbframes;
735     pthread_mutex_unlock(&m_framecounter_lock);
736 }
737
738 /**
739  * Increments the frame counter in a thread safe way.
740  * Also updates the timestamp.
741  *
742  * @note the offsets defined by setTicksOffset and setFrameOffset
743  *       are added here.
744  *
745  * @param nbframes the number of frames to add
746  * @param new_timestamp the new timestamp
747  */
748 void TimestampedBuffer::incrementFrameCounter(int nbframes, uint64_t new_timestamp) {
749    
750     // add the offsets
751     int64_t diff=m_buffer_next_tail_timestamp - m_buffer_tail_timestamp;
752     if (diff < 0) diff += m_wrap_at;
753    
754     float rate=(float)diff / (float)m_update_period;
755        
756     int64_t ts=new_timestamp;
757     ts += (int64_t)m_tick_offset;
758    
759     if (ts >= (int64_t)m_wrap_at) {
760         ts -= m_wrap_at;
761     } else if (ts < 0) {
762         ts += m_wrap_at;
763     }
764    
765     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Setting buffer tail timestamp for (%p) to %11llu => %11lld\n",
766                 this, new_timestamp, ts);
767    
768 #ifdef DEBUG
769     if (new_timestamp >= m_wrap_at) {
770         debugWarning("timestamp not wrapped: %llu\n",new_timestamp);
771     }   
772     if ((ts >= (int64_t)m_wrap_at) || (ts < 0 )) {
773         debugWarning("ts not wrapped correctly: %lld\n",ts);
774     }
775 #endif
776
777     // update the DLL
778     diff = ts-(int64_t)m_buffer_next_tail_timestamp;
779    
780 #ifdef DEBUG
781     if ((diff > 1000) || (diff < -1000)) {
782         debugWarning("(%p) difference rather large: %lld, %011lld, %011lld\n",
783             this, diff, ts, m_buffer_next_tail_timestamp);
784     }
785 #endif
786
787     // idea to implement it for nbframes values that differ from m_update_period:
788     // diff = diff * nbframes/m_update_period
789     // m_buffer_next_tail_timestamp = m_buffer_tail_timestamp + diff
790    
791     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "(%p): diff=%lld ",
792                 this, diff);
793                    
794     // the maximal difference we can allow (64secs)
795     const int64_t max=m_wrap_at/2;
796    
797     if(diff > max) {
798         diff -= m_wrap_at;
799     } else if (diff < -max) {
800         diff += m_wrap_at;
801     }
802    
803     float err=diff;
804    
805     debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, "diff2=%lld err=%f\n",
806                     diff, err);
807     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "B: FC=%10u, TS=%011llu, NTS=%011llu\n",
808                     m_framecounter, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp);
809    
810     pthread_mutex_lock(&m_framecounter_lock);
811     m_framecounter += nbframes;
812    
813     m_buffer_tail_timestamp=m_buffer_next_tail_timestamp;
814     m_buffer_next_tail_timestamp += (int64_t)(m_dll_b * err + m_dll_e2);
815    
816     m_dll_e2 += m_dll_c*err;
817    
818     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "U: FC=%10u, TS=%011llu, NTS=%011llu\n",
819                     m_framecounter, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp);
820    
821     if (m_buffer_next_tail_timestamp >= m_wrap_at) {
822         debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Unwrapping next tail timestamp: %011llu",
823                 m_buffer_next_tail_timestamp);
824                
825         m_buffer_next_tail_timestamp -= m_wrap_at;
826        
827         debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, " => %011llu\n",
828                 m_buffer_next_tail_timestamp);
829
830     }
831    
832     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "A: TS=%011llu, NTS=%011llu, DLLe2=%f, RATE=%f\n",
833                 m_buffer_tail_timestamp, m_buffer_next_tail_timestamp, m_dll_e2, rate);
834    
835     pthread_mutex_unlock(&m_framecounter_lock);
836    
837     if(m_buffer_tail_timestamp>=m_wrap_at) {
838         debugError("Wrapping failed for m_buffer_tail_timestamp! %011llu\n",m_buffer_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     if(m_buffer_next_tail_timestamp>=m_wrap_at) {
844         debugError("Wrapping failed for m_buffer_next_tail_timestamp! %011llu\n",m_buffer_next_tail_timestamp);
845         debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " IN=%011lld, TS=%011llu, NTS=%011llu\n",
846                     ts, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp);
847     }
848    
849     // this DLL allows the calculation of any sample timestamp relative to the buffer tail,
850     // to the next period and beyond (through extrapolation)
851     //
852     // ts(x) = m_buffer_tail_timestamp +
853     //         (m_buffer_next_tail_timestamp - m_buffer_tail_timestamp)/(samples_between_updates)*x
854    
855 }
856
857 /**
858  * @brief Print status info.
859  */
860 void TimestampedBuffer::dumpInfo() {
861    
862     uint64_t ts_head, fc;
863     getBufferHeadTimestamp(&ts_head,&fc);
864    
865     int64_t diff=(int64_t)ts_head - (int64_t)m_buffer_tail_timestamp;
866
867     debugOutputShort( DEBUG_LEVEL_NORMAL, "  TimestampedBuffer (%p) info:\n",this);
868     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Frame counter         : %d\n", m_framecounter);
869     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Buffer head timestamp : %011llu\n",ts_head);
870     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Buffer tail timestamp : %011llu\n",m_buffer_tail_timestamp);
871     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Head - Tail           : %011lld\n",diff);
872     debugOutputShort( DEBUG_LEVEL_NORMAL, "  rate                  : %f (%f)\n",m_dll_e2,m_dll_e2/m_update_period);
873 }
874
875 } // end of namespace FreebobUtil
Note: See TracBrowser for help on using the browser.