瀏覽代碼

Merge pull request #2876 from Yohox/feature-ffmpeg-source-reconnect

obs-ffmpeg: Add auto reconnect to remote media sources
Jim 5 年之前
父節點
當前提交
43bb2cc380

+ 7 - 2
deps/media-playback/media-playback/media.c

@@ -587,6 +587,7 @@ static bool init_avformat(mp_media_t *m)
 		m->fmt->flags |= AVFMT_FLAG_NOBUFFER;
 	}
 	if (!m->is_local_file) {
+		av_dict_set(&opts, "stimeout", "30000000", 0);
 		m->fmt->interrupt_callback.callback = interrupt_callback;
 		m->fmt->interrupt_callback.opaque = m;
 	}
@@ -596,7 +597,9 @@ static bool init_avformat(mp_media_t *m)
 	av_dict_free(&opts);
 
 	if (ret < 0) {
-		blog(LOG_WARNING, "MP: Failed to open media: '%s'", m->path);
+		if (!m->reconnecting)
+			blog(LOG_WARNING, "MP: Failed to open media: '%s'",
+			     m->path);
 		return false;
 	}
 
@@ -606,6 +609,7 @@ static bool init_avformat(mp_media_t *m)
 		return false;
 	}
 
+	m->reconnecting = false;
 	m->has_video = mp_decode_init(m, AVMEDIA_TYPE_VIDEO, m->hw);
 	m->has_audio = mp_decode_init(m, AVMEDIA_TYPE_AUDIO, m->hw);
 
@@ -820,7 +824,7 @@ void mp_media_free(mp_media_t *media)
 	pthread_mutex_init_value(&media->mutex);
 }
 
-void mp_media_play(mp_media_t *m, bool loop)
+void mp_media_play(mp_media_t *m, bool loop, bool reconnecting)
 {
 	pthread_mutex_lock(&m->mutex);
 
@@ -829,6 +833,7 @@ void mp_media_play(mp_media_t *m, bool loop)
 
 	m->looping = loop;
 	m->active = true;
+	m->reconnecting = reconnecting;
 
 	pthread_mutex_unlock(&m->mutex);
 

+ 3 - 1
deps/media-playback/media-playback/media.h

@@ -64,6 +64,7 @@ struct mp_media {
 	struct mp_decode v;
 	struct mp_decode a;
 	bool is_local_file;
+	bool reconnecting;
 	bool has_video;
 	bool has_audio;
 	bool is_file;
@@ -117,12 +118,13 @@ struct mp_media_info {
 	enum video_range_type force_range;
 	bool hardware_decoding;
 	bool is_local_file;
+	bool reconnecting;
 };
 
 extern bool mp_media_init(mp_media_t *media, const struct mp_media_info *info);
 extern void mp_media_free(mp_media_t *media);
 
-extern void mp_media_play(mp_media_t *media, bool loop);
+extern void mp_media_play(mp_media_t *media, bool loop, bool reconnecting);
 extern void mp_media_stop(mp_media_t *media);
 extern void mp_media_play_pause(mp_media_t *media, bool pause);
 extern int64_t mp_get_current_time(mp_media_t *m);

+ 2 - 0
plugins/obs-ffmpeg/data/locale/en-US.ini

@@ -66,3 +66,5 @@ NVENC.OutdatedDriver="Your current video card driver does not support this NVENC
 NVENC.UnsupportedDevice="NVENC Error: Unsupported device. Check your video card supports NVENC and that the drivers are up to date."
 NVENC.TooManySessions="NVENC Error: Too many concurrent sessions. Try closing other recording software which might be using NVENC such as NVIDIA Shadowplay or Windows 10 Game DVR."
 NVENC.CheckDrivers="Please check your video drivers are up to date."
+
+ReconnectDelayTime="Reconnect Delay"

+ 90 - 14
plugins/obs-ffmpeg/obs-ffmpeg-source.c

@@ -58,6 +58,12 @@ struct ffmpeg_source {
 	bool close_when_inactive;
 	bool seekable;
 
+	pthread_t reconnect_thread;
+	bool stop_reconnect;
+	bool reconnect_thread_valid;
+	volatile bool reconnecting;
+	int reconnect_delay_sec;
+
 	enum obs_media_state state;
 	obs_hotkey_pair_id play_pause_hotkey;
 	obs_hotkey_id stop_hotkey;
@@ -85,6 +91,8 @@ static bool is_local_file_modified(obs_properties_t *props,
 		obs_properties_get(props, "close_when_inactive");
 	obs_property_t *seekable = obs_properties_get(props, "seekable");
 	obs_property_t *speed = obs_properties_get(props, "speed_percent");
+	obs_property_t *reconnect_delay_sec =
+		obs_properties_get(props, "reconnect_delay_sec");
 	obs_property_set_visible(input, !enabled);
 	obs_property_set_visible(input_format, !enabled);
 	obs_property_set_visible(buffering, !enabled);
@@ -93,6 +101,7 @@ static bool is_local_file_modified(obs_properties_t *props,
 	obs_property_set_visible(looping, enabled);
 	obs_property_set_visible(speed, enabled);
 	obs_property_set_visible(seekable, !enabled);
+	obs_property_set_visible(reconnect_delay_sec, !enabled);
 
 	return true;
 }
@@ -103,6 +112,7 @@ static void ffmpeg_source_defaults(obs_data_t *settings)
 	obs_data_set_default_bool(settings, "looping", false);
 	obs_data_set_default_bool(settings, "clear_on_media_end", true);
 	obs_data_set_default_bool(settings, "restart_on_activate", true);
+	obs_data_set_default_int(settings, "reconnect_delay_sec", 10);
 	obs_data_set_default_int(settings, "buffering_mb", 2);
 	obs_data_set_default_int(settings, "speed_percent", 100);
 }
@@ -174,6 +184,11 @@ static obs_properties_t *ffmpeg_source_getproperties(void *data)
 				obs_module_text("InputFormat"),
 				OBS_TEXT_DEFAULT);
 
+	prop = obs_properties_add_int_slider(
+		props, "reconnect_delay_sec",
+		obs_module_text("ReconnectDelayTime"), 1, 60, 1);
+	obs_property_int_set_suffix(prop, " S");
+
 #ifndef __APPLE__
 	obs_properties_add_bool(props, "hw_decode",
 				obs_module_text("HardwareDecode"));
@@ -245,12 +260,18 @@ static void preload_frame(void *opaque, struct obs_source_frame *f)
 
 	if (s->is_clear_on_media_end || s->is_looping)
 		obs_source_preload_video(s->source, f);
+
+	if (!s->is_local_file && os_atomic_set_bool(&s->reconnecting, false))
+		FF_BLOG(LOG_INFO, "Reconnected.");
 }
 
 static void get_audio(void *opaque, struct obs_source_audio *a)
 {
 	struct ffmpeg_source *s = opaque;
 	obs_source_output_audio(s->source, a);
+
+	if (!s->is_local_file && os_atomic_set_bool(&s->reconnecting, false))
+		FF_BLOG(LOG_INFO, "Reconnected.");
 }
 
 static void media_stopped(void *opaque)
@@ -282,12 +303,50 @@ static void ffmpeg_source_open(struct ffmpeg_source *s)
 			.speed = s->speed_percent,
 			.force_range = s->range,
 			.hardware_decoding = s->is_hw_decoding,
-			.is_local_file = s->is_local_file || s->seekable};
+			.is_local_file = s->is_local_file || s->seekable,
+			.reconnecting = s->reconnecting,
+		};
 
 		s->media_valid = mp_media_init(&s->media, &info);
 	}
 }
 
