Skip to content

Commit

Permalink
VideoPlayer implementation for Windows Desktop.
Browse files Browse the repository at this point in the history
  • Loading branch information
Ray Batts authored and Ray Batts committed Apr 22, 2014
1 parent fa66800 commit 3bbbe07
Show file tree
Hide file tree
Showing 5 changed files with 331 additions and 4 deletions.
15 changes: 12 additions & 3 deletions Build/Projects/MonoGame.Framework.definition
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@
<Platforms>Android,iOS,Linux,MacOS,Ouya,Windows8,Windows,WindowsGL,WindowsPhone</Platforms>
</Compile>
<Compile Include="Content\ContentReaders\VideoReader.cs">
<ExcludePlatforms>Linux,PSMobile,Windows,WindowsGL,WindowsPhone</ExcludePlatforms>
<ExcludePlatforms>Linux,PSMobile,WindowsGL,WindowsPhone</ExcludePlatforms>
</Compile>
<Compile Include="Content\ContentSerializerAttribute.cs" />
<Compile Include="Content\ContentSerializerIgnoreAttribute.cs" />
Expand Down Expand Up @@ -730,7 +730,7 @@
</Compile>
<Compile Include="Media\VideoSoundtrackType.cs" />
<Compile Include="Media\Video.cs">
<ExcludePlatforms>Linux,PSMobile,Windows,WindowsGL,WindowsPhone</ExcludePlatforms>
<ExcludePlatforms>Linux,PSMobile,WindowsGL,WindowsPhone</ExcludePlatforms>
</Compile>
<Compile Include="Media\Video.IOS.cs">
<Platforms>iOS</Platforms>
Expand All @@ -741,8 +741,11 @@
<Compile Include="Media\Video.Android.cs">
<Platforms>Android,Ouya</Platforms>
</Compile>
<Compile Include="Media\Video.WMS.cs">
<Platforms>Windows</Platforms>
</Compile>
<Compile Include="Media\VideoPlayer.cs">
<ExcludePlatforms>Linux,PSMobile,Windows,WindowsGL,WindowsPhone</ExcludePlatforms>
<ExcludePlatforms>Linux,PSMobile,WindowsGL,WindowsPhone</ExcludePlatforms>
</Compile>
<Compile Include="Media\VideoPlayer.IOS.cs">
<Platforms>iOS</Platforms>
Expand All @@ -756,6 +759,9 @@
<Compile Include="Media\VideoPlayer.Android.cs">
<Platforms>Android,Ouya</Platforms>
</Compile>
<Compile Include="Media\VideoPlayer.WMS.cs">
<Platforms>Windows</Platforms>
</Compile>

<!-- Microsoft.Devices.Sensors -->
<Compile Include="Microsoft\Devices\Sensors\AccelerometerFailedException.cs">
Expand Down Expand Up @@ -1212,6 +1218,9 @@
<Compile Include="Windows\WinFormsGameWindow.cs">
<Platforms>Windows</Platforms>
</Compile>
<Compile Include="Media\VideoSampleGrabber.cs">
<Platforms>Windows</Platforms>
</Compile>


