Кодирование DirectShow MPEG-2 с веб-камеры в файл

Я работал над приложением, использующим Directshow.NET, и решил начать возиться с GraphEd.exe. чтобы определить точный маршрут, по которому мне нужно идти.

Графический график

Самая простая версия того, что я хочу, представлена ​​на изображении выше (где «test.mpeg» — это фильтр «Запись файлов»).

Конечно же, то, что выглядит просто в GraphEd, не так просто, как кодирование. Через несколько секунд я понял, что директ-шоу — это то, чего я просто не понимаю. Вот моя попытка кода С#:

int hr;

IBaseFilter cameraStream = null;
IBaseFilter mpegEncoder = null;
IBaseFilter fileWriter = null;
ICaptureGraphBuilder2 capGraph = null;

filterGraph = (IFilterGraph2)new FilterGraph();
try
{
    capGraph = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
    hr = capGraph.SetFiltergraph(filterGraph);
    Marshal.ThrowExceptionForHR(hr);

    hr = filterGraph.AddSourceFilterForMoniker(dev.Mon, null, dev.Name, out cameraStream);
    Marshal.ThrowExceptionForHR(hr);

    IPin cameraData = null;
    hr = capGraph.FindPin(cameraStream, PinDirection.Output, PinCategory.Capture, MediaType.Video, true, 0, out cameraData );
    //hr = cameraStream.FindPin("Capture", out cameraData);
    Marshal.ThrowExceptionForHR(hr);

    mpegEncoder = (IBaseFilter)new MJPGEnc();
    hr = filterGraph.AddFilter(mpegEncoder, "mpeg encoder");
    //hr = filterGraph.FindFilterByName("Microsoft MPEG-2 Video Encoder", out mpegEncoder);
    Marshal.ThrowExceptionForHR(hr);

    IPin mpegInput = null;
    //hr = mpegEncoder.FindPin("Input0", out mpegInput);
    hr = capGraph.FindPin(mpegEncoder, PinDirection.Input, null, null, true, 0, out mpegInput);
    Marshal.ThrowExceptionForHR(hr);

    filterGraph.Connect(cameraData, mpegInput);

    IPin mpegOutput = null;
    //hr = mpegEncoder.FindPin("Output", out mpegOutput);
    hr = capGraph.FindPin(mpegEncoder, PinDirection.Output, null, null, true, 0, out mpegOutput);
    Marshal.ThrowExceptionForHR(hr);

    //hr = filterGraph.FindFilterByName("File writer", out fileWriter);
    fileWriter = (IBaseFilter)new FileWriter();
    IFileSinkFilter test = fileWriter as IFileSinkFilter2;
    AMMediaType mtype = new AMMediaType();
    mtype.majorType = MediaType.Video;
    mtype.subType = MediaSubType.RGB24;
    mtype.formatPtr = IntPtr.Zero;
    test.SetFileName(videoPath, mtype );

    IPin fwriterIn = null;
    //hr = fileWriter.FindPin("in", out fwriterIn);
    hr = capGraph.FindPin(fileWriter, PinDirection.Input, null, null, true, 0, out fwriterIn);
    Marshal.ThrowExceptionForHR(hr);

    filterGraph.Connect(mpegOutput, fwriterIn);

    hr = capGraph.RenderStream(null, null, cameraStream, null, fileWriter); // *** Breaks on this line! ***
    Marshal.ThrowExceptionForHR(hr);

    mediaControl = filterGraph as IMediaControl;

}
finally
{
    if (cameraStream != null)
    {
        Marshal.ReleaseComObject(cameraStream);
        cameraStream = null;
    }
    if (mpegEncoder != null)
    {
        Marshal.ReleaseComObject(mpegEncoder);
        mpegEncoder = null;
    }
    if (fileWriter != null)
    {
        Marshal.ReleaseComObject(fileWriter);
        fileWriter = null;
    }
    if (capGraph != null)
    {
        Marshal.ReleaseComObject(capGraph);
        capGraph = null;
    }
}

Написав и посмотрев на то, насколько нелепым был мой код (с точки зрения логики программирования), я понял, что мне нужна помощь.

Может ли кто-нибудь направить меня к какому-то структурированному учебному пособию по директ-шоу, которое объясняет мне вещи, как если бы я был большим новичком, или, возможно, связать меня с какой-то библиотекой C #, которая обрабатывает веб-камеру для записи в файл? Обычно я не программист win32, поэтому все, что облегчает боль, должно быть хорошим :)

Кроме того, у меня есть код перечисления устройств камеры, который, кажется, работает:

private void rescan_camera()
{
    comboBox1.Items.Clear();

    DsDevice[] cameraDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);

    for(int i=0; i < cameraDevices.Length; i++) {
        comboBox1.Items.Add(cameraDevices[i].Name);
    }
    if (comboBox1.Items.Count > 0) comboBox1.SelectedIndex = 0;
}

Я могу получить данные псевдонима из DsDevice, которые, как я знаю, являются ключевой частью для указания графу фильтра directshow, откуда я хочу записывать, но это примерно то, что я знаю.

Изменить: обновленный код, показывает точное место разрыва строки.


person OzBarry    schedule 17.10.2012    source источник
comment
Ваш код не так уж плох, какая помощь вам нужна с ним? Вам лучше не использовать имена контактов, а вместо этого искать контакты как первый неподключенный выходной контакт и т. д.   -  person Roman R.    schedule 18.10.2012
comment
Ну, во-первых, я понятия не имею, как настроить средство записи файлов, чтобы оно обращалось к файлу. Кроме того, я понятия не имею, как искать контакты как первый неподключенный выходной контакт.   -  person OzBarry    schedule 18.10.2012
comment
1 В вашей последней строке есть IFileSinkFilter test, а test.SetFileName настроит средство записи, см. заголовок stackoverflow.com/questions/6416957/   -  person Roman R.    schedule 18.10.2012
comment
2 В библиотеке DirectShow.NET есть помощники, например. обратите внимание на использование ByConnectionStatus здесь pin =DsFindPin.ByConnectionStatus(Source,DirectShowLib.PinConnectedStatus.Un­­­connected, 0); tech-archive.net/Archive/Development/   -  person Roman R.    schedule 18.10.2012
comment
Я обновил свой код; когда я запускаю в режиме отладки, он ломается в строке capGraph.RenderStream - есть ли совет по этому поводу? Кроме того, если вы ответите на него, а не добавите комментарий, я могу проголосовать за вас / выбрать ваш ответ, вы были чрезвычайно полезны, и я очень ценю это!   -  person OzBarry    schedule 18.10.2012
comment
Перерывы с ошибкой HRESULT или исключением? Не совсем понятно. По сути, если у вас это работает в GraphEdit, вы можете сделать это и в коде.   -  person Roman R.    schedule 18.10.2012
comment
В mscorlib.dll произошло необработанное исключение типа «System.Runtime.InteropServices.COMException». Дополнительные сведения: при вызове COM-компонента возвращена ошибка HRESULT E_FAIL. Технически это ошибка HRESULT. Я не очень понимаю идею capGraph.RenderStream(). Я попытался поиграть с этим и решил, что это может сработать (но не сработало): hr = capGraph.RenderStream(null, null, cameraStream, cameraEncoder, fileWriter);.   -  person OzBarry    schedule 18.10.2012
comment
Я еще раз прошелся по вашему коду... у вас подключены все контакты, вам вообще не нужен RenderStream. Вся цель этого состоит в том, чтобы быть помощником, ловко соединяющим фитлеры и штифты. Однако вы уже сделали всю эту работу. Закомментируйте его и IMediaControl.Run график.   -  person Roman R.    schedule 18.10.2012
comment
Когда я комментирую RenderStream, программа не выдает никаких исключений, однако файл, который я указываю для вывода видео, не создается.   -  person OzBarry    schedule 18.10.2012
comment
Я не вижу, чтобы ты делал Run. Вам также понадобится WaitForCompletion, чтобы дождаться завершения обработки (или Thread.Sleep(10000), чтобы записать 10 секунд, если вы захватываете из живого источника, как это кажется в случае).   -  person Roman R.    schedule 18.10.2012
comment
Это часть одного класса из гораздо большей программы. Вот полный класс, и большая часть приложения вызывает MyCapture recording(device, "c:\Users\<username>\Roaming\Recording\<unix timestamp>\camera.mjpeg"); recording.StartRecording(); /* .. waits for user to click stop recording button .. */ recording.StopRecording();   -  person OzBarry    schedule 18.10.2012
comment
Итак, я закончил тем, что изменил вывод файла: вместо того, чтобы установить тип носителя для setFileName, я изменил его на null, и теперь все в порядке :)   -  person OzBarry    schedule 19.10.2012


Ответы (1)


Чтобы повторить комментарий: Итак, я закончил тем, что изменил вывод файла: вместо того, чтобы установить тип носителя для setFileName, я изменил его на ноль, и теперь все в порядке :)

person OzBarry    schedule 22.10.2012