meta: add cursor metadata and examples

Add a moving and flashing circle cursor to video-src
Render the cursor in video-play
Alsa add VideoCrop metadata
This commit is contained in:
Wim Taymans 2018-11-30 11:07:45 +01:00
parent c982f73779
commit ac5f0b851d
4 changed files with 176 additions and 10 deletions

View file

@ -40,6 +40,8 @@ enum spa_meta_type {
SPA_META_Header, SPA_META_Header,
SPA_META_VideoCrop, SPA_META_VideoCrop,
SPA_META_VideoDamage, SPA_META_VideoDamage,
SPA_META_Bitmap,
SPA_META_Cursor,
}; };
/** /**
@ -89,6 +91,37 @@ struct spa_meta_region {
spa_meta_check(pos, meta); \ spa_meta_check(pos, meta); \
(pos)++) (pos)++)
#define spa_meta_bitmap_is_valid(m) ((m)->format != 0)
/**
* Bitmap information
*
* This metadata contains a bitmap image in the given format and size.
* It is typically used for cursor images or other small images that are
* better transfered inline.
*/
struct spa_meta_bitmap {
uint32_t format; /**< bitmap video format, one of enum spa_video_format */
struct spa_rectangle size; /**< width and height of bitmap */
int32_t stride; /**< stride of bitmap data */
uint32_t offset; /**< offset of bitmap data in this structure */
};
#define spa_meta_cursor_is_valid(m) ((m)->id != 0)
/**
* Cursor information
*
* Metadata to describe the position and appearance of a pointing device.
*/
struct spa_meta_cursor {
uint32_t id; /**< cursor id */
uint32_t flags; /**< extra flags */
struct spa_point position; /**< position on screen */
struct spa_point hotspot; /**< offsets for hotspot in bitmap */
uint32_t bitmap_offset; /**< offset of bitmap meta in this structure */
};
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */
#endif #endif

View file

