diff options
author | Michał Cichoń <michcic@gmail.com> | 2015-08-25 06:47:41 +0200 |
---|---|---|
committer | Michał Cichoń <michcic@gmail.com> | 2015-08-25 06:47:41 +0200 |
commit | 4f43140468cefba39573d1efbded5258fcc56c93 (patch) | |
tree | 7aa850b6252c1773ba33e9efa985f712709ea5af /src/player.c | |
parent | a3bae55d41f5159df3a0125f02a8fa322e81bbe3 (diff) | |
download | pianobar-windows-4f43140468cefba39573d1efbded5258fcc56c93.tar.gz pianobar-windows-4f43140468cefba39573d1efbded5258fcc56c93.tar.bz2 pianobar-windows-4f43140468cefba39573d1efbded5258fcc56c93.zip |
Port pianobar to Windows:
- use newly introduced console.h instead of terminal.h which emulate some behavior of VT terminals
- replace ffmpeg/libov player with more abstract one with DirectShow implementation
Diffstat (limited to 'src/player.c')
-rw-r--r-- | src/player.c | 451 |
1 files changed, 0 insertions, 451 deletions
diff --git a/src/player.c b/src/player.c deleted file mode 100644 index 0ba0f36..0000000 --- a/src/player.c +++ /dev/null @@ -1,451 +0,0 @@ -/* -Copyright (c) 2008-2014 - Lars-Dominik Braun <lars@6xq.net> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -/* receive/play audio stream */ - -#include "config.h" - -#include <unistd.h> -#include <string.h> -#include <math.h> -#include <stdint.h> -#include <limits.h> -#include <assert.h> -#include <arpa/inet.h> - -#include <libavcodec/avcodec.h> -#include <libavutil/avutil.h> -#include <libavfilter/avfilter.h> -#include <libavfilter/avfiltergraph.h> -#include <libavfilter/buffersink.h> -#include <libavfilter/buffersrc.h> -#ifdef HAVE_LIBAVFILTER_AVCODEC_H -/* required by ffmpeg1.2 for avfilter_copy_buf_props */ -#include <libavfilter/avcodec.h> -#endif -#include <libavutil/channel_layout.h> -#include <libavutil/opt.h> -#ifndef HAVE_AV_TIMEOUT -#include <libavutil/time.h> -#endif - -#include "player.h" -#include "ui.h" -#include "ui_types.h" - -/* default sample format */ -const enum AVSampleFormat avformat = AV_SAMPLE_FMT_S16; - -static void printError (const BarSettings_t * const settings, - const char * const msg, int ret) { - char avmsg[128]; - av_strerror (ret, avmsg, sizeof (avmsg)); - BarUiMsg (settings, MSG_ERR, "%s (%s)\n", msg, avmsg); -} - -/* global initialization - * - * XXX: in theory we can select the filters/formats we want to support, but - * this does not work in practise. - */ -void BarPlayerInit () { - ao_initialize (); - av_register_all (); - avfilter_register_all (); - avformat_network_init (); -} - -void BarPlayerDestroy () { - avformat_network_deinit (); - avfilter_uninit (); - ao_shutdown (); -} - -/* Update volume filter - */ -void BarPlayerSetVolume (player_t * const player) { - assert (player != NULL); - - if (player->mode != PLAYER_PLAYING) { - return; - } - - int ret; -#ifdef HAVE_AVFILTER_GRAPH_SEND_COMMAND - /* ffmpeg and libav disagree on the type of this option (string vs. double) - * -> print to string and let them parse it again */ - char strbuf[16]; - snprintf (strbuf, sizeof (strbuf), "%fdB", - player->settings->volume + player->gain); - assert (player->fgraph != NULL); - if ((ret = avfilter_graph_send_command (player->fgraph, "volume", "volume", - strbuf, NULL, 0, 0)) < 0) { -#else - /* convert from decibel */ - const double volume = pow (10, (player->settings->volume + player->gain) / 20); - /* libav does not provide other means to set this right now. it might not - * even work everywhere. */ - assert (player->fvolume != NULL); - if ((ret = av_opt_set_double (player->fvolume->priv, "volume", volume, - 0)) != 0) { -#endif - printError (player->settings, "Cannot set volume", ret); - } -} - -#define softfail(msg) \ - printError (player->settings, msg, ret); \ - return false; - -#ifndef HAVE_AV_TIMEOUT -/* interrupt callback for libav, which lacks a timeout option - * - * obviously calling ping() a lot of times and then calling av_gettime here - * again is rather inefficient. - */ -static int intCb (void * const data) { - player_t * const player = data; - assert (player != NULL); - /* 10 seconds timeout (usec) */ - return (av_gettime () - player->ping) > 10*1000000; -} - -#define ping() player->ping = av_gettime () -#else -#define ping() -#endif - -static bool openStream (player_t * const player) { - assert (player != NULL); - /* no leak? */ - assert (player->fctx == NULL); - - int ret; - - /* stream setup */ - AVDictionary *options = NULL; -#ifdef HAVE_AV_TIMEOUT - /* 10 seconds timeout on TCP r/w */ - av_dict_set (&options, "timeout", "10000000", 0); -#else - /* libav does not support the timeout option above. the workaround stores - * the current time with ping() now and then, registers an interrupt - * callback (below) and compares saved/current time in this callback. it’s - * not bullet-proof, but seems to work fine for av_read_frame. */ - player->fctx = avformat_alloc_context (); - player->fctx->interrupt_callback.callback = intCb; - player->fctx->interrupt_callback.opaque = player; -#endif - - assert (player->url != NULL); - ping (); - if ((ret = avformat_open_input (&player->fctx, player->url, NULL, &options)) < 0) { - softfail ("Unable to open audio file"); - } - - ping (); - if ((ret = avformat_find_stream_info (player->fctx, NULL)) < 0) { - softfail ("find_stream_info"); - } - - /* ignore all streams, undone for audio stream below */ - for (size_t i = 0; i < player->fctx->nb_streams; i++) { - player->fctx->streams[i]->discard = AVDISCARD_ALL; - } - - ping (); - player->streamIdx = av_find_best_stream (player->fctx, AVMEDIA_TYPE_AUDIO, - -1, -1, NULL, 0); - if (player->streamIdx < 0) { - softfail ("find_best_stream"); - } - - player->st = player->fctx->streams[player->streamIdx]; - AVCodecContext * const cctx = player->st->codec; - player->st->discard = AVDISCARD_DEFAULT; - - /* decoder setup */ - AVCodec * const decoder = avcodec_find_decoder (cctx->codec_id); - if (decoder == NULL) { - softfail ("find_decoder"); - } - - if ((ret = avcodec_open2 (cctx, decoder, NULL)) < 0) { - softfail ("codec_open2"); - } - - if (player->lastTimestamp > 0) { - ping (); - av_seek_frame (player->fctx, player->streamIdx, player->lastTimestamp, 0); - } - - player->songPlayed = 0; - player->songDuration = av_q2d (player->st->time_base) * - (double) player->st->duration; - - return true; -} - -/* setup filter chain - */ -static bool openFilter (player_t * const player) { - /* filter setup */ - char strbuf[256]; - int ret = 0; - AVCodecContext * const cctx = player->st->codec; - - if ((player->fgraph = avfilter_graph_alloc ()) == NULL) { - softfail ("graph_alloc"); - } - - /* abuffer */ - AVRational time_base = player->st->time_base; - - /* Workaround for a bug in libav-11, which reports an invalid channel - * layout mp3 files */ - if (cctx->channel_layout == 0) { - cctx->channel_layout = av_get_default_channel_layout (cctx->channels); - } - - snprintf (strbuf, sizeof (strbuf), - "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%"PRIx64, - time_base.num, time_base.den, cctx->sample_rate, - av_get_sample_fmt_name (cctx->sample_fmt), - cctx->channel_layout); - if ((ret = avfilter_graph_create_filter (&player->fabuf, - avfilter_get_by_name ("abuffer"), NULL, strbuf, NULL, - player->fgraph)) < 0) { - softfail ("create_filter abuffer"); - } - - /* volume */ - if ((ret = avfilter_graph_create_filter (&player->fvolume, - avfilter_get_by_name ("volume"), NULL, NULL, NULL, - player->fgraph)) < 0) { - softfail ("create_filter volume"); - } - - /* aformat: convert float samples into something more usable */ - AVFilterContext *fafmt = NULL; - 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, - player->fgraph)) < 0) { - softfail ("create_filter aformat"); - } - - /* abuffersink */ - if ((ret = avfilter_graph_create_filter (&player->fbufsink, - avfilter_get_by_name ("abuffersink"), NULL, NULL, NULL, - player->fgraph)) < 0) { - softfail ("create_filter abuffersink"); - } - - /* connect filter: abuffer -> volume -> aformat -> abuffersink */ - if (avfilter_link (player->fabuf, 0, player->fvolume, 0) != 0 || - avfilter_link (player->fvolume, 0, fafmt, 0) != 0 || - avfilter_link (fafmt, 0, player->fbufsink, 0) != 0) { - softfail ("filter_link"); - } - - if ((ret = avfilter_graph_config (player->fgraph, NULL)) < 0) { - softfail ("graph_config"); - } - - return true; -} - -/* setup libao - */ -static bool openDevice (player_t * const player) { - AVCodecContext * const cctx = player->st->codec; - - ao_sample_format aoFmt; - memset (&aoFmt, 0, sizeof (aoFmt)); - aoFmt.bits = av_get_bytes_per_sample (avformat) * 8; - assert (aoFmt.bits > 0); - aoFmt.channels = cctx->channels; - aoFmt.rate = cctx->sample_rate; - aoFmt.byte_format = AO_FMT_NATIVE; - - int driver = ao_default_driver_id (); - if ((player->aoDev = ao_open_live (driver, &aoFmt, NULL)) == NULL) { - BarUiMsg (player->settings, MSG_ERR, "Cannot open audio device.\n"); - return false; - } - - return true; -} - -/* decode and play stream. returns 0 or av error code. - */ -static int play (player_t * const player) { - assert (player != NULL); - - AVPacket pkt; - av_init_packet (&pkt); - pkt.data = NULL; - pkt.size = 0; - - AVFrame *frame = NULL, *filteredFrame = NULL; - frame = avcodec_alloc_frame (); - assert (frame != NULL); - filteredFrame = avcodec_alloc_frame (); - assert (filteredFrame != NULL); - - while (!player->doQuit) { - ping (); - int ret = av_read_frame (player->fctx, &pkt); - if (ret < 0) { - av_free_packet (&pkt); - return ret; - } else if (pkt.stream_index != player->streamIdx) { - av_free_packet (&pkt); - continue; - } - - AVPacket pkt_orig = pkt; - - /* pausing */ - pthread_mutex_lock (&player->pauseMutex); - if (player->doPause) { - av_read_pause (player->fctx); - do { - pthread_cond_wait (&player->pauseCond, &player->pauseMutex); - } while (player->doPause); - av_read_play (player->fctx); - } - pthread_mutex_unlock (&player->pauseMutex); - - while (pkt.size > 0 && !player->doQuit) { - int got_frame = 0; - - const int decoded = avcodec_decode_audio4 (player->st->codec, - frame, &got_frame, &pkt); - if (decoded < 0) { - /* skip this one */ - break; - } - - if (got_frame != 0) { - /* XXX: suppresses warning from resample filter */ - if (frame->pts == (int64_t) AV_NOPTS_VALUE) { - frame->pts = 0; - } - ret = av_buffersrc_write_frame (player->fabuf, frame); - assert (ret >= 0); - - while (true) { - AVFilterBufferRef *audioref = NULL; -#ifdef HAVE_AV_BUFFERSINK_GET_BUFFER_REF - /* ffmpeg’s compatibility layer is broken in some releases */ - if (av_buffersink_get_buffer_ref (player->fbufsink, - &audioref, 0) < 0) { -#else - if (av_buffersink_read (player->fbufsink, &audioref) < 0) { -#endif - /* try again next frame */ - break; - } - - ret = avfilter_copy_buf_props (filteredFrame, audioref); - assert (ret >= 0); - - 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); - - avfilter_unref_bufferp (&audioref); - } - } - - pkt.data += decoded; - pkt.size -= decoded; - }; - - av_free_packet (&pkt_orig); - - player->songPlayed = av_q2d (player->st->time_base) * (double) pkt.pts; - player->lastTimestamp = pkt.pts; - } - - avcodec_free_frame (&filteredFrame); - avcodec_free_frame (&frame); - - return 0; -} - -static void finish (player_t * const player) { - ao_close (player->aoDev); - player->aoDev = NULL; - if (player->fgraph != NULL) { - avfilter_graph_free (&player->fgraph); - player->fgraph = NULL; - } - if (player->st != NULL && player->st->codec != NULL) { - avcodec_close (player->st->codec); - player->st = NULL; - } - if (player->fctx != NULL) { - avformat_close_input (&player->fctx); - } -} - -/* player thread; for every song a new thread is started - * @param audioPlayer structure - * @return PLAYER_RET_* - */ -void *BarPlayerThread (void *data) { - assert (data != NULL); - - player_t * const player = data; - intptr_t pret = PLAYER_RET_OK; - - bool retry; - do { - retry = false; - if (openStream (player)) { - if (openFilter (player) && openDevice (player)) { - player->mode = PLAYER_PLAYING; - BarPlayerSetVolume (player); - retry = play (player) == AVERROR_INVALIDDATA; - } else { - /* filter missing or audio device busy */ - pret = PLAYER_RET_HARDFAIL; - } - } else { - /* stream not found */ - pret = PLAYER_RET_SOFTFAIL; - } - player->mode = PLAYER_WAITING; - finish (player); - } while (retry); - - player->mode = PLAYER_FINISHED; - - return (void *) pret; -} - |