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