diff --git a/src/video_display/multiplier.cpp b/src/video_display/multiplier.cpp new file mode 100644 index 000000000..f84f222c3 --- /dev/null +++ b/src/video_display/multiplier.cpp @@ -0,0 +1,299 @@ +/** + * @file video_display/multiplier.cpp + * @author Martin Pulec + * @author Martin Piatka + */ +/* + * Copyright (c) 2014-2015 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. + */ +/** + * @todo + * Rewrite the code to have more clear state machine! + */ + +#include "config.h" +#include "config_unix.h" +#include "config_win32.h" +#include "debug.h" +#include "lib_common.h" +#include "video.h" +#include "video_display.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +static constexpr int BUFFER_LEN = 5; +static constexpr chrono::milliseconds SOURCE_TIMEOUT(500); +static constexpr unsigned int IN_QUEUE_MAX_BUFFER_LEN = 5; + +struct sub_display { + struct display *real_display; + pthread_t thread_id; +}; + +struct state_multiplier_common { + ~state_multiplier_common() { + + for(auto& disp : displays){ + display_done(disp); + } + + for (auto && ssrc_map : frames) { + for (auto && frame : ssrc_map.second) { + vf_free(frame); + } + } + } + + std::vector displays; + + struct video_desc display_desc; + + queue incoming_queue; + condition_variable in_queue_decremented_cv; + //map > frames; + //unordered_map disabled_ssrc; + + mutex lock; + condition_variable cv; + + struct module *parent; +}; + +struct state_multiplier { + shared_ptr common; + struct video_desc desc; +}; + +static struct display *display_multiplier_fork(void *state) +{ + shared_ptr s = ((struct state_multiplier *)state)->common; + struct display *out; + char fmt[2 + sizeof(void *) * 2 + 1] = ""; + snprintf(fmt, sizeof fmt, "%p", state); + + int rc = initialize_video_display(s->parent, + "multiplier", fmt, 0, NULL, &out); + if (rc == 0) return out; else return NULL; + + return out; +} + +static void *display_multiplier_init(struct module *parent, const char *fmt, unsigned int flags) +{ + struct state_multiplier *s; + char *fmt_copy = NULL; + const char *requested_display = "gl"; + const char *cfg = NULL; + + s = new state_multiplier(); + + if (fmt && strlen(fmt) > 0) { + if (isdigit(fmt[0])) { // fork + struct state_multiplier *orig; + sscanf(fmt, "%p", &orig); + s->common = orig->common; + return s; + } else { + fmt_copy = strdup(fmt); + requested_display = fmt_copy; + char *delim = strchr(fmt_copy, ':'); + if (delim) { + *delim = '\0'; + cfg = delim + 1; + } + } + } + s->common = shared_ptr(new state_multiplier_common()); + assert (initialize_video_display(parent, requested_display, cfg, flags, NULL, &s->common->real_display) == 0); + free(fmt_copy); + + int ret = pthread_create(&s->common->thread_id, NULL, (void *(*)(void *)) display_run, + s->common->real_display); + assert (ret == 0); + + s->common->parent = parent; + + return s; +} + +static void check_reconf(struct state_multiplier_common *s, struct video_desc desc) +{ + if (!video_desc_eq(desc, s->display_desc)) { + s->display_desc = desc; + fprintf(stderr, "RECONFIGURED\n"); + for(auto& disp : displays){ + display_reconfigure(disp.real_display, s->display_desc, VIDEO_NORMAL); + } + } +} + +static void display_multiplier_run(void *state) +{ + shared_ptr s = ((struct state_multiplier *)state)->common; + + while (1) { + struct video_frame *frame; + { + unique_lock lg(s->lock); + s->cv.wait(lg, [s]{return s->incoming_queue.size() > 0;}); + frame = s->incoming_queue.front(); + s->incoming_queue.pop(); + s->in_queue_decremented_cv.notify_one(); + } + + if (!frame) { + //TODO poison all displays + display_put_frame(s->real_display, NULL, PUTF_BLOCKING); + break; + } + + check_reconf(s.get(), video_desc_from_frame(frame)); + + struct video_frame *real_display_frame = display_get_frame(s->real_display); + memcpy(real_display_frame->tiles[0].data, frame->tiles[0].data, frame->tiles[0].data_len); + vf_free(frame); + real_display_frame->ssrc = s->current_ssrc; + display_put_frame(s->real_display, real_display_frame, PUTF_BLOCKING); + } + + pthread_join(s->thread_id, NULL); +} + +static void display_multiplier_done(void *state) +{ + struct state_multiplier *s = (struct state_multiplier *)state; + delete s; +} + +static struct video_frame *display_multiplier_getf(void *state) +{ + struct state_multiplier *s = (struct state_multiplier *)state; + + return vf_alloc_desc_data(s->desc); +} + +static int display_multiplier_putf(void *state, struct video_frame *frame, int flags) +{ + shared_ptr s = ((struct state_multiplier *)state)->common; + + if (flags == PUTF_DISCARD) { + vf_free(frame); + } else { + unique_lock lg(s->lock); + if (s->incoming_queue.size() >= IN_QUEUE_MAX_BUFFER_LEN) { + fprintf(stderr, "Proxy: queue full!\n"); + } + if (flags == PUTF_NONBLOCK && s->incoming_queue.size() >= IN_QUEUE_MAX_BUFFER_LEN) { + return 1; + } + s->in_queue_decremented_cv.wait(lg, [s]{return s->incoming_queue.size() < IN_QUEUE_MAX_BUFFER_LEN;}); + s->incoming_queue.push(frame); + lg.unlock(); + s->cv.notify_one(); + } + + return 0; +} + +static int display_multiplier_get_property(void *state, int property, void *val, size_t *len) +{ + //TODO Figure out forking, for now just disable multi. sources + shared_ptr s = ((struct state_multiplier *)state)->common; + if (property == DISPLAY_PROPERTY_SUPPORTS_MULTI_SOURCES) { +#if 0 + ((struct multi_sources_supp_info *) val)->val = true; + ((struct multi_sources_supp_info *) val)->fork_display = display_multiplier_fork; + ((struct multi_sources_supp_info *) val)->state = state; + *len = sizeof(struct multi_sources_supp_info); + return TRUE; +#endif + return FALSE; + + } else { + return display_get_property(s->real_display, property, val, len); + } +} + +static int display_multiplier_reconfigure(void *state, struct video_desc desc) +{ + struct state_multiplier *s = (struct state_multiplier *) state; + + s->desc = desc; + + return 1; +} + +static void display_multiplier_put_audio_frame(void *state, struct audio_frame *frame) +{ + UNUSED(state); + UNUSED(frame); +} + +static int display_multiplier_reconfigure_audio(void *state, int quant_samples, int channels, + int sample_rate) +{ + UNUSED(state); + UNUSED(quant_samples); + UNUSED(channels); + UNUSED(sample_rate); + + return FALSE; +} + +static const struct video_display_info display_multiplier_info = { + [](struct device_info **available_cards, int *count) { + *available_cards = nullptr; + *count = 0; + }, + display_multiplier_init, + display_multiplier_run, + display_multiplier_done, + display_multiplier_getf, + display_multiplier_putf, + display_multiplier_reconfigure, + display_multiplier_get_property, + display_multiplier_put_audio_frame, + display_multiplier_reconfigure_audio, +}; + +REGISTER_MODULE(multiplier, &display_multiplier_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); +