Render video as a part of the storyboard

This commit is contained in:
voidedWarranties 2020-03-07 21:32:03 -08:00
parent d68d7edea3
commit 76c832518f
21 changed files with 136 additions and 719 deletions

View File

@ -6,7 +6,6 @@
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO;
@ -51,8 +50,6 @@ public WaveformTestBeatmap(AudioManager audioManager, Beatmap beatmap)
protected override Texture GetBackground() => null;
protected override VideoSprite GetVideo() => null;
protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(firstAudioFile));
protected override Track GetTrack() => trackStore.Get(firstAudioFile);

View File

@ -14,7 +14,6 @@
using osu.Framework.Audio.Track;
using osu.Framework.Extensions;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
using osu.Framework.Lists;
using osu.Framework.Logging;
using osu.Framework.Platform;
@ -403,7 +402,6 @@ public DummyConversionBeatmap(IBeatmap beatmap)
protected override IBeatmap GetBeatmap() => beatmap;
protected override Texture GetBackground() => null;
protected override VideoSprite GetVideo() => null;
protected override Track GetTrack() => null;
}

View File

@ -6,7 +6,6 @@
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
using osu.Framework.IO.Stores;
using osu.Framework.Logging;
using osu.Game.Beatmaps.Formats;
@ -67,24 +66,6 @@ protected override Texture GetBackground()
}
}
protected override VideoSprite GetVideo()
{
if (Metadata?.VideoFile == null)
return null;
try
{
var stream = textureStore.GetStream(getPathForFile(Metadata.VideoFile));
return stream == null ? null : new VideoSprite(stream);
}
catch (Exception e)
{
Logger.Error(e, "Video failed to load");
return null;
}
}
protected override Track GetTrack()
{
try

View File

@ -52,7 +52,6 @@ public string AuthorString
public int PreviewTime { get; set; }
public string AudioFile { get; set; }
public string BackgroundFile { get; set; }
public int VideoOffset { get; set; }
public string VideoFile { get; set; }
public override string ToString() => $"{Artist} - {Title} ({Author})";
@ -84,7 +83,6 @@ public bool Equals(BeatmapMetadata other)
&& PreviewTime == other.PreviewTime
&& AudioFile == other.AudioFile
&& BackgroundFile == other.BackgroundFile
&& VideoOffset == other.VideoOffset
&& VideoFile == other.VideoFile;
}
}

View File

@ -7,7 +7,6 @@
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
@ -45,8 +44,6 @@ public DummyWorkingBeatmap(AudioManager audio, TextureStore textures)
protected override Texture GetBackground() => textures?.Get(@"Backgrounds/bg4");
protected override VideoSprite GetVideo() => null;
protected override Track GetTrack() => GetVirtualTrack();
private class DummyRulesetInfo : RulesetInfo

View File

@ -303,11 +303,6 @@ private void handleEvent(string line)
beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[2]);
break;
case LegacyEventType.Video:
beatmap.BeatmapInfo.Metadata.VideoOffset = Parsing.ParseInt(split[1]);
beatmap.BeatmapInfo.Metadata.VideoFile = CleanFilename(split[2]);
break;
case LegacyEventType.Break:
double start = getOffsetTime(Parsing.ParseDouble(split[1]));

View File

@ -133,9 +133,6 @@ private void handleEvents(TextWriter writer)
if (!string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.BackgroundFile))
writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Background},0,\"{beatmap.BeatmapInfo.Metadata.BackgroundFile}\",0,0"));
if (!string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.VideoFile))
writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Video},{beatmap.BeatmapInfo.Metadata.VideoOffset},\"{beatmap.BeatmapInfo.Metadata.VideoFile}\",0,0"));
foreach (var b in beatmap.Breaks)
writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Break},{b.StartTime},{b.EndTime}"));
}

View File

