Windows 10 Anniversary Update–Fun with Composition and Video

Inspired by Kenny’s Tweet here;

image

and also by Rob’s blog post here;

Video Shenanigans

I thought I’d play with the new MediaPlayer in Windows 10 Anniversary Update and its capability to produce a brush that can be used to paint a Visual in the Visual Layer.

I took my cue from Kenny but worked in C# and made a blank UWP application on the 14366 preview SDK and removed the App.xaml and App.xaml.cs and the MainPage.xaml and MainPage.xaml.cs files and just added one single file to provide an entry point into the application without any XAML.

My whole idea was to see how many surfaces I could paint with an CompositionSurfaceBrush from a MediaPlayer and so I downloaded the Big Buck Bunny movie, embedded it into my project and set about playing it.

1

this is just a SpriteVisual within a ContainerVisual within a window where the visual is being painted with the brush obtained from the MediaPlayer but I added a click handler which increased the number of visuals from 1 to 4 to 9 to 25 and so on;

2

and I clicked a few times Smile

3

and the video continued to be lovely and smooth so on I went;

5

and up to 10;

10

and we were hitting the limits of my eyesight by 20;

20

but the video was playing on nicely and I guess it shows how scalable it is to have this one brush being used by the compositor to paint all these surfaces and the next step might be to add some effects and animations to the brushes.

The code is quite sparse but it’s also pretty simple – here’s the entirety of it;

using System;
using System.Collections.Generic;
using System.Numerics;
using Windows.ApplicationModel.Core;
using Windows.Foundation;
using Windows.Media.Playback;
using Windows.Storage;
using Windows.UI.Composition;
using Windows.UI.Core;

namespace App8
{
  class MainView : IFrameworkView, IFrameworkViewSource
  {
    public static void Main()
    {
      CoreApplication.Run(new MainView());
    }
    public MainView()
    {
      this.spriteVisuals = new List<SpriteVisual>();
    }
    public IFrameworkView CreateView()
    {
      return (this);
    }
    public void Run()
    {
      this.window.Activate();
      this.window.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessUntilQuit);
    }

    public void SetWindow(CoreWindow window)
    {
      this.window = window;
      this.compositor = new Compositor();
      this.containerVisual = this.compositor.CreateContainerVisual();
      var target = this.compositor.CreateTargetForCurrentView();
      target.Root = this.containerVisual;

      this.spriteVisuals.Add(this.MakeSpriteVisual());
      this.SizeVisuals(
        new Size(window.Bounds.Width, window.Bounds.Height));

      this.window.SizeChanged += (s, e) =>
      {
        this.SizeVisuals(e.Size);
      };
      this.window.PointerReleased += (s, e) =>
      {
        this.IncreaseVisuals();
      };
    }
    void IncreaseVisuals()
    {
      var root = Math.Sqrt(this.spriteVisuals.Count);
      var newCount = (int)Math.Pow(root + 1, 2.0);
      var delta = newCount - this.spriteVisuals.Count;

      for (int i = 0; i < delta; i++)
      {
        this.spriteVisuals.Add(this.MakeSpriteVisual());
      }
      this.SizeVisuals(
        new Size(
          this.window.Bounds.Width, 
          this.window.Bounds.Height));
    }

    void SizeVisuals(Size size)
    {
      this.containerVisual.Size = new Vector2(
        (float)size.Width, (float)size.Height);

      int rowSize = (int)Math.Sqrt(this.spriteVisuals.Count);
      float visualWidth = (float)size.Width / rowSize;
      float visualHeight = (float)size.Height / rowSize;
      var vectorSize = new Vector2(visualWidth, visualHeight);

      for (int i = 0; i < rowSize; i++)
      {
        for (int j = 0; j < rowSize; j++)
        {
          var visual = this.spriteVisuals[i * rowSize + j];
          visual.Size = vectorSize;
          visual.Offset = new Vector3(i * visualWidth, j * visualHeight, 0);
        }
      }
    }
    public async void Load(string entryPoint)
    {
      this.mediaPlayer = new MediaPlayer();

      var videoFile = await StorageFile.GetFileFromApplicationUriAsync(
        new Uri("ms-appx:///Assets/movie.avi"));

      this.mediaPlayer.SetFileSource(videoFile);

      var surface = this.mediaPlayer.GetSurface(this.compositor);

      this.mediaBrush = this.compositor.CreateSurfaceBrush(surface.CompositionSurface);

      foreach (var spriteVisual in this.spriteVisuals)
      {
        spriteVisual.Brush = this.mediaBrush;
      }

      this.mediaPlayer.Play();
    }
    SpriteVisual MakeSpriteVisual()
    {
      var spriteVisual = this.compositor.CreateSpriteVisual();
      spriteVisual.Brush = this.mediaBrush;
      this.containerVisual.Children.InsertAtTop(spriteVisual);
      return (spriteVisual);
    }
    public void Initialize(CoreApplicationView applicationView)
    {
    }
    public void Uninitialize()
    {
    }
    CoreWindow window;
    ContainerVisual containerVisual;
    MediaPlayer mediaPlayer;
    Compositor compositor;
    CompositionSurfaceBrush mediaBrush;
    List<SpriteVisual> spriteVisuals;
  }
}

