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

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

cycletimer.h:
- some extra operations on Ticks (diffTicks & substractTicks)

StreamProcessor?.cpp
AmdtpStreamProcessor?.cpp
MotuStreamProcessor?.cpp:
- Moved the syncDelay to StreamProcessor::getTimeUntilNextPeriodSignalUsecs(). This delay should be the delay between the actual period boundary and the time it is reported to the SPManager. Therefore it's place is not as a buffer offset, but in the calculation of the signalling time.
This makes that the buffer timestamps correspond to 'real' timestamps. These might have to be manipulated by the transmit or receive handles to account for e.g. iso buffering etc..., but at least the timestamps themselves have a well-defined meaning now.

StreamProcessorManager?.cpp:
- The only stream that needs to be running is the sync source stream. It is assumed that the other streams start running in time. 'In time' is currently about 2000 cycles afterwards.

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     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 FreebobUtil
Note: See TracBrowser for help on using the browser.