|
1 // Copyright (c) 2005-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\MmfBtSwCodecPlayDatapath.cpp |
|
15 // |
|
16 // |
|
17 |
|
18 #include "mmfBtSwCodecPlayDataPath.h" |
|
19 #include "mmfbtswcodecwrapper.h" |
|
20 #include "mmfbtswcodecwrappercustominterfacesuids.hrh" |
|
21 #include <mmfpaniccodes.h> |
|
22 #include "mmfBtSwCodecUtility.h" |
|
23 |
|
24 #include "MMFBtRoutingSoundDevice.h" |
|
25 #include "A2dpBTHeadsetAudioIfClientServer.h" // for TRange (will be deprecated) |
|
26 |
|
27 CMMFSwCodecPlayDataPath* CMMFSwCodecPlayDataPath::NewL( CRoutingSoundPlayDevice* aSoundDevice, |
|
28 TUid aDeviceUid) |
|
29 { |
|
30 CMMFSwCodecPlayDataPath* self = new(ELeave) CMMFSwCodecPlayDataPath(aSoundDevice, |
|
31 aDeviceUid); |
|
32 CleanupStack::PushL(self); |
|
33 self->ConstructL(); |
|
34 CleanupStack::Pop(); |
|
35 return self; |
|
36 } |
|
37 |
|
38 |
|
39 void CMMFSwCodecPlayDataPath::ConstructL() |
|
40 { |
|
41 iAudioPlayer = new (ELeave) CDataPathPlayer(*this,CActive::EPriorityUserInput); |
|
42 iSoundDeviceErrorReceiver = new (ELeave) CSoundDevPlayErrorReceiver(*this, CActive::EPriorityUserInput); |
|
43 iUtility = CMMFSwCodecUtility::NewL(); |
|
44 } |
|
45 |
|
46 |
|
47 CMMFSwCodecPlayDataPath::~CMMFSwCodecPlayDataPath() |
|
48 { |
|
49 delete iAudioPlayer; |
|
50 delete iSoundDeviceErrorReceiver; |
|
51 delete iUtility; |
|
52 |
|
53 TRequestStatus status; |
|
54 iSoundDevice->CloseDevice(iDeviceUid, status); |
|
55 //TODO there should be a timeout for the line below |
|
56 User::WaitForRequest(status); |
|
57 |
|
58 if (iCodec) |
|
59 { |
|
60 delete iSourceBuffer; |
|
61 if (!iCodec->IsNullCodec()) |
|
62 { |
|
63 delete iSoundDeviceBuffer; |
|
64 } |
|
65 } |
|
66 |
|
67 #ifdef __USE_MMF_TRANSFERBUFFERS__ |
|
68 delete iTransferWindow; |
|
69 |
|
70 if(iTransferBuffer) |
|
71 { |
|
72 iTransferBuffer->Close(); |
|
73 delete iTransferBuffer; |
|
74 } |
|
75 #endif |
|
76 |
|
77 #ifdef __USE_MMF_PTRBUFFERS__ |
|
78 delete iPtrBufferMemoryBlock; |
|
79 #endif |
|
80 } |
|
81 |
|
82 |
|
83 TInt CMMFSwCodecPlayDataPath::SetObserver(MMMFHwDeviceObserver& aObserver) |
|
84 { |
|
85 TInt error; |
|
86 if (iHwDeviceObserver) |
|
87 { |
|
88 error = KErrAlreadyExists; |
|
89 } |
|
90 else |
|
91 { |
|
92 iHwDeviceObserver = &aObserver; |
|
93 error = KErrNone; |
|
94 } |
|
95 return error; |
|
96 } |
|
97 |
|
98 |
|
99 TInt CMMFSwCodecPlayDataPath::AddCodec(CMMFSwCodec& aCodec) |
|
100 { |
|
101 if (iCodec) |
|
102 { |
|
103 return KErrNotSupported; //doesn't support multiple codecs |
|
104 } |
|
105 |
|
106 TInt err = KErrNone; |
|
107 |
|
108 iCodec = &aCodec; |
|
109 |
|
110 // Allocate data buffer |
|
111 iSourceBufferSize = iCodec->SourceBufferSize(); |
|
112 iSoundDevBufferSize = iCodec->SinkBufferSize(); |
|
113 |
|
114 if ((!iSourceBufferSize) || (!iSoundDevBufferSize)) |
|
115 { |
|
116 err = KErrArgument; //codec plugin has not specified buffer size |
|
117 } |
|
118 |
|
119 if (err == KErrNone) |
|
120 { |
|
121 #ifdef __USE_MMF_TRANSFERBUFFERS__ |
|
122 TRAP(err,iSourceBuffer = CreateTransferBufferL(iSourceBufferSize, static_cast<CMMFTransferBuffer*>(iSourceBuffer))); |
|
123 #endif |
|
124 #ifdef __USE_MMF_PTRBUFFERS__ |
|
125 TRAP(err,iSourceBuffer = CreatePtrBufferL(iSourceBufferSize)); |
|
126 #else |
|
127 TRAP(err,iSourceBuffer = CMMFDataBuffer::NewL(iSourceBufferSize)); |
|
128 #endif |
|
129 } |
|
130 |
|
131 if (err == KErrNone) |
|
132 { |
|
133 if (iCodec->IsNullCodec()) |
|
134 {//use source buffer for sound device buffer |
|
135 iSoundDeviceBuffer = NULL; |
|
136 } |
|
137 else |
|
138 {//codec needs separate source and sound device buffers |
|
139 TRAP(err,iSoundDeviceBuffer = CMMFDataBuffer::NewL(iSoundDevBufferSize)); |
|
140 } |
|
141 } |
|
142 return err; |
|
143 } |
|
144 |
|
145 |
|
146 TInt CMMFSwCodecPlayDataPath::Start() |
|
147 { |
|
148 TInt startError = KErrNone; |
|
149 |
|
150 if (!iCodec || (!iSoundDevice->Handle())) |
|
151 { |
|
152 //check that a codec has been added and the sound device is open |
|
153 startError = KErrNotReady; |
|
154 } |
|
155 |
|
156 if (iState == EPaused) |
|
157 {//we are paused so need to resume play |
|
158 if (!startError) |
|
159 { |
|
160 #ifdef _SCW_DEBUG |
|
161 RDebug::Print(_L("CMMFSwCodecPlayDataPath::Start-Resume")); |
|
162 #endif |
|
163 iAudioPlayer->ResumePlaying(); |
|
164 iState = EPlaying; |
|
165 } |
|
166 } |
|
167 else if (!startError) |
|
168 { |
|
169 #ifdef _SCW_DEBUG |
|
170 RDebug::Print(_L("CMMFSwCodecPlayDataPath::Start-Normal")); |
|
171 #endif |
|
172 if (!startError) |
|
173 { |
|
174 iNoMoreSourceData = EFalse; |
|
175 iSourceBuffer->SetLastBuffer(EFalse); |
|
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 } |
|
191 return startError; |
|
192 } |
|
193 |
|
194 |
|
195 // *** Main Play Loop *** |
|
196 |
|
197 void CMMFSwCodecPlayDataPath::FillSourceBufferL() |
|
198 {//asks observer to fill the source buffer |
|
199 // Ask immediately for data from the observer |
|
200 #ifdef __CYCLE_MMF_DATABUFFERS__ |
|
201 // Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics |
|
202 // If the creation fails, we carry on regardless as the original buffer will not have been |
|
203 // destroyed. Must do this as alloc fail tests will not run. |
|
204 if(iSourceBuffer) |
|
205 { |
|
206 iSourceBuffer = CycleAudioBuffer(iSourceBuffer); |
|
207 } |
|
208 #endif // __CYCLE_MMF_DATABUFFERS__ |
|
209 User::LeaveIfError(iHwDeviceObserver->FillThisHwBuffer(*iSourceBuffer)); |
|
210 |
|
211 } |
|
212 |
|
213 |
|
214 void CMMFSwCodecPlayDataPath::BufferFilledL(CMMFDataBuffer& aBuffer) |
|
215 {//call back from observer to indicate buffer has been filled |
|
216 if (iState == EStopped) |
|
217 User::Leave(KErrNotReady);//ok if paused? |
|
218 |
|
219 iSourceBuffer = &aBuffer; |
|
220 iSourceBuffer->SetStatus(EFull); |
|
221 #ifdef _SCW_DEBUG |
|
222 RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL")); |
|
223 #endif |
|
224 |
|
225 //need to check that the buffer size is not 0 - if so assume we've reached the end of the data |
|
226 if (!iSourceBuffer->BufferSize()) |
|
227 {//no buffer - could be end of source or could be that the source has no data?? |
|
228 iNoMoreSourceData = ETrue; |
|
229 #ifdef _SCW_DEBUG |
|
230 RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL-NoMoreSourceData")); |
|
231 #endif |
|
232 } |
|
233 //even if the buffer size is 0 we still |
|
234 //need to perform the following to get the sound device callback |
|
235 FillSoundDeviceBufferL(); //get buffer in pcm16 format for sound device |
|
236 |
|
237 // attenuate the amplitude of the samples if volume ramping has been changed |
|
238 // and is non-zero |
|
239 if (iCustomInterface) |
|
240 { |
|
241 TTimeIntervalMicroSeconds volumeRamp = iCustomInterface->VolumeRamp(); |
|
242 if (volumeRamp != iVolumeRamp) |
|
243 { |
|
244 iVolumeRamp = volumeRamp; |
|
245 if (iVolumeRamp.Int64() != 0) |
|
246 { |
|
247 iUtility->ConfigAudioRamper( |
|
248 iVolumeRamp.Int64(), |
|
249 iSampleRate, |
|
250 iChannels); |
|
251 iRampAudioSample = ETrue; |
|
252 } |
|
253 else |
|
254 { |
|
255 iRampAudioSample = EFalse; |
|
256 } |
|
257 } |
|
258 if (iRampAudioSample) |
|
259 { |
|
260 iRampAudioSample = iUtility->RampAudio(iSoundDeviceBuffer); |
|
261 } |
|
262 } |
|
263 |
|
264 iAudioPlayer->PlayData(*iSoundDeviceBuffer); //play data to sound drivers |
|
265 |
|
266 if (iSourceBuffer->LastBuffer())//check last buffer flag |
|
267 { |
|
268 iNoMoreSourceData = ETrue; |
|
269 #ifdef _SCW_DEBUG |
|
270 RDebug::Print(_L("CMMFSwCodecPlayDataPath::BufferFilledL-LBNoMoreSourceData")); |
|
271 #endif |
|
272 } |
|
273 } |
|
274 |
|
275 |
|
276 void CMMFSwCodecPlayDataPath::FillSoundDeviceBufferL() |
|
277 {//use CMMFSwCodec to fill the sound device buffer |
|
278 |
|
279 CMMFSwCodec::TCodecProcessResult codecProcessResult; |
|
280 |
|
281 if (iCodec->IsNullCodec()) |
|
282 {//no codec so data can be sent direct to sink |
|
283 iSoundDeviceBuffer = iSourceBuffer; |
|
284 iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full |
|
285 } |
|
286 else |
|
287 { |
|
288 //pass buffer to codec for processing |
|
289 codecProcessResult = iCodec->ProcessL(*iSourceBuffer, *iSoundDeviceBuffer); |
|
290 if (iSourceBuffer->LastBuffer()) //if source is last buffer so is sound dev |
|
291 iSoundDeviceBuffer->SetLastBuffer(ETrue); |
|
292 if ((!iSoundDeviceBuffer->BufferSize())&&(codecProcessResult.iDstBytesAdded)) |
|
293 {//the codec has added data but not set the buffer length |
|
294 iSoundDeviceBuffer->Data().SetLength(codecProcessResult.iDstBytesAdded); |
|
295 } |
|
296 //only supports EProcessComplete |
|
297 switch (codecProcessResult.iCodecProcessStatus) |
|
298 { |
|
299 case CMMFSwCodec::TCodecProcessResult::EProcessComplete: |
|
300 //finished procesing source data - all data in sink buffer |
|
301 { |
|
302 iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full |
|
303 } |
|
304 break; |
|
305 case CMMFSwCodec::TCodecProcessResult::EDstNotFilled: |
|
306 //could be the last buffer in which case dst might not get filled |
|
307 { |
|
308 iSoundDeviceBuffer->SetStatus(EFull); //sink buffer is full |
|
309 } |
|
310 break; |
|
311 case CMMFSwCodec::TCodecProcessResult::EEndOfData: |
|
312 //no more data - send what we've got to the sink |
|
313 //note we can't always rely on this - in many cases the codec will not know when |
|
314 //it has reached the end of data. |
|
315 { |
|
316 iSoundDeviceBuffer->SetStatus(EFull);//sink buffer may not really be 'full' but its as full as it going to get |
|
317 iNoMoreSourceData = ETrue; |
|
318 //doesn't matter if sink buffer is not full |
|
319 } |
|
320 break; |
|
321 default: |
|
322 Panic(EMMFSwCodecWrapperBadCodec); //should never get here - bad codec |
|
323 } |
|
324 } |
|
325 } |
|
326 |
|
327 |
|
328 void CMMFSwCodecPlayDataPath::BufferEmptiedL(const CMMFDataBuffer& aBuffer) |
|
329 {//call back from CDataPathPlayer when the sound device buffer has been emptied |
|
330 if (&aBuffer != iSoundDeviceBuffer) |
|
331 Panic(EMMFSwCodecWrapperBadBuffer); |
|
332 if (!iNoMoreSourceData) |
|
333 FillSourceBufferL(); |
|
334 } |
|
335 |
|
336 //*** End of Main Play Loop *** |
|
337 |
|
338 |
|
339 void CMMFSwCodecPlayDataPath::Stop() |
|
340 { |
|
341 iAudioPlayer->Cancel(); |
|
342 iSoundDeviceErrorReceiver->Cancel(); |
|
343 TRequestStatus status; |
|
344 iSoundDevice->CloseDevice(iDeviceUid, status); |
|
345 User::WaitForRequest(status); |
|
346 |
|
347 #ifdef __CYCLE_MMF_DATABUFFERS__ |
|
348 // Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics |
|
349 // If the creation fails, we carry on regardless as the original buffer will not have been |
|
350 // destroyed. Must do this as alloc fail tests will not run. |
|
351 if(iSourceBuffer) |
|
352 { |
|
353 iSourceBuffer = CycleAudioBuffer(iSourceBuffer); |
|
354 } |
|
355 #endif // __CYCLE_MMF_DATABUFFERS__ |
|
356 |
|
357 iState = EStopped; |
|
358 } |
|
359 |
|
360 |
|
361 void CMMFSwCodecPlayDataPath::Pause() |
|
362 { |
|
363 //since a pause can happen anyway in the datatransfer -need to set to a known |
|
364 //state so that when play is resumed the behaviour is predictable |
|
365 if (iSoundDevice->Handle()) |
|
366 { |
|
367 iSoundDevice->PauseBuffer(); // ignores return value? |
|
368 iState = EPaused; |
|
369 #ifdef _SCW_DEBUG |
|
370 RDebug::Print(_L("Pause")); |
|
371 #endif |
|
372 } |
|
373 else |
|
374 {//an error must have occured |
|
375 iState = EStopped; |
|
376 } |
|
377 } |
|
378 |
|
379 |
|
380 CRoutingSoundPlayDevice* CMMFSwCodecPlayDataPath::Device() |
|
381 { |
|
382 return iSoundDevice; |
|
383 } |
|
384 |
|
385 void CMMFSwCodecPlayDataPath::SoundDeviceException(TInt aError) |
|
386 { |
|
387 //this sends a request to the hw device observer usually Devsound |
|
388 //to update the bytes played |
|
389 //it is done here so that the sound driver can be closed prior to |
|
390 //updating the plicy and sending the error back |
|
391 TUid uidUpdateBytesPlayed; |
|
392 uidUpdateBytesPlayed.iUid = KMmfHwDeviceObserverUpdateBytesPlayed; |
|
393 TPtrC8 dummy(0,0); |
|
394 iHwDeviceObserver->MsgFromHwDevice(uidUpdateBytesPlayed,dummy); |
|
395 |
|
396 //this closes RMdaDevSound. |
|
397 Stop(); |
|
398 |
|
399 //inform devsound so it can update policy |
|
400 iHwDeviceObserver->Stopped(); |
|
401 |
|
402 // Inform the observer of the exception condition |
|
403 // We inform the hw device observer after the policy has been |
|
404 // updated incase the observer relied on the error to assume |
|
405 // the policy has been updated |
|
406 iHwDeviceObserver->Error(aError); |
|
407 } |
|
408 |
|
409 |
|
410 void CMMFSwCodecPlayDataPath::SetPlayCustomInterface(MPlayCustomInterface& aCustomInterface) |
|
411 { |
|
412 iCustomInterface = &aCustomInterface; |
|
413 } |
|
414 |
|
415 void CMMFSwCodecPlayDataPath::SetConfigForAudioRamp(TUint aSampleRate, TUint aChannels) |
|
416 { |
|
417 iSampleRate = aSampleRate; |
|
418 iChannels = aChannels; |
|
419 } |
|
420 |
|
421 /************************************************************************ |
|
422 * CDataPathPlayer * |
|
423 ************************************************************************/ |
|
424 |
|
425 CDataPathPlayer::CDataPathPlayer(CMMFSwCodecPlayDataPath& aParent, TInt aPriority) |
|
426 : CActive(aPriority), iParent(aParent) |
|
427 { |
|
428 CActiveScheduler::Add(this); |
|
429 } |
|
430 |
|
431 |
|
432 CDataPathPlayer::~CDataPathPlayer() |
|
433 { |
|
434 Cancel(); |
|
435 } |
|
436 |
|
437 |
|
438 void CDataPathPlayer::Start() |
|
439 { |
|
440 // No implementation |
|
441 } |
|
442 |
|
443 |
|
444 void CDataPathPlayer::ResumePlaying() |
|
445 { |
|
446 if (iParent.Device()->Handle()) |
|
447 { |
|
448 //should be ok to call this even if we are active |
|
449 iParent.Device()->ResumePlaying(); |
|
450 iResumePlaying = ETrue; |
|
451 } |
|
452 #ifdef _SCW_DEBUG |
|
453 RDebug::Print(_L("Playing Resumed")); |
|
454 #endif |
|
455 } |
|
456 |
|
457 |
|
458 void CDataPathPlayer::PlayData(const CMMFDataBuffer& aData) |
|
459 { |
|
460 iDataFromSource = &aData; |
|
461 if (!IsActive()) |
|
462 { |
|
463 #ifdef _SCW_DEBUG |
|
464 RDebug::Print(_L("CDataPathPlayer::PlayData")); |
|
465 #endif |
|
466 iParent.Device()->PlayData(aData.Data(), iStatus); |
|
467 SetActive(); |
|
468 } |
|
469 } |
|
470 |
|
471 |
|
472 void CDataPathPlayer::Stop() |
|
473 { |
|
474 if (!IsActive()) |
|
475 { |
|
476 iParent.Device()->FlushBuffer(); |
|
477 } |
|
478 Cancel(); |
|
479 iParent.SoundDeviceException(KErrCancel); |
|
480 } |
|
481 |
|
482 |
|
483 void CDataPathPlayer::RunL() |
|
484 { |
|
485 #ifdef _SCW_DEBUG |
|
486 RDebug::Print(_L("CDataPathPlayer::RunL error[%d]"), iStatus.Int()); |
|
487 #endif |
|
488 if (!iStatus.Int()) |
|
489 { |
|
490 iParent.BufferEmptiedL(static_cast<const CMMFDataBuffer&>(*iDataFromSource)); |
|
491 iResumePlaying = EFalse; |
|
492 } |
|
493 //if we don't have a sound driver handle then we have stopped |
|
494 //but the client still thinks we are recording so swallow error |
|
495 else if (iStatus.Int()!= KErrBadHandle) |
|
496 { |
|
497 iParent.SoundDeviceException(iStatus.Int()); |
|
498 } |
|
499 } |
|
500 |
|
501 |
|
502 TInt CDataPathPlayer::RunError(TInt aError) |
|
503 { |
|
504 Error(aError); |
|
505 return KErrNone; |
|
506 } |
|
507 |
|
508 |
|
509 void CDataPathPlayer::DoCancel() |
|
510 { |
|
511 if (iParent.Device()->Handle()) |
|
512 { |
|
513 iParent.Device()->CancelPlayData(); |
|
514 iParent.Device()->FlushBuffer(); |
|
515 } |
|
516 } |
|
517 |
|
518 |
|
519 void CDataPathPlayer::Error(TInt aError) |
|
520 { |
|
521 iParent.SoundDeviceException(aError); |
|
522 } |
|
523 |
|
524 |
|
525 /************************************************************************ |
|
526 * CSoundDevPlayErrorReceiver * |
|
527 ************************************************************************/ |
|
528 |
|
529 CSoundDevPlayErrorReceiver::CSoundDevPlayErrorReceiver(CMMFSwCodecPlayDataPath& aParent, TInt aPriority) |
|
530 : CActive(aPriority), iParent(aParent) |
|
531 { |
|
532 CActiveScheduler::Add(this); |
|
533 } |
|
534 |
|
535 CSoundDevPlayErrorReceiver::~CSoundDevPlayErrorReceiver() |
|
536 { |
|
537 Cancel(); |
|
538 } |
|
539 |
|
540 void CSoundDevPlayErrorReceiver::Start() |
|
541 { |
|
542 iParent.Device()->NotifyError(iStatus); |
|
543 SetActive(); |
|
544 } |
|
545 |
|
546 void CSoundDevPlayErrorReceiver::Stop() |
|
547 { |
|
548 Cancel(); |
|
549 } |
|
550 |
|
551 void CSoundDevPlayErrorReceiver::RunL() |
|
552 { |
|
553 // An error has been returned |
|
554 #ifdef _SCW_DEBUG |
|
555 RDebug::Print(_L("CSoundDevPlayErrorReceiver::RunL[%d]"), iStatus.Int()); |
|
556 #endif |
|
557 iParent.SoundDeviceException(iStatus.Int()); |
|
558 } |
|
559 |
|
560 void CSoundDevPlayErrorReceiver::DoCancel() |
|
561 { |
|
562 iParent.Device()->CancelNotifyError(); |
|
563 } |
|
564 |
|
565 |