Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

[AirTunes] receive and show music metadata #946

Merged
merged 2 commits into from

6 participants

@Memphiz
Owner

This adds the ability to AirTunesServer to receive metadata (currently only iTunes clients send them with RSA encryption. All iOS devices send metadata with FairPlay encryption only which is not hacked yet - so no metadata from iOS clients).

Basically this allows libshairport to receive metadata (DMAP buffer and jpeg Coverart) and passing it up to XBMC.

In XBMC we receive the metadata, parse the DMAP and set albumname, title and artist to the songtag. We also receive the coverart and put it into a temp jpeg and set it to the infomanager.

@davilla
Collaborator

otherwise, +1 to inject.

@Memphiz
Owner

done and also added the patch to lib/libshairport for our linux friends.

@amejia1
Collaborator

@Memphiz Would you mind supplying a PR for me against https://github.com/amejia1/libshairport? Your patch in this PR doesn't apply cleanly.

@Memphiz
Owner

Did a PR to amejia (for debian/libshairport) and consider this one ready to go for june merge window. It will get a slight refactor because i will do a PR for adding AirTunes support to windows including metadata at the same time (so basically the metadata functions will be shared between windows and others).

I will merge the windows PR first in june, then adapt this one to the windows changes and merge this one. Will ask for quick review during merge window then.

@Memphiz Memphiz was assigned
@Memphiz
Owner

amejia1 are we ready for this PR from the linux/package side?

@Memphiz Memphiz merged commit 58d5c9d into from
@mkortstiege
Collaborator

AirTunesServer.cpp:670 error: ‘struct AudioOutput’ has no member named ‘ao_set_metadata’
AirTunesServer.cpp:671 error: ‘struct AudioOutput’ has no member named ‘ao_set_metadata_coverart’

Is there a way to make it depend on the libshairport used?

Owner

Nope - this is mainline code it just insists on using the correct version. So either build the libshairport in lib/libshairport or use the 1.2.1 package which amejia should have done already.

Collaborator

Ok.

@georgehenze

This whitespace should be a tab, right now these are spaces. You can't compile libshairport right now because make is complaining about them.

@night199uk
Collaborator

I also get:
AirTunesServer.cpp:670 error: ‘struct AudioOutput’ has no member named ‘ao_set_metadata’
AirTunesServer.cpp:671 error: ‘struct AudioOutput’ has no member named ‘ao_set_metadata_coverart’

But I'm on OSX - is libshairport used? I rebuild from scratch made clean made all depends and did a make install in libshairport also, no dice.

@Memphiz
Owner

stale dependency

make -C tools/darwin/depends/libshairport distclean
make -C tools/darwin/depends/libshairport

that should you get going with latest git code on osx...

@dalehamel dalehamel referenced this pull request from a commit in RasPlex/plex-home-theatre
@tru tru Ping the transcoder when paused.
Fixes #946
35a8633
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 1, 2012
  1. @Memphiz

    [libshairport] - pass metadata to xbmc

    Memphiz authored Memphiz committed
  2. @Memphiz
