From 9cc4389052670fe4ba3b341f8f21d493759f95e3 Mon Sep 17 00:00:00 2001 From: nebojsa Date: Mon, 28 Sep 2009 00:27:05 +0000 Subject: [PATCH] added file id to smil and implemented XML-RPC callback for playback start --- .../AudioPlayerEventListener.h | 11 ++ .../PlaylistExecutor/AudioPlayerInterface.h | 5 +- .../playlistExecutor/src/GstreamerPlayer.cxx | 37 +++++- .../playlistExecutor/src/GstreamerPlayer.h | 16 +-- .../playlistExecutor/src/SmilHandler.h | 18 ++- .../storageClient/src/WebStorageClient.cxx | 12 ++ .../products/gLiveSupport/src/CuePlayer.cxx | 8 ++ .../src/products/gLiveSupport/src/CuePlayer.h | 9 ++ .../gLiveSupport/src/GLiveSupport.cxx | 21 +++- .../products/gLiveSupport/src/GLiveSupport.h | 9 ++ .../gLiveSupport/src/LiveModeWindow.cxx | 2 +- .../gLiveSupport/src/MasterPanelWindow.h | 11 ++ .../products/gLiveSupport/src/NowPlaying.cxx | 109 +++++++++++++++++- .../products/gLiveSupport/src/NowPlaying.h | 12 ++ .../products/scheduler/src/PlaylistEvent.cxx | 2 +- 15 files changed, 254 insertions(+), 28 deletions(-) diff --git a/campcaster/src/modules/playlistExecutor/include/LiveSupport/PlaylistExecutor/AudioPlayerEventListener.h b/campcaster/src/modules/playlistExecutor/include/LiveSupport/PlaylistExecutor/AudioPlayerEventListener.h index 2fe470ea7..966113f21 100644 --- a/campcaster/src/modules/playlistExecutor/include/LiveSupport/PlaylistExecutor/AudioPlayerEventListener.h +++ b/campcaster/src/modules/playlistExecutor/include/LiveSupport/PlaylistExecutor/AudioPlayerEventListener.h @@ -90,6 +90,17 @@ class AudioPlayerEventListener virtual void onStop(Ptr::Ref errorMessage) throw () = 0; + + /** + * Catch the event when playback started. + * Every time player plays a new file, it sends this event at the beginning. + * App should use this to synchronize playback + * + * @param id represents the file that just started + */ + virtual void + onStart(gint64 id) + throw () = 0; }; diff --git a/campcaster/src/modules/playlistExecutor/include/LiveSupport/PlaylistExecutor/AudioPlayerInterface.h b/campcaster/src/modules/playlistExecutor/include/LiveSupport/PlaylistExecutor/AudioPlayerInterface.h index 9ecac3c39..1a11873ae 100644 --- a/campcaster/src/modules/playlistExecutor/include/LiveSupport/PlaylistExecutor/AudioPlayerInterface.h +++ b/campcaster/src/modules/playlistExecutor/include/LiveSupport/PlaylistExecutor/AudioPlayerInterface.h @@ -153,9 +153,8 @@ class AudioPlayerInterface * @see #start */ virtual bool - open(const std::string fileUrl) throw (std::invalid_argument, - std::runtime_error) - = 0; + open(const std::string fileUrl, gint64 id) + throw (std::invalid_argument, std::runtime_error) = 0; /** * Tell if the audio player has been openned. diff --git a/campcaster/src/modules/playlistExecutor/src/GstreamerPlayer.cxx b/campcaster/src/modules/playlistExecutor/src/GstreamerPlayer.cxx index c9be5c8c8..a60c95b29 100644 --- a/campcaster/src/modules/playlistExecutor/src/GstreamerPlayer.cxx +++ b/campcaster/src/modules/playlistExecutor/src/GstreamerPlayer.cxx @@ -73,8 +73,8 @@ static gboolean my_bus_callback (GstBus *bus, GstMessage *message, gpointer data switch (GST_MESSAGE_TYPE (message)) { //we shall handle errors as non critical events as we should not stop playback in any case - case GST_MESSAGE_EOS: case GST_MESSAGE_ERROR: + case GST_MESSAGE_EOS: if(player->playNextSmil()){ break; } @@ -210,6 +210,28 @@ GstreamerPlayer :: fireOnStopEvent(gpointer self) throw () return false; } +/*------------------------------------------------------------------------------ + * Send the onStart event to all attached listeners. + *----------------------------------------------------------------------------*/ +gboolean +GstreamerPlayer :: fireOnStartEvent(gpointer self) throw () +{ + DEBUG_BLOCK + + + GstreamerPlayer* const player = (GstreamerPlayer*) self; + + ListenerVector::iterator it = player->m_listeners.begin(); + ListenerVector::iterator end = player->m_listeners.end(); + while (it != end) { + (*it)->onStart(player->m_Id); + ++it; + } + + // false == Don't call this idle function again + return false; +} + /*------------------------------------------------------------------------------ * Preload a file, to speed up the subsequent open() call. @@ -231,9 +253,8 @@ GstreamerPlayer :: preload(const std::string fileUrl) * Specify which file to play *----------------------------------------------------------------------------*/ bool -GstreamerPlayer :: open(const std::string fileUri) - throw (std::invalid_argument, - std::runtime_error) +GstreamerPlayer :: open(const std::string fileUri, gint64 id) + throw (std::invalid_argument, std::runtime_error) { DEBUG_BLOCK @@ -254,9 +275,13 @@ GstreamerPlayer :: open(const std::string fileUri) m_smilHandler = new SmilHandler(); m_smilHandler->openSmilFile(fileUri.c_str()); AudioDescription *audioDescription = m_smilHandler->getNext(); + m_Id = audioDescription->m_Id; m_open=m_playContext->openSource(audioDescription); + m_url = (const char*) audioDescription->m_src; }else{ m_open=m_playContext->openSource(fileUri.c_str()); + m_url = fileUri; + m_Id = id; } if(!m_open){ @@ -266,6 +291,7 @@ GstreamerPlayer :: open(const std::string fileUri) m_playContext->forceEOS(); return false; } + g_idle_add(GstreamerPlayer::fireOnStartEvent, this); return true; } @@ -297,6 +323,9 @@ GstreamerPlayer :: playNextSmil(void) throw ( m_playContext->forceEOS(); return true; } + m_Id = audioDescription->m_Id; + m_url = (const char*) audioDescription->m_src; + g_idle_add(GstreamerPlayer::fireOnStartEvent, this); m_smilOffset += m_currentPlayLength; m_playContext->playContext(); return true; diff --git a/campcaster/src/modules/playlistExecutor/src/GstreamerPlayer.h b/campcaster/src/modules/playlistExecutor/src/GstreamerPlayer.h index d0d607f90..fb5745fe4 100644 --- a/campcaster/src/modules/playlistExecutor/src/GstreamerPlayer.h +++ b/campcaster/src/modules/playlistExecutor/src/GstreamerPlayer.h @@ -122,14 +122,10 @@ class GstreamerPlayer : virtual public Configurable, * The audio device to play on. */ std::string m_audioDevice; - - /** - * The URL of the preloaded file. Empty if nothing is preloaded. - */ - std::string m_preloadUrl; gint64 m_smilOffset; gint64 m_currentPlayLength; + gint64 m_Id; public: @@ -164,6 +160,12 @@ public: static gboolean fireOnStopEvent(gpointer self) throw (); + /** + * Send the onStart event to all attached listeners. + */ + static gboolean + fireOnStartEvent(gpointer self) throw (); + public: /** @@ -286,8 +288,8 @@ public: * @see #start */ virtual bool - open(const std::string fileUrl) throw (std::invalid_argument, - std::runtime_error); + open(const std::string fileUrl, gint64 id) + throw (std::invalid_argument, std::runtime_error); /** * Tell if the object is currently opened (has a file source to diff --git a/campcaster/src/modules/playlistExecutor/src/SmilHandler.h b/campcaster/src/modules/playlistExecutor/src/SmilHandler.h index 28bb07507..d5616fcfd 100644 --- a/campcaster/src/modules/playlistExecutor/src/SmilHandler.h +++ b/campcaster/src/modules/playlistExecutor/src/SmilHandler.h @@ -84,11 +84,9 @@ public: m_begin(0), m_end(0) { -// std::cout << "AnimationDescription created!" << std::endl; } ~AnimationDescription() { -// std::cout << "AnimationDescription destroyed!" << std::endl; } }; @@ -101,19 +99,19 @@ public: gint64 m_begin; gint64 m_clipBegin; gint64 m_clipEnd; + gint64 m_Id; std::vector m_animations; AudioDescription(): m_src(NULL), m_begin(0), m_clipBegin(0), - m_clipEnd(0) + m_clipEnd(0), + m_Id(0) { -// std::cout << "AudioDescription created!" << std::endl; } ~AudioDescription() { -// std::cout << "AudioDescription destroyed!" << std::endl; } void release() @@ -287,6 +285,7 @@ private: gchar * begin = 0; gchar * clipBegin = 0; gchar * clipEnd = 0; + gchar * idStr = 0; /* handle the attributes */ for (attr = ((xmlElement*)audio)->attributes; attr; attr = (xmlAttribute*) attr->next) { @@ -296,6 +295,10 @@ private: if ((node = attr->children) && node->type == XML_TEXT_NODE) { src = (gchar*) node->content; } + } else if (!strcmp((const char*)attr->name, "id")) { + if ((node = attr->children) && node->type == XML_TEXT_NODE) { + idStr = (gchar*) node->content; + } } else if (!strcmp((const char*)attr->name, "begin")) { if ((node = attr->children) && node->type == XML_TEXT_NODE) { begin = (gchar*) node->content; @@ -328,6 +331,11 @@ private: audioDescription->m_begin = su_smil_clock_value_to_nanosec(begin); audioDescription->m_clipBegin = su_smil_clock_value_to_nanosec(clipBegin); audioDescription->m_clipEnd = su_smil_clock_value_to_nanosec(clipEnd); + if(idStr) + { + std::stringstream idReader(idStr); + idReader >> audioDescription->m_Id; + } // now handle the possible animate elements inside this audio element for (node = audio->children; node; node = node->next) { if (node->type == XML_ELEMENT_NODE) { diff --git a/campcaster/src/modules/storageClient/src/WebStorageClient.cxx b/campcaster/src/modules/storageClient/src/WebStorageClient.cxx index 4637b6b53..180cd08b0 100644 --- a/campcaster/src/modules/storageClient/src/WebStorageClient.cxx +++ b/campcaster/src/modules/storageClient/src/WebStorageClient.cxx @@ -146,6 +146,11 @@ const std::string smilPlayableNodeName = "audio"; *----------------------------------------------------------------------------*/ const std::string smilPlayableUriAttrName = "src"; +/*------------------------------------------------------------------------------ + * The name of the attribute containing the Id of the Playable element. + *----------------------------------------------------------------------------*/ +const std::string smilPlayableIdAttrName = "id"; + /*------------------------------------------------------------------------------ * The name of the attribute containing the relative offset of the element. *----------------------------------------------------------------------------*/ @@ -1591,9 +1596,13 @@ WebStorageClient :: acquirePlaylist(Ptr::Ref id, switch (plElement->getType()) { case PlaylistElement::AudioClipType : + { url.reset(new std::string( contentElement[getPlaylistUrlParamName])); playable = plElement->getAudioClip(); + Ptr::Ref audioClip = playable; + subPlaylistId = audioClip->getId(); + } break; case PlaylistElement::PlaylistType : subPlaylistId = plElement->getPlaylist()->getId(); @@ -1612,6 +1621,9 @@ WebStorageClient :: acquirePlaylist(Ptr::Ref id, smilPlayableNode->set_attribute( smilPlayableUriAttrName, *url ); + smilPlayableNode->set_attribute( + smilPlayableIdAttrName, + *subPlaylistId->toDecimalString() ); smilPlayableNode->set_attribute( smilRelativeOffsetAttrName, *TimeConversion::timeDurationToSmilString( diff --git a/campcaster/src/products/gLiveSupport/src/CuePlayer.cxx b/campcaster/src/products/gLiveSupport/src/CuePlayer.cxx index 329484ce9..b43c9df1f 100644 --- a/campcaster/src/products/gLiveSupport/src/CuePlayer.cxx +++ b/campcaster/src/products/gLiveSupport/src/CuePlayer.cxx @@ -211,6 +211,14 @@ CuePlayer :: onStop(Ptr::Ref errorMessage) throw () } } +/*------------------------------------------------------------------------------ + * Event handler for the "cue audio player has started" event. + *----------------------------------------------------------------------------*/ +void +CuePlayer :: onStart(gint64 id) throw () +{ +} + /*------------------------------------------------------------------------------ * Set the state of the widget. diff --git a/campcaster/src/products/gLiveSupport/src/CuePlayer.h b/campcaster/src/products/gLiveSupport/src/CuePlayer.h index e9ebb308a..5382ac7c2 100644 --- a/campcaster/src/products/gLiveSupport/src/CuePlayer.h +++ b/campcaster/src/products/gLiveSupport/src/CuePlayer.h @@ -175,6 +175,15 @@ class CuePlayer : public GuiComponent, onStop(Ptr::Ref errorMessage = Ptr::Ref()) throw (); + + /** + * Event handler for the "cue audio player has started" event. + * + * @param fileName + */ + virtual void + onStart(gint64 id) + throw (); }; diff --git a/campcaster/src/products/gLiveSupport/src/GLiveSupport.cxx b/campcaster/src/products/gLiveSupport/src/GLiveSupport.cxx index 4188ae0c7..7acee5425 100644 --- a/campcaster/src/products/gLiveSupport/src/GLiveSupport.cxx +++ b/campcaster/src/products/gLiveSupport/src/GLiveSupport.cxx @@ -1298,7 +1298,7 @@ GLiveSupport :: playOutputAudio(Ptr::Ref playable) switch (playable->getType()) { case Playable::AudioClipType: outputItemPlayingNow = acquireAudioClip(playable->getId()); - if(false == outputPlayer->open(*outputItemPlayingNow->getUri())) + if(false == outputPlayer->open(*outputItemPlayingNow->getUri(), (gint64)outputItemPlayingNow->getId()->getId())) { return false; } @@ -1310,7 +1310,7 @@ GLiveSupport :: playOutputAudio(Ptr::Ref playable) case Playable::PlaylistType: outputItemPlayingNow = acquirePlaylist(playable->getId()); - outputPlayer->open(*outputItemPlayingNow->getUri()); + outputPlayer->open(*outputItemPlayingNow->getUri(), (gint64)outputItemPlayingNow->getId()->getId()); outputPlayer->start(); std::cerr << "gLiveSupport: Live Mode playing playlist '" << *playable->getTitle() @@ -1414,6 +1414,17 @@ GLiveSupport :: onStop(Ptr::Ref errorMessage) } } +/*------------------------------------------------------------------------------ + * Event handler for the "output audio player has started" event. + *----------------------------------------------------------------------------*/ +void +LiveSupport :: GLiveSupport :: +GLiveSupport :: onStart(gint64 id) + throw () +{ + masterPanel->setCurrentInnerPlayable(id); +} + /*------------------------------------------------------------------------------ * Play a Playable object using the cue audio player. @@ -1432,7 +1443,7 @@ GLiveSupport :: playCueAudio(Ptr::Ref playable) switch (playable->getType()) { case Playable::AudioClipType: cueItemPlayingNow = acquireAudioClip(playable->getId()); - cuePlayer->open(*cueItemPlayingNow->getUri()); + cuePlayer->open(*cueItemPlayingNow->getUri(), (gint64)cueItemPlayingNow->getId()->getId()); cuePlayer->start(); std::cerr << "gLiveSupport: Cue playing audio clip '" << *playable->getTitle() @@ -1441,7 +1452,7 @@ GLiveSupport :: playCueAudio(Ptr::Ref playable) case Playable::PlaylistType: cueItemPlayingNow = acquirePlaylist(playable->getId()); - cuePlayer->open(*cueItemPlayingNow->getUri()); + cuePlayer->open(*cueItemPlayingNow->getUri(), (gint64)cueItemPlayingNow->getId()->getId()); cuePlayer->start(); std::cerr << "gLiveSupport: Cue playing playlist '" << *playable->getTitle() @@ -1758,7 +1769,7 @@ GLiveSupport :: playTestSoundOnCue(Ptr::Ref oldDevice, cuePlayer->close(); } cuePlayer->setAudioDevice(*newDevice); - cuePlayer->open(*testAudioUrl); + cuePlayer->open(*testAudioUrl, (gint64)0); cuePlayer->start(); Ptr::Ref sleepT(new time_duration(microseconds(10))); while (cuePlayer->isPlaying()) { diff --git a/campcaster/src/products/gLiveSupport/src/GLiveSupport.h b/campcaster/src/products/gLiveSupport/src/GLiveSupport.h index ab5972593..f5171cb74 100644 --- a/campcaster/src/products/gLiveSupport/src/GLiveSupport.h +++ b/campcaster/src/products/gLiveSupport/src/GLiveSupport.h @@ -1125,6 +1125,15 @@ class GLiveSupport : public LocalizedConfigurable, = Ptr::Ref()) throw (); + /** + * Event handler for the "output audio player has started" event. + * + * @param fileName + */ + virtual void + onStart(gint64 id) + throw (); + /** * Display the playable item on the master panel as "now playing". */ diff --git a/campcaster/src/products/gLiveSupport/src/LiveModeWindow.cxx b/campcaster/src/products/gLiveSupport/src/LiveModeWindow.cxx index 6d8a7effd..a36b67618 100644 --- a/campcaster/src/products/gLiveSupport/src/LiveModeWindow.cxx +++ b/campcaster/src/products/gLiveSupport/src/LiveModeWindow.cxx @@ -411,12 +411,12 @@ LiveModeWindow :: onOutputPlay(void) throw () if (playable) { try { + gLiveSupport->setNowPlaying(playable); if(false == gLiveSupport->playOutputAudio(playable)) { treeView->removeItem(itemPlayed); return; } - gLiveSupport->setNowPlaying(playable); treeView->removeItem(itemPlayed); diff --git a/campcaster/src/products/gLiveSupport/src/MasterPanelWindow.h b/campcaster/src/products/gLiveSupport/src/MasterPanelWindow.h index 74684fdfe..884b91fab 100644 --- a/campcaster/src/products/gLiveSupport/src/MasterPanelWindow.h +++ b/campcaster/src/products/gLiveSupport/src/MasterPanelWindow.h @@ -559,6 +559,17 @@ class MasterPanelWindow : public GuiWindow return nowPlayingWidget->getCurrentInnerPlayable(); } + /** + * Set the Playable currently shown in the "now playing" display. + * + * @return the currently playing item; 0 if nothing is playing. + */ + void + setCurrentInnerPlayable (gint64 id) throw () + { + nowPlayingWidget->setCurrentInnerPlayable(id); + } + /** * Upload a Playable object to the network hub. * And display it in the Transports tab of the Search Window. diff --git a/campcaster/src/products/gLiveSupport/src/NowPlaying.cxx b/campcaster/src/products/gLiveSupport/src/NowPlaying.cxx index 788c74f01..dbd08fae5 100644 --- a/campcaster/src/products/gLiveSupport/src/NowPlaying.cxx +++ b/campcaster/src/products/gLiveSupport/src/NowPlaying.cxx @@ -141,6 +141,20 @@ NowPlaying :: setStyle (Gtk::Label * label, label->set_attributes(attributeList); } +void +NowPlaying :: setCurrentInnerPlayable (gint64 id) throw () +{ + playableMutex.lock(); + if((gint64)currentInnerPlayable->getId()->getId() != id) + { + //we are not playing a correct file, must have had an error - adjust the playlist +std::cout << "NowPlaying :: setCurrentInnerPlayable ERROR DETECTED! called = " << id << ", current = " << (gint64)currentInnerPlayable->getId()->getId() << std::endl; + } + else{ +std::cout << "NowPlaying :: setCurrentInnerPlayable CORRECT!" << std::endl; + } + playableMutex.unlock(); +} /*------------------------------------------------------------------------------ * Set the title etc. of the playable shown in the widget. @@ -162,8 +176,99 @@ NowPlaying :: setPlayable (Ptr::Ref playable) throw () isActive = true; isPaused = false; resetRemainsTimeState(); - onUpdateTime(); - + + remainsTimeCounter++; + if (remainsTimeCounter == 2*blinkingConstant) { + remainsTimeCounter = 0; + } + + Ptr::Ref elapsed; + try { + elapsed = gLiveSupport->getOutputAudioPosition(); + } catch (std::logic_error &e) { + elapsed.reset(new time_duration(microseconds(0))); + } + + Ptr::Ref totalLength + = TimeConversion::roundToNearestSecond( + playable->getPlaylength()); + Ptr::Ref remains(new time_duration( + *totalLength - *elapsed)); + switch (remainsTimeState) { + case TIME_GREEN : + if (*remains <= seconds(20)) { + remainsTimeState = TIME_YELLOW; + } + break; + + case TIME_YELLOW : + if (*remains <= seconds(10)) { + remainsTimeState = TIME_RED; + } + break; + + case TIME_RED : + break; + } + setRemainsTimeColor(remainsTimeState); + + Ptr::Ref innerPlayable = playable; + Ptr::Ref innerElapsed = elapsed; + Ptr::Ref innerRemains = remains; + Glib::ustring playlistInfo; + bool isFirst = true; + + while (innerPlayable->getType() == Playable::PlaylistType) { + if (isFirst) { + isFirst = false; + } else { + playlistInfo += " >>> "; + } + playlistInfo += *innerPlayable->getTitle(); + playlistInfo += " ["; + playlistInfo += *TimeConversion::timeDurationToHhMmSsString(innerRemains); + playlistInfo += "/"; + playlistInfo += *TimeConversion::timeDurationToHhMmSsString(innerPlayable->getPlaylength()); + playlistInfo += "]"; + + Ptr::Ref element = innerPlayable->getPlaylist()->findAtOffset(elapsed); + if (!element) { + break; + } + innerPlayable = element->getPlayable(); + *innerElapsed -= *element->getRelativeOffset(); + *innerRemains = *TimeConversion::roundToNearestSecond( + innerPlayable->getPlaylength()) - *innerElapsed; + } + + playlistLabel->set_text(playlistInfo); + + titleLabel->set_text(*innerPlayable->getTitle()); + + Ptr::Ref + creator = innerPlayable->getMetadata("dc:creator"); + if (creator) { + creatorLabel->set_text(*creator); + } else { + creatorLabel->set_text(""); + } + + elapsedTimeLabel->set_text(*TimeConversion::timeDurationToHhMmSsString(innerElapsed )); + remainsTimeLabel->set_text(*TimeConversion::timeDurationToHhMmSsString(innerRemains )); + + long elapsedMilliSec = innerElapsed->total_milliseconds(); + long totalMilliSec = elapsedMilliSec + + innerRemains->total_milliseconds(); + double fraction = double(elapsedMilliSec) / double(totalMilliSec); + if (fraction < 0.0) { + fraction = 0.0; // can't happen afaik + } + if (fraction > 1.0) { + fraction = 1.0; // can and does happen! + } + progressBar->set_fraction(fraction); + + currentInnerPlayable = innerPlayable; } else { if (isActive && !isPaused) { playButton->set_label(playStockImageName); diff --git a/campcaster/src/products/gLiveSupport/src/NowPlaying.h b/campcaster/src/products/gLiveSupport/src/NowPlaying.h index 2cde0a281..60dddd6ae 100644 --- a/campcaster/src/products/gLiveSupport/src/NowPlaying.h +++ b/campcaster/src/products/gLiveSupport/src/NowPlaying.h @@ -290,6 +290,18 @@ class NowPlaying : public GuiComponent return currentInnerPlayable; } + /** + * Set the Playable object which is playing now. + * If a playlist is playing, does not return the playlist, but + * the audio clip inside the playlist (possibly several levels deep). + * + * This is used by GLiveSupport::substituteRdsData(). + * + * @return void + */ + void + setCurrentInnerPlayable (gint64 id) throw (); + /** * Change the user interface language of the widget. * diff --git a/campcaster/src/products/scheduler/src/PlaylistEvent.cxx b/campcaster/src/products/scheduler/src/PlaylistEvent.cxx index 7f1112cca..0b51489bc 100644 --- a/campcaster/src/products/scheduler/src/PlaylistEvent.cxx +++ b/campcaster/src/products/scheduler/src/PlaylistEvent.cxx @@ -158,7 +158,7 @@ PlaylistEvent :: start(void) throw () } try { - audioPlayer->open(*playlist->getUri()); + audioPlayer->open(*playlist->getUri(), (gint64)playlist->getId()->getId()); audioPlayer->start(); playLog->addPlayLogEntry(playlist->getId(), TimeConversion::now());