Skip to content

Commit 78fe6ee

Browse files
authored
Merge pull request #7 from sohonetlabs/PVT-746
PVT-746 - Add R12L support to testcard2
2 parents 7f2afc9 + 4c71ce3 commit 78fe6ee

6 files changed

Lines changed: 263 additions & 58 deletions

File tree

‎src/video_capture/testcard2.c‎

Lines changed: 79 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,14 @@
7575
#define BANNER_MARGIN_BOTTOM 75L
7676
#define BUFFER_SEC 1
7777
#define DEFAULT_FORMAT "1920:1080:24:UYVY"
78-
#define EPS_PLUS_1 1.0001
7978
#define FONT_HEIGHT 108
8079
#define MOD_NAME "[testcard2] "
80+
#define RECT1_BASE_STEP_PX 6 ///< rounded up to dest pixfmt block size
81+
#define RECT1_SIZE_PX (13 * PIX_BLOCK_LCM) ///< square edge; multiple of LCM of block pixels for supported formats
82+
#define RECT2_BASE_STEP_PX 12 ///< rounded up to dest pixfmt block size
83+
#define RECT2_FILL_COLOR 0xFFFF00AAU ///< RGBA purple (R=AA G=00 B=FF A=FF); rect1 fill is implicit black from zero-init
84+
#define RECT2_SIZE_PX (4 * PIX_BLOCK_LCM) ///< square edge; multiple of LCM of block pixels for supported formats
85+
#define RECT2_Y_STEP_PX 9 ///< vertical step; no block-alignment needed (lines independent)
8186

8287
#ifdef HAVE_LIBSDL_TTF
8388
#ifdef _WIN32
@@ -171,10 +176,6 @@ static int vidcap_testcard2_init(struct vidcap_params *params, void **state)
171176
break;
172177
}
173178
s->desc.width = atoi(tmp);
174-
if(s->desc.width % 2 != 0) {
175-
log_msg(LOG_LEVEL_ERROR, "Width must be multiple of 2.\n");
176-
break;
177-
}
178179
if (!(tmp = strtok_r(NULL, ":", &save_ptr))) {
179180
log_msg(LOG_LEVEL_ERROR, "Missing height for testcard\n");
180181
break;
@@ -192,6 +193,13 @@ static int vidcap_testcard2_init(struct vidcap_params *params, void **state)
192193
}
193194
s->desc.color_spec = get_codec_from_name(tmp);
194195

196+
int block_px = get_pf_block_pixels(s->desc.color_spec);
197+
if (s->desc.width % block_px != 0) {
198+
log_msg(LOG_LEVEL_ERROR, "Width must be a multiple of %d for codec %s.\n",
199+
block_px, get_codec_name(s->desc.color_spec));
200+
break;
201+
}
202+
195203
ret = VIDCAP_INIT_OK;
196204
} while(0);
197205
free(fmt);
@@ -282,10 +290,33 @@ static void vidcap_testcard2_done(void *state)
282290
free(s);
283291
}
284292

