尽管分辨率发生变化,视频捕捉输出始终为 320x240

发布于 2024-12-17 06:33:48 字数 2691 浏览 0 评论 0原文

好的,我已经这样做了 2 天,需要有关最后一部分的帮助。

我有一台 Microsoft LifeCam Cinema 摄像机,并使用 .NET DirectShowLib 捕获视频流。好吧,实际上我使用 WPFMediaKit,但我现在在直接处理直接显示库的源代码中。

我的工作内容是: - 查看摄像机的视频输出 - 以 ASF 或 AVI 格式记录相机的视频输出(ICaptureGraphBuilder2 支持的唯一 2 种 MediaType)

问题是:我可以将其另存为 .avi。这在 1280x720 分辨率下工作正常,但它以 RAW 输出保存文件。这意味着每秒大约 50-60MB。太高了。

或者我可以将其切换为 .asf 并输出 WMV,但当我这样做时,捕获和输出的分辨率都变为 320x240。

在 WPFMediaKit 中,我更改了一个函数,因为显然使用 Microsoft LifeCam Cinema 摄像机的很多人都遇到此问题。因此,您不必创建或更改 AMMediaType,而是进行迭代,然后使用它来调用 SetFormat。

        ///* Make the VIDEOINFOHEADER 'readable' */
        var videoInfo = new VideoInfoHeader();

        int iCount = 0, iSize = 0;
        videoStreamConfig.GetNumberOfCapabilities(out iCount, out iSize);

        IntPtr TaskMemPointer = Marshal.AllocCoTaskMem(iSize);


        AMMediaType pmtConfig = null;
        for (int iFormat = 0; iFormat < iCount; iFormat++)
        {
            IntPtr ptr = IntPtr.Zero;

            videoStreamConfig.GetStreamCaps(iFormat, out pmtConfig, TaskMemPointer);

            videoInfo = (VideoInfoHeader)Marshal.PtrToStructure(pmtConfig.formatPtr, typeof(VideoInfoHeader));

            if (videoInfo.BmiHeader.Width == DesiredWidth && videoInfo.BmiHeader.Height == DesiredHeight)
            {

                ///* Setup the VIDEOINFOHEADER with the parameters we want */
                videoInfo.AvgTimePerFrame = DSHOW_ONE_SECOND_UNIT / FPS;

                if (mediaSubType != Guid.Empty)
                {
                    int fourCC = 0;
                    byte[] b = mediaSubType.ToByteArray();
                    fourCC = b[0];
                    fourCC |= b[1] << 8;
                    fourCC |= b[2] << 16;
                    fourCC |= b[3] << 24;

                    videoInfo.BmiHeader.Compression = fourCC;
                   // pmtConfig.subType = mediaSubType;

                }

                /* Copy the data back to unmanaged memory */
                Marshal.StructureToPtr(videoInfo, pmtConfig.formatPtr, true);

                hr = videoStreamConfig.SetFormat(pmtConfig);
                break;
            }

        }

        /* Free memory */
        Marshal.FreeCoTaskMem(TaskMemPointer);
        DsUtils.FreeAMMediaType(pmtConfig);

        if (hr < 0)
            return false;

        return true;

实现后,只要将 SetOutputFilename 设置为 MediaType.Avi,我最终就可以以 1280x720 的分辨率查看捕获的视频。

如果我将其设置为 MediaType.Asf,它将变为 320x240 并且输出是相同的。

或者 AVI 以正确的格式工作和输出,但在 RAW 视频中这样做,因此文件大小非常大。我尝试在图表中添加压缩器,但没有成功,这远远超出了我的经验。

我正在寻找 2 个答案中的 1 个。

  1. 以 1280x720 录制 ASF
  2. 向图表中添加压缩器,以便输出的 AVI 文件大小较小。

Ok I have been at this for 2 days and need help with this last part.

I have a Microsoft LifeCam Cinema camera and I use the .NET DirectShowLib to capture the video stream. Well actually I use WPFMediaKit, but I am in the source code of that dealing directly with the direct show library now.

