Skip to content
This repository
Browse code

Merge pull request #2130 from ulion/ios_play_background

ios support play background and native media control bar
  • Loading branch information...
commit 643ba7a8d7c875115ea5576021f3d78e4b72f45e 2 parents 2eed73d + ad19633
Memphiz authored March 08, 2013
8  XBMC-IOS.xcodeproj/project.pbxproj
@@ -52,6 +52,8 @@
52 52
 		7CC3106F162AAFE3003E7579 /* EdenVideoArtUpdater.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CC3106D162AAFE3003E7579 /* EdenVideoArtUpdater.cpp */; };
53 53
 		7CCFD9991514950700211D82 /* PCMCodec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CCFD9971514950700211D82 /* PCMCodec.cpp */; };
54 54
 		7CEE2E7F13D6B7D4000ABF2A /* TimeSmoother.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CEE2E7D13D6B7D4000ABF2A /* TimeSmoother.cpp */; };
  55
+		8253940516B25C2C00A85509 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8253940416B25C2C00A85509 /* AVFoundation.framework */; };
  56
+		8253940716B25C2C00A85509 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8253940616B25C2C00A85509 /* MediaPlayer.framework */; };
55 57
 		C80711AD135DB85F002F601B /* InputOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C80711AB135DB85F002F601B /* InputOperations.cpp */; };
56 58
 		C8B929D01573557B00284190 /* Epg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929C31573557B00284190 /* Epg.cpp */; };
57 59
 		C8B929D11573557B00284190 /* EpgContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929C51573557B00284190 /* EpgContainer.cpp */; };
@@ -1132,6 +1134,8 @@
1132 1134
 		7CCFD9981514950700211D82 /* PCMCodec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PCMCodec.h; sourceTree = "<group>"; };
1133 1135
 		7CEE2E7D13D6B7D4000ABF2A /* TimeSmoother.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimeSmoother.cpp; sourceTree = "<group>"; };
1134 1136
 		7CEE2E7E13D6B7D4000ABF2A /* TimeSmoother.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimeSmoother.h; sourceTree = "<group>"; };
  1137
+		8253940416B25C2C00A85509 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
  1138
+		8253940616B25C2C00A85509 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; };
1135 1139
 		83D619BB13C0D25300418A0F /* README.ios */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.ios; sourceTree = "<group>"; };
1136 1140
 		8D576316048677EA00EA77CD /* XBMC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XBMC.app; sourceTree = BUILT_PRODUCTS_DIR; };
1137 1141
 		C80711AB135DB85F002F601B /* InputOperations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InputOperations.cpp; sourceTree = "<group>"; };
@@ -3285,6 +3289,8 @@
3285 3289
 				F56C8C12131F4811000AD0F6 /* librtv.a in Frameworks */,
3286 3290
 				F56C8C14131F4811000AD0F6 /* libxdaap.a in Frameworks */,
3287 3291
 				18404DD31396C3D200863BBA /* SlingboxLib.a in Frameworks */,
  3292
+				8253940516B25C2C00A85509 /* AVFoundation.framework in Frameworks */,
  3293
+				8253940716B25C2C00A85509 /* MediaPlayer.framework in Frameworks */,
3288 3294
 			);
3289 3295
 			runOnlyForDeploymentPostprocessing = 0;
3290 3296
 		};
@@ -6435,6 +6441,7 @@
6435 6441
 				F589AE481288E07D00D8079E /* libxml2.dylib */,
6436 6442
 				F589AE461288E07400D8079E /* libz.dylib */,
6437 6443
 				F56B14A412CAF523009B4C96 /* AudioToolbox.framework */,
  6444
+				8253940416B25C2C00A85509 /* AVFoundation.framework */,
6438 6445
 				4D5D2E1D1301758F006ABC13 /* CFNetwork.framework */,
6439 6446
 				3255316512B2D02400837CD2 /* CoreAudio.framework */,
6440 6447
 				F56B143312CAF279009B4C96 /* CoreVideo.framework */,
@@ -6442,6 +6449,7 @@
6442 6449
 				F56B15D412CD67A9009B4C96 /* CoreGraphics.framework */,
6443 6450
 				F5899DCD1287212700D8079E /* Foundation.framework */,
6444 6451
 				F56B160712CD6999009B4C96 /* ImageIO.framework */,
  6452
+				8253940616B25C2C00A85509 /* MediaPlayer.framework */,
6445 6453
 				F5899DCB1287212700D8079E /* OpenGLES.framework */,
6446 6454
 				F5899DCA1287212700D8079E /* QuartzCore.framework */,
6447 6455
 				F5899DCC1287212700D8079E /* UIKit.framework */,
18  xbmc/Application.cpp
@@ -404,6 +404,9 @@ CApplication::CApplication(void)
404 404
   XInitThreads();
405 405
 #endif
406 406
 
  407
+  // we start in frontend
  408
+  m_bInBackground = false;
  409
+
407 410
   /* for now always keep this around */
408 411
 #ifdef HAS_KARAOKE
409 412
   m_pKaraokeMgr = new CKaraokeLyricsManager();
@@ -2260,8 +2263,8 @@ void CApplication::NewFrame()
2260 2263
 
2261 2264
 void CApplication::Render()
2262 2265
 {
2263  
-  // do not render if we are stopped
2264  
-  if (m_bStop)
  2266
+  // do not render if we are stopped or in background
  2267
+  if (m_bStop || m_bInBackground)
2265 2268
     return;
2266 2269
 
2267 2270
   if (!m_AppActive && !m_bStop && (!IsPlayingVideo() || IsPaused()))
@@ -4610,6 +4613,8 @@ bool CApplication::WakeUpScreenSaver(bool bPowerOffKeyPressed /* = false */)
4610 4613
 
4611 4614
 void CApplication::CheckScreenSaverAndDPMS()
4612 4615
 {
  4616
+  if (m_bInBackground)
  4617
+    return;
4613 4618
   if (!m_dpmsIsActive)
4614 4619
     g_Windowing.ResetOSScreensaver();
4615 4620
 
@@ -4702,6 +4707,15 @@ void CApplication::ActivateScreenSaver(bool forceType /*= false */)
4702 4707
     g_windowManager.ActivateWindow(WINDOW_SCREENSAVER);
4703 4708
 }
4704 4709
 
  4710
+void CApplication::SetInBackground(bool background)
  4711
+{
  4712
+  if (!background)
  4713
+  {
  4714
+    ResetScreenSaverTimer();
  4715
+  }
  4716
+  m_bInBackground = background;
  4717
+}
  4718
+
4705 4719
 void CApplication::CheckShutdown()
4706 4720
 {
4707 4721
   // first check if we should reset the timer
4  xbmc/Application.h
@@ -298,6 +298,10 @@ class CApplication : public CXBApplicationEx, public IPlayerCallback, public IMs
298 298
 
299 299
   bool m_bPlaybackStarting;
300 300
 
  301
+  bool m_bInBackground;
  302
+  inline bool IsInBackground() { return m_bInBackground; };
  303
+  void SetInBackground(bool background);
  304
+
301 305
   CKaraokeLyricsManager* m_pKaraokeMgr;
302 306
 
303 307
   PLAYERCOREID m_eForcedNextPlayer;
21  xbmc/ApplicationMessenger.cpp
@@ -512,6 +512,15 @@ void CApplicationMessenger::ProcessMessage(ThreadMessage *pMsg)
512 512
       }
513 513
       break;
514 514
 
  515
+    case TMSG_MEDIA_PAUSE_IF_PLAYING:
  516
+      if (g_application.IsPlaying() && !g_application.IsPaused())
  517
+      {
  518
+        g_application.ResetScreenSaver();
  519
+        g_application.WakeUpScreenSaverAndDPMS();
  520
+        g_application.m_pPlayer->Pause();
  521
+      }
  522
+      break;
  523
+
515 524
     case TMSG_SWITCHTOFULLSCREEN:
516 525
       if( g_windowManager.GetActiveWindow() != WINDOW_FULLSCREEN_VIDEO )
517 526
         g_application.SwitchToFullScreen();
@@ -929,6 +938,18 @@ void CApplicationMessenger::MediaPause()
929 938
   SendMessage(tMsg, true);
930 939
 }