This page is out of date. Refresh to see the latest.
View
154 lib/libshairport/010_handle_metadata.patch
@@ -0,0 +1,154 @@
+diff -ruP src/ao.h libshairport.new/src/ao.h
+--- src/ao.h 2012-05-07 22:26:53.000000000 +0200
++++ src/ao.h 2012-05-08 18:46:42.000000000 +0200
+@@ -152,5 +152,7 @@
+ /* -- Device Setup/Playback/Teardown -- */
+ int (*ao_append_option)(ao_option **, const char *, const char *);
+ void (*ao_free_options)(ao_option *);
+- char* (*ao_get_option)(ao_option *, const char* );
++ char* (*ao_get_option)(ao_option *, const char* );
++ void (*ao_set_metadata)(const char *buffer, unsigned int size);
++ void (*ao_set_metadata_coverart)(const char *buffer, unsigned int size);
+ };
+diff -ruP src/hairtunes.c src/hairtunes.c
+--- src/hairtunes.c 2012-05-07 22:26:53.000000000 +0200
++++ src/hairtunes.c 2012-05-08 18:45:51.000000000 +0200
+@@ -267,6 +267,16 @@
+ fix_volume = 65536.0 * volume;
+ }
+
++void hairtunes_set_metadata(const char *buffer, unsigned int size)
++{
++ g_ao.ao_set_metadata(buffer, size);
++}
++
++void hairtunes_set_metadata_coverart(const char *buffer, unsigned int size)
++{
++ g_ao.ao_set_metadata_coverart(buffer, size);
++}
++
+ void hairtunes_flush(void)
+ {
+ pthread_mutex_lock(&ab_mutex);
+diff -ruP src/hairtunes.h src/hairtunes.h
+--- src/hairtunes.h 2011-08-21 00:06:21.000000000 +0200
++++ src/hairtunes.h 2012-05-08 18:46:00.000000000 +0200
+@@ -4,6 +4,8 @@
+ int hairtunes_init(char *pAeskey, char *pAesiv, char *pFmtpstr, int pCtrlPort, int pTimingPort,
+ int pDataPort, char *pRtpHost, char*pPipeName, char *pLibaoDriver, char *pLibaoDeviceName, char *pLibaoDeviceId);
+ void hairtunes_setvolume(float vol);
++void hairtunes_set_metadata(const char *buffer, unsigned int size);
++void hairtunes_set_metadata_coverart(const char *buffer, unsigned int size);
+ void hairtunes_flush(void);
+ void hairtunes_cleanup(void);
+
+diff -ruP src/shairport.c src/shairport.c
+--- src/shairport.c 2012-05-07 22:26:53.000000000 +0200
++++ src/shairport.c 2012-05-08 18:45:30.000000000 +0200
+@@ -513,7 +513,8 @@
+ while(1 == tMoreDataNeeded)
+ {
+ tError = readDataFromClient(pSock, &(tConn.recv));
+- if(!tError && strlen(tConn.recv.data) > 0)
++ //if(!tError && strlen(tConn.recv.data) > 0)
++ if(!tError && tConn.recv.current > 0)
+ {
+ xprintf("Finished Reading some data from client\n");
+ // parse client request
+@@ -632,7 +633,7 @@
+ }
+
+ tSize = (int) (tShortest - tFound);
+- xprintf("Found %.*s length: %d\n", tSize, tFound, tSize);
++ xprintf("Found %s length: %d\n",tFound, tSize);
+ if(pReturnSize != NULL)
+ {
+ *pReturnSize = tSize;
+@@ -744,7 +745,7 @@
+ if(tContent != NULL)
+ {
+ int tContentSize = atoi(tContent);
+- if(pConn->recv.marker == 0 || strlen(pConn->recv.data+pConn->recv.marker) != tContentSize)
++ if(pConn->recv.marker == 0 || pConn->recv.current-pConn->recv.marker != tContentSize)
+ {
+ if(isLogEnabledFor(HEADER_LOG_LEVEL))
+ {
+@@ -752,7 +753,7 @@
+ if(pConn->recv.marker != 0)
+ {
+ xprintf("ContentPtr has %d, but needs %d\n",
+- strlen(pConn->recv.data+pConn->recv.marker), tContentSize);
++ (pConn->recv.current-pConn->recv.marker), tContentSize);
+ }
+ }
+ // check if value in tContent > 2nd read from client.
+@@ -989,15 +990,67 @@
+ {
+ propogateCSeq(pConn);
+ int tSize = 0;
++ char *buffer = NULL;
++ char *contentType = getFromHeader(pConn->recv.data, "Content-Type", &tSize);
++ char *tContent = getFromHeader(pConn->recv.data, "Content-Length", NULL);
++ int iContentSize = 0;
++ int isJpg = 0;
++
++ if(tContent != NULL)
++ {
++ iContentSize = atoi(tContent);
++ }
++
++ if( tSize > 1 &&
++ (strncmp(contentType, "application/x-dmap-tagged", tSize) == 0) ||
++ (strncmp(contentType, "image/jpeg", tSize) == 0) )
++ {
++ if( (pConn->recv.current - pConn->recv.marker) == iContentSize && pConn->recv.marker != 0)
++ {
++ if(strncmp(contentType, "image/jpeg", tSize) == 0)
++ {
++ isJpg = 1;
++ }
++ buffer = (char *)malloc(iContentSize * sizeof(char));
++ memcpy(buffer, pConn->recv.data + pConn->recv.marker, iContentSize);
++ }
++ else
++ {
++ iContentSize = 0;
++ }
++ }
++ else
++ {
++ iContentSize = 0;
++ }
+ char *tVol = getFromHeader(pConn->recv.data, "volume", &tSize);
+- xprintf("About to write [vol: %.*s] data to hairtunes\n", tSize, tVol);
++ if( tVol)
++ {
++ xprintf("About to write [vol: %.*s] data to hairtunes\n", tSize, tVol);
++ }
+ // TBD VOLUME
+ #ifndef XBMC
+ write(pConn->hairtunes->in[1], "vol: ", 5);
+ write(pConn->hairtunes->in[1], tVol, tSize);
+ write(pConn->hairtunes->in[1], "\n", 1);
+ #else
+- hairtunes_setvolume(atof(tVol));
++ if(tVol)
++ {
++ hairtunes_setvolume(atof(tVol));
++ }
++
++ if(iContentSize)
++ {
++ if(isJpg)
++ {
++ hairtunes_set_metadata_coverart(buffer, iContentSize);
++ }
++ else
++ {
++ hairtunes_set_metadata(buffer, iContentSize);
++ }
++ free(buffer);
++ }
+ #endif
+ xprintf("Finished writing data write data to hairtunes\n");
+ }
View
1  lib/libshairport/Makefile
@@ -45,6 +45,7 @@ $(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE)
cd $(SOURCE); patch -p0 < ../007_fix_syslog_defines.patch
cd $(SOURCE); patch -p0 < ../008-add-missing-libs.patch
cd $(SOURCE); patch -p0 < ../009_fix_ipv6.patch
+ cd $(SOURCE); patch -p0 < ../010_handle_metadata.patch
cd $(SOURCE); autoreconf -vif
cd $(SOURCE); $(CONFIGURE)
View
154 tools/darwin/depends/libshairport/010_handle_metadata.patch
@@ -0,0 +1,154 @@
+diff -ruP src/ao.h libshairport.new/src/ao.h
+--- src/ao.h 2012-05-07 22:26:53.000000000 +0200
++++ src/ao.h 2012-05-08 18:46:42.000000000 +0200
+@@ -152,5 +152,7 @@
+ /* -- Device Setup/Playback/Teardown -- */
+ int (*ao_append_option)(ao_option **, const char *, const char *);
+ void (*ao_free_options)(ao_option *);
+- char* (*ao_get_option)(ao_option *, const char* );
++ char* (*ao_get_option)(ao_option *, const char* );
++ void (*ao_set_metadata)(const char *buffer, unsigned int size);
++ void (*ao_set_metadata_coverart)(const char *buffer, unsigned int size);
+ };
+diff -ruP src/hairtunes.c src/hairtunes.c
+--- src/hairtunes.c 2012-05-07 22:26:53.000000000 +0200
++++ src/hairtunes.c 2012-05-08 18:45:51.000000000 +0200
+@@ -267,6 +267,16 @@
+ fix_volume = 65536.0 * volume;
+ }
+
++void hairtunes_set_metadata(const char *buffer, unsigned int size)
++{
++ g_ao.ao_set_metadata(buffer, size);
++}
++
++void hairtunes_set_metadata_coverart(const char *buffer, unsigned int size)
++{
++ g_ao.ao_set_metadata_coverart(buffer, size);
++}
++
+ void hairtunes_flush(void)
+ {
+ pthread_mutex_lock(&ab_mutex);
+diff -ruP src/hairtunes.h src/hairtunes.h
+--- src/hairtunes.h 2011-08-21 00:06:21.000000000 +0200
++++ src/hairtunes.h 2012-05-08 18:46:00.000000000 +0200
+@@ -4,6 +4,8 @@
+ int hairtunes_init(char *pAeskey, char *pAesiv, char *pFmtpstr, int pCtrlPort, int pTimingPort,
+ int pDataPort, char *pRtpHost, char*pPipeName, char *pLibaoDriver, char *pLibaoDeviceName, char *pLibaoDeviceId);
+ void hairtunes_setvolume(float vol);
++void hairtunes_set_metadata(const char *buffer, unsigned int size);
++void hairtunes_set_metadata_coverart(const char *buffer, unsigned int size);
+ void hairtunes_flush(void);
+ void hairtunes_cleanup(void);
+
+diff -ruP src/shairport.c src/shairport.c
+--- src/shairport.c 2012-05-07 22:26:53.000000000 +0200
++++ src/shairport.c 2012-05-08 18:45:30.000000000 +0200
+@@ -513,7 +513,8 @@
+ while(1 == tMoreDataNeeded)
+ {
+ tError = readDataFromClient(pSock, &(tConn.recv));
+- if(!tError && strlen(tConn.recv.data) > 0)
++ //if(!tError && strlen(tConn.recv.data) > 0)
++ if(!tError && tConn.recv.current > 0)
+ {
+ xprintf("Finished Reading some data from client\n");
+ // parse client request
+@@ -632,7 +633,7 @@
+ }
+
+ tSize = (int) (tShortest - tFound);
+- xprintf("Found %.*s length: %d\n", tSize, tFound, tSize);
++ xprintf("Found %s length: %d\n",tFound, tSize);
+ if(pReturnSize != NULL)
+ {
+ *pReturnSize = tSize;
+@@ -744,7 +745,7 @@
+ if(tContent != NULL)
+ {
+ int tContentSize = atoi(tContent);
+- if(pConn->recv.marker == 0 || strlen(pConn->recv.data+pConn->recv.marker) != tContentSize)
++ if(pConn->recv.marker == 0 || pConn->recv.current-pConn->recv.marker != tContentSize)
+ {
+ if(isLogEnabledFor(HEADER_LOG_LEVEL))
+ {
+@@ -752,7 +753,7 @@
+ if(pConn->recv.marker != 0)
+ {
+ xprintf("ContentPtr has %d, but needs %d\n",
+- strlen(pConn->recv.data+pConn->recv.marker), tContentSize);
++ (pConn->recv.current-pConn->recv.marker), tContentSize);
+ }
+ }
+ // check if value in tContent > 2nd read from client.
+@@ -989,15 +990,67 @@
+ {
+ propogateCSeq(pConn);
+ int tSize = 0;
++ char *buffer = NULL;
++ char *contentType = getFromHeader(pConn->recv.data, "Content-Type", &tSize);
++ char *tContent = getFromHeader(pConn->recv.data, "Content-Length", NULL);
++ int iContentSize = 0;
++ int isJpg = 0;
++
++ if(tContent != NULL)
++ {
++ iContentSize = atoi(tContent);
++ }
++
++ if( tSize > 1 &&
++ (strncmp(contentType, "application/x-dmap-tagged", tSize) == 0) ||
++ (strncmp(contentType, "image/jpeg", tSize) == 0) )
++ {
++ if( (pConn->recv.current - pConn->recv.marker) == iContentSize && pConn->recv.marker != 0)
++ {
++ if(strncmp(contentType, "image/jpeg", tSize) == 0)
++ {
++ isJpg = 1;
++ }
++ buffer = (char *)malloc(iContentSize * sizeof(char));
++ memcpy(buffer, pConn->recv.data + pConn->recv.marker, iContentSize);
++ }
++ else
++ {
++ iContentSize = 0;
++ }
++ }
++ else
++ {
++ iContentSize = 0;
++ }
+ char *tVol = getFromHeader(pConn->recv.data, "volume", &tSize);
+- xprintf("About to write [vol: %.*s] data to hairtunes\n", tSize, tVol);
++ if( tVol)
++ {
++ xprintf("About to write [vol: %.*s] data to hairtunes\n", tSize, tVol);
++ }
+ // TBD VOLUME
+ #ifndef XBMC
+ write(pConn->hairtunes->in[1], "vol: ", 5);
+ write(pConn->hairtunes->in[1], tVol, tSize);
+ write(pConn->hairtunes->in[1], "\n", 1);
+ #else
+- hairtunes_setvolume(atof(tVol));
++ if(tVol)
++ {
++ hairtunes_setvolume(atof(tVol));
++ }
++
++ if(iContentSize)
++ {
++ if(isJpg)
++ {
++ hairtunes_set_metadata_coverart(buffer, iContentSize);
++ }
++ else
++ {
++ hairtunes_set_metadata(buffer, iContentSize);
++ }
++ free(buffer);
++ }
+ #endif
+ xprintf("Finished writing data write data to hairtunes\n");
+ }
View
1  tools/darwin/depends/libshairport/Makefile
@@ -31,6 +31,7 @@ $(SOURCE): $(TARBALLS_LOCATION)/$(ARCHIVE)
cd $(SOURCE); patch -p0 < ../007_fix_syslog_defines.patch
cd $(SOURCE); patch -p0 < ../008-add-missing-libs.patch
cd $(SOURCE); patch -p0 < ../009_fix_ipv6.patch
+ cd $(SOURCE); patch -p0 < ../010_handle_metadata.patch
cd $(SOURCE); autoreconf -vif
cd $(SOURCE); $(CONFIGURE)
View
13 xbmc/network/AirTunesServer.cpp
@@ -47,7 +47,6 @@
#include <map>
#include <string>
-
using namespace XFILE;
#if defined(TARGET_WINDOWS)
@@ -390,6 +389,16 @@ int CAirTunesServer::AudioOutputFunctions::ao_close(ao_device *device)
return 0;
}
+void CAirTunesServer::AudioOutputFunctions::ao_set_metadata(const char *buffer, unsigned int size)
+{
+ CAirTunesServer::SetMetadataFromBuffer(buffer, size);
+}
+
+void CAirTunesServer::AudioOutputFunctions::ao_set_metadata_coverart(const char *buffer, unsigned int size)
+{
+ CAirTunesServer::SetCoverArtFromBuffer(buffer, size);
+}
+
/* -- Device Setup/Playback/Teardown -- */
int CAirTunesServer::AudioOutputFunctions::ao_append_option(ao_option **options, const char *key, const char *value)
{
@@ -658,6 +667,8 @@ bool CAirTunesServer::Initialize(const CStdString &password)
ao.ao_append_option = AudioOutputFunctions::ao_append_option;
ao.ao_free_options = AudioOutputFunctions::ao_free_options;
ao.ao_get_option = AudioOutputFunctions::ao_get_option;
+ ao.ao_set_metadata = AudioOutputFunctions::ao_set_metadata;
+ ao.ao_set_metadata_coverart = AudioOutputFunctions::ao_set_metadata_coverart;
struct printfPtr funcPtr;
funcPtr.extprintf = shairport_log;
View
2  xbmc/network/AirTunesServer.h
@@ -96,6 +96,8 @@ class CAirTunesServer : public CThread
static int ao_append_option(ao_option **options, const char *key, const char *value);
static void ao_free_options(ao_option *options);
static char* ao_get_option(ao_option *options, const char* key);
+ static void ao_set_metadata(const char *buffer, unsigned int size);
+ static void ao_set_metadata_coverart(const char *buffer, unsigned int size);
#endif
};
};
Something went wrong with that request. Please try again.