From 8012d6cb4bb65a858105ef878c5b98d91b24e1cd Mon Sep 17 00:00:00 2001
From: Lars-Dominik Braun <lars@6xq.net>
Date: Mon, 7 Apr 2014 17:25:20 +0200
Subject: ffmpeg compatibility

Adds support for ffmpeg 2.2 and 1.2. Right now the maintenance overhead
of supporting both libav implementations is not that big.

Fixes #437 and #435.
---
 Makefile     | 54 +++++++++++++++++++++++++++++++++++-------------------
 src/player.c | 40 ++++++++++++++++++++++++++++------------
 src/player.h |  4 +++-
 3 files changed, 66 insertions(+), 32 deletions(-)

diff --git a/Makefile b/Makefile
index 2267419..53b5cb0 100644
--- a/Makefile
+++ b/Makefile
@@ -85,30 +85,52 @@ LIBGCRYPT_LDFLAGS:=-lgcrypt
 LIBJSONC_CFLAGS:=$(shell pkg-config --cflags json-c 2>/dev/null || pkg-config --cflags json)
 LIBJSONC_LDFLAGS:=$(shell pkg-config --libs json-c 2>/dev/null || pkg-config --libs json)
 
+# simple feature testing
+define TEST_AV_BUFFERSINK_GET_BUFFER_REF
+#include <libavfilter/buffersink.h>\n
+int main() {
+	void *foo = av_buffersink_get_buffer_ref;
+}
+endef
+
+define TEST_AVFILTER_GRAPH_SEND_COMMAND
+#include <libavfilter/avfiltergraph.h>\n
+int main() {
+	void *foo = avfilter_graph_send_command;
+}
+endef
+
+EXTRA_CFLAGS:=\
+		$(shell echo -e "${TEST_AV_BUFFERSINK_GET_BUFFER_REF}" | ${CC} ${LIBAV_CFLAGS} ${LIBAV_LDFLAGS} -x c -c -o /dev/null - 2>/dev/null && echo "-DHAVE_AV_BUFFERSINK_GET_BUFFER_REF") \
+		$(shell echo -e "${TEST_AVFILTER_GRAPH_SEND_COMMAND}" | ${CC} ${LIBAV_CFLAGS} ${LIBAV_LDFLAGS} -x c -c -o /dev/null - 2>/dev/null && echo "-DHAVE_AVFILTER_GRAPH_SEND_COMMAND")
+
+# combine all flags
+ALL_CFLAGS:=${CFLAGS} -I ${LIBPIANO_INCLUDE} -I ${LIBWAITRESS_INCLUDE} \
+			${LIBAV_CFLAGS} ${LIBGNUTLS_CFLAGS} \
+			${LIBGCRYPT_CFLAGS} ${LIBJSONC_CFLAGS} ${EXTRA_CFLAGS}
+ALL_LDFLAGS:=${LDFLAGS} -lao -lpthread -lm \
+			${LIBAV_LDFLAGS} ${LIBGNUTLS_LDFLAGS} \
+			${LIBGCRYPT_LDFLAGS} ${LIBJSONC_LDFLAGS}
+
 # build pianobar
 ifeq (${DYNLINK},1)
 pianobar: ${PIANOBAR_OBJ} ${PIANOBAR_HDR} libpiano.so.0
 	@echo "  LINK  $@"
-	@${CC} -o $@ ${PIANOBAR_OBJ} ${LDFLAGS} -lao -lpthread -lm -L. -lpiano \
-			${LIBAV_LDFLAGS} ${LIBGNUTLS_LDFLAGS} ${LIBGCRYPT_LDFLAGS}
+	@${CC} -o $@ ${PIANOBAR_OBJ} -L. -lpiano ${ALL_LDFLAGS}
 else
 pianobar: ${PIANOBAR_OBJ} ${PIANOBAR_HDR} ${LIBPIANO_OBJ} ${LIBWAITRESS_OBJ} \
 		${LIBWAITRESS_HDR}
 	@echo "  LINK  $@"
