How can I get and set the directshow preview resolution?

3.5k views Asked by At

This is my code in a form I added:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using DirectShowLib;
using DirectShowLib.BDA;
using DirectShowLib.DES;
using DirectShowLib.DMO;
using DirectShowLib.Dvd;
using DirectShowLib.MultimediaStreaming;
using DirectShowLib.SBE;
using System.Runtime.InteropServices;
using System.Management;

namespace Youtube_Manager
{

    public partial class Elgato_Video_Capture : Form
    {


        IAMStreamConfig iasc;
        IFilterGraph2 graph;
        ICaptureGraphBuilder2 captureGraph;
        IBaseFilter elgatoFilter;
        IBaseFilter smartTeeFilter;
        IBaseFilter videoRendererFilter;
        Size videoSize;
        string error = "";
        List<Object> devices = new List<Object>();

        public Elgato_Video_Capture()
        {
            InitializeComponent();
            try
            {



                //Set the video size to use for capture and recording
                videoSize = new Size(827, 505);//1280, 720);

                //Initialize filter graph and capture graph
                graph = (IFilterGraph2)new FilterGraph();
                captureGraph = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
                captureGraph.SetFiltergraph(graph);
                //Create filter for Elgato
                Guid elgatoGuid = new Guid("39F50F4C-99E1-464A-B6F9-D605B4FB5918");
                Type comType = Type.GetTypeFromCLSID(elgatoGuid);
                elgatoFilter = (IBaseFilter)Activator.CreateInstance(comType);
                graph.AddFilter(elgatoFilter, "Elgato Video Capture Filter");

                //Create smart tee filter, add to graph, connect Elgato's video out to smart tee in
                smartTeeFilter = (IBaseFilter)new SmartTee();
                graph.AddFilter(smartTeeFilter, "Smart Tee");
                IPin outPin = GetPin(elgatoFilter, "Video"); 

                IPin inPin = GetPin(smartTeeFilter, "Input");

                graph.Connect(outPin, inPin);

                //Create video renderer filter, add it to graph, connect smartTee Preview pin to video renderer's input pin
                videoRendererFilter = (IBaseFilter)new VideoRenderer();
                graph.AddFilter(videoRendererFilter, "Video Renderer");

                outPin = GetPin(smartTeeFilter, "Preview");

                inPin = GetPin(videoRendererFilter, "Input");
                graph.Connect(outPin, inPin);
                //Render stream from video renderer
                captureGraph.RenderStream(PinCategory.Preview, MediaType.Video, videoRendererFilter, null, null);

                //Set the video preview to be the videoFeed panel
                IVideoWindow vw = (IVideoWindow)graph;
                vw.put_Owner(pictureBox1.Handle);
                vw.put_MessageDrain(this.Handle);
                vw.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipSiblings | WindowStyle.ClipChildren);
                vw.SetWindowPosition(0, 0, 827, 505);//1280, 720);

                //Start the preview
                IMediaControl mediaControl = graph as IMediaControl;
                mediaControl.Run();


            }
            catch (Exception err)
            {
                error = err.ToString();
            }
        }


         IPin GetPin(IBaseFilter filter, string pinname)
        {
            IEnumPins epins;
            int hr = filter.EnumPins(out epins);
            checkHR(hr, "Can't enumerate pins");
            IntPtr fetched = Marshal.AllocCoTaskMem(4);
            IPin[] pins = new IPin[1];
            while (epins.Next(1, pins, fetched) == 0)
            {
                PinInfo pinfo;
                pins[0].QueryPinInfo(out pinfo);
                bool found = (pinfo.name == pinname);
                DsUtils.FreePinInfo(pinfo);
                if (found)
                    return pins[0];
            }
            checkHR(-1, "Pin not found");
            return null;
        }

        public  void checkHR(int hr, string msg)
        {
            if (hr < 0)
            {
                MessageBox.Show(msg);
                DsError.ThrowExceptionForHR(hr);
            }



        }



        private void Elgato_Video_Capture_Load(object sender, EventArgs e)
        {

        }
    }
}

I read in some posts that I need to use IAMStreamConfig::SetFormat and GetFormat first to find the current format then to find what formats/resolutions the device/pin support and in the end to SetFormat to the format(resolution ?) I want.

