GUIMediaWindows.cpp build playlists dynamically
#1
Hello

I tried to fix some performance issues on large (music) playlists.

Here is my new approach, but still in progress...

cpp:

bool CGUIMediaWindow::OnPlayAndQueueMedia(const CFileItemPtr &item, std:Confusedtring player)
{
  //play and add current directory to temporary playlist
  int iPlaylist = m_guiState->GetPlaylist();
  if (iPlaylist != PLAYLIST_NONE)
  {
    CServiceBroker::GetPlaylistPlayer().ClearPlaylist(iPlaylist);
    CServiceBroker::GetPlaylistPlayer().Reset();
    int mediaToPlay = 0;

    // first try to find mainDVD file (VIDEO_TS.IFO).
    // If we find this we should not allow to queue VOB files
    std:Confusedtring mainDVD;
    for (int i = 0; i < m_vecItems->Size(); i++)
    {
      std:Confusedtring path = URIUtils::GetFileName(m_vecItems->Get(i)->GetPath());
      if (StringUtils::EqualsNoCase(path, "VIDEO_TS.IFO"))
      {
        mainDVD = path;
        break;
      }
    }



    // Load selected file into playlist
    for ( int i = 0; i < m_vecItems->Size(); i++ )
    {
      CFileItemPtr nItem = m_vecItems->Get(i);

      if (nItem->m_bIsFolder)
        continue;

      if (item->IsSamePath(nItem.get()))
      { // item that was clicked
          CServiceBroker::GetPlaylistPlayer().Add(iPlaylist, nItem);
          mediaToPlay = CServiceBroker::GetPlaylistPlayer().GetPlaylist(iPlaylist).size() - 1;
      }
    }




    // Save current window and directory to know where the selected item was
    if (m_guiState.get())
      m_guiState->SetPlaylistDirectory(m_vecItems->GetPath());

    // figure out where we start playback
    if (CServiceBroker::GetPlaylistPlayer().IsShuffled(iPlaylist))
    {
      int iIndex = CServiceBroker::GetPlaylistPlayer().GetPlaylist(iPlaylist).FindOrder(mediaToPlay);
      CServiceBroker::GetPlaylistPlayer().GetPlaylist(iPlaylist).Swap(0, iIndex);
      mediaToPlay = 0;
    }

    // play
    CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(iPlaylist);
    CServiceBroker::GetPlaylistPlayer().Play(mediaToPlay, player);




    // now queue...
    //THIS SHOULD RUN IN A ASYNCRON THREAD
    for ( int i = 0; i < m_vecItems->Size(); i++ )
    {
      CFileItemPtr nItem = m_vecItems->Get(i);

      if (nItem->m_bIsFolder)
        continue;

      if (!nItem->IsZIP() && !nItem->IsRAR() && (!nItem->IsDVDFile() || (URIUtils::GetFileName(nItem->GetPath()) == mainDVD)))


          
          if (i <= 10) //For debug reasons, playlist is limited to 10 items

              


              if (!item->IsSamePath(nItem.get()))
              { // item that was clicked
                  CServiceBroker::GetPlaylistPlayer().Insert(iPlaylist, nItem, i);
              }
    }
  }

  return true;
}


As you can see, the playlist creation is not a two stage solution.
First, just load the selected item and play it directly.

Then queue the playlist in the second step. This part has to be transferred in a asynchron thread. This is still pending.

Another pending task is shuffling. The playlist should be shuffled while creation.
Reply
#2
https://github.com/xbmc/xbmc/pull/15390
Reply
#3
just looked at your comments in your pr

why would you create a playlist of 50,000 songs am I missing something ?
Reply
#4
I closed the PR and update the code. It's almost done.

Why 50.000? Cause my musicdatabase is in total around 330.000.
One possible selection is genre based and the largest genre is "Pop", around 50.000.

And I'm not the only one with troubles...
https://forum.kodi.tv/showthread.php?tid=213737
Reply
#5
To all the kodi/c++ cracks, I need two hints.
First is an easy one, how can I read an item from a playlist?
Actually I wanna check if an item is already in the playlist.
Something like...Read playlist item via index ID.

Next more tricky...
I wanna define a subroutine array or send an ID parameter to a threading subroutine.

Code as follows, please don't complain about the layout, it's a debug version:
Reply
#6
GUIMediaWindow.h

cpp:
...
#include "guilib/GUIWindow.h"
#include "playlists/SmartPlayList.h"
#include "view/GUIViewControl.h"

#include <atomic>



//THIS IS NEW
#include <thread>









class CFileItemList;
class CGUIViewState;
namespace
{
class CGetDirectoryItems;
}

// base class for all media windows
class CGUIMediaWindow : public CGUIWindow
{
public:
  CGUIMediaWindow(int id, const char *xmlFile);
  ~CGUIMediaWindow(void) override;



...

protected:
 ....

  // current path and history
  CFileItemList* m_vecItems;
  CFileItemList* m_unfilteredItems;        ///< \brief items prior to filtering using FilterItems()
  CDirectoryHistory m_history;
  std::unique_ptr<CGUIViewState> m_guiState;
  std::atomic_bool m_vecItemsUpdating = {false};






//THIS IS NEW



