Cómo escribir un moderador de EVR

En este artículo se describe cómo escribir un moderador personalizado para el representador de vídeo mejorado (EVR). Un moderador personalizado se puede usar con DirectShow y Media Foundation; las interfaces y el modelo de objetos son las mismas para ambas tecnologías, aunque la secuencia exacta de operaciones puede variar.

El código de ejemplo de este tema se ha adaptado del ejemplo EVRPresenter, que se proporciona en Windows SDK.

Este tema contiene las siguientes secciones:

Requisitos previos

Antes de escribir un moderador personalizado, debe estar familiarizado con las siguientes tecnologías:

  • Representador de vídeo mejorado. Consulte Representador de vídeo mejorado.
  • Gráficos direct3D. No es necesario comprender los gráficos 3D para escribir un moderador, pero debes saber cómo crear un dispositivo Direct3D y administrar superficies de Direct3D. Si no está familiarizado con Direct3D, lea las secciones "Dispositivos Direct3D" y "Recursos de Direct3D" en la documentación del SDK de gráficos de DirectX.
  • DirectShow filter graphs or the Media Foundation pipeline, dependiendo de la tecnología que usará la aplicación para representar vídeo.
  • Transformaciones de Media Foundation. El mezclador EVR es una transformación de Media Foundation y el moderador llama a métodos directamente en el mezclador.
  • Implementación de objetos COM. El moderador es un objeto COM en proceso y sin subprocesos.

Modelo de objetos del moderador

Esta sección contiene información general sobre el modelo de objetos y las interfaces del moderador.

Data Flow Dentro del EVR

El EVR usa dos componentes de complemento para representar vídeo: el mezclador y el moderador. El mezclador combina las secuencias de vídeo y desinterlaza el vídeo si es necesario. El moderador dibuja (o presenta) el vídeo en la pantalla y programa cuando se dibuja cada fotograma. Las aplicaciones pueden reemplazar cualquiera de estos objetos por una implementación personalizada.

El EVR tiene uno o varios flujos de entrada y el mezclador tiene un número correspondiente de flujos de entrada. Stream 0 es siempre la secuencia de referencia. Las otras secuencias son substreams, que el mezclador alfa combina en la secuencia de referencia. La secuencia de referencia determina la velocidad de fotogramas maestra para el vídeo compuesto. Para cada fotograma de referencia, el mezclador toma el fotograma más reciente de cada substream, los mezcla alfa en el marco de referencia y genera un único marco compuesto. El mezclador también realiza la desinterlacación y conversión de color de YUV a RGB si es necesario. El EVR siempre inserta el mezclador en la canalización de vídeo, independientemente del número de secuencias de entrada o el formato de vídeo. En la imagen siguiente se muestra este proceso.

diagrama que muestra la secuencia de referencia y la substream que apunta al mezclador, que apunta al moderador, que apunta a la pantalla

El moderador realiza las siguientes tareas:

  • Establece el formato de salida en el mezclador. Antes de que comience el streaming, el moderador establece un tipo de medio en el flujo de salida del mezclador. Este tipo de medio define el formato de la imagen compuesta.
  • Crea el dispositivo Direct3D.
  • Asigna superficies direct3D. El mezclador quita los marcos compuestos en estas superficies.
  • Obtiene la salida del mezclador.
  • Programa cuándo se presentan los fotogramas. El EVR proporciona el reloj de presentación y el moderador programa fotogramas según este reloj.
  • Presenta cada fotograma con Direct3D.
  • Realiza la ejecución paso a paso y limpieza de fotogramas.

Estados del moderador

En cualquier momento, el moderador se encuentra en uno de los siguientes estados:

  • Iniciado. El reloj de presentación del EVR se está ejecutando. El moderador programa fotogramas de vídeo para la presentación a medida que llegan.
  • En pausa. El reloj de presentación está suspendido. El moderador no presenta ninguna muestra nueva, pero mantiene su cola de ejemplos programados. Si se reciben nuevos ejemplos, el moderador los agrega a la cola.
  • Detenido. El reloj de presentación se detiene. El moderador descarta las muestras programadas.
  • Apagada. El moderador libera todos los recursos relacionados con el streaming, como las superficies de Direct3D. Este es el estado inicial del moderador y el estado final antes de que se destruya el moderador.

En el código de ejemplo de este tema, el estado del moderador se representa mediante una enumeración:

enum RENDER_STATE
{
    RENDER_STATE_STARTED = 1,
    RENDER_STATE_STOPPED,
    RENDER_STATE_PAUSED,
    RENDER_STATE_SHUTDOWN,  // Initial state.
};

Algunas operaciones no son válidas mientras el moderador está en estado de apagado. El código de ejemplo comprueba este estado mediante una llamada a un método auxiliar:

    HRESULT CheckShutdown() const
    {
        if (m_RenderState == RENDER_STATE_SHUTDOWN)
        {
            return MF_E_SHUTDOWN;
        }
        else
        {
            return S_OK;
        }
    }

Interfaces del moderador

Se requiere un moderador para implementar las siguientes interfaces:

Interfaz Descripción
IMFClockStateSink Notifica al moderador cuándo cambia el estado del reloj del EVR. Consulte Implementación de IMFClockStateSink.
IMFGetService Proporciona una manera de que la aplicación y otros componentes de la canalización obtengan interfaces del moderador.
IMFTopologyServiceLookupClient Permite al moderador obtener interfaces del EVR o del mezclador. Consulte Implementación de IMFTopologyServiceLookupClient.
IMFVideoDeviceID Garantiza que el moderador y el mezclador usen tecnologías compatibles. Vea Implementación del IMFVideoDeviceID.
IMFVideoPresenter Procesa los mensajes del EVR. Vea Implementación de IMFVideoPresenter.

 

Las interfaces siguientes son opcionales:

Interfaz Descripción
IEVRTrustedVideoPlugin Permite al moderador trabajar con medios protegidos. Implemente esta interfaz si el moderador es un componente de confianza diseñado para funcionar en la ruta de acceso multimedia protegida (PMP).
IMFRateSupport Informa del intervalo de velocidades de reproducción que admite el moderador. Consulte Implementación de IMFRateSupport.
IMFVideoPositionMapper Asigna las coordenadas del fotograma de vídeo de salida a las coordenadas del fotograma de vídeo de entrada.
IQualProp Informa de la información de rendimiento. El EVR utiliza esta información para la gestión del control de calidad. Esta interfaz se documenta en el SDK de DirectShow.

 

También puede proporcionar interfaces para que la aplicación se comunique con el moderador. El moderador estándar implementa la interfaz IMFVideoDisplayControl para este fin. Puede implementar esta interfaz o definir la suya propia. La aplicación obtiene interfaces del moderador mediante una llamada a IMFGetService::GetService en el EVR. Cuando el GUID del servicio se MR_VIDEO_RENDER_SERVICE, el EVR pasa la solicitud GetService al moderador.

Implementación del IMFVideoDeviceID

La interfaz IMFVideoDeviceID contiene un método , GetDeviceID, que devuelve un GUID de dispositivo. El GUID del dispositivo garantiza que el moderador y el mezclador usen tecnologías compatibles. Si los GUID del dispositivo no coinciden, el EVR no se inicializa.

El mezclador estándar y el moderador usan Direct3D 9, con el GUID del dispositivo igual a IID_IDirect3DDevice9. Si tiene previsto usar el moderador personalizado con el mezclador estándar, el GUID del dispositivo del moderador debe ser IID_IDirect3DDevice9. Si reemplaza ambos componentes, podría definir un nuevo GUID de dispositivo. En el resto de este artículo, se supone que el moderador usa Direct3D 9. Esta es la implementación estándar de GetDeviceID:

HRESULT EVRCustomPresenter::GetDeviceID(IID* pDeviceID)
{
    if (pDeviceID == NULL)
    {
        return E_POINTER;
    }

    *pDeviceID = __uuidof(IDirect3DDevice9);
    return S_OK;
}

El método debe realizarse correctamente incluso cuando se cierra el moderador.

Implementación de IMFTopologyServiceLookupClient

La interfaz IMFTopologyServiceLookupClient permite al moderador obtener punteros de interfaz desde el EVR y desde el mezclador de la siguiente manera:

  1. Cuando el EVR inicializa el moderador, llama al método IMFTopologyServiceLookupClient::InitServicePointers del moderador. El argumento es un puntero a la interfaz IMFTopologyServiceLookup del EVR.
  2. El moderador llama a IMFTopologyServiceLookup::LookupService para obtener punteros de interfaz desde el EVR o el mezclador.

El método LookupService es similar al método IMFGetService::GetService . Ambos métodos toman un GUID de servicio y un identificador de interfaz (IID) como entrada, pero LookupService devuelve una matriz de punteros de interfaz, mientras que GetService devuelve un único puntero. En la práctica, sin embargo, siempre puede establecer el tamaño de la matriz en 1. El objeto consultado depende del GUID del servicio:

  • Si el GUID del servicio es MR_VIDEO_RENDER_SERVICE, se consulta el EVR.
  • Si el GUID del servicio es MR_VIDEO_MIXER_SERVICE, se consulta el mezclador.

En la implementación de InitServicePointers, obtenga las siguientes interfaces de EVR:

Interfaz EVR Descripción
IMediaEventSink Proporciona una manera de que el moderador envíe mensajes al EVR. Esta interfaz se define en el SDK de DirectShow, por lo que los mensajes siguen el patrón de eventos DirectShow, no los eventos de Media Foundation.
IMFClock Representa el reloj del EVR. El moderador usa esta interfaz para programar ejemplos de presentación. El EVR se puede ejecutar sin un reloj, por lo que es posible que esta interfaz no esté disponible. Si no es así, ignore el código de error de LookupService.
El reloj también implementa la interfaz IMFTimer . En la canalización de Media Foundation, el reloj implementa la interfaz IMFPresentationClock . No implementa esta interfaz en DirectShow.

 