931 940
 
  941
+void CApplicationMessenger::MediaUnPause()
  942
+{
  943
+  ThreadMessage tMsg = {TMSG_MEDIA_UNPAUSE};
  944
+  SendMessage(tMsg, true);
  945
+}
  946
+
  947
+void CApplicationMessenger::MediaPauseIfPlaying()
  948
+{
  949
+  ThreadMessage tMsg = {TMSG_MEDIA_PAUSE_IF_PLAYING};
  950
+  SendMessage(tMsg, true);
  951
+}
  952
+
932 953
 void CApplicationMessenger::MediaRestart(bool bWait)
933 954
 {
934 955
   ThreadMessage tMsg = {TMSG_MEDIA_RESTART};
4  xbmc/ApplicationMessenger.h
@@ -52,9 +52,11 @@ namespace MUSIC_INFO
52 52
 
53 53
 #define TMSG_MEDIA_PLAY           200
54 54
 #define TMSG_MEDIA_STOP           201
  55
+// the PAUSE is indeed a PLAYPAUSE
55 56
 #define TMSG_MEDIA_PAUSE          202
56 57
 #define TMSG_MEDIA_RESTART        203
57 58
 #define TMSG_MEDIA_UNPAUSE        204
  59
+#define TMSG_MEDIA_PAUSE_IF_PLAYING   205
58 60
 
59 61
 #define TMSG_PLAYLISTPLAYER_PLAY  210
60 62
 #define TMSG_PLAYLISTPLAYER_NEXT  211
@@ -168,6 +170,8 @@ class CApplicationMessenger
168 170
   void MediaPlay(int playlistid, int song = -1);
169 171
   void MediaStop(bool bWait = true, int playlistid = -1);
170 172
   void MediaPause();
  173
+  void MediaUnPause();
  174
+  void MediaPauseIfPlaying();
171 175
   void MediaRestart(bool bWait);
172 176
 
173 177
   void PlayListPlayerPlay();
4  xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAE.cpp
@@ -488,7 +488,11 @@ IAEStream* CCoreAudioAE::MakeStream(enum AEDataFormat dataFormat,
488 488
   // if we are suspended we don't
489 489
   // want anyone to mess with us
490 490
   if (m_isSuspended && !m_softSuspend)
  491
+#if defined(TARGET_DARWIN_IOS) && !defined(TARGET_DARWIN_IOS_ATV)
  492
+    Resume();
  493
+#else
491 494
     return NULL;
  495
+#endif
492 496
 
493 497
   CAEChannelInfo channelInfo(channelLayout);
494 498
   CLog::Log(LOGINFO, "CCoreAudioAE::MakeStream - %s, %u, %u, %s",
19  xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAEHALIOS.cpp
@@ -32,7 +32,8 @@
32 32
 
33 33
 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
34 34
 
35  
-#define BUFFERED_FRAMES 1024
  35
+// use the maximum frames per slice allows audio play when the screen is locked
  36
+#define BUFFERED_FRAMES 4096
36 37
 
37 38
 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
38 39
 // CIOSCoreAudioHardware
@@ -634,6 +635,10 @@ bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format, boo
634 635
   if (!m_audioUnit->Open(m_audioGraph, kAudioUnitType_Output, kAudioUnitSubType_RemoteIO, kAudioUnitManufacturer_Apple))
635 636
     return false;
636 637
 
  638
+  UInt32 bufferFrames = m_audioUnit->GetBufferFrameSize();
  639
+
  640
+  m_audioUnit->SetMaxFramesPerSlice(bufferFrames);
  641
+
637 642
   if (!m_audioUnit->EnableInputOuput())
638 643
     return false;
639 644
 
@@ -662,6 +667,8 @@ bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format, boo
662 667
     if (!m_mixerUnit->Open(m_audioGraph, kAudioUnitType_Mixer, kAudioUnitSubType_MultiChannelMixer, kAudioUnitManufacturer_Apple))
663 668
       return false;
664 669
 
  670
+    m_mixerUnit->SetMaxFramesPerSlice(bufferFrames);
  671
+
665 672
     // set number of input buses
666 673
     if (!m_mixerUnit->SetInputBusCount(MAX_CONNECTION_LIMIT))
667 674
       return false;
@@ -698,6 +705,8 @@ bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format, boo
698 705
     if (!m_inputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple))
699 706
       return false;
700 707
 
  708
+    m_inputUnit->SetMaxFramesPerSlice(bufferFrames);
  709
+
701 710
     if (!m_inputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus))
702 711
       return false;
703 712
 
@@ -773,12 +782,6 @@ bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format, boo
773 782
 
774 783
   SetCurrentVolume(initVolume);
775 784
 
776  
-  UInt32 bufferFrames = m_audioUnit->GetBufferFrameSize();
777  
-
778  
-  m_audioUnit->SetMaxFramesPerSlice(bufferFrames);
779  
-  if (m_inputUnit)
780  
-    m_inputUnit->SetMaxFramesPerSlice(bufferFrames);
781  
-
782 785
   SetInputSource(pSource);
783 786
 
784 787
   ShowGraph();
@@ -985,6 +988,8 @@ CAUOutputDevice *CCoreAudioGraph::CreateUnit(AEAudioFormat &format)
985 988
   CAUOutputDevice *outputUnit = new CAUOutputDevice();
986 989
   if (!outputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple))
987 990
     goto error;
  991
+  
  992
+  outputUnit->SetMaxFramesPerSlice(m_audioUnit->GetBufferFrameSize());
988 993
 
989 994
   m_audioUnit->GetFormatDesc(format, &inputFormat);
