forked from MonoGame/MonoGame
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
VideoPlayer implementation for Windows Desktop.
- Loading branch information
Ray Batts
authored and
Ray Batts
committed
Apr 22, 2014
1 parent
fa66800
commit 3bbbe07
Showing
5 changed files
with
331 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
{ | ||
|
||
} | ||
} | ||
} |