[vlc-devel] [PATCH] bonjour rd: Add bonjour renderer discovery submodule

Marvin Scholz epirat07 at gmail.com
Mon Dec 5 11:49:55 CET 2016


Add a bonjour renderer submodule to the bonjour service discovery
module, so it can discover chromecast renderers (for now) and others
in the future.
There is still some work needed to make it detect chromecast
capabilities correctly and to not hardcode it to chromecast.
(See the TODO comment)
---
 modules/services_discovery/bonjour.m | 316 ++++++++++++++++++++++++++---------
 1 file changed, 239 insertions(+), 77 deletions(-)

diff --git a/modules/services_discovery/bonjour.m b/modules/services_discovery/bonjour.m
index 0cc076b..04feac6 100644
--- a/modules/services_discovery/bonjour.m
+++ b/modules/services_discovery/bonjour.m
@@ -28,13 +28,30 @@
 #include <vlc_plugin.h>
 #include <vlc_modules.h>
 #include <vlc_services_discovery.h>
+#include <vlc_renderer_discovery.h>
 
 #import <Foundation/Foundation.h>
 
-static int Open( vlc_object_t * );
-static void Close( vlc_object_t * );
+#pragma mark Function declarations
+
+static int OpenSD( vlc_object_t * );
+static void CloseSD( vlc_object_t * );
+
+static int OpenRD( vlc_object_t * );
+static void CloseRD( vlc_object_t * );
 
 VLC_SD_PROBE_HELPER( "Bonjour", N_("Bonjour Network Discovery"), SD_CAT_LAN )
+VLC_RD_PROBE_HELPER( "Bonjour_renderer", "Bonjour Renderer Discovery" )
+
+struct services_discovery_sys_t
+{
+    CFTypeRef _Nullable discoveryController;
+};
+
+struct vlc_renderer_discovery_sys
+{
+    CFTypeRef _Nullable discoveryController;
+};
 
 /*
  * Module descriptor
@@ -45,81 +62,138 @@
     set_category( CAT_PLAYLIST )
     set_subcategory( SUBCAT_PLAYLIST_SD )
     set_capability( "services_discovery", 0 )
-    set_callbacks( Open, Close )
+    set_callbacks( OpenSD, CloseSD )
     add_shortcut( "mdns", "bonjour" )
-
     VLC_SD_PROBE_SUBMODULE
-vlc_module_end ()
-
-NSString *const VLCBonjourProtocolName = @"VLCBonjourProtocolName";
-NSString *const VLCBonjourProtocolServiceName = @"VLCBonjourProtocolServiceName";
-
+    add_submodule() \
+        set_description( N_( "Bonjour Renderer Discovery" ) )
+        set_category( CAT_SOUT )
+        set_subcategory( SUBCAT_SOUT_RENDERER )
+        set_capability( "renderer_discovery", 0 )
+        set_callbacks( OpenRD, CloseRD )
+        add_shortcut( "mdns_renderer", "bonjour_renderer" )
+        VLC_RD_PROBE_SUBMODULE
+vlc_module_end()
+
+NSString *const VLCBonjourProtocolName          = @"VLCBonjourProtocolName";
+NSString *const VLCBonjourProtocolServiceName   = @"VLCBonjourProtocolServiceName";
+NSString *const VLCBonjourIsRenderer            = @"VLCBonjourIsRenderer";
+NSString *const VLCBonjourRendererFlags         = @"VLCBonjourRendererFlags";
+NSString *const VLCBonjourRendererDemux         = @"VLCBonjourRendererDemux";
+
+#pragma mark -
+#pragma mark Interface definition
 @interface VLCNetServiceDiscoveryController : NSObject <NSNetServiceBrowserDelegate, NSNetServiceDelegate>
 {
-#ifdef MAC_OS_X_VERSION_10_11
-    NSArray<NSNetServiceBrowser *> *_serviceBrowsers;
-
-    NSMutableArray<NSNetService *> *_rawNetServices;
-    NSMutableArray<NSNetService *> *_resolvedNetServices;
-    NSMutableArray<NSValue *> *_inputItemsForNetServices;
-#else
+    /* Stores all used service browsers, one for each protocol, usually */
     NSArray *_serviceBrowsers;
 
+    /* Holds a required reference to all NSNetServices */
     NSMutableArray *_rawNetServices;
+
+    /* Holds all successfully resolved NSNetServices */
     NSMutableArray *_resolvedNetServices;
