Skip to content

Commit fbab0fb

Browse files
committed
Beutl.Extensions.MediaFoundation.Encoding プロジェクトを追加
1 parent fc29357 commit fbab0fb

File tree

5 files changed

+309
-0
lines changed

5 files changed

+309
-0
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using Beutl.Media.Encoding;
2+
3+
#if MF_BUILD_IN
4+
namespace Beutl.Embedding.MediaFoundation.Encoding;
5+
#else
6+
namespace Beutl.Extensions.MediaFoundation.Encoding;
7+
#endif
8+
9+
public class MFEncoderInfo : IEncoderInfo
10+
{
11+
public string Name => "Media Foundation Encoder";
12+
13+
public MediaWriter? Create(string file, VideoEncoderSettings videoConfig, AudioEncoderSettings audioConfig)
14+
{
15+
if (videoConfig is not MFVideoEncoderSettings mfVideoConfig)
16+
return null;
17+
18+
return new MFWriter(file, mfVideoConfig, audioConfig);
19+
}
20+
21+
public IEnumerable<string> SupportExtensions()
22+
{
23+
yield return ".mp4";
24+
yield return ".mov";
25+
yield return ".m4v";
26+
yield return ".avi";
27+
yield return ".wmv";
28+
yield return ".sami";
29+
yield return ".smi";
30+
yield return ".adts";
31+
yield return ".asf";
32+
yield return ".3gp";
33+
yield return ".3gp2";
34+
yield return ".3gpp";
35+
}
36+
37+
public VideoEncoderSettings DefaultVideoConfig() => new MFVideoEncoderSettings();
38+
39+
public AudioEncoderSettings DefaultAudioConfig() => new AudioEncoderSettings();
40+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using Beutl.Extensibility;
2+
using Beutl.Media.Encoding;
3+
4+
#if MF_BUILD_IN
5+
namespace Beutl.Embedding.MediaFoundation.Encoding;
6+
#else
7+
namespace Beutl.Extensions.MediaFoundation.Encoding;
8+
#endif
9+
10+
[Export]
11+
public class MFEncodingExtension : EncodingExtension
12+
{
13+
public override string Name => "Media Foundation Encoder";
14+
15+
public override string DisplayName => "Media Foundation Encoder";
16+
17+
public override IEncoderInfo GetEncoderInfo() => new MFEncoderInfo();
18+
19+
public override void Load()
20+
{
21+
if (OperatingSystem.IsWindows())
22+
{
23+
EncoderRegistry.Register(GetEncoderInfo());
24+
}
25+
}
26+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Beutl.Media.Encoding;
2+
3+
#if MF_BUILD_IN
4+
namespace Beutl.Embedding.MediaFoundation.Encoding;
5+
#else
6+
namespace Beutl.Extensions.MediaFoundation.Encoding;
7+
#endif
8+
9+
public class MFVideoEncoderSettings : VideoEncoderSettings
10+
{
11+
public MFVideoFormat Format { get; set; }
12+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using SharpDX.MediaFoundation;
2+
3+
#if MF_BUILD_IN
4+
namespace Beutl.Embedding.MediaFoundation.Encoding;
5+
#else
6+
namespace Beutl.Extensions.MediaFoundation.Encoding;
7+
#endif
8+
9+
// MFVideoFormatとVideoFormatGuidsを相互に変換するクラス
10+
public static class MFVideoFormatExtension
11+
{
12+
// MFVideoFormatからVideoFormatGuidsに変換する
13+
public static Guid ToVideoFormatGuid(this MFVideoFormat format)
14+
{
15+
return format switch
16+
{
17+
MFVideoFormat.Wmv1 => VideoFormatGuids.Wmv1,
18+
MFVideoFormat.Wmv2 => VideoFormatGuids.Wmv2,
19+
MFVideoFormat.Wmv3 => VideoFormatGuids.Wmv3,
20+
MFVideoFormat.Dvc => VideoFormatGuids.Dvc,
21+
MFVideoFormat.Dv50 => VideoFormatGuids.Dv50,
22+
MFVideoFormat.Dv25 => VideoFormatGuids.Dv25,
23+
MFVideoFormat.H263 => VideoFormatGuids.H263,
24+
MFVideoFormat.H264 => VideoFormatGuids.H264,
25+
MFVideoFormat.H265 => VideoFormatGuids.H265,
26+
MFVideoFormat.Hevc => VideoFormatGuids.Hevc,
27+
MFVideoFormat.HevcEs => VideoFormatGuids.HevcEs,
28+
MFVideoFormat.Vp80 => VideoFormatGuids.Vp80,
29+
MFVideoFormat.Vp90 => VideoFormatGuids.Vp90,
30+
MFVideoFormat.MultisampledS2 => VideoFormatGuids.MultisampledS2,
31+
MFVideoFormat.M4S2 => VideoFormatGuids.M4S2,
32+
MFVideoFormat.Wvc1 => VideoFormatGuids.Wvc1,
33+
MFVideoFormat.P010 => VideoFormatGuids.P010,
34+
MFVideoFormat.AI44 => VideoFormatGuids.AI44,
35+
MFVideoFormat.Dvh1 => VideoFormatGuids.Dvh1,
36+
MFVideoFormat.Dvhd => VideoFormatGuids.Dvhd,
37+
MFVideoFormat.MultisampledS1 => VideoFormatGuids.MultisampledS1,
38+
MFVideoFormat.Mp43 => VideoFormatGuids.Mp43,
39+
MFVideoFormat.Mp4s => VideoFormatGuids.Mp4s,
40+
MFVideoFormat.Mp4v => VideoFormatGuids.Mp4v,
41+
MFVideoFormat.Mpg1 => VideoFormatGuids.Mpg1,
42+
MFVideoFormat.Mjpg => VideoFormatGuids.Mjpg,
43+
MFVideoFormat.Dvsl => VideoFormatGuids.Dvsl,
44+
MFVideoFormat.YUY2 => VideoFormatGuids.YUY2,
45+
MFVideoFormat.Yv12 => VideoFormatGuids.Yv12,
46+
MFVideoFormat.P016 => VideoFormatGuids.P016,
47+
MFVideoFormat.P210 => VideoFormatGuids.P210,
48+
MFVideoFormat.P216 => VideoFormatGuids.P216,
49+
MFVideoFormat.I420 => VideoFormatGuids.I420,
50+
MFVideoFormat.Dvsd => VideoFormatGuids.Dvsd,
51+
MFVideoFormat.Y42T => VideoFormatGuids.Y42T,
52+
MFVideoFormat.NV12 => VideoFormatGuids.NV12,
53+
MFVideoFormat.NV11 => VideoFormatGuids.NV11,
54+
MFVideoFormat.Y210 => VideoFormatGuids.Y210,
55+
MFVideoFormat.Y216 => VideoFormatGuids.Y216,
56+
MFVideoFormat.Y410 => VideoFormatGuids.Y410,
57+
MFVideoFormat.Y416 => VideoFormatGuids.Y416,
58+
MFVideoFormat.Y41P => VideoFormatGuids.Y41P,
59+
MFVideoFormat.Y41T => VideoFormatGuids.Y41T,
60+
MFVideoFormat.Yvu9 => VideoFormatGuids.Yvu9,
61+
MFVideoFormat.Yvyu => VideoFormatGuids.Yvyu,
62+
MFVideoFormat.Iyuv => VideoFormatGuids.Iyuv,
63+
_ => throw new ArgumentOutOfRangeException(nameof(format), format, null)
64+
};
65+
}
66+
}
67+
68+
public enum MFVideoFormat
69+
{
70+
// SharpDX.MediaFoundation.VideoFormatGuidsから作成
71+
Wmv1,
72+
Wmv2,
73+
Wmv3,
74+
Dvc,
75+
Dv50,
76+
Dv25,
77+
H263,
78+
H264,
79+
H265,
80+
Hevc,
81+
HevcEs,
82+
Vp80,
83+
Vp90,
84+
MultisampledS2,
85+
M4S2,
86+
Wvc1,
87+
P010,
88+
AI44,
89+
Dvh1,
90+
Dvhd,
91+
MultisampledS1,
92+
Mp43,
93+
Mp4s,
94+
Mp4v,
95+
Mpg1,
96+
Mjpg,
97+
Dvsl,
98+
YUY2,
99+
Yv12,
100+
P016,
101+
P210,
102+
P216,
103+
I420,
104+
Dvsd,
105+
Y42T,
106+
NV12,
107+
NV11,
108+
Y210,
109+
Y216,
110+
Y410,
111+
Y416,
112+
Y41P,
113+
Y41T,
114+
Yvu9,
115+
Yvyu,
116+
Iyuv
117+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
using Beutl.Media;
2+
using Beutl.Media.Encoding;
3+
using Beutl.Media.Music;
4+
using Beutl.Media.Pixel;
5+
using SharpDX;
6+
using SharpDX.Direct3D9;
7+
using SharpDX.MediaFoundation;
8+
using SharpDX.Multimedia;
9+
using SharpDX.Win32;
10+
11+
#if MF_BUILD_IN
12+
namespace Beutl.Embedding.MediaFoundation.Encoding;
13+
#else
14+
namespace Beutl.Extensions.MediaFoundation.Encoding;
15+
#endif
16+
17+
// MediaFoundationを使用して、Bitmapから動画を作成するクラス
18+
public unsafe class MFWriter : MediaWriter
19+
{
20+
private SinkWriter _sinkWriter;
21+
private int _videoStreamIndex;
22+
23+
public MFWriter(string file, MFVideoEncoderSettings videoConfig, AudioEncoderSettings audioConfig)
24+
: base(videoConfig, audioConfig)
25+
{
26+
// sinkwriterを初期化
27+
_sinkWriter = MediaFactory.CreateSinkWriterFromURL(file, null, null);
28+
_videoStreamIndex = ConfigureVideoEncoder(videoConfig);
29+
30+
_sinkWriter.BeginWriting();
31+
}
32+
33+
// IMFMediaTypeを作成
34+
private static MediaType CreateMediaTypeFromSubtype(Guid subtype, int width, int height, double rate)
35+
{
36+
var mediaType = new MediaType();
37+
mediaType.Set(MediaTypeAttributeKeys.MajorType, MediaTypeGuids.Video);
38+
mediaType.Set(MediaTypeAttributeKeys.Subtype, subtype);
39+
mediaType.Set(MediaTypeAttributeKeys.InterlaceMode, (int)VideoInterlaceMode.Progressive);
40+
mediaType.Set(MediaTypeAttributeKeys.FrameSize, ((long)width << 32) | (uint)height);
41+
mediaType.Set(MediaTypeAttributeKeys.FrameRate, ((long)(int)(rate * 10000000) << 32 | 10000000));
42+
return mediaType;
43+
}
44+
45+
// ConfigureVideoEncoder
46+
private int ConfigureVideoEncoder(MFVideoEncoderSettings videoConfig)
47+
{
48+
using var outputType = CreateMediaTypeFromSubtype(
49+
videoConfig.Format.ToVideoFormatGuid(),
50+
videoConfig.DestinationSize.Width,
51+
videoConfig.DestinationSize.Height,
52+
videoConfig.FrameRate.ToDouble());
53+
outputType.Set(MediaTypeAttributeKeys.AvgBitrate, videoConfig.Bitrate);
54+
_sinkWriter.AddStream(outputType, out int streamIndex);
55+
56+
// InputType
57+
using var inputType = CreateMediaTypeFromSubtype(
58+
VideoFormatGuids.Argb32,
59+
videoConfig.SourceSize.Width,
60+
videoConfig.SourceSize.Height,
61+
videoConfig.FrameRate.ToDouble());
62+
_sinkWriter.SetInputMediaType(streamIndex, inputType, null);
63+
64+
return streamIndex;
65+
}
66+
67+
public override long NumberOfFrames { get; }
68+
69+
public override long NumberOfSamples { get; }
70+
71+
public override bool AddVideo(IBitmap image)
72+
{
73+
bool requireDispose = false;
74+
75+
if (image is not Bitmap<Bgra8888>)
76+
{
77+
image = image.Convert<Bgra8888>();
78+
requireDispose = true;
79+
}
80+
81+
try
82+
{
83+
using var buffer = MediaFactory.CreateMemoryBuffer(image.ByteCount);
84+
IntPtr ptr = buffer.Lock(out _, out _);
85+
Buffer.MemoryCopy((void*)image.Data, (void*)ptr, image.ByteCount, image.ByteCount);
86+
buffer.Unlock();
87+
buffer.CurrentLength = image.ByteCount;
88+
89+
using var sample = MediaFactory.CreateSample();
90+
sample.AddBuffer(buffer);
91+
92+
_sinkWriter.WriteSample(_videoStreamIndex, sample);
93+
94+
return true;
95+
}
96+
finally
97+
{
98+
if (requireDispose)
99+
image.Dispose();
100+
}
101+
}
102+
103+
public override bool AddAudio(IPcm sound)
104+
{
105+
return false;
106+
}
107+
108+
protected override void Dispose(bool disposing)
109+
{
110+
base.Dispose(disposing);
111+
_sinkWriter.Finalize();
112+
_sinkWriter.Dispose();
113+
}
114+
}

0 commit comments

Comments
 (0)