|
1 // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // source\server\mmfswcodecplaydatapath.cpp |
|
15 // |
|
16 // |
|
17 |
|
18 #include "mmfSwCodecPlayDataPath.h" |
|
19 #include <mmf/server/mmfswcodecwrapper.h> |
|
20 #include <mmf/server/mmfswcodecwrappercustominterfacesuids.hrh> |
|
21 #include <mmf/common/mmfpaniccodes.h> |
|
22 #include "mmfSwCodecUtility.h" |
|
23 |
|
24 const TInt KBytesPerSample = 2; |
|
25 const TInt KMaxBytesInSec = 192000; //considering maximum samplerate 96KHz |
|
26 CMMFSwCodecPlayDataPath* CMMFSwCodecPlayDataPath::NewL() |
|
27 { |
|
28 CMMFSwCodecPlayDataPath* self = new(ELeave) CMMFSwCodecPlayDataPath; |
|
29 CleanupStack::PushL(self); |
|
30 self->ConstructL(); |
|
31 CleanupStack::Pop(); |
|
32 return self; |
|
33 } |
|
34 |
|
35 |
|
36 void CMMFSwCodecPlayDataPath::ConstructL() |
|
37 { |
|
38 iAudioPlayer = new (ELeave) CDataPathPlayer(*this,CActive::EPriorityUserInput); |
|
39 iSoundDeviceErrorReceiver = new (ELeave) CSoundDevPlayErrorReceiver(*this, CActive::EPriorityUserInput); |
|
40 iUtility = CMMFSwCodecUtility::NewL(); |
|
41 iVbrFlag = EFalse; |
|
42 } |
|
43 |
|
44 |
|
45 CMMFSwCodecPlayDataPath::~CMMFSwCodecPlayDataPath() |
|
46 { |
|
47 delete iAudioPlayer; |
|
48 delete iSoundDeviceErrorReceiver; |
|
49 delete iUtility; |
|
50 |
|
51 iSoundDevice.Close(); |
|
52 |
|
53 if (iCodec) |
|
54 { |
|
55 delete iSourceBuffer; |
|
56 if (!iCodec->IsNullCodec()) |
|
57 { |
|
58 delete iSoundDeviceBuffer; |
|
59 } |
|
60 } |
|
61 |
|
62 #ifdef __USE_MMF_TRANSFERBUFFERS__ |
|
63 delete iTransferWindow; |
|
64 |
|
65 if(iTransferBuffer) |
|
66 { |
|
67 iTransferBuffer->Close(); |
|
68 delete iTransferBuffer; |
|
69 } |
|
70 #endif |
|
71 |
|
72 #ifdef __USE_MMF_PTRBUFFERS__ |
|
73 delete iPtrBufferMemoryBlock; |
|
74 #endif |
|
75 } |
|
76 |
|
77 |
|
78 TInt CMMFSwCodecPlayDataPath::SetObserver(MMMFHwDeviceObserver& aObserver) |
|
79 { |
|
80 TInt error; |
|
81 if (iHwDeviceObserver) |
|
82 { |
|
83 error = KErrAlreadyExists; |
|
84 } |
|
85 else |
|
86 { |
|
87 iHwDeviceObserver = &aObserver; |
|
88 error = KErrNone; |
|
89 } |
|
90 return error; |
|
91 } |
|
92 |
|
93 |
|
94 TInt CMMFSwCodecPlayDataPath::AddCodec(CMMFSwCodec& aCodec) |
|
95 { |
|
96 if (iCodec) |
|
97 return KErrNotSupported; //doesn't support multiple codecs |
|
98 |
|
99 TInt err = KErrNone; |
|
100 |
|
101 iCodec = &aCodec; |
|
102 |
|
103 // Allocate data buffer |
|
104 iSourceBufferSize = iCodec->SourceBufferSize(); |
|
105 iSoundDevBufferSize = iCodec->SinkBufferSize(); |
|
106 |
|
107 if ((!iSourceBufferSize)||(!iSoundDevBufferSize)) |
|
108 err = KErrArgument; //codec plugin has not specified buffer size |
|
109 |
|
110 if (err == KErrNone) |
|
111 { |
|
112 #ifdef __USE_MMF_TRANSFERBUFFERS__ |
|
113 TRAP(err,iSourceBuffer = CreateTransferBufferL(iSourceBufferSize, static_cast<CMMFTransferBuffer*>(iSourceBuffer))); |
|
114 #endif |
|
115 #ifdef __USE_MMF_PTRBUFFERS__ |
|
116 TRAP(err,iSourceBuffer = CreatePtrBufferL(iSourceBufferSize)); |
|
117 #else |
|
118 TRAP(err,iSourceBuffer = CMMFDataBuffer::NewL(iSourceBufferSize)); |
|
119 #endif |
|
120 } |
|
121 |
|
122 if (err == KErrNone) |
|
123 { |
|
124 if (iCodec->IsNullCodec()) |
|
125 {//use source buffer for sound device buffer |
|
126 iSoundDeviceBuffer = NULL; |
|
127 } |
|
128 else |
|
129 {//codec needs separate source and sound device buffers |
|
130 TRAP(err,iSoundDeviceBuffer = CMMFDataBuffer::NewL(iSoundDevBufferSize)); |
|
131 } |
|
132 } |
|
133 return err; |
|
134 } |
|
135 |
|
136 |
|
137 TInt CMMFSwCodecPlayDataPath::Start() |
|
138 { |
|
139 TInt startError = KErrNone; |
|
140 |
|
141 if (!iCodec) |
|
142 {//check that a codec has been added |
|
143 startError = KErrNotReady; |
|
144 } |
|
145 if ((!iSoundDevice.Handle())&&(!startError)) |
|
146 {//check that the sound drivers can be opened |
|
147 startError = iSoundDevice.Open(); |
|
148 } |
|
149 |
|
150 if (iState == EPaused) |
|
151 {//we are paused so need to resume play |
|
152 if (!startError) |
|
153 { |
|
154 #ifdef _SCW_DEBUG |
|
155 RDebug::Print(_L("CMMFSwCodecPlayDataPath::Start-Resume")); |
|
156 #endif |
|
157 iAudioPlayer->ResumePlaying(); |
|
158 iState = EPlaying; |
|
159 } |
|
160 } |
|
161 else if (!startError) |
|
162 { |
|
163 #ifdef _SCW_DEBUG |
|
164 RDebug::Print(_L("CMMFSwCodecPlayDataPath::Start-Normal")); |
|
165 #endif |
|
166 // get sample rate and channels from RMdaDevSound |
|
167 RMdaDevSound::TCurrentSoundFormatBuf format; |
|
168 iSoundDevice.GetPlayFormat(format); |
|
169 iSampleRate = format().iRate; |
|
170 iChannels = format().iChannels; |
|
171 |
|
172 iNoMoreSourceData = EFalse; |
|
173 iNoMoreSoundDeviceData = EFalse; |
|
174 iSourceBuffer->SetLastBuffer(EFalse); |
|
175 iBytesPlayed = 0; |
|
176 iState = EPlaying; |
|
177 iSoundDeviceErrorReceiver->Start(); |
|
178 TRAP(startError,FillSourceBufferL()); //get initial buffer of audio data |
|
179 if (startError == KErrNone) |
|
180 { |
|
181 // Start the player objects |
|
182 iAudioPlayer->Start(); |
|
183 } |
|
184 else |
|
185 {//failed to start up correctly go back to stopped state |
|
186 iState = EStopped; |
|
187 iSoundDeviceErrorReceiver->Stop(); |
|
188 } |
|
189 } |
|
190 return startError; |
|
191 } |
|
192 |
|
193 |
|
194 // *** Main Play Loop *** |
|
195 |
|
196 void CMMFSwCodecPlayDataPath::FillSourceBufferL() |
|
197 {//asks observer to fill the source buffer |
|
198 // Ask immediately for data from the observer |
|
199 #ifdef __CYCLE_MMF_DATABUFFERS__ |
|
200 // Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics |
|
201 // If the creation fails, we carry on regardless as the original buffer will not have been |
|
202 // destroyed. Must do this as alloc fail tests will not run. |
|
203 if(iSourceBuffer) |
|
204 { |
|
205 iSourceBuffer = CycleAudioBuffer(iSourceBuffer); |
|
206 } |
|
207 #endif // __CYCLE_MMF_DATABUFFERS__ |
|
208 User::LeaveIfError(iHwDeviceObserver->FillThisHwBuffer(*iSourceBuffer)); |
|
209 |
|
210 } |
|
211 |
|
212 |
|
213 void CMMFSwCodecPlayDataPath::BufferFilledL(CMMFDataBuffer& aBuffer) |
|
214 {//call back from observer to indicate buffer has been filled |
|
215 if (iState == EStopped) |
|
216 User::Leave(KErrNotReady);//ok if paused? |
|
217 |
|
218 iSourceBuffer = &aBuffer; |
|
219 iSourceBuffer->SetStatus(EFull); |
|
220 #ifdef _SCW_DEBUG |
|
221 RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL")); |
|
222 #endif |
|
223 |
|
224 //need to check that the buffer size is not 0 - if so assume we've reached the end of the data |
|
225 if (!iSourceBuffer->BufferSize()) |
|
226 {//no buffer - could be end of source or could be that the source has no data?? |
|
227 iNoMoreSourceData = ETrue; |
|
228 #ifdef _SCW_DEBUG |
|
229 RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL-NoMoreSourceData")); |
|
230 #endif |
|
231 } |
|
232 //even if the buffer size is 0 we still |
|
233 //need to perform the following to get the sound device callback |
|
234 FillSoundDeviceBufferL(); //get buffer in pcm16 format for sound device |
|
235 |
|
236 /* iVbrFlag is added to datapath to avail the alternative dataflow wherein datapath makes sure that |
|
237 destinationbuffer is filled to its maximum length before sending it to the sound driver. |
|
238 Sending the buffer directly to the device causes underflow incase of Vorbis codecs.*/ |
|
239 if (iVbrFlag) |
|
240 { |
|
241 /*There are two cases we need to deal here |
|
242 1. When the output of the codec is 0 for header data. |
|
243 in that case, repeat till actual decoding of ogg packets and pages. |
|
244 2. When destination buffer is not filled even to its half length, get next source buffer |
|
245 and decode it. This is to avoid underflows when ever we receive little pcm for a |
|
246 a given source buffer. |
|
247 */ |
|
248 if (iSoundDeviceBuffer->Data().Length() < iSoundDeviceBuffer->Data().MaxLength()/2 && !(iSoundDeviceBuffer->LastBuffer())) |
|
249 { |
|
250 iSourceBuffer->SetStatus(EAvailable); //source buffer is now available |
|
251 iSoundDeviceBuffer->SetPosition(iSoundDeviceBuffer->Data().Length());//this indicates the available space in the buffer to the codec |
|
252 FillSourceBufferL(); |
|
253 return; |
|
254 } |
|
255 else //data is sufficient to avoid underflows |
|
256 { |
|
257 iSoundDeviceBuffer->SetPosition(0); |
|
258 if(iSoundDeviceBuffer->Data().Length()==0 && iSoundDeviceBuffer->LastBuffer()) |
|
259 { |
|
260 iNoMoreSoundDeviceData = ETrue; |
|
261 } |
|
262 } |
|
263 } |
|
264 |
|
265 // attenuate the amplitude of the samples if volume ramping has been changed |
|
266 // and is non-zero |
|
267 if (iCustomInterface) |
|
268 { |
|
269 TTimeIntervalMicroSeconds volumeRamp = iCustomInterface->VolumeRamp(); |
|
270 if (volumeRamp != iVolumeRamp) |
|
271 { |
|
272 iVolumeRamp = volumeRamp; |
|
273 if (iVolumeRamp.Int64() != 0) |
|
274 { |
|
275 iUtility->ConfigAudioRamper( |
|
276 iVolumeRamp.Int64(), |
|
277 iSampleRate, |
|
278 iChannels); |
|
279 iRampAudioSample = ETrue; |
|
280 } |
|
281 else |
|
282 { |
|
283 iRampAudioSample = EFalse; |
|
284 } |
|
285 |
|
286 } |
|
287 if (iRampAudioSample) |
|
288 iRampAudioSample = iUtility->RampAudio(iSoundDeviceBuffer); |
|
289 } |
|
290 |
|
291 iAudioPlayer->PlayData(*iSoundDeviceBuffer); //play data to sound drivers |
|
292 |
|
293 if (iSourceBuffer->LastBuffer())//check last buffer flag |
|
294 { |
|
295 iNoMoreSourceData = ETrue; |
|
296 #ifdef _SCW_DEBUG |
|
297 RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL-LBNoMoreSourceData")); |
|
298 #endif |
|
299 } |
|
300 } |
|
301 |
|
302 |
|
303 void CMMFSwCodecPlayDataPath::FillSoundDeviceBufferL() |
|
304 {//use CMMFSwCodec to fill the sound device buffer |
|
305 |
|
306 CMMFSwCodec::TCodecProcessResult codecProcessResult; |
|
307 |
|
308 if (iCodec->IsNullCodec()) |
|
309 {//no codec so data can be sent direct to sink |
|
310 iSoundDeviceBuffer = iSourceBuffer; |
|
311 iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full |
|
312 } |
|
313 else |
|
314 { |
|
315 //pass buffer to codec for processing |
|
316 codecProcessResult = iCodec->ProcessL(*iSourceBuffer, *iSoundDeviceBuffer); |
|
317 |
|
318 if (iSourceBuffer->LastBuffer()) //if source is last buffer so is sound dev |
|
319 iSoundDeviceBuffer->SetLastBuffer(ETrue); |
|
320 if ((!iSoundDeviceBuffer->BufferSize())&&(codecProcessResult.iDstBytesAdded)) |
|
321 {//the codec has added data but not set the buffer length |
|
322 iSoundDeviceBuffer->Data().SetLength(codecProcessResult.iDstBytesAdded); |
|
323 } |
|
324 //only supports EProcessComplete |
|
325 switch (codecProcessResult.iCodecProcessStatus) |
|
326 { |
|
327 case CMMFSwCodec::TCodecProcessResult::EProcessComplete: |
|
328 //finished procesing source data - all data in sink buffer |
|
329 { |
|
330 iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full |
|
331 } |
|
332 break; |
|
333 #ifdef SYMBIAN_VARIABLE_BITRATE_CODEC |
|
334 case CMMFSwCodec::TCodecProcessResult::EProcessIncomplete: |
|
335 //finished procesing source data - all data in sink buffer |
|
336 { |
|
337 iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full |
|
338 } |
|
339 break; |
|
340 #endif |
|
341 case CMMFSwCodec::TCodecProcessResult::EDstNotFilled: |
|
342 //could be the last buffer in which case dst might not get filled |
|
343 { |
|
344 iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full |
|
345 } |
|
346 break; |
|
347 case CMMFSwCodec::TCodecProcessResult::EEndOfData: |
|
348 //no more data - send what we've got to the sink |
|
349 //note we can't always rely on this - in many cases the codec will not know when |
|
350 //it has reached the end of data. |
|
351 { |
|
352 iSoundDeviceBuffer->SetStatus(EFull);//sink buffer may not really be 'full' but its as full as it going to get |
|
353 iNoMoreSourceData = ETrue; |
|
354 //doesn't matter if sink buffer is not full |
|
355 } |
|
356 break; |
|
357 default: |
|
358 Panic(EMMFSwCodecWrapperBadCodec); //should never get here - bad codec |
|
359 } |
|
360 } |
|
361 } |
|
362 |
|
363 |
|
364 void CMMFSwCodecPlayDataPath::BufferEmptiedL(const CMMFDataBuffer& aBuffer) |
|
365 {//call back from CDataPathPlayer when the sound device buffer has been emptied |
|
366 if (&aBuffer != iSoundDeviceBuffer) |
|
367 Panic(EMMFSwCodecWrapperBadBuffer); |
|
368 if(iVbrFlag && (iSourceBuffer->Status() == EUnAvailable || iNoMoreSourceData)) |
|
369 {//No more source data. Play rest of the decoded data.Inform codec not to consider the source buffer |
|
370 if(iSourceBuffer->Status()!=EUnAvailable) |
|
371 { |
|
372 iSourceBuffer->SetStatus(EUnAvailable); |
|
373 } |
|
374 FillSoundDeviceBufferL(); |
|
375 if(iSoundDeviceBuffer->BufferSize() > 0) |
|
376 { |
|
377 // attenuate the amplitude of the samples if volume ramping has been changed |
|
378 // and is non-zero |
|
379 if (iCustomInterface) |
|
380 { |
|
381 TTimeIntervalMicroSeconds volumeRamp = iCustomInterface->VolumeRamp(); |
|
382 if (volumeRamp != iVolumeRamp) |
|
383 { |
|
384 iVolumeRamp = volumeRamp; |
|
385 if (iVolumeRamp.Int64() != 0) |
|
386 { |
|
387 iUtility->ConfigAudioRamper(iVolumeRamp.Int64(), iSampleRate, iChannels); |
|
388 iRampAudioSample = ETrue; |
|
389 } |
|
390 else |
|
391 { |
|
392 iRampAudioSample = EFalse; |
|
393 } |
|
394 |
|
395 } |
|
396 if (iRampAudioSample) |
|
397 { |
|
398 iRampAudioSample = iUtility->RampAudio(iSoundDeviceBuffer); |
|
399 } |
|
400 |
|
401 } |
|
402 iAudioPlayer->PlayData(*iSoundDeviceBuffer); //play data to sound drivers |
|
403 return; |
|
404 } |
|
405 else |
|
406 { |
|
407 if(iNoMoreSourceData) |
|
408 { |
|
409 iNoMoreSoundDeviceData = ETrue; |
|
410 } |
|
411 iSourceBuffer->SetStatus(EAvailable); |
|
412 } |
|
413 } |
|
414 if (!iNoMoreSourceData) |
|
415 FillSourceBufferL(); |
|
416 } |
|
417 |
|
418 //*** End of Main Play Loop *** |
|
419 |
|
420 |
|
421 void CMMFSwCodecPlayDataPath::Stop() |
|
422 { |
|
423 iAudioPlayer->Cancel(); |
|
424 iSoundDeviceErrorReceiver->Cancel(); |
|
425 iSoundDevice.Close(); |
|
426 |
|
427 #ifdef __CYCLE_MMF_DATABUFFERS__ |
|
428 // Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics |
|
429 // If the creation fails, we carry on regardless as the original buffer will not have been |
|
430 // destroyed. Must do this as alloc fail tests will not run. |
|
431 if(iSourceBuffer) |
|
432 { |
|
433 iSourceBuffer = CycleAudioBuffer(iSourceBuffer); |
|
434 } |
|
435 #endif // __CYCLE_MMF_DATABUFFERS__ |
|
436 |
|
437 iState = EStopped; |
|
438 } |
|
439 |
|
440 |
|
441 void CMMFSwCodecPlayDataPath::Pause() |
|
442 { |
|
443 //since a pause can happen anyway in the datatransfer -need to set to a known |
|
444 //state so that when play is resumed the behaviour is predictable |
|
445 if (iSoundDevice.Handle()) |
|
446 { |
|
447 iSoundDevice.PausePlayBuffer(); //needs new LDD |
|
448 iState = EPaused; |
|
449 #ifdef _SCW_DEBUG |
|
450 RDebug::Print(_L("Pause")); |
|
451 #endif |
|
452 } |
|
453 else |
|
454 {//an error must have occured |
|
455 iState = EStopped; |
|
456 } |
|
457 } |
|
458 |
|
459 |
|
460 TInt CMMFSwCodecPlayDataPath::EmptyBuffers() |
|
461 { |
|
462 TInt error = KErrNone; |
|
463 if (iSoundDevice.Handle() == 0) |
|
464 { |
|
465 error = KErrNotReady; |
|
466 } |
|
467 else |
|
468 { |
|
469 iAudioPlayer->Cancel(); |
|
470 iSoundDevice.CancelPlayData(); |
|
471 iSoundDeviceErrorReceiver->Stop(); |
|
472 iState = EStopped; |
|
473 } |
|
474 return error; |
|
475 } |
|
476 |
|
477 |
|
478 RMdaDevSound& CMMFSwCodecPlayDataPath::Device() |
|
479 { |
|
480 return iSoundDevice; |
|
481 } |
|
482 |
|
483 |
|
484 void CMMFSwCodecPlayDataPath::SoundDeviceException(TInt aError) |
|
485 { |
|
486 if(iIgnoreUnderflow) |
|
487 { |
|
488 if(!iVbrFlag && aError==KErrUnderflow && !iNoMoreSourceData) |
|
489 { |
|
490 //ignore underflow |
|
491 return; |
|
492 } |
|
493 //for VBR codec data,no more source does not mean that no more sounddevice data |
|
494 //so ignore underflows till the last buffer is played from the codec |
|
495 else if(iVbrFlag && aError==KErrUnderflow && !iNoMoreSoundDeviceData) |
|
496 { |
|
497 //ignore underflow |
|
498 return; |
|
499 } |
|
500 } |
|
501 |
|
502 //this sends a request to the hw device observer usually Devsound |
|
503 //to update the bytes played |
|
504 //it is done here so that the sound driver can be closed prior to |
|
505 //updating the plicy and sending the error back |
|
506 TUid uidUpdateBytesPlayed; |
|
507 uidUpdateBytesPlayed.iUid = KMmfHwDeviceObserverUpdateBytesPlayed; |
|
508 TPtrC8 dummy(0,0); |
|
509 iHwDeviceObserver->MsgFromHwDevice(uidUpdateBytesPlayed,dummy); |
|
510 |
|
511 //this closes RMdaDevSound. |
|
512 Stop(); |
|
513 |
|
514 //inform devsound so it can update policy |
|
515 iHwDeviceObserver->Stopped(); |
|
516 |
|
517 // Inform the observer of the exception condition |
|
518 // We inform the hw device observer after the policy has been |
|
519 // updated incase the observer relied on the error to assume |
|
520 // the policy has been updated |
|
521 iHwDeviceObserver->Error(aError); |
|
522 } |
|
523 |
|
524 |
|
525 void CMMFSwCodecPlayDataPath::SetPlayCustomInterface(MPlayCustomInterface& aCustomInterface) |
|
526 { |
|
527 iCustomInterface = &aCustomInterface; |
|
528 } |
|
529 |
|
530 /** |
|
531 Retrieves a custom interface to the device. |
|
532 The reference CMMFSwCodecWrapper supports two custom interfaces, |
|
533 MEmptyBuffersCustomInterface and MSetVbrFlagCustomInterface |
|
534 |
|
535 @param aInterface |
|
536 Interface UID, defined with the custom interface. |
|
537 aInterface = KMmfUidEmptyBuffersCustomInterface for MEmptyBuffersCustomInterface, |
|
538 KSetVbrFlagCustomInterfaceTypeUid for MSetVbrFlagCustomInterface |
|
539 |
|
540 @return A pointer to the interface implementation, or NULL if the device can not |
|
541 implement the interface requested. The return value must be cast to the |
|
542 correct type by the user. |
|
543 */ |
|
544 TAny* CMMFSwCodecPlayDataPath::CustomInterface(TUid aInterface) |
|
545 { |
|
546 TAny* ret = NULL; |
|
547 if (aInterface.iUid == KMmfUidEmptyBuffersCustomInterface) |
|
548 { |
|
549 MEmptyBuffersCustomInterface* result = static_cast<MEmptyBuffersCustomInterface*> (this); |
|
550 ret = static_cast<TAny*>(result); |
|
551 } |
|
552 else if(aInterface.iUid == KSetVbrFlagCustomInterfaceTypeUid) |
|
553 { |
|
554 SetVbrFlag(); |
|
555 } |
|
556 if (aInterface == KTimePlayedCustomInterfaceTypeUid) |
|
557 { |
|
558 MTimePlayedCustomInterface* result = static_cast<MTimePlayedCustomInterface*> (this); |
|
559 ret = static_cast<TAny*>(result); |
|
560 } |
|
561 if (aInterface == KIgnoreUnderflowCustomInterfaceTypeUid) |
|
562 { |
|
563 MIgnoreUnderflowEventsCustomInterface* result = static_cast<MIgnoreUnderflowEventsCustomInterface*> (this); |
|
564 ret = static_cast<TAny*>(result); |
|
565 } |
|
566 return ret; |
|
567 } |
|
568 |
|
569 /** |
|
570 Used to set iVbrFlag on the datapath. |
|
571 |
|
572 This method is used to set the iVbrFlag in datapath. This flag is added to datapath to avail the |
|
573 alternative dataflow wherein datapath makes sure that destinationbuffer is filled to its maximum length |
|
574 before sending it to the sound driver. Sending the buffer directly to the device causes underflow incase of VBR codecs. |
|
575 */ |
|
576 void CMMFSwCodecPlayDataPath::SetVbrFlag() |
|
577 { |
|
578 iVbrFlag = ETrue; |
|
579 } |
|
580 |
|
581 TInt CMMFSwCodecPlayDataPath::GetTimePlayed(TTimeIntervalMicroSeconds& aTime) |
|
582 { |
|
583 if(iSoundDevice.Handle()) |
|
584 { |
|
585 TInt bytes = iSoundDevice.BytesPlayed(); |
|
586 //Work around for overflow of bytes played from driver. |
|
587 //This code will be removed when Base provides the TimePlayed() API which returns the play time |
|
588 //Assuming GetTimePlayed() gets called in an interval not more than 3 secs, reset driver's bytes played when it is near KMaxInt |
|
589 if(bytes > (KMaxTInt - 3*KMaxBytesInSec)) |
|
590 { |
|
591 iBytesPlayed = iBytesPlayed+bytes; |
|
592 iSoundDevice.ResetBytesPlayed(); |
|
593 bytes = 0; |
|
594 } |
|
595 TInt64 samplesPlayed = (iBytesPlayed+bytes)/(KBytesPerSample*iChannels); |
|
596 aTime = (samplesPlayed*1000000)/iSampleRate; |
|
597 } |
|
598 else |
|
599 { |
|
600 aTime = 0; |
|
601 } |
|
602 |
|
603 return KErrNone; |
|
604 } |
|
605 |
|
606 void CMMFSwCodecPlayDataPath::IgnoreUnderflowEvents() |
|
607 { |
|
608 iIgnoreUnderflow = ETrue; |
|
609 } |
|
610 /************************************************************************ |
|
611 * CDataPathPlayer * |
|
612 ************************************************************************/ |
|
613 |
|
614 CDataPathPlayer::CDataPathPlayer(CMMFSwCodecPlayDataPath& aParent, TInt aPriority) |
|
615 : CActive(aPriority), iParent(aParent) |
|
616 { |
|
617 CActiveScheduler::Add(this); |
|
618 } |
|
619 |
|
620 |
|
621 CDataPathPlayer::~CDataPathPlayer() |
|
622 { |
|
623 Cancel(); |
|
624 } |
|
625 |
|
626 |
|
627 void CDataPathPlayer::Start() |
|
628 { |
|
629 // No implementation |
|
630 } |
|
631 |
|
632 |
|
633 void CDataPathPlayer::ResumePlaying() |
|
634 { |
|
635 if (iParent.Device().Handle()) |
|
636 { |
|
637 //should be ok to call this even if we are active |
|
638 iParent.Device().ResumePlaying(); |
|
639 iResumePlaying = ETrue; |
|
640 } |
|
641 #ifdef _SCW_DEBUG |
|
642 RDebug::Print(_L("Playing Resumed")); |
|
643 #endif |
|
644 } |
|
645 |
|
646 |
|
647 void CDataPathPlayer::PlayData(const CMMFDataBuffer& aData) |
|
648 { |
|
649 iDataFromSource = &aData; |
|
650 if (!IsActive()) |
|
651 { |
|
652 #ifdef _SCW_DEBUG |
|
653 RDebug::Print(_L("CDataPathPlayer::PlayData")); |
|
654 #endif |
|
655 iParent.Device().PlayData(iStatus,(STATIC_CAST(const CMMFDataBuffer*, iDataFromSource))->Data()); |
|
656 SetActive(); |
|
657 } |
|
658 } |
|
659 |
|
660 |
|
661 void CDataPathPlayer::Stop() |
|
662 { |
|
663 if (!IsActive()) |
|
664 iParent.Device().FlushPlayBuffer(); // Otherwise won't be flushed |
|
665 Cancel(); |
|
666 iParent.SoundDeviceException(KErrCancel); |
|
667 } |
|
668 |
|
669 |
|
670 void CDataPathPlayer::RunL() |
|
671 { |
|
672 #ifdef _SCW_DEBUG |
|
673 RDebug::Print(_L("CDataPathPlayer::RunL error[%d]"), iStatus.Int()); |
|
674 #endif |
|
675 if (iStatus.Int()!=KErrNone) |
|
676 { |
|
677 iParent.SoundDeviceException(iStatus.Int()); |
|
678 } |
|
679 else |
|
680 { |
|
681 iParent.BufferEmptiedL(static_cast<const CMMFDataBuffer&>(*iDataFromSource)); |
|
682 iResumePlaying = EFalse; |
|
683 } |
|
684 } |
|
685 |
|
686 |
|
687 TInt CDataPathPlayer::RunError(TInt aError) |
|
688 { |
|
689 Error(aError); |
|
690 return KErrNone; |
|
691 } |
|
692 |
|
693 |
|
694 void CDataPathPlayer::DoCancel() |
|
695 { |
|
696 if (iParent.Device().Handle()) |
|
697 { |
|
698 iParent.Device().CancelPlayData(); |
|
699 iParent.Device().FlushPlayBuffer(); |
|
700 } |
|
701 } |
|
702 |
|
703 |
|
704 void CDataPathPlayer::Error(TInt aError) |
|
705 { |
|
706 iParent.SoundDeviceException(aError); |
|
707 } |
|
708 |
|
709 |
|
710 /************************************************************************ |
|
711 * CSoundDevPlayErrorReceiver * |
|
712 ************************************************************************/ |
|
713 |
|
714 CSoundDevPlayErrorReceiver::CSoundDevPlayErrorReceiver(CMMFSwCodecPlayDataPath& aParent, TInt aPriority) |
|
715 : CActive(aPriority), iParent(aParent) |
|
716 { |
|
717 CActiveScheduler::Add(this); |
|
718 } |
|
719 |
|
720 CSoundDevPlayErrorReceiver::~CSoundDevPlayErrorReceiver() |
|
721 { |
|
722 Cancel(); |
|
723 } |
|
724 |
|
725 void CSoundDevPlayErrorReceiver::Start() |
|
726 { |
|
727 iParent.Device().NotifyPlayError(iStatus); |
|
728 SetActive(); |
|
729 } |
|
730 |
|
731 void CSoundDevPlayErrorReceiver::Stop() |
|
732 { |
|
733 Cancel(); |
|
734 } |
|
735 |
|
736 void CSoundDevPlayErrorReceiver::RunL() |
|
737 { |
|
738 TInt reason = iStatus.Int(); |
|
739 Start(); |
|
740 // An error has been returned |
|
741 #ifdef _SCW_DEBUG |
|
742 RDebug::Print(_L("CSoundDevPlayErrorReceiver::RunL[%d]"), reason); |
|
743 #endif |
|
744 iParent.SoundDeviceException(reason); |
|
745 } |
|
746 |
|
747 void CSoundDevPlayErrorReceiver::DoCancel() |
|
748 { |
|
749 iParent.Device().CancelNotifyPlayError(); |
|
750 } |
|
751 |
|
752 |