How can i change resolution in real time with the Elgato preview?

358 views Asked by At

I'm using directshow to preview live video from the Elgato device connected to the usb.

In form1:

private void button3_Click(object sender, EventArgs e)
        {
            Elgato_Video_Capture evc = new Elgato_Video_Capture();
            evc.Show();
        }

Then the new form:

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();

            InitDevice();
        }

        private void InitDevice()
        {
            try
            {
                //Set the video size to use for capture and recording
                videoSize = new Size(640, 480);//827, 505);//1280, 720);

                //Initialize filter graph and capture graph
                graph = (IFilterGraph2)new FilterGraph();
                captureGraph = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
                captureGraph.SetFiltergraph(graph);
                //rot = new DsROTEntry(graph);
                //Create filter for Elgato
                Guid elgatoGuid = new Guid("39F50F4C-99E1-464A-B6F9-D605B4FB5918");
                Type comType = Type.GetTypeFromCLSID(elgatoGuid);
                IBaseFilter  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
                IBaseFilter smartTeeFilter = (IBaseFilter)new SmartTee();

                graph.AddFilter(smartTeeFilter, "Smart Tee");
                IPin outPin = GetPin(elgatoFilter, "Video"); //GetPin(PinDirection.Output, "Video", elgatoFilter);
                //IPin inPin = GetPin(elgatoFilter, "Video");//GetPin(PinDirection.Input, smartTeeFilter);
                IPin inPin = GetPin(smartTeeFilter, "Input");//GetPin(PinDirection.Input, smartTeeFilter);
                SetAndGetAllAvailableResolution(outPin);

                graph.Connect(outPin, inPin);


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

                graph.AddFilter(videoRendererFilter, "Video Renderer");
                // outPin = GetPin(elgatoFilter, "Video");//GetPin(PinDirection.Output, "Preview", smartTeeFilter);
                outPin = GetPin(smartTeeFilter, "Preview");//GetPin(PinDirection.Output, "Preview", smartTeeFilter);
                //outPin = GetPin(smartTeeFilter, "Capture");//GetPin(PinDirection.Output, "Preview", smartTeeFilter);

                //inPin = GetPin(elgatoFilter, "Video");//GetPin(PinDirection.Input, videoRendererFilter);
                inPin = GetPin(videoRendererFilter, "Input");//GetPin(PinDirection.Input, videoRendererFilter);
                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, 640, 480);//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);
            }



        }


        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);
            if (comboBox1.Items.Count == 0)
            {
                for (int xx = 1; xx <= piCount; xx++)
                {
                    comboBox1.Items.Add(xx);
                }
            }
            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 == comboBox1.SelectedIndex)//2)// 4
                {
                    CorectvidFormat = searchmedia;
                }
            }
            hr = streamConfig.SetFormat(CorectvidFormat);
        }


        private void Elgato_Video_Capture_Load(object sender, EventArgs e)
        {

        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            InitDevice();
        }
    }
}

EDIT

In the comboBox1 selecyedindex event i did now:

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (resolutionChanged == true)
            {
                mediaControl.Stop();
                outPin.Disconnect();
                SetAndGetAllAvailableResolution(outPin);
                outPin.Connect(outPin, null);
                mediaControl.Run();

            }
        }

The problem is that i'm not calling InitDevice again so it wont start all the method again.

But then in the selectedindex changed event when i'm calling etAndGetAllAvailableResolution(outPin); I;m geting exception on line:

IAMStreamConfig streamConfig = (IAMStreamConfig)VideoOutPin;

InvalidCastException

Unable to cast COM object of type 'System.__ComObject' to interface type 'DirectShowLib.IAMStreamConfig'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{C6E13340-30AC-11D0-A18C-00A0C9118956}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))

So from one side i don't want to make a change and then call InitDevice and start all over again.

In the other hand if i"m not calling InitDevice again in the comboBox1 selected index event then it will throw the exception.

Before i called SetAndGetAllAvailableResolution() before connecting the pins in the graph. But now i'm calling it only when change resolution in the comboBox1 selected index event. And then it throw the exception.

So what should i do now ? Can someone reveal/doscover and help me out how the code should look like ?

1

There are 1 answers

2
Roman Ryltsov On

Resolution can be changed before the pin is connected. If it is connected and streaming is active, then you need to stop, disconnect it, change resolution as you do now, then connect back and restart.