+static void ffmpeg_source_start(struct ffmpeg_source *s)
+{
+	if (!s->media_valid)
+		ffmpeg_source_open(s);
+
+	if (!s->media_valid)
+		return;
+
+	mp_media_play(&s->media, s->is_looping, s->reconnecting);
+	if (s->is_local_file)
+		obs_source_show_preloaded_video(s->source);
+
+	set_media_state(s, OBS_MEDIA_STATE_PLAYING);
+	obs_source_media_started(s->source);
+}
+
+static void *ffmpeg_source_reconnect(void *data)
+{
+	struct ffmpeg_source *s = data;
+	os_sleep_ms(s->reconnect_delay_sec * 1000);
+
+	if (s->stop_reconnect || s->media_valid)
+		goto finish;
+
+	bool active = obs_source_active(s->source);
+	if (!s->close_when_inactive || active)
+		ffmpeg_source_open(s);
+
+	if (!s->restart_on_activate || active)
+		ffmpeg_source_start(s);
+
+finish:
+	s->reconnect_thread_valid = false;
+	return NULL;
+}
+
 static void ffmpeg_source_tick(void *data, float seconds)
 {
 	UNUSED_PARAMETER(seconds);
@@ -298,21 +357,22 @@ static void ffmpeg_source_tick(void *data, float seconds)
 			mp_media_free(&s->media);
 			s->media_valid = false;
 		}
-		s->destroy_media = false;
-	}
-}
 
-static void ffmpeg_source_start(struct ffmpeg_source *s)
-{
-	if (!s->media_valid)
-		ffmpeg_source_open(s);
+		s->destroy_media = false;
 
-	if (s->media_valid) {
-		mp_media_play(&s->media, s->is_looping);
-		if (s->is_local_file)
-			obs_source_show_preloaded_video(s->source);
-		set_media_state(s, OBS_MEDIA_STATE_PLAYING);
-		obs_source_media_started(s->source);
+		if (!s->is_local_file) {
+			if (!os_atomic_set_bool(&s->reconnecting, true)) {
+				FF_BLOG(LOG_WARNING, "Disconnected. "
+						     "Reconnecting...");
+			}
+			if (pthread_create(&s->reconnect_thread, NULL,
+					   ffmpeg_source_reconnect, s) != 0) {
+				FF_BLOG(LOG_WARNING, "Could not create "
+						     "reconnect thread");
+				return;
+			}
+			s->reconnect_thread_valid = true;
+		}
 	}
 }
 
@@ -338,8 +398,19 @@ static void ffmpeg_source_update(void *data, obs_data_t *settings)
 		input = (char *)obs_data_get_string(settings, "input");
 		input_format =
 			(char *)obs_data_get_string(settings, "input_format");
+		s->reconnect_delay_sec =
+			(int)obs_data_get_int(settings, "reconnect_delay_sec");
+		s->reconnect_delay_sec = s->reconnect_delay_sec == 0
+						 ? 10
+						 : s->reconnect_delay_sec;
 		s->is_looping = false;
 		s->close_when_inactive = true;
+
+		if (s->reconnect_thread_valid) {
+			s->stop_reconnect = true;
+			pthread_join(s->reconnect_thread, NULL);
+			s->stop_reconnect = false;
+		}
 	}
 
 	s->input = input ? bstrdup(input) : NULL;
@@ -539,6 +610,11 @@ static void ffmpeg_source_destroy(void *data)
 
 	if (s->hotkey)
 		obs_hotkey_unregister(s->hotkey);
+	if (!s->is_local_file) {
+		s->stop_reconnect = true;
+		if (s->reconnect_thread_valid)
+			pthread_join(s->reconnect_thread, NULL);
+	}
 	if (s->media_valid)
 		mp_media_free(&s->media);