obs-outputs: Stop in thread to prevent locking UI

Sometimes stopping a connection can lock up due to data that still
remains to be sent, and this would lock up the thread requesting the
stop (typically the UI thread).  So instead of locking up the calling
thread, spawn a new thread specifically for stopping so the calling
thread can continue uninterrupted.  If the user attempts to reconnect,
it will wait for the stop thread to complete in the connect thread
before attempting to connect.
master
jp9000 2015-11-01 15:15:20 -08:00
parent a0e112ffd9
commit e350eb5a4c
1 changed files with 77 additions and 29 deletions

View File

@ -53,6 +53,8 @@ struct rtmp_stream {
volatile bool disconnected; volatile bool disconnected;
pthread_t send_thread; pthread_t send_thread;
bool stopping;
pthread_t stop_thread;
int max_shutdown_time_sec; int max_shutdown_time_sec;
os_sem_t *send_sem; os_sem_t *send_sem;
@ -108,14 +110,28 @@ static inline void free_packets(struct rtmp_stream *stream)
pthread_mutex_unlock(&stream->packets_mutex); pthread_mutex_unlock(&stream->packets_mutex);
} }
static void rtmp_stream_stop(void *data); static void *rtmp_stream_actual_stop(void *data);
static void rtmp_stream_destroy(void *data) static void rtmp_stream_destroy(void *data)
{ {
struct rtmp_stream *stream = data; struct rtmp_stream *stream = data;
if (stream->connecting || stream->active) if (stream->stopping) {
rtmp_stream_stop(data); pthread_join(stream->stop_thread, NULL);
} else if (stream->connecting || stream->active) {
os_event_signal(stream->stop_event);
if (stream->connecting)
pthread_join(stream->connect_thread, NULL);
if (stream->active) {
os_sem_post(stream->send_sem);
obs_output_end_data_capture(stream->output);
}
rtmp_stream_actual_stop(data);
}
if (stream) { if (stream) {
free_packets(stream); free_packets(stream);
@ -155,9 +171,30 @@ fail:
return NULL; return NULL;
} }
static void *rtmp_stream_actual_stop(void *data)
{
struct rtmp_stream *stream = data;
void *ret;
if (stream->active) {
pthread_join(stream->send_thread, &ret);
RTMP_Close(&stream->rtmp);
}
os_event_reset(stream->stop_event);
stream->sent_headers = false;
stream->stopping = false;
return NULL;
}
static void rtmp_stream_stop(void *data) static void rtmp_stream_stop(void *data)
{ {
struct rtmp_stream *stream = data; struct rtmp_stream *stream = data;
int ret;
if (stream->stopping)
return;
os_event_signal(stream->stop_event); os_event_signal(stream->stop_event);
@ -166,14 +203,16 @@ static void rtmp_stream_stop(void *data)
if (stream->active) { if (stream->active) {
os_sem_post(stream->send_sem); os_sem_post(stream->send_sem);
pthread_join(stream->send_thread, NULL);
obs_output_end_data_capture(stream->output); obs_output_end_data_capture(stream->output);
RTMP_Close(&stream->rtmp);
} }
os_event_reset(stream->stop_event); stream->stopping = true;
ret = pthread_create(&stream->stop_thread, NULL,
stream->sent_headers = false; rtmp_stream_actual_stop, stream);
if (ret != 0) {
warn("Could not create stop thread! Stopping directly");
rtmp_stream_actual_stop(stream);
}
} }
static inline void set_rtmp_str(AVal *val, const char *str) static inline void set_rtmp_str(AVal *val, const char *str)
@ -472,10 +511,39 @@ static int try_connect(struct rtmp_stream *stream)
return init_send(stream); return init_send(stream);
} }
static void init_connect(struct rtmp_stream *stream)
{
obs_service_t *service = obs_output_get_service(stream->output);
obs_data_t *settings;
if (stream->stopping)
pthread_join(stream->stop_thread, NULL);
stream->disconnected = false;
stream->total_bytes_sent = 0;
stream->dropped_frames = 0;
stream->min_drop_dts_usec= 0;
stream->min_priority = 0;
settings = obs_output_get_settings(stream->output);
dstr_copy(&stream->path, obs_service_get_url(service));
dstr_copy(&stream->key, obs_service_get_key(service));
dstr_copy(&stream->username, obs_service_get_username(service));
dstr_copy(&stream->password, obs_service_get_password(service));
stream->drop_threshold_usec =
(int64_t)obs_data_get_int(settings, OPT_DROP_THRESHOLD) * 1000;
stream->max_shutdown_time_sec =
(int)obs_data_get_int(settings, OPT_MAX_SHUTDOWN_TIME_SEC);
obs_data_release(settings);
}
static void *connect_thread(void *data) static void *connect_thread(void *data)
{ {
struct rtmp_stream *stream = data; struct rtmp_stream *stream = data;
int ret = try_connect(stream); int ret;
init_connect(stream);
ret = try_connect(stream);
if (ret != OBS_OUTPUT_SUCCESS) { if (ret != OBS_OUTPUT_SUCCESS) {
obs_output_signal_stop(stream->output, ret); obs_output_signal_stop(stream->output, ret);
@ -492,32 +560,12 @@ static void *connect_thread(void *data)
static bool rtmp_stream_start(void *data) static bool rtmp_stream_start(void *data)
{ {
struct rtmp_stream *stream = data; struct rtmp_stream *stream = data;
obs_service_t *service = obs_output_get_service(stream->output);
obs_data_t *settings;
stream->disconnected = false;
if (!obs_output_can_begin_data_capture(stream->output, 0)) if (!obs_output_can_begin_data_capture(stream->output, 0))
return false; return false;
if (!obs_output_initialize_encoders(stream->output, 0)) if (!obs_output_initialize_encoders(stream->output, 0))
return false; return false;
stream->total_bytes_sent = 0;
stream->dropped_frames = 0;
stream->min_drop_dts_usec= 0;
stream->min_priority = 0;
settings = obs_output_get_settings(stream->output);
dstr_copy(&stream->path, obs_service_get_url(service));
dstr_copy(&stream->key, obs_service_get_key(service));
dstr_copy(&stream->username, obs_service_get_username(service));
dstr_copy(&stream->password, obs_service_get_password(service));
stream->drop_threshold_usec =
(int64_t)obs_data_get_int(settings, OPT_DROP_THRESHOLD) * 1000;
stream->max_shutdown_time_sec =
(int)obs_data_get_int(settings, OPT_MAX_SHUTDOWN_TIME_SEC);
obs_data_release(settings);
return pthread_create(&stream->connect_thread, NULL, connect_thread, return pthread_create(&stream->connect_thread, NULL, connect_thread,
stream) == 0; stream) == 0;
} }