00001 /* 00002 !concept {:name => "Tracking music playback on Symbian"} 00003 */ 00004 00005 /** 00006 00007 Music artist and track logging 00008 00009 References: 00010 00011 Sample source code for example in the [Music Player Remote https://www.iyouit.eu/portal/Software.aspx] PyS60 extension. 00012 00013 As for APIs, see the [Media Player Engine API http://wiki.forum.nokia.com/index.php/Media_Player_Engine_API]. Related, possibly interesting API: [Audio Metadata Reader API http://wiki.forum.nokia.com/index.php/Audio_Metadata_Reader_API]. [Music Player Remote Control API http://wiki.forum.nokia.com/index.php/Music_Player_Remote_Control_API] sounds highly related despite the name, but do note [this http://wiki.forum.nokia.com/index.php/KIS001005_-_Music_Player_Remote_Control_API_does_not_work_in_all_S60_3rd_Edition%2C_FP1_and_FP2_devices] issue. For more recent devices see [MPX Playback Utility API http://wiki.forum.nokia.com/index.php/TSS001652_-_Using_MPX_Playback_Utility_API]. 00014 00015 */ 00016 00017 #include "epoc-music.hpp" 00018 00019 #include "er_errors.h" 00020 #include "ld_logging.h" 00021 #include "sa_sensor_list_log_db.h" 00022 #include "utils_cl2.h" 00023 00024 NONSHARABLE_CLASS(MyTrackInfo) 00025 { 00026 public: 00027 MyTrackInfo() : iUrl(0), iArtist(0), iTitle(0), iAlbum(0) {} 00028 00029 HBufC8* iUrl; // owned 00030 HBufC8* iArtist; // owned 00031 HBufC8* iTitle; // owned 00032 HBufC8* iAlbum; // owned 00033 00034 void Reset() { 00035 DELETE_Z(iUrl); 00036 DELETE_Z(iTitle); 00037 DELETE_Z(iArtist); 00038 DELETE_Z(iAlbum); 00039 } 00040 00041 ~MyTrackInfo() { Reset(); } 00042 00043 int Cmp(const MyTrackInfo& another) const; 00044 }; 00045 00046 static int CmpHBufC8(const HBufC8* a, const HBufC8* b) 00047 { 00048 if (!a && !b) return 0; 00049 if (a && !b) return 1; 00050 if (!a && b) return -1; 00051 return (*a).Compare(*b); 00052 } 00053 00054 int MyTrackInfo::Cmp(const MyTrackInfo& another) const 00055 { 00056 return CmpHBufC8(iUrl, another.iUrl) || 00057 CmpHBufC8(iTitle, another.iTitle) || 00058 CmpHBufC8(iArtist, another.iArtist) || 00059 CmpHBufC8(iAlbum, another.iAlbum); 00060 } 00061 00062 CSensor_music* CSensor_music::NewLC(ac_AppContext* aAppContext) 00063 { 00064 CSensor_music* obj = new (ELeave) CSensor_music(aAppContext); 00065 CleanupStack::PushL(obj); 00066 obj->ConstructL(); 00067 return obj; 00068 } 00069 00070 CSensor_music* CSensor_music::NewL(ac_AppContext* aAppContext) 00071 { 00072 CSensor_music* obj = CSensor_music::NewLC(aAppContext); 00073 CleanupStack::Pop(obj); 00074 return obj; 00075 } 00076 00077 CSensor_music::~CSensor_music() 00078 { 00079 CloseSession(); 00080 delete iTrackInfo; 00081 delete iOldTrackInfo; 00082 } 00083 00084 void CSensor_music::CloseSession() 00085 { 00086 if (iPlaybackUtility) { 00087 //TRAP_IGNORE(iPlaybackUtility->CommandL(EPbCmdClose)); 00088 //iPlaybackUtility->CancelRequest(); 00089 iPlaybackUtility->Close(); 00090 // We appear to be getting USER 42 if we delete iPlaybackUtility. 00091 // Hopefully Close() is enough. 00092 //delete iPlaybackUtility; // not 00093 iPlaybackUtility = NULL; 00094 } 00095 } 00096 00097 CSensor_music::CSensor_music(ac_AppContext* aAppContext) : 00098 iAppContext(aAppContext) 00099 { 00100 } 00101 00102 void CSensor_music::ConstructL() 00103 { 00104 iTrackInfo = new (ELeave) MyTrackInfo; 00105 iOldTrackInfo = new (ELeave) MyTrackInfo; 00106 00107 iPlaybackUtility = MMPXPlaybackUtility::NewL(KPbModeActivePlayer, this); 00108 00109 if ((iPlaybackUtility->StateL() != EPbStateNotInitialised) && 00110 (iPlaybackUtility->StateL() != EPbStateInitialising)) { 00111 // Request info for anything already playing. 00112 RequestMediaL(); 00113 } 00114 } 00115 00116 #include <mpxmessagegeneraldefs.h> 00117 #include <mpxplaybackmessage.h> 00118 00119 #include <glib/gprintf.h> 00120 00121 void CSensor_music::HandlePlaybackMessage(CMPXMessage* aMessage, TInt errCode) 00122 { 00123 if (errCode) { 00124 guilogf("music: error %d", errCode); 00125 er_log_symbian(0, errCode, "INACTIVATE: music data fetch error"); 00126 CloseSession(); 00127 return; 00128 } 00129 00130 int eventId = -1; // logged 00131 const char* detail = NULL; // logged 00132 #define DETAILBUFSIZE 50 00133 char detailBuf[DETAILBUFSIZE]; 00134 #define DETAILFMT(f...) g_snprintf(detailBuf, DETAILBUFSIZE, f) 00135 00136 TMPXMessageId msgId(*aMessage->Value<TMPXMessageId>(KMPXMessageGeneralId)); 00137 //guilogf("music: message ID %d", msgId); 00138 if (msgId != KMPXMessageGeneral) { 00139 return; 00140 } else { 00141 eventId = aMessage->ValueTObjectL<TInt>(KMPXMessageGeneralEvent); 00142 //guilogf("music: event ID %d", eventId); 00143 switch (eventId) 00144 { 00145 case TMPXPlaybackMessage::ENoEvent: 00146 { 00147 guilogf("music: ENoEvent"); 00148 break; 00149 } 00150 case TMPXPlaybackMessage::EError: 00151 { 00152 guilogf("music: EError"); 00153 break; 00154 } 00155 case TMPXPlaybackMessage::ECommandReceived: /* seen often */ 00156 { 00157 //guilogf("music: ECommandReceived"); 00158 break; 00159 } 00160 case TMPXPlaybackMessage::ECommandComplete: 00161 { 00162 guilogf("music: ECommandComplete"); 00163 break; 00164 } 00165 case TMPXPlaybackMessage::EPropertyChanged: /* seen often */ 00166 { 00167 TMPXPlaybackProperty propId(aMessage->ValueTObjectL<TMPXPlaybackProperty>(KMPXMessageGeneralType)); 00168 TInt value(aMessage->ValueTObjectL<TInt>(KMPXMessageGeneralData)); 00169 const char* propStr; 00170 switch (propId) 00171 { 00172 case EPbPropertyVolume: 00173 { 00174 propStr = "EPbPropertyVolume"; 00175 break; 00176 } 00177 case EPbPropertyMaxVolume: 00178 { 00179 propStr = "EPbPropertyMaxVolume"; 00180 break; 00181 } 00182 case EPbPropertyVolumeRamp: 00183 { 00184 propStr = "EPbPropertyVolumeRamp"; 00185 break; 00186 } 00187 case EPbPropertyMute: 00188 { 00189 propStr = "EPbPropertyMute"; 00190 break; 00191 } 00192 case EPbPropertyBalance: 00193 { 00194 propStr = "EPbPropertyBalance"; 00195 break; 00196 } 00197 case EPbPropertyEmbeddedMode: 00198 { 00199 propStr = "EPbPropertyEmbeddedMode"; 00200 break; 00201 } 00202 case EPbPropertyCrossFade: 00203 { 00204 propStr = "EPbPropertyCrossFade"; 00205 break; 00206 } 00207 case EPbPropertyRandomMode: 00208 { 00209 propStr = "EPbPropertyRandomMode"; 00210 break; 00211 } 00212 case EPbPropertyRepeatMode: 00213 { 00214 propStr = "EPbPropertyRepeatMode"; 00215 break; 00216 } 00217 case EPbPropertyAccessPoint: 00218 { 00219 propStr = "EPbPropertyAccessPoint"; 00220 break; 00221 } 00222 case EPbPropertyPosition: 00223 { 00224 #if 0 00225 propStr = "EPbPropertyPosition"; 00226 break; 00227 #else 00228 // These events are very frequent as track position 00229 // progress is frequently reported. 00230 return; 00231 #endif 00232 } 00233 case EPbPropertyDuration: 00234 { 00235 propStr = "EPbPropertyDuration"; 00236 break; 00237 } 00238 case EPbPropertySongValid: 00239 { 00240 propStr = "EPbPropertySongValid"; 00241 break; 00242 } 00243 case EPbPropertyRemote: 00244 { 00245 propStr = "EPbPropertyRemote"; 00246 break; 00247 } 00248 case EPbPropertySupportedFeatures: 00249 { 00250 propStr = "EPbPropertySupportedFeatures"; 00251 break; 00252 } 00253 default: 00254 { 00255 propStr = "<unknown>"; 00256 break; 00257 } 00258 } 00259 DETAILFMT("{property: %d, value: %d}", propId, value); 00260 detail = detailBuf; 00261 guilogf("music: EPropertyChanged: '%s' = %d", propStr, value); 00262 break; 00263 } 00264 case TMPXPlaybackMessage::EStateChanged: /* seen often */ 00265 { 00266 TInt stateId(aMessage->ValueTObjectL<TMPXPlaybackState>(KMPXMessageGeneralType)); 00267 if (stateId == iOldPbState) 00268 // We seem to get repeats, avoid that. 00269 return; 00270 iOldPbState = stateId; 00271 const char* stateStr; 00272 switch (stateId) 00273 { 00274 case EPbStateNotInitialised: 00275 { 00276 stateStr = "EPbStateNotInitialised"; 00277 break; 00278 } 00279 case EPbStateInitialising: 00280 { 00281 stateStr = "EPbStateInitialising"; 00282 break; 00283 } 00284 case EPbStatePlaying: 00285 { 00286 stateStr = "EPbStatePlaying"; 00287 break; 00288 } 00289 case EPbStatePaused: 00290 { 00291 stateStr = "EPbStatePaused"; 00292 break; 00293 } 00294 case EPbStateStopped: 00295 { 00296 stateStr = "EPbStateStopped"; 00297 break; 00298 } 00299 case EPbStateSeekingForward: 00300 { 00301 stateStr = "EPbStateSeekingForward"; 00302 break; 00303 } 00304 case EPbStateSeekingBackward: 00305 { 00306 stateStr = "EPbStateSeekingBackward"; 00307 break; 00308 } 00309 case EPbStateShuttingDown: 00310 { 00311 stateStr = "EPbStateShuttingDown"; 00312 break; 00313 } 00314 case EPbStateBuffering: 00315 { 00316 stateStr = "EPbStateBuffering"; 00317 break; 00318 } 00319 case EPbStateDownloading: 00320 { 00321 stateStr = "EPbStateDownloading"; 00322 break; 00323 } 00324 default: 00325 { 00326 stateStr = "<unknown>"; 00327 break; 00328 } 00329 } 00330 DETAILFMT("{state: %d}", stateId); 00331 detail = detailBuf; 00332 //guilogf("music: EStateChanged %d", stateId); 00333 guilogf("music: playback state %s (%d)", stateStr, stateId); 00334 break; 00335 } 00336 case TMPXPlaybackMessage::ESongCorrupt: 00337 { 00338 guilogf("music: ESongCorrupt"); 00339 break; 00340 } 00341 case TMPXPlaybackMessage::ESongContainerChanged: 00342 { 00343 guilogf("music: ESongContainerChanged"); 00344 break; 00345 } 00346 case TMPXPlaybackMessage::EInitializeComplete: /* seen often */ 00347 { 00348 guilogf("music: EInitializeComplete"); 00349 break; 00350 } 00351 case TMPXPlaybackMessage::ESongChanged: 00352 { 00353 guilogf("music: ESongChanged"); 00354 break; 00355 } 00356 case TMPXPlaybackMessage::EPlayerChanged: 00357 { 00358 guilogf("music: EPlayerChanged"); 00359 break; 00360 } 00361 case TMPXPlaybackMessage::EActivePlayerChanged: 00362 { 00363 guilogf("music: EActivePlayerChanged"); 00364 break; 00365 } 00366 case TMPXPlaybackMessage::ESubPlayersChanged: 00367 { 00368 guilogf("music: ESubPlayersChanged"); 00369 break; 00370 } 00371 case TMPXPlaybackMessage::EPlayerSelectionChanged: 00372 { 00373 guilogf("music: EPlayerSelectionChanged"); 00374 break; 00375 } 00376 case TMPXPlaybackMessage::EDownloadStarted: 00377 { 00378 guilogf("music: EDownloadStarted"); 00379 break; 00380 } 00381 case TMPXPlaybackMessage::EDownloadUpdated: 00382 { 00383 guilogf("music: EDownloadUpdated"); 00384 break; 00385 } 00386 case TMPXPlaybackMessage::EDownloadComplete: 00387 { 00388 guilogf("music: EDownloadComplete"); 00389 break; 00390 } 00391 case TMPXPlaybackMessage::EDownloadPositionChanged: 00392 { 00393 guilogf("music: EDownloadPositionChanged"); 00394 break; 00395 } 00396 case TMPXPlaybackMessage::EDownloadStateChanged: 00397 { 00398 guilogf("music: EDownloadStateChanged"); 00399 break; 00400 } 00401 case TMPXPlaybackMessage::EDownloadCmdPauseDownload: 00402 { 00403 guilogf("music: EDownloadCmdPauseDownload"); 00404 break; 00405 } 00406 case TMPXPlaybackMessage::EDownloadCmdResumeDownload: 00407 { 00408 guilogf("music: EDownloadCmdResumeDownload"); 00409 break; 00410 } 00411 case TMPXPlaybackMessage::EDownloadCmdCancelDownload: 00412 { 00413 guilogf("music: EDownloadCmdCancelDownload"); 00414 break; 00415 } 00416 case TMPXPlaybackMessage::EAccessoryChanged: 00417 { 00418 guilogf("music: EAccessoryChanged"); 00419 break; 00420 } 00421 case TMPXPlaybackMessage::EMediaChanged: /* seen often */ 00422 { 00423 guilogf("music: EMediaChanged"); 00424 RequestMediaL(); 00425 break; 00426 } 00427 case TMPXPlaybackMessage::ESkipping: 00428 { 00429 guilogf("music: ESkipping"); 00430 break; 00431 } 00432 case TMPXPlaybackMessage::ESkipEnd: 00433 { 00434 guilogf("music: ESkipEnd"); 00435 break; 00436 } 00437 case TMPXPlaybackMessage::EPlayerUnavailable: 00438 { 00439 guilogf("music: EPlayerUnavailable"); 00440 break; 00441 } 00442 case TMPXPlaybackMessage::EPlaylistUpdated: 00443 { 00444 guilogf("music: EPlaylistUpdated"); 00445 RequestMediaL(); 00446 break; 00447 } 00448 case TMPXPlaybackMessage::EReachedEndOfPlaylist: 00449 { 00450 guilogf("music: EReachedEndOfPlaylist"); 00451 break; 00452 } 00453 default: 00454 { 00455 guilogf("music: unknown event"); 00456 break; 00457 } 00458 } 00459 00460 log_db_log_musicplayer(GetLogDb(), eventId, detail, NULL); 00461 } 00462 } 00463 00464 #include <mpxmediageneraldefs.h> 00465 #include <mpxmediamusicdefs.h> 00466 00467 // adapted from code in Music Player Remote Python extension 00468 void CSensor_music::RequestMediaL() 00469 { 00470 MMPXSource* source(iPlaybackUtility->Source()); 00471 if (source) 00472 { 00473 RArray<TMPXAttribute> attrs; 00474 CleanupClosePushL(attrs); 00475 attrs.Append(KMPXMediaGeneralUri); 00476 attrs.Append(KMPXMediaGeneralTitle); 00477 attrs.Append(KMPXMediaMusicArtist); 00478 attrs.Append(KMPXMediaMusicAlbum); 00479 // The two-arg method is deprecated, we assume passing NULL as 00480 // the third arg has the same semantics. The arg is 00481 // CMPXAttributeSpecs* aSpecs (typedef CMPXMedia 00482 // CMPXAttributeSpecs). 00483 CMPXAttributeSpecs* specs = NULL; 00484 source->MediaL(attrs.Array(), *this, specs); 00485 CleanupStack::PopAndDestroy(&attrs); 00486 } 00487 } 00488 00489 #define SWAP(t,x,y) { t _tmp = x; x = y; y = _tmp; } 00490 00491 // adapted from code in Music Player Remote Python extension 00492 // also see mpxmedia.h 00493 void CSensor_music::HandleMediaL(const CMPXMedia& aMedia, 00494 TInt errCode) 00495 { 00496 //guilogf("HandleMediaL (%d)", errCode); 00497 00498 if (errCode) 00499 return; 00500 00501 SWAP(MyTrackInfo*, iOldTrackInfo, iTrackInfo); // current becomes old 00502 00503 iTrackInfo->Reset(); // clear space for new data 00504 00505 HBufC8*& iUrl = iTrackInfo->iUrl; 00506 HBufC8*& iArtist = iTrackInfo->iArtist; 00507 HBufC8*& iTitle = iTrackInfo->iTitle; 00508 HBufC8*& iAlbum = iTrackInfo->iAlbum; 00509 00510 if (aMedia.IsSupported(KMPXMediaGeneralUri)) { 00511 const TDesC& text = aMedia.ValueText(KMPXMediaGeneralUri); 00512 /* 00513 TParsePtrC filePath(text); 00514 iUrl = ConvToUtf8ZL(filePath.Name()); 00515 */ 00516 iUrl = ConvToUtf8ZL(text); 00517 } 00518 if (aMedia.IsSupported(KMPXMediaGeneralTitle)) { 00519 iTitle = ConvToUtf8ZL(aMedia.ValueText(KMPXMediaGeneralTitle)); 00520 } 00521 if (aMedia.IsSupported(KMPXMediaMusicArtist)) { 00522 iArtist = ConvToUtf8ZL(aMedia.ValueText(KMPXMediaMusicArtist)); 00523 } 00524 if (aMedia.IsSupported(KMPXMediaMusicAlbum)) { 00525 iAlbum = ConvToUtf8ZL(aMedia.ValueText(KMPXMediaMusicAlbum)); 00526 } 00527 00528 if (iTrackInfo->Cmp(*iOldTrackInfo) == 0) 00529 return; // no duplicates 00530 00531 const char* url = (iUrl ? (const char*)(iUrl->Ptr()) : NULL); 00532 const char* title = (iTitle ? (const char*)(iTitle->Ptr()) : NULL); 00533 const char* artist = (iArtist ? (const char*)(iArtist->Ptr()) : NULL); 00534 const char* album = (iAlbum ? (const char*)(iAlbum->Ptr()) : NULL); 00535 00536 log_db_log_musictrack(GetLogDb(), url, title, artist, album, NULL); 00537 00538 if (url) 00539 guilogf("music: URL '%s'", url); 00540 if (title) 00541 guilogf("music: title '%s'", title); 00542 if (artist) 00543 guilogf("music: artist '%s'", artist); 00544 if (album) 00545 guilogf("music: album '%s'", album); 00546 } 00547 00548 /** 00549 00550 Copyright 2011 Helsinki Institute for Information Technology (HIIT) 00551 and the authors. All rights reserved. 00552 00553 Authors: Tero Hasu <tero.hasu@hut.fi> 00554 00555 Permission is hereby granted, free of charge, to any person 00556 obtaining a copy of this software and associated documentation files 00557 (the "Software"), to deal in the Software without restriction, 00558 including without limitation the rights to use, copy, modify, merge, 00559 publish, distribute, sublicense, and/or sell copies of the Software, 00560 and to permit persons to whom the Software is furnished to do so, 00561 subject to the following conditions: 00562 00563 The above copyright notice and this permission notice shall be 00564 included in all copies or substantial portions of the Software. 00565 00566 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 00567 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00568 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 00569 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 00570 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 00571 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 00572 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 00573 SOFTWARE. 00574 00575 **/
ContextLogger2—ContextLogger2 Logger Daemon Internals—Generated on Mon May 2 13:49:52 2011 by Doxygen 1.6.1