[vlc-commits] macosx: add remote control service and export now playing metadata

Felix Paul Kühne git at videolan.org
Fri Jun 8 20:15:04 CEST 2018


vlc | branch: master | Felix Paul Kühne <felix at feepk.net> | Sun Apr 22 12:49:03 2018 +0200| [64a43503534ffc895f1788a6b1c9fbecb2ad8491] | committer: Felix Paul Kühne

macosx: add remote control service and export now playing metadata

Fixes #17727

Note that out-of-rate position changes within VLC are not yet communicated to the OS

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=64a43503534ffc895f1788a6b1c9fbecb2ad8491
---

 .../package/macosx/VLC.xcodeproj/project.pbxproj   |  10 +-
 modules/gui/macosx/CompatibilityFixes.h            |  52 +++++++-
 modules/gui/macosx/Makefile.am                     |   3 +-
 modules/gui/macosx/VLCCoreInteraction.h            |   4 +-
 modules/gui/macosx/VLCCoreInteraction.m            |  19 ++-
 modules/gui/macosx/VLCInputManager.m               |  74 ++++++++++-
 modules/gui/macosx/VLCRemoteControlService.h       |  32 +++++
 modules/gui/macosx/VLCRemoteControlService.m       | 147 +++++++++++++++++++++
 modules/gui/macosx/misc.m                          |   2 +-
 9 files changed, 331 insertions(+), 12 deletions(-)

diff --git a/extras/package/macosx/VLC.xcodeproj/project.pbxproj b/extras/package/macosx/VLC.xcodeproj/project.pbxproj
index 3e5aa4963a..60e4f00d37 100644
--- a/extras/package/macosx/VLC.xcodeproj/project.pbxproj
+++ b/extras/package/macosx/VLC.xcodeproj/project.pbxproj
@@ -120,6 +120,7 @@
 		6BF56C3E1FCF00AF004A411A /* audiotoolbox_midi.c in Sources */ = {isa = PBXBuildFile; fileRef = 6BF56C3D1FCF00AF004A411A /* audiotoolbox_midi.c */; };
 		6BF5C5041EFE66EF008A9C12 /* VLCHUDTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BF5C5031EFE66EF008A9C12 /* VLCHUDTableView.m */; };
 		6BF5C5071EFE7E58008A9C12 /* VLCTintedImageButtonCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BF5C5061EFE7E58008A9C12 /* VLCTintedImageButtonCell.m */; };
+		7DD2F5C52081B73B007EE187 /* VLCRemoteControlService.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DD2F5C42081B73B007EE187 /* VLCRemoteControlService.m */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
@@ -485,6 +486,8 @@
 		7DB65D5218ABD6380053B874 /* VLCAddonsWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCAddonsWindowController.h; sourceTree = "<group>"; };
 		7DB65D5318ABD6380053B874 /* VLCAddonsWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCAddonsWindowController.m; sourceTree = "<group>"; };
 		7DBB06631CC2314D004C74D2 /* caopengllayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = caopengllayer.m; path = ../../../modules/video_output/caopengllayer.m; sourceTree = "<group>"; };
+		7DD2F5C32081B73B007EE187 /* VLCRemoteControlService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCRemoteControlService.h; sourceTree = "<group>"; };
+		7DD2F5C42081B73B007EE187 /* VLCRemoteControlService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCRemoteControlService.m; sourceTree = "<group>"; };
 		7DF0435E1972E26A0022B534 /* VLCAddonListItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCAddonListItem.h; sourceTree = "<group>"; };
 		7DF0435F1972E26A0022B534 /* VLCAddonListItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCAddonListItem.m; sourceTree = "<group>"; };
 		7DF812ED1B555A340052293C /* VLCInputManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCInputManager.h; sourceTree = "<group>"; };
@@ -689,6 +692,8 @@
 				7D871D391B5E684D000B56C0 /* helpers.h */,
 				7DF812ED1B555A340052293C /* VLCInputManager.h */,
 				7DF812EE1B555A340052293C /* VLCInputManager.m */,