  //PlaylistAsync Threading
  int PlaylistPosition[1] = {-1};
  int PlayListContentVerification[1] = {-1};
  bool PlaylistThreadRunning[1] = {false};
  bool PlaylistThreadTerminate[1] = {false};
  void* PlaylistAddAsyncAudio();
  void* PlaylistAddAsyncVideo();



  std::thread PlaylistAddAsyncThreadAudio;
  std::thread PlaylistAddAsyncThreadVideo;





...




  class CUpdateGuard
  {
  public:
    CUpdateGuard(std::atomic_bool &update) : m_update(update)
    {
      m_update = true;
    }
    ~CUpdateGuard()
    {
      m_update = false;
    }
 

...


   because it is used for filtering by appending the currently active
   filter as a "filter" parameter to the filter path/URL.

   \sa Update
   */
  std:Confusedtring m_strFilterPath;
  bool m_backgroundLoad = false;
};
Reply
#7
Sorry, I cannot post the full code, cause the forum software is complaining about "images" in my code,...

So only a small part here:

cpp:

    // play
    CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(iPlaylist);
    CServiceBroker::GetPlaylistPlayer().Play(mediaToPlay, player);

    //Start Async Thread

    if (iPlaylist == 0)
    {
      PlaylistAddAsyncThreadAudio = std::thread(&CGUIMediaWindow:TonguelaylistAddAsyncAudio, this);
    }
    else
    {
      PlaylistAddAsyncThreadVideo = std::thread(&CGUIMediaWindow:TonguelaylistAddAsyncVideo, this);
    }





cpp:


//Load rest of the playlist items async
void* CGUIMediaWindow:TonguelaylistAddAsyncAudio()
{
  PlaylistThreadRunning[0] = true;
  //Create local copies of data
  int LocaliPlaylist = m_guiState->GetPlaylist();
  std::unique_ptr<CFileItemList> Localm_vecItems;
  Localm_vecItems.reset(new CFileItemList);
  Localm_vecItems->Copy(*m_vecItems);
  int Localm_vecItemsSize = Localm_vecItems->Size();

  for ( int i = 0; i < Localm_vecItemsSize; i++ )
  {
    if (PlaylistThreadTerminate[0])
    {
      break;
    }

    CFileItemPtr nItem = Localm_vecItems->Get(i);
    std::this_thread:Confusedleep_for(std::chrono::milliseconds(100));

    if (i != CGUIMediaWindow:TonguelaylistPosition[0])
    { // item that was clicked
      CServiceBroker::GetPlaylistPlayer().Insert(LocaliPlaylist, nItem, i);
    }
  }

  PlaylistThreadTerminate[0] = false;
  PlaylistThreadRunning[0] = false;
  PlaylistAddAsyncThreadAudio.detach();
  return 0;
}


void* CGUIMediaWindow:TonguelaylistAddAsyncVideo()
{
  PlaylistThreadRunning[1] = true;
  //Create local copies of data
  int LocaliPlaylist = m_guiState->GetPlaylist();
  std::unique_ptr<CFileItemList> Localm_vecItems;
  Localm_vecItems.reset(new CFileItemList);
  Localm_vecItems->Copy(*m_vecItems);
  int Localm_vecItemsSize = Localm_vecItems->Size();

  for ( int i = 0; i < Localm_vecItemsSize; i++ )
  {
    if (PlaylistThreadTerminate[1])
    {
      break;
    }

    CFileItemPtr nItem = Localm_vecItems->Get(i);
    std::this_thread:Confusedleep_for(std::chrono::milliseconds(100));

    if (i != CGUIMediaWindow:TonguelaylistPosition[1])
    { // item that was clicked
      CServiceBroker::GetPlaylistPlayer().Insert(LocaliPlaylist, nItem, i);
    }
  }

  PlaylistThreadTerminate[1] = false;
  PlaylistThreadRunning[1] = false;
  PlaylistAddAsyncThreadVideo.detach();
  return 0;
}

Reply
#8
As you can see, I call two identical subroutines just with different names:
PlaylistAddAsyncThreadAudio
PlaylistAddAsyncThreadVideo

This is damn ugly, I wannt get rid of that. Any hints?
Reply
#9
Nevermind, I found the solution
Reply
#10
(2019-02-02, 15:30)quickmic Wrote: I closed the PR and update the code. It's almost done.

Why 50.000? Cause my musicdatabase is in total around 330.000.
One possible selection is genre based and the largest genre is "Pop", around 50.000.

And I'm not the only one with troubles...
https://forum.kodi.tv/showthread.php?tid=213737
Use Party Mode with a rule to only select from the genre Pop or whatever.

I understand that your music library is 10 times the size of mine, but how would you ever listen to 50,000 say 3 minute songs in one go, come on that's continuous playing for almost a third of a year.
Reply
#11
Yes, I know there are workarounds but this should not be the goal.
I just want kodi works as it should.
Even if there a million songs in the playlist, it should not stall, delay or whatever.

btw, https://github.com/xbmc/xbmc/pull/15435
Reply
#12
https://github.com/xbmc/xbmc/pull/15541
Reply
#13
https://github.com/xbmc/xbmc/pull/15651
Reply

Logout Mark Read Team Forum Stats Members Help
GUIMediaWindows.cpp build playlists dynamically0