0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【UWP】MediaPlayerに映像効果(明るさ・コントラスト・彩度)を追加する方法

0
Last updated at Posted at 2026-07-24

単純にIBasicVideoEffectを継承したクラスを作成して、MediaPlayer.AddVideoEffect()に渡したいところですが、やんごとなき理由でアプリとは別のプロジェクト(Windowsランタイムコンポーネント)として作成する必要があります。

1:Windowsランタイムコンポーネントとしてプロジェクト追加する

image.png

(WinAppSDKではない)UWPの場合は上の .Net ネイティブ の方を選択します

2:win2d.uwp のnuget参照を追加

<ItemGroup>
  <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
    <Version>6.2.14</Version>
  </PackageReference>
  <PackageReference Include="Win2D.uwp">
    <Version>1.28.3</Version>
  </PackageReference>
</ItemGroup>

3:ColorAdjustmentEffectクラスを追加

ColorAdjustmentEffect.cs
namespace VideoEffects;

using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;
using System.Collections.Generic;
using Windows.Foundation.Collections;
using Windows.Graphics.DirectX.Direct3D11;
using Windows.Media.Effects;
using Windows.Media.MediaProperties;

public static class PropertySetExtensionsForColorAdjustmentEffect
{
    public static bool IsDefaultBrightness(float v) => v == 0;
    public static bool IsDefaultContrast(float v) => v == 1;
    public static bool IsDefaultSaturation(float v) => v == 1;

    public static void SetBrightness(this IPropertySet ps, float value)
    {
        ps["Brightness"] = value;        
    }

    public static void SetContrast(this IPropertySet ps, float value)
    {
        ps["Contrast"] = value;
    }

    public static void SetSaturation(this IPropertySet ps, float value)
    {
        ps["Saturation"] = value;
    }
}

public sealed class ColorAdjustmentEffect : IBasicVideoEffect
{
    private CanvasDevice _canvasDevice;
    private ColorMatrixEffect _colorMatrixEffect;
    private SaturationEffect _saturationEffect;
    private PropertySet _properties;
    
    // パラメーター(デフォルト値)
    private float _brightness = 0.0f; // Exposure: -1.0 〜 1.0
    private float _contrast = 1.0f;   // Contrast: 0.0 〜 2.0
    private float _saturation = 1.0f; // Saturation: 0.0(モノクロ)〜 2.0+
    
    // GPU描画処理の有効化
    public bool UseGraphicsDeviceGpu => true;
    public MediaMemoryTypes SupportedMemoryTypes => MediaMemoryTypes.Gpu;
    public bool IsReadOnly => false;
    public IReadOnlyList<VideoEncodingProperties> SupportedEncodingProperties => new List<VideoEncodingProperties>();
    
    public bool TimeIndependent => true;
    
    public void SetProperties(IPropertySet configuration)
    {
        if (_properties != null)
        {
            _properties.MapChanged -= _properties_MapChanged;
        }
        _properties = (PropertySet)configuration;        
        _properties.MapChanged += _properties_MapChanged;    
        UpdateParameters();
    }
    
    private void _properties_MapChanged(IObservableMap<string, object> sender, IMapChangedEventArgs<string> @event)
    {
        if (@event.Key.Equals("Brightness", System.StringComparison.Ordinal)
            && sender.TryGetValue("Brightness", out object b) 
            && b is float brightness)
        {
            _brightness = brightness;
        }
        else if (@event.Key.Equals("Contrast", System.StringComparison.Ordinal)
            && sender.TryGetValue("Contrast", out object c)
            && c is float contrast)
        {
            _contrast = contrast;
        }
        else if (@event.Key.Equals("Saturation", System.StringComparison.Ordinal)
            && sender.TryGetValue("Saturation", out object s)
            && s is float saturation)
        {
            _saturation = saturation;
        }
    }
    
