Hello community, here is the log from the commit of package pianobar for openSUSE:Factory checked in at 2019-02-15 10:02:33 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/pianobar (Old) and /work/SRC/openSUSE:Factory/.pianobar.new.28833 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "pianobar" Fri Feb 15 10:02:33 2019 rev:4 rq:674972 version:2019.02.14 Changes: -------- --- /work/SRC/openSUSE:Factory/pianobar/pianobar.changes 2018-07-09 13:31:22.602486273 +0200 +++ /work/SRC/openSUSE:Factory/.pianobar.new.28833/pianobar.changes 2019-02-15 10:02:36.247621842 +0100 @@ -1,0 +2,9 @@ +Thu Feb 14 12:53:46 UTC 2019 - Luigi Baldoni <aloi...@gmx.com> + +- Update to version 2019.02.14 + * Fix MP3 playback (affects premium subscribers with audio + quality “high”) + version 2019.01.25 + * Implement audio buffering + +------------------------------------------------------------------- Old: ---- pianobar-2018.06.22.tar.gz New: ---- pianobar-2019.02.14.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ pianobar.spec ++++++ --- /var/tmp/diff_new_pack.yeL8zR/_old 2019-02-15 10:02:37.935621298 +0100 +++ /var/tmp/diff_new_pack.yeL8zR/_new 2019-02-15 10:02:37.979621284 +0100 @@ -1,7 +1,7 @@ # # spec file for package pianobar # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # Copyright (c) 2016 Packman team: http://packman.links2linux.org/ # # All modifications and additions to the file contributed by third parties @@ -13,17 +13,17 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # Name: pianobar -Version: 2018.06.22 +Version: 2019.02.14 Release: 0 Summary: Pandora Player License: MIT Group: Productivity/Multimedia/Sound/Utilities -Url: https://6xq.net/pianobar +URL: https://6xq.net/pianobar Source0: https://github.com/PromyLOPh/pianobar/archive/%{version}.tar.gz#/%{name}-%{version}.tar.gz BuildRequires: libgcrypt-devel BuildRequires: pkgconfig ++++++ pianobar-2018.06.22.tar.gz -> pianobar-2019.02.14.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2018.06.22/.github/issue_template.md new/pianobar-2019.02.14/.github/issue_template.md --- old/pianobar-2018.06.22/.github/issue_template.md 1970-01-01 01:00:00.000000000 +0100 +++ new/pianobar-2019.02.14/.github/issue_template.md 2019-02-14 09:37:03.000000000 +0100 @@ -0,0 +1,19 @@ +### Subject of the issue +Briefly describe your issue here. + +### Your environment +* version of pianobar +* your Linux distribution and release version +* ffmpeg/libav version and the flags it was compiled with (if you compiled yourself) +* your config file + +### Steps to reproduce +Tell us how to reproduce this issue. + +### Expected behaviour +Tell us what should happen. + +### Actual behaviour +Tell us what happens instead, including a copy of all error messages. + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2018.06.22/ChangeLog new/pianobar-2019.02.14/ChangeLog --- old/pianobar-2018.06.22/ChangeLog 2018-06-22 08:57:26.000000000 +0200 +++ new/pianobar-2019.02.14/ChangeLog 2019-02-14 09:37:03.000000000 +0100 @@ -1,3 +1,11 @@ +Release 2019.02.14 + +- Fix MP3 playback (affects premium subscribers with audio quality “high”) + +Release 2019.01.25 + +- Implement audio buffering + Release 2018.06.22 - Happy 10th birthday pianobar! diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2018.06.22/README.md new/pianobar-2019.02.14/README.md --- old/pianobar-2018.06.22/README.md 2018-06-22 08:57:26.000000000 +0200 +++ new/pianobar-2019.02.14/README.md 2019-02-14 09:37:03.000000000 +0100 @@ -1,6 +1,6 @@ # pianobar -pianobar is a console client for the personalized web radio [Pandora](http://www.pandora.com). +pianobar is a console client for the personalized web radio [Pandora](https://www.pandora.com). ### Features @@ -13,9 +13,9 @@ ### Source Code -The source code can be downloaded at [github.com](http://github.com/PromyLOPh/pianobar/) -or [6xq.net](http://6xq.net/projects/pianobar/). +The source code can be downloaded at [github.com](https://github.com/PromyLOPh/pianobar) +or [6xq.net](https://6xq.net/pianobar/). ### Download/Installation -There are community provided packages available for most Linux distributions (see your distribution’s package manager), Mac OS X ([MacPorts](http://trac.macports.org/browser/trunk/dports/audio/pianobar/Portfile) or [homebrew](http://brew.sh/)) and *BSD as well as a [native Windows port](https://github.com/thedmd/pianobar-windows). +There are community provided packages available for most Linux distributions (see your distribution’s package manager), macOS ([MacPorts](https://www.macports.org) or [Homebrew](https://brew.sh)) and *BSD as well as a [native Windows port](https://github.com/thedmd/pianobar-windows). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2018.06.22/contrib/pianobar.1 new/pianobar-2019.02.14/contrib/pianobar.1 --- old/pianobar-2018.06.22/contrib/pianobar.1 2018-06-22 08:57:26.000000000 +0200 +++ new/pianobar-2019.02.14/contrib/pianobar.1 2019-02-14 09:37:03.000000000 +0100 @@ -209,6 +209,10 @@ .B route-nopull. .TP +.B buffer_seconds = 5 +Audio buffer size in seconds. + +.TP .B ca_bundle = /etc/ssl/certs/ca-certificates.crt Path to CA certifiate bundle, containing the root and intermediate certificates required to validate Pandora's SSL certificate. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2018.06.22/src/config.h new/pianobar-2019.02.14/src/config.h --- old/pianobar-2018.06.22/src/config.h 2018-06-22 08:57:26.000000000 +0200 +++ new/pianobar-2019.02.14/src/config.h 2019-02-14 09:37:03.000000000 +0100 @@ -3,7 +3,7 @@ /* package name */ #define PACKAGE "pianobar" -#define VERSION "2018.06.22" +#define VERSION "2019.02.14" /* glibc feature test macros, define _before_ including other files */ #define _POSIX_C_SOURCE 200809L @@ -12,10 +12,33 @@ * ffmpeg’s micro versions always start at 100, that’s how we can distinguish * ffmpeg and libav */ #include <libavfilter/version.h> +#include <libavformat/version.h> /* does graph_send_command exist (ffmpeg >=2.2) */ -#if LIBAVFILTER_VERSION_MAJOR >= 4 && \ +#if !defined(HAVE_AVFILTER_GRAPH_SEND_COMMAND) && \ + LIBAVFILTER_VERSION_MAJOR >= 4 && \ LIBAVFILTER_VERSION_MICRO >= 100 #define HAVE_AVFILTER_GRAPH_SEND_COMMAND #endif +/* explicit init is optional for ffmpeg>=4.0 */ +#if !defined(HAVE_AVFORMAT_NETWORK_INIT) && \ + LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 5, 100) && \ + LIBAVFORMAT_VERSION_MICRO >= 100 +#define HAVE_AVFORMAT_NETWORK_INIT +#endif + +/* dito */ +#if !defined(HAVE_AVFILTER_REGISTER_ALL) && \ + LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7, 14, 100) && \ + LIBAVFILTER_VERSION_MICRO >= 100 +#define HAVE_AVFILTER_REGISTER_ALL +#endif + +/* dito */ +#if !defined(HAVE_AV_REGISTER_ALL) && \ + LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100) && \ + LIBAVFORMAT_VERSION_MICRO >= 100 +#define HAVE_AV_REGISTER_ALL +#endif + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2018.06.22/src/player.c new/pianobar-2019.02.14/src/player.c --- old/pianobar-2018.06.22/src/player.c 2018-06-22 08:57:26.000000000 +0200 +++ new/pianobar-2019.02.14/src/player.c 2019-02-14 09:37:03.000000000 +0100 @@ -21,7 +21,16 @@ THE SOFTWARE. */ -/* receive/play audio stream */ +/* receive/play audio stream. + * + * There are two threads involved here: + * BarPlayerThread + * Sets up the stream and fetches the data into a ffmpeg buffersrc + * BarAoPlayThread + * Reads data from the filter chain’s sink and hands it over to libao for + * playback. + * + */ #include "config.h" @@ -61,19 +70,24 @@ } /* global initialization - * - * XXX: in theory we can select the filters/formats we want to support, but - * this does not work in practise. */ void BarPlayerInit (player_t * const p, const BarSettings_t * const settings) { ao_initialize (); av_log_set_level (AV_LOG_FATAL); +#ifdef HAVE_AV_REGISTER_ALL av_register_all (); +#endif +#ifdef HAVE_AVFILTER_REGISTER_ALL avfilter_register_all (); +#endif +#ifdef HAVE_AVFORMAT_NETWORK_INIT avformat_network_init (); +#endif pthread_mutex_init (&p->lock, NULL); pthread_cond_init (&p->cond, NULL); + pthread_mutex_init (&p->aoplayLock, NULL); + pthread_cond_init (&p->aoplayCond, NULL); BarPlayerReset (p); p->settings = settings; } @@ -81,8 +95,12 @@ void BarPlayerDestroy (player_t * const p) { pthread_cond_destroy (&p->cond); pthread_mutex_destroy (&p->lock); + pthread_cond_destroy (&p->aoplayCond); + pthread_mutex_destroy (&p->aoplayLock); +#ifdef HAVE_AVFORMAT_NETWORK_INIT avformat_network_deinit (); +#endif ao_shutdown (); } @@ -257,14 +275,14 @@ av_get_sample_fmt_name (player->cctx->sample_fmt), cp->channel_layout); if ((ret = avfilter_graph_create_filter (&player->fabuf, - avfilter_get_by_name ("abuffer"), NULL, strbuf, NULL, + avfilter_get_by_name ("abuffer"), "source", strbuf, NULL, player->fgraph)) < 0) { softfail ("create_filter abuffer"); } /* volume */ if ((ret = avfilter_graph_create_filter (&player->fvolume, - avfilter_get_by_name ("volume"), NULL, "0dB", NULL, + avfilter_get_by_name ("volume"), "volume", "0dB", NULL, player->fgraph)) < 0) { softfail ("create_filter volume"); } @@ -274,14 +292,14 @@ snprintf (strbuf, sizeof (strbuf), "sample_fmts=%s", av_get_sample_fmt_name (avformat)); if ((ret = avfilter_graph_create_filter (&fafmt, - avfilter_get_by_name ("aformat"), NULL, strbuf, NULL, + avfilter_get_by_name ("aformat"), "format", strbuf, NULL, player->fgraph)) < 0) { softfail ("create_filter aformat"); } /* abuffersink */ if ((ret = avfilter_graph_create_filter (&player->fbufsink, - avfilter_get_by_name ("abuffersink"), NULL, NULL, NULL, + avfilter_get_by_name ("abuffersink"), "sink", NULL, NULL, player->fgraph)) < 0) { softfail ("create_filter abuffersink"); } @@ -349,6 +367,7 @@ */ static int play (player_t * const player) { assert (player != NULL); + const int64_t minBufferHealth = player->settings->bufferSecs; AVPacket pkt; AVCodecContext * const cctx = player->cctx; @@ -356,14 +375,14 @@ pkt.data = NULL; pkt.size = 0; - AVFrame *frame = NULL, *filteredFrame = NULL; + AVFrame *frame = NULL; frame = av_frame_alloc (); assert (frame != NULL); - filteredFrame = av_frame_alloc (); - assert (filteredFrame != NULL); - + pthread_t aoplaythread; + pthread_create (&aoplaythread, NULL, BarAoPlayThread, player); enum { FILL, DRAIN, DONE } drainMode = FILL; int ret = 0; + const double timeBase = av_q2d (player->st->time_base); while (!shouldQuit (player) && drainMode != DONE) { if (drainMode == FILL) { ret = av_read_frame (player->fctx, &pkt); @@ -377,6 +396,12 @@ continue; } else if (ret < 0) { /* error, abort */ + /* mark the EOF, so that BarAoPlayThread can quit*/ + pthread_mutex_lock (&player->aoplayLock); + const int rt = av_buffersrc_add_frame (player->fabuf, NULL); + assert (rt == 0); + pthread_cond_broadcast (&player->aoplayCond); + pthread_mutex_unlock (&player->aoplayLock); break; } else { /* fill buffer */ @@ -384,22 +409,17 @@ } } - /* pausing */ - pthread_mutex_lock (&player->lock); - if (player->doPause) { - av_read_pause (player->fctx); - do { - pthread_cond_wait (&player->cond, &player->lock); - } while (player->doPause); - av_read_play (player->fctx); - } - pthread_mutex_unlock (&player->lock); - while (!shouldQuit (player)) { ret = avcodec_receive_frame (cctx, frame); if (ret == AVERROR_EOF) { /* done draining */ drainMode = DONE; + /* mark the EOF*/ + pthread_mutex_lock (&player->aoplayLock); + const int rt = av_buffersrc_add_frame (player->fabuf, NULL); + assert (rt == 0); + pthread_cond_broadcast (&player->aoplayCond); + pthread_mutex_unlock (&player->aoplayLock); break; } else if (ret != 0) { /* no more output */ @@ -410,36 +430,29 @@ if (frame->pts == (int64_t) AV_NOPTS_VALUE) { frame->pts = 0; } + pthread_mutex_lock (&player->aoplayLock); ret = av_buffersrc_write_frame (player->fabuf, frame); assert (ret >= 0); - - while (true) { - if (av_buffersink_get_frame (player->fbufsink, filteredFrame) < 0) { - /* try again next frame */ - break; + pthread_mutex_unlock (&player->aoplayLock); + + int64_t bufferHealth = 0; + do { + pthread_mutex_lock (&player->aoplayLock); + bufferHealth = timeBase * (double) (frame->pts - player->lastTimestamp); + if (bufferHealth > minBufferHealth) { + /* Buffer get healthy, resume */ + pthread_cond_broadcast (&player->aoplayCond); + /* Buffer is healthy enough, wait */ + pthread_cond_wait (&player->aoplayCond, &player->aoplayLock); } - - const int numChannels = av_get_channel_layout_nb_channels ( - filteredFrame->channel_layout); - const int bps = av_get_bytes_per_sample(filteredFrame->format); - ao_play (player->aoDev, (char *) filteredFrame->data[0], - filteredFrame->nb_samples * numChannels * bps); - - av_frame_unref (filteredFrame); - } + pthread_mutex_unlock (&player->aoplayLock); + } while (bufferHealth > minBufferHealth); } - const unsigned int songPlayed = av_q2d (player->st->time_base) * (double) pkt.pts; - pthread_mutex_lock (&player->lock); - player->songPlayed = songPlayed; - pthread_mutex_unlock (&player->lock); - player->lastTimestamp = pkt.pts; - av_packet_unref (&pkt); } - - av_frame_free (&filteredFrame); av_frame_free (&frame); + pthread_join (aoplaythread, NULL); return ret; } @@ -496,3 +509,65 @@ return (void *) pret; } +void *BarAoPlayThread (void *data) { + assert (data != NULL); + + player_t * const player = data; + + AVFrame *filteredFrame = NULL; + filteredFrame = av_frame_alloc (); + assert (filteredFrame != NULL); + + int ret; + const double timeBase = av_q2d (av_buffersink_get_time_base (player->fbufsink)), + timeBaseSt = av_q2d (player->st->time_base); + while (!shouldQuit(player)) { + pthread_mutex_lock (&player->aoplayLock); + ret = av_buffersink_get_frame (player->fbufsink, filteredFrame); + if (ret == AVERROR_EOF || shouldQuit (player)) { + /* we are done here */ + pthread_mutex_unlock (&player->aoplayLock); + break; + } else if (ret < 0) { + /* wait for more frames */ + pthread_cond_broadcast (&player->aoplayCond); + pthread_cond_wait (&player->aoplayCond, &player->aoplayLock); + pthread_mutex_unlock (&player->aoplayLock); + continue; + } + pthread_mutex_unlock (&player->aoplayLock); + + const int numChannels = av_get_channel_layout_nb_channels ( + filteredFrame->channel_layout); + const int bps = av_get_bytes_per_sample (filteredFrame->format); + ao_play (player->aoDev, (char *) filteredFrame->data[0], + filteredFrame->nb_samples * numChannels * bps); + + const double timestamp = (double) filteredFrame->pts * timeBase; + const unsigned int songPlayed = timestamp; + + pthread_mutex_lock (&player->lock); + player->songPlayed = songPlayed; + /* pausing */ + if (player->doPause) { + do { + pthread_cond_wait (&player->cond, &player->lock); + } while (player->doPause); + } + pthread_mutex_unlock (&player->lock); + + /* lastTimestamp must be the last pts, but expressed in terms of + * st->time_base, not the sink’s time_base. */ + const int64_t lastTimestamp = timestamp/timeBaseSt; + /* notify download thread, we might need more data */ + pthread_mutex_lock (&player->aoplayLock); + player->lastTimestamp = lastTimestamp; + pthread_cond_broadcast (&player->aoplayCond); + pthread_mutex_unlock (&player->aoplayLock); + + av_frame_unref (filteredFrame); + } + av_frame_free (&filteredFrame); + + return (void *) 0; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2018.06.22/src/player.h new/pianobar-2019.02.14/src/player.h --- old/pianobar-2018.06.22/src/player.h 2018-06-22 08:57:26.000000000 +0200 +++ new/pianobar-2019.02.14/src/player.h 2019-02-14 09:37:03.000000000 +0100 @@ -51,9 +51,8 @@ typedef struct { /* public attributes protected by mutex */ - pthread_mutex_t lock; - pthread_cond_t cond; /* broadcast changes to doPause */ - + pthread_mutex_t lock, aoplayLock; + pthread_cond_t cond, aoplayCond; /* broadcast changes to doPause */ bool doQuit, doPause; /* measured in seconds */ @@ -86,6 +85,7 @@ enum {PLAYER_RET_OK = 0, PLAYER_RET_HARDFAIL = 1, PLAYER_RET_SOFTFAIL = 2}; void *BarPlayerThread (void *data); +void *BarAoPlayThread (void *data); void BarPlayerSetVolume (player_t * const player); void BarPlayerInit (player_t * const p, const BarSettings_t * const settings); void BarPlayerReset (player_t * const p); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2018.06.22/src/settings.c new/pianobar-2019.02.14/src/settings.c --- old/pianobar-2018.06.22/src/settings.c 2018-06-22 08:57:26.000000000 +0200 +++ new/pianobar-2019.02.14/src/settings.c 2019-02-14 09:37:03.000000000 +0100 @@ -163,7 +163,9 @@ settings->volume = 0; settings->timeout = 30; /* seconds */ settings->gainMul = 1.0; - settings->maxRetry = 3; + /* should be > 4, otherwise expired audio urls (403) can stop playback */ + settings->maxRetry = 5; + settings->bufferSecs = 5; settings->sortOrder = BAR_SORT_NAME_AZ; settings->loveIcon = strdup (" <3"); settings->banIcon = strdup (" </3"); @@ -342,6 +344,8 @@ settings->maxRetry = atoi (val); } else if (streq ("timeout", key)) { settings->timeout = atoi (val); + } else if (streq ("buffer_seconds", key)) { + settings->bufferSecs = atoi (val); } else if (streq ("sort", key)) { size_t i; static const char *mapping[] = {"name_az", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2018.06.22/src/settings.h new/pianobar-2019.02.14/src/settings.h --- old/pianobar-2018.06.22/src/settings.h 2018-06-22 08:57:26.000000000 +0200 +++ new/pianobar-2019.02.14/src/settings.h 2019-02-14 09:37:03.000000000 +0100 @@ -84,7 +84,7 @@ typedef struct { bool autoselect; - unsigned int history, maxRetry, timeout; + unsigned int history, maxRetry, timeout, bufferSecs; int volume; float gainMul; BarStationSorting_t sortOrder; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2018.06.22/src/ui.c new/pianobar-2019.02.14/src/ui.c --- old/pianobar-2018.06.22/src/ui.c 2018-06-22 08:57:26.000000000 +0200 +++ new/pianobar-2019.02.14/src/ui.c 2019-02-14 09:37:03.000000000 +0100 @@ -250,7 +250,7 @@ free (buffer.data); buffer.data = NULL; buffer.pos = 0; - if (retry > settings->maxRetry) { + if (retry >= settings->maxRetry) { break; } } else { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pianobar-2018.06.22/src/ui_act.c new/pianobar-2019.02.14/src/ui_act.c --- old/pianobar-2018.06.22/src/ui_act.c 2018-06-22 08:57:26.000000000 +0200 +++ new/pianobar-2019.02.14/src/ui_act.c 2019-02-14 09:37:03.000000000 +0100 @@ -57,6 +57,9 @@ player->doPause = false; pthread_cond_broadcast (&player->cond); pthread_mutex_unlock (&player->lock); + pthread_mutex_lock (&player->aoplayLock); + pthread_cond_broadcast (&player->aoplayCond); + pthread_mutex_unlock (&player->aoplayLock); } /* transform station if necessary to allow changes like rename, rate, ... @@ -561,15 +564,15 @@ if (selStation->isQuickMix) { PianoStation_t *toggleStation; while ((toggleStation = BarUiSelectStation (app, app->ph.stations, - "Toggle quickmix for station: ", + "Toggle QuickMix for station: ", BarUiActQuickmixCallback, false)) != NULL) { toggleStation->useQuickMix = !toggleStation->useQuickMix; } - BarUiMsg (&app->settings, MSG_INFO, "Setting quickmix stations... "); + BarUiMsg (&app->settings, MSG_INFO, "Setting QuickMix stations... "); BarUiActDefaultPianoCall (PIANO_REQUEST_SET_QUICKMIX, NULL); BarUiActDefaultEventcmd ("stationquickmixtoggle"); } else { - BarUiMsg (&app->settings, MSG_ERR, "Not a QuickMix station.\n"); + BarUiMsg (&app->settings, MSG_ERR, "Please select a QuickMix station first.\n"); } } @@ -881,4 +884,3 @@ PianoDestroyStationInfo (&reqData.info); } -