I am working with DirectShow for video playback and trying to get the video displayed on a D3D9 texture via SlimDX. I am using IVMRSurfaceAllocator9, IVMRImagePresenter9 for getting a surface from the video as soon as a frame is available.
Everything is working fine if I am doing my main loop rendering inside the presentImage function but if I try to do the rendering separately and getting the texture from the presentImage function and then trying to display it in my main loop then a flickering video is being played.
I have tried a solution that says that changing rendering target to YUV solves the problem but I did it and is not getting the desired results. Can any one please help me out to resolve this problem?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SlimDX;
using SlimDX.Direct3D9;
using DirectShowLib;
using System.Collections;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form,IVMRSurfaceAllocator9,IVMRImagePresenter9
{
#region slimdxVariables
Device Device;
PresentParameters PresentParameters;
Direct3D Direct3D;
Surface renderTarget = null;
private Hashtable textures = null;
private Hashtable surfaces = null;
private Texture privateTexture;
private Surface privateSurface = null;
private VertexBuffer vertexBuffer = null;
private const int E_FAIL = unchecked((int)0x80004005);
private VertexDeclaration vertexDecl;
private SlimDX.Direct3D9.Font Font;
#endregion
#region DirectShowVariables
private IVMRSurfaceAllocatorNotify9 vmrSurfaceAllocatorNotify = null;
private IntPtr[] unmanagedSurfaces = null;
private FilterGraph FilterGraph =null ;
private IGraphBuilder graph = null;
private IBaseFilter filter = null;
private IMediaControl mediaControl = null;
private IVMRMixerControl9 pMix;
private VideoMixingRenderer9 VMR;
private IntPtr userId = new IntPtr(unchecked((int)0xACDCACDC));
#endregion
#region Form VAriables
OpenFileDialog openFileDialog;
#endregion
public Form1()
{
path = GetMoviePath();
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
StartGraph();
}
#region FunCtions For the Form Class
private string GetMoviePath()
{
openFileDialog = new OpenFileDialog();
if (openFileDialog.ShowDialog(this) == DialogResult.OK)
{
return openFileDialog.FileName;
}
else
return string.Empty;
}
private void SetAllocatorPresenter()
{
int hr = 0;
IVMRSurfaceAllocatorNotify9 vmrSurfAllocNotify = (IVMRSurfaceAllocatorNotify9)filter;
try
{
hr = vmrSurfAllocNotify.AdviseSurfaceAllocator(userId, this);
DsError.ThrowExceptionForHR(hr);
hr = this.AdviseNotify(vmrSurfAllocNotify);
DsError.ThrowExceptionForHR(hr);
}
catch(SlimDXException e)
{
MessageBox.Show("Couldn't run: " + e.Message);
throw;
}
}
string path;
int i = 0;
VMR9MixerPrefs dwpref;
VMR9MixerPrefs prefs;
private void StartGraph()
{
dwpref = new VMR9MixerPrefs();
onCreateDevice();
int hr = 0;
FilterGraph = new FilterGraph();
if (path == string.Empty)
return;
try
{
VMR =new VideoMixingRenderer9 ();
//graph = (IGraphBuilder)FilterGraph;
filter = (IBaseFilter)VMR;
graph = new FilterGraphNoThread() as IGraphBuilder;
IVMRFilterConfig9 filterConfig = (IVMRFilterConfig9)filter;
graph.AddFilter(filter, "VMR");
hr = filterConfig.SetRenderingMode(VMR9Mode.Renderless);
IEnumPins pinEnum;
filter.EnumPins(out pinEnum);
hr = filterConfig.SetNumberOfStreams(2);
DsError.ThrowExceptionForHR(hr);
pMix = (IVMRMixerControl9)filter;
//if (pMix != null)
{
pMix.GetMixingPrefs(out prefs);
prefs &= ~VMR9MixerPrefs.RenderTargetMask;
prefs |= VMR9MixerPrefs.RenderTargetYUV;
prefs |= VMR9MixerPrefs.NonSquareMixing;
pMix.SetMixingPrefs(prefs);
}
SetAllocatorPresenter();
hr = graph.AddFilter(filter, "Video Mixing Renderer 9");
DsError.ThrowExceptionForHR(hr);
hr = graph.RenderFile(path, null);
DsError.ThrowExceptionForHR(hr);
mediaControl = (IMediaControl)graph;
hr = mediaControl.Run();
DsError.ThrowExceptionForHR(hr);
}
catch(SlimDXException e)
{
MessageBox.Show(e.Message);
}
}
#endregion
private void onCreateDevice()
{
Direct3D = new Direct3D();
PresentParameters = new PresentParameters();
//PresentParameters.BackBufferCount = 0;
PresentParameters.BackBufferHeight = Screen.PrimaryScreen.Bounds.Height;
PresentParameters.BackBufferWidth = Screen.PrimaryScreen.Bounds.Width;
PresentParameters.PresentFlags = PresentFlags.Video;
PresentParameters.PresentationInterval = PresentInterval.Default;
PresentParameters.BackBufferFormat = Direct3D.Adapters[0].CurrentDisplayMode.Format;
PresentParameters.DeviceWindowHandle = this.Handle;
PresentParameters.SwapEffect = SwapEffect.Copy;
//PresentParameters.Windowed = false;
Device = new Device(Direct3D,Direct3D.Adapters.DefaultAdapter.Adapter,DeviceType.Hardware,this.Handle,CreateFlags.SoftwareVertexProcessing | CreateFlags.Multithreaded,PresentParameters);
renderTarget = Device.GetRenderTarget(0);
vertexBuffer = new VertexBuffer(Device, 6 * Vertex.SizeBytes, Usage.WriteOnly, VertexFormat.None, Pool.Managed);
// write the new vertex information into the buffer
DataStream stream = vertexBuffer.Lock(0, 0, LockFlags.None);
stream.WriteRange(BuildVertexData());
vertexBuffer.Unlock();
var vertexElems = new[] {
new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position , 0),
new VertexElement(0, 12 , DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 0),
VertexElement.VertexDeclarationEnd
};
vertexDecl = new VertexDeclaration(Device, vertexElems);
Font = new SlimDX.Direct3D9.Font(Device, 50, 0, FontWeight.Regular, 1, false, CharacterSet.Default, Precision.Default, FontQuality.Default, PitchAndFamily.Default, "");
txt = new Texture(Device, 1650, 1080, 0, 0, Format.X8R8G8B8, Pool.Default);
privateTexture = new Texture(Device, 1360,
768,
0,
Usage.None,
Format.X8R8G8B8, Pool.Default);
}
private void DeleteSurfaces()
{
lock (this)
{
if (privateTexture != null)
{
privateTexture.Dispose();
privateTexture = null;
}
if (privateSurface != null)
{
privateSurface.Dispose();
privateSurface = null;
}
if (textures != null)
{
foreach (Texture tex in textures.Values)
{
tex.Dispose();
}
textures = null;
}
if (surfaces != null)
{
foreach (Surface surf in surfaces.Values)
{
surf.Dispose();
}
surfaces = null;
}
}
}
[StructLayout(LayoutKind.Sequential)]
struct Vertex
{
public Vector3 Position;
public float Tu;
public float Tv;
public float Tu1;
public float Tv1;
public static int SizeBytes
{
get { return Marshal.SizeOf(typeof(Vertex)); }
}
}
private Vertex[] BuildVertexData()
{
Vertex[] vertexData = new Vertex[6];
vertexData[0].Position = new Vector3(-1.0f, 1.0f, 0.0f);
vertexData[0].Tu = 0.0f;
vertexData[0].Tv = 0.0f;
vertexData[1].Position = new Vector3(-1.0f, -1.0f, 0.0f);
vertexData[1].Tu = 0.0f;
vertexData[1].Tv = 1.0f;
vertexData[2].Position = new Vector3(1.0f, 1.0f, 0.0f);
vertexData[2].Tu = 1.0f;
vertexData[2].Tv = 0.0f;
vertexData[3].Position = new Vector3(-1.0f, -1.0f, 0.0f);
vertexData[3].Tu = 0.0f;
vertexData[3].Tv = 1.0f;
vertexData[4].Position = new Vector3(1.0f, -1.0f, 0.0f);
vertexData[4].Tu = 1.0f;
vertexData[4].Tv = 1.0f;
vertexData[5].Position = new Vector3(1.0f, 1.0f, 0.0f);
vertexData[5].Tu = 1.0f;
vertexData[5].Tv = 0.0f;
return vertexData;
}
Sprite Sprite,FontSprite;
public int Init(Device d3dDev)
{
try
{
d3dDev.SetRenderState(RenderState.CullMode,false);
d3dDev.SetRenderState(RenderState.Lighting,false);
//d3dDev.SetSamplerState(0, SamplerState.MipFilter, TextureFilter.Linear);
d3dDev.SetSamplerState(0, SamplerState.MinFilter, TextureFilter.Linear);
d3dDev.SetSamplerState(0, SamplerState.MagFilter, TextureFilter.Linear);
//Device.SetRenderTarget(0, renderTarget);
Sprite = new Sprite(Device);
FontSprite = new Sprite(Device);
}
catch (SlimDXException e)
{
MessageBox.Show(e.Message);
}
catch
{
return E_FAIL;
}
return 0;
}
float xpos=1650f;
public void RenderVideo()
{
pMix.SetMixingPrefs(prefs);
Device.SetRenderTarget(0, renderTarget);
if (xpos < 0) { xpos = 1650; }
Device.SetRenderTarget(0, renderTarget);
// clear the scene so we don't have any articats left
{
Device.SetRenderTarget(0, renderTarget);
Device.BeginScene();
xpos -=5f;
Device.SetTexture(0, privateTexture);
Device.VertexDeclaration = vertexDecl;
Device.SetStreamSource(0, vertexBuffer, 0, Vertex.SizeBytes);
FontSprite.Begin(SpriteFlags.AlphaBlend);
Font.DrawString(FontSprite, "Texture Rendering of the video by the help of slimdx Direct3d9 surface ", (int)xpos, 900, Color.Red);
FontSprite.End();
Device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2);
Device.EndScene();
Device.Present();
IsframeAvailable = false;
}
//Sprite.Begin(SpriteFlags.None);
//Sprite.Draw(txt, Color.Transparent);
}
private bool NeedToHandleDisplayChange()
{
if (vmrSurfaceAllocatorNotify == null)
return false;
IntPtr currentMonitor = Direct3D.GetAdapterMonitor(Device.CreationParameters.AdapterOrdinal);
IntPtr defaultMonitor = Direct3D.GetAdapterMonitor(Direct3D.Adapters[0].Adapter);
return currentMonitor != defaultMonitor;
}
public bool IsframeAvailable = false;
public Texture txt;
public int PresentHelper(VMR9PresentationInfo lpPresInfo)
{
Device.Clear(ClearFlags.ZBuffer , Color.AliceBlue, 1.0f, 0);
Device.SetRenderTarget(0, renderTarget);
int hr = 0;
try
{
if (privateTexture != null)
{
Marshal.AddRef(lpPresInfo.lpSurf);
using (Surface surface =Surface.FromPointer(lpPresInfo.lpSurf))
{
Device.StretchRectangle(surface,new Rectangle(0, 0, surface.Description.Width, surface.Description.Height),privateSurface,
new Rectangle(0, 0, privateSurface.Description.Width, privateSurface.Description.Height),
TextureFilter.None
);
}
//hr = DrawScene(Device, privateTexture);
//txt = privateTexture;
//IsframeAvailable = true;
if (hr < 0)
return hr;
}
else
{
if (textures.ContainsKey(lpPresInfo.lpSurf))
{
privateTexture = textures[lpPresInfo.lpSurf] as Texture;
if (hr < 0)
return hr;
}
else
hr = E_FAIL;
}
return 0;
}
catch (SlimDXException e)
{
MessageBox.Show(e.Message);
return 0;
}
catch
{
return E_FAIL;
}
}
#region IVMRSurfaceAllocator9 Members
public int AdviseNotify(IVMRSurfaceAllocatorNotify9 lpIVMRSurfAllocNotify)
{
lock (this)
{
vmrSurfaceAllocatorNotify = lpIVMRSurfAllocNotify;
IntPtr unmanagedDevice = Device.ComPointer;
IntPtr hMonitor = Direct3D.GetAdapterMonitor(0 );
return vmrSurfaceAllocatorNotify.SetD3DDevice(unmanagedDevice, hMonitor);
}
}
public int GetSurface(IntPtr dwUserID, int SurfaceIndex, int SurfaceFlags, out IntPtr lplpSurface)
{
lplpSurface = IntPtr.Zero;
if (SurfaceIndex > unmanagedSurfaces.Length)
return E_FAIL;
lock (this)
{
lplpSurface = unmanagedSurfaces[SurfaceIndex];
//pMix.SetMixingPrefs(prefs);
Marshal.AddRef(lplpSurface);
return 0;
}
}
private static IntPtr GetComPointer(object comObj)
{
if (!Marshal.IsComObject(comObj))
throw new ArgumentException("The argument is not a COM object", "COMObj");
IntPtr pComObj = Marshal.GetIUnknownForObject(comObj);
/* Get IUnknownForObject adds a reference count
* to the COM object so we remove a reference count
* before we return the pointer to avoid any possible
* memory leaks with COM */
Marshal.Release(pComObj);
return pComObj;
}
private IntPtr GetAdapterMonitor(uint adapterOrdinal)
{
IntPtr pMonitor = Direct3D.GetAdapterMonitor(Direct3D.Adapters.DefaultAdapter.Adapter);
return pMonitor;
}
public int InitializeDevice(IntPtr dwUserID, ref VMR9AllocationInfo lpAllocInfo, ref int lpNumBuffers)
{
//int width = 1;
//int height = 1;
//float fTU = 1.0f;
//float fTV = 1.0f;
int hr = 0;
try
{
//IntPtr hMonitor = Direct3D.GetAdapterMonitor(Direct3D.Adapters[0].Monitor);
//hr = vmrSurfaceAllocatorNotify.SetD3DDevice(unmanagedDevice, hMonitor);
//DsError.ThrowExceptionForHR(hr);
//var pDevice = GetComPointer(Device);
//var pMonitor = GetAdapterMonitor(0);
//hr = vmrSurfaceAllocatorNotify.SetD3DDevice(pDevice, pMonitor);
//DsError.ThrowExceptionForHR(hr);
lpAllocInfo.dwFlags |= VMR9SurfaceAllocationFlags.TextureSurface;
//DeleteSurfaces();
unmanagedSurfaces = new IntPtr[lpNumBuffers];
hr = vmrSurfaceAllocatorNotify.AllocateSurfaceHelper(ref lpAllocInfo, ref lpNumBuffers, unmanagedSurfaces);
// If we couldn't create a texture surface and
// the format is not an alpha format,
// then we probably cannot create a texture.
// So what we need to do is create a private texture
// and copy the decoded images onto it.
//{
surfaces = new Hashtable(unmanagedSurfaces.Length);
textures = new Hashtable(unmanagedSurfaces.Length);
for (int i = 0; i < lpNumBuffers; i++)
{
Surface surf = Surface.FromPointer(unmanagedSurfaces[i]);
Texture text = surf.GetContainer<Texture>();
surfaces.Add(unmanagedSurfaces[i], surf);
textures.Add(unmanagedSurfaces[i], text);
}
//}
return Init(Device);
}
catch (SlimDXException e)
{
MessageBox.Show(e.Message ) ;
return 0;
}
catch
{
return E_FAIL;
}
}
public int TerminateDevice(IntPtr dwID)
{
DeleteSurfaces();
return 0;
}
#endregion
#region IVMRImagePresenter9 Members
public int PresentImage(IntPtr dwUserID, ref VMR9PresentationInfo lpPresInfo)
{
Device.SetRenderTarget(0, renderTarget);
//IsframeAvailable = true;
int hr = 0;
lock (this)
{
try
{
// if we are in the middle of the display change
if (NeedToHandleDisplayChange())
{
// NOTE: this piece of code is left as a user exercise.
// The D3DDevice here needs to be switched
// to the device that is using another adapter
}
hr = PresentHelper(lpPresInfo);
IsframeAvailable = true;
return hr;
}
catch (SlimDXException e)
{
MessageBox.Show(e.Message);
return 0;
}
catch
{
return E_FAIL;
}
}
}
public int StartPresenting(IntPtr dwUserID)
{
lock (this)
{
if (Device == null)
return E_FAIL;
return 0;
}
}
public int StopPresenting(IntPtr dwUserID)
{
return 0;
}
#endregion
//private bool IsFullScreenFlag=true;
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Alt && e.KeyCode == Keys.Enter)
{
int hr = 0;
graph = (IGraphBuilder)FilterGraph;
filter = (IBaseFilter)new VideoMixingRenderer9();
IVMRFilterConfig9 filterConfig = (IVMRFilterConfig9)filter;
hr = filterConfig.SetRenderingMode(VMR9Mode.Renderless);
DsError.ThrowExceptionForHR(hr);
hr = filterConfig.SetNumberOfStreams(2);
DsError.ThrowExceptionForHR(hr);
SetAllocatorPresenter();
hr = graph.AddFilter(filter, "Video Mixing Renderer 9");
DsError.ThrowExceptionForHR(hr);
hr = graph.RenderFile(path, null);
DsError.ThrowExceptionForHR(hr);
mediaControl = (IMediaControl)graph;
hr = mediaControl.Run();
DsError.ThrowExceptionForHR(hr);
}
}
}
}
This is the code that I have written in the main form class and in the program class of windows form where my main method resides is
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using SlimDX.Windows;
namespace WindowsFormsApplication1
{
static class Program
{
static Form1 form = new Form1();
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
MessagePump.Run(form, () => {
form.RenderVideo();
});
}
}
}
If I run the main loop in accordance with the present image function that is when a frame is available then only my main loop should render a frame then every thing is going fine but if I don't do the same then the text that I am trying to display over my video is flickering as present image changes the render target each time I think and the whole rendering is disturbed. And lastly this the class for counting frames per second