예제 #1
0
void
camera_restart(void)
	{
	VideoCircularBuffer	*vcb = &video_circular_buffer;

	pthread_mutex_lock(&vcb->mutex);
	video_record_stop(vcb);
	vcb->state = VCB_STATE_RESTARTING;
	pikrellcam.camera_adjust = camera_adjust_temp;	/* May not be changed */
	pthread_mutex_unlock(&vcb->mutex);

	camera_stop();
	camera_start();
	}
예제 #2
0
void
command_process(char *command_line)
	{
	VideoCircularBuffer	*vcb = &video_circular_buffer;
	Command	*cmd;
	char	command[64], args[128], buf[32], *path;
	int		i, n;

	if (!command_line || *command_line == '\0')
		return;

	n = sscanf(command_line, "%63s %[^\n]", command, args);
	if (n < 1 || command[0] == '#')
		return;
	for (cmd = NULL, i = 0; i < COMMAND_SIZE; cmd = NULL, ++i)
		{
		cmd = &commands[i];
		if (!strcmp(command, cmd->name))
			{
			if (cmd->n_args != n - 1)
				{
				log_printf("Wrong number of args for command: %s\n", command);
				return;
				}
			break;
			}
		}
	if (!cmd)
		{
		if (   !config_set_option(command, args, FALSE)
		    && !mmalcam_config_parameter_set(command, args, TRUE)
	       )
			log_printf("Bad command: [%s] [%s]\n", command, args);
		return;
		}
	if (cmd->code != display_cmd)
		log_printf("command_process: %s\n", command_line);

	if (cmd->code < display_cmd && !display_is_default())
		{
		display_set_default();
		return;
		}

	switch (cmd->code)
		{
		case record:
			pthread_mutex_lock(&vcb->mutex);
			if (config_boolean_value(args) == TRUE)
				{
				if (vcb->pause)
					vcb->pause = FALSE;
				else
					{
					if (vcb->state == VCB_STATE_MOTION_RECORD)
						video_record_stop(vcb);
					video_record_start(vcb, VCB_STATE_MANUAL_RECORD_START);
					}
				}
			else
				video_record_stop(vcb);
			pthread_mutex_unlock(&vcb->mutex);
			break;

		case record_pause:
			/* Can pause manual record only.  Because of event_gap/capture
			|  times, I'm not even sure what it would mean to pause a
			|  motion record.
			*/
			pthread_mutex_lock(&vcb->mutex);
			if (vcb->state == VCB_STATE_MANUAL_RECORD)
				vcb->pause = vcb->pause ? FALSE : TRUE;
			else
				vcb->pause = FALSE;
			pthread_mutex_unlock(&vcb->mutex);
			break;

		case still:
			snprintf(buf, sizeof(buf), "%d", pikrellcam.still_sequence);
			path = media_pathname(pikrellcam.still_dir, pikrellcam.still_filename,
							'N',  buf,
							'\0', NULL);
			pikrellcam.still_sequence += 1;
			still_capture(path);
			free(path);
			break;

		case tl_start:
			if ((n = atoi(args)) < 1)
				n = 0;
			time_lapse.activated = TRUE;
			time_lapse.on_hold = FALSE;
			if (!time_lapse.event && n > 0)
				{
				time_lapse.sequence = 0;
				++time_lapse.series;
				time_lapse.event = event_add("timelapse",
							pikrellcam.t_now, n, timelapse_capture, NULL);
				}
			else if (n > 0)		/* Change the period */
				{
				time_lapse.event->time += (n - time_lapse.period);
				time_lapse.event->period = n;
				}
			if (n > 0)
				time_lapse.period = n;	/* n == 0 just sets on_hold FALSE */
			config_timelapse_save_status();	
			break;

		case tl_hold:
				config_set_boolean(&time_lapse.on_hold, args);
				config_timelapse_save_status();
			break;

		case tl_end:
			if (time_lapse.activated)
				{
				event_remove(time_lapse.event);
				time_lapse.event = NULL;
				time_lapse.activated = FALSE;
				time_lapse.on_hold = FALSE;
				config_timelapse_save_status();
				exec_no_wait(pikrellcam.on_timelapse_end_cmd, NULL);
				}
			break;

		case tl_inform_convert:
			if (!strcmp(args, "done"))
				{
				event_remove(time_lapse.inform_event);
				dup_string(&time_lapse.convert_name, "");
				time_lapse.convert_size = 0;
				}
			else
				{
				dup_string(&time_lapse.convert_name, args);
				time_lapse.inform_event = event_add("tl_inform_convert",
						pikrellcam.t_now, 5, timelapse_inform_convert, NULL);
				}
			break;

		case tl_show_status:
			config_set_boolean(&time_lapse.show_status, args);
			break;

		case display_cmd:
			display_command(args);
			break;

		case motion_cmd:
			motion_command(args);
			break;

		case motion_enable:
			n = motion_frame.motion_enable;
			config_set_boolean(&motion_frame.motion_enable, args);

			if (n && !motion_frame.motion_enable)
				{
				pthread_mutex_lock(&vcb->mutex);
				if (vcb->state == VCB_STATE_MOTION_RECORD)
					video_record_stop(vcb);
				pthread_mutex_unlock(&vcb->mutex);
				}
			break;

		case save_config:
			config_save(pikrellcam.config_file);
			break;

		case quit:
			config_timelapse_save_status();
			if (pikrellcam.config_modified)
				config_save(pikrellcam.config_file);
			display_quit();
			exit(0);
			break;

		default:
			log_printf("command in table with no action!\n");
			break;
		}
	}