Obtenga las siguientes interfaces del mezclador:

Interfaz de mezclador Descripción
IMFTransform Permite al moderador comunicarse con el mezclador.
IMFVideoDeviceID Permite al moderador validar el GUID del dispositivo del mezclador.

 

El código siguiente implementa el método InitServicePointers :

HRESULT EVRCustomPresenter::InitServicePointers(
    IMFTopologyServiceLookup *pLookup
    )
{
    if (pLookup == NULL)
    {
        return E_POINTER;
    }

    HRESULT             hr = S_OK;
    DWORD               dwObjectCount = 0;

    EnterCriticalSection(&m_ObjectLock);

    // Do not allow initializing when playing or paused.
    if (IsActive())
    {
        hr = MF_E_INVALIDREQUEST;
        goto done;
    }

    SafeRelease(&m_pClock);
    SafeRelease(&m_pMixer);
    SafeRelease(&m_pMediaEventSink);

    // Ask for the clock. Optional, because the EVR might not have a clock.
    dwObjectCount = 1;

    (void)pLookup->LookupService(
        MF_SERVICE_LOOKUP_GLOBAL,   // Not used.
        0,                          // Reserved.
        MR_VIDEO_RENDER_SERVICE,    // Service to look up.
        IID_PPV_ARGS(&m_pClock),    // Interface to retrieve.
        &dwObjectCount              // Number of elements retrieved.
        );

    // Ask for the mixer. (Required.)
    dwObjectCount = 1;

    hr = pLookup->LookupService(
        MF_SERVICE_LOOKUP_GLOBAL, 0,
        MR_VIDEO_MIXER_SERVICE, IID_PPV_ARGS(&m_pMixer), &dwObjectCount
        );

    if (FAILED(hr))
    {
        goto done;
    }

    // Make sure that we can work with this mixer.
    hr = ConfigureMixer(m_pMixer);
    if (FAILED(hr))
    {
        goto done;
    }

    // Ask for the EVR's event-sink interface. (Required.)
    dwObjectCount = 1;

    hr = pLookup->LookupService(
        MF_SERVICE_LOOKUP_GLOBAL, 0,
        MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_pMediaEventSink),
        &dwObjectCount
        );

    if (FAILED(hr))
    {
        goto done;
    }

    // Successfully initialized. Set the state to "stopped."
    m_RenderState = RENDER_STATE_STOPPED;

done:
    LeaveCriticalSection(&m_ObjectLock);
    return hr;
}

Cuando los punteros de interfaz obtenidos de LookupService ya no son válidos, el EVR llama a IMFTopologyServiceLookupClient::ReleaseServicePointers. Dentro de este método, libere todos los punteros de interfaz y establezca el estado del moderador para apagar:

HRESULT EVRCustomPresenter::ReleaseServicePointers()
{
    // Enter the shut-down state.
    EnterCriticalSection(&m_ObjectLock);

    m_RenderState = RENDER_STATE_SHUTDOWN;

    LeaveCriticalSection(&m_ObjectLock);

    // Flush any samples that were scheduled.
    Flush();

    // Clear the media type and release related resources.
    SetMediaType(NULL);

    // Release all services that were acquired from InitServicePointers.
    SafeRelease(&m_pClock);
    SafeRelease(&m_pMixer);
    SafeRelease(&m_pMediaEventSink);

    return S_OK;
}

EvR llama a ReleaseServicePointers por diversos motivos, entre los que se incluyen:

  • Desconexión o reconexión de patillas (DirectShow) o adición o eliminación de receptores de secuencias (Media Foundation).
  • Cambiar formato.
  • Establecer un reloj nuevo.
  • Cierre final del EVR.

Durante la vigencia del moderador, el EVR podría llamar a InitServicePointers y ReleaseServicePointers varias veces.

Implementación de IMFVideoPresenter

La interfaz IMFVideoPresenter hereda IMFClockStateSink y agrega dos métodos:

Método Descripción
GetCurrentMediaType Devuelve el tipo de medio de los fotogramas de vídeo compuestos.
ProcessMessage Indica al moderador que realice varias acciones.

 

El método GetCurrentMediaType devuelve el tipo de medio del moderador. (Para obtener más información sobre cómo establecer el tipo de medio, vea Negociación de formatos). El tipo de medio se devuelve como puntero de interfaz IMFVideoMediaType . En el ejemplo siguiente se supone que el moderador almacena el tipo de medio como puntero IMFMediaType . Para obtener la interfaz IMFVideoMediaType del tipo de medio, llame a QueryInterface:

HRESULT EVRCustomPresenter::GetCurrentMediaType(
    IMFVideoMediaType** ppMediaType
    )
{
    HRESULT hr = S_OK;

    if (ppMediaType == NULL)
    {
        return E_POINTER;
    }

    *ppMediaType = NULL;

    EnterCriticalSection(&m_ObjectLock);

    hr = CheckShutdown();
    if (FAILED(hr))
    {
        goto done;
    }

    if (m_pMediaType == NULL)
    {
        hr = MF_E_NOT_INITIALIZED;
        goto done;
    }

    hr = m_pMediaType->QueryInterface(IID_PPV_ARGS(ppMediaType));

done:
    LeaveCriticalSection(&m_ObjectLock);
    return hr;
}

El método ProcessMessage es el mecanismo principal para que el EVR se comunique con el moderador. Se definen los siguientes mensajes. Los detalles de la implementación de cada mensaje se proporcionan en el resto de este tema.

Message Descripción
MFVP_MESSAGE_INVALIDATEMEDIATYPE El tipo de medio de salida del mezclador no es válido. El moderador debe negociar un nuevo tipo de medio con el mezclador. Consulte Formatos de negociación.
MFVP_MESSAGE_BEGINSTREAMING Se ha iniciado el streaming. Este mensaje no requiere ninguna acción concreta, pero puede usarla para asignar recursos.
MFVP_MESSAGE_ENDSTREAMING La transmisión ha finalizado. Libere los recursos que haya asignado en respuesta al mensaje MFVP_MESSAGE_BEGINSTREAMING .
MFVP_MESSAGE_PROCESSINPUTNOTIFY El mezclador ha recibido un nuevo ejemplo de entrada y podría generar un nuevo marco de salida. El moderador debe llamar a IMFTransform::P rocessOutput en el mezclador. Consulte Procesamiento de la salida.
MFVP_MESSAGE_ENDOFSTREAM La presentación ha finalizado. Consulte Fin de la secuencia.
MFVP_MESSAGE_FLUSH EL EVR vacia los datos en su canalización de representación. El moderador debe descartar los fotogramas de vídeo programados para la presentación.
MFVP_MESSAGE_STEP Solicita al moderador que avance N fotogramas. El moderador debe descartar los siguientes fotogramas N-1 y mostrar el fotograma Nth. Consulte Paso a paso de fotogramas.
MFVP_MESSAGE_CANCELSTEP Cancela la ejecución paso a paso de fotogramas.

 

Implementación de IMFClockStateSink

El moderador debe implementar la interfaz IMFClockStateSink como parte de su implementación de IMFVideoPresenter, que hereda IMFClockStateSink. El EVR usa esta interfaz para notificar al moderador cada vez que cambia el estado del reloj del EVR. Para obtener más información sobre los estados del reloj, vea Reloj de presentación.

Estas son algunas directrices para implementar los métodos en esta interfaz. Todos los métodos deben producir un error si el moderador está apagado.

