Forráskód Böngészése

VLCHTTPConnection: Add WebSocket to allow remote playback control

Felix Paul Kühne 9 éve
szülő
commit
c3ecab6fbe

+ 17 - 0
SharedSources/VLCPlayerControlWebSocket.h

@@ -0,0 +1,17 @@
+/*****************************************************************************
+ * VLCPlayerControlWebSocket.h
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2015 VideoLAN. All rights reserved.
+ * $Id$
+ *
+ * Authors: Felix Paul Kühne <fkuehne # videolan.org>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+
+#import "WebSocket.h"
+
+@interface VLCPlayerControlWebSocket : WebSocket
+
+@end

+ 302 - 0
SharedSources/VLCPlayerControlWebSocket.m

@@ -0,0 +1,302 @@
+/*****************************************************************************
+ * VLCPlayerControlWebSocket.m
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2015 VideoLAN. All rights reserved.
+ * $Id$
+ *
+ * Authors: Felix Paul Kühne <fkuehne # videolan.org>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+
+#import "VLCPlayerControlWebSocket.h"
+
+@implementation VLCPlayerControlWebSocket
+
+- (void)dealloc
+{
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (void)didOpen
+{
+    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
+    [notificationCenter addObserver:self
+                           selector:@selector(playbackStarted)
+                               name:VLCPlaybackControllerPlaybackDidStart
+                             object:nil];
+    [notificationCenter addObserver:self
+                           selector:@selector(playbackStarted)
+                               name:VLCPlaybackControllerPlaybackDidResume
+                             object:nil];
+    [notificationCenter addObserver:self
+                           selector:@selector(_respondToPlaying)
+                               name:VLCPlaybackControllerPlaybackMetadataDidChange
+                             object:nil];
+    [notificationCenter addObserver:self
+                           selector:@selector(playbackPaused)
+                               name:VLCPlaybackControllerPlaybackDidPause
+                             object:nil];
+    [notificationCenter addObserver:self
+                           selector:@selector(playbackEnded)
+                               name:VLCPlaybackControllerPlaybackDidStop
+                             object:nil];
+    [notificationCenter addObserver:self
+                           selector:@selector(playbackEnded)
+                               name:VLCPlaybackControllerPlaybackDidFail
+                             object:nil];
+    [notificationCenter addObserver:self
+                           selector:@selector(playbackSeekTo)
+                               name:VLCPlaybackControllerPlaybackPositionUpdated
+                             object:nil];
+
+    APLog(@"web socket did open");
+
+    [super didOpen];
+}
+
+- (void)didReceiveMessage:(NSString *)msg
+{
+    APLog(@"web socket received message: '%@'", msg);
+    if ([msg isEqualToString:@"playing"]) {
+        [self _respondToPlaying];
+    } else if ([msg isEqualToString:@"play"]) {
+        [self _respondToPlay];
+    } else if ([msg isEqualToString:@"pause"]) {
+        [self _respondToPause];
+    } else if ([msg isEqualToString:@"ended"]) {
+        [self _respondToEnded];
+    } else if ([msg isEqualToString:@"seekTo"]) {
+        [self _respondToSeek];
+    } else if ([msg isEqualToString:@"volume"]) {
+        [self sendMessage:@"VOLUME CONTROL NOT SUPPORTED ON THIS DEVICE"];
+    } else
+        [self sendMessage:@"INVALID REQUEST!"];
+}
+
+#ifndef NDEBUG
+- (void)didClose
+{
+    APLog(@"web socket did close");
+
+    [super didClose];
+}
+#endif
+
+- (void)_respondToPlaying
+{
+    /* JSON response
+     {
+        "type": "playing",
+        "currentTime": 42,
+        "media": {
+            "id": "some id",
+            "title": "some title",
+            "duration": 120000
+        }
+     }
+     */
+
+    VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
+    NSDictionary *returnDict;
+
+    if (vpc.activePlaybackSession) {
+        VLCMediaPlayer *player = vpc.mediaPlayer;
+        if (player) {
+            VLCMedia *media = player.media;
+
+            if (media) {
+                NSString *mediaTitle = vpc.mediaTitle;
+                if (!mediaTitle)
+                    mediaTitle = @"";
+                NSDictionary *mediaDict = @{ @"id" : media.url.absoluteString,
+                                             @"title" : mediaTitle,
+                                             @"duration" : @(media.length.intValue)};
+                returnDict = @{ @"currentTime" : @(player.time.intValue),
+                                @"type" : @"playing",
+                                @"media" : mediaDict };
+            }
+        }
+    }
+    if (!returnDict) {
+        returnDict = [NSDictionary dictionary];
+    }
+
+    NSError *error;
+    NSData *returnData = [NSJSONSerialization dataWithJSONObject:returnDict options:0 error:&error];
+    if (error != nil) {
+        APLog(@"%s: JSON serialization failed %@", __PRETTY_FUNCTION__, error);
+    }
+
+    [self sendData:returnData];
+}
+
+#pragma mark - play
+
+- (void)_respondToPlay
+{
+    VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
+    VLCMediaListPlayer *listPlayer = vpc.listPlayer;
+    if (listPlayer) {
+        [listPlayer play];
+    }
+}
+
+- (void)playbackStarted
+{
+    /*
+     {
+        "type": "play",
+        "currentTime": 42,
+        "media": {
+            "id": 42
+        }
+     }
+     */
+
+    VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
+    VLCMediaPlayer *player = vpc.mediaPlayer;
+    if (player) {
+        VLCMedia *media = player.media;
+        if (media) {
+            NSDictionary *mediaDict = @{ @"id" : media.url.absoluteString};
+            NSDictionary *returnDict = @{ @"currentTime" : @(player.time.intValue),
+                                          @"type" : @"play",
+                                          @"media" : mediaDict };
+
+            NSError *error;
+            NSData *returnData = [NSJSONSerialization dataWithJSONObject:returnDict options:0 error:&error];
+            if (error != nil) {
+                APLog(@"%s: JSON serialization failed %@", __PRETTY_FUNCTION__, error);
+            }
+
+            [self sendData:returnData];
+        }
+    }
+}
+
+#pragma mark - pause
+
+- (void)_respondToPause
+{
+    VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
+    VLCMediaListPlayer *listPlayer = vpc.listPlayer;
+    if (listPlayer) {
+        [listPlayer pause];
+    }
+}
+
+- (void)playbackPaused
+{
+    /*
+     {
+        "type": "pause",
+        "currentTime": 42,
+        "media": {
+            "id": 42
+        }
+     }
+     */
+
+    VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
+    VLCMediaPlayer *player = vpc.mediaPlayer;
+    if (player) {
+        VLCMedia *media = player.media;
+        if (media) {
+            NSDictionary *mediaDict = @{ @"id" : media.url.absoluteString};
+            NSDictionary *returnDict = @{ @"currentTime" : @(player.time.intValue),
+                                          @"type" : @"pause",
+                                          @"media" : mediaDict };
+
+            NSError *error;
+            NSData *returnData = [NSJSONSerialization dataWithJSONObject:returnDict options:0 error:&error];
+            if (error != nil) {
+                APLog(@"%s: JSON serialization failed %@", __PRETTY_FUNCTION__, error);
+            }
+
+            [self sendData:returnData];
+        }
+    }
+}
+
+#pragma mark - ended
+
+- (void)_respondToEnded
+{
+    VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
+    [vpc stopPlayback];
+}
+
+- (void)playbackEnded
+{
+    /*
+     {
+        "type": "ended",
+        "media": {
+            "id": 42
+        }
+     }
+     */
+
+    VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
+    VLCMediaPlayer *player = vpc.mediaPlayer;
+    if (player) {
+        VLCMedia *media = player.media;
+        if (media) {
+            NSDictionary *mediaDict = @{ @"id" : media.url.absoluteString};
+            NSDictionary *returnDict = @{ @"type" : @"ended",
+                                          @"media" : mediaDict };
+
+            NSError *error;
+            NSData *returnData = [NSJSONSerialization dataWithJSONObject:returnDict options:0 error:&error];
+            if (error != nil) {
+                APLog(@"%s: JSON serialization failed %@", __PRETTY_FUNCTION__, error);
+            }
+
+            [self sendData:returnData];
+        }
+    }
+}
+
+#pragma mark - seek
+
+- (void)_respondToSeek
+{
+    [self sendMessage:@"VOLUME CONTROL NOT SUPPORTED ON THIS DEVICE"];
+}
+
+- (void)playbackSeekTo
+{
+    /* 
+     {
+        "type": "seekTo",
+        "currentTime": 42,
+        "media": {
+            "id": 42
+        }
+     }
+     */
+
+    VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
+    VLCMediaPlayer *player = vpc.mediaPlayer;
+    if (player) {
+        VLCMedia *media = player.media;
+        if (media) {
+            NSDictionary *mediaDict = @{ @"id" : media.url.absoluteString};
+            NSDictionary *returnDict = @{ @"currentTime" : @(player.time.intValue),
+                                          @"type" : @"seekTo",
+                                          @"media" : mediaDict };
+
+            NSError *error;
+            NSData *returnData = [NSJSONSerialization dataWithJSONObject:returnDict options:0 error:&error];
+            if (error != nil) {
+                APLog(@"%s: JSON serialization failed %@", __PRETTY_FUNCTION__, error);
+            }
+
+            [self sendData:returnData];
+        }
+    }
+}
+
+@end