+
+    /* Holds the respective pointers to a vlc_object for each resolved and added NSNetService */
     NSMutableArray *_inputItemsForNetServices;
-#endif
 
+    /* Stores all protocols that are currently discovered */
     NSArray *_activeProtocols;
 }
 
- at property (readwrite, nonatomic) services_discovery_t *p_sd;
+ at property (readonly) BOOL isRendererDiscovery;
+ at property (readonly, nonatomic) vlc_object_t *p_this;
+
+- (instancetype)initWithRendererDiscoveryObject:(vlc_renderer_discovery_t *)p_rd;
+- (instancetype)initWithServicesDiscoveryObject:(services_discovery_t *)p_sd;
 
 - (void)startDiscovery;
 - (void)stopDiscovery;
 
 @end
 
-struct services_discovery_sys_t
+ at implementation VLCNetServiceDiscoveryController
+
+- (instancetype)initWithRendererDiscoveryObject:(vlc_renderer_discovery_t *)p_rd
 {
-    CFTypeRef _Nullable discoveryController;
-};
+    self = [super init];
+    if (self) {
+        _p_this = VLC_OBJECT( p_rd );
+        _isRendererDiscovery = YES;
+    }
 
- at implementation VLCNetServiceDiscoveryController
+    return self;
+}
+
+- (instancetype)initWithServicesDiscoveryObject:(services_discovery_t *)p_sd
+{
+    self = [super init];
+    if (self) {
+        _p_this = VLC_OBJECT( p_sd );
+        _isRendererDiscovery = NO;
+    }
+
+    return self;
+}
 
 - (void)startDiscovery
 {
+    NSDictionary *VLCFtpProtocol = @{ VLCBonjourProtocolName        : @"ftp",
+                                      VLCBonjourProtocolServiceName : @"_ftp._tcp.",
+                                      VLCBonjourIsRenderer          : @(NO)
+                                      };
+    NSDictionary *VLCSmbProtocol = @{ VLCBonjourProtocolName        : @"smb",
+                                      VLCBonjourProtocolServiceName : @"_smb._tcp.",
+                                      VLCBonjourIsRenderer          : @(NO)
+                                      };
+    NSDictionary *VLCNfsProtocol = @{ VLCBonjourProtocolName        : @"nfs",
+                                      VLCBonjourProtocolServiceName : @"_nfs._tcp.",
+                                      VLCBonjourIsRenderer          : @(NO)
+                                      };
+    NSDictionary *VLCSftpProtocol = @{ VLCBonjourProtocolName       : @"sftp",
+                                       VLCBonjourProtocolServiceName: @"_sftp-ssh._tcp.",
+                                       VLCBonjourIsRenderer         : @(NO)
+                                       };
+    NSDictionary *VLCCastProtocol = @{ VLCBonjourProtocolName       : @"chromecast",
+                                       VLCBonjourProtocolServiceName: @"_googlecast._tcp.",
+                                       VLCBonjourIsRenderer         : @(YES),
+                                       VLCBonjourRendererFlags      : @(VLC_RENDERER_CAN_AUDIO),
+                                       VLCBonjourRendererDemux      : @"cc_demux"
+                                       };
+
+    NSArray *VLCSupportedProtocols = @[VLCFtpProtocol,
+                                      VLCSmbProtocol,
+                                      VLCNfsProtocol,
+                                      VLCSftpProtocol,
+                                      VLCCastProtocol];
+
     _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];
+    msg_Info(_p_this, "starting discovery");
+    for (NSDictionary *protocol in VLCSupportedProtocols) {
+        msg_Info(_p_this, "looking up %s", [protocol[VLCBonjourProtocolName] UTF8String]);
+        /* Only discover hosts if we actually have a module that can handle those */
+#if 0
+        if (!module_exists([protocol[VLCBonjourProtocolName] UTF8String])) {
+            msg_Info(_p_this, "no module for %s, skipping", [protocol[VLCBonjourProtocolName] UTF8String]);
+            continue;
+        }
+#endif
 
-        /* only discover hosts if we actually have a module that can handle those */
-        if (!module_exists([protocol[VLCBonjourProtocolName] UTF8String]))
+        /* Only discover hosts it they match the current mode (renderer or service) */
+        if ([protocol[VLCBonjourIsRenderer] boolValue] != _isRendererDiscovery) {
+            msg_Info(_p_this, "%s does not match current discovery mode, skipping", [protocol[VLCBonjourProtocolName] UTF8String]);
             continue;
+        }
 
         NSNetServiceBrowser *serviceBrowser = [[NSNetServiceBrowser alloc] init];
-        serviceBrowser.delegate = self;
+        [serviceBrowser setDelegate:self];
+        msg_Info(_p_this, "starting discovery for type %s", [protocol[VLCBonjourProtocolServiceName] UTF8String]);
         [serviceBrowser searchForServicesOfType:protocol[VLCBonjourProtocolServiceName] inDomain:@"local."];
         [discoverers addObject:serviceBrowser];
         [protocols addObject:protocol];
@@ -133,24 +207,26 @@ - (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);
+    for (NSValue *item in _inputItemsForNetServices) {
+        if (_isRendererDiscovery) {
+            [self removeRawRendererItem:item];
+        } else {
+            [self removeRawInputItem:item];
         }
     }
 
     [_inputItemsForNetServices removeAllObjects];
     [_resolvedNetServices removeAllObjects];
+    msg_Info(_p_this, "stopped discovery");
 }
 
