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