char/char-rtc-obs

git clone https://git.t4t.associates/char/char-rtc-obs

videogame hackerAdd UI in Tools menu for setup35e5e7d

main
6.1 KiB232 linesraw
1#include "whip-output.h"
2
3static void whip_output_close_unsafe(struct whip_output *output)
4{
5	if (output && output->whip_output) {
6		char_rtc_output_close(output->whip_output);
7		char_rtc_output_free(output->whip_output);
8		output->whip_output = NULL;
9	}
10}
11
12static const char *whip_output_getname(void *type_data)
13{
14	UNUSED_PARAMETER(type_data);
15
16	return obs_module_text("Output.Name");
17}
18
19static void *whip_output_create(obs_data_t *settings, obs_output_t *obs_output)
20{
21	UNUSED_PARAMETER(settings);
22
23	struct whip_output *output = bzalloc(sizeof(struct whip_output));
24	pthread_mutex_init_value(&output->write_mutex);
25
26	output->output = obs_output;
27	output->whip_output = NULL;
28
29	// This needs to be recursive due to `obs_output_signal_stop` calling
30	// `whip_output_total_bytes_sent` (also guarded by mutex)
31	if (pthread_mutex_init_recursive(&output->write_mutex) != 0)
32		goto fail;
33
34	return output;
35
36fail:
37	pthread_mutex_destroy(&output->write_mutex);
38	bfree(output);
39	return NULL;
40}
41
42static void whip_output_destroy(void *data)
43{
44	struct whip_output *output = data;
45
46	pthread_mutex_lock(&output->write_mutex);
47	whip_output_close_unsafe(output);
48	pthread_mutex_unlock(&output->write_mutex);
49
50	pthread_mutex_destroy(&output->write_mutex);
51	bfree(output);
52}
53
54static int map_rtc_error_to_obs_error(RTCOutputError error)
55{
56	switch (error) {
57	case RTCOutputError_ConnectFailed:
58		return OBS_OUTPUT_CONNECT_FAILED;
59	case RTCOutputError_NetworkError:
60		return OBS_OUTPUT_ERROR;
61	default:
62		blog(LOG_ERROR, "Invalid whip error code: %d", error);
63		return OBS_OUTPUT_ERROR;
64	}
65}
66
67static void whip_output_error_callback(void *data, RTCOutputError error)
68{
69	struct whip_output *output = data;
70	pthread_mutex_lock(&output->write_mutex);
71	if (output->whip_output) {
72		whip_output_close_unsafe(output);
73		obs_output_signal_stop(output->output,
74				       map_rtc_error_to_obs_error(error));
75	}
76	pthread_mutex_unlock(&output->write_mutex);
77}
78
79static bool whip_output_start(void *data)
80{
81	struct whip_output *output = data;
82	obs_service_t *service;
83	obs_data_t *service_settings;
84	const char *url, *bearer_token;
85
86	service = obs_output_get_service(output->output);
87	if (!service)
88		return false;
89
90	if (!obs_output_can_begin_data_capture(output->output, 0))
91		return false;
92
93	if (!obs_output_initialize_encoders(output->output, 0))
94		return false;
95
96	output->whip_output = char_rtc_output_new();
97	if (!output->whip_output) {
98		blog(LOG_ERROR, "Unable to initialize whip output");
99		return false;
100	}
101
102	char_rtc_output_set_error_callback(
103		output->whip_output,
104		(RTCOutputErrorCallback)whip_output_error_callback, output);
105
106	service_settings = obs_service_get_settings(service);
107	if (!service_settings)
108		return false;
109
110	url = obs_service_get_url(service);
111	bearer_token = obs_data_get_string(service_settings, "bearer_token");
112	char_rtc_output_connect(output->whip_output, url, bearer_token);
113
114	obs_output_begin_data_capture(output->output, 0);
115
116	obs_data_release(service_settings);
117	return true;
118}
119
120static void whip_output_stop(void *data, uint64_t ts)
121{
122	UNUSED_PARAMETER(ts);
123
124	struct whip_output *output = data;
125
126	pthread_mutex_lock(&output->write_mutex);
127	whip_output_close_unsafe(output);
128	pthread_mutex_unlock(&output->write_mutex);
129
130	obs_output_signal_stop(output->output, OBS_OUTPUT_SUCCESS);
131}
132
133static void whip_output_data(void *data, struct encoder_packet *packet)
134{
135	struct whip_output *output = data;
136	int64_t duration = 0;
137	bool is_audio = false;
138
139	if (packet->type == OBS_ENCODER_VIDEO) {
140		duration = packet->dts_usec - output->video_timestamp;
141		output->video_timestamp = packet->dts_usec;
142	} else if (packet->type == OBS_ENCODER_AUDIO) {
143		is_audio = true;
144		duration = packet->dts_usec - output->audio_timestamp;
145		output->audio_timestamp = packet->dts_usec;
146	}
147
148	pthread_mutex_lock(&output->write_mutex);
149	if (output->whip_output) {
150		if (!char_rtc_output_write(output->whip_output, packet->data,
151					   packet->size, duration, is_audio)) {
152			blog(LOG_ERROR,
153			     "Unable to write packets to whip output");
154		}
155	}
156	pthread_mutex_unlock(&output->write_mutex);
157}
158
159static void whip_output_defaults(obs_data_t *defaults)
160{
161	UNUSED_PARAMETER(defaults);
162}
163
164static obs_properties_t *whip_output_properties(void *unused)
165{
166	UNUSED_PARAMETER(unused);
167
168	obs_properties_t *props = obs_properties_create();
169
170	return props;
171}
172
173static uint64_t whip_output_total_bytes_sent(void *data)
174{
175	struct whip_output *output = data;
176	pthread_mutex_lock(&output->write_mutex);
177	uint64_t bytes_sent = 0;
178	if (output->whip_output)
179		bytes_sent = char_rtc_output_bytes_sent(output->whip_output);
180	pthread_mutex_unlock(&output->write_mutex);
181
182	return bytes_sent;
183}
184
185static int whip_output_dropped_frames(void *data)
186{
187	struct whip_output *output = data;
188	pthread_mutex_lock(&output->write_mutex);
189	uint32_t dropped_frames = 0;
190	if (output->whip_output)
191		dropped_frames =
192			char_rtc_output_dropped_frames(output->whip_output);
193	pthread_mutex_unlock(&output->write_mutex);
194
195	return dropped_frames;
196}
197
198static int whip_output_connect_time_ms(void *data)
199{
200	struct whip_output *output = data;
201	pthread_mutex_lock(&output->write_mutex);
202	uint32_t connect_time = 0;
203	if (output->whip_output)
204		connect_time =
205			char_rtc_output_connect_time_ms(output->whip_output);
206	pthread_mutex_unlock(&output->write_mutex);
207
208	return connect_time;
209}
210
211struct obs_output_info whip_output_info = {
212	.id = "whip_output",
213	.flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | OBS_OUTPUT_SERVICE,
214	.encoded_video_codecs = "h264",
215	.encoded_audio_codecs = "opus",
216	.get_name = whip_output_getname,
217	.create = whip_output_create,
218	.destroy = whip_output_destroy,
219	.start = whip_output_start,
220	.stop = whip_output_stop,
221	.encoded_packet = whip_output_data,
222	.get_defaults = whip_output_defaults,
223	.get_properties = whip_output_properties,
224	.get_total_bytes = whip_output_total_bytes_sent,
225	.get_connect_time_ms = whip_output_connect_time_ms,
226	.get_dropped_frames = whip_output_dropped_frames,
227};
228
229void whip_register_output()
230{
231	obs_register_output(&whip_output_info);
232}