-
-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
using Beutl.Media.Encoding; | ||
|
||
#if MF_BUILD_IN | ||
namespace Beutl.Embedding.MediaFoundation.Encoding; | ||
#else | ||
namespace Beutl.Extensions.MediaFoundation.Encoding; | ||
#endif | ||
|
||
public class MFEncoderInfo : IEncoderInfo | ||
Check warning on line 9 in src/Beutl.Extensions.MediaFoundation/Encoding/MFEncoderInfo.cs GitHub Actions / Analyze (csharp)
|
||
{ | ||
public string Name => "Media Foundation Encoder"; | ||
|
||
public MediaWriter? Create(string file, VideoEncoderSettings videoConfig, AudioEncoderSettings audioConfig) | ||
Check warning on line 13 in src/Beutl.Extensions.MediaFoundation/Encoding/MFEncoderInfo.cs GitHub Actions / Analyze (csharp)
Check warning on line 13 in src/Beutl.Extensions.MediaFoundation/Encoding/MFEncoderInfo.cs GitHub Actions / Analyze (csharp)
Check warning on line 13 in src/Beutl.Extensions.MediaFoundation/Encoding/MFEncoderInfo.cs GitHub Actions / build
|
||
{ | ||
if (videoConfig is not MFVideoEncoderSettings mfVideoConfig) | ||
return null; | ||
|
||
return new MFWriter(file, mfVideoConfig, audioConfig); | ||
} | ||
|
||
public IEnumerable<string> SupportExtensions() | ||
{ | ||
yield return ".mp4"; | ||
yield return ".mov"; | ||
yield return ".m4v"; | ||
yield return ".avi"; | ||
yield return ".wmv"; | ||
yield return ".sami"; | ||
yield return ".smi"; | ||
yield return ".adts"; | ||
yield return ".asf"; | ||
yield return ".3gp"; | ||
yield return ".3gp2"; | ||
yield return ".3gpp"; | ||
} | ||
|
||
public VideoEncoderSettings DefaultVideoConfig() => new MFVideoEncoderSettings(); | ||
|
||
public AudioEncoderSettings DefaultAudioConfig() => new AudioEncoderSettings(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
using Beutl.Extensibility; | ||
using Beutl.Media.Encoding; | ||
|
||
#if MF_BUILD_IN | ||
namespace Beutl.Embedding.MediaFoundation.Encoding; | ||
#else | ||
namespace Beutl.Extensions.MediaFoundation.Encoding; | ||
#endif | ||
|
||
[Export] | ||
public class MFEncodingExtension : EncodingExtension | ||
Check warning on line 11 in src/Beutl.Extensions.MediaFoundation/Encoding/MFEncodingExtension.cs GitHub Actions / Analyze (csharp)
|
||
{ | ||
public override string Name => "Media Foundation Encoder"; | ||
|
||
public override string DisplayName => "Media Foundation Encoder"; | ||
|
||
public override IEncoderInfo GetEncoderInfo() => new MFEncoderInfo(); | ||
|
||
public override void Load() | ||
{ | ||
if (OperatingSystem.IsWindows()) | ||
{ | ||
EncoderRegistry.Register(GetEncoderInfo()); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using Beutl.Media.Encoding; | ||
|
||
#if MF_BUILD_IN | ||
namespace Beutl.Embedding.MediaFoundation.Encoding; | ||
#else | ||
namespace Beutl.Extensions.MediaFoundation.Encoding; | ||
#endif | ||
|
||
public class MFVideoEncoderSettings : VideoEncoderSettings | ||
{ | ||
public MFVideoFormat Format { get; set; } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
using SharpDX.MediaFoundation; | ||
|
||
#if MF_BUILD_IN | ||
namespace Beutl.Embedding.MediaFoundation.Encoding; | ||
#else | ||
namespace Beutl.Extensions.MediaFoundation.Encoding; | ||
#endif | ||
|
||
// MFVideoFormatとVideoFormatGuidsを相互に変換するクラス | ||
public static class MFVideoFormatExtension | ||
{ | ||
// MFVideoFormatからVideoFormatGuidsに変換する | ||
public static Guid ToVideoFormatGuid(this MFVideoFormat format) | ||
{ | ||
return format switch | ||
{ | ||
MFVideoFormat.Wmv1 => VideoFormatGuids.Wmv1, | ||
MFVideoFormat.Wmv2 => VideoFormatGuids.Wmv2, | ||
MFVideoFormat.Wmv3 => VideoFormatGuids.Wmv3, | ||
MFVideoFormat.Dvc => VideoFormatGuids.Dvc, | ||
MFVideoFormat.Dv50 => VideoFormatGuids.Dv50, | ||
MFVideoFormat.Dv25 => VideoFormatGuids.Dv25, | ||
MFVideoFormat.H263 => VideoFormatGuids.H263, | ||
MFVideoFormat.H264 => VideoFormatGuids.H264, | ||
MFVideoFormat.H265 => VideoFormatGuids.H265, | ||
MFVideoFormat.Hevc => VideoFormatGuids.Hevc, | ||
MFVideoFormat.HevcEs => VideoFormatGuids.HevcEs, | ||
MFVideoFormat.Vp80 => VideoFormatGuids.Vp80, | ||
MFVideoFormat.Vp90 => VideoFormatGuids.Vp90, | ||
MFVideoFormat.MultisampledS2 => VideoFormatGuids.MultisampledS2, | ||
MFVideoFormat.M4S2 => VideoFormatGuids.M4S2, | ||
MFVideoFormat.Wvc1 => VideoFormatGuids.Wvc1, | ||
MFVideoFormat.P010 => VideoFormatGuids.P010, | ||
MFVideoFormat.AI44 => VideoFormatGuids.AI44, | ||
MFVideoFormat.Dvh1 => VideoFormatGuids.Dvh1, | ||
MFVideoFormat.Dvhd => VideoFormatGuids.Dvhd, | ||
MFVideoFormat.MultisampledS1 => VideoFormatGuids.MultisampledS1, | ||
MFVideoFormat.Mp43 => VideoFormatGuids.Mp43, | ||
MFVideoFormat.Mp4s => VideoFormatGuids.Mp4s, | ||
MFVideoFormat.Mp4v => VideoFormatGuids.Mp4v, | ||
MFVideoFormat.Mpg1 => VideoFormatGuids.Mpg1, | ||
MFVideoFormat.Mjpg => VideoFormatGuids.Mjpg, | ||
MFVideoFormat.Dvsl => VideoFormatGuids.Dvsl, | ||
MFVideoFormat.YUY2 => VideoFormatGuids.YUY2, | ||
MFVideoFormat.Yv12 => VideoFormatGuids.Yv12, | ||
MFVideoFormat.P016 => VideoFormatGuids.P016, | ||
MFVideoFormat.P210 => VideoFormatGuids.P210, | ||
MFVideoFormat.P216 => VideoFormatGuids.P216, | ||
MFVideoFormat.I420 => VideoFormatGuids.I420, | ||
MFVideoFormat.Dvsd => VideoFormatGuids.Dvsd, | ||
MFVideoFormat.Y42T => VideoFormatGuids.Y42T, | ||
MFVideoFormat.NV12 => VideoFormatGuids.NV12, | ||
MFVideoFormat.NV11 => VideoFormatGuids.NV11, | ||
MFVideoFormat.Y210 => VideoFormatGuids.Y210, | ||
MFVideoFormat.Y216 => VideoFormatGuids.Y216, | ||
MFVideoFormat.Y410 => VideoFormatGuids.Y410, | ||
MFVideoFormat.Y416 => VideoFormatGuids.Y416, | ||
MFVideoFormat.Y41P => VideoFormatGuids.Y41P, | ||
MFVideoFormat.Y41T => VideoFormatGuids.Y41T, | ||
MFVideoFormat.Yvu9 => VideoFormatGuids.Yvu9, | ||
MFVideoFormat.Yvyu => VideoFormatGuids.Yvyu, | ||
MFVideoFormat.Iyuv => VideoFormatGuids.Iyuv, | ||
_ => throw new ArgumentOutOfRangeException(nameof(format), format, null) | ||
}; | ||
} | ||
} | ||
|
||
public enum MFVideoFormat | ||
{ | ||
// SharpDX.MediaFoundation.VideoFormatGuidsから作成 | ||
Wmv1, | ||
Wmv2, | ||
Wmv3, | ||
Dvc, | ||
Dv50, | ||
Dv25, | ||
H263, | ||
H264, | ||
H265, | ||
Hevc, | ||
HevcEs, | ||
Vp80, | ||
Vp90, | ||
MultisampledS2, | ||
M4S2, | ||
Wvc1, | ||
P010, | ||
AI44, | ||
Dvh1, | ||
Dvhd, | ||
MultisampledS1, | ||
Mp43, | ||
Mp4s, | ||
Mp4v, | ||
Mpg1, | ||
Mjpg, | ||
Dvsl, | ||
YUY2, | ||
Yv12, | ||
P016, | ||
P210, | ||
P216, | ||
I420, | ||
Dvsd, | ||
Y42T, | ||
NV12, | ||
NV11, | ||
Y210, | ||
Y216, | ||
Y410, | ||
Y416, | ||
Y41P, | ||
Y41T, | ||
Yvu9, | ||
Yvyu, | ||
Iyuv | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
using Beutl.Media; | ||
using Beutl.Media.Encoding; | ||
using Beutl.Media.Music; | ||
using Beutl.Media.Pixel; | ||
using SharpDX; | ||
using SharpDX.Direct3D9; | ||
using SharpDX.MediaFoundation; | ||
using SharpDX.Multimedia; | ||
using SharpDX.Win32; | ||
|
||
#if MF_BUILD_IN | ||
namespace Beutl.Embedding.MediaFoundation.Encoding; | ||
#else | ||
namespace Beutl.Extensions.MediaFoundation.Encoding; | ||
#endif | ||
|
||
// MediaFoundationを使用して、Bitmapから動画を作成するクラス | ||
public unsafe class MFWriter : MediaWriter | ||
{ | ||
private SinkWriter _sinkWriter; | ||
private int _videoStreamIndex; | ||
|
||
public MFWriter(string file, MFVideoEncoderSettings videoConfig, AudioEncoderSettings audioConfig) | ||
: base(videoConfig, audioConfig) | ||
{ | ||
// sinkwriterを初期化 | ||
_sinkWriter = MediaFactory.CreateSinkWriterFromURL(file, null, null); | ||
_videoStreamIndex = ConfigureVideoEncoder(videoConfig); | ||
|
||
_sinkWriter.BeginWriting(); | ||
} | ||
|
||
// IMFMediaTypeを作成 | ||
private static MediaType CreateMediaTypeFromSubtype(Guid subtype, int width, int height, double rate) | ||
{ | ||
var mediaType = new MediaType(); | ||
mediaType.Set(MediaTypeAttributeKeys.MajorType, MediaTypeGuids.Video); | ||
mediaType.Set(MediaTypeAttributeKeys.Subtype, subtype); | ||
mediaType.Set(MediaTypeAttributeKeys.InterlaceMode, (int)VideoInterlaceMode.Progressive); | ||
mediaType.Set(MediaTypeAttributeKeys.FrameSize, ((long)width << 32) | (uint)height); | ||
mediaType.Set(MediaTypeAttributeKeys.FrameRate, ((long)(int)(rate * 10000000) << 32 | 10000000)); | ||
return mediaType; | ||
} | ||
|
||
// ConfigureVideoEncoder | ||
private int ConfigureVideoEncoder(MFVideoEncoderSettings videoConfig) | ||
{ | ||
using var outputType = CreateMediaTypeFromSubtype( | ||
videoConfig.Format.ToVideoFormatGuid(), | ||
videoConfig.DestinationSize.Width, | ||
videoConfig.DestinationSize.Height, | ||
videoConfig.FrameRate.ToDouble()); | ||
outputType.Set(MediaTypeAttributeKeys.AvgBitrate, videoConfig.Bitrate); | ||
_sinkWriter.AddStream(outputType, out int streamIndex); | ||
|
||
// InputType | ||
using var inputType = CreateMediaTypeFromSubtype( | ||
VideoFormatGuids.Argb32, | ||
videoConfig.SourceSize.Width, | ||
videoConfig.SourceSize.Height, | ||
videoConfig.FrameRate.ToDouble()); | ||
_sinkWriter.SetInputMediaType(streamIndex, inputType, null); | ||
|
||
return streamIndex; | ||
} | ||
|
||
public override long NumberOfFrames { get; } | ||
|
||
public override long NumberOfSamples { get; } | ||
|
||
public override bool AddVideo(IBitmap image) | ||
{ | ||
bool requireDispose = false; | ||
|
||
if (image is not Bitmap<Bgra8888>) | ||
{ | ||
image = image.Convert<Bgra8888>(); | ||
requireDispose = true; | ||
} | ||
|
||
try | ||
{ | ||
using var buffer = MediaFactory.CreateMemoryBuffer(image.ByteCount); | ||
IntPtr ptr = buffer.Lock(out _, out _); | ||
Buffer.MemoryCopy((void*)image.Data, (void*)ptr, image.ByteCount, image.ByteCount); | ||
buffer.Unlock(); | ||
buffer.CurrentLength = image.ByteCount; | ||
|
||
using var sample = MediaFactory.CreateSample(); | ||
sample.AddBuffer(buffer); | ||
|
||
_sinkWriter.WriteSample(_videoStreamIndex, sample); | ||
|
||
return true; | ||
} | ||
finally | ||
{ | ||
if (requireDispose) | ||
image.Dispose(); | ||
} | ||
} | ||
|
||
public override bool AddAudio(IPcm sound) | ||
{ | ||
return false; | ||
} | ||
|
||
protected override void Dispose(bool disposing) | ||
{ | ||
base.Dispose(disposing); | ||
_sinkWriter.Finalize(); | ||
_sinkWriter.Dispose(); | ||
} | ||
} |