<!-- Windows Phone Platform -->
Expand Down
119 changes: 119 additions & 0 deletions MonoGame.Framework/Media/Video.WMS.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using SharpDX;
using SharpDX.MediaFoundation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Microsoft.Xna.Framework.Media
{
public sealed partial class Video : IDisposable
{
private Topology _topology;
internal Topology Topology { get { return _topology; } }

internal VideoSampleGrabber SampleGrabber { get; private set; }

MediaType _mediaType;

private void PlatformInitialize()
{
if (Topology != null)
return;

MediaManagerState.CheckStartup();

MediaFactory.CreateTopology(out _topology);

SharpDX.MediaFoundation.MediaSource mediaSource;
{
SourceResolver resolver;
MediaFactory.CreateSourceResolver(out resolver);

ObjectType otype;
ComObject source;
resolver.CreateObjectFromURL(FileName, (int)SourceResolverFlags.MediaSource, null, out otype,
out source);
mediaSource = source.QueryInterface<SharpDX.MediaFoundation.MediaSource>();
resolver.Dispose();
source.Dispose();
}

PresentationDescriptor presDesc;
mediaSource.CreatePresentationDescriptor(out presDesc);

for (var i = 0; i < presDesc.StreamDescriptorCount; i++)
{
Bool selected;
StreamDescriptor desc;
presDesc.GetStreamDescriptorByIndex(i, out selected, out desc);

if (selected)
{
TopologyNode sourceNode;
MediaFactory.CreateTopologyNode(TopologyType.SourceStreamNode, out sourceNode);

sourceNode.Set(TopologyNodeAttributeKeys.Source, mediaSource);
sourceNode.Set(TopologyNodeAttributeKeys.PresentationDescriptor, presDesc);
sourceNode.Set(TopologyNodeAttributeKeys.StreamDescriptor, desc);

TopologyNode outputNode;
MediaFactory.CreateTopologyNode(TopologyType.OutputNode, out outputNode);

var majorType = desc.MediaTypeHandler.MajorType;
if (majorType == MediaTypeGuids.Video)
{
Activate activate;

SampleGrabber = new VideoSampleGrabber();

_mediaType = new MediaType();

_mediaType.Set(MediaTypeAttributeKeys.MajorType, MediaTypeGuids.Video);

// Specify that we want the data to come in as RGB32.
_mediaType.Set(MediaTypeAttributeKeys.Subtype, new Guid("00000016-0000-0010-8000-00AA00389B71"));

MediaFactory.CreateSampleGrabberSinkActivate(_mediaType, SampleGrabber, out activate);
outputNode.Object = activate;
}

if (majorType == MediaTypeGuids.Audio)
{
Activate activate;
MediaFactory.CreateAudioRendererActivate(out activate);

outputNode.Object = activate;
}

_topology.AddNode(sourceNode);
_topology.AddNode(outputNode);
sourceNode.ConnectOutput(0, outputNode, 0);

sourceNode.Dispose();
outputNode.Dispose();
}

desc.Dispose();
}

presDesc.Dispose();
mediaSource.Dispose();
}

private void PlatformDispose(bool disposing)
{
if (_topology != null)
{
_topology.Dispose();
_topology = null;
}

if (SampleGrabber != null)
{
SampleGrabber.Dispose();
SampleGrabber = null;
}
}
}
}
2 changes: 1 addition & 1 deletion MonoGame.Framework/Media/Video.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ internal Video(string fileName)
{
FileName = fileName;

#if !WINDOWS && !WINRT
#if !WINRT
PlatformInitialize();
#endif
}
Expand Down
142 changes: 142 additions & 0 deletions MonoGame.Framework/Media/VideoPlayer.WMS.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using Microsoft.Xna.Framework.Graphics;
using SharpDX;
using SharpDX.MediaFoundation;
using SharpDX.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Microsoft.Xna.Framework.Media
{
public sealed partial class VideoPlayer : IDisposable
{
private static MediaSession _session;
private static SimpleAudioVolume _volumeController;
private static PresentationClock _clock;

// HACK: Need SharpDX to fix this.
private static readonly Guid MRPolicyVolumeService = Guid.Parse("1abaa2ac-9d3b-47c6-ab48-c59506de784d");
private static readonly Guid SimpleAudioVolumeGuid = Guid.Parse("089EDF13-CF71-4338-8D13-9E569DBDC319");

private static Callback _callback;

private class Callback : IAsyncCallback
{
public void Dispose()
{
}

public IDisposable Shadow { get; set; }
public void Invoke(AsyncResult asyncResultRef)
{
var ev = _session.EndGetEvent(asyncResultRef);

// Trigger an "on Video Ended" event here if needed

_session.BeginGetEvent(this, null);
}

public AsyncCallbackFlags Flags { get; private set; }
public WorkQueueId WorkQueueId { get; private set; }
}

private void PlatformInitialize()
{
MediaManagerState.CheckStartup();
MediaFactory.CreateMediaSession(null, out _session);
}

private Texture2D PlatformGetTexture()
{
var sampleGrabber = _currentVideo.SampleGrabber;

var texData = sampleGrabber.TextureData;

if (texData == null)
return null;

// TODO: This could likely be optimized if we held on to the SharpDX Surface/Texture data,
// and set it on an XNA one rather than constructing a new one every time this is called.
var retTex = new Texture2D(Game.Instance.GraphicsDevice, _currentVideo.Width, _currentVideo.Height, false, SurfaceFormat.Bgr32);

retTex.SetData(texData);

return retTex;
}

private void PlatformPause()
{
_session.Pause();
}

private void PlatformPlay()
{
// Cleanup the last song first.
if (State != MediaState.Stopped)
{
_session.Stop();
_volumeController.Dispose();
_clock.Dispose();
}

// Set the new song.
_session.SetTopology(0, _currentVideo.Topology);

// Get the volume interface.
IntPtr volumeObj;


try
{
MediaFactory.GetService(_session, MRPolicyVolumeService, SimpleAudioVolumeGuid, out volumeObj);
}
catch
{
MediaFactory.GetService(_session, MRPolicyVolumeService, SimpleAudioVolumeGuid, out volumeObj);
}


_volumeController = CppObject.FromPointer<SimpleAudioVolume>(volumeObj);
_volumeController.Mute = IsMuted;
_volumeController.MasterVolume = _volume;

// Get the clock.
_clock = _session.Clock.QueryInterface<PresentationClock>();

//create the callback if it hasn't been created yet
if (_callback == null)
{
_callback = new Callback();
_session.BeginGetEvent(_callback, null);
}

// Start playing.
var varStart = new Variant();
_session.Start(null, varStart);
}

private void PlatformResume()
{
_session.Start(null, null);
}

private void PlatformStop()
{
_session.Stop();
}

private void PlatformSetVolume()
{
if (_volumeController == null)
return;

_volumeController.MasterVolume = _volume;
}

private TimeSpan PlatformGetPlayPosition()
{
return TimeSpan.Zero;
}
}
}
57 changes: 57 additions & 0 deletions MonoGame.Framework/Media/VideoSampleGrabber.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using SharpDX.MediaFoundation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace Microsoft.Xna.Framework.Media
{
internal class VideoSampleGrabber : SharpDX.CallbackBase, SampleGrabberSinkCallback
{
internal byte[] TextureData { get; private set; }

public void OnProcessSample(Guid guidMajorMediaType, int dwSampleFlags, long llSampleTime, long llSampleDuration, IntPtr sampleBufferRef, int dwSampleSize)
{
if (TextureData == null || TextureData.Length != dwSampleSize)
TextureData = new byte[dwSampleSize];

Marshal.Copy(sampleBufferRef, TextureData, 0, dwSampleSize);
}

public void OnSetPresentationClock(PresentationClock presentationClockRef)
{

}

public void OnShutdown()
{

}

public void OnClockPause(long systemTime)
{

}

public void OnClockRestart(long systemTime)
{

}

public void OnClockSetRate(long systemTime, float flRate)
{

}

public void OnClockStart(long systemTime, long llClockStartOffset)
{

}

public void OnClockStop(long hnsSystemTime)
{

}
}
}

0 comments on commit 3bbbe07

Please sign in to comment.