+				7DD2F5C32081B73B007EE187 /* VLCRemoteControlService.h */,
+				7DD2F5C42081B73B007EE187 /* VLCRemoteControlService.m */,
 				7D871D371B5E6844000B56C0 /* VLCMain+OldPrefs.h */,
 				7D871D381B5E6844000B56C0 /* VLCMain+OldPrefs.m */,
 				8ED6C27C03E2EB1C0059A3A7 /* VLCMain.h */,
@@ -1309,7 +1314,7 @@
 					};
 				};
 			};
-			buildConfigurationList = C2F2A6EA09588F1B00018C74 /* Build configuration list for PBXProject "vlc" */;
+			buildConfigurationList = C2F2A6EA09588F1B00018C74 /* Build configuration list for PBXProject "VLC" */;
 			compatibilityVersion = "Xcode 6.3";
 			developmentRegion = English;
 			hasScannedForEncodings = 1;
@@ -1395,6 +1400,7 @@
 				6BF5C5041EFE66EF008A9C12 /* VLCHUDTableView.m in Sources */,
 				6BBB05E01EEFF165003A1019 /* VLCHUDTableCornerView.m in Sources */,
 				1C31139D1E508C6900D4DD76 /* VLCControlsBarCommon.m in Sources */,
+				7DD2F5C52081B73B007EE187 /* VLCRemoteControlService.m in Sources */,
 				1C31139F1E508C6900D4DD76 /* VLCMainWindowControlsBar.m in Sources */,
 				1C3113A11E508C6900D4DD76 /* VLCConvertAndSaveWindowController.m in Sources */,
 				1C3113A31E508C6900D4DD76 /* VLCCoreDialogProvider.m in Sources */,
@@ -1657,7 +1663,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Default;
 		};
