Add full metadata and coverart support to the library
authorJuho Vähä-Herttua <juhovh@iki.fi>
Sun, 13 May 2012 13:45:41 +0000 (16:45 +0300)
committerJuho Vähä-Herttua <juhovh@iki.fi>
Wed, 16 May 2012 21:57:07 +0000 (00:57 +0300)
src/include/raop.h
src/lib/raop.c
src/lib/raop_rtp.c
src/test/example.c

index e5158cb2634bce4fc156241a26ea095111ce2185..aa1a47f9d7376ec48470721da3444a64a534396b 100644 (file)
@@ -17,6 +17,8 @@ struct raop_callbacks_s {
        void* cls;
        void* (*audio_init)(void *cls, int bits, int channels, int samplerate);
        void  (*audio_set_volume)(void *cls, void *session, float volume);
+       void  (*audio_set_metadata)(void *cls, void *session, const void *buffer, int buflen);
+       void  (*audio_set_coverart)(void *cls, void *session, const void *buffer, int buflen);
        void  (*audio_process)(void *cls, void *session, const void *buffer, int buflen);
        void  (*audio_flush)(void *cls, void *session);
        void  (*audio_destroy)(void *cls, void *session);
index a0289b7319f41eed7fe7f7a0028d09ec83e93467..0d2b11fa3f522d7583d663e782c852f69c710a4d 100644 (file)
@@ -365,8 +365,8 @@ raop_init(raop_callbacks_t *callbacks, const char *pemkey)
        }
 
        /* Validate the callbacks structure */
