UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
temporal-deint.c
Go to the documentation of this file.
1
10/*
11 * Copyright (c) 2012-2023 CESNET, z. s. p. o.
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, is permitted provided that the following conditions
16 * are met:
17 *
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 *
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 *
25 * 3. Neither the name of CESNET nor the names of its contributors may be
26 * used to endorse or promote products derived from this software without
27 * specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
30 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
31 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
32 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
33 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
34 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
35 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
36 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
39 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
40 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 */
42
43#ifdef HAVE_CONFIG_H
44#include "config.h"
45#include "config_unix.h"
46#include "config_win32.h"
47#endif
48
49#include <stdlib.h>
50
51#include "debug.h"
52#include "lib_common.h"
53#include "tv.h"
54#include "utils/color_out.h"
55#include "utils/text.h"
56#include "video.h"
57#include "video_display.h"
58#include "vo_postprocess.h"
59
60#define MOD_NAME "[temporal deint] "
61#define TIMEOUT "20ms"
62#define DFR_DEINTERLACE_IMPOSSIBLE_MSG_ID 0x27ff0a78
63
64enum algo { DF, BOB, LINEAR };
65
77
78static void print_common_opts() {
79 color_printf("\t" TBOLD("force ") " - apply deinterlacing even if input is not interlaced\n");
80 color_printf("\t" TBOLD("nodelay") " - do not delay the other frame to keep timing. Both frames are output in burst. May not work correctly (depends on display).\n");
81}
82
83static void df_usage()
84{
85 char desc[] = TBOLD("double-framerate") " is an interleaver that "
86 "creates creates a progressive stream by consecutive "
87 "interleaving fields, eg. f1f2f3f4f5f6 -> \"f1-- f1f2 f3f2 "
88 "f3f4 f5f4 f5f6 --f6\". So saw-like artifacts will still occur "
89 "and blending can be used.\n\n";
91 color_printf("Usage:\n");
92 color_printf("\t" TBOLD(TRED("-p double_framerate") "[:d][:nodelay]") "\n");
93 color_printf("\nwhere:\n");
94 color_printf("\t" TBOLD("d ") " - blend the output\n");
96}
97
98static void * init_common(enum algo algo, const char *config) {
99 bool deinterlace = false;
100 bool force = false;
101 bool nodelay = false;
102
103 if (strcmp(config, "d") == 0) {
104 deinterlace = true;
105 } else if (strcmp(config, "nodelay") == 0) {
106 nodelay = true;
107 } else if (strcmp(config, "force") == 0) {
108 force = true;
109 } else if (strlen(config) > 0) {
110 log_msg(LOG_LEVEL_ERROR, "Unknown config: %s\n", config);
111 return NULL;
112 }
113
114 struct state_df *s = calloc(1, sizeof *s);
115 assert(s != NULL);
116 s->algo = algo;
117
118 s->in = vf_alloc(1);
119 s->buffers[0] = s->buffers[1] = NULL;
120 s->buffer_current = 0;
122 s->force = force;
123 s->nodelay = nodelay;
124
125 if (s->nodelay && get_commandline_param("decoder-drop-policy") == NULL) {
126 log_msg(LOG_LEVEL_NOTICE, MOD_NAME "nodelay option used, setting drop policy to %s timeout.\n", TIMEOUT);
127 set_commandline_param("decoder-drop-policy", TIMEOUT);
128 }
129
130 return s;
131}
132
133static void * df_init(const char *config) {
134 if (strcmp(config, "help") == 0) {
135 df_usage();
136 return NULL;
137 }
138 return init_common(DF, config);
139}
140
141static void * bob_init(const char *config) {
142 if (strcmp(config, "help") == 0) {
143 color_printf("Deinterlacer " TBOLD("bob") " is a simple doubler. It doubles "
144 "every field line to achieve full resolution.\n\n");
145 color_printf("Usage:\n");
146 color_printf("\t" TBOLD(TRED("-p deinterlace_bob")) "\n");
147 return NULL;
148 }
149 return init_common(BOB, config);
150}
151
152static void * linear_init(const char *config) {
153 if (strcmp(config, "help") == 0) {
154 color_printf(TBOLD("Linear") " deinterlacer doubles every field to "
155 "full resolution by interpolating missing lines.\n\n");
156 color_printf("Usage:\n");
157 color_printf("\t" TBOLD(TRED("-p deinterlace_linear")) "\n");
158 return NULL;
159 }
160 return init_common(LINEAR, config);
161}
162
163static bool common_get_property(void *state, int property, void *val, size_t *len)
164{
165 UNUSED(state);
167 UNUSED(val);
168 UNUSED(len);
169
170 return false;
171}
172
173static bool
175{
176 struct state_df *s = (struct state_df *) state;
177 struct tile *in_tile = vf_get_tile(s->in, 0);
178
179 free(s->buffers[0]);
180 free(s->buffers[1]);
181
183 s->in->fps = desc.fps;
185 if (desc.interlacing != INTERLACED_MERGED && !s->force) {
186 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Warning: %s video detected. This filter is intended "
187 "mainly for interlaced merged video. Framerate will be needlessly doubled.\n",
189 }
190
191 in_tile->width = desc.width;
192 in_tile->height = desc.height;
193
195 desc.height;
196
197 s->buffers[0] = (char *) malloc(in_tile->data_len);
198 s->buffers[1] = (char *) malloc(in_tile->data_len);
199 in_tile->data = s->buffers[s->buffer_current];
200
201 return true;
202}
203
204static struct video_frame * common_getf(void *state)
205{
206 struct state_df *s = (struct state_df *) state;
207
208 s->buffer_current = (s->buffer_current + 1) % 2;
209 s->in->tiles[0].data = s->buffers[s->buffer_current];
210
211 return s->in;
212}
213
215static void perform_df(struct state_df *s, struct video_frame *in, struct video_frame *out, int req_pitch)
216{
217 if(in != NULL) {
218 char *src = s->buffers[(s->buffer_current + 1) % 2] + vc_get_linesize(s->in->tiles[0].width, s->in->color_spec);
219 char *dst = out->tiles[0].data + req_pitch;
220 for (unsigned y = 0; y < out->tiles[0].height; y += 2) {
221 memcpy(dst, src, vc_get_linesize(s->in->tiles[0].width, s->in->color_spec));
222 dst += 2 * req_pitch;
223 src += 2 * vc_get_linesize(s->in->tiles[0].width, s->in->color_spec);
224 }
225 src = s->buffers[s->buffer_current];
226 dst = out->tiles[0].data;
227 for (unsigned y = 1; y < out->tiles[0].height; y += 2) {
228 memcpy(dst, src, vc_get_linesize(s->in->tiles[0].width, s->in->color_spec));
229 dst += 2 * req_pitch;
230 src += 2 * vc_get_linesize(s->in->tiles[0].width, s->in->color_spec);
231 }
232 } else {
233 char *src = s->buffers[s->buffer_current];
234 char *dst = out->tiles[0].data;
235 for (unsigned y = 0; y < out->tiles[0].height; ++y) {
236 memcpy(dst, src, vc_get_linesize(s->in->tiles[0].width, s->in->color_spec));
237 dst += req_pitch;
238 src += vc_get_linesize(s->in->tiles[0].width, s->in->color_spec);
239 }
240 }
241
242 if (s->deinterlace) {
244 (unsigned char *) out->tiles[0].data, vc_get_linesize(out->tiles[0].width, out->color_spec),
245 (unsigned char *) out->tiles[0].data, vc_get_linesize(out->tiles[0].width, out->color_spec),
246 out->tiles[0].height)) {
247 log_msg_once(LOG_LEVEL_ERROR, DFR_DEINTERLACE_IMPOSSIBLE_MSG_ID, MOD_NAME "Cannot deinterlace, unsupported pixel format '%s'!\n", get_codec_name(out->color_spec));
248 }
249 }
250}
251
252static void perform_bob(struct state_df *s, struct video_frame *in, struct video_frame *out, int pitch)
253{
254 int linesize = vc_get_linesize(s->in->tiles[0].width, s->in->color_spec);
255 char *dst = out->tiles[0].data;
256 char *src = s->buffers[s->buffer_current] + (in ? 0 : linesize);
257 if (in == NULL) {
258 memcpy(dst, src, linesize); // copy first line up
259 dst += pitch; // then copy every even line to subsequent odd line
260 }
261 unsigned y = in ? 0 : 1;
262 for ( ; y < out->tiles[0].height - 1; y += 2) {
263 memcpy(dst, src, linesize);
264 memcpy(dst + pitch, src, linesize);
265 dst += 2 * pitch;
266 src += 2 * linesize;
267 }
268 if (y < out->tiles[0].height) {
269 memcpy(dst, dst - pitch, linesize);
270 }
271}
272
279static bool avg_lines(codec_t codec, size_t linesize, char *src1, char *src2, char *dst)
280{
281 char *s1 = (char *) src1;
282 char *s2 = (char *) src2;
283 char *d = (char *) dst;
285 return false;
286 }
287 int bpp = get_bits_per_component(codec);
288 if (bpp == 8 || bpp == 16) {
289 size_t x = 0;
290 if (bpp == 8) {
291 typedef unsigned char v16uc __attribute__ ((vector_size (16)));
292 for ( ; x < linesize / 16; ++x) {
293 v16uc i1, i2;
294 memcpy(&i1, s1, sizeof i1);
295 memcpy(&i2, s2, sizeof i2);
296 v16uc res = ((i1 / 2) + (i2 / 2) + (i1 % 2 + i1 % 2) / 2);
297 memcpy(d, &res, sizeof res);
298 s1 += 16;
299 s2 += 16;
300 d += 16;
301 }
302 } else {
303 typedef unsigned short v16us __attribute__ ((vector_size (16)));
304 for ( ; x < linesize / 16; ++x) {
305 v16us i1, i2;
306 memcpy(&i1, s1, sizeof i1);
307 memcpy(&i2, s2, sizeof i2);
308 v16us res = ((i1 / 2) + (i2 / 2) + (i1 % 2 + i1 % 2) / 2);
309 memcpy(d, &res, sizeof res);
310 s1 += 16;
311 s2 += 16;
312 d += 16;
313 }
314 }
315 x *= 16;
316 if (bpp == 8) {
317 for ( ; x < linesize; ++x) {
318 *d++ = (*s1++ + *s2++ + 1) >> 1;
319 }
320 } else {
321 uint16_t *d16 = (uint16_t *)(void *) d;
322 uint16_t *s16_1 = (uint16_t *)(void *) s1;
323 uint16_t *s16_2 = (uint16_t *)(void *) s2;
324 for ( ; x < linesize / 2; ++x) {
325 *d16++ = (*s16_1++ + *s16_2++ + 1) >> 1;
326 }
327 }
328 } else if (codec == v210) {
329 uint32_t *s32_1 = (uint32_t *)(void *) s1;
330 uint32_t *s32_2 = (uint32_t *)(void *) s2;
331 uint32_t *d32 = (uint32_t *)(void *) d;
332 for (size_t x = 0; x < linesize / 16; ++x) {
333 #pragma GCC unroll 4
334 for (int y = 0; y < 4; ++y) {
335 uint32_t v1 = *s32_1++;
336 uint32_t v2 = *s32_2++;
337 *d32++ =
338 (((v1 >> 20 ) + (v2 >> 20 ) + 1) / 2) << 20 |
339 (((v1 >> 10 & 0x3ff) + (v2 >> 10 & 0x3ff) + 1) / 2) << 10 |
340 (((v1 & 0x3ff) + (v2 & 0x3ff) + 1) / 2);
341 }
342 }
343 } else if (codec == R10k) {
344 uint32_t *s32_1 = (uint32_t *)(void *) s1;
345 uint32_t *s32_2 = (uint32_t *)(void *) s2;
346 uint32_t *d32 = (uint32_t *)(void *) d;
347 for (size_t x = 0; x < linesize / 4; ++x) {
348 #pragma GCC unroll 4
349 for (int y = 0; y < 4; ++y) {
350 uint32_t v1 = ntohl(*s32_1++);
351 uint32_t v2 = ntohl(*s32_2++);
352 *d32++ =
353 (((v1 >> 22 ) + (v2 >> 22 ) + 1) / 2) << 22 |
354 (((v1 >> 12 & 0x3ff) + (v2 >> 12 & 0x3ff) + 1) / 2) << 12 |
355 (((v1 >> 2 & 0x3ff) + (v2 >> 2 & 0x3ff) + 1) / 2) << 2;
356 }
357 }
358 } else if (codec == R12L) {
359 uint32_t *s32_1 = (uint32_t *)(void *) s1;
360 uint32_t *s32_2 = (uint32_t *)(void *) s2;
361 uint32_t *d32 = (uint32_t *)(void *) d;
362 int shift = 0;
363 uint32_t remain1 = 0;
364 uint32_t remain2 = 0;
365 uint32_t out = 0;
366 for (size_t x = 0; x < linesize / 16; ++x) {
367 #pragma GCC unroll 8
368 for (int y = 0; y < 4; ++y) {
369 uint32_t in1 = *s32_1++;
370 uint32_t in2 = *s32_2++;
371 if (shift > 0) {
372 remain1 = remain1 | (in1 & ((1<<((shift + 12) % 32)) - 1)) << (32-shift);
373 remain2 = remain2 | (in2 & ((1<<((shift + 12) % 32)) - 1)) << (32-shift);
374 uint32_t ret = (remain1 + remain2 + 1) / 2;
375 out |= ret << shift;
376 *d32++ = out;
377 out = ret >> (32-shift);
378 shift = (shift + 12) % 32;
379 in1 >>= shift;
380 in2 >>= shift;
381 }
382 while (shift <= 32 - 12) {
383 out |= ((((in1 & 0xfff) + (in2 & 0xfff)) + 1) / 2) << shift;
384 in1 >>= 12;
385 in2 >>= 12;
386 shift += 12;
387 }
388 if (shift == 32) {
389 *d32++ = out;
390 out = 0;
391 shift = 0;
392 } else {
393 remain1 = in1;
394 remain2 = in2;
395 }
396 }
397 }
398 } else {
399 return false;
400 }
401 return true;
402}
403
404static void perform_linear(struct state_df *s, struct video_frame *in, struct video_frame *out, int pitch)
405{
406 int linesize = vc_get_linesize(s->in->tiles[0].width, s->in->color_spec);
407 char *src = s->buffers[s->buffer_current] + (in ? 0 : linesize);
408 char *dst = out->tiles[0].data;
409 if (in == NULL) {
410 memcpy(dst, src, linesize); // copy first line up
411 dst += pitch; // then copy every even line to subsequent odd line
412 }
413 unsigned y = in ? 0 : 1;
414 for ( ; y < out->tiles[0].height - 2; y += 2) {
415 memcpy(dst, src, linesize);
416 if (!avg_lines(out->color_spec, linesize, src, src + 2 * linesize, dst + pitch)) {
417 memcpy(dst + pitch, src, linesize); // fallback bob
418 }
419 dst += 2 * pitch;
420 src += 2 * linesize;
421 }
422 for ( ; y < out->tiles[0].height; y++) { // last line(s) if needed
423 memcpy(dst, src, linesize);
424 dst += pitch;
425 }
426}
427
429static bool common_postprocess(void *state, struct video_frame *in, struct video_frame *out, int req_pitch)
430{
431 struct state_df *s = (struct state_df *) state;
432
433 if (s->in->interlacing == INTERLACED_MERGED || s->force) {
434 switch (s->algo) {
435 case DF:
436 perform_df(s, in, out, req_pitch);
437 break;
438 case BOB:
439 perform_bob(s, in, out, req_pitch);
440 break;
441 case LINEAR:
442 perform_linear(s, in, out, req_pitch);
443 break;
444 }
445 } else {
446 s->in->tiles[0].data = s->buffers[0]; // always write to first buffer
447 if (in) {
448 memcpy(out->tiles[0].data, in->tiles[0].data, in->tiles[0].data_len);
449 }
450 }
451
452 if (!s->nodelay) {
453 // In following code we fix timing in order not to pass both frames
454 // in bulk but rather we busy-wait half of the frame time.
455 if (in) {
457 } else {
458 time_ns_t t = 0;
459 do {
460 t = get_time_in_ns();
461 } while ((t - s->frame_received) / NS_IN_SEC_DBL <= 0.5 / out->fps);
462 }
463 }
464
465 return true;
466}
467
468static void common_done(void *state)
469{
470 struct state_df *s = (struct state_df *) state;
471
472 free(s->buffers[0]);
473 free(s->buffers[1]);
474 vf_free(s->in);
475 free(s);
476}
477
478static void common_get_out_desc(void *state, struct video_desc *out, int *in_display_mode, int *out_frames)
479{
480 struct state_df *s = (struct state_df *) state;
481
482 out->width = vf_get_tile(s->in, 0)->width;
483 out->height = vf_get_tile(s->in, 0)->height;
484 out->color_spec = s->in->color_spec;
486 out->fps = s->in->fps * 2.0;
487 out->tile_count = 1;
488
489 *in_display_mode = DISPLAY_PROPERTY_VIDEO_MERGED;
490 *out_frames = 2;
491}
492
502
512
522
526
#define UNUSED(x)
void ** state
int void * val
struct video_desc desc
int void size_t * len
int property
struct video_frame * vf_alloc(int count)
Allocates blank video frame.
void vf_free(struct video_frame *buf)
Frees video_frame structure.
int vc_get_linesize(unsigned int width, codec_t codec)
const char * get_codec_name(codec_t codec)
int color_printf(const char *format,...)
#define TRED(x)
Definition color_out.h:75
#define TBOLD(x)
Definition color_out.h:70
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
void log_msg_once(int level, uint32_t id, const char *msg,...)
Definition debug.cpp:137
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
@ DISPLAY_PROPERTY_VIDEO_MERGED
monolithic framebuffer
const char * get_commandline_param(const char *key)
Definition host.cpp:894
void set_commandline_param(const char *key, const char *val)
Definition host.cpp:904
@ LIBRARY_CLASS_VIDEO_POSTPROCESS
Definition lib_common.h:83
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
struct __attribute__((packed))
Definition rtp.h:88
int buffer_current
bool deinterlace
enum algo algo
struct video_frame * in
char * buffers[2]
time_ns_t frame_received
Struct tile is an area of video_frame.
Definition types.h:279
unsigned int width
Definition types.h:280
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
unsigned int data_len
length of the data
Definition types.h:288
unsigned int height
Definition types.h:281
unsigned int tile_count
Definition types.h:185
codec_t color_spec
Definition types.h:182
enum interlacing_t interlacing
Definition types.h:184
double fps
Definition types.h:183
unsigned int width
width of every tile (!)
Definition types.h:179
unsigned int height
height of every tile (!)
Definition types.h:180
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
enum interlacing_t interlacing
Definition types.h:307
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
Definition types.h:345
double fps
Definition types.h:308
codec_t color_spec
Definition types.h:306
static void perform_linear(struct state_df *s, struct video_frame *in, struct video_frame *out, int pitch)
#define DFR_DEINTERLACE_IMPOSSIBLE_MSG_ID
static bool common_postprocess_reconfigure(void *state, struct video_desc desc)
static void common_done(void *state)
#define TIMEOUT
static const struct vo_postprocess_info vo_pp_bob_info
static void perform_bob(struct state_df *s, struct video_frame *in, struct video_frame *out, int pitch)
static void * init_common(enum algo algo, const char *config)
static bool common_get_property(void *state, int property, void *val, size_t *len)
static struct video_frame * common_getf(void *state)
static void print_common_opts()
static void perform_df(struct state_df *s, struct video_frame *in, struct video_frame *out, int req_pitch)
static void * df_init(const char *config)
static const struct vo_postprocess_info vo_pp_df_info
static void common_get_out_desc(void *state, struct video_desc *out, int *in_display_mode, int *out_frames)
static const struct vo_postprocess_info vo_pp_linear_info
static void * linear_init(const char *config)
#define MOD_NAME
@ DF
@ LINEAR
@ BOB
static void df_usage()
static bool avg_lines(codec_t codec, size_t linesize, char *src1, char *src2, char *dst)
static void * bob_init(const char *config)
static bool common_postprocess(void *state, struct video_frame *in, struct video_frame *out, int req_pitch)
long long time_ns_t
Definition tv.h:81
#define NS_IN_SEC_DBL
Definition tv.h:93
static time_ns_t get_time_in_ns()
Definition tv.h:101
@ PROGRESSIVE
progressive frame
Definition types.h:164
@ INTERLACED_MERGED
Columngs of both fields are interlaced together.
Definition types.h:167
codec_t
Definition types.h:65
@ R10k
RGB 10-bit packed - RGBX, big-endian (2 bit padding)
Definition types.h:73
@ R12L
RGB 12-bit packed, little-endian.
Definition types.h:74
@ v210
YCbCr 422 10-bit - xVY0U|xY2UY1|xUY3V|xY5VY4, little-endian (2 bit padding /'x'/),...
Definition types.h:75
char * wrap_paragraph(char *text)
Definition text.c:204
This is an umbrella header for video functions.
bool is_codec_opaque(codec_t codec)
bool vc_deinterlace_ex(codec_t codec, unsigned char *src, size_t src_linesize, unsigned char *dst, size_t dst_pitch, size_t lines)
bool codec_is_planar(codec_t codec)
int get_bits_per_component(codec_t codec)
struct tile * vf_get_tile(struct video_frame *buf, int pos)
Returns pointer to n-th tile from video frame Equivalent to &video_frame::tiles[pos].
const char * get_interlacing_description(enum interlacing_t interlacing)
Returns description of interlacing Eg. "progressive".
#define VO_PP_ABI_VERSION