293+
/**
294+
* Fill the rectangle inside the frame by repeatedly memcpy-ing a
295+
* pre-converted pixel-format block (block_size bytes, block_px pixels
296+
* wide) across each row. Assumes the rectangle's x is aligned to a
297+
* block boundary; partial blocks at the right edge (when w/h has been
298+
* clipped) are skipped, so up to block_px-1 pixels of background may
299+
* show through on the frame following a bounce off the right wall.
300+
*/
301+
static void draw_rect_to_buffer(unsigned char *frame, long linesize,
302+
const struct testcard_rect *r,
303+
const unsigned char *block, int block_px, int block_size)
304+
{
305+
// Calculate initial pointer location of corner of rectangle (aligned to the block size)
306+
unsigned char *ptr = frame + (long) r->y * linesize + (long) (r->x / block_px) * block_size;
307+
// Copy in the pixels from the block
308+
for (int y = 0; y < r->h; ++y) {
309+
for (int x = 0; x < r->w / block_px; ++x) {
310+
memcpy(ptr + block_size * x, block, block_size);
311+
}
312+
ptr += linesize;
313+
}
314+
}
315+
285316
/**
286317
* Only text banner is rendered in RGBA, other elements (background, squares) are already
287-
* converted to destination color space. Keep in mind that the regions should be aligned
288-
* to 6 (v210 block size), won't work for R12L
318+
* converted to destination color space. Overlay regions and motion steps are aligned to
319+
* the destination pixel format's block size (e.g. 6 pixels for v210, 8 for R12L).
289320
*/
290321
void * vidcap_testcard2_thread(void *arg)
291322
{
@@ -295,11 +326,20 @@ void * vidcap_testcard2_thread(void *arg)
295326
s = (struct testcard_state2 *)arg;
296327
struct timeval next_frame_time = { 0 };
297328
srand(time(NULL));
298-
int prev_x1 = rand() % ((s->desc.width - 300) / 6) * 6;
299-
int prev_y1 = rand() % ((s->desc.height - 300) / 6) * 6;
329+
const int block_px = get_pf_block_pixels(s->desc.color_spec);
330+
const int block_size = get_pf_block_bytes(s->desc.color_spec);
331+
const int step1 = (RECT1_BASE_STEP_PX + block_px - 1) / block_px * block_px;
332+
const int step2 = (RECT2_BASE_STEP_PX + block_px - 1) / block_px * block_px;
333+
// Generate the initial position of the rectangles
334+
struct testcard_rect rect1 = {
335+
.x = rand() % ((s->desc.width - RECT1_SIZE_PX) / block_px) * block_px,
336+
.y = rand() % ((s->desc.height - RECT1_SIZE_PX) / block_px) * block_px,
337+
};
300338
int down1 = rand() % 2, right1 = rand() % 2;
301-
int prev_x2 = rand() % ((s->desc.width - 96) / 6) * 6;
302-
int prev_y2 = rand() % ((s->desc.height - 96) / 6) * 6;
339+
struct testcard_rect rect2 = {
340+
.x = rand() % ((s->desc.width - RECT2_SIZE_PX) / block_px) * block_px,
341+
.y = rand() % ((s->desc.height - RECT2_SIZE_PX) / block_px) * block_px,
342+
};
303343
int down2 = rand() % 2, right2 = rand() % 2;
304344

305345
int stat_count_prev = 0;
@@ -330,59 +370,40 @@ void * vidcap_testcard2_thread(void *arg)
330370
}
331371

332372
#endif
333-
/// @note R12l has pixel block size 8 pixels, so the below won't work for that pixfmt
334-
unsigned char square_cols[2][48];
335-
uint32_t src[6 + MAX_PADDING] = { 0 };
336-
testcard_convert_buffer(RGBA, s->desc.color_spec, square_cols[0], (unsigned char *) src, 6, 1);
337-
for (int i = 0; i < 6; ++i) src[i] = 0xffff00aa;
338-
testcard_convert_buffer(RGBA, s->desc.color_spec, square_cols[1], (unsigned char *) src, 6, 1);
339-
340-
ptrdiff_t block_size = 6 * EPS_PLUS_1 * get_bpp(s->desc.color_spec);
373+
// Create colour lookups for rectangles.
374+
unsigned char square_cols[2][MAX_PADDING];
375+
uint32_t src[PIX_BLOCK_LCM + MAX_PADDING] = { 0 };
376+
assert((size_t) block_size <= sizeof square_cols[0]);
377+
// Fill the rectangle colour lookups with black and RECT2_FILL_COLOR
378+
testcard_convert_buffer(RGBA, s->desc.color_spec, square_cols[0], (unsigned char *) src, block_px, 1);
379+
for (int i = 0; i < block_px; ++i) src[i] = RECT2_FILL_COLOR;
380+
testcard_convert_buffer(RGBA, s->desc.color_spec, square_cols[1], (unsigned char *) src, block_px, 1);
381+
382+
const size_t data_len = vc_get_datalen(s->desc.width, s->desc.height, s->desc.color_spec);
383+
const long linesize = vc_get_linesize(s->desc.width, s->desc.color_spec);
341384

