[SCM] libgroove/upstream: player: support attaching a dummy device. closes #60
andrewrk-guest at users.alioth.debian.org
andrewrk-guest at users.alioth.debian.org
Tue May 13 03:19:11 UTC 2014
The following commit has been merged in the upstream branch:
commit f9ce1afe02541d1b0aaa8fb64cca2ec09e3ac70a
Author: Andrew Kelley <superjoe30 at gmail.com>
Date: Mon May 12 16:49:27 2014 -0700
player: support attaching a dummy device. closes #60
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4aeaa30..208af12 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,15 +2,16 @@
* GrooveBuffer struct contains the presentation time stamp
* move include statements to outside of extern C
- * player: specify device by index rather than name. closes #44
- * fingerprinter: encode/decode return 0 on success, < 0 on error
- * fingerprinter: info struct contains raw fingerprint instead of
- compressed string. closes #61
* ability to set true peak on playlist items. closes #50
+ * support per-sink gain adjustment. closes #41
* GroovePlaylist: `volume` renamed to `gain`
- `groove_playlist_set_gain` renamed to `groove_playlist_set_item_gain`
- `groove_playlist_set_volume` renamed to `groove_playlist_set_gain`
- * support per-sink gain adjustment. closes #41
+ * player: specify device by index rather than name. closes #44
+ * player: ability to attach a dummy player device. closes #60
+ * fingerprinter: encode/decode return 0 on success, < 0 on error
+ * fingerprinter: info struct contains raw fingerprint instead of
+ compressed string. closes #61
### Version 3.1.1 (2014-04-21)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 26fc2db..eeb5150 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -236,7 +236,7 @@ configure_file (
"${PROJECT_BINARY_DIR}/config.h"
)
-set(LIB_CFLAGS "${C99_C_FLAGS} -pedantic -Werror -Wall -Werror=strict-prototypes -Werror=old-style-definition -Werror=missing-prototypes -D_REENTRANT")
+set(LIB_CFLAGS "${C99_C_FLAGS} -pedantic -Werror -Wall -Werror=strict-prototypes -Werror=old-style-definition -Werror=missing-prototypes -D_REENTRANT -D_POSIX_C_SOURCE=200809L")
set(EXAMPLE_CFLAGS "${C99_C_FLAGS} -pedantic -Werror -Wall -g")
set(EXAMPLE_INCLUDES "${PROJECT_SOURCE_DIR}")
diff --git a/example/playlist.c b/example/playlist.c
index e538f70..9184766 100644
--- a/example/playlist.c
+++ b/example/playlist.c
@@ -31,7 +31,9 @@ int main(int argc, char * argv[]) {
char *arg = argv[i];
if (arg[0] == '-' && arg[1] == '-') {
arg += 2;
- if (i + 1 >= argc) {
+ if (strcmp(arg, "dummy") == 0) {
+ player->device_index = GROOVE_PLAYER_DUMMY_DEVICE;
+ } else if (i + 1 >= argc) {
return usage(exe);
} else if (strcmp(arg, "volume") == 0) {
double volume = atof(argv[++i]);
diff --git a/groove/groove.h b/groove/groove.h
index 835deb8..5a26631 100644
--- a/groove/groove.h
+++ b/groove/groove.h
@@ -326,6 +326,10 @@ struct GrooveSink {
* all your references to the GroovePlaylistItem.
*/
void (*purge)(struct GrooveSink *, struct GroovePlaylistItem *);
+ /* called when the playlist is paused */
+ void (*pause)(struct GrooveSink *);
+ /* called when the playlist is played */
+ void (*play)(struct GrooveSink *);
/* read-only. set when you call groove_sink_attach. cleared when you call
* groove_sink_detach
diff --git a/groove/playlist.c b/groove/playlist.c
index 22bc7b7..0319323 100644
--- a/groove/playlist.c
+++ b/groove/playlist.c
@@ -808,6 +808,20 @@ static int add_sink_to_map(struct GroovePlaylist *playlist, struct GrooveSink *s
return 0;
}
+static int groove_sink_play(struct GrooveSink *sink) {
+ if (sink->play)
+ sink->play(sink);
+
+ return 0;
+}
+
+static int groove_sink_pause(struct GrooveSink *sink) {
+ if (sink->pause)
+ sink->pause(sink);
+
+ return 0;
+}
+
int groove_sink_detach(struct GrooveSink *sink) {
struct GroovePlaylist *playlist = sink->playlist;
@@ -1014,13 +1028,19 @@ void groove_playlist_destroy(struct GroovePlaylist *playlist) {
void groove_playlist_play(struct GroovePlaylist *playlist) {
struct GroovePlaylistPrivate *p = (struct GroovePlaylistPrivate *) playlist;
// no mutex needed for this boolean flag
+ if (p->paused == 0)
+ return;
p->paused = 0;
+ every_sink(playlist, groove_sink_play, 0);
}
void groove_playlist_pause(struct GroovePlaylist *playlist) {
struct GroovePlaylistPrivate *p = (struct GroovePlaylistPrivate *) playlist;
// no mutex needed for this boolean flag
+ if (p->paused == 1)
+ return;
p->paused = 1;
+ every_sink(playlist, groove_sink_pause, 0);
}
void groove_playlist_seek(struct GroovePlaylist *playlist, struct GroovePlaylistItem *item, double seconds) {
diff --git a/grooveplayer/player.c b/grooveplayer/player.c
index d1f4f5b..22f2632 100644
--- a/grooveplayer/player.c
+++ b/grooveplayer/player.c
@@ -13,6 +13,7 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_audio.h>
#include <pthread.h>
+#include <time.h>
struct GroovePlayerPrivate {
struct GroovePlayer externals;
@@ -33,6 +34,14 @@ struct GroovePlayerPrivate {
struct GrooveSink *sink;
struct GrooveQueue *eventq;
+
+ // for dummy player
+ pthread_t thread_id;
+ int abort_request;
+ double start_nanos;
+ pthread_cond_t pause_cond;
+ int pause_cond_inited;
+ int paused;
};
static Uint16 groove_fmt_to_sdl_fmt(enum GrooveSampleFormat fmt) {
@@ -78,6 +87,80 @@ static void emit_event(struct GrooveQueue *queue, enum GroovePlayerEventType typ
av_log(NULL, AV_LOG_ERROR, "unable to put event on queue: out of memory\n");
}
+static double now_nanos(void) {
+ struct timespec tms;
+ clock_gettime(CLOCK_MONOTONIC, &tms);
+ uint64_t nanos = tms.tv_sec * 1000000000 + tms.tv_nsec;
+ return nanos;
+}
+
+// this thread is started if the user selects a dummy device instead of a
+// real device.
+static void *dummy_thread(void *arg) {
+ struct GroovePlayer *player = arg;
+ struct GroovePlayerPrivate *p = (struct GroovePlayerPrivate *) player;
+
+ while (!p->abort_request) {
+ pthread_mutex_lock(&p->play_head_mutex);
+ if (p->paused) {
+ pthread_cond_wait(&p->pause_cond, &p->play_head_mutex);
+ pthread_mutex_unlock(&p->play_head_mutex);
+ continue;
+ }
+
+ double now = now_nanos();
+ int more = 1;
+ while (more) {
+ more = 0;
+ if (!p->audio_buf || p->audio_buf_index >= p->audio_buf->frame_count) {
+ groove_buffer_unref(p->audio_buf);
+ p->audio_buf_index = 0;
+ p->audio_buf_size = 0;
+ int ret = groove_sink_buffer_get(p->sink, &p->audio_buf, 0);
+ if (ret == GROOVE_BUFFER_END) {
+ emit_event(p->eventq, GROOVE_EVENT_NOWPLAYING);
+ p->play_head = NULL;
+ p->play_pos = -1.0;
+ } else if (ret == GROOVE_BUFFER_YES) {
+ if (p->play_head != p->audio_buf->item)
+ emit_event(p->eventq, GROOVE_EVENT_NOWPLAYING);
+
+ p->play_head = p->audio_buf->item;
+ p->play_pos = p->audio_buf->pos;
+ p->audio_buf_size = p->audio_buf->size;
+ } else {
+ // errors are treated the same as no buffer ready
+ emit_event(p->eventq, GROOVE_EVENT_BUFFERUNDERRUN);
+ p->start_nanos = now;
+ pthread_mutex_unlock(&p->play_head_mutex);
+ break;
+ }
+ }
+ if (p->audio_buf) {
+ double nanos_to_kill = now - p->start_nanos;
+ double dbl_sample_rate = p->audio_buf->format.sample_rate;
+ double nanos_per_frame = 1000000000.0 / dbl_sample_rate;
+ int frames_to_kill = nanos_to_kill / nanos_per_frame;
+ if (frames_to_kill > p->audio_buf->frame_count) {
+ more = 1;
+ frames_to_kill = p->audio_buf->frame_count;
+ }
+ double nanos_killed = frames_to_kill * nanos_per_frame;
+ p->start_nanos += nanos_killed;
+ p->audio_buf_index += frames_to_kill;
+ p->play_pos += frames_to_kill / dbl_sample_rate;
+ }
+ }
+
+ // sleep for a little while
+ struct timespec tms;
+ clock_gettime(CLOCK_REALTIME, &tms);
+ tms.tv_nsec += 100000000;
+ pthread_cond_timedwait(&p->pause_cond, &p->play_head_mutex, &tms);
+ pthread_mutex_unlock(&p->play_head_mutex);
+ }
+ return NULL;
+}
static void sdl_audio_callback(void *opaque, Uint8 *stream, int len) {
struct GroovePlayerPrivate *p = opaque;
@@ -144,12 +227,44 @@ static void sink_purge(struct GrooveSink *sink, struct GroovePlaylistItem *item)
p->audio_buf = NULL;
p->audio_buf_index = 0;
p->audio_buf_size = 0;
+ p->start_nanos = now_nanos();
emit_event(p->eventq, GROOVE_EVENT_NOWPLAYING);
}
pthread_mutex_unlock(&p->play_head_mutex);
}
+static void sink_pause(struct GrooveSink *sink) {
+ struct GroovePlayer *player = sink->userdata;
+
+ // only the dummy device needs to handle pausing
+ if (player->device_index != GROOVE_PLAYER_DUMMY_DEVICE)
+ return;
+
+ struct GroovePlayerPrivate *p = (struct GroovePlayerPrivate *) player;
+
+ // mark the position in time that we paused at.
+ // no mutex needed for simple boolean flag
+ p->paused = 1;
+}
+
+static void sink_play(struct GrooveSink *sink) {
+ struct GroovePlayer *player = sink->userdata;
+
+ // only the dummy device needs to handle playing
+ if (player->device_index != GROOVE_PLAYER_DUMMY_DEVICE)
+ return;
+
+ struct GroovePlayerPrivate *p = (struct GroovePlayerPrivate *) player;
+
+ // mark the position in time that we started playing at.
+ pthread_mutex_lock(&p->play_head_mutex);
+ p->start_nanos = now_nanos();
+ p->paused = 0;
+ pthread_cond_signal(&p->pause_cond);
+ pthread_mutex_unlock(&p->play_head_mutex);
+}
+
static void sink_flush(struct GrooveSink *sink) {
struct GroovePlayerPrivate *p = sink->userdata;
@@ -159,6 +274,7 @@ static void sink_flush(struct GrooveSink *sink) {
p->audio_buf = NULL;
p->audio_buf_index = 0;
p->audio_buf_size = 0;
+ p->start_nanos = now_nanos();
pthread_mutex_unlock(&p->play_head_mutex);
}
@@ -190,6 +306,8 @@ struct GroovePlayer *groove_player_create(void) {
p->sink->userdata = player;
p->sink->purge = sink_purge;
p->sink->flush = sink_flush;
+ p->sink->pause = sink_pause;
+ p->sink->play = sink_play;
if (pthread_mutex_init(&p->play_head_mutex, NULL) != 0) {
groove_player_destroy(player);
@@ -198,6 +316,13 @@ struct GroovePlayer *groove_player_create(void) {
}
p->play_head_mutex_inited = 1;
+ if (pthread_cond_init(&p->pause_cond, NULL) != 0) {
+ groove_player_destroy(player);
+ av_log(NULL, AV_LOG_ERROR, "unable to create mutex condition\n");
+ return NULL;
+ }
+ p->pause_cond_inited = 1;
+
p->eventq = groove_queue_create();
if (!p->eventq) {
groove_player_destroy(player);
@@ -213,6 +338,7 @@ struct GroovePlayer *groove_player_create(void) {
player->device_buffer_size = 1024;
player->sink_buffer_size = 8192;
player->gain = p->sink->gain;
+ player->device_index = -1; // default device
return player;
}
@@ -228,6 +354,9 @@ void groove_player_destroy(struct GroovePlayer *player) {
if (p->play_head_mutex_inited)
pthread_mutex_destroy(&p->play_head_mutex);
+ if (p->pause_cond_inited)
+ pthread_cond_destroy(&p->pause_cond);
+
if (p->eventq)
groove_queue_destroy(p->eventq);
@@ -239,36 +368,47 @@ void groove_player_destroy(struct GroovePlayer *player) {
int groove_player_attach(struct GroovePlayer *player, struct GroovePlaylist *playlist) {
struct GroovePlayerPrivate *p = (struct GroovePlayerPrivate *) player;
- SDL_AudioSpec wanted_spec, spec;
- wanted_spec.format = groove_fmt_to_sdl_fmt(player->target_audio_format.sample_fmt);
- wanted_spec.freq = player->target_audio_format.sample_rate;
- wanted_spec.channels = groove_channel_layout_count(player->target_audio_format.channel_layout);
- wanted_spec.samples = player->device_buffer_size;
- wanted_spec.callback = sdl_audio_callback;
- wanted_spec.userdata = player;
-
- const char* device_name = SDL_GetAudioDeviceName(player->device_index, 0);
- p->device_id = SDL_OpenAudioDevice(device_name, 0, &wanted_spec,
- &spec, SDL_AUDIO_ALLOW_ANY_CHANGE);
- if (p->device_id == 0) {
- av_log(NULL, AV_LOG_ERROR, "unable to open audio device: %s\n", SDL_GetError());
- return -1;
- }
+ p->sink->gain = player->gain;
+ p->sink->buffer_size = player->sink_buffer_size;
- // save the actual spec back into the struct
- player->actual_audio_format.sample_rate = spec.freq;
- player->actual_audio_format.channel_layout = groove_channel_layout_default(spec.channels);
- player->actual_audio_format.sample_fmt = sdl_fmt_to_groove_fmt(spec.format);
+ if (player->device_index == GROOVE_PLAYER_DUMMY_DEVICE) {
+ // dummy device
+ player->actual_audio_format = player->target_audio_format;
+ p->sink->audio_format = player->actual_audio_format;
+ p->sink->disable_resample = 1;
+ } else {
+ SDL_AudioSpec wanted_spec, spec;
+ wanted_spec.format = groove_fmt_to_sdl_fmt(player->target_audio_format.sample_fmt);
+ wanted_spec.freq = player->target_audio_format.sample_rate;
+ wanted_spec.channels = groove_channel_layout_count(player->target_audio_format.channel_layout);
+ wanted_spec.samples = player->device_buffer_size;
+ wanted_spec.callback = sdl_audio_callback;
+ wanted_spec.userdata = player;
+
+ const char* device_name = NULL;
+
+ if (player->device_index >= 0)
+ device_name = SDL_GetAudioDeviceName(player->device_index, 0);
+ p->device_id = SDL_OpenAudioDevice(device_name, 0, &wanted_spec,
+ &spec, SDL_AUDIO_ALLOW_ANY_CHANGE);
+ if (p->device_id == 0) {
+ av_log(NULL, AV_LOG_ERROR, "unable to open audio device: %s\n", SDL_GetError());
+ return -1;
+ }
- // based on spec that we got, attach a sink with those properties
- p->sink->buffer_size = player->sink_buffer_size;
- p->sink->audio_format = player->actual_audio_format;
- p->sink->gain = player->gain;
+ // save the actual spec back into the struct
+ player->actual_audio_format.sample_rate = spec.freq;
+ player->actual_audio_format.channel_layout = groove_channel_layout_default(spec.channels);
+ player->actual_audio_format.sample_fmt = sdl_fmt_to_groove_fmt(spec.format);
- if (p->sink->audio_format.sample_fmt == GROOVE_SAMPLE_FMT_NONE) {
- groove_player_detach(player);
- av_log(NULL, AV_LOG_ERROR, "unsupported audio device sample format\n");
- return -1;
+ // based on spec that we got, attach a sink with those properties
+ p->sink->audio_format = player->actual_audio_format;
+
+ if (p->sink->audio_format.sample_fmt == GROOVE_SAMPLE_FMT_NONE) {
+ groove_player_detach(player);
+ av_log(NULL, AV_LOG_ERROR, "unsupported audio device sample format\n");
+ return -1;
+ }
}
int err = groove_sink_attach(p->sink, playlist);
@@ -282,13 +422,28 @@ int groove_player_attach(struct GroovePlayer *player, struct GroovePlaylist *pla
groove_queue_reset(p->eventq);
- SDL_PauseAudioDevice(p->device_id, 0);
+ if (player->device_index == GROOVE_PLAYER_DUMMY_DEVICE) {
+ if (groove_playlist_playing(playlist))
+ sink_play(p->sink);
+ else
+ sink_pause(p->sink);
+
+ // set up thread to keep track of time
+ if (pthread_create(&p->thread_id, NULL, dummy_thread, player) != 0) {
+ groove_player_detach(player);
+ av_log(NULL, AV_LOG_ERROR, "unable to create dummy player thread\n");
+ return -1;
+ }
+ } else {
+ SDL_PauseAudioDevice(p->device_id, 0);
+ }
return 0;
}
int groove_player_detach(struct GroovePlayer *player) {
struct GroovePlayerPrivate *p = (struct GroovePlayerPrivate *) player;
+ p->abort_request = 1;
if (p->eventq) {
groove_queue_flush(p->eventq);
groove_queue_abort(p->eventq);
@@ -300,6 +455,9 @@ int groove_player_detach(struct GroovePlayer *player) {
SDL_CloseAudioDevice(p->device_id);
p->device_id = 0;
}
+ pthread_cond_signal(&p->pause_cond);
+ pthread_join(p->thread_id, NULL);
+
player->playlist = NULL;
groove_buffer_unref(p->audio_buf);
diff --git a/grooveplayer/player.h b/grooveplayer/player.h
index fda3057..f01130c 100644
--- a/grooveplayer/player.h
+++ b/grooveplayer/player.h
@@ -29,10 +29,12 @@ union GroovePlayerEvent {
enum GroovePlayerEventType type;
};
+#define GROOVE_PLAYER_DEFAULT_DEVICE (-1)
+#define GROOVE_PLAYER_DUMMY_DEVICE (-2)
+
struct GroovePlayer {
/* set this to the device you want to open
- * -1 means default device
- * -2 means dummy device
+ * could also be GROOVE_PLAYER_DEFAULT_DEVICE or GROOVE_PLAYER_DUMMY_DEVICE
*/
int device_index;
--
libgroove packaging
More information about the pkg-multimedia-commits
mailing list