Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MediaFoundation Encoderを追加 #975

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
}
}
Loading