using System; using System.Collections.Generic; using BizHawk.Emulation.Common; namespace BizHawk.Client.EmuHawk { public interface IVideoWriter : IDisposable { /// /// sets the codec token to be used for video compression /// void SetVideoCodecToken(IDisposable token); /// /// sets to a default video codec token without calling any UI - for automated dumping /// void SetDefaultVideoCodecToken(); /// /// Returns whether this VideoWriter dumps audio /// bool UsesAudio { get; } /// /// Returns whether this VideoWriter dumps video /// bool UsesVideo { get; } // why no OpenFile(IEnumerator) ? // different video writers may have different ideas of how and why splitting is to occur /// /// opens a recording stream /// set a video codec token first. /// void OpenFile(string baseName); /// /// close recording stream /// void CloseFile(); /// /// tells which emulation frame we're on. Happens before AddFrame() or AddSamples() /// void SetFrame(int frame); /// /// adds a frame to the stream /// void AddFrame(IVideoProvider source); /// /// adds audio samples to the stream /// no attempt is made to sync this to the video /// recommendation: try not to have the size or pacing of the audio chunks be too "weird" /// void AddSamples(short[] samples); /// /// obtain a set of recording compression parameters /// return null on user cancel /// /// hwnd to attach to if the user is shown config dialog /// codec token, dispose of it when you're done with it IDisposable AcquireVideoCodecToken(System.Windows.Forms.IWin32Window hwnd); /// /// set framerate to fpsNum/fpsDen (assumed to be unchanging over the life of the stream) /// void SetMovieParameters(int fpsNum, int fpsDen); /// /// set resolution parameters (width x height) /// must be set before file is opened /// can be changed in future /// should always match IVideoProvider /// void SetVideoParameters(int width, int height); /// /// set audio parameters. cannot change later /// void SetAudioParameters(int sampleRate, int channels, int bits); /// /// set metadata parameters; should be called before opening file /// ok to not set at all, if not applicable /// /// The name of the game loaded /// Authors on movie file /// Length of movie file in milliseconds /// Number of rerecords on movie file void SetMetaData(string gameName, string authors, ulong lengthMs, ulong rerecords); /// /// what default extension this writer would like to put on its output /// string DesiredExtension(); } [AttributeUsage(AttributeTargets.Class)] public sealed class VideoWriterAttribute : Attribute { public string ShortName { get; } public string Name { get; } public string Description { get; } public VideoWriterAttribute(string shortName, string name, string description) { ShortName = shortName; Name = name; Description = description; } } [AttributeUsage(AttributeTargets.Class)] public sealed class VideoWriterIgnoreAttribute : Attribute { } public class VideoWriterInfo { public VideoWriterAttribute Attribs { get; } private readonly Type _type; public VideoWriterInfo(VideoWriterAttribute attribs, Type type) { _type = type; Attribs = attribs; } public IVideoWriter Create() => (IVideoWriter)Activator.CreateInstance(_type); public override string ToString() => Attribs.Name; } /// /// contains methods to find all IVideoWriter /// public static class VideoWriterInventory { private static readonly Dictionary VideoWriters = new Dictionary(); static VideoWriterInventory() { foreach (Type t in typeof(VideoWriterInventory).Assembly.GetTypes()) { if (!t.IsInterface && typeof(IVideoWriter).IsAssignableFrom(t) && !t.IsAbstract && t.GetCustomAttributes(typeof(VideoWriterIgnoreAttribute), false).Length == 0) { var a = (VideoWriterAttribute)t.GetCustomAttributes(typeof(VideoWriterAttribute), false)[0]; VideoWriters.Add(a.ShortName, new VideoWriterInfo(a, t)); } } } public static IEnumerable GetAllWriters() => VideoWriters.Values; /// /// find an IVideoWriter by its short name /// public static IVideoWriter GetVideoWriter(string name) { return VideoWriters.TryGetValue(name, out var ret) ? ret.Create() : null; } } }