1 /******************************************************************************
2     Copyright (C) 2018 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 "obs-internal.h"
19 
gpu_encode_thread(void * unused)20 static void *gpu_encode_thread(void *unused)
21 {
22 	struct obs_core_video *video = &obs->video;
23 	uint64_t interval = video_output_get_frame_time(obs->video.video);
24 	DARRAY(obs_encoder_t *) encoders;
25 	int wait_frames = NUM_ENCODE_TEXTURE_FRAMES_TO_WAIT;
26 
27 	UNUSED_PARAMETER(unused);
28 	da_init(encoders);
29 
30 	os_set_thread_name("obs gpu encode thread");
31 
32 	while (os_sem_wait(video->gpu_encode_semaphore) == 0) {
33 		struct obs_tex_frame tf;
34 		uint64_t timestamp;
35 		uint64_t lock_key;
36 		uint64_t next_key;
37 		size_t lock_count = 0;
38 
39 		if (os_atomic_load_bool(&video->gpu_encode_stop))
40 			break;
41 
42 		if (wait_frames) {
43 			wait_frames--;
44 			continue;
45 		}
46 
47 		os_event_reset(video->gpu_encode_inactive);
48 
49 		/* -------------- */
50 
51 		pthread_mutex_lock(&video->gpu_encoder_mutex);
52 
53 		circlebuf_pop_front(&video->gpu_encoder_queue, &tf, sizeof(tf));
54 		timestamp = tf.timestamp;
55 		lock_key = tf.lock_key;
56 		next_key = tf.lock_key;
57 
58 		video_output_inc_texture_frames(video->video);
59 
60 		for (size_t i = 0; i < video->gpu_encoders.num; i++) {
61 			obs_encoder_t *encoder = obs_encoder_get_ref(
62 				video->gpu_encoders.array[i]);
63 			if (encoder)
64 				da_push_back(encoders, &encoder);
65 		}
66 
67 		pthread_mutex_unlock(&video->gpu_encoder_mutex);
68 
69 		/* -------------- */
70 
71 		for (size_t i = 0; i < encoders.num; i++) {
72 			struct encoder_packet pkt = {0};
73 			bool received = false;
74 			bool success;
75 
76 			obs_encoder_t *encoder = encoders.array[i];
77 			struct obs_encoder *pair = encoder->paired_encoder;
78 
79 			pkt.timebase_num = encoder->timebase_num;
80 			pkt.timebase_den = encoder->timebase_den;
81 			pkt.encoder = encoder;
82 
83 			if (!encoder->first_received && pair) {
84 				if (!pair->first_received ||
85 				    pair->first_raw_ts > timestamp) {
86 					continue;
87 				}
88 			}
89 
90 			if (video_pause_check(&encoder->pause, timestamp))
91 				continue;
92 
93 			if (encoder->reconfigure_requested) {
94 				encoder->reconfigure_requested = false;
95 				encoder->info.update(encoder->context.data,
96 						     encoder->context.settings);
97 			}
98 
99 			if (!encoder->start_ts)
100 				encoder->start_ts = timestamp;
101 
102 			if (++lock_count == encoders.num)
103 				next_key = 0;
104 			else
105 				next_key++;
106 
107 			success = encoder->info.encode_texture(
108 				encoder->context.data, tf.handle,
109 				encoder->cur_pts, lock_key, &next_key, &pkt,
110 				&received);
111 			send_off_encoder_packet(encoder, success, received,
112 						&pkt);
113 
114 			lock_key = next_key;
115 
116 			encoder->cur_pts += encoder->timebase_num;
117 		}
118 
119 		/* -------------- */
120 
121 		pthread_mutex_lock(&video->gpu_encoder_mutex);
122 
123 		tf.lock_key = next_key;
124 
125 		if (--tf.count) {
126 			tf.timestamp += interval;
127 			circlebuf_push_front(&video->gpu_encoder_queue, &tf,
128 					     sizeof(tf));
129 
130 			video_output_inc_texture_skipped_frames(video->video);
131 		} else {
132 			circlebuf_push_back(&video->gpu_encoder_avail_queue,
133 					    &tf, sizeof(tf));
134 		}
135 
136 		pthread_mutex_unlock(&video->gpu_encoder_mutex);
137 
138 		/* -------------- */
139 
140 		os_event_signal(video->gpu_encode_inactive);
141 
142 		for (size_t i = 0; i < encoders.num; i++)
143 			obs_encoder_release(encoders.array[i]);
144 
145 		da_resize(encoders, 0);
146 	}
147 
148 	da_free(encoders);
149 	return NULL;
150 }
151 
init_gpu_encoding(struct obs_core_video * video)152 bool init_gpu_encoding(struct obs_core_video *video)
153 {
154 #ifdef _WIN32
155 	struct obs_video_info *ovi = &video->ovi;
156 
157 	video->gpu_encode_stop = false;
158 
159 	circlebuf_reserve(&video->gpu_encoder_avail_queue, NUM_ENCODE_TEXTURES);
160 	for (size_t i = 0; i < NUM_ENCODE_TEXTURES; i++) {
161 		gs_texture_t *tex;
162 		gs_texture_t *tex_uv;
163 
164 		gs_texture_create_nv12(&tex, &tex_uv, ovi->output_width,
165 				       ovi->output_height,
166 				       GS_RENDER_TARGET | GS_SHARED_KM_TEX);
167 		if (!tex) {
168 			return false;
169 		}
170 
171 		uint32_t handle = gs_texture_get_shared_handle(tex);
172 
173 		struct obs_tex_frame frame = {
174 			.tex = tex, .tex_uv = tex_uv, .handle = handle};
175 
176 		circlebuf_push_back(&video->gpu_encoder_avail_queue, &frame,
177 				    sizeof(frame));
178 	}
179 
180 	if (os_sem_init(&video->gpu_encode_semaphore, 0) != 0)
181 		return false;
182 	if (os_event_init(&video->gpu_encode_inactive, OS_EVENT_TYPE_MANUAL) !=
183 	    0)
184 		return false;
185 	if (pthread_create(&video->gpu_encode_thread, NULL, gpu_encode_thread,
186 			   NULL) != 0)
187 		return false;
188 
189 	os_event_signal(video->gpu_encode_inactive);
190 
191 	video->gpu_encode_thread_initialized = true;
192 	return true;
193 #else
194 	UNUSED_PARAMETER(video);
195 	return false;
196 #endif
197 }
198 
stop_gpu_encoding_thread(struct obs_core_video * video)199 void stop_gpu_encoding_thread(struct obs_core_video *video)
200 {
201 	if (video->gpu_encode_thread_initialized) {
202 		os_atomic_set_bool(&video->gpu_encode_stop, true);
203 		os_sem_post(video->gpu_encode_semaphore);
204 		pthread_join(video->gpu_encode_thread, NULL);
205 		video->gpu_encode_thread_initialized = false;
206 	}
207 }
208 
free_gpu_encoding(struct obs_core_video * video)209 void free_gpu_encoding(struct obs_core_video *video)
210 {
211 	if (video->gpu_encode_semaphore) {
212 		os_sem_destroy(video->gpu_encode_semaphore);
213 		video->gpu_encode_semaphore = NULL;
214 	}
215 	if (video->gpu_encode_inactive) {
216 		os_event_destroy(video->gpu_encode_inactive);
217 		video->gpu_encode_inactive = NULL;
218 	}
219 
220 #define free_circlebuf(x)                                               \
221 	do {                                                            \
222 		while (x.size) {                                        \
223 			struct obs_tex_frame frame;                     \
224 			circlebuf_pop_front(&x, &frame, sizeof(frame)); \
225 			gs_texture_destroy(frame.tex);                  \
226 			gs_texture_destroy(frame.tex_uv);               \
227 		}                                                       \
228 		circlebuf_free(&x);                                     \
229 	} while (false)
230 
231 	free_circlebuf(video->gpu_encoder_queue);
232 	free_circlebuf(video->gpu_encoder_avail_queue);
233 #undef free_circlebuf
234 }
235