diff mbox series

[v4,2/2] cec-follower: emulate programmed timer recordings

Message ID 9ae9d7f22ba673216785532b544bf02348019c47.1626155911.git.deborahbrouwer3563@gmail.com (mailing list archive)
State New, archived
Headers show
Series cec: expand Timer Programming tests | expand

Commit Message

Deborah Brouwer July 13, 2021, 6:09 a.m. UTC
Start and stop recording as timers are scheduled. Schedule future timers
if a completed timer has a recording sequence. Delete overlapped and
unfinished timers. Reduce available media space when a recording is
completed.

Signed-off-by: Deborah Brouwer <deborahbrouwer3563@gmail.com>
---
 utils/cec-follower/cec-follower.cpp   |  5 +++
 utils/cec-follower/cec-follower.h     |  1 +
 utils/cec-follower/cec-processing.cpp | 65 +++++++++++++++++++++++++++
 utils/cec-follower/cec-tuner.cpp      | 15 ++++++-
 4 files changed, 84 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/utils/cec-follower/cec-follower.cpp b/utils/cec-follower/cec-follower.cpp
index b273b988..0adf6ce8 100644
--- a/utils/cec-follower/cec-follower.cpp
+++ b/utils/cec-follower/cec-follower.cpp
@@ -301,6 +301,10 @@  void print_timers(struct node *node)
 {
 	if (show_info) {
 		printf("Timers Set:\n");
+		if (node->state.recording_controlled_by_timer)
+			printf("Deck is currently recording from the first timer.\n");
+		if (node->state.one_touch_record_on && !node->state.recording_controlled_by_timer)
+			printf("Deck is currently recording independent of timers.\n");
 		for (auto &t : programmed_timers) {
 			std::string start = ctime(&t.start_time);
 			time_t end_time = t.start_time + t.duration;
@@ -373,6 +377,7 @@  void state_init(struct node &node)
 	node.state.one_touch_record_on = false;
 	node.state.record_received_standby = false;
 	node.state.media_space_available = 36000; /* In MB; space for 10 hours @ 1MB/sec */
+	node.state.recording_controlled_by_timer = false;
 	tuner_dev_info_init(&node.state);
 	node.state.last_aud_rate_rx_ts = 0;
 }
diff --git a/utils/cec-follower/cec-follower.h b/utils/cec-follower/cec-follower.h
index 69c96aa7..7b22368b 100644
--- a/utils/cec-follower/cec-follower.h
+++ b/utils/cec-follower/cec-follower.h
@@ -60,6 +60,7 @@  struct state {
 	bool one_touch_record_on;
 	bool record_received_standby;
 	int media_space_available;
+	bool recording_controlled_by_timer;
 	time_t toggle_power_status;
 	__u64 last_aud_rate_rx_ts;
 };
diff --git a/utils/cec-follower/cec-processing.cpp b/utils/cec-follower/cec-processing.cpp
index 32375966..566444a4 100644
--- a/utils/cec-follower/cec-processing.cpp
+++ b/utils/cec-follower/cec-processing.cpp
@@ -1164,6 +1164,71 @@  void testProcessing(struct node *node, bool wallclock)
 			node->state.deck_skip_start = 0;
 			update_deck_state(node, me, CEC_OP_DECK_INFO_PLAY);
 		}
+
+		if (!programmed_timers.empty()) {
+			std::set<struct Timer>::iterator it = programmed_timers.begin();
+			/* Use the current minute because timers do not have second precision. */
+			time_t current_minute = time(nullptr) / 60;
+			time_t timer_start_minute = it->start_time / 60;
+			time_t timer_end_minute = (it->start_time + it->duration) / 60;
+
+			/* Start the timed recording only if the deck is not already recording. */
+			if (timer_start_minute == current_minute && !node->state.one_touch_record_on) {
+				node->state.one_touch_record_on = true;
+				node->state.recording_controlled_by_timer = true;
+				print_timers(node);
+			}
+
+			/* Delete an overlapped timer. Recording will be at best incomplete. */
+			if (timer_start_minute < current_minute &&
+			    (!node->state.recording_controlled_by_timer || !node->state.one_touch_record_on)) {
+				programmed_timers.erase(*it);
+				if (show_info)
+					printf("Deleted overlapped timer.\n");
+				print_timers(node);
+			}
+
+			/* Delete finished timers. */
+			if (timer_end_minute == current_minute && node->state.recording_controlled_by_timer) {
+				node->state.one_touch_record_on = false;
+				node->state.recording_controlled_by_timer = false;
+				node->state.media_space_available -= it->duration; /* 1 MB per second */
+				/*
+				 * TODO: We are only ever decreasing the amount of space available,
+				 * there is no heuristic that reclaims the space.
+				 */
+
+				if (it->recording_seq) {
+					struct tm *last_start_time = localtime(&(it->start_time));
+					int next_wday = (last_start_time->tm_wday + 1) % 7;
+					int days_to_move_ahead = 1;
+
+					while ((it->recording_seq & (1 << next_wday)) == 0) {
+						days_to_move_ahead++;
+						next_wday = (next_wday + 1) % 7;
+					}
+					struct Timer next_timer = {};
+					next_timer = *it;
+					last_start_time->tm_mday += days_to_move_ahead;
+					last_start_time->tm_isdst = -1;
+					next_timer.start_time = mktime(last_start_time);
+					programmed_timers.insert(next_timer);
+				}
+				programmed_timers.erase(*it);
+				if (show_info)
+					printf("Deleted finished timer.\n");
+				print_timers(node);
+				/*
+				 * If the finished timer was recording, and standby was received during recording,
+				 * enter standby when the recording stops unless recording device is the active source.
+				 */
+				if (node->state.record_received_standby) {
+					if (node->phys_addr != node->state.active_source_pa)
+						enter_standby(node);
+					node->state.record_received_standby = false;
+				}
+			}
+		}
 	}
 	mode = CEC_MODE_INITIATOR;
 	doioctl(node, CEC_S_MODE, &mode);