@ -1,7 +1,7 @@
executable('video-src', executable('video-src',
'video-src.c', 'video-src.c',
install: false, install: false,
dependencies : [pipewire_dep], dependencies : [pipewire_dep, mathlib],
) )
executable('audio-src', executable('audio-src',
'audio-src.c', 'audio-src.c',

View file

@ -46,6 +46,7 @@ struct data {
SDL_Renderer *renderer; SDL_Renderer *renderer;
SDL_Window *window; SDL_Window *window;
SDL_Texture *texture; SDL_Texture *texture;
SDL_Texture *cursor;
struct pw_main_loop *loop; struct pw_main_loop *loop;
@ -60,6 +61,8 @@ struct data {
int32_t stride; int32_t stride;
int counter; int counter;
SDL_Rect rect;
SDL_Rect cursor_rect;
}; };
static void handle_events(struct data *data) static void handle_events(struct data *data)
@ -83,8 +86,11 @@ on_process(void *_data)
struct spa_buffer *buf; struct spa_buffer *buf;
void *sdata, *ddata; void *sdata, *ddata;
int sstride, dstride, ostride; int sstride, dstride, ostride;
struct spa_meta_region *mc;
struct spa_meta_cursor *mcs;
uint32_t i; uint32_t i;
uint8_t *src, *dst; uint8_t *src, *dst;
bool render_cursor = false;
b = pw_stream_dequeue_buffer(stream); b = pw_stream_dequeue_buffer(stream);
if (b == NULL) if (b == NULL)
@ -103,6 +109,44 @@ on_process(void *_data)
fprintf(stderr, "Couldn't lock texture: %s\n", SDL_GetError()); fprintf(stderr, "Couldn't lock texture: %s\n", SDL_GetError());
return; return;
} }
if ((mc = spa_buffer_find_meta_data(buf, SPA_META_VideoCrop, sizeof(*mc)))) {
data->rect.x = mc->region.position.x;
data->rect.y = mc->region.position.y;
data->rect.w = mc->region.size.width;
data->rect.h = mc->region.size.height;
}
if ((mcs = spa_buffer_find_meta_data(buf, SPA_META_Cursor, sizeof(*mcs)))) {
struct spa_meta_bitmap *mb;
void *cdata;
int cstride;
data->cursor_rect.x = mcs->position.x;
data->cursor_rect.y = mcs->position.y;
mb = SPA_MEMBER(mcs, mcs->bitmap_offset, struct spa_meta_bitmap);
data->cursor_rect.w = mb->size.width;
data->cursor_rect.h = mb->size.height;
if (SDL_LockTexture(data->cursor, NULL, &cdata, &cstride) < 0) {
fprintf(stderr, "Couldn't lock cursor texture: %s\n", SDL_GetError());
goto done;
}
src = SPA_MEMBER(mb, mb->offset, uint8_t);
dst = cdata;
ostride = SPA_MIN(cstride, mb->stride);
for (i = 0; i < mb->size.height; i++) {
memcpy(dst, src, ostride);
dst += cstride;
src += mb->stride;
}
SDL_UnlockTexture(data->cursor);
render_cursor = true;
}
sstride = buf->datas[0].chunk->stride; sstride = buf->datas[0].chunk->stride;
ostride = SPA_MIN(sstride, dstride); ostride = SPA_MIN(sstride, dstride);
@ -116,10 +160,13 @@ on_process(void *_data)
SDL_UnlockTexture(data->texture); SDL_UnlockTexture(data->texture);
SDL_RenderClear(data->renderer); SDL_RenderClear(data->renderer);
SDL_RenderCopy(data->renderer, data->texture, NULL, NULL); SDL_RenderCopy(data->renderer, data->texture, &data->rect, NULL);
if (render_cursor) {
SDL_RenderCopy(data->renderer, data->cursor, NULL, &data->cursor_rect);
}
SDL_RenderPresent(data->renderer); SDL_RenderPresent(data->renderer);
done:
pw_stream_queue_buffer(stream, b); pw_stream_queue_buffer(stream, b);
} }
@ -147,7 +194,7 @@ on_stream_format_changed(void *_data, const struct spa_pod *format)
struct pw_stream *stream = data->stream; struct pw_stream *stream = data->stream;
uint8_t params_buffer[1024]; uint8_t params_buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer)); struct spa_pod_builder b = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
const struct spa_pod *params[2]; const struct spa_pod *params[5];
Uint32 sdl_format; Uint32 sdl_format;
void *d; void *d;
@ -175,6 +222,17 @@ on_stream_format_changed(void *_data, const struct spa_pod *format)
SDL_LockTexture(data->texture, NULL, &d, &data->stride); SDL_LockTexture(data->texture, NULL, &d, &data->stride);
SDL_UnlockTexture(data->texture); SDL_UnlockTexture(data->texture);
data->cursor = SDL_CreateTexture(data->renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
64, 64);
SDL_SetTextureBlendMode(data->cursor, SDL_BLENDMODE_BLEND);
data->rect.x = 0;
data->rect.y = 0;
data->rect.w = data->format.size.width;
data->rect.h = data->format.size.height;
params[0] = spa_pod_builder_object(&b, params[0] = spa_pod_builder_object(&b,
SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
SPA_PARAM_BUFFERS_buffers, &SPA_POD_CHOICE_RANGE_Int(8, 2, MAX_BUFFERS), SPA_PARAM_BUFFERS_buffers, &SPA_POD_CHOICE_RANGE_Int(8, 2, MAX_BUFFERS),
@ -189,8 +247,20 @@ on_stream_format_changed(void *_data, const struct spa_pod *format)
SPA_PARAM_META_type, &SPA_POD_Id(SPA_META_Header), SPA_PARAM_META_type, &SPA_POD_Id(SPA_META_Header),
SPA_PARAM_META_size, &SPA_POD_Int(sizeof(struct spa_meta_header)), SPA_PARAM_META_size, &SPA_POD_Int(sizeof(struct spa_meta_header)),
0); 0);
params[2] = spa_pod_builder_object(&b,
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
SPA_PARAM_META_type, &SPA_POD_Id(SPA_META_VideoCrop),
SPA_PARAM_META_size, &SPA_POD_Int(sizeof(struct spa_meta_region)),
0);
params[3] = spa_pod_builder_object(&b,
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
SPA_PARAM_META_type, &SPA_POD_Id(SPA_META_Cursor),
SPA_PARAM_META_size, &SPA_POD_Int(sizeof(struct spa_meta_cursor) +
sizeof(struct spa_meta_bitmap) +
64 * 64 * 4),
0);
pw_stream_finish_format(stream, 0, params, 2); pw_stream_finish_format(stream, 0, params, 4);
} }
static const struct pw_stream_events stream_events = { static const struct pw_stream_events stream_events = {
@ -267,6 +337,7 @@ int main(int argc, char *argv[])
pw_main_loop_destroy(data.loop); pw_main_loop_destroy(data.loop);
SDL_DestroyTexture(data.texture); SDL_DestroyTexture(data.texture);
SDL_DestroyTexture(data.cursor);
SDL_DestroyRenderer(data.renderer); SDL_DestroyRenderer(data.renderer);
SDL_DestroyWindow(data.window); SDL_DestroyWindow(data.window);

View file

@ -25,14 +25,19 @@
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
#include <time.h> #include <time.h>
#include <sys/mman.h> #include <math.h>
#include <spa/param/video/format-utils.h> #include <spa/param/video/format-utils.h>
#include <spa/param/props.h> #include <spa/param/props.h>
#include <pipewire/pipewire.h> #include <pipewire/pipewire.h>
#define BPP 3 #define BPP 3
#define WIDTH 320
#define HEIGHT 200
#define CROP 8
#define M_PI_M2 ( M_PI + M_PI )
struct data { struct data {
struct pw_main_loop *loop; struct pw_main_loop *loop;
@ -50,6 +55,9 @@ struct data {
int counter; int counter;
uint32_t seq; uint32_t seq;
double crop;
double accumulator;
}; };
static void on_timeout(void *userdata, uint64_t expirations) static void on_timeout(void *userdata, uint64_t expirations)
@ -59,8 +67,10 @@ static void on_timeout(void *userdata, uint64_t expirations)
struct spa_buffer *buf; struct spa_buffer *buf;
int i, j; int i, j;
uint8_t *p; uint8_t *p;
struct spa_meta_header *h;
struct spa_meta *m; struct spa_meta *m;
struct spa_meta_header *h;
struct spa_meta_region *mc;
struct spa_meta_cursor *mcs;
pw_log_trace("timeout"); pw_log_trace("timeout");
@ -96,6 +106,42 @@ static void on_timeout(void *userdata, uint64_t expirations)
if (spa_meta_check(r, m)) if (spa_meta_check(r, m))
r->region = SPA_REGION(0,0,0,0); r->region = SPA_REGION(0,0,0,0);
} }
if ((mc = spa_buffer_find_meta_data(buf, SPA_META_VideoCrop, sizeof(*mc)))) {
data->crop = (sin(data->accumulator) + 1.0) * 32.0;
mc->region.position.x = data->crop;
mc->region.position.y = data->crop;
mc->region.size.width = WIDTH - data->crop*2;
mc->region.size.height = HEIGHT - data->crop*2;
}
if ((mcs = spa_buffer_find_meta_data(buf, SPA_META_Cursor, sizeof(*mcs)))) {
struct spa_meta_bitmap *mb;
uint32_t *bitmap, color;
mcs->id = 1;
mcs->position.x = (sin(data->accumulator) + 1.0) * 160.0 + 80;
mcs->position.y = (cos(data->accumulator) + 1.0) * 100.0 + 50;
mcs->hotspot.x = 0;
mcs->hotspot.y = 0;
mcs->bitmap_offset = sizeof(struct spa_meta_cursor);
mb = SPA_MEMBER(mcs, mcs->bitmap_offset, struct spa_meta_bitmap);
mb->format = SPA_VIDEO_FORMAT_ARGB;
mb->size.width = 64;
mb->size.height = 64;
mb->stride = 64 * 4;
mb->offset = sizeof(struct spa_meta_bitmap);
bitmap = SPA_MEMBER(mb, mb->offset, uint32_t);
color = (cos(data->accumulator) + 1.0) * (1 << 23);
color |= 0xff000000;
for (i = 0; i < mb->size.height; i++) {
for (j = 0; j < mb->size.width; j++) {
int v = (i - 32) * (i - 32) + (j - 32) * (j - 32);
bitmap[i*64+j] = (v <= 32*32) ? color : 0x00000000;
}
}
}
for (i = 0; i < data->format.size.height; i++) { for (i = 0; i < data->format.size.height; i++) {
for (j = 0; j < data->format.size.width * BPP; j++) { for (j = 0; j < data->format.size.width * BPP; j++) {
@ -105,6 +151,10 @@ static void on_timeout(void *userdata, uint64_t expirations)
data->counter += 13; data->counter += 13;
} }
data->accumulator += M_PI_M2 / 50.0;
if (data->accumulator >= M_PI_M2)
data->accumulator -= M_PI_M2;
buf->datas[0].chunk->size = buf->datas[0].maxsize; buf->datas[0].chunk->size = buf->datas[0].maxsize;
pw_stream_queue_buffer(data->stream, b); pw_stream_queue_buffer(data->stream, b);
@ -148,7 +198,7 @@ on_stream_format_changed(void *_data, const struct spa_pod *format)
struct pw_stream *stream = data->stream; struct pw_stream *stream = data->stream;
uint8_t params_buffer[1024]; uint8_t params_buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer)); struct spa_pod_builder b = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
const struct spa_pod *params[3]; const struct spa_pod *params[5];
if (format == NULL) { if (format == NULL) {
pw_stream_finish_format(stream, 0, NULL, 0); pw_stream_finish_format(stream, 0, NULL, 0);
@ -181,8 +231,20 @@ on_stream_format_changed(void *_data, const struct spa_pod *format)
sizeof(struct spa_meta_region) * 1, sizeof(struct spa_meta_region) * 1,
sizeof(struct spa_meta_region) * 16), sizeof(struct spa_meta_region) * 16),
0); 0);
params[3] = spa_pod_builder_object(&b,
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
SPA_PARAM_META_type, &SPA_POD_Id(SPA_META_VideoCrop),
SPA_PARAM_META_size, &SPA_POD_Int(sizeof(struct spa_meta_region)),
0);
params[4] = spa_pod_builder_object(&b,
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
SPA_PARAM_META_type, &SPA_POD_Id(SPA_META_Cursor),
SPA_PARAM_META_size, &SPA_POD_Int(sizeof(struct spa_meta_cursor) +
sizeof(struct spa_meta_bitmap) +
64 * 64 * 4),
0);
pw_stream_finish_format(stream, 0, params, 3); pw_stream_finish_format(stream, 0, params, 5);
} }
static const struct pw_stream_events stream_events = { static const struct pw_stream_events stream_events = {