342385
while(!s->should_exit)
343386
{
344-
size_t data_len = vc_get_datalen(s->desc.width, s->desc.height, s->desc.color_spec);
387+
// Each iteration allocates a fresh buffer; ownership is handed
388+
// off to s->data and freed by the consumer via vf_data_deleter.
345389
unsigned char *tmp = malloc(data_len);
390+
391+
// Copy in the pre-computed bars background
346392
memcpy(tmp, s->bg, data_len);
347393

348-
struct testcard_rect r;
349-
r.w = 300;
350-
r.h = 300;
351-
r.x = prev_x1 + (right1 ? 1 : -1) * 6;
352-
r.y = prev_y1 + (down1 ? 1 : -1) * 6;
353-
if(r.x < 0) { right1 = 1; r.x = 0; }
354-
if(r.y < 0) { down1 = 1; r.y = 0; }
355-
if((unsigned int) r.x + r.w > s->desc.width) { right1 = 0; r.w = s->desc.width - r.x; }
356-
if((unsigned int) r.y + r.h > s->desc.height) { down1 = 0; r.h = s->desc.height - r.y; }
357-
prev_x1 = r.x;
358-
prev_y1 = r.y;
359-
360-
unsigned char *ptr = tmp + r.y * vc_get_linesize(s->desc.width, s->desc.color_spec) + (int) (r.x * EPS_PLUS_1 * get_bpp(s->desc.color_spec));
361-
for (int y = 0; y < r.h; ++y) {
362-
for (int x = 0; x < r.w / 6; x += 1) {
363-
memcpy(ptr + block_size * x, square_cols[0], block_size);
364-
}
365-
ptr += vc_get_linesize(s->desc.width, s->desc.color_spec);
366-
}
394+
// Reset rect size
395+
rect1.w = RECT1_SIZE_PX;
396+
rect1.h = RECT1_SIZE_PX;
397+
testcard_rect_move(&rect1, &right1, &down1, step1, step1,
398+
s->desc.width, s->desc.height);
399+
draw_rect_to_buffer(tmp, linesize, &rect1, square_cols[0], block_px, block_size);
367400

368-
r.w = 96;
369-
r.h = 96;
370-
r.x = prev_x2 + (right2 ? 1 : -1) * 12;
371-
r.y = prev_y2 + (down2 ? 1 : -1) * 9;
372-
if(r.x < 0) { right2 = 1; r.x = 0; }
373-
if(r.y < 0) { down2 = 1; r.y = 0; }
374-
if((unsigned int) r.x + r.w > s->desc.width) { right2 = 0; r.w = s->desc.width - r.x; }
375-
if((unsigned int) r.y + r.h > s->desc.height) { down2 = 0; r.h = s->desc.height - r.y; }
376-
prev_x2 = r.x;
377-
prev_y2 = r.y;
378-
379-
ptr = tmp + (long) r.y * vc_get_linesize(s->desc.width, s->desc.color_spec) + (long) (r.x * EPS_PLUS_1 * get_bpp(s->desc.color_spec));
380-
for (int y = 0; y < r.h; ++y) {
381-
for (int x = 0; x < r.w / 6; x += 1) {
382-
memcpy(ptr + block_size * x, square_cols[1], block_size);
383-
}
384-
ptr += vc_get_linesize(s->desc.width, s->desc.color_spec);
385-
}
401+
// Reset rect size
402+
rect2.w = RECT2_SIZE_PX;
403+
rect2.h = RECT2_SIZE_PX;
404+
testcard_rect_move(&rect2, &right2, &down2, step2, RECT2_Y_STEP_PX,
405+
s->desc.width, s->desc.height);
406+
draw_rect_to_buffer(tmp, linesize, &rect2, square_cols[1], block_px, block_size);
386407

387408
#ifdef HAVE_LIBSDL_TTF
388409
memset(banner, 0xFF, 4L * s->desc.width * BANNER_HEIGHT);
@@ -408,7 +429,7 @@ void * vidcap_testcard2_thread(void *arg)
408429
d++;
409430
}
410431
}
411-
testcard_convert_buffer(RGBA, s->desc.color_spec, tmp + (s->desc.height - BANNER_MARGIN_BOTTOM - BANNER_HEIGHT) * vc_get_linesize(s->desc.width, s->desc.color_spec), (unsigned char *) banner, s->desc.width, BANNER_HEIGHT);
432+
testcard_convert_buffer(RGBA, s->desc.color_spec, tmp + (s->desc.height - BANNER_MARGIN_BOTTOM - BANNER_HEIGHT) * linesize, (unsigned char *) banner, s->desc.width, BANNER_HEIGHT);
412433
SDL_FreeSurface(text);
413434
#endif
414435

