From e66f4f7c826ad2424b71ed0d11f4f6b13adb3e23 Mon Sep 17 00:00:00 2001 From: Aperence <anthony.doeraene@hotmail.com> Date: Wed, 7 Aug 2024 18:21:34 +0200 Subject: [PATCH] Add Multipath TCP (MPTCP) support MPTCP is a TCP extension allowing to improve network reliabilty by using mutiple network interface for the same TCP connection. Check https://www.mptcp.dev and https://developer.apple.com/documentation/foundation/urlsessionconfiguration/improving_network_reliability_using_multipath_tcp for details. Changes to this repository include declaring a new sharedMPTCPSession property in NSURLSession, and replacing previous uses of [NSURLSession sharedSession] to [NSURLSession sharedMPTCPSession] --- Resources/iOS/VLC.entitlements | 2 + .../NSURLSession+sharedMPTCPSession.h | 17 +++++++ .../NSURLSession+sharedMPTCPSession.m | 25 +++++++++++ .../NSURLSessionConfiguration+default.h | 16 +++++++ .../NSURLSessionConfiguration+default.m | 31 +++++++++++++ .../Network/Download/VLCHTTPFileDownloader.m | 3 +- .../VLCOpenNetworkSubtitlesFinder.m | 3 +- .../VLCOpenSubtitlesDownloader.m | 3 +- Sources/UI Elements/VLCNetworkImageView.m | 3 +- VLC.xcodeproj/project.pbxproj | 44 +++++++++++++++++++ 10 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 Sources/Extensions/NSURLSession/NSURLSession+sharedMPTCPSession.h create mode 100644 Sources/Extensions/NSURLSession/NSURLSession+sharedMPTCPSession.m create mode 100644 Sources/Extensions/NSURLSessionConfiguration/NSURLSessionConfiguration+default.h create mode 100644 Sources/Extensions/NSURLSessionConfiguration/NSURLSessionConfiguration+default.m diff --git a/Resources/iOS/VLC.entitlements b/Resources/iOS/VLC.entitlements index 1aa7ff0e6..4a66c8f41 100644 --- a/Resources/iOS/VLC.entitlements +++ b/Resources/iOS/VLC.entitlements @@ -18,6 +18,8 @@ </array> <key>com.apple.developer.networking.multicast</key> <true/> + <key>com.apple.developer.networking.multipath</key> + <true/> <key>com.apple.developer.siri</key> <true/> <key>com.apple.developer.ubiquity-container-identifiers</key> diff --git a/Sources/Extensions/NSURLSession/NSURLSession+sharedMPTCPSession.h b/Sources/Extensions/NSURLSession/NSURLSession+sharedMPTCPSession.h new file mode 100644 index 000000000..424f7459c --- /dev/null +++ b/Sources/Extensions/NSURLSession/NSURLSession+sharedMPTCPSession.h @@ -0,0 +1,17 @@ +/***************************************************************************** + * NSURLSession+sharedMPTCPSession.h + * VLC for iOS + ***************************************************************************** + * + * Author: Anthony Doeraene <anthony.doeraene@hotmail.com> + * + *****************************************************************************/ + + #ifndef NSURLSession_sharedMPTCPSession_h + #define NSURLSession_sharedMPTCPSession_h + + @interface NSURLSession (sharedMPTCPSession) + + (NSURLSession *) sharedMPTCPSession; + @end + + #endif /* NSURLSession_sharedMPTCPSession_h */ diff --git a/Sources/Extensions/NSURLSession/NSURLSession+sharedMPTCPSession.m b/Sources/Extensions/NSURLSession/NSURLSession+sharedMPTCPSession.m new file mode 100644 index 000000000..7faebcc38 --- /dev/null +++ b/Sources/Extensions/NSURLSession/NSURLSession+sharedMPTCPSession.m @@ -0,0 +1,25 @@ +/***************************************************************************** + * NSURLSession+sharedMPTCPSession.m + * VLC for iOS + ***************************************************************************** + * + * Author: Anthony Doeraene <anthony.doeraene@hotmail.com> + * + *****************************************************************************/ + + #import <Foundation/Foundation.h> + #include "NSURLSession+sharedMPTCPSession.h" + #include "NSURLSessionConfiguration+default.h" + + @implementation NSURLSession (sharedMPTCPSession) + + (NSURLSession *) sharedMPTCPSession { + static NSURLSession *sharedMPTCPSession = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSURLSessionConfiguration *conf = [NSURLSessionConfiguration defaultMPTCPConfiguration]; + // create a session with the MPTCP configuration if MPTCP is enabled, else fall back to TCP + sharedMPTCPSession = [NSURLSession sessionWithConfiguration:conf]; + }); + return sharedMPTCPSession; + } + @end diff --git a/Sources/Extensions/NSURLSessionConfiguration/NSURLSessionConfiguration+default.h b/Sources/Extensions/NSURLSessionConfiguration/NSURLSessionConfiguration+default.h new file mode 100644 index 000000000..3206a0181 --- /dev/null +++ b/Sources/Extensions/NSURLSessionConfiguration/NSURLSessionConfiguration+default.h @@ -0,0 +1,16 @@ +/***************************************************************************** + * NSURLSessionConfiguration+default.h + * VLC for iOS + ***************************************************************************** + * + * Author: Anthony Doeraene <anthony.doeraene@hotmail.com> + * + *****************************************************************************/ + + #ifndef NSURLSessionConfiguration_default_h + #define NSURLSessionConfiguration_default_h + // add a static var defaultMPTCPConfiguration to NSURLSessionConfiguration + @interface NSURLSessionConfiguration (defaultMPTCPConfiguration) + + (NSURLSessionConfiguration *) defaultMPTCPConfiguration; + @end + #endif /* NSURLSessionConfiguration_default_h */ diff --git a/Sources/Extensions/NSURLSessionConfiguration/NSURLSessionConfiguration+default.m b/Sources/Extensions/NSURLSessionConfiguration/NSURLSessionConfiguration+default.m new file mode 100644 index 000000000..723e7db9c --- /dev/null +++ b/Sources/Extensions/NSURLSessionConfiguration/NSURLSessionConfiguration+default.m @@ -0,0 +1,31 @@ +/***************************************************************************** + * NSURLSessionConfiguration+default.m + * VLC for iOS + ***************************************************************************** + * + * Author: Anthony Doeraene <anthony.doeraene@hotmail.com> + * + *****************************************************************************/ + + #import <Foundation/Foundation.h> + #include "NSURLSessionConfiguration+default.h" + + @implementation NSURLSessionConfiguration (defaultMPTCPConfiguration) + + (NSURLSessionConfiguration *) defaultMPTCPConfiguration { + static NSURLSessionConfiguration *defaultMPTCPConfiguration = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSURLSessionConfiguration *conf = [NSURLSessionConfiguration defaultSessionConfiguration]; + #if TARGET_OS_IOS + // multipath is only supported on iOS 11.0+ + if (@available(iOS 11.0, *)) { + conf.multipathServiceType = NSURLSessionMultipathServiceTypeHandover; + } + #endif + // on other platforms, the defaultMPTCPConfiguration is simply [NSURLSessionConfiguration defaultSessionConfiguration], which is a standard TCP configuration + + defaultMPTCPConfiguration = conf; + }); + return defaultMPTCPConfiguration; + } + @end diff --git a/Sources/Network/Download/VLCHTTPFileDownloader.m b/Sources/Network/Download/VLCHTTPFileDownloader.m index 21d37f94e..33ebf5221 100644 --- a/Sources/Network/Download/VLCHTTPFileDownloader.m +++ b/Sources/Network/Download/VLCHTTPFileDownloader.m @@ -15,6 +15,7 @@ #import "VLCActivityManager.h" #import "VLCMediaFileDiscoverer.h" #import "VLC-Swift.h" +#import "NSURLSessionConfiguration+default.h" @interface VLCHTTPFileDownloader () <NSURLSessionDelegate> { @@ -35,7 +36,7 @@ - (instancetype)init { if (self = [super init]) { - _urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] + _urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultMPTCPConfiguration] delegate:self delegateQueue:nil]; _downloadsAccessQueue = dispatch_queue_create("VLCHTTPFileDownloader.downloadsQueue", DISPATCH_QUEUE_SERIAL); diff --git a/Sources/Network/Open Network Stream/VLCOpenNetworkSubtitlesFinder.m b/Sources/Network/Open Network Stream/VLCOpenNetworkSubtitlesFinder.m index 25112f8f6..b5b7b3799 100644 --- a/Sources/Network/Open Network Stream/VLCOpenNetworkSubtitlesFinder.m +++ b/Sources/Network/Open Network Stream/VLCOpenNetworkSubtitlesFinder.m @@ -12,6 +12,7 @@ #import "VLCOpenNetworkSubtitlesFinder.h" #import "VLCPlaybackService.h" +#import "NSURLSession+sharedMPTCPSession.h" @implementation VLCOpenNetworkSubtitlesFinder @@ -61,7 +62,7 @@ NSURLResponse __block *urlResponse; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable _data, NSURLResponse * _Nullable _response, NSError * _Nullable _error) { + [[[NSURLSession sharedMPTCPSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable _data, NSURLResponse * _Nullable _response, NSError * _Nullable _error) { urlResponse = _response; erreur = _error; data = _data; diff --git a/Sources/Playback/Subtitles Downloading/VLCOpenSubtitlesDownloader.m b/Sources/Playback/Subtitles Downloading/VLCOpenSubtitlesDownloader.m index e1c7223e6..82198906a 100644 --- a/Sources/Playback/Subtitles Downloading/VLCOpenSubtitlesDownloader.m +++ b/Sources/Playback/Subtitles Downloading/VLCOpenSubtitlesDownloader.m @@ -8,6 +8,7 @@ // #import "VLCOpenSubtitlesDownloader.h" +#import "NSURLSession+sharedMPTCPSession.h" #define kVLCOpenSubtitlesDownloaderApiKey @"" @@ -45,7 +46,7 @@ static NSString * const kDomain = @"org.videolan.vlc-ios.openSubtitlesDownloader self = [super init]; if (!self) return nil; - _session = [NSURLSession sharedSession]; + _session = [NSURLSession sharedMPTCPSession]; _userAgent = userAgent; _apiKey = apiKey; diff --git a/Sources/UI Elements/VLCNetworkImageView.m b/Sources/UI Elements/VLCNetworkImageView.m index 3b53cd4fa..fe3f8b7ee 100644 --- a/Sources/UI Elements/VLCNetworkImageView.m +++ b/Sources/UI Elements/VLCNetworkImageView.m @@ -11,6 +11,7 @@ #import "VLCNetworkImageView.h" +#import "NSURLSession+sharedMPTCPSession.h" @implementation VLCNetworkImageView @@ -51,7 +52,7 @@ static NSCache *sharedImageCache = nil; self.image = cachedImage; } else { __weak typeof(self) weakSelf = self; - NSURLSession *sharedSession = [NSURLSession sharedSession]; + NSURLSession *sharedSession = [NSURLSession sharedMPTCPSession]; self.downloadTask = [sharedSession dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (!data) { return; diff --git a/VLC.xcodeproj/project.pbxproj b/VLC.xcodeproj/project.pbxproj index d061cecfb..f9811efb1 100644 --- a/VLC.xcodeproj/project.pbxproj +++ b/VLC.xcodeproj/project.pbxproj @@ -10,6 +10,16 @@ 0F007EB12C2B46440042A95F /* VLCMLMedia+isWatched.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F007EB02C2B46440042A95F /* VLCMLMedia+isWatched.m */; }; 0F47AB962C1C42FF004B630F /* LongPressPlaybackSpeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F47AB942C1C42FF004B630F /* LongPressPlaybackSpeedView.swift */; }; 13925DC647EC27EEB2818FD2 /* libPods-VLC-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B0E3BE184ED7B95399C04EB6 /* libPods-VLC-tvOS.a */; }; + 1B39FA082C73BBA200F6F960 /* NSURLSessionConfiguration+default.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B2DEE8F2C63D57300E414B4 /* NSURLSessionConfiguration+default.m */; }; + 1B39FA092C73BBA200F6F960 /* NSURLSessionConfiguration+default.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B2DEE8F2C63D57300E414B4 /* NSURLSessionConfiguration+default.m */; }; + 1B39FA0A2C73BBA300F6F960 /* NSURLSessionConfiguration+default.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B2DEE8F2C63D57300E414B4 /* NSURLSessionConfiguration+default.m */; }; + 1B39FA0B2C73BBA300F6F960 /* NSURLSessionConfiguration+default.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B2DEE8F2C63D57300E414B4 /* NSURLSessionConfiguration+default.m */; }; + 1B39FA0D2C73BBA400F6F960 /* NSURLSessionConfiguration+default.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B2DEE8F2C63D57300E414B4 /* NSURLSessionConfiguration+default.m */; }; + 1B39FA0E2C73BBA800F6F960 /* NSURLSession+sharedMPTCPSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B2DEE8C2C63D52000E414B4 /* NSURLSession+sharedMPTCPSession.m */; }; + 1B39FA0F2C73BBA800F6F960 /* NSURLSession+sharedMPTCPSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B2DEE8C2C63D52000E414B4 /* NSURLSession+sharedMPTCPSession.m */; }; + 1B39FA102C73BBA900F6F960 /* NSURLSession+sharedMPTCPSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B2DEE8C2C63D52000E414B4 /* NSURLSession+sharedMPTCPSession.m */; }; + 1B39FA112C73BBA900F6F960 /* NSURLSession+sharedMPTCPSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B2DEE8C2C63D52000E414B4 /* NSURLSession+sharedMPTCPSession.m */; }; + 1B39FA122C73BBA900F6F960 /* NSURLSession+sharedMPTCPSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B2DEE8C2C63D52000E414B4 /* NSURLSession+sharedMPTCPSession.m */; }; 226D57DD2B42C4710090AD88 /* VLCFavoriteService.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DA58D642B10E96800E5BA84 /* VLCFavoriteService.m */; }; 226D57E12B42FE060090AD88 /* FavoriteHeaderContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226D57E02B42FE060090AD88 /* FavoriteHeaderContentView.swift */; }; 226D57E22B42FE060090AD88 /* FavoriteHeaderContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226D57E02B42FE060090AD88 /* FavoriteHeaderContentView.swift */; }; @@ -520,6 +530,10 @@ 0F47AB942C1C42FF004B630F /* LongPressPlaybackSpeedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LongPressPlaybackSpeedView.swift; sourceTree = "<group>"; }; 1ACB60BB25275E5700405250 /* VLCFirstStepsBaseViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCFirstStepsBaseViewController.h; sourceTree = "<group>"; }; 1ACB60BC25275E5700405250 /* VLCFirstStepsBaseViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCFirstStepsBaseViewController.m; sourceTree = "<group>"; }; + 1B2DEE8B2C63D4FF00E414B4 /* NSURLSession+sharedMPTCPSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSURLSession+sharedMPTCPSession.h"; sourceTree = "<group>"; }; + 1B2DEE8C2C63D52000E414B4 /* NSURLSession+sharedMPTCPSession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSURLSession+sharedMPTCPSession.m"; sourceTree = "<group>"; }; + 1B2DEE8E2C63D54900E414B4 /* NSURLSessionConfiguration+default.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSURLSessionConfiguration+default.h"; sourceTree = "<group>"; }; + 1B2DEE8F2C63D57300E414B4 /* NSURLSessionConfiguration+default.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSURLSessionConfiguration+default.m"; sourceTree = "<group>"; }; 226D57E02B42FE060090AD88 /* FavoriteHeaderContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteHeaderContentView.swift; sourceTree = "<group>"; }; 226D57E82B4439080090AD88 /* FavoriteSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteSectionHeader.swift; sourceTree = "<group>"; }; 226D57EB2B4439880090AD88 /* VLCFavoriteListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VLCFavoriteListViewController.swift; sourceTree = "<group>"; }; @@ -1364,6 +1378,24 @@ path = Pods; sourceTree = "<group>"; }; + 1B2DEE8A2C63D4DE00E414B4 /* NSURLSession */ = { + isa = PBXGroup; + children = ( + 1B2DEE8B2C63D4FF00E414B4 /* NSURLSession+sharedMPTCPSession.h */, + 1B2DEE8C2C63D52000E414B4 /* NSURLSession+sharedMPTCPSession.m */, + ); + path = NSURLSession; + sourceTree = "<group>"; + }; + 1B2DEE8D2C63D53100E414B4 /* NSURLSessionConfiguration */ = { + isa = PBXGroup; + children = ( + 1B2DEE8E2C63D54900E414B4 /* NSURLSessionConfiguration+default.h */, + 1B2DEE8F2C63D57300E414B4 /* NSURLSessionConfiguration+default.m */, + ); + path = NSURLSessionConfiguration; + sourceTree = "<group>"; + }; 226D57E62B44379E0090AD88 /* iOS */ = { isa = PBXGroup; children = ( @@ -2079,6 +2111,8 @@ 7DC7BA7628C894DA00109F28 /* Extensions */ = { isa = PBXGroup; children = ( + 1B2DEE8D2C63D53100E414B4 /* NSURLSessionConfiguration */, + 1B2DEE8A2C63D4DE00E414B4 /* NSURLSession */, 7DC7BAB928C8959300109F28 /* URLs */, 7DC7BAA628C8957A00109F28 /* UIKit */, 7DC7BA9828C8955700109F28 /* UI Elements */, @@ -3494,10 +3528,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1B39FA122C73BBA900F6F960 /* NSURLSession+sharedMPTCPSession.m in Sources */, 4133CAE922CCB26C0047A4EC /* VLCAccessibilityIdentifier.swift in Sources */, 4133CAE722CCB0820047A4EC /* Screenshot.swift in Sources */, 4133CAE822CCB1250047A4EC /* TestHelper.swift in Sources */, 4133CAEA22CCB4E10047A4EC /* SnapshotHelper.swift in Sources */, + 1B39FA0D2C73BBA400F6F960 /* NSURLSessionConfiguration+default.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3505,6 +3541,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1B39FA112C73BBA900F6F960 /* NSURLSession+sharedMPTCPSession.m in Sources */, + 1B39FA0B2C73BBA300F6F960 /* NSURLSessionConfiguration+default.m in Sources */, 41533C9E2113392F00EC3ABA /* URLHandlerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3513,10 +3551,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1B39FA102C73BBA900F6F960 /* NSURLSession+sharedMPTCPSession.m in Sources */, CAA0B0F720726A0E00B9274E /* TestHelper.swift in Sources */, 8DD6516F208C89BC0052EE68 /* VLCAccessibilityIdentifier.swift in Sources */, CAA0B0F02072651A00B9274E /* XCUIElement+Helpers.swift in Sources */, CAA0B0ED2072651000B9274E /* VLCTestMenu.swift in Sources */, + 1B39FA0A2C73BBA300F6F960 /* NSURLSessionConfiguration+default.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3532,6 +3572,7 @@ DD8095E01BE3EFC20065D8E1 /* VLCPlaybackInfoTVViewController.m in Sources */, DD9D8F651C01F96700B4060F /* VLCPlaybackInfoChaptersTVViewController.m in Sources */, DDEAECFE1BDFFAEE00756C83 /* Reachability.m in Sources */, + 1B39FA092C73BBA200F6F960 /* NSURLSessionConfiguration+default.m in Sources */, DD3EFF561BDEBCE500B68579 /* VLCLocalNetworkServiceBrowserDSM.m in Sources */, 7D0C207128C896B800CCFFEF /* URL+isExcludedFromBackup.swift in Sources */, DDA1B90A1CE902EE0076BC45 /* VLCNetworkServerLoginInformation+Keychain.m in Sources */, @@ -3572,6 +3613,7 @@ 226D57DD2B42C4710090AD88 /* VLCFavoriteService.m in Sources */, 7D1A2DB11BF66335002E0962 /* VLCMDFBrowsingArtworkProvider.m in Sources */, DD3EFF501BDEBCE500B68579 /* VLCPlexParser.m in Sources */, + 1B39FA0F2C73BBA800F6F960 /* NSURLSession+sharedMPTCPSession.m in Sources */, 7D405ED31BEA11CD006ED886 /* VLCHTTPUploaderController.m in Sources */, 7DC869C328CB7E5700EE99F8 /* VLCNetworkLoginTVViewController.swift in Sources */, 7DF90B4B1BE7A8110059C0E3 /* IASKSpecifier.m in Sources */, @@ -3774,6 +3816,7 @@ DD3EFF3B1BDEBCE500B68579 /* VLCNetworkServerBrowserVLCMedia.m in Sources */, 8DB0D71924CED15C00915506 /* VideoPlayerControls.swift in Sources */, 41B93C051A53835300102E8B /* VLCCloudServiceCell.m in Sources */, + 1B39FA082C73BBA200F6F960 /* NSURLSessionConfiguration+default.m in Sources */, 8DE1887421089B3A00A091D2 /* MediaLibraryBaseModel.swift in Sources */, 7D6D2CEF2A0A6056001604B3 /* VLCOneDriveController.m in Sources */, 7D745DD12B88CEBE004B4DCD /* VLCSEPA.m in Sources */, @@ -3880,6 +3923,7 @@ 444E5BFA24C6081B0003B69C /* PasscodeLockController.swift in Sources */, 7D6D2CEE2A0A6056001604B3 /* VLCOneDriveTableViewController.m in Sources */, 7D30F3DF183AB31E00FFC021 /* VLCWiFiUploadTableViewCell.m in Sources */, + 1B39FA0E2C73BBA800F6F960 /* NSURLSession+sharedMPTCPSession.m in Sources */, 7D30F3EA183AB34200FFC021 /* VLCGoogleDriveController.m in Sources */, 7D37196428F1F0B800C5A9F2 /* VLCAppSceneDelegate.m in Sources */, 417D7F601F7BA26200DDF36A /* VLCRemoteControlService.m in Sources */, -- GitLab