PageRenderTime 86ms CodeModel.GetById 1ms app.highlight 75ms RepoModel.GetById 1ms app.codeStats 1ms

/xbmc/cores/paplayer/PAPlayer.cpp

http://github.com/xbmc/xbmc
C++ | 1164 lines | 925 code | 162 blank | 77 comment | 205 complexity | bfeac87057c3c87e8ff2ca6216380d36 MD5 | raw file
   1/*
   2 *  Copyright (C) 2005-2018 Team Kodi
   3 *  This file is part of Kodi - https://kodi.tv
   4 *
   5 *  SPDX-License-Identifier: GPL-2.0-or-later
   6 *  See LICENSES/README.md for more information.
   7 */
   8
   9#include "PAPlayer.h"
  10
  11#include "CodecFactory.h"
  12#include "ServiceBroker.h"
  13#include "Util.h"
  14#include "cores/AudioEngine/Interfaces/AE.h"
  15#include "cores/AudioEngine/Interfaces/AEStream.h"
  16#include "cores/AudioEngine/Utils/AEStreamData.h"
  17#include "cores/AudioEngine/Utils/AEUtil.h"
  18#include "cores/DataCacheCore.h"
  19#include "cores/VideoPlayer/Process/ProcessInfo.h"
  20#include "messaging/ApplicationMessenger.h"
  21#include "music/tags/MusicInfoTag.h"
  22#include "settings/AdvancedSettings.h"
  23#include "settings/Settings.h"
  24#include "settings/SettingsComponent.h"
  25#include "utils/JobManager.h"
  26#include "utils/log.h"
  27#include "video/Bookmark.h"
  28
  29using namespace KODI::MESSAGING;
  30
  31#define TIME_TO_CACHE_NEXT_FILE 5000 /* 5 seconds before end of song, start caching the next song */
  32#define FAST_XFADE_TIME           80 /* 80 milliseconds */
  33#define MAX_SKIP_XFADE_TIME     2000 /* max 2 seconds crossfade on track skip */
  34
  35// PAP: Psycho-acoustic Audio Player
  36// Supporting all open  audio codec standards.
  37// First one being nullsoft's nsv audio decoder format
  38
  39PAPlayer::PAPlayer(IPlayerCallback& callback) :
  40  IPlayer(callback),
  41  CThread("PAPlayer"),
  42  m_signalSpeedChange(false),
  43  m_playbackSpeed(1    ),
  44  m_isPlaying(false),
  45  m_isPaused(false),
  46  m_isFinished(false),
  47  m_defaultCrossfadeMS (0),
  48  m_upcomingCrossfadeMS(0),
  49  m_audioCallback(NULL ),
  50  m_jobCounter(0),
  51  m_newForcedPlayerTime(-1),
  52  m_newForcedTotalTime (-1)
  53{
  54  memset(&m_playerGUIData, 0, sizeof(m_playerGUIData));
  55  m_processInfo.reset(CProcessInfo::CreateInstance());
  56  m_processInfo->SetDataCache(&CServiceBroker::GetDataCacheCore());
  57}
  58
  59PAPlayer::~PAPlayer()
  60{
  61  CloseFile();
  62}
  63
  64bool PAPlayer::HandlesType(const std::string &type)
  65{
  66  ICodec* codec = CodecFactory::CreateCodec(type);
  67  if (codec && codec->CanInit())
  68  {
  69    delete codec;
  70    return true;
  71  }
  72
  73  return false;
  74}
  75
  76void PAPlayer::SoftStart(bool wait/* = false */)
  77{
  78  CSingleLock lock(m_streamsLock);
  79  for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
  80  {
  81    StreamInfo* si = *itt;
  82    if (si->m_fadeOutTriggered)
  83      continue;
  84
  85    si->m_stream->Resume();
  86    si->m_stream->FadeVolume(0.0f, 1.0f, FAST_XFADE_TIME);
  87  }
  88
  89  if (wait)
  90  {
  91    /* wait for them to fade in */
  92    lock.Leave();
  93    CThread::Sleep(FAST_XFADE_TIME);
  94    lock.Enter();
  95
  96    /* be sure they have faded in */
  97    while(wait)
  98    {
  99      wait = false;
 100      for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
 101      {
 102        StreamInfo* si = *itt;
 103        if (si->m_stream->IsFading())
 104        {
 105          lock.Leave();
 106          wait = true;
 107          CThread::Sleep(1);
 108          lock.Enter();
 109          break;
 110        }
 111      }
 112    }
 113  }
 114}
 115
 116void PAPlayer::SoftStop(bool wait/* = false */, bool close/* = true */)
 117{
 118  /* fade all the streams out fast for a nice soft stop */
 119  CSingleLock lock(m_streamsLock);
 120  for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
 121  {
 122    StreamInfo* si = *itt;
 123    if (si->m_stream)
 124      si->m_stream->FadeVolume(1.0f, 0.0f, FAST_XFADE_TIME);
 125
 126    if (close)
 127    {
 128      si->m_prepareTriggered  = true;
 129      si->m_playNextTriggered = true;
 130      si->m_fadeOutTriggered  = true;
 131    }
 132  }
 133
 134  /* if we are going to wait for them to finish fading */
 135  if(wait)
 136  {
 137    // fail safe timer, do not wait longer than 1000ms
 138    XbmcThreads::EndTime timer(1000);
 139
 140    /* wait for them to fade out */
 141    lock.Leave();
 142    CThread::Sleep(FAST_XFADE_TIME);
 143    lock.Enter();
 144
 145    /* be sure they have faded out */
 146    while(wait && !CServiceBroker::GetActiveAE()->IsSuspended() && !timer.IsTimePast())
 147    {
 148      wait = false;
 149      for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
 150      {
 151        StreamInfo* si = *itt;
 152        if (si->m_stream && si->m_stream->IsFading())
 153        {
 154          lock.Leave();
 155          wait = true;
 156          CThread::Sleep(1);
 157          lock.Enter();
 158          break;
 159        }
 160      }
 161    }
 162
 163    /* if we are not closing the streams, pause them */
 164    if (!close)
 165    {
 166      for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
 167      {
 168        StreamInfo* si = *itt;
 169        si->m_stream->Pause();
 170      }
 171    }
 172  }
 173}
 174
 175void PAPlayer::CloseAllStreams(bool fade/* = true */)
 176{
 177  if (!fade)
 178  {
 179    CSingleLock lock(m_streamsLock);
 180    while (!m_streams.empty())
 181    {
 182      StreamInfo* si = m_streams.front();
 183      m_streams.pop_front();
 184
 185      if (si->m_stream)
 186      {
 187        CloseFileCB(*si);
 188        CServiceBroker::GetActiveAE()->FreeStream(si->m_stream, true);
 189        si->m_stream = NULL;
 190      }
 191
 192      si->m_decoder.Destroy();
 193      delete si;
 194    }
 195
 196    while (!m_finishing.empty())
 197    {
 198      StreamInfo* si = m_finishing.front();
 199      m_finishing.pop_front();
 200
 201      if (si->m_stream)
 202      {
 203        CloseFileCB(*si);
 204        CServiceBroker::GetActiveAE()->FreeStream(si->m_stream, true);
 205        si->m_stream = nullptr;
 206      }
 207
 208      si->m_decoder.Destroy();
 209      delete si;
 210    }
 211    m_currentStream = nullptr;
 212  }
 213  else
 214  {
 215    SoftStop(false, true);
 216    CSingleLock lock(m_streamsLock);
 217    m_currentStream = NULL;
 218  }
 219}
 220
 221bool PAPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
 222{
 223  m_defaultCrossfadeMS = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_MUSICPLAYER_CROSSFADE) * 1000;
 224  m_fullScreen = options.fullscreen;
 225
 226  if (m_streams.size() > 1 || !m_defaultCrossfadeMS || m_isPaused)
 227  {
 228    CloseAllStreams(!m_isPaused);
 229    StopThread();
 230    m_isPaused = false; // Make sure to reset the pause state
 231  }
 232
 233  {
 234    CSingleLock lock(m_streamsLock);
 235    m_jobCounter++;
 236  }
 237  CJobManager::GetInstance().Submit(
 238    [=]() { QueueNextFileEx(file, false); },
 239    this,
 240    CJob::PRIORITY_NORMAL
 241  );
 242
 243  CSingleLock lock(m_streamsLock);
 244  if (m_streams.size() == 2)
 245  {
 246    //do a short crossfade on trackskip, set to max 2 seconds for these prev/next transitions
 247    m_upcomingCrossfadeMS = std::min(m_defaultCrossfadeMS, (unsigned int)MAX_SKIP_XFADE_TIME);
 248
 249    //start transition to next track
 250    StreamInfo* si = m_streams.front();
 251    si->m_playNextAtFrame  = si->m_framesSent; //start next track at current frame
 252    si->m_prepareTriggered = true; //next track is ready to go
 253  }
 254  lock.Leave();
 255
 256  if (!IsRunning())
 257    Create();
 258
 259  /* trigger playback start */
 260  m_isPlaying = true;
 261  m_startEvent.Set();
 262
 263  // OnPlayBackStarted to be made only once. Callback processing may be slower than player process
 264  // so clear signal flag first otherwise async stream processing could also make callback
 265  m_signalStarted = false;
 266  m_callback.OnPlayBackStarted(file);
 267
 268  return true;
 269}
 270
 271void PAPlayer::UpdateCrossfadeTime(const CFileItem& file)
 272{
 273  // we explicitly disable crossfading for audio cds
 274  if (file.IsCDDA())
 275   m_upcomingCrossfadeMS = 0;
 276  else
 277    m_upcomingCrossfadeMS = m_defaultCrossfadeMS = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_MUSICPLAYER_CROSSFADE) * 1000;
 278
 279  if (m_upcomingCrossfadeMS)
 280  {
 281    if (!m_currentStream ||
 282         (file.HasMusicInfoTag() && !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICPLAYER_CROSSFADEALBUMTRACKS) &&
 283          m_currentStream->m_fileItem.HasMusicInfoTag() &&
 284          (m_currentStream->m_fileItem.GetMusicInfoTag()->GetAlbum() != "") &&
 285          (m_currentStream->m_fileItem.GetMusicInfoTag()->GetAlbum() == file.GetMusicInfoTag()->GetAlbum()) &&
 286          (m_currentStream->m_fileItem.GetMusicInfoTag()->GetDiscNumber() == file.GetMusicInfoTag()->GetDiscNumber()) &&
 287          (m_currentStream->m_fileItem.GetMusicInfoTag()->GetTrackNumber() == file.GetMusicInfoTag()->GetTrackNumber() - 1)))
 288    {
 289      //do not crossfade when playing consecutive albumtracks
 290      m_upcomingCrossfadeMS = 0;
 291    }
 292  }
 293}
 294
 295bool PAPlayer::QueueNextFile(const CFileItem &file)
 296{
 297  {
 298    CSingleLock lock(m_streamsLock);
 299    m_jobCounter++;
 300  }
 301  CJobManager::GetInstance().Submit([this, file]() {
 302    QueueNextFileEx(file, true);
 303  }, this, CJob::PRIORITY_NORMAL);
 304
 305  return true;
 306}
 307
 308bool PAPlayer::QueueNextFileEx(const CFileItem &file, bool fadeIn)
 309{
 310  if (m_currentStream)
 311  {
 312    // check if we advance a track of a CUE sheet
 313    // if this is the case we don't need to open a new stream
 314    std::string newURL = file.GetDynURL().GetFileName();
 315    std::string oldURL = m_currentStream->m_fileItem.GetDynURL().GetFileName();
 316    if (newURL.compare(oldURL) == 0 &&
 317        file.m_lStartOffset &&
 318        file.m_lStartOffset == m_currentStream->m_fileItem.m_lEndOffset &&
 319        m_currentStream && m_currentStream->m_prepareTriggered)
 320    {
 321      m_currentStream->m_nextFileItem.reset(new CFileItem(file));
 322      m_upcomingCrossfadeMS = 0;
 323      return true;
 324    }
 325    m_currentStream->m_nextFileItem.reset();
 326  }
 327
 328  StreamInfo *si = new StreamInfo();
 329  si->m_fileItem = file;
 330  if (!si->m_decoder.Create(file, si->m_fileItem.m_lStartOffset))
 331  {
 332    CLog::Log(LOGWARNING, "PAPlayer::QueueNextFileEx - Failed to create the decoder");
 333
 334    // advance playlist
 335    AdvancePlaylistOnError(si->m_fileItem);
 336    m_callback.OnQueueNextItem();
 337
 338    delete si;
 339    return false;
 340  }
 341
 342  /* decode until there is data-available */
 343  si->m_decoder.Start();
 344  while (si->m_decoder.GetDataSize(true) == 0)
 345  {
 346    int status = si->m_decoder.GetStatus();
 347    if (status == STATUS_ENDED   ||
 348        status == STATUS_NO_FILE ||
 349        si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
 350    {
 351      CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error reading samples");
 352
 353      si->m_decoder.Destroy();
 354      // advance playlist
 355      AdvancePlaylistOnError(si->m_fileItem);
 356      m_callback.OnQueueNextItem();
 357      delete si;
 358      return false;
 359    }
 360
 361    /* yield our time so that the main PAP thread doesnt stall */
 362    CThread::Sleep(1);
 363  }
 364
 365  // set m_upcomingCrossfadeMS depending on type of file and user settings
 366  UpdateCrossfadeTime(si->m_fileItem);
 367
 368  /* init the streaminfo struct */
 369  si->m_audioFormat = si->m_decoder.GetFormat();
 370  si->m_startOffset = file.m_lStartOffset;
 371  si->m_endOffset = file.m_lEndOffset;
 372  si->m_bytesPerSample = CAEUtil::DataFormatToBits(si->m_audioFormat.m_dataFormat) >> 3;
 373  si->m_bytesPerFrame = si->m_bytesPerSample * si->m_audioFormat.m_channelLayout.Count();
 374  si->m_started = false;
 375  si->m_finishing = false;
 376  si->m_framesSent = 0;
 377  si->m_seekNextAtFrame = 0;
 378  if (si->m_fileItem.HasProperty("audiobook_bookmark"))
 379    si->m_seekFrame = si->m_audioFormat.m_sampleRate * CUtil::ConvertMilliSecsToSecs(si->m_fileItem.GetProperty("audiobook_bookmark").asInteger());
 380  else
 381    si->m_seekFrame = -1;
 382  si->m_stream = NULL;
 383  si->m_volume = (fadeIn && m_upcomingCrossfadeMS) ? 0.0f : 1.0f;
 384  si->m_fadeOutTriggered = false;
 385  si->m_isSlaved = false;
 386
 387  si->m_decoderTotal = si->m_decoder.TotalTime();
 388  int64_t streamTotalTime = si->m_decoderTotal;
 389  if (si->m_endOffset)
 390    streamTotalTime = si->m_endOffset - si->m_startOffset;
 391
 392  si->m_prepareNextAtFrame = 0;
 393  // cd drives don't really like it to be crossfaded or prepared
 394  if(!file.IsCDDA())
 395  {
 396    if (streamTotalTime >= TIME_TO_CACHE_NEXT_FILE + m_defaultCrossfadeMS)
 397      si->m_prepareNextAtFrame = (int)((streamTotalTime - TIME_TO_CACHE_NEXT_FILE - m_defaultCrossfadeMS) * si->m_audioFormat.m_sampleRate / 1000.0f);
 398  }
 399
 400  if (m_currentStream && ((m_currentStream->m_audioFormat.m_dataFormat == AE_FMT_RAW) || (si->m_audioFormat.m_dataFormat == AE_FMT_RAW)))
 401  {
 402    m_currentStream->m_prepareTriggered = false;
 403    m_currentStream->m_waitOnDrain = true;
 404    m_currentStream->m_prepareNextAtFrame = 0;
 405    si->m_decoder.Destroy();
 406    delete si;
 407    return false;
 408  }
 409
 410  si->m_prepareTriggered = false;
 411  si->m_playNextAtFrame = 0;
 412  si->m_playNextTriggered = false;
 413  si->m_waitOnDrain = false;
 414
 415  if (!PrepareStream(si))
 416  {
 417    CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error preparing stream");
 418
 419    si->m_decoder.Destroy();
 420    // advance playlist
 421    AdvancePlaylistOnError(si->m_fileItem);
 422    m_callback.OnQueueNextItem();
 423    delete si;
 424    return false;
 425  }
 426
 427  /* add the stream to the list */
 428  CSingleLock lock(m_streamsLock);
 429  m_streams.push_back(si);
 430  //update the current stream to start playing the next track at the correct frame.
 431  UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS);
 432
 433  return true;
 434}
 435
 436void PAPlayer::UpdateStreamInfoPlayNextAtFrame(StreamInfo *si, unsigned int crossFadingTime)
 437{
 438  // if no crossfading or cue sheet, wait for eof
 439  if (si && (crossFadingTime || si->m_endOffset))
 440  {
 441    int64_t streamTotalTime = si->m_decoder.TotalTime();
 442    if (si->m_endOffset)
 443      streamTotalTime = si->m_endOffset - si->m_startOffset;
 444    if (streamTotalTime < crossFadingTime)
 445      si->m_playNextAtFrame = (int)((streamTotalTime / 2) * si->m_audioFormat.m_sampleRate / 1000.0f);
 446    else
 447      si->m_playNextAtFrame = (int)((streamTotalTime - crossFadingTime) * si->m_audioFormat.m_sampleRate / 1000.0f);
 448  }
 449}
 450
 451inline bool PAPlayer::PrepareStream(StreamInfo *si)
 452{
 453  /* if we have a stream we are already prepared */
 454  if (si->m_stream)
 455    return true;
 456
 457  /* get a paused stream */
 458  AEAudioFormat format = si->m_audioFormat;
 459  si->m_stream = CServiceBroker::GetActiveAE()->MakeStream(
 460    format,
 461    AESTREAM_PAUSED
 462  );
 463
 464  if (!si->m_stream)
 465  {
 466    CLog::Log(LOGDEBUG, "PAPlayer::PrepareStream - Failed to get IAEStream");
 467    return false;
 468  }
 469
 470  si->m_stream->SetVolume(si->m_volume);
 471  float peak = 1.0;
 472  float gain = si->m_decoder.GetReplayGain(peak);
 473  if (peak * gain <= 1.0)
 474    // No clipping protection needed
 475    si->m_stream->SetReplayGain(gain);
 476  else if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICPLAYER_REPLAYGAINAVOIDCLIPPING))
 477    // Normalise volume reducing replaygain to avoid needing clipping protection, plays file at lower level
 478    si->m_stream->SetReplayGain(1.0f / fabs(peak));
 479  else
 480    // Clipping protection (when enabled in AE) by audio limiting, applied just where needed
 481    si->m_stream->SetAmplification(gain);
 482
 483  /* if its not the first stream and crossfade is not enabled */
 484  if (m_currentStream && m_currentStream != si && !m_upcomingCrossfadeMS)
 485  {
 486    /* slave the stream for gapless */
 487    si->m_isSlaved = true;
 488    m_currentStream->m_stream->RegisterSlave(si->m_stream);
 489  }
 490
 491  /* fill the stream's buffer */
 492  while(si->m_stream->IsBuffering())
 493  {
 494    int status = si->m_decoder.GetStatus();
 495    if (status == STATUS_ENDED   ||
 496        status == STATUS_NO_FILE ||
 497        si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
 498    {
 499      CLog::Log(LOGINFO, "PAPlayer::PrepareStream - Stream Finished");
 500      break;
 501    }
 502
 503    if (!QueueData(si))
 504      break;
 505
 506    /* yield our time so that the main PAP thread doesnt stall */
 507    CThread::Sleep(1);
 508  }
 509
 510  CLog::Log(LOGINFO, "PAPlayer::PrepareStream - Ready");
 511
 512  return true;
 513}
 514
 515bool PAPlayer::CloseFile(bool reopen)
 516{
 517  if (reopen)
 518    CServiceBroker::GetActiveAE()->KeepConfiguration(3000);
 519
 520  if (!m_isPaused)
 521    SoftStop(true, true);
 522  CloseAllStreams(false);
 523
 524  /* wait for the thread to terminate */
 525  StopThread(true);//true - wait for end of thread
 526
 527  // wait for any pending jobs to complete
 528  {
 529    CSingleLock lock(m_streamsLock);
 530    while (m_jobCounter > 0)
 531    {
 532      lock.Leave();
 533      m_jobEvent.WaitMSec(100);
 534      lock.Enter();
 535    }
 536  }
 537
 538  return true;
 539}
 540
 541void PAPlayer::Process()
 542{
 543  if (!m_startEvent.WaitMSec(100))
 544  {
 545    CLog::Log(LOGDEBUG, "PAPlayer::Process - Failed to receive start event");
 546    return;
 547  }
 548
 549  CLog::Log(LOGDEBUG, "PAPlayer::Process - Playback started");
 550  while(m_isPlaying && !m_bStop)
 551  {
 552    /* this needs to happen outside of any locks to prevent deadlocks */
 553    if (m_signalSpeedChange)
 554    {
 555      m_callback.OnPlayBackSpeedChanged(m_playbackSpeed);
 556      m_signalSpeedChange = false;
 557    }
 558
 559    double freeBufferTime = 0.0;
 560    ProcessStreams(freeBufferTime);
 561
 562    // if none of our streams wants at least 10ms of data, we sleep
 563    if (freeBufferTime < 0.01)
 564    {
 565      CThread::Sleep(10);
 566    }
 567
 568    if (m_newForcedPlayerTime != -1)
 569    {
 570      if (SetTimeInternal(m_newForcedPlayerTime))
 571      {
 572        m_newForcedPlayerTime = -1;
 573      }
 574    }
 575
 576    if (m_newForcedTotalTime != -1)
 577    {
 578      if (SetTotalTimeInternal(m_newForcedTotalTime))
 579      {
 580        m_newForcedTotalTime = -1;
 581      }
 582    }
 583
 584    GetTimeInternal(); //update for GUI
 585  }
 586  m_isPlaying = false;
 587}
 588
 589inline void PAPlayer::ProcessStreams(double &freeBufferTime)
 590{
 591  CSingleLock sharedLock(m_streamsLock);
 592  if (m_isFinished && m_streams.empty() && m_finishing.empty())
 593  {
 594    m_isPlaying = false;
 595    freeBufferTime = 1.0;
 596    return;
 597  }
 598
 599  /* destroy any drained streams */
 600  for (auto itt = m_finishing.begin(); itt != m_finishing.end();)
 601  {
 602    StreamInfo* si = *itt;
 603    if (si->m_stream->IsDrained())
 604    {
 605      itt = m_finishing.erase(itt);
 606      CloseFileCB(*si);
 607      CServiceBroker::GetActiveAE()->FreeStream(si->m_stream, true);
 608      delete si;
 609      CLog::Log(LOGDEBUG, "PAPlayer::ProcessStreams - Stream Freed");
 610    }
 611    else
 612      ++itt;
 613  }
 614
 615  sharedLock.Leave();
 616  CSingleLock lock(m_streamsLock);
 617
 618  for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
 619  {
 620    StreamInfo* si = *itt;
 621    if (!m_currentStream && !si->m_started)
 622    {
 623      m_currentStream = si;
 624      UpdateGUIData(si); //update for GUI
 625    }
 626    /* if the stream is finishing */
 627    if ((si->m_playNextTriggered && si->m_stream && !si->m_stream->IsFading()) || !ProcessStream(si, freeBufferTime))
 628    {
 629      if (!si->m_prepareTriggered)
 630      {
 631        if (si->m_waitOnDrain)
 632        {
 633          si->m_stream->Drain(true);
 634          si->m_waitOnDrain = false;
 635        }
 636        si->m_prepareTriggered = true;
 637        m_callback.OnQueueNextItem();
 638      }
 639
 640      /* remove the stream */
 641      itt = m_streams.erase(itt);
 642      /* if its the current stream */
 643      if (si == m_currentStream)
 644      {
 645        /* if it was the last stream */
 646        if (itt == m_streams.end())
 647        {
 648          /* if it didnt trigger the next queue item */
 649          if (!si->m_prepareTriggered)
 650          {
 651            if (si->m_waitOnDrain)
 652            {
 653              si->m_stream->Drain(true);
 654              si->m_waitOnDrain = false;
 655            }
 656            m_callback.OnQueueNextItem();
 657            si->m_prepareTriggered = true;
 658          }
 659          m_currentStream = NULL;
 660        }
 661        else
 662        {
 663          m_currentStream = *itt;
 664          UpdateGUIData(*itt); //update for GUI
 665        }
 666      }
 667
 668      /* unregister the audio callback */
 669      si->m_stream->UnRegisterAudioCallback();
 670      si->m_decoder.Destroy();
 671      si->m_stream->Drain(false);
 672      m_finishing.push_back(si);
 673      return;
 674    }
 675
 676    if (!si->m_started)
 677      continue;
 678
 679    // is it time to prepare the next stream?
 680    if (si->m_prepareNextAtFrame > 0 && !si->m_prepareTriggered && si->m_framesSent >= si->m_prepareNextAtFrame)
 681    {
 682      si->m_prepareTriggered = true;
 683      m_callback.OnQueueNextItem();
 684    }
 685
 686    // it is time to start playing the next stream?
 687    if (si->m_playNextAtFrame > 0 && !si->m_playNextTriggered && !si->m_nextFileItem && si->m_framesSent >= si->m_playNextAtFrame)
 688    {
 689      if (!si->m_prepareTriggered)
 690      {
 691        si->m_prepareTriggered = true;
 692        m_callback.OnQueueNextItem();
 693      }
 694
 695      if (!m_isFinished)
 696      {
 697        if (m_upcomingCrossfadeMS)
 698        {
 699          si->m_stream->FadeVolume(1.0f, 0.0f, m_upcomingCrossfadeMS);
 700          si->m_fadeOutTriggered = true;
 701        }
 702        m_currentStream = NULL;
 703
 704        /* unregister the audio callback */
 705        si->m_stream->UnRegisterAudioCallback();
 706      }
 707
 708      si->m_playNextTriggered = true;
 709    }
 710  }
 711}
 712
 713inline bool PAPlayer::ProcessStream(StreamInfo *si, double &freeBufferTime)
 714{
 715  /* if playback needs to start on this stream, do it */
 716  if (si == m_currentStream && !si->m_started)
 717  {
 718    si->m_started = true;
 719    si->m_stream->RegisterAudioCallback(m_audioCallback);
 720    if (!si->m_isSlaved)
 721      si->m_stream->Resume();
 722    si->m_stream->FadeVolume(0.0f, 1.0f, m_upcomingCrossfadeMS);
 723    if (m_signalStarted)
 724      m_callback.OnPlayBackStarted(si->m_fileItem);
 725    m_signalStarted = true;
 726    if (m_fullScreen)
 727    {
 728      CApplicationMessenger::GetInstance().PostMsg(TMSG_SWITCHTOFULLSCREEN);
 729      m_fullScreen = false;
 730    }
 731    m_callback.OnAVStarted(si->m_fileItem);
 732  }
 733
 734  /* if we have not started yet and the stream has been primed */
 735  unsigned int space = si->m_stream->GetSpace();
 736  if (!si->m_started && !space)
 737    return true;
 738
 739  /* see if it is time yet to FF/RW or a direct seek */
 740  if (!si->m_playNextTriggered && ((m_playbackSpeed != 1 && si->m_framesSent >= si->m_seekNextAtFrame) || si->m_seekFrame > -1))
 741  {
 742    int64_t time = (int64_t)0;
 743    /* if its a direct seek */
 744    if (si->m_seekFrame > -1)
 745    {
 746      time = (int64_t)((float)si->m_seekFrame / (float)si->m_audioFormat.m_sampleRate * 1000.0f);
 747      si->m_framesSent = (int)(si->m_seekFrame - ((float)si->m_startOffset * (float)si->m_audioFormat.m_sampleRate) / 1000.0f);
 748      si->m_seekFrame  = -1;
 749      m_playerGUIData.m_time = time; //update for GUI
 750      si->m_seekNextAtFrame = 0;
 751      CDataCacheCore::GetInstance().SetPlayTimes(0, time, 0, m_playerGUIData.m_totalTime);
 752    }
 753    /* if its FF/RW */
 754    else
 755    {
 756      si->m_framesSent      += si->m_audioFormat.m_sampleRate * (m_playbackSpeed  - 1);
 757      si->m_seekNextAtFrame  = si->m_framesSent + si->m_audioFormat.m_sampleRate / 2;
 758      time = (int64_t)(((float)si->m_framesSent / (float)si->m_audioFormat.m_sampleRate * 1000.0f) + (float)si->m_startOffset);
 759    }
 760
 761    /* if we are seeking back before the start of the track start normal playback */
 762    if (time < si->m_startOffset || si->m_framesSent < 0)
 763    {
 764      time = si->m_startOffset;
 765      si->m_framesSent      = 0;
 766      si->m_seekNextAtFrame = 0;
 767      SetSpeed(1);
 768    }
 769
 770    si->m_decoder.Seek(time);
 771  }
 772
 773  int status = si->m_decoder.GetStatus();
 774  if (status == STATUS_ENDED   ||
 775      status == STATUS_NO_FILE ||
 776      si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR ||
 777      ((si->m_endOffset) && (si->m_framesSent / si->m_audioFormat.m_sampleRate >= (si->m_endOffset - si->m_startOffset) / 1000)))
 778  {
 779    if (si == m_currentStream && si->m_nextFileItem)
 780    {
 781      CloseFileCB(*si);
 782
 783      // update current stream with info of next track
 784      si->m_startOffset = si->m_nextFileItem->m_lStartOffset;
 785      if (si->m_nextFileItem->m_lEndOffset)
 786        si->m_endOffset = si->m_nextFileItem->m_lEndOffset;
 787      else
 788        si->m_endOffset = 0;
 789      si->m_framesSent = 0;
 790
 791      si->m_fileItem = *si->m_nextFileItem;
 792      si->m_nextFileItem.reset();
 793
 794      int64_t streamTotalTime = si->m_decoder.TotalTime() - si->m_startOffset;
 795      if (si->m_endOffset)
 796        streamTotalTime = si->m_endOffset - si->m_startOffset;
 797
 798      // calculate time when to prepare next stream
 799      si->m_prepareNextAtFrame = 0;
 800      if (streamTotalTime >= TIME_TO_CACHE_NEXT_FILE + m_defaultCrossfadeMS)
 801        si->m_prepareNextAtFrame = (int)((streamTotalTime - TIME_TO_CACHE_NEXT_FILE - m_defaultCrossfadeMS) * si->m_audioFormat.m_sampleRate / 1000.0f);
 802
 803      si->m_prepareTriggered = false;
 804      si->m_playNextAtFrame = 0;
 805      si->m_playNextTriggered = false;
 806      si->m_seekNextAtFrame = 0;
 807
 808      //update the current stream to start playing the next track at the correct frame.
 809      UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS);
 810
 811      UpdateGUIData(si);
 812      if (m_signalStarted)
 813        m_callback.OnPlayBackStarted(si->m_fileItem);
 814      m_signalStarted = true;
 815      m_callback.OnAVStarted(si->m_fileItem);
 816    }
 817    else
 818    {
 819      CLog::Log(LOGINFO, "PAPlayer::ProcessStream - Stream Finished");
 820      return false;
 821    }
 822  }
 823
 824  if (!QueueData(si))
 825    return false;
 826
 827  /* update free buffer time if we are running */
 828  if (si->m_started)
 829  {
 830    if (si->m_stream->IsBuffering())
 831      freeBufferTime = 1.0;
 832    else
 833    {
 834      double free_space;
 835      if (si->m_audioFormat.m_dataFormat != AE_FMT_RAW)
 836        free_space = (double)(si->m_stream->GetSpace() / si->m_bytesPerSample) / si->m_audioFormat.m_sampleRate;
 837      else
 838        free_space = (double) si->m_stream->GetSpace() * si->m_audioFormat.m_streamInfo.GetDuration() / 1000;
 839
 840      freeBufferTime = std::max(freeBufferTime , free_space);
 841    }
 842  }
 843
 844  return true;
 845}
 846
 847bool PAPlayer::QueueData(StreamInfo *si)
 848{
 849  unsigned int space = si->m_stream->GetSpace();
 850
 851  if (si->m_audioFormat.m_dataFormat != AE_FMT_RAW)
 852  {
 853    unsigned int samples = std::min(si->m_decoder.GetDataSize(false), space / si->m_bytesPerSample);
 854    if (!samples)
 855      return true;
 856
 857    // we want complete frames
 858    samples -= samples % si->m_audioFormat.m_channelLayout.Count();
 859
 860    uint8_t* data = (uint8_t*)si->m_decoder.GetData(samples);
 861    if (!data)
 862    {
 863      CLog::Log(LOGERROR, "PAPlayer::QueueData - Failed to get data from the decoder");
 864      return false;
 865    }
 866
 867    unsigned int frames = samples/si->m_audioFormat.m_channelLayout.Count();
 868    unsigned int added = si->m_stream->AddData(&data, 0, frames, nullptr);
 869    si->m_framesSent += added;
 870  }
 871  else
 872  {
 873    if (!space)
 874      return true;
 875
 876    int size;
 877    uint8_t *data = si->m_decoder.GetRawData(size);
 878    if (data && size)
 879    {
 880      int added = si->m_stream->AddData(&data, 0, size, nullptr);
 881      if (added != size)
 882      {
 883        CLog::Log(LOGERROR, "PAPlayer::QueueData - unknown error");
 884        return false;
 885      }
 886      si->m_framesSent += si->m_audioFormat.m_streamInfo.GetDuration() / 1000 * si->m_audioFormat.m_streamInfo.m_sampleRate;
 887    }
 888  }
 889
 890  const ICodec* codec = si->m_decoder.GetCodec();
 891  m_playerGUIData.m_cacheLevel = codec ? codec->GetCacheLevel() : 0; //update for GUI
 892
 893  return true;
 894}
 895
 896void PAPlayer::OnExit()
 897{
 898  //@todo signal OnPlayBackError if there was an error on last stream
 899  if (m_isFinished && !m_bStop)
 900    m_callback.OnPlayBackEnded();
 901  else
 902    m_callback.OnPlayBackStopped();
 903}
 904
 905void PAPlayer::OnNothingToQueueNotify()
 906{
 907  m_isFinished = true;
 908}
 909
 910bool PAPlayer::IsPlaying() const
 911{
 912  return m_isPlaying;
 913}
 914
 915void PAPlayer::Pause()
 916{
 917  if (m_isPaused)
 918  {
 919    SetSpeed(1);
 920  }
 921  else
 922  {
 923    SetSpeed(0);
 924  }
 925}
 926
 927void PAPlayer::SetVolume(float volume)
 928{
 929
 930}
 931
 932void PAPlayer::SetDynamicRangeCompression(long drc)
 933{
 934
 935}
 936
 937void PAPlayer::SetSpeed(float speed)
 938{
 939  m_playbackSpeed = static_cast<int>(speed);
 940  CDataCacheCore::GetInstance().SetSpeed(1.0, speed);
 941  if (m_playbackSpeed != 0 && m_isPaused)
 942  {
 943    m_isPaused = false;
 944    SoftStart();
 945    m_callback.OnPlayBackResumed();
 946  }
 947  else if (m_playbackSpeed == 0 && !m_isPaused)
 948  {
 949    m_isPaused = true;
 950    SoftStop(true, false);
 951    m_callback.OnPlayBackPaused();
 952  }
 953  m_signalSpeedChange = true;
 954}
 955
 956int64_t PAPlayer::GetTimeInternal()
 957{
 958  CSingleLock lock(m_streamsLock);
 959  if (!m_currentStream)
 960    return 0;
 961
 962  double time = ((double)m_currentStream->m_framesSent / (double)m_currentStream->m_audioFormat.m_sampleRate);
 963  if (m_currentStream->m_stream)
 964    time -= m_currentStream->m_stream->GetDelay();
 965  time = time * 1000.0;
 966
 967  m_playerGUIData.m_time = (int64_t)time; //update for GUI
 968  CDataCacheCore::GetInstance().SetPlayTimes(0, time, 0, m_playerGUIData.m_totalTime);
 969
 970  return (int64_t)time;
 971}
 972
 973bool PAPlayer::SetTotalTimeInternal(int64_t time)
 974{
 975  CSingleLock lock(m_streamsLock);
 976  if (!m_currentStream)
 977  {
 978    return false;
 979  }
 980
 981  m_currentStream->m_decoder.SetTotalTime(time);
 982  UpdateGUIData(m_currentStream);
 983  
 984  return true;
 985}
 986
 987bool PAPlayer::SetTimeInternal(int64_t time)
 988{
 989  CSingleLock lock(m_streamsLock);
 990  if (!m_currentStream)
 991    return false;
 992
 993  m_currentStream->m_framesSent = time / 1000 * m_currentStream->m_audioFormat.m_sampleRate;
 994
 995  if (m_currentStream->m_stream)
 996    m_currentStream->m_framesSent += m_currentStream->m_stream->GetDelay() * m_currentStream->m_audioFormat.m_sampleRate;
 997  
 998  return true;
 999}
