[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