-	@${CC} ${CFLAGS} ${LDFLAGS} ${PIANOBAR_OBJ} ${LIBPIANO_OBJ} \
-			${LIBWAITRESS_OBJ} -lao -lpthread -lm \
-			${LIBAV_LDFLAGS} ${LIBGNUTLS_LDFLAGS} \
-			${LIBGCRYPT_LDFLAGS} ${LIBJSONC_LDFLAGS} -o $@
+	@${CC} -o $@ ${PIANOBAR_OBJ} ${LIBPIANO_OBJ} ${LIBWAITRESS_OBJ} \
+			${ALL_LDFLAGS}
 endif
 
 # build shared and static libpiano
 libpiano.so.0: ${LIBPIANO_RELOBJ} ${LIBPIANO_HDR} ${LIBWAITRESS_RELOBJ} \
 		${LIBWAITRESS_HDR} ${LIBPIANO_OBJ} ${LIBWAITRESS_OBJ}
 	@echo "  LINK  $@"
-	@${CC} -shared -Wl,-soname,libpiano.so.0 ${CFLAGS} ${LDFLAGS} \
-			-o libpiano.so.0.0.0 ${LIBPIANO_RELOBJ} \
-			${LIBWAITRESS_RELOBJ} ${LIBGNUTLS_LDFLAGS} ${LIBGCRYPT_LDFLAGS} \
-			${LIBJSONC_LDFLAGS}
+	@${CC} -shared -Wl,-soname,libpiano.so.0 -o libpiano.so.0.0.0 \
+			${LIBPIANO_RELOBJ} ${LIBWAITRESS_RELOBJ} ${ALL_LDFLAGS}
 	@ln -s libpiano.so.0.0.0 libpiano.so.0
 	@ln -s libpiano.so.0 libpiano.so
 	@echo "    AR  libpiano.a"
@@ -118,9 +140,7 @@ libpiano.so.0: ${LIBPIANO_RELOBJ} ${LIBPIANO_HDR} ${LIBWAITRESS_RELOBJ} \
 # build dependency files
 %.d: %.c
 	@set -e; rm -f $@; \
-			$(CC) -M ${CFLAGS} -I ${LIBPIANO_INCLUDE} -I ${LIBWAITRESS_INCLUDE} \
-			${LIBAV_CFLAGS} ${LIBGNUTLS_CFLAGS} \
-			${LIBGCRYPT_CFLAGS} ${LIBJSONC_CFLAGS} $< > $@.$$$$; \
+			$(CC) -M ${ALL_CFLAGS} $< > $@.$$$$; \
 			sed '1 s,^.*\.o[ :]*,$*.o $@ : ,g' < $@.$$$$ > $@; \
 			rm -f $@.$$$$
 
@@ -131,16 +151,12 @@ libpiano.so.0: ${LIBPIANO_RELOBJ} ${LIBPIANO_HDR} ${LIBWAITRESS_RELOBJ} \
 # build standard object files
 %.o: %.c
 	@echo "    CC  $<"
-	@${CC} ${CFLAGS} -I ${LIBPIANO_INCLUDE} -I ${LIBWAITRESS_INCLUDE} \
-			${LIBAV_CFLAGS} ${LIBGNUTLS_CFLAGS} \
-			${LIBGCRYPT_CFLAGS} ${LIBJSONC_CFLAGS} -c -o $@ $<
+	@${CC} -c -o $@ ${ALL_CFLAGS} $<
 
 # create position independent code (for shared libraries)
 %.lo: %.c
 	@echo "    CC  $< (PIC)"
-	@${CC} ${CFLAGS} -I ${LIBPIANO_INCLUDE} -I ${LIBWAITRESS_INCLUDE} \
-			${LIBJSONC_CFLAGS} \
-			-c -fPIC -o $@ $<
+	@${CC} -c -fPIC -o $@ ${ALL_CFLAGS} $<
 
 clean:
 	@echo " CLEAN"
diff --git a/src/player.c b/src/player.c
index ac5c42b..f3d9838 100644
--- a/src/player.c
+++ b/src/player.c
@@ -73,19 +73,28 @@ void BarPlayerDestroy () {
 }
 
 /*	Update volume filter
- *
- *	XXX: I’m not sure whether this is thread-safe or not
  */
 void BarPlayerSetVolume (struct audioPlayer * const player) {
 	assert (player != NULL);
 	assert (player->fvolume != NULL);
 
 	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);
+	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);
-	/* XXX: can we avoid accessing ->priv here? */
+	/* libav does not provide other means to set this right now. it might not
+	 * even work everywhere. */
 	if ((ret = av_opt_set_double (player->fvolume->priv, "volume", volume,
 			0)) != 0) {
+#endif
 		printError (player->settings, "Cannot set volume", ret);
 	}
 }
