diff --git a/deps/media-playback/media-playback/decode.c b/deps/media-playback/media-playback/decode.c index b82d329fa..d5327b37c 100644 --- a/deps/media-playback/media-playback/decode.c +++ b/deps/media-playback/media-playback/decode.c @@ -338,6 +338,15 @@ bool mp_decode_next(struct mp_decode *d) d->stream->time_base, (AVRational){1, 1000000000}); + if (d->m->speed != 100) { + d->frame_pts = av_rescale_q(d->frame_pts, + (AVRational){1, d->m->speed}, + (AVRational){1, 100}); + duration = av_rescale_q(duration, + (AVRational){1, d->m->speed}, + (AVRational){1, 100}); + } + d->last_duration = duration; d->next_pts = d->frame_pts + duration; } diff --git a/deps/media-playback/media-playback/media.c b/deps/media-playback/media-playback/media.c index 49a1c8f77..e335e2396 100644 --- a/deps/media-playback/media-playback/media.c +++ b/deps/media-playback/media-playback/media.c @@ -271,7 +271,7 @@ static void mp_media_next_audio(mp_media_t *m) for (size_t i = 0; i < MAX_AV_PLANES; i++) audio.data[i] = f->data[i]; - audio.samples_per_sec = f->sample_rate; + audio.samples_per_sec = f->sample_rate * m->speed / 100; audio.speakers = convert_speaker_layout(f->channels); audio.format = convert_sample_format(f->format); audio.frames = f->nb_samples; @@ -687,8 +687,12 @@ bool mp_media_init(mp_media_t *media, const struct mp_media_info *info) media->v_preload_cb = info->v_preload_cb; media->force_range = info->force_range; media->buffering = info->buffering; + media->speed = info->speed; media->is_local_file = info->is_local_file; + if (!info->is_local_file || media->speed < 1 || media->speed > 200) + media->speed = 100; + static bool initialized = false; if (!initialized) { av_register_all(); diff --git a/deps/media-playback/media-playback/media.h b/deps/media-playback/media-playback/media.h index 65346670a..4847d7136 100644 --- a/deps/media-playback/media-playback/media.h +++ b/deps/media-playback/media-playback/media.h @@ -54,6 +54,7 @@ struct mp_media { char *path; char *format_name; int buffering; + int speed; enum AVPixelFormat scale_format; struct SwsContext *swscale; @@ -107,6 +108,7 @@ struct mp_media_info { const char *path; const char *format; int buffering; + int speed; enum video_range_type force_range; bool hardware_decoding; bool is_local_file; diff --git a/plugins/obs-ffmpeg/data/locale/en-US.ini b/plugins/obs-ffmpeg/data/locale/en-US.ini index d315cfcec..4295afa1e 100644 --- a/plugins/obs-ffmpeg/data/locale/en-US.ini +++ b/plugins/obs-ffmpeg/data/locale/en-US.ini @@ -36,6 +36,7 @@ ColorRange.Auto="Auto" ColorRange.Partial="Partial" ColorRange.Full="Full" RestartMedia="Restart Media" +SpeedPercentage="Speed (perecent)" Seekable="Seekable" MediaFileFilter.AllMediaFiles="All Media Files" diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-source.c b/plugins/obs-ffmpeg/obs-ffmpeg-source.c index 46b5eeb3d..730ad6da9 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-source.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-source.c @@ -49,6 +49,7 @@ struct ffmpeg_source { char *input; char *input_format; int buffering_mb; + int speed_percent; bool is_looping; bool is_local_file; bool is_hw_decoding; @@ -72,12 +73,14 @@ static bool is_local_file_modified(obs_properties_t *props, obs_property_t *buffering = obs_properties_get(props, "buffering_mb"); obs_property_t *close = 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_set_visible(input, !enabled); obs_property_set_visible(input_format, !enabled); obs_property_set_visible(buffering, !enabled); obs_property_set_visible(close, enabled); obs_property_set_visible(local_file, enabled); obs_property_set_visible(looping, enabled); + obs_property_set_visible(speed, enabled); obs_property_set_visible(seekable, !enabled); return true; @@ -93,6 +96,7 @@ static void ffmpeg_source_defaults(obs_data_t *settings) obs_data_set_default_bool(settings, "hw_decode", true); #endif obs_data_set_default_int(settings, "buffering_mb", 2); + obs_data_set_default_int(settings, "speed_percent", 100); } static const char *media_filter = @@ -171,6 +175,9 @@ static obs_properties_t *ffmpeg_source_getproperties(void *data) obs_property_set_long_description(prop, obs_module_text("CloseFileWhenInactive.ToolTip")); + obs_properties_add_int_slider(props, "speed_percent", + obs_module_text("SpeedPercentage"), 1, 200, 1); + prop = obs_properties_add_list(props, "color_range", obs_module_text("ColorRange"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); @@ -193,6 +200,7 @@ static void dump_source_info(struct ffmpeg_source *s, const char *input, "settings:\n" "\tinput: %s\n" "\tinput_format: %s\n" + "\tspeed: %d\n" "\tis_looping: %s\n" "\tis_hw_decoding: %s\n" "\tis_clear_on_media_end: %s\n" @@ -200,6 +208,7 @@ static void dump_source_info(struct ffmpeg_source *s, const char *input, "\tclose_when_inactive: %s", input ? input : "(null)", input_format ? input_format : "(null)", + s->speed_percent, s->is_looping ? "yes" : "no", s->is_hw_decoding ? "yes" : "no", s->is_clear_on_media_end ? "yes" : "no", @@ -251,6 +260,7 @@ static void ffmpeg_source_open(struct ffmpeg_source *s) .path = s->input, .format = s->input_format, .buffering = s->buffering_mb * 1024 * 1024, + .speed = s->speed_percent, .force_range = s->range, .hardware_decoding = s->is_hw_decoding, .is_local_file = s->is_local_file || s->seekable @@ -328,9 +338,13 @@ static void ffmpeg_source_update(void *data, obs_data_t *settings) s->range = (enum video_range_type)obs_data_get_int(settings, "color_range"); s->buffering_mb = (int)obs_data_get_int(settings, "buffering_mb"); + s->speed_percent = (int)obs_data_get_int(settings, "speed_percent"); s->is_local_file = is_local_file; s->seekable = obs_data_get_bool(settings, "seekable"); + if (s->speed_percent < 1 || s->speed_percent > 200) + s->speed_percent = 100; + if (s->media_valid) { mp_media_free(&s->media); s->media_valid = false;