From 1baae638b5759ff092c7977ab17185975f7e6524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Paul=20K=C3=BChne?= Date: Tue, 9 Feb 2016 13:59:12 +0100 Subject: [PATCH] Add Bonjour service discovery module --- NEWS | 1 + modules/MODULES_LIST | 1 + modules/services_discovery/Makefile.am | 6 + modules/services_discovery/bonjour.m | 242 +++++++++++++++++++++++++ po/POTFILES.in | 1 + 5 files changed, 251 insertions(+) create mode 100644 modules/services_discovery/bonjour.m diff --git a/NEWS b/NEWS index 3e69c3ac073a..46b007dda22e 100644 --- a/NEWS +++ b/NEWS @@ -142,6 +142,7 @@ Muxers: Service Discovery: * New NetBios service discovery using libdsm * New mDNS services discovery using libmicrodns + * New mDNS services discovery using Bonjour (Mac OS X, tvOS, iOS) * Rewrite of the UPnP service discovery Mac OS X Interface diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST index 394b3db82b21..e3764127df25 100644 --- a/modules/MODULES_LIST +++ b/modules/MODULES_LIST @@ -62,6 +62,7 @@ $Id$ * avio: Access and Stream output module using libavformat network * ball: Augmented reality ball video filter module * bandlimited_resampler: Bandlimited interpolation audio resampler + * bonjour: mDNS services discovery module based on Bonjour * blend: a picture filter that blends two pictures * blendbench: a picture filter that test performance of blending routines * bluescreen: Bluescreen (weather channel like) video filter diff --git a/modules/services_discovery/Makefile.am b/modules/services_discovery/Makefile.am index e72a55883c59..d2fcebb05a31 100644 --- a/modules/services_discovery/Makefile.am +++ b/modules/services_discovery/Makefile.am @@ -76,3 +76,9 @@ libmicrodns_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(sddir)' sd_LTLIBRARIES += $(LTLIBmicrodns) EXTRA_LTLIBRARIES += libmicrodns_plugin.la +libbonjour_plugin_la_SOURCES = services_discovery/bonjour.m +libbonjour_plugin_la_OBJCFLAGS = $(AM_CFLAGS) -fobjc-arc +libbonjour_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(sddir)' -Wl,-framework,Foundation +if HAVE_DARWIN +sd_LTLIBRARIES += libbonjour_plugin.la +endif diff --git a/modules/services_discovery/bonjour.m b/modules/services_discovery/bonjour.m new file mode 100644 index 000000000000..cadddee3cb9b --- /dev/null +++ b/modules/services_discovery/bonjour.m @@ -0,0 +1,242 @@ +/***************************************************************************** + * bonjour.m: mDNS services discovery module based on Bonjour + ***************************************************************************** + * Copyright (C) 2016 VLC authors, VideoLAN and VideoLabs + * + * Authors: Felix Paul Kühne + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#import + +static int Open( vlc_object_t * ); +static void Close( vlc_object_t * ); + +VLC_SD_PROBE_HELPER( "Bonjour", "Bonjour Network Discovery", SD_CAT_LAN ) + +/* + * Module descriptor + */ +vlc_module_begin() +set_shortname( "bonjour" ) +set_description( N_( "Bonjour Network Discovery" ) ) +set_category( CAT_PLAYLIST ) +set_subcategory( SUBCAT_PLAYLIST_SD ) +set_capability( "services_discovery", 0 ) +set_callbacks( Open, Close ) +VLC_SD_PROBE_SUBMODULE +vlc_module_end () + +NSString *const VLCBonjourProtocolName = @"VLCBonjourProtocolName"; +NSString *const VLCBonjourProtocolServiceName = @"VLCBonjourProtocolServiceName"; + +@interface VLCNetServiceDiscoveryController : NSObject +{ + NSArray *_serviceBrowsers; + + NSMutableArray *_rawNetServices; + NSMutableArray *_resolvedNetServices; + NSMutableArray *_inputItemsForNetServices; + + NSArray *_activeProtocols; +} + +@property (readwrite, nonatomic) services_discovery_t *p_sd; + +- (void)startDiscovery; +- (void)stopDiscovery; + +@end + +struct services_discovery_sys_t +{ + CFTypeRef _Nullable discoveryController; +}; + +@implementation VLCNetServiceDiscoveryController + +- (void)startDiscovery +{ + _rawNetServices = [[NSMutableArray alloc] init]; + _resolvedNetServices = [[NSMutableArray alloc] init]; + _inputItemsForNetServices = [[NSMutableArray alloc] init]; + + NSDictionary *VLCFtpProtocol = @{ VLCBonjourProtocolName : @"ftp", + VLCBonjourProtocolServiceName : @"_ftp._tcp." }; + NSDictionary *VLCSmbProtocol = @{ VLCBonjourProtocolName : @"smb", + VLCBonjourProtocolServiceName : @"_smb._tcp." }; + NSDictionary *VLCNfsProtocol = @{ VLCBonjourProtocolName : @"nfs", + VLCBonjourProtocolServiceName : @"_nfs._tcp." }; + NSDictionary *VLCSftpProtocol = @{ VLCBonjourProtocolName : @"sftp", + VLCBonjourProtocolServiceName : @"_sftp-ssh._tcp." }; + + NSArray *VLCSupportedProtocols = @[VLCFtpProtocol, + VLCSmbProtocol, + VLCNfsProtocol, + VLCSftpProtocol]; + + NSUInteger count = VLCSupportedProtocols.count; + NSMutableArray *discoverers = [[NSMutableArray alloc] init]; + NSMutableArray *protocols = [[NSMutableArray alloc] init]; + + for (NSUInteger i = 0; i < count; i++) { + NSDictionary *protocol = VLCSupportedProtocols[i]; + + /* only discover hosts if we actually have a module that can handle those */ + if (!module_exists([protocol[VLCBonjourProtocolName] UTF8String])) + continue; + + NSNetServiceBrowser *serviceBrowser = [[NSNetServiceBrowser alloc] init]; + serviceBrowser.delegate = self; + [serviceBrowser searchForServicesOfType:protocol[VLCBonjourProtocolServiceName] inDomain:@"local."]; + [discoverers addObject:serviceBrowser]; + [protocols addObject:protocol]; + } + + _serviceBrowsers = [discoverers copy]; + _activeProtocols = [protocols copy]; +} + +- (void)stopDiscovery +{ + [_serviceBrowsers makeObjectsPerformSelector:@selector(stop)]; + + NSUInteger inputItemCount = _inputItemsForNetServices.count; + for (NSUInteger i = 0; i < inputItemCount; i++) { + input_item_t *p_input_item = [_inputItemsForNetServices[i] pointerValue]; + if (p_input_item != NULL) { + services_discovery_RemoveItem(self.p_sd, p_input_item); + input_item_Release(p_input_item); + } + } + + [_inputItemsForNetServices removeAllObjects]; + [_resolvedNetServices removeAllObjects]; +} + +#pragma mark - functional delegation + +- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing +{ + msg_Dbg(self.p_sd, "found bonjour service: %s (%s)", [aNetService.name UTF8String], [aNetService.type UTF8String]); + [_rawNetServices addObject:aNetService]; + aNetService.delegate = self; + [aNetService resolveWithTimeout:5.]; +} + +- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing +{ + msg_Dbg(self.p_sd, "bonjour service disappeared: %s", [aNetService.name UTF8String]); + if ([_rawNetServices containsObject:aNetService]) + [_rawNetServices removeObject:aNetService]; + + if ([_resolvedNetServices containsObject:aNetService]) { + NSInteger index = [_resolvedNetServices indexOfObject:aNetService]; + if (index == NSNotFound) + return; + + [_resolvedNetServices removeObjectAtIndex:index]; + input_item_t *p_input_item = [_inputItemsForNetServices[index] pointerValue]; + if (p_input_item != NULL) { + services_discovery_RemoveItem(self.p_sd, p_input_item); + input_item_Release(p_input_item); + } + } +} + +- (void)netServiceDidResolveAddress:(NSNetService *)aNetService +{ + if (![_resolvedNetServices containsObject:aNetService]) { + NSString *serviceType = aNetService.type; + NSUInteger count = _activeProtocols.count; + NSString *protocol = nil; + for (NSUInteger i = 0; i < count; i++) { + NSDictionary *protocolDefinition = _activeProtocols[i]; + if ([serviceType isEqualToString:protocolDefinition[VLCBonjourProtocolServiceName]]) { + protocol = protocolDefinition[VLCBonjourProtocolName]; + } + } + + NSString *uri = [NSString stringWithFormat:@"%@://%@:%ld", + protocol, + aNetService.hostName, + aNetService.port]; + + input_item_t *p_input_item = input_item_NewWithTypeExt([uri UTF8String], + [aNetService.name UTF8String], + 0, NULL, 0, -1, + ITEM_TYPE_NODE, true ); + + if (p_input_item != NULL) { + services_discovery_AddItem(self.p_sd, p_input_item, NULL); + [_inputItemsForNetServices addObject:[NSValue valueWithPointer:p_input_item]]; + [_resolvedNetServices addObject:aNetService]; + } + } + + [_rawNetServices removeObject:aNetService]; +} + +- (void)netService:(NSNetService *)aNetService didNotResolve:(NSDictionary *)errorDict +{ + msg_Dbg(self.p_sd, "failed to resolve: %s", [aNetService.name UTF8String]); + [_rawNetServices removeObject:aNetService]; +} + +@end + +static int Open(vlc_object_t *p_this) +{ + services_discovery_t *p_sd = (services_discovery_t *)p_this; + services_discovery_sys_t *p_sys = NULL; + + p_sd->p_sys = p_sys = calloc(1, sizeof(services_discovery_sys_t)); + if (!p_sys) { + return VLC_ENOMEM; + } + + VLCNetServiceDiscoveryController *discoveryController = [[VLCNetServiceDiscoveryController alloc] init]; + discoveryController.p_sd = p_sd; + + p_sys->discoveryController = CFBridgingRetain(discoveryController); + + [discoveryController startDiscovery]; + + return VLC_SUCCESS; +} + +static void Close(vlc_object_t *p_this) +{ + services_discovery_t *p_sd = (services_discovery_t *)p_this; + services_discovery_sys_t *p_sys = p_sd->p_sys; + + VLCNetServiceDiscoveryController *discoveryController = (__bridge VLCNetServiceDiscoveryController *)(p_sys->discoveryController); + [discoveryController stopDiscovery]; + + CFBridgingRelease(p_sys->discoveryController); + discoveryController = nil; + + free(p_sys); +} diff --git a/po/POTFILES.in b/po/POTFILES.in index 68b7f49b0a89..a85ce89c1aee 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1014,6 +1014,7 @@ modules/packetizer/mpeg4video.c modules/packetizer/mpegvideo.c modules/packetizer/vc1.c modules/services_discovery/avahi.c +modules/services_discovery/bonjour.m modules/services_discovery/mediadirs.c modules/services_discovery/mtp.c modules/services_discovery/os2drive.c