|
1 /* |
|
2 * Copyright (c) 2004-2007 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: Implementation of Mcc Jitterbuffer |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 // INCLUDE FILES |
|
22 #include "mmcccodecinformation.h" |
|
23 #include "mccjitterbufferimpl.h" |
|
24 #include "mcccngenerator.h" |
|
25 #include "mccinternalevents.h" |
|
26 #include "mccinternaldef.h" |
|
27 #include "mccjitterbufferlogs.h" |
|
28 #include "mccjitterbufferobserver.h" |
|
29 |
|
30 // LOCAL CONSTANTS |
|
31 const TInt KDefaultSampleRateInkHz = 8; |
|
32 const TInt KWbSampleRateInkHz = 16; |
|
33 const TInt KLowBufferLimit = 2; |
|
34 const TInt KOverflowAlarmLimit = 20; |
|
35 const TInt KSequentialLateFramesLimit = 20; |
|
36 |
|
37 // Codec frame sizes |
|
38 const TInt KAMRNBFrameSize = 32; |
|
39 const TInt KAMRWBFrameSize = 64; |
|
40 const TInt KILBCFrameSize = 52; |
|
41 const TInt KG729FrameSize = 24; // 10 octets + 2 for header |
|
42 |
|
43 // Maximum possible sequence number of RTP packet with standard RTP header. |
|
44 const TInt KMaxSeqNumber = 65535; // 16-bit value |
|
45 |
|
46 // Codec frame times |
|
47 const TInt KAMRNBFrameTime = 20; |
|
48 |
|
49 // ============================= LOCAL FUNCTIONS =============================== |
|
50 |
|
51 // ----------------------------------------------------------------------------- |
|
52 // TJitterBufferElement::CompareSeqNum |
|
53 // Compare SequenceNumber |
|
54 // ----------------------------------------------------------------------------- |
|
55 // |
|
56 TInt TJitterBufferElement::CompareSeqNum( const TJitterBufferElement& aElem1, |
|
57 const TJitterBufferElement& aElem2 ) |
|
58 { |
|
59 if ( aElem1.iSequenceNumber > aElem2.iSequenceNumber ) |
|
60 { |
|
61 return (-1); |
|
62 } |
|
63 else if ( aElem1.iSequenceNumber < aElem2.iSequenceNumber ) |
|
64 { |
|
65 return (1); |
|
66 } |
|
67 else |
|
68 { |
|
69 return (0); |
|
70 } |
|
71 } |
|
72 |
|
73 // ----------------------------------------------------------------------------- |
|
74 // TJitterBufferElement::CompareStamp |
|
75 // Compare Stamp |
|
76 // ----------------------------------------------------------------------------- |
|
77 // |
|
78 TInt TJitterBufferElement::CompareStamp( const TJitterBufferElement& aElem1, |
|
79 const TJitterBufferElement& aElem2 ) |
|
80 { |
|
81 if ( aElem1.iStamp > aElem2.iStamp ) |
|
82 { |
|
83 return (-1); |
|
84 } |
|
85 else if ( aElem1.iStamp < aElem2.iStamp ) |
|
86 { |
|
87 return (1); |
|
88 } |
|
89 else |
|
90 { |
|
91 return (0); |
|
92 } |
|
93 } |
|
94 |
|
95 // ============================ MEMBER FUNCTIONS =============================== |
|
96 |
|
97 // ----------------------------------------------------------------------------- |
|
98 // CMccJitterBufferImpl::CMccJitterBufferImpl |
|
99 // C++ default constructor can NOT contain any code, that |
|
100 // might leave. |
|
101 // ----------------------------------------------------------------------------- |
|
102 // |
|
103 CMccJitterBufferImpl::CMccJitterBufferImpl( MJitterBufferObserver* aObserver): |
|
104 iPacketsInBuffer( 0 ), |
|
105 iLastGetSeqNum( -1 ), |
|
106 iBufStampSorter( |
|
107 TLinearOrder<TJitterBufferElement>( TJitterBufferElement::CompareStamp ) |
|
108 ), |
|
109 iBufSequenceSorter( |
|
110 TLinearOrder<TJitterBufferElement>( TJitterBufferElement::CompareSeqNum ) |
|
111 ), |
|
112 iSeqNumIncrement( 0 ), |
|
113 iPlay( EFalse ), |
|
114 iObserver( aObserver ), |
|
115 iOverflowCounter( 0 ), |
|
116 iPlayToneInterval( 0 ), |
|
117 iNotifyUser( ETrue ), |
|
118 iLatestNotifiedEvent( KMccEventNone ), |
|
119 iSampleRate( KDefaultSampleRateInkHz ) |
|
120 { |
|
121 iTonePlayTime.UniversalTime(); |
|
122 } |
|
123 |
|
124 // ----------------------------------------------------------------------------- |
|
125 // CMccJitterBufferImpl::NewL |
|
126 // Static constructor. |
|
127 // ----------------------------------------------------------------------------- |
|
128 // |
|
129 CMccJitterBufferImpl* CMccJitterBufferImpl::NewL( MJitterBufferObserver* aObserver ) |
|
130 { |
|
131 __JITTER_BUFFER( "CMccJitterBufferImpl::NewL 64bit version" ) |
|
132 |
|
133 CMccJitterBufferImpl* self = new( ELeave ) CMccJitterBufferImpl( aObserver ); |
|
134 CleanupStack::PushL( self ); |
|
135 self->ConstructL(); |
|
136 CleanupStack::Pop( self ); |
|
137 |
|
138 return self; |
|
139 } |
|
140 |
|
141 // ----------------------------------------------------------------------------- |
|
142 // CMccJitterBufferImpl::ConstructL |
|
143 // Symbian 2nd phase constructor can leave. |
|
144 // ----------------------------------------------------------------------------- |
|
145 // |
|
146 void CMccJitterBufferImpl::ConstructL() |
|
147 { |
|
148 SetBufferState( EIdle ); |
|
149 SendStreamEventToClient( KMccStreamIdle, ETrue ); |
|
150 } |
|
151 |
|
152 // ----------------------------------------------------------------------------- |
|
153 // CMccJitterBufferImpl::~CMccJitterBufferImpl |
|
154 // Destructor deallocate memory. |
|
155 // ----------------------------------------------------------------------------- |
|
156 // |
|
157 CMccJitterBufferImpl::~CMccJitterBufferImpl() |
|
158 { |
|
159 __JITTER_BUFFER( "CMccJitterBufferImpl::~CMccJitterBufferImpl entry" ) |
|
160 |
|
161 delete iCnGenerator; |
|
162 |
|
163 // Deallocate payload memory of jitter buffer elements. |
|
164 const TInt count = iBuffer.Count(); |
|
165 for ( TInt i = 0; i < count; i++ ) |
|
166 { |
|
167 delete iBuffer[i].iDataFrame; |
|
168 } |
|
169 |
|
170 iBuffer.Close(); |
|
171 iObserver = NULL; |
|
172 |
|
173 __JITTER_BUFFER( "CMccJitterBufferImpl::~CMccJitterBufferImpl exit" ) |
|
174 } |
|
175 |
|
176 // ----------------------------------------------------------------------------- |
|
177 // CMccJitterBufferImpl::SetupL |
|
178 // Setup Jitterbuffer |
|
179 // ----------------------------------------------------------------------------- |
|
180 // |
|
181 void CMccJitterBufferImpl::SetupL( |
|
182 TInt aPlayoutThreshold, |
|
183 const TMccCodecInfo& aCInfo, |
|
184 CMMFDevSound& aDevSound, |
|
185 MAsyncEventHandler* aEventHandler, |
|
186 TUint32 aEndpointId ) |
|
187 { |
|
188 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aCInfo.iJitterBufBufferLength:", |
|
189 aCInfo.iJitterBufBufferLength ) |
|
190 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aPlayoutThreshold:", |
|
191 aPlayoutThreshold ) |
|
192 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aCInfo.iJitterBufInactivityTimeOut:", |
|
193 aCInfo.iJitterBufInactivityTimeOut ) |
|
194 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aCInfo.iJitterBufPlayToneTimeout:", |
|
195 aCInfo.iJitterBufPlayToneTimeout ) |
|
196 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aCInfo.iJitterBufPlayToneFrequency:", |
|
197 aCInfo.iJitterBufPlayToneFrequency ) |
|
198 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aCInfo.iJitterBufPlayToneDuration:", |
|
199 aCInfo.iJitterBufPlayToneDuration ) |
|
200 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aCInfo.iHwFrameTime:", |
|
201 aCInfo.iHwFrameTime ) |
|
202 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL aCInfo.iPtime:", |
|
203 aCInfo.iPtime ) |
|
204 |
|
205 // Changed if-statement to asserts. Added also check agains 0 hwframetime |
|
206 __ASSERT_ALWAYS( aCInfo.iPtime, User::Leave( KErrArgument ) ); |
|
207 __ASSERT_ALWAYS( aCInfo.iJitterBufBufferLength, User::Leave( KErrArgument ) ); |
|
208 __ASSERT_ALWAYS( aCInfo.iHwFrameTime, User::Leave( KErrArgument ) ); |
|
209 |
|
210 // Save the original HW frame time because we may need it in case of |
|
211 // dynamic G.711 adjustment. |
|
212 const TUint8 origHwtime = iCInfo.iHwFrameTime; |
|
213 iCInfo = aCInfo; |
|
214 iEventHandler = aEventHandler; |
|
215 iEndpointId = aEndpointId; |
|
216 |
|
217 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL origHwtime:", origHwtime ) |
|
218 |
|
219 if( iCInfo.iJitterBufInactivityTimeOut ) |
|
220 { |
|
221 if( ( iCInfo.iJitterBufPlayToneFrequency > 0 ) && |
|
222 ( iCInfo.iJitterBufPlayToneDuration > 0 ) ) |
|
223 { |
|
224 iPlayToneInterval = iCInfo.iJitterBufPlayToneTimeout; |
|
225 iPlay = ETrue; |
|
226 } |
|
227 } |
|
228 |
|
229 TInt bufLenMultiplier = 1; |
|
230 if ( iCInfo.iFourCC == KMMFFourCCCodeAMR ) |
|
231 { |
|
232 iFrameSize = KAMRNBFrameSize; |
|
233 iFrameTime = KAMRNBFrameTime; |
|
234 } |
|
235 else if ( iCInfo.iFourCC == KMMFFourCCCodeAWB ) |
|
236 { |
|
237 iFrameSize = KAMRWBFrameSize; |
|
238 iFrameTime = KAMRNBFrameTime; |
|
239 iSampleRate = KWbSampleRateInkHz; |
|
240 } |
|
241 else if( iCInfo.iFourCC == KMccFourCCIdG711 ) |
|
242 { |
|
243 // G.711 is configured dynamically. Take voip headerlength also in to |
|
244 // account. G.711 hwframetime is in milliseconds, so need to multiply. |
|
245 iFrameSize = ( iCInfo.iHwFrameTime * KDefaultSampleRateInkHz ) |
|
246 + KVoIPHeaderLength; |
|
247 iFrameTime = 0; |
|
248 |
|
249 // In case of G.711 codec dynamic configuration, we may need to double |
|
250 // the jitterbuffer length if HW frame time is changed from 20ms to |
|
251 // 10ms. |
|
252 if ( origHwtime ) |
|
253 { |
|
254 bufLenMultiplier = origHwtime / iCInfo.iHwFrameTime; |
|
255 if ( !bufLenMultiplier ) |
|
256 { |
|
257 bufLenMultiplier = 1; |
|
258 } |
|
259 } |
|
260 } |
|
261 else if( iCInfo.iFourCC == KMccFourCCIdILBC ) |
|
262 { |
|
263 iFrameSize = KILBCFrameSize; |
|
264 iFrameTime = 0; |
|
265 } |
|
266 else if( iCInfo.iFourCC == KMccFourCCIdG729 ) |
|
267 { |
|
268 iFrameSize = KG729FrameSize; |
|
269 iFrameTime = 0; |
|
270 // Multiply G.729 also by two... |
|
271 bufLenMultiplier = 2; |
|
272 } |
|
273 else |
|
274 { |
|
275 __JITTER_BUFFER( "CMccJitterBufferImpl::SetupL KErrNotSupported" ) |
|
276 |
|
277 User::Leave( KErrNotSupported ); |
|
278 } |
|
279 |
|
280 // Delete old buffer & reset it |
|
281 const TInt elems = iBuffer.Count(); |
|
282 for( TInt i = 0; i < elems; i++ ) |
|
283 { |
|
284 delete iBuffer[i].iDataFrame; |
|
285 iBuffer[i].iDataFrame = NULL; |
|
286 } |
|
287 |
|
288 iBuffer.Reset(); |
|
289 |
|
290 // Calculate needed elements |
|
291 TInt bufLen = iCInfo.iJitterBufBufferLength * bufLenMultiplier; |
|
292 |
|
293 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL G.711 bufLenMultiplier ", |
|
294 bufLenMultiplier ) |
|
295 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::SetupL iBufferLength: ", |
|
296 bufLen ) |
|
297 |
|
298 __ASSERT_ALWAYS( aPlayoutThreshold < bufLen, User::Leave( KErrTooBig ) ); |
|
299 |
|
300 // if differences between bufferlength and treshold set by client |
|
301 // is less than 10, increase bufferlength so the differences is 10 |
|
302 // this is to help buffer goes to overflow easily. |
|
303 // Also possible G.711/729 multiplier needs to be taken care of. |
|
304 CheckThresholdBufferLength( bufLen, aPlayoutThreshold ); |
|
305 iCurrentPlayThreshold = aPlayoutThreshold * bufLenMultiplier; |
|
306 iOriginalPlayThreshold = aPlayoutThreshold * bufLenMultiplier; |
|
307 |
|
308 if( iCnGenerator ) |
|
309 { |
|
310 delete iCnGenerator; |
|
311 iCnGenerator = NULL; |
|
312 } |
|
313 |
|
314 iCnGenerator = CMccCnGenerator::NewL( iCInfo.iFourCC, aDevSound ); |
|
315 |
|
316 // Create the elements in the Buffer |
|
317 for( TInt k = 0; k < bufLen; k++ ) |
|
318 { |
|
319 CMMFDataBuffer* buf = CMMFDataBuffer::NewL( iFrameSize ); |
|
320 CleanupStack::PushL( buf ); |
|
321 TJitterBufferElement newElement; |
|
322 newElement.iDataFrame = buf; |
|
323 newElement.iSequenceNumber = -1; |
|
324 newElement.iStamp = -1; |
|
325 iBuffer.AppendL( newElement ); |
|
326 CleanupStack::Pop( buf ); |
|
327 } |
|
328 |
|
329 // Zero the statistic members |
|
330 iFramesLost = 0; |
|
331 iFramesReceived = 0; |
|
332 iNumOfLateFrames = 0; |
|
333 iFramesRemoved = 0; |
|
334 iFramesPlayed = 0; |
|
335 iPacketsInBuffer = 0; |
|
336 |
|
337 // Calculate the sequence number increment |
|
338 iSeqNumIncrement = iSampleRate * iCInfo.iHwFrameTime; |
|
339 } |
|
340 |
|
341 // ----------------------------------------------------------------------------- |
|
342 // CMccJitterBufferImpl::ResetBufferL |
|
343 // Reset Jitterbuffer |
|
344 // ----------------------------------------------------------------------------- |
|
345 // |
|
346 |
|
347 void CMccJitterBufferImpl::ResetBuffer( TBool aPlayTone, TBool aNotifyUser ) |
|
348 { |
|
349 const TInt bufLen = BufferLength(); |
|
350 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::ResetBuffer bufLen: ", bufLen ) |
|
351 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::ResetBuffer pktsIn: ", iPacketsInBuffer ) |
|
352 |
|
353 for( TInt i = 0; i < bufLen; i++ ) |
|
354 { |
|
355 iBuffer[i].iSequenceNumber = -1; |
|
356 iBuffer[i].iStamp = -1; |
|
357 } |
|
358 |
|
359 iLastGetSeqNum = -1; |
|
360 iCurrentPlayThreshold = iOriginalPlayThreshold; |
|
361 iPacketsInBuffer = 0; |
|
362 iPlay = aPlayTone; |
|
363 |
|
364 if ( BufferState() != EIdle && |
|
365 aNotifyUser && |
|
366 iLatestNotifiedEvent != KMccStreamIdle ) |
|
367 { |
|
368 SendStreamEventToClient( KMccStreamIdle, ETrue ); |
|
369 } |
|
370 |
|
371 SetBufferState( EIdle ); |
|
372 iDropNextFrame = EFalse; |
|
373 } |
|
374 |
|
375 // ----------------------------------------------------------------------------- |
|
376 // CMccJitterBufferImpl::CurrentCodec |
|
377 // Return current codec. |
|
378 // ----------------------------------------------------------------------------- |
|
379 // |
|
380 TFourCC CMccJitterBufferImpl::CurrentCodec() const |
|
381 { |
|
382 return iCInfo.iFourCC; |
|
383 } |
|
384 |
|
385 // ----------------------------------------------------------------------------- |
|
386 // CMccJitterBufferImpl::DelayUpL |
|
387 // Adds a buffer element into the jitter buffer |
|
388 // ----------------------------------------------------------------------------- |
|
389 // |
|
390 void CMccJitterBufferImpl::DelayUpL() |
|
391 { |
|
392 CMMFDataBuffer* buf = CMMFDataBuffer::NewL( iFrameSize ); |
|
393 CleanupStack::PushL( buf ); |
|
394 TJitterBufferElement newElement; |
|
395 newElement.iDataFrame = buf; |
|
396 newElement.iSequenceNumber = -1; |
|
397 newElement.iStamp = -1; |
|
398 iBuffer.AppendL( newElement ); |
|
399 CleanupStack::Pop( buf ); |
|
400 |
|
401 // Insert one NO_DATA frame into the audio stream, so the jitterbuffer has |
|
402 // the possibility to grow at least one frame from the current status. |
|
403 // If the current playout threshold is zero, then there is playout ongoing or |
|
404 // a DTX period. This means that we should buffer at least one frame before |
|
405 // starting playout. |
|
406 if( 0 == iCurrentPlayThreshold ) |
|
407 { |
|
408 __JITTER_BUFFER( "CMccJitterBufferImpl::DelayUpL Adjusting Playout Threshold" ) |
|
409 |
|
410 iCurrentPlayThreshold = iPacketsInBuffer + 1; |
|
411 } |
|
412 |
|
413 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::DelayUpL BUF_LEN:", BufferLength() ) |
|
414 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::DelayUpL CUR_FRAMES:", iPacketsInBuffer ) |
|
415 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::DelayUpL PLAY_TH:", iCurrentPlayThreshold ) |
|
416 } |
|
417 |
|
418 // ----------------------------------------------------------------------------- |
|
419 // CMccJitterBufferImpl::DelayDownL |
|
420 // Removes an buffer element from the jitter buffer |
|
421 // ----------------------------------------------------------------------------- |
|
422 // |
|
423 void CMccJitterBufferImpl::DelayDownL() |
|
424 { |
|
425 // We need to remove one frame from the jitterbuffer. If currently we are |
|
426 // in a DTX period, we're in luck as we do not affect voice quality at all. |
|
427 |
|
428 // DURING DTX: |
|
429 // During DTX periods the jitterbuffer can be empty, so we cannot remove |
|
430 // anything from nothing. Then we basically can remove one element from the |
|
431 // buffer so it is shorter. |
|
432 |
|
433 // DURING SPEECH: |
|
434 // We'll need to remove one audio frame and mark the one preceding the |
|
435 // removed frame as bad. Then we'll continue as usual. |
|
436 const TInt bufLen = BufferLength(); |
|
437 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::DelayDownL BUF_LEN:", bufLen ) |
|
438 __JITTER_BUFFER_INT1("CMccJitterBufferImpl::DelayDownL CUR_FRAMES:", iPacketsInBuffer ) |
|
439 |
|
440 if( this->IsEmpty() ) |
|
441 { |
|
442 // Cannot do anything for empty buffer |
|
443 if( KLowBufferLimit < bufLen ) |
|
444 { |
|
445 __JITTER_BUFFER( "CMccJitterBufferImpl::DelayDownL Empty buffer" ) |
|
446 |
|
447 delete iBuffer[bufLen - 1].iDataFrame; |
|
448 iBuffer.Remove( bufLen - 1 ); |
|
449 } |
|
450 else if( 0 < iCurrentPlayThreshold ) |
|
451 { |
|
452 __JITTER_BUFFER( "CMccJitterBufferImpl::DelayDownL Playthreshold modification" ) |
|
453 |
|
454 iCurrentPlayThreshold--; |
|
455 } |
|
456 else |
|
457 { |
|
458 __JITTER_BUFFER( "CMccJitterBufferImpl::DelayDownL Buffer empty, cant do anything" ) |
|
459 // Cannot do anything currently |
|
460 return; |
|
461 } |
|
462 } |
|
463 else if( this->IsFull() ) |
|
464 { |
|
465 // If there is sufficiently data in the buffer, then remove one |
|
466 // frame and mark the one preceding it bad |
|
467 if( KLowBufferLimit < iPacketsInBuffer ) |
|
468 { |
|
469 __JITTER_BUFFER( "CMccJitterBufferImpl::DelayDownL Buffer full, doing removal" ) |
|
470 |
|
471 // Remove the 2nd oldest frame and mark the oldest as bad |
|
472 delete iBuffer[1].iDataFrame; |
|
473 iBuffer.Remove( 1 ); |
|
474 |
|
475 // MARK THE FIRST AS BAD FRAME!!! |
|
476 iPacketsInBuffer--; |
|
477 iFramesRemoved++; |
|
478 } |
|
479 else |
|
480 { |
|
481 // Cant do removing, see if playthreshold can be adjusted |
|
482 if( 0 < iCurrentPlayThreshold ) |
|
483 { |
|
484 __JITTER_BUFFER( "CMccJitterBufferImpl::DelayDownL Buffer full, cant do anything" ) |
|
485 |
|
486 iCurrentPlayThreshold--; |
|
487 } |
|
488 } |
|
489 } |
|
490 else |
|
491 { |
|
492 if( KLowBufferLimit < bufLen ) |
|
493 { |
|
494 __JITTER_BUFFER( "CMccJitterBufferImpl::DelayDownL Some frames, adjusting length" ) |
|
495 |
|
496 // Adjust the length of the buffer |
|
497 delete iBuffer[bufLen - 1].iDataFrame; |
|
498 iBuffer.Remove( bufLen - 1 ); |
|
499 } |
|
500 else |
|
501 { |
|
502 __JITTER_BUFFER( "CMccJitterBufferImpl::DelayDownL Some frames, too small buffer" ) |
|
503 } |
|
504 } |
|
505 } |
|
506 |
|
507 // ----------------------------------------------------------------------------- |
|
508 // CMccJitterBufferImpl::CalculateDelay |
|
509 // Calculates the current delay that jitterbuffer has in the playback |
|
510 // ----------------------------------------------------------------------------- |
|
511 // |
|
512 TTimeIntervalMicroSeconds32 CMccJitterBufferImpl::CalculateDelay() const |
|
513 { |
|
514 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::CalculateDelay:", iCInfo.iPtime * iPacketsInBuffer ) |
|
515 |
|
516 return iCInfo.iPtime * iPacketsInBuffer; |
|
517 } |
|
518 |
|
519 // ----------------------------------------------------------------------------- |
|
520 // CMccJitterBufferImpl::AddDataFrameL |
|
521 // Adds a audio frame into the jitterbuffer passed in the aDataBuffer |
|
522 // ----------------------------------------------------------------------------- |
|
523 // |
|
524 void CMccJitterBufferImpl::AddDataFrameL( CMMFBuffer* aDataBuffer ) |
|
525 { |
|
526 User::LeaveIfNull( aDataBuffer ); |
|
527 const TInt64 seqNum = aDataBuffer->FrameNumber(); |
|
528 iFramesReceived++; |
|
529 |
|
530 const TInt num( FindLargestSeqNum() ); |
|
531 |
|
532 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::AddDataFrameL FRAM_SEQNUM:", seqNum ) |
|
533 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::AddDataFrameL LAST_SEQNUM:", iLastGetSeqNum ) |
|
534 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::AddDataFrameL PACKETS:", iPacketsInBuffer ) |
|
535 |
|
536 const CMMFDataBuffer* dataBuffer( static_cast<CMMFDataBuffer*>( aDataBuffer ) ); |
|
537 |
|
538 if( seqNum > iLastGetSeqNum ) |
|
539 { |
|
540 iNumOfSequentialLateFrames = 0; |
|
541 if( IsFull() ) |
|
542 { |
|
543 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::AddDataFrameL OFLOW dropnext: ", |
|
544 iDropNextFrame ); |
|
545 |
|
546 iFramesRemoved++; |
|
547 iOverflowCounter++; |
|
548 if ( iObserver && iOverflowCounter > KOverflowAlarmLimit ) |
|
549 { |
|
550 __JITTER_BUFFER( "CMccJitterBufferImpl::AddDataFrameL OFLOW reported" ) |
|
551 |
|
552 iOverflowCounter = 0; |
|
553 iObserver->JitterBufferError( MJitterBufferObserver::EBufferOverflow ); |
|
554 ResetBuffer( ETrue, EFalse ); |
|
555 |
|
556 // Do not inform resource unavailability several times. Once playback |
|
557 // is ok again, resource unavailability can be informed if it occurs. |
|
558 SendStreamEventToClient( KMccResourceNotAvailable, ETrue ); |
|
559 iNotifyUser = EFalse; |
|
560 } |
|
561 |
|
562 // Check that we actually have packets to remove from the buffer. |
|
563 if ( !iDropNextFrame && iPacketsInBuffer ) |
|
564 { |
|
565 // Remove the newest frame in the buffer |
|
566 (void) iBuffer[ iPacketsInBuffer - 1 ].iSequenceNumber; |
|
567 iBuffer[ iPacketsInBuffer - 1 ].iSequenceNumber = -1; |
|
568 iBuffer[ iPacketsInBuffer - 1 ].iStamp = -1; |
|
569 iPacketsInBuffer--; |
|
570 |
|
571 iDropNextFrame = ETrue; |
|
572 } |
|
573 else |
|
574 { |
|
575 iDropNextFrame = EFalse; |
|
576 } |
|
577 |
|
578 // If we dropped this frame, then return from here. |
|
579 if ( !iDropNextFrame ) |
|
580 { |
|
581 return; |
|
582 } |
|
583 } |
|
584 else if( IsEmpty() ) |
|
585 { |
|
586 // Buffering |
|
587 HandleNotifications( ETrue ); |
|
588 |
|
589 TTimeIntervalSeconds interval; |
|
590 TTime currentTime; |
|
591 currentTime.UniversalTime(); |
|
592 currentTime.SecondsFrom( iTonePlayTime, interval ); |
|
593 |
|
594 if( iPlay && interval.Int() >= iPlayToneInterval ) |
|
595 { |
|
596 __JITTER_BUFFER( "CMccJitterBufferImpl::AddDataFrameL tone to be played" ) |
|
597 iTonePlayTime.UniversalTime(); |
|
598 iPlay = EFalse; |
|
599 } |
|
600 |
|
601 const TInt64 nextGetSeqNum( iLastGetSeqNum + iSeqNumIncrement ); |
|
602 if( seqNum > nextGetSeqNum ) |
|
603 { |
|
604 // We are empty and there is frames missing between last get frame |
|
605 // and frame sequence number. This might be because of DTX period |
|
606 // so we need to adjust to the situation |
|
607 iLastGetSeqNum = seqNum - iSeqNumIncrement; |
|
608 |
|
609 __JITTER_BUFFER( "CMccJitterBufferImpl::AddDataFrameL MISSING & EMPTY" ) |
|
610 __JITTER_BUFFER_INT2( "CMccJitterBufferImpl::AddDataFrameL ADJ_LAST:", iLastGetSeqNum, |
|
611 " FRM_SEQ:", seqNum ) |
|
612 } |
|
613 } |
|
614 else |
|
615 { |
|
616 __JITTER_BUFFER( "CMccJitterBufferImpl::AddDataFrameL NORMAL" ) |
|
617 |
|
618 iOverflowCounter = 0; |
|
619 } |
|
620 |
|
621 // Reset inactivity timing. NB. all branches need to reset inactivity |
|
622 // and insert the new element. |
|
623 iInactivityTime = 0; |
|
624 InsertBufferElement( dataBuffer->Data(), num, seqNum ); |
|
625 } |
|
626 else |
|
627 { |
|
628 iNumOfLateFrames++; |
|
629 if(++iNumOfSequentialLateFrames > KSequentialLateFramesLimit ) |
|
630 iLastGetSeqNum = -1; |
|
631 |
|
632 __JITTER_BUFFER( "CMccJitterBufferImpl::AddDataFrameL TOO LATE" ) |
|
633 } |
|
634 } |
|
635 |
|
636 // ----------------------------------------------------------------------------- |
|
637 // CMccJitterBufferImpl::GetDataFrameL |
|
638 // Get DataFrame |
|
639 // ----------------------------------------------------------------------------- |
|
640 // |
|
641 void CMccJitterBufferImpl::GetDataFrameL( CMMFBuffer* aBuffer ) |
|
642 { |
|
643 User::LeaveIfNull( aBuffer ); |
|
644 CMMFDataBuffer& dataBuffer = static_cast<CMMFDataBuffer&>( *aBuffer ); |
|
645 TDes8& playBuffer( dataBuffer.Data() ); |
|
646 iFramesPlayed++; |
|
647 |
|
648 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL PACKETS:", iPacketsInBuffer ) |
|
649 |
|
650 if( IsEmpty() ) |
|
651 { |
|
652 // Buffer is empty, so we need to generate CN frames. During DTX period |
|
653 // this is where we'll end up. If we are empty because of DTX period, |
|
654 // the CN generator will do the error concealment. |
|
655 iCnGenerator->GenerateSidPacket( playBuffer, dataBuffer.RequestSize() ); |
|
656 iCurrentPlayThreshold = iOriginalPlayThreshold; |
|
657 |
|
658 // Used for talkburst, if talkburst over, reset buffer and sequence numbers |
|
659 if( iCInfo.iJitterBufInactivityTimeOut > 0 && iInactivityTime >= 0 ) |
|
660 { |
|
661 iInactivityTime = iInactivityTime + iFrameTime; |
|
662 if( iInactivityTime >= (TInt) iCInfo.iJitterBufInactivityTimeOut ) |
|
663 { |
|
664 __JITTER_BUFFER_INT2("CMccJitterBufferImpl::Inactivity timeout:", |
|
665 iCInfo.iJitterBufInactivityTimeOut, |
|
666 " time:", iInactivityTime ) |
|
667 |
|
668 iInactivityTime = -1; |
|
669 ResetBuffer(); |
|
670 } |
|
671 } |
|
672 } |
|
673 else |
|
674 { |
|
675 // This is where we buffer some data before starting the playout so we can |
|
676 // take some jitter by nature without adaptation. |
|
677 if( iPacketsInBuffer < iCurrentPlayThreshold ) |
|
678 { |
|
679 // Give comfort noise when we are buffering for playback start |
|
680 __JITTER_BUFFER( "CMccJitterBufferImpl::GetDataFrameL THRESHOLD NOT REACHED" ) |
|
681 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL iCurrentPlayThreshold", |
|
682 iCurrentPlayThreshold ) |
|
683 |
|
684 iCnGenerator->GenerateSidPacket( |
|
685 playBuffer, dataBuffer.RequestSize() ); |
|
686 } |
|
687 else |
|
688 { |
|
689 // Not buffering |
|
690 HandleNotifications( EFalse ); |
|
691 |
|
692 // Set the playout threshold to zero so we play the current talkspurt to the |
|
693 // end and do not affect the speech quality. This means that we will play the |
|
694 // buffer until it is empty. After that it is either DTX period or it is packet |
|
695 // loss. |
|
696 iCurrentPlayThreshold = 0; |
|
697 |
|
698 // Next sequence number that should be in the buffer |
|
699 TInt64 nextSeqNum = iLastGetSeqNum + iSeqNumIncrement; |
|
700 if( -1 == iLastGetSeqNum ) |
|
701 { |
|
702 // Last get sequence number was -1, so add one |
|
703 __JITTER_BUFFER( "CMccJitterBufferImpl::GetDataFrameL LAST WAS -1 ADJUSTING" ) |
|
704 |
|
705 nextSeqNum += 1; |
|
706 } |
|
707 |
|
708 // The actual sequence number which is in the buffer |
|
709 const TInt64 nextFrameSeqNum( |
|
710 iBuffer[ iPacketsInBuffer - 1 ].iSequenceNumber ); |
|
711 |
|
712 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL NEXT_GET_SEQ_NUM:", nextSeqNum ) |
|
713 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL FRAME_SEQ_NUM:", nextFrameSeqNum ) |
|
714 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL PACKETS:", iPacketsInBuffer ) |
|
715 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL LAST_GET_SEQ_NUM:", iLastGetSeqNum ) |
|
716 |
|
717 // Check if the next sequence number is smaller than the frame number of the next |
|
718 // frame to be played out. This will mean that a frame was lost and we need to do |
|
719 // error concealment. |
|
720 // Also we need to take account the start of the playout when iLastGetSeqNum == -1 |
|
721 // so we play the first frame in the buffer regardless of it's sequence number. |
|
722 // This should also take the sequence number wrap around situation. |
|
723 |
|
724 // As if(0) is intentional, PC-lint warning disabled: |
|
725 /*lint -e506 */ |
|
726 //if ( nextSeqNum < nextFrameSeqNum ) |
|
727 if ( 0 ) |
|
728 { |
|
729 __JITTER_BUFFER( "CMccJitterBufferImpl::GetDataFrameL LOST FRAME" ) |
|
730 |
|
731 iFramesLost++; |
|
732 iLastGetSeqNum = iLastGetSeqNum + iSeqNumIncrement; |
|
733 |
|
734 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL 1. LAST_GET_SEQ_NUM IS NOW:", iLastGetSeqNum ) |
|
735 |
|
736 iCnGenerator->GenerateSidPacket( playBuffer, |
|
737 dataBuffer.RequestSize() ); |
|
738 } |
|
739 else |
|
740 { |
|
741 __JITTER_BUFFER( "CMccJitterBufferImpl::GetDataFrameL NORMAL" ) |
|
742 |
|
743 TJitterBufferElement& bufferElement = iBuffer[ iPacketsInBuffer - 1 ]; |
|
744 if ( playBuffer.MaxLength() >= bufferElement.iDataFrame->Data().Length() ) |
|
745 { |
|
746 playBuffer.Copy( bufferElement.iDataFrame->Data() ); |
|
747 } |
|
748 else |
|
749 { |
|
750 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::GetDataFrameL ERROR - output buffer too small, maxlen:", |
|
751 playBuffer.MaxLength() ) |
|
752 |
|
753 iCnGenerator->GenerateSidPacket( playBuffer, dataBuffer.RequestSize() ); |
|
754 } |
|
755 |
|
756 iLastGetSeqNum = bufferElement.iSequenceNumber; |
|
757 bufferElement.iSequenceNumber = -1; |
|
758 bufferElement.iStamp = -1; |
|
759 iPacketsInBuffer--; |
|
760 iInactivityTime = -1; |
|
761 |
|
762 // Lets see if the new buffer will lead into a DTX period |
|
763 iCnGenerator->DoDtxDecision( playBuffer ); |
|
764 } |
|
765 } |
|
766 } |
|
767 } |
|
768 |
|
769 // ----------------------------------------------------------------------------- |
|
770 // CMccJitterBufferImpl::FindLargestSeqNum |
|
771 // Find Largest SequenceNumber |
|
772 // ----------------------------------------------------------------------------- |
|
773 // |
|
774 TInt CMccJitterBufferImpl::FindLargestSeqNum() const |
|
775 { |
|
776 TInt64 num = 0; |
|
777 TInt pos = 0; |
|
778 const TInt len( BufferLength() ); |
|
779 |
|
780 for( TInt i = 0; i < len; i++ ) |
|
781 { |
|
782 if( iBuffer[i].iSequenceNumber > num ) |
|
783 { |
|
784 num = iBuffer[i].iSequenceNumber; |
|
785 pos = i; |
|
786 } |
|
787 } |
|
788 |
|
789 return pos; |
|
790 } |
|
791 |
|
792 // ----------------------------------------------------------------------------- |
|
793 // CMccJitterBufferImpl::InsertBufferElement |
|
794 // Insert Buffer Element |
|
795 // ----------------------------------------------------------------------------- |
|
796 // |
|
797 void CMccJitterBufferImpl::InsertBufferElement( const TDesC8& aBuffer, |
|
798 TInt64 aLargestSeqNum, TInt64 aFrameNumber ) |
|
799 { |
|
800 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::InsertBufferElement aFrameNumber:", |
|
801 aFrameNumber ) |
|
802 |
|
803 if ( !aBuffer.Length() || aBuffer.Length() > iFrameSize ) |
|
804 { |
|
805 __JITTER_BUFFER( "CMccJitterBufferImpl::InsertBufferElement INVALID DATA, IGNORING" ) |
|
806 return; |
|
807 } |
|
808 |
|
809 const TInt len( this->BufferLength() ); |
|
810 iBuffer[len-1].iDataFrame->Data().Copy( aBuffer ); |
|
811 iBuffer[len-1].iSequenceNumber = aFrameNumber; |
|
812 |
|
813 if ( IsSeqNumWrappedAround( iBuffer[aLargestSeqNum].iSequenceNumber, aFrameNumber ) ) |
|
814 { |
|
815 for ( TInt i = 0; i < len; i++ ) |
|
816 { |
|
817 if( iBuffer[i].iStamp > aLargestSeqNum ) |
|
818 { |
|
819 aLargestSeqNum = iBuffer[i].iStamp; |
|
820 } |
|
821 } |
|
822 |
|
823 iBuffer[len-1].iStamp = aLargestSeqNum + 1; |
|
824 iBuffer.Sort( iBufStampSorter ); |
|
825 iLastGetSeqNum = -1; |
|
826 } |
|
827 else |
|
828 { |
|
829 iBuffer[len-1].iStamp = aFrameNumber - BufferLength(); |
|
830 iBuffer.Sort( iBufSequenceSorter ); |
|
831 } |
|
832 |
|
833 iPacketsInBuffer++; |
|
834 } |
|
835 |
|
836 // ----------------------------------------------------------------------------- |
|
837 // CMccJitterBufferImpl::BufferLength() |
|
838 // Return bufferlength. |
|
839 // ----------------------------------------------------------------------------- |
|
840 // |
|
841 TInt CMccJitterBufferImpl::BufferLength() const |
|
842 { |
|
843 return iBuffer.Count(); |
|
844 } |
|
845 |
|
846 // ----------------------------------------------------------------------------- |
|
847 // CMccJitterBufferImpl::IsFull() |
|
848 // Return: True if full |
|
849 // False if not full |
|
850 // ----------------------------------------------------------------------------- |
|
851 // |
|
852 TBool CMccJitterBufferImpl::IsFull() const |
|
853 { |
|
854 return ( iPacketsInBuffer == BufferLength() ); |
|
855 } |
|
856 |
|
857 // ----------------------------------------------------------------------------- |
|
858 // CMccJitterBufferImpl::IsEmpty() |
|
859 // Return: True if empty |
|
860 // False if not empty |
|
861 // ----------------------------------------------------------------------------- |
|
862 // |
|
863 TBool CMccJitterBufferImpl::IsEmpty() const |
|
864 { |
|
865 return ( iPacketsInBuffer == 0 ); |
|
866 } |
|
867 |
|
868 // ----------------------------------------------------------------------------- |
|
869 // CMccJitterBufferImpl::IsSeqNumWrappedAround |
|
870 // Checks if sequence number wrapped around from aSeqNum1 to aSeqNum2 |
|
871 // The sequence number wrap-around condition cannot be reliably detected by |
|
872 // checking for exact numbers, i.e., aSeqNum1==KMaxSeqNumber and aSeqNum2==0, |
|
873 // because, due to packet losses in network, sequence numbers may have gaps |
|
874 // between them. |
|
875 // ----------------------------------------------------------------------------- |
|
876 // |
|
877 TBool CMccJitterBufferImpl::IsSeqNumWrappedAround( TInt64 aSeqNum1, |
|
878 TInt64 aSeqNum2 ) const |
|
879 { |
|
880 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::IsSeqNumWrappedAround aSeqNum1:", aSeqNum1 ) |
|
881 __JITTER_BUFFER_INT1( "CMccJitterBufferImpl::IsSeqNumWrappedAround aSeqNum2:", aSeqNum2 ) |
|
882 |
|
883 if ( aSeqNum1 > ( KMaxSeqNumber - BufferLength() ) && |
|
884 aSeqNum2 < BufferLength() ) |
|
885 { |
|
886 __JITTER_BUFFER( "CMccJitterBufferImpl::IsSeqNumWrappedAround ETrue" ) |
|
887 return ETrue; |
|
888 } |
|
889 else |
|
890 { |
|
891 __JITTER_BUFFER( "CMccJitterBufferImpl::IsSeqNumWrappedAround EFalse" ) |
|
892 return EFalse; |
|
893 } |
|
894 } |
|
895 |
|
896 // ----------------------------------------------------------------------------- |
|
897 // CMccJitterBufferImpl::GenerateStatistics |
|
898 // Generates the statistics from the jitterbuffer |
|
899 // ----------------------------------------------------------------------------- |
|
900 // |
|
901 void CMccJitterBufferImpl::GenerateStatistics( TMccJitterBufferEventData& aEvent ) const |
|
902 { |
|
903 aEvent.iFramesReceived = iFramesReceived; |
|
904 aEvent.iBufferLength = BufferLength(); |
|
905 aEvent.iFramesInBuffer = iPacketsInBuffer; |
|
906 aEvent.iFrameLoss = iFramesLost; |
|
907 aEvent.iLateFrames = iNumOfLateFrames; |
|
908 aEvent.iFramesRemoved = iFramesRemoved; |
|
909 aEvent.iFramesPlayed = iFramesPlayed; |
|
910 } |
|
911 |
|
912 // ----------------------------------------------------------------------------- |
|
913 // CMccJitterBufferImpl::HandleNotifications |
|
914 // ----------------------------------------------------------------------------- |
|
915 // |
|
916 void CMccJitterBufferImpl::HandleNotifications( TBool aBuffering ) |
|
917 { |
|
918 if ( aBuffering ) |
|
919 { |
|
920 if ( BufferState() < EPlaying ) |
|
921 { |
|
922 __JITTER_BUFFER( "CMccJitterBufferImpl::HandleNotifications, to buffering state" ) |
|
923 |
|
924 SetBufferState( EBuffering ); |
|
925 SendStreamEventToClient( KMccStreamBuffering, ETrue ); |
|
926 } |
|
927 } |
|
928 else |
|
929 { |
|
930 if ( BufferState() >= EBuffering ) |
|
931 { |
|
932 // Do not notify about streaming until we know that speaker |
|
933 // is really playing provided data. This cannot be known when |
|
934 // first buffer is sent to speaker. |
|
935 if ( BufferState() == EDetermining ) |
|
936 { |
|
937 __JITTER_BUFFER( "CMccJitterBufferImpl::HandleNotifications, to playing state" ) |
|
938 |
|
939 SetBufferState( EPlaying ); |
|
940 iNotifyUser = ETrue; |
|
941 |
|
942 // As we are hiding buffer state changes when doing several |
|
943 // speaker starting attempts in row: |
|
944 // idle->buffering->streaming->idle->noresources->(buffering)-> |
|
945 // (streaming)->(idle)->(noresources)->(buffering)-> etc. |
|
946 // We have to "fake" the buffering event when we are |
|
947 // sure that playback is fine. |
|
948 if ( KMccStreamBuffering != iLatestNotifiedEvent ) |
|
949 { |
|
950 SendStreamEventToClient( KMccStreamBuffering, ETrue ); |
|
951 } |
|
952 |
|
953 SendStreamEventToClient( KMccStreamPlaying, ETrue ); |
|
954 } |
|
955 else |
|
956 { |
|
957 SetBufferState( EDetermining ); |
|
958 } |
|
959 } |
|
960 } |
|
961 } |
|
962 |
|
963 // ----------------------------------------------------------------------------- |
|
964 // CMccJitterBufferImpl::CheckThresholdBufferLength |
|
965 // ----------------------------------------------------------------------------- |
|
966 // |
|
967 void CMccJitterBufferImpl::CheckThresholdBufferLength( |
|
968 TInt& aBufferLength, TInt aTreshhold ) const |
|
969 { |
|
970 const TInt numTen = 10; |
|
971 if ( ( aBufferLength - aTreshhold ) < numTen ) |
|
972 { |
|
973 aBufferLength = aTreshhold + numTen; |
|
974 } |
|
975 } |
|
976 |
|
977 // ----------------------------------------------------------------------------- |
|
978 // CMccJitterBufferImpl::SendStreamEventToClient |
|
979 // ----------------------------------------------------------------------------- |
|
980 // |
|
981 void CMccJitterBufferImpl::SendStreamEventToClient( |
|
982 TMccEventType aEventType, |
|
983 TBool aAllEndpoints ) |
|
984 { |
|
985 __JITTER_BUFFER( "CMccJitterBufferImpl::SendStreamEventToClient" ) |
|
986 |
|
987 if ( iEventHandler && iNotifyUser ) |
|
988 { |
|
989 const TUint32 endpointId = aAllEndpoints ? 0 : iEndpointId; |
|
990 // Clear old event |
|
991 { |
|
992 iMccEvent = TMccEvent(); |
|
993 } |
|
994 |
|
995 iMccEvent.iEndpointId = endpointId; |
|
996 iMccEvent.iEventCategory = KMccEventCategoryStream; |
|
997 iMccEvent.iEventType = aEventType; |
|
998 |
|
999 TMccInternalEvent internalEvent( KMccJitterBufferUid, |
|
1000 EMccInternalEventNone, |
|
1001 iMccEvent ); |
|
1002 |
|
1003 iEventHandler->SendEventToClient( internalEvent ); |
|
1004 iLatestNotifiedEvent = aEventType; |
|
1005 } |
|
1006 else |
|
1007 { |
|
1008 __JITTER_BUFFER( "CMccJitterBufferImpl::SendStreamEventToClient, sending ignored" ) |
|
1009 } |
|
1010 } |
|
1011 |
|
1012 // ----------------------------------------------------------------------------- |
|
1013 // CMccJitterBufferImpl::SetBufferState |
|
1014 // ----------------------------------------------------------------------------- |
|
1015 // |
|
1016 void CMccJitterBufferImpl::SetBufferState( TMccJitterBufferImplState aState ) |
|
1017 { |
|
1018 iBufferState = aState; |
|
1019 } |
|
1020 |
|
1021 // ----------------------------------------------------------------------------- |
|
1022 // CMccJitterBufferImpl::BufferState |
|
1023 // ----------------------------------------------------------------------------- |
|
1024 // |
|
1025 CMccJitterBufferImpl::TMccJitterBufferImplState |
|
1026 CMccJitterBufferImpl::BufferState() const |
|
1027 { |
|
1028 return iBufferState; |
|
1029 } |
|
1030 |
|
1031 // ========================== OTHER EXPORTED FUNCTIONS ========================= |
|
1032 |
|
1033 // End of File |
|
1034 |