mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-10-29 05:40:27 -04:00 
			
		
		
		
	Integrating libcamera
This commit is contained in:
		
							parent
							
								
									ee1b79c4cd
								
							
						
					
					
						commit
						9024cc4444
					
				
					 24 changed files with 4826 additions and 13 deletions
				
			
		|  | @ -92,6 +92,10 @@ option('v4l2', | |||
|        description: 'Enable v4l2 spa plugin integration', | ||||
|        type: 'boolean', | ||||
|        value: true) | ||||
| option('libcamera', | ||||
|        description: 'Enable libcamera spa plugin integration', | ||||
|        type: 'boolean', | ||||
|        value: true) | ||||
| option('videoconvert', | ||||
|        description: 'Enable videoconvert spa plugin integration', | ||||
|        type: 'boolean', | ||||
|  |  | |||
							
								
								
									
										542
									
								
								spa/examples/local-libcamera.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										542
									
								
								spa/examples/local-libcamera.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,542 @@ | |||
| /* Spa
 | ||||
|  * | ||||
|  * Copyright (C) 2020, Collabora Ltd. | ||||
|  *     Author: Raghavendra Rao Sidlagatta <raghavendra.rao@collabora.com> | ||||
|  * | ||||
|  * local-libcamera.c | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a | ||||
|  * copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation | ||||
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||||
|  * and/or sell copies of the Software, and to permit persons to whom the | ||||
|  * Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice (including the next | ||||
|  * paragraph) shall be included in all copies or substantial portions of the | ||||
|  * Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | ||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
|  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||
|  * DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <dlfcn.h> | ||||
| #include <poll.h> | ||||
| #include <pthread.h> | ||||
| #include <errno.h> | ||||
| #include <sys/mman.h> | ||||
| 
 | ||||
| #include <SDL2/SDL.h> | ||||
| 
 | ||||
| #include <spa/support/plugin.h> | ||||
| #include <spa/utils/names.h> | ||||
| #include <spa/utils/result.h> | ||||
| #include <spa/support/log-impl.h> | ||||
| #include <spa/support/loop.h> | ||||
| #include <spa/node/node.h> | ||||
| #include <spa/node/io.h> | ||||
| #include <spa/node/utils.h> | ||||
| #include <spa/param/param.h> | ||||
| #include <spa/param/props.h> | ||||
| #include <spa/param/video/format-utils.h> | ||||
| #include <spa/debug/pod.h> | ||||
| 
 | ||||
| #define WIDTH   640 | ||||
| #define HEIGHT  480 | ||||
| 
 | ||||
| static SPA_LOG_IMPL(default_log); | ||||
| 
 | ||||
| #define PATH "build/spa/plugins/" | ||||
| 
 | ||||
| #define MAX_BUFFERS     8 | ||||
| 
 | ||||
| #define USE_BUFFER 		false | ||||
| 
 | ||||
| struct buffer { | ||||
| 	struct spa_buffer buffer; | ||||
| 	struct spa_meta metas[1]; | ||||
| 	struct spa_meta_header header; | ||||
| 	struct spa_data datas[1]; | ||||
| 	struct spa_chunk chunks[1]; | ||||
| 	SDL_Texture *texture; | ||||
| }; | ||||
| 
 | ||||
| struct data { | ||||
| 	struct spa_log *log; | ||||
| 	struct spa_system *system; | ||||
| 	struct spa_loop *loop; | ||||
| 	struct spa_loop_control *control; | ||||
| 
 | ||||
| 	struct spa_support support[5]; | ||||
| 	uint32_t n_support; | ||||
| 
 | ||||
| 	struct spa_node *source; | ||||
| 	struct spa_hook listener; | ||||
| 	struct spa_io_buffers source_output[1]; | ||||
| 
 | ||||
| 	SDL_Renderer *renderer; | ||||
| 	SDL_Window *window; | ||||
| 	SDL_Texture *texture; | ||||
| 
 | ||||
| 	bool use_buffer; | ||||
| 
 | ||||
| 	bool running; | ||||
| 	pthread_t thread; | ||||
| 
 | ||||
| 	struct spa_buffer *bp[MAX_BUFFERS]; | ||||
| 	struct buffer buffers[MAX_BUFFERS]; | ||||
| 	unsigned int n_buffers; | ||||
| }; | ||||
| 
 | ||||
| static int load_handle(struct data *data, struct spa_handle **handle, const char *lib, const char *name) | ||||
| { | ||||
| 	int res; | ||||
| 	void *hnd; | ||||
| 	spa_handle_factory_enum_func_t enum_func; | ||||
| 	uint32_t i; | ||||
| 
 | ||||
| 	if ((hnd = dlopen(lib, RTLD_NOW)) == NULL) { | ||||
| 		printf("can't load %s: %s\n", lib, dlerror()); | ||||
| 		return -errno; | ||||
| 	} | ||||
| 	if ((enum_func = dlsym(hnd, SPA_HANDLE_FACTORY_ENUM_FUNC_NAME)) == NULL) { | ||||
| 		printf("can't find enum function\n"); | ||||
| 		return -errno; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0;;) { | ||||
| 		const struct spa_handle_factory *factory; | ||||
| 
 | ||||
| 		if ((res = enum_func(&factory, &i)) <= 0) { | ||||
| 			if (res != 0) | ||||
| 				printf("can't enumerate factories: %s\n", spa_strerror(res)); | ||||
| 			break; | ||||
| 		} | ||||
| 		if (strcmp(factory->name, name)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		*handle = calloc(1, spa_handle_factory_get_size(factory, NULL)); | ||||
| 		if ((res = spa_handle_factory_init(factory, *handle, | ||||
| 						NULL, data->support, | ||||
| 						data->n_support)) < 0) { | ||||
| 			printf("can't make factory instance: %d\n", res); | ||||
| 			return res; | ||||
| 		} | ||||
| 		return 0; | ||||
| 	} | ||||
| 	return -EBADF; | ||||
| } | ||||
| 
 | ||||
| static int make_node(struct data *data, struct spa_node **node, const char *lib, const char *name) | ||||
| { | ||||
| 	struct spa_handle *handle = NULL; | ||||
| 	void *iface; | ||||
| 	int res; | ||||
| 
 | ||||
| 	if ((res = load_handle(data, &handle, lib, name)) < 0) | ||||
| 		return res; | ||||
| 
 | ||||
| 	if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Node, &iface)) < 0) { | ||||
| 		printf("can't get interface %d\n", res); | ||||
| 		return res; | ||||
| 	} | ||||
| 	*node = iface; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int on_source_ready(void *_data, int status) | ||||
| { | ||||
| 	struct data *data = _data; | ||||
| 	int res; | ||||
| 	struct buffer *b; | ||||
| 	void *sdata, *ddata; | ||||
| 	int sstride, dstride; | ||||
| 	int i; | ||||
| 	uint8_t *src, *dst; | ||||
| 	struct spa_data *datas; | ||||
| 	struct spa_io_buffers *io = &data->source_output[0]; | ||||
| 
 | ||||
| 	if (io->status != SPA_STATUS_HAVE_DATA || | ||||
| 	    io->buffer_id >= MAX_BUFFERS) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	b = &data->buffers[io->buffer_id]; | ||||
| 	io->status = SPA_STATUS_NEED_DATA; | ||||
| 
 | ||||
| 	datas = b->buffer.datas; | ||||
| 
 | ||||
| 	if (b->texture) { | ||||
| 		SDL_Texture *texture = b->texture; | ||||
| 
 | ||||
| 		SDL_UnlockTexture(texture); | ||||
| 
 | ||||
| 		SDL_RenderClear(data->renderer); | ||||
| 		SDL_RenderCopy(data->renderer, texture, NULL, NULL); | ||||
| 		SDL_RenderPresent(data->renderer); | ||||
| 
 | ||||
| 		if (SDL_LockTexture(texture, NULL, &sdata, &sstride) < 0) { | ||||
| 			fprintf(stderr, "Couldn't lock texture: %s\n", SDL_GetError()); | ||||
| 			return -EIO; | ||||
| 		} | ||||
| 	} else { | ||||
| 		uint8_t *map; | ||||
| 
 | ||||
| 		if (SDL_LockTexture(data->texture, NULL, &ddata, &dstride) < 0) { | ||||
| 			fprintf(stderr, "Couldn't lock texture: %s\n", SDL_GetError()); | ||||
| 			return -EIO; | ||||
| 		} | ||||
| 		sdata = datas[0].data; | ||||
| 		if (datas[0].type == SPA_DATA_MemFd || | ||||
| 		    datas[0].type == SPA_DATA_DmaBuf) { | ||||
| 			map = mmap(NULL, datas[0].maxsize + datas[0].mapoffset, PROT_READ, | ||||
| 				   MAP_PRIVATE, datas[0].fd, 0); | ||||
| 			if (map == MAP_FAILED) | ||||
| 				return -errno; | ||||
| 			sdata = SPA_MEMBER(map, datas[0].mapoffset, uint8_t); | ||||
| 		} else if (datas[0].type == SPA_DATA_MemPtr) { | ||||
| 			map = NULL; | ||||
| 			sdata = datas[0].data; | ||||
| 		} else | ||||
| 			return -EIO; | ||||
| 
 | ||||
| 		sstride = datas[0].chunk->stride; | ||||
| 
 | ||||
| 		for (i = 0; i < HEIGHT; i++) { | ||||
| 			src = ((uint8_t *) sdata + i * sstride); | ||||
| 			dst = ((uint8_t *) ddata + i * dstride); | ||||
| 			memcpy(dst, src, SPA_MIN(sstride, dstride)); | ||||
| 		} | ||||
| 		SDL_UnlockTexture(data->texture); | ||||
| 
 | ||||
| 		SDL_RenderClear(data->renderer); | ||||
| 		SDL_RenderCopy(data->renderer, data->texture, NULL, NULL); | ||||
| 		SDL_RenderPresent(data->renderer); | ||||
| 
 | ||||
| 		if (map) | ||||
| 			munmap(map, datas[0].maxsize + datas[0].mapoffset); | ||||
| 	} | ||||
| 
 | ||||
| 	if ((res = spa_node_process(data->source)) < 0) | ||||
| 		printf("got process error %d\n", res); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct spa_node_callbacks source_callbacks = { | ||||
| 	SPA_VERSION_NODE_CALLBACKS, | ||||
| 	.ready = on_source_ready, | ||||
| }; | ||||
| 
 | ||||
| static int make_nodes(struct data *data, const char *device) | ||||
| { | ||||
| 	int res; | ||||
| 	struct spa_pod *props; | ||||
| 	struct spa_pod_builder b = { 0 }; | ||||
| 	uint8_t buffer[256]; | ||||
| 	uint32_t index; | ||||
| 
 | ||||
| 	if ((res = | ||||
| 	     make_node(data, &data->source, | ||||
| 		     PATH "libcamera/libspa-libcamera.so", | ||||
| 		     SPA_NAME_API_LIBCAMERA_SOURCE)) < 0) { | ||||
| 		printf("can't create libcamera-source: %d\n", res); | ||||
| 		return res; | ||||
| 	} | ||||
| 
 | ||||
| 	spa_node_set_callbacks(data->source, &source_callbacks, data); | ||||
| 
 | ||||
| 	index = 0; | ||||
| 	spa_pod_builder_init(&b, buffer, sizeof(buffer)); | ||||
| 	if ((res = spa_node_enum_params_sync(data->source, SPA_PARAM_Props, | ||||
| 			&index, NULL, &props, &b)) == 1) { | ||||
| 		spa_debug_pod(0, NULL, props); | ||||
| 	} | ||||
| 
 | ||||
| 	spa_pod_builder_init(&b, buffer, sizeof(buffer)); | ||||
| 	props = spa_pod_builder_add_object(&b, | ||||
| 		SPA_TYPE_OBJECT_Props, 0, | ||||
| 		SPA_PROP_device, SPA_POD_String(device ? device : "/dev/media0")); | ||||
| 
 | ||||
| 	if ((res = spa_node_set_param(data->source, SPA_PARAM_Props, 0, props)) < 0) | ||||
| 		printf("got set_props error %d\n", res); | ||||
| 
 | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| static int setup_buffers(struct data *data) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < MAX_BUFFERS; i++) { | ||||
| 		struct buffer *b = &data->buffers[i]; | ||||
| 
 | ||||
| 		data->bp[i] = &b->buffer; | ||||
| 
 | ||||
| 		b->texture = NULL; | ||||
| 
 | ||||
| 		b->buffer.metas = b->metas; | ||||
| 		b->buffer.n_metas = 1; | ||||
| 		b->buffer.datas = b->datas; | ||||
| 		b->buffer.n_datas = 1; | ||||
| 
 | ||||
| 		b->header.flags = 0; | ||||
| 		b->header.seq = 0; | ||||
| 		b->header.pts = 0; | ||||
| 		b->header.dts_offset = 0; | ||||
| 		b->metas[0].type = SPA_META_Header; | ||||
| 		b->metas[0].data = &b->header; | ||||
| 		b->metas[0].size = sizeof(b->header); | ||||
| 
 | ||||
| 		b->datas[0].type = SPA_DATA_DmaBuf; | ||||
| 		b->datas[0].flags = 0; | ||||
| 		b->datas[0].fd = -1; | ||||
| 		b->datas[0].mapoffset = 0; | ||||
| 		b->datas[0].maxsize = 0; | ||||
| 		b->datas[0].data = NULL; | ||||
| 		b->datas[0].chunk = &b->chunks[0]; | ||||
| 		b->datas[0].chunk->offset = 0; | ||||
| 		b->datas[0].chunk->size = 0; | ||||
| 		b->datas[0].chunk->stride = 0; | ||||
| 	} | ||||
| 	data->n_buffers = MAX_BUFFERS; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int sdl_alloc_buffers(struct data *data) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < MAX_BUFFERS; i++) { | ||||
| 		struct buffer *b = &data->buffers[i]; | ||||
| 		SDL_Texture *texture; | ||||
| 		void *ptr; | ||||
| 		int stride; | ||||
| 
 | ||||
| 		texture = SDL_CreateTexture(data->renderer, | ||||
| 					    SDL_PIXELFORMAT_YUY2, | ||||
| 					    SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT); | ||||
| 		if (!texture) { | ||||
| 			printf("can't create texture: %s\n", SDL_GetError()); | ||||
| 			return -ENOMEM; | ||||
| 		} | ||||
| 		if (SDL_LockTexture(texture, NULL, &ptr, &stride) < 0) { | ||||
| 			fprintf(stderr, "Couldn't lock texture: %s\n", SDL_GetError()); | ||||
| 			return -EIO; | ||||
| 		} | ||||
| 		b->texture = texture; | ||||
| 
 | ||||
| 		b->datas[0].type = SPA_DATA_DmaBuf; | ||||
| 		b->datas[0].maxsize = stride * HEIGHT; | ||||
| 		b->datas[0].data = ptr; | ||||
| 		b->datas[0].chunk->offset = 0; | ||||
| 		b->datas[0].chunk->size = stride * HEIGHT; | ||||
| 		b->datas[0].chunk->stride = stride; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int negotiate_formats(struct data *data) | ||||
| { | ||||
| 	int res; | ||||
| 	struct spa_pod *format; | ||||
| 	uint8_t buffer[256]; | ||||
| 	struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); | ||||
| 
 | ||||
| 	data->source_output[0] = SPA_IO_BUFFERS_INIT; | ||||
| 
 | ||||
| 	if ((res = | ||||
| 	     spa_node_port_set_io(data->source, | ||||
| 				  SPA_DIRECTION_OUTPUT, 0, | ||||
| 				  SPA_IO_Buffers, | ||||
| 				  &data->source_output[0], sizeof(data->source_output[0]))) < 0) | ||||
| 		return res; | ||||
| 
 | ||||
| 	format = spa_format_video_raw_build(&b, 0, | ||||
| 			&SPA_VIDEO_INFO_RAW_INIT( | ||||
| 				.format =  SPA_VIDEO_FORMAT_YUY2, | ||||
| 				.size = SPA_RECTANGLE(WIDTH, HEIGHT), | ||||
| 				.framerate = SPA_FRACTION(25,1))); | ||||
| 
 | ||||
| 	if ((res = spa_node_port_set_param(data->source, | ||||
| 					   SPA_DIRECTION_OUTPUT, 0, | ||||
| 					   SPA_PARAM_Format, 0, | ||||
| 					   format)) < 0) | ||||
| 		return res; | ||||
| 
 | ||||
| 
 | ||||
| 	setup_buffers(data); | ||||
| 
 | ||||
| 	if (data->use_buffer) { | ||||
| 		if ((res = sdl_alloc_buffers(data)) < 0) | ||||
| 			return res; | ||||
| 
 | ||||
| 		if ((res = spa_node_port_use_buffers(data->source, | ||||
| 						SPA_DIRECTION_OUTPUT, 0, 0, | ||||
| 						data->bp, data->n_buffers)) < 0) { | ||||
| 			printf("can't allocate buffers: %s\n", spa_strerror(res)); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} else { | ||||
| 		unsigned int n_buffers; | ||||
| 
 | ||||
| 		data->texture = SDL_CreateTexture(data->renderer, | ||||
| 						  SDL_PIXELFORMAT_YUY2, | ||||
| 						  SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT); | ||||
| 		if (!data->texture) { | ||||
| 			printf("can't create texture: %s\n", SDL_GetError()); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		n_buffers = MAX_BUFFERS; | ||||
| 		if ((res = spa_node_port_use_buffers(data->source, | ||||
| 						SPA_DIRECTION_OUTPUT, 0, | ||||
| 						SPA_NODE_BUFFERS_FLAG_ALLOC, | ||||
| 						data->bp, n_buffers)) < 0) { | ||||
| 			printf("can't allocate buffers: %s\n", spa_strerror(res)); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		data->n_buffers = n_buffers; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void *loop(void *user_data) | ||||
| { | ||||
| 	struct data *data = user_data; | ||||
| 
 | ||||
| 	printf("enter thread\n"); | ||||
|     spa_loop_control_enter(data->control); | ||||
| 
 | ||||
|     while (data->running) { | ||||
| 		spa_loop_control_iterate(data->control, -1); | ||||
| 	} | ||||
| 
 | ||||
| 	printf("leave thread\n"); | ||||
|     spa_loop_control_leave(data->control); | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static void run_async_source(struct data *data) | ||||
| { | ||||
| 	int res, err; | ||||
| 	struct spa_command cmd; | ||||
| 	SDL_Event event; | ||||
| 	bool running = true; | ||||
| 
 | ||||
| 	printf("starting...\n\n"); | ||||
| 	cmd = SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Start); | ||||
| 	if ((res = spa_node_send_command(data->source, &cmd)) < 0) | ||||
| 		printf("got error %d\n", res); | ||||
| 
 | ||||
| 	spa_loop_control_leave(data->control); | ||||
| 
 | ||||
| 	data->running = true; | ||||
| 	if ((err = pthread_create(&data->thread, NULL, loop, data)) != 0) { | ||||
| 		printf("can't create thread: %d %s", err, strerror(err)); | ||||
| 		data->running = false; | ||||
| 	} | ||||
| 
 | ||||
| 	while (running && SDL_WaitEvent(&event)) { | ||||
| 		switch (event.type) { | ||||
| 		case SDL_QUIT: | ||||
| 			running = false; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (data->running) { | ||||
| 		data->running = false; | ||||
| 		pthread_join(data->thread, NULL); | ||||
| 	} | ||||
| 
 | ||||
| 	spa_loop_control_enter(data->control); | ||||
| 
 | ||||
| 	printf("pausing...\n\n"); | ||||
| 	cmd = SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Pause); | ||||
| 	if ((res = spa_node_send_command(data->source, &cmd)) < 0) | ||||
| 		printf("got error %d\n", res); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	struct data data = { 0 }; | ||||
| 	int res; | ||||
| 	const char *str; | ||||
| 	struct spa_handle *handle = NULL; | ||||
| 	void *iface; | ||||
| 
 | ||||
| 	if ((res = load_handle(&data, &handle, | ||||
| 					PATH "support/libspa-support.so", | ||||
| 					SPA_NAME_SUPPORT_SYSTEM)) < 0) | ||||
| 		return res; | ||||
| 
 | ||||
| 	if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_System, &iface)) < 0) { | ||||
| 		printf("can't get System interface %d\n", res); | ||||
| 		return res; | ||||
| 	} | ||||
| 	data.system = iface; | ||||
| 	data.support[data.n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_System, data.system); | ||||
| 
 | ||||
| 	if ((res = load_handle(&data, &handle, | ||||
| 					PATH "support/libspa-support.so", | ||||
| 					SPA_NAME_SUPPORT_LOOP)) < 0) | ||||
| 		return res; | ||||
| 
 | ||||
| 	if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Loop, &iface)) < 0) { | ||||
| 		printf("can't get interface %d\n", res); | ||||
| 		return res; | ||||
| 	} | ||||
| 	data.loop = iface; | ||||
| 	if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_LoopControl, &iface)) < 0) { | ||||
| 		printf("can't get interface %d\n", res); | ||||
| 		return res; | ||||
| 	} | ||||
| 	data.control = iface; | ||||
| 
 | ||||
| 	data.use_buffer = USE_BUFFER; | ||||
| 
 | ||||
| 	data.log = &default_log.log; | ||||
| 
 | ||||
| 	if ((str = getenv("SPA_DEBUG"))) | ||||
| 		data.log->level = atoi(str); | ||||
| 
 | ||||
| 	data.support[data.n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Log, data.log); | ||||
| 	data.support[data.n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Loop, data.loop); | ||||
| 	data.support[data.n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataLoop, data.loop); | ||||
| 
 | ||||
| 	if (SDL_Init(SDL_INIT_VIDEO) < 0) { | ||||
| 		printf("can't initialize SDL: %s\n", SDL_GetError()); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (SDL_CreateWindowAndRenderer | ||||
| 	    (WIDTH, HEIGHT, SDL_WINDOW_RESIZABLE, &data.window, &data.renderer)) { | ||||
| 		printf("can't create window: %s\n", SDL_GetError()); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((res = make_nodes(&data, argv[1])) < 0) { | ||||
| 		printf("can't make nodes: %d\n", res); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((res = negotiate_formats(&data)) < 0) { | ||||
| 		printf("can't negotiate nodes: %d\n", res); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	spa_loop_control_enter(data.control); | ||||
| 	run_async_source(&data); | ||||
| 	spa_loop_control_leave(data.control); | ||||
| 
 | ||||
| 	SDL_DestroyRenderer(data.renderer); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -3,6 +3,10 @@ if sdl_dep.found() | |||
|              include_directories : [spa_inc ], | ||||
|              dependencies : [dl_lib, sdl_dep, pthread_lib], | ||||
|              install : false) | ||||
|   executable('local-libcamera', 'local-libcamera.c', | ||||
|              include_directories : [spa_inc ], | ||||
|              dependencies : [dl_lib, sdl_dep, pthread_lib, libcamera_dep], | ||||
|              install : false) | ||||
| endif | ||||
| 
 | ||||
| executable('example-control', 'example-control.c', | ||||
|  |  | |||
|  | @ -75,6 +75,7 @@ enum spa_param_buffers { | |||
| 	SPA_PARAM_BUFFERS_stride,	/**< stride of data block memory (Int) */ | ||||
| 	SPA_PARAM_BUFFERS_align,	/**< alignment of data block memory (Int) */ | ||||
| 	SPA_PARAM_BUFFERS_dataType,	/**< possible memory types (Int, mask of enum spa_data_type) */ | ||||
| 	SPA_PARAM_BUFFERS_datas,	/**< number of datas (Int) */ | ||||
| }; | ||||
| 
 | ||||
