added file id to smil and implemented XML-RPC callback for playback start
This commit is contained in:
parent
2b4ce13d7e
commit
9cc4389052
|
@ -90,6 +90,17 @@ class AudioPlayerEventListener
|
|||
virtual void
|
||||
onStop(Ptr<const Glib::ustring>::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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<AnimationDescription*> 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) {
|
||||
|
|
|
@ -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<const UniqueId>::Ref id,
|
|||
|
||||
switch (plElement->getType()) {
|
||||
case PlaylistElement::AudioClipType :
|
||||
{
|
||||
url.reset(new std::string(
|
||||
contentElement[getPlaylistUrlParamName]));
|
||||
playable = plElement->getAudioClip();
|
||||
Ptr<Playable>::Ref audioClip = playable;
|
||||
subPlaylistId = audioClip->getId();
|
||||
}
|
||||
break;
|
||||
case PlaylistElement::PlaylistType :
|
||||
subPlaylistId = plElement->getPlaylist()->getId();
|
||||
|
@ -1612,6 +1621,9 @@ WebStorageClient :: acquirePlaylist(Ptr<const UniqueId>::Ref id,
|
|||
smilPlayableNode->set_attribute(
|
||||
smilPlayableUriAttrName,
|
||||
*url );
|
||||
smilPlayableNode->set_attribute(
|
||||
smilPlayableIdAttrName,
|
||||
*subPlaylistId->toDecimalString() );
|
||||
smilPlayableNode->set_attribute(
|
||||
smilRelativeOffsetAttrName,
|
||||
*TimeConversion::timeDurationToSmilString(
|
||||
|
|
|
@ -211,6 +211,14 @@ CuePlayer :: onStop(Ptr<const Glib::ustring>::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.
|
||||
|
|
|
@ -175,6 +175,15 @@ class CuePlayer : public GuiComponent,
|
|||
onStop(Ptr<const Glib::ustring>::Ref errorMessage
|
||||
= Ptr<const Glib::ustring>::Ref())
|
||||
throw ();
|
||||
|
||||
/**
|
||||
* Event handler for the "cue audio player has started" event.
|
||||
*
|
||||
* @param fileName
|
||||
*/
|
||||
virtual void
|
||||
onStart(gint64 id)
|
||||
throw ();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1298,7 +1298,7 @@ GLiveSupport :: playOutputAudio(Ptr<Playable>::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<Playable>::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<const Glib::ustring>::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<Playable>::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<Playable>::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<const Glib::ustring>::Ref oldDevice,
|
|||
cuePlayer->close();
|
||||
}
|
||||
cuePlayer->setAudioDevice(*newDevice);
|
||||
cuePlayer->open(*testAudioUrl);
|
||||
cuePlayer->open(*testAudioUrl, (gint64)0);
|
||||
cuePlayer->start();
|
||||
Ptr<time_duration>::Ref sleepT(new time_duration(microseconds(10)));
|
||||
while (cuePlayer->isPlaying()) {
|
||||
|
|
|
@ -1125,6 +1125,15 @@ class GLiveSupport : public LocalizedConfigurable,
|
|||
= Ptr<const Glib::ustring>::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".
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<Playable>::Ref playable) throw ()
|
|||
isActive = true;
|
||||
isPaused = false;
|
||||
resetRemainsTimeState();
|
||||
onUpdateTime();
|
||||
|
||||
|
||||
remainsTimeCounter++;
|
||||
if (remainsTimeCounter == 2*blinkingConstant) {
|
||||
remainsTimeCounter = 0;
|
||||
}
|
||||
|
||||
Ptr<time_duration>::Ref elapsed;
|
||||
try {
|
||||
elapsed = gLiveSupport->getOutputAudioPosition();
|
||||
} catch (std::logic_error &e) {
|
||||
elapsed.reset(new time_duration(microseconds(0)));
|
||||
}
|
||||
|
||||
Ptr<time_duration>::Ref totalLength
|
||||
= TimeConversion::roundToNearestSecond(
|
||||
playable->getPlaylength());
|
||||
Ptr<time_duration>::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<Playable>::Ref innerPlayable = playable;
|
||||
Ptr<time_duration>::Ref innerElapsed = elapsed;
|
||||
Ptr<time_duration>::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<PlaylistElement>::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<Glib::ustring>::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);
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue