Выход видеозахвата всегда в 320x240, несмотря на изменение разрешения

Хорошо, я занимаюсь этим 2 дня и нуждаюсь в помощи с этой последней частью.

У меня есть камера Microsoft LifeCam Cinema, и я использую .NET DirectShowLib для захвата видеопотока. Ну, на самом деле я использую WPFMediaKit, но сейчас я в исходном коде, который напрямую связан с прямой библиотекой.

У меня есть работа: - Просмотр видеовыхода камеры - Запись видеовыхода камеры в ASF или AVI (только 2 MediaType поддерживается с помощью ICaptureGraphBuilder2)

Проблема заключается в следующем: я могу сохранить его как .avi. Это работает отлично и с разрешением 1280x720, но сохраняет файл в RAW-выходе. Это примерно 50-60 МБ в секунду. Слишком высокий уровень.

Или я могу переключить его на .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;

Когда это было реализовано, я мог наконец просмотреть захваченное видео как 1280x720, пока я установил SetOutputFilename в MediaType.Avi.

Если я устанавливаю его в MediaType.Asf, он переходит к 320x240, и вывод одинаков.

Или AVI работает и выводится в правильном формате, но делает это в RAW-видео, следовательно, очень большой размер файла. Я попытался добавить компрессор к графику, но не повезло, это далеко от моего опыта.

Я ищу 1 из 2 ответов.

  • Запись ASF при 1280x720
  • Добавление компрессора к графику, чтобы размер файла моего AVI файла был небольшим.

Ответ 1

Я понял это. Поэтому я размещаю его здесь для любой другой бедной души, которая проходит мимо, задаваясь вопросом, почему она не работает.

  • Загрузите источник WPFMediaKit, вам нужно будет изменить код.

  • Перейдите в папку DirectShow > MediaPlayers и откройте VideoCapturePlayer.cs

  • Найдите функцию SetVideoCaptureParameters и замените ее следующим образом:

    /// <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 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;
    }
    

Теперь, когда вы будете сортировать свой экранный экран по желаемому разрешению, при условии, что ваша камера поддерживает его.

Затем вы скоро поймете, что этот новый правильный захват, который вы используете, не применяется при записи видео на диск.

Так как метод ICaptureBuilder2 поддерживает только Avi и Asf (это wmv), вам нужно установить свой медиатип для одного из них.

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

Вы найдете эту строку в функции SetupGraph.

Asf будет выводиться только в 320x240, но Avi будет выводить требуемое разрешение, но несжатое (значение 50-60 МБ в секунду для видеосигнала 1280x720), которое слишком велико.

Итак, у вас есть 2 варианта

  • Выясните, как добавить кодер (фильтр сжатия) к выходному AVI

  • Выясните, как изменить профиль WMV

Я пробовал 1, без успеха. Главным образом из-за того, что это мой первый опыт работы с DirectShow и просто понять смысл графиков.

Но я был успешным С# 2, и вот как я это сделал.

Особая благодарность (http://www.codeproject.com/KB/audio-video/videosav.aspx). Я вынул необходимый код здесь.

  • Создайте новый класс в той же папке с именем WMLib.cs и поместите в него следующее

Загрузите демонстрационный проект из http://www.codeproject.com/KB/audio-video/videosav.aspx и скопируйте и вставьте WMLib.cs в свой проект (при необходимости измените пространство имен)

  • Создайте функцию в классе VideoCapturePlayer.cs

    /// <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;
    }
    
  • В функции SetupGraph найдите SetOutputFileName и под ним поставьте

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

  • Теперь создайте файл с именем wmv.prx на своем диске c: и поместите в него соответствующую информацию.

Здесь вы можете увидеть образец файла PRX из демонстрационного проекта: http://www.codeproject.com/KB/audio-video/videosav.aspx (Pal90.prx)

И теперь наслаждайтесь вашим файлом .wmv, выведенным в нужном размере. Да, я знаю, что код, который я разместил, был довольно лоскутным, но я оставлю его вам, чтобы отполировать его.

Ответ 2

Lifecam известен неочевидным поведением с настройкой формата захвата (более конкретно, откат к другим форматам). Смотрите обсуждения, которые могут предложить вам решение: