mirror of
https://github.com/outbackdingo/UltraGrid.git
synced 2026-03-22 09:40:30 +00:00
339 lines
12 KiB
C++
339 lines
12 KiB
C++
/**
|
|
* @file video_compress/dxt_glsl.cpp
|
|
* @author Martin Pulec <pulec@cesnet.cz>
|
|
*/
|
|
/*
|
|
* Copyright (c) 2011-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.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#include "config_unix.h"
|
|
#include "config_win32.h"
|
|
#endif // HAVE_CONFIG_H
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "gl_context.h"
|
|
|
|
#include "debug.h"
|
|
#include "dxt_compress/dxt_encoder.h"
|
|
#include "dxt_compress/dxt_util.h"
|
|
#include "host.h"
|
|
#include "lib_common.h"
|
|
#include "module.h"
|
|
#include "utils/video_frame_pool.h"
|
|
#include "video.h"
|
|
#include "video_compress.h"
|
|
|
|
#include <memory>
|
|
|
|
using namespace std;
|
|
|
|
namespace {
|
|
|
|
struct state_video_compress_rtdxt {
|
|
struct module module_data;
|
|
|
|
struct dxt_encoder **encoder;
|
|
int encoder_count;
|
|
|
|
decoder_t decoder;
|
|
unique_ptr<char []> decoded;
|
|
unsigned int configured:1;
|
|
unsigned int interlaced_input:1;
|
|
codec_t color_spec;
|
|
|
|
int encoder_input_linesize;
|
|
|
|
struct gl_context gl_context;
|
|
|
|
video_frame_pool pool;
|
|
};
|
|
|
|
static int configure_with(struct state_video_compress_rtdxt *s, struct video_frame *frame);
|
|
static void dxt_glsl_compress_done(struct module *mod);
|
|
|
|
static int configure_with(struct state_video_compress_rtdxt *s, struct video_frame *frame)
|
|
{
|
|
unsigned int x;
|
|
enum dxt_format format{};
|
|
|
|
for (x = 0; x < frame->tile_count; ++x) {
|
|
if (vf_get_tile(frame, x)->width != vf_get_tile(frame, 0)->width ||
|
|
vf_get_tile(frame, x)->width != vf_get_tile(frame, 0)->width) {
|
|
|
|
fprintf(stderr,"[RTDXT] Requested to compress tiles of different size!");
|
|
exit_uv(EXIT_FAILURE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (get_bits_per_component(frame->color_spec) > 8) {
|
|
LOG(LOG_LEVEL_NOTICE) << "[RTDXT] Converting from " << get_bits_per_component(frame->color_spec) <<
|
|
" to 8 bits. You may directly capture 8-bit signal to improve performance.\n";
|
|
}
|
|
|
|
s->decoder = NULL;
|
|
codec_t codec_try[3] = {};
|
|
if (s->color_spec == DXT1_YUV) {
|
|
codec_try[0] = UYVY; // RTDXT doesn't convert RGB to (DXT1_)YUV
|
|
if (codec_is_a_rgb(frame->color_spec)) {
|
|
log_msg(LOG_LEVEL_WARNING, "[RTDXT] Compression of a RGB to DXT1_YUV will use CPU conversion!\n");
|
|
}
|
|
} else if (codec_is_a_rgb(frame->color_spec)) {
|
|
codec_try[0] = RGB;
|
|
codec_try[1] = RGBA;
|
|
codec_try[2] = UYVY;
|
|
} else {
|
|
codec_try[0] = UYVY;
|
|
codec_try[1] = RGB;
|
|
codec_try[2] = RGBA;
|
|
}
|
|
for (size_t i = 0; i < sizeof codec_try / sizeof codec_try[0]; ++i) {
|
|
if ((s->decoder = get_decoder_from_to(frame->color_spec, codec_try[i])) != NULL) {
|
|
switch (codec_try[i]) {
|
|
case RGB: format = DXT_FORMAT_RGB; break;
|
|
case RGBA: format = DXT_FORMAT_RGBA; break;
|
|
case UYVY: format = DXT_FORMAT_YUV422; break;
|
|
case VIDEO_CODEC_NONE: break;
|
|
default: assert(0 && "Shouldn't get other codecs than the above.");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!s->decoder) {
|
|
fprintf(stderr, "[RTDXT] Unsupported codec: %d\n", frame->color_spec);
|
|
return FALSE;
|
|
}
|
|
|
|
int data_len = 0;
|
|
|
|
s->encoder = (struct dxt_encoder **) calloc(frame->tile_count, sizeof(struct dxt_encoder *));
|
|
if(s->color_spec == DXT1) {
|
|
for(int i = 0; i < (int) frame->tile_count; ++i) {
|
|
s->encoder[i] =
|
|
dxt_encoder_create(DXT_TYPE_DXT1, frame->tiles[0].width, frame->tiles[0].height, format,
|
|
s->gl_context.legacy);
|
|
}
|
|
data_len = dxt_get_size(frame->tiles[0].width, frame->tiles[0].height, DXT_TYPE_DXT1);
|
|
} else if(s->color_spec == DXT1_YUV) {
|
|
for(int i = 0; i < (int) frame->tile_count; ++i) {
|
|
s->encoder[i] =
|
|
dxt_encoder_create(DXT_TYPE_DXT1_YUV, frame->tiles[0].width, frame->tiles[0].height, format,
|
|
s->gl_context.legacy);
|
|
}
|
|
data_len = dxt_get_size(frame->tiles[0].width, frame->tiles[0].height, DXT_TYPE_DXT1);
|
|
} else if(s->color_spec == DXT5){
|
|
for(int i = 0; i < (int) frame->tile_count; ++i) {
|
|
s->encoder[i] =
|
|
dxt_encoder_create(DXT_TYPE_DXT5_YCOCG, frame->tiles[0].width, frame->tiles[0].height, format,
|
|
s->gl_context.legacy);
|
|
}
|
|
data_len = dxt_get_size(frame->tiles[0].width, frame->tiles[0].height, DXT_TYPE_DXT5_YCOCG);
|
|
}
|
|
s->encoder_count = frame->tile_count;
|
|
|
|
for(int i = 0; i < (int) frame->tile_count; ++i) {
|
|
if(s->encoder[i] == NULL) {
|
|
fprintf(stderr, "[RTDXT] Unable to create decoder.\n");
|
|
exit_uv(EXIT_FAILURE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
s->encoder_input_linesize = frame->tiles[0].width;
|
|
switch(format) {
|
|
case DXT_FORMAT_RGBA:
|
|
s->encoder_input_linesize *= 4;
|
|
break;
|
|
case DXT_FORMAT_RGB:
|
|
s->encoder_input_linesize *= 3;
|
|
break;
|
|
case DXT_FORMAT_YUV422:
|
|
s->encoder_input_linesize *= 2;
|
|
break;
|
|
case DXT_FORMAT_YUV:
|
|
/* not used - just not compilator to complain */
|
|
abort();
|
|
break;
|
|
}
|
|
|
|
assert(data_len > 0);
|
|
assert(s->encoder_input_linesize > 0);
|
|
|
|
struct video_desc compressed_desc;
|
|
compressed_desc = video_desc_from_frame(frame);
|
|
compressed_desc.color_spec = s->color_spec;
|
|
/* We will deinterlace the output frame */
|
|
if(frame->interlacing == INTERLACED_MERGED) {
|
|
compressed_desc.interlacing = PROGRESSIVE;
|
|
s->interlaced_input = TRUE;
|
|
fprintf(stderr, "[DXT compress] Enabling automatic deinterlacing.\n");
|
|
} else {
|
|
s->interlaced_input = FALSE;
|
|
}
|
|
s->pool.reconfigure(compressed_desc, data_len);
|
|
|
|
s->decoded = unique_ptr<char []>(new char[4 * compressed_desc.width * compressed_desc.height]);
|
|
|
|
s->configured = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
struct module *dxt_glsl_compress_init(struct module *parent, const char *opts)
|
|
{
|
|
struct state_video_compress_rtdxt *s;
|
|
|
|
if(strcmp(opts, "help") == 0) {
|
|
printf("DXT GLSL comperssion usage:\n");
|
|
printf("\t-c RTDXT:DXT1\n");
|
|
printf("\t\tcompress with DXT1\n");
|
|
printf("\t-c RTDXT:DXT1_YUV\n");
|
|
printf("\t\tcompress with DXT1_YUV\n");
|
|
printf("\t-c RTDXT:DXT5\n");
|
|
printf("\t\tcompress with DXT5 YCoCg\n");
|
|
return static_cast<module*>(INIT_NOERR);
|
|
}
|
|
|
|
s = new state_video_compress_rtdxt();
|
|
|
|
if (strcasecmp(opts, "DXT5") == 0) {
|
|
s->color_spec = DXT5;
|
|
} else if (strcasecmp(opts, "DXT1") == 0) {
|
|
s->color_spec = DXT1;
|
|
} else if (strcasecmp(opts, "DXT1_YUV") == 0) {
|
|
s->color_spec = DXT1_YUV;
|
|
} else if (opts[0] == '\0') {
|
|
s->color_spec = DXT1;
|
|
} else {
|
|
fprintf(stderr, "Unknown compression: %s\n", opts);
|
|
delete s;
|
|
return NULL;
|
|
}
|
|
|
|
if(!init_gl_context(&s->gl_context, GL_CONTEXT_ANY)) {
|
|
fprintf(stderr, "[RTDXT] Error initializing GL context");
|
|
delete s;
|
|
return NULL;
|
|
}
|
|
|
|
gl_context_make_current(NULL);
|
|
|
|
module_init_default(&s->module_data);
|
|
s->module_data.cls = MODULE_CLASS_DATA;
|
|
s->module_data.priv_data = s;
|
|
s->module_data.deleter = dxt_glsl_compress_done;
|
|
module_register(&s->module_data, parent);
|
|
|
|
return &s->module_data;
|
|
}
|
|
|
|
shared_ptr<video_frame> dxt_glsl_compress(struct module *mod, shared_ptr<video_frame> tx)
|
|
{
|
|
struct state_video_compress_rtdxt *s = (struct state_video_compress_rtdxt *) mod->priv_data;
|
|
int i;
|
|
unsigned char *line1, *line2;
|
|
|
|
unsigned int x;
|
|
|
|
gl_context_make_current(&s->gl_context);
|
|
|
|
if(!s->configured) {
|
|
int ret;
|
|
ret = configure_with(s, tx.get());
|
|
if(!ret)
|
|
return NULL;
|
|
}
|
|
|
|
shared_ptr<video_frame> out_frame = s->pool.get_frame();
|
|
|
|
for (x = 0; x < tx->tile_count; ++x) {
|
|
struct tile *in_tile = vf_get_tile(tx.get(), x);
|
|
struct tile *out_tile = vf_get_tile(out_frame.get(), x);
|
|
|
|
line1 = (unsigned char *) in_tile->data;
|
|
line2 = (unsigned char *) s->decoded.get();
|
|
|
|
for (i = 0; i < (int) in_tile->height; ++i) {
|
|
s->decoder(line2, line1, s->encoder_input_linesize,
|
|
0, 8, 16);
|
|
line1 += vc_get_linesize(in_tile->width, tx->color_spec);
|
|
line2 += s->encoder_input_linesize;
|
|
}
|
|
|
|
if(s->interlaced_input)
|
|
vc_deinterlace((unsigned char *) s->decoded.get(), s->encoder_input_linesize,
|
|
in_tile->height);
|
|
|
|
dxt_encoder_compress(s->encoder[x],
|
|
(unsigned char *) s->decoded.get(),
|
|
(unsigned char *) out_tile->data);
|
|
}
|
|
|
|
gl_context_make_current(NULL);
|
|
|
|
return out_frame;
|
|
}
|
|
|
|
static void dxt_glsl_compress_done(struct module *mod)
|
|
{
|
|
struct state_video_compress_rtdxt *s = (struct state_video_compress_rtdxt *) mod->priv_data;
|
|
|
|
if(s->encoder) {
|
|
for(int i = 0; i < s->encoder_count; ++i) {
|
|
if(s->encoder[i])
|
|
dxt_encoder_destroy(s->encoder[i]);
|
|
}
|
|
}
|
|
|
|
destroy_gl_context(&s->gl_context);
|
|
delete s;
|
|
}
|
|
|
|
const struct video_compress_info rtdxt_info = {
|
|
"RTDXT",
|
|
dxt_glsl_compress_init,
|
|
dxt_glsl_compress,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
REGISTER_MODULE(rtdxt, &rtdxt_info, LIBRARY_CLASS_VIDEO_COMPRESS, VIDEO_COMPRESS_ABI_VERSION);
|
|
|
|
} // end of anonymous namespace
|
|
|