4 thoughts on “Windows 10 Anniversary Update–Fun with Composition and Video

  1. Pingback: Windows 10 Anniversary Update–Fun with Composition and Video | Tech News

  2. How to add WIN2D effects like HueRotationEffect into SpriteVisual and into the MediaPlayer instance?

  3. WIN2D Effects Added:
    using Microsoft.Graphics.Canvas.Effects;
    using System;
    using System.Collections.Generic;
    using System.Numerics;
    using Windows.ApplicationModel.Core;
    using Windows.Foundation;
    using Windows.Media.Playback;
    using Windows.Storage;
    using Windows.UI.Composition;
    using Windows.UI.Core;

    namespace MediaPlayBack
    {
    internal class MainView : IFrameworkView, IFrameworkViewSource
    {
    private CoreWindow window;
    private CoreApplicationView _view;
    private ContainerVisual containerVisual;

    //private SpriteVisual effectVisual;
    private MediaPlayer mediaPlayer;

    private MediaPlayerSurface _mediaSurface;
    private Compositor compositor;
    private CompositionSurfaceBrush mediaBrush;
    private CompositionEffectBrush _effectBrush;
    //private CompositionEffectFactory _effectFactory;

    //private CompositionTarget compositionTarget;
    private List spriteVisuals;

    public static void Main()
    {
    CoreApplication.Run(new MainView());
    }

    public MainView()
    {
    this.spriteVisuals = new List();
    }

    public IFrameworkView CreateView()
    {
    return (this);
    }

    public void Run()
    {
    this.window.Activate();
    this.window.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessUntilQuit);
    }

    public void SetWindow(CoreWindow window)
    {
    this.window = window;
    this.compositor = new Compositor();
    var clip = this.compositor.CreateInsetClip();
    clip.LeftInset = 2.0f;
    clip.RightInset = 2.0f;
    clip.TopInset = 2.0f;
    clip.BottomInset = 2.0f;
    this.containerVisual = this.compositor.CreateContainerVisual();
    this.containerVisual.Clip = clip;
    this.containerVisual.Opacity = 0.6f;
    this.containerVisual.RotationAngleInDegrees = 0.0f;
    //this.containerVisual.Children.InsertAtTop(AddEffect());
    foreach (var child in this.containerVisual.Children)
    {
    child.RotationAngleInDegrees = 45.0f;
    break;
    }
    var target = this.compositor.CreateTargetForCurrentView();
    target.Root = this.containerVisual;
    this.AddEffect();
    this.spriteVisuals.Add(this.MakeSpriteVisual());
    this.SizeVisuals(
    new Size(window.Bounds.Width, window.Bounds.Height));

    this.window.SizeChanged += (s, e) =>
    {
    this.SizeVisuals(e.Size);
    };
    this.window.PointerReleased += (s, e) =>
    {
    this.IncreaseVisuals();
    };
    }

    private CompositionEffectBrush AddEffect()
    {
    // Create our HueRotation effect.
    var hueRotationEffect = new HueRotationEffect
    {
    Name = “HueRotation”,
    Source = new CompositionEffectSourceParameter(“HueRotation”),
    Angle = 0.5f
    };
    // Create our Saturation effect, range 0->1
    var saturationEffect = new SaturationEffect
    {
    Name = “Saturation”,
    Source = new CompositionEffectSourceParameter(“Saturation”),
    Saturation = 0.0f
    };
    // Create our Exposure effect, range -2 -> 2, default value 0.
    var exposureEffect = new ExposureEffect
    {
    Name = “Exposure”,
    Source = new CompositionEffectSourceParameter(“Exposure”),
    Exposure = 0.0f
    };
    // Create our Exposure effect, range 0 -> 1, default value 0.5
    var sepiaEffect = new SepiaEffect
    {
    Name = “Sepia”,
    Source = new CompositionEffectSourceParameter(“Sepia”),
    Intensity = 0.5f
    };

    var arithmeticCompositeEffect = new ArithmeticCompositeEffect
    {
    Name = “ArithmeticComposite”,
    Source1 = new CompositionEffectSourceParameter(“ArithmeticComposite”),
    MultiplyAmount = 2,
    Source1Amount = 0,
    Source2Amount = 0,
    Offset = 0,

    Source2 = new Transform2DEffect
    {
    Name = “Transform2D”,
    Source = new CompositionEffectSourceParameter(“ArithmeticComposite”),
    TransformMatrix = Matrix3x2.CreateRotation(0.0f)
    }
    };

    //var effectFactory = compositor.CreateEffectFactory(
    // hueRotationEffect,
    // new string[] { “HueRotation.Angle” });

    //var effectFactory = compositor.CreateEffectFactory(
    // saturationEffect,
    // new string[] { “Saturation.Saturation” });

    //var effectFactory = compositor.CreateEffectFactory(
    // exposureEffect,
    // new string[] { “Exposure.Exposure” });

    var effectFactory = compositor.CreateEffectFactory(
    sepiaEffect,
    new string[] { “Sepia.Intensity” });

    //var effectFactory = compositor.CreateEffectFactory(
    // arithmeticCompositeEffect,
    // new string[] { “ArithmeticComposite.MultiplyAmount” });

    _effectBrush = effectFactory.CreateBrush();

    return _effectBrush;
    }

    private void IncreaseVisuals()
    {
    var root = Math.Sqrt(this.spriteVisuals.Count);
    var newCount = (int)Math.Pow(root + 1, 2.0);
    var delta = newCount – this.spriteVisuals.Count;

    for (int i = 0; i < delta; i++)
    {
    this.spriteVisuals.Add(this.MakeSpriteVisual());
    //this.containerVisual.Children.InsertAtTop(AddEffect());
    }
    this.SizeVisuals(
    new Size(
    this.window.Bounds.Width,
    this.window.Bounds.Height));
    }

    private void SizeVisuals(Size size)
    {
    this.containerVisual.Size = new Vector2(
    (float)size.Width, (float)size.Height);

    int rowSize = (int)Math.Sqrt(this.spriteVisuals.Count);
    float visualWidth = (float)size.Width / rowSize;
    float visualHeight = (float)size.Height / rowSize;
    var vectorSize = new Vector2(visualWidth, visualHeight);

    for (int i = 0; i < rowSize; i++)
    {
    for (int j = 0; j < rowSize; j++)
    {
    var visual = this.spriteVisuals[i * rowSize + j];
    visual.Size = vectorSize;
    visual.Offset = new Vector3(i * visualWidth, j * visualHeight, 0);
    }
    }
    }

    public async void Load(string entryPoint)
    {
    this.mediaPlayer = new MediaPlayer();
    this.mediaPlayer.IsLoopingEnabled = true;
    this.mediaPlayer.SystemMediaTransportControls.IsEnabled = true;

    var videoFile = await StorageFile.GetFileFromApplicationUriAsync(
    new Uri("ms-appx:///Assets/Duck.mp4"));

    this.mediaPlayer.SetFileSource(videoFile);

    //Create a surface from our MediaPlayer.
    _mediaSurface = this.mediaPlayer.GetSurface(compositor);
    var _surface = _mediaSurface.CompositionSurface;

    // Make a brush out of it and apply it to our effect.
    this.mediaBrush = this.compositor.CreateSurfaceBrush(_surface);
    this.mediaBrush.Stretch = CompositionStretch.UniformToFill;
    //_visual.Brush = _videoBrush;
    _effectBrush.SetSourceParameter("Sepia", mediaBrush);

    ////containerVisual.Children.InsertAtTop(effectVisual);
    ////ElementCompositionPreview.SetElementChildVisual((Windows.UI.Xaml.UIElement)this.containerVisual, this.effectVisual);
    ////var propertySet = compositor.CreatePropertySet();
    ////propertySet.InsertScalar("HueRotation.Angle", 0.0f);

    var easing = this.compositor.CreateLinearEasingFunction();

    var animation = this.compositor.CreateScalarKeyFrameAnimation();
    animation.InsertKeyFrame(0.0f, 0.0f);
    animation.InsertKeyFrame(1.0f, 2.0f * (float)Math.PI, easing);
    //animation.InsertKeyFrame(1.0f, 1.0f, easing);
    animation.Duration = TimeSpan.FromMilliseconds(8000);
    animation.IterationBehavior = AnimationIterationBehavior.Forever;
    _effectBrush.StartAnimation("Sepia.Intensity", animation);

    foreach (var spriteVisual in this.spriteVisuals)
    {
    spriteVisual.Brush = this.mediaBrush;
    }

    this.mediaPlayer.Play();
    }

    private SpriteVisual MakeSpriteVisual()
    {
    var spriteVisual = this.compositor.CreateSpriteVisual();
    spriteVisual.Brush = _effectBrush;

    this.containerVisual.Children.InsertAtTop(spriteVisual);

    return (spriteVisual);
    }

    public void Initialize(CoreApplicationView applicationView)
    {
    _view = applicationView;
    }

    public void Uninitialize()
    {
    window = null;
    _view = null;
    }
    }
    }

Comments are closed.