<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">2015-01-29 16:57 GMT+01:00 Rémi Denis-Courmont <span dir="ltr"><<a href="mailto:remi@remlab.net" target="_blank">remi@remlab.net</a>></span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Le jeudi 29 janvier 2015, 16:04:02 Jonathan Calmels a écrit :<br>
<div><div>> ---<br>
>  <a href="http://configure.ac" target="_blank">configure.ac</a>                       |   5 +<br>
>  modules/access/Makefile.am         |   7 +<br>
>  modules/access/torrent/access.cpp  | 219 ++++++++++++++++++++++++++++<br>
>  modules/access/torrent/thread.h    | 126 ++++++++++++++++<br>
>  modules/access/torrent/torrent.cpp | 284<br>
> +++++++++++++++++++++++++++++++++++++ modules/access/torrent/torrent.h   |<br>
> 152 ++++++++++++++++++++<br>
>  6 files changed, 793 insertions(+)<br>
>  create mode 100644 modules/access/torrent/access.cpp<br>
>  create mode 100644 modules/access/torrent/thread.h<br>
>  create mode 100644 modules/access/torrent/torrent.cpp<br>
>  create mode 100644 modules/access/torrent/torrent.h<br>
><br>
> diff --git a/<a href="http://configure.ac" target="_blank">configure.ac</a> b/<a href="http://configure.ac" target="_blank">configure.ac</a><br>
> index e5a22c3..b165fde 100644<br>
> --- a/<a href="http://configure.ac" target="_blank">configure.ac</a><br>
> +++ b/<a href="http://configure.ac" target="_blank">configure.ac</a><br>
> @@ -1599,6 +1599,11 @@ dnl<br>
>  PKG_ENABLE_MODULES_VLC([ARCHIVE], [access_archive], [libarchive >= 3.1.0],<br>
> (libarchive support), [auto])<br>
><br>
>  dnl<br>
> +dnl libtorrent<br>
> +dnl<br>
> +PKG_ENABLE_MODULES_VLC([TORRENT], [access_torrent], [libtorrent-rasterbar],<br>
> (libtorrent support), [auto]) +<br>
> +dnl<br>
>  dnl  live555 input<br>
>  dnl<br>
>  AC_ARG_ENABLE(live555,<br>
> diff --git a/modules/access/Makefile.am b/modules/access/Makefile.am<br>
> index ec8119f..e0526b4 100644<br>
> --- a/modules/access/Makefile.am<br>
> +++ b/modules/access/Makefile.am<br>
> @@ -29,6 +29,13 @@ access_LTLIBRARIES += <a href="http://libfilesystem_plugin.la" target="_blank">libfilesystem_plugin.la</a><br>
>  libidummy_plugin_la_SOURCES = access/idummy.c<br>
>  access_LTLIBRARIES += <a href="http://libidummy_plugin.la" target="_blank">libidummy_plugin.la</a><br>
><br>
> +libaccess_torrent_plugin_la_SOURCES = access/torrent/access.cpp<br>
> access/torrent/torrent.cpp +libaccess_torrent_plugin_la_CXXFLAGS =<br>
> $(AM_CXXFLAGS) "-std=c++14" $(TORRENT_CFLAGS)<br>
> +libaccess_torrent_plugin_la_LIBADD = $(AM_LIBADD) $(TORRENT_LIBS)<br>
> +libaccess_torrent_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(accessdir)'<br>
> +access_LTLIBRARIES += $(LTLIBaccess_torrent)<br>
> +EXTRA_LTLIBRARIES += <a href="http://libaccess_torrent_plugin.la" target="_blank">libaccess_torrent_plugin.la</a><br>
> +<br>
>  libimem_plugin_la_SOURCES = access/imem.c<br>
>  libimem_plugin_la_LIBADD = $(LIBM)<br>
>  access_LTLIBRARIES += <a href="http://libimem_plugin.la" target="_blank">libimem_plugin.la</a><br>
> diff --git a/modules/access/torrent/access.cpp<br>
> b/modules/access/torrent/access.cpp new file mode 100644<br>
> index 0000000..db59553<br>
> --- /dev/null<br>
> +++ b/modules/access/torrent/access.cpp<br>
> @@ -0,0 +1,219 @@<br>
> +/**************************************************************************<br>
</div></div>> *** + * Copyright (C) 2014 VLC authors, VideoLAN and Videolabs<br>
<span>> + *<br>
> + * Authors: Jonathan Calmels <<a href="mailto:exxo@videolabs.io" target="_blank">exxo@videolabs.io</a>><br>
> + *<br>
> + * This program is free software; you can redistribute it and/or modify it<br>
> + * under the terms of the GNU Lesser General Public License as published by<br>
> + * the Free Software Foundation; either version 2.1 of the License, or + *<br>
> (at your option) any later version.<br>
> + *<br>
> + * This program is distributed in the hope that it will be useful,<br>
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
> + * GNU Lesser General Public License for more details.<br>
> + *<br>
> + * You should have received a copy of the GNU Lesser General Public License<br>
> + * along with this program; if not, write to the Free Software Foundation,<br>
> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. +<br>
> ***************************************************************************<br>
</span>> **/ +<br>
<div><div>> +#ifdef HAVE_CONFIG_H<br>
> +# include "config.h"<br>
> +#endif<br>
> +<br>
> +#include <vlc_common.h><br>
> +#include <vlc_access.h><br>
> +#include <vlc_plugin.h><br>
> +#include <vlc_url.h><br>
> +#include <vlc_input_item.h><br>
> +#include <vlc_configuration.h><br>
> +<br>
> +#include "torrent.h"<br>
> +<br>
> +static int Open(vlc_object_t*);<br>
> +static void Close(vlc_object_t*);<br>
> +static int ReadDir(access_t*, input_item_node_t*);<br>
> +static int Control(access_t*, int, va_list);<br>
> +static int Seek(access_t*, uint64_t);<br>
> +static block_t* Block(access_t*);<br>
> +<br>
> +struct access_sys_t<br>
> +{<br>
> +    TorrentAccess torrent;<br>
> +};<br>
> +<br>
> +/**************************************************************************<br>
</div></div>> *** + * Module descriptor<br>
> +<br>
> ***************************************************************************<br>
> **/ +<br>
<span>> +vlc_module_begin()<br>
> +<br>
> +    set_shortname(N_("Torrent / Magnet"))<br>
> +    set_description(N_("Torrent file and Magnet link"))<br>
> +    set_capability("access", 51)<br>
> +    set_category(CAT_INPUT)<br>
> +    set_subcategory(SUBCAT_INPUT_ACCESS)<br>
> +    add_shortcut("torrent", "file", "magnet")<br>
> +    set_callbacks(Open, Close)<br>
> +<br>
> +    add_integer("file_at", -1, nullptr, nullptr, false)<br>
> +    change_private()<br>
<br>
</span>Private means not visible in the preferences. That´s not an excuse for no<br>
documentation.<br>
<br>
The name is too generic.<br>
<br>
We don´t use underscores. Per the command line style, dash is the separator.<br>
<span><br>
> +<br>
> +    add_directory("download_dir", nullptr, "Download directory",<br>
> +      "Directory used to store dowloaded files", false)<br>
<br>
</span>Ditto.<br>
<br>
> +<br>
> +vlc_module_end()<br>
> +<br>
> +/**************************************************************************<br>
> *** + * Open:<br>
> +<br>
> ***************************************************************************<br>
> **/ +<br>
<div><div>> +static unique_char_ptr var_GetDownloadDir(const access_t* p_access)<br>
> +{<br>
> +    auto dir = var_InheritString(p_access, "download_dir");<br>
> +    if (dir == nullptr)<br>
> +        dir = config_GetUserDir(VLC_DOWNLOAD_DIR);<br>
> +    return {dir, std::free};<br>
> +}<br>
> +<br>
> +static int open(access_t* p_access)<br>
> +{<br>
> +    lt::add_torrent_params params;<br>
> +<br>
> +    if (TorrentAccess::ParseURI(p_access->psz_location, params) !=<br>
> VLC_SUCCESS)<br>
> +        return VLC_EGENERIC;<br>
> +<br>
> +    auto dir = var_GetDownloadDir(p_access);<br>
> +    if (dir == nullptr)<br>
> +        return VLC_EGENERIC;<br>
> +<br>
> +    p_access->p_sys = new access_sys_t{{p_access}};<br>
> +    auto& torrent = p_access->p_sys->torrent;<br>
> +    auto file_at = var_InheritInteger(p_access, "file_at");<br>
> +<br>
> +    torrent.set_parameters(std::move(params));<br>
> +    torrent.set_download_dir(std::move(dir));<br>
> +<br>
> +    if (!torrent.has_metadata()) {<br>
> +        // This is a magnet link, first we need to generate the torrent<br>
> file.<br>
> +        if (torrent.RetrieveMetadata() != VLC_SUCCESS)<br>
> +            return VLC_EGENERIC;<br>
> +    }<br>
> +    if (file_at < 0) {<br>
> +        // Browse the torrent metadata and generate a playlist with the<br>
> files in it.<br>
> +        ACCESS_SET_CALLBACKS(nullptr, nullptr, Control,<br>
> nullptr);<br>
> +        p_access->pf_readdir = ReadDir;<br>
> +        return VLC_SUCCESS;<br>
> +    }<br>
> +    // Torrent file has been browsed, start the download.<br>
> +    ACCESS_SET_CALLBACKS(nullptr, Block, Control, Seek);<br>
> +    return torrent.StartDownload(file_at);<br>
> +}<br>
> +<br>
> +static int Open(vlc_object_t* p_this)<br>
> +{<br>
> +    auto p_access = (access_t*) p_this;<br>
> +    access_InitFields(p_access);<br>
<br>
</div></div>Belongs on success path.<br>
<span><br>
> +<br>
> +    try {<br>
> +        auto r = open(p_access);<br>
> +        if (r != VLC_SUCCESS)<br>
> +            delete p_access->p_sys;<br>
<br>
</span>New in one function and delete in another is rather ugly.<br>
<div><div><br>
> +        return r;<br>
> +    }<br>
> +    catch (std::bad_alloc& e) {<br>
> +        delete p_access->p_sys;<br>
> +        return VLC_ENOMEM;<br>
> +    }<br>
> +}<br>
> +<br>
> +/**************************************************************************<br>
> ***<br>
> + * Close:<br>
> +<br>
> ***************************************************************************<br>
> **/<br>
> +<br>
> +static void Close(vlc_object_t* p_this)<br>
> +{<br>
> +    auto p_access = (access_t*) p_this;<br>
> +    delete p_access->p_sys;<br>
> +}<br>
> +<br>
> +/**************************************************************************<br>
> *** + * Callbacks<br>
> +<br>
> ***************************************************************************<br>
</div></div>> **/ +<br>
<div><div>> +static int ReadDir(access_t* p_access, input_item_node_t* p_node)<br>
> +{<br>
> +    const auto& torrent = p_access->p_sys->torrent;<br>
> +    const auto& metadata = torrent.metadata();<br>
> +<br>
> +    auto i = 0;<br>
> +    for (auto f = metadata.begin_files(); f != metadata.end_files(); ++f,<br>
> ++i) { +        const auto psz_uri = torrent.uri().c_str();<br>
> +        const auto psz_name = f->filename();<br>
> +        const auto psz_option = "file_at=" + std::to_string(i);<br>
> +<br>
> +        auto p_item = input_item_New(psz_uri, psz_name.c_str());<br>
> +        input_item_AddOption(p_item, psz_option.c_str(),<br>
> VLC_INPUT_OPTION_TRUSTED); +        input_item_node_AppendItem(p_node,<br>
> p_item);<br>
> +        input_item_Release(p_item);<br>
> +    }<br>
> +    return VLC_SUCCESS;<br>
> +}<br>
> +<br>
> +static int Control(access_t* p_access, int i_query, va_list args)<br>
> +{<br>
> +    switch(i_query) {<br>
> +    case ACCESS_CAN_FASTSEEK:<br>
> +        *va_arg(args, bool*) = false;<br>
> +        break;<br>
> +<br>
> +    case ACCESS_CAN_PAUSE:<br>
> +    case ACCESS_CAN_SEEK:<br>
> +    case ACCESS_CAN_CONTROL_PACE:<br>
> +        *va_arg(args, bool*) = true;<br>
> +        break;<br>
> +<br>
> +    case ACCESS_GET_PTS_DELAY:<br>
> +        *va_arg(args, int64_t*) = DEFAULT_PTS_DELAY * 1000;<br>
> +        break;<br>
> +<br>
> +    case ACCESS_SET_PAUSE_STATE:<br>
> +    case ACCESS_SET_SEEKPOINT:<br>
> +        return VLC_SUCCESS;<br>
<br>
</div></div>Dubious.<br>
<div><div><br>
> +<br>
> +    case ACCESS_GET_TITLE_INFO:<br>
> +    case ACCESS_SET_TITLE:<br>
> +    case ACCESS_SET_PRIVATE_ID_STATE:<br>
> +        return VLC_EGENERIC;<br>
> +<br>
> +    default:<br>
> +        msg_Warn(p_access, "unimplemented query in control");<br>
> +        return VLC_EGENERIC;<br>
> +    }<br>
> +<br>
> +    return VLC_SUCCESS;<br>
> +}<br>
> +<br>
> +static block_t* Block(access_t* p_access)<br>
> +{<br>
> +    Piece p;<br>
> +    bool eof;<br>
> +<br>
> +    auto& torrent = p_access->p_sys->torrent;<br>
> +    torrent.ReadNextPiece(p, eof);<br>
> +<br>
> +    p_access->info.b_eof = eof;<br>
> +    if (eof || p.data == nullptr)<br>
> +        return nullptr;<br>
> +    p_access->info.i_pos += p.length;<br>
> +    return p.data.release();<br>
> +}<br>
> +<br>
> +static int Seek(access_t *p_access, uint64_t i_pos)<br>
> +{<br>
> +    auto& torrent = p_access->p_sys->torrent;<br>
> +    torrent.SelectPieces(i_pos);<br>
> +    p_access->info.i_pos = i_pos;<br>
> +    return VLC_SUCCESS;<br>
> +}<br>
> diff --git a/modules/access/torrent/thread.h<br>
> b/modules/access/torrent/thread.h new file mode 100644<br>
> index 0000000..b172761<br>
> --- /dev/null<br>
> +++ b/modules/access/torrent/thread.h<br>
> @@ -0,0 +1,126 @@<br>
> +/**************************************************************************<br>
</div></div>> *** + * Copyright (C) 2014 VLC authors, VideoLAN and Videolabs<br>
<span>> + *<br>
> + * Authors: Jonathan Calmels <<a href="mailto:exxo@videolabs.io" target="_blank">exxo@videolabs.io</a>><br>
> + *<br>
> + * This program is free software; you can redistribute it and/or modify it<br>
> + * under the terms of the GNU Lesser General Public License as published by<br>
> + * the Free Software Foundation; either version 2.1 of the License, or + *<br>
> (at your option) any later version.<br>
> + *<br>
> + * This program is distributed in the hope that it will be useful,<br>
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
> + * GNU Lesser General Public License for more details.<br>
> + *<br>
> + * You should have received a copy of the GNU Lesser General Public License<br>
> + * along with this program; if not, write to the Free Software Foundation,<br>
> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. +<br>
> ***************************************************************************<br>
</span>> **/ +<br>
<span>> +#include <mutex><br>
> +#include <functional><br>
> +<br>
> +#include <vlc_access.h><br>
<br>
</span>Should not be needed.<br>
<div><div><br>
> +#include <vlc_threads.h><br>
> +<br>
> +namespace vlc {<br>
> +<br>
> +class Mutex<br>
> +{<br>
> +    friend class CondVar;<br>
> +<br>
> +    public:<br>
> +        Mutex()<br>
> +        {<br>
> +            vlc_mutex_init(&lock_);<br>
> +        }<br>
> +        ~Mutex()<br>
> +        {<br>
> +            vlc_mutex_destroy(&lock_);<br>
> +        }<br>
> +<br>
> +        void lock()<br>
> +        {<br>
> +            vlc_mutex_lock(&lock_);<br>
> +        }<br>
> +        void unlock() noexcept<br>
> +        {<br>
> +            vlc_mutex_unlock(&lock_);<br>
> +        }<br>
> +<br>
> +    private:<br>
> +        vlc_mutex_t lock_;<br>
> +};<br>
> +<br>
> +class CondVar<br>
> +{<br>
> +    public:<br>
> +        CondVar()<br>
> +        {<br>
> +            vlc_cond_init(&cond_);<br>
> +        }<br>
> +        ~CondVar()<br>
> +        {<br>
> +            vlc_cond_destroy(&cond_);<br>
> +        }<br>
> +<br>
> +        template <class Predicate, typename Rep, typename Period><br>
> +        bool WaitFor(std::unique_lock<Mutex>& m, std::chrono::duration<Rep,<br>
> Period> timeout, Predicate pred);<br>
> +        void Signal()<br>
> +        {<br>
> +            vlc_cond_signal(&cond_);<br>
> +        }<br>
> +<br>
> +    private:<br>
> +        vlc_cond_t  cond_;<br>
> +};<br>
> +<br>
> +template <class Predicate, typename Rep, typename Period><br>
> +bool CondVar::WaitFor(std::unique_lock<Mutex>& m,<br>
> std::chrono::duration<Rep, Period> timeout, Predicate pred) +{<br>
> +    using namespace std::chrono;<br>
> +<br>
> +    auto t = mdate() + duration_cast<microseconds>(timeout).count();<br>
> +    while (!pred())<br>
> +        if (vlc_cond_timedwait(&cond_, &m.mutex()->lock_, t) == ETIMEDOUT)<br>
> +            return pred();<br>
> +    return true;<br>
> +}<br>
> +<br>
> +class JoinableThread<br>
> +{<br>
> +    public:<br>
> +        JoinableThread() = default;<br>
> +        ~JoinableThread()<br>
> +        {<br>
> +            if (joinable_)<br>
> +                vlc_join(thread_, nullptr);<br>
> +        }<br>
> +<br>
> +        template <class Functor><br>
> +        int Start(access_t* access, const Functor& func);<br>
> +<br>
> +    private:<br>
> +        vlc_thread_t  thread_;<br>
> +        bool          joinable_ = false;<br>
> +};<br>
> +<br>
> +template <class Functor><br>
> +int JoinableThread::Start(access_t* access, const Functor& func)<br>
> +{<br>
> +    static std::function<void()> trampoline;<br>
> +<br>
> +    trampoline = func;<br>
> +    auto f = [](void*) -> void* {<br>
> +        trampoline();<br>
> +        return nullptr;<br>
> +    };<br>
> +    if (vlc_clone(&thread_, f, access, VLC_THREAD_PRIORITY_INPUT) !=<br>
> VLC_SUCCESS)<br>
> +        return VLC_EGENERIC;<br>
<br>
</div></div>After so intensely C++11 code, using VLC threads is really weird. Besides, it<br>
won´t get you any benefits, since VLC threads and the C++11 runtime will not<br>
interoperate if the C++ runtime lacks thread support.<br>
<span><br></span></blockquote><div><br></div><div>Agreed, however for windows, the C++11 thread implementation uses winpthread (with mingw) while the VLC implementation does not.</div><div>That being said, I don't mind changing it if that's what you want.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span>
> +<br>
> +    joinable_ = true;<br>
> +    return VLC_SUCCESS;<br>
> +}<br>
> +<br>
> +}<br>
> diff --git a/modules/access/torrent/torrent.cpp<br>
> b/modules/access/torrent/torrent.cpp new file mode 100644<br>
> index 0000000..c39addb<br>
> --- /dev/null<br>
> +++ b/modules/access/torrent/torrent.cpp<br>
> @@ -0,0 +1,284 @@<br>
> +/**************************************************************************<br>
</span>> *** + * Copyright (C) 2014 VLC authors, VideoLAN and Videolabs<br>
<span>> + *<br>
> + * Authors: Jonathan Calmels <<a href="mailto:exxo@videolabs.io" target="_blank">exxo@videolabs.io</a>><br>
> + *<br>
> + * This program is free software; you can redistribute it and/or modify it<br>
> + * under the terms of the GNU Lesser General Public License as published by<br>
> + * the Free Software Foundation; either version 2.1 of the License, or + *<br>
> (at your option) any later version.<br>
> + *<br>
> + * This program is distributed in the hope that it will be useful,<br>
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
> + * GNU Lesser General Public License for more details.<br>
> + *<br>
> + * You should have received a copy of the GNU Lesser General Public License<br>
> + * along with this program; if not, write to the Free Software Foundation,<br>
> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. +<br>
> ***************************************************************************<br>
</span>> **/ +<br>
<div><div>> +#include <cassert><br>
> +#include <cmath><br>
> +#include <cstring><br>
> +#include <algorithm><br>
> +#include <functional><br>
> +#include <fstream><br>
> +<br>
> +#include <vlc_common.h><br>
> +#include <vlc_url.h><br>
> +<br>
> +#include <libtorrent/alert_types.hpp><br>
> +#include <libtorrent/create_torrent.hpp><br>
> +#include <libtorrent/extensions/metadata_transfer.hpp><br>
> +#include <libtorrent/extensions/ut_metadata.hpp><br>
> +#include <libtorrent/magnet_uri.hpp><br>
> +#include <libtorrent/bencode.hpp><br>
> +<br>
> +#include "torrent.h"<br>
> +<br>
> +TorrentAccess::~TorrentAccess()<br>
> +{<br>
> +    stopped_ = true;<br>
> +    session_.pause();<br>
> +    try {<br>
> +        session_.remove_torrent(handle_);<br>
> +    } catch (const lt::libtorrent_exception&) {}<br>
> +}<br>
> +<br>
> +int TorrentAccess::ParseURI(const std::string& uri, lt::add_torrent_params&<br>
> params) +{<br>
> +    lt::error_code ec;<br>
> +<br>
> +    const auto prefix = "magnet:?"s;<br>
> +    const auto uri_decoded =<br>
> std::string{decode_URI_duplicate(uri.c_str())}; +<br>
> +    if (!uri_decoded.compare(0, prefix.size(), prefix)) {<br>
> +        lt::parse_magnet_uri(uri_decoded, params, ec);<br>
> +        if (ec)<br>
> +            return VLC_EGENERIC;<br>
> +    }<br>
> +    else {<br>
> +        params.ti = new lt::torrent_info{uri_decoded, ec};<br>
> +        if (ec)<br>
> +            return VLC_EGENERIC;<br>
> +    }<br>
> +    return VLC_SUCCESS;<br>
> +}<br>
> +<br>
> +int TorrentAccess::RetrieveMetadata()<br>
> +{<br>
> +    lt::error_code ec;<br>
> +<br>
> +    assert(download_dir_ != nullptr);<br>
> +<br>
> +    session_.set_alert_mask(lta::status_notification);<br>
> +    session_.add_extension(&lt::create_metadata_plugin);<br>
> +    session_.add_extension(&lt::create_ut_metadata_plugin);<br>
> +    handle_ = session_.add_torrent(params_, ec);<br>
> +    if (ec)<br>
> +        return VLC_EGENERIC;<br>
> +<br>
> +    Run();<br>
> +    session_.remove_torrent(handle_);<br>
> +<br>
> +    const auto& metadata = handle_.get_torrent_info();<br>
> +    params_.ti = new lt::torrent_info{metadata};<br>
> +<br>
> +    // Create the torrent file.<br>
> +    const auto torrent = lt::create_torrent{metadata};<br>
> +    const auto path = download_dir_.get() + "/"s + <a href="http://metadata.name" target="_blank">metadata.name</a>() +<br>
> ".torrent"; +    std::ofstream file{path, std::ios_base::binary};<br>
> +    if (!file.is_open())<br>
> +        return VLC_EGENERIC;<br>
> +    lt::bencode(std::ostream_iterator<char>{file}, torrent.generate());<br>
> +    uri_ = "torrent://" + path; // Change the initial URI to point to the<br>
> torrent generated. +<br>
> +    return VLC_SUCCESS;<br>
> +}<br>
> +<br>
> +int TorrentAccess::StartDownload(int file_at)<br>
> +{<br>
> +    lt::error_code ec;<br>
> +<br>
> +    assert(has_metadata() && file_at >= 0 && download_dir_ != nullptr);<br>
> +<br>
> +    session_.set_alert_mask(lta::status_notification |<br>
> lta::storage_notification | lta::progress_notification); +<br>
> params_.save_path = download_dir_.get();<br>
> +    params_.storage_mode = lt::storage_mode_allocate;<br>
> +    handle_ = session_.add_torrent(params_, ec);<br>
> +    if (ec)<br>
> +        return VLC_EGENERIC;<br>
> +<br>
> +    file_at_ = file_at;<br>
> +    SelectPieces(0);<br>
> +    handle_.set_sequential_download(true);<br>
> +    status_.state = handle_.status().state;<br>
> +<br>
> +    const auto run = std::bind(std::mem_fn(&TorrentAccess::Run), this);<br>
> +    return thread_.Start(access_, run);<br>
> +}<br>
> +<br>
> +void TorrentAccess::Run()<br>
> +{<br>
> +    std::deque<lt::alert*> alerts;<br>
> +<br>
> +    while (!stopped_) {<br>
> +        if (!session_.wait_for_alert(lt::seconds(1)))<br>
> +            continue;<br>
> +<br>
> +        session_.pop_alerts(&alerts);<br>
> +        for (const auto alert : alerts) {<br>
> +            switch (alert->type()) {<br>
> +                case lt::piece_finished_alert::alert_type: {<br>
> +                    const auto a =<br>
> lt::alert_cast<lt::piece_finished_alert>(alert); +<br>
> msg_Dbg(access_, "Piece finished: %d", a->piece_index); +<br>
>  break;<br>
> +                }<br>
> +                case lt::state_changed_alert::alert_type:<br>
> +                    HandleStateChanged(alert);<br>
> +                    break;<br>
> +                case lt::read_piece_alert::alert_type:<br>
> +                    HandleReadPiece(alert);<br>
> +                    break;<br>
> +                case lt::metadata_received_alert::alert_type: // Magnet<br>
> file only. +                    return;<br>
> +            }<br>
> +        }<br>
> +        alerts.clear();<br>
> +    }<br>
> +}<br>
> +<br>
> +void TorrentAccess::SelectPieces(uint64_t offset)<br>
> +{<br>
> +    assert(has_metadata() && file_at_ >= 0);<br>
> +<br>
> +    const auto& meta = metadata();<br>
> +    const auto& file = meta.file_at(file_at_);<br>
> +    auto req = meta.map_file(file_at_, offset, file.size - offset);<br>
> +    const auto piece_size = meta.piece_length();<br>
> +    const auto num_pieces = meta.num_pieces();<br>
> +    const auto req_pieces = std::ceil((float) (req.length + req.start) /<br>
> piece_size); +<br>
> +    auto lock = std::unique_lock<vlc::Mutex>{queue_.mutex};<br>
> +    queue_.pieces.clear();<br>
> +<br>
> +    for (auto i = 0; i < num_pieces; ++i) {<br>
> +        if (i < req.piece || i >= req.piece + req_pieces) {<br>
> +            handle_.piece_priority(i, 0); // Discard unwanted pieces.<br>
> +            continue;<br>
> +        }<br>
> +<br>
> +        auto len = 0;<br>
> +        auto off = 0;<br>
> +        if (i == req.piece) { // First piece.<br>
> +            off = req.start;<br>
> +            len = (req.length < piece_size - off) ? req.length : piece_size<br>
> - off; +        }<br>
> +        else if (i == req.piece + req_pieces - 1) // Last piece.<br>
> +            len = req.length;<br>
> +        else<br>
> +            len = piece_size;<br>
> +<br>
> +        handle_.piece_priority(i, 7);<br>
> +        queue_.pieces.push_back({i, off, len});<br>
> +        req.length -= len;<br>
> +    }<br>
> +}<br>
> +<br>
> +void TorrentAccess::HandleStateChanged(const lt::alert* alert)<br>
> +{<br>
> +    const auto a = lt::alert_cast<lt::state_changed_alert>(alert);<br>
> +    const char* msg;<br>
> +<br>
> +    switch (a->state) {<br>
> +        case lts::queued_for_checking:<br>
> +            msg = "Queued for checking";<br>
> +            break;<br>
> +        case lts::downloading_metadata:<br>
> +            msg = "Downloading metadata";<br>
> +            break;<br>
> +        case lts::finished:<br>
> +            msg = "Finished";<br>
> +            break;<br>
> +        case lts::allocating:<br>
> +            msg = "Allocating space";<br>
> +            break;<br>
> +        case lts::checking_resume_data:<br>
> +            msg = "Resuming";<br>
> +            break;<br>
> +        case lts::checking_files:<br>
> +            msg = "Checking files";<br>
> +            break;<br>
> +        case lts::seeding:<br>
> +            msg = "Seeding";<br>
> +            break;<br>
> +        case lts::downloading:<br>
> +            msg = "Downloading";<br>
> +            break;<br>
> +        default:<br>
> +            return;<br>
> +    }<br>
> +    msg_Info(access_, "State changed to: %s", msg);<br>
> +<br>
> +    auto lock = std::unique_lock<vlc::Mutex>{status_.mutex};<br>
> +    status_.state = a->state;<br>
> +    status_.cond.Signal();<br>
> +}<br>
> +<br>
> +void TorrentAccess::HandleReadPiece(const lt::alert* alert)<br>
> +{<br>
> +    const auto a = lt::alert_cast<lt::read_piece_alert>(alert);<br>
> +<br>
> +    if (a->buffer == nullptr) { // Read error, try again.<br>
> +        handle_.read_piece(a->piece);<br>
> +        return;<br>
> +    }<br>
> +<br>
> +    auto lock = std::unique_lock<vlc::Mutex>{queue_.mutex};<br>
> +<br>
> +    auto p = std::find_if(std::begin(queue_.pieces),<br>
> std::end(queue_.pieces), +      [a](const Piece& p) { return a->piece ==<br>
> <a href="http://p.id" target="_blank">p.id</a>; }<br>
> +    );<br>
> +    if (p == std::end(queue_.pieces) || p->data != nullptr)<br>
> +        return;<br>
> +<br>
> +    assert(a->size >= p->length);<br>
> +    p->data = {block_Alloc(p->length), block_Release};<br>
> +    std::memcpy(p->data->p_buffer, a->buffer.get() + p->offset, p->length);<br>
> +    if (p->id == queue_.pieces.front().id)<br>
> +        queue_.cond.Signal();<br>
> +}<br>
> +<br>
> +void TorrentAccess::ReadNextPiece(Piece& piece, bool& eof)<br>
> +{<br>
> +    eof = false;<br>
> +<br>
> +    auto s_lock = std::unique_lock<vlc::Mutex>{status_.mutex};<br>
> +    auto cond = status_.cond.WaitFor(s_lock, 500ms, [s = status_.state]{<br>
> +      return s == lts::downloading || s == lts::finished || s ==<br>
> lts::seeding;<br>
> +    });<br>
> +    if (!cond)<br>
> +        return;<br>
> +<br>
> +    s_lock.unlock();<br>
> +<br>
> +    auto q_lock = std::unique_lock<vlc::Mutex>{queue_.mutex};<br>
> +    if (queue_.pieces.empty()) {<br>
> +        eof = true;<br>
> +        return;<br>
> +    }<br>
> +<br>
> +    auto& next_piece = queue_.pieces.front();<br>
> +    if (!next_piece.requested) {<br>
> +        handle_.set_piece_deadline(<a href="http://next_piece.id" target="_blank">next_piece.id</a>, 0,<br>
> lt::torrent_handle::alert_when_available); +        next_piece.requested =<br>
> true;<br>
> +        msg_Dbg(access_, "Piece requested: %d", <a href="http://next_piece.id" target="_blank">next_piece.id</a>);<br>
> +    }<br>
> +    if (!queue_.cond.WaitFor(q_lock, 500ms, [&next_piece]{ return<br>
> next_piece.data != nullptr; }))<br>
> +        return;<br>
> +<br>
> +    piece = std::move(next_piece);<br>
> +    queue_.pieces.pop_front();<br>
> +    msg_Dbg(access_, "Got piece: %d", <a href="http://piece.id" target="_blank">piece.id</a>);<br>
> +}<br>
> diff --git a/modules/access/torrent/torrent.h<br>
> b/modules/access/torrent/torrent.h new file mode 100644<br>
> index 0000000..0938a62<br>
> --- /dev/null<br>
> +++ b/modules/access/torrent/torrent.h<br>
> @@ -0,0 +1,152 @@<br>
> +/**************************************************************************<br>
</div></div>> *** + * Copyright (C) 2014 VLC authors, VideoLAN and Videolabs<br>
<span>> + *<br>
> + * Authors: Jonathan Calmels <<a href="mailto:exxo@videolabs.io" target="_blank">exxo@videolabs.io</a>><br>
> + *<br>
> + * This program is free software; you can redistribute it and/or modify it<br>
> + * under the terms of the GNU Lesser General Public License as published by<br>
> + * the Free Software Foundation; either version 2.1 of the License, or + *<br>
> (at your option) any later version.<br>
> + *<br>
> + * This program is distributed in the hope that it will be useful,<br>
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>
> + * GNU Lesser General Public License for more details.<br>
> + *<br>
> + * You should have received a copy of the GNU Lesser General Public License<br>
> + * along with this program; if not, write to the Free Software Foundation,<br>
> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. +<br>
> ***************************************************************************<br>
</span>> **/ +<br>
<div><div>> +#include <string><br>
> +#include <cstdlib><br>
> +#include <memory><br>
> +#include <deque><br>
> +#include <atomic><br>
> +#include <chrono><br>
> +<br>
> +#ifdef HAVE_CONFIG_H<br>
> +# include "config.h"<br>
> +#endif<br>
> +<br>
> +#include <vlc_common.h><br>
> +#include <vlc_access.h><br>
> +<br>
> +#include <libtorrent/session.hpp><br>
> +#include <libtorrent/torrent_handle.hpp><br>
> +#include <libtorrent/torrent_info.hpp><br>
> +#include <libtorrent/add_torrent_params.hpp><br>
> +<br>
> +#include "thread.h"<br>
> +<br>
> +namespace lt = libtorrent;<br>
> +using namespace std::string_literals;<br>
> +using namespace std::literals::chrono_literals;<br>
> +<br>
> +using lta = lt::alert;<br>
> +using lts = lt::torrent_status;<br>
> +using unique_char_ptr = std::unique_ptr<char, void (*)(void*)>;<br>
> +using unique_block_ptr = std::unique_ptr<block_t, void (*)(block_t*)>;<br>
> +<br>
> +struct Piece<br>
> +{<br>
> +    Piece() : Piece{0, 0, 0} {}<br>
> +    Piece(int i, int off, int len) :<br>
> +        id{i},<br>
> +        offset{off},<br>
> +        length{len},<br>
> +        requested{false},<br>
> +        data{nullptr, block_Release}<br>
> +    {}<br>
> +<br>
> +    int              id;<br>
> +    int              offset;<br>
> +    int              length;<br>
> +    bool             requested;<br>
> +    unique_block_ptr data;<br>
> +};<br>
> +<br>
> +struct PiecesQueue<br>
> +{<br>
> +    vlc::Mutex        mutex;<br>
> +    vlc::CondVar      cond;<br>
> +    std::deque<Piece> pieces;<br>
> +};<br>
> +<br>
> +struct Status<br>
> +{<br>
> +    vlc::Mutex    mutex;<br>
> +    vlc::CondVar  cond;<br>
> +    lts::state_t  state;<br>
> +};<br>
> +<br>
> +class TorrentAccess<br>
> +{<br>
> +    public:<br>
> +        TorrentAccess(access_t* p_access) :<br>
> +            access_{p_access},<br>
> +            file_at_{-1},<br>
> +            stopped_{false},<br>
> +            download_dir_{nullptr, std::free},<br>
> +            uri_{"torrent://"s + p_access->psz_location},<br>
> +            fingerprint_{"VL", PACKAGE_VERSION_MAJOR,<br>
> PACKAGE_VERSION_MINOR, +<br>
> PACKAGE_VERSION_REVISION, PACKAGE_VERSION_EXTRA}, +<br>
> session_{fingerprint_}<br>
> +        {}<br>
> +        ~TorrentAccess();<br>
> +<br>
> +        static int ParseURI(const std::string& uri, lt::add_torrent_params&<br>
> params); +        int RetrieveMetadata();<br>
> +        int StartDownload(int file_at);<br>
> +        void ReadNextPiece(Piece& piece, bool& eof);<br>
> +        void SelectPieces(uint64_t offset);<br>
> +<br>
> +        void set_download_dir(unique_char_ptr&& dir);<br>
> +        void set_parameters(lt::add_torrent_params&& params);<br>
> +        const lt::torrent_info& metadata() const;<br>
> +        bool has_metadata() const;<br>
> +        const std::string& uri() const;<br>
> +<br>
> +    private:<br>
> +        void Run();<br>
> +        void HandleStateChanged(const lt::alert* alert);<br>
> +        void HandleReadPiece(const lt::alert* alert);<br>
> +<br>
> +        access_t*               access_;<br>
> +        int                     file_at_;<br>
> +        std::atomic_bool        stopped_;<br>
> +        unique_char_ptr         download_dir_;<br>
> +        std::string             uri_;<br>
> +        lt::fingerprint         fingerprint_;<br>
> +        lt::session             session_;<br>
> +        PiecesQueue             queue_;<br>
> +        Status                  status_;<br>
> +        lt::add_torrent_params  params_;<br>
> +        lt::torrent_handle      handle_;<br>
> +        vlc::JoinableThread     thread_;<br>
> +};<br>
> +<br>
> +inline void TorrentAccess::set_download_dir(unique_char_ptr&& dir)<br>
> +{<br>
> +    download_dir_ = std::move(dir);<br>
> +}<br>
> +<br>
> +inline void TorrentAccess::set_parameters(lt::add_torrent_params&& params)<br>
> +{<br>
> +    params_ = std::move(params);<br>
> +}<br>
> +<br>
> +inline const lt::torrent_info& TorrentAccess::metadata() const<br>
> +{<br>
> +    return *params_.ti;<br>
> +}<br>
> +<br>
> +inline bool TorrentAccess::has_metadata() const<br>
> +{<br>
> +    return (params_.ti == nullptr) ? false : true;<br>
> +}<br>
> +<br>
> +inline const std::string& TorrentAccess::uri() const<br>
> +{<br>
> +    return uri_;<br>
> +}<br>
<br>
--<br>
</div></div>Rémi Denis-Courmont<br>
<a href="http://www.remlab.net/" target="_blank">http://www.remlab.net/</a><br>
<br>
_______________________________________________<br>
vlc-devel mailing list<br>
To unsubscribe or modify your subscription options:<br>
<a href="https://mailman.videolan.org/listinfo/vlc-devel" target="_blank">https://mailman.videolan.org/listinfo/vlc-devel</a><br>
</blockquote></div><br></div></div>