What I have working is:
- View the video output of the camera
- Record the video output of the camera in ASF or AVI (the only 2 MediaType's supported with ICaptureGraphBuilder2)

The problem is: I can save it as a .avi. This works fine and at a resolution of 1280x720 but it saves the file in RAW output. Meaning it is about 50-60MB per second. Way too high.

Or I can switch it to .asf and it outputs a WMV, but when I do this the capture and the output both go to resolution 320x240.

In WPFMediaKit there is a function I changed because apparently with Microsoft LifeCam Cinema cameras a lot of people have this problem. So instead of creating or changing the AMMediaType you iterate through and then use that to call SetFormat.

        ///* Make the VIDEOINFOHEADER 'readable' */
        var videoInfo = new VideoInfoHeader();

        int iCount = 0, iSize = 0;
        videoStreamConfig.GetNumberOfCapabilities(out iCount, out iSize);

        IntPtr TaskMemPointer = Marshal.AllocCoTaskMem(iSize);


        AMMediaType pmtConfig = null;
        for (int iFormat = 0; iFormat < iCount; iFormat++)
        {
            IntPtr ptr = IntPtr.Zero;

            videoStreamConfig.GetStreamCaps(iFormat, out pmtConfig, TaskMemPointer);

            videoInfo = (VideoInfoHeader)Marshal.PtrToStructure(pmtConfig.formatPtr, typeof(VideoInfoHeader));

            if (videoInfo.BmiHeader.Width == DesiredWidth && videoInfo.BmiHeader.Height == DesiredHeight)
            {

                ///* Setup the VIDEOINFOHEADER with the parameters we want */
                videoInfo.AvgTimePerFrame = DSHOW_ONE_SECOND_UNIT / FPS;

                if (mediaSubType != Guid.Empty)
                {
                    int fourCC = 0;
                    byte[] b = mediaSubType.ToByteArray();
                    fourCC = b[0];
                    fourCC |= b[1] << 8;
                    fourCC |= b[2] << 16;
                    fourCC |= b[3] << 24;

                    videoInfo.BmiHeader.Compression = fourCC;
                   // pmtConfig.subType = mediaSubType;

                }

                /* Copy the data back to unmanaged memory */
                Marshal.StructureToPtr(videoInfo, pmtConfig.formatPtr, true);

                hr = videoStreamConfig.SetFormat(pmtConfig);
                break;
            }

        }

        /* Free memory */
        Marshal.FreeCoTaskMem(TaskMemPointer);
        DsUtils.FreeAMMediaType(pmtConfig);

        if (hr < 0)
            return false;

        return true;

When that was implemented I could finally view the captured video as 1280x720 as long as I set the SetOutputFilename to a MediaType.Avi.

If I set it to a MediaType.Asf it goes to 320x240 and the output is the same.

Or the AVI works and outputs in the correct format but does so in RAW video, hence a very large file size. I have attempted to add a compressor to the graph but with no luck, this is far out of my experience.

I am looking for 1 of 2 answers.

  1. Recording the ASF at 1280x720
  2. Adding a compressor to the graph so that the filesize of my outputted AVI is small.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一枫情书 2024-12-24 06:33:48

我想通了。因此,我将其发布在这里,供其他路过的可怜人想知道为什么它不起作用。

  1. 下载 WPFMediaKit 的源代码,您将需要更改一些代码。

  2. 转到文件夹 DirectShow > MediaPlayers 并打开 VideoCapturePlayer.cs

  3. 找到函数 SetVideoCaptureParameters 并将其替换为:

    /// <摘要>
    /// 设置视频采集设备的采集参数
    /// 
    私人布尔SetVideoCaptureParameters(ICaptureGraphBuilder2 capGraph,IBaseFilter captureFilter,Guid mediaSubType)
    {
        /* 流配置接口 */
        对象流配置;
    
        /* 获取流的配置接口 */
        int hr = capGraph.FindInterface(PinCategory.Capture,
                                        媒体类型.视频,
                                        捕获过滤器,
                                        typeof(IAMStreamConfig).GUID,
                                        输出流配置);
    
        DsError.ThrowExceptionForHR(hr);
    
        var videoStreamConfig = StreamConfig 作为 IAMStreamConfig;
    
        /* 如果 QueryInterface 失败... */
        如果(视频流配置==空)
        {
            throw new Exception("获取 IAMStreamConfig 失败");
        }
    
        ///* 使 VIDEOINFOHEADER '可读' */
        var videoInfo = new VideoInfoHeader();
    
        int iCount = 0, iSize = 0;
        videoStreamConfig.GetNumberOfCapability(out iCount, out iSize);
    
        IntPtr TaskMemPointer = Marshal.AllocCoTaskMem(iSize);
    
    
        AMMediaType pmtConfig = null;
        for (int iFormat = 0; iFormat < iCount; iFormat++)
        {
            IntPtr ptr = IntPtr.0;
    
            videoStreamConfig.GetStreamCaps(iFormat, out pmtConfig, TaskMemPointer);
    
            videoInfo = (VideoInfoHeader)Marshal.PtrToStructure(pmtConfig.formatPtr, typeof(VideoInfoHeader));
    
            if (videoInfo.BmiHeader.Width == DesiredWidth && videoInfo.BmiHeader.Height == DesiredHeight)
            {
    
                ///* 使用我们想要的参数设置 VIDEOINFOHEADER */
                videoInfo.AvgTimePerFrame = DSHOW_ONE_SECOND_UNIT / FPS;
    
                if (mediaSubType != Guid.Empty)
                {
                    int fourCC = 0;
                    byte[] b = mediaSubType.ToByteArray();
                    fourCC = b[0];
                    fourCC |= b[1] << 8;
                    fourCC |= b[2] << 16;
                    fourCC |= b[3] << 24;
    
                    videoInfo.BmiHeader. Compression = fourCC;
                   // pmtConfig.subType = mediaSubType;
    
                }
    
                /* 将数据复制回非托管内存 */
                Marshal.StructureToPtr(videoInfo, pmtConfig.formatPtr, true);
    
                hr = videoStreamConfig.SetFormat(pmtConfig);
                休息;
            }
    
        }
    
        /* 释放内存 */
        Marshal.FreeCoTaskMem(TaskMemPointer);
        DsUtils.FreeAMMediaType(pmtConfig);
    
        如果(小时<0)
            返回假;
    
        返回真;
    }
    

现在,只要您的相机支持,它就会以您想要的分辨率排序您的屏幕显示。

接下来,您很快就会发现,在将视频写入磁盘时,您没有应用这个新的正确捕获。

由于 ICaptureBuilder2 方法仅支持 Avi 和 Asf(即 wmv),因此您需要将媒体类型设置为其中之一。

hr = graphBuilder.SetOutputFileName(MediaSubType.Asf, this.m_fileName, out mux, out sink);

您将在 SetupGraph 函数中找到该行。

Asf 只能以 320x240 输出,而 Avi 将以所需的分辨率输出,但未压缩(意味着 1280x720 视频源每秒 50-60MB),这太高了。

因此,您有 2 个选择

  1. 了解如何将编码器(压缩过滤器)添加到 Avi 输出

  2. 了解如何更改 WMV 配置文件

我尝试过 1,但没有成功。主要是因为这是我第一次使用DirectShow,仅仅掌握了图形的含义。

但我在#2 上取得了成功,这就是我是如何做到的。

特别感谢(http://www.codeproject.com/KB/audio-video/videosav.aspx)我从这里提取了所需的代码。

  1. 在名为 WMLib.cs 的同一文件夹中创建一个新类,并将以下内容放入其中

http 下载演示项目://www.codeproject.com/KB/audio-video/videosav.aspx 并将 WMLib.cs 复制并粘贴到您的项目中(根据需要更改命名空间)

  1. 在 VideoCapturePlayer 中创建一个函数。 CS类

    /// <摘要>
    /// 配置从文件到 Asf 文件编写器的配置文件
    /// 
    /// ;
    /// ;
    /// ;
    公共 bool ConfigProfileFromFile(IBaseFilter asfWriter, 字符串文件名)
    {
        小时;
        //字符串profilePath =“test.prx”;
        // 设置用于转换的配置文件
        if ((文件名!= null) && (File.Exists(文件名)))
        {
            // 加载配置文件 XML 内容
            字符串配置文件数据;
            使用 (StreamReader reader = new StreamReader(File.OpenRead(filename)))
            {
                profileData = reader.ReadToEnd();
            }
    
            // 根据数据创建适当的 IWMProfile
            // 打开配置文件管理器
            IWMProfileManager 配置文件管理器;
            IWMProfile wmProfile = null;
            hr = WMLib.WMCreateProfileManager(out profileManager);
            如果(小时> = 0)
            {
                // 错误消息:配置文件无效 (0xC00D0BC6)
                // 例如没有 ;标签
                hr = profileManager.LoadProfileByData(profileData, out wmProfile);
            }
    
            if (profileManager != null)
            {
                Marshal.ReleaseComObject(profileManager);
                配置文件管理器=空;
            }
    
            // 仅当检索到配置文件时才配置
            如果(小时> = 0)
            {
                // 设置 writer 的配置文件
                IConfigAsfWriter configWriter = (IConfigAsfWriter)asfWriter;
                hr = configWriter.ConfigureFilterUsingProfile(wmProfile);
                如果(小时> = 0)
                {
                    返回真;
                }
            }
        }
        返回假;
    }
    
  2. 在SetupGraph函数中找到SetOutputFileName并在其下方放置

    ConfigProfileFromFile(mux, "c:\wmv.prx");

  3. 现在在您的 c: 驱动器上创建一个名为 wmv.prx 的文件,并将相关信息放入其中。

您可以在此处查看演示项目中的 PRX 文件示例:http://www.codeproject.com /KB/audio-video/videosav.aspx (Pal90.prx)

现在享受以正确大小输出的 .wmv 文件。
是的,我知道我放入的代码相当杂乱,但我将让您来完善它。

I figured this out. So I am posting it here for any other poor soul who passes by wondering why it doesn't work.

  1. Download the source of the WPFMediaKit, you are going to need to change some code.

  2. Go to Folder DirectShow > MediaPlayers and open up VideoCapturePlayer.cs

  3. Find the function SetVideoCaptureParameters and replace it with this:

    /// <summary>
    /// Sets the capture parameters for the video capture device
    /// </summary>
    private bool SetVideoCaptureParameters(ICaptureGraphBuilder2 capGraph, IBaseFilter captureFilter, Guid mediaSubType)
    {
        /* The stream config interface */
        object streamConfig;
    
        /* Get the stream's configuration interface */
        int hr = capGraph.FindInterface(PinCategory.Capture,
                                        MediaType.Video,
                                        captureFilter,
                                        typeof(IAMStreamConfig).GUID,
                                        out streamConfig);
    
        DsError.ThrowExceptionForHR(hr);
    
        var videoStreamConfig = streamConfig as IAMStreamConfig;
    
        /* If QueryInterface fails... */
        if (videoStreamConfig == null)
        {
            throw new Exception("Failed to get IAMStreamConfig");
        }
    
        ///* Make the VIDEOINFOHEADER 'readable' */
        var videoInfo = new VideoInfoHeader();
    
        int iCount = 0, iSize = 0;
        videoStreamConfig.GetNumberOfCapabilities(out iCount, out iSize);
    
        IntPtr TaskMemPointer = Marshal.AllocCoTaskMem(iSize);
    
    
        AMMediaType pmtConfig = null;
        for (int iFormat = 0; iFormat < iCount; iFormat++)
        {
            IntPtr ptr = IntPtr.Zero;
    
            videoStreamConfig.GetStreamCaps(iFormat, out pmtConfig, TaskMemPointer);
    
            videoInfo = (VideoInfoHeader)Marshal.PtrToStructure(pmtConfig.formatPtr, typeof(VideoInfoHeader));
    
            if (videoInfo.BmiHeader.Width == DesiredWidth && videoInfo.BmiHeader.Height == DesiredHeight)
            {
    
                ///* Setup the VIDEOINFOHEADER with the parameters we want */
                videoInfo.AvgTimePerFrame = DSHOW_ONE_SECOND_UNIT / FPS;
    
                if (mediaSubType != Guid.Empty)
                {
                    int fourCC = 0;
                    byte[] b = mediaSubType.ToByteArray();
                    fourCC = b[0];
                    fourCC |= b[1] << 8;
                    fourCC |= b[2] << 16;
                    fourCC |= b[3] << 24;
    
                    videoInfo.BmiHeader.Compression = fourCC;
                   // pmtConfig.subType = mediaSubType;
    
                }
    
                /* Copy the data back to unmanaged memory */
                Marshal.StructureToPtr(videoInfo, pmtConfig.formatPtr, true);
    
                hr = videoStreamConfig.SetFormat(pmtConfig);
                break;
            }
    
        }
    
        /* Free memory */
        Marshal.FreeCoTaskMem(TaskMemPointer);
        DsUtils.FreeAMMediaType(pmtConfig);
    
        if (hr < 0)
            return false;
    
        return true;
    }
    

Now that will sort out your screen display at what ever desired resolution you want, provided that your camera supports it.

Next you will soon figure out that this new correct capture you have isnt applied when writing the video to disk.

Since the ICaptureBuilder2 method only supports Avi and Asf (which is wmv) you need to set your mediatype to one of them.

hr = graphBuilder.SetOutputFileName(MediaSubType.Asf, this.m_fileName, out mux, out sink);

You will find that line in the SetupGraph function.

Asf will only output in 320x240, yet the Avi will output in the desired resolution, but uncompressed (meaning 50-60MB per second for a 1280x720 video feed), which is too high.

So that leaves you with 2 options

  1. Figure out how to add a encoder (compression filter) to the Avi output

  2. Figure out how to change the WMV profile

I tried 1, with no success. Mainly due to the fact this is my first time working with DirectShow and only just grasp the meaning of graphs.

But I was successful with #2 and here is how I did it.

Special thanks to (http://www.codeproject.com/KB/audio-video/videosav.aspx) I pulled out the needed code from here.

  1. Create a new class in the same folder called WMLib.cs and place the following in it

Download the demo project from http://www.codeproject.com/KB/audio-video/videosav.aspx and copy and paste the WMLib.cs into your project (change the namespace as necessary)

  1. Create a function in the VideoCapturePlayer.cs class

    /// <summary>
    /// Configure profile from file to Asf file writer
    /// </summary>
    /// <param name="asfWriter"></param>
    /// <param name="filename"></param>
    /// <returns></returns>
    public bool ConfigProfileFromFile(IBaseFilter asfWriter, string filename)
    {
        int hr;
        //string profilePath = "test.prx";
        // Set the profile to be used for conversion
        if ((filename != null) && (File.Exists(filename)))
        {
            // Load the profile XML contents
            string profileData;
            using (StreamReader reader = new StreamReader(File.OpenRead(filename)))
            {
                profileData = reader.ReadToEnd();
            }
    
            // Create an appropriate IWMProfile from the data
            // Open the profile manager
            IWMProfileManager profileManager;
            IWMProfile wmProfile = null;
            hr = WMLib.WMCreateProfileManager(out profileManager);
            if (hr >= 0)
            {
                // error message: The profile is invalid (0xC00D0BC6)
                // E.g. no <prx> tags
                hr = profileManager.LoadProfileByData(profileData, out wmProfile);
            }
    
            if (profileManager != null)
            {
                Marshal.ReleaseComObject(profileManager);
                profileManager = null;
            }
    
            // Config only if there is a profile retrieved
            if (hr >= 0)
            {
                // Set the profile on the writer
                IConfigAsfWriter configWriter = (IConfigAsfWriter)asfWriter;
                hr = configWriter.ConfigureFilterUsingProfile(wmProfile);
                if (hr >= 0)
                {
                    return true;
                }
            }
        }
        return false;
    }
    
  2. In the SetupGraph function find the SetOutputFileName and below it put

    ConfigProfileFromFile(mux, "c:\wmv.prx");

  3. Now create a file called wmv.prx on your c: drive and place the relevant information in it.

You can see a sample of a PRX file from the demo project here: http://www.codeproject.com/KB/audio-video/videosav.aspx (Pal90.prx)

And now enjoy your .wmv file outputted at the right size.
Yes I know the code I placed in was rather scrappy but I will leave it up to you to polish it up.

合约呢 2024-12-24 06:33:48

Lifecam 因设置捕获格式(更具体地说,回退到其他格式)时的不明显行为而闻名。请参阅之前的讨论,这些讨论可能会建议您解决方案:

Lifecam is known for unobvious behavior with setting capture format (more specifically, falling back to other formats). See prevoius discussions which are likely to suggest you a solution:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文