‎src/video_capture/testcard_common.c‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,36 @@ bool testcard_has_conversion(codec_t c)
182182
testcard_conv_handled_internally(c);
183183
}
184184

185+
/**
186+
* Advance the rectangle by one frame's step and bounce off the frame
187+
* boundary. The rectangle carries its x/y position across frames; the
188+
* caller sets w/h to the intended full size on entry, and the direction
189+
* flags are flipped in place when an edge is hit. If the rect would
190+
* extend past max_w or max_h, its w/h is clipped on exit so the caller
191+
* can fill without overrunning.
192+
*/
193+
void testcard_rect_move(struct testcard_rect *r, int *right, int *down,
194+
int x_step, int y_step,
195+
unsigned int max_w, unsigned int max_h)
196+
{
197+
r->x += (*right ? 1 : -1) * x_step;
198+
r->y += (*down ? 1 : -1) * y_step;
199+
if (r->x < 0) {
200+
*right = 1;
201+
r->x = 0;
202+
}
203+
if (r->y < 0) {
204+
*down = 1;
205+
r->y = 0;
206+
}
207+
208+
if ((unsigned int) r->x + r->w > max_w) {
209+
*right = 0;
210+
r->w = max_w - r->x;
211+
}
212+
if ((unsigned int) r->y + r->h > max_h) {
213+
*down = 0;
214+
r->h = max_h - r->y;
215+
}
216+
}
217+

‎src/video_capture/testcard_common.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ void testcard_fillRect(struct testcard_pixmap *s, struct testcard_rect *r, uint3
6666
void testcard_convert_buffer(codec_t in_c, codec_t out_c, unsigned char *out, const unsigned char *in, int width, int height);
6767
void testcard_show_codec_help(const char *name, bool src_8b_only);
6868
bool testcard_has_conversion(codec_t c);
69+
void testcard_rect_move(struct testcard_rect *r, int *right, int *down,
70+
int x_step, int y_step,
71+
unsigned int max_w, unsigned int max_h);
6972

7073
#ifdef __cplusplus
7174
}

‎src/video_codec.c‎

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,6 +1752,89 @@ static void vc_copylineRGBtoR12L(unsigned char * __restrict dst, const unsigned
17521752
}
17531753
}
17541754

