Example #1
0
  /* Handle various savings of a jpeg associated with a video recording.
  |  For motion records: save a copy of the mjpeg.jpg for later processing
  |  with on_motion_preview_save_cmd.  If mode is "best", this copy may be
  |  written multiple times.  This copy will be disposed of.
  */
void
event_preview_save(void)
	{
	FILE	*f_src, *f_dst;
	char	*s, *base, *path, buf[BUFSIZ];
	int		n;

	path = strdup(pikrellcam.video_pathname);
	if (   (s = strstr(path, ".mp4")) != NULL
	    || (s = strstr(path, ".h264")) != NULL
	   )
		{
		strcpy(s, ".jpg");

		/* Copy the current mjpeg.jpg into a motion preview file.
		*/
		if ((f_src = fopen(pikrellcam.mjpeg_filename, "r")) != NULL)
			{
			base = fname_base(path);
			if (pikrellcam.preview_filename)
				free(pikrellcam.preview_filename);
			asprintf(&pikrellcam.preview_filename, "%s/%s",
							pikrellcam.tmpfs_dir, base);

			log_printf("event preview save: copy %s -> %s\n",
								pikrellcam.mjpeg_filename,
								pikrellcam.preview_filename);
			if ((f_dst = fopen(pikrellcam.preview_filename, "w")) != NULL)
				{
				while ((n = fread(buf, 1, sizeof(buf), f_src)) > 0)
					{
					if (fwrite(buf, 1, n, f_dst) != n)
						break;
					}
				fclose(f_dst);
				}
			fclose(f_src);
			}
		}
	free(path);

	/* Let mmalcam.c mjpeg_callback() continue renaming the mjpeg file.
	*/
	pikrellcam.mjpeg_rename_holdoff = FALSE;
	}
Example #2
0
  /* In pikrellcam, this callback receives resized I420 frames before
  |  sending them on to a jpeg encoder component which generates the
  |  mjpeg.jpg stream image.  Here we send the frame data to the motion display
  |  routine for possible drawing of region outlines, motion vectors
  |  and/or various status text.  Motion detection was done in the h264
  |  callback and a flag is set there so these two paths can be synchronized
  |  so motion vectors can be drawn on the right frame.
  */
void
I420_video_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
	{
	CameraObject          *obj = (CameraObject *) port->userdata;
	MMAL_BUFFER_HEADER_T  *buffer_in;
	static struct timeval timer;
	int                   utime;
	static int            encoder_busy_count;

	if (   buffer->length > 0
	    && motion_frame_event
	   )
		{
		motion_frame_event = FALSE;

		/* Do not send buffer to encoder if it has not received the previous
		|  one we sent unless this is the frame we want for a preview save.
		|  In that case, we may be sending a buffer to preview save before
		|  the previous buffer is handled.  This is accounted for below.
		*/
		if (   mjpeg_encoder_send_count == mjpeg_encoder_recv_count
		    || motion_frame.do_preview_save
		   )
			{
			if (obj->callback_port_in && obj->callback_pool_in)
				{
				buffer_in = mmal_queue_get(obj->callback_pool_in->queue);
				if (   buffer_in
				    && obj->callback_port_in->buffer_size >= buffer->length
				   )
					{
					mmal_buffer_header_mem_lock(buffer);
					memcpy(buffer_in->data, buffer->data, buffer->length);
					buffer_in->length = buffer->length;
					mmal_buffer_header_mem_unlock(buffer);
					display_draw(buffer_in->data);

					if (motion_frame.do_preview_save)
						{
						/* If mjpeg encoder has not received previous buffer,
						|  then the buffer to save will be the second buffer
						|  it gets from now. Otherwise it's the next buffer.
						*/
						pthread_mutex_lock(&mjpeg_encoder_count_lock);
						if (mjpeg_encoder_send_count == mjpeg_encoder_recv_count)
							mjpeg_do_preview_save = 1;
						else
							mjpeg_do_preview_save = 2;
						pthread_mutex_unlock(&mjpeg_encoder_count_lock);
						if (mjpeg_do_preview_save == 2 && pikrellcam.debug)
							printf("%s: encoder not clear -> preview save delayed\n",
								fname_base(pikrellcam.video_pathname));
						}
					motion_frame.do_preview_save = FALSE;
					++mjpeg_encoder_send_count;
					mmal_port_send_buffer(obj->callback_port_in, buffer_in);
					}
				}
			}
		else
			{
			++encoder_busy_count;
			if (pikrellcam.debug)
				printf("encoder not clear (%d) -> skipping mjpeg frame.\n",
					   encoder_busy_count);
			if (encoder_busy_count > 2)	/* Frame maybe dropped ??, move on */
				{
				if (pikrellcam.debug)
					printf("  Syncing recv/send counts.\n");
				encoder_busy_count = 0;
				mjpeg_encoder_recv_count = mjpeg_encoder_send_count;
				}
			}
		if (buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END)
			{
			if (pikrellcam.debug_fps && (utime = micro_elapsed_time(&timer)) > 0)
				printf("%s fps %d\n", obj->name, 1000000 / utime);
			}
		}
	return_buffer_to_port(port, buffer);
	}