990 995
 
4  xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAEStream.cpp
@@ -596,6 +596,10 @@ void CCoreAudioAEStream::Pause()
596 596
 
597 597
 void CCoreAudioAEStream::Resume()
598 598
 {
  599
+#if defined(TARGET_DARWIN_IOS) && !defined(TARGET_DARWIN_IOS_ATV)
  600
+  if (CAEFactory::IsSuspended())
  601
+    CAEFactory::Resume();
  602
+#endif
599 603
   m_paused = false;
600 604
 }
601 605
 
2  xbmc/cores/paplayer/PAPlayer.cpp
@@ -91,8 +91,8 @@ void PAPlayer::SoftStart(bool wait/* = false */)
91 91
     if (si->m_fadeOutTriggered)
92 92
       continue;
93 93
 
94  
-    si->m_stream->FadeVolume(0.0f, 1.0f, FAST_XFADE_TIME);
95 94
     si->m_stream->Resume();
  95
+    si->m_stream->FadeVolume(0.0f, 1.0f, FAST_XFADE_TIME);
96 96
   }
97 97
   
98 98
   if (wait)
31  xbmc/osx/IOSEAGLView.mm
@@ -46,6 +46,7 @@
46 46
 #import "IOSScreenManager.h"
47 47
 #import "AutoPool.h"
48 48
 #import "DarwinUtils.h"
  49
+#import "XBMCDebugHelpers.h"
49 50
 
50 51
 //--------------------------------------------------------------
51 52
 @interface IOSEAGLView (PrivateMethods)
@@ -145,7 +146,7 @@ - (void) setScreen:(UIScreen *)screen withFrameBufferResize:(BOOL)resize;
145 146
 //--------------------------------------------------------------
