[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(<::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_;
+}
--
2.2.2
More information about the vlc-devel
mailing list