+ 10 - 0
Sources/VLCHTTPConnection.m

@@ -29,6 +29,9 @@
 #if TARGET_OS_IOS
 #import "VLCThumbnailsCache.h"
 #endif
+#if TARGET_OS_TV
+#import "VLCPlayerControlWebSocket.h"
+#endif
 
 @interface VLCHTTPConnection()
 {
@@ -548,6 +551,13 @@
     return [super httpResponseForMethod:method URI:path];
 }
 
+#if TARGET_OS_TV
+- (WebSocket *)webSocketForURI:(NSString *)path
+{
+    return [[VLCPlayerControlWebSocket alloc] initWithRequest:request socket:asyncSocket];
+}
+#endif
+
 - (void)prepareForBodyWithSize:(UInt64)contentLength
 {
     // set up mime parser

+ 1 - 0
Sources/VLCPlaybackController.h

@@ -19,6 +19,7 @@ extern NSString *const VLCPlaybackControllerPlaybackDidResume;
 extern NSString *const VLCPlaybackControllerPlaybackDidStop;
 extern NSString *const VLCPlaybackControllerPlaybackDidFail;
 extern NSString *const VLCPlaybackControllerPlaybackMetadataDidChange;
+extern NSString *const VLCPlaybackControllerPlaybackPositionUpdated;
 
 @class VLCPlaybackController;
 

+ 4 - 0
Sources/VLCPlaybackController.m

@@ -36,6 +36,7 @@ NSString *const VLCPlaybackControllerPlaybackDidResume = @"VLCPlaybackController
 NSString *const VLCPlaybackControllerPlaybackDidStop = @"VLCPlaybackControllerPlaybackDidStop";
 NSString *const VLCPlaybackControllerPlaybackMetadataDidChange = @"VLCPlaybackControllerPlaybackMetadataDidChange";
 NSString *const VLCPlaybackControllerPlaybackDidFail = @"VLCPlaybackControllerPlaybackDidFail";
+NSString *const VLCPlaybackControllerPlaybackPositionUpdated = @"VLCPlaybackControllerPlaybackPositionUpdated";
 
 @interface VLCPlaybackController () <VLCMediaPlayerDelegate, VLCMediaDelegate>
 {
@@ -483,6 +484,9 @@ NSString *const VLCPlaybackControllerPlaybackDidFail = @"VLCPlaybackControllerPl
 
     if ([self.delegate respondsToSelector:@selector(playbackPositionUpdated:)])
         [self.delegate playbackPositionUpdated:self];
+
+    [[NSNotificationCenter defaultCenter] postNotificationName:VLCPlaybackControllerPlaybackPositionUpdated
+                                                        object:self];
 }
 
 - (NSInteger)mediaDuration

+ 6 - 0
VLC for iOS.xcodeproj/project.pbxproj

@@ -216,6 +216,7 @@
 		7DF28AE11BA31C580030C944 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DF28AE01BA31C580030C944 /* libc++.tbd */; };
 		7DF383AC1BF206F100D71A5C /* VLCRemoteBrowsingCollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DF383AB1BF206F100D71A5C /* VLCRemoteBrowsingCollectionViewController.m */; };
 		7DF383AE1BF206FB00D71A5C /* VLCRemoteBrowsingCollectionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7DF383AD1BF206FB00D71A5C /* VLCRemoteBrowsingCollectionViewController.xib */; };
