Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • videolan/vlc
  • chouquette/vlc
  • bakiewicz.marek122/vlc
  • devnexen/vlc
  • rohanrajpal/vlc
  • blurrrb/vlc
  • gsoc/gsoc2019/darkapex/vlc
  • b1ue/vlc
  • fkuehne/vlc
  • magsoft/vlc
  • chub/vlc
  • cramiro9/vlc
  • robUx4/vlc
  • rom1v/vlc
  • akshayaky/vlc
  • tmk907/vlc
  • akymaster/vlc
  • govind.sharma/vlc
  • psilokos/vlc
  • xjbeta/vlc
  • jahan/vlc
  • 1480c1/vlc
  • amanchande/vlc
  • aaqib/vlc
  • rist/vlc
  • apol/vlc
  • mindfreeze/vlc
  • alexandre-janniaux/vlc
  • sandsmark/vlc
  • jagannatharjun/vlc
  • gsoc/gsoc2020/matiaslgonzalez/vlc
  • gsoc/gsoc2020/jagannatharjun/vlc
  • mstorsjo/vlc
  • gsoc/gsoc2020/vedenta/vlc
  • gsoc/gsoc2020/arnav-ishaan/vlc
  • gsoc/gsoc2020/andreduong/vlc
  • fuzun/vlc
  • gsoc/gsoc2020/vatsin/vlc
  • gsoc/gsoc2020/sagid/vlc
  • yaron/vlc
  • Phoenix/vlc
  • Garf/vlc
  • ePiratWorkarounds/vlc
  • tguillem/vlc
  • jnqnfe/vlc
  • mdc/vlc
  • Vedaa/vlc
  • rasa/vlc
  • quink/vlc
  • yealo/vlc
  • aleksey_ak/vlc
  • ePirat/vlc
  • ilya.yanok/vlc
  • asenat/vlc
  • m/vlc
  • bunjee/vlc
  • BLumia/vlc
  • sagudev/vlc
  • hamedmonji30/vlc
  • nullgemm/vlc
  • DivyamAhuja/vlc
  • thesamesam/vlc
  • dag7/vlc
  • snehil101/vlc
  • haasn/vlc
  • jbk/vlc
  • ValZapod/vlc
  • mfkl/vlc
  • WangChuan/vlc
  • core1024/vlc
  • GhostVaibhav/vlc
  • dfuhrmann/vlc
  • davide.prade/vlc
  • tmatth/vlc
  • Courmisch/vlc
  • zouya/vlc
  • hpi/vlc
  • EwoutH/vlc
  • aleung27/vlc
  • hengwu0/vlc
  • saladin/vlc
  • ashuio/vlc
  • richselwood/vlc
  • verma16Ayush/vlc
  • chemicalflash/vlc
  • PoignardAzur/vlc
  • huangjieNT/vlc
  • Blake-Haydon/vlc
  • AnuthaDev/vlc
  • gsoc/gsoc2021/mpd/vlc
  • nicolas_lequec/vlc
  • sambassaly/vlc
  • thresh/vlc
  • bonniegong/vlc
  • myaashish/vlc
  • stavros.vagionitis/vlc
  • ileoo/vlc
  • louis-santucci/vlc
  • cchristiansen/vlc
  • sabyasachi07/vlc
  • AbduAmeen/vlc
  • ashishb0410/vlc
  • urbanhusky/vlc
  • davidepietrasanta/vlc
  • riksleutelstad/vlc
  • jeremyVignelles/vlc
  • komh/vlc
  • iamjithinjohn/vlc
  • JohannesKauffmann/vlc2
  • kunglao/vlc
  • natzberg/vlc
  • jill/vlc
  • cwendling/vlc
  • adufou/vlc
  • ErwanAirone/vlc
  • HasinduDilshan10/vlc
  • vagrantc/vlc
  • rafiv/macos-bigsur-icon
  • Aymeriic/vlc
  • saranshg20/vlc
  • metzlove24/vlc
  • linkfanel/vlc
  • Ds886/vlc
  • metehan-arslan/vlc
  • Skantes/vlc
  • kgsandundananjaya96/vlc
  • mitchcapper/vlc
  • advaitgupta/vlc
  • StefanBruens/vlc
  • ratajs/vlc
  • T.M.F.B.3761/vlc
  • m222059/vlc
  • casemerrick/vlc
  • joshuaword2alt/vlc
  • sjwaddy/vlc
  • dima/vlc
  • Ybalrid/vlc
  • umxprime/vlc
  • eschmidt/vlc
  • vannieuwenhuysenmichelle/vlc
  • badcf00d/vlc
  • wesinator/vlc
  • louis/vlc
  • xqq/vlc
  • EmperorYP7/vlc
  • NicoLiam/vlc
  • loveleen/vlc
  • rofferom/vlc
  • rbultje/vlc
  • TheUnamed/vlc
  • pratiksharma341/vlc
  • Saurab17/vlc
  • purist.coder/vlc
  • Shuicheng/vlc
  • mdrrubel292/vlc
  • silverbleu00/vlc
  • metif12/vlc
  • asher-m/vlc
  • jeffk/vlc
  • Brandonbr1/vlc
  • beautyyuyanli/vlc
  • rego21/vlc
  • muyangren907/vlc
  • collectionbylawrencejason/vlc
  • evelez/vlc
  • GSMgeeth/vlc
  • Oneric/vlc
  • TJ5/vlc
  • XuanTung95/vlc
  • darrenjenny21/vlc
  • Trenly/vlc
  • RockyTDR/vlc
  • mjakubowski/vlc
  • caprica/vlc
  • ForteFrankie/vlc
  • seannamiller19/vlc
  • junlon2006/vlc
  • kiwiren6666/vlc
  • iuseiphonexs/vlc
  • fenngtun/vlc
  • Rajdutt999/vlc
  • typx/vlc
  • leon.vitanos/vlc
  • robertogarci0938/vlc
  • gsoc/gsoc2022/luc65r/vlc-mpd
  • skeller/vlc
  • MCJack123/vlc
  • luc65r/vlc-mpd
  • popov895/vlc
  • claucambra/vlc
  • brad/vlc
  • matthewmurua88/vlc
  • Tomas8874/vlc
  • philenotfound/vlc
  • makita-do3/vlc
  • LZXCorp/vlc
  • mar0x/vlc
  • senojetkennedy0102/vlc
  • shaneb243/vlc
  • ahmadbader/vlc
  • rajduttcse26/vlc-audio-filters
  • Juniorzito8415/vlc
  • achernyakov/vlc
  • lucasjetgroup/vlc
  • pupdoggy666/vlc
  • gmde9363/vlc
  • alexnwayne/vlc
  • bahareebrahimi781/vlc
  • hamad633666/vlc
  • umghof3112/vlc
  • joe0199771874/vlc
  • Octocats66666666/vlc
  • jjm_223/vlc
  • btech10110.19/vlc
  • sunnykfc028/vlc-audio-filters
  • loic/vlc
  • nguyenminhducmx1/vlc
  • JanekKrueger/vlc
  • bstubbington2/vlc
  • rcombs/vlc
  • Ordissimo/vlc
  • king7532/vlc
  • noobsauce101/vlc
  • schong0525/vlc
  • myQwil/vlc
  • apisbg91/vlc
  • geeboy0101017/vlc
  • kim.faughey/vlc
  • nurupo/vlc
  • yyusea/vlc
  • 0711235879.khco/vlc
  • ialo/vlc
  • iloveyeye2/vlc
  • gdtdftdqtd/vlc
  • leandroconsiglio/vlc
  • AndyHTML2012/vlc
  • ncz/vlc
  • lucenticus/vlc
  • knr1931/vlc
  • kjoonlee/vlc
  • chandrakant100/vlc-qt
  • johge42/vlc
  • polter/vlc
  • hexchain/vlc
  • Tushwrld/vlc
  • mztea928/vlc
  • jbelloncastro/vlc
  • alvinhochun/vlc
  • ghostpiratecrow/vlc
  • ujjwaltwitx/vlc
  • alexsonarin06/vlc
  • adrianbon76/vlc
  • altsod/vlc
  • damien.lucas44/vlc
  • dmytrivtaisa/vlc
  • utk202/vlc
  • aaxhrj/vlc
  • thomas.hermes/vlc
  • structurenewworldorder/vlc
  • slomo/vlc
  • wantlamy/vlc
  • musc.o3cminc/vlc
  • thebarshablog/vlc
  • kerrick/vlc
  • kratos142518/vlc
  • leogps/vlc
  • vacantron/vlc
  • luna_koly/vlc
  • Ratio2/vlc
  • anuoshemohammad/vlc
  • apsun/vlc
  • aaa1115910/vlc
  • alimotmoyo/vlc
  • Ambossmann/vlc
  • Sam-LearnsToCode/vlc
  • Chilledheart/vlc
  • Labnann/vlc
  • ktcoooot1/vlc
  • mohit-marathe/vlc
  • johnddx/vlc
  • manstabuk/vlc
  • Omar-ahmed314/vlc
  • vineethkm/vlc
  • 9Enemi86/vlc
  • radoslav.m.panteleev/vlc
  • ashishami2002/vlc
  • Corbax/vlc
  • firnasahmed/vlc
  • pelayarmalam4/vlc
  • c0ff330k/vlc
  • shikhindahikar/vlc
  • l342723951/vlc
  • christianschwandner/vlc
  • douniwan5788/vlc
  • 7damian7/vlc
  • ferdnyc/vlc
  • f.ales1/vlc
  • pandagby/vlc
  • BaaBaa/vlc
  • jewe37/vlc
  • w00drow/vlc
  • russelltg/vlc
  • ironicallygod/vlc
  • soumyaDghosh/vlc
  • linzihao1999/vlc
  • deyayush6/vlc
  • mibi88/vlc
  • newabdallah10/vlc
  • jhorbincolombia/vlc
  • rimvihaqueshupto/vlc
  • andrewkhon98/vlc
  • fab78/vlc
  • lapaz17/vlc
  • amanna13/vlc
  • mdakram28/vlc
  • 07jw1980/vlc
  • sohamgupta/vlc
  • Eson-Jia1/vlc
  • Sumou/vlc
  • vikram-kangotra/vlc
  • chalice191/vlc
  • olivercalder/vlc
  • aaasg4001/vlc
  • zipdox/vlc
  • kwizart/vlc
  • Dragon-S/vlc
  • jdemeule/vlc
  • gabriel_lt/vlc
  • locutusofborg/vlc
  • sammirata/vlc-librist
  • another/vlc
  • Benjamin_Loison/vlc
  • ahmedmoselhi/vlc
  • petergaal/vlc
  • huynhsontung/vlc
  • dariusmihut/vlc
  • tvermaashutosh/vlc
  • buti/vlc
  • Niram7777/vlc
  • rohan-here/vlc
  • balaji-sivasakthi/vlc
  • rlindner81/vlc
  • Kakadus/vlc
  • djain/vlc
  • ABBurmeister/vlc
  • craighuggins/vlc
  • orbea/vlc
  • maxos/vlc
  • aakarshmj/vlc
  • kblaschke/vlc
  • ankitm/vlc
  • advait-0/vlc
  • mohak2003/vlc
  • yselkowitz/vlc
  • AZM999/vlc-azm
  • andrey.turkin/vlc
  • Disha-Baghel/vlc
  • nowrep/vlc
  • Apeng/vlc
  • Choucroute_melba/vlc
  • autra/vlc
  • eclipseo/vlc
  • fhuber/vlc
  • olafhering/vlc
  • sdasda7777/vlc
  • 1div0/vlc
  • skosnits/vlc-extended-playlist-support
  • dnicolson/vlc
  • Timshel/vlc
  • octopols/vlc
  • MangalK/vlc
  • nima64/vlc
  • misawai/vlc
  • Alexander-Wilms/vlc
  • Maxime2/vlc-fork-for-visualizer
  • ww/vlc
  • jeske/vlc
  • sgross-emlix/vlc
  • morenonatural/vlc
  • freakingLovesVLC/vlc
  • borisgolovnev/vlc
  • mpromonet/vlc
  • diogo.simao-marques/vlc
  • masstock/vlc
  • pratikpatel8982/vlc
  • hugok79/vlc
  • longervision/vlc
  • abhiudaysurya/vlc
  • rishabhgarg/vlc
  • tumic/vlc
  • cart/vlc
  • shubham442/vlc
  • Aditya692005/vlc
  • sammirata/vlc4
  • syrykh/vlc
  • Vvorcun/macos-new-icon
  • AyaanshC/vlc
  • nasso/vlc
  • Quark/vlc
  • sebastinas/vlc
  • rhstone/vlc
  • talregev/vlc
  • Managor/vlc
403 results
Show changes
Commits on Source (4)
......@@ -237,6 +237,8 @@ libqt_plugin_la_SOURCES = \
menus/menus.cpp menus/menus.hpp \
network/mediatreelistener.cpp \
network/mediatreelistener.hpp \
network/devicesourceprovider.cpp \
network/devicesourceprovider.hpp \
network/networkdevicemodel.cpp \
network/networkdevicemodel.hpp \
network/networksourcesmodel.cpp \
......@@ -275,6 +277,7 @@ libqt_plugin_la_SOURCES = \
util/asynctask.hpp \
util/audio_device_model.cpp \
util/audio_device_model.hpp \
util/workerthreadset.hpp util/workerthreadset.cpp \
util/base_model.hpp util/base_model_p.hpp util/base_model.cpp \
util/color_scheme_model.cpp util/color_scheme_model.hpp \
util/color_svg_image_provider.cpp util/color_svg_image_provider.hpp \
......@@ -422,6 +425,7 @@ nodist_libqt_plugin_la_SOURCES = \
menus/custom_menus.moc.cpp \
menus/qml_menu_wrapper.moc.cpp \
menus/menus.moc.cpp \
network/devicesourceprovider.moc.cpp \
network/networkdevicemodel.moc.cpp \
network/networksourcesmodel.moc.cpp \
network/networkmediamodel.moc.cpp \
......@@ -440,6 +444,7 @@ nodist_libqt_plugin_la_SOURCES = \
playlist/playlist_model.moc.cpp \
util/asynctask.moc.cpp \
util/audio_device_model.moc.cpp \
util/workerthreadset.moc.cpp \
util/base_model.moc.cpp \
util/color_scheme_model.moc.cpp \
util/color_svg_image_provider.moc.cpp \
......
......@@ -34,6 +34,7 @@
#include "compositor.hpp"
#include "util/renderer_manager.hpp"
#include "util/csdbuttonmodel.hpp"
#include "util/workerthreadset.hpp"
#include "widgets/native/customwidgets.hpp" // qtEventToVLCKey, QVLCStackedWidget
#include "util/qt_dirs.hpp" // toNativeSeparators
......@@ -522,6 +523,16 @@ inline void MainCtx::initSystray()
createSystray();
}
WorkerThreadSet* MainCtx::workersThreads() const
{
if (!m_workersThreads)
{
m_workersThreads.reset( new WorkerThreadSet );
}
return m_workersThreads.get();
}
void MainCtx::setMediaLibraryVisible( bool visible )
{
if (m_mediaLibraryVisible == visible)
......
......@@ -67,6 +67,7 @@ class VideoSurfaceProvider;
class ControlbarProfileModel;
class SearchCtx;
class SortCtx;
class WorkerThreadSet;
namespace vlc {
namespace playlist {
......@@ -277,6 +278,8 @@ public:
Q_INVOKABLE bool useXmasCone() const;
WorkerThreadSet *workersThreads() const;
protected:
/* Systray */
void createSystray();
......@@ -354,6 +357,8 @@ protected:
SearchCtx* m_search = nullptr;
SortCtx* m_sort = nullptr;
mutable std::unique_ptr<WorkerThreadSet> m_workersThreads;
public slots:
void toggleUpdateSystrayMenu();
void showUpdateSystrayMenu();
......
......@@ -102,6 +102,7 @@ moc_headers = files(
'menus/custom_menus.hpp',
'menus/qml_menu_wrapper.hpp',
'menus/menus.hpp',
'network/devicesourceprovider.hpp',
'network/networkdevicemodel.hpp',
'network/networksourcesmodel.hpp',
'network/networkmediamodel.hpp',
......@@ -120,6 +121,7 @@ moc_headers = files(
'playlist/playlist_model.hpp',
'util/asynctask.hpp',
'util/audio_device_model.hpp',
'util/workerthreadset.hpp',
'util/base_model.hpp',
'util/color_scheme_model.hpp',
'util/color_svg_image_provider.hpp',
......@@ -363,6 +365,8 @@ some_sources = files(
'menus/menus.hpp',
'network/mediatreelistener.cpp',
'network/mediatreelistener.hpp',
'network/devicesourceprovider.cpp',
'network/devicesourceprovider.hpp',
'network/networkdevicemodel.cpp',
'network/networkdevicemodel.hpp',
'network/networksourcesmodel.cpp',
......@@ -405,6 +409,8 @@ some_sources = files(
'util/asynctask.hpp',
'util/audio_device_model.cpp',
'util/audio_device_model.hpp',
'util/workerthreadset.cpp',
'util/workerthreadset.hpp',
'util/base_model.cpp',
'util/base_model.hpp',
'util/base_model_p.hpp',
......
/*****************************************************************************
* Copyright (C) 2024 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* ( at your option ) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "devicesourceprovider.hpp"
#include "networkmediamodel.hpp"
//handle discovery events from the media source provider
struct DeviceSourceProvider::ListenerCb : public MediaTreeListener::MediaTreeListenerCb {
ListenerCb(DeviceSourceProvider* provider, NetworkDeviceModel::MediaSourcePtr mediaSource)
: provider(provider)
, mediaSource(std::move(mediaSource))
{}
inline void onItemPreparseEnded( MediaTreePtr, input_item_node_t *, enum input_item_preparse_status ) override final {}
void onItemCleared( MediaTreePtr tree, input_item_node_t* node ) override
{
if (node != &tree->root)
return;
refresh( node->pp_children, node->i_children, true);
}
void onItemAdded( MediaTreePtr tree, input_item_node_t* parent, input_item_node_t *const children[], size_t count ) override
{
if (parent != &tree->root)
return;
refresh( children, count, false );
}
void onItemRemoved( MediaTreePtr tree, input_item_node_t * node, input_item_node_t *const children[], size_t count ) override
{
if (node != &tree->root)
return;
std::vector<SharedInputItem> itemList;
itemList.reserve( count );
for ( auto i = 0u; i < count; ++i )
itemList.emplace_back( children[i]->p_item );
QMetaObject::invokeMethod(provider, [provider = this->provider,
itemList = std::move(itemList),
mediaSource = this->mediaSource]()
{
provider->removeItems(itemList, mediaSource);
});
}
void refresh(input_item_node_t * const children[], size_t count,
bool clear)
{
std::vector<SharedInputItem> itemList;
itemList.reserve(count);
for (size_t i = 0; i < count; i++)
itemList.emplace_back(children[i]->p_item);
QMetaObject::invokeMethod(provider, [provider = this->provider,
itemList = std::move(itemList),
mediaSource = this->mediaSource, clear]()
{
provider->addItems(itemList, mediaSource, clear);
});
}
DeviceSourceProvider *provider;
MediaSourcePtr mediaSource;
};
DeviceSourceProvider::DeviceSourceProvider(NetworkDeviceModel::SDCatType sdSource
, const QString &sourceName, QObject *parent)
: QObject(parent)
, m_sdSource {sdSource}
, m_sourceName {sourceName}
{
}
void DeviceSourceProvider::init(qt_intf_t *intf)
{
using SourceMetaPtr = std::unique_ptr<vlc_media_source_meta_list_t,
decltype( &vlc_media_source_meta_list_Delete )>;
auto libvlc = vlc_object_instance(intf);
auto provider = vlc_media_source_provider_Get( libvlc );
SourceMetaPtr providerList( vlc_media_source_provider_List(
provider,
static_cast<services_discovery_category_e>(m_sdSource) ),
&vlc_media_source_meta_list_Delete );
if (!providerList)
{
emit failed();
return;
}
size_t nbProviders = vlc_media_source_meta_list_Count( providerList.get() );
for ( auto i = 0u; i < nbProviders; ++i )
{
auto meta = vlc_media_source_meta_list_Get( providerList.get(), i );
const QString sourceName = qfu( meta->name );
if ( m_sourceName != '*' && m_sourceName != sourceName )
continue;
m_name += m_name.isEmpty() ? qfu( meta->longname ) : ", " + qfu( meta->longname );
MediaSourcePtr mediaSource(
vlc_media_source_provider_GetMediaSource(provider, meta->name)
, false );
if ( mediaSource == nullptr )
continue;
std::unique_ptr<MediaTreeListener> l{ new MediaTreeListener(
MediaTreePtr{ mediaSource->tree },
std::make_unique<DeviceSourceProvider::ListenerCb>(this, mediaSource) ) };
if ( l->listener == nullptr )
break;
m_mediaSources.push_back( std::move( mediaSource ) );
m_listeners.push_back( std::move( l ) );
}
if ( !m_name.isEmpty() )
emit nameUpdated( m_name );
if ( !m_listeners.empty() )
emit itemsUpdated( m_items );
else
emit failed();
}
void DeviceSourceProvider::addItems(const std::vector<SharedInputItem> &inputList,
const MediaSourcePtr &mediaSource, const bool clear)
{
bool dataChanged = false;
if (clear)
{
const qsizetype removed = m_items.removeIf([&mediaSource](const NetworkDeviceItemPtr &item)
{
return item->mediaSource == mediaSource;
});
if (removed > 0)
dataChanged = true;
}
for (const SharedInputItem & inputItem : inputList)
{
auto newItem = std::make_shared<NetworkDeviceItem>(inputItem, mediaSource);
auto it = m_items.find(newItem);
if (it != m_items.end())
{
(*it)->mrls.push_back(std::make_pair(newItem->mainMrl, mediaSource));
}
else
{
m_items.insert(std::move(newItem));
dataChanged = true;
}
}
if (dataChanged)
{
emit itemsUpdated(m_items);
}
}
void DeviceSourceProvider::removeItems(const std::vector<SharedInputItem> &inputList,
const MediaSourcePtr &mediaSource)
{
bool dataChanged = false;
for (const SharedInputItem& p_item : inputList)
{
auto oldItem = std::make_shared<NetworkDeviceItem>(p_item, mediaSource);
NetworkDeviceItemSet::iterator it = m_items.find(oldItem);
if (it != m_items.end())
{
bool found = false;
const NetworkDeviceItemPtr& item = *it;
if (item->mrls.size() > 1)
{
auto mrlIt = std::find_if(
item->mrls.begin(), item->mrls.end(),
[&oldItem]( const std::pair<QUrl, MediaSourcePtr>& mrl ) {
return mrl.first.matches(oldItem->mainMrl, QUrl::StripTrailingSlash)
&& mrl.second == oldItem->mediaSource;
});
if ( mrlIt != item->mrls.end() )
{
found = true;
item->mrls.erase( mrlIt );
}
}
if (!found)
{
m_items.erase(it);
dataChanged = true;
}
}
}
if (dataChanged)
emit itemsUpdated(m_items);
}
/*****************************************************************************
* Copyright (C) 2024 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* ( at your option ) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#pragma once
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "qt.hpp"
#include <QString>
#include <QSet>
#include <vector>
#include "networkdevicemodel.hpp"
#include "mediatreelistener.hpp"
//represents an entry of the model
struct NetworkDeviceItem
{
NetworkDeviceItem(const SharedInputItem& item, const NetworkDeviceModel::MediaSourcePtr& mediaSource)
: name(qfu(item->psz_name))
, mainMrl(QUrl::fromEncoded(item->psz_uri))
, protocol(mainMrl.scheme())
, type( static_cast<NetworkDeviceModel::ItemType>(item->i_type))
, mediaSource(mediaSource)
, inputItem(item)
{
id = qHash(name) ^ qHash(protocol);
mrls.push_back(std::make_pair(mainMrl, mediaSource));
char* artworkUrl = input_item_GetArtworkURL(inputItem.get());
if (artworkUrl)
{
artwork = QString::fromUtf8(artworkUrl);
free(artworkUrl);
}
}
uint id;
QString name;
QUrl mainMrl;
std::vector<std::pair<QUrl, NetworkDeviceModel::MediaSourcePtr>> mrls;
QString protocol;
NetworkDeviceModel::ItemType type;
NetworkDeviceModel::MediaSourcePtr mediaSource;
SharedInputItem inputItem;
QString artwork;
};
using NetworkDeviceItemPtr =std::shared_ptr<NetworkDeviceItem>;
static inline bool operator == (const NetworkDeviceItemPtr& a, const NetworkDeviceItemPtr& b) noexcept
{
return a->id == b->id
&& QString::compare(a->name, b->name, Qt::CaseInsensitive) == 0
&& QString::compare(a->protocol, b->protocol, Qt::CaseInsensitive) == 0;
}
static inline std::size_t qHash(const NetworkDeviceItemPtr& s, size_t = 0) noexcept
{
return s->id;
}
using NetworkDeviceItemSet = QSet<NetworkDeviceItemPtr>;
class DeviceSourceProvider : public QObject
{
Q_OBJECT
public:
using MediaSourcePtr = NetworkDeviceModel::MediaSourcePtr;
DeviceSourceProvider(NetworkDeviceModel::SDCatType sdSource,
const QString &sourceName,
QObject *parent = nullptr);
void init(qt_intf_t *intf);
signals:
void failed();
void nameUpdated( QString name );
void itemsUpdated( NetworkDeviceItemSet items );
private:
struct ListenerCb;
void addItems(const std::vector<SharedInputItem>& inputList,
const MediaSourcePtr& mediaSource,
bool clear);
void removeItems(const std::vector<SharedInputItem>& inputList,
const MediaSourcePtr& mediaSource);
NetworkDeviceModel::SDCatType m_sdSource;
QString m_sourceName; // '*' -> all sources
QString m_name; // source long name
NetworkDeviceItemSet m_items;
// destruction of listeners may cause destruction of source 'MediaSource'
// maintain a seperate reference of MediaSources to fix cyclic free
std::vector<MediaSourcePtr> m_mediaSources;
std::vector<std::unique_ptr<MediaTreeListener>> m_listeners;
};
......@@ -17,12 +17,13 @@
*****************************************************************************/
#include <unordered_set>
#include <QTimer>
#include "maininterface/mainctx.hpp"
#include "util/workerthreadset.hpp"
#include "networkdevicemodel.hpp"
#include "devicesourceprovider.hpp"
#include "networkmediamodel.hpp"
#include "mediatreelistener.hpp"
#include "playlist/media.hpp"
#include "playlist/playlist_controller.hpp"
......@@ -33,63 +34,6 @@
namespace
{
//represents an entry of the model
struct NetworkDeviceItem
{
NetworkDeviceItem(const SharedInputItem& item, const NetworkDeviceModel::MediaSourcePtr& mediaSource)
: name(qfu(item->psz_name))
, mainMrl(QUrl::fromEncoded(item->psz_uri))
, protocol(mainMrl.scheme())
, type( static_cast<NetworkDeviceModel::ItemType>(item->i_type))
, mediaSource(mediaSource)
, inputItem(item)
{
id = qHash(name) ^ qHash(protocol);
mrls.push_back(std::make_pair(mainMrl, mediaSource));
char* artworkUrl = input_item_GetArtworkURL(inputItem.get());
if (artworkUrl)
{
artwork = QString::fromUtf8(artworkUrl);
free(artworkUrl);
}
}
uint id;
QString name;
QUrl mainMrl;
std::vector<std::pair<QUrl, NetworkDeviceModel::MediaSourcePtr>> mrls;
QString protocol;
NetworkDeviceModel::ItemType type;
NetworkDeviceModel::MediaSourcePtr mediaSource;
SharedInputItem inputItem;
QString artwork;
};
using NetworkDeviceItemPtr = std::shared_ptr<NetworkDeviceItem>;
using NetworkDeviceModelLoader = LocalListCacheLoader<NetworkDeviceItemPtr>;
//hash and compare function for std::unordered_set
struct NetworkDeviceItemHash
{
std::size_t operator()(const NetworkDeviceItemPtr& s) const noexcept
{
return s->id;
}
};
struct NetworkDeviceItemEqual
{
bool operator()(const NetworkDeviceItemPtr& a, const NetworkDeviceItemPtr& b) const noexcept
{
return a->id == b->id
&& QString::compare(a->name, b->name, Qt::CaseInsensitive) == 0
&& QString::compare(a->protocol, b->protocol, Qt::CaseInsensitive) == 0;
}
};
using NetworkDeviceItemSet = std::unordered_set<NetworkDeviceItemPtr, NetworkDeviceItemHash, NetworkDeviceItemEqual>;
bool itemMatchPattern(const NetworkDeviceItemPtr& a, const QString& pattern)
{
return a->name.contains(pattern, Qt::CaseInsensitive);
......@@ -129,22 +73,6 @@ bool descendingName(const NetworkDeviceItemPtr& a, const NetworkDeviceItemPtr& b
}
//handle discovery events from the media source provider
struct NetworkDeviceModel::ListenerCb : public MediaTreeListener::MediaTreeListenerCb {
ListenerCb(NetworkDeviceModel* model, MediaSourcePtr mediaSource)
: model(model)
, mediaSource(std::move(mediaSource))
{}
void onItemCleared( MediaTreePtr tree, input_item_node_t* node ) override;
void onItemAdded( MediaTreePtr tree, input_item_node_t* parent, input_item_node_t *const children[], size_t count ) override;
void onItemRemoved( MediaTreePtr tree, input_item_node_t * node, input_item_node_t *const children[], size_t count ) override;
inline void onItemPreparseEnded( MediaTreePtr, input_item_node_t *, enum input_item_preparse_status ) override {}
NetworkDeviceModel *model;
MediaSourcePtr mediaSource;
};
// ListCache specialisation
template<>
......@@ -156,6 +84,8 @@ bool ListCache<NetworkDeviceItemPtr>::compareItems(const NetworkDeviceItemPtr& a
// NetworkDeviceModelPrivate
using NetworkDeviceModelLoader = LocalListCacheLoader<NetworkDeviceItemPtr>;
class NetworkDeviceModelPrivate
: public LocalListBaseModelPrivate<NetworkDeviceItemPtr>
{
......@@ -163,7 +93,6 @@ class NetworkDeviceModelPrivate
public:
NetworkDeviceModelPrivate(NetworkDeviceModel * pub)
: LocalListBaseModelPrivate<NetworkDeviceItemPtr>(pub)
, m_items(0, NetworkDeviceItemHash{}, NetworkDeviceItemEqual{})
{}
NetworkDeviceModelLoader::ItemCompare getSortFunction() const override
......@@ -191,54 +120,64 @@ public:
if (m_qmlInitializing || !q->m_ctx || q->m_sdSource == NetworkDeviceModel::CAT_UNDEFINED || q->m_sourceName.isEmpty())
return false;
auto libvlc = vlc_object_instance(q->m_ctx->getIntf());
m_listeners.clear();
m_items.clear();
if (m_sources)
{
q->disconnect( m_sources );
m_sources->deleteLater();
m_sources = nullptr;
}
q->m_name = QString {};
emit q->nameChanged();
auto provider = vlc_media_source_provider_Get( libvlc );
m_sources = new DeviceSourceProvider( q->m_sdSource, q->m_sourceName );
q->m_ctx->workersThreads()->assignToWorkerThread( m_sources );
using SourceMetaPtr = std::unique_ptr<vlc_media_source_meta_list_t,
decltype( &vlc_media_source_meta_list_Delete )>;
// make sure we're not releasing resources on main thread
// by clearing copies of model before source provider
QObject::connect(q, &QObject::destroyed, m_sources, [sources = m_sources]()
{
sources->deleteLater();
});
SourceMetaPtr providerList( vlc_media_source_provider_List( provider, static_cast<services_discovery_category_e>(q->m_sdSource) ),
&vlc_media_source_meta_list_Delete );
if ( providerList == nullptr )
return false;
QObject::connect(m_sources, &DeviceSourceProvider::failed, q,
[this]()
{
m_items.clear();
auto nbProviders = vlc_media_source_meta_list_Count( providerList.get() );
m_revision += 1;
invalidateCache();
});
for ( auto i = 0u; i < nbProviders; ++i )
QObject::connect(m_sources, &DeviceSourceProvider::nameUpdated, q,
[q](QString name)
{
auto meta = vlc_media_source_meta_list_Get( providerList.get(), i );
const QString sourceName = qfu( meta->name );
if ( q->m_sourceName != '*' && q->m_sourceName != sourceName )
continue;
q->m_name += q->m_name.isEmpty() ? qfu( meta->longname ) : ", " + qfu( meta->longname );
q->m_name = name;
emit q->nameChanged();
});
MediaSourcePtr mediaSource(
vlc_media_source_provider_GetMediaSource(provider, meta->name)
, false );
if ( mediaSource == nullptr )
continue;
std::unique_ptr<MediaTreeListener> l{ new MediaTreeListener(
MediaTreePtr{ mediaSource->tree },
std::make_unique<NetworkDeviceModel::ListenerCb>(q, MediaSourcePtr{ mediaSource }) ) };
if ( l->listener == nullptr )
return false;
m_listeners.push_back( std::move( l ) );
}
QObject::connect(m_sources, &DeviceSourceProvider::itemsUpdated, q,
[this](NetworkDeviceItemSet items)
{
m_items = items;
m_revision += 1;
invalidateCache();
});
QMetaObject::invokeMethod(m_sources,
[sources = this->m_sources, intf = q->m_ctx->getIntf()]()
{
sources->init( intf );
});
//service discovery don't notify preparse end
m_loading = false;
emit q->loadingChanged();
return m_listeners.empty() == false;
return true;
}
const NetworkDeviceItem* getItemForRow(int row) const
......@@ -249,96 +188,6 @@ public:
return nullptr;
}
void addItems(
const std::vector<SharedInputItem>& inputList,
const MediaSourcePtr& mediaSource,
bool clear)
{
bool dataChanged = false;
if (clear)
{
//std::remove_if doesn't work with unordered_set
//due to iterators being const
for (auto it = m_items.begin(); it != m_items.end(); )
{
it = std::find_if(
it, m_items.end(),
[&mediaSource](const NetworkDeviceItemPtr& item) {
return item->mediaSource == mediaSource;
});
if (it != m_items.end())
it = m_items.erase(it);
}
dataChanged = true;
}
for (const SharedInputItem & inputItem : inputList)
{
auto newItem = std::make_shared<NetworkDeviceItem>(inputItem, mediaSource);
auto it = m_items.find(newItem);
if (it != m_items.end())
{
(*it)->mrls.push_back(std::make_pair(newItem->mainMrl, mediaSource));
}
else
{
m_items.emplace(std::move(newItem));
dataChanged = true;
}
}
if (dataChanged)
{
m_revision += 1;
invalidateCache();
}
}
void removeItems(const std::vector<SharedInputItem>& inputList, const MediaSourcePtr& mediaSource)
{
bool dataChanged = false;
for (const SharedInputItem& p_item : inputList)
{
auto oldItem = std::make_shared<NetworkDeviceItem>(p_item, mediaSource);
NetworkDeviceItemSet::iterator it = m_items.find(oldItem);
if (it != m_items.end())
{
bool found = false;
const NetworkDeviceItemPtr& item = *it;
if (item->mrls.size() > 1)
{
auto mrlIt = std::find_if(
item->mrls.begin(), item->mrls.end(),
[&oldItem]( const std::pair<QUrl, MediaSourcePtr>& mrl ) {
return mrl.first.matches(oldItem->mainMrl, QUrl::StripTrailingSlash)
&& mrl.second == oldItem->mediaSource;
});
if ( mrlIt != item->mrls.end() )
{
found = true;
item->mrls.erase( mrlIt );
}
}
if (!found)
{
m_items.erase(it);
dataChanged = true;
}
}
}
if (dataChanged)
{
m_revision += 1;
invalidateCache();
}
}
public: //LocalListCacheLoader::ModelSource
std::vector<NetworkDeviceItemPtr> getModelData(const QString& pattern) const override
{
......@@ -363,7 +212,7 @@ public: //LocalListCacheLoader::ModelSource
public:
NetworkDeviceItemSet m_items;
std::vector<std::unique_ptr<MediaTreeListener>> m_listeners;
QPointer<DeviceSourceProvider> m_sources {};
};
NetworkDeviceModel::NetworkDeviceModel( QObject* parent )
......@@ -576,55 +425,3 @@ QVariantList NetworkDeviceModel::getItemsForIndexes(const QModelIndexList & inde
return items;
}
// NetworkDeviceModel::ListenerCb implementation
void NetworkDeviceModel::ListenerCb::onItemCleared( MediaTreePtr tree, input_item_node_t* node )
{
if (node != &tree->root)
return;
model->refreshDeviceList( mediaSource, node->pp_children, node->i_children, true );
}
void NetworkDeviceModel::ListenerCb::onItemAdded( MediaTreePtr tree, input_item_node_t* parent,
input_item_node_t *const children[],
size_t count )
{
if (parent != &tree->root)
return;
model->refreshDeviceList( mediaSource, children, count, false );
}
void NetworkDeviceModel::ListenerCb::onItemRemoved( MediaTreePtr tree, input_item_node_t* node,
input_item_node_t *const children[],
size_t count )
{
if (node != &tree->root)
return;
std::vector<SharedInputItem> itemList;
itemList.reserve( count );
for ( auto i = 0u; i < count; ++i )
itemList.emplace_back( children[i]->p_item );
QMetaObject::invokeMethod(model, [model=model, mediaSource=mediaSource, itemList=std::move(itemList)]() {
model->d_func()->removeItems(itemList, mediaSource);
}, Qt::QueuedConnection);
}
void NetworkDeviceModel::refreshDeviceList(MediaSourcePtr mediaSource,
input_item_node_t * const children[], size_t count,
bool clear)
{
std::vector<SharedInputItem> itemList;
itemList.reserve(count);
for (size_t i = 0; i < count; i++)
itemList.emplace_back(children[i]->p_item);
QMetaObject::invokeMethod(this, [this, clear, itemList = std::move(itemList), mediaSource]() mutable
{
Q_D(NetworkDeviceModel);
d->addItems(itemList, mediaSource, clear);
}, Qt::QueuedConnection);
}
......@@ -134,7 +134,6 @@ private:
QString m_sourceName; // '*' -> all sources
QString m_name; // source long name
struct ListenerCb;
Q_DECLARE_PRIVATE(NetworkDeviceModel)
};
......
/*****************************************************************************
* Copyright (C) 2024 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* ( at your option ) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "workerthreadset.hpp"
#include <QTimer>
WorkerThreadSet::~WorkerThreadSet()
{
for (auto worker : workers)
{
if (!worker.thread)
continue;
finish( worker );
}
}
void WorkerThreadSet::assignToWorkerThread(QObject *obj)
{
auto worker = reserve();
obj->moveToThread( worker );
QObject::connect(obj, &QObject::destroyed, worker, [this, worker]()
{
unreserve( worker );
});
}
void WorkerThreadSet::finish(Worker &worker)
{
worker.thread->quit();
worker.thread->wait();
delete worker.thread;
}
QThread *WorkerThreadSet::reserve()
{
auto itr = std::min_element(std::begin(workers)
, std::end(workers)
, [](const Worker &l, const Worker &r)
{
return l.load < r.load;
});
assert(itr != std::end(workers));
if (!itr->thread)
{
itr->thread = new QThread;
itr->thread->start();
}
itr->load++;
itr->inactiveTime.invalidate();
return itr->thread;
}
void WorkerThreadSet::unreserve(QThread *thread)
{
auto itr = std::find_if(std::begin(workers)
, std::end(workers)
, [thread](const Worker &i) { return i.thread == thread; });
if (itr == std::end(workers)) return; // impossible?
const int load = --itr->load;
if (load != 0)
return;
itr->inactiveTime.start();
QTimer::singleShot(CLEANUP_TIMEOUT, this, &WorkerThreadSet::cleanupInactiveWorker);
}
void WorkerThreadSet::cleanupInactiveWorker()
{
for (auto &worker : workers)
{
if ((worker.load == 0)
&& worker.inactiveTime.hasExpired(MAX_INACTIVE_TIME)
&& worker.thread)
{
finish( worker );
worker.thread = nullptr;
}
}
}
/*****************************************************************************
* Copyright (C) 2024 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* ( at your option ) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#pragma once
#include <QObject>
#include <QThread>
// maintains a set of reusable worker threads
// class is not thread safe and must be accessed from Main thread
class WorkerThreadSet : public QObject
{
Q_OBJECT
public:
using QObject::QObject;
~WorkerThreadSet();
// changes the thread affinity of 'obj' to a worker thread
void assignToWorkerThread(QObject *obj);
private:
struct Worker
{
QThread *thread = nullptr;
int load = 0;
QElapsedTimer inactiveTime;
};
const static int MAX_WORKER = 2;
const static int CLEANUP_TIMEOUT = 10000; // 10seconds
const static int MAX_INACTIVE_TIME = 6000; // 6seconds
void finish(Worker &worker);
// returns a worker thread after increasing it's load
QThread *reserve();
// reduces load of the previously allocated 'thread'
// and makes it available for future operations
// threads are automatically freed when they remain
// inactive for extended amount of time
void unreserve(QThread *thread);
void cleanupInactiveWorker();
Worker workers[MAX_WORKER] {};
};