[vlc-devel] [RFC 04/82] medialibrary: Add an IFileSysteFactory implementation for network
Pierre Lamot
pierre at videolabs.io
Fri Feb 1 14:01:08 CET 2019
From: Romain Vimont <rom1v at videolabs.io>
Register a file system factory to media library to browse network.
Co-authored-by: Hugo Beauzée-Luyssen <hugo at beauzee.fr>
Pierre Lamot <pierre at videolabs.io>
---
modules/misc/Makefile.am | 12 +-
modules/misc/medialibrary/fs/device.cpp | 156 +++++++++++++
modules/misc/medialibrary/fs/device.h | 56 +++++
modules/misc/medialibrary/fs/directory.cpp | 208 +++++++++++++++++
modules/misc/medialibrary/fs/directory.h | 58 +++++
modules/misc/medialibrary/fs/file.cpp | 69 ++++++
modules/misc/medialibrary/fs/file.h | 52 +++++
modules/misc/medialibrary/fs/fs.cpp | 252 +++++++++++++++++++++
modules/misc/medialibrary/fs/fs.h | 102 +++++++++
modules/misc/medialibrary/fs/util.cpp | 57 +++++
modules/misc/medialibrary/fs/util.h | 37 +++
modules/misc/medialibrary/medialib.cpp | 7 +
12 files changed, 1065 insertions(+), 1 deletion(-)
create mode 100644 modules/misc/medialibrary/fs/device.cpp
create mode 100644 modules/misc/medialibrary/fs/device.h
create mode 100644 modules/misc/medialibrary/fs/directory.cpp
create mode 100644 modules/misc/medialibrary/fs/directory.h
create mode 100644 modules/misc/medialibrary/fs/file.cpp
create mode 100644 modules/misc/medialibrary/fs/file.h
create mode 100644 modules/misc/medialibrary/fs/fs.cpp
create mode 100644 modules/misc/medialibrary/fs/fs.h
create mode 100644 modules/misc/medialibrary/fs/util.cpp
create mode 100644 modules/misc/medialibrary/fs/util.h
diff --git a/modules/misc/Makefile.am b/modules/misc/Makefile.am
index 7848080109..f634fce3e4 100644
--- a/modules/misc/Makefile.am
+++ b/modules/misc/Makefile.am
@@ -106,7 +106,17 @@ libmedialibrary_plugin_la_SOURCES = \
misc/medialibrary/MetadataExtractor.cpp \
misc/medialibrary/entities.cpp \
misc/medialibrary/Thumbnailer.cpp \
- misc/medialibrary/medialibrary.h
+ misc/medialibrary/medialibrary.h \
+ misc/medialibrary/fs/device.h \
+ misc/medialibrary/fs/device.cpp \
+ misc/medialibrary/fs/directory.h \
+ misc/medialibrary/fs/directory.cpp \
+ misc/medialibrary/fs/file.h \
+ misc/medialibrary/fs/file.cpp \
+ misc/medialibrary/fs/fs.h \
+ misc/medialibrary/fs/fs.cpp \
+ misc/medialibrary/fs/util.h \
+ misc/medialibrary/fs/util.cpp
libmedialibrary_plugin_la_CXXFLAGS = $(AM_CXXFLAGS) $(MEDIALIBRARY_CFLAGS)
libmedialibrary_plugin_la_LIBADD = $(MEDIALIBRARY_LIBS)
diff --git a/modules/misc/medialibrary/fs/device.cpp b/modules/misc/medialibrary/fs/device.cpp
new file mode 100644
index 0000000000..c4c257daad
--- /dev/null
+++ b/modules/misc/medialibrary/fs/device.cpp
@@ -0,0 +1,156 @@
+/*****************************************************************************
+ * device.cpp: Media library network device
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "device.h"
+#include <vlc_common.h>
+#include <vlc_url.h>
+
+#include <algorithm>
+#include <cassert>
+#include <strings.h>
+
+namespace vlc {
+ namespace medialibrary {
+
+SDDevice::SDDevice( const std::string& uuid, std::string mrl )
+ : m_uuid(uuid)
+{
+ // Ensure the mountpoint always ends with a '/' to avoid mismatch between
+ // smb://foo and smb://foo/
+ if ( *mrl.crbegin() != '/' )
+ mrl += '/';
+ m_mountpoints.push_back( std::move( mrl ) );
+}
+
+const std::string &
+SDDevice::uuid() const
+{
+ return m_uuid;
+}
+
+bool
+SDDevice::isRemovable() const
+{
+ return true;
+}
+
+bool
+SDDevice::isPresent() const
+{
+ return m_mountpoints.empty() == false;
+}
+
+const
+std::string &SDDevice::mountpoint() const
+{
+ return m_mountpoints[0];
+}
+
+void SDDevice::addMountpoint( std::string mrl )
+{
+ m_mountpoints.push_back( std::move( mrl ) );
+}
+
+void SDDevice::removeMountpoint( const std::string& mrl )
+{
+ auto it = std::find( begin( m_mountpoints ), end( m_mountpoints ), mrl );
+ if ( it != end( m_mountpoints ) )
+ m_mountpoints.erase( it );
+}
+
+std::tuple<bool, std::string>
+SDDevice::matchesMountpoint( const std::string& mrl ) const
+{
+ vlc_url_t probedUrl;
+ vlc_UrlParse( &probedUrl, mrl.c_str() );
+
+ for ( const auto& m : m_mountpoints )
+ {
+ vlc_url_t url;
+ vlc_UrlParse( &url, m.c_str() );
+ if ( strcasecmp( probedUrl.psz_protocol, url.psz_protocol ) )
+ {
+ vlc_UrlClean( &url );
+ continue;
+ }
+ if ( strcasecmp( probedUrl.psz_host, url.psz_host ) )
+ {
+ vlc_UrlClean( &url );
+ continue;
+ }
+ if ( probedUrl.i_port != url.i_port )
+ {
+ unsigned int defaultPort = 0;
+ if ( !strcasecmp( probedUrl.psz_protocol, "smb" ) )
+ defaultPort = 445;
+ if ( defaultPort != 0 )
+ {
+ if ( probedUrl.i_port != 0 && probedUrl.i_port != defaultPort &&
+ url.i_port != 0 && url.i_port != defaultPort )
+ {
+ vlc_UrlClean( &url );
+ continue;
+ }
+ else
+ {
+ url.i_port = probedUrl.i_port;
+ char* tmpUrl_psz = vlc_uri_compose(&url);
+ vlc_UrlClean( &url );
+ if (!tmpUrl_psz)
+ continue;
+ std::string tmpUrl(tmpUrl_psz);
+ free(tmpUrl_psz);
+ return { true, tmpUrl };
+ }
+ }
+ else
+ {
+ vlc_UrlClean( &url );
+ continue;
+ }
+ }
+ vlc_UrlClean( &url );
+ return { true, m };
+ }
+ vlc_UrlClean( &probedUrl );
+ return { false, "" };
+}
+
+std::string SDDevice::relativeMrl( const std::string& absoluteMrl ) const
+{
+ auto match = matchesMountpoint( absoluteMrl );
+ if ( std::get<0>( match ) == false )
+ return absoluteMrl;
+ const auto& mountpoint = std::get<1>( match );
+ return absoluteMrl.substr( mountpoint.length() );
+}
+
+std::string SDDevice::absoluteMrl( const std::string& relativeMrl ) const
+{
+ assert( m_mountpoints.empty() == false );
+ return m_mountpoints[0] + relativeMrl;
+}
+
+ } /* namespace medialibrary */
+} /* namespace vlc */
diff --git a/modules/misc/medialibrary/fs/device.h b/modules/misc/medialibrary/fs/device.h
new file mode 100644
index 0000000000..0ee6984d99
--- /dev/null
+++ b/modules/misc/medialibrary/fs/device.h
@@ -0,0 +1,56 @@
+/*****************************************************************************
+ * device.h: Media library network device
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ *****************************************************************************/
+
+#ifndef SD_DEVICE_H
+#define SD_DEVICE_H
+
+#include <medialibrary/filesystem/IDevice.h>
+#include <vector>
+
+namespace vlc {
+ namespace medialibrary {
+
+using namespace ::medialibrary::fs;
+
+class SDDevice : public IDevice
+{
+public:
+ SDDevice( const std::string& uuid, std::string mrl );
+
+ const std::string &uuid() const override;
+ bool isRemovable() const override;
+ bool isPresent() const override;
+ const std::string &mountpoint() const override;
+ void addMountpoint( std::string mrl ) override;
+ void removeMountpoint( const std::string& mrl ) override;
+ std::tuple<bool, std::string> matchesMountpoint( const std::string& mrl ) const override;
+ std::string relativeMrl( const std::string& absoluteMrl ) const override;
+ std::string absoluteMrl( const std::string& relativeMrl ) const override;
+
+
+private:
+ std::string m_uuid;
+ std::vector<std::string> m_mountpoints;
+};
+
+ } /* namespace medialibrary */
+} /* namespace vlc */
+
+#endif
diff --git a/modules/misc/medialibrary/fs/directory.cpp b/modules/misc/medialibrary/fs/directory.cpp
new file mode 100644
index 0000000000..2eb7f7b77f
--- /dev/null
+++ b/modules/misc/medialibrary/fs/directory.cpp
@@ -0,0 +1,208 @@
+/*****************************************************************************
+ * directory.cpp: Media library network directory
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "directory.h"
+#include "file.h"
+
+#include <assert.h>
+#include <vector>
+#include <system_error>
+#include <vlc_common.h>
+#include <vlc_input_item.h>
+#include <vlc_input.h>
+#include <vlc_threads.h>
+#include <vlc_cxx_helpers.hpp>
+
+using InputItemPtr = vlc_shared_data_ptr_type(input_item_t,
+ input_item_Hold,
+ input_item_Release);
+
+namespace vlc {
+ namespace medialibrary {
+
+SDDirectory::SDDirectory(const std::string &mrl, SDFileSystemFactory &fs)
+ : m_mrl(mrl)
+ , m_fs(fs)
+{
+ if ( *m_mrl.crbegin() != '/' )
+ m_mrl += '/';
+}
+
+const std::string &
+SDDirectory::mrl() const
+{
+ return m_mrl;
+}
+
+const std::vector<std::shared_ptr<IFile>> &
+SDDirectory::files() const
+{
+ if (!m_read_done)
+ read();
+ return m_files;
+}
+
+const std::vector<std::shared_ptr<IDirectory>> &
+SDDirectory::dirs() const
+{
+ if (!m_read_done)
+ read();
+ return m_dirs;
+}
+
+std::shared_ptr<IDevice>
+SDDirectory::device() const
+{
+ if (!m_device)
+ m_device = m_fs.createDeviceFromMrl(mrl());
+ return m_device;
+}
+
+struct metadata_request {
+ vlc::threads::mutex lock;
+ vlc::threads::condition_variable cond;
+ /* results */
+ input_state_e state;
+ bool probe;
+ std::vector<InputItemPtr> *children;
+};
+
+ } /* namespace medialibrary */
+} /* namespace vlc */
+
+extern "C" {
+
+static void onInputEvent( input_thread_t*, const struct vlc_input_event *event,
+ void *data )
+{
+ auto req = static_cast<vlc::medialibrary::metadata_request*>( data );
+ switch ( event->type )
+ {
+ case INPUT_EVENT_SUBITEMS:
+ {
+ for (int i = 0; i < event->subitems->i_children; ++i)
+ {
+ input_item_node_t *child = event->subitems->pp_children[i];
+ /* this class assumes we always receive a flat list */
+ assert(child->i_children == 0);
+ input_item_t *media = child->p_item;
+ req->children->emplace_back( media );
+ }
+ break;
+ }
+ case INPUT_EVENT_STATE:
+ {
+ vlc::threads::mutex_locker lock( req->lock );
+ req->state = event->state;
+ }
+ break;
+ case INPUT_EVENT_DEAD:
+ {
+ vlc::threads::mutex_locker lock( req->lock );
+ // We need to probe the item now, but not from the input thread
+ req->probe = true;
+ }
+ req->cond.signal();
+ break;
+ default:
+ break;
+ }
+
+}
+} /* extern C */
+
+namespace vlc {
+ namespace medialibrary {
+
+static bool request_metadata_sync( libvlc_int_t *libvlc, input_item_t *media,
+ std::vector<InputItemPtr> *out_children )
+{
+ metadata_request req;
+ req.children = out_children;
+ req.probe = false;
+ auto deadline = vlc_tick_now() + VLC_TICK_FROM_SEC( 5 );
+
+ media->i_preparse_depth = 1;
+ auto inputThread = vlc::wrap_cptr(
+ input_CreatePreparser( VLC_OBJECT( libvlc ), onInputEvent, &req, media ),
+ &input_Close );
+
+ if ( inputThread == nullptr )
+ return false;
+
+ vlc::threads::mutex_locker lock( req.lock );
+ if ( input_Start( inputThread.get() ) != VLC_SUCCESS )
+ return false;
+ while ( req.probe == false )
+ {
+ auto res = req.cond.timedwait( req.lock, deadline );
+ if (res != 0 )
+ {
+ input_Stop( inputThread.get() );
+ throw std::system_error( ETIMEDOUT, std::generic_category(),
+ "Failed to browse network directory: "
+ "Network is too slow");
+ }
+ if ( req.probe == true )
+ {
+ if ( req.state == END_S || req.state == ERROR_S )
+ break;
+ req.probe = false;
+ }
+ }
+ return req.state == END_S;
+}
+
+void
+SDDirectory::read() const
+{
+ auto media = vlc::wrap_cptr( input_item_New(m_mrl.c_str(), m_mrl.c_str()),
+ &input_item_Release );
+ if (!media)
+ throw std::bad_alloc();
+
+ std::vector<InputItemPtr> children;
+
+ auto status = request_metadata_sync( m_fs.libvlc(), media.get(), &children);
+
+ if ( status == false )
+ throw std::system_error(EIO, std::generic_category(),
+ "Failed to browse network directory: "
+ "Unknown error");
+
+ for (const InputItemPtr &m : children)
+ {
+ const char *mrl = m.get()->psz_uri;
+ enum input_item_type_e type = m->i_type;
+ if (type == ITEM_TYPE_DIRECTORY)
+ m_dirs.push_back(std::make_shared<SDDirectory>(mrl, m_fs));
+ else if (type == ITEM_TYPE_FILE)
+ m_files.push_back(std::make_shared<SDFile>(mrl));
+ }
+
+ m_read_done = true;
+}
+
+ } /* namespace medialibrary */
+} /* namespace vlc */
diff --git a/modules/misc/medialibrary/fs/directory.h b/modules/misc/medialibrary/fs/directory.h
new file mode 100644
index 0000000000..bc5395b396
--- /dev/null
+++ b/modules/misc/medialibrary/fs/directory.h
@@ -0,0 +1,58 @@
+/*****************************************************************************
+ * directory.h: Media library network directory
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ *****************************************************************************/
+
+#ifndef SD_DIRECTORY_H
+#define SD_DIRECTORY_H
+
+#include <medialibrary/filesystem/IDirectory.h>
+#include <medialibrary/filesystem/IFile.h>
+
+#include "fs.h"
+
+namespace vlc {
+ namespace medialibrary {
+
+using namespace ::medialibrary::fs;
+
+class SDDirectory : public IDirectory
+{
+public:
+ explicit SDDirectory(const std::string &mrl, SDFileSystemFactory &fs);
+ const std::string &mrl() const override;
+ const std::vector<std::shared_ptr<IFile>> &files() const override;
+ const std::vector<std::shared_ptr<IDirectory>> &dirs() const override;
+ std::shared_ptr<IDevice> device() const override;
+
+private:
+ void read() const;
+
+ std::string m_mrl;
+ SDFileSystemFactory &m_fs;
+
+ mutable bool m_read_done = false;
+ mutable std::vector<std::shared_ptr<IFile>> m_files;
+ mutable std::vector<std::shared_ptr<IDirectory>> m_dirs;
+ mutable std::shared_ptr<IDevice> m_device;
+};
+
+ } /* namespace medialibrary */
+} /* namespace vlc */
+
+#endif
diff --git a/modules/misc/medialibrary/fs/file.cpp b/modules/misc/medialibrary/fs/file.cpp
new file mode 100644
index 0000000000..f6c6c04397
--- /dev/null
+++ b/modules/misc/medialibrary/fs/file.cpp
@@ -0,0 +1,69 @@
+/*****************************************************************************
+ * file.cpp: Media library network file
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "file.h"
+#include "util.h"
+
+namespace vlc {
+ namespace medialibrary {
+
+SDFile::SDFile(const std::string &mrl)
+ : m_mrl(mrl)
+ , m_name(utils::fileName(mrl))
+ , m_extension(utils::extension(mrl))
+{
+}
+
+const std::string &
+SDFile::mrl() const
+{
+ return m_mrl;
+}
+
+const std::string &
+SDFile::name() const
+{
+ return m_name;
+}
+
+const std::string &
+SDFile::extension() const
+{
+ return m_extension;
+}
+
+unsigned int
+SDFile::lastModificationDate() const
+{
+ return 0;
+}
+
+unsigned int
+SDFile::size() const
+{
+ return 0;
+}
+
+ } /* namespace medialibrary */
+} /* namespace vlc */
diff --git a/modules/misc/medialibrary/fs/file.h b/modules/misc/medialibrary/fs/file.h
new file mode 100644
index 0000000000..18ecf2b1d8
--- /dev/null
+++ b/modules/misc/medialibrary/fs/file.h
@@ -0,0 +1,52 @@
+/*****************************************************************************
+ * file.h: Media library network file
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ *****************************************************************************/
+
+#ifndef SD_FILE_H
+#define SD_FILE_H
+
+#include <medialibrary/filesystem/IFile.h>
+
+namespace vlc {
+ namespace medialibrary {
+
+using namespace ::medialibrary::fs;
+
+class SDFile : public IFile
+{
+public:
+ explicit SDFile(const std::string &mrl);
+ virtual ~SDFile() = default;
+ const std::string& mrl() const override;
+ const std::string& name() const override;
+ const std::string& extension() const override;
+ unsigned int lastModificationDate() const override;
+ unsigned int size() const override;
+ inline bool isNetwork() const override { return true; }
+
+private:
+ std::string m_mrl;
+ std::string m_name;
+ std::string m_extension;
+};
+
+ } /* namespace medialibrary */
+} /* namespace vlc */
+
+#endif
diff --git a/modules/misc/medialibrary/fs/fs.cpp b/modules/misc/medialibrary/fs/fs.cpp
new file mode 100644
index 0000000000..5c7c00ee80
--- /dev/null
+++ b/modules/misc/medialibrary/fs/fs.cpp
@@ -0,0 +1,252 @@
+/*****************************************************************************
+ * util.cpp: Media library utils
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "fs.h"
+
+#include <algorithm>
+#include <vlc_services_discovery.h>
+#include <medialibrary/IDeviceLister.h>
+#include <medialibrary/filesystem/IDevice.h>
+
+#include "device.h"
+#include "directory.h"
+#include "util.h"
+
+extern "C" {
+
+static void
+services_discovery_item_added(services_discovery_t *sd,
+ input_item_t *parent, input_item_t *media,
+ const char *cat)
+{
+ VLC_UNUSED(parent);
+ VLC_UNUSED(cat);
+ vlc::medialibrary::SDFileSystemFactory *that =
+ static_cast<vlc::medialibrary::SDFileSystemFactory *>(sd->owner.sys);
+ that->onDeviceAdded(media);
+}
+
+static void
+services_discovery_item_removed(services_discovery_t *sd, input_item_t *media)
+{
+ vlc::medialibrary::SDFileSystemFactory *that =
+ static_cast<vlc::medialibrary::SDFileSystemFactory *>(sd->owner.sys);
+ that->onDeviceRemoved(media);
+}
+
+static const struct services_discovery_callbacks sd_cbs = {
+ .item_added = services_discovery_item_added,
+ .item_removed = services_discovery_item_removed,
+};
+
+}
+
+namespace vlc {
+ namespace medialibrary {
+
+using namespace ::medialibrary;
+
+SDFileSystemFactory::SDFileSystemFactory(vlc_object_t *parent,
+ const std::string &scheme)
+ : m_parent(parent)
+ , m_scheme(scheme)
+{
+}
+
+std::shared_ptr<IDirectory>
+SDFileSystemFactory::createDirectory(const std::string &mrl)
+{
+ return std::make_shared<SDDirectory>(mrl, *this);
+}
+
+std::shared_ptr<IDevice>
+SDFileSystemFactory::createDevice(const std::string &uuid)
+{
+ vlc::threads::mutex_locker locker(m_mutex);
+
+ vlc_tick_t deadline = vlc_tick_now() + VLC_TICK_FROM_SEC(5);
+ while ( true )
+ {
+ auto it = std::find_if(m_devices.cbegin(), m_devices.cend(),
+ [&uuid](const std::shared_ptr<IDevice>& device) {
+ return strcasecmp( uuid.c_str(), device->uuid().c_str() ) == 0;
+ });
+ if (it != m_devices.cend())
+ return (*it);
+ /* wait a bit, maybe the device is not detected yet */
+ int timeout = m_itemAddedCond.timedwait(m_mutex, deadline);
+ if (timeout)
+ return nullptr;
+ }
+ vlc_assert_unreachable();
+}
+
+std::shared_ptr<IDevice>
+SDFileSystemFactory::createDeviceFromMrl(const std::string &mrl)
+{
+ vlc::threads::mutex_locker locker(m_mutex);
+
+ auto it = std::find_if(m_devices.cbegin(), m_devices.cend(),
+ [&mrl](const std::shared_ptr<IDevice>& device) {
+ auto match = device->matchesMountpoint( mrl );
+ return std::get<0>( match );
+ });
+ if (it != m_devices.cend())
+ return (*it);
+ return nullptr;
+}
+
+void
+SDFileSystemFactory::refreshDevices()
+{
+ /* nothing to do */
+}
+
+bool
+SDFileSystemFactory::isMrlSupported(const std::string &path) const
+{
+ return !path.compare(0, m_scheme.length(), m_scheme);
+}
+
+bool
+SDFileSystemFactory::isNetworkFileSystem() const
+{
+ return true;
+}
+
+const std::string &
+SDFileSystemFactory::scheme() const
+{
+ return m_scheme;
+}
+
+bool
+SDFileSystemFactory::start(IFileSystemFactoryCb *callbacks)
+{
+ this->m_callbacks = callbacks;
+ struct services_discovery_owner_t owner = {
+ .cbs = &sd_cbs,
+ .sys = this,
+ };
+ char** sdLongNames;
+ int* categories;
+ auto releaser = [](char** ptr) {
+ for ( auto i = 0u; ptr[i] != nullptr; ++i )
+ free( ptr[i] );
+ free( ptr );
+ };
+ auto sdNames = vlc_sd_GetNames( libvlc(), &sdLongNames, &categories );
+ if ( sdNames == nullptr )
+ return false;
+ auto sdNamesPtr = vlc::wrap_carray( sdNames, releaser );
+ auto sdLongNamesPtr = vlc::wrap_carray( sdLongNames, releaser );
+ auto categoriesPtr = vlc::wrap_carray( categories );
+ for ( auto i = 0u; sdNames[i] != nullptr; ++i )
+ {
+ if ( categories[i] != SD_CAT_LAN )
+ continue;
+ SdPtr sd{ vlc_sd_Create( libvlc(), sdNames[i], &owner ), &vlc_sd_Destroy };
+ if ( sd == nullptr )
+ continue;
+ m_sds.push_back( std::move( sd ) );
+ }
+ return m_sds.empty() == false;
+}
+
+void
+SDFileSystemFactory::stop()
+{
+ m_sds.clear();
+ m_callbacks = nullptr;
+}
+
+libvlc_int_t *
+SDFileSystemFactory::libvlc() const
+{
+ return m_parent->obj.libvlc;
+}
+
+void
+SDFileSystemFactory::onDeviceAdded(input_item_t *media)
+{
+ auto mrl = std::string{ media->psz_uri };
+ auto name = media->psz_name;
+ if ( *mrl.crbegin() != '/' )
+ mrl += '/';
+
+ if ( strncasecmp( mrl.c_str(), m_scheme.c_str(), m_scheme.length() ) != 0 )
+ return;
+
+ {
+ vlc::threads::mutex_locker locker(m_mutex);
+ auto it = std::find_if(m_devices.begin(), m_devices.end(),
+ [name](const std::shared_ptr<IDevice>& device) {
+ return strcasecmp( name, device->uuid().c_str() ) == 0;
+ });
+ if (it != m_devices.end())
+ {
+ auto& device = (*it);
+ auto match = device->matchesMountpoint( mrl );
+ if ( std::get<0>( match ) == false )
+ {
+ device->addMountpoint( mrl );
+ m_callbacks->onDeviceMounted( *device, mrl );
+ }
+ return; /* already exists */
+ }
+ auto device = std::make_shared<SDDevice>( name, mrl );
+ m_devices.push_back( device );
+ m_callbacks->onDeviceMounted( *device, mrl );
+ }
+
+ m_itemAddedCond.signal();
+}
+
+void
+SDFileSystemFactory::onDeviceRemoved(input_item_t *media)
+{
+ auto name = media->psz_name;
+ auto mrl = std::string{ media->psz_uri };
+ if ( *mrl.crbegin() != '/' )
+ mrl += '/';
+
+ if ( strncasecmp( mrl.c_str(), m_scheme.c_str(), m_scheme.length() ) != 0 )
+ return;
+
+ {
+ vlc::threads::mutex_locker locker(m_mutex);
+ auto it = std::find_if(m_devices.begin(), m_devices.end(),
+ [&name](const std::shared_ptr<IDevice>& device) {
+ return strcasecmp( name, device->uuid().c_str() ) == 0;
+ });
+ if ( it != m_devices.end() )
+ {
+ (*it)->removeMountpoint( mrl );
+ m_callbacks->onDeviceUnmounted( *(*it), mrl );
+ }
+ }
+}
+
+ } /* namespace medialibrary */
+} /* namespace vlc */
diff --git a/modules/misc/medialibrary/fs/fs.h b/modules/misc/medialibrary/fs/fs.h
new file mode 100644
index 0000000000..9a28d9c666
--- /dev/null
+++ b/modules/misc/medialibrary/fs/fs.h
@@ -0,0 +1,102 @@
+/*****************************************************************************
+ * fs.h: Media library network file system
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ *****************************************************************************/
+
+#ifndef SD_FS_H
+#define SD_FS_H
+
+#include <memory>
+#include <vector>
+#include <vlc_common.h>
+#include <vlc_threads.h>
+#include <vlc_cxx_helpers.hpp>
+#include <medialibrary/filesystem/IFileSystemFactory.h>
+
+struct input_item_t;
+struct services_discovery_t;
+struct libvlc_int_t;
+extern "C" {
+void vlc_sd_Destroy(services_discovery_t *sd);
+}
+
+namespace medialibrary {
+struct IDeviceListerCb;
+}
+
+namespace vlc {
+ namespace medialibrary {
+
+using namespace ::medialibrary;
+using namespace ::medialibrary::fs;
+
+class SDFileSystemFactory : public IFileSystemFactory {
+public:
+ SDFileSystemFactory(vlc_object_t *m_parent,
+ const std::string &scheme);
+
+ std::shared_ptr<IDirectory>
+ createDirectory(const std::string &mrl) override;
+
+ std::shared_ptr<IDevice>
+ createDevice(const std::string &uuid) override;
+
+ std::shared_ptr<IDevice>
+ createDeviceFromMrl(const std::string &path) override;
+
+ void
+ refreshDevices() override;
+
+ bool
+ isMrlSupported(const std::string &path) const override;
+
+ bool
+ isNetworkFileSystem() const override;
+
+ const std::string &
+ scheme() const override;
+
+ bool
+ start(IFileSystemFactoryCb *m_callbacks) override;
+
+ void
+ stop() override;
+
+ libvlc_int_t *
+ libvlc() const;
+
+ /* public to be called from C callback */
+ void onDeviceAdded(input_item_t *media);
+ void onDeviceRemoved(input_item_t *media);
+
+private:
+ vlc_object_t *const m_parent;
+ const std::string m_scheme;
+ using SdPtr = std::unique_ptr<services_discovery_t, decltype(&vlc_sd_Destroy)>;
+ std::vector<SdPtr> m_sds;
+ IFileSystemFactoryCb *m_callbacks;
+
+ vlc::threads::mutex m_mutex;
+ vlc::threads::condition_variable m_itemAddedCond;
+ std::vector<std::shared_ptr<IDevice>> m_devices;
+};
+
+ } /* namespace medialibrary */
+} /* namespace vlc */
+
+#endif
diff --git a/modules/misc/medialibrary/fs/util.cpp b/modules/misc/medialibrary/fs/util.cpp
new file mode 100644
index 0000000000..f25f96d161
--- /dev/null
+++ b/modules/misc/medialibrary/fs/util.cpp
@@ -0,0 +1,57 @@
+/*****************************************************************************
+ * Copyright (C) 2019 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.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "util.h"
+
+#include <stdexcept>
+
+namespace vlc {
+ namespace medialibrary {
+ namespace utils {
+
+#ifdef _WIN32
+# define DIR_SEPARATORS "\\/"
+#else
+# define DIR_SEPARATORS "/"
+#endif
+
+std::string
+extension(const std::string &fileName)
+{
+ auto pos = fileName.find_last_of('.');
+ if (pos == std::string::npos)
+ return {};
+ return fileName.substr(pos + 1);
+}
+
+std::string
+fileName(const std::string &filePath)
+{
+ auto pos = filePath.find_last_of(DIR_SEPARATORS);
+ if (pos == std::string::npos)
+ return filePath;
+ return filePath.substr(pos + 1);
+}
+
+ } /* namespace utils */
+ } /* namespace medialibrary */
+} /* namespace vlc */
diff --git a/modules/misc/medialibrary/fs/util.h b/modules/misc/medialibrary/fs/util.h
new file mode 100644
index 0000000000..6cbff37114
--- /dev/null
+++ b/modules/misc/medialibrary/fs/util.h
@@ -0,0 +1,37 @@
+/*****************************************************************************
+ * util.h: Media library utils
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ *****************************************************************************/
+
+#ifndef SD_UTIL_H
+#define SD_UTIL_H
+
+#include <string>
+
+namespace vlc {
+ namespace medialibrary {
+ namespace utils {
+
+std::string fileName(const std::string& filePath);
+std::string extension(const std::string& fileName);
+
+ } /* namespace utils */
+ } /* namespace medialibrary */
+} /* namespace vlc */
+
+#endif
diff --git a/modules/misc/medialibrary/medialib.cpp b/modules/misc/medialibrary/medialib.cpp
index c0b48969f1..fc636ee95b 100644
--- a/modules/misc/medialibrary/medialib.cpp
+++ b/modules/misc/medialibrary/medialib.cpp
@@ -27,6 +27,7 @@
#include <vlc_url.h>
#include <vlc_media_library.h>
#include "medialibrary.h"
+#include "fs/fs.h"
#include <medialibrary/IMedia.h>
#include <medialibrary/IAlbumTrack.h>
@@ -334,6 +335,12 @@ bool MediaLibrary::Start()
"medialib: %s", ex.what() );
return false;
}
+
+ auto networkFs = std::make_shared<vlc::medialibrary::SDFileSystemFactory>( VLC_OBJECT( m_vlc_ml ), "smb://");
+ ml->addNetworkFileSystemFactory( networkFs );
+ // Disabled by default for now
+ ml->setDiscoverNetworkEnabled( false );
+
if ( ml->start() == false )
{
msg_Err( m_vlc_ml, "Failed to start the MediaLibrary" );
--
2.19.1
More information about the vlc-devel
mailing list