Implement a rudimentary cover art file writing facility.

This commit is contained in:
Mike Brady
2018-01-16 22:22:03 +00:00
parent 740e46ddd4
commit 4aaa8d19fe
4 changed files with 165 additions and 59 deletions
+50
View File
@@ -38,6 +38,8 @@
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include "common.h"
#include <assert.h>
@@ -158,6 +160,54 @@ char *base64_enc(uint8_t *input, int length) {
return buf;
}
// The following two functions are adapted slightly and with thanks from Jonathan Leffler's sample
// code at
// https://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux
int do_mkdir(const char *path, mode_t mode) {
struct stat st;
int status = 0;
if (stat(path, &st) != 0) {
/* Directory does not exist. EEXIST for race condition */
if (mkdir(path, mode) != 0 && errno != EEXIST)
status = -1;
} else if (!S_ISDIR(st.st_mode)) {
errno = ENOTDIR;
status = -1;
}
return (status);
}
// mkpath - ensure all directories in path exist
// Algorithm takes the pessimistic view and works top-down to ensure
// each directory in path exists, rather than optimistically creating
// the last element and working backwards.
int mkpath(const char *path, mode_t mode) {
char *pp;
char *sp;
int status;
char *copypath = strdup(path);
status = 0;
pp = copypath;
while (status == 0 && (sp = strchr(pp, '/')) != 0) {
if (sp != pp) {
/* Neither root nor double slash in path */
*sp = '\0';
status = do_mkdir(copypath, mode);
*sp = '/';
}
pp = sp + 1;
}
if (status == 0)
status = do_mkdir(path, mode);
free(copypath);
return (status);
}
uint8_t *base64_dec(char *input, int *outlen) {
// slight problem here is that Apple cut the padding off their challenges. We must restore it
// before passing it in to the decoder, it seems
+8 -1
View File
@@ -129,7 +129,8 @@ typedef struct {
int daemonise_store_pid; // don't try to save a PID file
char *piddir;
char *computed_piddir; // the actual pid directory to create, if any
int logOutputLevel; // log output level
int logOutputLevel; // log output level
int statistics_requested, use_negotiated_latencies;
enum playback_mode_type playback_mode;
char *cmd_start, *cmd_stop, *cmd_set_volume;
@@ -174,6 +175,10 @@ typedef struct {
enum dbus_session_type mpris_service_bus_type;
#endif
#ifdef HAVE_METADATA_HUB
char *cover_art_cache_dir;
#endif
} shairport_cfg;
// true if Shairport Sync is supposed to be sending output to the output device, false otherwise
@@ -232,6 +237,8 @@ void command_start(void);
void command_stop(void);
void command_set_volume(double volume);
int mkpath(const char *path, mode_t mode);
void shairport_shutdown();
// void shairport_startup_complete(void);
+106 -10
View File
@@ -5,7 +5,7 @@
* then you need a metadata hub,
* where everything is stored
* This file is part of Shairport Sync.
* Copyright (c) Mike Brady 2017
* Copyright (c) Mike Brady 2017--2018
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
@@ -32,9 +32,30 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "config.h"
#include "common.h"
#include "dacp.h"
#include "metadata_hub.h"
#ifdef HAVE_LIBMBEDTLS
#include <mbedtls/md5.h>
#endif
#ifdef HAVE_LIBPOLARSSL
#include <polarssl/md5.h>
#endif
#ifdef HAVE_LIBSSL
#include <openssl/md5.h>
#endif
void metadata_hub_init(void) {
debug(1, "Metadata bundle initialisation.");
memset(&metadata_store, 0, sizeof(metadata_store));
@@ -61,6 +82,83 @@ void run_metadata_watchers(void) {
}
}
void metadata_write_image_file(const char *buf, int len) {
uint8_t img_md5[16];
// uint8_t ap_md5[16];
#ifdef HAVE_LIBSSL
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, buf, len);
MD5_Final(img_md5, &ctx);
#endif
#ifdef HAVE_LIBMBEDTLS
mbedtls_md5_context tctx;
mbedtls_md5_starts(&tctx);
mbedtls_md5_update(&tctx, buf, len);
mbedtls_md5_finish(&tctx, img_md5);
#endif
#ifdef HAVE_LIBPOLARSSL
md5_context tctx;
md5_starts(&tctx);
md5_update(&tctx, buf, len);
md5_finish(&tctx, img_md5);
#endif
char img_md5_str[33];
memset(img_md5_str, 0, sizeof(img_md5_str));
char *ext;
char png[] = "png";
char jpg[] = "jpg";
int i;
for (i = 0; i < 16; i++)
sprintf(&img_md5_str[i * 2], "%02x", (uint8_t)img_md5[i]);
// see if the file is a jpeg or a png
if (strncmp(buf, "\xFF\xD8\xFF", 3) == 0)
ext = jpg;
else if (strncmp(buf, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == 0)
ext = png;
else {
debug(1, "Unidentified image type of cover art -- jpg extension used.");
ext = jpg;
}
int result = mkpath(config.cover_art_cache_dir, 0700);
if ((result == 0) || (result == -EEXIST)) {
debug(1, "Cover art cache directory okay");
// see if the file exists by opening it.
// if it exists, we're done
char *prefix = "cover-";
size_t pl = strlen(config.cover_art_cache_dir) + 1 + strlen(prefix) + strlen(img_md5_str) + 1 +
strlen(ext);
char *path = malloc(pl + 1);
snprintf(path, pl + 1, "%s/%s%s.%s", config.cover_art_cache_dir, prefix, img_md5_str, ext);
int cover_fd = open(path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (cover_fd > 0) {
// write the contents
if (write(cover_fd, buf, len) < len) {
warn("Writing cover art file \"%s\" failed!", path);
}
close(cover_fd);
free(path);
} else {
if (errno == EEXIST)
debug(1, "Cover art file \"%s\" already exists!", path);
else
warn("Could not open file \"%s\" for writing cover art", path);
free(path);
}
} else {
debug(1, "Couldn't access or create the cover art cache directory \"%s\".",
config.cover_art_cache_dir);
}
}
void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t length) {
// metadata coming in from the audio source or from Shairport Sync itself passes through here
// this has more information about tags, which might be relevant:
@@ -89,8 +187,7 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
}
break;
case 'ascm':
if ((metadata_store.comment == NULL) ||
(strncmp(metadata_store.comment, data, length) != 0)) {
if ((metadata_store.comment == NULL) || (strncmp(metadata_store.comment, data, length) != 0)) {
if (metadata_store.comment)
free(metadata_store.comment);
metadata_store.comment = strndup(data, length);
@@ -100,8 +197,7 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
}
break;
case 'asgn':
if ((metadata_store.genre == NULL) ||
(strncmp(metadata_store.genre, data, length) != 0)) {
if ((metadata_store.genre == NULL) || (strncmp(metadata_store.genre, data, length) != 0)) {
if (metadata_store.genre)
free(metadata_store.genre);
metadata_store.genre = strndup(data, length);
@@ -155,8 +251,7 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
}
break;
case 'assn':
if ((metadata_store.sort_as == NULL) ||
(strncmp(metadata_store.sort_as, data, length) != 0)) {
if ((metadata_store.sort_as == NULL) || (strncmp(metadata_store.sort_as, data, length) != 0)) {
if (metadata_store.sort_as)
free(metadata_store.sort_as);
metadata_store.sort_as = strndup(data, length);
@@ -167,6 +262,8 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
break;
case 'PICT':
debug(1, "MH Picture received, length %u bytes.", length);
if (length > 16)
metadata_write_image_file(data, length);
break;
case 'clip':
if ((metadata_store.client_ip == NULL) ||
@@ -179,10 +276,9 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
metadata_store.changed = 1;
}
break;
default:
if (type == 'ssnc')
{
if (type == 'ssnc') {
char typestring[5];
*(uint32_t *)typestring = htonl(type);
typestring[4] = 0;
+1 -48
View File
@@ -132,54 +132,6 @@ static void sig_connect_audio_output(int foo, siginfo_t *bar, void *baz) {
set_requested_connection_state_to_output(1);
}
// The following two functions are adapted slightly and with thanks from Jonathan Leffler's sample
// code at
// https://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux
int do_mkdir(const char *path, mode_t mode) {
struct stat st;
int status = 0;
if (stat(path, &st) != 0) {
/* Directory does not exist. EEXIST for race condition */
if (mkdir(path, mode) != 0 && errno != EEXIST)
status = -1;
} else if (!S_ISDIR(st.st_mode)) {
errno = ENOTDIR;
status = -1;
}
return (status);
}
// mkpath - ensure all directories in path exist
// Algorithm takes the pessimistic view and works top-down to ensure
// each directory in path exists, rather than optimistically creating
// the last element and working backwards.
int mkpath(const char *path, mode_t mode) {
char *pp;
char *sp;
int status;
char *copypath = strdup(path);
status = 0;
pp = copypath;
while (status == 0 && (sp = strchr(pp, '/')) != 0) {
if (sp != pp) {
/* Neither root nor double slash in path */
*sp = '\0';
status = do_mkdir(copypath, mode);
*sp = '/';
}
pp = sp + 1;
}
if (status == 0)
status = do_mkdir(path, mode);
free(copypath);
return (status);
}
char *get_version_string() {
char *version_string = malloc(200);
if (version_string) {
@@ -438,6 +390,7 @@ int parse_options(int argc, char **argv) {
config.airplay_volume = -18.0; // if no volume is ever set, default to initial default value if
// nothing else comes in first.
config.fixedLatencyOffset = 11025; // this sounds like it works properly.
config.cover_art_cache_dir = "/tmp/shairport-sync/.cache/coverart";
config_setting_t *setting;
const char *str = 0;