Files
UltraGrid/src/video_display/dummy.cpp
Martin Pulec 00801fa3d4 video display: updated putf api - use timeout
Replace blocking/nonblock/discard flags with numerical timeout for
fine-grainer control of latency.

The original flags are kept as convenience macros so the API changes is
only small as long as non-block (default) or blocking is used.
2022-10-05 16:24:24 +02:00

245 lines
9.4 KiB
C++

/**
* @file video_display/dummy.cpp
* @author Martin Pulec <pulec@cesnet.cz>
*/
/*
* 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 <algorithm>
#include <chrono>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
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<codec_t> codecs = {I420, UYVY, YUYV, v210, R10k, R12L, RGBA, RGB, BGR, RG48};
vector<int> 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=<codec>][:rgb_shift=<r>,<g>,<b>][:hexdump[=<n>]][:dump_to_file[=skip=<n>]]\n" << style::reset;
cout << "where\n";
cout << "\t" << style::bold << "<codec>" << 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[=<n>]" << 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.<ext> (optionally skip <n> first frames)\n";
return static_cast<void *>(&display_init_noerr);
}
auto s = make_unique<dummy_display_state>();
auto *ccpy = static_cast<char *>(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<void *>(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<unsigned char *>(frame->tiles[0].data), min<size_t>(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<dummy_display_state *>(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);