void PAPlayer::CloseAllStreams(bool fade/* = true */) { if (!fade) { CSingleLock lock(m_streamsLock); while (!m_streams.empty()) { StreamInfo* si = m_streams.front(); m_streams.pop_front(); if (si->m_stream) { CloseFileCB(*si); CServiceBroker::GetActiveAE().FreeStream(si->m_stream); si->m_stream = NULL; } si->m_decoder.Destroy(); delete si; } while (!m_finishing.empty()) { StreamInfo* si = m_finishing.front(); m_finishing.pop_front(); if (si->m_stream) { CloseFileCB(*si); CServiceBroker::GetActiveAE().FreeStream(si->m_stream); si->m_stream = nullptr; } si->m_decoder.Destroy(); delete si; } m_currentStream = nullptr; } else { SoftStop(false, true); CSingleLock lock(m_streamsLock); m_currentStream = NULL; } }
inline void PAPlayer::ProcessStreams(double &freeBufferTime) { CSingleLock sharedLock(m_streamsLock); if (m_isFinished && m_streams.empty() && m_finishing.empty()) { m_isPlaying = false; freeBufferTime = 1.0; return; } /* destroy any drained streams */ for (auto itt = m_finishing.begin(); itt != m_finishing.end();) { StreamInfo* si = *itt; if (si->m_stream->IsDrained()) { itt = m_finishing.erase(itt); CloseFileCB(*si); CServiceBroker::GetActiveAE().FreeStream(si->m_stream); delete si; CLog::Log(LOGDEBUG, "PAPlayer::ProcessStreams - Stream Freed"); } else ++itt; } sharedLock.Leave(); CSingleLock lock(m_streamsLock); for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt) { StreamInfo* si = *itt; if (!m_currentStream && !si->m_started) { m_currentStream = si; UpdateGUIData(si); //update for GUI } /* if the stream is finishing */ if ((si->m_playNextTriggered && si->m_stream && !si->m_stream->IsFading()) || !ProcessStream(si, freeBufferTime)) { if (!si->m_prepareTriggered) { if (si->m_waitOnDrain) { si->m_stream->Drain(true); si->m_waitOnDrain = false; } si->m_prepareTriggered = true; m_callback.OnQueueNextItem(); } /* remove the stream */ itt = m_streams.erase(itt); /* if its the current stream */ if (si == m_currentStream) { /* if it was the last stream */ if (itt == m_streams.end()) { /* if it didnt trigger the next queue item */ if (!si->m_prepareTriggered) { if (si->m_waitOnDrain) { si->m_stream->Drain(true); si->m_waitOnDrain = false; } m_callback.OnQueueNextItem(); si->m_prepareTriggered = true; } m_currentStream = NULL; } else { m_currentStream = *itt; UpdateGUIData(*itt); //update for GUI } } /* unregister the audio callback */ si->m_stream->UnRegisterAudioCallback(); si->m_decoder.Destroy(); si->m_stream->Drain(false); m_finishing.push_back(si); return; } if (!si->m_started) continue; /* is it time to prepare the next stream? */ if (si->m_prepareNextAtFrame > 0 && !si->m_prepareTriggered && si->m_framesSent >= si->m_prepareNextAtFrame) { si->m_prepareTriggered = true; m_callback.OnQueueNextItem(); } /* it is time to start playing the next stream? */ if (si->m_playNextAtFrame > 0 && !si->m_playNextTriggered && !m_continueStream && si->m_framesSent >= si->m_playNextAtFrame) { if (!si->m_prepareTriggered) { si->m_prepareTriggered = true; m_callback.OnQueueNextItem(); } if (!m_isFinished) { if (m_upcomingCrossfadeMS) { si->m_stream->FadeVolume(1.0f, 0.0f, m_upcomingCrossfadeMS); si->m_fadeOutTriggered = true; } m_currentStream = NULL; /* unregister the audio callback */ si->m_stream->UnRegisterAudioCallback(); } si->m_playNextTriggered = true; } } }
inline bool PAPlayer::ProcessStream(StreamInfo *si, double &freeBufferTime) { /* if playback needs to start on this stream, do it */ if (si == m_currentStream && !si->m_started) { si->m_started = true; si->m_stream->RegisterAudioCallback(m_audioCallback); if (!si->m_isSlaved) si->m_stream->Resume(); si->m_stream->FadeVolume(0.0f, 1.0f, m_upcomingCrossfadeMS); if (m_signalStarted) m_callback.OnPlayBackStarted(si->m_fileItem); m_signalStarted = true; m_callback.OnAVStarted(si->m_fileItem); } /* if we have not started yet and the stream has been primed */ unsigned int space = si->m_stream->GetSpace(); if (!si->m_started && !space) return true; /* see if it is time yet to FF/RW or a direct seek */ if (!si->m_playNextTriggered && ((m_playbackSpeed != 1 && si->m_framesSent >= si->m_seekNextAtFrame) || si->m_seekFrame > -1)) { int64_t time = (int64_t)0; /* if its a direct seek */ if (si->m_seekFrame > -1) { time = (int64_t)((float)si->m_seekFrame / (float)si->m_audioFormat.m_sampleRate * 1000.0f); si->m_framesSent = (int)(si->m_seekFrame - ((float)si->m_startOffset * (float)si->m_audioFormat.m_sampleRate) / 1000.0f); si->m_seekFrame = -1; m_playerGUIData.m_time = time; //update for GUI si->m_seekNextAtFrame = 0; CDataCacheCore::GetInstance().SetPlayTimes(0, time, 0, m_playerGUIData.m_totalTime); } /* if its FF/RW */ else { si->m_framesSent += si->m_audioFormat.m_sampleRate * (m_playbackSpeed - 1); si->m_seekNextAtFrame = si->m_framesSent + si->m_audioFormat.m_sampleRate / 2; time = (int64_t)(((float)si->m_framesSent / (float)si->m_audioFormat.m_sampleRate * 1000.0f) + (float)si->m_startOffset); } /* if we are seeking back before the start of the track start normal playback */ if (time < si->m_startOffset || si->m_framesSent < 0) { time = si->m_startOffset; si->m_framesSent = 0; si->m_seekNextAtFrame = 0; SetSpeed(1); } si->m_decoder.Seek(time); } int status = si->m_decoder.GetStatus(); if (status == STATUS_ENDED || status == STATUS_NO_FILE || si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR || ((si->m_endOffset) && (si->m_framesSent / si->m_audioFormat.m_sampleRate >= (si->m_endOffset - si->m_startOffset) / 1000))) { if (si == m_currentStream && si->m_nextFileItem) { CloseFileCB(*si); // update current stream with info of next track si->m_startOffset = si->m_nextFileItem->m_lStartOffset; if (si->m_nextFileItem->m_lEndOffset) si->m_endOffset = si->m_nextFileItem->m_lEndOffset; else si->m_endOffset = 0; si->m_framesSent = 0; si->m_fileItem = *si->m_nextFileItem; si->m_nextFileItem.reset(); int64_t streamTotalTime = si->m_decoder.TotalTime() - si->m_startOffset; if (si->m_endOffset) streamTotalTime = si->m_endOffset - si->m_startOffset; // calculate time when to prepare next stream si->m_prepareNextAtFrame = 0; if (streamTotalTime >= TIME_TO_CACHE_NEXT_FILE + m_defaultCrossfadeMS) si->m_prepareNextAtFrame = (int)((streamTotalTime - TIME_TO_CACHE_NEXT_FILE - m_defaultCrossfadeMS) * si->m_audioFormat.m_sampleRate / 1000.0f); si->m_prepareTriggered = false; si->m_playNextAtFrame = 0; si->m_playNextTriggered = false; si->m_seekNextAtFrame = 0; //update the current stream to start playing the next track at the correct frame. UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS); UpdateGUIData(si); if (m_signalStarted) m_callback.OnPlayBackStarted(si->m_fileItem); m_signalStarted = true; m_callback.OnAVStarted(si->m_fileItem); } else { CLog::Log(LOGINFO, "PAPlayer::ProcessStream - Stream Finished"); return false; } } if (!QueueData(si)) return false; /* update free buffer time if we are running */ if (si->m_started) { if (si->m_stream->IsBuffering()) freeBufferTime = 1.0; else { double free_space; if (si->m_audioFormat.m_dataFormat != AE_FMT_RAW) free_space = (double)(si->m_stream->GetSpace() / si->m_bytesPerSample) / si->m_audioFormat.m_sampleRate; else free_space = (double) si->m_stream->GetSpace() * si->m_audioFormat.m_streamInfo.GetDuration() / 1000; freeBufferTime = std::max(freeBufferTime , free_space); } } return true; }