1000
1001void PAPlayer::SetTime(int64_t time)
1002{
1003  m_newForcedPlayerTime = time;
1004}
1005
1006int64_t PAPlayer::GetTotalTime64()
1007{
1008  CSingleLock lock(m_streamsLock);
1009  if (!m_currentStream)
1010    return 0;
1011
1012  int64_t total = m_currentStream->m_decoder.TotalTime();
1013  if (m_currentStream->m_endOffset)
1014    total = m_currentStream->m_endOffset;
1015  total -= m_currentStream->m_startOffset;
1016  return total;
1017}
1018
1019void PAPlayer::SetTotalTime(int64_t time)
1020{
1021  m_newForcedTotalTime = time;
1022}
1023
1024int PAPlayer::GetCacheLevel() const
1025{
1026  return m_playerGUIData.m_cacheLevel;
1027}
1028
1029void PAPlayer::GetAudioStreamInfo(int index, AudioStreamInfo &info)
1030{
1031  info.bitrate = m_playerGUIData.m_audioBitrate;
1032  info.channels = m_playerGUIData.m_channelCount;
1033  info.codecName = m_playerGUIData.m_codec;
1034  info.samplerate = m_playerGUIData.m_sampleRate;
1035  info.bitspersample = m_playerGUIData.m_bitsPerSample;
1036}
1037
1038bool PAPlayer::CanSeek()
1039{
1040  return m_playerGUIData.m_canSeek;
1041}
1042
1043void PAPlayer::Seek(bool bPlus, bool bLargeStep, bool bChapterOverride)
1044{
1045  if (!CanSeek()) return;
1046
1047  long long seek;
1048  const std::shared_ptr<CAdvancedSettings> advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
1049  if (advancedSettings->m_musicUseTimeSeeking && m_playerGUIData.m_totalTime > 2 * advancedSettings->m_musicTimeSeekForwardBig)
1050  {
1051    if (bLargeStep)
1052      seek = bPlus ? advancedSettings->m_musicTimeSeekForwardBig : advancedSettings->m_musicTimeSeekBackwardBig;
1053    else
1054      seek = bPlus ? advancedSettings->m_musicTimeSeekForward : advancedSettings->m_musicTimeSeekBackward;
1055    seek *= 1000;
1056    seek += m_playerGUIData.m_time;
1057  }
1058  else
1059  {
1060    float percent;
1061    if (bLargeStep)
1062      percent = bPlus ? static_cast<float>(advancedSettings->m_musicPercentSeekForwardBig) : static_cast<float>(advancedSettings->m_musicPercentSeekBackwardBig);
1063    else
1064      percent = bPlus ? static_cast<float>(advancedSettings->m_musicPercentSeekForward) : static_cast<float>(advancedSettings->m_musicPercentSeekBackward);
1065    seek = static_cast<long long>(GetTotalTime64() * (GetPercentage() + percent) / 100);
1066  }
1067
1068  SeekTime(seek);
1069}
1070
1071void PAPlayer::SeekTime(int64_t iTime /*=0*/)
1072{
1073  if (!CanSeek()) return;
1074
1075  CSingleLock lock(m_streamsLock);
1076  if (!m_currentStream)
1077    return;
1078
1079  int64_t seekOffset = iTime - GetTimeInternal();
1080
1081  if (m_playbackSpeed != 1)
1082    SetSpeed(1);
1083
1084  m_currentStream->m_seekFrame = (int)((float)m_currentStream->m_audioFormat.m_sampleRate * ((float)iTime + (float)m_currentStream->m_startOffset) / 1000.0f);
1085  m_callback.OnPlayBackSeek(iTime, seekOffset);
1086}
1087
1088void PAPlayer::SeekPercentage(float fPercent /*=0*/)
1089{
1090  if (fPercent < 0.0f  ) fPercent = 0.0f;
1091  if (fPercent > 100.0f) fPercent = 100.0f;
1092  SeekTime((int64_t)(fPercent * 0.01f * (float)GetTotalTime64()));
1093}
1094
1095float PAPlayer::GetPercentage()
1096{
1097  if (m_playerGUIData.m_totalTime > 0)
1098    return m_playerGUIData.m_time * 100.0f / m_playerGUIData.m_totalTime;
1099
1100  return 0.0f;
1101}
1102
1103void PAPlayer::UpdateGUIData(StreamInfo *si)
1104{
1105  /* Store data need by external threads in member
1106   * structure to prevent locking conflicts when
1107   * data required by GUI and main application
1108   */
1109  CSingleLock lock(m_streamsLock);
1110
1111  m_playerGUIData.m_sampleRate    = si->m_audioFormat.m_sampleRate;
1112  m_playerGUIData.m_channelCount  = si->m_audioFormat.m_channelLayout.Count();
1113  m_playerGUIData.m_canSeek       = si->m_decoder.CanSeek();
1114
1115  const ICodec* codec = si->m_decoder.GetCodec();
1116
1117  m_playerGUIData.m_audioBitrate = codec ? codec->m_bitRate : 0;
1118  strncpy(m_playerGUIData.m_codec,codec ? codec->m_CodecName.c_str() : "",20);
1119  m_playerGUIData.m_cacheLevel   = codec ? codec->GetCacheLevel() : 0;
1120  m_playerGUIData.m_bitsPerSample = (codec && codec->m_bitsPerCodedSample) ? codec->m_bitsPerCodedSample : si->m_bytesPerSample << 3;
1121
1122  int64_t total = si->m_decoder.TotalTime();
1123  if (si->m_endOffset)
1124    total = m_currentStream->m_endOffset;
1125  total -= m_currentStream->m_startOffset;
1126  m_playerGUIData.m_totalTime = total;
1127
1128  CServiceBroker::GetDataCacheCore().SignalAudioInfoChange();
1129}
1130
1131void PAPlayer::OnJobComplete(unsigned int jobID, bool success, CJob *job)
1132{
1133  CSingleLock lock(m_streamsLock);
1134  m_jobCounter--;
1135  m_jobEvent.Set();
1136}
1137
1138void PAPlayer::CloseFileCB(StreamInfo &si)
1139{
1140  IPlayerCallback *cb = &m_callback;
1141  CFileItem fileItem(si.m_fileItem);
1142  CBookmark bookmark;
1143  double total = si.m_decoderTotal;
1144  if (si.m_endOffset)
1145    total = si.m_endOffset;
1146  total -= si.m_startOffset;
1147  bookmark.totalTimeInSeconds = total / 1000;
1148  bookmark.timeInSeconds = (static_cast<double>(si.m_framesSent) /
1149                            static_cast<double>(si.m_audioFormat.m_sampleRate));
1150  bookmark.timeInSeconds -= si.m_stream->GetDelay();
1151  bookmark.player = m_name;
1152  bookmark.playerState = GetPlayerState();
1153  CJobManager::GetInstance().Submit([=]() {
1154    cb->OnPlayerCloseFile(fileItem, bookmark);
1155  }, CJob::PRIORITY_NORMAL);
1156}
1157
1158void PAPlayer::AdvancePlaylistOnError(CFileItem &fileItem)
1159{
1160  if (m_signalStarted)
1161    m_callback.OnPlayBackStarted(fileItem);
1162  m_signalStarted = true;
1163  m_callback.OnAVStarted(fileItem);
1164}