@ -5,13 +5,13 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Utils;
using osu.Game.Beatmaps.Legacy;
using osu.Game.IO;
using osu.Game.Storyboards;
using osu.Game.Beatmaps.Legacy;
using osu.Framework.Utils;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Beatmaps.Formats
{
@ -88,6 +88,15 @@ private void handleEvents(string line)
switch (type)
{
case LegacyEventType.Video:
{
var offset = Parsing.ParseInt(split[1]);
var filename = CleanFilename(split[2]);
storyboard.GetLayer("Video").Add(new StoryboardVideo(filename, offset));
break;
}
case LegacyEventType.Sprite:
{
var layer = parseLayer(split[1]);

View File

@ -4,7 +4,6 @@
using System.Collections.Generic;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
@ -26,11 +25,6 @@ public interface IWorkingBeatmap
/// </summary>
Texture Background { get; }
/// <summary>
/// Retrieves the video background file for this <see cref="WorkingBeatmap"/>.
/// </summary>
VideoSprite Video { get; }
/// <summary>
/// Retrieves the audio track for this <see cref="WorkingBeatmap"/>.
/// </summary>

View File

@ -1,23 +1,22 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic;
using osu.Game.Storyboards;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Framework.Logging;
using osu.Framework.Statistics;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.UI;
using osu.Game.Skinning;
using osu.Framework.Graphics.Video;
using osu.Framework.Logging;
using osu.Game.Storyboards;
namespace osu.Game.Beatmaps
{
@ -208,10 +207,6 @@ public IBeatmap Beatmap
protected abstract Texture GetBackground();
private readonly RecyclableLazy<Texture> background;
public VideoSprite Video => GetVideo();
protected abstract VideoSprite GetVideo();
public bool TrackLoaded => track.IsResultAvailable;
public Track Track => track.Value;
protected abstract Track GetTrack();

View File

@ -1,508 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20200307015200_AddVideoOffset")]
partial class AddVideoOffset
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.6-servicing-10079");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<double>("SliderMultiplier");
b.Property<double>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<double>("AudioLeadIn");
b.Property<double>("BPM");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<double>("Length");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<int>("Status");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash");
b.HasIndex("MD5Hash");
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.Property<string>("VideoFile");
b.Property<int>("VideoOffset");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<DateTimeOffset>("DateAdded");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.Property<int>("Status");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Key")
.HasColumnName("Key");
b.Property<int?>("RulesetID");
b.Property<int?>("SkinInfoID");
b.Property<string>("StringValue")
.HasColumnName("Value");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("SkinInfoID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.Property<string>("ShortName");
b.HasKey("ID");
b.HasIndex("Available");
b.HasIndex("ShortName")
.IsUnique();
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int?>("ScoreInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("ScoreInfoID");
b.ToTable("ScoreFileInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<double>("Accuracy")
.HasColumnType("DECIMAL(1,4)");
b.Property<int>("BeatmapInfoID");
b.Property<int>("Combo");
b.Property<DateTimeOffset>("Date");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int>("MaxCombo");
b.Property<string>("ModsJson")
.HasColumnName("Mods");
b.Property<long?>("OnlineScoreID");
b.Property<double?>("PP");
b.Property<int>("Rank");
b.Property<int>("RulesetID");
b.Property<string>("StatisticsJson")
.HasColumnName("Statistics");
b.Property<long>("TotalScore");
b.Property<long?>("UserID")
.HasColumnName("UserID");
b.Property<string>("UserString")
.HasColumnName("User");
b.HasKey("ID");
b.HasIndex("BeatmapInfoID");
b.HasIndex("OnlineScoreID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("ScoreInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int>("SkinInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("SkinInfoID");
b.ToTable("SkinFileInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Creator");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<string>("Name");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.ToTable("SkinInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Settings")
.HasForeignKey("SkinInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Scoring.ScoreInfo")
.WithMany("Files")
.HasForeignKey("ScoreInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap")
.WithMany("Scores")
.HasForeignKey("BeatmapInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Files")
.HasForeignKey("SkinInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,23 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations
{
public partial class AddVideoOffset : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "VideoOffset",
table: "BeatmapMetadata",
nullable: false,
defaultValue: 0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "VideoOffset",
table: "BeatmapMetadata");
}
}
}

View File

@ -39,7 +39,6 @@ public void ApplyToPlayer(Player player)
{
player.Background.EnableUserDim.Value = false;
player.DimmableVideo.IgnoreUserSettings.Value = true;
player.DimmableStoryboard.IgnoreUserSettings.Value = true;
player.BreakOverlay.Hide();

View File

@ -1,118 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Video;
using osu.Framework.Timing;
using osu.Game.Graphics.Containers;
using osuTK.Graphics;
namespace osu.Game.Screens.Play
{
public class DimmableVideo : UserDimContainer
{
private readonly VideoSprite video;
private readonly int offset;
private DrawableVideo drawableVideo;
public DimmableVideo(VideoSprite video, int offset)
{
this.video = video;
this.offset = offset;
}
[BackgroundDependencyLoader]
private void load()
{
initializeVideo(false);
}
protected override void LoadComplete()
{
ShowVideo.BindValueChanged(_ => initializeVideo(true), true);
base.LoadComplete();
}
protected override bool ShowDimContent => IgnoreUserSettings.Value || (ShowVideo.Value && DimLevel < 1);
private void initializeVideo(bool async)
{
if (video == null)
return;
if (drawableVideo != null)
return;
if (!ShowVideo.Value && !IgnoreUserSettings.Value)
return;
drawableVideo = new DrawableVideo(video, offset);
if (async)
LoadComponentAsync(drawableVideo, Add);
else
Add(drawableVideo);
}
private class DrawableVideo : Container
{
private readonly Drawable cover;
private readonly int offset;
private readonly ManualClock videoClock;
private bool videoStarted;
public DrawableVideo(VideoSprite video, int offset)
{
this.offset = offset;
RelativeSizeAxes = Axes.Both;
Masking = true;
video.RelativeSizeAxes = Axes.Both;
video.FillMode = FillMode.Fit;
video.Anchor = Anchor.Centre;
video.Origin = Anchor.Centre;
videoClock = new ManualClock();
video.Clock = new FramedClock(videoClock);
AddRangeInternal(new[]
{
video,
cover = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
});
}
[BackgroundDependencyLoader]
private void load(GameplayClock clock)
{
if (clock != null)
Clock = clock;
}
protected override void Update()
{
if (videoClock != null && Clock.CurrentTime > offset)
{
if (!videoStarted)
{
cover.FadeOut(500);
videoStarted = true;
}
// handle seeking before the video starts (break skipping, replay seek)
videoClock.CurrentTime = Clock.CurrentTime - offset;
}
base.Update();
}
}
}
}

View File

@ -117,8 +117,15 @@ private void load(OsuConfigManager config)
startTime = Math.Min(startTime, firstHitObjectTime - beatmap.BeatmapInfo.AudioLeadIn);
// some beatmaps have no AudioLeadIn but the video starts before the first object
if (beatmap.Video != null && beatmap.Metadata.VideoOffset != 0)
startTime = Math.Min(startTime, beatmap.Metadata.VideoOffset);
var videoLayer = beatmap.Storyboard.GetLayer("Video");
if (videoLayer.Elements.Any())
{
var videoOffset = videoLayer.Elements.First().StartTime;
if (videoOffset != 0)
startTime = Math.Min(startTime, videoOffset);
}
Seek(startTime);

View File

@ -85,7 +85,6 @@ public class Player : ScreenWithBeatmapBackground
protected GameplayClockContainer GameplayClockContainer { get; private set; }
public DimmableStoryboard DimmableStoryboard { get; private set; }
public DimmableVideo DimmableVideo { get; private set; }
[Cached]
[Cached(Type = typeof(IBindable<IReadOnlyList<Mod>>))]
@ -189,7 +188,6 @@ private void load(AudioManager audio, OsuConfigManager config)
private void addUnderlayComponents(Container target)
{
target.Add(DimmableVideo = new DimmableVideo(Beatmap.Value.Video, Beatmap.Value.Metadata.VideoOffset) { RelativeSizeAxes = Axes.Both });
target.Add(DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both });
}
@ -549,7 +547,6 @@ public override void OnEntering(IScreen last)
// bind component bindables.
Background.IsBreakTime.BindTo(BreakOverlay.IsBreakTime);
DimmableStoryboard.IsBreakTime.BindTo(BreakOverlay.IsBreakTime);
DimmableVideo.IsBreakTime.BindTo(BreakOverlay.IsBreakTime);
Background.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);
DimmableStoryboard.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);

View File

@ -0,0 +1,82 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Screens.Play;
namespace osu.Game.Storyboards.Drawables
{
public class DrawableStoryboardVideo : Container
{
public readonly StoryboardVideo Video;
private VideoSprite videoSprite;
private ManualClock videoClock;
private GameplayClock clock;
private bool videoStarted;
public override bool RemoveWhenNotAlive => false;
public DrawableStoryboardVideo(StoryboardVideo video)
{
Video = video;
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(GameplayClock clock, IBindable<WorkingBeatmap> beatmap, TextureStore textureStore)
{
if (clock != null)
this.clock = clock;
var path = beatmap.Value.BeatmapSetInfo?.Files?.Find(f => f.Filename.Equals(Video.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath;
if (path == null)
return;
var stream = textureStore.GetStream(path);
if (stream == null)
return;
AddInternal(videoSprite = new VideoSprite(stream)
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AlwaysPresent = true,
Alpha = 0
});
videoClock = new ManualClock();
videoSprite.Clock = new FramedClock(videoClock);
}
protected override void Update()
{
if (clock.CurrentTime > Video.StartTime)
{
if (!videoStarted)
{
videoSprite.FadeIn(500);
videoStarted = true;
}
// handle seeking before the video starts (break skipping, replay seek)
videoClock.CurrentTime = clock.CurrentTime - Video.StartTime;
}
base.Update();
}
}
}

View File

@ -21,6 +21,7 @@ public class Storyboard
public Storyboard()
{
layers.Add("Video", new StoryboardLayer("Video", 4));
layers.Add("Background", new StoryboardLayer("Background", 3));
layers.Add("Fail", new StoryboardLayer("Fail", 2) { EnabledWhenPassing = false, });
layers.Add("Pass", new StoryboardLayer("Pass", 1) { EnabledWhenFailing = false, });
@ -53,7 +54,7 @@ public bool ReplacesBackground
public DrawableStoryboard CreateDrawable(WorkingBeatmap working = null)
{
var drawable = new DrawableStoryboard(this);
drawable.Width = drawable.Height * (BeatmapInfo.WidescreenStoryboard ? 16 / 9f : 4 / 3f);
drawable.Width = drawable.Height * (BeatmapInfo.WidescreenStoryboard || GetLayer("Video").Elements.Any() ? 16 / 9f : 4 / 3f);
return drawable;
}
}

View File

@ -0,0 +1,25 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Storyboards.Drawables;
namespace osu.Game.Storyboards
{
public class StoryboardVideo : IStoryboardElement
{
public string Path { get; }
public bool IsDrawable => true;
public double StartTime { get; }
public StoryboardVideo(string path, int offset)
{
Path = path;
StartTime = offset;
}
public Drawable CreateDrawable() => new DrawableStoryboardVideo(this);
}
}

View File

@ -10,7 +10,6 @@
using NUnit.Framework;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO;
@ -207,8 +206,6 @@ public ConversionWorkingBeatmap(IBeatmap beatmap)
protected override Texture GetBackground() => throw new NotImplementedException();
protected override VideoSprite GetVideo() => throw new NotImplementedException();
protected override Track GetTrack() => throw new NotImplementedException();
protected override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)

View File

@ -3,7 +3,6 @@
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
using osu.Game.Beatmaps;
using osu.Game.Storyboards;
@ -32,8 +31,6 @@ public TestWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
protected override Texture GetBackground() => null;
protected override VideoSprite GetVideo() => null;
protected override Track GetTrack() => null;
}
}