    public void SetEncodingProperties(VideoEncodingProperties encodingProperties, IDirect3DDevice device)
    {
        // Media Foundation から渡された Direct3D デバイスから CanvasDevice を作成
        _canvasDevice = CanvasDevice.CreateFromDirect3D11Device(device);
        _colorMatrixEffect = new ColorMatrixEffect();
        _saturationEffect = new SaturationEffect();
    }
    
    public void ProcessFrame(ProcessVideoFrameContext context)
    {                
        // 入力フレームと出力フレームの D3D サーフェスから CanvasBitmap / CanvasRenderTarget を作成
        using (var inputBitmap = CanvasBitmap.CreateFromDirect3D11Surface(_canvasDevice, context.InputFrame.Direct3DSurface))
        using (var renderTarget = CanvasRenderTarget.CreateFromDirect3D11Surface(_canvasDevice, context.OutputFrame.Direct3DSurface))
        using (var ds = renderTarget.CreateDrawingSession())
        {
            ICanvasImage targetSource = inputBitmap;
            if (_contrast != 1 && _brightness != 0)
            {
                float offset = (1.0f - _contrast) * 0.5f + _brightness;
    
                _colorMatrixEffect.Source = targetSource;
                _colorMatrixEffect.ColorMatrix = new Matrix5x4
                {
                    M11 = _contrast, M12 = 0,        M13 = 0,        M14 = 0,
                    M21 = 0,        M22 = _contrast, M23 = 0,        M24 = 0,
                    M31 = 0,        M32 = 0,        M33 = _contrast, M34 = 0,
                    M41 = 0,        M42 = 0,        M43 = 0,        M44 = 1,
                    M51 = offset,   M52 = offset,   M53 = offset,   M54 = 0
                };
                targetSource = _colorMatrixEffect;
            }
    
            // 3. 彩度 (SaturationEffect)
            if (_saturation != 1)
            {
                _saturationEffect.Source = targetSource;
                _saturationEffect.Saturation = _saturation;
                targetSource = _saturationEffect;
            }
    
            // 最終結果を出力サーフェスに描画
            ds.DrawImage(targetSource);
        }
    }
    
    private void UpdateParameters()
    {
        if (_properties == null) return;
    
        if (_properties.TryGetValue("Brightness", out object b) && b is float fb)
            _brightness = fb;
        if (_properties.TryGetValue("Contrast", out object c) && c is float fc)
            _contrast = fc;
        if (_properties.TryGetValue("Saturation", out object s) && s is float fs)
            _saturation = fs;
    }
    
    public void Close(MediaEffectClosedReason reason)
    {
        _canvasDevice?.Dispose();
        _colorMatrixEffect?.Dispose();
        if (_properties != null)
        {
            _properties.MapChanged -= _properties_MapChanged;
        }
    }
    
    public void DiscardQueuedFrames() { }
}

4:プロジェクト参照を追加

VisualStudioのソリューションエクスプローラのプロジェクトのメニューから追加を選択し、追加したプロジェクトを参照するように設定します。

image.png

5:MediaPlayer.AddVideoEffectを呼ぶ

IPropertySet _myVideoEffectConfig = new PropertySet();
_mediaPlayer.AddVideoEffect(typeof(ColorAdjustmentEffect).FullName, false, _myVideoEffectConfig);

// 例:明るさの値を変更する
// 前述のPropertySetExtensionsForColorAdjustmentEffectを参照のこと
_myVideoEffectConfig.SetBrightness(1.2f);

PropertySetはIObservableMapを実装しているためProperties["Brightness"] = 0f;のようにキーに対して値を設定すると映像効果として反映されます。

以上です。

参考

自作の動画プレイヤーアプリの実装が確認できます

動画エフェクトのプロジェクト
https://github.com/tor4kichi/TsubameViewer/tree/develop/VideoEffects

MediaPlayer.AddVideoEffectを呼び出している箇所
https://github.com/tor4kichi/TsubameViewer/blob/eebd8a65d7410f046e319bf01a8382d885667e39/TsubameViewer/Views/MovieViewerPage.xaml.cs#L1078

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?