diff --git a/spa/include/spa/buffer/meta.h b/spa/include/spa/buffer/meta.h index 7d3d14f5e..dae5a589e 100644 --- a/spa/include/spa/buffer/meta.h +++ b/spa/include/spa/buffer/meta.h @@ -40,6 +40,8 @@ enum spa_meta_type { SPA_META_Header, SPA_META_VideoCrop, SPA_META_VideoDamage, + SPA_META_Bitmap, + SPA_META_Cursor, }; /** @@ -89,6 +91,37 @@ struct spa_meta_region { spa_meta_check(pos, meta); \ (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 } /* extern "C" */ #endif diff --git a/src/examples/meson.build b/src/examples/meson.build index 828e2d42e..44907d0fe 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -1,7 +1,7 @@ executable('video-src', 'video-src.c', install: false, - dependencies : [pipewire_dep], + dependencies : [pipewire_dep, mathlib], ) executable('audio-src', 'audio-src.c', diff --git a/src/examples/video-play.c b/src/examples/video-play.c index 78f617943..d34c49818 100644 --- a/src/examples/video-play.c +++ b/src/examples/video-play.c @@ -46,6 +46,7 @@ struct data { SDL_Renderer *renderer; SDL_Window *window; SDL_Texture *texture; + SDL_Texture *cursor; struct pw_main_loop *loop; @@ -60,6 +61,8 @@ struct data { int32_t stride; int counter; + SDL_Rect rect; + SDL_Rect cursor_rect; }; static void handle_events(struct data *data) @@ -83,8 +86,11 @@ on_process(void *_data) struct spa_buffer *buf; void *sdata, *ddata; int sstride, dstride, ostride; + struct spa_meta_region *mc; + struct spa_meta_cursor *mcs; uint32_t i; uint8_t *src, *dst; + bool render_cursor = false; b = pw_stream_dequeue_buffer(stream); if (b == NULL) @@ -103,6 +109,44 @@ on_process(void *_data) fprintf(stderr, "Couldn't lock texture: %s\n", SDL_GetError()); 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; ostride = SPA_MIN(sstride, dstride); @@ -116,10 +160,13 @@ on_process(void *_data) SDL_UnlockTexture(data->texture); 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); - + done: 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; uint8_t params_buffer[1024]; 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; 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_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, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_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_size, &SPA_POD_Int(sizeof(struct spa_meta_header)), 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 = { @@ -267,6 +337,7 @@ int main(int argc, char *argv[]) pw_main_loop_destroy(data.loop); SDL_DestroyTexture(data.texture); + SDL_DestroyTexture(data.cursor); SDL_DestroyRenderer(data.renderer); SDL_DestroyWindow(data.window); diff --git a/src/examples/video-src.c b/src/examples/video-src.c index f68c2de06..4220e4dce 100644 --- a/src/examples/video-src.c +++ b/src/examples/video-src.c @@ -25,14 +25,19 @@ #include #include #include -#include +#include #include #include #include -#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 pw_main_loop *loop; @@ -50,6 +55,9 @@ struct data { int counter; uint32_t seq; + + double crop; + double accumulator; }; 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; int i, j; uint8_t *p; - struct spa_meta_header *h; struct spa_meta *m; + struct spa_meta_header *h; + struct spa_meta_region *mc; + struct spa_meta_cursor *mcs; pw_log_trace("timeout"); @@ -96,6 +106,42 @@ static void on_timeout(void *userdata, uint64_t expirations) if (spa_meta_check(r, m)) 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 (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->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; 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; uint8_t params_buffer[1024]; 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) { 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) * 16), 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 = {