-       if (!callbacks->audio_init || !callbacks->audio_set_volume ||
-           !callbacks->audio_process || !callbacks->audio_flush ||
+       if (!callbacks->audio_init ||
+           !callbacks->audio_process ||
            !callbacks->audio_destroy) {
                return NULL;
        }
index 1896e160b562c50e1b67081cb65c1950c86165fe..77814ba08680528e9a2d96b9ee9d42d40792d16a 100644 (file)
@@ -245,6 +245,77 @@ raop_rtp_resend_callback(void *opaque, unsigned short seqnum, unsigned short cou
        return 0;
 }
 
+static int
+raop_rtp_process_events(raop_rtp_t *raop_rtp, void *cb_data, float *volume)
+{
+       int flush;
+       int volume_changed;
+       unsigned char *metadata;
+       int metadata_len;
+       unsigned char *coverart;
+       int coverart_len;
+
+       assert(raop_rtp);
+       assert(volume);
+
+       MUTEX_LOCK(raop_rtp->run_mutex);
+       if (!raop_rtp->running) {
+               MUTEX_UNLOCK(raop_rtp->run_mutex);
+               return 1;
+       }
+
+       /* Read the volume level */
+       volume_changed = (*volume != raop_rtp->volume);
+       *volume = raop_rtp->volume;
+
+       /* Read the flush value */
+       flush = raop_rtp->flush;
+       raop_rtp->flush = NO_FLUSH;
+
+       /* Read the metadata */
+       metadata = raop_rtp->metadata;
+       metadata_len = raop_rtp->metadata_len;
+       raop_rtp->metadata = NULL;
+       raop_rtp->metadata_len = 0;
+
+       /* Read the coverart */
+       coverart = raop_rtp->coverart;
+       coverart_len = raop_rtp->coverart_len;
+       raop_rtp->coverart = NULL;
+       raop_rtp->coverart_len = 0;
+       MUTEX_UNLOCK(raop_rtp->run_mutex);
+
+       /* Call set_volume callback if changed */
+       if (volume_changed) {
+               if (raop_rtp->callbacks.audio_set_volume) {
+                       raop_rtp->callbacks.audio_set_volume(raop_rtp->callbacks.cls, cb_data, *volume);
+               }
+       }
+
+       /* Handle flush if requested */
+       if (flush != NO_FLUSH) {
+               raop_buffer_flush(raop_rtp->buffer, flush);
+               if (raop_rtp->callbacks.audio_flush) {
+                       raop_rtp->callbacks.audio_flush(raop_rtp->callbacks.cls, cb_data);
+               }
+       }
+       if (metadata != NULL) {
+               if (raop_rtp->callbacks.audio_set_metadata) {
+                       raop_rtp->callbacks.audio_set_metadata(raop_rtp->callbacks.cls, cb_data, metadata, metadata_len);
+               }
+               free(metadata);
+               metadata = NULL;
+       }
+       if (coverart != NULL) {
+               if (raop_rtp->callbacks.audio_set_coverart) {
+                       raop_rtp->callbacks.audio_set_coverart(raop_rtp->callbacks.cls, cb_data, coverart, coverart_len);
+               }
+               free(coverart);
+               coverart = NULL;
+       }
+       return 0;
+}
+
 static THREAD_RETVAL
 raop_rtp_thread_udp(void *arg)
 {
@@ -267,35 +338,14 @@ raop_rtp_thread_udp(void *arg)
                                       config->sampleRate);
 
        while(1) {
-               int volume_changed;
-               int flush;
-
                fd_set rfds;
                struct timeval tv;
                int nfds, ret;
 
-               MUTEX_LOCK(raop_rtp->run_mutex);
-               if (!raop_rtp->running) {
-                       MUTEX_UNLOCK(raop_rtp->run_mutex);
+               /* Check if we are still running and process callbacks */
+               if (raop_rtp_process_events(raop_rtp, cb_data, &volume)) {
                        break;
                }
-               /* Read the volume level */
-               volume_changed = (volume != raop_rtp->volume);
-               volume = raop_rtp->volume;
-
-               /* Read the flush value */
-               flush = raop_rtp->flush;
-               raop_rtp->flush = NO_FLUSH;
-               MUTEX_UNLOCK(raop_rtp->run_mutex);
-
-               /* Call set_volume callback if changed */
-               if (volume_changed) {
-                       raop_rtp->callbacks.audio_set_volume(raop_rtp->callbacks.cls, cb_data, volume);
-               }
-               if (flush != NO_FLUSH) {
-                       raop_buffer_flush(raop_rtp->buffer, flush);
-                       raop_rtp->callbacks.audio_flush(raop_rtp->callbacks.cls, cb_data);
-               }
 
                /* Set timeout value to 5ms */
                tv.tv_sec = 0;
@@ -396,25 +446,14 @@ raop_rtp_thread_tcp(void *arg)
                                       config->sampleRate);
 
        while (1) {
-               int volume_changed;
-
                fd_set rfds;
                struct timeval tv;
                int nfds, ret;
 
-               MUTEX_LOCK(raop_rtp->run_mutex);
-               if (!raop_rtp->running) {
-                       MUTEX_UNLOCK(raop_rtp->run_mutex);
+               /* Check if we are still running and process callbacks */
+               if (raop_rtp_process_events(raop_rtp, cb_data, &volume)) {
                        break;
                }
-               volume_changed = (volume != raop_rtp->volume);
-               volume = raop_rtp->volume;
-               MUTEX_UNLOCK(raop_rtp->run_mutex);
-
-               /* Call set_volume callback if changed */
-               if (volume_changed) {
-                       raop_rtp->callbacks.audio_set_volume(raop_rtp->callbacks.cls, cb_data, volume);
-               }
 
                /* Set timeout value to 5ms */
                tv.tv_sec = 0;
index 960e1258b70dd6c0367eceb1184551d252e7f34b..c1ae7f0150f4974f3bc5c8e139e3d8b6498d7cc1 100644 (file)
@@ -21,6 +21,30 @@ audio_set_volume(void *cls, void *session, float volume)
        printf("Setting volume to %f\n", volume);
 }
 
+static void
+audio_set_metadata(void *cls, void *session, const void *buffer, int buflen)
+{
+       int orig = buflen;
+       FILE *file = fopen("metadata.bin", "wb");
+       while (buflen > 0) {
+               buflen -= fwrite(buffer+orig-buflen, 1, buflen, file);
+       }
+       fclose(file);
+       printf("Metadata of length %d saved as metadata.bin\n", orig);
+}
+
+static void
+audio_set_coverart(void *cls, void *session, const void *buffer, int buflen)
+{
+       int orig = buflen;
+       FILE *file = fopen("coverart.jpg", "wb");
+       while (buflen > 0) {
+               buflen -= fwrite(buffer+orig-buflen, 1, buflen, file);
+       }
+       fclose(file);
+       printf("Coverart of length %d saved as coverart.jpg\n", orig);
+}
+
 static void
 audio_process(void *cls, void *session, const void *buffer, int buflen)
 {
@@ -56,6 +80,8 @@ main(int argc, char *argv[])
        raop_cbs.cls = NULL;
        raop_cbs.audio_init = audio_init;
        raop_cbs.audio_set_volume = audio_set_volume;
+       raop_cbs.audio_set_metadata = audio_set_metadata;
+       raop_cbs.audio_set_coverart = audio_set_coverart;
        raop_cbs.audio_process = audio_process;
        raop_cbs.audio_flush = audio_flush;
        raop_cbs.audio_destroy = audio_destroy;