From db600494aded16956c15205b1a056ee465c9bc49 Mon Sep 17 00:00:00 2001 From: Stefan Pöschel Date: Sun, 24 Jul 2016 19:29:52 +0200 Subject: VLC input: use ICY artist/title if available --- src/VLCInput.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++---------- src/VLCInput.h | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/VLCInput.cpp b/src/VLCInput.cpp index 9f0aa65..0ce4f00 100644 --- a/src/VLCInput.cpp +++ b/src/VLCInput.cpp @@ -324,17 +324,34 @@ ssize_t VLCInput::m_read(uint8_t* buf, size_t length) break; } - char* nowplaying_sz = libvlc_media_get_meta(media, libvlc_meta_NowPlaying); - if (nowplaying_sz) { - std::lock_guard lock(m_nowplaying_mutex); - m_nowplaying = nowplaying_sz; + // handle meta data + char* artist_sz = libvlc_media_get_meta(media, libvlc_meta_Artist); + char* title_sz = libvlc_media_get_meta(media, libvlc_meta_Title); - free(nowplaying_sz); + if (artist_sz && title_sz) { + // use Artist and Title + std::lock_guard lock(m_nowplaying_mutex); + m_nowplaying.useArtistTitle(artist_sz, title_sz); + } else { + // try fallback to NowPlaying + char* nowplaying_sz = libvlc_media_get_meta(media, libvlc_meta_NowPlaying); + if (nowplaying_sz) { + std::lock_guard lock(m_nowplaying_mutex); + m_nowplaying.useNowPlaying(nowplaying_sz); + free(nowplaying_sz); + } } + + if (artist_sz) + free(artist_sz); + if (title_sz) + free(title_sz); } return err; } +const std::string VLCInput::ICY_TEXT_SEPARATOR = " - "; + /*! Write the corresponding text to a file readable by mot-encoder, with optional * DL+ information. The text is passed as a copy because we actually use the * m_nowplaying variable which is also accessed in another thread, so better @@ -342,11 +359,12 @@ ssize_t VLCInput::m_read(uint8_t* buf, size_t length) * * \return false on failure */ -bool write_icy_to_file(const std::string text, const std::string& filename, bool dl_plus) +bool write_icy_to_file(const ICY_TEXT_T text, const std::string& filename, bool dl_plus) { FILE* fd = fopen(filename.c_str(), "wb"); if (fd) { bool ret = true; + bool artist_title_used = !text.artist.empty() && !text.title.empty(); // if desired, prepend DL Plus information if (dl_plus) { @@ -354,15 +372,32 @@ bool write_icy_to_file(const std::string text, const std::string& filename, bool ss << "##### parameters { #####\n"; ss << "DL_PLUS=1\n"; - // if non-empty text, add PROGRAMME.NOW tag - if (!text.empty()) - ss << "DL_PLUS_TAG=33 0 " << (strlen_utf8(text.c_str()) - 1) << "\n"; // -1 ! + // if non-empty text, add tag + if (artist_title_used) { + size_t artist_len = strlen_utf8(text.artist.c_str()); + size_t title_start = artist_len + strlen_utf8(VLCInput::ICY_TEXT_SEPARATOR.c_str()); + + // ITEM.ARTIST + ss << "DL_PLUS_TAG=4 0 " << (artist_len - 1) << "\n"; // -1 ! + + // ITEM.TITLE + ss << "DL_PLUS_TAG=1 " << title_start << " " << (strlen_utf8(text.title.c_str()) - 1) << "\n"; // -1 ! + } else if (!text.now_playing.empty()) { + // PROGRAMME.NOW + ss << "DL_PLUS_TAG=33 0 " << (strlen_utf8(text.now_playing.c_str()) - 1) << "\n"; // -1 ! + } ss << "##### parameters } #####\n"; ret &= fputs(ss.str().c_str(), fd) >= 0; } - ret &= fputs(text.c_str(), fd) >= 0; + if (artist_title_used) { + ret &= fputs(text.artist.c_str(), fd) >= 0; + ret &= fputs(VLCInput::ICY_TEXT_SEPARATOR.c_str(), fd) >= 0; + ret &= fputs(text.title.c_str(), fd) >= 0; + } else { + ret &= fputs(text.now_playing.c_str(), fd) >= 0; + } fclose(fd); return ret; diff --git a/src/VLCInput.h b/src/VLCInput.h index 761492b..da4b7ae 100644 --- a/src/VLCInput.h +++ b/src/VLCInput.h @@ -49,6 +49,34 @@ extern "C" { /*! Common functionality for the direct libvlc input and the * threaded libvlc input */ + +struct ICY_TEXT_T { + std::string artist; + std::string title; + std::string now_playing; + + bool operator==(const ICY_TEXT_T& other) const { + return + artist == other.artist && + title == other.title && + now_playing == other.now_playing; + } + bool operator!=(const ICY_TEXT_T& other) const { + return !(*this == other); + } + void useArtistTitle(std::string artist, std::string title) { + this->artist = artist; + this->title = title; + now_playing = ""; + } + void useNowPlaying(std::string now_playing) { + artist = ""; + title = ""; + this->now_playing = now_playing; + } +}; + + class VLCInput { public: @@ -119,6 +147,9 @@ class VLCInput bool fault_detected() { return m_fault; }; + /*! Separator string used when artist/title are written + */ + static const std::string ICY_TEXT_SEPARATOR; private: /*! Stop the player and release resources */ @@ -160,8 +191,8 @@ class VLCInput */ std::future icy_text_written; std::mutex m_nowplaying_mutex; - std::string m_nowplaying; - std::string m_nowplaying_previous; + ICY_TEXT_T m_nowplaying; + ICY_TEXT_T m_nowplaying_previous; // VLC pointers libvlc_instance_t *m_vlc; -- cgit v1.2.3 From 5f2817a40d509e9dc172a161ac1a3efb66b8e7b8 Mon Sep 17 00:00:00 2001 From: Stefan Pöschel Date: Fri, 29 Jul 2016 21:33:53 +0200 Subject: MOT encoder: also try PNG when resizing Fixes also a memleak when more than one JPG quality is tried --- src/mot-encoder.cpp | 58 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/src/mot-encoder.cpp b/src/mot-encoder.cpp index 8393bb7..b17c74d 100644 --- a/src/mot-encoder.cpp +++ b/src/mot-encoder.cpp @@ -1067,9 +1067,13 @@ void warnOnSmallerImage(size_t height, size_t width, std::string& fname) { * \return the blobsize */ #if HAVE_MAGICKWAND -size_t resizeImage(MagickWand* m_wand, unsigned char** blob, std::string& fname) +size_t resizeImage(MagickWand* m_wand, unsigned char** blob, std::string& fname, bool* jfif_not_png) { - size_t blobsize; + unsigned char* blob_png; + unsigned char* blob_jpg; + size_t blobsize_png; + size_t blobsize_jpg; + size_t height = MagickGetImageHeight(m_wand); size_t width = MagickGetImageWidth(m_wand); @@ -1088,33 +1092,52 @@ size_t resizeImage(MagickWand* m_wand, unsigned char** blob, std::string& fname) height = MagickGetImageHeight(m_wand); width = MagickGetImageWidth(m_wand); - MagickSetImageFormat(m_wand, "jpg"); + // try PNG (zlib level 9 / possibly adaptive filtering) + MagickSetImageFormat(m_wand, "png"); + MagickSetImageCompressionQuality(m_wand, 95); + blob_png = MagickGetImageBlob(m_wand, &blobsize_png); - int quality = 100; + // try JPG + MagickSetImageFormat(m_wand, "jpg"); + blob_jpg = NULL; + int quality_jpg = 100; do { - quality -= 5; + free(blob_jpg); + quality_jpg -= 5; - MagickSetImageCompressionQuality(m_wand, quality); - *blob = MagickGetImagesBlob(m_wand, &blobsize); - } while (blobsize > MAXSLIDESIZE && quality > MINQUALITY); + MagickSetImageCompressionQuality(m_wand, quality_jpg); + blob_jpg = MagickGetImageBlob(m_wand, &blobsize_jpg); + } while (blobsize_jpg > MAXSLIDESIZE && quality_jpg > MINQUALITY); - if (blobsize > MAXSLIDESIZE) { - fprintf(stderr, "mot-encoder: Image Size too large after compression: %zu bytes\n", - blobsize); + // check for max size + if (blobsize_png > MAXSLIDESIZE && blobsize_jpg > MAXSLIDESIZE) { + fprintf(stderr, "mot-encoder: Image Size too large after compression: %zu bytes (PNG), %zu bytes (JPEG)\n", + blobsize_png, blobsize_jpg); + free(blob_png); + free(blob_jpg); return 0; } + // choose the smaller one (at least one does not exceed the max size) + *jfif_not_png = blobsize_jpg < blobsize_png; + if (verbose) { - fprintf(stderr, "mot-encoder resized image to %zu x %zu. Size after compression %zu bytes (q=%d)\n", - width, height, blobsize, quality); + if (*jfif_not_png) + fprintf(stderr, "mot-encoder resized image to %zu x %zu. Size after compression %zu bytes (JPEG, q=%d; PNG was %zu bytes)\n", + width, height, blobsize_jpg, quality_jpg, blobsize_png); + else + fprintf(stderr, "mot-encoder resized image to %zu x %zu. Size after compression %zu bytes (PNG; JPEG was %zu bytes)\n", + width, height, blobsize_png, blobsize_jpg); } // warn if resized image smaller than default dimension warnOnSmallerImage(height, width, fname); - return blobsize; + free(*jfif_not_png ? blob_png : blob_jpg); + *blob = *jfif_not_png ? blob_jpg : blob_png; + return *jfif_not_png ? blobsize_jpg : blobsize_png; } #endif @@ -1222,7 +1245,7 @@ int encodeFile(int output_fd, std::string& fname, int fidx, bool raw_slides) if ((orig_is_jpeg || orig_is_png) && height <= 240 && width <= 320 && not jpeg_progr) { // Don't recompress the image and check if the blobsize is suitable - blob = MagickGetImagesBlob(m_wand, &blobsize); + blob = MagickGetImageBlob(m_wand, &blobsize); if (blobsize <= MAXSLIDESIZE) { if (verbose) { @@ -1234,10 +1257,7 @@ int encodeFile(int output_fd, std::string& fname, int fidx, bool raw_slides) } if (resize_required) { - blobsize = resizeImage(m_wand, &blob, fname); - - // resizeImage always creates a jpg output - jfif_not_png = true; + blobsize = resizeImage(m_wand, &blob, fname, &jfif_not_png); } else { // warn if unresized image smaller than default dimension -- cgit v1.2.3