Как определить, можно ли соединить пины?

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

Поскольку большинство производителей карт захвата предлагают разные способы конфигурации, мое приложение попытается проанализировать соответствующие фильтры и их контакты. Одним из распространенных является проверка того, может ли определенный вывод быть подключен к рендереру напрямую, или ему сначала нужен кодировщик/мультиплексор.

Примечание. Я использую библиотеку DirectShow .NET с VB.NET, но вы можете ответить на С# или С++, если хотите.

Я попытался проверить, принимает ли выходной контакт определенный AMMediaType со следующей функцией:

Private Function Check1(filter As IBaseFilter, type As AMMediaType) As Boolean
  Dim enumPins As IEnumPins = Nothing
  If filter.EnumPins(enumPins) = 0 Then
    Dim pin(0) As IPin
    While (enumPins.Next(1, pin, Nothing) = 0)

      Dim accepted As Boolean = (pin(0).QueryAccept(type) = 0)
      Marshal.ReleaseComObject(pin(0))
      If accepted Then Return True

    End While
  End If
  Return False
End Function

Эта функция всегда возвращает False. После некоторой отладки я обнаружил, что QueryAccept всегда возвращает -2147467259. Документация не упоминать такое возвращаемое значение. После еще одного исследования я обнаружил, что QueryAccept используется для предложения нового MediaType, поэтому я предполагаю, что QueryAccept работает только тогда, когда вызывающий контакт уже подключен. Подробнее читайте здесь .

Я могу проверить, предпочитает ли определенный AMMediaType выходной контакт, используя функцию ниже:

Private Function Check2(filter As IBaseFilter, type As AMMediaType) As Boolean
  Dim enumPins As IEnumPins = Nothing
  If filter.EnumPins(enumPins) = 0 Then
    Dim pin(0) As IPin
    While (enumPins.Next(1, pin, Nothing) = 0)

      Dim enumMediaTypes As IEnumMediaTypes = Nothing
      If pin(0).EnumMediaTypes(enumMediaTypes) = 0 Then
        Dim mediaType(0) As AMMediaType
        While (enumMediaTypes.Next(1, mediaType, Nothing) = 0)

          Dim equals As Boolean = (type Is Nothing OrElse
            (type.majorType = Nothing OrElse mediaType(0).majorType = type.majorType) AndAlso
            (type.subType = Nothing OrElse mediaType(0).subType = type.subType) AndAlso
            (type.formatType = Nothing OrElse mediaType(0).formatType = type.formatType))
          DsUtils.FreeAMMediaType(mediaType(0))
          If equals Then
            Return True
          End If

        End While
      End If
    End While
  End If
  Return False
End Function

Перечисляя EnumMediaTypes, я могу определить, указан ли определенный AMMediaType как предпочтительный. Это, однако, не гарантирует мне, что связь возможна. Часто встречаются типы, которые не входят в это перечисление, но все же могут быть использованы для подключения. Иногда это перечисление пусто.

То, что я ищу, - это способ определить, может ли фильтр быть подключен к другому фильтру напрямую, или он должен сначала подключиться к кодеру/мультиплексору. Кто-нибудь знает, как я могу этого добиться?

Примечание. Просто подключить фильтры и использовать возвращаемое значение, чтобы определить, было ли оно успешным, нельзя. По моему опыту, ICaptureGraphBuilder:: RenderStream часто возвращает S_OK, хотя соединение не установлено. Например, попробуйте использовать MediaType.AnalogVideo, пока нет аналогового видеовывода.


person Rudey    schedule 29.11.2012    source источник
comment
Если вы хотите проверить, могут ли контакты подключаться напрямую, просто возьмите выходной контакт из исходного фильтра и входной контакт из средства визуализации и выполните IPin::Connect с этим адресом. Источник может напрямую подключаться к рендерингу видео только в том случае, если это необработанный тип видео, например RGB24, YUY2 или NV12. Но часто вам нужен декодер между ними. Для телевизионных карт это будет декодер MPEG2 или h264.   -  person CPlusSharp    schedule 29.11.2012
comment
@CPlusSharp: я пробовал IPin::Connect, но в некоторых случаях это не сработало должным образом. Однако это вдохновило меня на использование IGraphBuilder::Connect. Смотрите мой собственный ответ.   -  person Rudey    schedule 29.11.2012