| /** properties for SPA_TYPE_OBJECT_ParamMeta */ | ||||
|  |  | |||
|  | @ -77,6 +77,20 @@ extern "C" { | |||
| #define SPA_KEY_API_V4L2_PATH		"api.v4l2.path"			/**< v4l2 device path as can be | ||||
| 									  *  used in open() */ | ||||
| 
 | ||||
| /** keys for libcamera api */ | ||||
| #define SPA_KEY_API_LIBCAMERA		"api.libcamera"			/**< key for the libcamera api */ | ||||
| #define SPA_KEY_API_LIBCAMERA_PATH	"api.libcamera.path"	/**< libcamera device path as can be | ||||
| 									  *  used in open() */ | ||||
| 
 | ||||
| /** info from libcamera_capability */ | ||||
| #define SPA_KEY_API_LIBCAMERA_CAP_DRIVER	"api.libcamera.cap.driver"	/**< driver from capbility */ | ||||
| #define SPA_KEY_API_LIBCAMERA_CAP_CARD	"api.libcamera.cap.card"		/**< caps from capability */ | ||||
| #define SPA_KEY_API_LIBCAMERA_CAP_BUS_INFO	"api.libcamera.cap.bus_info"/**< bus_info from capability */ | ||||
| #define SPA_KEY_API_LIBCAMERA_CAP_VERSION	"api.libcamera.cap.version"	/**< version from capability as %u.%u.%u */ | ||||
| #define SPA_KEY_API_LIBCAMERA_CAP_CAPABILITIES	\ | ||||
| 					"api.libcamera.cap.capabilities"	/**< capabilities from capability */ | ||||
| #define SPA_KEY_API_LIBCAMERA_CAP_DEVICE_CAPS	\ | ||||
| 					"api.libcamera.cap.device-caps"	/**< device_caps from capability */ | ||||
| /** info from v4l2_capability */ | ||||
| #define SPA_KEY_API_V4L2_CAP_DRIVER	"api.v4l2.cap.driver"		/**< driver from capbility */ | ||||
| #define SPA_KEY_API_V4L2_CAP_CARD	"api.v4l2.cap.card"		/**< caps from capability */ | ||||
|  |  | |||
|  | @ -112,6 +112,13 @@ extern "C" { | |||
| #define SPA_NAME_API_V4L2_DEVICE	"api.v4l2.device"		/**< a v4l2 Device interface */ | ||||
| #define SPA_NAME_API_V4L2_SOURCE	"api.v4l2.source"		/**< a v4l2 Node interface for | ||||
| 									  *  capturing */ | ||||
| 
 | ||||
| /** keys for libcamera factory names */ | ||||
| #define SPA_NAME_API_LIBCAMERA_ENUM_CLIENT	"api.libcamera.enum.client"	/**< a libcamera client Device interface */ | ||||
| #define SPA_NAME_API_LIBCAMERA_DEVICE		"api.libcamera.device"		/**< a libcamera Device interface */ | ||||
| #define SPA_NAME_API_LIBCAMERA_SOURCE		"api.libcamera.source"		/**< a libcamera Node interface for | ||||
| 									  *  capturing */ | ||||
| 
 | ||||
| /** keys for jack factory names */ | ||||
| #define SPA_NAME_API_JACK_DEVICE	"api.jack.device"		/**< a jack device. This is a | ||||
| 									  *  client connected to a server */ | ||||
|  |  | |||
|  | @ -33,6 +33,9 @@ if get_option('spa-plugins') | |||
|   if get_option('vulkan') | ||||
|     vulkan_dep = dependency('vulkan') | ||||
|   endif | ||||
|   if get_option('libcamera') | ||||
|     libcamera_dep = dependency('camera') | ||||
|   endif | ||||
| 
 | ||||
|   subdir('plugins') | ||||
| endif | ||||
|  |  | |||
|  | @ -34,6 +34,9 @@ | |||
| #  define _mm256_setr_m128i(v0, v1) _mm256_set_m128i((v1), (v0)) | ||||
| #endif | ||||
| 
 | ||||
| #define _mm256_set_m128i(v0, v1)  _mm256_insertf128_si256(_mm256_castsi128_si256(v1), (v0), 1) | ||||
| #define _mm256_setr_m128i(v0, v1) _mm256_set_m128i((v1), (v0)) | ||||
| 
 | ||||
| static void | ||||
| conv_s16_to_f32d_1s_avx2(void *data, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src, | ||||
| 		uint32_t n_channels, uint32_t n_samples) | ||||
|  |  | |||
							
								
								
									
										249
									
								
								spa/plugins/libcamera/libcamera-client.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								spa/plugins/libcamera/libcamera-client.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,249 @@ | |||
| /* Spa libcamera client
 | ||||
|  * | ||||
|  * Copyright (C) 2020, Collabora Ltd. | ||||
|  *     Author: Raghavendra Rao Sidlagatta <raghavendra.rao@collabora.com> | ||||
|  * | ||||
|  * libcamera-client.c | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a | ||||
|  * copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation | ||||
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||||
|  * and/or sell copies of the Software, and to permit persons to whom the | ||||
|  * Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice (including the next | ||||
|  * paragraph) shall be included in all copies or substantial portions of the | ||||
|  * Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | ||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
|  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||
|  * DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <errno.h> | ||||
| #include <stddef.h> | ||||
| #include <stdio.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <fcntl.h> | ||||
| 
 | ||||
| #include <spa/support/log.h> | ||||
| #include <spa/support/loop.h> | ||||
| #include <spa/support/plugin.h> | ||||
| #include <spa/utils/type.h> | ||||
| #include <spa/utils/keys.h> | ||||
| #include <spa/utils/names.h> | ||||
| #include <spa/monitor/device.h> | ||||
| #include <spa/monitor/utils.h> | ||||
| 
 | ||||
| #include "libcamera.h" | ||||
| 
 | ||||
| #define NAME "libcamera-client" | ||||
| 
 | ||||
| struct impl { | ||||
| 	struct spa_handle handle; | ||||
| 	struct spa_device device; | ||||
| 
 | ||||
| 	struct spa_log *log; | ||||
| 	struct spa_loop *main_loop; | ||||
| 
 | ||||
| 	struct spa_hook_list hooks; | ||||
| 
 | ||||
| 	uint64_t info_all; | ||||
| 	struct spa_device_info info; | ||||
| 
 | ||||
| 	struct spa_source source; | ||||
| 	struct spa_libcamera_device dev; | ||||
| }; | ||||
| 
 | ||||
| static int emit_object_info(struct impl *this, uint32_t id) | ||||
| { | ||||
| 	struct spa_device_object_info info; | ||||
| 	const char *str; | ||||
| 	struct spa_dict_item items[20]; | ||||
| 	uint32_t n_items = 0; | ||||
| 
 | ||||
| 	info = SPA_DEVICE_OBJECT_INFO_INIT(); | ||||
| 
 | ||||
| 	info.type = SPA_TYPE_INTERFACE_Device; | ||||
| 	info.factory_name = SPA_NAME_API_LIBCAMERA_DEVICE; | ||||
| 	info.change_mask = (SPA_DEVICE_OBJECT_CHANGE_MASK_FLAGS | | ||||
| 		SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS); | ||||
| 	info.flags = 0; | ||||
| 
 | ||||
| 	items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_ENUM_API,"libcamera-client"); | ||||
| 	items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API, "libcamera"); | ||||
| 	items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Video/Device"); | ||||
| 
 | ||||
|     info.props = &SPA_DICT_INIT(items, n_items); | ||||
|     spa_device_emit_object_info(&this->hooks, id, &info); | ||||
| 
 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static const struct spa_dict_item device_info_items[] = { | ||||
| 	{ SPA_KEY_DEVICE_API, "libcamera" }, | ||||
| 	{ SPA_KEY_DEVICE_NICK, "libcamera-client" }, | ||||
| 	{ SPA_KEY_API_UDEV_MATCH, "libcamera" }, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static void emit_device_info(struct impl *this, bool full) | ||||
| { | ||||
| 	if (full) | ||||
| 		this->info.change_mask = this->info_all; | ||||
| 	if (this->info.change_mask) { | ||||
| 		this->info.props = &SPA_DICT_INIT_ARRAY(device_info_items); | ||||
| 		spa_device_emit_info(&this->hooks, &this->info); | ||||
| 		this->info.change_mask = 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void impl_hook_removed(struct spa_hook *hook) | ||||
| { | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| impl_device_add_listener(void *object, struct spa_hook *listener, | ||||
| 		const struct spa_device_events *events, void *data) | ||||
| { | ||||
| 	int res; | ||||
| 	struct impl *this = object; | ||||
|     struct spa_hook_list save; | ||||
| 
 | ||||
| 	spa_return_val_if_fail(this != NULL, -EINVAL); | ||||
| 	spa_return_val_if_fail(events != NULL, -EINVAL); | ||||
| 
 | ||||
|     spa_hook_list_isolate(&this->hooks, &save, listener, events, data); | ||||
| 
 | ||||
| 	emit_device_info(this, true); | ||||
| 
 | ||||
| 	emit_object_info(this, 0); | ||||
| 
 | ||||
|     spa_hook_list_join(&this->hooks, &save); | ||||
| 
 | ||||
| 	listener->removed = impl_hook_removed; | ||||
| 	listener->priv = this; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct spa_device_methods impl_device = { | ||||
| 	SPA_VERSION_DEVICE_METHODS, | ||||
| 	.add_listener = impl_device_add_listener, | ||||
| }; | ||||
| 
 | ||||
| static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) | ||||
| { | ||||
| 	struct impl *this; | ||||
| 
 | ||||
| 	spa_return_val_if_fail(handle != NULL, -EINVAL); | ||||
| 	spa_return_val_if_fail(interface != NULL, -EINVAL); | ||||
| 
 | ||||
| 	this = (struct impl *) handle; | ||||
| 
 | ||||
| 	if (strcmp(type, SPA_TYPE_INTERFACE_Device) == 0) | ||||
| 		*interface = &this->device; | ||||
| 	else | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int impl_clear(struct spa_handle *handle) | ||||
| { | ||||
| 	struct impl *this = (struct impl *) handle; | ||||
| 
 | ||||
| 	if(this->dev.camera) { | ||||
| 		deleteLibCamera(this->dev.camera); | ||||
| 		free(this->dev.camera); | ||||
| 		this->dev.camera = NULL; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static size_t | ||||
| impl_get_size(const struct spa_handle_factory *factory, | ||||
| 	      const struct spa_dict *params) | ||||
| { | ||||
| 	return sizeof(struct impl); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| impl_init(const struct spa_handle_factory *factory, | ||||
| 	  struct spa_handle *handle, | ||||
| 	  const struct spa_dict *info, | ||||
| 	  const struct spa_support *support, | ||||
| 	  uint32_t n_support) | ||||
| { | ||||
| 	struct impl *this; | ||||
| 
 | ||||
| 	spa_return_val_if_fail(factory != NULL, -EINVAL); | ||||
| 	spa_return_val_if_fail(handle != NULL, -EINVAL); | ||||
| 
 | ||||
| 	handle->get_interface = impl_get_interface; | ||||
| 	handle->clear = impl_clear; | ||||
| 
 | ||||
| 	this = (struct impl *) handle; | ||||
| 
 | ||||
| 	this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); | ||||
| 	this->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop); | ||||
| 
 | ||||
| 	if (this->main_loop == NULL) { | ||||
| 		spa_log_error(this->log, "a main-loop is needed"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	spa_hook_list_init(&this->hooks); | ||||
| 
 | ||||
| 	this->device.iface = SPA_INTERFACE_INIT( | ||||
| 			SPA_TYPE_INTERFACE_Device, | ||||
| 			SPA_VERSION_DEVICE, | ||||
| 			&impl_device, this); | ||||
| 
 | ||||
| 	this->info = SPA_DEVICE_INFO_INIT(); | ||||
| 	this->info_all = SPA_DEVICE_CHANGE_MASK_FLAGS | | ||||
| 			SPA_DEVICE_CHANGE_MASK_PROPS; | ||||
| 	this->info.flags = 0; | ||||
| 
 | ||||
| 	if(this->dev.camera == NULL) { | ||||
| 		this->dev.camera = (LibCamera*)newLibCamera(); | ||||
| 		libcamera_set_log(this->dev.camera, this->dev.log); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct spa_interface_info impl_interfaces[] = { | ||||
| 	{SPA_TYPE_INTERFACE_Device,}, | ||||
| }; | ||||
| 
 | ||||
| static int | ||||
| impl_enum_interface_info(const struct spa_handle_factory *factory, | ||||
| 			 const struct spa_interface_info **info, | ||||
| 			 uint32_t *index) | ||||
| { | ||||
| 	spa_return_val_if_fail(factory != NULL, -EINVAL); | ||||
| 	spa_return_val_if_fail(info != NULL, -EINVAL); | ||||
| 	spa_return_val_if_fail(index != NULL, -EINVAL); | ||||
| 
 | ||||
| 	if (*index >= SPA_N_ELEMENTS(impl_interfaces)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	*info = &impl_interfaces[(*index)++]; | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| const struct spa_handle_factory spa_libcamera_client_factory = { | ||||
| 	SPA_VERSION_HANDLE_FACTORY, | ||||
| 	SPA_NAME_API_LIBCAMERA_ENUM_CLIENT, | ||||
| 	NULL, | ||||
| 	impl_get_size, | ||||
| 	impl_init, | ||||
| 	impl_enum_interface_info, | ||||
| }; | ||||
							
								
								
									
										289
									
								
								spa/plugins/libcamera/libcamera-device.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										289
									
								
								spa/plugins/libcamera/libcamera-device.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,289 @@ | |||
| /* Spa libcamera Source
 | ||||
|  * | ||||
|  * Copyright (C) 2020, Collabora Ltd. | ||||
|  *     Author: Raghavendra Rao Sidlagatta <raghavendra.rao@collabora.com> | ||||
|  * | ||||
|  * libcamera-device.c | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a | ||||
|  * copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation | ||||
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||||
|  * and/or sell copies of the Software, and to permit persons to whom the | ||||
|  * Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice (including the next | ||||
|  * paragraph) shall be included in all copies or substantial portions of the | ||||
|  * Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | ||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
|  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||
|  * DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <stddef.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <fcntl.h> | ||||
| 
 | ||||
| #include <sys/ioctl.h> | ||||
| 
 | ||||
| #include <spa/support/plugin.h> | ||||
| #include <spa/support/log.h> | ||||
| #include <spa/support/loop.h> | ||||
| #include <spa/utils/keys.h> | ||||
| #include <spa/utils/names.h> | ||||
| #include <spa/node/node.h> | ||||
| #include <spa/pod/builder.h> | ||||
| #include <spa/monitor/device.h> | ||||
| #include <spa/monitor/utils.h> | ||||
| #include <spa/debug/pod.h> | ||||
| 
 | ||||
| #include "libcamera.h" | ||||
| 
 | ||||
| #define NAME "libcamera-device" | ||||
| 
 | ||||
| static const char default_device[] = "/dev/media0"; | ||||
| 
 | ||||
| struct props { | ||||
| 	char device[64]; | ||||
| 	char device_name[128]; | ||||
| 	int device_fd; | ||||
| }; | ||||
| 
 | ||||
| static void reset_props(struct props *props) | ||||
| { | ||||
| 	strncpy(props->device, default_device, 64); | ||||
| } | ||||
| 
 | ||||
| struct impl { | ||||
| 	struct spa_handle handle; | ||||
| 	struct spa_device device; | ||||
| 
 | ||||
| 	struct spa_log *log; | ||||
| 
 | ||||
| 	struct props props; | ||||
| 
 | ||||
| 	struct spa_hook_list hooks; | ||||
| 
 | ||||
| 	struct spa_libcamera_device dev; | ||||
| }; | ||||
| 
 | ||||
| static int emit_info(struct impl *this, bool full) | ||||
| { | ||||
| 	int res, err, fd; | ||||
| 	struct spa_dict_item items[10]; | ||||
| 	uint32_t n_items = 0; | ||||
| 	struct spa_device_info info; | ||||
| 	struct spa_param_info params[2]; | ||||
|     char path[128], version[16], capabilities[16], device_caps[16]; | ||||
| 
 | ||||
| 	if ((res = spa_libcamera_open(&this->dev)) < 0) | ||||
| 		return res; | ||||
| 
 | ||||
| 	info = SPA_DEVICE_INFO_INIT(); | ||||
| 
 | ||||
| 	info.change_mask = SPA_DEVICE_CHANGE_MASK_PROPS; | ||||
| 	fd = get_dev_fd(&this->dev); | ||||
| 
 | ||||
| 	do { | ||||
| 		err = ioctl(this->dev.fd, MEDIA_IOC_DEVICE_INFO, &this->dev.dev_info); | ||||
| 	} while (err == -1 && errno == EINTR); | ||||
| 
 | ||||
| 	if(err < 0) { | ||||
| 		spa_log_error(this->log, "%s:: Failed to query MEDIA_IOC_DEVICE_INFO on fd %d\n", __FUNCTION__, this->dev.fd); | ||||
| 	} | ||||
| 
 | ||||
| #define ADD_ITEM(key, value) items[n_items++] = SPA_DICT_ITEM_INIT(key, value) | ||||
| 	snprintf(path, sizeof(path), "libcamera:%s", this->props.device); | ||||
| 	ADD_ITEM(SPA_KEY_OBJECT_PATH, path); | ||||
| 	ADD_ITEM(SPA_KEY_DEVICE_API, "libcamera"); | ||||
| 	ADD_ITEM(SPA_KEY_MEDIA_CLASS, "Video/Device"); | ||||
| 	ADD_ITEM(SPA_KEY_API_LIBCAMERA_PATH, (char *)this->props.device); | ||||
| 	ADD_ITEM(SPA_KEY_API_LIBCAMERA_CAP_DRIVER, (char *)this->dev.dev_info.driver); | ||||
| 	ADD_ITEM(SPA_KEY_API_LIBCAMERA_CAP_CARD, (char *)this->dev.dev_info.model); | ||||
| 	ADD_ITEM(SPA_KEY_API_LIBCAMERA_CAP_BUS_INFO, (char *)this->dev.dev_info.bus_info); | ||||
| 	snprintf(version, sizeof(version), "%u.%u.%u", | ||||
| 			(this->dev.dev_info.media_version >> 16) & 0xFF, | ||||
| 			(this->dev.dev_info.media_version >> 8) & 0xFF, | ||||
| 			(this->dev.dev_info.media_version) & 0xFF); | ||||
| 	ADD_ITEM(SPA_KEY_API_LIBCAMERA_CAP_VERSION, version); | ||||
| #undef ADD_ITEM | ||||
| 	info.props = &SPA_DICT_INIT(items, n_items); | ||||
| 
 | ||||
| 	info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS; | ||||
| 	params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumProfile, SPA_PARAM_INFO_READ); | ||||
| 	params[1] = SPA_PARAM_INFO(SPA_PARAM_Profile, SPA_PARAM_INFO_WRITE); | ||||
| 	info.n_params = SPA_N_ELEMENTS(params); | ||||
| 	info.params = params; | ||||
| 
 | ||||
| 	spa_device_emit_info(&this->hooks, &info); | ||||
| 
 | ||||
| 	if (spa_libcamera_is_capture(&this->dev)) { | ||||
| 		struct spa_device_object_info oinfo; | ||||
| 
 | ||||
| 		oinfo = SPA_DEVICE_OBJECT_INFO_INIT(); | ||||
| 		oinfo.type = SPA_TYPE_INTERFACE_Node; | ||||
| 		oinfo.factory_name = SPA_NAME_API_LIBCAMERA_SOURCE; | ||||
| 		oinfo.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS; | ||||
| 		oinfo.props = &SPA_DICT_INIT(items, n_items); | ||||
| 
 | ||||
| 		spa_device_emit_object_info(&this->hooks, 0, &oinfo); | ||||
| 	} | ||||
| 
 | ||||
| 	spa_libcamera_close(&this->dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int impl_add_listener(void *object, | ||||
| 			struct spa_hook *listener, | ||||
| 			const struct spa_device_events *events, | ||||
| 			void *data) | ||||
| { | ||||
| 	struct impl *this = object; | ||||
| 	struct spa_hook_list save; | ||||
| 	int res = 0; | ||||
| 
 | ||||
| 	spa_return_val_if_fail(this != NULL, -EINVAL); | ||||
| 	spa_return_val_if_fail(events != NULL, -EINVAL); | ||||
| 
 | ||||
| 	spa_hook_list_isolate(&this->hooks, &save, listener, events, data); | ||||
| 
 | ||||
| 	if (events->info || events->object_info) | ||||
| 		res = emit_info(this, true); | ||||
| 
 | ||||
| 	spa_hook_list_join(&this->hooks, &save); | ||||
| 
 | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| static int impl_sync(void *object, int seq) | ||||
| { | ||||
| 	struct impl *this = object; | ||||
| 
 | ||||
| 	spa_return_val_if_fail(this != NULL, -EINVAL); | ||||
| 
 | ||||
| 	spa_device_emit_result(&this->hooks, seq, 0, 0, NULL); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int impl_enum_params(void *object, int seq, | ||||
| 			    uint32_t id, uint32_t start, uint32_t num, | ||||
| 			    const struct spa_pod *filter) | ||||
| { | ||||
| 	return -ENOTSUP; | ||||
| } | ||||
| 
 | ||||
| static int impl_set_param(void *object, | ||||
| 			  uint32_t id, uint32_t flags, | ||||
| 			  const struct spa_pod *param) | ||||
| { | ||||
| 	return -ENOTSUP; | ||||
| } | ||||
| 
 | ||||
| static const struct spa_device_methods impl_device = { | ||||
| 	SPA_VERSION_DEVICE_METHODS, | ||||
| 	.add_listener = impl_add_listener, | ||||
| 	.sync = impl_sync, | ||||
| 	.enum_params = impl_enum_params, | ||||
| 	.set_param = impl_set_param, | ||||
| }; | ||||
| 
 | ||||
| static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) | ||||
| { | ||||
| 	struct impl *this; | ||||
| 
 | ||||
| 	spa_return_val_if_fail(handle != NULL, -EINVAL); | ||||
| 	spa_return_val_if_fail(interface != NULL, -EINVAL); | ||||
| 
 | ||||
| 	this = (struct impl *) handle; | ||||
| 
 | ||||
| 	if (strcmp(type, SPA_TYPE_INTERFACE_Device) == 0) | ||||
| 		*interface = &this->device; | ||||
| 	else | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int impl_clear(struct spa_handle *handle) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static size_t | ||||
| impl_get_size(const struct spa_handle_factory *factory, | ||||
| 	      const struct spa_dict *params) | ||||
| { | ||||
| 	return sizeof(struct impl); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| impl_init(const struct spa_handle_factory *factory, | ||||
| 	  struct spa_handle *handle, | ||||
| 	  const struct spa_dict *info, | ||||
| 	  const struct spa_support *support, | ||||
| 	  uint32_t n_support) | ||||
| { | ||||
| 	struct impl *this; | ||||
| 	const char *str; | ||||
| 	int res; | ||||
| 
 | ||||
| 	spa_return_val_if_fail(factory != NULL, -EINVAL); | ||||
| 	spa_return_val_if_fail(handle != NULL, -EINVAL); | ||||
| 
 | ||||
| 	handle->get_interface = impl_get_interface; | ||||
| 	handle->clear = impl_clear, this = (struct impl *) handle; | ||||
| 
 | ||||
| 	this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); | ||||
| 
 | ||||
| 	spa_hook_list_init(&this->hooks); | ||||
| 
 | ||||
| 	this->device.iface = SPA_INTERFACE_INIT( | ||||
| 			SPA_TYPE_INTERFACE_Device, | ||||
| 			SPA_VERSION_DEVICE, | ||||
| 			&impl_device, this); | ||||
| 	this->dev.log = this->log; | ||||
| 	this->dev.fd = -1; | ||||
| 
 | ||||
| 	reset_props(&this->props); | ||||
| 
 | ||||
| 	if (info && (str = spa_dict_lookup(info, SPA_KEY_API_LIBCAMERA_PATH))) | ||||
| 		strncpy(this->props.device, str, 63); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct spa_interface_info impl_interfaces[] = { | ||||
| 	{SPA_TYPE_INTERFACE_Device,}, | ||||
| }; | ||||
| 
 | ||||
| static int impl_enum_interface_info(const struct spa_handle_factory *factory, | ||||
| 				    const struct spa_interface_info **info, | ||||
| 				    uint32_t *index) | ||||
| { | ||||
| 	spa_return_val_if_fail(factory != NULL, -EINVAL); | ||||
| 	spa_return_val_if_fail(info != NULL, -EINVAL); | ||||
| 	spa_return_val_if_fail(index != NULL, -EINVAL); | ||||
| 
 | ||||
| 	if (*index >= SPA_N_ELEMENTS(impl_interfaces)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	*info = &impl_interfaces[(*index)++]; | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| const struct spa_handle_factory spa_libcamera_device_factory = { | ||||
| 	SPA_VERSION_HANDLE_FACTORY, | ||||
| 	SPA_NAME_API_LIBCAMERA_DEVICE, | ||||
| 	NULL, | ||||
| 	impl_get_size, | ||||
| 	impl_init, | ||||
| 	impl_enum_interface_info, | ||||
| }; | ||||
							
								
								
									
										1026
									
								
								spa/plugins/libcamera/libcamera-source.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1026
									
								
								spa/plugins/libcamera/libcamera-source.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										955
									
								
								spa/plugins/libcamera/libcamera-utils.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										955
									
								
								spa/plugins/libcamera/libcamera-utils.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,955 @@ | |||
| /* Spa
 | ||||
|  * | ||||
|  * Copyright (C) 2020, Collabora Ltd. | ||||
|  *     Author: Raghavendra Rao Sidlagatta <raghavendra.rao@collabora.com> | ||||
|  * | ||||
|  * libcamera-utils.c | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a | ||||
|  * copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation | ||||
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||||
|  * and/or sell copies of the Software, and to permit persons to whom the | ||||
|  * Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice (including the next | ||||
|  * paragraph) shall be included in all copies or substantial portions of the | ||||
|  * Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | ||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
|  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||
|  * DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
| #include <sched.h> | ||||
| #include <errno.h> | ||||
| #include <sys/mman.h> | ||||
| #include <poll.h> | ||||
| 
 | ||||
| #include <linux/media.h> | ||||
| 
 | ||||
| static void libcamera_on_fd_events(struct spa_source *source); | ||||
| 
 | ||||
| int get_dev_fd(struct spa_libcamera_device *dev) { | ||||
| 	if(dev->fd == -1) { | ||||
| 		int fd = open("/dev/media0", O_RDONLY | O_NONBLOCK, 0); | ||||
| 		return fd; | ||||
| 	} else { | ||||
| 		return dev->fd; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int spa_libcamera_open(struct spa_libcamera_device *dev) | ||||
| { | ||||
| 	int refCnt = 0; | ||||
| 
 | ||||
| 	if(!dev) { | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	dev->fd = get_dev_fd(dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int spa_libcamera_is_capture(struct spa_libcamera_device *dev) | ||||
| { | ||||
| 	if(!dev) { | ||||
| 		spa_log_error(dev->log, "Invalid argument"); | ||||
| 		return false; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| int spa_libcamera_close(struct spa_libcamera_device *dev) | ||||
| { | ||||
| 	int refCnt = 0; | ||||
| 	if(!dev) { | ||||
| 		spa_log_error(dev->log, "Invalid argument"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dev->fd == -1) { | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dev->active || dev->have_format) { | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	if (close(dev->fd)) { | ||||
| 		spa_log_warn(dev->log, "close: %m"); | ||||
| 	} | ||||
| 
 | ||||
| 	dev->fd = -1; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int spa_libcamera_buffer_recycle(struct impl *this, uint32_t buffer_id) | ||||
| { | ||||
| 	struct port *port = &this->out_ports[0]; | ||||
| 	struct buffer *b = &port->buffers[buffer_id]; | ||||
| 	struct spa_libcamera_device *dev = &port->dev; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (!SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUTSTANDING)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUTSTANDING); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int spa_libcamera_clear_buffers(struct impl *this) | ||||
| { | ||||
| 	struct port *port = &this->out_ports[0]; | ||||
| 	uint32_t i; | ||||
| 
 | ||||
| 	if (port->n_buffers == 0) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	for (i = 0; i < port->n_buffers; i++) { | ||||
| 		struct buffer *b; | ||||
| 		struct spa_data *d; | ||||
| 
 | ||||
| 		b = &port->buffers[i]; | ||||
| 		d = b->outbuf->datas; | ||||
| 
 | ||||
| 		if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUTSTANDING)) { | ||||
| 			spa_log_debug(this->log, "libcamera: queueing outstanding buffer %p", b); | ||||
| 			spa_libcamera_buffer_recycle(this, i); | ||||
| 		} | ||||
| 		if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_MAPPED)) { | ||||
| 			munmap(SPA_MEMBER(b->ptr, -d[0].mapoffset, void), | ||||
| 					d[0].maxsize - d[0].mapoffset); | ||||
| 		} | ||||
| 		if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_ALLOCATED)) { | ||||
| 			close(d[0].fd); | ||||
| 		} | ||||
| 		d[0].type = SPA_ID_INVALID; | ||||
| 	} | ||||
| 
 | ||||
| 	port->n_buffers = 0; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| struct format_info { | ||||
| 	char fourcc[32]; | ||||
| 	uint32_t format; | ||||
| 	uint32_t media_type; | ||||
| 	uint32_t media_subtype; | ||||
| }; | ||||
| 
 | ||||
| #define VIDEO   SPA_MEDIA_TYPE_video | ||||
| #define IMAGE   SPA_MEDIA_TYPE_image | ||||
| 
 | ||||
| #define RAW     SPA_MEDIA_SUBTYPE_raw | ||||
| 
 | ||||
| #define BAYER   SPA_MEDIA_SUBTYPE_bayer | ||||
| #define MJPG    SPA_MEDIA_SUBTYPE_mjpg | ||||
| #define JPEG    SPA_MEDIA_SUBTYPE_jpeg | ||||
| #define DV      SPA_MEDIA_SUBTYPE_dv | ||||
| #define MPEGTS  SPA_MEDIA_SUBTYPE_mpegts | ||||
| #define H264    SPA_MEDIA_SUBTYPE_h264 | ||||
| #define H263    SPA_MEDIA_SUBTYPE_h263 | ||||
| #define MPEG1   SPA_MEDIA_SUBTYPE_mpeg1 | ||||
| #define MPEG2   SPA_MEDIA_SUBTYPE_mpeg2 | ||||
| #define MPEG4   SPA_MEDIA_SUBTYPE_mpeg4 | ||||
| #define XVID    SPA_MEDIA_SUBTYPE_xvid | ||||
| #define VC1     SPA_MEDIA_SUBTYPE_vc1 | ||||
| #define VP8     SPA_MEDIA_SUBTYPE_vp8 | ||||
| 
 | ||||
| #define FORMAT_UNKNOWN    SPA_VIDEO_FORMAT_UNKNOWN | ||||
| #define FORMAT_ENCODED    SPA_VIDEO_FORMAT_ENCODED | ||||
| #define FORMAT_RGB15      SPA_VIDEO_FORMAT_RGB15 | ||||
| #define FORMAT_BGR15      SPA_VIDEO_FORMAT_BGR15 | ||||
| #define FORMAT_RGB16      SPA_VIDEO_FORMAT_RGB16 | ||||
| #define FORMAT_BGR        SPA_VIDEO_FORMAT_BGR | ||||
| #define FORMAT_RGB        SPA_VIDEO_FORMAT_RGB | ||||
| #define FORMAT_BGRA       SPA_VIDEO_FORMAT_BGRA | ||||
| #define FORMAT_BGRx       SPA_VIDEO_FORMAT_BGRx | ||||
| #define FORMAT_ARGB       SPA_VIDEO_FORMAT_ARGB | ||||
| #define FORMAT_xRGB       SPA_VIDEO_FORMAT_xRGB | ||||
| #define FORMAT_GRAY8      SPA_VIDEO_FORMAT_GRAY8 | ||||
| #define FORMAT_GRAY16_LE  SPA_VIDEO_FORMAT_GRAY16_LE | ||||
| #define FORMAT_GRAY16_BE  SPA_VIDEO_FORMAT_GRAY16_BE | ||||
| #define FORMAT_YVU9       SPA_VIDEO_FORMAT_YVU9 | ||||
| #define FORMAT_YV12       SPA_VIDEO_FORMAT_YV12 | ||||
| #define FORMAT_YUY2       SPA_VIDEO_FORMAT_YUY2 | ||||
| #define FORMAT_YVYU       SPA_VIDEO_FORMAT_YVYU | ||||
| #define FORMAT_UYVY       SPA_VIDEO_FORMAT_UYVY | ||||
| #define FORMAT_Y42B       SPA_VIDEO_FORMAT_Y42B | ||||
| #define FORMAT_Y41B       SPA_VIDEO_FORMAT_Y41B | ||||
| #define FORMAT_YUV9       SPA_VIDEO_FORMAT_YUV9 | ||||
| #define FORMAT_I420       SPA_VIDEO_FORMAT_I420 | ||||
| #define FORMAT_NV12       SPA_VIDEO_FORMAT_NV12 | ||||
| #define FORMAT_NV12_64Z32 SPA_VIDEO_FORMAT_NV12_64Z32 | ||||
| #define FORMAT_NV21       SPA_VIDEO_FORMAT_NV21 | ||||
| #define FORMAT_NV16       SPA_VIDEO_FORMAT_NV16 | ||||
| #define FORMAT_NV61       SPA_VIDEO_FORMAT_NV61 | ||||
| #define FORMAT_NV24       SPA_VIDEO_FORMAT_NV24 | ||||
| 
 | ||||
| static const struct format_info format_info[] = { | ||||
| 	/* RGB formats */ | ||||
| 	{{"RGB332"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"ARGB555"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"XRGB555"}, FORMAT_RGB15, VIDEO, RAW}, | ||||
| 	{{"ARGB555X"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"XRGB555X"}, FORMAT_BGR15, VIDEO, RAW}, | ||||
| 	{{"RGB565"}, FORMAT_RGB16, VIDEO, RAW}, | ||||
| 	{{"RGB565X"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"BGR666"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"BGR24"}, FORMAT_BGR, VIDEO, RAW}, | ||||
| 	{{"RGB24"}, FORMAT_RGB, VIDEO, RAW}, | ||||
| 	{{"ABGR32"}, FORMAT_BGRA, VIDEO, RAW}, | ||||
| 	{{"XBGR32"}, FORMAT_BGRx, VIDEO, RAW}, | ||||
| 	{{"ARGB32"}, FORMAT_ARGB, VIDEO, RAW}, | ||||
| 	{{"XRGB32"}, FORMAT_xRGB, VIDEO, RAW}, | ||||
| 
 | ||||
| 	/* Deprecated Packed RGB Image Formats (alpha ambiguity) */ | ||||
| 	{{"RGB444"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"RGB555"}, FORMAT_RGB15, VIDEO, RAW}, | ||||
| 	{{"RGB555X"}, FORMAT_BGR15, VIDEO, RAW}, | ||||
| 	{{"BGR32"}, FORMAT_BGRx, VIDEO, RAW}, | ||||
| 	{{"RGB32"}, FORMAT_xRGB, VIDEO, RAW}, | ||||
| 
 | ||||
| 	/* Grey formats */ | ||||
| 	{{"GREY"}, FORMAT_GRAY8, VIDEO, RAW}, | ||||
| 	{{"Y4"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"Y6"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"Y10"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"Y12"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"Y16"}, FORMAT_GRAY16_LE, VIDEO, RAW}, | ||||
| 	{{"Y16_BE"}, FORMAT_GRAY16_BE, VIDEO, RAW}, | ||||
| 	{{"Y10BPACK"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 
 | ||||
| 	/* Palette formats */ | ||||
| 	{{"PAL8"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 
 | ||||
| 	/* Chrominance formats */ | ||||
| 	{{"UV8"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 
 | ||||
| 	/* Luminance+Chrominance formats */ | ||||
| 	{{"YVU410"}, FORMAT_YVU9, VIDEO, RAW}, | ||||
| 	{{"YVU420"}, FORMAT_YV12, VIDEO, RAW}, | ||||
| 	{{"YVU420M"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"YUYV"}, FORMAT_YUY2, VIDEO, RAW}, | ||||
| 	{{"YYUV"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"YVYU"}, FORMAT_YVYU, VIDEO, RAW}, | ||||
| 	{{"UYVY"}, FORMAT_UYVY, VIDEO, RAW}, | ||||
| 	{{"VYUY"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"YUV422P"}, FORMAT_Y42B, VIDEO, RAW}, | ||||
| 	{{"YUV411P"}, FORMAT_Y41B, VIDEO, RAW}, | ||||
| 	{{"Y41P"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"YUV444"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"YUV555"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"YUV565"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"YUV32"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"YUV410"}, FORMAT_YUV9, VIDEO, RAW}, | ||||
| 	{{"YUV420"}, FORMAT_I420, VIDEO, RAW}, | ||||
| 	{{"YUV420M"}, FORMAT_I420, VIDEO, RAW}, | ||||
| 	{{"HI240"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"HM12"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"M420"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 
 | ||||
| 	/* two planes -- one Y, one Cr + Cb interleaved  */ | ||||
| 	{{"NV12"}, FORMAT_NV12, VIDEO, RAW}, | ||||
| 	{{"NV12M"}, FORMAT_NV12, VIDEO, RAW}, | ||||
| 	{{"NV12MT"}, FORMAT_NV12_64Z32, VIDEO, RAW}, | ||||
| 	{{"NV12MT_16X16"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"NV21"}, FORMAT_NV21, VIDEO, RAW}, | ||||
| 	{{"NV21M"}, FORMAT_NV21, VIDEO, RAW}, | ||||
| 	{{"NV16"}, FORMAT_NV16, VIDEO, RAW}, | ||||
| 	{{"NV16M"}, FORMAT_NV16, VIDEO, RAW}, | ||||
| 	{{"NV61"}, FORMAT_NV61, VIDEO, RAW}, | ||||
| 	{{"NV61M"}, FORMAT_NV61, VIDEO, RAW}, | ||||
| 	{{"NV24"}, FORMAT_NV24, VIDEO, RAW}, | ||||
| 	{{"NV42"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 
 | ||||
| 	/* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */ | ||||
| 	{{"SBGGR8"}, FORMAT_UNKNOWN, VIDEO, BAYER}, | ||||
| 	{{"SGBRG8"}, FORMAT_UNKNOWN, VIDEO, BAYER}, | ||||
| 	{{"SGRBG8"}, FORMAT_UNKNOWN, VIDEO, BAYER}, | ||||
| 	{{"SRGGB8"}, FORMAT_UNKNOWN, VIDEO, BAYER}, | ||||
| 
 | ||||
| 	/* compressed formats */ | ||||
| 	{{"MJPEG"}, FORMAT_ENCODED, VIDEO, MJPG}, | ||||
| 	{{"JPEG"}, FORMAT_ENCODED, VIDEO, MJPG}, | ||||
| 	{{"PJPG"}, FORMAT_ENCODED, VIDEO, MJPG}, | ||||
| 	{{"DV"}, FORMAT_ENCODED, VIDEO, DV}, | ||||
| 	{{"MPEG"}, FORMAT_ENCODED, VIDEO, MPEGTS}, | ||||
| 	{{"H264"}, FORMAT_ENCODED, VIDEO, H264}, | ||||
| 	{{"H264_NO_SC"}, FORMAT_ENCODED, VIDEO, H264}, | ||||
| 	{{"H264_MVC"}, FORMAT_ENCODED, VIDEO, H264}, | ||||
| 	{{"H263"}, FORMAT_ENCODED, VIDEO, H263}, | ||||
| 	{{"MPEG1"}, FORMAT_ENCODED, VIDEO, MPEG1}, | ||||
| 	{{"MPEG2"}, FORMAT_ENCODED, VIDEO, MPEG2}, | ||||
| 	{{"MPEG4"}, FORMAT_ENCODED, VIDEO, MPEG4}, | ||||
| 	{{"XVID"}, FORMAT_ENCODED, VIDEO, XVID}, | ||||
| 	{{"VC1_ANNEX_G"}, FORMAT_ENCODED, VIDEO, VC1}, | ||||
| 	{{"VC1_ANNEX_L"}, FORMAT_ENCODED, VIDEO, VC1}, | ||||
| 	{{"VP8"}, FORMAT_ENCODED, VIDEO, VP8}, | ||||
| 
 | ||||
| 	/*  Vendor-specific formats   */ | ||||
| 	{{"WNVA"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"SN9C10X"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"PWC1"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| 	{{"PWC2"}, FORMAT_UNKNOWN, VIDEO, RAW}, | ||||
| }; | ||||
| 
 | ||||
| static const struct format_info *video_format_to_info(uint32_t fmt) { | ||||
| 	size_t i; | ||||
| 
 | ||||
| 	for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) { | ||||
| 		if (format_info[i].format == fmt) | ||||
| 			return &format_info[i]; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static const struct format_info *find_format_info_by_media_type(uint32_t type, | ||||
| 								uint32_t subtype, | ||||
| 								uint32_t format, | ||||
| 								int startidx) | ||||
| { | ||||
| 	size_t i; | ||||
| 
 | ||||
| 	for (i = startidx; i < SPA_N_ELEMENTS(format_info); i++) { | ||||
| 		if ((format_info[i].media_type == type) && | ||||
| 		    (format_info[i].media_subtype == subtype) && | ||||
| 		    (format == 0 || format_info[i].format == format)) | ||||
| 			return &format_info[i]; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static uint32_t | ||||
| enum_filter_format(uint32_t media_type, int32_t media_subtype, | ||||
| 		   const struct spa_pod *filter, uint32_t index) | ||||
| { | ||||
| 	uint32_t video_format = 0; | ||||
| 
 | ||||
| 	switch (media_type) { | ||||
| 	case SPA_MEDIA_TYPE_video: | ||||
| 	case SPA_MEDIA_TYPE_image: | ||||
| 		if (media_subtype == SPA_MEDIA_SUBTYPE_raw) { | ||||
| 			const struct spa_pod_prop *p; | ||||
| 			const struct spa_pod *val; | ||||
| 			uint32_t n_values, choice; | ||||
| 			const uint32_t *values; | ||||
| 
 | ||||
| 			if (!(p = spa_pod_find_prop(filter, NULL, SPA_FORMAT_VIDEO_format))) | ||||
| 				return SPA_VIDEO_FORMAT_UNKNOWN; | ||||
| 
 | ||||
| 			val = spa_pod_get_values(&p->value, &n_values, &choice); | ||||
| 
 | ||||
| 			if (val->type != SPA_TYPE_Id) | ||||
| 				return SPA_VIDEO_FORMAT_UNKNOWN; | ||||
| 
 | ||||
| 			values = SPA_POD_BODY(val); | ||||
| 
 | ||||
| 			if (choice == SPA_CHOICE_None) { | ||||
| 				if (index == 0) | ||||
| 					video_format = values[0]; | ||||
| 			} else { | ||||
| 				if (index + 1 < n_values) | ||||
| 					video_format = values[index + 1]; | ||||
| 			} | ||||
| 		} else { | ||||
| 			if (index == 0) | ||||
| 				video_format = SPA_VIDEO_FORMAT_ENCODED; | ||||
| 		} | ||||
| 	} | ||||
| 	return video_format; | ||||
| } | ||||
| 
 | ||||
| #define FOURCC_ARGS(f) (f)&0x7f,((f)>>8)&0x7f,((f)>>16)&0x7f,((f)>>24)&0x7f | ||||
| 
 | ||||
| static int | ||||
| spa_libcamera_enum_format(struct impl *this, int seq, | ||||
| 		     uint32_t start, uint32_t num, | ||||
| 		     const struct spa_pod *filter) | ||||
| { | ||||
| 	struct port *port = &this->out_ports[0]; | ||||
| 	int res, n_fractions; | ||||
| 	const struct format_info *info; | ||||
| 	struct spa_pod_choice *choice; | ||||
| 	uint32_t filter_media_type, filter_media_subtype, video_format; | ||||
| 	struct spa_libcamera_device *dev = &port->dev; | ||||
| 	uint8_t buffer[1024]; | ||||
| 	struct spa_pod_builder b = { 0 }; | ||||
| 	struct spa_pod_frame f[2]; | ||||
| 	struct spa_result_node_params result; | ||||
| 	uint32_t count = 0; | ||||
| 	uint32_t width = 0, height = 0; | ||||
| 
 | ||||
| 	if ((res = spa_libcamera_open(dev)) < 0) { | ||||
| 		spa_log_error(dev->log, "failed to open libcamera device"); | ||||
| 		return res; | ||||
| 	} | ||||
| 
 | ||||
| 	result.id = SPA_PARAM_EnumFormat; | ||||
| 	result.next = start; | ||||
| 
 | ||||
| 	if (result.next == 0) { | ||||
| 		port->fmtdesc_index = 0; | ||||
| 		spa_zero(port->fmt); | ||||
| 	} | ||||
| 
 | ||||
| next_fmtdesc: | ||||
| 	port->fmtdesc_index++; | ||||
| 
 | ||||
| next: | ||||
| 	result.index = result.next++; | ||||
| 
 | ||||
| 	/* Enumerate all the video formats supported by libcamera */ | ||||
| 	video_format = libcamera_drm_to_video_format( | ||||
| 		libcamera_enum_streamcfgpixel_format(dev->camera, port->fmtdesc_index)); | ||||
| 	if(UINT32_MAX == video_format) { | ||||
| 		goto enum_end; | ||||
| 	} | ||||
| 	port->fmt.pixelformat = video_format; | ||||
| 	port->fmt.width = libcamera_get_streamcfg_width(dev->camera); | ||||
| 	port->fmt.height = libcamera_get_streamcfg_height(dev->camera); | ||||
| 
 | ||||
| 	if (!(info = video_format_to_info(video_format))) { | ||||
| 		goto next_fmtdesc; | ||||
| 	} | ||||
| 
 | ||||
| 	spa_pod_builder_init(&b, buffer, sizeof(buffer)); | ||||
| 	spa_pod_builder_push_object(&b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat); | ||||
| 	spa_pod_builder_add(&b, | ||||
| 			SPA_FORMAT_mediaType,    SPA_POD_Id(info->media_type), | ||||
| 			SPA_FORMAT_mediaSubtype, SPA_POD_Id(info->media_subtype), | ||||
| 			0); | ||||
| 
 | ||||
| 	if (info->media_subtype == SPA_MEDIA_SUBTYPE_raw) { | ||||
| 		spa_pod_builder_prop(&b, SPA_FORMAT_VIDEO_format, 0); | ||||
| 		spa_pod_builder_id(&b, info->format); | ||||
| 	} | ||||
| 
 | ||||
| have_size: | ||||
| 	spa_log_info(this->log, "%s:: In have_size: Got width = %u height = %u\n", __FUNCTION__, width, height); | ||||
| 
 | ||||
| 	spa_pod_builder_prop(&b, SPA_FORMAT_VIDEO_size, 0); | ||||
| 	spa_pod_builder_rectangle(&b, port->fmt.width, port->fmt.height); | ||||
| 
 | ||||
| have_framerate: | ||||
| 	spa_pod_builder_prop(&b, SPA_FORMAT_VIDEO_framerate, 0); | ||||
| 
 | ||||
| 	spa_pod_builder_push_choice(&b, &f[1], SPA_CHOICE_None, 0); | ||||
| 	choice = (struct spa_pod_choice*)spa_pod_builder_frame(&b, &f[1]); | ||||
| 
 | ||||
| 	/* Below framerates are hardcoded until framerates are queried from libcamera */ | ||||
| 	port->fmt.denominator = 30; | ||||
| 	port->fmt.numerator = 1; | ||||
| 
 | ||||
| 	spa_pod_builder_fraction(&b, | ||||
| 				 port->fmt.denominator, | ||||
| 				 port->fmt.numerator); | ||||
| 
 | ||||
| 	spa_pod_builder_pop(&b, &f[1]); | ||||
| 	result.param = spa_pod_builder_pop(&b, &f[0]); | ||||
| 
 | ||||
| 	spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); | ||||
| 
 | ||||
| 	goto next_fmtdesc; | ||||
| 
 | ||||
| enum_end: | ||||
| 	res = 0; | ||||
| exit: | ||||
| 	spa_libcamera_close(dev); | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| static int spa_libcamera_set_format(struct impl *this, struct spa_video_info *format, bool try_only) | ||||
| { | ||||
| 	struct port *port = &this->out_ports[0]; | ||||
| 	struct spa_libcamera_device *dev = &port->dev; | ||||
| 	int res, cmd; | ||||
| 	struct camera_fmt fmt; | ||||
| 	const struct format_info *info = NULL; | ||||
| 	uint32_t video_format; | ||||
| 	struct spa_rectangle *size = NULL; | ||||
| 	struct spa_fraction *framerate = NULL; | ||||
| 
 | ||||
| 	spa_zero(fmt); | ||||
| 
 | ||||
| 	switch (format->media_subtype) { | ||||
| 	case SPA_MEDIA_SUBTYPE_raw: | ||||
| 		video_format = format->info.raw.format; | ||||
| 		size = &format->info.raw.size; | ||||
| 		framerate = &format->info.raw.framerate; | ||||
| 		break; | ||||
| 	case SPA_MEDIA_SUBTYPE_mjpg: | ||||
| 	case SPA_MEDIA_SUBTYPE_jpeg: | ||||
| 		video_format = SPA_VIDEO_FORMAT_ENCODED; | ||||
| 		size = &format->info.mjpg.size; | ||||
| 		framerate = &format->info.mjpg.framerate; | ||||
| 		break; | ||||
| 	case SPA_MEDIA_SUBTYPE_h264: | ||||
| 		video_format = SPA_VIDEO_FORMAT_ENCODED; | ||||
| 		size = &format->info.h264.size; | ||||
| 		framerate = &format->info.h264.framerate; | ||||
| 		break; | ||||
| 	default: | ||||
| 		video_format = SPA_VIDEO_FORMAT_ENCODED; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	info = find_format_info_by_media_type(format->media_type, | ||||
| 					      format->media_subtype, video_format, 0); | ||||
| 	if (info == NULL || size == NULL || framerate == NULL) { | ||||
| 		spa_log_error(this->log, "libcamera: unknown media type %d %d %d", format->media_type, | ||||
| 			      format->media_subtype, video_format); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	fmt.pixelformat = video_format; | ||||
| 	fmt.width = size->width; | ||||
| 	fmt.height = size->height; | ||||
| 	fmt.sizeimage = libcamera_get_max_size(dev->camera); | ||||
| 	fmt.bytesperline = libcamera_get_stride(dev->camera); | ||||
| 	fmt.numerator = framerate->denom; | ||||
| 	fmt.denominator = framerate->num; | ||||
| 
 | ||||
| 	if ((res = spa_libcamera_open(dev)) < 0) | ||||
| 		return res; | ||||
| 
 | ||||
| 	/* stop the camera first. It might have opened with different configuration*/ | ||||
| 	libcamera_stop_capture(dev->camera); | ||||
| 
 | ||||
| 	spa_log_info(dev->log, "libcamera: set %s %dx%d %d/%d\n", (char *)&info->fourcc, | ||||
| 		     fmt.width, fmt.height, | ||||
| 		     fmt.denominator, fmt.numerator); | ||||
| 
 | ||||
| 	libcamera_set_streamcfgpixel_format(dev->camera, libcamera_video_format_to_drm(video_format)); | ||||
| 	libcamera_set_streamcfg_width(dev->camera, size->width); | ||||
| 	libcamera_set_streamcfg_height(dev->camera, size->height); | ||||
| 
 | ||||
| 	/* start the camera now with the configured params */ | ||||
| 	libcamera_start_capture(dev->camera); | ||||
| 
 | ||||
| 	dev->have_format = true; | ||||
| 	size->width = libcamera_get_streamcfg_width(dev->camera); | ||||
| 	size->height = libcamera_get_streamcfg_height(dev->camera); | ||||
| 	port->rate.denom = framerate->num = fmt.denominator; | ||||
| 	port->rate.num = framerate->denom = fmt.numerator; | ||||
| 
 | ||||
| 	port->fmt = fmt; | ||||
| 	port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS | SPA_PORT_CHANGE_MASK_RATE; | ||||
| 	port->info.flags = (port->export_buf ? SPA_PORT_FLAG_CAN_ALLOC_BUFFERS : 0) | | ||||
| 		SPA_PORT_FLAG_LIVE | | ||||
| 		SPA_PORT_FLAG_PHYSICAL | | ||||
| 		SPA_PORT_FLAG_TERMINAL; | ||||
| 	port->info.rate = SPA_FRACTION(port->rate.num, port->rate.denom); | ||||
| 
 | ||||
| 	spa_log_info(dev->log, " got format. width = %d height = %d and fmt = %s. bytesperline = %u sizeimage = %u\n",  | ||||
| 		fmt.width, fmt.height, | ||||
| 		(char *)&info->fourcc, fmt.bytesperline, fmt.sizeimage); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| spa_libcamera_enum_controls(struct impl *this, int seq, | ||||
| 		       uint32_t start, uint32_t num, | ||||
| 		       const struct spa_pod *filter) | ||||
| { | ||||
| 	return -ENOTSUP; | ||||
| } | ||||
| 
 | ||||
| static int mmap_read(struct impl *this) | ||||
| { | ||||
| 	struct port *port = &this->out_ports[0]; | ||||
| 	struct spa_libcamera_device *dev = &port->dev; | ||||
| 	struct buffer *b; | ||||
| 	struct spa_data *d; | ||||
| 	unsigned int sequence; | ||||
| 	struct timeval timestamp; | ||||
| 	int64_t pts; | ||||
| 	struct OutBuf *pOut = NULL; | ||||
| 	struct CamData *pDatas = NULL; | ||||
| 	uint32_t bytesused; | ||||
| 
 | ||||
| 	if(dev->camera) { | ||||
| 		if(!libcamera_is_data_available(dev->camera)) { | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		pOut = (struct OutBuf *)libcamera_get_ring_buffer_data(dev->camera); | ||||
| 		if(!pOut) { | ||||
| 			spa_log_debug(this->log, "Exiting %s as pOut is NULL\n", __FUNCTION__); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		/* update the read index of the ring buffer */ | ||||
| 		libcamera_ringbuffer_read_update(dev->camera); | ||||
| 
 | ||||
| 		pDatas = pOut->datas; | ||||
| 		if(NULL == pDatas) { | ||||
| 			spa_log_debug(this->log, "Exiting %s on NULL pointer\n", __FUNCTION__); | ||||
| 			goto end; | ||||
| 		} | ||||
| 
 | ||||
| 		b = &port->buffers[pOut->bufIdx]; | ||||
| 		b->outbuf->n_datas = pOut->n_datas; | ||||
| 
 | ||||
| 		if(NULL == b->outbuf->datas) { | ||||
| 			spa_log_debug(this->log, "Exiting %s as b->outbuf->datas is NULL\n", __FUNCTION__); | ||||
| 			goto end; | ||||
| 		} | ||||
| 
 | ||||
| 		for(unsigned int i = 0;  i < pOut->n_datas; ++i) { | ||||
| 			struct CamData *pData = &pDatas[i]; | ||||
| 			if(NULL == pData) { | ||||
| 				spa_log_debug(this->log, "Exiting %s on NULL pointer\n", __FUNCTION__); | ||||
| 				goto end; | ||||
| 			} | ||||
| 			b->outbuf->datas[i].flags = SPA_DATA_FLAG_READABLE; | ||||
| 			if(port->memtype == SPA_DATA_DmaBuf) { | ||||
| 				b->outbuf->datas[i].fd = pData->fd; | ||||
| 			} | ||||
| 			bytesused = b->outbuf->datas[i].chunk->size = pData->size; | ||||
| 			timestamp = pData->timestamp; | ||||
| 			sequence = pData->sequence; | ||||
| 
 | ||||
| 			b->outbuf->datas[i].mapoffset = 0; | ||||
| 			b->outbuf->datas[i].chunk->offset = 0; | ||||
| 			b->outbuf->datas[i].chunk->flags = 0; | ||||
| 			//b->outbuf->datas[i].chunk->stride = pData->sstride; /* FIXME:: This needs to be appropriately filled */
 | ||||
| 			b->outbuf->datas[i].maxsize = pData->maxsize; | ||||
| 
 | ||||
| 			spa_log_trace(this->log,"Spa libcamera Source::%s:: got bufIdx = %d and ndatas = %d\t", | ||||
| 				__FUNCTION__, pOut->bufIdx, pOut->n_datas); | ||||
| 			spa_log_trace(this->log," data[%d] --> fd = %ld bytesused = %d sequence = %d\n", | ||||
| 				i, b->outbuf->datas[i].fd, bytesused, sequence); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	pts = SPA_TIMEVAL_TO_NSEC(×tamp); | ||||
| 
 | ||||
| 	if (this->clock) { | ||||
| 		this->clock->nsec = pts; | ||||
| 		this->clock->rate = port->rate; | ||||
| 		this->clock->position = sequence; | ||||
| 		this->clock->duration = 1; | ||||
| 		this->clock->delay = 0; | ||||
| 		this->clock->rate_diff = 1.0; | ||||
| 		this->clock->next_nsec = pts + 1000000000LL / port->rate.denom; | ||||
| 	} | ||||
| 
 | ||||
| 	if (b->h) { | ||||
| 		b->h->flags = 0; | ||||
| 		b->h->offset = 0; | ||||
| 		b->h->seq = sequence; | ||||
| 		b->h->pts = pts; | ||||
| 		b->h->dts_offset = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	d = b->outbuf->datas; | ||||
| 	d[0].chunk->offset = 0; | ||||
| 	d[0].chunk->size = bytesused; | ||||
| 	d[0].chunk->flags = 0; | ||||
| 	d[0].data = b->ptr; | ||||
| 	spa_log_trace(this->log,"%s:: b->ptr = %p d[0].data = %p\n", | ||||
| 				__FUNCTION__, b->ptr, d[0].data); | ||||
| 	spa_list_append(&port->queue, &b->link); | ||||
| end: | ||||
| 	libcamera_free_CamData(dev->camera, pDatas); | ||||
| 	libcamera_free_OutBuf(dev->camera, pOut); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void libcamera_on_fd_events(struct spa_source *source) | ||||
| { | ||||
| 	struct impl *this = source->data; | ||||
| 	struct spa_io_buffers *io; | ||||
| 	struct port *port = &this->out_ports[0]; | ||||
| 	struct buffer *b; | ||||
| 
 | ||||
| 	if (source->rmask & SPA_IO_ERR) { | ||||
| 		struct port *port = &this->out_ports[0]; | ||||
| 		spa_log_error(this->log, "libcamera %p: error %08x", this, source->rmask); | ||||
| 		if (port->source.loop) | ||||
| 			spa_loop_remove_source(this->data_loop, &port->source); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!(source->rmask & SPA_IO_IN)) { | ||||
| 		spa_log_warn(this->log, "libcamera %p: spurious wakeup %d", this, source->rmask); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (mmap_read(this) < 0) { | ||||
| 		spa_log_debug(this->log, "%s:: mmap_read failure\n", __FUNCTION__); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (spa_list_is_empty(&port->queue)) { | ||||
| 		spa_log_debug(this->log, "Exiting %s as spa list is empty\n", __FUNCTION__); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	io = port->io; | ||||
| 	if (io != NULL && io->status != SPA_STATUS_HAVE_DATA) { | ||||
| 		if (io->buffer_id < port->n_buffers) | ||||
| 			spa_libcamera_buffer_recycle(this, io->buffer_id); | ||||
| 
 | ||||
| 		b = spa_list_first(&port->queue, struct buffer, link); | ||||
| 		spa_list_remove(&b->link); | ||||
| 		SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUTSTANDING); | ||||
| 
 | ||||
| 		io->buffer_id = b->id; | ||||
| 		io->status = SPA_STATUS_HAVE_DATA; | ||||
| 		spa_log_trace(this->log, "libcamera %p: now queued %d", this, b->id); | ||||
| 	} | ||||
| 	spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_DATA); | ||||
| } | ||||
| 
 | ||||
| static int spa_libcamera_use_buffers(struct impl *this, struct spa_buffer **buffers, uint32_t n_buffers) | ||||
| { | ||||
| 	struct port *port = &this->out_ports[0]; | ||||
| 	unsigned int i, j; | ||||
| 	struct spa_data *d; | ||||
| 
 | ||||
| 	n_buffers = libcamera_get_nbuffers(port->dev.camera); | ||||
| 	if (n_buffers > 0) { | ||||
| 		d = buffers[0]->datas; | ||||
| 
 | ||||
| 		port->memtype = SPA_DATA_DmaBuf; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < n_buffers; i++) { | ||||
| 		struct buffer *b; | ||||
| 		int64_t fd; | ||||
| 
 | ||||
| 		b = &port->buffers[i]; | ||||
| 		b->id = i; | ||||
| 		b->outbuf = buffers[i]; | ||||
| 		b->flags = BUFFER_FLAG_OUTSTANDING; | ||||
| 		b->h = spa_buffer_find_meta_data(buffers[i], SPA_META_Header, sizeof(*b->h)); | ||||
| 
 | ||||
| 		spa_log_debug(this->log, "libcamera: import buffer %p", buffers[i]); | ||||
| 
 | ||||
| 		if (buffers[i]->n_datas < 1) { | ||||
| 			spa_log_error(this->log, "libcamera: invalid memory on buffer %p", buffers[i]); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 
 | ||||
| 		d = buffers[i]->datas; | ||||
| 		for(j = 0; j < buffers[i]->n_datas; ++j) { | ||||
| 			d[j].mapoffset = 0; | ||||
| 			d[j].maxsize = libcamera_get_max_size(port->dev.camera); | ||||
| 
 | ||||
| 			if (port->memtype == SPA_DATA_MemPtr) { | ||||
| 				if (d[j].data == NULL) { | ||||
| 					d[j].fd = -1; | ||||
| 					d[j].data = mmap(NULL, | ||||
| 						    d[j].maxsize + d[j].mapoffset, | ||||
| 						    PROT_READ, MAP_SHARED, | ||||
| 						    libcamera_get_fd(port->dev.camera, i, j), | ||||
| 						    0); | ||||
| 					if (d[j].data == MAP_FAILED) { | ||||
| 						return -errno; | ||||
| 					} | ||||
| 
 | ||||
| 					b->ptr = d[j].data; | ||||
| 					spa_log_debug(this->log, "libcamera: In spa_libcamera_use_buffers(). mmap ptr:%p for fd = %ld buffer: #%d", | ||||
| 						d[j].data, d[j].fd, i); | ||||
| 					SPA_FLAG_SET(b->flags, BUFFER_FLAG_MAPPED); | ||||
| 				} else { | ||||
| 					b->ptr = d[j].data; | ||||
| 					spa_log_debug(this->log, "libcamera: In spa_libcamera_use_buffers(). b->ptr = %p d[j].maxsize = %d for buffer: #%d", | ||||
| 						d[j].data, d[j].maxsize, i); | ||||
| 				} | ||||
| 				spa_log_debug(this->log, "libcamera: In spa_libcamera_use_buffers(). setting b->ptr = %p for buffer: #%d on libcamera", | ||||
| 						b->ptr, i); | ||||
| 			} | ||||
| 			else if (port->memtype == SPA_DATA_DmaBuf) { | ||||
| 				d[j].fd = libcamera_get_fd(port->dev.camera, i, j); | ||||
| 				spa_log_debug(this->log, "libcamera: Got fd = %ld for buffer: #%d", d[j].fd, i); | ||||
| 			} | ||||
| 			else { | ||||
| 				spa_log_error(this->log, "libcamera: Exiting spa_libcamera_use_buffers() with -EIO"); | ||||
| 				return -EIO; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		spa_libcamera_buffer_recycle(this, i); | ||||
| 	} | ||||
| 	port->n_buffers = n_buffers; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| mmap_init(struct impl *this, | ||||
| 		struct spa_buffer **buffers, uint32_t n_buffers) | ||||
| { | ||||
| 	struct port *port = &this->out_ports[0]; | ||||
| 	unsigned int i, j; | ||||
| 
 | ||||
| 	spa_log_info(this->log, "libcamera: In mmap_init()"); | ||||
| 
 | ||||
| 	port->memtype = SPA_DATA_DmaBuf; | ||||
| 
 | ||||
| 	/* get n_buffers from libcamera */ | ||||
| 	uint32_t libcamera_nbuffers = libcamera_get_nbuffers(port->dev.camera); | ||||
| 
 | ||||
| 	for (i = 0; i < libcamera_nbuffers; i++) { | ||||
| 		struct buffer *b; | ||||
| 		struct spa_data *d; | ||||
| 
 | ||||
| 		if (buffers[i]->n_datas < 1) { | ||||
| 			spa_log_error(this->log, "libcamera: invalid buffer data"); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 
 | ||||
| 		b = &port->buffers[i]; | ||||
| 		b->id = i; | ||||
| 		b->outbuf = buffers[i]; | ||||
| 		b->flags = BUFFER_FLAG_OUTSTANDING; | ||||
| 		b->h = spa_buffer_find_meta_data(buffers[i], SPA_META_Header, sizeof(*b->h)); | ||||
| 
 | ||||
| 		d = buffers[i]->datas; | ||||
| 		for(j = 0; j < buffers[i]->n_datas; ++j) { | ||||
| 			d[j].type = SPA_DATA_DmaBuf; | ||||
| 			d[j].flags = SPA_DATA_FLAG_READABLE; | ||||
| 			d[j].mapoffset = 0; | ||||
| 			d[j].maxsize = libcamera_get_max_size(port->dev.camera); | ||||
| 			d[j].chunk->offset = 0; | ||||
| 			d[j].chunk->size = 0; | ||||
| 			d[j].chunk->stride = port->fmt.bytesperline; /* FIXME:: This needs to be appropriately filled */ | ||||
| 			d[j].chunk->flags = 0; | ||||
| 
 | ||||
| 			d[j].flags = SPA_DATA_FLAG_READABLE; | ||||
| 
 | ||||
| 			if(port->memtype == SPA_DATA_DmaBuf) { | ||||
| 				d[j].fd = libcamera_get_fd(port->dev.camera, i, j); | ||||
| 				spa_log_info(this->log, "libcamera: Got fd = %ld for buffer: #%d\n", d[j].fd, i); | ||||
| 				d[j].data = NULL; | ||||
| 				SPA_FLAG_SET(b->flags, BUFFER_FLAG_ALLOCATED); | ||||
| 			} | ||||
| 			else if(port->memtype == SPA_DATA_MemPtr) { | ||||
| 				d[j].fd = -1; | ||||
| 				d[j].data = mmap(NULL, | ||||
| 						    d[j].maxsize + d[j].mapoffset, | ||||
| 						    PROT_READ, MAP_SHARED, | ||||
| 						    libcamera_get_fd(port->dev.camera, i, j), | ||||
| 						    0); | ||||
| 				if (d[j].data == MAP_FAILED) { | ||||
| 					spa_log_error(this->log, "mmap: %m"); | ||||
| 					continue; | ||||
| 				} | ||||
| 				b->ptr = d[j].data; | ||||
| 				SPA_FLAG_SET(b->flags, BUFFER_FLAG_MAPPED); | ||||
| 				spa_log_info(this->log, "libcamera: mmap ptr:%p", d[j].data); | ||||
| 			} else { | ||||
| 				spa_log_error(this->log, "libcamera: invalid buffer type"); | ||||
| 				return -EIO; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		spa_libcamera_buffer_recycle(this, i); | ||||
| 	} | ||||
| 	port->n_buffers = libcamera_nbuffers; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int userptr_init(struct impl *this) | ||||
| { | ||||
| 	return -ENOTSUP; | ||||
| } | ||||
| 
 | ||||
| static int read_init(struct impl *this) | ||||
| { | ||||
| 	return -ENOTSUP; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| spa_libcamera_alloc_buffers(struct impl *this, | ||||
| 		       struct spa_buffer **buffers, | ||||
| 		       uint32_t n_buffers) | ||||
| { | ||||
| 	int res; | ||||
| 	struct port *port = &this->out_ports[0]; | ||||
| 	struct spa_libcamera_device *dev = &port->dev; | ||||
| 
 | ||||
| 	if (port->n_buffers > 0) | ||||
| 		return -EIO; | ||||
| 
 | ||||
| 	if ((res = mmap_init(this, buffers, n_buffers)) < 0) { | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int spa_libcamera_stream_on(struct impl *this) | ||||
| { | ||||
| 	struct port *port = &this->out_ports[0]; | ||||
| 	struct spa_libcamera_device *dev = &port->dev; | ||||
| 
 | ||||
| 	if (!dev->have_format) { | ||||
| 		spa_log_error(this->log, "Exting %s with -EIO\n", __FUNCTION__); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dev->active) { | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	spa_log_info(this->log, "connecting camera"); | ||||
| 
 | ||||
| 	libcamera_connect(dev->camera); | ||||
| 
 | ||||
| 	port->source.func = libcamera_on_fd_events; | ||||
| 	port->source.data = this; | ||||
| 	port->source.fd = get_dev_fd(dev); | ||||
| 	port->source.mask = SPA_IO_IN | SPA_IO_ERR; | ||||
| 	port->source.rmask = 0; | ||||
| 	spa_loop_add_source(this->data_loop, &port->source); | ||||
| 
 | ||||
| 	dev->active = true; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int do_remove_source(struct spa_loop *loop, | ||||
| 			    bool async, | ||||
| 			    uint32_t seq, | ||||
| 			    const void *data, | ||||
| 			    size_t size, | ||||
| 			    void *user_data) | ||||
| { | ||||
| 	struct port *port = user_data; | ||||
| 	if (port->source.loop) | ||||
| 		spa_loop_remove_source(loop, &port->source); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int spa_libcamera_stream_off(struct impl *this) | ||||
| { | ||||
| 	struct port *port = &this->out_ports[0]; | ||||
| 	struct spa_libcamera_device *dev = &port->dev; | ||||
| 
 | ||||
| 	if (!dev->active) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	spa_log_info(this->log, "stopping camera"); | ||||
| 
 | ||||
| 	libcamera_stop_capture(dev->camera); | ||||
| 
 | ||||
| 	spa_loop_invoke(this->data_loop, do_remove_source, 0, NULL, 0, true, port); | ||||
| 
 | ||||
| 	spa_list_init(&port->queue); | ||||
| 	dev->active = false; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										55
									
								
								spa/plugins/libcamera/libcamera.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								spa/plugins/libcamera/libcamera.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | |||
| /* Spa libcamera support
 | ||||
|  * | ||||
|  * Copyright © 2020 collabora | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a | ||||
|  * copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation | ||||
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||||
|  * and/or sell copies of the Software, and to permit persons to whom the | ||||
|  * Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice (including the next | ||||
|  * paragraph) shall be included in all copies or substantial portions of the | ||||
|  * Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | ||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
|  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||
|  * DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <errno.h> | ||||
| 
 | ||||
| #include <spa/support/plugin.h> | ||||
| 
 | ||||
| extern const struct spa_handle_factory spa_libcamera_source_factory; | ||||
| extern const struct spa_handle_factory spa_libcamera_client_factory; | ||||
| extern const struct spa_handle_factory spa_libcamera_device_factory; | ||||
| 
 | ||||
| SPA_EXPORT | ||||
| int spa_handle_factory_enum(const struct spa_handle_factory **factory, | ||||
| 			uint32_t *index) | ||||
| { | ||||
| 	spa_return_val_if_fail(factory != NULL, -EINVAL); | ||||
| 	spa_return_val_if_fail(index != NULL, -EINVAL); | ||||
| 
 | ||||
| 	switch (*index) { | ||||
| 	case 0: | ||||
| 		*factory = &spa_libcamera_source_factory; | ||||
| 		break; | ||||
| 	case 1: | ||||
| 		*factory = &spa_libcamera_client_factory; | ||||
| 		break; | ||||
| 	case 2: | ||||
| 		*factory = &spa_libcamera_device_factory; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return 0; | ||||
| 	} | ||||
| 	(*index)++; | ||||
| 	return 1; | ||||
| } | ||||
							
								
								
									
										43
									
								
								spa/plugins/libcamera/libcamera.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								spa/plugins/libcamera/libcamera.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | |||
| /* Spa libcamera support
 | ||||
|  * | ||||
|  * Copyright © 2020 collabora | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a | ||||
|  * copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation | ||||
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||||
|  * and/or sell copies of the Software, and to permit persons to whom the | ||||
|  * Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice (including the next | ||||
|  * paragraph) shall be included in all copies or substantial portions of the | ||||
|  * Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | ||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
|  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||
|  * DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <errno.h> | ||||
| 
 | ||||
| #include <linux/media.h> | ||||
| 
 | ||||
| #include "libcamera_wrapper.h" | ||||
| 
 | ||||
| struct spa_libcamera_device { | ||||
| 	struct spa_log *log; | ||||
| 	int fd; | ||||
| 	struct media_device_info dev_info; | ||||
| 	unsigned int active:1; | ||||
| 	unsigned int have_format:1; | ||||
| 	LibCamera *camera; | ||||
| }; | ||||
| 
 | ||||
| int spa_libcamera_open(struct spa_libcamera_device *dev); | ||||
| int spa_libcamera_close(struct spa_libcamera_device *dev); | ||||
| int spa_libcamera_is_capture(struct spa_libcamera_device *dev); | ||||
| int get_dev_fd(struct spa_libcamera_device *dev); | ||||
							
								
								
									
										945
									
								
								spa/plugins/libcamera/libcamera_wrapper.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										945
									
								
								spa/plugins/libcamera/libcamera_wrapper.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,945 @@ | |||
| /* Spa libcamera support
 | ||||
|  * | ||||
|  * Copyright (C) 2020, Collabora Ltd. | ||||
|  *     Author: Raghavendra Rao Sidlagatta <raghavendra.rao@collabora.com> | ||||
|  * | ||||
|  * libcamera_wrapper.cpp | ||||
|  * | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a | ||||
|  * copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation | ||||
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||||
|  * and/or sell copies of the Software, and to permit persons to whom the | ||||
|  * Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice (including the next | ||||
|  * paragraph) shall be included in all copies or substantial portions of the | ||||
|  * Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | ||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
|  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||
|  * DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| #include <pthread.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
| 
 | ||||
| #include <climits> | ||||
| #include <fcntl.h> | ||||
| #include <iomanip> | ||||
| #include <iostream> | ||||
| #include <sstream> | ||||
| #include <string.h> | ||||
| #include <sys/mman.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| #include <spa/support/log.h> | ||||
| #include <spa/param/props.h> | ||||
| #include <spa/param/video/raw.h> | ||||
| 
 | ||||
| #include <libcamera/camera.h> | ||||
| #include <libcamera/camera_manager.h> | ||||
| #include <libcamera/request.h> | ||||
| #include <libcamera/framebuffer_allocator.h> | ||||
| #include <libcamera/buffer.h> | ||||
| #include <libcamera/property_ids.h> | ||||
| #include <libcamera/controls.h> | ||||
| 
 | ||||
| #include <libcamera/control_ids.h> | ||||
| #include <linux/videodev2.h> | ||||
| 
 | ||||
| using namespace libcamera; | ||||
| using namespace controls; | ||||
| 
 | ||||
| #include "libcamera_wrapper.h" | ||||
| 
 | ||||
| #define DEFAULT_WIDTH			640 | ||||
| #define DEFAULT_HEIGHT			480 | ||||
| #define DEFAULT_PIXEL_FMT		DRM_FORMAT_YUYV | ||||
| 
 | ||||
| extern "C" { | ||||
| 
 | ||||
| 	static struct { | ||||
| 		spa_video_format video_format; | ||||
| 		unsigned int drm_fourcc; | ||||
| 	} format_map[] = { | ||||
| 		{ SPA_VIDEO_FORMAT_ENCODED, DRM_FORMAT_MJPEG }, | ||||
| 		{ SPA_VIDEO_FORMAT_RGB, DRM_FORMAT_BGR888 }, | ||||
| 		{ SPA_VIDEO_FORMAT_BGR, DRM_FORMAT_RGB888 }, | ||||
| 		{ SPA_VIDEO_FORMAT_ARGB, DRM_FORMAT_BGRA8888 }, | ||||
| 		{ SPA_VIDEO_FORMAT_NV12, DRM_FORMAT_NV12 }, | ||||
| 		{ SPA_VIDEO_FORMAT_NV21, DRM_FORMAT_NV21 }, | ||||
| 		{ SPA_VIDEO_FORMAT_NV16, DRM_FORMAT_NV16 }, | ||||
| 		{ SPA_VIDEO_FORMAT_NV61, DRM_FORMAT_NV61 }, | ||||
| 		{ SPA_VIDEO_FORMAT_NV24, DRM_FORMAT_NV24 }, | ||||
| 		{ SPA_VIDEO_FORMAT_UYVY, DRM_FORMAT_UYVY }, | ||||
| 		{ SPA_VIDEO_FORMAT_VYUY, DRM_FORMAT_VYUY }, | ||||
| 		{ SPA_VIDEO_FORMAT_YUY2, DRM_FORMAT_YUYV }, | ||||
| 		{ SPA_VIDEO_FORMAT_YVYU, DRM_FORMAT_YVYU }, | ||||
| 		/* \todo NV42 is used in libcamera but is not mapped in here yet. */ | ||||
| 	}; | ||||
| 
 | ||||
| 	typedef struct ring_buf { | ||||
| 		uint32_t read_index; | ||||
| 		uint32_t write_index; | ||||
| 	}ring_buf; | ||||
| 
 | ||||
| 	typedef struct LibCamera { | ||||
| 		std::unique_ptr<CameraManager> cm_; | ||||
| 		std::shared_ptr<Camera> cam_; | ||||
| 		std::unique_ptr<CameraConfiguration> config_; | ||||
| 		FrameBufferAllocator *allocator_; | ||||
| 		std::map<Stream*, std::string> streamName_; | ||||
| 
 | ||||
| 		uint32_t nbuffers_; | ||||
| 		uint32_t nplanes_; | ||||
| 		uint32_t bufIdx_; | ||||
| 		int64_t **fd_; | ||||
| 		uint32_t maxSize_; | ||||
| 		bool isAvail_; | ||||
| 		uint32_t width_; | ||||
| 		uint32_t height_; | ||||
| 		uint32_t pixelFormat_; | ||||
| 		uint32_t stride_; | ||||
| 
 | ||||
| 		struct ring_buf ringbuf_; | ||||
| 		void *ringbuf_data_[MAX_NUM_BUFFERS] = {}; | ||||
| 		struct spa_log *log_; | ||||
| 		pthread_mutex_t lock; | ||||
| 
 | ||||
| 		/* Methods */ | ||||
| 		int32_t listProperties(); | ||||
| 		void requestComplete(Request *request); | ||||
| 		void item_free_fn(); | ||||
| 		void ring_buffer_init(); | ||||
| 		void *ring_buffer_read(); | ||||
| 		void ring_buffer_write(void *p); | ||||
| 		void empty_data(); | ||||
| 		void fill_data(); | ||||
| 		bool open(); | ||||
| 		void close(); | ||||
| 		int request_capture(); | ||||
| 		int start(); | ||||
| 		void stop(); | ||||
| 		void connect(); | ||||
| 		void disconnect(); | ||||
| 		bool set_config(); | ||||
| 
 | ||||
| 		std::shared_ptr<Camera> get_camera(); | ||||
| 		std::string choose_camera(); | ||||
| 
 | ||||
| 		/* Mutators */ | ||||
| 		void set_streamcfg_width(uint32_t w); | ||||
| 		void set_streamcfg_height(uint32_t h); | ||||
| 		void set_streamcfgpixel_format(uint32_t fmt); | ||||
| 		void set_max_size(uint32_t s); | ||||
| 		void set_nbuffers(uint32_t n); | ||||
| 		void set_nplanes(uint32_t n); | ||||
| 		void set_stride(uint32_t s); | ||||
| 		void set_fd(Stream *stream); | ||||
| 		void ring_buffer_set_read_index(uint32_t idx); | ||||
| 		void ring_buffer_set_write_index(uint32_t idx); | ||||
| 		void ring_buffer_update_read_index(); | ||||
| 		void ring_buffer_update_write_index(); | ||||
| 		void reset_ring_buffer_data(); | ||||
| 		int32_t set_control(ControlList &controls, uint32_t control_id, float value); | ||||
| 
 | ||||
| 		/* Accessors */ | ||||
| 		uint32_t get_streamcfg_width(); | ||||
| 		uint32_t get_streamcfg_height(); | ||||
| 		uint32_t get_streamcfgpixel_format(); | ||||
| 		uint32_t get_max_size(); | ||||
| 		uint32_t get_nbuffers(); | ||||
| 		uint32_t get_nplanes(); | ||||
| 		uint32_t get_stride(); | ||||
| 		uint32_t ring_buffer_get_read_index(); | ||||
| 		uint32_t ring_buffer_get_write_index(); | ||||
| 		bool is_data_available(); | ||||
| 	}LibCamera; | ||||
| 
 | ||||
| 	bool LibCamera::is_data_available() { | ||||
| 		return this->isAvail_; | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t LibCamera::get_max_size() { | ||||
| 		return this->maxSize_; | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::set_max_size(uint32_t s) { | ||||
| 		this->maxSize_ = s; | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t LibCamera::get_nbuffers() { | ||||
| 		return this->nbuffers_; | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::set_nbuffers(uint32_t n) { | ||||
| 		this->nbuffers_ = n; | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::set_nplanes(uint32_t n) { | ||||
| 		this->nplanes_ = n; | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::set_stride(uint32_t s) { | ||||
| 		this->stride_ = s; | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t LibCamera::get_stride() { | ||||
| 		return this->stride_; | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::set_fd(Stream *stream) { | ||||
| 		this->fd_ = new int64_t*[this->nbuffers_]; | ||||
| 
 | ||||
| 		uint32_t bufIdx = 0; | ||||
| 		for (const std::unique_ptr<FrameBuffer> &buffer : this->allocator_->buffers(stream)) { | ||||
| 			uint32_t nplanes = buffer->planes().size(); | ||||
| 			this->fd_[bufIdx] = new int64_t[this->nplanes_]; | ||||
| 			for(uint32_t planeIdx = 0; planeIdx < nplanes; ++planeIdx) { | ||||
| 				const FrameBuffer::Plane &plane = buffer->planes().front(); | ||||
| 				this->fd_[bufIdx][planeIdx] = plane.fd.fd(); | ||||
| 			} | ||||
| 			bufIdx++; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t LibCamera::get_nplanes() { | ||||
| 		return this->nplanes_; | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::ring_buffer_init() { | ||||
| 		this->ringbuf_.read_index = 0; | ||||
| 		this->ringbuf_.write_index = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t LibCamera::ring_buffer_get_read_index() { | ||||
| 		uint32_t idx; | ||||
| 		idx = __atomic_load_n(&this->ringbuf_.read_index, __ATOMIC_RELAXED); | ||||
| 
 | ||||
| 		return idx; | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t LibCamera::ring_buffer_get_write_index() { | ||||
| 		uint32_t idx; | ||||
| 		idx = __atomic_load_n(&this->ringbuf_.write_index, __ATOMIC_RELAXED); | ||||
| 
 | ||||
| 		return idx; | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::ring_buffer_set_read_index(uint32_t idx) { | ||||
| 		__atomic_store_n(&this->ringbuf_.read_index, idx, __ATOMIC_RELEASE); | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::ring_buffer_set_write_index(uint32_t idx) { | ||||
| 		__atomic_store_n(&this->ringbuf_.write_index, idx, __ATOMIC_RELEASE); | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::ring_buffer_update_read_index() { | ||||
| 		uint32_t idx; | ||||
| 
 | ||||
| 		idx = this->ring_buffer_get_read_index(); | ||||
| 		this->ringbuf_data_[idx] = nullptr; | ||||
| 		++idx; | ||||
| 		if(idx == MAX_NUM_BUFFERS) { | ||||
| 			idx = 0; | ||||
| 		} | ||||
| 		this->ring_buffer_set_read_index(idx); | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::ring_buffer_update_write_index() { | ||||
| 		uint32_t idx; | ||||
| 
 | ||||
| 		idx = this->ring_buffer_get_write_index(); | ||||
| 		++idx; | ||||
| 		if(idx == MAX_NUM_BUFFERS) { | ||||
| 			idx = 0; | ||||
| 		} | ||||
| 		this->ring_buffer_set_write_index(idx); | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::ring_buffer_write(void *p) | ||||
| 	{ | ||||
| 		uint32_t idx; | ||||
| 
 | ||||
| 		idx = this->ring_buffer_get_write_index(); | ||||
| 		pthread_mutex_lock(&this->lock); | ||||
| 		ringbuf_data_[idx] = p; | ||||
| 		pthread_mutex_unlock(&this->lock); | ||||
| 	} | ||||
| 
 | ||||
| 	void *LibCamera::ring_buffer_read() | ||||
| 	{ | ||||
| 		uint32_t idx; | ||||
| 		void *p; | ||||
| 
 | ||||
| 		idx = this->ring_buffer_get_read_index(); | ||||
| 		pthread_mutex_lock(&this->lock); | ||||
| 		p = (void *)this->ringbuf_data_[idx]; | ||||
| 		pthread_mutex_unlock(&this->lock); | ||||
| 
 | ||||
| 		return p; | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::empty_data() { | ||||
| 		pthread_mutex_lock(&this->lock); | ||||
| 		this->isAvail_ = true; | ||||
| 		pthread_mutex_unlock(&this->lock); | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::fill_data() { | ||||
| 		pthread_mutex_lock(&this->lock); | ||||
| 		this->isAvail_ = false; | ||||
| 		pthread_mutex_unlock(&this->lock); | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::item_free_fn() { | ||||
| 		uint32_t ringbuf_read_index; | ||||
| 		struct OutBuf *pOut = NULL; | ||||
| 		struct CamData *pDatas = NULL; | ||||
| 
 | ||||
| 		ringbuf_read_index = this->ring_buffer_get_read_index(); | ||||
| 		for(int i = 0; i < MAX_NUM_BUFFERS; i++) { | ||||
| 			pOut = (struct OutBuf *)ringbuf_data_[ringbuf_read_index]; | ||||
| 			if(pOut) { | ||||
| 				pDatas = pOut->datas; | ||||
| 				if(pDatas) { | ||||
| 					libcamera_free_CamData(this, pDatas); | ||||
| 				} | ||||
| 				libcamera_free_OutBuf(this, pOut); | ||||
| 			} | ||||
| 			++ringbuf_read_index; | ||||
| 			if(ringbuf_read_index == MAX_NUM_BUFFERS) { | ||||
| 				ringbuf_read_index = 0; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	std::string LibCamera::choose_camera() { | ||||
| 		if (!this->cm_) { | ||||
| 			return std::string(); | ||||
| 		} | ||||
| 
 | ||||
| 		if (this->cm_->cameras().empty()) { | ||||
| 			return std::string(); | ||||
| 		} | ||||
| 		/* If only one camera is available, use it automatically. */ | ||||
| 		else if (this->cm_->cameras().size() == 1) { | ||||
| 			return this->cm_->cameras()[0]->name(); | ||||
| 		} | ||||
| 		/* TODO::
 | ||||
| 		 * 1. Allow the user to provide a camera name to select.			* | ||||
| 		 * 2. Select the camera based on the camera name provided by User 	* | ||||
| 		 */ | ||||
| 		/* For time being, return the first camera if more than 1 camera devices are available */ | ||||
| 		else { | ||||
| 			return this->cm_->cameras()[0]->name(); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	std::shared_ptr<Camera> LibCamera::get_camera() { | ||||
| 		std::string camName = this->choose_camera(); | ||||
| 		std::shared_ptr<Camera> cam; | ||||
| 
 | ||||
| 		if (camName == "") { | ||||
| 			return nullptr; | ||||
| 		} | ||||
| 
 | ||||
| 		cam = this->cm_->get(camName); | ||||
| 		if (!cam) { | ||||
| 			return nullptr; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Sanity check that the camera has streams. */ | ||||
| 		if (cam->streams().empty()) { | ||||
| 			return nullptr; | ||||
| 		} | ||||
| 
 | ||||
| 		return cam; | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t LibCamera::get_streamcfg_width() { | ||||
| 		return this->width_; | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t LibCamera::get_streamcfg_height() { | ||||
| 		return this->height_; | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t LibCamera::get_streamcfgpixel_format() { | ||||
| 		return this->pixelFormat_; | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::set_streamcfg_width(uint32_t w) { | ||||
| 		this->width_ = w; | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::set_streamcfg_height(uint32_t h) { | ||||
| 		this->height_ = h; | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::set_streamcfgpixel_format(uint32_t fmt) { | ||||
| 		this->pixelFormat_ = fmt; | ||||
| 	} | ||||
| 
 | ||||
| 	bool LibCamera::set_config() { | ||||
|     	if(!this->cam_) { | ||||
|     		return false; | ||||
|     	} | ||||
| 
 | ||||
|     	this->config_ = this->cam_->generateConfiguration({ StreamRole::VideoRecording }); | ||||
|     	if (!this->config_ || this->config_->size() != 1) { | ||||
|     		return false; | ||||
| 		} | ||||
| 
 | ||||
| 		StreamConfiguration &cfg = this->config_->at(0); | ||||
| 
 | ||||
| 		cfg.size.width = this->get_streamcfg_width(); | ||||
| 		cfg.size.height = this->get_streamcfg_height(); | ||||
| 		cfg.pixelFormat = PixelFormat(this->get_streamcfgpixel_format()); | ||||
| 
 | ||||
| 		/* Validate the configuration. */ | ||||
| 		if (this->config_->validate() == CameraConfiguration::Invalid) { | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
|     	if (this->cam_->configure(this->config_.get())) { | ||||
|     		return false; | ||||
| 		} | ||||
| 
 | ||||
| 		this->listProperties(); | ||||
| 
 | ||||
| 		this->allocator_ = new FrameBufferAllocator(this->cam_); | ||||
| 		uint32_t nbuffers = UINT_MAX, nplanes = 0; | ||||
| 
 | ||||
| 		Stream *stream = cfg.stream(); | ||||
| 		int ret = this->allocator_->allocate(stream); | ||||
| 		if (ret < 0) { | ||||
| 			return -ENOMEM; | ||||
| 		} | ||||
| 
 | ||||
| 		uint32_t allocated = this->allocator_->buffers(cfg.stream()).size(); | ||||
| 		nbuffers = std::min(nbuffers, allocated); | ||||
| 
 | ||||
| 		this->set_nbuffers(nbuffers); | ||||
| 
 | ||||
| 		int id = 0; | ||||
| 		uint32_t max_size = 0; | ||||
| 		for (const std::unique_ptr<FrameBuffer> &buffer : this->allocator_->buffers(stream)) { | ||||
| 			nplanes = buffer->planes().size(); | ||||
| 			const FrameBuffer::Plane &plane = buffer->planes().front(); | ||||
| 			max_size = std::max(max_size, plane.length); | ||||
| 			++id; | ||||
| 		} | ||||
| 		this->set_max_size(max_size); | ||||
| 		this->set_nplanes(nplanes); | ||||
| 		this->set_fd(stream); | ||||
| 		this->set_stride(cfg.stride); | ||||
| 
 | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	int LibCamera::request_capture() { | ||||
|     	int ret = 0; | ||||
| 
 | ||||
|     	StreamConfiguration &cfg = this->config_->at(0); | ||||
| 		Stream *stream = cfg.stream(); | ||||
| 
 | ||||
| 		std::vector<Request *> requests; | ||||
| 
 | ||||
| 		for (const std::unique_ptr<FrameBuffer> &buffer : this->allocator_->buffers(stream)) { | ||||
| 			Request *request = this->cam_->createRequest(); | ||||
| 			if (!request) { | ||||
| 				spa_log_error(this->log_, "Cannot create request"); | ||||
| 				return -ENOMEM; | ||||
| 			} | ||||
| 
 | ||||
| 			if (request->addBuffer(stream, buffer.get())) { | ||||
| 				spa_log_error(this->log_, "Failed to associating buffer with request"); | ||||
| 				return -ENOMEM; | ||||
| 			} | ||||
| 
 | ||||
| 			requests.push_back(request); | ||||
| 		} | ||||
| 
 | ||||
| 		for (Request *request : requests) { | ||||
| 			ret = this->cam_->queueRequest(request); | ||||
| 			if (ret < 0) { | ||||
| 				spa_log_error(this->log_, "Cannot create request"); | ||||
| 				return ret; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return ret; | ||||
|     } | ||||
| 
 | ||||
| 	bool LibCamera::open() { | ||||
| 		std::shared_ptr<Camera> cam; | ||||
| 		int err; | ||||
| 		int ret = 0; | ||||
| 
 | ||||
| 		cam = this->get_camera(); | ||||
| 		if(!cam) { | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		ret = cam->acquire(); | ||||
| 		if (ret) { | ||||
| 			err = errno; | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		this->cam_ = cam; | ||||
| 
 | ||||
| 		if(!this->set_config()) { | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	int LibCamera::start() { | ||||
| 		if(!this->set_config()) { | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		this->streamName_.clear(); | ||||
| 		for (unsigned int index = 0; index < this->config_->size(); ++index) { | ||||
| 			StreamConfiguration &cfg = this->config_->at(index); | ||||
| 			this->streamName_[cfg.stream()] = "stream" + std::to_string(index); | ||||
| 		} | ||||
| 
 | ||||
| 		spa_log_info(this->log_, "Starting camera ..."); | ||||
| 
 | ||||
| 		/* start the camera now */ | ||||
| 		if (this->cam_->start()) { | ||||
| 			spa_log_error(this->log_, "failed to start camera"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		this->ring_buffer_init(); | ||||
| 
 | ||||
| 		if(this->request_capture()) { | ||||
| 			spa_log_error(this->log_, "failed to create request"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::stop() { | ||||
| 		this->disconnect(); | ||||
| 
 | ||||
|     	spa_log_info(this->log_, "Stopping camera ..."); | ||||
|     	this->cam_->stop(); | ||||
|     	if(this->allocator_) { | ||||
| 	    	delete this->allocator_; | ||||
| 	    	this->allocator_ = nullptr; | ||||
|     	} | ||||
| 
 | ||||
|     	if(this->fd_) { | ||||
|     		for(uint32_t i = 0; i < this->nplanes_; i++) { | ||||
|     			delete this->fd_[i]; | ||||
|     			this->fd_[i] = nullptr; | ||||
|     		} | ||||
| 	    	delete this->fd_; | ||||
| 	    	this->fd_ = nullptr; | ||||
| 	    } | ||||
| 
 | ||||
| 	    this->item_free_fn(); | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::close() { | ||||
|     	this->stop(); | ||||
| 		this->cam_->release(); | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::connect() | ||||
| 	{ | ||||
| 		this->cam_->requestCompleted.connect(this, &LibCamera::requestComplete); | ||||
| 	} | ||||
| 
 | ||||
| 	void LibCamera::disconnect() | ||||
| 	{ | ||||
| 		this->cam_->requestCompleted.disconnect(this, &LibCamera::requestComplete); | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t libcamera_get_streamcfg_width(LibCamera *camera) { | ||||
| 		return camera->get_streamcfg_width(); | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t libcamera_get_streamcfg_height(LibCamera *camera) { | ||||
| 		return camera->get_streamcfg_height(); | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t libcamera_get_streamcfgpixel_format(LibCamera *camera) { | ||||
| 		return camera->get_streamcfgpixel_format(); | ||||
| 	} | ||||
| 
 | ||||
| 	void libcamera_set_streamcfg_width(LibCamera *camera, uint32_t w) { | ||||
| 		camera->set_streamcfg_width(w); | ||||
| 	} | ||||
| 
 | ||||
| 	void libcamera_set_streamcfg_height(LibCamera *camera, uint32_t h) { | ||||
| 		camera->set_streamcfg_height(h); | ||||
| 	} | ||||
| 
 | ||||
| 	void libcamera_set_streamcfgpixel_format(LibCamera *camera, uint32_t fmt) { | ||||
| 		camera->set_streamcfgpixel_format(fmt); | ||||
| 	} | ||||
| 
 | ||||
| 	void libcamera_ringbuffer_read_update(LibCamera *camera) { | ||||
| 		camera->fill_data(); | ||||
| 		camera->ring_buffer_update_read_index(); | ||||
| 	} | ||||
| 
 | ||||
| 	void *libcamera_get_ring_buffer_data(LibCamera *camera) { | ||||
| 		return camera->ring_buffer_read(); | ||||
| 	} | ||||
| 
 | ||||
| 	void libcamera_free_OutBuf(LibCamera *camera, OutBuf *p) { | ||||
| 		pthread_mutex_lock(&camera->lock); | ||||
|     	if(p != nullptr) { | ||||
|     		delete p; | ||||
|     		p = nullptr; | ||||
|     	} | ||||
|     	pthread_mutex_unlock(&camera->lock); | ||||
|     } | ||||
| 
 | ||||
|     void libcamera_free_CamData(LibCamera *camera, CamData *p) { | ||||
|     	pthread_mutex_lock(&camera->lock); | ||||
|     	if(p != nullptr) { | ||||
|     		delete p; | ||||
|     		p = nullptr; | ||||
|     	} | ||||
|     	pthread_mutex_unlock(&camera->lock); | ||||
|     } | ||||
| 
 | ||||
| 	void libcamera_set_log(LibCamera *camera, struct spa_log *log) { | ||||
| 		camera->log_ = log; | ||||
| 	} | ||||
| 
 | ||||
| 	bool libcamera_is_data_available(LibCamera *camera) { | ||||
| 		return camera->is_data_available(); | ||||
| 	} | ||||
| 
 | ||||
| 	spa_video_format libcamera_map_drm_fourcc_format(unsigned int fourcc) { | ||||
| 		for (const auto &item : format_map) { | ||||
| 			if (item.drm_fourcc == fourcc) { | ||||
| 				return item.video_format; | ||||
| 			} | ||||
| 		} | ||||
| 		return (spa_video_format)UINT32_MAX; | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t libcamera_drm_to_video_format(unsigned int drm) { | ||||
| 		return libcamera_map_drm_fourcc_format(drm); | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t libcamera_video_format_to_drm(uint32_t format) | ||||
| 	{ | ||||
| 		if (format == SPA_VIDEO_FORMAT_ENCODED) { | ||||
| 			return DRM_FORMAT_INVALID; | ||||
| 		} | ||||
| 
 | ||||
| 		for (const auto &item : format_map) { | ||||
| 			if (item.video_format == format) { | ||||
| 				return item.drm_fourcc; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return DRM_FORMAT_INVALID; | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t libcamera_enum_streamcfgpixel_format(LibCamera *camera, uint32_t idx) { | ||||
| 		if(!camera) { | ||||
| 			return -1; | ||||
| 		} | ||||
| 		if (!camera->config_) { | ||||
| 			spa_log_error(camera->log_, "Cannot get stream information without a camera"); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 
 | ||||
| 		uint32_t index = 0; | ||||
| 		for (const StreamConfiguration &cfg : *camera->config_) { | ||||
| 			uint32_t index = 0; | ||||
| 			const StreamFormats &formats = cfg.formats(); | ||||
| 			for (PixelFormat pixelformat : formats.pixelformats()) { | ||||
| 				if(index == idx) { | ||||
| 					return pixelformat.fourcc(); | ||||
| 				} | ||||
| 				++index; | ||||
| 			} | ||||
| 		} | ||||
| 		/* We shouldn't be here */ | ||||
| 		return UINT32_MAX; | ||||
| 	} | ||||
| 
 | ||||
| 	void libcamera_get_streamcfg_size(LibCamera *camera, uint32_t idx, uint32_t *width, uint32_t *height) { | ||||
| 		if(!camera) { | ||||
| 			return; | ||||
| 		} | ||||
| 		if (!camera->config_) { | ||||
| 			spa_log_error(camera->log_, "Cannot get stream information without a camera");; | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		for (const StreamConfiguration &cfg : *camera->config_) { | ||||
| 			uint32_t index = 0; | ||||
| 			const StreamFormats &formats = cfg.formats(); | ||||
| 			for (PixelFormat pixelformat : formats.pixelformats()) { | ||||
| 				uint32_t index = 0; | ||||
| 				for (const Size &size : formats.sizes(pixelformat)) { | ||||
| 					if(index == idx) { | ||||
| 						*width = size.width; | ||||
| 						*height = size.height; | ||||
| 						return; | ||||
| 					} | ||||
| 					++index; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		/* We shouldn't be here */ | ||||
| 		*width = *height = UINT32_MAX; | ||||
| 	} | ||||
| 
 | ||||
| 	int LibCamera::listProperties() | ||||
| 	{ | ||||
| 		if (!cam_) { | ||||
| 			spa_log_error(log_, "Cannot list properties without a camera");; | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 
 | ||||
| 		spa_log_info(log_, "listing properties"); | ||||
| 		for (const auto &prop : cam_->properties()) { | ||||
| 			const ControlId *id = properties::properties.at(prop.first); | ||||
| 			const ControlValue &value = prop.second; | ||||
| 
 | ||||
| 			spa_log_info(log_, "Property: %s = %s",id->name().c_str(), value.toString().c_str()); | ||||
| 		} | ||||
| 
 | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	int64_t libcamera_get_fd(LibCamera *camera, int bufIdx, int planeIdx) { | ||||
| 		if((bufIdx >= (int)camera->nbuffers_) || (planeIdx >= (int)camera->nplanes_)){ | ||||
| 			return -1; | ||||
| 		} else { | ||||
| 			return camera->fd_[bufIdx][planeIdx]; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	int libcamera_get_max_size(LibCamera *camera) { | ||||
| 		return camera->get_max_size(); | ||||
| 	} | ||||
| 
 | ||||
| 	void libcamera_connect(LibCamera *camera) { | ||||
|     	if(!camera || !camera->cam_) { | ||||
|     		return; | ||||
|     	} | ||||
|     	camera->connect(); | ||||
|     } | ||||
| 
 | ||||
|     uint32_t libcamera_get_nbuffers(LibCamera *camera) { | ||||
| 		return camera->get_nbuffers(); | ||||
|     } | ||||
| 
 | ||||
|     uint32_t libcamera_get_nplanes(LibCamera *camera) { | ||||
|     	return camera->get_nplanes(); | ||||
|     } | ||||
| 
 | ||||
|     uint32_t libcamera_get_stride(LibCamera *camera) { | ||||
|     	return camera->get_stride(); | ||||
|     } | ||||
| 
 | ||||
| 	int libcamera_start_capture(LibCamera *camera) { | ||||
| 		if (!camera || !camera->cm_ || !camera->cam_) { | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		return camera->start(); | ||||
| 	} | ||||
| 
 | ||||
|     void libcamera_disconnect(LibCamera *camera) { | ||||
|     	if(!camera || !camera->cam_) { | ||||
|     		return; | ||||
|     	} | ||||
|     	camera->disconnect(); | ||||
|     } | ||||
| 
 | ||||
|     void libcamera_stop_capture(LibCamera *camera) { | ||||
|     	if(!camera || !camera->cm_ || !camera->cam_) { | ||||
|     		return; | ||||
|     	} | ||||
| 
 | ||||
|     	camera->stop(); | ||||
|     } | ||||
| 
 | ||||
| 	LibCamera* newLibCamera() { | ||||
| 		int err; | ||||
| 		int ret = 0; | ||||
| 		pthread_mutexattr_t attr; | ||||
| 		std::unique_ptr<CameraManager> cm = std::make_unique<CameraManager>(); | ||||
| 		LibCamera* camera = new LibCamera(); | ||||
| 
 | ||||
| 		ret = cm->start(); | ||||
| 		if (ret) { | ||||
| 			err = errno; | ||||
| 			return nullptr; | ||||
| 		} | ||||
| 
 | ||||
| 		camera->cm_ = std::move(cm); | ||||
| 
 | ||||
| 		camera->bufIdx_ = 0; | ||||
| 
 | ||||
| 		camera->set_streamcfg_width(DEFAULT_WIDTH); | ||||
| 		camera->set_streamcfg_height(DEFAULT_HEIGHT); | ||||
| 		camera->set_streamcfgpixel_format(DEFAULT_PIXEL_FMT); | ||||
| 
 | ||||
| 		if(!camera->open()) { | ||||
| 			deleteLibCamera(camera); | ||||
| 			return nullptr; | ||||
| 		} | ||||
| 
 | ||||
| 		pthread_mutexattr_init(&attr); | ||||
| 		pthread_mutex_init(&camera->lock, &attr); | ||||
| 
 | ||||
| 		camera->ring_buffer_init(); | ||||
| 
 | ||||
| 		return camera; | ||||
| 	} | ||||
| 
 | ||||
|     void deleteLibCamera(LibCamera *camera) { | ||||
|     	if(camera == nullptr) { | ||||
|     		return; | ||||
|     	} | ||||
| 
 | ||||
|     	pthread_mutex_destroy(&camera->lock); | ||||
| 
 | ||||
|     	camera->close(); | ||||
| 
 | ||||
|     	if(camera->cm_) | ||||
|     		camera->cm_->stop(); | ||||
| 
 | ||||
|     	delete camera; | ||||
|     	camera = nullptr; | ||||
|     } | ||||
| 
 | ||||
|     void LibCamera::requestComplete(Request *request) { | ||||
|     	if (request->status() == Request::RequestCancelled) { | ||||
|     		return; | ||||
|     	} | ||||
| 
 | ||||
|     	++bufIdx_; | ||||
| 		if(bufIdx_ >= nbuffers_) { | ||||
| 			bufIdx_ = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		StreamConfiguration &cfg = config_->at(0); | ||||
| 		const std::map<Stream *, FrameBuffer *> &buffers = request->buffers(); | ||||
| 
 | ||||
| 		unsigned int idx = 0; | ||||
| 		for (auto it = buffers.begin(); it != buffers.end(); ++it) { | ||||
| 			Stream *stream = it->first; | ||||
| 			FrameBuffer *buffer = it->second; | ||||
| 			const std::string &name = streamName_[stream]; | ||||
| 			unsigned int nplanes = buffer->planes().size(); | ||||
| 			OutBuf *pBuf = new OutBuf(); | ||||
| 			uint32_t ringbuf_write_index; | ||||
| 
 | ||||
| 			pBuf->bufIdx = bufIdx_; | ||||
| 			pBuf->n_datas = nplanes; | ||||
| 			pBuf->datas = new CamData[pBuf->n_datas]; | ||||
| 
 | ||||
| 			unsigned int planeIdx = 0; | ||||
| 			const std::vector<FrameBuffer::Plane> &planes = buffer->planes(); | ||||
| 			const FrameMetadata &metadata = buffer->metadata(); | ||||
| 			for (const FrameMetadata::Plane &plane : metadata.planes) { | ||||
| 				pBuf->datas[planeIdx].idx = planeIdx; | ||||
| 				pBuf->datas[planeIdx].type = 3; /*SPA_DATA_DmaBuf;*/ | ||||
| 				pBuf->datas[planeIdx].fd = planes[planeIdx].fd.fd(); | ||||
| 				pBuf->datas[planeIdx].size = plane.bytesused; | ||||
| 				pBuf->datas[planeIdx].maxsize = buffer->planes()[planeIdx].length; | ||||
| 				pBuf->datas[planeIdx].sequence = metadata.sequence; | ||||
| 				pBuf->datas[planeIdx].timestamp.tv_sec = metadata.timestamp / 1000000000; | ||||
| 				pBuf->datas[planeIdx].timestamp.tv_usec = (metadata.timestamp / 1000) % 1000000; | ||||
| 				++planeIdx; | ||||
| 			} | ||||
| 
 | ||||
| 			/* Push the buffer to ring buffer */ | ||||
| 			if(pBuf && pBuf->datas) { | ||||
| 				this->ring_buffer_write(pBuf); | ||||
| 				spa_log_trace(log_, "%s::Pushing buffer %p at index: %d\n", __FUNCTION__, pBuf, ringbuf_write_index); | ||||
| 				/* Now update the write index of the ring buffer */ | ||||
| 				this->ring_buffer_update_write_index(); | ||||
| 				this->empty_data(); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Create a new request and populate it with one buffer for each | ||||
| 		 * stream. | ||||
| 		 */ | ||||
| 		request = cam_->createRequest(); | ||||
| 		if (!request) { | ||||
| 			spa_log_error(log_, "Cannot create request"); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		for (auto it = buffers.begin(); it != buffers.end(); ++it) { | ||||
| 			Stream *stream = it->first; | ||||
| 			FrameBuffer *buffer = it->second; | ||||
| 
 | ||||
| 			request->addBuffer(stream, buffer); | ||||
| 		} | ||||
| 
 | ||||
| 		cam_->queueRequest(request); | ||||
|     } | ||||
| 
 | ||||
|     int32_t LibCamera::set_control(ControlList &controls, uint32_t control_id, float value) { | ||||
|     	switch(control_id) { | ||||
|     		case SPA_PROP_brightness: | ||||
|     			controls.set(controls::Brightness, value); | ||||
|     		break; | ||||
| 
 | ||||
|     		case SPA_PROP_contrast: | ||||
|     			controls.set(controls::Contrast, value); | ||||
|     		break; | ||||
| 
 | ||||
|     		case SPA_PROP_saturation: | ||||
|     			controls.set(controls::Saturation, value); | ||||
|     		break; | ||||
| 
 | ||||
|     		case SPA_PROP_exposure: | ||||
|     			controls.set(controls::ExposureValue, value); | ||||
|     		break; | ||||
| 
 | ||||
|     		case SPA_PROP_gain: | ||||
|     			controls.set(controls::AnalogueGain, value); | ||||
|     		break; | ||||
| 
 | ||||
|     		default: | ||||
|     		return -1; | ||||
|     	} | ||||
|     	return 0; | ||||
|     } | ||||
| 
 | ||||
|     int32_t libcamera_set_control(LibCamera *camera, uint32_t control_id, float value) { | ||||
|     	int32_t res; | ||||
| 
 | ||||
|     	if(!camera || !camera->cm_ || !camera->cam_) | ||||
|     		return -1; | ||||
| 
 | ||||
|     	Request *request = camera->cam_->createRequest(); | ||||
|     	ControlList &controls = request->controls(); | ||||
|     	res = camera->set_control(controls, control_id, value); | ||||
|     	camera->cam_->queueRequest(request); | ||||
| 
 | ||||
|     	return res; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										128
									
								
								spa/plugins/libcamera/libcamera_wrapper.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								spa/plugins/libcamera/libcamera_wrapper.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,128 @@ | |||
| /* Spa libcamera support
 | ||||
|  * | ||||
|  * Copyright (C) 2020, Collabora Ltd. | ||||
|  *     Author: Raghavendra Rao Sidlagatta <raghavendra.rao@collabora.com> | ||||
|  * | ||||
|  * libcamera_wrapper.h | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a | ||||
|  * copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation | ||||
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||||
|  * and/or sell copies of the Software, and to permit persons to whom the | ||||
|  * Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice (including the next | ||||
|  * paragraph) shall be included in all copies or substantial portions of the | ||||
|  * Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | ||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
|  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||
|  * DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __LIBCAMERA_WRAPPER_H | ||||
| #define __LIBCAMERA_WRAPPER_H | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| #define MAX_NUM_BUFFERS				16 | ||||
| 
 | ||||
| typedef struct CamData { | ||||
| 	uint32_t idx; | ||||
| 	uint32_t type; | ||||
| 	int64_t fd; | ||||
| 	uint32_t maxsize; /**< max size of data */ | ||||
| 	uint32_t size; /**< size of valid data. Should be clamped to
 | ||||
| 					  *  maxsize. */ | ||||
| 	struct timeval timestamp; | ||||
| 	uint32_t sequence; | ||||
| 	void *data; | ||||
| }CamData; | ||||
| 
 | ||||
| typedef struct OutBuf { | ||||
| 	uint32_t bufIdx; | ||||
| 	uint32_t n_datas;		/**< number of data members */ | ||||
| 	struct CamData *datas;	/**< array of data members */ | ||||
| }OutBuf; | ||||
| 
 | ||||
| typedef struct LibCamera LibCamera; | ||||
| 
 | ||||
| LibCamera *newLibCamera(); | ||||
| 
 | ||||
| void deleteLibCamera(LibCamera *camera); | ||||
| 
 | ||||
| void libcamera_set_log(LibCamera *camera, struct spa_log *log); | ||||
| 
 | ||||
| bool libcamera_open(LibCamera *camera); | ||||
| 
 | ||||
| void libcamera_close(LibCamera *camera); | ||||
| 
 | ||||
| void libcamera_connect(LibCamera *camera); | ||||
| 
 | ||||
| void libcamera_disconnect(LibCamera *camera); | ||||
| 
 | ||||
| int libcamera_isCapturing(LibCamera *camera); | ||||
| 
 | ||||
| int libcamera_start_capture(LibCamera *camera); | ||||
| 
 | ||||
| void libcamera_stop_capture(LibCamera *camera); | ||||
| 
 | ||||
| int libcamera_get_refcnt(LibCamera *camera); | ||||
| 
 | ||||
| uint32_t libcamera_get_streamcfg_width(LibCamera *camera); | ||||
| 
 | ||||
| uint32_t libcamera_get_streamcfg_height(LibCamera *camera); | ||||
| 
 | ||||
| uint32_t libcamera_get_streamcfgpixel_format(LibCamera *camera); | ||||
| 
 | ||||
| uint32_t libcamera_enum_streamcfgpixel_format(LibCamera *camera, uint32_t idx); | ||||
| 
 | ||||
| uint32_t libcamera_video_format_to_drm(uint32_t fmt); | ||||
| 
 | ||||
| uint32_t libcamera_drm_to_video_format(unsigned int drm); | ||||
| 
 | ||||
| uint32_t libcamera_get_nbuffers(LibCamera *camera); | ||||
| 
 | ||||
| uint32_t libcamera_get_nplanes(LibCamera *camera); | ||||
| 
 | ||||
| int64_t libcamera_get_fd(LibCamera *camera, int bufIdx, int planeIdx); | ||||
| 
 | ||||
| int32_t libcamera_get_max_size(LibCamera *camera); | ||||
| 
 | ||||
| int32_t libcamera_set_control(LibCamera *camera, uint32_t control_id, float value); | ||||
| 
 | ||||
| void libcamera_set_streamcfg_width(LibCamera *camera, uint32_t w); | ||||
| 
 | ||||
| void libcamera_set_streamcfg_height(LibCamera *camera, uint32_t w); | ||||
| 
 | ||||
| void libcamera_set_streamcfgpixel_format(LibCamera *camera, uint32_t fmt); | ||||
| 
 | ||||
| void libcamera_get_streamcfg_size(LibCamera *camera, uint32_t idx, uint32_t *width, uint32_t *height); | ||||
| 
 | ||||
| uint32_t libcamera_get_stride(LibCamera *camera); | ||||
| 
 | ||||
| void *libcamera_get_ring_buffer_data(LibCamera *camera); | ||||
| 
 | ||||
| void libcamera_reset_ring_buffer_data(LibCamera *camera); | ||||
| 
 | ||||
| void libcamera_ringbuffer_read_update(LibCamera *camera); | ||||
| 
 | ||||
| bool libcamera_is_data_available(LibCamera *camera); | ||||
| 
 | ||||
| void libcamera_consume_data(LibCamera *camera); | ||||
| 
 | ||||
| void libcamera_free_CamData(LibCamera *camera, CamData *p); | ||||
| 
 | ||||
| void libcamera_free_OutBuf(LibCamera *camera, OutBuf *p); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif /* extern "C" */ | ||||
| #endif /* __LIBCAMERA_WRAPPER_H */ | ||||
							
								
								
									
										12
									
								
								spa/plugins/libcamera/meson.build
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								spa/plugins/libcamera/meson.build
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| libcamera_sources = ['libcamera.c', | ||||
|                 'libcamera-device.c', | ||||
|                 'libcamera-client.c', | ||||
|                 'libcamera-source.c', | ||||
|                 'libcamera_wrapper.cpp'] | ||||
| 
 | ||||
| libcameralib = shared_library('spa-libcamera', | ||||
|                           libcamera_sources, | ||||
|                           include_directories : [ spa_inc ], | ||||
|                           dependencies : [ libudev_dep, libcamera_dep, pthread_lib  ], | ||||
|                           install : true, | ||||
| 		          install_dir : join_paths(spa_plugindir, 'libcamera')) | ||||
|  | @ -43,3 +43,6 @@ endif | |||
| if get_option('v4l2') | ||||
|   subdir('v4l2') | ||||
| endif | ||||
| if get_option('libcamera') | ||||
|   subdir('libcamera') | ||||
| endif | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ set-prop link.max-buffers		16		# version < 3 clients can't handle more | |||
| add-spa-lib audio.convert* audioconvert/libspa-audioconvert | ||||
| add-spa-lib api.alsa.* alsa/libspa-alsa | ||||
| add-spa-lib api.v4l2.* v4l2/libspa-v4l2 | ||||
| add-spa-lib api.libcamera.* libcamera/libspa-libcamera | ||||
| add-spa-lib api.bluez5.* bluez5/libspa-bluez5 | ||||
| add-spa-lib api.vulkan.* vulkan/libspa-vulkan | ||||
| add-spa-lib api.jack.* jack/libspa-jack | ||||
|  | @ -41,7 +42,7 @@ add-spa-lib support.* support/libspa-support | |||
| # Loads a module with the given parameters. Normally failure is | ||||
| # fatal if the module is not found, unless -ifexists is given. | ||||
| # | ||||
| load-module libpipewire-module-rtkit # rt.prio=20 rt.time.soft=200000 rt.time.hard=200000 | ||||
| #load-module libpipewire-module-rtkit # rt.prio=20 rt.time.soft=200000 rt.time.hard=200000 | ||||
| load-module libpipewire-module-protocol-native | ||||
| load-module libpipewire-module-profiler | ||||
| load-module libpipewire-module-metadata | ||||
|  |  | |||
							
								
								
									
										498
									
								
								src/examples/media-session/libcamera-monitor.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										498
									
								
								src/examples/media-session/libcamera-monitor.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,498 @@ | |||
| /* PipeWire
 | ||||
|  * | ||||
|  * Copyright © 2019 Wim Taymans | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a | ||||
|  * copy of this software and associated documentation files (the "Software"), | ||||
|  * to deal in the Software without restriction, including without limitation | ||||
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||||
|  * and/or sell copies of the Software, and to permit persons to whom the | ||||
|  * Software is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice (including the next | ||||
|  * paragraph) shall be included in all copies or substantial portions of the | ||||
|  * Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | ||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
|  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||
|  * DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <errno.h> | ||||
| #include <math.h> | ||||
| #include <time.h> | ||||
| 
 | ||||
| #include "config.h" | ||||
| 
 | ||||
| #include <spa/monitor/device.h> | ||||
| #include <spa/node/node.h> | ||||
| #include <spa/utils/hook.h> | ||||
| #include <spa/utils/names.h> | ||||
| #include <spa/utils/result.h> | ||||
| #include <spa/param/props.h> | ||||
| #include <spa/debug/dict.h> | ||||
| #include <spa/pod/builder.h> | ||||
| 
 | ||||
| #include "pipewire/pipewire.h" | ||||
| 
 | ||||
| #include "media-session.h" | ||||
| 
 | ||||
| struct device; | ||||
| 
 | ||||
| struct node { | ||||
| 	struct impl *impl; | ||||
| 	struct device *device; | ||||
| 	struct spa_list link; | ||||
| 	uint32_t id; | ||||
| 
 | ||||
| 	struct pw_properties *props; | ||||
| 
 | ||||
| 	struct pw_proxy *proxy; | ||||
| 	struct spa_node *node; | ||||
| }; | ||||
| 
 | ||||
| struct device { | ||||
| 	struct impl *impl; | ||||
| 	struct spa_list link; | ||||
| 	uint32_t id; | ||||
| 	uint32_t device_id; | ||||
| 
 | ||||
| 	int priority; | ||||
| 	int profile; | ||||
| 
 | ||||
| 	struct pw_properties *props; | ||||
| 
 | ||||
| 	struct spa_handle *handle; | ||||
| 	struct spa_device *device; | ||||
| 	struct spa_hook device_listener; | ||||
| 
 | ||||
| 	struct sm_device *sdevice; | ||||
| 	struct spa_hook listener; | ||||
| 
 | ||||
| 	unsigned int appeared:1; | ||||
| 	struct spa_list node_list; | ||||
| }; | ||||
| 
 | ||||
| struct impl { | ||||
| 	struct sm_media_session *session; | ||||
| 	struct spa_hook session_listener; | ||||
| 
 | ||||
| 	struct spa_handle *handle; | ||||
| 	struct spa_device *monitor; | ||||
| 	struct spa_hook listener; | ||||
| 
 | ||||
| 	struct spa_list device_list; | ||||
| }; | ||||
| 
 | ||||
| static struct node *libcamera_find_node(struct device *dev, uint32_t id) | ||||
| { | ||||
| 	struct node *node; | ||||
| 
 | ||||
| 	spa_list_for_each(node, &dev->node_list, link) { | ||||
| 		if (node->id == id) | ||||
| 			return node; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static void libcamera_update_node(struct device *dev, struct node *node, | ||||
| 		const struct spa_device_object_info *info) | ||||
| { | ||||
| 	pw_log_debug("update node %u", node->id); | ||||
| 
 | ||||
| 	if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG)) | ||||
| 		spa_debug_dict(0, info->props); | ||||
| 
 | ||||
| 	pw_properties_update(node->props, info->props); | ||||
| } | ||||
| 
 | ||||
| static struct node *libcamera_create_node(struct device *dev, uint32_t id, | ||||
| 		const struct spa_device_object_info *info) | ||||
| { | ||||
| 	struct node *node; | ||||
| 	struct impl *impl = dev->impl; | ||||
| 	int res; | ||||
| 	const char *str; | ||||
| 
 | ||||
| 	pw_log_debug("new node %u", id); | ||||
| 
 | ||||
| 	if (strcmp(info->type, SPA_TYPE_INTERFACE_Node) != 0) { | ||||
| 		errno = EINVAL; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	node = calloc(1, sizeof(*node)); | ||||
| 	if (node == NULL) { | ||||
| 		res = -errno; | ||||
| 		goto exit; | ||||
| 	} | ||||
| 
 | ||||
| 	node->props = pw_properties_new_dict(info->props); | ||||
| 
 | ||||
| 	pw_properties_setf(node->props, PW_KEY_DEVICE_ID, "%d", dev->device_id); | ||||
| 
 | ||||
| 	str = pw_properties_get(dev->props, SPA_KEY_DEVICE_NAME); | ||||
| 	if (str == NULL) | ||||
| 		str = pw_properties_get(dev->props, SPA_KEY_DEVICE_NICK); | ||||
| 	if (str == NULL) | ||||
| 		str = pw_properties_get(dev->props, SPA_KEY_DEVICE_ALIAS); | ||||
| 	if (str == NULL) | ||||
| 		str = "libcamera-device"; | ||||
| 	pw_properties_setf(node->props, PW_KEY_NODE_NAME, "%s.%s", info->factory_name, str); | ||||
| 
 | ||||
| 	str = pw_properties_get(dev->props, SPA_KEY_DEVICE_DESCRIPTION); | ||||
| 	if (str == NULL) | ||||
| 		str = "libcamera-device"; | ||||
| 	pw_properties_set(node->props, PW_KEY_NODE_DESCRIPTION, str); | ||||
| 
 | ||||
| 	pw_properties_set(node->props, PW_KEY_FACTORY_NAME, info->factory_name); | ||||
| 
 | ||||
| 	node->impl = impl; | ||||
| 	node->device = dev; | ||||
| 	node->id = id; | ||||
| 	node->proxy = sm_media_session_create_object(impl->session, | ||||
| 				"spa-node-factory", | ||||
| 				PW_TYPE_INTERFACE_Node, | ||||
| 				PW_VERSION_NODE, | ||||
| 				&node->props->dict, | ||||
|                                 0); | ||||
| 	if (node->proxy == NULL) { | ||||
| 		res = -errno; | ||||
| 		goto clean_node; | ||||
| 	} | ||||
| 
 | ||||
| 	spa_list_append(&dev->node_list, &node->link); | ||||
| 
 | ||||
| 	return node; | ||||
| 
 | ||||
| clean_node: | ||||
| 	pw_properties_free(node->props); | ||||
| 	free(node); | ||||
| exit: | ||||
| 	errno = -res; | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static void libcamera_remove_node(struct device *dev, struct node *node) | ||||
| { | ||||
| 	pw_log_debug("remove node %u", node->id); | ||||
| 	spa_list_remove(&node->link); | ||||
| 	pw_proxy_destroy(node->proxy); | ||||
| 	pw_properties_free(node->props); | ||||
| 	free(node); | ||||
| } | ||||
| 
 | ||||
| static void libcamera_device_info(void *data, const struct spa_device_info *info) | ||||
| { | ||||
| 	struct device *dev = data; | ||||
| 
 | ||||
| 	if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG)) | ||||
| 		spa_debug_dict(0, info->props); | ||||
| 
 | ||||
| 	pw_properties_update(dev->props, info->props); | ||||
| } | ||||
| 
 | ||||
| static void libcamera_device_object_info(void *data, uint32_t id, | ||||
|                 const struct spa_device_object_info *info) | ||||
| { | ||||
| 	struct device *dev = data; | ||||
| 	struct node *node; | ||||
| 
 | ||||
| 	node = libcamera_find_node(dev, id); | ||||
| 
 | ||||
| 	if (info == NULL) { | ||||
| 		if (node == NULL) { | ||||
| 			pw_log_warn("device %p: unknown node %u", dev, id); | ||||
| 			return; | ||||
| 		} | ||||
| 		libcamera_remove_node(dev, node); | ||||
| 	} else if (node == NULL) { | ||||
| 		libcamera_create_node(dev, id, info); | ||||
| 	} else { | ||||
| 		libcamera_update_node(dev, node, info); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static const struct spa_device_events libcamera_device_events = { | ||||
| 	SPA_VERSION_DEVICE_EVENTS, | ||||
| 	.info = libcamera_device_info, | ||||
| 	.object_info = libcamera_device_object_info | ||||
| }; | ||||
| 
 | ||||
| static struct device *libcamera_find_device(struct impl *impl, uint32_t id) | ||||
| { | ||||
| 	struct device *dev; | ||||
| 
 | ||||
| 	pw_log_info("In %s:: Invoking spa_list_for_each for id = %d", __FUNCTION__, id); | ||||
| 	spa_list_for_each(dev, &impl->device_list, link) { | ||||
| 		if (dev->id == id) | ||||
| 			return dev; | ||||
| 	} | ||||
| 	pw_log_info("In %s:: spa_list_for_each returns NULL for id = %d", __FUNCTION__, id); | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static void libcamera_update_device(struct impl *impl, struct device *dev, | ||||
| 		const struct spa_device_object_info *info) | ||||
| { | ||||
| 	pw_log_debug("update device %u", dev->id); | ||||
| 
 | ||||
| 	if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG)) | ||||
| 		spa_debug_dict(0, info->props); | ||||
| 
 | ||||
| 	pw_properties_update(dev->props, info->props); | ||||
| } | ||||
| 
 | ||||
| static int libcamera_update_device_props(struct device *dev) | ||||
| { | ||||
| 	struct pw_properties *p = dev->props; | ||||
| 	const char *s, *d; | ||||
| 	char temp[32]; | ||||
| 
 | ||||
| 	if ((s = pw_properties_get(p, SPA_KEY_DEVICE_NAME)) == NULL) { | ||||
| 		if ((s = pw_properties_get(p, SPA_KEY_DEVICE_BUS_ID)) == NULL) { | ||||
| 			if ((s = pw_properties_get(p, SPA_KEY_DEVICE_BUS_PATH)) == NULL) { | ||||
| 				snprintf(temp, sizeof(temp), "%d", dev->id); | ||||
| 				s = temp; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	pw_properties_setf(p, PW_KEY_DEVICE_NAME, "libcamera_device.%s", s); | ||||
| 
 | ||||
| 	if (pw_properties_get(p, PW_KEY_DEVICE_DESCRIPTION) == NULL) { | ||||
| 		d = pw_properties_get(p, PW_KEY_DEVICE_PRODUCT_NAME); | ||||
| 		if (!d) | ||||
| 			d = "Unknown device"; | ||||
| 
 | ||||
| 		pw_properties_set(p, PW_KEY_DEVICE_DESCRIPTION, d); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void set_profile(struct device *device, int index) | ||||
| { | ||||
| 	char buf[1024]; | ||||
| 	struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); | ||||
| 
 | ||||
| 	pw_log_debug("%p: set profile %d id:%d", device, index, device->device_id); | ||||
| 
 | ||||
| 	device->profile = index; | ||||
| 	if (device->device_id != 0) { | ||||
| 		spa_device_set_param(device->device, | ||||
| 				SPA_PARAM_Profile, 0, | ||||
| 				spa_pod_builder_add_object(&b, | ||||
| 					SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, | ||||
| 					SPA_PARAM_PROFILE_index,   SPA_POD_Int(index))); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void device_destroy(void *data) | ||||
| { | ||||
| 	struct device *device = data; | ||||
| 	struct node *node; | ||||
| 
 | ||||
| 	pw_log_debug("device %p destroy", device); | ||||
| 
 | ||||
| 	spa_list_consume(node, &device->node_list, link) | ||||
| 		libcamera_remove_node(device, node); | ||||
| } | ||||
| 
 | ||||
| static void device_update(void *data) | ||||
| { | ||||
| 	struct device *device = data; | ||||
| 
 | ||||
| 	pw_log_debug("device %p appeared %d %d", device, device->appeared, device->profile); | ||||
| 
 | ||||
| 	if (device->appeared) | ||||
| 		return; | ||||
| 
 | ||||
| 	device->device_id = device->sdevice->obj.id; | ||||
| 	device->appeared = true; | ||||
| 
 | ||||
| 	spa_device_add_listener(device->device, | ||||
| 		&device->device_listener, | ||||
| 		&libcamera_device_events, device); | ||||
| 
 | ||||
| 	set_profile(device, 1); | ||||
| 	sm_object_sync_update(&device->sdevice->obj); | ||||
| } | ||||
| 
 | ||||
| static const struct sm_object_events device_events = { | ||||
| 	SM_VERSION_OBJECT_EVENTS, | ||||
|         .destroy = device_destroy, | ||||
|         .update = device_update, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static struct device *libcamera_create_device(struct impl *impl, uint32_t id, | ||||
| 		const struct spa_device_object_info *info) | ||||
| { | ||||
| 	struct pw_context *context = impl->session->context; | ||||
| 	struct device *dev; | ||||
| 	struct spa_handle *handle; | ||||
| 	int res; | ||||
| 	void *iface; | ||||
| 
 | ||||
| 	pw_log_debug("new device %u", id); | ||||
| 
 | ||||
| 	if (strcmp(info->type, SPA_TYPE_INTERFACE_Device) != 0) { | ||||
| 		errno = EINVAL; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	handle = pw_context_load_spa_handle(context, | ||||
| 			info->factory_name, | ||||
| 			info->props); | ||||
| 	if (handle == NULL) { | ||||
| 		res = -errno; | ||||
| 		pw_log_error("can't make factory instance: %m"); | ||||
| 		goto exit; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((res = spa_handle_get_interface(handle, info->type, &iface)) < 0) { | ||||
| 		pw_log_error("can't get %s interface: %s", info->type, spa_strerror(res)); | ||||
| 		goto unload_handle; | ||||
| 	} | ||||
| 
 | ||||
| 	dev = calloc(1, sizeof(*dev)); | ||||
| 	if (dev == NULL) { | ||||
| 		res = -errno; | ||||
| 		goto unload_handle; | ||||
| 	} | ||||
| 
 | ||||
| 	dev->impl = impl; | ||||
| 	dev->id = id; | ||||
| 	dev->handle = handle; | ||||
| 	dev->device = iface; | ||||
| 	dev->props = pw_properties_new_dict(info->props); | ||||
| 	libcamera_update_device_props(dev); | ||||
| 
 | ||||
| 	dev->sdevice = sm_media_session_export_device(impl->session, | ||||
| 			&dev->props->dict, dev->device); | ||||
| 
 | ||||
| 	if (dev->sdevice == NULL) { | ||||
| 		res = -errno; | ||||
| 		goto clean_device; | ||||
| 	} | ||||
| 
 | ||||
| 	pw_log_debug("got object %p", &dev->sdevice->obj); | ||||
| 
 | ||||
| 	sm_object_add_listener(&dev->sdevice->obj, | ||||
| 			&dev->listener, | ||||
| 			&device_events, dev); | ||||
| 
 | ||||
| 	spa_list_init(&dev->node_list); | ||||
| 	spa_list_append(&impl->device_list, &dev->link); | ||||
| 
 | ||||
| 	return dev; | ||||
| 
 | ||||
| clean_device: | ||||
| 	free(dev); | ||||
| unload_handle: | ||||
| 	pw_unload_spa_handle(handle); | ||||
| exit: | ||||
| 	errno = -res; | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static void libcamera_remove_device(struct impl *impl, struct device *dev) | ||||
| { | ||||
| 	pw_log_debug("remove device %u", dev->id); | ||||
| 	spa_list_remove(&dev->link); | ||||
| 	if (dev->appeared) | ||||
| 		spa_hook_remove(&dev->device_listener); | ||||
| 	if (dev->sdevice) | ||||
| 		sm_object_destroy(&dev->sdevice->obj); | ||||
| 	spa_hook_remove(&dev->listener); | ||||
| 	pw_unload_spa_handle(dev->handle); | ||||
| 	pw_properties_free(dev->props); | ||||
| 	free(dev); | ||||
| } | ||||
| 
 | ||||
| static void libcamera_udev_object_info(void *data, uint32_t id, | ||||
|                 const struct spa_device_object_info *info) | ||||
| { | ||||
| 	struct impl *impl = data; | ||||
| 	struct device *dev = NULL; | ||||
| 
 | ||||
| 	pw_log_info("In %s:: Invoking libcamera_find_device for id = %d", __FUNCTION__, id); | ||||
| 	dev = libcamera_find_device(impl, id); | ||||
| 
 | ||||
| 	if (info == NULL) { | ||||
| 		if (dev == NULL) | ||||
| 			return; | ||||
| 		libcamera_remove_device(impl, dev); | ||||
| 	} else if (dev == NULL) { | ||||
| 		if (libcamera_create_device(impl, id, info) == NULL) | ||||
| 			return; | ||||
| 	} else { | ||||
| 		libcamera_update_device(impl, dev, info); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static const struct spa_device_events libcamera_udev_callbacks = | ||||
| { | ||||
| 	SPA_VERSION_DEVICE_EVENTS, | ||||
| 	.object_info = libcamera_udev_object_info, | ||||
| }; | ||||
| 
 | ||||
| static void session_destroy(void *data) | ||||
| { | ||||
| 	struct impl *impl = data; | ||||
| 	spa_hook_remove(&impl->session_listener); | ||||
| 	spa_hook_remove(&impl->listener); | ||||
| 	pw_unload_spa_handle(impl->handle); | ||||
| 	free(impl); | ||||
| } | ||||
| 
 | ||||
| static const struct sm_media_session_events session_events = { | ||||
| 	SM_VERSION_MEDIA_SESSION_EVENTS, | ||||
| 	.destroy = session_destroy, | ||||
| }; | ||||
| 
 | ||||
| int sm_libcamera_monitor_start(struct sm_media_session *sess) | ||||
| { | ||||
| 	struct pw_context *context = sess->context; | ||||
| 	struct impl *impl; | ||||
| 	int res; | ||||
| 	void *iface; | ||||
| 
 | ||||
| 	impl = calloc(1, sizeof(struct impl)); | ||||
| 	if (impl == NULL) | ||||
| 		return -errno; | ||||
| 
 | ||||
| 	impl->session = sess; | ||||
| 	pw_log_info("%s:: Loading spa handle: %s", __FUNCTION__, SPA_NAME_API_LIBCAMERA_DEVICE); | ||||
| 
 | ||||
| 	impl->handle = pw_context_load_spa_handle(context, SPA_NAME_API_LIBCAMERA_ENUM_CLIENT, NULL); | ||||
| 	if (impl->handle == NULL) { | ||||
| 		res = -errno; | ||||
| 		goto out_free; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((res = spa_handle_get_interface(impl->handle, SPA_TYPE_INTERFACE_Device, &iface)) < 0) { | ||||
| 		pw_log_error("can't get MONITOR interface: %d", res); | ||||
| 		goto out_unload; | ||||
| 	} | ||||
| 
 | ||||
| 	impl->monitor = iface; | ||||
| 	spa_list_init(&impl->device_list); | ||||
| 
 | ||||
| 	spa_device_add_listener(impl->monitor, &impl->listener, | ||||
| 			&libcamera_udev_callbacks, impl); | ||||
| 
 | ||||
| 	sm_media_session_add_listener(sess, &impl->session_listener, &session_events, impl); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| out_unload: | ||||
| 	pw_unload_spa_handle(impl->handle); | ||||
| out_free: | ||||
| 	free(impl); | ||||
| 	return res; | ||||
| } | ||||
|  | @ -66,6 +66,7 @@ | |||
| int sm_metadata_start(struct sm_media_session *sess); | ||||
| int sm_alsa_midi_start(struct sm_media_session *sess); | ||||
| int sm_v4l2_monitor_start(struct sm_media_session *sess); | ||||
| int sm_libcamera_monitor_start(struct sm_media_session *sess); | ||||
| int sm_bluez5_monitor_start(struct sm_media_session *sess); | ||||
| int sm_alsa_monitor_start(struct sm_media_session *sess); | ||||
| int sm_suspend_node_start(struct sm_media_session *sess); | ||||
|  | @ -1715,8 +1716,7 @@ static void do_quit(void *data, int signal_number) | |||
| 	pw_main_loop_quit(impl->loop); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #define DEFAULT_ENABLED		"alsa-pcm,alsa-seq,v4l2,bluez5,metadata,suspend-node,policy-node" | ||||
| #define DEFAULT_ENABLED		"alsa-pcm,alsa-seq,v4l2,libcamera,bluez5,metadata,suspend-node,policy-node" | ||||
| #define DEFAULT_DISABLED	"" | ||||
| 
 | ||||
| static const struct { | ||||
|  | @ -1728,6 +1728,7 @@ static const struct { | |||
| 	{ "alsa-seq", "alsa seq midi support", sm_alsa_midi_start }, | ||||
| 	{ "alsa-pcm", "alsa pcm udev detection", sm_alsa_monitor_start }, | ||||
| 	{ "v4l2", "video for linux udev detection", sm_v4l2_monitor_start }, | ||||
| 	{ "libcamera", "libcamera udev detection", sm_libcamera_monitor_start }, | ||||
| 	{ "bluez5", "bluetooth support", sm_bluez5_monitor_start }, | ||||
| 	{ "metadata", "export metadata API", sm_metadata_start }, | ||||
| 	{ "suspend-node", "suspend inactive nodes", sm_suspend_node_start }, | ||||
|  | @ -1836,6 +1837,7 @@ int main(int argc, char *argv[]) | |||
| 	pw_context_add_spa_lib(impl.this.context, "api.bluez5.*", "bluez5/libspa-bluez5"); | ||||
| 	pw_context_add_spa_lib(impl.this.context, "api.alsa.*", "alsa/libspa-alsa"); | ||||
| 	pw_context_add_spa_lib(impl.this.context, "api.v4l2.*", "v4l2/libspa-v4l2"); | ||||
| 	pw_context_add_spa_lib(impl.this.context, "api.libcamera.*", "libcamera/libspa-libcamera"); | ||||
| 
 | ||||
| 	pw_context_set_object(impl.this.context, SM_TYPE_MEDIA_SESSION, &impl); | ||||
| 
 | ||||
|  | @ -1865,7 +1867,7 @@ int main(int argc, char *argv[]) | |||
| 		const char *name = modules[i].name; | ||||
| 		if (opt_contains(opt_enabled, name) && | ||||
| 		    !opt_contains(opt_disabled, name)) { | ||||
| 			pw_log_info("enable: %s", name); | ||||
| 			pw_log_info("enable: %s. Starting module.", name); | ||||
| 			modules[i].start(&impl.this); | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -141,6 +141,7 @@ static int client_endpoint_create_link(void *object, const struct spa_dict *prop | |||
| 	struct pw_properties *p; | ||||
| 	int res; | ||||
| 
 | ||||
| 	pw_log_info(NAME " %s. ", __FUNCTION__); | ||||
| 	pw_log_debug(NAME" %p: endpoint %p", impl, endpoint); | ||||
| 
 | ||||
| 	if (props == NULL) | ||||
|  | @ -160,11 +161,15 @@ static int client_endpoint_create_link(void *object, const struct spa_dict *prop | |||
| 			res = -EINVAL; | ||||
| 			goto exit; | ||||
| 		} | ||||
| 		pw_log_info(NAME " %s. Got %s from PW_KEY_ENDPOINT_LINK_INPUT_ENDPOINT", __FUNCTION__, str); | ||||
| 
 | ||||
| 		obj = sm_media_session_find_object(impl->session, atoi(str)); | ||||
| 		if (obj == NULL || strcmp(obj->type, PW_TYPE_INTERFACE_Endpoint) != 0) { | ||||
| 			pw_log_warn(NAME" %p: could not find endpoint %s (%p)", impl, str, obj); | ||||
| 			res = -EINVAL; | ||||
| 			goto exit; | ||||
| 		} else { | ||||
| 			pw_log_info(NAME " %s: comparing obj->type[%s] with %s.", __FUNCTION__, obj->type, PW_TYPE_INTERFACE_Endpoint); | ||||
| 		} | ||||
| 
 | ||||
| 		pw_properties_setf(p, PW_KEY_LINK_OUTPUT_NODE, "%d", endpoint->node->node->info->id); | ||||
|  | @ -198,6 +203,7 @@ static struct stream *endpoint_add_stream(struct endpoint *endpoint) | |||
| 	struct stream *s; | ||||
| 	const char *str; | ||||
| 
 | ||||
| 	pw_log_info(NAME " %s. ", __FUNCTION__); | ||||
| 	s = calloc(1, sizeof(*s)); | ||||
| 	if (s == NULL) | ||||
| 		return NULL; | ||||
|  | @ -222,7 +228,7 @@ static struct stream *endpoint_add_stream(struct endpoint *endpoint) | |||
| 	s->info.change_mask = PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS; | ||||
| 	s->info.props = &s->props->dict; | ||||
| 
 | ||||
| 	pw_log_debug("stream %d", s->info.id); | ||||
| 	pw_log_info("%s:: stream %s %d", __FUNCTION__, s->info.name, s->info.id); | ||||
| 	pw_client_endpoint_stream_update(endpoint->client_endpoint, | ||||
| 			s->info.id, | ||||
| 			PW_CLIENT_ENDPOINT_STREAM_UPDATE_INFO, | ||||
|  | @ -239,6 +245,7 @@ static void destroy_stream(struct stream *stream) | |||
| { | ||||
| 	struct endpoint *endpoint = stream->endpoint; | ||||
| 
 | ||||
| 	pw_log_info(NAME " %s. ", __FUNCTION__); | ||||
| 	pw_client_endpoint_stream_update(endpoint->client_endpoint, | ||||
| 			stream->info.id, | ||||
| 			PW_CLIENT_ENDPOINT_STREAM_UPDATE_DESTROYED, | ||||
|  | @ -260,6 +267,7 @@ static void update_params(void *data) | |||
| 	struct sm_node *node = endpoint->node->node; | ||||
| 	struct sm_param *p; | ||||
| 
 | ||||
| 	pw_log_info(NAME " %s. ", __FUNCTION__); | ||||
| 	pw_log_debug(NAME" %p: endpoint", endpoint); | ||||
| 
 | ||||
| 	params = alloca(sizeof(struct spa_pod *) * node->n_params); | ||||
|  | @ -307,6 +315,7 @@ static void complete_endpoint(void *data) | |||
| 	struct stream *stream; | ||||
| 	struct sm_param *p; | ||||
| 
 | ||||
| 	pw_log_info(NAME " %s. ", __FUNCTION__); | ||||
| 	pw_log_debug("endpoint %p: complete", endpoint); | ||||
| 
 | ||||
| 	spa_list_for_each(p, &endpoint->node->node->param_list, link) { | ||||
|  | @ -346,6 +355,7 @@ static void proxy_destroy(void *data) | |||
| 	struct endpoint *endpoint = data; | ||||
| 	struct stream *s; | ||||
| 
 | ||||
| 	pw_log_info(NAME " %s. ", __FUNCTION__); | ||||
| 	pw_log_debug("endpoint %p: destroy", endpoint); | ||||
| 
 | ||||
| 	spa_list_consume(s, &endpoint->stream_list, link) | ||||
|  | @ -361,6 +371,7 @@ static void proxy_destroy(void *data) | |||
| static void proxy_bound(void *data, uint32_t id) | ||||
| { | ||||
| 	struct endpoint *endpoint = data; | ||||
| 	pw_log_info(NAME " %s. ", __FUNCTION__); | ||||
| 	endpoint->info.id = id; | ||||
| } | ||||
| 
 | ||||
|  | @ -382,6 +393,7 @@ static struct endpoint *create_endpoint(struct node *node) | |||
| 	struct pw_properties *pr = node->node->obj.props; | ||||
| 	enum pw_direction direction; | ||||
| 
 | ||||
| 	pw_log_info(NAME " %s. ", __FUNCTION__); | ||||
| 	if (pr == NULL) { | ||||
| 		errno = EINVAL; | ||||
| 		return NULL; | ||||
|  | @ -415,6 +427,7 @@ static struct endpoint *create_endpoint(struct node *node) | |||
| 	if ((str = pw_properties_get(pr, PW_KEY_DEVICE_ICON_NAME)) != NULL) | ||||
| 		pw_properties_set(props, PW_KEY_ENDPOINT_ICON_NAME, str); | ||||
| 
 | ||||
| 	pw_log_info(NAME " %s. Invoking sm_media_session_create_object", __FUNCTION__); | ||||
| 	proxy = sm_media_session_create_object(impl->session, | ||||
| 						"client-endpoint", | ||||
| 						PW_TYPE_INTERFACE_ClientEndpoint, | ||||
|  | @ -449,7 +462,7 @@ static struct endpoint *create_endpoint(struct node *node) | |||
| 	endpoint->info.n_params = 2; | ||||
| 	spa_list_init(&endpoint->stream_list); | ||||
| 
 | ||||
| 	pw_log_debug(NAME" %p: new endpoint %p for v4l2 node %p", impl, endpoint, node); | ||||
| 	pw_log_info(NAME" %p: new endpoint %p %s for v4l2 node %p", impl, endpoint, endpoint->info.name, node); | ||||
| 	pw_proxy_add_listener(proxy, | ||||
| 			&endpoint->proxy_listener, | ||||
| 			&proxy_events, endpoint); | ||||
|  | @ -489,6 +502,7 @@ static int setup_v4l2_endpoint(struct device *device) | |||
| 	struct sm_node *n; | ||||
| 	struct sm_device *d = device->device; | ||||
| 
 | ||||
| 	pw_log_info(NAME " %s. ", __FUNCTION__); | ||||
| 	pw_log_debug(NAME" %p: device %p setup", impl, d); | ||||
| 
 | ||||
| 	spa_list_for_each(n, &d->node_list, link) { | ||||
|  | @ -500,6 +514,7 @@ static int setup_v4l2_endpoint(struct device *device) | |||
| 		node->device = device; | ||||
| 		node->node = n; | ||||
| 		node->impl = impl; | ||||
| 		pw_log_info(NAME " %s. Invoking create_endpoint", __FUNCTION__); | ||||
| 		node->endpoint = create_endpoint(node); | ||||
| 		if (node->endpoint == NULL) | ||||
| 			return -errno; | ||||
|  | @ -509,6 +524,8 @@ static int setup_v4l2_endpoint(struct device *device) | |||
| 
 | ||||
| static int activate_device(struct device *device) | ||||
| { | ||||
| 	pw_log_info(NAME " In %s. Activating....", __FUNCTION__); | ||||
| 	pw_log_info(NAME " In %s. Invoking setup_libcamera_endpoint", __FUNCTION__); | ||||
| 	return setup_v4l2_endpoint(device); | ||||
| } | ||||
| 
 | ||||
|  | @ -517,6 +534,7 @@ static int deactivate_device(struct device *device) | |||
| 	struct impl *impl = device->impl; | ||||
| 	struct endpoint *e; | ||||
| 
 | ||||
| 	pw_log_info(NAME " %s. ", __FUNCTION__); | ||||
| 	pw_log_debug(NAME" %p: device %p deactivate", impl, device->device); | ||||
| 	spa_list_consume(e, &device->endpoint_list, link) | ||||
| 		destroy_endpoint(impl, e); | ||||
|  | @ -529,7 +547,8 @@ static void device_update(void *data) | |||
| 	struct device *device = data; | ||||
| 	struct impl *impl = device->impl; | ||||
| 
 | ||||
| 	pw_log_debug(NAME" %p: device %p %08x %08x", impl, device, | ||||
| 	pw_log_info(NAME " %s. ", __FUNCTION__); | ||||
| 	pw_log_info(NAME" %p: device %p %08x %08x", impl, device, | ||||
| 			device->device->obj.avail, device->device->obj.changed); | ||||
| 
 | ||||
| 	if (!SPA_FLAG_IS_SET(device->device->obj.avail, | ||||
|  | @ -554,13 +573,14 @@ handle_device(struct impl *impl, struct sm_object *obj) | |||
| 	const char *media_class, *str; | ||||
| 	struct device *device; | ||||
| 
 | ||||
| 	pw_log_info(NAME " In %s. ", __FUNCTION__); | ||||
| 	if (obj->props == NULL) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	media_class = pw_properties_get(obj->props, PW_KEY_MEDIA_CLASS); | ||||
| 	str = pw_properties_get(obj->props, PW_KEY_DEVICE_API); | ||||
| 
 | ||||
| 	pw_log_debug(NAME" %p: device "PW_KEY_MEDIA_CLASS":%s api:%s", impl, media_class, str); | ||||
| 	pw_log_info(NAME" %p: device "PW_KEY_MEDIA_CLASS":%s api:%s", impl, media_class, str); | ||||
| 
 | ||||
| 	if (strstr(media_class, "Video/") != media_class) | ||||
| 		return 0; | ||||
|  | @ -572,7 +592,7 @@ handle_device(struct impl *impl, struct sm_object *obj) | |||
| 	device->id = obj->id; | ||||
| 	device->device = (struct sm_device*)obj; | ||||
| 	spa_list_init(&device->endpoint_list); | ||||
| 	pw_log_debug(NAME" %p: found v4l2 device %d media_class %s", impl, obj->id, media_class); | ||||
| 	pw_log_info(NAME" %p: found v4l2 device %d media_class %s", impl, obj->id, media_class); | ||||
| 
 | ||||
| 	sm_object_add_listener(obj, &device->listener, &device_events, device); | ||||
| 
 | ||||
|  | @ -581,6 +601,7 @@ handle_device(struct impl *impl, struct sm_object *obj) | |||
| 
 | ||||
| static void destroy_device(struct impl *impl, struct device *device) | ||||
| { | ||||
| 	pw_log_info(NAME " In %s. Deactivating...", __FUNCTION__); | ||||
| 	deactivate_device(device); | ||||
| 	spa_hook_remove(&device->listener); | ||||
| 	sm_object_remove_data((struct sm_object*)device->device, SESSION_KEY); | ||||
|  | @ -591,6 +612,7 @@ static void session_create(void *data, struct sm_object *object) | |||
| 	struct impl *impl = data; | ||||
| 	int res; | ||||
| 
 | ||||
| 	pw_log_info(NAME " In %s. comparing object->type[%s] with %s", __FUNCTION__, object->type, PW_TYPE_INTERFACE_Device); | ||||
| 	if (strcmp(object->type, PW_TYPE_INTERFACE_Device) == 0) | ||||
| 		res = handle_device(impl, object); | ||||
| 	else | ||||
|  | @ -606,6 +628,7 @@ static void session_remove(void *data, struct sm_object *object) | |||
| { | ||||
| 	struct impl *impl = data; | ||||
| 
 | ||||
| 	pw_log_info(NAME " In %s. ", __FUNCTION__); | ||||
| 	if (strcmp(object->type, PW_TYPE_INTERFACE_Device) == 0) { | ||||
| 		struct device *device; | ||||
| 		if ((device = sm_object_get_data(object, SESSION_KEY)) != NULL) | ||||
|  | @ -631,11 +654,13 @@ int sm_v4l2_endpoint_start(struct sm_media_session *session) | |||
| { | ||||
| 	struct impl *impl; | ||||
| 
 | ||||
| 	pw_log_info(NAME " In %s. ", __FUNCTION__); | ||||
| 	impl = calloc(1, sizeof(struct impl)); | ||||
| 	if (impl == NULL) | ||||
| 		return -errno; | ||||
| 
 | ||||
| 	impl->session = session; | ||||
| 	pw_log_info(NAME " In %s. Invoking sm_media_session_add_listener", __FUNCTION__); | ||||
| 	sm_media_session_add_listener(session, &impl->listener, &session_events, impl); | ||||
| 
 | ||||
| 	return 0; | ||||
|  |  | |||
|  | @ -65,6 +65,7 @@ if alsa_dep.found() | |||
|     'media-session/policy-node.c', | ||||
|     'media-session/v4l2-monitor.c', | ||||
|     'media-session/v4l2-endpoint.c', | ||||
|     'media-session/libcamera-monitor.c', | ||||
|     'media-session/suspend-node.c', | ||||
|     c_args : [ '-D_GNU_SOURCE' ], | ||||
|     install: true, | ||||
|  | @ -99,6 +100,7 @@ if sdl_dep.found() | |||
|     install: false, | ||||
|     dependencies : [pipewire_dep, sdl_dep], | ||||
|   ) | ||||
| 
 | ||||
|   executable('export-sink', | ||||
|     'export-sink.c', | ||||
|     c_args : [ '-D_GNU_SOURCE' ], | ||||
|  |  | |||
|  | @ -239,7 +239,7 @@ int pw_buffers_negotiate(struct pw_context *context, uint32_t flags, | |||
| 	struct spa_pod **params, *param; | ||||
| 	uint8_t buffer[4096]; | ||||
| 	struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); | ||||
| 	uint32_t i, offset, n_params; | ||||
| 	uint32_t i, offset, n_params, n_datas = 1; | ||||
| 	uint32_t max_buffers; | ||||
| 	size_t minsize, stride, align; | ||||
| 	uint32_t data_sizes[1]; | ||||
|  | @ -288,7 +288,7 @@ int pw_buffers_negotiate(struct pw_context *context, uint32_t flags, | |||
| 	if (param) { | ||||
| 		uint32_t qmax_buffers = max_buffers, | ||||
| 		    qminsize = minsize, qstride = stride, qalign = align; | ||||
| 		uint32_t qtypes = types; | ||||
| 		uint32_t qtypes = types, qn_datas = n_datas; | ||||
| 
 | ||||
| 		spa_pod_parse_object(param, | ||||
| 			SPA_TYPE_OBJECT_ParamBuffers, NULL, | ||||
|  | @ -296,7 +296,8 @@ int pw_buffers_negotiate(struct pw_context *context, uint32_t flags, | |||
| 			SPA_PARAM_BUFFERS_size,     SPA_POD_OPT_Int(&qminsize), | ||||
| 			SPA_PARAM_BUFFERS_stride,   SPA_POD_OPT_Int(&qstride), | ||||
| 			SPA_PARAM_BUFFERS_align,    SPA_POD_OPT_Int(&qalign), | ||||
| 			SPA_PARAM_BUFFERS_dataType, SPA_POD_OPT_Int(&qtypes)); | ||||
| 			SPA_PARAM_BUFFERS_dataType, SPA_POD_OPT_Int(&qtypes), | ||||
| 			SPA_PARAM_BUFFERS_datas,	SPA_POD_OPT_Int(&qn_datas)); | ||||
| 
 | ||||
| 		max_buffers = | ||||
| 		    qmax_buffers == 0 ? max_buffers : SPA_MIN(qmax_buffers, | ||||
|  | @ -305,6 +306,7 @@ int pw_buffers_negotiate(struct pw_context *context, uint32_t flags, | |||
| 		stride = SPA_MAX(stride, qstride); | ||||
| 		align = SPA_MAX(align, qalign); | ||||
| 		types = qtypes; | ||||
| 		n_datas = SPA_MAX(n_datas, qn_datas); | ||||
| 
 | ||||
| 		pw_log_debug(NAME" %p: %d %d %d %d %d -> %zd %zd %d %zd %d", result, | ||||
| 				qminsize, qstride, qmax_buffers, qalign, qtypes, | ||||
|  | @ -327,7 +329,7 @@ int pw_buffers_negotiate(struct pw_context *context, uint32_t flags, | |||
| 				 max_buffers, | ||||
| 				 n_params, | ||||
| 				 params, | ||||
| 				 1, | ||||
| 				 n_datas, | ||||
| 				 data_sizes, data_strides, | ||||
| 				 data_aligns, data_types, | ||||
| 				 flags, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 raghu447
						raghu447