/** * @file video_display/dummy.cpp * @author Martin Pulec */ /* * Copyright (c) 2015-2021 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 "config.h" #include "config_unix.h" #include "config_win32.h" #include "debug.h" #include "lib_common.h" #include "rang.hpp" #include "video.h" #include "video_codec.h" #include "video_display.h" #include #include #include #include #include #include #include const size_t DEFAULT_DUMP_LEN = 32; const constexpr char *MOD_NAME = "[dummy] "; using namespace std; using namespace std::chrono; using namespace std::string_literals; using rang::fg; using rang::style; struct dummy_display_state { struct video_frame *f = nullptr; vector codecs = {I420, UYVY, YUYV, v210, R10k, R12L, RGBA, RGB, BGR, RG48}; vector rgb_shift = DEFAULT_RGB_SHIFT_INIT; size_t dump_bytes = 0; bool dump_to_file = false; int dump_to_file_skip_frames = 0; }; static auto display_dummy_init(struct module * /* parent */, const char *cfg, unsigned int /* flags */) -> void * { if ("help"s == cfg) { cout << "Usage:\n"; cout << "\t" << style::bold << fg::red << "-d dummy" << fg::reset << "[:codec=][:rgb_shift=,,][:hexdump[=]][:dump_to_file[=skip=]]\n" << style::reset; cout << "where\n"; cout << "\t" << style::bold << "" << style::reset << " - force the use of a codec instead of default set\n"; cout << "\t" << style::bold << "rgb_shift" << style::reset << " - if using output codec RGBA, use specified shifts instead of default (" << DEFAULT_R_SHIFT << ", " << DEFAULT_G_SHIFT << ", " << DEFAULT_B_SHIFT << ")\n"; cout << "\t" << style::bold << "hexdump[=]" << style::reset << " - dump first n (default " << DEFAULT_DUMP_LEN << ") bytes of every frame in hexadecimal format\n"; cout << "\t" << style::bold << "dump_to_file" << style::reset << " - dump first frame to file dummy. (optionally skip first frames)\n"; return static_cast(&display_init_noerr); } auto s = make_unique(); auto *ccpy = static_cast(alloca(strlen(cfg) + 1)); strcpy(ccpy, cfg); char *item = nullptr; char *save_ptr = nullptr; while ((item = strtok_r(ccpy, ":", &save_ptr)) != nullptr) { if (strstr(item, "codec=") != nullptr) { s->codecs.clear(); s->codecs.push_back(get_codec_from_name(item + "codec="s.length())); if (s->codecs[0] == VIDEO_CODEC_NONE) { LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Wrong codec spec!\n"; return nullptr; } } else if (strstr(item, "dump_to_file") != nullptr) { s->dump_to_file = true; if (strstr(item, "dump_to_file=skip=") != nullptr) { s->dump_to_file_skip_frames = stoi(item + "dump_to_file=skip="s.length()); } } else if (strstr(item, "hexdump") != nullptr) { if (strstr(item, "hexdump=") != nullptr) { s->dump_bytes = stol(item + strlen("hexdump="), nullptr, 0); } else { s->dump_bytes = DEFAULT_DUMP_LEN; } } else if (strstr(item, "rgb_shift=") != nullptr) { item += strlen("rgb_shift="); size_t len; s->rgb_shift[0] = stoi(item, &len); item += len + 1; s->rgb_shift[1] = stoi(item, &len); item += len + 1; s->rgb_shift[2] = stoi(item, &len); } else { LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Unrecognized option: " << item << "\n"; return nullptr; } ccpy = nullptr; } return static_cast(s.release()); } static void display_dummy_run(void *) { } static void display_dummy_done(void *state) { auto s = (dummy_display_state *) state; vf_free(s->f); delete s; } static struct video_frame *display_dummy_getf(void *state) { return ((dummy_display_state *) state)->f; } static void dump_buf(unsigned char *buf, size_t len, int block_size) { printf("Frame content: "); for (size_t i = 0; i < len; ++i) { printf("%02hhx ", *buf++); if (block_size > 0 && (i + 1) % block_size == 0) { printf(" "); } } printf("\n"); } static int display_dummy_putf(void *state, struct video_frame *frame, long long flags) { if (flags == PUTF_DISCARD || frame == nullptr) { return 0; } auto s = (dummy_display_state *) state; if (s->dump_bytes > 0) { dump_buf(reinterpret_cast(frame->tiles[0].data), min(frame->tiles[0].data_len, s->dump_bytes), get_pf_block_bytes(frame->color_spec)); } if (s->dump_to_file) { if (s->dump_to_file_skip_frames-- == 0) { std::string filename = "dummy."s + get_codec_file_extension(frame->color_spec); std::ofstream out(filename, std::ifstream::out | std::ifstream::binary); out.write(frame->tiles[0].data, frame->tiles[0].data_len); LOG(LOG_LEVEL_NOTICE) << MOD_NAME << "Written dump to file " << filename << "\n"; s->dump_to_file = false; } } return 0; } static auto display_dummy_get_property(void *state, int property, void *val, size_t *len) { auto *s = static_cast(state); switch (property) { case DISPLAY_PROPERTY_CODECS: if (s->codecs.size() * sizeof s->codecs[0] > *len) { return FALSE; } *len = s->codecs.size() * sizeof(codec_t); memcpy(val, s->codecs.data(), *len); break; case DISPLAY_PROPERTY_RGB_SHIFT: if (s->rgb_shift.size() * sizeof s->rgb_shift[0] > *len) { return FALSE; } *len = s->rgb_shift.size() * sizeof(s->rgb_shift[0]); memcpy(val, s->rgb_shift.data(), *len); break; default: return FALSE; } return TRUE; } static int display_dummy_reconfigure(void *state, struct video_desc desc) { dummy_display_state *s = (dummy_display_state *) state; vf_free(s->f); s->f = vf_alloc_desc_data(desc); return TRUE; } static void display_dummy_put_audio_frame(void *, const struct audio_frame *) { } static int display_dummy_reconfigure_audio(void *, int, int, int) { return TRUE; } static void display_dummy_probe(struct device_info **available_cards, int *count, void (**deleter)(void *)) { UNUSED(deleter); *available_cards = nullptr; *count = 0; } static const struct video_display_info display_dummy_info = { display_dummy_probe, display_dummy_init, display_dummy_run, display_dummy_done, display_dummy_getf, display_dummy_putf, display_dummy_reconfigure, display_dummy_get_property, display_dummy_put_audio_frame, display_dummy_reconfigure_audio, DISPLAY_DOESNT_NEED_MAINLOOP, MOD_NAME, }; REGISTER_MODULE(dummy, &display_dummy_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION);