Skip to content

Commit

Permalink
Beutl.Extensions.MediaFoundation.Encoding プロジェクトを追加
Browse files Browse the repository at this point in the history
  • Loading branch information
yuto-trd committed Aug 6, 2024
1 parent 20b467e commit 9a1bc44
Show file tree
Hide file tree
Showing 5 changed files with 309 additions and 0 deletions.
40 changes: 40 additions & 0 deletions src/Beutl.Extensions.MediaFoundation/Encoding/MFEncoderInfo.cs
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

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'IEncoderInfo' is obsolete: 'Use EncodingController instead.'

Check warning on line 9 in src/Beutl.Extensions.MediaFoundation/Encoding/MFEncoderInfo.cs

View workflow job for this annotation

GitHub Actions / build

'IEncoderInfo' is obsolete: 'Use EncodingController instead.'
{
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

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'MediaWriter' is obsolete: 'Use EncodingController instead.'

Check warning on line 13 in src/Beutl.Extensions.MediaFoundation/Encoding/MFEncoderInfo.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'MediaWriter' is obsolete: 'Use EncodingController instead.'

Check warning on line 13 in src/Beutl.Extensions.MediaFoundation/Encoding/MFEncoderInfo.cs

View workflow job for this annotation

GitHub Actions / build

'MediaWriter' is obsolete: 'Use EncodingController instead.'

Check warning on line 13 in src/Beutl.Extensions.MediaFoundation/Encoding/MFEncoderInfo.cs

View workflow job for this annotation

GitHub Actions / build

'MediaWriter' is obsolete: 'Use EncodingController instead.'
{
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

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'EncodingExtension' is obsolete: 'Use EncodingController instead.'

Check warning on line 11 in src/Beutl.Extensions.MediaFoundation/Encoding/MFEncodingExtension.cs

View workflow job for this annotation

GitHub Actions / build

'EncodingExtension' is obsolete: 'Use EncodingController instead.'
{
public override string Name => "Media Foundation Encoder";

public override string DisplayName => "Media Foundation Encoder";

public override IEncoderInfo GetEncoderInfo() => new MFEncoderInfo();

Check warning on line 17 in src/Beutl.Extensions.MediaFoundation/Encoding/MFEncodingExtension.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'IEncoderInfo' is obsolete: 'Use EncodingController instead.'

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; }
}
117 changes: 117 additions & 0 deletions src/Beutl.Extensions.MediaFoundation/Encoding/MFVideoFormat.cs
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
}
114 changes: 114 additions & 0 deletions src/Beutl.Extensions.MediaFoundation/Encoding/MFWriter.cs
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();
}
}

0 comments on commit 9a1bc44

Please sign in to comment.