[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 16:04:02 CET 2015


---
 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()
+
+    add_directory("download_dir", nullptr, "Download directory",
+      "Directory used to store dowloaded files", false)
+
+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);
+
+    try {
+        auto r = open(p_access);
+        if (r != VLC_SUCCESS)
+            delete p_access->p_sys;
+        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;
+
+    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>
+#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;
+
+    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_;
+}
-- 
2.2.2




More information about the vlc-devel mailing list