libobs: Fix lockup when an encode call fails

(This commit also modifies the UI, obs-ffmpeg, and obs-output modules)

Fixes a long-time regression where the program would lock up if an
encode call fails.  Shuts down all outputs associated with the failing
encoder and displays an error message to the user.

Ideally, it would be best if a more detailed error could be displayed to
the user about the nature of the error, though the primary problem is
the encoder errors are typically not something the user would be able to
understand.  The current message is a bit of a generic error message;
improvement is welcome.

Another suggestion is to try to have the encoder restart seamlessly,
though it would take a significant amount of work to be able to make it
do something like that properly, and it sort of assumes that encoder
failures are sporadic, which may not necessarily be the case with some
hardware encoders on some systems.  It may be better just to use another
encoder in that case.  For now, seamless restart is ruled out.
This commit is contained in:
jp9000
2019-05-17 01:19:36 -07:00
parent 19aca12025
commit 973d31b8c2
11 changed files with 136 additions and 27 deletions

View File

@@ -331,7 +331,7 @@ static bool ffmpeg_mux_start(void *data)
return true;
}
static int deactivate(struct ffmpeg_muxer *stream)
static int deactivate(struct ffmpeg_muxer *stream, int code)
{
int ret = -1;
@@ -345,8 +345,11 @@ static int deactivate(struct ffmpeg_muxer *stream)
info("Output of file '%s' stopped", stream->path.array);
}
if (stopping(stream))
if (code) {
obs_output_signal_stop(stream->output, code);
} else if (stopping(stream)) {
obs_output_end_data_capture(stream->output);
}
os_atomic_set_bool(&stream->stopping, false);
return ret;
@@ -380,7 +383,7 @@ static void signal_failure(struct ffmpeg_muxer *stream)
obs_output_set_last_error (stream->output, error);
}
ret = deactivate(stream);
ret = deactivate(stream, 0);
switch (ret) {
case FFM_UNSUPPORTED: code = OBS_OUTPUT_UNSUPPORTED; break;
@@ -479,6 +482,12 @@ static void ffmpeg_mux_data(void *data, struct encoder_packet *packet)
if (!active(stream))
return;
/* encoder failure */
if (!packet) {
deactivate(stream, OBS_OUTPUT_ENCODE_ERROR);
return;
}
if (!stream->sent_headers) {
if (!send_headers(stream))
return;
@@ -488,7 +497,7 @@ static void ffmpeg_mux_data(void *data, struct encoder_packet *packet)
if (stopping(stream)) {
if (packet->sys_dts_usec >= stream->stop_ts) {
deactivate(stream);
deactivate(stream, 0);
return;
}
}
@@ -803,10 +812,13 @@ static void replay_buffer_save(struct ffmpeg_muxer *stream)
replay_buffer_mux_thread, stream) == 0;
}
static void deactivate_replay_buffer(struct ffmpeg_muxer *stream)
static void deactivate_replay_buffer(struct ffmpeg_muxer *stream, int code)
{
if (stopping(stream))
if (code) {
obs_output_signal_stop(stream->output, code);
} else if (stopping(stream)) {
obs_output_end_data_capture(stream->output);
}
os_atomic_set_bool(&stream->active, false);
os_atomic_set_bool(&stream->sent_headers, false);
@@ -822,9 +834,15 @@ static void replay_buffer_data(void *data, struct encoder_packet *packet)
if (!active(stream))
return;
/* encoder failure */
if (!packet) {
deactivate_replay_buffer(stream, OBS_OUTPUT_ENCODE_ERROR);
return;
}
if (stopping(stream)) {
if (packet->sys_dts_usec >= stream->stop_ts) {
deactivate_replay_buffer(stream);
deactivate_replay_buffer(stream, 0);
return;
}
}

View File

@@ -193,7 +193,7 @@ static void flv_output_stop(void *data, uint64_t ts)
os_atomic_set_bool(&stream->stopping, true);
}
static void flv_output_actual_stop(struct flv_output *stream)
static void flv_output_actual_stop(struct flv_output *stream, int code)
{
os_atomic_set_bool(&stream->active, false);
@@ -203,7 +203,11 @@ static void flv_output_actual_stop(struct flv_output *stream)
fclose(stream->file);
}
obs_output_end_data_capture(stream->output);
if (code) {
obs_output_signal_stop(stream->output, code);
} else {
obs_output_end_data_capture(stream->output);
}
info("FLV file output complete");
}
@@ -218,9 +222,14 @@ static void flv_output_data(void *data, struct encoder_packet *packet)
if (!active(stream))
goto unlock;
if (!packet) {
flv_output_actual_stop(stream, OBS_OUTPUT_ENCODE_ERROR);
goto unlock;
}
if (stopping(stream)) {
if (packet->sys_dts_usec >= (int64_t)stream->stop_ts) {
flv_output_actual_stop(stream);
flv_output_actual_stop(stream, 0);
goto unlock;
}
}