Example #3
0
  /* If video_fps is too high and strains GPU, resized frames to this
  |  callback may be dropped.  Set debug_fps to 1 to check things... or
  |  just watch the web mjpeg stream and see it slow down.
  */
void
mjpeg_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
	{
	CameraObject           *data = (CameraObject *) port->userdata;
	static struct timeval  timer;
	int                    n, utime;
	static FILE            *file	= NULL;
	static char            *fname_part;
	boolean                do_preview_save = FALSE;
	static char	           *tcp_buf;
	static int	           tcp_buf_offset;


	if (!fname_part)
		asprintf(&fname_part, "%s.part", pikrellcam.mjpeg_filename);

	if (!tcp_buf)
		tcp_buf = mjpeg_server_queue_get();

	if (file && buffer->length > 0)
		{
		mmal_buffer_header_mem_lock(buffer);
		n = fwrite(buffer->data, 1, buffer->length, file);
		if (tcp_buf)
			{
			memcpy(tcp_buf + tcp_buf_offset, buffer->data, buffer->length);
			tcp_buf_offset += buffer->length;
			}
		mmal_buffer_header_mem_unlock(buffer);
		if (n != buffer->length)
			{
			log_printf("mjpeg_callback: %s file write error.  %m\n", data->name);
			exit(1);
			}
		}
	if (buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END)
		{
		if (tcp_buf)
			{
			mjpeg_server_queue_put(tcp_buf, tcp_buf_offset);
			tcp_buf = NULL;
			tcp_buf_offset = 0;
			}

		if (pikrellcam.debug_fps && (utime = micro_elapsed_time(&timer)) > 0)
			printf("%s fps %d\n", data->name, 1000000 / utime);
		if (file)
			{
			fclose(file);
			file = NULL;

			pthread_mutex_lock(&mjpeg_encoder_count_lock);
			++mjpeg_encoder_recv_count;
			if (mjpeg_do_preview_save == 1)
				{
				mjpeg_do_preview_save = 0;
				do_preview_save = TRUE;
				}
			else if (mjpeg_do_preview_save > 1)
				--mjpeg_do_preview_save;
			pthread_mutex_unlock(&mjpeg_encoder_count_lock);

			/* When adding an event_preview_save, set a rename holdoff that
			|  will be reset when the preview_save is done.  Don't do
			|  any renames until the holdoff reset to ensure the correct
			|  mjpeg image is saved into the preview.  Otherwise there is
			|  a race condition.  Could just directly run the preview save
			|  function here, but we are inside a GPU callback and I don't
			|  want to overload the time spent here.
			*/
			if (!pikrellcam.mjpeg_rename_holdoff)
				rename(fname_part, pikrellcam.mjpeg_filename);
			else if (pikrellcam.debug)
				printf("%s: holdoff not clear -> rename skipped\n",
						fname_base(pikrellcam.video_pathname));
			if (do_preview_save)
				{
				pikrellcam.mjpeg_rename_holdoff = TRUE;
				event_add("motion preview save", pikrellcam.t_now, 0,
						event_preview_save, NULL);
				if (motion_frame.do_preview_save_cmd)
					{
					event_add("motion area thumb", pikrellcam.t_now, 0,
							event_motion_area_thumb, NULL);
					event_add("preview save command", pikrellcam.t_now, 0,
							event_preview_save_cmd,
							pikrellcam.on_motion_preview_save_cmd);
					}
				}
			motion_frame.do_preview_save_cmd = FALSE;
			}
		file = fopen(fname_part, "w");
		if (!file)
			log_printf("mjpeg_callback: could not open %s file. %m", fname_part);
		}
	return_buffer_to_port(port, buffer);
	}