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