<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(<::create_metadata_plugin);<br>
> + session_.add_extension(<::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>