-		C2F2A6EA09588F1B00018C74 /* Build configuration list for PBXProject "vlc" */ = {
+		C2F2A6EA09588F1B00018C74 /* Build configuration list for PBXProject "VLC" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				C2F2A6EB09588F1B00018C74 /* Development */,
diff --git a/modules/gui/macosx/CompatibilityFixes.h b/modules/gui/macosx/CompatibilityFixes.h
index 069aefd8d7..3a52279975 100644
--- a/modules/gui/macosx/CompatibilityFixes.h
+++ b/modules/gui/macosx/CompatibilityFixes.h
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * CompatibilityFixes.h: MacOS X interface module
  *****************************************************************************
- * Copyright (C) 2011-2017 VLC authors and VideoLAN
+ * Copyright (C) 2011-2018 VLC authors and VideoLAN
  * $Id$
  *
  * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
@@ -32,9 +32,9 @@
 #define OSX_YOSEMITE_AND_HIGHER (NSAppKitVersionNumber >= 1334)
 #define OSX_EL_CAPITAN_AND_HIGHER (NSAppKitVersionNumber >= 1404)
 #define OSX_SIERRA_AND_HIGHER (NSAppKitVersionNumber >= 1485)
+#define OSX_SIERRA_DOT_TWO_AND_HIGHER (NSAppKitVersionNumber >= 1504.76) // this is needed to check for MPRemoteCommandCenter
 #define OSX_HIGH_SIERRA_AND_HIGHER (NSAppKitVersionNumber >= 1560)
 
-
 // Sierra only APIs
 #ifndef MAC_OS_X_VERSION_10_12
 
@@ -56,5 +56,51 @@ typedef NSUInteger NSWindowStyleMask;
 
 #endif
 
-void swapoutOverride(Class _Nonnull cls, SEL _Nonnull selector);
+#ifndef MAC_OS_X_VERSION_10_12_2
+
+typedef NS_ENUM(NSUInteger, MPNowPlayingInfoMediaType) {
+    MPNowPlayingInfoMediaTypeNone = 0,
+    MPNowPlayingInfoMediaTypeAudio,
+    MPNowPlayingInfoMediaTypeVideo,
+};
+
+typedef NS_ENUM(NSUInteger, MPNowPlayingPlaybackState) {
+    MPNowPlayingPlaybackStateUnknown = 0,
+    MPNowPlayingPlaybackStatePlaying,
+    MPNowPlayingPlaybackStatePaused,
+    MPNowPlayingPlaybackStateStopped,
+    MPNowPlayingPlaybackStateInterrupted
+};
+
+NSString * const MPNowPlayingInfoPropertyElapsedPlaybackTime;
+NSString * const MPNowPlayingInfoPropertyPlaybackRate;
+NSString * const MPNowPlayingInfoPropertyDefaultPlaybackRate;
+NSString * const MPNowPlayingInfoPropertyPlaybackQueueIndex;
+NSString * const MPNowPlayingInfoPropertyPlaybackQueueCount;
+NSString * const MPNowPlayingInfoPropertyChapterNumber;
+NSString * const MPNowPlayingInfoPropertyChapterCount
+NSString * const MPNowPlayingInfoPropertyIsLiveStream;
+NSString * const MPNowPlayingInfoPropertyAvailableLanguageOptions;
+NSString * const MPNowPlayingInfoPropertyCurrentLanguageOptions;
+NSString * const MPNowPlayingInfoCollectionIdentifier;
+NSString * const MPNowPlayingInfoPropertyExternalContentIdentifier;
+NSString * const MPNowPlayingInfoPropertyExternalUserProfileIdentifier
+NSString * const MPNowPlayingInfoPropertyServiceIdentifier;
+NSString * const MPNowPlayingInfoPropertyPlaybackProgress
+NSString * const MPNowPlayingInfoPropertyMediaType
+NSString * const MPNowPlayingInfoPropertyAssetURL;
+NSString * const MPNowPlayingInfoPropertyCurrentPlaybackDate;
 
+ at interface MPNowPlayingInfoCenter : NSObject
++ (MPNowPlayingInfoCenter *)defaultCenter;
++ (instancetype)new NS_UNAVAILABLE;
+- (instancetype)init NS_UNAVAILABLE;
+
+ at property (nonatomic, copy, nullable) NSDictionary<NSString *, id> *nowPlayingInfo;
+ at property (nonatomic) MPNowPlayingPlaybackState playbackState MP_API(macos(10.12.2));
+
+ at end
+
+#endif
+
+void swapoutOverride(Class _Nonnull cls, SEL _Nonnull selector);
diff --git a/modules/gui/macosx/Makefile.am b/modules/gui/macosx/Makefile.am
index b85af0db4e..27955bb3c7 100644
--- a/modules/gui/macosx/Makefile.am
+++ b/modules/gui/macosx/Makefile.am
@@ -6,7 +6,7 @@ libmacosx_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(guidir)' \
 	-Wl,-framework,AVFoundation -Wl,-framework,CoreMedia -Wl,-framework,IOKit \
 	-Wl,-framework,AddressBook -Wl,-framework,WebKit -Wl,-framework,CoreAudio \
 	-Wl,-framework,SystemConfiguration -Wl,-framework,ScriptingBridge \
-	-Wl,-framework,QuartzCore
+	-Wl,-framework,QuartzCore -Wl,-framework,MediaPlayer
 
 if HAVE_SPARKLE
 libmacosx_plugin_la_LDFLAGS += -Wl,-framework,Sparkle
@@ -78,6 +78,7 @@ libmacosx_plugin_la_SOURCES = \
 	gui/macosx/VLCHUDTableView.h gui/macosx/VLCHUDTableView.m \
 	gui/macosx/VLCHUDTableCornerView.h gui/macosx/VLCHUDTableCornerView.m \
 	gui/macosx/VLCInputManager.h gui/macosx/VLCInputManager.m \
+	gui/macosx/VLCRemoteControlService.h gui/macosx/VLCRemoteControlService.m \
 	gui/macosx/VLCMainWindow.h gui/macosx/VLCMainWindow.m \
 	gui/macosx/VLCRendererDiscovery.h gui/macosx/VLCRendererDiscovery.m \
 	gui/macosx/VLCRendererItem.h gui/macosx/VLCRendererItem.m \
diff --git a/modules/gui/macosx/VLCCoreInteraction.h b/modules/gui/macosx/VLCCoreInteraction.h
index 6d49c9a9e2..3616c181c5 100644
--- a/modules/gui/macosx/VLCCoreInteraction.h
+++ b/modules/gui/macosx/VLCCoreInteraction.h
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * CoreInteraction.h: MacOS X interface module
  *****************************************************************************
- * Copyright (C) 2011-2015 Felix Paul Kühne
+ * Copyright (C) 2011-2018 Felix Paul Kühne
  * $Id$
  *
  * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
@@ -36,6 +36,7 @@
 @property (readonly) NSString * nameOfCurrentPlaylistItem;
 @property (nonatomic, readwrite) BOOL mute;
 
+- (void)play;
 - (void)playOrPause;
 - (void)pause;
 - (void)stop;
@@ -55,6 +56,7 @@
 - (void)backwardMedium;
 - (void)forwardLong;
 - (void)backwardLong;
+- (void)jumpToTime:(mtime_t)time;
 
 - (void)repeatOne;
 - (void)repeatAll;
diff --git a/modules/gui/macosx/VLCCoreInteraction.m b/modules/gui/macosx/VLCCoreInteraction.m
index 1f13283938..220a19fb8b 100644
--- a/modules/gui/macosx/VLCCoreInteraction.m
+++ b/modules/gui/macosx/VLCCoreInteraction.m
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * CoreInteraction.m: MacOS X interface module
  *****************************************************************************
- * Copyright (C) 2011-2015 Felix Paul Kühne
+ * Copyright (C) 2011-2018 Felix Paul Kühne
  * $Id$
  *
  * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
@@ -129,6 +129,13 @@ static int BossCallback(vlc_object_t *p_this, const char *psz_var,
 
 #pragma mark - Playback Controls
 
+- (void)play
+{
+    playlist_t *p_playlist = pl_Get(getIntf());
+
+    playlist_Play(p_playlist);
+}
+
 - (void)playOrPause
 {
     input_thread_t *p_input = pl_CurrentInput(getIntf());
@@ -532,6 +539,16 @@ static int BossCallback(vlc_object_t *p_this, const char *psz_var,
     }
 }
 
+- (void)jumpToTime:(mtime_t)time
+{
+    input_thread_t * p_input = pl_CurrentInput(getIntf());
+    if (p_input) {
+        mtime_t currentTime = var_GetInteger(p_input, "time");
+        var_SetInteger(p_input, "time", time);
+        vlc_object_release(p_input);
+    }
+}
+
 - (void)volumeUp
 {
     intf_thread_t *p_intf = getIntf();
diff --git a/modules/gui/macosx/VLCInputManager.m b/modules/gui/macosx/VLCInputManager.m
index 69d746c71c..db8d9a3008 100644
--- a/modules/gui/macosx/VLCInputManager.m
+++ b/modules/gui/macosx/VLCInputManager.m
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * VLCInputManager.m: MacOS X interface module
  *****************************************************************************
- * Copyright (C) 2015 VLC authors and VideoLAN
+ * Copyright (C) 2015-2018 VLC authors and VideoLAN
  * $Id$
  *
  * This program is free software; you can redistribute it and/or modify
@@ -34,10 +34,13 @@
 #import "VLCResumeDialogController.h"
 #import "VLCTrackSynchronizationWindowController.h"
 #import "VLCVoutView.h"
+#import "VLCRemoteControlService.h"
 
 #import "iTunes.h"
 #import "Spotify.h"
 
+#import <MediaPlayer/MediaPlayer.h>
+
 #pragma mark Callbacks
 
 static int InputThreadChanged(vlc_object_t *p_this, const char *psz_var,
@@ -155,6 +158,9 @@ static int InputEvent(vlc_object_t *p_this, const char *psz_var,
     BOOL b_has_itunes_paused;
     BOOL b_has_spotify_paused;
 
+    /* remote control support */
+    VLCRemoteControlService *_remoteControlService;
+
     NSTimer *hasEndedTimer;
 }
 @end
@@ -182,6 +188,8 @@ static int InputEvent(vlc_object_t *p_this, const char *psz_var,
 
         informInputChangedQueue = dispatch_queue_create("org.videolan.vlc.inputChangedQueue", DISPATCH_QUEUE_SERIAL);
 
+        _remoteControlService = [[VLCRemoteControlService alloc] init];
+        [_remoteControlService subscribeToRemoteCommands];
     }
     return self;
 }
@@ -196,6 +204,8 @@ static int InputEvent(vlc_object_t *p_this, const char *psz_var,
 - (void)deinit
 {
     msg_Dbg(getIntf(), "Deinitializing input manager");
+    [_remoteControlService unsubscribeFromRemoteCommands];
+
     if (p_current_input) {
         /* continue playback where you left off */
         [self storePlaybackPositionForItem:p_current_input];
@@ -283,7 +293,6 @@ static int InputEvent(vlc_object_t *p_this, const char *psz_var,
         return;
     }
 
-    intf_thread_t *p_intf = getIntf();
     int state = -1;
     if (p_current_input) {
         state = var_GetInteger(p_current_input, "state");
@@ -304,14 +313,29 @@ static int InputEvent(vlc_object_t *p_this, const char *psz_var,
 
         [[o_main mainMenu] setPause];
         [[o_main mainWindow] setPause];
+
+        if (OSX_SIERRA_DOT_TWO_AND_HIGHER) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpartial-availability"
+            [MPNowPlayingInfoCenter defaultCenter].playbackState = MPNowPlayingPlaybackStatePlaying;
+#pragma clang diagnostic pop
+        }
     } else {
         [[o_main mainMenu] setSubmenusEnabled: FALSE];
         [[o_main mainMenu] setPlay];
         [[o_main mainWindow] setPlay];
 
-        if (state == PAUSE_S)
+        if (state == PAUSE_S) {
             [self releaseSleepBlockers];
 
+            if (OSX_SIERRA_DOT_TWO_AND_HIGHER) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpartial-availability"
+                [MPNowPlayingInfoCenter defaultCenter].playbackState = MPNowPlayingPlaybackStatePaused;
+#pragma clang diagnostic pop
+            }
+        }
+
         if (state == END_S || state == -1) {
             /* continue playback where you left off */
             if (p_current_input)
@@ -325,6 +349,13 @@ static int InputEvent(vlc_object_t *p_this, const char *psz_var,
                                                            selector: @selector(onPlaybackHasEnded:)
                                                            userInfo: nil
                                                             repeats: NO];
+
+            if (OSX_SIERRA_DOT_TWO_AND_HIGHER) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpartial-availability"
+                [MPNowPlayingInfoCenter defaultCenter].playbackState = MPNowPlayingPlaybackStateStopped;
+#pragma clang diagnostic pop
+            }
         }
     }
 
@@ -490,6 +521,43 @@ static int InputEvent(vlc_object_t *p_this, const char *psz_var,
 
     [[[o_main playlist] model] updateItem:p_input_item];
     [[[VLCMain sharedInstance] currentMediaInfoPanel] updatePanelWithItem:p_input_item];
+
+    if (!OSX_SIERRA_DOT_TWO_AND_HIGHER) {
+        return;
+    }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpartial-availability"
+    if (!p_input_item) {
+        return;
+    }
+
+    NSMutableDictionary *currentlyPlayingTrackInfo = [NSMutableDictionary dictionary];
+
+    currentlyPlayingTrackInfo[MPMediaItemPropertyPlaybackDuration] = @(input_item_GetDuration(p_input_item) / CLOCK_FREQ);
+    currentlyPlayingTrackInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = @(var_GetInteger(p_current_input, "time"));
+    currentlyPlayingTrackInfo[MPNowPlayingInfoPropertyPlaybackRate] = @(var_GetFloat(p_current_input, "rate"));
+
+    char *psz_title = input_item_GetTitle(p_input_item);
+    if (!psz_title)
+        psz_title = input_item_GetName(p_input_item);
+    currentlyPlayingTrackInfo[MPMediaItemPropertyTitle] = toNSStr(psz_title);
+    FREENULL(psz_title);
+
+    char *psz_artist = input_item_GetArtist(p_input_item);
+    currentlyPlayingTrackInfo[MPMediaItemPropertyArtist] = toNSStr(psz_artist);
+    FREENULL(psz_artist);
+
+    char *psz_album = input_item_GetAlbum(p_input_item);
+    currentlyPlayingTrackInfo[MPMediaItemPropertyAlbumTitle] = toNSStr(psz_album);
+    FREENULL(psz_album);
+
+    char *psz_track_number = input_item_GetTrackNumber(p_input_item);
+    currentlyPlayingTrackInfo[MPMediaItemPropertyAlbumTrackNumber] = toNSStr(psz_track_number);
+    FREENULL(psz_track_number);
+
+    [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = currentlyPlayingTrackInfo;
+#pragma clang diagnostic pop
 }
 
 - (void)updateMainWindow
diff --git a/modules/gui/macosx/VLCRemoteControlService.h b/modules/gui/macosx/VLCRemoteControlService.h
new file mode 100644
index 0000000000..3d9a1f3679
--- /dev/null
+++ b/modules/gui/macosx/VLCRemoteControlService.h
@@ -0,0 +1,32 @@
+/*****************************************************************************
+ * VLCRemoteControlService.h: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2017, 2018 VLC authors and VideoLAN
+ * $Id$
+ *
+ * Authors: Carola Nitz <nitz.carola # gmail.com>
+ *          Felix Paul Kühne <fkuehne # videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+
+ at interface VLCRemoteControlService : NSObject
+
+- (void)subscribeToRemoteCommands;
+- (void)unsubscribeFromRemoteCommands;
+
+ at end
diff --git a/modules/gui/macosx/VLCRemoteControlService.m b/modules/gui/macosx/VLCRemoteControlService.m
new file mode 100644
index 0000000000..8c1237fe52
--- /dev/null
+++ b/modules/gui/macosx/VLCRemoteControlService.m
@@ -0,0 +1,147 @@
+/*****************************************************************************
+ * VLCRemoteControlService.m: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2017, 2018 VLC authors and VideoLAN
+ * $Id$
+ *
+ * Authors: Carola Nitz <nitz.carola # gmail.com>
+ *          Felix Paul Kühne <fkuehne # videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#import "VLCRemoteControlService.h"
+#import "VLCCoreInteraction.h"
+#import "VLCMain.h"
+#import <MediaPlayer/MediaPlayer.h>
+#import "CompatibilityFixes.h"
+
+#define kVLCSettingPlaybackForwardSkipLength @(60)
+#define kVLCSettingPlaybackBackwardSkipLength @(60)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpartial-availability"
+
+ at implementation VLCRemoteControlService
+
+static inline NSArray * RemoteCommandCenterCommandsToHandle()
+{
+    MPRemoteCommandCenter *cc = [MPRemoteCommandCenter sharedCommandCenter];
+    NSMutableArray *commands = [NSMutableArray arrayWithObjects:
+                                cc.playCommand,
+                                cc.pauseCommand,
+                                cc.stopCommand,
+                                cc.togglePlayPauseCommand,
+                                cc.nextTrackCommand,
+                                cc.previousTrackCommand,
+                                cc.skipForwardCommand,
+                                cc.skipBackwardCommand,
+                                cc.changePlaybackPositionCommand,
+                                nil];
+    return [commands copy];
+}
+
+- (void)subscribeToRemoteCommands
+{
+    if (!OSX_SIERRA_DOT_TWO_AND_HIGHER) {
+        return;
+    }
+
+    MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
+
+    //Enable when you want to support these
+    commandCenter.ratingCommand.enabled = NO;
+    commandCenter.likeCommand.enabled = NO;
+    commandCenter.dislikeCommand.enabled = NO;
+    commandCenter.bookmarkCommand.enabled = NO;
+    commandCenter.enableLanguageOptionCommand.enabled = NO;
+    commandCenter.disableLanguageOptionCommand.enabled = NO;
+    commandCenter.changeRepeatModeCommand.enabled = NO;
+    commandCenter.changeShuffleModeCommand.enabled = NO;
+    commandCenter.seekForwardCommand.enabled = NO;
+    commandCenter.seekBackwardCommand.enabled = NO;
+
+    commandCenter.skipForwardCommand.preferredIntervals = @[kVLCSettingPlaybackForwardSkipLength];
+    commandCenter.skipBackwardCommand.preferredIntervals = @[kVLCSettingPlaybackBackwardSkipLength];
+
+    for (MPRemoteCommand *command in RemoteCommandCenterCommandsToHandle()) {
+        [command addTarget:self action:@selector(remoteCommandEvent:)];
+    }
+}
+
+- (void)unsubscribeFromRemoteCommands
+{
+    if (!OSX_SIERRA_DOT_TWO_AND_HIGHER) {
+        return;
+    }
+
+    [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nil;
+
+    for (MPRemoteCommand *command in RemoteCommandCenterCommandsToHandle()) {
+        [command removeTarget:self];
+    }
+}
+
+- (MPRemoteCommandHandlerStatus )remoteCommandEvent:(MPRemoteCommandEvent *)event
+{
+    MPRemoteCommandCenter *cc = [MPRemoteCommandCenter sharedCommandCenter];
+    VLCCoreInteraction *coreInteraction = [VLCCoreInteraction sharedInstance];
+
+    if (event.command == cc.playCommand) {
+        [coreInteraction play];
+        return MPRemoteCommandHandlerStatusSuccess;
+    }
+    if (event.command == cc.pauseCommand) {
+        [coreInteraction pause];
+        return MPRemoteCommandHandlerStatusSuccess;
+    }
+    if (event.command == cc.stopCommand) {
+        [coreInteraction stop];
+        return MPRemoteCommandHandlerStatusSuccess;
+    }
+    if (event.command == cc.togglePlayPauseCommand) {
+        [coreInteraction playOrPause];
+        return MPRemoteCommandHandlerStatusSuccess;
+    }
+    if (event.command == cc.nextTrackCommand) {
+        [coreInteraction next];
+        return MPRemoteCommandHandlerStatusSuccess;
+    }
+    if (event.command == cc.previousTrackCommand) {
+        [coreInteraction previous];
+        return MPRemoteCommandHandlerStatusSuccess;
+    }
+    if (event.command == cc.skipForwardCommand) {
+        [coreInteraction forwardMedium];
+        return MPRemoteCommandHandlerStatusSuccess;
+    }
+    if (event.command == cc.skipBackwardCommand) {
+        [coreInteraction backwardMedium];
+        return MPRemoteCommandHandlerStatusSuccess;
+    }
+    if (event.command == cc.changePlaybackPositionCommand) {
+        MPChangePlaybackPositionCommandEvent *positionEvent = (MPChangePlaybackPositionCommandEvent *)event;
+        [coreInteraction jumpToTime:positionEvent.positionTime * CLOCK_FREQ];
+        return MPRemoteCommandHandlerStatusSuccess;
+    }
+
+    NSAssert(NO, @"remote control event not handled");
+    msg_Dbg(getIntf(), "%s Wasn't able to handle remote control event: %s",__PRETTY_FUNCTION__,[event.description UTF8String]);
+    return MPRemoteCommandHandlerStatusCommandFailed;
+}
+
+ at end
+
+#pragma clang diagnostic pop
diff --git a/modules/gui/macosx/misc.m b/modules/gui/macosx/misc.m
index acffad80bb..ac80878619 100644
--- a/modules/gui/macosx/misc.m
+++ b/modules/gui/macosx/misc.m
@@ -24,11 +24,11 @@
 
 #import "CompatibilityFixes.h"
 #import "misc.h"
-#import "VLCMain.h"                                          /* VLCApplication */
 #import "VLCMainWindow.h"
 #import "VLCMainMenu.h"
 #import "VLCControlsBarCommon.h"
 #import "VLCCoreInteraction.h"
+#import "VLCStringUtility.h"
 #import <vlc_actions.h>
 
 /*****************************************************************************



More information about the vlc-commits mailing list