[vlc-devel] [WIP PATCH 4/4] access: add new access for torrent files and magnet links

Rémi Denis-Courmont remi at remlab.net
Thu Jan 29 16:57:04 CET 2015


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.

> +
> +    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/




More information about the vlc-devel mailing list