diff --git a/Makefile b/Makefile
index adfaa409..2d7cee83 100644
--- a/Makefile
+++ b/Makefile
@@ -36,7 +36,9 @@ LDFLAGS += -ldl -lpthread -lm
ifeq ($(CONFIG_LIBICONV),yes)
LDFLAGS += -liconv
endif
-ifneq ($(PLATFORM), darwin)
+ifeq ($(PLATFORM), darwin)
+LDFLAGS += -framework CoreServices
+else
LDFLAGS += -lrt
endif
@@ -261,6 +263,9 @@ SRCS-${CONFIG_INOTIFY} += \
# Avahi
SRCS-$(CONFIG_AVAHI) += src/avahi.c
+# Bonjour
+SRCS-$(CONFIG_BONJOUR) += src/bonjour.c
+
# libav
SRCS-$(CONFIG_LIBAV) += src/libav.c \
src/muxer/muxer_libav.c \
diff --git a/configure b/configure
index a637379f..e125992d 100755
--- a/configure
+++ b/configure
@@ -172,6 +172,8 @@ fi
#
if [ ${PLATFORM} = "darwin" ]; then
disable linuxdvb
+ disable avahi
+ enable bonjour
fi
#
diff --git a/src/bonjour.c b/src/bonjour.c
new file mode 100644
index 00000000..b9e0205d
--- /dev/null
+++ b/src/bonjour.c
@@ -0,0 +1,120 @@
+/*
+ * Bonjour service publisher
+ * Copyright (C) 2014 Damjan Marion
+ *
+ * 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 3 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, see .
+ */
+
+#include
+#include
+#include
+
+#include "tvheadend.h"
+#include "bonjour.h"
+
+#include
+
+typedef struct {
+ char *key;
+ char *value;
+} txt_rec_t;
+
+pthread_t bonjour_tid;
+CFNetServiceRef svc_http, svc_htsp;
+
+static void
+bonjour_callback(CFNetServiceRef theService, CFStreamError* error, void* info)
+{
+ if (error->error) {
+ tvhlog(LOG_ERR, "bonjour", "callback error (domain = %ld, error =%d)",
+ error->domain, error->error);
+ }
+}
+
+static void
+bonjour_start_service(CFNetServiceRef *svc, char *service_type,
+ uint32_t port, txt_rec_t *txt)
+{
+ CFStringRef str;
+ CFStreamError error = {0};
+ CFNetServiceClientContext context = {0, NULL, NULL, NULL, NULL};
+
+ str = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, service_type,
+ kCFStringEncodingASCII,
+ kCFAllocatorNull);
+
+ *svc = CFNetServiceCreate(NULL, CFSTR(""), str, CFSTR("Tvheadend"), port);
+ if (!*svc) {
+ tvhlog(LOG_ERR, "bonjour", "service creation failed");
+ return;
+ }
+
+ CFNetServiceSetClient(*svc, bonjour_callback, &context);
+ CFNetServiceScheduleWithRunLoop(*svc, CFRunLoopGetCurrent(),
+ kCFRunLoopCommonModes);
+
+ if (txt) {
+ CFDataRef data = NULL;
+ CFMutableDictionaryRef dict;
+ dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ while(txt->key) {
+ str = CFStringCreateWithCString (NULL, txt->key, kCFStringEncodingASCII);
+ data = CFDataCreate (NULL, (uint8_t *) txt->value, strlen(txt->value));
+ CFDictionaryAddValue(dict, str, data);
+ txt++;
+ }
+
+ data = CFNetServiceCreateTXTDataWithDictionary(NULL, dict);
+ CFNetServiceSetTXTData(*svc, data);
+ CFRelease(data);
+ CFRelease(dict);
+ }
+
+ if (!CFNetServiceRegisterWithOptions(*svc, 0, &error))
+ tvhlog(LOG_ERR, "bonjour", "registration failed (service type = %s, "
+ "domain = %ld, error =%d)", service_type, error.domain, error.error);
+ else
+ tvhlog(LOG_INFO, "bonjour", "service '%s' successfully established",
+ service_type);
+}
+
+static void
+bonjour_stop_service(CFNetServiceRef *svc)
+{
+ CFNetServiceUnscheduleFromRunLoop(*svc, CFRunLoopGetCurrent(),
+ kCFRunLoopCommonModes);
+ CFNetServiceSetClient(*svc, NULL, NULL);
+ CFRelease(*svc);
+}
+
+void
+bonjour_init(void)
+{
+ txt_rec_t txt_rec_http[] = {
+ { "path", "/" },
+ { .key = NULL }
+ };
+
+ bonjour_start_service(&svc_http, "_http._tcp", 9981, txt_rec_http);
+ bonjour_start_service(&svc_htsp, "_htsp._tcp", 9982, NULL);
+}
+
+void
+bonjour_done(void)
+{
+ bonjour_stop_service(&svc_http);
+ bonjour_stop_service(&svc_htsp);
+}
diff --git a/src/bonjour.h b/src/bonjour.h
new file mode 100644
index 00000000..0e20c5f1
--- /dev/null
+++ b/src/bonjour.h
@@ -0,0 +1,7 @@
+#ifdef CONFIG_BONJOUR
+void bonjour_init(void);
+void bonjour_done(void);
+#else
+static inline void bonjour_init(void) { }
+static inline void bonjour_done(void) { }
+#endif
diff --git a/src/main.c b/src/main.c
index 0eda2218..1f32b88d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -51,6 +51,7 @@
#include "dvr/dvr.h"
#include "htsp_server.h"
#include "avahi.h"
+#include "bonjour.h"
#include "input.h"
#include "service.h"
#include "trap.h"
@@ -812,6 +813,7 @@ main(int argc, char **argv)
subscription_dummy_join(opt_subscribe, 1);
avahi_init();
+ bonjour_init();
epg_updated(); // cleanup now all prev ref's should have been created
@@ -874,6 +876,7 @@ main(int argc, char **argv)
tvhftrace("main", access_done);
tvhftrace("main", epg_done);
tvhftrace("main", avahi_done);
+ tvhftrace("main", bonjour_done);
tvhftrace("main", imagecache_done);
tvhftrace("main", idnode_done);
tvhftrace("main", lang_code_done);