mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	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:
		
							parent
							
								
									c982f73779
								
							
						
					
					
						commit
						ac5f0b851d
					
				
					 4 changed files with 176 additions and 10 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,14 +25,19 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
#include <spa/param/video/format-utils.h>
 | 
			
		||||
#include <spa/param/props.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 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 = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue