1 /******************************************************************************
2     Copyright (C) 2015 by Hugh Bailey <obs.jim@gmail.com>
3 
4     This program is free software: you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation, either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 ******************************************************************************/
17 
18 #include <inttypes.h>
19 #include "obs-internal.h"
20 
delay_active(const struct obs_output * output)21 static inline bool delay_active(const struct obs_output *output)
22 {
23 	return os_atomic_load_bool(&output->delay_active);
24 }
25 
delay_capturing(const struct obs_output * output)26 static inline bool delay_capturing(const struct obs_output *output)
27 {
28 	return os_atomic_load_bool(&output->delay_capturing);
29 }
30 
push_packet(struct obs_output * output,struct encoder_packet * packet,uint64_t t)31 static inline void push_packet(struct obs_output *output,
32 			       struct encoder_packet *packet, uint64_t t)
33 {
34 	struct delay_data dd;
35 
36 	dd.msg = DELAY_MSG_PACKET;
37 	dd.ts = t;
38 	obs_encoder_packet_create_instance(&dd.packet, packet);
39 
40 	pthread_mutex_lock(&output->delay_mutex);
41 	circlebuf_push_back(&output->delay_data, &dd, sizeof(dd));
42 	pthread_mutex_unlock(&output->delay_mutex);
43 }
44 
process_delay_data(struct obs_output * output,struct delay_data * dd)45 static inline void process_delay_data(struct obs_output *output,
46 				      struct delay_data *dd)
47 {
48 	switch (dd->msg) {
49 	case DELAY_MSG_PACKET:
50 		if (!delay_active(output) || !delay_capturing(output))
51 			obs_encoder_packet_release(&dd->packet);
52 		else
53 			output->delay_callback(output, &dd->packet);
54 		break;
55 	case DELAY_MSG_START:
56 		obs_output_actual_start(output);
57 		break;
58 	case DELAY_MSG_STOP:
59 		obs_output_actual_stop(output, false, dd->ts);
60 		break;
61 	}
62 }
63 
obs_output_cleanup_delay(obs_output_t * output)64 void obs_output_cleanup_delay(obs_output_t *output)
65 {
66 	struct delay_data dd;
67 
68 	while (output->delay_data.size) {
69 		circlebuf_pop_front(&output->delay_data, &dd, sizeof(dd));
70 		if (dd.msg == DELAY_MSG_PACKET) {
71 			obs_encoder_packet_release(&dd.packet);
72 		}
73 	}
74 
75 	output->active_delay_ns = 0;
76 	os_atomic_set_long(&output->delay_restart_refs, 0);
77 }
78 
pop_packet(struct obs_output * output,uint64_t t)79 static inline bool pop_packet(struct obs_output *output, uint64_t t)
80 {
81 	uint64_t elapsed_time;
82 	struct delay_data dd;
83 	bool popped = false;
84 	bool preserve;
85 
86 	/* ------------------------------------------------ */
87 
88 	preserve = (output->delay_cur_flags & OBS_OUTPUT_DELAY_PRESERVE) != 0;
89 
90 	pthread_mutex_lock(&output->delay_mutex);
91 
92 	if (output->delay_data.size) {
93 		circlebuf_peek_front(&output->delay_data, &dd, sizeof(dd));
94 		elapsed_time = (t - dd.ts);
95 
96 		if (preserve && output->reconnecting) {
97 			output->active_delay_ns = elapsed_time;
98 
99 		} else if (elapsed_time > output->active_delay_ns) {
100 			circlebuf_pop_front(&output->delay_data, NULL,
101 					    sizeof(dd));
102 			popped = true;
103 		}
104 	}
105 
106 	pthread_mutex_unlock(&output->delay_mutex);
107 
108 	/* ------------------------------------------------ */
109 
110 	if (popped)
111 		process_delay_data(output, &dd);
112 
113 	return popped;
114 }
115 
process_delay(void * data,struct encoder_packet * packet)116 void process_delay(void *data, struct encoder_packet *packet)
117 {
118 	struct obs_output *output = data;
119 	uint64_t t = os_gettime_ns();
120 	push_packet(output, packet, t);
121 	while (pop_packet(output, t))
122 		;
123 }
124 
obs_output_signal_delay(obs_output_t * output,const char * signal)125 void obs_output_signal_delay(obs_output_t *output, const char *signal)
126 {
127 	struct calldata params;
128 	uint8_t stack[128];
129 
130 	calldata_init_fixed(&params, stack, sizeof(stack));
131 	calldata_set_ptr(&params, "output", output);
132 	calldata_set_int(&params, "sec", output->active_delay_ns / 1000000000);
133 	signal_handler_signal(output->context.signals, signal, &params);
134 }
135 
obs_output_delay_start(obs_output_t * output)136 bool obs_output_delay_start(obs_output_t *output)
137 {
138 	struct delay_data dd = {
139 		.msg = DELAY_MSG_START,
140 		.ts = os_gettime_ns(),
141 	};
142 
143 	if (!delay_active(output)) {
144 		bool can_begin = obs_output_can_begin_data_capture(output, 0);
145 		if (!can_begin)
146 			return false;
147 		if (!obs_output_initialize_encoders(output, 0))
148 			return false;
149 	}
150 
151 	pthread_mutex_lock(&output->delay_mutex);
152 	circlebuf_push_back(&output->delay_data, &dd, sizeof(dd));
153 	pthread_mutex_unlock(&output->delay_mutex);
154 
155 	os_atomic_inc_long(&output->delay_restart_refs);
156 
157 	if (delay_active(output)) {
158 		do_output_signal(output, "starting");
159 		return true;
160 	}
161 
162 	if (!obs_output_begin_data_capture(output, 0)) {
163 		obs_output_cleanup_delay(output);
164 		return false;
165 	}
166 
167 	return true;
168 }
169 
obs_output_delay_stop(obs_output_t * output)170 void obs_output_delay_stop(obs_output_t *output)
171 {
172 	struct delay_data dd = {
173 		.msg = DELAY_MSG_STOP,
174 		.ts = os_gettime_ns(),
175 	};
176 
177 	pthread_mutex_lock(&output->delay_mutex);
178 	circlebuf_push_back(&output->delay_data, &dd, sizeof(dd));
179 	pthread_mutex_unlock(&output->delay_mutex);
180 
181 	do_output_signal(output, "stopping");
182 }
183 
obs_output_set_delay(obs_output_t * output,uint32_t delay_sec,uint32_t flags)184 void obs_output_set_delay(obs_output_t *output, uint32_t delay_sec,
185 			  uint32_t flags)
186 {
187 	if (!obs_output_valid(output, "obs_output_set_delay"))
188 		return;
189 
190 	if ((output->info.flags & OBS_OUTPUT_ENCODED) == 0) {
191 		blog(LOG_WARNING,
192 		     "Output '%s': Tried to set a delay "
193 		     "value on a non-encoded output",
194 		     output->context.name);
195 		return;
196 	}
197 
198 	output->delay_sec = delay_sec;
199 	output->delay_flags = flags;
200 }
201 
obs_output_get_delay(const obs_output_t * output)202 uint32_t obs_output_get_delay(const obs_output_t *output)
203 {
204 	return obs_output_valid(output, "obs_output_set_delay")
205 		       ? output->delay_sec
206 		       : 0;
207 }
208 
obs_output_get_active_delay(const obs_output_t * output)209 uint32_t obs_output_get_active_delay(const obs_output_t *output)
210 {
211 	return obs_output_valid(output, "obs_output_set_delay")
212 		       ? (uint32_t)(output->active_delay_ns / 1000000000ULL)
213 		       : 0;
214 }
215