-#pragma mark - functional delegation
+#pragma mark - 
+#pragma mark Delegate methods
 
 - (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]);
+    msg_Info(_p_this, "found something, looking up");
+    msg_Dbg(self.p_this, "found bonjour service: %s (%s)", [aNetService.name UTF8String], [aNetService.type UTF8String]);
     [_rawNetServices addObject:aNetService];
     aNetService.delegate = self;
     [aNetService resolveWithTimeout:5.];
@@ -158,50 +234,48 @@ - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindServi
 
 - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing
 {
-    msg_Dbg(self.p_sd, "bonjour service disappeared: %s", [aNetService.name UTF8String]);
+    msg_Dbg(self.p_this, "bonjour service disappeared: %s", [aNetService.name UTF8String]);
+
+    /* If the item was not looked-up yet, just remove it */
     if ([_rawNetServices containsObject:aNetService])
         [_rawNetServices removeObject:aNetService];
 
+    /* If the item was already resolved, the associated input or renderer items needs to be removed as well */
     if ([_resolvedNetServices containsObject:aNetService]) {
         NSInteger index = [_resolvedNetServices indexOfObject:aNetService];
-        if (index == NSNotFound)
+        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);
+
+        if (_isRendererDiscovery) {
+            [self removeRawRendererItem:_inputItemsForNetServices[index]];
+        } else {
+            [self removeRawInputItem:_inputItemsForNetServices[index]];
         }
+
+        /* Remove item pointer from our lookup array */
+        [_inputItemsForNetServices removeObjectAtIndex:index];
     }
 }
 
 - (void)netServiceDidResolveAddress:(NSNetService *)aNetService
 {
+    msg_Info(_p_this, "resolved something");
     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];
+        for (NSDictionary *protocolDefinition in _activeProtocols) {
             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_NewDirectory([uri UTF8String],
-                                                             [aNetService.name UTF8String],
-                                                             ITEM_NET );
-
-        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];
+        if (_isRendererDiscovery) {
+            [self addResolvedRendererItem:aNetService withProtocol:protocol];
+        } else {
+            [self addResolvedInputItem:aNetService withProtocol:protocol];
         }
     }
 
@@ -210,13 +284,69 @@ - (void)netServiceDidResolveAddress:(NSNetService *)aNetService
 
 - (void)netService:(NSNetService *)aNetService didNotResolve:(NSDictionary *)errorDict
 {
-    msg_Dbg(self.p_sd, "failed to resolve: %s", [aNetService.name UTF8String]);
+    msg_Dbg(_p_this, "failed to resolve: %s", [aNetService.name UTF8String]);
     [_rawNetServices removeObject:aNetService];
 }
 
