From 0ac03fde3bf40c5c79eb2a77f0105742ed62c047 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Tue, 21 May 2019 11:51:09 +0000 Subject: [PATCH] Feature: Added support for setting format and usage --- include/shout/shout.h.in | 19 ++++++ src/proto_http.c | 25 ++------ src/shout.c | 131 +++++++++++++++++++++++++++++++++++++-- src/shout_private.h | 4 ++ 4 files changed, 153 insertions(+), 26 deletions(-) diff --git a/include/shout/shout.h.in b/include/shout/shout.h.in index cc4c564..d37440d 100644 --- a/include/shout/shout.h.in +++ b/include/shout/shout.h.in @@ -56,6 +56,22 @@ extern "C" { /* backward-compatibility alias */ #define SHOUT_FORMAT_VORBIS SHOUT_FORMAT_OGG +/* Usages */ +#define SHOUT_USAGE_AUDIO (0x0001U) /* Audio substreams*/ +#define SHOUT_USAGE_VISUAL (0x0002U) /* Picture/Video substreams (most often combined with SHOUT_USAGE_AUDIO) */ +#define SHOUT_USAGE_TEXT (0x0004U) /* Text substreams that are not subtitles */ +#define SHOUT_USAGE_SUBTITLE (0x0008U) /* Subtitle substreams */ +#define SHOUT_USAGE_LIGHT (0x0010U) /* Light control substreams */ +#define SHOUT_USAGE_UI (0x0020U) /* User interface data, such as DVD menus or buttons */ +#define SHOUT_USAGE_METADATA (0x0040U) /* Substreams that include metadata for the stream */ +#define SHOUT_USAGE_APPLICATION (0x0080U) /* Application specific data substreams */ +#define SHOUT_USAGE_CONTROL (0x0100U) /* Substreams that control the infrastructure */ +#define SHOUT_USAGE_COMPLEX (0x0200U) /* Substreams that are themself a mixture of other types */ +#define SHOUT_USAGE_OTHER (0x0400U) /* Substream of types not listed here */ +#define SHOUT_USAGE_UNKNOWN (0x0800U) /* The stream MAY contain additional substreams of unknown nature */ +#define SHOUT_USAGE_3D (0x1000U) /* The Stream contains information for 3D playback */ +#define SHOUT_USAGE_4D (0x2000U) /* The Stream contains information for 4D/XD playback */ + #define SHOUT_PROTOCOL_HTTP ( 0) #define SHOUT_PROTOCOL_XAUDIOCAST ( 1) /* Deprecated. May be removed in future versions. Do not use. */ #define SHOUT_PROTOCOL_ICY ( 2) @@ -214,6 +230,9 @@ unsigned int shout_get_public(shout_t *self); int shout_set_format(shout_t *self, unsigned int format); unsigned int shout_get_format(shout_t *self); +int shout_set_content_format(shout_t *self, unsigned int format, unsigned int usage, const char *codecs); +int shout_get_content_format(shout_t *self, unsigned int *format, unsigned int *usage, const char **codecs); + /* takes a SHOUT_PROTOCOL_xxxxx argument */ int shout_set_protocol(shout_t *self, unsigned int protocol); unsigned int shout_get_protocol(shout_t *self); diff --git a/src/proto_http.c b/src/proto_http.c index a366eaa..8fda5a5 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -91,27 +91,10 @@ static shout_connection_return_state_t shout_create_http_request_source(shout_t const char *mimetype; char *mount = NULL; - switch (self->format) { - case SHOUT_FORMAT_OGG: - mimetype = "application/ogg"; - break; - - case SHOUT_FORMAT_MP3: - mimetype = "audio/mpeg"; - break; - - case SHOUT_FORMAT_WEBM: - mimetype = "video/webm"; - break; - - case SHOUT_FORMAT_WEBMAUDIO: - mimetype = "audio/webm"; - break; - - default: - shout_connection_set_error(connection, SHOUTERR_INSANE); - return SHOUT_RS_ERROR; - break; + mimetype = shout_get_mimetype_from_self(self); + if (!mimetype) { + shout_connection_set_error(connection, SHOUTERR_INSANE); + return SHOUT_RS_ERROR; } /* this is lazy code that relies on the only error from queue_* being diff --git a/src/shout.c b/src/shout.c index 1ffd24f..10ed250 100644 --- a/src/shout.c +++ b/src/shout.c @@ -131,6 +131,7 @@ shout_t *shout_new(void) self->port = LIBSHOUT_DEFAULT_PORT; self->format = LIBSHOUT_DEFAULT_FORMAT; + self->usage = LIBSHOUT_DEFAULT_USAGE; self->protocol = LIBSHOUT_DEFAULT_PROTOCOL; return self; @@ -811,22 +812,142 @@ int shout_set_format(shout_t *self, unsigned int format) if (self->connection) return self->error = SHOUTERR_CONNECTED; - if (format != SHOUT_FORMAT_OGG && format != SHOUT_FORMAT_MP3 && - format != SHOUT_FORMAT_WEBM && format != SHOUT_FORMAT_WEBMAUDIO) { + switch (format) { + case SHOUT_FORMAT_OGG: + return shout_set_content_format(self, SHOUT_FORMAT_OGG, SHOUT_USAGE_UNKNOWN, NULL); + break; + case SHOUT_FORMAT_MP3: + return shout_set_content_format(self, SHOUT_FORMAT_MP3, SHOUT_USAGE_AUDIO, NULL); + break; + case SHOUT_FORMAT_WEBM: + return shout_set_content_format(self, SHOUT_FORMAT_WEBM, SHOUT_USAGE_AUDIO|SHOUT_USAGE_VISUAL, NULL); + break; + case SHOUT_FORMAT_WEBMAUDIO: + return shout_set_content_format(self, SHOUT_FORMAT_WEBM, SHOUT_USAGE_AUDIO, NULL); + break; + } + + return self->error = SHOUTERR_UNSUPPORTED; +} + +unsigned int shout_get_format(shout_t* self) +{ + if (!self) + return 0; + + if (self->format == SHOUT_FORMAT_WEBM && self->usage == SHOUT_USAGE_AUDIO) { + return SHOUT_FORMAT_WEBMAUDIO; + } + + return self->format; +} + +static inline unsigned int remove_bits(unsigned int value, unsigned int to_remove) +{ + value |= to_remove; + value -= to_remove; + + return value; +} + +static inline int is_audio(unsigned int usage) +{ + if (!(usage & SHOUT_USAGE_AUDIO)) + return 0; + + if (remove_bits(usage, SHOUT_USAGE_AUDIO|SHOUT_USAGE_SUBTITLE)) + return 0; + + return 1; +} + +static inline int is_video(unsigned int usage) +{ + if (!(usage & SHOUT_USAGE_VISUAL)) + return 0; + + if (remove_bits(usage, SHOUT_USAGE_VISUAL|SHOUT_USAGE_AUDIO|SHOUT_USAGE_SUBTITLE|SHOUT_USAGE_3D|SHOUT_USAGE_4D)) + return 0; + + return 1; +} + +static const char *shout_get_mimetype(unsigned int format, unsigned int usage, const char *codecs) +{ + if (codecs) + return NULL; + + switch (format) { + case SHOUT_FORMAT_OGG: + if (is_audio(usage)) { + return "audio/ogg"; + } else if (is_video(usage)) { + return "video/ogg"; + } else { + return "application/ogg"; + } + break; + + case SHOUT_FORMAT_MP3: + /* MP3 *ONLY* support Audio. So all other values are outright invalid */ + if (usage == SHOUT_USAGE_AUDIO) { + return "audio/mpeg"; + } + break; + case SHOUT_FORMAT_WEBM: + if (is_audio(usage)) { + return "audio/webm"; + } else if (is_video(usage)) { + return "video/webm"; + } + break; + } + + return NULL; +} + +const char *shout_get_mimetype_from_self(shout_t *self) +{ + return shout_get_mimetype(self->format, self->usage, NULL); +} + +int shout_set_content_format(shout_t *self, unsigned int format, unsigned int usage, const char *codecs) +{ + if (!self) + return SHOUTERR_INSANE; + + if (self->connection) + return self->error = SHOUTERR_CONNECTED; + + if (codecs) { + return self->error = SHOUTERR_UNSUPPORTED; + } + + if (!shout_get_mimetype(format, usage, codecs)) { return self->error = SHOUTERR_UNSUPPORTED; } self->format = format; + self->usage = usage; return self->error = SHOUTERR_SUCCESS; } -unsigned int shout_get_format(shout_t* self) +int shout_get_content_format(shout_t *self, unsigned int *format, unsigned int *usage, const char **codecs) { if (!self) - return 0; + return SHOUTERR_INSANE; - return self->format; + if (format) + *format = self->format; + + if (usage) + *usage = self->usage; + + if (codecs) + *codecs = NULL; + + return self->error = SHOUTERR_SUCCESS; } int shout_set_protocol(shout_t *self, unsigned int protocol) diff --git a/src/shout_private.h b/src/shout_private.h index 7a8161a..aa18f21 100644 --- a/src/shout_private.h +++ b/src/shout_private.h @@ -48,6 +48,7 @@ #define LIBSHOUT_DEFAULT_HOST "localhost" #define LIBSHOUT_DEFAULT_PORT 8000 #define LIBSHOUT_DEFAULT_FORMAT SHOUT_FORMAT_OGG +#define LIBSHOUT_DEFAULT_USAGE SHOUT_USAGE_UNKNOWN #define LIBSHOUT_DEFAULT_PROTOCOL SHOUT_PROTOCOL_HTTP #define LIBSHOUT_DEFAULT_USER "source" #define LIBSHOUT_DEFAULT_USERAGENT "libshout/" VERSION @@ -214,6 +215,7 @@ struct shout { unsigned int protocol; /* type of data being sent */ unsigned int format; + unsigned int usage; /* audio encoding parameters */ util_dict *audio_info; @@ -263,6 +265,8 @@ struct shout { }; /* helper functions */ +const char *shout_get_mimetype_from_self(shout_t *self); + int shout_queue_data(shout_queue_t *queue, const unsigned char *data, size_t len); int shout_queue_str(shout_connection_t *self, const char *str); int shout_queue_printf(shout_connection_t *self, const char *fmt, ...);