From 1cacbf51e7c90e6bac2b51440cc059be7fd0bed7 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Sat, 31 May 2014 20:52:40 -0400 Subject: [PATCH] driver: Move Gst code into the GstDriver This would have to happen eventually. Might as well do it now! I also updated the TestDriver test to match the changes I had to make. Signed-off-by: Anna Schumaker --- DESIGN | 16 ++++-- include/audio.h | 4 -- include/driver.h | 36 +++++++++---- lib/audio.cpp | 127 ++++++++++++---------------------------------- lib/driver.cpp | 128 ++++++++++++++++++++++++++++++++++++++++++----- tests/driver.cpp | 18 +++++-- 6 files changed, 200 insertions(+), 129 deletions(-) diff --git a/DESIGN b/DESIGN index 58da9a44..7f058ff3 100644 --- a/DESIGN +++ b/DESIGN @@ -1313,11 +1313,12 @@ Audio Driver: public: Driver(); ~Driver(); - virtual void init(int *, char ***, void (*)()) = 0; + virtual void init(int *, char ***, void (*)(), void (*)()) = 0; virtual void load(const std::string &) = 0; virtual void play() = 0; virtual void pause() = 0; + virtual void is_playing() = 0; virtual void seek_to(long) = 0; virtual long position() = 0; @@ -1333,18 +1334,25 @@ Audio Driver: In the GSTDriver case, call gst_deinit() to avoid memory leak false positives. - void Driver :: init(int argc, char **argv, void (*eos_cb)()); + void Driver :: init(int argc, char **argv, void (*eos_cb)(), + void (*error_cb)()); The GSTDriver will use this function to set up the playbin2. When an end-of-stream message is received, call eos_cb(). + If there is an error, call error_cb(); void Driver :: load(const std::string &file); Load file for playback, but do not begin playback yet. void Driver :: play(); - Start playback. + Start playback. Return true if the state change operation + succeeds, false otherwise. void Driver :: pause(); - Pause playback. + Pause playback. Return true if the state change operation + succeeds, false otherwise. + + bool Driver :: is_playing(); + Return true if the player is currently playing, false otherwise. void Driver :: seek_to(long pos); Change playback position in the current track in nanoseconds. diff --git a/include/audio.h b/include/audio.h index 3d188aa0..ce5ccd68 100644 --- a/include/audio.h +++ b/include/audio.h @@ -7,10 +7,6 @@ #include #include -extern "C" { - #include -} - #include namespace audio diff --git a/include/driver.h b/include/driver.h index 018c9105..ef0c8454 100644 --- a/include/driver.h +++ b/include/driver.h @@ -7,15 +7,17 @@ class Driver { protected: void (*on_eos)(); + void (*on_error)(); public: Driver(); ~Driver(); - virtual void init(int *, char ***, void (*)()) = 0; + virtual void init(int *, char ***, void (*)(), void (*)()) = 0; virtual void load(const std::string &) = 0; - virtual void play() = 0; - virtual void pause() = 0; + virtual bool play() = 0; + virtual bool pause() = 0; + virtual bool is_playing() = 0; virtual void seek_to(long) = 0; virtual long position() = 0; @@ -34,38 +36,50 @@ public: TestDriver(); ~TestDriver(); - void init(int *, char ***, void (*)()); + void init(int *, char ***, void (*)(), void (*)()); void load(const std::string &); - void play(); - void pause(); + bool play(); + bool pause(); + bool is_playing(); void seek_to(long); long position(); long duration(); void eos(); + void error(); }; -#else /* CONFIG_TEST */ +#else /* !CONFIG_TEST */ + +#include + class GSTDriver : public Driver { +private: + GstElement *player; + std::string cur_file; + bool change_state(GstState state); + public: GSTDriver(); ~GSTDriver(); - void init(int *, char ***, void (*)()); + void init(int *, char ***, void (*)(), void (*)()); void load(const std::string &); - void play(); - void pause(); + bool play(); + bool pause(); + bool is_playing(); void seek_to(long); long position(); long duration(); + + void on_message(GstMessage *); }; #endif /* CONFIG_TEST */ - namespace driver { diff --git a/lib/audio.cpp b/lib/audio.cpp index 1fa4478b..12b708e0 100644 --- a/lib/audio.cpp +++ b/lib/audio.cpp @@ -4,13 +4,12 @@ #include #include #include +#include #include #include #include -static GstElement *ocarina_player; - static bool player_playing = false; static bool track_loaded = false; static unsigned int cur_trackid = 0; @@ -22,19 +21,6 @@ static bool o_should_pause = false; static File f_cur_track("cur_track", 0); -static void parse_error(GstMessage *error) -{ - GError *err; - gchar *debug; - Track *track = tagdb :: lookup(cur_trackid); - - gst_message_parse_error(error, &err, &debug); - g_print("Error playing file: %s\n", track->path().c_str()); - g_print("Error: %s\n", err->message); - g_error_free(err); - g_free(debug); -} - static void handle_pause_count() { if (o_pause_enabled == false) @@ -48,45 +34,24 @@ static void handle_pause_count() get_callbacks()->on_pause_count_changed(o_pause_enabled, o_pause_count); } -static gboolean on_message(GstBus *bus, GstMessage *message, gpointer data) +static void on_error() +{ + audio :: next(); + audio :: play(); +} + +static void on_eos() { Track *track; - switch (GST_MESSAGE_TYPE(message)) { - case GST_MESSAGE_ERROR: - parse_error(message); - audio :: next(); - audio :: play(); - break; - case GST_MESSAGE_EOS: - handle_pause_count(); - track = tagdb :: lookup(cur_trackid); - if (track) { - track->played(); - library :: get_queue()->updated(track); - } - audio :: next(); - audio :: seek_to(0); - default: - break; - } - return TRUE; -} - -static bool change_state(GstState state) -{ - GstStateChangeReturn ret; - - ret = gst_element_set_state(GST_ELEMENT(ocarina_player), state); - switch (ret) { - case GST_STATE_CHANGE_SUCCESS: - case GST_STATE_CHANGE_ASYNC: - player_playing = (state == GST_STATE_PLAYING); - return true; - default: - player_playing = false; - return false; + handle_pause_count(); + track = tagdb :: lookup(cur_trackid); + if (track) { + track->played(); + library :: get_queue()->updated(track); } + audio :: next(); + audio :: seek_to(0); } static void save_state() @@ -98,37 +63,25 @@ static void save_state() static bool load_song(Track *track) { - GstState state; - gchar *uri; - std::string filepath = track->path(); + bool start_playback; - if (o_should_pause == true) { - state = GST_STATE_PAUSED; - o_should_pause = false; - } else { - gst_element_get_state(GST_ELEMENT(ocarina_player), &state, - NULL, GST_CLOCK_TIME_NONE); - } - - change_state(GST_STATE_NULL); - uri = gst_filename_to_uri(filepath.c_str(), NULL); - g_object_set(G_OBJECT(ocarina_player), "uri", uri, NULL); - g_free(uri); + if (o_should_pause) + start_playback = false; + else + start_playback = driver :: get_driver()->is_playing(); + driver :: get_driver()->load(track->path()); get_callbacks()->on_track_loaded(track); - return change_state(state); + + if (start_playback) + return driver :: get_driver()->play(); + else + return driver :: get_driver()->pause(); } void audio :: init(int *argc, char ***argv) { - GstBus *bus; - - gst_init(argc, argv); - - ocarina_player = gst_element_factory_make("playbin", "ocarina_player"); - bus = gst_pipeline_get_bus(GST_PIPELINE(ocarina_player)); - - gst_bus_add_watch(bus, on_message, NULL); + driver :: get_driver()->init(argc, argv, on_eos, on_error); } void audio :: load_state() @@ -144,15 +97,13 @@ void audio :: load_state() void audio :: quit() { - change_state(GST_STATE_NULL); - gst_deinit(); } void audio :: play() { if (track_loaded == false) return; - if (change_state(GST_STATE_PLAYING)) + if (driver :: get_driver()->play()) get_callbacks()->on_play(); } @@ -160,7 +111,7 @@ void audio :: pause() { if (track_loaded == false) return; - if (change_state(GST_STATE_PAUSED)) + if (driver :: get_driver()->pause()) get_callbacks()->on_pause(); } @@ -180,15 +131,9 @@ void audio :: stop() void audio :: seek_to(long pos) { - bool ret; if (track_loaded == false) return; - ret = gst_element_seek_simple(GST_ELEMENT(ocarina_player), - GST_FORMAT_TIME, - GST_SEEK_FLAG_FLUSH, - pos); - if (!ret) - throw -E_AUDIO; + driver :: get_driver()->seek_to(pos); } void audio :: next() @@ -245,13 +190,9 @@ unsigned int audio :: current_trackid() long audio :: position() { - long position; - if (track_loaded == false) return 0; - if (!gst_element_query_position(GST_ELEMENT(ocarina_player), GST_FORMAT_TIME, &position)) - return 0; - return position; + return driver :: get_driver()->position(); } std::string audio :: position_str() @@ -270,13 +211,9 @@ std::string audio :: position_str() long audio :: duration() { - long duration; - if (track_loaded == false) return 0; - if (!gst_element_query_duration(ocarina_player, GST_FORMAT_TIME, &duration)) - return 0; - return duration; + return driver :: get_driver()->duration(); } void audio :: pause_after(bool enabled, unsigned int n) diff --git a/lib/driver.cpp b/lib/driver.cpp index 28cc71fe..03ffd393 100644 --- a/lib/driver.cpp +++ b/lib/driver.cpp @@ -13,30 +13,134 @@ Driver :: ~Driver() {} TestDriver :: TestDriver() : playing(false), cur_pos(0), cur_duration(0) {} TestDriver :: ~TestDriver() {} -void TestDriver :: init(int *argc, char ***argv, void (*eos_cb)()) { on_eos = eos_cb; } +void TestDriver :: init(int *argc, char ***argv, void (*eos_cb)(), void (*error_cb)()) + { on_eos = eos_cb; on_error = error_cb; } void TestDriver :: load(const std::string &file) { cur_file = file; } -void TestDriver :: play() { playing = true; } -void TestDriver :: pause() { playing = false; } +bool TestDriver :: play() { playing = true; return true; } +bool TestDriver :: pause() { playing = false; return true; } +bool TestDriver :: is_playing() { return playing; } void TestDriver :: seek_to(long pos) { cur_pos = pos; } long TestDriver :: position() { return cur_pos; } long TestDriver :: duration() { return cur_duration; } -void TestDriver :: eos() { on_eos(); } +void TestDriver :: eos() { on_eos(); } +void TestDriver :: error() { on_error(); } #else /* CONFIG_TEST */ +static gboolean on_gst_message(GstBus *bus, GstMessage *message, gpointer data) +{ + GSTDriver *driver = (GSTDriver *)data; + driver->on_message(message); + return TRUE; +} + +static void parse_gst_error(GstMessage *error, const std::string filepath) +{ + GError *err; + gchar *debug; + + gst_message_parse_error(error, &err, &debug); + g_print("Error playing file: %s\n", filepath.c_str()); + g_print("Error: %s\n", err->message); + g_error_free(err); + g_free(debug); +} + GSTDriver :: GSTDriver() {} -GSTDriver :: ~GSTDriver() {} -void GSTDriver :: init(int argc, char **argv, void (*eos_cb)()) { } -void GSTDriver :: load(const std::string &file) { } -void GSTDriver :: play() { } -void GSTDriver :: pause() { } +GSTDriver :: ~GSTDriver() +{ + change_state(GST_STATE_NULL); + gst_deinit(); +} -void GSTDriver :: seek_to(long pos) { } -long GSTDriver :: position() { return 0; } -long GSTDriver :: duration() { return 0; } +bool GSTDriver :: change_state(GstState state) +{ + GstStateChangeReturn ret = gst_element_set_state(player, state); + switch (ret) { + case GST_STATE_CHANGE_SUCCESS: + case GST_STATE_CHANGE_ASYNC: + return true; + default: + return false; + } +} + +void GSTDriver :: init(int *argc, char ***argv, void (*eos_cb)(), void (*error_cb)()) +{ + GstBus *bus; + + on_eos = eos_cb; + on_error = error_cb; + + gst_init(argc, argv); + player = gst_element_factory_make("playbin", "ocarina_player"); + bus = gst_pipeline_get_bus(GST_PIPELINE(player)); + gst_bus_add_watch(bus, on_gst_message, this); +} + +void GSTDriver :: load(const std::string &filepath) +{ + gchar *uri; + + cur_file = filepath; + change_state(GST_STATE_NULL); + uri = gst_filename_to_uri(filepath.c_str(), NULL); + g_object_set(G_OBJECT(player), "uri", uri, NULL); + g_free(uri); +} + +bool GSTDriver :: play() +{ + return change_state(GST_STATE_PLAYING); +} + +bool GSTDriver :: pause() +{ + return change_state(GST_STATE_PAUSED); +} + +bool GSTDriver :: is_playing() +{ + GstState state; + gst_element_get_state(player, &state, NULL, GST_CLOCK_TIME_NONE); + return state == GST_STATE_PLAYING; +} + +void GSTDriver :: seek_to(long pos) +{ + gst_element_seek_simple(player, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, pos); +} + +long GSTDriver :: position() +{ + long position; + if (gst_element_query_position(player, GST_FORMAT_TIME, &position)) + return position; + return 0; +} + +long GSTDriver :: duration() +{ + long duration; + + if (gst_element_query_duration(player, GST_FORMAT_TIME, &duration)) + return duration; + return 0; +} + +void GSTDriver :: on_message(GstMessage *message) +{ + switch (GST_MESSAGE_TYPE(message)) { + case GST_MESSAGE_ERROR: + parse_gst_error(message, cur_file); + on_error(); + default: + break; + } +} #endif /* CONFIG_TEST */ diff --git a/tests/driver.cpp b/tests/driver.cpp index a492e291..8cc32170 100644 --- a/tests/driver.cpp +++ b/tests/driver.cpp @@ -6,12 +6,18 @@ static TestDriver *DRIVER_NULL = NULL; static unsigned int eos_count = 0; +static unsigned int error_count = 0; void on_eos() { eos_count++; } +void on_error() +{ + error_count++; +} + void test_driver() { TestDriver *driver = (TestDriver *)driver :: get_driver(); @@ -19,15 +25,18 @@ void test_driver() test_not_equal(driver, DRIVER_NULL); - driver->init(0, NULL, on_eos); + driver->init(0, NULL, on_eos, on_error); driver->load(file); test_equal(driver->cur_file, file); - driver->play(); + test_equal(driver->play(), true); test_equal(driver->playing, true); - driver->pause(); + test_equal(driver->is_playing(), true); + + test_equal(driver->pause(), true); test_equal(driver->playing, false); + test_equal(driver->is_playing(), false); driver->seek_to(4242); test_equal(driver->cur_pos, (long)4242); @@ -38,6 +47,9 @@ void test_driver() driver->eos(); test_equal(eos_count, (unsigned)1); + + driver->error(); + test_equal(error_count, (unsigned)1); } int main(int argc, char **argv)