+#pragma mark -
+#pragma mark Helper methods
+
+- (void)addResolvedRendererItem:(NSNetService *)netService withProtocol:(NSString *)protocol
+{
+    vlc_renderer_discovery_t *p_rd = (vlc_renderer_discovery_t *)_p_this;
+
+    NSString *uri = [NSString stringWithFormat:@"%@://%@:%ld", protocol, netService.hostName, netService.port];
+    NSDictionary *txtDict = [NSNetService dictionaryFromTXTRecordData:[netService TXTRecordData]];
+
+    // TODO: Detect rendered capabilities and adapt to work with not just chromecast
+    vlc_renderer_item_t *p_renderer_item = vlc_renderer_item_new( "chromecast", [netService.name UTF8String],
+                                                                 [uri UTF8String], NULL, "cc_demux",
+                                                                 "", VLC_RENDERER_CAN_VIDEO );
+    if (p_renderer_item != NULL) {
+        vlc_rd_add_item( p_rd, p_renderer_item );
+        [_inputItemsForNetServices addObject:[NSValue valueWithPointer:p_renderer_item]];
+        [_resolvedNetServices addObject:netService];
+    }
+}
+
+- (void)removeRawRendererItem:(NSValue *)item
+{
+    vlc_renderer_discovery_t *p_rd = (vlc_renderer_discovery_t *)_p_this;
+    vlc_renderer_item_t *input_item = [item pointerValue];
+
+    if (input_item != NULL) {
+        vlc_rd_remove_item( p_rd, input_item );
+        vlc_renderer_item_release( input_item );
+    }
+}
+
+- (void)addResolvedInputItem:(NSNetService *)netService withProtocol:(NSString *)protocol
+{
+    services_discovery_t *p_sd = (services_discovery_t *)_p_this;
+
+    NSString *uri = [NSString stringWithFormat:@"%@://%@:%ld", protocol, netService.hostName, netService.port];
+    input_item_t *p_input_item = input_item_NewDirectory([uri UTF8String], [netService.name UTF8String], ITEM_NET );
+    if (p_input_item != NULL) {
+        services_discovery_AddItem( p_sd, p_input_item, NULL);
+        [_inputItemsForNetServices addObject:[NSValue valueWithPointer:p_input_item]];
+        [_resolvedNetServices addObject:netService];
+    }
+}
+
+- (void)removeRawInputItem:(NSValue *)item
+{
+    services_discovery_t *p_sd = (services_discovery_t *)_p_this;
+    input_item_t *input_item = [item pointerValue];
+
+    if (input_item != NULL) {
+        services_discovery_RemoveItem( p_sd, input_item );
+        input_item_Release( input_item );
+    }
+}
+
 @end
 
-static int Open(vlc_object_t *p_this)
+static int OpenSD(vlc_object_t *p_this)
 {
     services_discovery_t *p_sd = (services_discovery_t *)p_this;
     services_discovery_sys_t *p_sys = NULL;
@@ -228,8 +358,7 @@ static int Open(vlc_object_t *p_this)
 
     p_sd->description = _("Bonjour Network Discovery");
 
-    VLCNetServiceDiscoveryController *discoveryController = [[VLCNetServiceDiscoveryController alloc] init];
-    discoveryController.p_sd = p_sd;
+    VLCNetServiceDiscoveryController *discoveryController = [[VLCNetServiceDiscoveryController alloc] initWithServicesDiscoveryObject:p_sd];
 
     p_sys->discoveryController = CFBridgingRetain(discoveryController);
 
@@ -238,7 +367,7 @@ static int Open(vlc_object_t *p_this)
     return VLC_SUCCESS;
 }
 
-static void Close(vlc_object_t *p_this)
+static void CloseSD(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;
@@ -251,3 +380,36 @@ static void Close(vlc_object_t *p_this)
 
     free(p_sys);
 }
+
+static int OpenRD(vlc_object_t *p_this)
+{
+    vlc_renderer_discovery_t *p_rd = (vlc_renderer_discovery_t *)p_this;
+    vlc_renderer_discovery_sys *p_sys = NULL;
+
+    p_rd->p_sys = p_sys = calloc(1, sizeof(vlc_renderer_discovery_sys));
+    if (!p_sys) {
+        return VLC_ENOMEM;
+    }
+
+    VLCNetServiceDiscoveryController *discoveryController = [[VLCNetServiceDiscoveryController alloc] initWithRendererDiscoveryObject:p_rd];
+
+    p_sys->discoveryController = CFBridgingRetain(discoveryController);
+
+    [discoveryController startDiscovery];
+
+    return VLC_SUCCESS;
+}
+
+static void CloseRD(vlc_object_t *p_this)
+{
+    vlc_renderer_discovery_t *p_rd = (vlc_renderer_discovery_t *)p_this;
+    vlc_renderer_discovery_sys *p_sys = p_rd->p_sys;
+
+    VLCNetServiceDiscoveryController *discoveryController = (__bridge VLCNetServiceDiscoveryController *)(p_sys->discoveryController);
+    [discoveryController stopDiscovery];
+
+    CFBridgingRelease(p_sys->discoveryController);
+    discoveryController = nil;
+
+    free(p_sys);
+}
-- 
2.9.3 (Apple Git-75)



More information about the vlc-devel mailing list