@@ -110,7 +119,6 @@ void *BarPlayerThread (void *data) {
 	ao_device *aoDev = NULL;
 	ao_sample_format aoFmt;
 
-	AVFilterGraph *fgraph = NULL;
 	AVFrame *frame = NULL, *filteredFrame = NULL;
 	AVFormatContext *fctx = NULL;
 	AVCodecContext *cctx = NULL;
@@ -162,7 +170,7 @@ void *BarPlayerThread (void *data) {
 	/* filter setup */
 	char strbuf[256];
 
-	if ((fgraph = avfilter_graph_alloc ()) == NULL) {
+	if ((player->fgraph = avfilter_graph_alloc ()) == NULL) {
 		softfail ("graph_alloc");
 	}
 
@@ -175,13 +183,15 @@ void *BarPlayerThread (void *data) {
 			av_get_sample_fmt_name (cctx->sample_fmt),
 			cctx->channel_layout);
 	if ((ret = avfilter_graph_create_filter (&fabuf,
-			avfilter_get_by_name ("abuffer"), NULL, strbuf, NULL, fgraph)) < 0) {
+			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, fgraph)) < 0) {
+			avfilter_get_by_name ("volume"), NULL, NULL, NULL,
+			player->fgraph)) < 0) {
 		softfail ("create_filter volume");
 	}
 	BarPlayerSetVolume (player);
@@ -192,14 +202,15 @@ void *BarPlayerThread (void *data) {
 			av_get_sample_fmt_name (avformat));
 	if ((ret = avfilter_graph_create_filter (&fafmt,
 					avfilter_get_by_name ("aformat"), NULL, strbuf, NULL,
-					fgraph)) < 0) {
+					player->fgraph)) < 0) {
 		softfail ("create_filter aformat");
 	}
 
 	/* abuffersink */
 	AVFilterContext *fbufsink = NULL;
 	if ((ret = avfilter_graph_create_filter (&fbufsink,
-			avfilter_get_by_name ("abuffersink"), NULL, NULL, NULL, fgraph)) < 0) {
+			avfilter_get_by_name ("abuffersink"), NULL, NULL, NULL,
+			player->fgraph)) < 0) {
 		softfail ("create_filter abuffersink");
 	}
 
@@ -210,7 +221,7 @@ void *BarPlayerThread (void *data) {
 		softfail ("filter_link");
 	}
 
-	if ((ret = avfilter_graph_config (fgraph, NULL)) < 0) {
+	if ((ret = avfilter_graph_config (player->fgraph, NULL)) < 0) {
 		softfail ("graph_config");
 	}
 
@@ -277,7 +288,12 @@ void *BarPlayerThread (void *data) {
 
 				while (true) {
 					AVFilterBufferRef *audioref = NULL;
+#ifdef TEST_AV_BUFFERSINK_GET_BUFFER_REF
+					/* ffmpeg’s compatibility layer is broken in some releases */
+					if (av_buffersink_get_buffer_ref (fbufsink, &audioref, 0) < 0) {
+#else
 					if (av_buffersink_read (fbufsink, &audioref) < 0) {
+#endif
 						/* try again next frame */
 						break;
 					}
@@ -306,8 +322,8 @@ void *BarPlayerThread (void *data) {
 
 finish:
 	ao_close (aoDev);
-	if (fgraph != NULL) {
-		avfilter_graph_free (&fgraph);
+	if (player->fgraph != NULL) {
+		avfilter_graph_free (&player->fgraph);
 	}
 	if (cctx != NULL) {
 		avcodec_close (cctx);
diff --git a/src/player.h b/src/player.h
index 562c12d..f9e94ef 100644
--- a/src/player.h
+++ b/src/player.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2013
+Copyright (c) 2008-2014
 	Lars-Dominik Braun <lars@6xq.net>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -32,6 +32,7 @@ THE SOFTWARE.
 #include <stdint.h>
 
 #include <libavfilter/avfilter.h>
+#include <libavfilter/avfiltergraph.h>
 #include <piano.h>
 #include <waitress.h>
 
@@ -52,6 +53,7 @@ struct audioPlayer {
 	} mode;
 
 	AVFilterContext *fvolume;
+	AVFilterGraph *fgraph;
 
 	volatile double volume;
 	double gain;
-- 
cgit v1.2.3