/** * @file video_rxtx.cpp * @author Martin Pulec */ /* * Copyright (c) 2013-2023 CESNET z.s.p.o. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, is permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of CESNET nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include "debug.h" #include "export.h" #include "host.h" #include "lib_common.h" #include "messaging.h" #include "module.h" #include "pdb.h" #include "rtp/rtp.h" #include "rtp/video_decoders.h" #include "rtp/pbuf.h" #include "tfrc.h" #include "transmit.h" #include "tv.h" #include "utils/thread.h" #include "utils/vf_split.h" #include "video.h" #include "video_compress.h" #include "video_decompress.h" #include "video_display.h" #include "video_rxtx.hpp" using std::map; using std::shared_ptr; using std::string; video_rxtx::video_rxtx(map const ¶ms): m_port_id("default"), m_rxtx_mode(params.at("rxtx_mode").i), m_parent(static_cast(params.at("parent").ptr)), m_frames_sent(0ull), m_compression(nullptr), m_exporter(static_cast(params.at("exporter").ptr)), m_thread_id(), m_poisoned(false), m_joined(true) { module_init_default(&m_sender_mod); m_sender_mod.cls = MODULE_CLASS_SENDER; module_register(&m_sender_mod, static_cast(params.at("parent").ptr)); module_init_default(&m_receiver_mod); m_receiver_mod.cls = MODULE_CLASS_RECEIVER; module_register(&m_receiver_mod, static_cast(params.at("parent").ptr)); try { int ret = compress_init(&m_sender_mod, static_cast(params.at("compression").ptr), &m_compression); if(ret != 0) { if(ret < 0) { throw string("Error initializing compression."); } if(ret > 0) { throw EXIT_SUCCESS; } } pthread_mutex_init(&m_lock, NULL); } catch (...) { if (m_compression) { compress_done(m_compression); } module_done(&m_receiver_mod); module_done(&m_sender_mod); throw; } } static void should_exit_video_rxtx(void *state) { video_rxtx *s = (video_rxtx *) state; s->m_should_exit = true; } video_rxtx::~video_rxtx() { join(); if (!m_poisoned && m_compression) { send(NULL); compress_pop(m_compression); } compress_done(m_compression); module_done(&m_receiver_mod); module_done(&m_sender_mod); } void video_rxtx::start() { register_should_exit_callback(m_parent, should_exit_video_rxtx, this); if (pthread_create (&m_thread_id, NULL, video_rxtx::sender_thread, (void *) this) != 0) { throw string("Unable to create sender thread!\n"); } m_joined = false; } void video_rxtx::join() { if (m_joined) { return; } send(NULL); // pass poisoned pill pthread_join(m_thread_id, NULL); unregister_should_exit_callback(m_parent, should_exit_video_rxtx, this); m_joined = true; } const char *video_rxtx::get_long_name(string const & short_name) { auto vri = static_cast(load_library(short_name.c_str(), LIBRARY_CLASS_VIDEO_RXTX, VIDEO_RXTX_ABI_VERSION)); if (vri) { return vri->long_name; } else { return ""; } } void video_rxtx::send(shared_ptr frame) { if (!frame && m_poisoned) { return; } compress_frame(m_compression, frame); if (!frame) { m_poisoned = true; } } void *video_rxtx::sender_thread(void *args) { return static_cast(args)->sender_loop(); } void video_rxtx::check_sender_messages() { // process external messages struct message *msg_external; while((msg_external = check_message(&m_sender_mod))) { struct response *r = process_sender_message((struct msg_sender *) msg_external); free_message(msg_external, r); } } void *video_rxtx::sender_loop() { set_thread_name(__func__); struct video_desc saved_vid_desc; memset(&saved_vid_desc, 0, sizeof(saved_vid_desc)); while(1) { check_sender_messages(); shared_ptr tx_frame; tx_frame = compress_pop(m_compression); if (!tx_frame) { break; } export_video(m_exporter, tx_frame.get()); send_frame(std::move(tx_frame)); m_frames_sent += 1; } check_sender_messages(); return NULL; } video_rxtx *video_rxtx::create(string const & proto, std::map const ¶ms) { auto vri = static_cast(load_library(proto.c_str(), LIBRARY_CLASS_VIDEO_RXTX, VIDEO_RXTX_ABI_VERSION)); if (!vri) { return nullptr; } auto ret = vri->create(params); if (!ret) { return nullptr; } ret->start(); return ret; } void video_rxtx::list(bool full) { printf("Available TX protocols:\n"); list_modules(LIBRARY_CLASS_VIDEO_RXTX, VIDEO_RXTX_ABI_VERSION, full); }