diff --git a/utils/cec-follower/cec-tuner.cpp b/utils/cec-follower/cec-tuner.cpp
index e3e64a58..c3042e18 100644
--- a/utils/cec-follower/cec-tuner.cpp
+++ b/utils/cec-follower/cec-tuner.cpp
@@ -858,6 +858,15 @@  void process_tuner_record_timer_msgs(struct node *node, struct cec_msg &msg, uns
 		cec_msg_record_status(&msg, CEC_OP_RECORD_STATUS_TERMINATED_OK);
 		transmit(node, &msg);
 		node->state.one_touch_record_on = false;
+
+		/* Delete any currently active recording timer or it may restart itself in first minute. */
+		if (node->state.recording_controlled_by_timer) {
+			node->state.recording_controlled_by_timer = false;
+			programmed_timers.erase(programmed_timers.begin());
+			if (show_info)
+				printf("Deleted manually stopped timer.\n");
+			print_timers(node);
+		}
 		/*
 		 * If standby was received during recording, enter standby when the
 		 * recording is finished unless recording device is the active source.
@@ -957,9 +966,10 @@  void process_tuner_record_timer_msgs(struct node *node, struct cec_msg &msg, uns
 		auto it_previous_year = programmed_timers.find(timer_in_previous_year);
 
 		if (it_previous_year != programmed_timers.end()) {
-			if (node->state.one_touch_record_on && it_previous_year == programmed_timers.begin()) {
+			if (node->state.recording_controlled_by_timer && it_previous_year == programmed_timers.begin()) {
 				timer_cleared_status = CEC_OP_TIMER_CLR_STAT_RECORDING;
 				node->state.one_touch_record_on = false;
+				node->state.recording_controlled_by_timer = false;
 			} else {
 				timer_cleared_status = CEC_OP_TIMER_CLR_STAT_CLEARED;
 			}
@@ -972,9 +982,10 @@  void process_tuner_record_timer_msgs(struct node *node, struct cec_msg &msg, uns
 		auto it_current_year = programmed_timers.find(timer_in_current_year);
 
 		if (it_current_year != programmed_timers.end()) {
-			if (node->state.one_touch_record_on && it_current_year == programmed_timers.begin()) {
+			if (node->state.recording_controlled_by_timer && it_current_year == programmed_timers.begin()) {
 				timer_cleared_status = CEC_OP_TIMER_CLR_STAT_RECORDING;
 				node->state.one_touch_record_on = false;
+				node->state.recording_controlled_by_timer = false;
 			} else {
 				/* Do not overwrite status if already set. */
 				if (timer_cleared_status == CEC_OP_TIMER_CLR_STAT_NO_MATCHING)