1755+
/**
1756+
* Converts 8-bit RGBA to 12-bit packed RGB in full range (compatible with
1757+
* SMPTE 268M DPX version 1, Annex C, Method C4 packing). Alpha is discarded.
1758+
*/
1759+
static void vc_copylineRGBAtoR12L(unsigned char * __restrict dst, const unsigned char * __restrict src, int dst_len,
1760+
int rshift, int gshift, int bshift) {
1761+
UNUSED(rshift);
1762+
UNUSED(gshift);
1763+
UNUSED(bshift);
1764+
1765+
OPTIMIZED_FOR (int x = 0; x <= dst_len - 36; x += 36) {
1766+
unsigned char r = *src++;
1767+
unsigned char g = *src++;
1768+
unsigned char b = *src++;
1769+
src++;
1770+
dst[BYTE_SWAP(0)] = r << 4;
1771+
dst[BYTE_SWAP(1)] = r >> 4;
1772+
dst[BYTE_SWAP(2)] = g;
1773+
dst[BYTE_SWAP(3)] = b << 4;
1774+
dst[4 + BYTE_SWAP(0)] = b >> 4;
1775+
r = *src++;
1776+
g = *src++;
1777+
b = *src++;
1778+
src++;
1779+
dst[4 + BYTE_SWAP(1)] = r;
1780+
dst[4 + BYTE_SWAP(2)] = g << 4;
1781+
dst[4 + BYTE_SWAP(3)] = g >> 4;
1782+
dst[8 + BYTE_SWAP(0)] = b;
1783+
r = *src++;
1784+
g = *src++;
1785+
b = *src++;
1786+
src++;
1787+
dst[8 + BYTE_SWAP(1)] = r << 4;
1788+
dst[8 + BYTE_SWAP(2)] = r >> 4;
1789+
dst[8 + BYTE_SWAP(3)] = g;
1790+
dst[12 + BYTE_SWAP(0)] = b << 4;
1791+
dst[12 + BYTE_SWAP(1)] = b >> 4;
1792+
r = *src++;
1793+
g = *src++;
1794+
b = *src++;
1795+
src++;
1796+
dst[12 + BYTE_SWAP(2)] = r;
1797+
dst[12 + BYTE_SWAP(3)] = g << 4;
1798+
dst[16 + BYTE_SWAP(0)] = g >> 4;
1799+
dst[16 + BYTE_SWAP(1)] = b;
1800+
r = *src++;
1801+
g = *src++;
1802+
b = *src++;
1803+
src++;
1804+
dst[16 + BYTE_SWAP(2)] = r << 4;
1805+
dst[16 + BYTE_SWAP(3)] = r >> 4;
1806+
dst[20 + BYTE_SWAP(0)] = g;
1807+
dst[20 + BYTE_SWAP(1)] = b << 4;
1808+
dst[20 + BYTE_SWAP(2)] = b >> 4;
1809+
r = *src++;
1810+
g = *src++;
1811+
b = *src++;
1812+
src++;
1813+
dst[20 + BYTE_SWAP(3)] = r;
1814+
dst[24 + BYTE_SWAP(0)] = g << 4;
1815+
dst[24 + BYTE_SWAP(1)] = g >> 4;
1816+
dst[24 + BYTE_SWAP(2)] = b;
1817+
r = *src++;
1818+
g = *src++;
1819+
b = *src++;
1820+
src++;
1821+
dst[24 + BYTE_SWAP(3)] = r << 4;
1822+
dst[28 + BYTE_SWAP(0)] = r >> 4;
1823+
dst[28 + BYTE_SWAP(1)] = g;
1824+
dst[28 + BYTE_SWAP(2)] = b << 4;
1825+
dst[28 + BYTE_SWAP(3)] = b >> 4;
1826+
r = *src++;
1827+
g = *src++;
1828+
b = *src++;
1829+
src++;
1830+
dst[32 + BYTE_SWAP(0)] = r;
1831+
dst[32 + BYTE_SWAP(1)] = g << 4;
1832+
dst[32 + BYTE_SWAP(2)] = g >> 4;
1833+
dst[32 + BYTE_SWAP(3)] = b;
1834+
dst += 36;
1835+
}
1836+
}
1837+
17551838
static void vc_copylineRGBAtoRG48(unsigned char * __restrict dst, const unsigned char * __restrict src, int dst_len,
17561839
int rshift, int gshift, int bshift) {
17571840
UNUSED(rshift);
@@ -2873,6 +2956,7 @@ static const struct decoder_item decoders[] = {
28732956
{ vc_copylineR12LtoRGB, R12L, RGB, false },
28742957
{ vc_copylineR12LtoRG48, R12L, RG48, false },
28752958
{ vc_copylineRGBtoR12L, RGB, R12L, false },
2959+
{ vc_copylineRGBAtoR12L, RGBA, R12L, false },
28762960
{ vc_copylineRGBAtoRG48, RGBA, RG48, false },
28772961
{ vc_copylineRGBtoRG48, RGB, RG48, false },
28782962
{ vc_copylineUYVYtoRG48, UYVY, RG48, true },

0 commit comments

Comments
 (0)