From e9e15ac445100aee25fb3314a3ad89e7142c33fb Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Fri, 16 Oct 2015 18:49:57 +0300 Subject: [PATCH] Implement basic playback controls --- source/client.cpp | 31 ++++++++++++++++++-- source/client.h | 4 ++- source/xinestream.cpp | 67 +++++++++++++++++++++++++++++++++++++++++-- source/xinestream.h | 19 ++++++++++++ 4 files changed, 116 insertions(+), 5 deletions(-) diff --git a/source/client.cpp b/source/client.cpp index 1f21648..0e87e20 100644 --- a/source/client.cpp +++ b/source/client.cpp @@ -3,7 +3,6 @@ #include #include "client.h" #include "xinema.h" -#include "xinestream.h" using namespace std; using namespace Msp; @@ -55,6 +54,15 @@ void Client::end_of_stream() stale = true; } +XineStream &Client::get_stream() const +{ + XineStream *stream = xinema.get_stream(); + if(stream) + return *stream; + + throw runtime_error("No stream"); +} + void Client::process_command(const string &cmd) { string::size_type space = cmd.find(' '); @@ -62,12 +70,21 @@ void Client::process_command(const string &cmd) string args; if(space!=string::npos) args = cmd.substr(space+1); + if(keyword=="list_directory") list_directory(args); else if(keyword=="play_file") xinema.play_file(args); + else if(keyword=="play") + get_stream().play(); + else if(keyword=="seek") + get_stream().seek(lexical_cast(args)*Time::sec); + else if(keyword=="pause") + get_stream().pause(); + else if(keyword=="stop") + get_stream().stop(); else - send_reply("error Invalid command"); + throw runtime_error("Invalid command"); } void Client::send_reply(const string &reply) @@ -92,16 +109,26 @@ void Client::list_directory(const FS::Path &dn) void Client::stream_created(XineStream &stream) { + stream.signal_state_changed.connect(sigc::mem_fun(this, &Client::stream_state_changed)); stream.signal_title_changed.connect(sigc::mem_fun(this, &Client::stream_title_changed)); stream.signal_duration_changed.connect(sigc::mem_fun(this, &Client::stream_duration_changed)); stream.signal_position_changed.connect(sigc::mem_fun(this, &Client::stream_position_changed)); + + stream_state_changed(stream.get_state()); + string title = stream.get_title(); if(!title.empty()) send_reply("title "+title); + if(const Time::TimeDelta &dur = stream.get_duration()) stream_duration_changed(dur); } +void Client::stream_state_changed(XineStream::State state) +{ + send_reply(format("state %s", state)); +} + void Client::stream_title_changed(const string &title) { send_reply("title "+title); diff --git a/source/client.h b/source/client.h index 31bdbec..2300fc2 100644 --- a/source/client.h +++ b/source/client.h @@ -5,8 +5,8 @@ #include #include #include +#include "xinestream.h" -class XineStream; class Xinema; class Client: public sigc::trackable @@ -27,11 +27,13 @@ private: void data_available(); void end_of_stream(); + XineStream &get_stream() const; void process_command(const std::string &); void send_reply(const std::string &); void list_directory(const Msp::FS::Path &); void stream_created(XineStream &); + void stream_state_changed(XineStream::State); void stream_title_changed(const std::string &); void stream_duration_changed(const Msp::Time::TimeDelta &); void stream_position_changed(const Msp::Time::TimeDelta &); diff --git a/source/xinestream.cpp b/source/xinestream.cpp index 5614dd0..619ec58 100644 --- a/source/xinestream.cpp +++ b/source/xinestream.cpp @@ -6,7 +6,8 @@ using namespace std; using namespace Msp; XineStream::XineStream(XineEngine &e, const string &mrl): - engine(e) + engine(e), + state(STOPPED) { stream = xine_stream_new(engine.get_engine(), engine.get_audio_driver(), engine.get_video_driver()); xine_open(stream, mrl.c_str()); @@ -29,12 +30,46 @@ XineStream::~XineStream() void XineStream::play() { - xine_play(stream, 0, 0); + if(state==STOPPED) + xine_play(stream, 0, 0); + else if(state==PAUSED) + xine_set_param(stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL); + + set_state(PLAYING); +} + +void XineStream::seek(const Time::TimeDelta &time) +{ + xine_play(stream, 0, time/Time::msec); + if(state!=PLAYING) + { + xine_set_param(stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE); + set_state(PAUSED); + } +} + +void XineStream::pause() +{ + if(state==PLAYING) + { + xine_set_param(stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE); + set_state(PAUSED); + } } void XineStream::stop() { xine_stop(stream); + set_state(STOPPED); +} + +void XineStream::set_state(State s) +{ + if(s==state) + return; + + state = s; + signal_state_changed.emit(state); } void XineStream::tick() @@ -85,6 +120,9 @@ void XineStream::handle_event(const xine_event_t &event) { switch(event.type) { + case XINE_EVENT_UI_PLAYBACK_FINISHED: + set_state(STOPPED); + break; case XINE_EVENT_PROGRESS: { xine_progress_data_t *data = reinterpret_cast(event.data); @@ -93,3 +131,28 @@ void XineStream::handle_event(const xine_event_t &event) break; } } + + +void operator<<(LexicalConverter &conv, XineStream::State state) +{ + switch(state) + { + case XineStream::STOPPED: conv.result("STOPPED"); return; + case XineStream::PAUSED: conv.result("PAUSED"); return; + case XineStream::PLAYING: conv.result("PLAYING"); return; + default: conv.result(format("State(%d)", static_cast(state))); return; + } +} + +void operator>>(const LexicalConverter &conv, XineStream::State &state) +{ + const string &str = conv.get(); + if(str=="STOPPED") + state = XineStream::STOPPED; + else if(str=="PAUSED") + state = XineStream::PAUSED; + else if(str=="PLAYING") + state = XineStream::PLAYING; + else + throw lexical_error(format("Conversion of '%s' to XineStream::State", str)); +} diff --git a/source/xinestream.h b/source/xinestream.h index c1f27f7..def7f01 100644 --- a/source/xinestream.h +++ b/source/xinestream.h @@ -3,6 +3,7 @@ #include #include +#include #include class XineEngine; @@ -10,6 +11,14 @@ class XineEngine; class XineStream { public: + enum State + { + STOPPED, + PAUSED, + PLAYING + }; + + sigc::signal signal_state_changed; sigc::signal signal_title_changed; sigc::signal signal_duration_changed; sigc::signal signal_position_changed; @@ -18,6 +27,7 @@ private: XineEngine &engine; xine_stream_t *stream; xine_event_queue_t *queue; + State state; std::string title; Msp::Time::TimeDelta duration; Msp::Time::TimeDelta position; @@ -26,17 +36,26 @@ public: XineStream(XineEngine &, const std::string &); ~XineStream(); + State get_state() const { return state; } const Msp::Time::TimeDelta &get_duration() const { return duration; } const Msp::Time::TimeDelta &get_position() const { return position; } const std::string &get_title() const { return title; } void play(); + void seek(const Msp::Time::TimeDelta &); + void pause(); void stop(); +private: + void set_state(State); +public: void tick(); private: void check_info(); void handle_event(const xine_event_t &); }; +void operator<<(Msp::LexicalConverter &, XineStream::State); +void operator>>(const Msp::LexicalConverter &, XineStream::State &); + #endif -- 2.43.0