diff --git a/Pulsus/FFmpeg/FFmpegHelper.cs b/Pulsus/FFmpeg/FFmpegHelper.cs index 4007328..fe4b226 100644 --- a/Pulsus/FFmpeg/FFmpegHelper.cs +++ b/Pulsus/FFmpeg/FFmpegHelper.cs @@ -91,13 +91,7 @@ public static byte[] ImageFromFile(string path, out int width, out int height, o height = video.height; bytesPerPixel = 4; - if (video.isVideo) - { - video.ReadNextFrame(); - return video.imageBytes; - } - else - return video.ReadFrames(); + return video.ReadFrame(); } } diff --git a/Pulsus/FFmpeg/FFmpegVideo.cs b/Pulsus/FFmpeg/FFmpegVideo.cs index 7c2bf11..afad06c 100644 --- a/Pulsus/FFmpeg/FFmpegVideo.cs +++ b/Pulsus/FFmpeg/FFmpegVideo.cs @@ -105,13 +105,17 @@ public void Update(double deltaTime) } } - public byte[] ReadFrames() + public byte[] ReadFrame() { if (!ffContext.ReadFrame()) return null; - ffContext.GetFrameData(ref bytes, 0); + ffContext.GetFrameData(ref bytes, 0); + nextFramePts = ffContext.framePts; + presentedFrames++; + decodedFrames++; + return bytes; } @@ -147,7 +151,7 @@ private void LoadThread() } } - public bool ReadNextFrame() + private bool ReadNextFrame() { // decoder is one frame ahead of presentation if (decodedFrames <= presentedFrames) diff --git a/Pulsus/Gameplay/BGAObject.cs b/Pulsus/Gameplay/BGAObject.cs index 8ba7a27..51d3934 100644 --- a/Pulsus/Gameplay/BGAObject.cs +++ b/Pulsus/Gameplay/BGAObject.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using Pulsus.FFmpeg; using Pulsus.Graphics; @@ -8,51 +7,18 @@ namespace Pulsus.Gameplay public class BGAObject : IDisposable { public Texture2D texture { get; private set; } - public string filename { get; private set; } + public string path { get; private set; } public string name { get; private set; } - private FFmpegVideo video; + public FFmpegVideo video { get; private set; } - public bool isVideo { get { return video != null && video.isVideo; } } - public double frametime { get { return video == null ? 0.0 : video.frametime; } } + public bool isVideo { get { return video != null; } } + public double frametime { get { return video.frametime; } } public bool loaded { get { return texture != null; } } - // alternate paths where to look up missing files - static string[] lookupPaths = + public BGAObject(string path, string name) { - "", // current directory - "bg/", - "../", // previous directory (compatibility fix for charts in sub-folders) - }; - - static string[] lookupExtensions = - { - // image formats - ".bmp", - ".png", - ".jpg", - ".tga", - - // video formats - ".mpg", - ".avi", - ".mp4", - ".flv", - ".mp4", - ".mkv", - ".wmv", - ".ogv", - ".webm", - ".mov", - ".swf", - ".3gp", - ".asf", - ".m4v", - }; - - public BGAObject(string filename, string name) - { - this.filename = filename; + this.path = path; this.name = name; } @@ -64,65 +30,33 @@ public void Dispose() video.Dispose(); } - public bool Load(string basePath = "") + public void SetVideo(FFmpegVideo video) { - string fullPath = Utility.FindRealFile(Path.Combine(basePath, filename), lookupPaths, lookupExtensions); - if (!File.Exists(fullPath)) - { - Log.Warning("BGA file not found: " + filename); - return false; - } - - video = new FFmpegVideo(); - try - { - video.Load(fullPath); - texture = new Texture2D(video.width, video.height); - video.OnNextFrame += (data) => texture.SetData(data); - } - catch when (Path.GetExtension(filename).ToLower() == ".lua") - { - // scripted background are not supported (yet?) - Log.Error("Failed to load BGA '" + filename + "', scripted BGA not supported"); - return false; - } - catch (ApplicationException e) - { - Log.Error("Failed to load BGA '" + filename + "': " + e.Message); - - if (video != null) - video.Dispose(); - video = null; - - return false; - } - - if (isVideo) - { - // preload first frame of the video - video.ReadNextFrame(); - } - else - { - // fully load image files - video.OnNextFrame(video.ReadFrames()); - video.Dispose(); - video = null; - } - - return true; + this.video = video; + video.OnNextFrame = UpdateTexture; } public void Start() { - if (isVideo) + if (video != null) video.Start(); } public void Update(double deltaTime) { - if (isVideo) + if (video != null) video.Update(deltaTime); } + + private void UpdateTexture(byte[] data) + { + if (texture == null) + texture = new Texture2D(video.width, video.height); + + texture.SetData(data); + + if (!video.isVideo) + video = null; + } } } \ No newline at end of file diff --git a/Pulsus/Gameplay/EventPlayers/Loader.cs b/Pulsus/Gameplay/EventPlayers/Loader.cs index 905e439..b52a5b4 100644 --- a/Pulsus/Gameplay/EventPlayers/Loader.cs +++ b/Pulsus/Gameplay/EventPlayers/Loader.cs @@ -1,9 +1,10 @@ using System; -using System.Collections.Generic; using System.Collections.Concurrent; +using System.Collections.Generic; using System.IO; using System.Threading; using Pulsus.Audio; +using Pulsus.FFmpeg; namespace Pulsus.Gameplay { @@ -15,6 +16,7 @@ public class Loader : EventPlayer public bool skipBGA = false; AudioEngine audio; + object rendererLock = new object(); ConcurrentQueue soundQueue = new ConcurrentQueue(); ConcurrentQueue bgaQueue = new ConcurrentQueue(); @@ -41,6 +43,31 @@ public class Loader : EventPlayer ".m4a", }; + static string[] lookupImageExtensions = + { + // image formats + ".bmp", + ".png", + ".jpg", + ".tga", + + // video formats + ".gif", + ".mpg", + ".avi", + ".mp4", + ".flv", + ".mkv", + ".wmv", + ".ogv", + ".webm", + ".mov", + ".swf", + ".3gp", + ".asf", + ".m4v", + }; + public Loader(Chart chart, AudioEngine audio) : base(chart) { @@ -238,6 +265,46 @@ private void LoadSound(SoundObject soundObject) Log.Error("Sound file not found: " + soundObject.soundFile.path); } + private void LoadBGA(BGAObject bgaObject) + { + string path = Path.Combine(basePath, bgaObject.path); + path = Utility.FindRealFile(path, lookupPaths, lookupImageExtensions); + if (File.Exists(path)) + { + if (Path.GetExtension(bgaObject.path).ToLower() == ".lua") + { + Log.Error("Failed to load BGA '" + bgaObject.path + "', scripted BGAs are not supported"); + return; + } + + FFmpegVideo video = new FFmpegVideo(); + try + { + video.Load(path); + + byte[] bytes = video.ReadFrame(); + + bgaObject.SetVideo(video); + lock (rendererLock) + video.OnNextFrame(bytes); + } + catch (ThreadAbortException) + { + } + catch (Exception e) + { + Log.Error("Failed to load BGA '" + Path.GetFileName(bgaObject.path) + "': " + e.Message); + } + finally + { + if (!video.isVideo) + video.Dispose(); + } + } + else + Log.Warning("BGA file not found: " + bgaObject.path); + } + private void LoadThread() { try @@ -253,7 +320,7 @@ private void LoadThread() if (sound != null && !sound.loaded) LoadSound(sound); else if (bga != null && !bga.loaded) - bga.Load(basePath); + LoadBGA(bga); else if (!playing && soundQueue.Count == 0 && bgaQueue.Count == 0) break; }