|
1 // Copyright (c) 2002-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\mmfdatapath.cpp |
|
15 // |
|
16 // |
|
17 |
|
18 #include <e32math.h> |
|
19 #include <mmf/common/mmffourcc.h> |
|
20 #include <mmf/common/mmfpaniccodes.h> |
|
21 #include <mmf/server/mmfaudiooutput.h> |
|
22 #include <mmf/server/mmfaudioinput.h> |
|
23 #include <mmf/server/mmfdatapath.h> |
|
24 #include "mmfclientaudiostreamutils.h" |
|
25 #include <mmf/common/mmfaudio.h> |
|
26 #include <mmf/plugin/mmfcodecimplementationuids.hrh> // KUidMmfCodecAudioSettings |
|
27 #include <mmf/server/devsoundstandardcustominterfaces.h> |
|
28 |
|
29 const TUid KUidCodecAudioConfig = {KUidMmfCodecAudioSettings}; |
|
30 |
|
31 void Panic(TMMFDataPathPanicCode aPanicCode, TInt aSourceLineNumber) |
|
32 { |
|
33 _LIT(KMMFDataPathPanicCategory, "MMFDataPath"); |
|
34 User::Panic(KMMFDataPathPanicCategory, STATIC_CAST(TInt,aPanicCode) + aSourceLineNumber); |
|
35 } |
|
36 |
|
37 //all functions are exported form the DLL and are virtual to allow plugins to define there own CMMFDataPaths |
|
38 |
|
39 /** |
|
40 Allocates and constructs a data path. |
|
41 |
|
42 Use this function if the codec UID is not already known by CMMFController |
|
43 and there is no data path ambiguity - ie only one data path is possible. |
|
44 |
|
45 Will create codec via fourCC. |
|
46 |
|
47 @param aEventHandler |
|
48 Installs an event handler to provide message passing between clients and sources/sinks. |
|
49 |
|
50 @return Newly constructed data path object. |
|
51 */ |
|
52 |
|
53 EXPORT_C CMMFDataPath* CMMFDataPath::NewL(MAsyncEventHandler& aEventHandler) |
|
54 { |
|
55 CMMFDataPath* self = new(ELeave) CMMFDataPath(TMediaId(), aEventHandler); |
|
56 CleanupStack::PushL(self); |
|
57 self->ConstructL(); |
|
58 CleanupStack::Pop(); |
|
59 return self; |
|
60 } |
|
61 |
|
62 |
|
63 /** |
|
64 Allocates and constructs a data path according to the specified media ID. |
|
65 |
|
66 Use this function if the codec UID is not already known by CMMFController |
|
67 and there is ambiguity with the data path ie. there is more than one possible data path. |
|
68 |
|
69 @param aMediaId |
|
70 Optional media ID parameter when there are multiple media types. |
|
71 @param aEventHandler |
|
72 Installs an event handler to provide message passing between clients and sources/sinks. |
|
73 |
|
74 @return A newly constructed data path object. |
|
75 */ |
|
76 |
|
77 EXPORT_C CMMFDataPath* CMMFDataPath::NewL(TMediaId aMediaId, MAsyncEventHandler& aEventHandler) |
|
78 { |
|
79 CMMFDataPath* self = new(ELeave) CMMFDataPath(aMediaId, aEventHandler); |
|
80 CleanupStack::PushL(self); |
|
81 self->ConstructL(); |
|
82 CleanupStack::Pop(); |
|
83 return self; |
|
84 } |
|
85 |
|
86 /** |
|
87 Allocates and constructs a data path according to the specified codec UID. |
|
88 |
|
89 Use this function if the codec UID is already known by CMMFController |
|
90 and there is no data path ambiguity ie. only one data path is possible |
|
91 will create codec explicitly using the supplied codec Uid |
|
92 |
|
93 @param aCodecUid |
|
94 Optional mediaID parameter when there are multiple media types |
|
95 @param aEventHandler |
|
96 Installs an event handler to provide message passing between clients and sources/sinks. |
|
97 |
|
98 @return A newly constructed data path object. |
|
99 */ |
|
100 |
|
101 EXPORT_C CMMFDataPath* CMMFDataPath::NewL(TUid aCodecUid, MAsyncEventHandler& aEventHandler) |
|
102 { |
|
103 CMMFDataPath* self = new(ELeave) CMMFDataPath(TMediaId(), aEventHandler); |
|
104 CleanupStack::PushL(self); |
|
105 self->ConstructL(aCodecUid); |
|
106 CleanupStack::Pop(); |
|
107 return self; |
|
108 } |
|
109 |
|
110 |
|
111 /** |
|
112 Allocates and constructs a data path according to the specified codec UID. |
|
113 |
|
114 Use this function if the codec UID is already known by CMMFController |
|
115 and there is ambiguity ie. more than one possible data path. |
|
116 TMediaId used to select the path. |
|
117 |
|
118 @param aCodecUid |
|
119 The codec UID. |
|
120 @param aMediaId |
|
121 Optional mediaID parameter when there are multiple media types. |
|
122 @param aEventHandler |
|
123 Installs an event handler to provide message passing between clients and sources/sinks. |
|
124 |
|
125 @return A newly constructed data path object. |
|
126 */ |
|
127 EXPORT_C CMMFDataPath* CMMFDataPath::NewL(TUid aCodecUid, TMediaId aMediaId, MAsyncEventHandler& aEventHandler) |
|
128 { |
|
129 CMMFDataPath* self = new(ELeave) CMMFDataPath(aMediaId, aEventHandler); |
|
130 CleanupStack::PushL(self); |
|
131 self->ConstructL(aCodecUid); |
|
132 CleanupStack::Pop(); |
|
133 return self; |
|
134 } |
|
135 |
|
136 /** |
|
137 Standard destructor. |
|
138 */ |
|
139 |
|
140 EXPORT_C CMMFDataPath::~CMMFDataPath() |
|
141 { |
|
142 Cancel(); |
|
143 delete iCodec; |
|
144 DoCleanupBuffers(); |
|
145 |
|
146 //log off the source and sink |
|
147 if (iDataSource) |
|
148 iDataSource->SourceThreadLogoff(); |
|
149 if (iDataSink) |
|
150 iDataSink->SinkThreadLogoff(); |
|
151 |
|
152 if (iCompleteCallback) |
|
153 { |
|
154 iCompleteCallback->Cancel(); |
|
155 delete iCompleteCallback; |
|
156 } |
|
157 } |
|
158 |
|
159 /** |
|
160 Deletes buffers if this datapath's sources and sinks own the buffers returned by PrimeL(). |
|
161 Typically if buffers are created asychronously, the datapath doesn't own the buffer |
|
162 so leaves cleanup handling to the owner sources/sinks. |
|
163 |
|
164 Called when source and sink needs to be de-referenced. Sets iDataPathCreated, iSinkCanReceive, |
|
165 iSnkBufRef and iSrcBufRef to EFalse; sets iState to EStopped. |
|
166 */ |
|
167 EXPORT_C void CMMFDataPath::ResetL() |
|
168 { |
|
169 delete iCodec; |
|
170 iCodec = NULL; |
|
171 DoCleanupBuffers(); // Delete buffers |
|
172 //logoff and dereference source and sink |
|
173 if (iDataSource) |
|
174 { iDataSource->SourceThreadLogoff(); iDataSource = NULL; } |
|
175 if (iDataSink) |
|
176 { iDataSink->SinkThreadLogoff(); iDataSink = NULL; } |
|
177 |
|
178 // Reset states |
|
179 iDataPathCreated = EFalse; |
|
180 iState = EStopped; |
|
181 iSrcBufRef = EFalse; |
|
182 iSnkBufRef = EFalse; |
|
183 iPauseCalled = EFalse; |
|
184 |
|
185 delete iCompleteCallback; iCompleteCallback = NULL; |
|
186 } |
|
187 |
|
188 /** |
|
189 Delete source and/or sink buffers that are owned by DataPath. |
|
190 |
|
191 Ownership indicated by iSrcBufRef and iSnkBufRef. |
|
192 |
|
193 Ownership is assigned during buffer allocation within the datapath PrimeL(). |
|
194 */ |
|
195 void CMMFDataPath::DoCleanupBuffers() |
|
196 { |
|
197 // delete source and/or sink buffer that is owned by DataPath |
|
198 if ( !iSrcBufRef && iSourceBuffer ) |
|
199 { |
|
200 delete iSourceBuffer; |
|
201 } |
|
202 iSourceBuffer = NULL; |
|
203 if ( !iSnkBufRef && iSinkBuffer ) |
|
204 { |
|
205 delete iSinkBuffer; |
|
206 } |
|
207 iSinkBuffer = NULL; |
|
208 } |
|
209 |
|
210 |
|
211 /** |
|
212 Obtain source and/or sink buffer using the synchronous API CreateSourceBufferL() and CreateSinkBufferL(). |
|
213 */ |
|
214 void CMMFDataPath::ObtainSyncBuffersL() |
|
215 { |
|
216 //Try to create source and sink buffers. If we can't create them synchronously via |
|
217 //CreateSourceBufferL and CreateSinkBufferL we will need to obtain them by |
|
218 //asynchronous buffer creation when playing starts. |
|
219 |
|
220 if (iBuffersToUse & ENeedSourceBuffer) |
|
221 { |
|
222 if (!iSourceBuffer) //we may already have a buffer from a previous initialization |
|
223 { |
|
224 TRAPD(err, iSourceBuffer = iDataSource->CreateSourceBufferL(iMediaId,*iSinkBuffer, iSrcBufRef)); |
|
225 if(err != KErrNone && err != KErrNotSupported) |
|
226 { |
|
227 #ifdef _DP_DEBUG |
|
228 RDebug::Print(_L("DP::ObtainSyncBuffersL - Leaving %d (this 0x%x)\n"),err, this); |
|
229 #endif |
|
230 User::Leave(err); |
|
231 } |
|
232 } |
|
233 } |
|
234 |
|
235 |
|
236 if (iBuffersToUse & ENeedSinkBuffer) |
|
237 { |
|
238 if (!iSinkBuffer) //we may already have a buffer from a previous initialization |
|
239 { |
|
240 TRAPD(err, iSinkBuffer = iDataSink->CreateSinkBufferL(iMediaId, iSnkBufRef)); |
|
241 if(err != KErrNone && err != KErrNotSupported) |
|
242 { |
|
243 #ifdef _DP_DEBUG |
|
244 RDebug::Print(_L("DP::ObtainSyncBuffersL - Leaving %d (this 0x%x)\n"),err, this); |
|
245 #endif |
|
246 User::Leave(err); |
|
247 } |
|
248 } |
|
249 } |
|
250 |
|
251 if (iSourceBuffer && !(iBuffersToUse & ENeedSinkBuffer)) |
|
252 {//only need one buffer, use source |
|
253 iSinkBuffer =iSourceBuffer; |
|
254 iSnkBufRef = ETrue; //the sink buffer is not to be deleted |
|
255 } |
|
256 else if (iSinkBuffer && !(iBuffersToUse & ENeedSourceBuffer)) |
|
257 {//only need one buffer, use sink |
|
258 iSourceBuffer =iSinkBuffer; |
|
259 iSrcBufRef = ETrue; //the sink buffer is not to be deleted |
|
260 } |
|
261 |
|
262 #ifdef _DP_DEBUG |
|
263 RDebug::Print(_L("DP::ObtainSyncBuffersL - DONE iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); |
|
264 #endif |
|
265 } |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 |
|
271 /** |
|
272 Constructs a source. |
|
273 |
|
274 The default implementation leaves with KErrNotSupported. |
|
275 |
|
276 @param aInitData |
|
277 The initialisation data. |
|
278 */ |
|
279 |
|
280 EXPORT_C void CMMFDataPath::ConstructSourceL( const TDesC8& /*aInitData*/ ) |
|
281 { |
|
282 User::Leave(KErrNotSupported); |
|
283 } |
|
284 |
|
285 /** |
|
286 Constructs a sink. |
|
287 |
|
288 Overridable constuction specific to this datasource. |
|
289 |
|
290 The default implementation leaves with KErrNotSupported. |
|
291 |
|
292 @param aInitData |
|
293 The initialisation data. |
|
294 */ |
|
295 EXPORT_C void CMMFDataPath::ConstructSinkL( const TDesC8& /*aInitData*/ ) |
|
296 { |
|
297 User::Leave(KErrNotSupported); |
|
298 } |
|
299 |
|
300 /** |
|
301 Takes UID of codec on construction, and if not an NULL codec sets the datapath up for codec instantiation. |
|
302 |
|
303 @param aCodecUid |
|
304 The UID of the codec. |
|
305 */ |
|
306 |
|
307 EXPORT_C void CMMFDataPath::ConstructL(TUid aCodecUid) |
|
308 { |
|
309 iUseSuppliedCodecUid = EFalse; //initially assume no supplied codec uid |
|
310 |
|
311 if (aCodecUid != KNullUid) |
|
312 {//the data path NewL has specified a specific codec |
|
313 //create CMMFCodec here |
|
314 iCodec = CMMFCodec::NewL(aCodecUid); |
|
315 if (iCodec) |
|
316 iUseSuppliedCodecUid = ETrue; |
|
317 } |
|
318 |
|
319 iSrcBufRef = EFalse; |
|
320 iSnkBufRef = EFalse; |
|
321 iObtainingAsyncSourceBuffer = EFalse; |
|
322 iObtainingAsyncSinkBuffer = EFalse; |
|
323 iSourceBufferWithSource = EFalse; |
|
324 iSinkBufferWithSink = EFalse; |
|
325 } |
|
326 |
|
327 |
|
328 /** |
|
329 Adds a data source to the datapath and, if the sink already exists, tries to establish a connection |
|
330 between the source and sink. |
|
331 |
|
332 @param aSource |
|
333 The data source to add to the data path. |
|
334 */ |
|
335 EXPORT_C void CMMFDataPath::AddDataSourceL(MDataSource* aSource) |
|
336 { |
|
337 if (!iDataSink) iDataSource=aSource; //can't create a data path without the MDataSink as well |
|
338 else if (!iUseSuppliedCodecUid) //no supplied uid need to see if we can create codec to establish a data path |
|
339 {//we have a data sink as well so check a data path can be established between source&sink |
|
340 CreateDataPathL(aSource, iDataSink); |
|
341 iDataSource = aSource; |
|
342 } |
|
343 else //the CMMFController specified the codec uid so must use existing codec |
|
344 {//note we are assuming here that the CMMFController knows what it is doing ie the supplied codec uid |
|
345 //can make the appropriate data conversion |
|
346 iDataPathCreated = ETrue; |
|
347 iDataSource = aSource; |
|
348 } |
|
349 ClearPlayWindowL() ; |
|
350 User::LeaveIfError(iDataSource->SourceThreadLogon(*this)); |
|
351 } |
|
352 |
|
353 |
|
354 /** |
|
355 Adds a data sink to the datapath and, if the source already exists, tries to establish a connection |
|
356 between the source and sink. |
|
357 |
|
358 @param aSink |
|
359 The data sink to add to the data path. |
|
360 */ |
|
361 |
|
362 EXPORT_C void CMMFDataPath::AddDataSinkL(MDataSink* aSink) |
|
363 { |
|
364 if (!iDataSource) iDataSink=aSink; //can't create a data path without the MDataSource as well |
|
365 else if (!iUseSuppliedCodecUid) //no supplied uid need to see if we can create codec to establish a data path |
|
366 {//we have a data source as well so check a media path can be established between source&sink |
|
367 CreateDataPathL(iDataSource, aSink); |
|
368 iDataSink = aSink; |
|
369 } |
|
370 else //the CMMFController specified the codec uid so must use existing codec |
|
371 {//note we are assuming here that the CMMFController knows what it is doing ie the supplied codec uid |
|
372 //can make the appropriate data conversion |
|
373 iDataPathCreated = ETrue; |
|
374 iDataSink = aSink; |
|
375 |
|
376 //set 4CCs |
|
377 iSourceFourCC = iDataSink->SinkDataTypeCode(iMediaId);//sink because CMMFDataPath is an MDataSink to its MDataSource! |
|
378 iSinkFourCC = iDataSource->SourceDataTypeCode(iMediaId);//source because CMMFDataPath is an MDataSource to its MDataSink! |
|
379 } |
|
380 User::LeaveIfError(iDataSink->SinkThreadLogon(*this)); |
|
381 } |
|
382 |
|
383 |
|
384 /* |
|
385 * CreateDataPathL |
|
386 * |
|
387 * internal function to establish a datapath between the source and sink |
|
388 * the data supplied by the sink adn expected by the source are checked and |
|
389 * a codec is instantiated if necessary |
|
390 * |
|
391 * @param aSource |
|
392 * @param aSink |
|
393 */ |
|
394 |
|
395 void CMMFDataPath::CreateDataPathL(MDataSource* aSource, MDataSink* aSink) |
|
396 { //procedure to attempt to match the source to the sink creating a codec if necessary |
|
397 // returns ETrue if the datapath could be constructed else false |
|
398 //sets iCodec to the appropriate codec.& sets its own iSink/iSource FourCC datatype codes |
|
399 iDataPathCreated = EFalse; |
|
400 if (aSource && aSink) //have a source and sink |
|
401 { //we have a data source & sink but no codec so try and find one - if required |
|
402 TFourCC sourceFourCCCode = aSource->SourceDataTypeCode(iMediaId); //get MDataSource data type fourCC code |
|
403 TFourCC sinkFourCCCode = aSink->SinkDataTypeCode(iMediaId); //get MDataSink data type fourCC code |
|
404 if ((sourceFourCCCode != sinkFourCCCode) && //MDataSource & MDataSink datatypes are not compatible |
|
405 (sourceFourCCCode != KMMFFourCCCodeNULL) && (sinkFourCCCode != KMMFFourCCCodeNULL)) |
|
406 {//we need a codec to make the conversion between the source and the sink |
|
407 CMMFCodec* codec = CMMFCodec::NewL(sourceFourCCCode, sinkFourCCCode); |
|
408 |
|
409 if (codec) |
|
410 { |
|
411 delete iCodec; |
|
412 iCodec = codec; |
|
413 //data path created ie have source/sink and can match their datatypes |
|
414 iDataPathCreated = ETrue; |
|
415 |
|
416 //now we have an source attached we need to configure the codec for sample rate |
|
417 //and number of channels |
|
418 |
|
419 //prepare a package to send to a codec |
|
420 TMMFAudioConfig audioSettings; |
|
421 |
|
422 //test for decoder |
|
423 if (aSource->DataSourceType() == KUidMmfFormatDecode) |
|
424 { |
|
425 audioSettings.iSampleRate = static_cast<CMMFFormatDecode*>(aSource)->SampleRate(); |
|
426 audioSettings.iChannels = static_cast<CMMFFormatDecode*>(aSource)->NumChannels(); |
|
427 } |
|
428 |
|
429 //package up to send to codec |
|
430 TPckgBuf<TMMFAudioConfig> configPackage(audioSettings); |
|
431 |
|
432 //we need to catch User::Leave(KErrNotSupported) as by default most codecs |
|
433 //do not support the ConfigureL method. |
|
434 TRAPD(err,iCodec->ConfigureL(KUidCodecAudioConfig, configPackage)); |
|
435 // need to check other error here |
|
436 if (err != KErrNone && err != KErrNotSupported) |
|
437 { |
|
438 User::Leave(err); |
|
439 } |
|
440 } |
|
441 else |
|
442 { |
|
443 User::Leave( KErrNotSupported ) ; //couldn't find suitable codec |
|
444 } |
|
445 } //if (sourceFourCCCode != sinkFourCCCode) |
|
446 else |
|
447 { //source & sink fourCC datatypes are the same so no codec is required |
|
448 __ASSERT_DEBUG(iCodec == NULL, Panic(EMMFDataPathPanicProgrammingError,__LINE__)); |
|
449 |
|
450 iDataPathCreated = ETrue; |
|
451 } |
|
452 //can assign FourCC codes for the CMMFDataPath |
|
453 iSinkFourCC = sourceFourCCCode; //sink because CMMFDataPath is an MDataSink to its MDataSource! |
|
454 iSourceFourCC = sinkFourCCCode; //source because CMMFDataPath is an MDataSource to its MDataSink! |
|
455 //If sink & source need its own Prime() done in controller |
|
456 } |
|
457 } |
|
458 |
|
459 /** |
|
460 Clears the specified buffer. |
|
461 |
|
462 Pure virtual dummy implementation, not needed by datapath |
|
463 comes from MDataSink - CMMFData path is a sink to its MDataSource. |
|
464 |
|
465 This is only required for an active push MDataSource requesting a buffer empty. |
|
466 |
|
467 @param aBuffer |
|
468 The buffer to empty. |
|
469 @param aSupplier |
|
470 The MDataSource supplying this buffer. |
|
471 @param aMediaId |
|
472 An optional mediaID parameter when there are multiple buffers arriving of different media types. |
|
473 |
|
474 */ |
|
475 EXPORT_C void CMMFDataPath::EmptyBufferL(CMMFBuffer* /* aBuffer */, MDataSource* /*aSupplier*/, TMediaId /*aMediaId*/) |
|
476 { |
|
477 //not implemented |
|
478 } |
|
479 |
|
480 |
|
481 |
|
482 /* |
|
483 * FillSourceBufferL |
|
484 * |
|
485 * Function to get data from the datapath's iDataSource |
|
486 */ |
|
487 |
|
488 void CMMFDataPath::FillSourceBufferL() |
|
489 { |
|
490 #ifdef _DP_DEBUG |
|
491 RDebug::Print(_L("DP::FillSourceBufferL tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
492 #endif |
|
493 |
|
494 __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__)); |
|
495 |
|
496 |
|
497 // clear the no-more-source flag here (as well as in PlayL()) because |
|
498 // there may have been a re-position since the last call to BufferFilledL() |
|
499 iNoMoreSourceData = EFalse; |
|
500 |
|
501 if(!iObtainingAsyncSourceBuffer) |
|
502 {//this is a normal request for data. |
|
503 //If we are getting asynchronous buffers, then can't do this as iSourceBuffer == NULL |
|
504 iSourceBuffer->SetFrameNumber(++iCurrentSourceFrameNumber); //so source knows which data to load buffer with |
|
505 iSourceBuffer->SetStatus(EBeingFilled); |
|
506 iSourceBuffer->SetLastBuffer(EFalse); |
|
507 } |
|
508 |
|
509 #ifdef _DP_DEBUG |
|
510 RDebug::Print(_L("DP asking for buffer %d - ptr=0x%x (this 0x%x)\n"), iCurrentSourceFrameNumber, iSourceBuffer,this); |
|
511 #endif |
|
512 |
|
513 iSourceBufferWithSource = ETrue; |
|
514 |
|
515 // wait for BufferFilled callback from source. Do this here as some sources cause |
|
516 //re-entrancy into data path via BufferFilledL |
|
517 ChangeDataPathTransferState(EWaitSource); |
|
518 |
|
519 iDataSource->FillBufferL(iSourceBuffer, this, iMediaId); |
|
520 |
|
521 #ifdef _DP_DEBUG |
|
522 RDebug::Print(_L("DP::FillSourceBufferL - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
523 #endif |
|
524 } |
|
525 |
|
526 |
|
527 /** |
|
528 Indicates the data source has filled the specified buffer. |
|
529 |
|
530 Called by the CMMFDataPath's MDataSource when it has filled the buffer. |
|
531 |
|
532 @param aBuffer |
|
533 A pointer to the filled buffer. |
|
534 */ |
|
535 EXPORT_C void CMMFDataPath::BufferFilledL(CMMFBuffer* aBuffer) |
|
536 { |
|
537 #ifdef _DP_DEBUG |
|
538 RDebug::Print(_L("DP::BufferFilledL src has filled buffer %d (ptr=0x%x) with %d bytes EoF = %d tick-%d (this 0x%x)\n"),aBuffer->FrameNumber(),aBuffer, aBuffer->BufferSize(),aBuffer->LastBuffer(), User::TickCount(),this); |
|
539 #endif |
|
540 |
|
541 //This assertion is commented because of PDEF117405 |
|
542 //state only used if we are passing data |
|
543 //__ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__)); |
|
544 |
|
545 __ASSERT_DEBUG((!iNoMoreSourceData), Panic(EMMFDataPathPanicBadState,__LINE__)); |
|
546 |
|
547 iSourceBufferWithSource = EFalse; |
|
548 |
|
549 //Has the datapath stopped running, if so were not interested in any callbacks. |
|
550 if(iState == EStopped || iState == EPrimed || (iPauseCalled && iState != ERecording)) |
|
551 { |
|
552 #ifdef _DP_DEBUG |
|
553 RDebug::Print(_L("DP::BufferFilledL called while not expecting callback iState=%d iPauseCalled=%d (this 0x%x)\n"),iState, iPauseCalled,this); |
|
554 #endif |
|
555 return; |
|
556 } |
|
557 |
|
558 #ifdef REPOSITION_SPEEDUP |
|
559 // if the source has been re-positioned, then go & get some more source data now |
|
560 if (!iObtainingAsyncSourceBuffer && iSourceBuffer->FrameNumber() != iCurrentSourceFrameNumber) |
|
561 { |
|
562 #ifdef _DP_DEBUG |
|
563 RDebug::Print(_L("DP::BufferFilledL source was re-positioned re-requesting source data (this 0x%x)\n"),this); |
|
564 #endif |
|
565 ChangeDataPathTransferState(ENeedSourceData); |
|
566 return; |
|
567 } |
|
568 #endif //REPOSITION_SPEEDUP |
|
569 |
|
570 //bufer is NULL, indicating no more source data. |
|
571 if (!aBuffer) |
|
572 { |
|
573 //If we only hold a reference to the source buffer, set that to NULL |
|
574 if(iSnkBufRef) |
|
575 iSourceBuffer = NULL; |
|
576 |
|
577 |
|
578 iNoMoreSourceData = ETrue; |
|
579 |
|
580 if(!iCodec || //there's only one buffer and that has been returned as NULL, so must be end of data |
|
581 iSinkBufferWithSink) //buffer is with sink, we don't have any more data to put in it, so must be end of data |
|
582 { |
|
583 ChangeDataPathTransferState(EEndOfData); |
|
584 } |
|
585 else //sink buffer is with datapath, see if there is anything to send to sink |
|
586 ChangeDataPathTransferState(ENeedToMatchSourceToSink); |
|
587 |
|
588 #ifdef _DP_DEBUG |
|
589 RDebug::Print(_L("DP::BufferFilledL DONE aBuffer==NULL tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
590 #endif |
|
591 return; |
|
592 } |
|
593 |
|
594 |
|
595 //We were waiting for a response from the source to get an asynchronous buffer. |
|
596 //We now have it, and we proceed to transfer this data to the sink. |
|
597 if (iObtainingAsyncSourceBuffer) |
|
598 { |
|
599 iObtainingAsyncSourceBuffer = EFalse; |
|
600 } |
|
601 |
|
602 |
|
603 aBuffer->SetStatus(EFull); |
|
604 |
|
605 if(iSourceBuffer != aBuffer) |
|
606 {//buffer has been changed by the source |
|
607 iSourceBuffer = aBuffer; |
|
608 if (!(iBuffersToUse & ENeedSinkBuffer)) |
|
609 {//we only need one buffer and use source |
|
610 iSinkBuffer = iSourceBuffer; |
|
611 iSnkBufRef = ETrue; |
|
612 } |
|
613 #ifdef _DP_DEBUG |
|
614 RDebug::Print(_L("DP::BufferFilledL - iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); |
|
615 #endif |
|
616 } |
|
617 //Is this the last buffer from the source (0 length or LastBuffer flag set) |
|
618 //or have reached the end of the play window; we only look at the play window here |
|
619 //if we are converting. For conversion we look at the data we have read. This is then passed onto |
|
620 //the source |
|
621 if (!iSourceBuffer->BufferSize() || iSourceBuffer->LastBuffer() || |
|
622 (((iState == EConverting) || (iState == EPlaying)) && (iPlayWindowEndPosition < iCachedSourceDuration) && ( InputPosition() >= iPlayWindowEndPosition ))) |
|
623 { |
|
624 #ifdef _DP_DEBUG |
|
625 RDebug::Print(_L("DP::BufferFilledL end of input data tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
626 RDebug::Print(_L("iSourceBuffer->BufferSize()=%d\n"),iSourceBuffer->BufferSize()); |
|
627 RDebug::Print(_L("iSourceBuffer->LastBuffer()=%d\n"),iSourceBuffer->LastBuffer()); |
|
628 RDebug::Print(_L("InputPosition()=%d >= iPlayWindowEndPosition=%d\n"),I64INT(InputPosition().Int64()),I64INT(iPlayWindowEndPosition.Int64())); |
|
629 #endif |
|
630 iNoMoreSourceData = ETrue; |
|
631 iSourceBuffer->SetLastBuffer(ETrue); //just in-case we are terminating on BufferSize == 0 or play window |
|
632 } |
|
633 |
|
634 |
|
635 if (!iCodec) |
|
636 ChangeDataPathTransferState(ESendDataToSink); |
|
637 else if(!iSinkBufferWithSink) //sink buffer is with data path, can try to fill it |
|
638 ChangeDataPathTransferState(ENeedToMatchSourceToSink); |
|
639 //else wait for sink to return buffer BufferEmptied will send us into ENeedToMatchSourceToSink state |
|
640 |
|
641 #ifdef _DP_DEBUG |
|
642 RDebug::Print(_L("DP::BufferFilledL - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
643 #endif |
|
644 } |
|
645 |
|
646 |
|
647 |
|
648 |
|
649 /* |
|
650 * FillSinkBufferL |
|
651 * |
|
652 * Function to take the data from an already full source buffer and by using |
|
653 * a codec if necessary fills the sink buffer |
|
654 */ |
|
655 |
|
656 void CMMFDataPath::FillSinkBufferL() |
|
657 { |
|
658 #ifdef _DP_DEBUG |
|
659 RDebug::Print(_L("DP::FillSinkBufferL tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
660 #endif |
|
661 |
|
662 //This state is only used if we are passing data |
|
663 __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__)); |
|
664 |
|
665 //This state is only used if we have a real codec |
|
666 __ASSERT_DEBUG(iCodec, Panic(EMMFDataPathPanicBadState,__LINE__)); |
|
667 |
|
668 |
|
669 //The sink buffer is with the sink so we can't fill it. |
|
670 //When it has been emptied, this state will be re-entered from BufferEmptiedL |
|
671 if(iSinkBufferWithSink) |
|
672 { |
|
673 #ifdef _DP_DEBUG |
|
674 RDebug::Print(_L("DP::FillSinkBufferL buffer is in use by SINK - DONE (this 0x%x)\n"),this); |
|
675 #endif |
|
676 ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink |
|
677 return; |
|
678 } |
|
679 |
|
680 //The source buffer is with the source so we can't take data from it. |
|
681 //When it has been filled, this state will be re-entered from BufferFilledL |
|
682 if(iSourceBufferWithSource) |
|
683 { |
|
684 #ifdef _DP_DEBUG |
|
685 RDebug::Print(_L("DP::FillSinkBufferL buffer is in use by SOURCE - DONE (this 0x%x)\n"),this); |
|
686 #endif |
|
687 ChangeDataPathTransferState(EWaitSource); // wait for BufferFilled callback from source |
|
688 return; |
|
689 } |
|
690 |
|
691 //source buffer is NULL, can't be any more data to send. |
|
692 //iNoMoreSourceData is set and the source buffer is empty, can't be any more data to send. |
|
693 if(!iSourceBuffer || (iNoMoreSourceData && !iSourceBuffer->BufferSize())) |
|
694 { |
|
695 if(iSinkBuffer->Status() == EBeingFilled) |
|
696 {//if we have data in sink buffer, mark it as last buffer and send |
|
697 iSinkBuffer->SetLastBuffer(ETrue); |
|
698 ChangeDataPathTransferState(ESendDataToSink); |
|
699 } |
|
700 else //the sink buffer can't have anything in it |
|
701 ChangeDataPathTransferState(EEndOfData); |
|
702 } |
|
703 |
|
704 #ifdef REPOSITION_SPEEDUP |
|
705 // if the source has been re-positioned, |
|
706 // speed things up by getting some more source data now |
|
707 if(iSourceBuffer && iSourceBuffer->FrameNumber() != iCurrentSourceFrameNumber) |
|
708 { |
|
709 #ifdef _DP_DEBUG |
|
710 RDebug::Print(_L("DP::FillSinkBufferL() source was re-positioned re-requesting source data (this 0x%x)\n"),this); |
|
711 #endif |
|
712 ChangeDataPathTransferState(ENeedSourceData); |
|
713 return; |
|
714 } |
|
715 #endif //REPOSITION_SPEEDUP |
|
716 |
|
717 iSinkBuffer->SetStatus(EBeingFilled); |
|
718 iSinkBuffer->SetLastBuffer(EFalse); |
|
719 |
|
720 //pass buffer to codec for processing |
|
721 iCodecProcessResult = iCodec->ProcessL(*iSourceBuffer, *iSinkBuffer); |
|
722 //the codec tries to fill the sink buffer to its max length |
|
723 //TCodecProcessResult returns the status of the codec Process - |
|
724 //this can result in result conditions such as: |
|
725 //EProcessComplete - the codec processed all the source data into the sink buffer |
|
726 //EProcessIncomplete - the codec filled sink buffer before all the source buffer was processed |
|
727 //EDstNotFilled - the codec processed the source buffer but the sink buffer was not filled |
|
728 //EEndOfData - the codec detected the end data - all source data in processed but sink may not be full |
|
729 //EProcessError - the codec process error condition |
|
730 |
|
731 |
|
732 switch (iCodecProcessResult.iStatus) |
|
733 { |
|
734 case TCodecProcessResult::EProcessComplete: |
|
735 //finished procesing source data - all data in sink buffer |
|
736 { |
|
737 #ifdef _DP_DEBUG |
|
738 RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EProcessComplete (this 0x%x)\n"),this); |
|
739 #endif |
|
740 iSourceBuffer->SetStatus(EAvailable); //source buffer is now avaialble |
|
741 iSinkBuffer->SetStatus(EFull); //sink buffer is full |
|
742 if (iNoMoreSourceData) |
|
743 iSinkBuffer->SetLastBuffer(ETrue); |
|
744 ChangeDataPathTransferState(ESendDataToSink);// the full sink buffer needs to be sent to the sink |
|
745 } |
|
746 break; |
|
747 case TCodecProcessResult::EProcessIncomplete: |
|
748 // the sink was filled before all the src was processed |
|
749 // therefore still send everything to sink |
|
750 //but datapath needs to carry on processing the source buffer before it gets more source data |
|
751 //when sink has emptied data path needs to send rest of data |
|
752 { |
|
753 #ifdef _DP_DEBUG |
|
754 RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EProcessIncomplete (this 0x%x)\n"),this); |
|
755 #endif |
|
756 TUint sourceBufferPosition = iCodecProcessResult.iSrcBytesProcessed + iSourceBuffer->Position(); |
|
757 iSourceBuffer->SetPosition(sourceBufferPosition);//update source buffer position |
|
758 iSinkBuffer->SetStatus(EFull); //sink & source buffers are both full |
|
759 ChangeDataPathTransferState(ESendDataToSink); // the full sink buffer needs to be sent to the sink |
|
760 } |
|
761 break; |
|
762 case TCodecProcessResult::EDstNotFilled: |
|
763 // the destination is not full |
|
764 { |
|
765 #ifdef _DP_DEBUG |
|
766 RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EDstNotFilled (this 0x%x)\n"),this); |
|
767 #endif |
|
768 iSourceBuffer->SetStatus(EAvailable); //source buffer is now available |
|
769 TUint sinkBufferPosition = iCodecProcessResult.iDstBytesAdded + iSinkBuffer->Position(); |
|
770 iSinkBuffer->SetPosition(sinkBufferPosition);//update sink buffer position (still EBeingFilled) |
|
771 // if this was the last source buffer, send what we've got (if anything) |
|
772 // to the sink... EmptySinkBuffer() should then enter EEndOfData state |
|
773 if (iNoMoreSourceData) |
|
774 { |
|
775 iSinkBuffer->SetLastBuffer(ETrue); |
|
776 ChangeDataPathTransferState(ESendDataToSink);//send what we've got to the sink - |
|
777 } |
|
778 else |
|
779 { |
|
780 ChangeDataPathTransferState(ENeedSourceData); //need to get more source data to fill sink buffer |
|
781 } |
|
782 } |
|
783 break; |
|
784 case TCodecProcessResult::EEndOfData: |
|
785 //no more data - send what we've got to the sink |
|
786 //note we can't always rely on this - in many cases the codec will not know when |
|
787 //it has reached the end of data. |
|
788 { |
|
789 #ifdef _DP_DEBUG |
|
790 RDebug::Print(_L("CMMFDataPath::FillSinkBufferL codec EEndOfData (this 0x%x)\n"),this); |
|
791 #endif |
|
792 iSourceBuffer->SetStatus(EAvailable); //source buffer is now avaialble |
|
793 iSinkBuffer->SetStatus(EFull);//sink buffer may not really be 'full' but its as full as it going to get |
|
794 |
|
795 //This only occurs where the codec can detect the end of data, but the source can't |
|
796 iNoMoreSourceData=ETrue; |
|
797 iSinkBuffer->SetLastBuffer(ETrue); |
|
798 |
|
799 ChangeDataPathTransferState(ESendDataToSink);//send what we've got to the sink - |
|
800 //doesn't matter if sink buffer is not full |
|
801 } |
|
802 break; |
|
803 case TCodecProcessResult::EProcessError: |
|
804 #ifdef _DP_DEBUG |
|
805 RDebug::Print(_L("DP::FillSinkBufferL tick-%d DONE %d (this 0x%x)\n"),User::TickCount(), __LINE__,this); |
|
806 #endif |
|
807 User::Leave(KErrCorrupt); //codec process error |
|
808 break; |
|
809 default: |
|
810 #ifdef _DP_DEBUG |
|
811 RDebug::Print(_L("DP::FillSinkBufferL tick-%d DONE %d (this 0x%x)\n"),User::TickCount(), __LINE__,this); |
|
812 #endif |
|
813 User::Leave(KErrCorrupt); //should never get here |
|
814 } |
|
815 #ifdef _DP_DEBUG |
|
816 RDebug::Print(_L("DP::FillSinkBufferL - done tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
817 #endif |
|
818 } |
|
819 |
|
820 |
|
821 |
|
822 /** |
|
823 Tests whether the data path can create a sink buffer. |
|
824 |
|
825 The default implementation returns false. |
|
826 |
|
827 @return ETrue if the data path can create a sink buffer. EFalse if the data path cannot create a sink buffer. |
|
828 */ |
|
829 EXPORT_C TBool CMMFDataPath::CanCreateSinkBuffer() |
|
830 { |
|
831 return NULL; //CMMFDataPath cannot create buffer |
|
832 } |
|
833 |
|
834 /** |
|
835 Creates a sink buffer according to the specifed media ID. |
|
836 |
|
837 Intended for synchronous usage (buffers supplied by datapath for an MDataSink). |
|
838 This method is essentially a dummy implementation of an MDataSink pure virtual. |
|
839 |
|
840 The default implementation returns NULL. |
|
841 |
|
842 @param aMediaId |
|
843 An optional mediaID parameter when there are multiple buffers arriving of different media types. |
|
844 |
|
845 @return Returns NULL in this instance as datapath can't create sink buffers |
|
846 */ |
|
847 EXPORT_C CMMFBuffer* CMMFDataPath::CreateSinkBufferL(TMediaId /*aMediaId*/) |
|
848 {//CMMFDataPath can't create buffers |
|
849 return NULL; |
|
850 } |
|
851 |
|
852 /** |
|
853 Creates a sink buffer according to the specifed media ID and reference. |
|
854 |
|
855 Intended for asynchronous usage (buffers supplied by Devsound device). |
|
856 This method is essentially a dummy implementation of an MDataSink pure virtual. |
|
857 |
|
858 The default implementation returns NULL. |
|
859 |
|
860 @param aMediaId |
|
861 An optional mediaID parameter when there are multiple buffers arriving for different media types. |
|
862 @param aReference |
|
863 A boolean indicating buffer ownership. |
|
864 |
|
865 @return Returns NULL in this instance as datapath can't create sink buffers. |
|
866 */ |
|
867 EXPORT_C CMMFBuffer* CMMFDataPath::CreateSinkBufferL(TMediaId /*aMediaId*/, TBool& /*aReference*/) |
|
868 {//CMMFDataPath can't create buffers |
|
869 return NULL; |
|
870 } |
|
871 |
|
872 /** |
|
873 Gets the sink's data type for the specified media ID. |
|
874 |
|
875 @param aMediaId |
|
876 An optional parameter to specifiy the specific stream when datasource contains more than one stream of data |
|
877 @return The sink's data type. |
|
878 */ |
|
879 EXPORT_C TFourCC CMMFDataPath::SinkDataTypeCode(TMediaId /*aMediaId*/) |
|
880 { |
|
881 return(iSinkFourCC); |
|
882 } |
|
883 |
|
884 /** |
|
885 Fills the specified buffer. |
|
886 |
|
887 Pure virtual dummy implementation, not needed by datapath |
|
888 comes from MDataSink - CMMFData path is a source to its MDataSink |
|
889 |
|
890 Only required for an active pull MDataSink requesting a buffer fill. The default implementation is empty. |
|
891 |
|
892 @param aBuffer |
|
893 The buffer to fill. |
|
894 @param aConsumer |
|
895 The MDataSink supplying this buffer. |
|
896 @param aMediaId |
|
897 An optional mediaID parameter when there are multiple buffers arriving of different media types |
|
898 */ |
|
899 EXPORT_C void CMMFDataPath::FillBufferL(CMMFBuffer* /*aBuffer*/, MDataSink* /*aConsumer*/, TMediaId /*aMediaId*/) |
|
900 { |
|
901 //not implementated |
|
902 } |
|
903 |
|
904 void CMMFDataPath::SetBuffersAvailable() |
|
905 { |
|
906 // set source buffer to be available |
|
907 if (iSourceBuffer) |
|
908 iSourceBuffer->SetStatus(EAvailable); |
|
909 // set sink buffer to be available |
|
910 if (iSinkBuffer) |
|
911 iSinkBuffer->SetStatus(EAvailable); |
|
912 } |
|
913 |
|
914 void CMMFDataPath::ResetRefBuffers() |
|
915 { |
|
916 #ifdef _DP_DEBUG |
|
917 RDebug::Print(_L("DP::ResetRefBuffers iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); |
|
918 #endif |
|
919 |
|
920 // Reset the buffer pointers to NULL if they are supplied by DevSound |
|
921 // We do this because buffers that are not owned by the datapath may not be valid any more. |
|
922 if (iSrcBufRef) |
|
923 { |
|
924 iSourceBuffer = NULL; |
|
925 } |
|
926 if (iSnkBufRef) |
|
927 { |
|
928 iSinkBuffer = NULL; |
|
929 } |
|
930 } |
|
931 |
|
932 |
|
933 |
|
934 |
|
935 TInt CMMFDataPath::DetermineBuffersToUseL(void) const |
|
936 { |
|
937 TInt buffs = ENoBuffers; |
|
938 if(iCodec) |
|
939 {//Using a real Codec, need both sets of buffers |
|
940 if(!iDataSink->CanCreateSinkBuffer() || ! iDataSource->CanCreateSourceBuffer()) |
|
941 User::Leave(KErrNotSupported); |
|
942 |
|
943 buffs = CMMFDataPath::ENeedSinkBuffer | CMMFDataPath::ENeedSourceBuffer; |
|
944 } |
|
945 else //we are using a Null Codec, only need one buffer, but which one? |
|
946 {//use buffer from DevSound, if no DevSound (ie, clip to clip), prefer source buffer. |
|
947 //If preferring source but it can't create buffers, use sink. |
|
948 if ((iDataSink->DataSinkType() == KUidMmfAudioOutput) && (iDataSink->CanCreateSinkBuffer())) |
|
949 buffs = ENeedSinkBuffer; |
|
950 else if(iDataSource->CanCreateSourceBuffer()) |
|
951 buffs = ENeedSourceBuffer; |
|
952 else if(iDataSink->CanCreateSinkBuffer()) |
|
953 buffs = ENeedSinkBuffer; |
|
954 else |
|
955 User::Leave(KErrNotSupported); |
|
956 } |
|
957 return buffs; |
|
958 } |
|
959 |
|
960 |
|
961 |
|
962 /* |
|
963 * InitializeSinkL |
|
964 * |
|
965 * Function to initialize iDataSink before it can start sending data |
|
966 * This is a one time prime. This will synchronize DataPath's data driving |
|
967 * mechanism with HwDevice implementation. |
|
968 * |
|
969 * This initialisation method detects its attached sources and sinks and makes adjustments to the state machine |
|
970 * This avoid the possibility of NULL buffers (not yet returned by an asychronous sink or source) being passed around the MMF |
|
971 */ |
|
972 |
|
973 void CMMFDataPath::InitializeSinkL() |
|
974 { |
|
975 #ifdef _DP_DEBUG |
|
976 RDebug::Print(_L("DP::InitializeSinkL iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); |
|
977 #endif |
|
978 |
|
979 //state only used if we are passing data |
|
980 __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__)); |
|
981 |
|
982 iObtainingAsyncSinkBuffer = EFalse; |
|
983 |
|
984 if (iBuffersToUse & ENeedSinkBuffer) |
|
985 { |
|
986 //Buffers are initially created in the Prime method. But following a pause, we must re-create |
|
987 //any referenced buffers, so try direct creation. |
|
988 //NB: this does mean we are trying this twice, Prime and here |
|
989 if (!iSinkBuffer) //we may already have a buffer from a previous initialization |
|
990 { |
|
991 TRAPD(err, iSinkBuffer = iDataSink->CreateSinkBufferL(iMediaId, iSnkBufRef)); |
|
992 if(err != KErrNone && err != KErrNotSupported) |
|
993 User::Leave(err); |
|
994 } |
|
995 |
|
996 |
|
997 //If buffer has not been supplied via CreateSinkBufferL, |
|
998 //must use asynchronous buffer creation |
|
999 if (!iSinkBuffer) |
|
1000 { |
|
1001 iObtainingAsyncSinkBuffer = ETrue; |
|
1002 ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink |
|
1003 iDataSink->EmptyBufferL(iSinkBuffer, this, iMediaId); |
|
1004 } |
|
1005 else |
|
1006 { |
|
1007 //we have a sink buffer from CreateSinkBufferL |
|
1008 iSinkBuffer->SetStatus(EAvailable); |
|
1009 |
|
1010 if (iBuffersToUse & ENeedSourceBuffer) |
|
1011 {//need a source buffer, go get it |
|
1012 ChangeDataPathTransferState(EInitializeSource); |
|
1013 } |
|
1014 else |
|
1015 {//only need one buffer, use sink |
|
1016 iSourceBuffer = iSinkBuffer; |
|
1017 iSrcBufRef = ETrue; //the src buffer is not to be deleted |
|
1018 |
|
1019 ChangeDataPathTransferState(ENeedSourceData); //got all buffers, start getting data |
|
1020 } |
|
1021 } |
|
1022 } |
|
1023 else |
|
1024 {//don't need a sink buffer, but we need a source one |
|
1025 ChangeDataPathTransferState(EInitializeSource); |
|
1026 } |
|
1027 |
|
1028 #ifdef _DP_DEBUG |
|
1029 RDebug::Print(_L("DP::InitializeSinkL - DONE iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); |
|
1030 #endif |
|
1031 } |
|
1032 |
|
1033 |
|
1034 /* |
|
1035 * InitializeSourceL |
|
1036 * |
|
1037 * Function to initialize iDataSource before it can start sending data |
|
1038 * This is a one time prime. This will synchronize DataPath's data driving |
|
1039 * mechanism with HwDevice implementation. |
|
1040 * |
|
1041 * This initialisation method detects its attached sources and sinks and makes adjustments to the state machine |
|
1042 * This avoid the possibility of NULL buffers (not yet returned by an asychronous sink or source) being passed around the MMF |
|
1043 */ |
|
1044 |
|
1045 void CMMFDataPath::InitializeSourceL() |
|
1046 { |
|
1047 #ifdef _DP_DEBUG |
|
1048 RDebug::Print(_L("DP::InitializeSourceL - iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); |
|
1049 #endif |
|
1050 |
|
1051 //state only used if we are passing data |
|
1052 __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording), Panic(EMMFDataPathPanicBadState,__LINE__)); |
|
1053 |
|
1054 iObtainingAsyncSourceBuffer = EFalse; |
|
1055 |
|
1056 if (iBuffersToUse & ENeedSourceBuffer) |
|
1057 { |
|
1058 //Buffers are initially created in the Prime method. But following a pause, we must re-create |
|
1059 //any referenced buffers, so try direct creation. |
|
1060 //NB: this does mean we are trying this twice, Prime and here. |
|
1061 if (!iSourceBuffer) //we may already have a buffer from a previous initialization |
|
1062 { |
|
1063 TRAPD(err, iSourceBuffer = iDataSource->CreateSourceBufferL(iMediaId,*iSinkBuffer, iSrcBufRef)); |
|
1064 if(err != KErrNone && err != KErrNotSupported) |
|
1065 User::Leave(err); |
|
1066 } |
|
1067 |
|
1068 |
|
1069 //If buffer has not been supplied via CreateSourceBufferL |
|
1070 //must use asynchronous buffer creation |
|
1071 if (!iSourceBuffer) |
|
1072 { |
|
1073 iObtainingAsyncSourceBuffer = ETrue; |
|
1074 ChangeDataPathTransferState(ENeedSourceData); |
|
1075 } |
|
1076 else |
|
1077 {//we have a source buffer from CreateSourceBufferL |
|
1078 iSourceBuffer->SetStatus(EAvailable); |
|
1079 |
|
1080 if (!(iBuffersToUse & ENeedSinkBuffer)) |
|
1081 {//only need one buffer, use sink |
|
1082 iSinkBuffer = iSourceBuffer; |
|
1083 iSnkBufRef = ETrue; |
|
1084 } |
|
1085 |
|
1086 ChangeDataPathTransferState(ENeedSourceData); //got all buffers, start getting data |
|
1087 } |
|
1088 } |
|
1089 else |
|
1090 {//don't need a source buffer, use sinks |
|
1091 if(iSinkBuffer) |
|
1092 { |
|
1093 iSourceBuffer = iSinkBuffer; |
|
1094 iSrcBufRef = iSnkBufRef; |
|
1095 SetBuffersAvailable(); |
|
1096 } |
|
1097 #ifdef _DP_DEBUG |
|
1098 else |
|
1099 Panic(EMMFDataPathPanicProgrammingError,__LINE__); |
|
1100 #endif |
|
1101 ChangeDataPathTransferState(ENeedSourceData); //got all buffers, start getting data |
|
1102 |
|
1103 |
|
1104 #ifdef _DP_DEBUG |
|
1105 RDebug::Print(_L("DP::InitializeSourceL - iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); |
|
1106 #endif |
|
1107 } |
|
1108 } |
|
1109 |
|
1110 /* |
|
1111 * EmptySinkBufferL |
|
1112 * |
|
1113 * Function to pass a full databuffer to the iDataSink |
|
1114 */ |
|
1115 void CMMFDataPath::EmptySinkBufferL() |
|
1116 { |
|
1117 #ifdef _DP_DEBUG |
|
1118 RDebug::Print(_L("DP::EmptySinkBufferL pass data to sink tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
1119 if(iSinkBuffer) |
|
1120 RDebug::Print(_L("iSinkBuffer %d contains %d bytes eof = %d line %d (this 0x%x)\n"),iSinkBuffer->FrameNumber(), iSinkBuffer->BufferSize(),iSinkBuffer->LastBuffer(),__LINE__,this); |
|
1121 #endif |
|
1122 |
|
1123 //Before emptying the sink buffer we need to check it has data to empty - this |
|
1124 //may not be the case if there is no more data ie iNoMoreSourceData is true. |
|
1125 //In this case we need to check to see if there is any data left in the sink |
|
1126 //buffer, ie the sink buffer is either full or being filled. If there is not any |
|
1127 //data in the sink buffer, ie it is not in the state of EBeingFilled or EFull |
|
1128 //then there is nothing to empty so the datapath state is set to EEndOfData and |
|
1129 //we return from the procedure. |
|
1130 |
|
1131 //state only used if we are passing data |
|
1132 __ASSERT_DEBUG((iState == EPlaying || iState == EConverting || iState == ERecording || (iState == EPrimed && iPauseCalled)), Panic(EMMFDataPathPanicBadState,__LINE__)); |
|
1133 __ASSERT_DEBUG(iSinkBuffer && |
|
1134 ((iSinkBuffer->Status()==EBeingFilled) || (iSinkBuffer->Status()==EFull)), |
|
1135 Panic(EMMFDataPathPanicProgrammingError,__LINE__)); |
|
1136 |
|
1137 __ASSERT_DEBUG(iSinkBufferWithSink == EFalse, Panic(EMMFDataPathPanicBadState,__LINE__)); |
|
1138 |
|
1139 |
|
1140 //Due to sinks that may call BuferEmptied directly (ie. re-entrancy onto DataPath) we |
|
1141 //must work out next state here. If re-entrancy, the next state may validly get overwritten |
|
1142 // in BuferEmptied. |
|
1143 if(iObtainingAsyncSinkBuffer) //wait for buffer to be returned in BufferEmptied |
|
1144 { |
|
1145 ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink |
|
1146 } |
|
1147 |
|
1148 #ifdef REPOSITION_SPEEDUP |
|
1149 // if the source has been re-positioned, |
|
1150 // speed things up by getting some more source data now |
|
1151 if(iSourceBuffer && iSourceBuffer->FrameNumber() != iCurrentSourceFrameNumber) |
|
1152 { |
|
1153 #ifdef _DP_DEBUG |
|
1154 RDebug::Print(_L("DP::EmptySinkBufferL() source was re-positioned re-requesting source data (this 0x%x)\n"),this); |
|
1155 #endif |
|
1156 ChangeDataPathTransferState(ENeedSourceData); |
|
1157 return; |
|
1158 } |
|
1159 #endif //REPOSITION_SPEEDUP |
|
1160 |
|
1161 //We have sent data to sink, if we are using a real Codec, we can now get more data from source |
|
1162 //if there is any more to get and the codec has emptied it. |
|
1163 //NB: No need to check we own the source buffer as we will no be in this state |
|
1164 //if we have asked for more source data and haven't received it. |
|
1165 else if (iCodec && !iNoMoreSourceData && (iSourceBuffer->Status() == EAvailable)) |
|
1166 { |
|
1167 #ifdef _DP_DEBUG |
|
1168 RDebug::Print(_L("ASKING for more source data iCodec = 0x%x iNoMoreSourceData=%d iSourceBufferWithSource = %d (this 0x%x)\n"), iCodec, iNoMoreSourceData, iSourceBufferWithSource,this); |
|
1169 #endif |
|
1170 ChangeDataPathTransferState(ENeedSourceData); |
|
1171 } |
|
1172 else |
|
1173 { |
|
1174 #ifdef _DP_DEBUG |
|
1175 RDebug::Print(_L("Not asking for any more source data iCodec = 0x%x iNoMoreSourceData=%d iSourceBufferWithSource = %d iSourceBuffer->Status=%d (this 0x%x)\n"), iCodec, iNoMoreSourceData, iSourceBufferWithSource ,iSourceBuffer->Status(), this); |
|
1176 #endif |
|
1177 |
|
1178 //if this is the last buffer, set this flag so we can deal with KErrUnderflow |
|
1179 //as a valid termination of playing |
|
1180 if(iSinkBuffer->LastBuffer()) |
|
1181 iAllDataSentToSink=ETrue; |
|
1182 |
|
1183 ChangeDataPathTransferState(EWaitSink); // wait for BufferEmptied callback from sink |
|
1184 } |
|
1185 |
|
1186 |
|
1187 if(!iObtainingAsyncSinkBuffer) //normal data transfer |
|
1188 iSinkBuffer->SetFrameNumber(++iCurrentSinkFrameNumber); |
|
1189 |
|
1190 #ifdef _DP_DEBUG |
|
1191 RDebug::Print(_L("DP sending buffer %d ptr=0x%x of %d bytes to sink (this 0x%x)\n"), iSinkBuffer->FrameNumber(),iSinkBuffer,iSinkBuffer->BufferSize(),this); |
|
1192 #endif |
|
1193 |
|
1194 iSinkBufferWithSink = ETrue; |
|
1195 TRAPD(error, iDataSink->EmptyBufferL(iSinkBuffer, this, iMediaId)); |
|
1196 |
|
1197 // Check that we haven't exceeded the maximum clip length - if so, go to the EndOfData state |
|
1198 // so we perform necessary cleanup. |
|
1199 if (error == KErrEof || error == KErrOverflow || error == KErrUnderflow) |
|
1200 { |
|
1201 #ifdef _DP_DEBUG |
|
1202 RDebug::Print(_L("DP::EmptySinkBufferL DONE %d error = %d tick-%d (this 0x%x)\n"),__LINE__, error, User::TickCount(),this); |
|
1203 #endif |
|
1204 iDataPathCompletedErrorCode = error; |
|
1205 ChangeDataPathTransferState(EEndOfData); |
|
1206 return; |
|
1207 } |
|
1208 User::LeaveIfError(error); |
|
1209 |
|
1210 #ifdef _DP_DEBUG |
|
1211 RDebug::Print(_L("DP::EmptySinkBufferL - done tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
1212 #endif |
|
1213 } |
|
1214 |
|
1215 |
|
1216 /** |
|
1217 Indicates the data sink has emptied the buffer. |
|
1218 |
|
1219 Called by the CMMFDataPath's MDataSink when it has emptied the buffer |
|
1220 |
|
1221 @param aBuffer |
|
1222 The emptied buffer. |
|
1223 */ |
|
1224 EXPORT_C void CMMFDataPath::BufferEmptiedL(CMMFBuffer* aBuffer) |
|
1225 { |
|
1226 #ifdef _DP_DEBUG |
|
1227 TInt bufNum = 9999; |
|
1228 if(aBuffer) |
|
1229 bufNum = aBuffer->FrameNumber(); |
|
1230 else |
|
1231 RDebug::Print(_L("DP::BufferEmptiedL returned NULL (this 0x%x)\n"),this); |
|
1232 |
|
1233 RDebug::Print(_L("DP::BufferEmptiedL sink has taken buffer %d (ptr=0x%x) bytes %d eof= %d iNoMoreSourceData = %d tick-%d (this 0x%x)\n"), |
|
1234 bufNum, aBuffer, aBuffer->BufferSize(),aBuffer->LastBuffer(), iNoMoreSourceData, User::TickCount(),this); |
|
1235 #endif |
|
1236 |
|
1237 iSinkBufferWithSink = EFalse; |
|
1238 |
|
1239 //Has the datapath stopped running, if so were not interested in any callbacks. |
|
1240 if(iState == EStopped || (iState == EPrimed && !iPauseCalled)) |
|
1241 { |
|
1242 #ifdef _DP_DEBUG |
|
1243 RDebug::Print(_L("DP::BufferEmptiedL called while not expecting callback iState=%d iPauseCalled=%d (this 0x%x)\n"),iState, iPauseCalled,this); |
|
1244 #endif |
|
1245 return; |
|
1246 } |
|
1247 |
|
1248 |
|
1249 // This will allow MDataSink to send dynamic buffer to DataPath with each BufferEmptiedL request. |
|
1250 if (iSinkBuffer != aBuffer) //buffer has been updated |
|
1251 { |
|
1252 iSinkBuffer = aBuffer; |
|
1253 if (!(iBuffersToUse & ENeedSourceBuffer)) |
|
1254 { //can use a single buffer |
|
1255 iSourceBuffer = iSinkBuffer; |
|
1256 iSrcBufRef = iSnkBufRef; |
|
1257 } |
|
1258 |
|
1259 } |
|
1260 |
|
1261 iSinkBuffer->SetStatus(EAvailable); |
|
1262 |
|
1263 if (iObtainingAsyncSinkBuffer) //we are creating an asynchronous sink buffer |
|
1264 { |
|
1265 iObtainingAsyncSinkBuffer = EFalse; |
|
1266 |
|
1267 //we have a sink buffer, should this also be used by the source |
|
1268 if (!(iBuffersToUse & ENeedSourceBuffer)) |
|
1269 {//using a single buffer, so start getting data |
|
1270 iSourceBuffer = iSinkBuffer; |
|
1271 iSrcBufRef = iSnkBufRef; |
|
1272 |
|
1273 ChangeDataPathTransferState(ENeedSourceData); |
|
1274 } |
|
1275 else //obtain a separate source buffer |
|
1276 ChangeDataPathTransferState(EInitializeSource); |
|
1277 |
|
1278 #ifdef _DP_DEBUG |
|
1279 RDebug::Print(_L("DP::BufferEmptiedL - DONE iSourceBuffer=0x%x ref=%d iSinkBuffer=0x%x ref=%d (this 0x%x)\n"),iSourceBuffer,iSrcBufRef,iSinkBuffer,iSnkBufRef, this); |
|
1280 #endif |
|
1281 return; |
|
1282 } |
|
1283 |
|
1284 if(!iCodec) //No Codec in use |
|
1285 { |
|
1286 if(iNoMoreSourceData) |
|
1287 ChangeDataPathTransferState(EEndOfData);//final buffer returned from sink |
|
1288 else |
|
1289 ChangeDataPathTransferState(ENeedSourceData);//get more data from source |
|
1290 } |
|
1291 else //real codecs |
|
1292 { |
|
1293 //There is more source data and src buffer is being filled or we are about to go into |
|
1294 // ENeedSourceData state to fill it, so wait for source data to arrive. |
|
1295 //NB:if there was more source data and we are using a real codec and source buffer was empty, |
|
1296 //more source data would have been requested in EmptySinkBuffer |
|
1297 if(!iNoMoreSourceData && (iSourceBufferWithSource || iTransferState == ENeedSourceData)) |
|
1298 { |
|
1299 #ifdef _DP_DEBUG |
|
1300 RDebug::Print(_L("DP::BufferEmptiedL - waiting for more source data - DONE tick-%d line %d (this 0x%x)\n"),User::TickCount(),__LINE__,this); |
|
1301 #endif |
|
1302 return; |
|
1303 } |
|
1304 |
|
1305 //source has supplied a NULL buffer or it has been emptied; no more data to send. |
|
1306 if(!iSourceBuffer || (iSourceBuffer->Status() == EAvailable)) |
|
1307 ChangeDataPathTransferState(EEndOfData); |
|
1308 else if(iSourceBuffer->Status() == EFull) //there is data in the source buffer, go and get it |
|
1309 ChangeDataPathTransferState(ENeedToMatchSourceToSink); |
|
1310 |
|
1311 #ifdef _DP_DEBUG |
|
1312 else |
|
1313 Panic(EMMFDataPathPanicProgrammingError,__LINE__); |
|
1314 #endif |
|
1315 } |
|
1316 |
|
1317 |
|
1318 #ifdef _DP_DEBUG |
|
1319 RDebug::Print(_L("DP::BufferEmptiedL - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
1320 #endif |
|
1321 } |
|
1322 |
|
1323 |
|
1324 |
|
1325 /** |
|
1326 Tests whether the data path can create a source buffer. |
|
1327 |
|
1328 Would expect datapath to always return NULL, so this is a default implementation of a pure virtual from MDataSink. |
|
1329 |
|
1330 The default implementation returns EFalse. |
|
1331 |
|
1332 @return ETrue if the data path can create a source buffer. EFalse if the data path cannot create a source buffer. |
|
1333 */ |
|
1334 EXPORT_C TBool CMMFDataPath::CanCreateSourceBuffer() //from both MDataSource & MDataSink? |
|
1335 { |
|
1336 return EFalse; //CMMFDataPath cannot create buffer |
|
1337 } |
|
1338 |
|
1339 /** |
|
1340 Creates a source buffer. |
|
1341 |
|
1342 Intended for synchronous usage (buffers supplied by datapath for a MDataSource) |
|
1343 This method is essentially a dummy implementation of an MDataSource pure virtual. |
|
1344 |
|
1345 The default implementation leaves with KErrNotSupported and returns NULL. |
|
1346 |
|
1347 @param aMediaId |
|
1348 An optional mediaID parameter when there are multiple buffers arriving of different media types. |
|
1349 @return A pointer to a newly created buffer. Returns NULL in this instance as datapath can't create source buffers |
|
1350 */ |
|
1351 EXPORT_C CMMFBuffer* CMMFDataPath::CreateSourceBufferL(TMediaId /*aMediaId*/) //CMMFDataPath can't create buffers |
|
1352 { |
|
1353 User::Leave(KErrNotSupported); |
|
1354 return NULL; |
|
1355 } |
|
1356 |
|
1357 /** |
|
1358 Creates a source buffer according to the specifed media ID and reference. |
|
1359 |
|
1360 Intended for asynchronous usage (buffers supplied by datapath for a MDataSource) |
|
1361 This method is essentially a dummy implementation of an MDataSource pure virtual. |
|
1362 |
|
1363 The default implementation leaves with KErrNotSupported and returns NULL. |
|
1364 |
|
1365 @param aMediaId |
|
1366 An optional mediaID parameter when there are multiple buffers arriving of different media types. |
|
1367 @param aReference |
|
1368 A boolean indicating buffer ownership. ETrue if the MDataSource owns the buffer, EFalse if the caller owns the buffer. |
|
1369 |
|
1370 @return A pointer to a newly created buffer. Returns NULL in this instance as datapath can't create source buffers. |
|
1371 */ |
|
1372 EXPORT_C CMMFBuffer* CMMFDataPath::CreateSourceBufferL(TMediaId /*aMediaId*/, TBool& /*aReference*/) //CMMFDataPath can't create buffers |
|
1373 { |
|
1374 User::Leave(KErrNotSupported); |
|
1375 return NULL; |
|
1376 } |
|
1377 |
|
1378 |
|
1379 /** |
|
1380 Gets the source data type for the specified media ID. |
|
1381 |
|
1382 @param aMediaId |
|
1383 An optional parameter to specifiy specific stream when datasource contains more than one stream of data. |
|
1384 |
|
1385 @return The source data type. |
|
1386 */ |
|
1387 EXPORT_C TFourCC CMMFDataPath::SourceDataTypeCode(TMediaId /*aMediaId*/) |
|
1388 { |
|
1389 return(iSourceFourCC); |
|
1390 } |
|
1391 |
|
1392 |
|
1393 /** |
|
1394 Allocates buffers in preparation to play. |
|
1395 |
|
1396 Must be called before calling PlayL(). |
|
1397 |
|
1398 iSnkBufRef and iSrcBufRef contain ETrue if these buffers are created and owned by a MDataSource or MDataSink |
|
1399 For clean-up purposes, datapath only cleans up buffers allocated directly by PrimeL(). |
|
1400 */ |
|
1401 |
|
1402 EXPORT_C void CMMFDataPath::PrimeL() |
|
1403 { |
|
1404 #ifdef _DP_DEBUG |
|
1405 RDebug::Print(_L("DP::PrimeL tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
1406 #endif |
|
1407 |
|
1408 //allocate resources ie buffers & prepare to play |
|
1409 if (iDataPathCreated && (iState == EStopped)) |
|
1410 //luckily the client utility does this. |
|
1411 {//can only prime from the stopped state |
|
1412 |
|
1413 //This will determine what buffers we need to run the datapath. |
|
1414 //Can leave KERRNotSupported |
|
1415 iBuffersToUse = DetermineBuffersToUseL(); |
|
1416 |
|
1417 //Try to create source and sink buffers. If we can't create them in the Prime, |
|
1418 //we will need to obtain them by asynchronous buffer creation when playing starts. |
|
1419 ObtainSyncBuffersL(); |
|
1420 |
|
1421 iDataSource->SourcePrimeL(); //propogate state change to source |
|
1422 iDataSink->SinkPrimeL(); //propogate state change to sink |
|
1423 |
|
1424 |
|
1425 //If Client has set these, they will be set following the prime |
|
1426 iPlayWindowStartPosition = 0; |
|
1427 iPlayWindowEndPosition = Duration(); |
|
1428 |
|
1429 iState = EPrimed; |
|
1430 iPauseCalled = EFalse; |
|
1431 |
|
1432 if (iCompleteCallback) |
|
1433 { |
|
1434 delete iCompleteCallback; |
|
1435 iCompleteCallback = NULL; |
|
1436 } |
|
1437 TBool waitForSink = (iDataSink->DataSinkType() == KUidMmfAudioOutput)?ETrue:EFalse; |
|
1438 iCompleteCallback = new (ELeave) CCompleteCallback(*this,waitForSink); |
|
1439 } |
|
1440 #ifdef _DP_DEBUG |
|
1441 RDebug::Print(_L("DP::PrimeL Done tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
1442 #endif |
|
1443 } |
|
1444 |
|
1445 |
|
1446 /** |
|
1447 Starts an active scheduler 'play' loop. |
|
1448 |
|
1449 Can only play from the primed state. |
|
1450 */ |
|
1451 EXPORT_C void CMMFDataPath::PlayL() |
|
1452 { |
|
1453 #if defined(__PROFILING) |
|
1454 RDebug::ProfileEnd(1); |
|
1455 #endif // defined(__PROFILING) |
|
1456 |
|
1457 #ifdef _DP_DEBUG |
|
1458 RDebug::Print(_L("DP::PlayL, on src buff %d sink buf %d (this 0x%x)\n"), iCurrentSourceFrameNumber, iCurrentSinkFrameNumber,this); |
|
1459 RDebug::Print(_L("iStartPosition = %d\n"), I64INT(iStartPosition.Int64())); |
|
1460 #endif |
|
1461 |
|
1462 if ((iDataPathCreated) && (iState == EPrimed)) |
|
1463 { |
|
1464 //can only play from the primed state |
|
1465 |
|
1466 TBool savedPauseCalled=EFalse; |
|
1467 if(iPauseCalled) //sink and source will have been stopped, and we will not have been re-primed |
|
1468 { |
|
1469 savedPauseCalled=ETrue; |
|
1470 iDataSink->SinkPrimeL(); //propagate change down to sink |
|
1471 iPauseCalled = EFalse; |
|
1472 } |
|
1473 |
|
1474 iCurrentSourceFrameNumber = 0; //reset to beginning |
|
1475 iCurrentSinkFrameNumber = 0; //reset to beginning |
|
1476 |
|
1477 iSourceBufferWithSource = EFalse; |
|
1478 iSinkBufferWithSink = EFalse; |
|
1479 |
|
1480 iNoMoreSourceData = EFalse; |
|
1481 iAllDataSentToSink=EFalse; |
|
1482 iDataPathCompletedErrorCode=KErrNone; |
|
1483 |
|
1484 SetPositionL( iStartPosition ) ; |
|
1485 iReferenceAudioSamplesPlayed = 0; |
|
1486 iReferenceAudioSamplesRecorded = 0; |
|
1487 |
|
1488 //complete a request on iStatus to invoke play code |
|
1489 iDataSource->SourcePlayL(); //propagate state change to source |
|
1490 if (!(savedPauseCalled && (iTransferState==EWaitSink || iTransferState==EInitializeSink))) |
|
1491 { |
|
1492 iDataSink->SinkPlayL(); //propogate state change to sink |
|
1493 } |
|
1494 |
|
1495 if (iDataSink->DataSinkType() == KUidMmfAudioOutput) |
|
1496 iState = EPlaying; |
|
1497 else if(iDataSource->DataSourceType() == KUidMmfAudioInput) |
|
1498 iState = ERecording; |
|
1499 else |
|
1500 iState = EConverting; |
|
1501 |
|
1502 //need to re-initialize any buffer(s) that we only own references to |
|
1503 ChangeDataPathTransferState(EInitializeSink); |
|
1504 } |
|
1505 #ifdef _DP_DEBUG |
|
1506 RDebug::Print(_L("DP::Play - DONE\n")); |
|
1507 #endif |
|
1508 } |
|
1509 |
|
1510 |
|
1511 /** |
|
1512 Pauses playing. |
|
1513 |
|
1514 Sends KMMFErrorCategoryDataPathGeneralError to the client if an error occurs. |
|
1515 */ |
|
1516 EXPORT_C void CMMFDataPath::Pause() |
|
1517 { |
|
1518 #ifdef _DP_DEBUG |
|
1519 RDebug::Print(_L("DP::Pause, on src buff %d sink buf %d (this 0x%x)\n"), iCurrentSourceFrameNumber, iCurrentSinkFrameNumber,this); |
|
1520 RDebug::Print(_L("DP::Pause current state=%d tick-%d (this 0x%x)\n"),iTransferState, User::TickCount(),this); |
|
1521 #endif |
|
1522 |
|
1523 TRAPD(err, DoPauseL()); |
|
1524 |
|
1525 if (err) |
|
1526 { |
|
1527 DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err); |
|
1528 } |
|
1529 #ifdef _DP_DEBUG |
|
1530 RDebug::Print(_L("DP::Pause - DONE tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
1531 #endif |
|
1532 } |
|
1533 |
|
1534 /** |
|
1535 Stops playing. |
|
1536 |
|
1537 Resets datapath position - currently does not clean up buffers. Sends KMMFErrorCategoryDataPathGeneralError |
|
1538 to the client if an error occurs. |
|
1539 */ |
|
1540 EXPORT_C void CMMFDataPath::Stop() |
|
1541 { |
|
1542 #ifdef _DP_DEBUG |
|
1543 RDebug::Print(_L("DP::Stop current state=%d tick-%d (this 0x%x)\n"), iTransferState, User::TickCount(),this); |
|
1544 #endif |
|
1545 |
|
1546 if ((iDataPathCreated) && (iState != EStopped)) |
|
1547 { |
|
1548 TRAPD(err, DoStopL()); |
|
1549 |
|
1550 if (err) |
|
1551 DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err); |
|
1552 } |
|
1553 } |
|
1554 /** |
|
1555 Forces and end of data state on the datapath |
|
1556 */ |
|
1557 EXPORT_C void CMMFDataPath::EndOfData() |
|
1558 { |
|
1559 TRAPD(err, DoEndOfDataL()); |
|
1560 |
|
1561 if (err) |
|
1562 { |
|
1563 DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, err); |
|
1564 } |
|
1565 } |
|
1566 |
|
1567 /** |
|
1568 |
|
1569 */ |
|
1570 TInt CMMFDataPath::AudioSamplesPlayed() const |
|
1571 { |
|
1572 if (iDataSink->DataSinkType() != KUidMmfAudioOutput) |
|
1573 return 0; |
|
1574 |
|
1575 CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink); |
|
1576 |
|
1577 TInt samples = 0; |
|
1578 |
|
1579 if(iState == EPlaying) |
|
1580 samples = audioOutput->SoundDevice().SamplesPlayed(); |
|
1581 |
|
1582 #ifdef _DP_DEBUG |
|
1583 RDebug::Print(_L("DP::AudioSamplesPlayed = %d\n"),samples); |
|
1584 #endif |
|
1585 |
|
1586 return samples; |
|
1587 } |
|
1588 |
|
1589 |
|
1590 /** |
|
1591 |
|
1592 */ |
|
1593 TInt CMMFDataPath::AudioSamplesRecorded() const |
|
1594 { |
|
1595 if (iDataSource->DataSourceType() != KUidMmfAudioInput) |
|
1596 return 0; |
|
1597 |
|
1598 CMMFAudioInput* audioInput = STATIC_CAST(CMMFAudioInput*,iDataSource); |
|
1599 |
|
1600 TInt samples = 0; |
|
1601 |
|
1602 if(iState == ERecording) |
|
1603 samples = audioInput->SoundDevice().SamplesRecorded(); |
|
1604 |
|
1605 #ifdef _DP_DEBUG |
|
1606 RDebug::Print(_L("DP::AudioSamplesRecorded = %d\n"),samples); |
|
1607 #endif |
|
1608 |
|
1609 return samples; |
|
1610 } |
|
1611 |
|
1612 |
|
1613 /** |
|
1614 |
|
1615 */ |
|
1616 TTimeIntervalMicroSeconds CMMFDataPath::CalculateAudioOutputPosition() const |
|
1617 { |
|
1618 //This operation can only be carried out on an Audio Output |
|
1619 __ASSERT_ALWAYS(iDataSink->DataSinkType() == KUidMmfAudioOutput, Panic(EMMFDataPathPanicProgrammingError,__LINE__)); |
|
1620 |
|
1621 |
|
1622 //If we are not playing, simply return where we will play from |
|
1623 if(iState != EPlaying || iCurrentSinkFrameNumber == 0) |
|
1624 return iStartPosition; |
|
1625 |
|
1626 #ifdef _DP_DEBUG |
|
1627 RDebug::Print(_L("DP::CalculateAudioOutputDuration from %d\n"),iReferenceAudioSamplesPlayed); |
|
1628 #endif |
|
1629 |
|
1630 TReal samplesPlayed = AudioSamplesPlayed() - iReferenceAudioSamplesPlayed; |
|
1631 |
|
1632 CMMFAudioOutput* audioOutput = STATIC_CAST(CMMFAudioOutput*,iDataSink); |
|
1633 CMMFDevSound& devSound = audioOutput->SoundDevice(); |
|
1634 |
|
1635 TMMFCapabilities devSoundConfig = devSound.Config(); |
|
1636 |
|
1637 TInt samplingFreq = StreamUtils::SampleRateAsValue(devSoundConfig); |
|
1638 |
|
1639 #ifdef _DP_DEBUG |
|
1640 RDebug::Print(_L("samplingFreq %d\n"),samplingFreq); |
|
1641 #endif |
|
1642 |
|
1643 TReal timePlayedSeconds = 0; |
|
1644 if(samplesPlayed) |
|
1645 timePlayedSeconds = samplesPlayed/samplingFreq; |
|
1646 |
|
1647 TInt64 timePlayed(I64DOUBLECAST(timePlayedSeconds * 1000000)); |
|
1648 |
|
1649 #ifdef _DP_DEBUG |
|
1650 RDebug::Print(_L("timePlayed %d\n"), I64LOW(timePlayed)); |
|
1651 #endif |
|
1652 |
|
1653 return TTimeIntervalMicroSeconds(timePlayed + iStartPosition.Int64()); |
|
1654 } |
|
1655 |
|
1656 |
|
1657 |
|
1658 /** |
|
1659 |
|
1660 */ |
|
1661 TTimeIntervalMicroSeconds CMMFDataPath::CalculateAudioInputPosition() const |
|
1662 { |
|
1663 //This operation can only be carried out on an Audio Input |
|
1664 __ASSERT_ALWAYS(iDataSource->DataSourceType() == KUidMmfAudioInput, Panic(EMMFDataPathPanicProgrammingError,__LINE__)); |
|
1665 |
|
1666 |
|
1667 //If we are not playing, simply return where we will play from |
|
1668 if(iState != ERecording) |
|
1669 return iStartPosition; |
|
1670 |
|
1671 #ifdef _DP_DEBUG |
|
1672 RDebug::Print(_L("DP::CalculateAudioInputPosition from %d\n"),iReferenceAudioSamplesRecorded); |
|
1673 #endif |
|
1674 |
|
1675 TReal samplesRecorded = AudioSamplesRecorded() - iReferenceAudioSamplesRecorded; |
|
1676 |
|
1677 CMMFAudioInput* audioInput = STATIC_CAST(CMMFAudioInput*,iDataSource); |
|
1678 CMMFDevSound& devSound = audioInput->SoundDevice(); |
|
1679 |
|
1680 TMMFCapabilities devSoundConfig = devSound.Config(); |
|
1681 |
|
1682 TInt samplingFreq = StreamUtils::SampleRateAsValue(devSoundConfig); |
|
1683 |
|
1684 #ifdef _DP_DEBUG |
|
1685 RDebug::Print(_L("samplingFreq %d\n"),samplingFreq); |
|
1686 #endif |
|
1687 |
|
1688 TReal timeRecordedSeconds = 0; |
|
1689 if(samplesRecorded) |
|
1690 timeRecordedSeconds = samplesRecorded/samplingFreq; |
|
1691 |
|
1692 TInt64 timeRecorded(I64DOUBLECAST(timeRecordedSeconds * 1000000)); |
|
1693 |
|
1694 #ifdef _DP_DEBUG |
|
1695 RDebug::Print(_L("timeRecorded %d\n"), I64LOW(timeRecorded)); |
|
1696 #endif |
|
1697 return TTimeIntervalMicroSeconds(timeRecorded); |
|
1698 } |
|
1699 |
|
1700 |
|
1701 |
|
1702 |
|
1703 |
|
1704 TTimeIntervalMicroSeconds CMMFDataPath::OutputPosition() const |
|
1705 { |
|
1706 TTimeIntervalMicroSeconds interval; |
|
1707 TTimeIntervalMicroSeconds position; |
|
1708 |
|
1709 if (iDataSink->DataSinkType() == KUidMmfAudioOutput) |
|
1710 { |
|
1711 position = CalculateAudioOutputPosition(); |
|
1712 #ifdef _DP_DEBUG |
|
1713 RDebug::Print(_L("DP::OutputPosition from audio output= %d\n"),I64INT(position.Int64())); |
|
1714 #endif |
|
1715 } |
|
1716 else if (iDataSink->DataSinkType() == KUidMmfFormatEncode) |
|
1717 { |
|
1718 //note Encode format position takes priority if both source & sink are formats? |
|
1719 // try to get the position directly from the format. If that fails, work it out here |
|
1720 TRAPD(error, position = ((CMMFFormatEncode*)iDataSink)->PositionL()); |
|
1721 if (error)//getting the position from the format didn't work so calculate it here |
|
1722 { |
|
1723 interval = ((CMMFFormatEncode*)iDataSink)->FrameTimeInterval(iMediaId); |
|
1724 TInt64 position64 = interval.Int64() * iCurrentSinkFrameNumber; |
|
1725 position = position64; |
|
1726 } |
|
1727 |
|
1728 TTimeIntervalMicroSeconds duration = CONST_CAST(CMMFDataPath*,this)->Duration(); //need this to check position doesn't exceed duration |
|
1729 if (position > duration)//this can happen on last buffer |
|
1730 position = duration; |
|
1731 |
|
1732 |
|
1733 #ifdef _DP_DEBUG |
|
1734 RDebug::Print(_L("DP::OutputPosition from format= %d\n"),I64INT(position.Int64())); |
|
1735 #endif |
|
1736 } |
|
1737 else |
|
1738 {//can only read output position if sink is a format or an audio output |
|
1739 return TTimeIntervalMicroSeconds(0); |
|
1740 } |
|
1741 |
|
1742 return position; |
|
1743 } |
|
1744 |
|
1745 TTimeIntervalMicroSeconds CMMFDataPath::InputPosition() const |
|
1746 { |
|
1747 TTimeIntervalMicroSeconds interval; |
|
1748 TTimeIntervalMicroSeconds position; |
|
1749 |
|
1750 if (iDataSource->DataSourceType() == KUidMmfAudioInput) |
|
1751 { |
|
1752 position = CalculateAudioInputPosition(); |
|
1753 #ifdef _DP_DEBUG |
|
1754 RDebug::Print(_L("DP::InputPosition from audio input= %d\n"),I64INT(position.Int64())); |
|
1755 #endif |
|
1756 } |
|
1757 else if (iDataSource->DataSourceType() == KUidMmfFormatDecode) |
|
1758 {//note Decode format position takes priority if both source & sink are formats? |
|
1759 // try to get the position directly from the format. If that fails, work it out here |
|
1760 TRAPD(error, position = ((CMMFFormatDecode*)iDataSource)->PositionL()); |
|
1761 if (error)//getting the position from the format didn't work so calculate it here |
|
1762 { |
|
1763 interval = ((CMMFFormatDecode*)iDataSource)->FrameTimeInterval(iMediaId); |
|
1764 TInt64 position64 = interval.Int64() * iCurrentSourceFrameNumber; |
|
1765 position = position64; |
|
1766 } |
|
1767 |
|
1768 TTimeIntervalMicroSeconds duration = CONST_CAST(CMMFDataPath*,this)->Duration(); //need this to check position doesn't exceed duration |
|
1769 if (position > duration)//this can happen on last buffer |
|
1770 position = duration; |
|
1771 |
|
1772 #ifdef _DP_DEBUG |
|
1773 RDebug::Print(_L("DP::InputPosition from format = %d\n"),I64INT(position.Int64())); |
|
1774 #endif |
|
1775 } |
|
1776 else |
|
1777 {//can only read input position if source is a format or an audio input |
|
1778 return TTimeIntervalMicroSeconds(0); |
|
1779 } |
|
1780 |
|
1781 return position; |
|
1782 } |
|
1783 |
|
1784 |
|
1785 |
|
1786 |
|
1787 /** |
|
1788 Gets the data path position. |
|
1789 |
|
1790 @return The data path position. |
|
1791 */ |
|
1792 EXPORT_C TTimeIntervalMicroSeconds CMMFDataPath::Position() const |
|
1793 { |
|
1794 if ((iState == ERecording) || (iState == EConverting)) |
|
1795 return InputPosition(); |
|
1796 else if(iState == EPlaying) |
|
1797 return OutputPosition(); |
|
1798 else |
|
1799 { |
|
1800 return iStartPosition; |
|
1801 } |
|
1802 } |
|
1803 |
|
1804 /** |
|
1805 Sets the data path position. |
|
1806 |
|
1807 @param aPosition |
|
1808 The data path position. |
|
1809 */ |
|
1810 EXPORT_C void CMMFDataPath::SetPositionL(const TTimeIntervalMicroSeconds& aPosition) |
|
1811 {//need to map to source position to frame position |
|
1812 #ifdef _DP_DEBUG |
|
1813 RDebug::Print(_L("DP::SetPositionL = %d ticks-%d (this 0x%x)\n"),I64INT(aPosition.Int64()), User::TickCount(),this); |
|
1814 #endif |
|
1815 |
|
1816 if (iState == EStopped) |
|
1817 User::Leave(KErrNotReady); //can only set position if primed |
|
1818 |
|
1819 //As this will affect the position, we need to know how many bytes were |
|
1820 //played when position was updated. Future Position() requests will |
|
1821 //then use this refernce to determine the current position. |
|
1822 iReferenceAudioSamplesPlayed = AudioSamplesPlayed(); |
|
1823 |
|
1824 // Force the new position to be inside the play window (also within the file duration) |
|
1825 if ( aPosition < iPlayWindowStartPosition ) |
|
1826 iStartPosition = iPlayWindowStartPosition; |
|
1827 else if ( aPosition > iPlayWindowEndPosition ) |
|
1828 iStartPosition = iPlayWindowEndPosition; //clearly this will cause nothing to be played |
|
1829 else |
|
1830 iStartPosition = aPosition; |
|
1831 |
|
1832 TTimeIntervalMicroSeconds interval; |
|
1833 |
|
1834 //can only set the position on an MDataSource that is a format object |
|
1835 //Note: position defaults to source if both source & sink are clips |
|
1836 if (iDataSource->DataSourceType() == KUidMmfFormatDecode) |
|
1837 { |
|
1838 //position is not beyond the end of file |
|
1839 interval = ((CMMFFormatDecode*)iDataSource)->FrameTimeInterval(iMediaId); |
|
1840 |
|
1841 // for some reason this code won't compile without these intermediate steps |
|
1842 TInt64 position = iStartPosition.Int64(); |
|
1843 TInt64 interval64 = interval.Int64(); |
|
1844 if (interval64 == 0) |
|
1845 User::Leave(KErrDivideByZero); |
|
1846 TInt64 datapos64 = position/interval64; |
|
1847 iCurrentSourceFrameNumber = I64LOW(datapos64); |
|
1848 |
|
1849 |
|
1850 // Try to set the position directly on the format |
|
1851 TRAP_IGNORE(((CMMFFormatDecode*)iDataSource)->SetPositionL(iStartPosition)); |
|
1852 //NB: don't worry about error, since we'll reposition anyway when we get the next buffer |
|
1853 } |
|
1854 else if (iDataSink->DataSinkType() == KUidMmfFormatEncode) |
|
1855 { |
|
1856 //position is not beyond the end of file |
|
1857 interval = ((CMMFFormatEncode*)iDataSink)->FrameTimeInterval(iMediaId); |
|
1858 |
|
1859 //convert to TUint - for some reason it won't compile without these intermediate steps |
|
1860 TInt64 position = iStartPosition.Int64(); |
|
1861 TInt64 interval64 = interval.Int64(); |
|
1862 if (interval64 == 0) |
|
1863 User::Leave(KErrDivideByZero); |
|
1864 TInt64 datapos64 = position/interval64; |
|
1865 iCurrentSinkFrameNumber = I64LOW(datapos64); |
|
1866 |
|
1867 |
|
1868 // Try to set the position directly on the format |
|
1869 TRAP_IGNORE(((CMMFFormatEncode*)iDataSink)->SetPositionL(iStartPosition)); |
|
1870 //NB: don't worry about error, since we'll reposition anyway when we get the next buffer |
|
1871 } |
|
1872 else |
|
1873 {//can only set position if source or sink is a format |
|
1874 //If both source and sink are formats position is relative to the source |
|
1875 User::Leave(KErrNotSupported); //can't set position if neither source nor sink are clips |
|
1876 } |
|
1877 |
|
1878 if(iCodec) //we have a real codec, must reset it |
|
1879 iCodec->ResetL(); // Need to preserve sync when resuming play |
|
1880 |
|
1881 // Once we've sent the last buffer to the sink it's too late to start |
|
1882 // changing the state since we may get a RunError(KErrUnderflow) at any time. |
|
1883 // Once this happens, the sound driver may have unloaded etc..and recovery |
|
1884 // would be complicated. |
|
1885 if (iAllDataSentToSink) |
|
1886 return; |
|
1887 |
|
1888 #ifdef _DP_DEBUG |
|
1889 RDebug::Print(_L("DP::SetPosition - Done iCurrentSourceFrameNumber=%d iStartPosition=%d ticks-%d (this 0x%x)\n"),iCurrentSourceFrameNumber, I64INT(iStartPosition.Int64()), User::TickCount(),this); |
|
1890 #endif |
|
1891 } |
|
1892 |
|
1893 |
|
1894 |
|
1895 |
|
1896 /** |
|
1897 Sets the play window absolutely (i.e. the parameters are relative to the start of the entire clip). |
|
1898 |
|
1899 @param aStart |
|
1900 The offset from the start of the Clip |
|
1901 @param aEnd |
|
1902 The offset from the end of the clip (if this is less than aStart, then the two will be inverted). |
|
1903 */ |
|
1904 EXPORT_C void CMMFDataPath::SetPlayWindowL( const TTimeIntervalMicroSeconds& aStart, const TTimeIntervalMicroSeconds& aEnd ) |
|
1905 { |
|
1906 // Clear the existing Play window |
|
1907 ClearPlayWindowL() ; |
|
1908 |
|
1909 // Check that the parameters are legitimate. 0 <= startpos < endpos <= duration & update member variables |
|
1910 TTimeIntervalMicroSeconds duration = Duration(); |
|
1911 |
|
1912 if ( aStart < TTimeIntervalMicroSeconds(0) ) |
|
1913 iPlayWindowStartPosition = TTimeIntervalMicroSeconds(0); |
|
1914 else if ( aStart > duration ) |
|
1915 iPlayWindowStartPosition = duration; |
|
1916 else iPlayWindowStartPosition = aStart; |
|
1917 |
|
1918 if ( aEnd < TTimeIntervalMicroSeconds(0) ) |
|
1919 iPlayWindowEndPosition = TTimeIntervalMicroSeconds(0); |
|
1920 else if ( aEnd > duration ) |
|
1921 iPlayWindowEndPosition = duration; |
|
1922 else iPlayWindowEndPosition = aEnd; |
|
1923 |
|
1924 // ensure that the current position is inside the new play window |
|
1925 if ( iPlayWindowEndPosition != TTimeIntervalMicroSeconds(0) ) |
|
1926 { |
|
1927 TTimeIntervalMicroSeconds currentPosition = Position() ; |
|
1928 if ( currentPosition < iPlayWindowStartPosition ) |
|
1929 SetPositionL( iPlayWindowStartPosition ) ; |
|
1930 else if ( currentPosition > iPlayWindowEndPosition ) |
|
1931 SetPositionL( iPlayWindowEndPosition ) ; |
|
1932 } |
|
1933 else |
|
1934 ClearPlayWindowL() ; |
|
1935 |
|
1936 #ifdef _DP_DEBUG |
|
1937 RDebug::Print(_L("DP::SetPlayWindowL iPlayWindowStartPosition=%d iPlayWindowEndPosition=%d\n"),I64INT(iPlayWindowStartPosition.Int64()),I64INT(iPlayWindowEndPosition.Int64())); |
|
1938 #endif |
|
1939 } |
|
1940 |
|
1941 |
|
1942 /** |
|
1943 Sets the play window to the full length of clip. |
|
1944 */ |
|
1945 EXPORT_C void CMMFDataPath::ClearPlayWindowL() |
|
1946 { |
|
1947 iPlayWindowStartPosition = TTimeIntervalMicroSeconds(0) ; |
|
1948 iPlayWindowEndPosition = Duration(); |
|
1949 |
|
1950 |
|
1951 if(iState == EStopped) |
|
1952 iStartPosition = iPlayWindowStartPosition; |
|
1953 } |
|
1954 |
|
1955 /** |
|
1956 Returns the current data path state. |
|
1957 */ |
|
1958 EXPORT_C TInt CMMFDataPath::State() |
|
1959 { |
|
1960 return iState ; |
|
1961 } |
|
1962 |
|
1963 |
|
1964 /** |
|
1965 Uses the AO mechanism to drive state changes between sources and sinks. |
|
1966 |
|
1967 RunL() moves and assigns buffers between its attached MDataSource/MDataSinks. |
|
1968 */ |
|
1969 void CMMFDataPath::ChangeDataPathTransferState(TTransferState aNewDataPathTransferState) |
|
1970 { |
|
1971 #ifdef _DP_DEBUG |
|
1972 switch (aNewDataPathTransferState) |
|
1973 { |
|
1974 case EWaitSink: |
|
1975 RDebug::Print(_L("Next State EWaitSink ticks-%d (this 0x%x)\n"),User::TickCount(),this); |
|
1976 break; |
|
1977 case EWaitSource: |
|
1978 RDebug::Print(_L("Next State EWaitSource ticks-%d (this 0x%x)\n"),User::TickCount(),this); |
|
1979 break; |
|
1980 case EInitializeSink: |
|
1981 RDebug::Print(_L("Next State EInitializeSink ticks-%d (this 0x%x)\n"),User::TickCount(),this); |
|
1982 break; |
|
1983 case EInitializeSource: |
|
1984 RDebug::Print(_L("Next State EInitializeSource ticks-%d (this 0x%x)\n"),User::TickCount(),this); |
|
1985 break; |
|
1986 case ENeedSourceData: |
|
1987 RDebug::Print(_L("Next State ENeedSourceData ticks-%d (this 0x%x)\n"),User::TickCount(),this); |
|
1988 break; |
|
1989 case ENeedSinkData: |
|
1990 RDebug::Print(_L("Next State ENeedSinkData ticks-%d (this 0x%x)\n"),User::TickCount(),this); |
|
1991 break; |
|
1992 case ENeedToMatchSourceToSink: |
|
1993 RDebug::Print(_L("Next State ENeedToMatchSourceToSink ticks-%d (this 0x%x)\n"),User::TickCount(),this); |
|
1994 break; |
|
1995 case ESendDataToSink: |
|
1996 RDebug::Print(_L("Next State ESendDataToSink ticks-%d (this 0x%x)\n"),User::TickCount(),this); |
|
1997 break; |
|
1998 case EEndOfData: |
|
1999 RDebug::Print(_L("Next State EEndOfData ticks-%d (this 0x%x)\n"),User::TickCount(),this); |
|
2000 break; |
|
2001 } |
|
2002 #endif |
|
2003 |
|
2004 |
|
2005 TRequestStatus* stat = &iStatus; |
|
2006 //change state |
|
2007 iTransferState = aNewDataPathTransferState; |
|
2008 if ((iTransferState != EWaitSink) && (iTransferState != EWaitSource) && |
|
2009 (iState == EPlaying || iState == ERecording || iState == EConverting || (iState == EPrimed && iPauseCalled))) |
|
2010 {//can go ahead with transfer |
|
2011 if (!IsActive()) |
|
2012 { |
|
2013 User::RequestComplete(stat, KErrNone); |
|
2014 SetActive(); |
|
2015 } |
|
2016 } |
|
2017 #ifdef _DP_DEBUG |
|
2018 else |
|
2019 RDebug::Print(_L("Datapath is no longer active, not going to new state (this 0x%x)\n"),this); |
|
2020 #endif |
|
2021 } |
|
2022 |
|
2023 /** |
|
2024 Runs the clip depending on the current data path and transfer state. |
|
2025 |
|
2026 For example, fills the sink buffer if TDataPathState is EPlaying and TTransferState is ENeedSinkData. |
|
2027 */ |
|
2028 EXPORT_C void CMMFDataPath::RunL() |
|
2029 { |
|
2030 #ifdef _DP_DEBUG |
|
2031 RDebug::Print(_L("DP::RunL tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
2032 #endif |
|
2033 |
|
2034 switch (iState) |
|
2035 { |
|
2036 case EStopped: |
|
2037 break; |
|
2038 case EPrimed: |
|
2039 //paused with stored position |
|
2040 break; |
|
2041 case EPlaying: |
|
2042 case ERecording: |
|
2043 case EConverting: |
|
2044 switch (iTransferState) |
|
2045 { |
|
2046 case EWaitSink: |
|
2047 case EWaitSource: |
|
2048 break; |
|
2049 case EInitializeSink: |
|
2050 InitializeSinkL(); |
|
2051 break; |
|
2052 case EInitializeSource: |
|
2053 InitializeSourceL(); |
|
2054 break; |
|
2055 case ENeedSourceData: |
|
2056 FillSourceBufferL(); |
|
2057 break; |
|
2058 case ENeedSinkData: |
|
2059 FillSinkBufferL(); |
|
2060 break; |
|
2061 case ENeedToMatchSourceToSink: |
|
2062 FillSinkBufferL(); |
|
2063 break; |
|
2064 case ESendDataToSink: |
|
2065 EmptySinkBufferL(); |
|
2066 break; |
|
2067 case EEndOfData: |
|
2068 EndOfData(); |
|
2069 break; |
|
2070 } |
|
2071 break; |
|
2072 default: |
|
2073 break; |
|
2074 } |
|
2075 #ifdef _DP_DEBUG |
|
2076 RDebug::Print(_L("DP::RunL DONE\n")); |
|
2077 #endif |
|
2078 } |
|
2079 |
|
2080 /** |
|
2081 Cancels the clip. |
|
2082 |
|
2083 The default implementation is empty. |
|
2084 */ |
|
2085 EXPORT_C void CMMFDataPath::DoCancel() |
|
2086 { |
|
2087 //don't need to do anything as we don't have any async requests to other objects |
|
2088 } |
|
2089 |
|
2090 /** |
|
2091 Handles errors coming from attached sources and passes them to the clients. |
|
2092 |
|
2093 @param aError |
|
2094 Standard error code (KErrNone = No Error). |
|
2095 |
|
2096 @return The event code returned to the data path. KErrNone if end of file is encountered. |
|
2097 */ |
|
2098 EXPORT_C TInt CMMFDataPath::RunError(TInt aError) |
|
2099 { |
|
2100 return DoSendEventToClient(KMMFErrorCategoryDataPathGeneralError, aError); |
|
2101 } |
|
2102 |
|
2103 |
|
2104 /** |
|
2105 Returns the duration of the clip. |
|
2106 |
|
2107 @return The length of clip in TTimeIntervalMicroSeconds. |
|
2108 */ |
|
2109 TTimeIntervalMicroSeconds CMMFDataPath::Duration() const |
|
2110 { |
|
2111 TTimeIntervalMicroSeconds duration(0); |
|
2112 |
|
2113 if ( iDataSource && ( iDataSource->DataSourceType() == KUidMmfFormatDecode ) ) |
|
2114 { |
|
2115 //this updated version of datapath caches the duration of the |
|
2116 //source clip for efficiency - since this meathod is const |
|
2117 //we need to cast away the constness to set iCachedSourceDuration |
|
2118 CMMFDataPath* thisNonConst = const_cast<CMMFDataPath*>(this); |
|
2119 duration = STATIC_CAST(CMMFFormatDecode*, thisNonConst->iDataSource )->Duration( iMediaId ) ; |
|
2120 thisNonConst->iCachedSourceDuration = duration; |
|
2121 } |
|
2122 else if ( iDataSink && ( iDataSink->DataSinkType() == KUidMmfFormatEncode ) ) |
|
2123 duration = STATIC_CAST(CMMFFormatEncode*, iDataSink )->Duration( iMediaId ) ; |
|
2124 |
|
2125 return duration ; |
|
2126 } |
|
2127 |
|
2128 /** |
|
2129 This is a virtual function datapath (or derivations off) that can be implemented but may be left blank for default behaviour. |
|
2130 |
|
2131 Additional Stop() method specific to this datapath. |
|
2132 */ |
|
2133 void CMMFDataPath::DoStopL() |
|
2134 { |
|
2135 // Note that the datapath needs to be paused first |
|
2136 // before it can be stopped - this is important for audio play |
|
2137 // for instance to effect an immediate stop rather than playing out |
|
2138 // the current buffer. |
|
2139 |
|
2140 |
|
2141 |
|
2142 // Stop data source and sink |
|
2143 iDataSource->SourceStopL(); // propagate state change to source |
|
2144 iDataSink->SinkStopL(); // propagate change down to sink |
|
2145 |
|
2146 iSinkBufferWithSink = EFalse; |
|
2147 iSourceBufferWithSource = EFalse; |
|
2148 |
|
2149 //Contains the completion code if the datapath completes as a result of an error |
|
2150 iDataPathCompletedErrorCode = KErrNone; |
|
2151 |
|
2152 ResetRefBuffers(); // buffer references may not be valid any more |
|
2153 |
|
2154 SetPositionL(iPlayWindowStartPosition); // reset position |
|
2155 |
|
2156 iState = EStopped; // stop succeeded, set state to stopped |
|
2157 } |
|
2158 |
|
2159 /** |
|
2160 This is a virtual function datapath (or derivations off) that can be implemented but may be left blank for default behaviour. |
|
2161 |
|
2162 Additional Pause method specific to this datapath. |
|
2163 |
|
2164 The DataPath implements pause by recording the current position and stopping the source and sink. |
|
2165 When Play is called the DataPath uses the stored position as the starting point and resumes |
|
2166 playing. The reason for this (rather than using the PauseL() API on sources and sinks) is that |
|
2167 some implementations do not support a suitable pause, therefore the DataPath is implemented to |
|
2168 support the lowest common denominator. |
|
2169 |
|
2170 Note: |
|
2171 A suitable pause implementation will retain any buffers in use. There will be no |
|
2172 need to call PrimeL() prior to PlayL(). |
|
2173 */ |
|
2174 void CMMFDataPath::DoPauseL() |
|
2175 { |
|
2176 #ifdef _DP_DEBUG |
|
2177 RDebug::Print(_L("DP::DoPauseL tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
2178 #endif |
|
2179 |
|
2180 |
|
2181 if ((iDataPathCreated) && ((iState == EPlaying) || (iState == EConverting))) |
|
2182 { |
|
2183 // try to pause source and sink |
|
2184 iDataSource->SourcePauseL(); // propagate state change to source |
|
2185 SetPositionL(Position()); |
|
2186 iDataSink->SinkStopL(); |
|
2187 |
|
2188 iPauseCalled = ETrue; // indicate pause is called |
|
2189 |
|
2190 iState = EPrimed; // go back to primed state (we're not playing) |
|
2191 |
|
2192 iSinkBufferWithSink = EFalse; |
|
2193 iSourceBufferWithSource = EFalse; |
|
2194 |
|
2195 ResetRefBuffers(); // buffer references may not be valid any more |
|
2196 |
|
2197 Cancel(); //Stop the state machine |
|
2198 } |
|
2199 else if(iState == ERecording) |
|
2200 { |
|
2201 iPauseCalled = ETrue; |
|
2202 #ifdef _DP_DEBUG |
|
2203 RDebug::Print(_L("DP::DoPauseL Recording, pause datasource\n")); |
|
2204 #endif |
|
2205 iDataSource->SourcePauseL(); |
|
2206 } |
|
2207 #ifdef _DP_DEBUG |
|
2208 RDebug::Print(_L("DP::DoPauseL - Done iReferenceAudioSamplesPlayed = %d\n"),iReferenceAudioSamplesPlayed); |
|
2209 RDebug::Print(_L("DP::DoPauseL - Done restart at %d tick-%d (this 0x%x)\n"),I64INT(iStartPosition.Int64()), User::TickCount(),this); |
|
2210 #endif |
|
2211 } |
|
2212 |
|
2213 /** |
|
2214 This is a virtual function datapath (or derivations off) that can be implemented or may be left blank for default behaviour. |
|
2215 |
|
2216 Additional Pause method specific to this datapath. |
|
2217 */ |
|
2218 void CMMFDataPath::DoEndOfDataL() |
|
2219 { |
|
2220 #ifdef _DP_DEBUG |
|
2221 RDebug::Print(_L("DP::DoEndOfDataL tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
2222 #endif |
|
2223 SetPositionL(iPlayWindowStartPosition); // reset position |
|
2224 |
|
2225 ASSERT(iCompleteCallback); |
|
2226 iCompleteCallback->SignalDataPathComplete(iDataPathCompletedErrorCode); |
|
2227 |
|
2228 ResetRefBuffers(); |
|
2229 iState = EStopped; |
|
2230 |
|
2231 #ifdef _DP_DEBUG |
|
2232 RDebug::Print(_L("DP::DoEndOfDataL - Done tick-%d (this 0x%x)\n"),User::TickCount(),this); |
|
2233 #endif |
|
2234 } |
|
2235 |
|
2236 |
|
2237 /** |
|
2238 Passes error handling and general messages up to clients. |
|
2239 |
|
2240 @param aEventType |
|
2241 Category code for the event. Category codes can be used as unique identifers. |
|
2242 @param aErrorCode |
|
2243 Standard error code. |
|
2244 |
|
2245 @return The event code sent to client. |
|
2246 */ |
|
2247 //error handling |
|
2248 EXPORT_C TInt CMMFDataPath::DoSendEventToClient(TUid aEventType, TInt aErrorCode) |
|
2249 { |
|
2250 TMMFEvent event(aEventType, aErrorCode); |
|
2251 return iEventHandler.SendEventToClient(event); |
|
2252 } |
|
2253 |
|
2254 /** |
|
2255 Passes error handling and general messages to clients. |
|
2256 |
|
2257 @param aEvent |
|
2258 TMMFEvent supplied by callee (typically DoSendEventToClient) |
|
2259 |
|
2260 @return The Event Message sent to the datapath event handler |
|
2261 */ |
|
2262 EXPORT_C TInt CMMFDataPath::SendEventToClient(const TMMFEvent& aEvent) |
|
2263 { |
|
2264 #ifdef _DP_DEBUG |
|
2265 RDebug::Print(_L("CMMFDataPath::SendEventToClient CODE = %d ticks=%d (this 0x%x)\n"),aEvent.iErrorCode,User::TickCount(),this); |
|
2266 RDebug::Print(_L("CMMFDataPath::SendEventToClient iEventType = %d == %d\n"),aEvent.iEventType, KMMFEventCategoryPlaybackComplete); |
|
2267 #endif |
|
2268 |
|
2269 //If we have sent all the data to the sink, it is legal for it to send KErrUnderFlow |
|
2270 //to us and we can go through a clean shutdown, otherwise we pass the error to the |
|
2271 //controller and it is responsible for determining what to do |
|
2272 if(iAllDataSentToSink && |
|
2273 (aEvent.iEventType == KMMFEventCategoryPlaybackComplete) && |
|
2274 (aEvent.iErrorCode == KErrUnderflow)) |
|
2275 { |
|
2276 #ifdef _DP_DEBUG |
|
2277 RDebug::Print(_L("CMMFDataPath::SendEventToClient Clean complete\n")); |
|
2278 #endif |
|
2279 //sink may not return the final buffer once it has finished with it |
|
2280 //force ourselves into the EndOfData state |
|
2281 if(iTransferState != EEndOfData) |
|
2282 { |
|
2283 iDataPathCompletedErrorCode = KErrNone; |
|
2284 TRAP_IGNORE(DoEndOfDataL()); |
|
2285 } |
|
2286 |
|
2287 iCompleteCallback->SignalSinkComplete(KErrNone); |
|
2288 return KErrNone; |
|
2289 } |
|
2290 |
|
2291 |
|
2292 |
|
2293 return iEventHandler.SendEventToClient(aEvent); |
|
2294 } |
|
2295 |
|
2296 CMMFDataPath::CCompleteCallback::CCompleteCallback(CMMFDataPath& aDataPath, TBool aWaitForSink) |
|
2297 : CActive(EPriorityStandard), |
|
2298 iDataPath(aDataPath), |
|
2299 iWaitForSink(aWaitForSink) |
|
2300 { |
|
2301 CActiveScheduler::Add(this); |
|
2302 } |
|
2303 |
|
2304 CMMFDataPath::CCompleteCallback::~CCompleteCallback() |
|
2305 { |
|
2306 Cancel(); |
|
2307 } |
|
2308 |
|
2309 void CMMFDataPath::CCompleteCallback::SignalDataPathComplete(TInt aDataPathError) |
|
2310 { |
|
2311 iDataPathComplete = ETrue; |
|
2312 iDataPathError = aDataPathError; |
|
2313 if (!IsActive()) |
|
2314 { |
|
2315 // Signal ourselves to run with the given completion code |
|
2316 TRequestStatus* status = &ActiveStatus(); |
|
2317 User::RequestComplete(status, KErrNone); |
|
2318 } |
|
2319 } |
|
2320 |
|
2321 void CMMFDataPath::CCompleteCallback::SignalSinkComplete(TInt aSinkError) |
|
2322 { |
|
2323 iSinkComplete = ETrue; |
|
2324 iSinkError = aSinkError; |
|
2325 if (!IsActive()) |
|
2326 { |
|
2327 // Signal ourselves to run with the given completion code |
|
2328 TRequestStatus* status = &ActiveStatus(); |
|
2329 User::RequestComplete(status, KErrNone); |
|
2330 } |
|
2331 } |
|
2332 |
|
2333 |
|
2334 TRequestStatus& CMMFDataPath::CCompleteCallback::ActiveStatus() |
|
2335 { |
|
2336 SetActive(); |
|
2337 return iStatus; |
|
2338 } |
|
2339 |
|
2340 void CMMFDataPath::CCompleteCallback::DoCancel() |
|
2341 { |
|
2342 } |
|
2343 |
|
2344 void CMMFDataPath::CCompleteCallback::RunL() |
|
2345 { |
|
2346 if (iWaitForSink) |
|
2347 { |
|
2348 if (iDataPathComplete && iSinkComplete) |
|
2349 { |
|
2350 #ifdef _DP_DEBUG |
|
2351 RDebug::Print(_L("CMMFDataPath::CCompleteCallback::RunL STOPPING src & sink ticks=%d (this 0x%x)\n"),User::TickCount(),this); |
|
2352 #endif |
|
2353 |
|
2354 TRAP_IGNORE(iDataPath.DoStopL()) |
|
2355 |
|
2356 iDataPathComplete = EFalse; |
|
2357 iSinkComplete = EFalse; |
|
2358 |
|
2359 // if we have to wait for the sink to complete, always use the sink error |
|
2360 iDataPath.DoSendEventToClient(KMMFEventCategoryPlaybackComplete, iSinkError); |
|
2361 } |
|
2362 } |
|
2363 else if (iDataPathComplete) |
|
2364 { |
|
2365 #ifdef _DP_DEBUG |
|
2366 RDebug::Print(_L("CMMFDataPath::CCompleteCallback::RunL STOPPING src & sink ticks=%d (this 0x%x)\n"),User::TickCount(),this); |
|
2367 #endif |
|
2368 |
|
2369 TRAP_IGNORE(iDataPath.DoStopL()) |
|
2370 |
|
2371 iDataPathComplete = EFalse; |
|
2372 iSinkComplete = EFalse; |
|
2373 |
|
2374 iDataPath.DoSendEventToClient(KMMFEventCategoryPlaybackComplete, iDataPathError); |
|
2375 } |
|
2376 } |
|
2377 |
|
2378 EXPORT_C TInt CMMFDataPath::SetBlockLength(TUint aBlockLength) |
|
2379 { |
|
2380 MMMFDevSoundCustomInterfaceFileBlockLength* fileBlockLengthCI = NULL; |
|
2381 TInt err = KErrNotSupported; |
|
2382 if (iCodec) |
|
2383 { |
|
2384 err = iCodec->ExtensionInterface(KUidCustomInterfaceDevSoundFileBlockLength.iUid, (TAny*&)fileBlockLengthCI); |
|
2385 } |
|
2386 |
|
2387 if (err == KErrNone) |
|
2388 { |
|
2389 fileBlockLengthCI->SetFileBlockLength(aBlockLength); |
|
2390 } |
|
2391 |
|
2392 return err; |
|
2393 } |
|
2394 |