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(¶ms, stack, sizeof(stack));
131 calldata_set_ptr(¶ms, "output", output);
132 calldata_set_int(¶ms, "sec", output->active_delay_ns / 1000000000);
133 signal_handler_signal(output->context.signals, signal, ¶ms);
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