bool PAPlayer::QueueNextFileEx(const CFileItem &file, bool fadeIn/* = true */) { StreamInfo *si = new StreamInfo(); if (!si->m_decoder.Create(file, (file.m_lStartOffset * 1000) / 75)) { CLog::Log(LOGWARNING, "PAPlayer::QueueNextFileEx - Failed to create the decoder"); delete si; m_callback.OnQueueNextItem(); return false; } /* decode until there is data-available */ si->m_decoder.Start(); while(si->m_decoder.GetDataSize() == 0) { int status = si->m_decoder.GetStatus(); if (status == STATUS_ENDED || status == STATUS_NO_FILE || si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR) { CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error reading samples"); si->m_decoder.Destroy(); delete si; m_callback.OnQueueNextItem(); return false; } /* yield our time so that the main PAP thread doesnt stall */ CThread::Sleep(1); } UpdateCrossfadeTime(file); /* init the streaminfo struct */ si->m_decoder.GetDataFormat(&si->m_channelInfo, &si->m_sampleRate, &si->m_encodedSampleRate, &si->m_dataFormat); si->m_startOffset = file.m_lStartOffset * 1000 / 75; si->m_endOffset = file.m_lEndOffset * 1000 / 75; si->m_bytesPerSample = CAEUtil::DataFormatToBits(si->m_dataFormat) >> 3; si->m_bytesPerFrame = si->m_bytesPerSample * si->m_channelInfo.Count(); si->m_started = false; si->m_finishing = false; si->m_framesSent = 0; si->m_seekNextAtFrame = 0; si->m_seekFrame = -1; si->m_stream = NULL; si->m_volume = (fadeIn && m_upcomingCrossfadeMS) ? 0.0f : 1.0f; si->m_fadeOutTriggered = false; si->m_isSlaved = false; int64_t streamTotalTime = si->m_decoder.TotalTime(); if (si->m_endOffset) streamTotalTime = si->m_endOffset - si->m_startOffset; 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_sampleRate / 1000.0f); si->m_prepareTriggered = false; si->m_playNextAtFrame = 0; si->m_playNextTriggered = false; if (!PrepareStream(si)) { CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error preparing stream"); si->m_decoder.Destroy(); delete si; m_callback.OnQueueNextItem(); return false; } /* add the stream to the list */ CExclusiveLock lock(m_streamsLock); m_streams.push_back(si); //update the current stream to start playing the next track at the correct frame. UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS); *m_FileItem = file; return true; }
bool PAPlayer::QueueNextFileEx(const CFileItem &file, bool fadeIn) { if (m_currentStream) { // check if we advance a track of a CUE sheet // if this is the case we don't need to open a new stream std::string newURL = file.GetDynURL().GetFileName(); std::string oldURL = m_currentStream->m_fileItem.GetDynURL().GetFileName(); if (newURL.compare(oldURL) == 0 && file.m_lStartOffset && file.m_lStartOffset == m_currentStream->m_fileItem.m_lEndOffset && m_currentStream && m_currentStream->m_prepareTriggered) { m_currentStream->m_nextFileItem.reset(new CFileItem(file)); m_upcomingCrossfadeMS = 0; return true; } m_currentStream->m_nextFileItem.reset(); } StreamInfo *si = new StreamInfo(); si->m_fileItem = file; if (!si->m_decoder.Create(file, si->m_fileItem.m_lStartOffset)) { CLog::Log(LOGWARNING, "PAPlayer::QueueNextFileEx - Failed to create the decoder"); // advance playlist AdvancePlaylistOnError(si->m_fileItem); m_callback.OnQueueNextItem(); delete si; return false; } /* decode until there is data-available */ si->m_decoder.Start(); while (si->m_decoder.GetDataSize(true) == 0) { int status = si->m_decoder.GetStatus(); if (status == STATUS_ENDED || status == STATUS_NO_FILE || si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR) { CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error reading samples"); si->m_decoder.Destroy(); // advance playlist AdvancePlaylistOnError(si->m_fileItem); m_callback.OnQueueNextItem(); delete si; return false; } /* yield our time so that the main PAP thread doesnt stall */ CThread::Sleep(1); } // set m_upcomingCrossfadeMS depending on type of file and user settings UpdateCrossfadeTime(si->m_fileItem); /* init the streaminfo struct */ si->m_audioFormat = si->m_decoder.GetFormat(); si->m_startOffset = file.m_lStartOffset; si->m_endOffset = file.m_lEndOffset; si->m_bytesPerSample = CAEUtil::DataFormatToBits(si->m_audioFormat.m_dataFormat) >> 3; si->m_bytesPerFrame = si->m_bytesPerSample * si->m_audioFormat.m_channelLayout.Count(); si->m_started = false; si->m_finishing = false; si->m_framesSent = 0; si->m_seekNextAtFrame = 0; si->m_seekFrame = -1; si->m_stream = NULL; si->m_volume = (fadeIn && m_upcomingCrossfadeMS) ? 0.0f : 1.0f; si->m_fadeOutTriggered = false; si->m_isSlaved = false; si->m_decoderTotal = si->m_decoder.TotalTime(); int64_t streamTotalTime = si->m_decoderTotal; if (si->m_endOffset) streamTotalTime = si->m_endOffset - si->m_startOffset; si->m_prepareNextAtFrame = 0; // cd drives don't really like it to be crossfaded or prepared if(!file.IsCDDA()) { 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); } if (m_currentStream && ((m_currentStream->m_audioFormat.m_dataFormat == AE_FMT_RAW) || (si->m_audioFormat.m_dataFormat == AE_FMT_RAW))) { m_currentStream->m_prepareTriggered = false; m_currentStream->m_waitOnDrain = true; m_currentStream->m_prepareNextAtFrame = 0; si->m_decoder.Destroy(); delete si; return false; } si->m_prepareTriggered = false; si->m_playNextAtFrame = 0; si->m_playNextTriggered = false; si->m_waitOnDrain = false; if (!PrepareStream(si)) { CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error preparing stream"); si->m_decoder.Destroy(); // advance playlist AdvancePlaylistOnError(si->m_fileItem); m_callback.OnQueueNextItem(); delete si; return false; } /* add the stream to the list */ CSingleLock lock(m_streamsLock); m_streams.push_back(si); //update the current stream to start playing the next track at the correct frame. UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS); return 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; }
bool PAPlayer::QueueNextFileEx(const CFileItem &file, bool fadeIn/* = true */, bool job /* = false */) { StreamInfo *si = new StreamInfo(); // check if we advance a track of a CUE sheet // if this is the case we don't need to open a new stream std::string newURL = file.GetMusicInfoTag() ? file.GetMusicInfoTag()->GetURL() : file.GetPath(); std::string oldURL = m_FileItem->GetMusicInfoTag() ? m_FileItem->GetMusicInfoTag()->GetURL() : m_FileItem->GetPath(); if (newURL.compare(oldURL) == 0 && file.m_lStartOffset && file.m_lStartOffset == m_FileItem->m_lEndOffset && m_currentStream && m_currentStream->m_prepareTriggered) { m_continueStream = true; m_upcomingCrossfadeMS = 0; *m_FileItem = file; return true; } else { m_continueStream = false; } if (!si->m_decoder.Create(file, (file.m_lStartOffset * 1000) / 75)) { CLog::Log(LOGWARNING, "PAPlayer::QueueNextFileEx - Failed to create the decoder"); delete si; // advance playlist if (job) m_callback.OnPlayBackStarted(); m_callback.OnQueueNextItem(); return false; } /* decode until there is data-available */ si->m_decoder.Start(); while(si->m_decoder.GetDataSize() == 0) { int status = si->m_decoder.GetStatus(); if (status == STATUS_ENDED || status == STATUS_NO_FILE || si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR) { CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error reading samples"); si->m_decoder.Destroy(); delete si; // advance playlist if (job) m_callback.OnPlayBackStarted(); m_callback.OnQueueNextItem(); return false; } /* yield our time so that the main PAP thread doesnt stall */ CThread::Sleep(1); } /* init the streaminfo struct */ si->m_decoder.GetDataFormat(&si->m_channelInfo, &si->m_sampleRate, &si->m_encodedSampleRate, &si->m_dataFormat); si->m_startOffset = file.m_lStartOffset * 1000 / 75; si->m_endOffset = file.m_lEndOffset * 1000 / 75; si->m_bytesPerSample = CAEUtil::DataFormatToBits(si->m_dataFormat) >> 3; si->m_bytesPerFrame = si->m_bytesPerSample * si->m_channelInfo.Count(); si->m_started = false; si->m_finishing = false; si->m_framesSent = 0; si->m_seekNextAtFrame = 0; si->m_seekFrame = -1; si->m_stream = NULL; si->m_volume = (fadeIn && m_upcomingCrossfadeMS) ? 0.0f : 1.0f; si->m_fadeOutTriggered = false; si->m_isSlaved = false; int64_t streamTotalTime = si->m_decoder.TotalTime(); if (si->m_endOffset) streamTotalTime = si->m_endOffset - si->m_startOffset; si->m_prepareNextAtFrame = 0; // cd drives don't really like it to be crossfaded or prepared if(!file.IsCDDA()) { if (streamTotalTime >= TIME_TO_CACHE_NEXT_FILE + m_defaultCrossfadeMS) si->m_prepareNextAtFrame = (int)((streamTotalTime - TIME_TO_CACHE_NEXT_FILE - m_defaultCrossfadeMS) * si->m_sampleRate / 1000.0f); } if (m_currentStream && (AE_IS_RAW(m_currentStream->m_dataFormat) || AE_IS_RAW(si->m_dataFormat))) { m_currentStream->m_prepareTriggered = false; m_currentStream->m_waitOnDrain = true; m_currentStream->m_prepareNextAtFrame = 0; si->m_decoder.Destroy(); delete si; return false; } UpdateCrossfadeTime(file); si->m_prepareTriggered = false; si->m_playNextAtFrame = 0; si->m_playNextTriggered = false; si->m_waitOnDrain = false; if (!PrepareStream(si)) { CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error preparing stream"); si->m_decoder.Destroy(); delete si; // advance playlist if (job) m_callback.OnPlayBackStarted(); m_callback.OnQueueNextItem(); return false; } /* add the stream to the list */ CExclusiveLock lock(m_streamsLock); m_streams.push_back(si); //update the current stream to start playing the next track at the correct frame. UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS); *m_FileItem = file; return true; }