Ответы (2)


Чтобы проверить, можно ли соединить контакты, вам нужно попробовать соединить их. Однако «обычный» метод подключения IGraphBuilder::Connect включает в себя так называемое Intelligent Connect, которое начинает пытаться вставить дополнительные фильтры посередине. Если вы этого не хотите, у вас есть метод IGraphBuilder::ConnectDirect. Не вызывайте IPin::Connect напрямую: хотя это может сработать, но не предполагается, что он вызывается напрямую, вместо этого его вызывает ConnectDirect.

Обратите внимание, что для ConnectDirect вы также должны указать тип носителя. Нулевой тип носителя может сработать, или вы можете попробовать те, которые перечислены на выходном контакте. По сути, это поведение по умолчанию, в первую очередь: выходной контакт будет пробовать тип носителя, указанный в качестве аргумента (если он не нулевой), затем пробует свой собственный, затем он пробует те, которые перечислены одноранговым входным контактом, затем начинает работать Intelligent Connect (если только он не вызов ConnectDirect).

Все RenderStream и друзья являются обертками поверх упомянутых.

Кроме того, -2147467259 означает 0x80004005 E_FAIL "Неизвестная ошибка".

person Roman R.    schedule 30.11.2012

Вместо ICaptureGraphBuilder::RenderStream, в итоге я использовал IGraphBuilder::Connect, но только для проверки возможности подключения. После подключения фильтров следующая функция немедленно отключает фильтры и использует HRESULT, чтобы определить, было ли подключение успешным:

Private Function Check3(graph As IGraphBuilder, filterOut As IBaseFilter, filterIn As IBaseFilter, type As AMMediaType) As Boolean
  Dim result As Boolean

  ' Enumerate output pins
  Dim enumPinsOut As IEnumPins = Nothing
  If filterOut.EnumPins(enumPinsOut) = 0 Then
    Dim pinOut(0) As IPin
    While enumPinsOut.Next(1, pinOut, Nothing) = 0

      ' Enumerate output media types
      Dim enumMediaTypes As IEnumMediaTypes = Nothing
      If pinOut(0).EnumMediaTypes(enumMediaTypes) = 0 Then
        Dim mediaType(0) As AMMediaType
        While enumMediaTypes.Next(1, mediaType, Nothing) = 0

          ' Compare media types
          If type Is Nothing OrElse
            (type.majorType = Nothing OrElse type.majorType = mediaType(0).majorType) AndAlso
            (type.subType = Nothing OrElse type.subType = mediaType(0).subType) AndAlso
            (type.formatType = Nothing OrElse type.formatType = mediaType(0).formatType) Then

            ' Enumerate input pins
            Dim enumPinsIn As IEnumPins = Nothing
            If filterIn.EnumPins(enumPinsIn) = 0 Then
              Dim pinIn(0) As IPin
              While enumPinsIn.Next(1, pinIn, Nothing) = 0

                ' Evaluate connection return value
                Dim hr As Integer = graph.Connect(pinOut(0), pinIn(0))
                graph.Disconnect(pinOut(0))
                result = (hr = 0)
                If result = False Then Console.WriteLine(DsError.GetErrorText(hr))

                Marshal.ReleaseComObject(pinIn(0))
                If result = True Then Exit While
              End While
            End If
          End If

          DsUtils.FreeAMMediaType(mediaType(0))
          If result = True Then Exit While
        End While
      End If

      Marshal.ReleaseComObject(pinOut(0))
      If result = True Then Exit While
    End While
  End If

  Return result
End Function
person Rudey    schedule 29.11.2012
comment
Если вы используете IGraphBuilder::Connect, тогда DirectShow использует Intelligent Connect и вставляет промежуточные фильтры! Именно это я и имел в виду в своем первом комментарии. Таким образом, вам нужно не забыть удалить теперь неиспользуемые фильтры (промежуточные фильтры) после разрыва соединения. - person CPlusSharp; 30.11.2012