diff --git a/spa/plugins/videotestsrc/draw.c b/spa/plugins/videotestsrc/draw.c index be4725f8d..ec8010fbd 100644 --- a/spa/plugins/videotestsrc/draw.c +++ b/spa/plugins/videotestsrc/draw.c @@ -17,12 +17,7 @@ * Boston, MA 02110-1301, USA. */ -#define PIXEL_SIZE 3 - -#define GET_IMAGE_WIDTH(this) this->current_format.info.raw.size.width -#define GET_IMAGE_HEIGHT(this) this->current_format.info.raw.size.height - -enum +typedef enum { GRAY = 0, YELLOW, @@ -38,56 +33,168 @@ enum DARK_BLACK, LIGHT_BLACK, N_COLORS +} Color; + +typedef struct _Pixel Pixel; + +struct _Pixel +{ + unsigned char R; + unsigned char G; + unsigned char B; + unsigned char Y; + unsigned char U; + unsigned char V; }; -struct pixel +static Pixel colors[N_COLORS] = { - char R; - char G; - char B; + {191, 191, 191, 0, 0, 0}, /* GRAY */ + {191, 191, 0, 0, 0, 0}, /* YELLOW */ + {0, 191, 191, 0, 0, 0}, /* CYAN */ + {0, 191, 0, 0, 0, 0}, /* GREEN */ + {191, 0, 191, 0, 0, 0}, /* MAGENTA */ + {191, 0, 0, 0, 0, 0}, /* RED */ + {0, 0, 191, 0, 0, 0}, /* BLUE */ + {19, 19, 19, 0, 0, 0}, /* BLACK */ + {0, 33, 76, 0, 0, 0}, /* NEGATIVE I */ + {255, 255, 255, 0, 0, 0}, /* WHITE */ + {49, 0, 107, 0, 0, 0}, /* POSITIVE Q */ + {9, 9, 9, 0, 0, 0}, /* DARK BLACK */ + {29, 29, 29, 0, 0, 0}, /* LIGHT BLACK */ +}; +/* YUV values are computed in init_colors() */ + +typedef struct _DrawingData DrawingData; + +typedef void (*DrawPixelFunc) (DrawingData *dd, + int x, + Pixel *pixel); + +struct _DrawingData { + char* line; + int width; + int height; + int stride; + DrawPixelFunc draw_pixel; }; -static struct pixel colors[N_COLORS] = +static inline void +update_yuv (Pixel *pixel) { - {191, 191, 191}, /* GRAY */ - {191, 191, 0}, /* YELLOW */ - {0, 191, 191}, /* CYAN */ - {0, 191, 0}, /* GREEN */ - {191, 0, 191}, /* MAGENTA */ - {191, 0, 0}, /* RED */ - {0, 0, 191}, /* BLUE */ - {19, 19, 19}, /* BLACK */ - {0, 33, 76}, /* NEGATIVE I */ - {255, 255, 255}, /* WHITE */ - {49, 0, 107}, /* POSITIVE Q */ - {9, 9, 9}, /* DARK BLACK */ - {29, 29, 29}, /* LIGHT BLACK */ -}; + uint16_t y, u, v; + + /* see https://en.wikipedia.org/wiki/YUV#Studio_swing_for_BT.601 */ + + y = 76 * pixel->R + 150 * pixel->G + 29 * pixel->B; + u = -43 * pixel->R - 84 * pixel->G + 127 * pixel->B; + v = 127 * pixel->R - 106 * pixel->G - 21 * pixel->B; + + y = (y + 128) >> 8; + u = (u + 128) >> 8; + v = (v + 128) >> 8; + + pixel->Y = y; + pixel->U = u + 128; + pixel->V = v + 128; +} static void -draw_line (char *data, struct pixel *c, int w) +init_colors (void) { int i; - for (i = 0; i < w; i++) { - data[i * PIXEL_SIZE + 0] = c->R; - data[i * PIXEL_SIZE + 1] = c->G; - data[i * PIXEL_SIZE + 2] = c->B; + if (colors[WHITE].Y != 0) { + /* already computed */ + return; + } + + for (i = 0; i < N_COLORS; i++) { + update_yuv (&colors[i]); } } -#define DRAW_LINE(data,line,offset,color,width) \ - draw_line (data + (line * w + offset) * PIXEL_SIZE, colors + color, width); +static void +draw_pixel_rgb (DrawingData *dd, int x, Pixel *color) +{ + dd->line[3 * x + 0] = color->R; + dd->line[3 * x + 1] = color->G; + dd->line[3 * x + 2] = color->B; +} static void -draw_smpte_snow (SpaVideoTestSrc *this, char *data) +draw_pixel_uyvy (DrawingData *dd, int x, Pixel *color) +{ + if (x & 1) { + /* odd pixel */ + dd->line[2 * (x - 1) + 3] = color->Y; + } else { + /* even pixel */ + dd->line[2 * x + 0] = color->U; + dd->line[2 * x + 1] = color->Y; + dd->line[2 * x + 2] = color->V; + } +} + +static SpaResult +drawing_data_init (DrawingData *dd, + SpaVideoTestSrc *this, + char* data) +{ + SpaFormatVideo *format = &this->current_format; + SpaRectangle *size = &format->info.raw.size; + + if (format->format.media_type != SPA_MEDIA_TYPE_VIDEO || + format->format.media_subtype != SPA_MEDIA_SUBTYPE_RAW) + return SPA_RESULT_NOT_IMPLEMENTED; + + switch (format->info.raw.format) { + case SPA_VIDEO_FORMAT_RGB: + dd->draw_pixel = draw_pixel_rgb; + break; + case SPA_VIDEO_FORMAT_UYVY: + dd->draw_pixel = draw_pixel_uyvy; + break; + default: + return SPA_RESULT_NOT_IMPLEMENTED; + } + + dd->line = data; + dd->width = size->width; + dd->height = size->height; + dd->stride = this->param_buffers.stride; + + return SPA_RESULT_OK; +} + +static inline void +draw_pixels (DrawingData *dd, + int offset, + Color color, + int length) +{ + int x; + + for (x = offset; x < offset + length; x++) { + dd->draw_pixel (dd, x, &colors[color]); + } +} + +static inline void +next_line (DrawingData *dd) +{ + dd->line += dd->stride; +} + +static void +draw_smpte_snow (DrawingData *dd) { int h, w; int y1, y2; int i, j; - w = GET_IMAGE_WIDTH (this); - h = GET_IMAGE_HEIGHT (this); + w = dd->width; + h = dd->height; y1 = 2 * h / 3; y2 = 3 * h / 4; @@ -95,49 +202,66 @@ draw_smpte_snow (SpaVideoTestSrc *this, char *data) for (j = 0; j < 7; j++) { int x1 = j * w / 7; int x2 = (j + 1) * w / 7; - DRAW_LINE (data, i, x1, j, x2 - x1); + draw_pixels (dd, x1, j, x2 - x1); } + next_line (dd); } for (i = y1; i < y2; i++) { for (j = 0; j < 7; j++) { int x1 = j * w / 7; int x2 = (j + 1) * w / 7; - int c = (j & 1) ? BLACK : BLUE - j; + Color c = (j & 1) ? BLACK : BLUE - j; - DRAW_LINE (data, i, x1, c, x2 - x1); + draw_pixels (dd, x1, c, x2 - x1); } + next_line (dd); } for (i = y2; i < h; i++) { - int x = 0; - /* negative I */ - DRAW_LINE (data, i, x, NEG_I, w / 6); - x += w / 6; + draw_pixels (dd, 0 * w / 6, NEG_I, w / 6); /* white */ - DRAW_LINE (data, i, x, WHITE, w / 6); - x += w / 6; + draw_pixels (dd, 1 * w / 6, WHITE, w / 6); /* positive Q */ - DRAW_LINE (data, i, x, POS_Q, w / 6); - x += w / 6; + draw_pixels (dd, 2 * w / 6, POS_Q, w / 6); /* pluge */ - DRAW_LINE (data, i, x, DARK_BLACK, w / 12); - x += w / 12; - DRAW_LINE (data, i, x, BLACK, w / 12); - x += w / 12; - DRAW_LINE (data, i, x, LIGHT_BLACK, w / 12); - x += w / 12; + draw_pixels (dd, 6 * w / 12, DARK_BLACK, w / 12); + draw_pixels (dd, 7 * w / 12, BLACK, w / 12); + draw_pixels (dd, 8 * w / 12, LIGHT_BLACK, w / 12); /* war of the ants (a.k.a. snow) */ - for (j = x; j < w; j++) { + for (j = 9 * w / 12; j < w; j++) { + Pixel p; unsigned char r = rand (); - data[(i * w + j) * PIXEL_SIZE + 0] = r; - data[(i * w + j) * PIXEL_SIZE + 1] = r; - data[(i * w + j) * PIXEL_SIZE + 2] = r; + + p.R = r; + p.G = r; + p.B = r; + update_yuv (&p); + dd->draw_pixel (dd, j, &p); } + + next_line (dd); } } + +static SpaResult +draw (SpaVideoTestSrc *this, char *data) +{ + DrawingData dd; + SpaResult res; + + init_colors (); + + res = drawing_data_init (&dd, this, data); + if (res != SPA_RESULT_OK) + return res; + + draw_smpte_snow (&dd); + + return res; +} diff --git a/spa/plugins/videotestsrc/videotestsrc.c b/spa/plugins/videotestsrc/videotestsrc.c index 064241549..fda008763 100644 --- a/spa/plugins/videotestsrc/videotestsrc.c +++ b/spa/plugins/videotestsrc/videotestsrc.c @@ -35,12 +35,6 @@ #define FRAMES_TO_TIME(this,f) ((this->current_format.info.raw.framerate.denom * (f) * SPA_NSEC_PER_SEC) / \ (this->current_format.info.raw.framerate.num)) -#define STATE_GET_IMAGE_WIDTH(this) this->current_format.info.raw.size.width -#define STATE_GET_IMAGE_HEIGHT(this) this->current_format.info.raw.size.height - -#define STATE_GET_IMAGE_SIZE(this) \ - (this->bpp * STATE_GET_IMAGE_WIDTH(this) * STATE_GET_IMAGE_HEIGHT(this)) - typedef struct { uint32_t node; uint32_t clock; @@ -200,9 +194,7 @@ send_have_output (SpaVideoTestSrc *this) static SpaResult fill_buffer (SpaVideoTestSrc *this, VTSBuffer *b) { - draw_smpte_snow (this, b->ptr); - - return SPA_RESULT_OK; + return draw (this, b->ptr); } static SpaResult update_loop_enabled (SpaVideoTestSrc *this, bool enabled); @@ -505,18 +497,29 @@ spa_videotestsrc_node_port_set_format (SpaNode *node, } if (this->have_format) { + SpaVideoInfoRaw *raw_info = &this->current_format.info.raw; + + switch (raw_info->format) { + case SPA_VIDEO_FORMAT_RGB: + this->bpp = 3; + break; + case SPA_VIDEO_FORMAT_UYVY: + this->bpp = 2; + break; + default: + return SPA_RESULT_NOT_IMPLEMENTED; + } + this->info.maxbuffering = -1; this->info.latency = 0; - this->bpp = 3; - this->info.n_params = 2; this->info.params = this->params; this->params[0] = &this->param_buffers.param; this->param_buffers.param.type = SPA_ALLOC_PARAM_TYPE_BUFFERS; this->param_buffers.param.size = sizeof (this->param_buffers); - this->param_buffers.minsize = STATE_GET_IMAGE_SIZE (this); - this->param_buffers.stride = this->bpp * STATE_GET_IMAGE_WIDTH (this); + this->param_buffers.stride = this->bpp * raw_info->size.width; + this->param_buffers.minsize = this->param_buffers.stride * raw_info->size.height; this->param_buffers.min_buffers = 2; this->param_buffers.max_buffers = 32; this->param_buffers.align = 16;