But I didn't understand how to use it in my code. I tried before the declare in the top of the form IAMStreamConfig but I'm not sure how and where to use it in my code.

Edit

I'm using this method:

public void GetAllAvailableResolution(IPin VideoOutPin)
        {
            IAMStreamConfig streamConfig = (IAMStreamConfig)VideoOutPin;
            AMMediaType searchmedia;
            AMMediaType CorectvidFormat = new AMMediaType();
            IntPtr ptr;

            int piCount, piSize;
            hr = streamConfig.GetNumberOfCapabilities(out piCount, out piSize);
            ptr = Marshal.AllocCoTaskMem(piSize);
            for (int i = 0; i < piCount; i++)
            {
                hr = streamConfig.GetStreamCaps(i, out searchmedia, ptr);
                VideoInfoHeader v = new VideoInfoHeader();
                Marshal.PtrToStructure(searchmedia.formatPtr, v);
                if (i == 4)
                {
                    CorectvidFormat = searchmedia;
                }
            }
            hr = streamConfig.SetFormat(CorectvidFormat); 
        }

When i set if (i == 4) then i see on the variable v the resolution 1920 x 1080 But still when i look in the video the graphics dosent look so smooth. Could be i'm getting too low fps and need to change it ?

This looks like 1920x1080 resolution ?

resolution

A comparison elgato device 640x480(default) and my same resolution 640x480 look the same ?

Comparison

EDIT

I'm using GetFormat to get the current resolution but not sure if it's the correct way and if it's working fine:

After the first graph.Connect i added this line:

            int hr = 0;
            IntPtr pmt = IntPtr.Zero;
            AMMediaType mediaType = new AMMediaType();
            IAMStreamConfig streamConfig1 = (IAMStreamConfig)outPin;
            hr = streamConfig1.GetFormat(out mediaType);
            VideoInfoHeader v = new VideoInfoHeader();
            Marshal.PtrToStructure(mediaType.formatPtr, v);
            x = v.BmiHeader.Width;
            y = v.BmiHeader.Height;

I'm getting resolution 16x0 strange. X = 0 and Y = 16.

Changed the method once again ended with this:

public void SetAndGetAllAvailableResolution(IPin VideoOutPin)
{
int hr = 0;
IAMStreamConfig streamConfig = (IAMStreamConfig)VideoOutPin;
AMMediaType searchmedia;
AMMediaType CorectvidFormat = new AMMediaType();
IntPtr ptr;
        int piCount, piSize;
        hr = streamConfig.GetNumberOfCapabilities(out piCount, out piSize);
        ptr = Marshal.AllocCoTaskMem(piSize);
        for (int i = 0; i < piCount; i++)
        {
            hr = streamConfig.GetStreamCaps(i, out searchmedia, ptr);
            VideoInfoHeader v = new VideoInfoHeader();

            Marshal.PtrToStructure(searchmedia.formatPtr, v);
            if (i == 2)// 4
            {
                CorectvidFormat = searchmedia;
            }
        }
        hr = streamConfig.SetFormat(CorectvidFormat);

        IntPtr pmt = IntPtr.Zero;
        AMMediaType mediaType = new AMMediaType();
        IAMStreamConfig streamConfig1 = (IAMStreamConfig)VideoOutPin;
        hr = streamConfig1.GetFormat(out mediaType);
        BitmapInfoHeader bmpih = new BitmapInfoHeader();
        Marshal.PtrToStructure(mediaType.formatPtr, bmpih);
        x = bmpih.Width;
        y = bmpih.Height;
    }

In the bottom x and y noth return the value 0. But if i'm looking on bmih XPelesPerMeter 1280 And YPelesPerMeter 720 Could be this is the resolution currently in use ? And if it is why it didn't set it to what i wanted ? hr = streamConfig.SetFormat(CorectvidFormat); It should be 640x480

1

There are 1 answers

6
Roman Ryltsov On

DxWebCam from DirectShow.NET Samples and other samples there show how to use SetFormat. There have been many questions on this here on StackOverflow [1, 2] and on MSDN Forums.

You obtain the interface and set resolution before connecting/rendering the pin further.