146 147
 - (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
147 148
 {
148  
-  //NSLog(@"%s", __PRETTY_FUNCTION__);
  149
+  //PRINT_SIGNATURE();
149 150
   framebufferResizeRequested = FALSE;
150 151
   if ((self = [super initWithFrame:frame]))
151 152
   {
@@ -165,9 +166,9 @@ - (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
165 166
       initWithAPI:kEAGLRenderingAPIOpenGLES2];
166 167
     
167 168
     if (!aContext)
168  
-      NSLog(@"Failed to create ES context");
  169
+      ELOG(@"Failed to create ES context");
169 170
     else if (![EAGLContext setCurrentContext:aContext])
170  
-      NSLog(@"Failed to set ES context current");
  171
+      ELOG(@"Failed to set ES context current");
171 172
     
172 173
     self.context = aContext;
173 174
     [aContext release];
@@ -188,7 +189,7 @@ - (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
188 189
 //--------------------------------------------------------------
189 190
 - (void) dealloc
190 191
 {
191  
-  //NSLog(@"%s", __PRETTY_FUNCTION__);
  192
+  //PRINT_SIGNATURE();
192 193
   [self deleteFramebuffer];    
193 194
   [context release];
194 195
   
@@ -203,7 +204,7 @@ - (EAGLContext *)context
203 204
 //--------------------------------------------------------------
204 205
 - (void)setContext:(EAGLContext *)newContext
205 206
 {
206  
-  //NSLog(@"%s", __PRETTY_FUNCTION__);
  207
+  PRINT_SIGNATURE();
207 208
   if (context != newContext)
208 209
   {
209 210
     [self deleteFramebuffer];
@@ -220,7 +221,7 @@ - (void)createFramebuffer
220 221
 {
221 222
   if (context && !defaultFramebuffer)
222 223
   {
223  
-    //NSLog(@"%s", __PRETTY_FUNCTION__);
  224
+    //PRINT_SIGNATURE();
224 225
     [EAGLContext setCurrentContext:context];
225 226
     
226 227
     // Create default framebuffer object.
@@ -241,7 +242,7 @@ - (void)createFramebuffer
241 242
     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
242 243
     
243 244
     if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
244  
-      NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
  245
+      ELOG(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
245 246
   }
246 247
 }
247 248
 //--------------------------------------------------------------
@@ -249,7 +250,7 @@ - (void) deleteFramebuffer
249 250
 {
250 251
   if (context && !pause)
251 252
   {
252  
-    //NSLog(@"%s", __PRETTY_FUNCTION__);
  253
+    PRINT_SIGNATURE();
253 254
     [EAGLContext setCurrentContext:context];
254 255
     
255 256
     if (defaultFramebuffer)
@@ -311,16 +312,21 @@ - (bool) presentFramebuffer
311 312
 //--------------------------------------------------------------
312 313
 - (void) pauseAnimation
313 314
 {
  315
+  PRINT_SIGNATURE();
314 316
   pause = TRUE;
  317
+  g_application.SetInBackground(true);
315 318
 }
316 319
 //--------------------------------------------------------------
317 320
 - (void) resumeAnimation
318 321
 {
  322
+  PRINT_SIGNATURE();
319 323
   pause = FALSE;
  324
+  g_application.SetInBackground(false);
320 325
 }
321 326
 //--------------------------------------------------------------
322 327
 - (void) startAnimation
323 328
 {
  329
+  PRINT_SIGNATURE();
324 330
 	if (!animating && context)
325 331
 	{
326 332
 		animating = TRUE;
@@ -338,6 +344,7 @@ - (void) startAnimation
338 344
 //--------------------------------------------------------------
339 345
 - (void) stopAnimation
340 346
 {
  347
+  PRINT_SIGNATURE();
341 348
 	if (animating && context)
342 349
 	{
343 350
     [self deinitDisplayLink];
@@ -385,19 +392,19 @@ - (void) runAnimation:(id) arg
385 392
   if (!g_application.Create())
386 393
   {
387 394
     readyToRun = false;
388  
-    NSLog(@"%sUnable to create application", __PRETTY_FUNCTION__);
  395
+    ELOG(@"%sUnable to create application", __PRETTY_FUNCTION__);
389 396
   }
390 397
 
391 398
   if (!g_application.CreateGUI())
392 399
   {
393 400
     readyToRun = false;
394  
-    NSLog(@"%sUnable to create GUI", __PRETTY_FUNCTION__);
  401
+    ELOG(@"%sUnable to create GUI", __PRETTY_FUNCTION__);
395 402
   }
396 403
 
397 404
   if (!g_application.Initialize())
398 405
   {
399 406
     readyToRun = false;
400  
-    NSLog(@"%sUnable to initialize application", __PRETTY_FUNCTION__);
  407
+    ELOG(@"%sUnable to initialize application", __PRETTY_FUNCTION__);
401 408
   }
402 409
   
403 410
   if (readyToRun)
@@ -412,7 +419,7 @@ - (void) runAnimation:(id) arg
412 419
     }
413 420
     catch(...)
414 421
     {
415  
-      NSLog(@"%sException caught on main loop. Exiting", __PRETTY_FUNCTION__);
  422
+      ELOG(@"%sException caught on main loop. Exiting", __PRETTY_FUNCTION__);
416 423
     }
417 424
   }
418 425
 
3  xbmc/osx/ios/XBMCApplication.h
@@ -19,7 +19,8 @@
19 19
  */
20 20
 
21 21
 #import <UIKit/UIKit.h>
  22
+#import <AVFoundation/AVFoundation.h>
22 23
 
23  
-@interface XBMCApplicationDelegate : NSObject <UIApplicationDelegate> {
  24
+@interface XBMCApplicationDelegate : NSObject <UIApplicationDelegate, AVAudioSessionDelegate> {
24 25
 }
25 26
 @end
59  xbmc/osx/ios/XBMCApplication.m
@@ -19,37 +19,53 @@
19 19
  */
20 20
  
21 21
 #import <UIKit/UIKit.h>
  22
+#import <AVFoundation/AVAudioSession.h>
22 23
 
23 24
 #import "XBMCApplication.h"
24 25
 #import "XBMCController.h"
25 26
 #import "IOSScreenManager.h"
  27
+#import "XBMCDebugHelpers.h"
26 28
 
27 29
 @implementation XBMCApplicationDelegate
28 30
 XBMCController *m_xbmcController;  
29 31
 
30 32
 - (void)applicationWillResignActive:(UIApplication *)application
31 33
 {
  34
+  PRINT_SIGNATURE();
  35
+
32 36
   [m_xbmcController pauseAnimation];
  37
+  [m_xbmcController becomeInactive];
33 38
 }
34 39
 
35 40
 - (void)applicationDidBecomeActive:(UIApplication *)application
36 41
 {
  42
+  PRINT_SIGNATURE();
  43
+
37 44
   [m_xbmcController resumeAnimation];
  45
+  [m_xbmcController enterForeground];
38 46
 }
39 47
 
40 48
 - (void)applicationDidEnterBackground:(UIApplication *)application
41 49
 {
42  
-  [m_xbmcController pauseAnimation];
  50
+  PRINT_SIGNATURE();
  51
+
  52
+  if (application.applicationState == UIApplicationStateBackground)
  53
+  {
  54
+    // the app is turn into background, not in by screen lock which has app state inactive.
  55
+    [m_xbmcController enterBackground];
  56
+  }
43 57
 }
44 58
 
45 59
 - (void)applicationWillTerminate:(UIApplication *)application
46 60
 {
47  
-  [m_xbmcController pauseAnimation];
  61
+  PRINT_SIGNATURE();
  62
+
  63
+  [m_xbmcController stopAnimation];
48 64
 }
49 65
 
50 66
 - (void)applicationWillEnterForeground:(UIApplication *)application
51 67
 {
52  
-  [m_xbmcController resumeAnimation];
  68
+  PRINT_SIGNATURE();
53 69
 }
54 70
 
55 71
 - (void)screenDidConnect:(NSNotification *)aNotification
@@ -84,6 +100,8 @@ - (void)registerScreenNotifications:(BOOL)bRegister
84 100
 
85 101
 - (void)applicationDidFinishLaunching:(UIApplication *)application 
86 102
 {
  103
+  PRINT_SIGNATURE();
  104
+
87 105
   [[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];
88 106
   UIScreen *currentScreen = [UIScreen mainScreen];
89 107
 
@@ -91,6 +109,37 @@ - (void)applicationDidFinishLaunching:(UIApplication *)application
91 109
   m_xbmcController.wantsFullScreenLayout = YES;  
92 110
   [m_xbmcController startAnimation];
93 111
   [self registerScreenNotifications:YES];
  112
+
  113
+  NSError *err = nil;
  114
+  if (![[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&err])
  115
+  {
  116
+    ELOG(@"AVAudioSession setCategory failed: %@", err);
  117
+  }
  118
+  err = nil;
  119
+  if (![[AVAudioSession sharedInstance] setActive: YES error: &err])
  120
+  {
  121
+    ELOG(@"AVAudioSession setActive failed: %@", err);
  122
+  }
  123
+  [[AVAudioSession sharedInstance] setDelegate:self];
  124
+}
  125
+
  126
+- (void)beginInterruption
  127
+{
  128
+  PRINT_SIGNATURE();
  129
+  [m_xbmcController beginInterruption];
  130
+}
  131
+- (void)endInterruptionWithFlags:(NSUInteger)flags
  132
+{
  133
+  LOG(@"%s: %d", __PRETTY_FUNCTION__, flags);
  134
+  if (flags & AVAudioSessionInterruptionFlags_ShouldResume)
  135
+  {
  136
+    NSError *err = nil;
  137
+    if (![[AVAudioSession sharedInstance] setActive: YES error: &err])
  138
+    {
  139
+      ELOG(@"AVAudioSession::endInterruption setActive failed: %@", err);
  140
+    }
  141
+    [m_xbmcController endInterruption];
  142
+  }
94 143
 }
95 144
 
96 145
 - (void)dealloc
@@ -123,11 +172,11 @@ int main(int argc, char *argv[]) {
123 172
   } 
124 173
   @catch (id theException) 
125 174
   {
126  
-    NSLog(@"%@", theException);
  175
+    ELOG(@"%@", theException);
127 176
   }
128 177
   @finally 
129 178
   {
130  
-    NSLog(@"This always happens.");
  179
+    ILOG(@"This always happens.");
131 180
   }
132 181
     
133 182
   [pool release];
21  xbmc/osx/ios/XBMCController.h
@@ -28,6 +28,13 @@
28 28
 
29 29
 @class IOSEAGLView;
30 30
 
  31
+typedef enum
  32
+{
  33
+  IOS_PLAYBACK_STOPPED,
  34
+  IOS_PLAYBACK_PAUSED,
  35
+  IOS_PLAYBACK_PLAYING
  36
+} IOSPlaybackState;
  37
+
31 38
 @interface XBMCController : UIViewController <UIGestureRecognizerDelegate>
32 39
 {
33 40
   UIWindow *m_window;
@@ -42,6 +49,12 @@
42 49
   int  m_screenIdx;
43 50
 
44 51
   UIInterfaceOrientation orientation;
  52
+  
  53
+  bool m_isPlayingBeforeInactive;
  54
+  bool m_isInterrupted;
  55
+  UIBackgroundTaskIdentifier m_bgTask;
  56
+  NSTimer *m_networkAutoSuspendTimer;
  57
+  IOSPlaybackState m_playbackState;
45 58
 }
46 59
 @property (readonly, nonatomic, getter=isAnimating) BOOL animating;
47 60
 @property CGPoint lastGesturePoint;
@@ -49,12 +62,18 @@
49 62
 @property bool touchBeginSignaled;
50 63
 @property int  m_screenIdx;
51 64
 @property CGSize screensize;
  65
+@property (nonatomic, retain) NSTimer *m_networkAutoSuspendTimer;
52 66
 
53 67
 // message from which our instance is obtained
54 68
 - (void) pauseAnimation;
55 69
 - (void) resumeAnimation;
56 70
 - (void) startAnimation;
57 71
 - (void) stopAnimation;
  72
+- (void) enterBackground;
  73
+- (void) enterForeground;
  74
+- (void) becomeInactive;
  75
+- (void) beginInterruption;
  76
+- (void) endInterruption;
58 77
 - (void) sendKey: (XBMCKey) key;
59 78
 - (void) observeDefaultCenterStuff: (NSNotification *) notification;
60 79
 - (void) initDisplayLink;
@@ -69,6 +88,8 @@
69 88
 - (void) activateKeyboard:(UIView *)view;
70 89
 - (void) deactivateKeyboard:(UIView *)view;
71 90
 
  91
+- (void) disableNetworkAutoSuspend;
  92
+- (void) enableNetworkAutoSuspend:(id)obj;
72 93
 - (void) disableSystemSleep;
73 94
 - (void) enableSystemSleep;
74 95
 - (void) disableScreenSaver;
454  xbmc/osx/ios/XBMCController.mm
@@ -28,16 +28,25 @@
28 28
 #include "settings/AdvancedSettings.h"
29 29
 #include "settings/Settings.h"
30 30
 #include "FileItem.h"
  31
+#include "MusicInfoTag.h"
  32
+#include "SpecialProtocol.h"
  33
+#include "PlayList.h"
  34
+#include "AEFactory.h"
31 35
 #include "ApplicationMessenger.h"
  36
+#include "Application.h"
  37
+#include "interfaces/AnnouncementManager.h"
32 38
 #include "input/touch/generic/GenericTouchActionHandler.h"
33 39
 #include "guilib/GUIControl.h"
34  
-
35 40
 #include "windowing/WindowingFactory.h"
36 41
 #include "video/VideoReferenceClock.h"
37 42
 #include "utils/log.h"
38 43
 #include "utils/TimeUtils.h"
  44
+#include "utils/Variant.h"
39 45
 #include "Util.h"
40 46
 #include "threads/Event.h"
  47
+#define id _id
  48
+#include "TextureCache.h"
  49
+#undef id
41 50
 #include <math.h>
42 51
 
43 52
 #ifndef M_PI
@@ -47,12 +56,23 @@
47 56
 
48 57
 #undef BOOL
49 58
 
  59
+#import <AVFoundation/AVAudioSession.h>
  60
+#import <MediaPlayer/MPMediaItem.h>
  61
+#ifdef __IPHONE_5_0
  62
+#import <MediaPlayer/MPNowPlayingInfoCenter.h>
  63
+#else
  64
+const NSString *MPNowPlayingInfoPropertyElapsedPlaybackTime = @"MPNowPlayingInfoPropertyElapsedPlaybackTime";
  65
+const NSString *MPNowPlayingInfoPropertyPlaybackRate = @"MPNowPlayingInfoPropertyPlaybackRate";
  66
+const NSString *MPNowPlayingInfoPropertyPlaybackQueueIndex = @"MPNowPlayingInfoPropertyPlaybackQueueIndex";
  67
+const NSString *MPNowPlayingInfoPropertyPlaybackQueueCount = @"MPNowPlayingInfoPropertyPlaybackQueueCount";
  68
+#endif
50 69
 #import "IOSEAGLView.h"
51 70
 
52 71
 #import "XBMCController.h"
53 72
 #import "IOSScreenManager.h"
54 73
 #import "XBMCApplication.h"
55 74
 #import "XBMCDebugHelpers.h"
  75
+#import "AutoPool.h"
56 76
 
57 77
 XBMCController *g_xbmcController;
58 78
 static CEvent screenChangeEvent;
@@ -62,6 +82,146 @@
62 82
 extern NSString* kBRScreenSaverActivated;
63 83
 extern NSString* kBRScreenSaverDismissed;
64 84
 
  85
+id objectFromVariant(const CVariant &data);
  86
+
  87
+NSArray *arrayFromVariantArray(const CVariant &data)
  88
+{
  89
+  if (!data.isArray())
  90
+    return nil;
  91
+  NSMutableArray *array = [[[NSMutableArray alloc] initWithCapacity:data.size()] autorelease];
  92
+  for (CVariant::const_iterator_array itr = data.begin_array(); itr != data.end_array(); ++itr)
  93
+  {
  94
+    [array addObject:objectFromVariant(*itr)];
  95
+  }
  96
+  return array;
  97
+}
  98
+
  99
+NSDictionary *dictionaryFromVariantMap(const CVariant &data)
  100
+{
  101
+  if (!data.isObject())
  102
+    return nil;
  103
+  NSMutableDictionary *dict = [[[NSMutableDictionary alloc] initWithCapacity:data.size()] autorelease];
  104
+  for (CVariant::const_iterator_map itr = data.begin_map(); itr != data.end_map(); ++itr)
  105
+  {
  106
+    [dict setValue:objectFromVariant(itr->second) forKey:[NSString stringWithUTF8String:itr->first.c_str()]];
  107
+  }
  108
+  return dict;
  109
+}
  110
+
  111
+id objectFromVariant(const CVariant &data)
  112
+{
  113
+  if (data.isNull())
  114
+    return nil;
  115
+  if (data.isString())
  116
+    return [NSString stringWithUTF8String:data.asString().c_str()];
  117
+  if (data.isWideString())
  118
+    return [NSString stringWithCString:(const char *)data.asWideString().c_str() encoding:NSUnicodeStringEncoding];
  119
+  if (data.isInteger())
  120
+    return [NSNumber numberWithLongLong:data.asInteger()];
  121
+  if (data.isUnsignedInteger())
  122
+    return [NSNumber numberWithUnsignedLongLong:data.asUnsignedInteger()];
  123
+  if (data.isBoolean())
  124
+    return [NSNumber numberWithInt:data.asBoolean()?1:0];
  125
+  if (data.isDouble())
  126
+    return [NSNumber numberWithDouble:data.asDouble()];
  127
+  if (data.isArray())
  128
+    return arrayFromVariantArray(data);
  129
+  if (data.isObject())
  130
+    return dictionaryFromVariantMap(data);
  131
+  return nil;
  132
+}
  133
+
  134
+void AnnounceBridge(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
  135
+{
  136
+  LOG(@"AnnounceBridge: [%s], [%s], [%s]", ANNOUNCEMENT::AnnouncementFlagToString(flag), sender, message);
  137
+  const std::string msg(message);
  138
+  if (msg == "OnPlay")
  139
+  {
  140
+    NSDictionary *item = dictionaryFromVariantMap(data["item"]);
  141
+    std::string thumb = g_application.CurrentFileItem().GetArt("thumb");
  142
+    if (!thumb.empty())
  143
+    {
  144
+      bool needsRecaching;
  145
+      CStdString cachedThumb(CTextureCache::Get().CheckCachedImage(thumb, false, needsRecaching));
  146
+      LOG("thumb: %s, %s", thumb.c_str(), cachedThumb.c_str());
  147
+      if (!cachedThumb.empty())
  148
+      {
  149
+        CStdString thumbRealPath = CSpecialProtocol::TranslatePath(cachedThumb);
  150
+        [item setValue:[NSString stringWithUTF8String:thumbRealPath.c_str()] forKey:@"thumb"];
  151
+      }
  152
+    }
  153
+    double duration = g_application.GetTotalTime();
  154
+    if (duration > 0)
  155
+      [item setValue:[NSNumber numberWithDouble:duration] forKey:@"duration"];
  156
+    [item setValue:[NSNumber numberWithInt:g_application.GetPlaySpeed()] forKey:@"speed"];
  157
+    [item setValue:[NSNumber numberWithDouble:g_application.GetTime()] forKey:@"elapsed"];
  158
+    int current = g_playlistPlayer.GetCurrentSong();
  159
+    if (current >= 0)
  160
+    {
  161
+      [item setValue:[NSNumber numberWithInt:current] forKey:@"current"];
  162
+      [item setValue:[NSNumber numberWithInt:g_playlistPlayer.GetPlaylist(g_playlistPlayer.GetCurrentPlaylist()).size()] forKey:@"total"];
  163
+    }
  164
+    if (g_application.CurrentFileItem().HasMusicInfoTag())
  165
+    {
  166
+      const std::vector<std::string> &genre = g_application.CurrentFileItem().GetMusicInfoTag()->GetGenre();
  167
+      if (!genre.empty())
  168
+      {
  169
+        NSMutableArray *genreArray = [[NSMutableArray alloc] initWithCapacity:genre.size()];
  170
+        for(std::vector<std::string>::const_iterator it = genre.begin(); it != genre.end(); ++it)
  171
+        {
  172
+          [genreArray addObject:[NSString stringWithUTF8String:it->c_str()]];
  173
+        }
  174
+        [item setValue:genreArray forKey:@"genre"];
  175
+      }
  176
+    }
  177
+    LOG(@"data: %@", item.description);
  178
+    [g_xbmcController performSelectorOnMainThread:@selector(onPlay:) withObject:item  waitUntilDone:NO];
  179
+  }
  180
+  else if (msg == "OnPause")
  181
+  {
  182
+    CAEFactory::Suspend();
  183
+    NSDictionary *item = dictionaryFromVariantMap(data["item"]);
  184
+    LOG(@"data: %@", item.description);
  185
+    [g_xbmcController performSelectorOnMainThread:@selector(onPause:) withObject:item  waitUntilDone:NO];
  186
+  }
  187
+  else if (msg == "OnStop")
  188
+  {
  189
+    CAEFactory::Suspend();
  190
+    NSDictionary *item = dictionaryFromVariantMap(data["item"]);
  191
+    LOG(@"data: %@", item.description);
  192
+    [g_xbmcController performSelectorOnMainThread:@selector(onStop:) withObject:item  waitUntilDone:NO];
  193
+  }
  194
+}
  195
+
  196
+class AnnounceReceiver : public ANNOUNCEMENT::IAnnouncer
  197
+{
  198
+public:
  199
+  virtual void Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
  200
+  {
  201
+    // not all Announce called from xbmc main thread, we need an auto poll here.
  202
+    CCocoaAutoPool pool;
  203
+    AnnounceBridge(flag, sender, message, data);
  204
+  }
  205
+  virtual ~AnnounceReceiver() {}
  206
+  static void init()
  207
+  {
  208
+    if (NULL==g_announceReceiver) {
  209
+      g_announceReceiver = new AnnounceReceiver();
  210
+      ANNOUNCEMENT::CAnnouncementManager::AddAnnouncer(g_announceReceiver);
  211
+    }
  212
+  }
  213
+  static void dealloc()
  214
+  {
  215
+    ANNOUNCEMENT::CAnnouncementManager::RemoveAnnouncer(g_announceReceiver);
  216
+    delete g_announceReceiver;
  217
+  }
  218
+private:
  219
+  AnnounceReceiver() {}
  220
+  static AnnounceReceiver *g_announceReceiver;
  221
+};
  222
+
  223
+AnnounceReceiver *AnnounceReceiver::g_announceReceiver = NULL;
  224
+
65 225
 //--------------------------------------------------------------
66 226
 //
67 227
 
@@ -80,6 +240,7 @@ @implementation XBMCController
80 240
 @synthesize touchBeginSignaled;
81 241
 @synthesize m_screenIdx;
82 242
 @synthesize screensize;
  243
+@synthesize m_networkAutoSuspendTimer;
83 244
 //--------------------------------------------------------------
84 245
 -(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
85 246
 {  
@@ -290,6 +451,7 @@ - (void) deactivateKeyboard:(UIView *)view
290 451
 {
291 452
   [view removeFromSuperview];
292 453
   m_glView.userInteractionEnabled = YES; 
  454
+  [self becomeFirstResponder];
293 455
 }
294 456
 //--------------------------------------------------------------
295 457
 -(void)handlePinch:(UIPinchGestureRecognizer*)sender 
@@ -528,12 +690,17 @@ - (IBAction)handleSingleFingerSingleLongTap:(UIGestureRecognizer *)sender
528 690
 //--------------------------------------------------------------
529 691
 - (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
530 692
 { 
531  
-  //NSLog(@"%s", __PRETTY_FUNCTION__);
  693
+  PRINT_SIGNATURE();
532 694
   m_screenIdx = 0;
533 695
   self = [super init];
534 696
   if ( !self )
535 697
     return ( nil );
536 698
 
  699
+  m_isPlayingBeforeInactive = NO;
  700
+  m_isInterrupted = NO;
  701
+  m_bgTask = UIBackgroundTaskInvalid;
  702
+  m_playbackState = IOS_PLAYBACK_STOPPED;
  703
+
537 704
   m_window = [[UIWindow alloc] initWithFrame:frame];
538 705
   [m_window setRootViewController:self];  
539 706
   m_window.screen = screen;
@@ -567,6 +734,8 @@ - (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
567 734
   [m_window makeKeyAndVisible];
568 735
   g_xbmcController = self;  
569 736
   
  737
+  AnnounceReceiver::init();
  738
+
570 739
   return self;
571 740
 }
572 741
 //--------------------------------------------------------------
@@ -577,6 +746,11 @@ -(void)viewDidLoad
577 746
 //--------------------------------------------------------------
578 747
 - (void)dealloc
579 748
 {
  749
+  // stop background task
  750
+  [m_networkAutoSuspendTimer invalidate];
  751
+  [self enableNetworkAutoSuspend:nil];
  752
+
  753
+  AnnounceReceiver::dealloc();
580 754
   [m_glView stopAnimation];
581 755
   [m_glView release];
582 756
   [m_window release];
@@ -591,7 +765,7 @@ - (void)dealloc
591 765
 //--------------------------------------------------------------
592 766
 - (void)viewWillAppear:(BOOL)animated
593 767
 {
594  
-  //NSLog(@"%s", __PRETTY_FUNCTION__);
  768
+  PRINT_SIGNATURE();
595 769
   
596 770
   // move this later into CocoaPowerSyscall
597 771
   [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
@@ -601,9 +775,17 @@ - (void)viewWillAppear:(BOOL)animated
601 775
   [super viewWillAppear:animated];
602 776
 }
603 777
 //--------------------------------------------------------------
  778
+-(void) viewDidAppear:(BOOL)animated
  779
+{
  780
+  [super viewDidAppear:animated];
  781
+
  782
+  [self becomeFirstResponder];
  783
+  [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
  784
+}
  785
+//--------------------------------------------------------------
604 786
 - (void)viewWillDisappear:(BOOL)animated
605 787
 {  
606  
-  //NSLog(@"%s", __PRETTY_FUNCTION__);
  788
+  PRINT_SIGNATURE();
607 789
   
608 790
   [self pauseAnimation];
609 791
   
@@ -613,8 +795,16 @@ - (void)viewWillDisappear:(BOOL)animated
613 795
   [super viewWillDisappear:animated];
614 796
 }
615 797
 //--------------------------------------------------------------
  798
+- (BOOL) canBecomeFirstResponder
  799
+{
  800
+  return YES;
  801
+}
  802
+//--------------------------------------------------------------
616 803
 - (void)viewDidUnload
617 804
 {
  805
+  [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
  806
+  [self resignFirstResponder];
  807
+
618 808
 	[super viewDidUnload];	
619 809
 }
620 810
 //--------------------------------------------------------------
@@ -658,7 +848,7 @@ - (CGFloat) getScreenScale:(UIScreen *)screen;
658 848
 //--------------------------------------------------------------
659 849
 - (BOOL) recreateOnReselect
660 850
 { 
661  
-  //NSLog(@"%s", __PRETTY_FUNCTION__);
  851
+  PRINT_SIGNATURE();
662 852
   return YES;
663 853
 }
664 854
 //--------------------------------------------------------------
@@ -670,6 +860,35 @@ - (void)didReceiveMemoryWarning
670 860
   // Release any cached data, images, etc. that aren't in use.
671 861
 }
672 862
 //--------------------------------------------------------------
  863
+- (void)disableNetworkAutoSuspend
  864
+{
  865
+  PRINT_SIGNATURE();
  866
+  if (m_bgTask != UIBackgroundTaskInvalid)
  867
+  {
  868
+    [[UIApplication sharedApplication] endBackgroundTask: m_bgTask];
  869
+    m_bgTask = UIBackgroundTaskInvalid;
  870
+  }
  871
+  // we have to alloc the background task for keep network working after screen lock and dark.
  872
+  UIBackgroundTaskIdentifier newTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
  873
+  m_bgTask = newTask;
  874
+
  875
+  if (m_networkAutoSuspendTimer)
  876
+  {
  877
+    [m_networkAutoSuspendTimer invalidate];
  878
+    self.m_networkAutoSuspendTimer = nil;
  879
+  }
  880
+}
  881
+//--------------------------------------------------------------
  882
+- (void)enableNetworkAutoSuspend:(id)obj
  883
+{
  884
+  PRINT_SIGNATURE();
  885
+  if (m_bgTask != UIBackgroundTaskInvalid)
  886
+  {
  887
+    [[UIApplication sharedApplication] endBackgroundTask: m_bgTask];
  888
+    m_bgTask = UIBackgroundTaskInvalid;
  889
+  }
  890
+}
  891
+//--------------------------------------------------------------
673 892
 - (void) disableSystemSleep
674 893
 {
675 894
 }
@@ -709,48 +928,239 @@ - (void) activateScreen: (UIScreen *)screen
709 928
   m_window.screen = screen;
710 929
 }
711 930
 //--------------------------------------------------------------
  931
+- (void) remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
  932
+  LOG(@"%s: type %d, subtype: %d", __PRETTY_FUNCTION__, receivedEvent.type, receivedEvent.subtype);
  933
+  if (receivedEvent.type == UIEventTypeRemoteControl)
  934
+  {
  935
+    [self disableNetworkAutoSuspend];
  936
+    switch (receivedEvent.subtype)
  937
+    {
  938
+      case UIEventSubtypeRemoteControlTogglePlayPause:
  939
+        CApplicationMessenger::Get().SendAction(ACTION_PLAYER_PLAYPAUSE);
  940
+        break;
  941
+      case UIEventSubtypeRemoteControlPlay:
  942
+        CApplicationMessenger::Get().SendAction(ACTION_PLAYER_PLAY);
  943
+        break;
  944
+      case UIEventSubtypeRemoteControlPause:
  945
+        // ACTION_PAUSE sometimes cause unpause, use MediaPauseIfPlaying to make sure pause only
  946
+        CApplicationMessenger::Get().MediaPauseIfPlaying();
  947
+        break;
  948
+      case UIEventSubtypeRemoteControlNextTrack:
  949
+        CApplicationMessenger::Get().SendAction(ACTION_NEXT_ITEM);
  950
+        break;
  951
+      case UIEventSubtypeRemoteControlPreviousTrack:
  952
+        CApplicationMessenger::Get().SendAction(ACTION_PREV_ITEM);
  953
+        break;
  954
+      case UIEventSubtypeRemoteControlBeginSeekingForward:
  955
+        // use 4X speed forward.
  956
+        CApplicationMessenger::Get().SendAction(ACTION_PLAYER_FORWARD);
  957
+        CApplicationMessenger::Get().SendAction(ACTION_PLAYER_FORWARD);
  958
+        break;
  959
+      case UIEventSubtypeRemoteControlBeginSeekingBackward:
  960
+        // use 4X speed rewind.
  961
+        CApplicationMessenger::Get().SendAction(ACTION_PLAYER_REWIND);
  962
+        CApplicationMessenger::Get().SendAction(ACTION_PLAYER_REWIND);
  963
+        break;
  964
+      case UIEventSubtypeRemoteControlEndSeekingForward:
  965
+      case UIEventSubtypeRemoteControlEndSeekingBackward:
  966
+        // restore to normal playback speed.
  967
+        if (g_application.IsPlaying() && !g_application.IsPaused())
  968
+          CApplicationMessenger::Get().SendAction(ACTION_PLAYER_PLAY);
  969
+        break;
  970
+      default:
  971
+        LOG(@"unhandled subtype: %d", receivedEvent.subtype);
  972
+        break;
  973
+    }
  974
+    [self rescheduleNetworkAutoSuspend];
  975
+  }
  976
+}
  977
+//--------------------------------------------------------------
  978
+- (void)enterBackground
  979
+{
  980
+  PRINT_SIGNATURE();
  981
+  if (g_application.IsPlaying() && !g_application.IsPaused())
  982
+  {
  983
+    m_isPlayingBeforeInactive = YES;
  984
+    CApplicationMessenger::Get().MediaPauseIfPlaying();
  985
+  }
  986
+}
  987
+
  988
+- (void)enterForeground
  989
+{
  990
+  PRINT_SIGNATURE();
  991
+  // when we come back, restore playing if we were.
  992
+  if (m_isPlayingBeforeInactive)
  993
+  {
  994
+    CApplicationMessenger::Get().MediaUnPause();
  995
+    m_isPlayingBeforeInactive = NO;
  996
+  }
  997
+  m_isInterrupted = NO;
  998
+}
  999
+
  1000
+- (void)becomeInactive
  1001
+{
  1002
+  LOG(@"%s: was interrupted: %d", __PRETTY_FUNCTION__,  m_isInterrupted);
  1003
+  // if we were interrupted, already paused here
  1004
+  // else if user background us or lock screen, only pause video here, audio keep playing.
  1005
+  if (g_application.IsPlayingVideo() && !g_application.IsPaused())
  1006
+  {
  1007
+    m_isPlayingBeforeInactive = YES;
  1008
+    CApplicationMessenger::Get().MediaPauseIfPlaying();
  1009
+  }
  1010
+  // check whether we need disable network auto suspend.
  1011
+  [self rescheduleNetworkAutoSuspend];
  1012
+}
  1013
+
  1014
+- (void)beginInterruption
  1015
+{
  1016
+  PRINT_SIGNATURE();
  1017
+  m_isInterrupted = YES;
  1018
+  CAEFactory::Suspend();
  1019
+}
  1020
+
  1021
+- (void)endInterruption
  1022
+{
  1023
+  PRINT_SIGNATURE();
  1024
+  if (CAEFactory::IsSuspended())
  1025
+    CAEFactory::Resume();
  1026
+}
  1027
+//--------------------------------------------------------------
712 1028
 - (void)pauseAnimation
713 1029
 {
714  
-  XBMC_Event newEvent;
715  
-  
716  
-  newEvent.appcommand.type = XBMC_APPCOMMAND;
717  
-  newEvent.appcommand.action = ACTION_PLAYER_PLAYPAUSE;
718  
-  CWinEventsIOS::MessagePush(&newEvent);
719  
-  
720  
-  /* Give player time to pause */
721  
-  Sleep(2000);
722  
-  //NSLog(@"%s", __PRETTY_FUNCTION__);
  1030
+  PRINT_SIGNATURE();
723 1031
   
724 1032
   [m_glView pauseAnimation];
725  
-  
726 1033
 }
727 1034
 //--------------------------------------------------------------
728 1035
 - (void)resumeAnimation
729 1036
 {  
730  
-  XBMC_Event newEvent;
731  
-  
732  
-  newEvent.appcommand.type = XBMC_APPCOMMAND;
733  
-  newEvent.appcommand.action = ACTION_PLAYER_PLAY;
734  
-  CWinEventsIOS::MessagePush(&newEvent);    
735  
-  
  1037
+  PRINT_SIGNATURE();
  1038
+
736 1039
   [m_glView resumeAnimation];
737 1040
 }
738 1041
 //--------------------------------------------------------------
739 1042
 - (void)startAnimation
740 1043
 {
  1044
+  PRINT_SIGNATURE();
  1045
+
741 1046
   [m_glView startAnimation];
742 1047
 }
743 1048
 //--------------------------------------------------------------
744 1049
 - (void)stopAnimation
745 1050
 {
  1051
+  PRINT_SIGNATURE();
  1052
+
746 1053
   [m_glView stopAnimation];
747 1054
 }
  1055
+//--------------------------------------------------------------
  1056
+- (void)onPlay:(NSDictionary *)item
  1057
+{
  1058
+  PRINT_SIGNATURE();
  1059
+  // MPNowPlayingInfoCenter is an ios5+ class, following code will work on ios5 even if compiled by xcode3
  1060
+  Class NowPlayingInfoCenter = NSClassFromString(@"MPNowPlayingInfoCenter");
  1061
+  if (NowPlayingInfoCenter)
  1062
+  {
  1063
+    NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
  1064
+
  1065
+    NSString *title = [item objectForKey:@"title"];
  1066
+    if (title && title.length > 0)
  1067
+      [dict setObject:title forKey:MPMediaItemPropertyTitle];
  1068
+    NSString *album = [item objectForKey:@"album"];
  1069
+    if (album && album.length > 0)
  1070
+      [dict setObject:album forKey:MPMediaItemPropertyAlbumTitle];
  1071
+    NSArray *artists = [item objectForKey:@"artist"];
  1072
+    if (artists && artists.count > 0)
  1073
+      [dict setObject:[artists componentsJoinedByString:@" "] forKey:MPMediaItemPropertyArtist];
  1074
+    NSNumber *track = [item objectForKey:@"track"];
  1075
+    if (track)
  1076
+      [dict setObject:track forKey:MPMediaItemPropertyAlbumTrackNumber];
  1077
+    NSNumber *duration = [item objectForKey:@"duration"];
  1078
+    if (duration)
  1079
+      [dict setObject:duration forKey:MPMediaItemPropertyPlaybackDuration];
  1080
+    NSArray *genres = [item objectForKey:@"genre"];
  1081
+    if (genres && genres.count > 0)
  1082
+      [dict setObject:[genres componentsJoinedByString:@" "] forKey:MPMediaItemPropertyGenre];
  1083
+    NSString *thumb = [item objectForKey:@"thumb"];
  1084
+    if (thumb && thumb.length > 0)
  1085
+    {
  1086
+      MPMediaItemArtwork *mArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageWithContentsOfFile:thumb]];
  1087
+      [dict setObject:mArt forKey:MPMediaItemPropertyArtwork];
  1088
+      [mArt release];
  1089
+    }
  1090
+    // these proprity keys are ios5+ only
  1091
+    NSNumber *elapsed = [item objectForKey:@"elapsed"];
  1092
+    if (elapsed)
  1093
+      [dict setObject:elapsed forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
  1094
+    NSNumber *speed = [item objectForKey:@"speed"];
  1095
+    if (speed)
  1096
+      [dict setObject:speed forKey:MPNowPlayingInfoPropertyPlaybackRate];
  1097
+    NSNumber *current = [item objectForKey:@"current"];
  1098
+    if (current)
  1099
+      [dict setObject:current forKey:MPNowPlayingInfoPropertyPlaybackQueueIndex];
  1100
+    NSNumber *total = [item objectForKey:@"total"];
  1101
+    if (total)
  1102
+      [dict setObject:total forKey:MPNowPlayingInfoPropertyPlaybackQueueCount];
  1103
+    /*
  1104
+     other properities can be set:
  1105
+     MPMediaItemPropertyAlbumTrackCount
  1106
+     MPMediaItemPropertyComposer