|
@@ -45,6 +45,14 @@ struct ffmpeg_source {
|
|
|
uint8_t *sws_data;
|
|
uint8_t *sws_data;
|
|
|
int sws_linesize;
|
|
int sws_linesize;
|
|
|
obs_source_t *source;
|
|
obs_source_t *source;
|
|
|
|
|
+
|
|
|
|
|
+ char *input;
|
|
|
|
|
+ char *input_format;
|
|
|
|
|
+ enum AVDiscard frame_drop;
|
|
|
|
|
+ int audio_buffer_size;
|
|
|
|
|
+ int video_buffer_size;
|
|
|
|
|
+ bool is_advanced;
|
|
|
|
|
+ bool is_looping;
|
|
|
bool is_forcing_scale;
|
|
bool is_forcing_scale;
|
|
|
bool is_hw_decoding;
|
|
bool is_hw_decoding;
|
|
|
bool is_clear_on_media_end;
|
|
bool is_clear_on_media_end;
|
|
@@ -303,9 +311,11 @@ static bool is_advanced_modified(obs_properties_t *props,
|
|
|
UNUSED_PARAMETER(prop);
|
|
UNUSED_PARAMETER(prop);
|
|
|
|
|
|
|
|
bool enabled = obs_data_get_bool(settings, "advanced");
|
|
bool enabled = obs_data_get_bool(settings, "advanced");
|
|
|
|
|
+ obs_property_t *fscale = obs_properties_get(props, "force_scale");
|
|
|
obs_property_t *abuf = obs_properties_get(props, "audio_buffer_size");
|
|
obs_property_t *abuf = obs_properties_get(props, "audio_buffer_size");
|
|
|
obs_property_t *vbuf = obs_properties_get(props, "video_buffer_size");
|
|
obs_property_t *vbuf = obs_properties_get(props, "video_buffer_size");
|
|
|
obs_property_t *frame_drop = obs_properties_get(props, "frame_drop");
|
|
obs_property_t *frame_drop = obs_properties_get(props, "frame_drop");
|
|
|
|
|
+ obs_property_set_visible(fscale, enabled);
|
|
|
obs_property_set_visible(abuf, enabled);
|
|
obs_property_set_visible(abuf, enabled);
|
|
|
obs_property_set_visible(vbuf, enabled);
|
|
obs_property_set_visible(vbuf, enabled);
|
|
|
obs_property_set_visible(frame_drop, enabled);
|
|
obs_property_set_visible(frame_drop, enabled);
|
|
@@ -313,6 +323,17 @@ static bool is_advanced_modified(obs_properties_t *props,
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+static void ffmpeg_source_defaults(obs_data_t *settings)
|
|
|
|
|
+{
|
|
|
|
|
+ obs_data_set_default_bool(settings, "is_local_file", true);
|
|
|
|
|
+ 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, "force_scale", true);
|
|
|
|
|
+#if defined(_WIN32) || defined(__APPLE__)
|
|
|
|
|
+ obs_data_set_default_bool(settings, "hw_decode", true);
|
|
|
|
|
+#endif
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
static const char *media_filter =
|
|
static const char *media_filter =
|
|
|
" (*.mp4 *.ts *.mov *.flv *.mkv *.avi *.mp3 *.ogg *.aac *.wav *.gif *.webm);;";
|
|
" (*.mp4 *.ts *.mov *.flv *.mkv *.avi *.mp3 *.ogg *.aac *.wav *.gif *.webm);;";
|
|
|
static const char *video_filter =
|
|
static const char *video_filter =
|
|
@@ -358,9 +379,6 @@ static obs_properties_t *ffmpeg_source_getproperties(void *data)
|
|
|
obs_properties_add_text(props, "input_format",
|
|
obs_properties_add_text(props, "input_format",
|
|
|
obs_module_text("InputFormat"), OBS_TEXT_DEFAULT);
|
|
obs_module_text("InputFormat"), OBS_TEXT_DEFAULT);
|
|
|
|
|
|
|
|
- obs_properties_add_bool(props, "force_scale",
|
|
|
|
|
- obs_module_text("ForceFormat"));
|
|
|
|
|
-
|
|
|
|
|
obs_properties_add_bool(props, "hw_decode",
|
|
obs_properties_add_bool(props, "hw_decode",
|
|
|
obs_module_text("HardwareDecode"));
|
|
obs_module_text("HardwareDecode"));
|
|
|
|
|
|
|
@@ -372,6 +390,9 @@ static obs_properties_t *ffmpeg_source_getproperties(void *data)
|
|
|
|
|
|
|
|
obs_property_set_modified_callback(prop, is_advanced_modified);
|
|
obs_property_set_modified_callback(prop, is_advanced_modified);
|
|
|
|
|
|
|
|
|
|
+ obs_properties_add_bool(props, "force_scale",
|
|
|
|
|
+ obs_module_text("ForceFormat"));
|
|
|
|
|
+
|
|
|
prop = obs_properties_add_int(props, "audio_buffer_size",
|
|
prop = obs_properties_add_int(props, "audio_buffer_size",
|
|
|
obs_module_text("AudioBufferSize"), 1, 9999, 1);
|
|
obs_module_text("AudioBufferSize"), 1, 9999, 1);
|
|
|
|
|
|
|
@@ -440,7 +461,7 @@ static void dump_source_info(struct ffmpeg_source *s, const char *input,
|
|
|
"\tis_clear_on_media_end: %s",
|
|
"\tis_clear_on_media_end: %s",
|
|
|
input ? input : "(null)",
|
|
input ? input : "(null)",
|
|
|
input_format ? input_format : "(null)",
|
|
input_format ? input_format : "(null)",
|
|
|
- s->demuxer->options.is_looping ? "yes" : "no",
|
|
|
|
|
|
|
+ s->is_looping ? "yes" : "no",
|
|
|
s->is_forcing_scale ? "yes" : "no",
|
|
s->is_forcing_scale ? "yes" : "no",
|
|
|
s->is_hw_decoding ? "yes" : "no",
|
|
s->is_hw_decoding ? "yes" : "no",
|
|
|
s->is_clear_on_media_end ? "yes" : "no");
|
|
s->is_clear_on_media_end ? "yes" : "no");
|
|
@@ -453,9 +474,37 @@ static void dump_source_info(struct ffmpeg_source *s, const char *input,
|
|
|
"\taudio_buffer_size: %d\n"
|
|
"\taudio_buffer_size: %d\n"
|
|
|
"\tvideo_buffer_size: %d\n"
|
|
"\tvideo_buffer_size: %d\n"
|
|
|
"\tframe_drop: %s",
|
|
"\tframe_drop: %s",
|
|
|
- s->demuxer->options.audio_frame_queue_size,
|
|
|
|
|
- s->demuxer->options.video_frame_queue_size,
|
|
|
|
|
- frame_drop_to_str(s->demuxer->options.frame_drop));
|
|
|
|
|
|
|
+ s->audio_buffer_size,
|
|
|
|
|
+ s->video_buffer_size,
|
|
|
|
|
+ frame_drop_to_str(s->frame_drop));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static void ffmpeg_source_start(struct ffmpeg_source *s)
|
|
|
|
|
+{
|
|
|
|
|
+ if (s->demuxer != NULL)
|
|
|
|
|
+ ff_demuxer_free(s->demuxer);
|
|
|
|
|
+
|
|
|
|
|
+ s->demuxer = ff_demuxer_init();
|
|
|
|
|
+ s->demuxer->options.is_hw_decoding = s->is_hw_decoding;
|
|
|
|
|
+ s->demuxer->options.is_looping = s->is_looping;
|
|
|
|
|
+
|
|
|
|
|
+ ff_demuxer_set_callbacks(&s->demuxer->video_callbacks,
|
|
|
|
|
+ video_frame, NULL,
|
|
|
|
|
+ NULL, NULL, NULL, s);
|
|
|
|
|
+
|
|
|
|
|
+ ff_demuxer_set_callbacks(&s->demuxer->audio_callbacks,
|
|
|
|
|
+ audio_frame, NULL,
|
|
|
|
|
+ NULL, NULL, NULL, s);
|
|
|
|
|
+
|
|
|
|
|
+ if (s->is_advanced) {
|
|
|
|
|
+ s->demuxer->options.audio_frame_queue_size =
|
|
|
|
|
+ s->audio_buffer_size;
|
|
|
|
|
+ s->demuxer->options.video_frame_queue_size =
|
|
|
|
|
+ s->video_buffer_size;
|
|
|
|
|
+ s->demuxer->options.frame_drop = s->frame_drop;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ff_demuxer_open(s->demuxer, s->input, s->input_format);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void ffmpeg_source_update(void *data, obs_data_t *settings)
|
|
static void ffmpeg_source_update(void *data, obs_data_t *settings)
|
|
@@ -465,77 +514,64 @@ static void ffmpeg_source_update(void *data, obs_data_t *settings)
|
|
|
bool is_local_file = obs_data_get_bool(settings, "is_local_file");
|
|
bool is_local_file = obs_data_get_bool(settings, "is_local_file");
|
|
|
bool is_advanced = obs_data_get_bool(settings, "advanced");
|
|
bool is_advanced = obs_data_get_bool(settings, "advanced");
|
|
|
|
|
|
|
|
- bool is_looping;
|
|
|
|
|
char *input;
|
|
char *input;
|
|
|
char *input_format;
|
|
char *input_format;
|
|
|
|
|
|
|
|
|
|
+ bfree(s->input);
|
|
|
|
|
+ bfree(s->input_format);
|
|
|
|
|
+
|
|
|
if (is_local_file) {
|
|
if (is_local_file) {
|
|
|
input = (char *)obs_data_get_string(settings, "local_file");
|
|
input = (char *)obs_data_get_string(settings, "local_file");
|
|
|
input_format = NULL;
|
|
input_format = NULL;
|
|
|
- is_looping = obs_data_get_bool(settings, "looping");
|
|
|
|
|
|
|
+ s->is_looping = obs_data_get_bool(settings, "looping");
|
|
|
} else {
|
|
} else {
|
|
|
input = (char *)obs_data_get_string(settings, "input");
|
|
input = (char *)obs_data_get_string(settings, "input");
|
|
|
input_format = (char *)obs_data_get_string(settings,
|
|
input_format = (char *)obs_data_get_string(settings,
|
|
|
"input_format");
|
|
"input_format");
|
|
|
- is_looping = false;
|
|
|
|
|
|
|
+ s->is_looping = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- s->is_forcing_scale = obs_data_get_bool(settings, "force_scale");
|
|
|
|
|
|
|
+ s->input = input ? bstrdup(input) : NULL;
|
|
|
|
|
+ s->input_format = input_format ? bstrdup(input_format) : NULL;
|
|
|
|
|
+ s->is_advanced = is_advanced;
|
|
|
s->is_hw_decoding = obs_data_get_bool(settings, "hw_decode");
|
|
s->is_hw_decoding = obs_data_get_bool(settings, "hw_decode");
|
|
|
s->is_clear_on_media_end = obs_data_get_bool(settings,
|
|
s->is_clear_on_media_end = obs_data_get_bool(settings,
|
|
|
"clear_on_media_end");
|
|
"clear_on_media_end");
|
|
|
-
|
|
|
|
|
- if (s->demuxer != NULL)
|
|
|
|
|
- ff_demuxer_free(s->demuxer);
|
|
|
|
|
-
|
|
|
|
|
- s->demuxer = ff_demuxer_init();
|
|
|
|
|
- s->demuxer->options.is_hw_decoding = s->is_hw_decoding;
|
|
|
|
|
- s->demuxer->options.is_looping = is_looping;
|
|
|
|
|
|
|
+ s->is_forcing_scale = true;
|
|
|
|
|
|
|
|
if (is_advanced) {
|
|
if (is_advanced) {
|
|
|
- int audio_buffer_size = (int)obs_data_get_int(settings,
|
|
|
|
|
|
|
+ s->audio_buffer_size = (int)obs_data_get_int(settings,
|
|
|
"audio_buffer_size");
|
|
"audio_buffer_size");
|
|
|
- int video_buffer_size = (int)obs_data_get_int(settings,
|
|
|
|
|
|
|
+ s->video_buffer_size = (int)obs_data_get_int(settings,
|
|
|
"video_buffer_size");
|
|
"video_buffer_size");
|
|
|
- enum AVDiscard frame_drop =
|
|
|
|
|
- (enum AVDiscard)obs_data_get_int(settings,
|
|
|
|
|
|
|
+ s->frame_drop = (enum AVDiscard)obs_data_get_int(settings,
|
|
|
"frame_drop");
|
|
"frame_drop");
|
|
|
|
|
+ s->is_forcing_scale = obs_data_get_bool(settings,
|
|
|
|
|
+ "force_scale");
|
|
|
|
|
|
|
|
- if (audio_buffer_size < 1) {
|
|
|
|
|
- audio_buffer_size = 1;
|
|
|
|
|
|
|
+ if (s->audio_buffer_size < 1) {
|
|
|
|
|
+ s->audio_buffer_size = 1;
|
|
|
FF_BLOG(LOG_WARNING, "invalid audio_buffer_size %d",
|
|
FF_BLOG(LOG_WARNING, "invalid audio_buffer_size %d",
|
|
|
- audio_buffer_size);
|
|
|
|
|
|
|
+ s->audio_buffer_size);
|
|
|
}
|
|
}
|
|
|
- if (video_buffer_size < 1) {
|
|
|
|
|
- video_buffer_size = 1;
|
|
|
|
|
|
|
+ if (s->video_buffer_size < 1) {
|
|
|
|
|
+ s->video_buffer_size = 1;
|
|
|
FF_BLOG(LOG_WARNING, "invalid audio_buffer_size %d",
|
|
FF_BLOG(LOG_WARNING, "invalid audio_buffer_size %d",
|
|
|
- audio_buffer_size);
|
|
|
|
|
|
|
+ s->audio_buffer_size);
|
|
|
}
|
|
}
|
|
|
- s->demuxer->options.audio_frame_queue_size = audio_buffer_size;
|
|
|
|
|
- s->demuxer->options.video_frame_queue_size = video_buffer_size;
|
|
|
|
|
|
|
|
|
|
- if (frame_drop < AVDISCARD_NONE || frame_drop > AVDISCARD_ALL) {
|
|
|
|
|
- frame_drop = AVDISCARD_NONE;
|
|
|
|
|
|
|
+ if (s->frame_drop < AVDISCARD_NONE ||
|
|
|
|
|
+ s->frame_drop > AVDISCARD_ALL) {
|
|
|
|
|
+ s->frame_drop = AVDISCARD_DEFAULT;
|
|
|
FF_BLOG(LOG_WARNING, "invalid frame_drop %d",
|
|
FF_BLOG(LOG_WARNING, "invalid frame_drop %d",
|
|
|
- frame_drop);
|
|
|
|
|
|
|
+ s->frame_drop);
|
|
|
}
|
|
}
|
|
|
- s->demuxer->options.frame_drop = frame_drop;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- ff_demuxer_set_callbacks(&s->demuxer->video_callbacks,
|
|
|
|
|
- video_frame, NULL,
|
|
|
|
|
- NULL, NULL, NULL, s);
|
|
|
|
|
-
|
|
|
|
|
- ff_demuxer_set_callbacks(&s->demuxer->audio_callbacks,
|
|
|
|
|
- audio_frame, NULL,
|
|
|
|
|
- NULL, NULL, NULL, s);
|
|
|
|
|
-
|
|
|
|
|
dump_source_info(s, input, input_format, is_advanced);
|
|
dump_source_info(s, input, input_format, is_advanced);
|
|
|
-
|
|
|
|
|
- ff_demuxer_open(s->demuxer, input, input_format);
|
|
|
|
|
|
|
+ ffmpeg_source_start(s);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-
|
|
|
|
|
static const char *ffmpeg_source_getname(void *unused)
|
|
static const char *ffmpeg_source_getname(void *unused)
|
|
|
{
|
|
{
|
|
|
UNUSED_PARAMETER(unused);
|
|
UNUSED_PARAMETER(unused);
|
|
@@ -557,13 +593,14 @@ static void ffmpeg_source_destroy(void *data)
|
|
|
{
|
|
{
|
|
|
struct ffmpeg_source *s = data;
|
|
struct ffmpeg_source *s = data;
|
|
|
|
|
|
|
|
- ff_demuxer_free(s->demuxer);
|
|
|
|
|
|
|
+ if (s->demuxer)
|
|
|
|
|
+ ff_demuxer_free(s->demuxer);
|
|
|
|
|
|
|
|
if (s->sws_ctx != NULL)
|
|
if (s->sws_ctx != NULL)
|
|
|
sws_freeContext(s->sws_ctx);
|
|
sws_freeContext(s->sws_ctx);
|
|
|
- if (s->sws_data != NULL)
|
|
|
|
|
- bfree(s->sws_data);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ bfree(s->sws_data);
|
|
|
|
|
+ bfree(s->input);
|
|
|
|
|
+ bfree(s->input_format);
|
|
|
bfree(s);
|
|
bfree(s);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -574,6 +611,7 @@ struct obs_source_info ffmpeg_source = {
|
|
|
.get_name = ffmpeg_source_getname,
|
|
.get_name = ffmpeg_source_getname,
|
|
|
.create = ffmpeg_source_create,
|
|
.create = ffmpeg_source_create,
|
|
|
.destroy = ffmpeg_source_destroy,
|
|
.destroy = ffmpeg_source_destroy,
|
|
|
|
|
+ .get_defaults = ffmpeg_source_defaults,
|
|
|
.get_properties = ffmpeg_source_getproperties,
|
|
.get_properties = ffmpeg_source_getproperties,
|
|
|
.update = ffmpeg_source_update
|
|
.update = ffmpeg_source_update
|
|
|
};
|
|
};
|