예제 #3
0
파일: mmalcam.c 프로젝트: billw2/pikrellcam
void
video_h264_encoder_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *mmalbuf)
	{
	VideoCircularBuffer *vcb = &video_circular_buffer;
	MotionFrame		*mf = &motion_frame;
	KeyFrame		*kf;
	int				i, end_space, t_elapsed, event = 0;
	int				t_usec, dt_frame;
	boolean			force_stop;
	time_t			t_cur = pikrellcam.t_now;
	static int		fps_count, pause_frame_count_adjust;
	static time_t	t_sec, t_prev;
	uint64_t		t64_now;
	static int		t_annotate, t_key_frame;
	static struct timeval	tv;
	static uint64_t	t0_stc, pts_prev;
	static boolean	prev_pause;

	if (vcb->state == VCB_STATE_RESTARTING)
		{
		if (mmalbuf->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG)
			h264_header_save(mmalbuf);
		fps_count = 0;
		return_buffer_to_port(port, mmalbuf);
		return;
		}

	if (mmalbuf->pts > 0)
		{
		if (pikrellcam.t_now > tv.tv_sec + 10)
			{	/* Rarely, but time skew can change if ntp updates. */
			gettimeofday(&tv, NULL);
			mmal_port_parameter_get_uint64(port, MMAL_PARAMETER_SYSTEM_TIME, &t0_stc);
			t0_stc = (uint64_t) tv.tv_sec * 1000000LL + (uint64_t) tv.tv_usec - t0_stc;
			}

		/* Skew adjust to the system clock to get second transitions.
		|  Annotate times need to be set early to get displayed time synced
		|  with system time.  Needs >2 frames + offset to get it to work over
		|  range of fps values.
		|  Key frames can be delivered one frame after a request, so request
		|  within 1 1/2 frames before second time transitions.
		*/
		t64_now = t0_stc + mmalbuf->pts;
		t_sec = (int) (t64_now / 1000000LL);
		t_usec = (int) (t64_now % 1000000LL);
		dt_frame = 1000000 / pikrellcam.camera_adjust.video_fps;

		if (   t_annotate < t_sec
		    && t_usec > 900000 - 5 * dt_frame / 2
		   )
			{
			t_annotate = t_sec;
			annotate_text_update(t_annotate + 1);
			}

		if (   (vcb->state == VCB_STATE_NONE || vcb->pause)
		    && t_key_frame < t_sec
		    && t_usec > 1000000 - 3 * dt_frame / 2
		   )
			{
			t_key_frame = t_sec;
			mmal_port_parameter_set_boolean(port,
						MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, 1);
			}
		if (   (mmalbuf->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME)
		         && t_cur < t_sec
		        )
			t_cur = t_sec;
		}

	pthread_mutex_lock(&vcb->mutex);
	
	if (mmalbuf->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG)
		h264_header_save(mmalbuf);
	else if (mmalbuf->flags & MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO)
		{
		if (++fps_count >= pikrellcam.mjpeg_divider)
			{
			motion_frame_event = TRUE;		/* synchronize with i420 callback */
			fps_count = 0;
			mmal_buffer_header_mem_lock(mmalbuf);
			memcpy(motion_frame.vectors, mmalbuf->data, motion_frame.vectors_size);
			mmal_buffer_header_mem_unlock(mmalbuf);
			motion_frame_process(vcb, &motion_frame);
			}
		}
	else
		{
		if (  (mmalbuf->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME)
		    && !vcb->in_keyframe
		   )
			{
			/* Keep key_frame[cur_frame_index] always pointing to the latest
			|  keyframe (this one) in the video buffer.  Then adjust the
			|  key_frame[pre_frame_index] to point to a keyframe
			|  in the video buffer that is pre_capture time behind.
			|  If paused, always keep tail pointing to the latest keyframe.
			*/
			vcb->in_keyframe = TRUE;
			vcb->cur_frame_index = (vcb->cur_frame_index + 1) % KEYFRAME_SIZE;
			kf = &vcb->key_frame[vcb->cur_frame_index];
			kf->position = vcb->head;
			kf->frame_count = 0;
			pause_frame_count_adjust = 0;
			if (vcb->pause && vcb->state == VCB_STATE_MANUAL_RECORD)
				{
				vcb->tail = vcb->head;
				pts_prev = mmalbuf->pts;
				pause_frame_count_adjust = 0;
				}
			kf->t_frame = t_cur;
			kf->frame_pts = mmalbuf->pts;
			while (t_cur - vcb->key_frame[vcb->pre_frame_index].t_frame
						 > pikrellcam.motion_times.pre_capture)
				{
				vcb->pre_frame_index = (vcb->pre_frame_index + 1) % KEYFRAME_SIZE;
				if (vcb->pre_frame_index == vcb->cur_frame_index)
					break;
				}
			}
		if (mmalbuf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END)
			{
			vcb->in_keyframe = FALSE;
			i = vcb->pre_frame_index; 
			while (1)
				{
				vcb->key_frame[i].frame_count += 1;
				if (i++ == vcb->cur_frame_index)
					break;
				i %= KEYFRAME_SIZE;
				}
			if (   vcb->state == VCB_STATE_MOTION_RECORD
			    || vcb->state == VCB_STATE_MANUAL_RECORD
			   )
				{
				if (!vcb->pause)
					vcb->frame_count += 1;
				else
					pause_frame_count_adjust += 1;
				}
			}

		if (t_cur > t_prev)
			{
			if (!vcb->pause)
				++vcb->record_elapsed_time;
			t_prev = t_cur;
			}

		if (vcb->state == VCB_STATE_MOTION_RECORD_START)
			{
			/* Write mp4 header and set tail to beginning of pre_capture
			|  video data, then write the entire pre_capture time data.
			|  The keyframe data we collected above keeps a pointer to
			|  video data close to the pre_capture time we want.
			*/
			fwrite(vcb->h264_header, 1, vcb->h264_header_position, vcb->file);
			pikrellcam.video_header_size = vcb->h264_header_position;
			pikrellcam.video_size = vcb->h264_header_position;

			vcb->tail = vcb->key_frame[vcb->record_start_frame_index].position;
			vcb_video_write(vcb);
			vcb->frame_count = vcb->key_frame[vcb->record_start_frame_index].frame_count;
			vcb->video_frame_count = vcb->frame_count;
			motion_event_write(vcb, mf);
			vcb->state = VCB_STATE_MOTION_RECORD;
			if (mf->external_trigger_time_limit > 0)
				{
				vcb->motion_sync_time = t_cur + mf->external_trigger_time_limit;
				vcb->max_record_time = mf->external_trigger_time_limit;
				}
			else
				{
				vcb->motion_sync_time = t_cur + pikrellcam.motion_times.post_capture;
				vcb->max_record_time = pikrellcam.motion_record_time_limit;
				}

			/* Schedule any motion begin command.
			*/
			event |= EVENT_MOTION_BEGIN;
			}

		if (vcb->state == VCB_STATE_MANUAL_RECORD_START)
			{
			/* Write mp4 header and set tail to most recent keyframe.
			|  So manual records may have up to about a sec pre_capture.
			*/
			fwrite(vcb->h264_header, 1, vcb->h264_header_position, vcb->file);
			pikrellcam.video_header_size = vcb->h264_header_position;
			pikrellcam.video_size = vcb->h264_header_position;

			vcb->tail = vcb->key_frame[vcb->record_start_frame_index].position;

			vcb_video_write(vcb);
			vcb->frame_count = vcb->key_frame[vcb->record_start_frame_index].frame_count;
			pts_prev = 0;
			vcb->state = VCB_STATE_MANUAL_RECORD;
			event |= EVENT_PREVIEW_SAVE;
			}

		if (h264_conn_status == H264_TCP_SEND_HEADER) 
			tcp_send_h264_header(vcb->h264_header, vcb->h264_header_position);

		/* Save video data into the circular buffer.
		*/
		mmal_buffer_header_mem_lock(mmalbuf);
		end_space = vcb->size - vcb->head;
		if (mmalbuf->length <= end_space)
			{
			memcpy(vcb->data + vcb->head, mmalbuf->data, mmalbuf->length);
			if(h264_conn_status == H264_TCP_SEND_DATA) 
				tcp_send_h264_data("data 1",vcb->data + vcb->head, mmalbuf->length);
			}
		else
			{
			memcpy(vcb->data + vcb->head, mmalbuf->data, end_space);
			memcpy(vcb->data, mmalbuf->data + end_space, mmalbuf->length - end_space);
			if (h264_conn_status == H264_TCP_SEND_DATA) 
      			{
				tcp_send_h264_data("data 2",vcb->data + vcb->head, end_space);
				tcp_send_h264_data("data 3",vcb->data, mmalbuf->length - end_space);
				}
			}
		vcb->head = (vcb->head + mmalbuf->length) % vcb->size;
		mmal_buffer_header_mem_unlock(mmalbuf);

		/* And write video data to a video file according to record state.
		|  Record time limit (if any) does not include pre capture times or
		|  manual paused time which is accounted for in record_elapsed_time.
		*/
		force_stop = FALSE;
		if (vcb->max_record_time > 0)
			{
			t_elapsed = vcb->record_elapsed_time;
			if (vcb->state == VCB_STATE_MOTION_RECORD)
				t_elapsed -= (mf->external_trigger_pre_capture > 0) ?
							  mf->external_trigger_pre_capture
							: pikrellcam.motion_times.pre_capture;
			else
				t_elapsed -= vcb->manual_pre_capture;

			if (t_elapsed >= vcb->max_record_time)
				force_stop = TRUE;
			}

		if (vcb->state == VCB_STATE_MANUAL_RECORD)
			{
			if (!vcb->pause)
				{
				if (mmalbuf->pts > 0)
					{
					if (pts_prev > 0)
						vcb->last_pts += mmalbuf->pts - pts_prev;
					else
						vcb->last_pts = mmalbuf->pts;
					pts_prev = mmalbuf->pts;
					}
				if (prev_pause)
					{
					vcb->frame_count += pause_frame_count_adjust;
					pause_frame_count_adjust = 0;
					}
				vcb->video_frame_count = vcb->frame_count;
				vcb_video_write(vcb);	/* Continuously write video data */
				}
			prev_pause = vcb->pause;
	
			if (force_stop)
				video_record_stop(vcb);
			}
		else if (vcb->state == VCB_STATE_MOTION_RECORD)
			{
			/* Always write until we reach motion_sync time (which is last
			|  motion detect time + post_capture time), then hold during
			|  event_gap time.  Motion events during event_gap time will bump
			|  motion_sync_time and event_gap expiration time higher thus
			|  triggering more writes up to the new sync_time.
			|  If there is not another motion event, event_gap time will be
			|  reached and we stop recording with the post_capture time
			|  already written.
			*/
			if (t_cur <= vcb->motion_sync_time)
				{
				if (mmalbuf->pts > 0)
					vcb->last_pts = mmalbuf->pts;
				vcb->video_frame_count = vcb->frame_count;
				vcb_video_write(vcb);
				}
			if (   force_stop
		        || (   mf->external_trigger_time_limit == 0
			        && t_cur >= vcb->motion_last_detect_time + pikrellcam.motion_times.event_gap
			       )
		       )
				{
				/* For motion recording, preview_save_mode "first" has been
				|  handled in motion_frame_process().  But if not "first",
				|  there is a preview save waiting to be handled.
				*/
				video_record_stop(vcb);
				event |= EVENT_MOTION_END;
				if (strcmp(pikrellcam.motion_preview_save_mode, "first") != 0)
					event |= EVENT_MOTION_PREVIEW_SAVE_CMD;
				}
			}
		}
	pthread_mutex_unlock(&vcb->mutex);
	return_buffer_to_port(port, mmalbuf);

	/* This handles preview saves for manual records for possible future use.
	|  preview_save_cmd does not apply for manual records.
	|  All preview saves for motion records are scheduled in motion_frame_process().
	|  preview_save_cmd for save mode "best" is handled in video_record_stop().
	*/
	if (event & EVENT_PREVIEW_SAVE)
		event_add("manual preview save", pikrellcam.t_now, 0,
					event_preview_save, NULL);

	if (   (event & EVENT_MOTION_BEGIN)
	    && *pikrellcam.on_motion_begin_cmd != '\0'
	   )
		event_add("motion begin", pikrellcam.t_now, 0,
					exec_no_wait, pikrellcam.on_motion_begin_cmd);
	/* motion end event and preview dispose are handled in video_record_stop()
	*/
	}