|
1 // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // source\server\mmfswcodecconvertdatapath.cpp |
|
15 // |
|
16 // |
|
17 |
|
18 #include "mmfSwCodecConvertDataPath.h" |
|
19 #include <mmf/server/mmfswcodecwrapper.h> |
|
20 #include <mmf/common/mmfpaniccodes.h> |
|
21 |
|
22 |
|
23 CMMFSwCodecConvertDataPath* CMMFSwCodecConvertDataPath::NewL() |
|
24 { |
|
25 CMMFSwCodecConvertDataPath* self = new(ELeave) CMMFSwCodecConvertDataPath; |
|
26 CleanupStack::PushL(self); |
|
27 self->ConstructL(); |
|
28 CleanupStack::Pop(); |
|
29 return self; |
|
30 } |
|
31 |
|
32 |
|
33 void CMMFSwCodecConvertDataPath::ConstructL() |
|
34 { |
|
35 iDataPathConverter = new (ELeave) CDataPathConverter(*this,CActive::EPriorityUserInput); |
|
36 } |
|
37 |
|
38 |
|
39 CMMFSwCodecConvertDataPath::~CMMFSwCodecConvertDataPath() |
|
40 { |
|
41 delete iDataPathConverter; |
|
42 if (iCodec) |
|
43 { |
|
44 delete iSourceBuffer; |
|
45 if (!iCodec->IsNullCodec()) |
|
46 delete iSinkBuffer; |
|
47 } |
|
48 } |
|
49 |
|
50 |
|
51 TInt CMMFSwCodecConvertDataPath::SetObserver(MMMFHwDeviceObserver& aObserver) |
|
52 { |
|
53 TInt error; |
|
54 if (iHwDeviceObserver) |
|
55 { |
|
56 error = KErrAlreadyExists; |
|
57 } |
|
58 else |
|
59 { |
|
60 iHwDeviceObserver = &aObserver; |
|
61 error = KErrNone; |
|
62 } |
|
63 return error; |
|
64 } |
|
65 |
|
66 |
|
67 TInt CMMFSwCodecConvertDataPath::AddCodec(CMMFSwCodec& aCodec) |
|
68 { |
|
69 if (iCodec) |
|
70 return KErrNotSupported; //doesn't support multiple codecs |
|
71 |
|
72 TInt err = KErrNone; |
|
73 iCodec = &aCodec; |
|
74 |
|
75 iSourceBufferSize = iCodec->SourceBufferSize(); |
|
76 iSinkBufferSize = iCodec->SinkBufferSize(); |
|
77 |
|
78 if ((!iSourceBufferSize)||(!iSinkBufferSize)) |
|
79 err = KErrArgument; //codec plugin has not specified buffer size |
|
80 |
|
81 if (err == KErrNone) |
|
82 { |
|
83 TRAP(err,iSourceBuffer = CMMFDataBuffer::NewL(iSourceBufferSize)); |
|
84 } |
|
85 |
|
86 if (err == KErrNone) |
|
87 { |
|
88 // Allocate data buffer |
|
89 if (iCodec->IsNullCodec()) |
|
90 {//don't need a separate sink buffer if null codec |
|
91 iSinkBuffer = NULL; //sink buffer is the sound device buffer |
|
92 } |
|
93 else |
|
94 {//need a separate sink buffer for the codec |
|
95 TRAP(err,iSinkBuffer = CMMFDataBuffer::NewL(iSinkBufferSize)); |
|
96 } |
|
97 } |
|
98 return err; |
|
99 } |
|
100 |
|
101 |
|
102 TInt CMMFSwCodecConvertDataPath::Start() |
|
103 { |
|
104 TInt startError = KErrNone; |
|
105 if (!iCodec) |
|
106 {//check that a codec has been added |
|
107 startError = KErrNotReady; |
|
108 } |
|
109 if (!startError) |
|
110 { |
|
111 // Start the player objects |
|
112 iSourceBuffer->SetLastBuffer(EFalse); |
|
113 iDataPathConverter->Start(); |
|
114 iState = EPlaying; |
|
115 iNoMoreSourceData = EFalse; |
|
116 } |
|
117 return startError; |
|
118 } |
|
119 |
|
120 |
|
121 |
|
122 void CMMFSwCodecConvertDataPath::Stop() |
|
123 { |
|
124 iDataPathConverter->Cancel(); |
|
125 iState = EStopped; |
|
126 } |
|
127 |
|
128 |
|
129 void CMMFSwCodecConvertDataPath::Pause() |
|
130 {//pause is equivalent to stop for a data transfer |
|
131 iDataPathConverter->Cancel(); |
|
132 iState = EStopped; |
|
133 } |
|
134 |
|
135 /*** Main play loop ***/ |
|
136 |
|
137 void CMMFSwCodecConvertDataPath::FillSourceBufferL() |
|
138 { |
|
139 STATIC_CAST(CMMFDataBuffer*, iSourceBuffer)->SetRequestSizeL(iSourceBufferSize); |
|
140 |
|
141 // Ask immediately for data from the observer |
|
142 User::LeaveIfError(iHwDeviceObserver->FillThisHwBuffer(*iSourceBuffer)); |
|
143 } |
|
144 |
|
145 /** |
|
146 * BufferFilledL. |
|
147 * (from MDataSink) |
|
148 * |
|
149 * called by the CMMFDataPath's MDataSource when it has filled the buffer |
|
150 * @internalComponent |
|
151 * @param aBuffer |
|
152 * |
|
153 */ |
|
154 void CMMFSwCodecConvertDataPath::BufferFilledL(CMMFDataBuffer& aBuffer) |
|
155 { |
|
156 iSourceBuffer = &aBuffer; |
|
157 iSourceBuffer->SetStatus(EFull); |
|
158 |
|
159 //need to check that the buffer size is not 0 - |
|
160 //if so assume we've reached the end of the data |
|
161 if (!iSourceBuffer->BufferSize()) |
|
162 {//no buffer - could be end of source or could be that the source has no data |
|
163 //also need to check the sink buffer is available else there is still some |
|
164 //stuff to do before the sink buffer is freed |
|
165 if (iSinkBuffer->Status()==EAvailable) |
|
166 { |
|
167 iNoMoreSourceData = ETrue; |
|
168 } |
|
169 } |
|
170 else |
|
171 { |
|
172 if (iSourceBuffer->LastBuffer()) //also check last buffer flag |
|
173 iNoMoreSourceData = ETrue; |
|
174 iDataPathConverter->ChangeConvertState(CDataPathConverter::EFillingSinkBuffer); |
|
175 } |
|
176 } |
|
177 |
|
178 /* |
|
179 * FillSinkBufferL |
|
180 * |
|
181 * Function to take the data from an already full source buffer and by using |
|
182 * a codec if necessary fills the sink buffer |
|
183 * @internalComponent |
|
184 */ |
|
185 void CMMFSwCodecConvertDataPath::FillSinkBufferL() |
|
186 { |
|
187 CMMFSwCodec::TCodecProcessResult codecProcessResult; |
|
188 |
|
189 if (iCodec->IsNullCodec()) |
|
190 {//no codec so data can be sent direct to sink |
|
191 iSinkBuffer = iSourceBuffer; |
|
192 iSinkBuffer->SetStatus(EFull); //sink buffer is full |
|
193 } |
|
194 else |
|
195 { |
|
196 //pass buffer to codec for processing |
|
197 codecProcessResult = iCodec->ProcessL(*iSourceBuffer, *iSinkBuffer); |
|
198 if (iSourceBuffer->LastBuffer()) //if source is last buffer so is sink |
|
199 iSinkBuffer->SetLastBuffer(ETrue); |
|
200 if ((!iSinkBuffer->BufferSize())&&(codecProcessResult.iDstBytesAdded)) |
|
201 {//the codec has added data but not set the buffer length |
|
202 iSinkBuffer->Data().SetLength(codecProcessResult.iDstBytesAdded); |
|
203 } |
|
204 |
|
205 //only supports EProcessComplete |
|
206 switch (codecProcessResult.iCodecProcessStatus) |
|
207 { |
|
208 case CMMFSwCodec::TCodecProcessResult::EProcessComplete: |
|
209 //finished procesing source data - all data in sink buffer |
|
210 { |
|
211 iSourceBuffer->SetStatus(EAvailable); //source buffer is now avaialble |
|
212 iSinkBuffer->SetStatus(EFull); //sink buffer is full |
|
213 } |
|
214 break; |
|
215 case CMMFSwCodec::TCodecProcessResult::EDstNotFilled: |
|
216 //could be the last buffer in which case dst might not get filled |
|
217 { |
|
218 iSourceBuffer->SetStatus(EAvailable); //source buffer is now avaialble |
|
219 iSinkBuffer->SetStatus(EFull); //sink buffer is full |
|
220 } |
|
221 break; |
|
222 case CMMFSwCodec::TCodecProcessResult::EEndOfData: |
|
223 //no more data - send what we've got to the sink |
|
224 //note we can't always rely on this - in many cases the codec will not know when |
|
225 //it has reached the end of data. |
|
226 { |
|
227 iSourceBuffer->SetStatus(EAvailable); //source buffer is now avaialble |
|
228 iSinkBuffer->SetStatus(EFull);//sink buffer may not really be 'full' but its as full as it going to get |
|
229 //doesn't matter if sink buffer is not full |
|
230 } |
|
231 break; |
|
232 default: |
|
233 Panic(EMMFSwCodecWrapperBadCodec); //should never get here - bad codec |
|
234 } |
|
235 } |
|
236 iDataPathConverter->ChangeConvertState(CDataPathConverter::EEmptyingSinkBuffer); |
|
237 } |
|
238 |
|
239 |
|
240 void CMMFSwCodecConvertDataPath::EmptySinkBufferL() |
|
241 { |
|
242 User::LeaveIfError(iHwDeviceObserver->EmptyThisHwBuffer(*iSinkBuffer)); |
|
243 } |
|
244 |
|
245 |
|
246 void CMMFSwCodecConvertDataPath::BufferEmptiedL(const CMMFDataBuffer& aBuffer) |
|
247 { |
|
248 if (&aBuffer != iSinkBuffer) |
|
249 User::Leave(KErrArgument); |
|
250 if (!iNoMoreSourceData) |
|
251 iDataPathConverter->ChangeConvertState(CDataPathConverter::EFillingSourceBuffer); |
|
252 else //no more source data so signal EOF |
|
253 SoundDeviceException(KErrEof); |
|
254 } |
|
255 |
|
256 void CMMFSwCodecConvertDataPath::SoundDeviceException(TInt aError) |
|
257 { |
|
258 // Inform the observer of the exception condition |
|
259 iHwDeviceObserver->Error(aError); |
|
260 |
|
261 Stop(); |
|
262 |
|
263 // Let the observer know we're fully stopped |
|
264 iHwDeviceObserver->Stopped(); |
|
265 } |
|
266 |
|
267 RMdaDevSound& CMMFSwCodecConvertDataPath::Device() |
|
268 { |
|
269 return iDummyDevSound;//convert doesn't have a RMdaDevSound |
|
270 } |
|
271 |
|
272 /*** End of main play loop ***/ |
|
273 |
|
274 |
|
275 |
|
276 |
|
277 /************************************************************************ |
|
278 * CDataPathConverter |
|
279 * This class performs the main data transfer between the source and the sink |
|
280 * This is done in a separate class as opposed to CMMFSwCodecConvertDataPath |
|
281 * because the class needs to be an active object to avoid recursive call stacks |
|
282 * in cases where the source and sink are not active objects - which is |
|
283 * the case with descriptors. Making CMMFSwCodecConvertDataPath derive |
|
284 * from CActive is less desirable as it would involve multiple inheretence |
|
285 ************************************************************************/ |
|
286 |
|
287 CMMFSwCodecConvertDataPath::CDataPathConverter::CDataPathConverter(CMMFSwCodecConvertDataPath& aParent, TInt aPriority) |
|
288 : CActive(aPriority), iParent(aParent) |
|
289 { |
|
290 CActiveScheduler::Add(this); |
|
291 iConvertState = EIdle; |
|
292 } |
|
293 |
|
294 |
|
295 CMMFSwCodecConvertDataPath::CDataPathConverter::~CDataPathConverter() |
|
296 { |
|
297 Cancel(); |
|
298 } |
|
299 |
|
300 /** |
|
301 * Start |
|
302 * |
|
303 * Starts active scheduler 'play' loop |
|
304 * @internalComponent |
|
305 */ |
|
306 void CMMFSwCodecConvertDataPath::CDataPathConverter::Start() |
|
307 { |
|
308 // If we're not already active, complete a request on ourselves to kick off the state machine |
|
309 if (iConvertState == EIdle) |
|
310 iConvertState = EFillingSourceBuffer; |
|
311 if (!IsActive()) |
|
312 { |
|
313 TRequestStatus* stat = &iStatus; |
|
314 User::RequestComplete(stat, KErrNone); |
|
315 SetActive(); |
|
316 } |
|
317 } |
|
318 |
|
319 |
|
320 void CMMFSwCodecConvertDataPath::CDataPathConverter::ChangeConvertState(TConvertState aNewConvertState) |
|
321 { |
|
322 TRequestStatus* stat = &iStatus; |
|
323 //change state |
|
324 iConvertState = aNewConvertState; |
|
325 if (!IsActive()) |
|
326 { |
|
327 User::RequestComplete(stat, KErrNone); |
|
328 SetActive(); |
|
329 } |
|
330 } |
|
331 |
|
332 /*** Main Convert Loop ***/ |
|
333 |
|
334 void CMMFSwCodecConvertDataPath::CDataPathConverter::FillSourceBufferL() |
|
335 { |
|
336 iParent.FillSourceBufferL(); |
|
337 } |
|
338 |
|
339 void CMMFSwCodecConvertDataPath::CDataPathConverter::FillSinkBufferL() |
|
340 { |
|
341 iParent.FillSinkBufferL(); |
|
342 } |
|
343 |
|
344 void CMMFSwCodecConvertDataPath::CDataPathConverter::EmptySinkBufferL() |
|
345 { |
|
346 iParent.EmptySinkBufferL(); |
|
347 } |
|
348 |
|
349 /*** End of main convert loop ***/ |
|
350 |
|
351 |
|
352 void CMMFSwCodecConvertDataPath::CDataPathConverter::RunL() |
|
353 { |
|
354 switch (iConvertState) |
|
355 { |
|
356 case EFillingSourceBuffer: |
|
357 FillSourceBufferL(); |
|
358 break; |
|
359 case EFillingSinkBuffer: |
|
360 FillSinkBufferL(); |
|
361 break; |
|
362 case EEmptyingSinkBuffer: |
|
363 EmptySinkBufferL(); |
|
364 break; |
|
365 case EIdle: |
|
366 break; |
|
367 } |
|
368 } |
|
369 |
|
370 void CMMFSwCodecConvertDataPath::CDataPathConverter::DoCancel() |
|
371 { |
|
372 //don't need to do anything as we don't have any async requests to other objects |
|
373 } |
|
374 |
|
375 TInt CMMFSwCodecConvertDataPath::CDataPathConverter::RunError(TInt aError) |
|
376 { |
|
377 iParent.SoundDeviceException(aError); |
|
378 return KErrNone; |
|
379 } |
|
380 |
|
381 |
|
382 |
|
383 |