Skip to content

Commit

Permalink
fix VideoPlayer.GetTexture() null (MonoGame#335)
Browse files Browse the repository at this point in the history
VideoPlayer.GetTexture() allways return a texture.
VideoPlayer.GetTexture() doesn't block/CPU Spin
  • Loading branch information
nkast committed Apr 30, 2023
1 parent 96df00f commit bda0efc
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 64 deletions.
52 changes: 23 additions & 29 deletions MonoGame.Framework/Media/VideoPlayer.WME.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.

// Copyright (C)2023 Nick Kastellanos

using System;
using Microsoft.Xna.Framework.Graphics;

Expand Down Expand Up @@ -61,27 +63,29 @@ private void OnMediaEngineEvent(MediaEngineEvent mediaEvent, long param1, int pa

private Texture2D PlatformGetTexture()
{
// This will return a null texture if
// the video hasn't started playing yet
// or the last frame if the video is stopped
// as per XNA's behavior.

if (_state != MediaState.Playing)
return _lastFrame;

long pts;
if (!_mediaEngine.HasVideo() || !_mediaEngine.OnVideoStreamTick(out pts))
return _lastFrame;
if (_lastFrame != null)
{
if (_lastFrame.Width != _currentVideo.Width || _lastFrame.Height != _currentVideo.Height)
{
_lastFrame.Dispose();
_lastFrame = null;
}
}
if (_lastFrame == null)
_lastFrame = new Texture2D(_currentVideo.GraphicsDevice, _currentVideo.Width, _currentVideo.Height, false, SurfaceFormat.Bgra32,
Texture2D.SurfaceType.RenderTarget);

_lastFrame = new Texture2D(_currentVideo.GraphicsDevice,
_currentVideo.Width, _currentVideo.Height,
false,
SurfaceFormat.Bgra32,
Texture2D.SurfaceType.RenderTarget);

var region = new SharpDX.Mathematics.Interop.RawRectangle(0, 0, _currentVideo.Width, _currentVideo.Height);
SharpDX.ComObject dstSurfRef = (SharpDX.ComObject)_lastFrame.Handle;
_mediaEngine.TransferVideoFrame(dstSurfRef, null, region, null);
if (_state == MediaState.Playing)
{
long pts;
if (_mediaEngine.HasVideo() && _mediaEngine.OnVideoStreamTick(out pts))
{
var region = new SharpDX.Mathematics.Interop.RawRectangle(0, 0, _currentVideo.Width, _currentVideo.Height);
SharpDX.ComObject dstSurfRef = (SharpDX.ComObject)_lastFrame.Handle;
_mediaEngine.TransferVideoFrame(dstSurfRef, null, region, null);
}
}

return _lastFrame;
}
Expand All @@ -92,11 +96,6 @@ private void PlatformGetState(ref MediaState result)

private void PlatformPause()
{
// Calling PlatformGetTexture() manually will save the last frame
// so we can return the same one without doing unnecessary copies
// if GetTexture() keeps getting called while paused
PlatformGetTexture();

_mediaEngine.Pause();
}

Expand All @@ -113,11 +112,6 @@ private void PlatformPlay()

private void PlatformStop()
{
// Calling PlatformGetTexture() manually will save the last frame
// so we can return the same one without doing unnecessary copies
// if GetTexture() keeps getting called while stopped
PlatformGetTexture();

_mediaEngine.Pause();
_mediaEngine.CurrentTime = 0.0;
}
Expand Down
31 changes: 15 additions & 16 deletions MonoGame.Framework/Media/VideoPlayer.WMS.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
// Copyright (C)2023 Nick Kastellanos

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
Expand Down Expand Up @@ -79,17 +81,21 @@ private void PlatformInitialize()

private Texture2D PlatformGetTexture()
{
VideoSampleGrabber sampleGrabber = _currentVideo.SampleGrabber;
byte[] texData = sampleGrabber.TextureData;
if (texData == null)
return null;

// It's entirely possible that we could lose the d3d context and therefore lose this texture,
// but it's better than allocating a new texture each call!
if (_lastFrame != null)
{
if (_lastFrame.Width != _currentVideo.Width || _lastFrame.Height != _currentVideo.Height)
{
_lastFrame.Dispose();
_lastFrame = null;
}
}
if (_lastFrame == null)
_lastFrame = new Texture2D(_currentVideo.GraphicsDevice, _currentVideo.Width, _currentVideo.Height, false, SurfaceFormat.Bgr32);

_lastFrame.SetData(texData);
VideoSampleGrabber sampleGrabber = _currentVideo.SampleGrabber;
byte[] texData = sampleGrabber.TextureData;
if (texData != null)
_lastFrame.SetData(texData);

return _lastFrame;
}
Expand Down Expand Up @@ -152,13 +158,6 @@ private void PlatformPlay()

// Start playing.
_session.Start(null, _positionCurrent);

// we need to dispose of the old texture if we have one
if (_lastFrame != null)
_lastFrame.Dispose();

// Create cached texture
_lastFrame = new Texture2D(_currentVideo.GraphicsDevice, _currentVideo.Width, _currentVideo.Height, false, SurfaceFormat.Bgr32);
}

private void PlatformResume()
Expand Down
22 changes: 3 additions & 19 deletions MonoGame.Framework/Media/VideoPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.

// Copyright (C)2023 Nick Kastellanos

using System;
using System.Diagnostics;
using System.Threading;
Expand Down Expand Up @@ -140,25 +142,7 @@ public Texture2D GetTexture()
if (_currentVideo == null)
throw new InvalidOperationException("Operation is not valid due to the current state of the object");

const int retries = 5;
const int sleepTimeFactor = 50;
Texture2D texture=null;

for (int i = 0; i < retries; i++)
{
texture = PlatformGetTexture();
if (texture != null)
break;

int sleepTime = i*sleepTimeFactor;
Debug.WriteLine("PlatformGetTexture returned null ({0}) sleeping for {1} ms", i + 1, sleepTime);
#if WINDOWS_UAP
Task.Delay(sleepTime).Wait();
#else
Thread.Sleep(sleepTime); //Sleep for longer and longer times
#endif
}

Texture2D texture = PlatformGetTexture();
System.Diagnostics.Debug.Assert(texture != null);

return texture;
Expand Down

0 comments on commit bda0efc

Please sign in to comment.