View File

@@ -72,6 +72,7 @@ struct ftl_stream {
volatile bool active;
volatile bool disconnected;
volatile bool encode_error;
pthread_t send_thread;
int max_shutdown_time_sec;
@@ -516,8 +517,12 @@ static void *send_thread(void *data)
}
}
bool encode_error = os_atomic_load_bool(&stream->encode_error);
if (disconnected(stream)) {
info("Disconnected from %s", stream->path.array);
} else if (encode_error) {
info("Encoder error, disconnecting");
} else {
info("User stopped the stream");
}
@@ -525,6 +530,8 @@ static void *send_thread(void *data)
if (!stopping(stream)) {
pthread_detach(stream->send_thread);
obs_output_signal_stop(stream->output, OBS_OUTPUT_DISCONNECTED);
} else if (encode_error) {
obs_output_signal_stop(stream->output, OBS_OUTPUT_ENCODE_ERROR);
} else {
obs_output_end_data_capture(stream->output);
}
@@ -809,6 +816,13 @@ static void ftl_stream_data(void *data, struct encoder_packet *packet)
if (disconnected(stream) || !active(stream))
return;
/* encoder failure */
if (!packet) {
os_atomic_set_bool(&stream->encode_error, true);
os_sem_post(stream->send_sem);
return;
}
if (packet->type == OBS_ENCODER_VIDEO)
obs_parse_avc_packet(&new_packet, packet);
else
@@ -1034,6 +1048,7 @@ static int init_connect(struct ftl_stream *stream)
}
os_atomic_set_bool(&stream->disconnected, false);
os_atomic_set_bool(&stream->encode_error, false);
stream->total_bytes_sent = 0;
stream->dropped_frames = 0;
stream->min_priority = 0;

View File

@@ -487,8 +487,12 @@ static void *send_thread(void *data)
}
}
bool encode_error = os_atomic_load_bool(&stream->encode_error);
if (disconnected(stream)) {
info("Disconnected from %s", stream->path.array);
} else if (encode_error) {
info("Encoder error, disconnecting");
} else {
info("User stopped the stream");
}
@@ -507,6 +511,8 @@ static void *send_thread(void *data)
if (!stopping(stream)) {
pthread_detach(stream->send_thread);
obs_output_signal_stop(stream->output, OBS_OUTPUT_DISCONNECTED);
} else if (encode_error) {
obs_output_signal_stop(stream->output, OBS_OUTPUT_ENCODE_ERROR);
} else {
obs_output_end_data_capture(stream->output);
}
@@ -886,6 +892,7 @@ static bool init_connect(struct rtmp_stream *stream)
return false;
os_atomic_set_bool(&stream->disconnected, false);
os_atomic_set_bool(&stream->encode_error, false);
stream->total_bytes_sent = 0;
stream->dropped_frames = 0;
stream->min_priority = 0;
@@ -1103,6 +1110,13 @@ static void rtmp_stream_data(void *data, struct encoder_packet *packet)
if (disconnected(stream) || !active(stream))
return;
/* encoder fail */
if (!packet) {
os_atomic_set_bool(&stream->encode_error, true);
os_sem_post(stream->send_sem);
return;
}
if (packet->type == OBS_ENCODER_VIDEO) {
if (!stream->got_first_video) {
stream->start_dts_offset =

View File

@@ -59,6 +59,7 @@ struct rtmp_stream {
volatile bool active;
volatile bool disconnected;
volatile bool encode_error;
pthread_t send_thread;
int max_shutdown_time_sec;