aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDeborah Brouwer <deborahbrouwer3563@gmail.com>2021-07-12 23:09:20 -0700
committerHans Verkuil <hverkuil-cisco@xs4all.nl>2021-07-13 09:02:23 +0200
commitd7c8faa5e6b81151866514016276db002916fe0d (patch)
treee28e906fd52bc0d419f6f428905fea11fc56a88c
parent7e07a868f273af25a85c8cf7aec730853971d93c (diff)
downloadv4l-utils-d7c8faa5e6b81151866514016276db002916fe0d.tar.gz
cec-follower: emulate programmed timer recordings
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> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
-rw-r--r--utils/cec-follower/cec-follower.cpp5
-rw-r--r--utils/cec-follower/cec-follower.h1
-rw-r--r--utils/cec-follower/cec-processing.cpp65
-rw-r--r--utils/cec-follower/cec-tuner.cpp15
4 files changed, 84 insertions, 2 deletions
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)