1 /* |
|
2 * Copyright (c) 2006-2007 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: ICY flow reader implementation |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 /* --------------------------------------------------------------------------- |
|
20 * Version history: |
|
21 * Template version: |
|
22 * <ccm_history> |
|
23 * |
|
24 * Version: 2, Tue Feb 28 18:00:00 2008 by Rohit/Kranthi |
|
25 * Ref: |
|
26 * Setting RawDataTransferredL() into DataTransferTracker for Byte Counter Impl |
|
27 * |
|
28 * </ccm_history> |
|
29 * ============================================================================ |
|
30 */ |
|
31 #include <utf.h> |
|
32 |
|
33 #include "iricyflowreader.h" |
|
34 #include "irdebug.h" |
|
35 #include "irmediaenginebuffer.h" |
|
36 #include "irmetadata.h" |
|
37 #include "irnetworkbuffer.h" |
|
38 #include "irstationconnection.h" |
|
39 #include "irstationdataobserver.h" |
|
40 #include "irstreamsourceerrors.h" |
|
41 #include "irnetworkcontroller.h" |
|
42 |
|
43 const TInt KMaxSongBufferSize = 61440; |
|
44 const TInt KNoInputBuffers = 60; |
|
45 const TInt KMaxBufferChunkSize = 1024; |
|
46 const TInt KMaxSocketBufferSize = 1024; |
|
47 const TInt KBufferPercentageInc = 1; |
|
48 const TInt KSixteen = 16; |
|
49 const TInt KThree=3; |
|
50 _LIT8( KIRStreamTitle, "StreamTitle='" ); |
|
51 _LIT8( KIRStreamUrl, "StreamUrl='" ); |
|
52 _LIT8( KIRMetaDataEndIdentifier, "';" ); |
|
53 _LIT8( KIRSongDelimiter, " - " ); |
|
54 |
|
55 // masks and prefices used in UTF-8 recognition |
|
56 const TInt KIRUtf8_2B1stByteMask = 0xE0; |
|
57 const TInt KIRUtf8_3B1stByteMask = 0xF0; |
|
58 const TInt KIRUtf8_4B1stByteMask = 0xF8; |
|
59 const TInt KIRUtf8FollowingByteMask = 0xC0; |
|
60 |
|
61 const TInt KIRUtf8_2B1stBytePrefix = 0xC0; |
|
62 const TInt KIRUtf8_3B1stBytePrefix = 0xE0; |
|
63 const TInt KIRUtf8_4B1stBytePrefix = 0xF0; |
|
64 const TInt KIRUtf8FollowingBytePrefix = 0x80; |
|
65 // ========================= MEMBER FUNCTIONS ================================ |
|
66 |
|
67 |
|
68 // --------------------------------------------------------------------------- |
|
69 // CIRIcyFlowReader::NewL |
|
70 // --------------------------------------------------------------------------- |
|
71 // |
|
72 CIRIcyFlowReader* CIRIcyFlowReader::NewL( RSocket& aSocket, CIRStationConnection& aOwner, |
|
73 MIRStationDataObserver& aDataObserver, TChannelInfo& aChannelInfo ) |
|
74 { |
|
75 CIRIcyFlowReader* self = new ( ELeave ) CIRIcyFlowReader( aSocket, aOwner, |
|
76 aDataObserver, aChannelInfo ); |
|
77 CleanupStack::PushL( self ); |
|
78 self->ConstructL(); |
|
79 CleanupStack::Pop( self ); |
|
80 return self; |
|
81 } |
|
82 |
|
83 // --------------------------------------------------------------------------- |
|
84 // CIRIcyFlowReader::CIRIcyFlowReader |
|
85 // --------------------------------------------------------------------------- |
|
86 // |
|
87 CIRIcyFlowReader::CIRIcyFlowReader( RSocket& aSocket, CIRStationConnection& aOwner, |
|
88 MIRStationDataObserver& aDataObserver, TChannelInfo& aChannelInfo ) |
|
89 :CActive( EPriorityStandard ), iSocket( aSocket ), iOwner( aOwner ), |
|
90 iDataObserver( aDataObserver ), |
|
91 iSocketBufferPtr( NULL, 0 ), iChannelInfo( aChannelInfo ) |
|
92 { |
|
93 |
|
94 } |
|
95 |
|
96 // --------------------------------------------------------------------------- |
|
97 // CIRIcyFlowReader::~CIRIcyFlowReader |
|
98 // --------------------------------------------------------------------------- |
|
99 // |
|
100 CIRIcyFlowReader::~CIRIcyFlowReader() |
|
101 { |
|
102 Cancel(); |
|
103 while(!iSinkQ.IsEmpty()) |
|
104 { |
|
105 //Deleting all the entries in sink buffers queue |
|
106 iTempBufferHolder = iSinkQ.First(); |
|
107 iSinkQ.Remove(*iTempBufferHolder); |
|
108 delete iTempBufferHolder; |
|
109 } |
|
110 while(!iSourceQ.IsEmpty()) |
|
111 { |
|
112 //deleting all the entries in source buffers queue |
|
113 iTempBufferHolder = iSourceQ.First(); |
|
114 iSourceQ.Remove(*iTempBufferHolder); |
|
115 delete iTempBufferHolder; |
|
116 } |
|
117 delete[] iSongBuffer; |
|
118 delete iSocketBuffer; |
|
119 delete iTempSongBuffer; |
|
120 delete iTempMetaBuffer; |
|
121 delete iMetaData; |
|
122 if(iNetworkControllerHandle) |
|
123 { |
|
124 iNetworkControllerHandle->Close(); |
|
125 } |
|
126 } |
|
127 |
|
128 // --------------------------------------------------------------------------- |
|
129 // CIRIcyFlowReader::ConstructL() |
|
130 // --------------------------------------------------------------------------- |
|
131 // |
|
132 void CIRIcyFlowReader::ConstructL() |
|
133 { |
|
134 IRLOG_DEBUG( "CIRIcyFlowReader::ConstructL" ); |
|
135 iAudioDataOffset = iChannelInfo.iAudioDataOffset; |
|
136 TInt f_off = _FOFF( CIRNetworkBuffer, iLink ); //for the buffer queue which is maintained |
|
137 iSourceQ.SetOffset( f_off ); // It is Queue of buffer given to socket to fill |
|
138 iSinkQ.SetOffset( f_off ); // It is Queue of buffer given to media engine |
|
139 InitializeBuffersL(); |
|
140 iMetaData = CIRMetaData::NewL(); |
|
141 CActiveScheduler::Add( this ); |
|
142 iNetworkControllerHandle = CIRNetworkController::OpenL(); |
|
143 IRLOG_DEBUG( "CIRIcyFlowReader::ConstructL - Exiting." ); |
|
144 } |
|
145 |
|
146 // --------------------------------------------------------------------------- |
|
147 // CIRIcyFlowReader::HandleReceivedDataL |
|
148 // --------------------------------------------------------------------------- |
|
149 // |
|
150 void CIRIcyFlowReader::HandleReceivedDataL( const TDesC8& aData ) |
|
151 { |
|
152 switch ( iParsingState ) |
|
153 { |
|
154 case EIRReadingAudioData: |
|
155 { |
|
156 if ( iAudioDataOffset + aData.Length() > iChannelInfo.iMetaInterval ) |
|
157 { |
|
158 // Part of this data contains meta data information already. |
|
159 TInt audioDataAmount = iChannelInfo.iMetaInterval - iAudioDataOffset; |
|
160 // Only the audio part of the data is added to the song buffer. |
|
161 HandleReceivedAudioData( aData.Left( audioDataAmount ) ); |
|
162 iParsingState = EIRReadingMetaDataLength; |
|
163 iAudioDataOffset = 0; // Resets the audio data offset, will start increment again after meta data is handled. |
|
164 HandleReceivedDataL( aData.Mid( audioDataAmount ) ); // Recursive call to handle meta data mixed in with this audio data block. |
|
165 } |
|
166 else // All of it is data is audio data. |
|
167 { |
|
168 HandleReceivedAudioData( aData ); |
|
169 } |
|
170 break; |
|
171 } |
|
172 case EIRReadingMetaDataLength: |
|
173 { |
|
174 // ICY protocol specifies that meta data length is the first byte of the data multiplied by 16. |
|
175 iMetaDataLength = aData[0] * KSixteen; |
|
176 |
|
177 delete iTempMetaBuffer; |
|
178 iTempMetaBuffer = NULL; |
|
179 |
|
180 if ( iMetaDataLength > 0 ) // Meta data is provided, so we have to parse it. |
|
181 { |
|
182 iTempMetaBuffer = HBufC8::NewL( iMetaDataLength ); |
|
183 iParsingState = EIRReadingMetaData; |
|
184 } |
|
185 else // No meta data available, so resume reading audio data. |
|
186 { |
|
187 iParsingState = EIRReadingAudioData; |
|
188 } |
|
189 |
|
190 if ( aData.Length() > 1 ) // Just to check that the data doesn't only contain the length byte. |
|
191 { |
|
192 HandleReceivedDataL( aData.Mid( 1 ) ); // Strips off the length byte. Recursive call as data can also contain audio data. |
|
193 } |
|
194 |
|
195 break; |
|
196 } |
|
197 case EIRReadingMetaData: |
|
198 { |
|
199 if ( iTempMetaBuffer->Length() + aData.Length() > iMetaDataLength ) |
|
200 { |
|
201 // All of the meta data block is now received, and part of it is continuation to the audio data. |
|
202 TInt metaDataAmount = iMetaDataLength - iTempMetaBuffer->Length(); |
|
203 HandleReceivedMetaData( aData.Left( metaDataAmount ) ); |
|
204 ExtractMetadataL(); // Extracts the meta data from the temporary meta data buffer. |
|
205 iParsingState = EIRReadingAudioData; |
|
206 HandleReceivedDataL( aData.Mid( metaDataAmount ) ); // Strips off the meta data from the descriptor. |
|
207 } |
|
208 else // All of it is meta data. |
|
209 { |
|
210 HandleReceivedMetaData( aData ); |
|
211 } |
|
212 break; |
|
213 } |
|
214 } |
|
215 } |
|
216 |
|
217 // --------------------------------------------------------------------------- |
|
218 // CIRIcyFlowReader::HandleReceivedAudioData |
|
219 // --------------------------------------------------------------------------- |
|
220 // |
|
221 void CIRIcyFlowReader::HandleReceivedAudioData( const TDesC8& aData ) |
|
222 { |
|
223 // Check to see if we've got enough audio data to fill a buffer. |
|
224 if ( iTempSongBuffer->Length() + aData.Length() >= KMaxBufferChunkSize ) |
|
225 { |
|
226 // Data contains more audio data than the buffer can handle. |
|
227 TInt amountToAdd = KMaxBufferChunkSize - iTempSongBuffer->Length(); |
|
228 |
|
229 // Enough audio data to fill the buffer is added. |
|
230 iTempSongBuffer->Des().Append( aData.Left( amountToAdd ) ); |
|
231 |
|
232 AddToSinkQueue( *iTempSongBuffer ); |
|
233 |
|
234 //while loop is written for only if left over amount in aData is |
|
235 //greater then 1024, then it should be again added into SinkQueue |
|
236 while(1) |
|
237 { |
|
238 iTempSongBuffer->Des().Zero(); |
|
239 //calculates the length of the leftover amount to be added |
|
240 TInt length = aData.Length() - amountToAdd; |
|
241 |
|
242 if(length <= 0) |
|
243 { |
|
244 break; |
|
245 } |
|
246 else if(length >= KMaxBufferChunkSize) //if the left over amount is >= 1024 then add to SinkQueue |
|
247 { |
|
248 iTempSongBuffer->Des().Append( aData.Mid( amountToAdd,KMaxBufferChunkSize ) ); |
|
249 // updates the amountToAdd value by 1024 |
|
250 amountToAdd += KMaxBufferChunkSize; |
|
251 |
|
252 AddToSinkQueue( *iTempSongBuffer ); |
|
253 } |
|
254 else //if the left over amount is < 1024 then append to tempSongBuffer & break |
|
255 { |
|
256 // Then the overflowing audio data part is added to the new clean buffer. |
|
257 iTempSongBuffer->Des().Append( aData.Mid( amountToAdd ) ); |
|
258 break; |
|
259 } |
|
260 } |
|
261 } |
|
262 else // There is enough room in the temporary audio buffer to hold all of the data. |
|
263 { |
|
264 iTempSongBuffer->Des().Append( aData ); |
|
265 } |
|
266 |
|
267 iAudioDataOffset += aData.Length(); |
|
268 |
|
269 |
|
270 |
|
271 } |
|
272 |
|
273 // --------------------------------------------------------------------------- |
|
274 // CIRIcyFlowReader::HandleReceivedMetaData |
|
275 // --------------------------------------------------------------------------- |
|
276 // |
|
277 void CIRIcyFlowReader::HandleReceivedMetaData( const TDesC8& aData ) |
|
278 { |
|
279 iTempMetaBuffer->Des().Append( aData ); |
|
280 } |
|
281 |
|
282 // --------------------------------------------------------------------------- |
|
283 // CIRIcyFlowReader::RunL |
|
284 // --------------------------------------------------------------------------- |
|
285 // |
|
286 void CIRIcyFlowReader::RunL() |
|
287 { |
|
288 // Active object request complete handler |
|
289 switch ( iStatus.Int() ) |
|
290 { |
|
291 case KErrNone: |
|
292 { |
|
293 // Byte Counter Impl |
|
294 iNetworkControllerHandle->DataTransferTracker().RawDataTransferredL( 0, |
|
295 iSocketBufferPtr.Size(), MIRDataTransferTracker::EIRTransferCategoryAudio); |
|
296 |
|
297 HandleReceivedDataL( *iSocketBuffer ); |
|
298 |
|
299 if ( !iSourceQ.IsEmpty() ) |
|
300 { |
|
301 //issue a read on empty buffer |
|
302 IssueRead(); |
|
303 } |
|
304 else |
|
305 { |
|
306 if( iReBuffering ) |
|
307 { |
|
308 // if rebuffering call continue using sink buffer |
|
309 FillRemainingBuffers(); |
|
310 } |
|
311 if( iInitialBuffering ) |
|
312 { |
|
313 // if first time intimate media client to fill the buffer |
|
314 iDataObserver.AudioDataEvent( MIRStationDataObserver::EBufferFilled, KErrNone ); |
|
315 iInitialBuffering = EFalse; |
|
316 } |
|
317 } |
|
318 |
|
319 break; |
|
320 } |
|
321 case KErrDisconnected: |
|
322 { |
|
323 IRLOG_ERROR( "CIRIcyFlowReader::RunL - KErrDisconnected"); |
|
324 iOwner.ConnectionError( KIRStreamSourceDisconnected ); |
|
325 } |
|
326 break; |
|
327 case KErrEof: |
|
328 { |
|
329 IRLOG_INFO( "CIRIcyFlowReader::RunL - KErrEof" ); |
|
330 iOwner.ConnectionError( KIRStreamSourceNoResponse ); |
|
331 } |
|
332 break; |
|
333 default: |
|
334 { |
|
335 IRLOG_ERROR2( "CIRIcyFlowReader::RunL - Error (%d)", iStatus.Int() ); |
|
336 iOwner.ConnectionError( KIRStreamSourceReadError ); |
|
337 } |
|
338 break; |
|
339 } |
|
340 } |
|
341 |
|
342 // --------------------------------------------------------------------------- |
|
343 // CIRIcyFlowReader::DoCancel |
|
344 // --------------------------------------------------------------------------- |
|
345 // |
|
346 void CIRIcyFlowReader::DoCancel() |
|
347 { |
|
348 iSocket.CancelRead(); |
|
349 } |
|
350 |
|
351 |
|
352 |
|
353 // --------------------------------------------------------------------------- |
|
354 // CIRIcyFlowReader::IssueRead |
|
355 // --------------------------------------------------------------------------- |
|
356 // |
|
357 void CIRIcyFlowReader::IssueRead() |
|
358 { |
|
359 if( !IsActive() ) |
|
360 { |
|
361 iSocketBufferPtr.Zero(); |
|
362 iSocket.Read( iSocketBufferPtr, iStatus ); |
|
363 SetActive(); |
|
364 } |
|
365 } |
|
366 |
|
367 // --------------------------------------------------------------------------- |
|
368 // CIRIcyFlowReader::Start |
|
369 // --------------------------------------------------------------------------- |
|
370 // |
|
371 void CIRIcyFlowReader::Start() |
|
372 { |
|
373 IRLOG_INFO( "CIRIcyFlowReader::Start" ); |
|
374 // Initiate a new read from socket into iBuffer |
|
375 iInitialBuffering = ETrue; |
|
376 iBufferCounter = 0; |
|
377 iPublishStationInfo = ETrue; |
|
378 IssueRead(); |
|
379 IRLOG_DEBUG( "CIRIcyFlowReader::Start - Exiting." ); |
|
380 } |
|
381 |
|
382 |
|
383 // --------------------------------------------------------------------------- |
|
384 // CIRIcyFlowReader::InitializeBuffersL |
|
385 // --------------------------------------------------------------------------- |
|
386 // |
|
387 void CIRIcyFlowReader::InitializeBuffersL() |
|
388 { |
|
389 IRLOG_DEBUG( "CIRIcyFlowReader::InitializeBuffersL" ); |
|
390 // Allocate the buffer for audio data on heap |
|
391 iSongBuffer = new TUint8[KMaxSongBufferSize]; |
|
392 User::LeaveIfNull( iSongBuffer ); |
|
393 |
|
394 IRLOG_INFO2( "CIRIcyFlowReader::InitializeBuffersL - Reserved %d bytes of memory", KMaxSongBufferSize ); |
|
395 TUint8* bufferaddress = iSongBuffer; |
|
396 // since sink buffers are not created initially all buffers are filled with data and appended to sink buffer |
|
397 // Create buffers ans append to source buffer queue |
|
398 for(TInt buffercount = 0; buffercount < KNoInputBuffers; buffercount++ ) |
|
399 { |
|
400 iTempBufferHolder = CIRNetworkBuffer::NewL(bufferaddress, |
|
401 KMaxBufferChunkSize); |
|
402 iSourceQ.AddLast(*iTempBufferHolder); |
|
403 bufferaddress += KMaxBufferChunkSize; |
|
404 } |
|
405 // Create a buffer for the data read from socket |
|
406 iSocketBuffer = HBufC8::NewL( KMaxSocketBufferSize ); |
|
407 iSocketBufferPtr.Set( iSocketBuffer->Des() ); |
|
408 iTempSongBuffer = HBufC8::NewL( KMaxSocketBufferSize ); |
|
409 IRLOG_DEBUG( "CIRIcyFlowReader::InitializeBuffersL - Exiting." ); |
|
410 } |
|
411 |
|
412 |
|
413 |
|
414 // --------------------------------------------------------------------------- |
|
415 // CIRIcyFlowReader::FillBuffer |
|
416 // Fills the mediaengine buffer and rebuffers if necessary |
|
417 // --------------------------------------------------------------------------- |
|
418 // |
|
419 void CIRIcyFlowReader::FillBuffer(TDes8& aInputBuffer) |
|
420 { |
|
421 FillMediaEngineBuffer(aInputBuffer); |
|
422 } |
|
423 |
|
424 // --------------------------------------------------------------------------- |
|
425 // CIRIcyFlowReader::AddToSinkQueue |
|
426 // Adds the filled buffers to the sink Q so that it can be copied to media |
|
427 // engine buffer |
|
428 // --------------------------------------------------------------------------- |
|
429 // |
|
430 void CIRIcyFlowReader::AddToSinkQueue( const TDesC8& aData ) |
|
431 { |
|
432 //call from runL |
|
433 //removes the buffer from source queue and put in sink queue |
|
434 |
|
435 if( !iSourceQ.IsEmpty() ) |
|
436 { |
|
437 iTempBufferHolder = iSourceQ.First(); |
|
438 TPtr8 bufferPointer(iTempBufferHolder->Des() ,KMaxBufferChunkSize, |
|
439 KMaxBufferChunkSize ); |
|
440 bufferPointer.Copy(aData); |
|
441 iSourceQ.Remove(*iTempBufferHolder); |
|
442 iSinkQ.AddLast(*iTempBufferHolder); |
|
443 if( iInitialBuffering ) |
|
444 { |
|
445 iBufferCounter += KBufferPercentageInc; |
|
446 iDataObserver.AudioDataEvent( MIRStationDataObserver::EBufferPercentage, iBufferCounter ); |
|
447 } |
|
448 } |
|
449 } |
|
450 |
|
451 // --------------------------------------------------------------------------- |
|
452 // CIRIcyFlowReader::FillMediaEngineBuffer |
|
453 // Fills the data into media engine's buffer |
|
454 // aInputBuffer Buffer into which data is to be filled |
|
455 // --------------------------------------------------------------------------- |
|
456 // |
|
457 |
|
458 void CIRIcyFlowReader::FillMediaEngineBuffer(const TDes8& aInputBuffer) |
|
459 { |
|
460 |
|
461 if( !iReBuffering ) |
|
462 { |
|
463 //Determine no of bytes of data to be filled |
|
464 TInt copyLength = aInputBuffer.MaxLength(); |
|
465 // Calculate the no of 1K chunks |
|
466 iNoOfChunks = copyLength/KMaxBufferChunkSize; |
|
467 // Initiailly remaining chunks to be filled is same as total no of |
|
468 // chunks to be filled |
|
469 iChunksRemaining = iNoOfChunks; |
|
470 IRLOG_DEBUG3( "CIRIcyFlowReader::FillMediaEngineBuffer - Copying %d bytes/%d chunks", copyLength, iNoOfChunks ); |
|
471 // Store the starting address of buffer into which data is to be |
|
472 // copied |
|
473 iBufferFillPointer = (TUint8 *)aInputBuffer.Ptr(); |
|
474 // Start filling of the empty media engine buffers |
|
475 FillRemainingBuffers(); |
|
476 } |
|
477 } |
|
478 |
|
479 |
|
480 // --------------------------------------------------------------------------- |
|
481 // CIRIcyFlowReader::FillRemainingBuffers |
|
482 // Fills the data into media engine's remaining buffers |
|
483 // called when the stream source runs out of buffers and |
|
484 // there is a pending request to media engine |
|
485 // --------------------------------------------------------------------------- |
|
486 // |
|
487 void CIRIcyFlowReader::FillRemainingBuffers() |
|
488 { |
|
489 TUint8* mediaBufferAddress = iBufferFillPointer; |
|
490 TInt chunksFilled = iNoOfChunks - iChunksRemaining; |
|
491 mediaBufferAddress += ( chunksFilled * KMaxBufferChunkSize ); |
|
492 |
|
493 TInt bufferNumber = iChunksRemaining; |
|
494 while ( bufferNumber ) |
|
495 { |
|
496 if ( !iSinkQ.IsEmpty() ) |
|
497 { |
|
498 iTempBufferHolder = iSinkQ.First(); |
|
499 TPtr8 mediaBufferPointer(mediaBufferAddress,KMaxBufferChunkSize, |
|
500 KMaxBufferChunkSize ); |
|
501 mediaBufferPointer.Copy( iTempBufferHolder->Des() , |
|
502 KMaxBufferChunkSize); |
|
503 TPtr8 tempBufferPointer(iTempBufferHolder->Des(), |
|
504 KMaxBufferChunkSize,KMaxBufferChunkSize ); |
|
505 tempBufferPointer.Delete(KMaxBufferChunkSize, |
|
506 KMaxBufferChunkSize); |
|
507 iSinkQ.Remove(*iTempBufferHolder); |
|
508 iSourceQ.AddLast(*iTempBufferHolder); |
|
509 iChunksRemaining--; |
|
510 bufferNumber--; |
|
511 iReBuffering = EFalse; |
|
512 mediaBufferAddress += KMaxBufferChunkSize; |
|
513 //issue source rebuffering here if source is not empty |
|
514 if( !iSourceQ.IsEmpty() ) |
|
515 { |
|
516 IssueRead(); |
|
517 } |
|
518 } |
|
519 else |
|
520 { |
|
521 //rebuffer if sink buffer is empty |
|
522 bufferNumber = 0; |
|
523 iReBuffering = ETrue; |
|
524 //issue source rebuffering here if source is not empty |
|
525 if( !iSourceQ.IsEmpty() ) |
|
526 { |
|
527 IssueRead(); |
|
528 } |
|
529 //break from for loop |
|
530 } |
|
531 } |
|
532 iBufferCounter += (K100Percentage - KNoInputBuffers) / KIRInputBufferCount; |
|
533 if ( iBufferCounter > K100Percentage ) |
|
534 { |
|
535 iBufferCounter = K100Percentage; |
|
536 } |
|
537 iDataObserver.AudioDataEvent( MIRStationDataObserver::EBufferPercentage, iBufferCounter ); |
|
538 if( !iReBuffering ) |
|
539 { |
|
540 iDataObserver.AudioDataEvent( MIRStationDataObserver::EOpenComplete, KErrNone ); |
|
541 } |
|
542 } |
|
543 |
|
544 // --------------------------------------------------------------------------- |
|
545 // CIRIcyFlowReader::ExtractMetadataL |
|
546 // Extracts the meta data from the stream |
|
547 // --------------------------------------------------------------------------- |
|
548 // |
|
549 void CIRIcyFlowReader::ExtractMetadataL() |
|
550 { |
|
551 IRLOG_DEBUG( "CIRIcyFlowReader::ExtractMetaDataL" ); |
|
552 |
|
553 // Erases old meta data information. |
|
554 iMetaData->SetArtistL( KNullDesC ); |
|
555 iMetaData->SetSongL( KNullDesC ); |
|
556 iMetaData->SetStreamUrlL( KNullDesC ); |
|
557 |
|
558 TPtrC8 ptr( *iTempMetaBuffer ); |
|
559 |
|
560 TInt streamTitleIndex = ptr.Find( KIRStreamTitle ); |
|
561 TInt streamUrlIndex = ptr.Find( KIRStreamUrl ); |
|
562 |
|
563 // Extracts the "StreamTitle" part of the meta data. |
|
564 if ( streamTitleIndex >= 0 ) |
|
565 { |
|
566 TPtrC8 streamTitlePtr( ptr.Mid( streamTitleIndex + KIRStreamTitle().Length() ) ); |
|
567 TInt streamTitleEndIndex = streamTitlePtr.Find( KIRMetaDataEndIdentifier ); |
|
568 if ( streamTitleEndIndex >= 0 ) |
|
569 { |
|
570 streamTitlePtr.Set( streamTitlePtr.Left( streamTitleEndIndex ) ); |
|
571 |
|
572 TPtrC8 artistPtr( KNullDesC8 ); |
|
573 TPtrC8 songPtr( KNullDesC8 ); |
|
574 |
|
575 TInt songDelimiterIndex = streamTitlePtr.Find( KIRSongDelimiter ); |
|
576 if ( songDelimiterIndex >= 0 ) |
|
577 { |
|
578 artistPtr.Set( streamTitlePtr.Left( songDelimiterIndex ) ); |
|
579 songPtr.Set( streamTitlePtr.Mid( songDelimiterIndex + |
|
580 KIRSongDelimiter().Length() ) ); |
|
581 } |
|
582 else |
|
583 { |
|
584 IRLOG_WARNING( "CIRIcyFlowReader::ExtractMetaDataL - Song delimiter was not found" ); |
|
585 artistPtr.Set( streamTitlePtr ); |
|
586 } |
|
587 |
|
588 HBufC* artist = DecodeMetadataStringLC( artistPtr ); |
|
589 iMetaData->SetArtistL( *artist ); |
|
590 CleanupStack::PopAndDestroy( artist ); |
|
591 |
|
592 HBufC* song = DecodeMetadataStringLC( songPtr ); |
|
593 iMetaData->SetSongL( *song ); |
|
594 CleanupStack::PopAndDestroy( song ); |
|
595 } |
|
596 else |
|
597 { |
|
598 IRLOG_WARNING( "CIRIcyFlowReader::ExtractMetaDataL - \"StreamTitle\" end was not found" ); |
|
599 } |
|
600 } |
|
601 else |
|
602 { |
|
603 IRLOG_WARNING( "CIRIcyFlowReader::ExtractMetaDataL - \"StreamTitle\" was not found" ); |
|
604 } |
|
605 |
|
606 // Extracts the "StreamUrl" part of the meta data. |
|
607 if ( streamUrlIndex >= 0 ) |
|
608 { |
|
609 TPtrC8 streamUrlPtr( ptr.Mid( streamUrlIndex + KIRStreamUrl().Length() ) ); |
|
610 |
|
611 TInt streamUrlEndIndex = streamUrlPtr.Find( KIRMetaDataEndIdentifier ); |
|
612 if ( streamUrlEndIndex >= 0 ) |
|
613 { |
|
614 streamUrlPtr.Set( streamUrlPtr.Left( streamUrlEndIndex ) ); |
|
615 HBufC* streamUrl = HBufC::NewLC( streamUrlPtr.Length() ); |
|
616 streamUrl->Des().Copy( streamUrlPtr ); // 8 bit to 16 bit descriptor conversion. |
|
617 iMetaData->SetStreamUrlL( *streamUrl ); |
|
618 CleanupStack::PopAndDestroy( streamUrl ); |
|
619 } |
|
620 else |
|
621 { |
|
622 IRLOG_WARNING( "CIRIcyFlowReader::ExtractMetaDataL - \"StreamUrl\" end was not found" ); |
|
623 } |
|
624 } |
|
625 else |
|
626 { |
|
627 IRLOG_WARNING( "CIRIcyFlowReader::ExtractMetaDataL - \"StreamUrl\" was not found" ); |
|
628 } |
|
629 |
|
630 iDataObserver.MetadataReceived( *iMetaData ); |
|
631 |
|
632 IRLOG_INFO4( "CIRIcyFlowReader::ExtractMetaDataL - Exit (artist=%S, song=%S, streamUrl=%S)", &iMetaData->Artist(), &iMetaData->Song(), &iMetaData->StreamUrl() ); |
|
633 } |
|
634 |
|
635 // --------------------------------------------------------------------------- |
|
636 // CIRIcyFlowReader::DecodeMetadataStringLC |
|
637 // --------------------------------------------------------------------------- |
|
638 // |
|
639 HBufC* CIRIcyFlowReader::DecodeMetadataStringLC( const TDesC8& aString ) const |
|
640 { |
|
641 IRLOG_DEBUG( "CIRIcyFlowReader::DecodeMetadataStringLC" ); |
|
642 HBufC* decodedString = NULL; |
|
643 if ( IsUtf8Encoded( aString ) ) |
|
644 { |
|
645 TRAPD( err, |
|
646 IRLOG_DEBUG( "CIRIcyFlowReader::DecodeMetadataStringLC - String is UTF-8 encoded" ); |
|
647 decodedString = CnvUtfConverter::ConvertToUnicodeFromUtf8L( aString ); |
|
648 ) |
|
649 if ( err != KErrNone ) |
|
650 { |
|
651 IRLOG_ERROR2( "CIRIcyFlowReader::DecodeMetadataStringLC - UTF-8 conversion failed, err=%d", err ); |
|
652 decodedString = HBufC::NewL( aString.Length() ); |
|
653 decodedString->Des().Copy( aString ); // 8 bit to 16 bit descriptor conversion (ISO-8859-1). |
|
654 } |
|
655 } |
|
656 else |
|
657 { |
|
658 decodedString = HBufC::NewL( aString.Length() ); |
|
659 decodedString->Des().Copy( aString ); // 8 bit to 16 bit descriptor conversion (ISO-8859-1). |
|
660 } |
|
661 CleanupStack::PushL( decodedString ); |
|
662 IRLOG_DEBUG2( "CIRIcyFlowReader::DecodeMetadataStringLC - Returning %S", decodedString ); |
|
663 return decodedString; |
|
664 } |
|
665 |
|
666 // --------------------------------------------------------------------------- |
|
667 // CIRIcyFlowReader::IsUtf8Encoded |
|
668 // --------------------------------------------------------------------------- |
|
669 // |
|
670 TBool CIRIcyFlowReader::IsUtf8Encoded( const TDesC8& aData ) const |
|
671 { |
|
672 IRLOG_DEBUG( "CIRIcyFlowReader::IsUtf8Encoded" ); |
|
673 TBool foundUtf8( EFalse ); |
|
674 |
|
675 for ( TInt i(0); i + 1 < aData.Length() && !foundUtf8; i++ ) |
|
676 { |
|
677 if ( ( aData[i] & KIRUtf8_2B1stByteMask ) == KIRUtf8_2B1stBytePrefix ) |
|
678 { |
|
679 // Two-byte presentation: 110yyyyy 10zzzzzz |
|
680 if ( ( aData[i + 1] & KIRUtf8FollowingByteMask ) == KIRUtf8FollowingBytePrefix ) |
|
681 { |
|
682 foundUtf8 = ETrue; |
|
683 } |
|
684 } |
|
685 else if ( ( aData[i] & KIRUtf8_3B1stByteMask ) == KIRUtf8_3B1stBytePrefix && |
|
686 i + 2 < aData.Length() ) |
|
687 { |
|
688 // Three-byte presentation: 1110xxxx 10yyyyyy 10zzzzzz |
|
689 if ( ( aData[i + 1] & KIRUtf8FollowingByteMask ) == KIRUtf8FollowingBytePrefix && |
|
690 ( aData[i + 2] & KIRUtf8FollowingByteMask ) == KIRUtf8FollowingBytePrefix ) |
|
691 { |
|
692 foundUtf8 = ETrue; |
|
693 } |
|
694 } |
|
695 else if ( ( aData[i] & KIRUtf8_4B1stByteMask ) == KIRUtf8_4B1stBytePrefix && |
|
696 i + KThree < aData.Length() ) |
|
697 { |
|
698 // Four-byte presentation: 11110www 10xxxxxx 10yyyyyy 10zzzzzz |
|
699 if ( ( aData[i + 1] & KIRUtf8FollowingByteMask ) == KIRUtf8FollowingBytePrefix && |
|
700 ( aData[i + 2] & KIRUtf8FollowingByteMask ) == KIRUtf8FollowingBytePrefix && |
|
701 ( aData[i + KThree] & KIRUtf8FollowingByteMask ) == KIRUtf8FollowingBytePrefix ) |
|
702 { |
|
703 foundUtf8 = ETrue; |
|
704 } |
|
705 } |
|
706 else |
|
707 { |
|
708 // NOP |
|
709 } |
|
710 } |
|
711 IRLOG_DEBUG2( "CIRIcyFlowReader::IsUtf8Encoded - Returning %d", foundUtf8 ); |
|
712 return foundUtf8; |
|
713 } |
|
714 |
|