+		7DF383B91BF21E4400D71A5C /* VLCPlayerControlWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DF383B81BF21E4400D71A5C /* VLCPlayerControlWebSocket.m */; };
 		7DF7CA0717650C2A00C61739 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DF7CA0617650C2A00C61739 /* AVFoundation.framework */; };
 		7DF7E791175F47DC0018858D /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DF7E790175F47DC0018858D /* MediaPlayer.framework */; };
 		7DF90B441BE7A5380059C0E3 /* VLCSettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DF90B431BE7A5380059C0E3 /* VLCSettingsTableViewController.m */; };
@@ -799,6 +800,8 @@
 		7DF383AA1BF206F100D71A5C /* VLCRemoteBrowsingCollectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCRemoteBrowsingCollectionViewController.h; sourceTree = "<group>"; };
 		7DF383AB1BF206F100D71A5C /* VLCRemoteBrowsingCollectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCRemoteBrowsingCollectionViewController.m; sourceTree = "<group>"; };
 		7DF383AD1BF206FB00D71A5C /* VLCRemoteBrowsingCollectionViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = VLCRemoteBrowsingCollectionViewController.xib; sourceTree = "<group>"; };
+		7DF383B71BF21E4400D71A5C /* VLCPlayerControlWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLCPlayerControlWebSocket.h; path = SharedSources/VLCPlayerControlWebSocket.h; sourceTree = SOURCE_ROOT; };
+		7DF383B81BF21E4400D71A5C /* VLCPlayerControlWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VLCPlayerControlWebSocket.m; path = SharedSources/VLCPlayerControlWebSocket.m; sourceTree = SOURCE_ROOT; };
 		7DF7CA0617650C2A00C61739 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
 		7DF7E790175F47DC0018858D /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; };
 		7DF90B421BE7A5380059C0E3 /* VLCSettingsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCSettingsTableViewController.h; sourceTree = "<group>"; };
@@ -1371,6 +1374,8 @@
 				7D30F3BF183AB24C00FFC021 /* VLCHTTPFileDownloader.m */,
 				7D30F3C0183AB24C00FFC021 /* VLCHTTPUploaderController.h */,
 				7D30F3C1183AB24C00FFC021 /* VLCHTTPUploaderController.m */,
+				7DF383B71BF21E4400D71A5C /* VLCPlayerControlWebSocket.h */,
+				7DF383B81BF21E4400D71A5C /* VLCPlayerControlWebSocket.m */,
 			);
 			name = "HTTP Connectivity";
 			path = ../AspenProject;
@@ -2658,6 +2663,7 @@
 				7DEC8BDA1BD67112006E1093 /* VLCFrostedGlasView.m in Sources */,
 				DD8095EB1BE4F04E0065D8E1 /* VLCPlaybackInfoRateTVViewController.m in Sources */,
 				DD3EAC051BE153B4003668DA /* VLCNetworkImageView.m in Sources */,
+				7DF383B91BF21E4400D71A5C /* VLCPlayerControlWebSocket.m in Sources */,
 				7D0C35341BD97C7B0058CD19 /* VLCOneDriveObject.m in Sources */,
 				7D0C35331BD97C100058CD19 /* VLCOneDriveController.m in Sources */,
 				7D0EDE061BE774BF00363AA1 /* WhiteRaccoon.m in Sources */,