Método Descripción
OnClockStart
  1. Establezca el estado del moderador en iniciado.
  2. Si llClockStartOffset no está PRESENTATION_CURRENT_POSITION, vacíe la cola de ejemplos del moderador. (Esto equivale a recibir un mensaje de MFVP_MESSAGE_FLUSH ).
  3. Si una solicitud de paso de fotograma anterior sigue pendiente, procese la solicitud (consulte Paso a paso de fotogramas). De lo contrario, intente procesar la salida del mezclador (consulte Procesamiento de salida.
OnClockStop
  1. Establezca el estado del moderador en detenido.
  2. Vacíe la cola de ejemplos del moderador.
  3. Cancele cualquier operación de paso de fotograma pendiente.
OnClockPause Establezca el estado del moderador en pausa.
OnClockRestart Trate lo mismo que OnClockStart , pero no vacíe la cola de ejemplos.
OnClockSetRate
  1. Si la velocidad cambia de cero a cero, cancele la ejecución paso a paso por fotogramas.
  2. Almacene la nueva velocidad de reloj. La velocidad del reloj afecta cuando se presentan muestras. Para obtener más información, vea Ejemplos de programación.

 

Implementación de IMFRateSupport

Para admitir velocidades de reproducción distintas de 1× velocidad, el moderador debe implementar la interfaz IMFRateSupport . Estas son algunas directrices para implementar los métodos en esta interfaz. Todos los métodos deben producir un error después de que se apague el moderador. Para obtener más información sobre esta interfaz, vea Control de velocidad.

Valor Descripción
GetSlowestRate Devuelve cero para indicar ninguna velocidad de reproducción mínima.
GetFastestRate Para la reproducción no delgada, la velocidad de reproducción no debe superar la frecuencia de actualización del monitor: velocidad = deactualización máxima (Hz) / velocidad de fotogramas de vídeo (fps). La velocidad de fotogramas de vídeo se especifica en el tipo de medio del moderador.
Para la reproducción fina, la velocidad de reproducción no está delimitada; devuelve el valor FLT_MAX. En la práctica, el origen y el descodificador serán los factores de limitación durante la reproducción fina.
Para la reproducción inversa, devuelve el negativo de la velocidad máxima.
IsRateSupported Devuelve MF_E_UNSUPPORTED_RATE si el valor absoluto de flRate supera la velocidad de reproducción máxima del moderador. Calcule la velocidad de reproducción máxima tal y como se describe en GetFastestRate.

 

En el ejemplo siguiente se muestra cómo implementar el método GetFastestRate :

float EVRCustomPresenter::GetMaxRate(BOOL bThin)
{
    // Non-thinned:
    // If we have a valid frame rate and a monitor refresh rate, the maximum
    // playback rate is equal to the refresh rate. Otherwise, the maximum rate
    // is unbounded (FLT_MAX).

    // Thinned: The maximum rate is unbounded.

    float   fMaxRate = FLT_MAX;
    MFRatio fps = { 0, 0 };
    UINT    MonitorRateHz = 0;

    if (!bThin && (m_pMediaType != NULL))
    {
        GetFrameRate(m_pMediaType, &fps);
        MonitorRateHz = m_pD3DPresentEngine->RefreshRate();

        if (fps.Denominator && fps.Numerator && MonitorRateHz)
        {
            // Max Rate = Refresh Rate / Frame Rate
            fMaxRate = (float)MulDiv(
                MonitorRateHz, fps.Denominator, fps.Numerator);
        }
    }

    return fMaxRate;
}

En el ejemplo anterior se llama a un método auxiliar, GetMaxRate, para calcular la velocidad máxima de reproducción hacia delante:

En el ejemplo siguiente se muestra cómo implementar el método IsRateSupported :

HRESULT EVRCustomPresenter::IsRateSupported(
    BOOL bThin,
    float fRate,
    float *pfNearestSupportedRate
    )
{
    EnterCriticalSection(&m_ObjectLock);

    float   fMaxRate = 0.0f;
    float   fNearestRate = fRate;  // If we support fRate, that is the nearest.

    HRESULT hr = CheckShutdown();
    if (FAILED(hr))
    {
        goto done;
    }

    // Find the maximum forward rate.
    // Note: We have no minimum rate (that is, we support anything down to 0).
    fMaxRate = GetMaxRate(bThin);

    if (fabsf(fRate) > fMaxRate)
    {
        // The (absolute) requested rate exceeds the maximum rate.
        hr = MF_E_UNSUPPORTED_RATE;

        // The nearest supported rate is fMaxRate.
        fNearestRate = fMaxRate;
        if (fRate < 0)
        {
            // Negative for reverse playback.
            fNearestRate = -fNearestRate;
        }
    }

    // Return the nearest supported rate.
    if (pfNearestSupportedRate != NULL)
    {
        *pfNearestSupportedRate = fNearestRate;
    }

done:
    LeaveCriticalSection(&m_ObjectLock);
    return hr;
}

Envío de eventos al EVR

El moderador debe notificar al EVR de varios eventos. Para ello, usa la interfaz IMediaEventSink de EVR, obtenida cuando el EVR llama al método IMFTopologyServiceLookupClient::InitServicePointers del moderador. (La interfaz IMediaEventSink es originalmente una interfaz DirectShow, pero se usa en directShow EVR y Media Foundation). En el código siguiente se muestra cómo enviar un evento al EVR:

    // NotifyEvent: Send an event to the EVR through its IMediaEventSink interface.
    void NotifyEvent(long EventCode, LONG_PTR Param1, LONG_PTR Param2)
    {
        if (m_pMediaEventSink)
        {
            m_pMediaEventSink->Notify(EventCode, Param1, Param2);
        }
    }

En la tabla siguiente se enumeran los eventos que envía el moderador, junto con los parámetros del evento.

Evento Descripción
EC_COMPLETE El moderador ha terminado de representar todos los fotogramas después del mensaje de MFVP_MESSAGE_ENDOFSTREAM.
  • Param1: HRESULT que indica el estado de la operación.
  • Param2: No se usa.
Para obtener más información, vea Fin de secuencia.
EC_DISPLAY_CHANGED El dispositivo Direct3D ha cambiado.
  • Param1: No se usa.
  • Param2: No se usa.
Para obtener más información, consulte Administración del dispositivo Direct3D.
EC_ERRORABORT Se ha producido un error que requiere que se detenga el streaming.
  • Param1: HRESULT que indica el error que se produjo.
  • Param2: No se usa.
EC_PROCESSING_LATENCY Especifica la cantidad de tiempo que tarda el moderador en representar cada fotograma. (Opcional).
  • Param1: puntero a un valor LONGLONG constante que contiene la cantidad de tiempo para procesar el fotograma, en unidades de 100 nanosegundos.
  • Param2: No se usa.
Para obtener más información, consulte Procesamiento de la salida.
EC_SAMPLE_LATENCY Especifica el tiempo de retardo actual en los ejemplos de representación. Si el valor es positivo, las muestras están retrasadas. Si el valor es negativo, las muestras están por delante de la programación. (Opcional).
  • Param1: puntero a un valor LONGLONG constante que contiene el tiempo de retardo, en unidades de 100 nanosegundos.
  • Param2: No se usa.
EC_SCRUB_TIME Se envía inmediatamente después de EC_STEP_COMPLETE si la velocidad de reproducción es cero. Este evento contiene la marca de tiempo del marco que se mostró.
  • Param1: 32 bits inferiores de la marca de tiempo.
  • Param2: 32 bits superiores de la marca de tiempo.
Para obtener más información, vea Paso a paso de fotogramas.
EC_STEP_COMPLETE El moderador ha completado o cancelado un paso de marco.
- Param1: No se usa.
- Param2: No se usa.
Para obtener más información, vea Paso a paso de fotogramas.
Nota: Una versión anterior de la documentación descrita Param1 incorrectamente. Este parámetro no se usa para este evento.

 

Formatos de negociación

Cada vez que el moderador recibe un mensaje de MFVP_MESSAGE_INVALIDATEMEDIATYPE del EVR, debe establecer el formato de salida en el mezclador, como se indica a continuación:

  1. Llame a IMFTransform::GetOutputAvailableType en el mezclador para obtener un posible tipo de salida. Este tipo describe un formato que el mezclador puede producir dadas las secuencias de entrada y las funcionalidades de procesamiento de vídeo del dispositivo gráfico.

  2. Compruebe si el moderador puede usar este tipo de medio como formato de representación. Estos son algunos aspectos que se deben comprobar, aunque la implementación puede tener sus propios requisitos:

    • El vídeo debe estar sin comprimir.
    • El vídeo solo debe tener fotogramas progresivos. Compruebe que el atributo MF_MT_INTERLACE_MODE es igual a MFVideoInterlace_Progressive.
    • El formato debe ser compatible con el dispositivo Direct3D.

    Si el tipo no es aceptable, vuelva al paso 1 y obtenga el siguiente tipo propuesto del mezclador.

  3. Cree un nuevo tipo de medio que sea un clon del tipo original y, a continuación, cambie los atributos siguientes:

    • Establezca el atributo MF_MT_FRAME_SIZE igual al ancho y alto que desee para las superficies de Direct3D que asignará.
    • Establezca el atributo MF_MT_PAN_SCAN_ENABLED en FALSE.
    • Establezca el atributo MF_MT_PIXEL_ASPECT_RATIO igual al par de la pantalla (normalmente 1:1).
    • Establezca la apertura geométrica (MF_MT_GEOMETRIC_APERTURE atributo) igual a un rectángulo dentro de la superficie de Direct3D. Cuando el mezclador genera un marco de salida, cambia la imagen de origen a este rectángulo. La apertura geométrica puede ser tan grande como la superficie, o puede ser un subrectangle dentro de la superficie. Para obtener más información, vea Rectángulos de origen y destino.
  4. Para comprobar si el mezclador aceptará el tipo de salida modificado, llame a IMFTransform::SetOutputType con la marca MFT_SET_TYPE_TEST_ONLY . Si el mezclador rechaza el tipo, vuelva al paso 1 y obtenga el siguiente tipo.

  5. Asigne un grupo de superficies de Direct3D, como se describe en Asignar superficies de Direct3D. El mezclador usará estas superficies cuando dibuje los fotogramas de vídeo compuestos.

  6. Establezca el tipo de salida en el mezclador llamando a SetOutputType sin marcas. Si la primera llamada a SetOutputType se realizó correctamente en el paso 4, el método debería volver a realizarse correctamente.

Si el mezclador se queda sin tipos, el método GetOutputAvailableType devuelve MF_E_NO_MORE_TYPES. Si el moderador no encuentra un tipo de salida adecuado para el mezclador, no se puede representar la secuencia. En ese caso, DirectShow o Media Foundation podrían probar otro formato de secuencia. Por lo tanto, el moderador puede recibir varios mensajes de MFVP_MESSAGE_INVALIDATEMEDIATYPE en una fila hasta que se encuentre un tipo válido.

El mezclador introduce automáticamente los cuadros de letras del vídeo, teniendo en cuenta la relación de aspecto de píxeles (PAR) del origen y el destino. Para obtener los mejores resultados, el ancho y la altura de la superficie y la apertura geométrica deben ser iguales al tamaño real que desea que el vídeo aparezca en la pantalla. En la imagen siguiente se muestra este proceso.

diagrama que muestra una fram compuesta que conduce a una superficie direct3d, lo que conduce a una ventana

En el código siguiente se muestra el esquema del proceso. Algunos de los pasos se colocan en funciones auxiliares, los detalles exactos de los cuales dependerán de los requisitos del moderador.

HRESULT EVRCustomPresenter::RenegotiateMediaType()
{
    HRESULT hr = S_OK;
    BOOL bFoundMediaType = FALSE;

    IMFMediaType *pMixerType = NULL;
    IMFMediaType *pOptimalType = NULL;
    IMFVideoMediaType *pVideoType = NULL;

    if (!m_pMixer)
    {
        return MF_E_INVALIDREQUEST;
    }

    // Loop through all of the mixer's proposed output types.
    DWORD iTypeIndex = 0;
    while (!bFoundMediaType && (hr != MF_E_NO_MORE_TYPES))
    {
        SafeRelease(&pMixerType);
        SafeRelease(&pOptimalType);

        // Step 1. Get the next media type supported by mixer.
        hr = m_pMixer->GetOutputAvailableType(0, iTypeIndex++, &pMixerType);
        if (FAILED(hr))
        {
            break;
        }

        // From now on, if anything in this loop fails, try the next type,
        // until we succeed or the mixer runs out of types.

        // Step 2. Check if we support this media type.
        if (SUCCEEDED(hr))
        {
            // Note: None of the modifications that we make later in CreateOptimalVideoType
            // will affect the suitability of the type, at least for us. (Possibly for the mixer.)
            hr = IsMediaTypeSupported(pMixerType);
        }

        // Step 3. Adjust the mixer's type to match our requirements.
        if (SUCCEEDED(hr))
        {
            hr = CreateOptimalVideoType(pMixerType, &pOptimalType);
        }

        // Step 4. Check if the mixer will accept this media type.
        if (SUCCEEDED(hr))
        {
            hr = m_pMixer->SetOutputType(0, pOptimalType, MFT_SET_TYPE_TEST_ONLY);
        }

        // Step 5. Try to set the media type on ourselves.
        if (SUCCEEDED(hr))
        {
            hr = SetMediaType(pOptimalType);
        }

        // Step 6. Set output media type on mixer.
        if (SUCCEEDED(hr))
        {
            hr = m_pMixer->SetOutputType(0, pOptimalType, 0);

            assert(SUCCEEDED(hr)); // This should succeed unless the MFT lied in the previous call.

            // If something went wrong, clear the media type.
            if (FAILED(hr))
            {
                SetMediaType(NULL);
            }
        }

        if (SUCCEEDED(hr))
        {
            bFoundMediaType = TRUE;
        }
    }

    SafeRelease(&pMixerType);
    SafeRelease(&pOptimalType);
    SafeRelease(&pVideoType);

    return hr;
}

Para obtener más información sobre los tipos de medios de vídeo, vea Tipos de medios de vídeo.

Administración del dispositivo Direct3D

El moderador crea el dispositivo Direct3D y controla cualquier pérdida de dispositivo durante el streaming. El moderador también hospeda el administrador de dispositivos direct3D, que proporciona una manera de que otros componentes usen el mismo dispositivo. Por ejemplo, el mezclador usa el dispositivo Direct3D para mezclar substreams, desinterlace y realizar ajustes de color. Los descodificadores pueden usar el dispositivo Direct3D para la descodificación acelerada por vídeo. (Para obtener más información sobre la aceleración de vídeo, consulta DirectX Video Acceleration 2.0).

Para configurar el dispositivo Direct3D, realice los pasos siguientes:

  1. Cree el objeto Direct3D llamando a Direct3DCreate9 o Direct3DCreate9Ex.
  2. Cree el dispositivo llamando a IDirect3D9::CreateDevice o IDirect3D9Ex::CreateDevice.
  3. Cree el administrador de dispositivos llamando a DXVA2CreateDirect3DDeviceManager9.
  4. Establezca el dispositivo en el administrador de dispositivos llamando a IDirect3DDeviceManager9::ResetDevice.

Si otro componente de canalización necesita el administrador de dispositivos, llama a IMFGetService::GetService en el EVR, especificando MR_VIDEO_ACCELERATION_SERVICE para el GUID del servicio. El EVR pasa la solicitud al moderador. Una vez que el objeto obtiene el puntero IDirect3DDeviceManager9 , puede obtener un identificador para el dispositivo llamando a IDirect3DDeviceManager9::OpenDeviceHandle. Cuando el objeto necesita usar el dispositivo, pasa el identificador del dispositivo al método IDirect3DDeviceManager9::LockDevice , que devuelve un puntero IDirect3DDevice9 .

Una vez creado el dispositivo, si el moderador destruye el dispositivo y crea uno nuevo, el moderador debe llamar a ResetDevice de nuevo. El método ResetDevice invalida los identificadores de dispositivo existentes, lo que hace que LockDevice devuelva DXVA2_E_NEW_VIDEO_DEVICE. Este código de error indica a otros objetos mediante el dispositivo que deben abrir un nuevo identificador de dispositivo. Para obtener más información sobre el uso del administrador de dispositivos, consulte Direct3D Administrador de dispositivos.

El moderador puede crear el dispositivo en modo de ventana o en modo exclusivo de pantalla completa. Para el modo con ventanas, debe proporcionar una manera de que la aplicación especifique la ventana de vídeo. El moderador estándar implementa el método IMFVideoDisplayControl::SetVideoWindow para este fin. Debe crear el dispositivo cuando se cree por primera vez el moderador. Normalmente, no conocerá todos los parámetros del dispositivo en este momento, como la ventana o el formato de búfer de reserva. Puede crear un dispositivo temporal y reemplazarlo más adelante&#;simplemente recuerde llamar a ResetDevice en el administrador de dispositivos.

Si crea un dispositivo o llama a IDirect3DDevice9::Reset o IDirect3DDevice9Ex::ResetEx en un dispositivo existente, envíe un evento EC_DISPLAY_CHANGED al EVR. Este evento notifica al EVR que renegocia el tipo de medio. El EVR omite los parámetros de evento de este evento.

Asignación de superficies direct3D

Una vez que el moderador establece el tipo de medio, puede asignar las superficies de Direct3D, que el mezclador usará para escribir los fotogramas de vídeo. La superficie debe coincidir con el tipo de medio del moderador:

  • El formato de superficie debe coincidir con el subtipo de medio. Por ejemplo, si el subtipo es MFVideoFormat_RGB24, el formato de superficie debe ser D3DFMT_X8R8G8B8. Para obtener más información sobre los subtipos y los formatos Direct3D, consulta Guid de subtipo de vídeo.
  • El ancho y el alto de la superficie deben coincidir con las dimensiones especificadas en el atributo MF_MT_FRAME_SIZE del tipo de medio.

La manera recomendada de asignar superficies depende de si el moderador se ejecuta en ventanas o en pantalla completa.

Si se muestra el dispositivo Direct3D, puede crear varias cadenas de intercambio, cada una con un único búfer de reserva. Con este enfoque, puede presentar cada superficie de forma independiente, ya que la presentación de una cadena de intercambio no interferirá con las demás cadenas de intercambio. El mezclador puede escribir datos en una superficie mientras se programa otra superficie para la presentación.

En primer lugar, decida cuántas cadenas de intercambio se van a crear. Se recomienda un mínimo de tres. Para cada cadena de intercambio, haga lo siguiente:

  1. Llame a IDirect3DDevice9::CreateAdditionalSwapChain para crear la cadena de intercambio.
  2. Llame a IDirect3DSwapChain9::GetBackBuffer para obtener un puntero a la superficie de búfer de reserva de la cadena de intercambio.
  3. Llama a MFCreateVideoSampleFromSurface y pasa un puntero a la superficie. Esta función devuelve un puntero a un objeto de ejemplo de vídeo. El objeto de ejemplo de vídeo implementa la interfaz IMFSample y el moderador usa esta interfaz para entregar la superficie al mezclador cuando el moderador llama al método IMFTransform::P rocessOutput del mezclador. Para obtener más información sobre el objeto de ejemplo de vídeo, vea Ejemplos de vídeo.
  4. Almacene el puntero IMFSample en una cola. El moderador extraerá ejemplos de esta cola durante el procesamiento, tal y como se describe en Procesamiento de salida.
  5. Mantenga una referencia al puntero IDirect3DSwapChain9 para que la cadena de intercambio no se libere.

En modo exclusivo de pantalla completa, el dispositivo no puede tener más de una cadena de intercambio. Esta cadena de intercambio se crea implícitamente al crear el dispositivo de pantalla completa. La cadena de intercambio puede tener más de un búfer de reserva. Desafortunadamente, sin embargo, si presenta un búfer de reserva mientras escribe en otro búfer de reserva en la misma cadena de intercambio, no hay ninguna manera fácil de coordinar las dos operaciones. Esto se debe a la forma en que Direct3D implementa el volteo de superficie. Cuando llamas a Present, el controlador de gráficos actualiza los punteros de superficie en la memoria de gráficos. Si mantienes punteros IDirect3DSurface9 al llamar a Present, apuntarán a búferes diferentes después de que se devuelva la llamada Present .

La opción más sencilla es crear un ejemplo de vídeo para la cadena de intercambio. Si elige esta opción, siga los mismos pasos indicados para el modo con ventanas. La única diferencia es que la cola de ejemplo contiene un solo ejemplo de vídeo. Otra opción es crear superficies fuera de la pantalla y, a continuación, transferirlas al búfer de reserva. Las superficies que cree deben admitir el método IDirectXVideoProcessor::VideoProcessBlt , que el mezclador usa para componer los fotogramas de salida.

Ejemplos de seguimiento

Cuando el moderador asigna primero los ejemplos de vídeo, los coloca en una cola. El moderador extrae de esta cola siempre que necesite obtener un nuevo marco del mezclador. Una vez que el mezclador genera el fotograma, el moderador mueve la muestra a una segunda cola. La segunda cola es para ejemplos que están esperando sus tiempos de presentación programados.

Para facilitar el seguimiento del estado de cada muestra, el objeto de ejemplo de vídeo implementa la interfaz IMFTrackedSample . Puede usar esta interfaz de la siguiente manera:

  1. Implemente la interfaz IMFAsyncCallback en el moderador.

  2. Antes de colocar un ejemplo en la cola programada, consulte el objeto de ejemplo de vídeo para la interfaz IMFTrackedSample .

  3. Llame a IMFTrackedSample::SetAllocator con un puntero a la interfaz de devolución de llamada.

  4. Cuando el ejemplo esté listo para la presentación, quítelo de la cola programada, presentelo y libere todas las referencias al ejemplo.

  5. El ejemplo invoca la devolución de llamada. (El objeto de ejemplo no se elimina en este caso porque contiene un recuento de referencias en sí mismo hasta que se invoca la devolución de llamada).

  6. Dentro de la devolución de llamada, devuelva el ejemplo a la cola disponible.

No es necesario que un moderador utilice IMFTrackedSample para realizar un seguimiento de las muestras; puede implementar cualquier técnica que funcione mejor para su diseño. Una ventaja de IMFTrackedSample es que puede mover las funciones de programación y representación del moderador a objetos auxiliares, y estos objetos no necesitan ningún mecanismo especial para volver a llamar al moderador cuando publiquen muestras de vídeo porque el objeto de ejemplo proporciona ese mecanismo.

El código siguiente muestra cómo establecer la devolución de llamada:

HRESULT EVRCustomPresenter::TrackSample(IMFSample *pSample)
{
    IMFTrackedSample *pTracked = NULL;

    HRESULT hr = pSample->QueryInterface(IID_PPV_ARGS(&pTracked));

    if (SUCCEEDED(hr))
    {
        hr = pTracked->SetAllocator(&m_SampleFreeCB, NULL);
    }

    SafeRelease(&pTracked);
    return hr;
}

En la devolución de llamada, llame a IMFAsyncResult::GetObject en el objeto de resultado asincrónico para recuperar un puntero al ejemplo:

HRESULT EVRCustomPresenter::OnSampleFree(IMFAsyncResult *pResult)
{
    IUnknown *pObject = NULL;
    IMFSample *pSample = NULL;
    IUnknown *pUnk = NULL;

    // Get the sample from the async result object.
    HRESULT hr = pResult->GetObject(&pObject);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pObject->QueryInterface(IID_PPV_ARGS(&pSample));
    if (FAILED(hr))
    {
        goto done;
    }

    // If this sample was submitted for a frame-step, the frame step operation
    // is complete.

    if (m_FrameStep.state == FRAMESTEP_SCHEDULED)
    {
        // Query the sample for IUnknown and compare it to our cached value.
        hr = pSample->QueryInterface(IID_PPV_ARGS(&pUnk));
        if (FAILED(hr))
        {
            goto done;
        }

        if (m_FrameStep.pSampleNoRef == (DWORD_PTR)pUnk)
        {
            // Notify the EVR.
            hr = CompleteFrameStep(pSample);
            if (FAILED(hr))
            {
                goto done;
            }
        }

        // Note: Although pObject is also an IUnknown pointer, it is not
        // guaranteed to be the exact pointer value returned through
        // QueryInterface. Therefore, the second QueryInterface call is
        // required.
    }

    /*** Begin lock ***/

    EnterCriticalSection(&m_ObjectLock);

    UINT32 token = MFGetAttributeUINT32(
        pSample, MFSamplePresenter_SampleCounter, (UINT32)-1);

    if (token == m_TokenCounter)
    {
        // Return the sample to the sample pool.
        hr = m_SamplePool.ReturnSample(pSample);
        if (SUCCEEDED(hr))
        {
            // A free sample is available. Process more data if possible.
            (void)ProcessOutputLoop();
        }
    }

    LeaveCriticalSection(&m_ObjectLock);

    /*** End lock ***/

done:
    if (FAILED(hr))
    {
        NotifyEvent(EC_ERRORABORT, hr, 0);
    }
    SafeRelease(&pObject);
    SafeRelease(&pSample);
    SafeRelease(&pUnk);
    return hr;
}

Procesamiento de la salida

Cada vez que el mezclador recibe una nueva muestra de entrada, el EVR envía un mensaje MFVP_MESSAGE_PROCESSINPUTNOTIFY al moderador. Este mensaje indica que el mezclador podría tener un nuevo fotograma de vídeo para entregar. En respuesta, el moderador llama a IMFTransform::P rocessOutput en el mezclador. Si el método se realiza correctamente, el moderador programa el ejemplo para la presentación.

Para obtener la salida del mezclador, realice los pasos siguientes:

  1. Compruebe el estado del reloj. Si el reloj está en pausa, ignore el mensaje MFVP_MESSAGE_PROCESSINPUTNOTIFY a menos que sea el primer fotograma de vídeo. Si el reloj se está ejecutando o si es el primer fotograma de vídeo, continúe.

  2. Obtenga un ejemplo de la cola de ejemplos disponibles. Si la cola está vacía, significa que todas las muestras asignadas están programadas actualmente para presentarse. En ese caso, omita el mensaje MFVP_MESSAGE_PROCESSINPUTNOTIFY en este momento. Cuando el ejemplo siguiente esté disponible, repita los pasos que se enumeran aquí.

  3. (Opcional). Si el reloj está disponible, obtenga la hora del reloj actual (T1) llamando a IMFClock::GetCorrelatedTime.

  4. Llame a IMFTransform::P rocessOutput en el mezclador. Si ProcessOutput se realiza correctamente, el ejemplo contiene un fotograma de vídeo. Si se produce un error en el método, compruebe el código devuelto. Los siguientes códigos de error de ProcessOutput no son errores críticos:

    Código de error Descripción
    MF_E_TRANSFORM_NEED_MORE_INPUT El mezclador necesita más entrada para poder generar un nuevo marco de salida.
    Si recibe este código de error, compruebe si el EVR ha llegado al final de la secuencia y responde según corresponda, tal y como se describe en Fin de secuencia. De lo contrario, omita este mensaje de MF_E_TRANSFORM_NEED_MORE_INPUT . El EVR enviará otro cuando el mezclador obtenga más entrada.
    MF_E_TRANSFORM_STREAM_CHANGE El tipo de salida del mezclador no es válido, posiblemente debido a un cambio de formato ascendente.
    Si obtiene este código de error, establezca el tipo de medio del moderador en NULL. El EVR solicitará un nuevo formato.
    MF_E_TRANSFORM_TYPE_NOT_SET El mezclador requiere un nuevo tipo de medio.
    Si obtiene este código de error, renegocia el tipo de salida del mezclador como se describe en Formatos de negociación.

     

    Si ProcessOutput se realiza correctamente, continúe.

  5. (Opcional). Si el reloj está disponible, obtenga la hora del reloj actual (T2). La cantidad de latencia introducida por el mezclador es (T2 - T1). Envíe un evento EC_PROCESSING_LATENCY con este valor al EVR. El EVR utiliza este valor para el control de calidad. Si no hay ningún reloj disponible, no hay ninguna razón para enviar el evento EC_PROCESSING_LATENCY .

  6. (Opcional). Consulte el ejemplo para IMFTrackedSample y llame a IMFTrackedSample::SetAllocator como se describe en Ejemplos de seguimiento.

  7. Programe el ejemplo para la presentación.

Esta secuencia de pasos puede finalizar antes de que el moderador obtenga cualquier salida del mezclador. Para asegurarse de que no se quita ninguna solicitud, debe repetir los mismos pasos cuando se produzca lo siguiente:

  • Se llama al método IMFClockStateSink::OnClockStart o IMFClockStateSink::OnClockStart del moderador. Esto controla el caso en el que el mezclador omite la entrada porque el reloj está en pausa (paso 1).
  • Se invoca la devolución de llamada IMFTrackedSample . Esto controla el caso en el que el mezclador recibe la entrada, pero todos los ejemplos de vídeo del moderador están en uso (paso 2).

En los siguientes ejemplos de código se muestran estos pasos con más detalle. El moderador llama al ProcessInputNotify método (que se muestra en el ejemplo siguiente) cuando obtiene el mensaje MFVP_MESSAGE_PROCESSINPUTNOTIFY .

//-----------------------------------------------------------------------------
// ProcessInputNotify
//
// Attempts to get a new output sample from the mixer.
//
// This method is called when the EVR sends an MFVP_MESSAGE_PROCESSINPUTNOTIFY
// message, which indicates that the mixer has a new input sample.
//
// Note: If there are multiple input streams, the mixer might not deliver an
// output sample for every input sample.
//-----------------------------------------------------------------------------

HRESULT EVRCustomPresenter::ProcessInputNotify()
{
    HRESULT hr = S_OK;

    // Set the flag that says the mixer has a new sample.
    m_bSampleNotify = TRUE;

    if (m_pMediaType == NULL)
    {
        // We don't have a valid media type yet.
        hr = MF_E_TRANSFORM_TYPE_NOT_SET;
    }
    else
    {
        // Try to process an output sample.
        ProcessOutputLoop();
    }
    return hr;
}

Este ProcessInputNotify método establece una marca booleana para registrar el hecho de que el mezclador tiene nueva entrada. A continuación, llama al ProcessOutputLoop método , que se muestra en el ejemplo siguiente. Este método intenta extraer tantas muestras como sea posible del mezclador:

void EVRCustomPresenter::ProcessOutputLoop()
{
    HRESULT hr = S_OK;

    // Process as many samples as possible.
    while (hr == S_OK)
    {
        // If the mixer doesn't have a new input sample, break from the loop.
        if (!m_bSampleNotify)
        {
            hr = MF_E_TRANSFORM_NEED_MORE_INPUT;
            break;
        }

        // Try to process a sample.
        hr = ProcessOutput();

        // NOTE: ProcessOutput can return S_FALSE to indicate it did not
        // process a sample. If so, break out of the loop.
    }

    if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
    {
        // The mixer has run out of input data. Check for end-of-stream.
        CheckEndOfStream();
    }
}

El ProcessOutput método, que se muestra en el ejemplo siguiente, intenta obtener un único fotograma de vídeo del mezclador. Si no hay ningún fotograma de vídeo disponible, ProcessSample devuelve S_FALSE o un código de error, cualquiera de los cuales interrumpe el bucle en ProcessOutputLoop. La mayoría del trabajo se realiza dentro del ProcessOutput método :

//-----------------------------------------------------------------------------
// ProcessOutput
//
// Attempts to get a new output sample from the mixer.
//
// Called in two situations:
// (1) ProcessOutputLoop, if the mixer has a new input sample.
// (2) Repainting the last frame.
//-----------------------------------------------------------------------------

HRESULT EVRCustomPresenter::ProcessOutput()
{
    assert(m_bSampleNotify || m_bRepaint);  // See note above.

    HRESULT     hr = S_OK;
    DWORD       dwStatus = 0;
    LONGLONG    mixerStartTime = 0, mixerEndTime = 0;
    MFTIME      systemTime = 0;
    BOOL        bRepaint = m_bRepaint; // Temporarily store this state flag.

    MFT_OUTPUT_DATA_BUFFER dataBuffer;
    ZeroMemory(&dataBuffer, sizeof(dataBuffer));

    IMFSample *pSample = NULL;

    // If the clock is not running, we present the first sample,
    // and then don't present any more until the clock starts.

    if ((m_RenderState != RENDER_STATE_STARTED) &&  // Not running.
         !m_bRepaint &&             // Not a repaint request.
         m_bPrerolled               // At least one sample has been presented.
         )
    {
        return S_FALSE;
    }

    // Make sure we have a pointer to the mixer.
    if (m_pMixer == NULL)
    {
        return MF_E_INVALIDREQUEST;
    }

    // Try to get a free sample from the video sample pool.
    hr = m_SamplePool.GetSample(&pSample);
    if (hr == MF_E_SAMPLEALLOCATOR_EMPTY)
    {
        // No free samples. Try again when a sample is released.
        return S_FALSE;
    }
    else if (FAILED(hr))
    {
        return hr;
    }

    // From now on, we have a valid video sample pointer, where the mixer will
    // write the video data.
    assert(pSample != NULL);

    // (If the following assertion fires, it means we are not managing the sample pool correctly.)
    assert(MFGetAttributeUINT32(pSample, MFSamplePresenter_SampleCounter, (UINT32)-1) == m_TokenCounter);

    if (m_bRepaint)
    {
        // Repaint request. Ask the mixer for the most recent sample.
        SetDesiredSampleTime(
            pSample,
            m_scheduler.LastSampleTime(),
            m_scheduler.FrameDuration()
            );

        m_bRepaint = FALSE; // OK to clear this flag now.
    }
    else
    {
        // Not a repaint request. Clear the desired sample time; the mixer will
        // give us the next frame in the stream.
        ClearDesiredSampleTime(pSample);

        if (m_pClock)
        {
            // Latency: Record the starting time for ProcessOutput.
            (void)m_pClock->GetCorrelatedTime(0, &mixerStartTime, &systemTime);
        }
    }

    // Now we are ready to get an output sample from the mixer.
    dataBuffer.dwStreamID = 0;
    dataBuffer.pSample = pSample;
    dataBuffer.dwStatus = 0;

    hr = m_pMixer->ProcessOutput(0, 1, &dataBuffer, &dwStatus);

    if (FAILED(hr))
    {
        // Return the sample to the pool.
        HRESULT hr2 = m_SamplePool.ReturnSample(pSample);
        if (FAILED(hr2))
        {
            hr = hr2;
            goto done;
        }
        // Handle some known error codes from ProcessOutput.
        if (hr == MF_E_TRANSFORM_TYPE_NOT_SET)
        {
            // The mixer's format is not set. Negotiate a new format.
            hr = RenegotiateMediaType();
        }
        else if (hr == MF_E_TRANSFORM_STREAM_CHANGE)
        {
            // There was a dynamic media type change. Clear our media type.
            SetMediaType(NULL);
        }
        else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
        {
            // The mixer needs more input.
            // We have to wait for the mixer to get more input.
            m_bSampleNotify = FALSE;
        }
    }
    else
    {
        // We got an output sample from the mixer.

        if (m_pClock && !bRepaint)
        {
            // Latency: Record the ending time for the ProcessOutput operation,
            // and notify the EVR of the latency.

            (void)m_pClock->GetCorrelatedTime(0, &mixerEndTime, &systemTime);

            LONGLONG latencyTime = mixerEndTime - mixerStartTime;
            NotifyEvent(EC_PROCESSING_LATENCY, (LONG_PTR)&latencyTime, 0);
        }

        // Set up notification for when the sample is released.
        hr = TrackSample(pSample);
        if (FAILED(hr))
        {
            goto done;
        }

        // Schedule the sample.
        if ((m_FrameStep.state == FRAMESTEP_NONE) || bRepaint)
        {
            hr = DeliverSample(pSample, bRepaint);
            if (FAILED(hr))
            {
                goto done;
            }
        }
        else
        {
            // We are frame-stepping (and this is not a repaint request).
            hr = DeliverFrameStepSample(pSample);
            if (FAILED(hr))
            {
                goto done;
            }
        }

        m_bPrerolled = TRUE; // We have presented at least one sample now.
    }

done:
    SafeRelease(&pSample);

    // Important: Release any events returned from the ProcessOutput method.
    SafeRelease(&dataBuffer.pEvents);
    return hr;
}

Algunos comentarios sobre este ejemplo:

  • Se supone que la variable m_SamplePool es un objeto de colección que contiene la cola de ejemplos de vídeo disponibles. El método del GetSample objeto devuelve MF_E_SAMPLEALLOCATOR_EMPTY si la cola está vacía.
  • Si el método ProcessOutput del mezclador devuelve MF_E_TRANSFORM_NEED_MORE_INPUT, significa que el mezclador no puede producir más salida, por lo que el moderador borra la marca m_fSampleNotify .
  • El TrackSample método , que establece la devolución de llamada IMFTrackedSample , se muestra en la sección Ejemplos de seguimiento.

Reintentos de marcos

En ocasiones, es posible que el moderador tenga que volver a pintar el fotograma de vídeo más reciente. Por ejemplo, el moderador estándar vuelve a pintar el marco en las situaciones siguientes:

Siga estos pasos para solicitar al mezclador que vuelva a crear el fotograma más reciente:

  1. Obtenga un ejemplo de vídeo de la cola.
  2. Consulte el ejemplo de la interfaz IMFDesiredSample .
  3. Llame a IMFDesiredSample::SetDesiredSampleTimeAndDuration. Especifique la marca de tiempo del fotograma de vídeo más reciente. (Tendrá que almacenar en caché este valor y actualizarlo para cada fotograma).
  4. Llame a ProcessOutput en el mezclador.

Al volver a dibujar un marco, puede omitir el reloj de presentación y presentar el marco inmediatamente.

Ejemplos de programación

Los fotogramas de vídeo pueden llegar al EVR en cualquier momento. El moderador es responsable de presentar cada fotograma en el momento correcto, en función de la marca de tiempo del marco. Cuando el moderador obtiene un nuevo ejemplo del mezclador, coloca el ejemplo en la cola programada. En un subproceso independiente, el moderador obtiene continuamente el primer ejemplo del encabezado de la cola y determina si se debe:

  • Presente el ejemplo.
  • Mantenga el ejemplo en la cola porque es temprano.
  • Descarte el ejemplo porque es tarde. Aunque debes evitar quitar fotogramas si es posible, es posible que tengas que hacerlo si el moderador se está retrasando continuamente.

Para obtener la marca de tiempo de un fotograma de vídeo, llame a IMFSample::GetSampleTime en el ejemplo de vídeo. La marca de tiempo es relativa al reloj de presentación del EVR. Para obtener la hora del reloj actual, llame a IMFClock::GetCorrelatedTime. Si el EVR no tiene un reloj de presentación o si una muestra no tiene una marca de tiempo, puede presentar la muestra inmediatamente después de obtenerla.

Para obtener la duración de cada muestra, llame a IMFSample::GetSampleDuration. Si el ejemplo no tiene una duración, puede usar la función MFFrameRateToAverageTimePerFrame para calcular la duración a partir de la velocidad de fotogramas.

Al programar ejemplos, tenga en cuenta lo siguiente:

  • Si la velocidad de reproducción es más rápida o más lenta que la velocidad normal, el reloj se ejecuta a una velocidad más rápida o más lenta. Esto significa que la marca de tiempo de una muestra siempre proporciona la hora de destino correcta en relación con el reloj de presentación. Sin embargo, si traduce los tiempos de presentación en algún otro tiempo de reloj (por ejemplo, el contador de rendimiento de alta resolución), debe escalar las horas en función de la velocidad del reloj. Si cambia la velocidad del reloj, el EVR llama al método IMFClockStateSink::OnClockSetRate del moderador.
  • La velocidad de reproducción puede ser negativa para la reproducción inversa. Cuando la velocidad de reproducción es negativa, el reloj de presentación se ejecuta hacia atrás. En otras palabras, la hora N + 1 se produce antes de la hora N.

En el ejemplo siguiente se calcula cómo es temprano o tarde un ejemplo, en relación con el reloj de presentación:

    LONGLONG hnsPresentationTime = 0;
    LONGLONG hnsTimeNow = 0;
    MFTIME   hnsSystemTime = 0;

    BOOL bPresentNow = TRUE;
    LONG lNextSleep = 0;

    if (m_pClock)
    {
        // Get the sample's time stamp. It is valid for a sample to
        // have no time stamp.
        hr = pSample->GetSampleTime(&hnsPresentationTime);

        // Get the clock time. (But if the sample does not have a time stamp,
        // we don't need the clock time.)
        if (SUCCEEDED(hr))
        {
            hr = m_pClock->GetCorrelatedTime(0, &hnsTimeNow, &hnsSystemTime);
        }

        // Calculate the time until the sample's presentation time.
        // A negative value means the sample is late.
        LONGLONG hnsDelta = hnsPresentationTime - hnsTimeNow;
        if (m_fRate < 0)
        {
            // For reverse playback, the clock runs backward. Therefore, the
            // delta is reversed.
            hnsDelta = - hnsDelta;
        }

El reloj de presentación suele estar controlado por el reloj del sistema o el representador de audio. (El representador de audio deriva el tiempo de la velocidad a la que la tarjeta de sonido consume audio). En general, el reloj de presentación no se sincroniza con la frecuencia de actualización del monitor.

Si los parámetros de presentación de Direct3D especifican D3DPRESENT_INTERVAL_DEFAULT o D3DPRESENT_INTERVAL_ONE para el intervalo de presentación, la operación Present espera el retroceso vertical del monitor. Se trata de una manera fácil de evitar el desgarro, pero reduce la precisión del algoritmo de programación. Por el contrario, si el intervalo de presentación es D3DPRESENT_INTERVAL_IMMEDIATE, el método Present se ejecuta inmediatamente, lo que provoca el desgarro a menos que el algoritmo de programación sea lo suficientemente preciso como para llamar a Present solo durante el período de retroceso vertical.

Las siguientes funciones pueden ayudarle a obtener información de tiempo precisa:

  • IDirect3DDevice9::GetRasterStatus devuelve información sobre el ráster, incluida la línea de examen actual y si el ráster está en el período en blanco vertical.
  • DwmGetCompositionTimingInfo devuelve información de tiempo para el administrador de ventanas de escritorio. Esta información es útil si la composición del escritorio está habilitada.

Presentación de ejemplos

En esta sección se supone que ha creado una cadena de intercambio independiente para cada superficie, tal como se describe en Asignar superficies direct3D. Para presentar un ejemplo, obtenga la cadena de intercambio del ejemplo de vídeo de la siguiente manera:

  1. Llame a IMFSample::GetBufferByIndex en el ejemplo de vídeo para obtener el búfer.
  2. Consulte el búfer para la interfaz IMFGetService .
  3. Llama a IMFGetService::GetService para obtener la interfaz IDirect3DSurface9 de la superficie direct3D. (Puede combinar este paso y el paso anterior en uno llamando a MFGetService).
  4. Llame a IDirect3DSurface9::GetContainer en la superficie para obtener un puntero a la cadena de intercambio.
  5. Llame a IDirect3DSwapChain9::P resent en la cadena de intercambio.

El siguiente código muestra estos pasos:

HRESULT D3DPresentEngine::PresentSample(IMFSample* pSample, LONGLONG llTarget)
{
    HRESULT hr = S_OK;

    IMFMediaBuffer* pBuffer = NULL;
    IDirect3DSurface9* pSurface = NULL;
    IDirect3DSwapChain9* pSwapChain = NULL;

    if (pSample)
    {
        // Get the buffer from the sample.
        hr = pSample->GetBufferByIndex(0, &pBuffer);
        if (FAILED(hr))
        {
            goto done;
        }

        // Get the surface from the buffer.
        hr = MFGetService(pBuffer, MR_BUFFER_SERVICE, IID_PPV_ARGS(&pSurface));
        if (FAILED(hr))
        {
            goto done;
        }
    }
    else if (m_pSurfaceRepaint)
    {
        // Redraw from the last surface.
        pSurface = m_pSurfaceRepaint;
        pSurface->AddRef();
    }

    if (pSurface)
    {
        // Get the swap chain from the surface.
        hr = pSurface->GetContainer(IID_PPV_ARGS(&pSwapChain));
        if (FAILED(hr))
        {
            goto done;
        }

        // Present the swap chain.
        hr = PresentSwapChain(pSwapChain, pSurface);
        if (FAILED(hr))
        {
            goto done;
        }

        // Store this pointer in case we need to repaint the surface.
        CopyComPointer(m_pSurfaceRepaint, pSurface);
    }
    else
    {
        // No surface. All we can do is paint a black rectangle.
        PaintFrameWithGDI();
    }

done:
    SafeRelease(&pSwapChain);
    SafeRelease(&pSurface);
    SafeRelease(&pBuffer);

    if (FAILED(hr))
    {
        if (hr == D3DERR_DEVICELOST || hr == D3DERR_DEVICENOTRESET || hr == D3DERR_DEVICEHUNG)
        {
            // We failed because the device was lost. Fill the destination rectangle.
            PaintFrameWithGDI();

            // Ignore. We need to reset or re-create the device, but this method
            // is probably being called from the scheduler thread, which is not the
            // same thread that created the device. The Reset(Ex) method must be
            // called from the thread that created the device.

            // The presenter will detect the state when it calls CheckDeviceState()
            // on the next sample.
            hr = S_OK;
        }
    }
    return hr;
}

Rectángulos de origen y destino

El rectángulo de origen es la parte del marco de vídeo que se va a mostrar. Se define con respecto a un sistema de coordenadas normalizado, en el que todo el fotograma de vídeo ocupa un rectángulo con coordenadas {0, 0, 1, 1}. El rectángulo de destino es el área dentro de la superficie de destino donde se dibuja el fotograma de vídeo. El moderador estándar permite a una aplicación establecer estos rectángulos mediante una llamada a IMFVideoDisplayControl::SetVideoPosition.

Hay varias opciones para aplicar rectángulos de origen y destino. La primera opción es permitir que el mezclador los aplique:

  • Establezca el rectángulo de origen mediante el atributo VIDEO_ZOOM_RECT . El mezclador aplicará el rectángulo de origen cuando muestre el vídeo en la superficie de destino. El rectángulo de origen predeterminado del mezclador es todo el marco.
  • Establezca el rectángulo de destino como apertura geométrica en el tipo de salida del mezclador. Para obtener más información, vea Negociación de formatos.

La segunda opción es aplicar los rectángulos cuando se especifican los parámetros IDirect3DSwapChain9::P resent especificando los parámetros pSourceRect y pDestRect en el método Present . Puede combinar estas opciones. Por ejemplo, podría establecer el rectángulo de origen en el mezclador, pero aplicar el rectángulo de destino en el método Present .

Si la aplicación cambia el rectángulo de destino o cambia el tamaño de la ventana, es posible que tenga que asignar nuevas superficies. Si es así, debe tener cuidado de sincronizar esta operación con el subproceso de programación. Vacíe la cola de programación y descarte los ejemplos antiguos antes de asignar nuevas superficies.

Fin de la secuencia

Cuando finaliza cada flujo de entrada del EVR, el EVR envía un mensaje MFVP_MESSAGE_ENDOFSTREAM al moderador. Sin embargo, en el momento en que reciba el mensaje, puede haber algunos fotogramas de vídeo restantes que se van a procesar. Antes de responder al mensaje de fin de secuencia, debe purgar toda la salida del mezclador y presentar todos los fotogramas restantes. Una vez presentado el último fotograma, envíe un evento EC_COMPLETE al EVR.

En el ejemplo siguiente se muestra un método que envía el evento EC_COMPLETE si se cumplen varias condiciones. De lo contrario, devuelve S_OK sin enviar el evento:

HRESULT EVRCustomPresenter::CheckEndOfStream()
{
    if (!m_bEndStreaming)
    {
        // The EVR did not send the MFVP_MESSAGE_ENDOFSTREAM message.
        return S_OK;
    }

    if (m_bSampleNotify)
    {
        // The mixer still has input.
        return S_OK;
    }

    if (m_SamplePool.AreSamplesPending())
    {
        // Samples are still scheduled for rendering.
        return S_OK;
    }

    // Everything is complete. Now we can tell the EVR that we are done.
    NotifyEvent(EC_COMPLETE, (LONG_PTR)S_OK, 0);
    m_bEndStreaming = FALSE;
    return S_OK;
}

Este método comprueba los siguientes estados:

  • Si la variable m_fSampleNotify es TRUE, significa que el mezclador tiene uno o varios fotogramas que aún no se han procesado. (Para obtener más información, consulte Procesamiento de la salida).
  • La variable m_fEndStreaming es una marca booleana cuyo valor inicial ES FALSE. El moderador establece la marca en TRUE cuando el EVR envía el mensaje MFVP_MESSAGE_ENDOFSTREAM .
  • Se supone que el AreSamplesPending método devuelve TRUE siempre que uno o varios fotogramas estén esperando en la cola programada.

En el método IMFVideoPresenter::P rocessMessage , establezca m_fEndStreaming enTRUE y llame CheckEndOfStream a cuando el EVR envíe el mensaje MFVP_MESSAGE_ENDOFSTREAM :

HRESULT EVRCustomPresenter::ProcessMessage(
    MFVP_MESSAGE_TYPE eMessage,
    ULONG_PTR ulParam
    )
{
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_ObjectLock);

    hr = CheckShutdown();
    if (FAILED(hr))
    {
        goto done;
    }

    switch (eMessage)
    {
    // Flush all pending samples.
    case MFVP_MESSAGE_FLUSH:
        hr = Flush();
        break;

    // Renegotiate the media type with the mixer.
    case MFVP_MESSAGE_INVALIDATEMEDIATYPE:
        hr = RenegotiateMediaType();
        break;

    // The mixer received a new input sample.
    case MFVP_MESSAGE_PROCESSINPUTNOTIFY:
        hr = ProcessInputNotify();
        break;

    // Streaming is about to start.
    case MFVP_MESSAGE_BEGINSTREAMING:
        hr = BeginStreaming();
        break;

    // Streaming has ended. (The EVR has stopped.)
    case MFVP_MESSAGE_ENDSTREAMING:
        hr = EndStreaming();
        break;

    // All input streams have ended.
    case MFVP_MESSAGE_ENDOFSTREAM:
        // Set the EOS flag.
        m_bEndStreaming = TRUE;
        // Check if it's time to send the EC_COMPLETE event to the EVR.
        hr = CheckEndOfStream();
        break;

    // Frame-stepping is starting.
    case MFVP_MESSAGE_STEP:
        hr = PrepareFrameStep(LODWORD(ulParam));
        break;

    // Cancels frame-stepping.
    case MFVP_MESSAGE_CANCELSTEP:
        hr = CancelFrameStep();
        break;

    default:
        hr = E_INVALIDARG; // Unknown message. This case should never occur.
        break;
    }

done:
    LeaveCriticalSection(&m_ObjectLock);
    return hr;
}

Además, llame a CheckEndOfStream si el método IMFTransform::P rocessOutput del mezclador devuelve MF_E_TRANSFORM_NEED_MORE_INPUT. Este código de error indica que el mezclador no tiene más ejemplos de entrada (consulte Procesamiento de salida).

Ejecución paso a paso de fotogramas

El EVR está diseñado para admitir la ejecución paso a paso de fotogramas en DirectShow y limpieza en Media Foundation. La ejecución paso a paso y la limpieza de fotogramas son conceptualmente similares. En ambos casos, la aplicación solicita un fotograma de vídeo a la vez. Internamente, el moderador usa el mismo mecanismo para implementar ambas características.

La ejecución paso a paso de fotogramas en DirectShow funciona de la siguiente manera:

  • La aplicación llama a IVideoFrameStep::Step. El número de pasos se proporciona en el parámetro dwSteps . El EVR envía un mensaje de MFVP_MESSAGE_STEP al moderador, donde el parámetro message (ulParam) es el número de pasos.
  • Si la aplicación llama a IVideoFrameStep::CancelStep o cambia el estado del gráfico (en ejecución, en pausa o detenido), el EVR envía un mensaje de MFVP_MESSAGE_CANCELSTEP .

La limpieza en Media Foundation funciona de la siguiente manera:

  • La aplicación establece la velocidad de reproducción en cero llamando a IMFRateControl::SetRate.
  • Para representar un nuevo marco, la aplicación llama a IMFMediaSession::Start con la posición deseada. El EVR envía un mensaje de MFVP_MESSAGE_STEP con ulParam igual a 1.
  • Para detener el limpieza, la aplicación establece la velocidad de reproducción en un valor distinto de cero. El EVR envía el mensaje de MFVP_MESSAGE_CANCELSTEP .

Después de recibir el mensaje de MFVP_MESSAGE_STEP , el moderador espera a que llegue el marco de destino. Si el número de pasos es N, el moderador descarta las muestras siguientes (N - 1) y presenta la muestra. Cuando el moderador completa el paso de marco, envía un evento de EC_STEP_COMPLETE al EVR con lParam1 establecido en FALSE. Además, si la velocidad de reproducción es cero, el moderador envía un evento EC_SCRUB_TIME . Si el EVR cancela la ejecución paso a paso de fotogramas mientras una operación de paso de fotograma sigue pendiente, el moderador envía un evento de EC_STEP_COMPLETE con lParam1 establecido en TRUE.

La aplicación puede enmarcar paso o limpiar varias veces, por lo que el moderador puede recibir varios mensajes de MFVP_MESSAGE_STEP antes de obtener un mensaje de MFVP_MESSAGE_CANCELSTEP . Además, el moderador puede recibir el mensaje MFVP_MESSAGE_STEP antes de que se inicie el reloj o mientras se ejecuta el reloj.

Implementación paso a paso de fotogramas

En esta sección se describe un algoritmo para implementar la ejecución paso a paso de fotogramas. El algoritmo de ejecución paso a paso de fotogramas usa las siguientes variables:

  • step_count. Entero sin signo que especifica el número de pasos de la operación paso a paso del marco actual.

  • step_queue. Una cola de punteros IMFSample .

  • step_state. En cualquier momento, el moderador puede estar en uno de los siguientes estados con respecto a la ejecución paso a paso de fotogramas:

    State Descripción
    NOT_STEPPING No paso a paso de marco.
    EN ESPERA El moderador ha recibido el mensaje MFVP_MESSAGE_STEP , pero el reloj no se ha iniciado.
    PENDING El moderador ha recibido el mensaje de MFVP_MESSAGE_STEP y el reloj se ha iniciado, pero el moderador está esperando recibir el marco de destino.
    PROGRAMADO El moderador ha recibido el marco de destino y lo ha programado para la presentación, pero el fotograma no se ha presentado.
    ÍNTEGRO El moderador ha presentado el marco de destino y ha enviado el evento EC_STEP_COMPLETE y está esperando el siguiente mensaje de MFVP_MESSAGE_STEP o MFVP_MESSAGE_CANCELSTEP .

     

    Estos estados son independientes de los estados del moderador enumerados en la sección Estados del moderador.

Los procedimientos siguientes se definen para el algoritmo de ejecución paso a paso de fotogramas:

Procedimiento PrepareFrameStep

  1. Incremente step_count.
  2. Establezca step_state en WAITING.
  3. Si el reloj se está ejecutando, llame a StartFrameStep.

Procedimiento StartFrameStep

  1. Si step_state es igual a WAITING, establezca step_state en PENDIENTE. Para cada ejemplo de step_queue, llame a DeliverFrameStepSample.
  2. Si step_state es igual a NOT_STEPPING, quite los ejemplos de step_queue y programe para su presentación.

Procedimiento CompleteFrameStep

  1. Establezca step_state en COMPLETE.
  2. Envíe el evento EC_STEP_COMPLETE con lParam1 = FALSE.
  3. Si la velocidad del reloj es cero, envíe el evento EC_SCRUB_TIME con la hora de muestra.

Procedimiento DeliverFrameStepSample

  1. Si la velocidad del reloj es cero y la hora delreloj de lamuestra< de tiempo + de muestreo, descarte la muestra. Salir.
  2. Si step_state es igual a SCHEDULED o COMPLETE, agregue el ejemplo a step_queue. Salir.
  3. Disminuir step_count.
  4. Si step_count> 0, descarte la muestra. Salir.
  5. Si step_state es igual a WAITING, agregue el ejemplo a step_queue. Salir.
  6. Programe el ejemplo para la presentación.
  7. Establezca step_state en PROGRAMADO.

Procedimiento CancelFrameStep

  1. Establezca step_state en NOT_STEPPING
  2. Restablezca step_count a cero.
  3. Si el valor anterior de step_state era WAITING, PENDING o SCHEDULED, envíe EC_STEP_COMPLETE con lParam1 = TRUE.

Llame a estos procedimientos de la siguiente manera:

Mensaje o método del moderador Procedimiento
mensaje de MFVP_MESSAGE_STEP PrepareFrameStep
mensaje de MFVP_MESSAGE_STEP CancelStep
IMFClockStateSink::OnClockStart StartFrameStep
IMFClockStateSink::OnClockRestart StartFrameStep
Devolución de llamada IMFTrackedSample CompleteFrameStep
IMFClockStateSink::OnClockStop CancelFrameStep
IMFClockStateSink::OnClockSetRate CancelFrameStep

 

En el siguiente gráfico de flujo se muestran los procedimientos de ejecución paso a paso de fotogramas.

diagrama de flujo que muestra rutas de acceso que comienzan con mfvp-message-step y mfvp-message-processinputnotify y terminan en

Establecimiento del moderador en el EVR

Después de implementar el moderador, el siguiente paso es configurar el EVR para usarlo.

Establecer el moderador en DirectShow

En una aplicación DirectShow, establezca el moderador en el EVR de la siguiente manera:

  1. Cree el filtro EVR llamando a CoCreateInstance. El CLSID es CLSID_EnhancedVideoRenderer.
  2. Agregue el EVR al gráfico de filtros.
  3. Cree una instancia del moderador. El moderador puede admitir la creación de objetos COM estándar a través de IClassFactory, pero esto no es obligatorio.
  4. Consulte el filtro EVR para la interfaz IMFVideoRenderer .
  5. Llame a IMFVideoRenderer::InitializeRenderer.

Establecer el moderador en Media Foundation

En Media Foundation, tienes varias opciones, dependiendo de si creas el receptor de medios EVR o el objeto de activación evR. Para obtener más información sobre los objetos de activación, vea Objetos de activación.

Para el receptor de medios EVR, haga lo siguiente:

  1. Llame a MFCreateVideoRenderer para crear el receptor de medios.
  2. Cree una instancia del moderador.
  3. Consulte el receptor de medios EVR para la interfaz IMFVideoRenderer .
  4. Llame a IMFVideoRenderer::InitializeRenderer.

Para el objeto de activación evR, haga lo siguiente:

  1. Llame a MFCreateVideoRendererActivate para crear el objeto de activación.

  2. Establezca uno de los siguientes atributos en el objeto de activación:

    Atributo Descripción
    MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE Puntero a un objeto de activación para el moderador.
    Con esta marca, debe proporcionar un objeto de activación para el moderador. El objeto de activación debe implementar la interfaz IMFActivate .
    MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_CLSID CLSID del moderador.
    Con esta marca, el moderador debe admitir la creación de objetos COM estándar a través de IClassFactory.

     

  3. Opcionalmente, establezca el atributo MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_FLAGS en el objeto de activación.

Representador de vídeo mejorado

Ejemplo de EVRPresenter