Esercitazione: Codifica di Windows Media da 1 passaggio

La codifica si riferisce al processo di conversione di supporti digitali da un formato a un altro. Ad esempio, la conversione dell'audio MP3 in formato Audio Windows Media come definito dalla specifica ADVANCED Systems Format (ASF).

Nota La specifica ASF definisce un tipo di contenitore per il file di output (.wma o .wmv) che può contenere dati multimediali in qualsiasi formato, compresso o non compresso. Ad esempio, un contenitore ASF, ad esempio un file .wma, può contenere dati multimediali in formato MP3. Il processo di codifica converte il formato effettivo dei dati contenuti nel file.

Questa esercitazione illustra la codifica di un'origine di input non protetta da DRM nel contenuto di Windows Media e la scrittura dei dati in un nuovo file ASF (con estensione wm*) usando i componenti ASF del livello pipeline. Questi componenti verranno usati per compilare una topologia di codifica parziale, che verrà controllata da un'istanza della sessione multimediale.

In questa esercitazione si creerà un'applicazione console che accetta i nomi file di input e output e la modalità di codifica come argomenti. Il file di input può essere compresso o un formato multimediale non compresso. Le modalità di codifica valide sono "CBR" (velocità in bit costante) o "VBR" (velocità in bit variabile). L'applicazione crea un'origine multimediale per rappresentare l'origine specificata dal nome file di input e il sink del file ASF per archiviare il contenuto codificato del file di origine in un file ASF. Per semplificare l'implementazione dello scenario, il file di output avrà un solo flusso audio e un flusso video. L'applicazione inserisce il codec Windows Media Audio 9.1 Professional per la conversione del formato di flusso audio e il codec Windows Media Video 9 per il flusso video.

Per la codifica della velocità in bit costante, prima dell'inizio della sessione di codifica, il codificatore deve conoscere la velocità di bit di destinazione che deve raggiungere. In questa esercitazione, per la modalità "CBR", l'applicazione usa la velocità di bit disponibile con il primo tipo di supporto di output recuperato dal codificatore durante la negoziazione del tipo di supporto come velocità in bit di destinazione. Per la codifica a velocità in bit variabile, questa esercitazione illustra la codifica con velocità in bit variabile impostando un livello di qualità. I flussi audio vengono codificati a livello di qualità di 98 flussi video a livello di qualità pari a 95.

La procedura seguente riepiloga i passaggi per codificare il contenuto di Windows Media in un contenitore ASF usando una modalità di codifica a 1 passaggio.

  1. Creare un'origine multimediale per l'oggetto specificato usando il resolver di origine.
  2. Enumerare i flussi nell'origine multimediale.
  3. Creare il sink multimediale ASF e aggiungere sink di flusso a seconda dei flussi nell'origine multimediale che devono essere codificati.
  4. Configurare il sink multimediale con le proprietà di codifica necessarie.
  5. Creare i codificatori Windows Media per i flussi nel file di output.
  6. Configurare i codificatori con le proprietà impostate nel sink multimediale.
  7. Compilare una topologia di codifica parziale.
  8. Creare un'istanza della sessione multimediale e impostare la topologia nella sessione multimediale.
  9. Avviare la sessione di codifica controllando la sessione multimediale e ottenendo tutti gli eventi pertinenti dalla sessione multimediale.
  10. Per la codifica VBR, ottenere i valori delle proprietà di codifica dal codificatore e impostarli nel sink multimediale.
  11. Chiudere e arrestare la sessione di codifica.

Questa esercitazione contiene le sezioni seguenti:

Prerequisiti

Nell’esercitazione si presuppongono le condizioni seguenti:

  • Si ha familiarità con la struttura dei file ASF, i componenti ASF del livello pipeline forniti da Media Foundation per lavorare con gli oggetti ASF. Questi componenti includono gli oggetti seguenti:

  • Si ha familiarità con i codificatori Windows Media e i vari tipi di codifica, in particolare la codifica a velocità in bit costante e la codifica della velocità in bit variabile basata sulla qualità.

  • Si ha familiarità con le operazioni MFT del codificatore. In particolare, la creazione di un'istanza del codificatore e l'impostazione dei tipi di input e output nel codificatore.

  • Si ha familiarità con gli oggetti topologia e come compilare una topologia di codifica. Per altre informazioni sulle topologie e sui nodi della topologia, vedere Creazione di topologie.

  • Si ha familiarità con il modello di evento di Media Session e le operazioni di controllo del flusso. Per informazioni, vedere Eventi sessione multimediale.

Configurare il progetto

  1. Includere le intestazioni seguenti nel file di origine:

    #include <new>
    #include <mfidl.h>            // Media Foundation interfaces
    #include <mfapi.h>            // Media Foundation platform APIs
    #include <mferror.h>        // Media Foundation error codes
    #include <wmcontainer.h>    // ASF-specific components
    #include <wmcodecdsp.h>        // Windows Media DSP interfaces
    #include <Dmo.h>            // DMO objects
    #include <uuids.h>            // Definition for FORMAT_VideoInfo
    #include <propvarutil.h>
    
    
  2. Collegamento ai file di libreria seguenti:

    // The required link libraries 
    #pragma comment(lib, "mfplat")
    #pragma comment(lib, "mf")
    #pragma comment(lib, "mfuuid")
    #pragma comment(lib, "msdmo")
    #pragma comment(lib, "strmiids")
    #pragma comment(lib, "propsys")
    
    
  3. Dichiarare la funzione Cassaforte Release.

    template <class T> void SafeRelease(T **ppT)
    {
        if (*ppT)
        {
            (*ppT)->Release();
            *ppT = NULL;
        }
    }
    
  4. Dichiarare l'enumerazione ENCODING_MODE per definire i tipi di codifica CBR e VBR.

    // Encoding mode
    typedef enum ENCODING_MODE {
      NONE  = 0x00000000,
      CBR   = 0x00000001,
      VBR   = 0x00000002,
    } ;
    
    
  5. Definire una costante per la finestra del buffer per un flusso video.

    // Video buffer window
    const INT32 VIDEO_WINDOW_MSEC = 3000;
    
    

Creare l'origine multimediale

Creare un'origine multimediale per l'origine di input. Media Foundation fornisce origini multimediali predefinite per vari formati multimediali: MP3, MP4/3GP, AVI/WAVE. Per informazioni sui formati supportati da Media Foundation, vedere Formati multimediali supportati in Media Foundation.

Per creare l'origine multimediale, usare il resolver di origine. Questo oggetto analizza l'URL specificato per il file di origine e crea l'origine multimediale appropriata.

Effettuare le chiamate seguenti:

Nell'esempio di codice seguente viene illustrata una funzione CreateMediaSource che crea un'origine multimediale per il file specificato.

//  Create a media source from a URL.
HRESULT CreateMediaSource(PCWSTR sURL, IMFMediaSource **ppSource)
{
    MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;

    IMFSourceResolver* pSourceResolver = NULL;
    IUnknown* pSource = NULL;

    // Create the source resolver.
    HRESULT hr = MFCreateSourceResolver(&pSourceResolver);
    if (FAILED(hr))
    {
        goto done;
    }

    // Use the source resolver to create the media source.

    // Note: For simplicity this sample uses the synchronous method to create 
    // the media source. However, creating a media source can take a noticeable
    // amount of time, especially for a network source. For a more responsive 
    // UI, use the asynchronous BeginCreateObjectFromURL method.

    hr = pSourceResolver->CreateObjectFromURL(
        sURL,                       // URL of the source.
        MF_RESOLUTION_MEDIASOURCE,  // Create a source object.
        NULL,                       // Optional property store.
        &ObjectType,        // Receives the created object type. 
        &pSource            // Receives a pointer to the media source.
        );
    if (FAILED(hr))
    {
        goto done;
    }

    // Get the IMFMediaSource interface from the media source.
    hr = pSource->QueryInterface(IID_PPV_ARGS(ppSource));

done:
    SafeRelease(&pSourceResolver);
    SafeRelease(&pSource);
    return hr;
}

Creare il sink di file ASF

Creare un'istanza del sink di file ASF che archivierà i dati multimediali codificati in un file ASF alla fine della sessione di codifica.

In questa esercitazione si creerà un oggetto di attivazione per il sink di file ASF. Il sink di file richiede le informazioni seguenti per creare i sink di flusso necessari.

  • Flussi da codificare e scrivere nel file finale.
  • Proprietà di codifica utilizzate per codificare il contenuto multimediale, ad esempio il tipo di codifica, il numero di passaggi di codifica e le proprietà correlate.
  • Le proprietà globali del file che indicano al sink multimediale se devono regolare automaticamente i parametri del bucket persa (velocità di bit e dimensioni del buffer).

Le informazioni sul flusso vengono configurate nell'oggetto Profilo ASF e le proprietà di codifica e globali vengono impostate in un archivio proprietà gestito dall'oggetto ContentInfo ASF.

Per una panoramica del sink del file ASF, vedere Sink multimediali ASF.

Creare l'oggetto profilo ASF

Affinché il sink del file ASF scriva i dati multimediali codificati in un file ASF, il sink deve conoscere il numero di flussi e il tipo di flussi per cui creare sink di flusso. In questa esercitazione verranno estratte tali informazioni dall'origine multimediale e verranno creati flussi di output corrispondenti. Limitare i flussi di output a un flusso audio e a un flusso video. Per ogni flusso selezionato nell'origine, creare un flusso di destinazione e aggiungerlo al profilo.

Per implementare questo passaggio, sono necessari gli oggetti seguenti.

  • Profilo ASF
  • Descrittore di presentazione per l'origine multimediale
  • Descrittori di flusso per i flussi selezionati nell'origine multimediale.
  • Tipi di supporti per i flussi selezionati.

I passaggi seguenti descrivono il processo di creazione del profilo ASF e dei flussi di destinazione.

Per creare il profilo ASF

  1. Chiama MFCreateASFProfile per creare un oggetto profilo vuoto.

  2. Chiamare IMFMediaSource::CreatePresentationDescriptor per creare il descrittore di presentazione per l'origine multimediale creata nel passaggio descritto nella sezione "Creare l'origine multimediale" di questa esercitazione.

  3. Chiamare IMFPresentationDescriptor::GetStreamDescriptorCount per ottenere il numero di flussi nell'origine multimediale.

  4. Chiamare IMFPresentationDescriptor::GetStreamDescriptorByIndex per ogni flusso nell'origine multimediale, ottenere il descrittore di flusso del flusso.

  5. Chiamare IMFStreamDescriptor::GetMediaTypeHandler seguito da IMFMediaTypeHandler::GetMediaTypeByIndex e ottenere il primo tipo di supporto per il flusso.

    Nota Per evitare chiamate complesse, presupporre che esista un solo tipo di supporto per flusso e selezionare il primo tipo di supporto del flusso. Per i flussi complessi, è necessario enumerare ogni tipo di supporto dal gestore del tipo di supporto e scegliere il tipo di supporto da codificare.

  6. Chiama IIMFMediaType::GetMajorType per ottenere il tipo principale del flusso per determinare se il flusso contiene audio o video.

  7. A seconda del tipo principale del flusso, creare tipi di supporti di destinazione. Questi tipi di supporti conterranno informazioni sul formato che il codificatore userà durante la sessione di codifica. Le sezioni seguenti di questa esercitazione descrivono il processo di creazione dei tipi di supporti di destinazione.

    • Creare un tipo di supporto audio compresso
    • Creare un tipo di supporto video compresso
  8. Creare un flusso in base al tipo di supporto di destinazione, configurare il flusso in base ai requisiti dell'applicazione e aggiungere il flusso al profilo. Per altre informazioni, vedere Aggiunta di informazioni sul flusso al sink di file ASF.

    1. Chiamare IMFASFProfile::CreateStream e passare il tipo di supporto di destinazione per creare il flusso di output. Il metodo recupera l'interfaccia IMFASFStreamConfig dell'oggetto flusso.
    2. Configurare il flusso.
      • Chiamare IMFASFStreamConfig::SetStreamNumber per assegnare un numero al flusso. Questa impostazione è obbligatoria.
      • Facoltativamente, configurare i parametri del bucket persi, l'estensione del payload, l'esclusione reciproca in ogni flusso chiamando i metodi IMFASFStreamConfig e gli attributi di configurazione del flusso pertinenti.
    3. Aggiungere le proprietà di codifica a livello di flusso usando l'oggetto ASF ContentInfo. Per altre informazioni su questo passaggio, vedere la sezione "Creare l'oggetto ContentInfo ASF" in questa esercitazione.
    4. Chiama IMFASFProfile::SetStream per aggiungere il flusso al profilo.

    L'esempio di codice seguente crea un flusso audio di output.

    //-------------------------------------------------------------------
    //  CreateAudioStream
    //  Create an audio stream and add it to the profile.
    //
    //  pProfile: A pointer to the ASF profile.
    //  wStreamNumber: Stream number to assign for the new stream.
    //-------------------------------------------------------------------
    
    HRESULT CreateAudioStream(IMFASFProfile* pProfile, WORD wStreamNumber)
    {
        if (!pProfile)
        {
            return E_INVALIDARG;
        }
        if (wStreamNumber < 1 || wStreamNumber > 127 )
        {
            return MF_E_INVALIDSTREAMNUMBER;
        }
    
        IMFMediaType* pAudioType = NULL;
        IMFASFStreamConfig* pAudioStream = NULL;
    
        //Create an output type from the encoder
        HRESULT hr = GetOutputTypeFromWMAEncoder(&pAudioType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Create a new stream with the audio type
        hr = pProfile->CreateStream(pAudioType, &pAudioStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Set stream number
        hr = pAudioStream->SetStreamNumber(wStreamNumber);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Add the stream to the profile
        hr = pProfile->SetStream(pAudioStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
        wprintf_s(L"Audio Stream created. Stream Number: %d.\n", wStreamNumber);
    
    done:
        SafeRelease(&pAudioStream);
        SafeRelease(&pAudioType);
        return hr;
    }
    

    L'esempio di codice seguente crea un flusso video di output.

    //-------------------------------------------------------------------
    //  CreateVideoStream
    //  Create an video stream and add it to the profile.
    //
    //  pProfile: A pointer to the ASF profile.
    //  wStreamNumber: Stream number to assign for the new stream.
    //    pType: A pointer to the source's video media type.
    //-------------------------------------------------------------------
    
    HRESULT CreateVideoStream(IMFASFProfile* pProfile, WORD wStreamNumber, IMFMediaType* pType)
    {
        if (!pProfile)
        {
            return E_INVALIDARG;
        }
        if (wStreamNumber < 1 || wStreamNumber > 127 )
        {
            return MF_E_INVALIDSTREAMNUMBER;
        }
    
        HRESULT hr = S_OK;
    
    
        IMFMediaType* pVideoType = NULL;
        IMFASFStreamConfig* pVideoStream = NULL;
    
        UINT32 dwBitRate = 0;
    
        //Create a new video type from the source type
        hr = CreateCompressedVideoType(pType, &pVideoType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Create a new stream with the video type
        hr = pProfile->CreateStream(pVideoType, &pVideoStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
    
        //Set a valid stream number
        hr = pVideoStream->SetStreamNumber(wStreamNumber);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Add the stream to the profile
        hr = pProfile->SetStream(pVideoStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
        wprintf_s(L"Video Stream created. Stream Number: %d .\n", wStreamNumber);
    
    done:
    
        SafeRelease(&pVideoStream);
        SafeRelease(&pVideoType);
    
        return hr;
    }
    

Nell'esempio di codice seguente vengono creati flussi di output a seconda dei flussi nell'origine.

    //For each stream in the source, add target stream information in the profile
    for (DWORD iStream = 0; iStream < dwSrcStream; iStream++)
    {
        hr = pPD->GetStreamDescriptorByIndex(
            iStream, &fSelected, &pStreamDesc);
        if (FAILED(hr))
        {
            goto done;
        }

        if (!fSelected)
        {
            continue;
        }

        //Get the media type handler
        hr = pStreamDesc->GetMediaTypeHandler(&pHandler);
        if (FAILED(hr))
        {
            goto done;
        }

        //Get the first media type 
        hr = pHandler->GetMediaTypeByIndex(0, &pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }

        //Get the major type
        hr = pMediaType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }

        // If this is a video stream, create the target video stream
        if (guidMajor == MFMediaType_Video)
        {
            //The stream level encoding properties will be set in this call
            hr = CreateVideoStream(pProfile, wStreamID, pMediaType);

            if (FAILED(hr))
            {
                goto done;
            }
        }
        
        // If this is an audio stream, create the target audio stream
        if (guidMajor == MFMediaType_Audio)
        {
            //The stream level encoding properties will be set in this call
            hr = CreateAudioStream(pProfile, wStreamID);

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

        //Get stream's encoding property
        hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamID, &pContentInfoProps);
        if (FAILED(hr))
        {
            goto done;
        }

        //Set the stream-level encoding properties
        hr = SetEncodingProperties(guidMajor, pContentInfoProps);       
        if (FAILED(hr))
        {
            goto done;
        }


        SafeRelease(&pMediaType);
        SafeRelease(&pStreamDesc);
        SafeRelease(&pContentInfoProps);
        wStreamID++;
    }

Creare un tipo di supporto audio compresso

Se vuoi includere un flusso audio nel file di output, crea un tipo audio specificando le caratteristiche del tipo codificato impostando gli attributi richiesti. Per assicurarsi che il tipo audio sia compatibile con il codificatore audio windows Media, creare un'istanza del codificatore MFT, impostare le proprietà di codifica e ottenere un tipo di supporto chiamando IMFTransform::GetOutputAvailableType. Ottenere il tipo di output richiesto eseguendo un ciclo in tutti i tipi disponibili, ottenendo gli attributi di ogni tipo di supporto e selezionando il tipo più vicino ai requisiti. In questa esercitazione ottenere il primo tipo disponibile dall'elenco dei tipi di supporti di output supportati dal codificatore.

Nota Per Windows 7, Media Foundation fornisce una nuova funzione, MFTranscodeGetAudioOutputAvailableTypes che recupera un elenco dei tipi audio compatibili. Questa funzione ottiene solo i tipi di supporti per la codifica CBR.

Per un tipo audio completo devono essere impostati gli attributi seguenti:

L'esempio di codice seguente crea un tipo di audio compresso ottenendo un tipo compatibile dal codificatore Windows Media Audio. L'implementazione di SetEncodingProperties è illustrata nella sezione "Creare l'oggetto ContentInfo ASF" di questa esercitazione.

//-------------------------------------------------------------------
//  GetOutputTypeFromWMAEncoder
//  Gets a compatible output type from the Windows Media audio encoder.
//
//  ppAudioType: Receives a pointer to the media type.
//-------------------------------------------------------------------

HRESULT GetOutputTypeFromWMAEncoder(IMFMediaType** ppAudioType)
{
    if (!ppAudioType)
    {
        return E_POINTER;
    }

    IMFTransform* pMFT = NULL;
    IMFMediaType* pType = NULL;
    IPropertyStore* pProps = NULL;

    //We need to find a suitable output media type
    //We need to create the encoder to get the available output types
    //and discard the instance.

    CLSID *pMFTCLSIDs = NULL;   // Pointer to an array of CLISDs. 
    UINT32 cCLSID = 0;            // Size of the array.

    MFT_REGISTER_TYPE_INFO tinfo;
    
    tinfo.guidMajorType = MFMediaType_Audio;
    tinfo.guidSubtype = MFAudioFormat_WMAudioV9;

    // Look for an encoder.
    HRESULT hr = MFTEnum(
        MFT_CATEGORY_AUDIO_ENCODER,
        0,                  // Reserved
        NULL,                // Input type to match. None.
        &tinfo,             // WMV encoded type.
        NULL,               // Attributes to match. (None.)
        &pMFTCLSIDs,        // Receives a pointer to an array of CLSIDs.
        &cCLSID             // Receives the size of the array.
        );
    if (FAILED(hr))
    {
        goto done;
    }

    // MFTEnum can return zero matches.
    if (cCLSID == 0)
    {
        hr = MF_E_TOPO_CODEC_NOT_FOUND;
        goto done;
    }
    else
    {
        // Create the MFT decoder
        hr = CoCreateInstance(pMFTCLSIDs[0], NULL,
            CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pMFT));

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

    }

    // Get the encoder's property store

    hr = pMFT->QueryInterface(IID_PPV_ARGS(&pProps));
    if (FAILED(hr))
    {
        goto done;
    }
    
    //Set encoding properties
    hr = SetEncodingProperties(MFMediaType_Audio, pProps);
    if (FAILED(hr))
    {
        goto done;
    }

    //Get the first output type
    //You can loop through the available output types to choose 
    //the one that meets your target requirements
    hr = pMFT->GetOutputAvailableType(0, 0, &pType);
    if (FAILED(hr))
    {
        goto done;
    }

    //Return to the caller
    *ppAudioType = pType;
    (*ppAudioType)->AddRef();
    
done:
    SafeRelease(&pProps);
    SafeRelease(&pType);
    SafeRelease(&pMFT);
    CoTaskMemFree(pMFTCLSIDs);
    return hr;
}

Creare un tipo di supporto video compresso

Se vuoi includere un flusso video nel file di output, crea un tipo di video con codifica completa. Il tipo di supporto completo deve includere la velocità di bit desiderata e i dati privati del codec.

Esistono due modi per creare un tipo di supporto video completo.

  • Creare un tipo di supporto vuoto e copiare gli attributi del tipo di supporto dal tipo di video di origine e sovrascrivere l'attributo MF_MT_SUBTYPE con la costante GUID MFVideoFormat_WMV3.

    Per un tipo di video completo devono essere impostati gli attributi seguenti:

    • MF_MT_MAJOR_TYPE
    • MF_MT_SUBTYPE
    • MF_MT_FRAME_RATE
    • MF_MT_FRAME_SIZE
    • MF_MT_INTERLACE_MODE
    • MF_MT_PIXEL_ASPECT_RATIO
    • MF_MT_AVG_BITRATE
    • MF_MT_U edizione Standard R_DATA

    L'esempio di codice seguente crea un tipo di video compresso dal tipo di video di origine.

    //-------------------------------------------------------------------
    //  CreateCompressedVideoType
    //  Creates an output type from source's video media type.
    //
    //    pType: A pointer to the source's video media type.
    //  ppVideoType: Receives a pointer to the media type.
    //-------------------------------------------------------------------
    
    HRESULT CreateCompressedVideoType(
            IMFMediaType* pType, 
            IMFMediaType** ppVideoType)
    {
        if (!pType)
        {
            return E_INVALIDARG;
        }
        if (!ppVideoType)
        {
            return E_POINTER;
        }
    
        HRESULT hr = S_OK;
        MFRatio fps = { 0 };
        MFRatio par = { 0 };
        UINT32 width = 0, height = 0;
        UINT32 interlace = MFVideoInterlace_Unknown;
        UINT32 fSamplesIndependent = 0;
        UINT32 cBitrate = 0;
    
        IMFMediaType* pMediaType = NULL;
    
        GUID guidMajor = GUID_NULL;
    
        hr = pType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }
    
        if (guidMajor != MFMediaType_Video )
        {
            hr = MF_E_INVALID_FORMAT;
            goto done;
        }
    
        hr = MFCreateMediaType(&pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pType->CopyAllItems(pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Fill the missing attributes
        //1. Frame rate
        hr = MFGetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, (UINT32*)&fps.Numerator, (UINT32*)&fps.Denominator);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            fps.Numerator = 30000;
            fps.Denominator = 1001;
    
            hr = MFSetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, fps.Numerator, fps.Denominator);
            if (FAILED(hr))
            {
                goto done;
            }
        }
    
        ////2. Frame size
        hr = MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &width, &height);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            width = 1280;
            height = 720;
    
            hr = MFSetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, width, height);
            if (FAILED(hr))
            {
                goto done;
            }
        }
    
        ////3. Interlace mode
    
        if (FAILED(pMediaType->GetUINT32(MF_MT_INTERLACE_MODE, &interlace)))
        {
            hr = pMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        ////4. Pixel aspect Ratio
        hr = MFGetAttributeRatio(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&par.Numerator, (UINT32*)&par.Denominator);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            par.Numerator = par.Denominator = 1;
            hr = MFSetAttributeRatio(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32)par.Numerator, (UINT32)par.Denominator);
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        //6. bit rate
        if (FAILED(pMediaType->GetUINT32(MF_MT_AVG_BITRATE, &cBitrate)))
        {
            hr = pMediaType->SetUINT32(MF_MT_AVG_BITRATE, 1000000);
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        hr = pMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_WMV3);
        if (FAILED(hr))
        {
            goto done;
        }
    
        // Return the pointer to the caller.
        *ppVideoType = pMediaType;
        (*ppVideoType)->AddRef();
    
    
    done:
        SafeRelease(&pMediaType);
        return hr;
    }
    
    
  • Ottenere un tipo di supporto compatibile dal codificatore video di Windows Media impostando le proprietà di codifica e quindi chiamando IMFTransform::GetOutputAvailableType. Questo metodo restituisce il tipo parziale. Assicurarsi di convertire il tipo parziale in un tipo completo aggiungendo le informazioni seguenti:

    Poiché IWMCodecPrivateData::GetPrivateData controlla la velocità di bit prima di restituire i dati privati del codec, assicurarsi di impostare la velocità di bit prima di ottenere i dati privati del codec.

    L'esempio di codice seguente crea un tipo di video compresso ottenendo un tipo compatibile dal codificatore Windows Media Video. Crea anche un tipo di video non compresso e lo imposta come input del codificatore. Questa operazione viene implementata nella funzione helper CreateUncompressedVideoType. GetOutputTypeFromWMVEncoder converte il tipo parziale restituito in un tipo completo aggiungendo dati privati del codec. L'implementazione di SetEncodingProperties è illustrata nella sezione "Creare l'oggetto ContentInfo ASF" di questa esercitazione.

    //-------------------------------------------------------------------
    //  GetOutputTypeFromWMVEncoder
    //  Gets a compatible output type from the Windows Media video encoder.
    //
    //  pType: A pointer to the source video stream's media type.
    //  ppVideoType: Receives a pointer to the media type.
    //-------------------------------------------------------------------
    
    HRESULT GetOutputTypeFromWMVEncoder(IMFMediaType* pType, IMFMediaType** ppVideoType)
    {
        if (!ppVideoType)
        {
            return E_POINTER;
        }
    
        IMFTransform* pMFT = NULL;
        IPropertyStore* pProps = NULL;
        IMFMediaType* pInputType = NULL;
        IMFMediaType* pOutputType = NULL;
    
        UINT32 cBitrate = 0;
    
        //We need to find a suitable output media type
        //We need to create the encoder to get the available output types
        //and discard the instance.
    
        CLSID *pMFTCLSIDs = NULL;   // Pointer to an array of CLISDs. 
        UINT32 cCLSID = 0;          // Size of the array.
    
        MFT_REGISTER_TYPE_INFO tinfo;
    
        tinfo.guidMajorType = MFMediaType_Video;
        tinfo.guidSubtype = MFVideoFormat_WMV3;
    
        // Look for an encoder.
        HRESULT hr = MFTEnum(
            MFT_CATEGORY_VIDEO_ENCODER,
            0,                  // Reserved
            NULL,               // Input type to match. None.
            &tinfo,             // WMV encoded type.
            NULL,               // Attributes to match. (None.)
            &pMFTCLSIDs,        // Receives a pointer to an array of CLSIDs.
            &cCLSID             // Receives the size of the array.
            );
        if (FAILED(hr))
        {
            goto done;
        }
    
        // MFTEnum can return zero matches.
        if (cCLSID == 0)
        {
            hr = MF_E_TOPO_CODEC_NOT_FOUND;
            goto done;
        }
        else
        {
            //Create the MFT decoder
            hr = CoCreateInstance(pMFTCLSIDs[0], NULL,
                CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pMFT));
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        //Get the video encoder property store
        hr = pMFT->QueryInterface(IID_PPV_ARGS(&pProps));
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Set encoding properties
        hr = SetEncodingProperties(MFMediaType_Video, pProps);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = CreateUncompressedVideoType(pType, &pInputType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pMFT->SetInputType(0, pInputType, 0);
    
        //Get the first output type
        //You can loop through the available output types to choose 
        //the one that meets your target requirements
        hr =  pMFT->GetOutputAvailableType(0, 0, &pOutputType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pType->GetUINT32(MF_MT_AVG_BITRATE, &cBitrate);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Now set the bit rate
        hr = pOutputType->SetUINT32(MF_MT_AVG_BITRATE, cBitrate);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = AddPrivateData(pMFT, pOutputType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Return to the caller
        *ppVideoType = pOutputType;
        (*ppVideoType)->AddRef();
    
    done:
        SafeRelease(&pProps);
        SafeRelease(&pOutputType);
        SafeRelease(&pInputType);
        SafeRelease(&pMFT);
        CoTaskMemFree(pMFTCLSIDs);
        return hr;
    }
    

    Nell'esempio di codice seguente viene creato un tipo di video non compresso.

    //-------------------------------------------------------------------
    //  CreateUncompressedVideoType
    //  Creates an uncompressed video type.
    //
    //    pType: A pointer to the source's video media type.
    //  ppVideoType: Receives a pointer to the media type.
    //-------------------------------------------------------------------
    
    HRESULT CreateUncompressedVideoType(IMFMediaType* pType, IMFMediaType** ppMediaType)
    {
        if (!pType)
        {
            return E_INVALIDARG;
        }
        if (!ppMediaType)
        {
            return E_POINTER;
        }
    
        MFRatio fps = { 0 };
        MFRatio par = { 0 };
        UINT32 width = 0, height = 0;
        UINT32 interlace = MFVideoInterlace_Unknown;
        UINT32 cBitrate = 0;
    
        IMFMediaType* pMediaType = NULL;
    
        GUID guidMajor = GUID_NULL;
    
        HRESULT hr = pType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }
    
        if (guidMajor != MFMediaType_Video )
        {
            hr = MF_E_INVALID_FORMAT;
            goto done;
        }
    
        hr = MFGetAttributeRatio(pType, MF_MT_FRAME_RATE, (UINT32*)&fps.Numerator, (UINT32*)&fps.Denominator);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            fps.Numerator = 30000;
            fps.Denominator = 1001;
    
        }
        hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            width = 1280;
            height = 720;
    
        }
    
        interlace = MFGetAttributeUINT32(pType, MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
    
        hr = MFGetAttributeRatio(pType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&par.Numerator, (UINT32*)&par.Denominator);
        if (FAILED(hr))
        {
            par.Numerator = par.Denominator = 1;
        }
    
        cBitrate = MFGetAttributeUINT32(pType, MF_MT_AVG_BITRATE, 1000000);
    
        hr = MFCreateMediaType(&pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pMediaType->SetGUID(MF_MT_MAJOR_TYPE, guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = MFSetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, fps.Numerator, fps.Denominator);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = MFSetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, width, height);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pMediaType->SetUINT32(MF_MT_INTERLACE_MODE, 2);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pMediaType->SetUINT32(MF_MT_AVG_BITRATE, cBitrate);
        if (FAILED(hr))
        {
            goto done;
        }
    
        // Return the pointer to the caller.
        *ppMediaType = pMediaType;
        (*ppMediaType)->AddRef();
    
    
    done:
        SafeRelease(&pMediaType);
        return hr;
    }
    

    Nell'esempio di codice seguente vengono aggiunti dati privati codec al tipo di supporto video specificato.

    //
    // AddPrivateData
    // Appends the private codec data to a media type.
    //
    // pMFT: The video encoder
    // pTypeOut: A video type from the encoder's type list.
    //
    // The function modifies pTypeOut by adding the codec data.
    //
    
    HRESULT AddPrivateData(IMFTransform *pMFT, IMFMediaType *pTypeOut)
    {
        HRESULT hr = S_OK;
        ULONG cbData = 0;
        BYTE *pData = NULL;
    
        IWMCodecPrivateData *pPrivData = NULL;
    
        DMO_MEDIA_TYPE mtOut = { 0 };
    
        // Convert the type to a DMO type.
        hr = MFInitAMMediaTypeFromMFMediaType(
            pTypeOut, 
            FORMAT_VideoInfo, 
            (AM_MEDIA_TYPE*)&mtOut
            );
    
        if (SUCCEEDED(hr))
        {
            hr = pMFT->QueryInterface(IID_PPV_ARGS(&pPrivData));
        }
    
        if (SUCCEEDED(hr))
        {
            hr = pPrivData->SetPartialOutputType(&mtOut);
        }
    
        //
        // Get the private codec data
        //
    
        // First get the buffer size.
        if (SUCCEEDED(hr))
        {
            hr = pPrivData->GetPrivateData(NULL, &cbData);
        }
    
        if (SUCCEEDED(hr))
        {
            pData = new BYTE[cbData];
    
            if (pData == NULL)
            {
                hr = E_OUTOFMEMORY;
            }
        }
    
        // Now get the data.
        if (SUCCEEDED(hr))
        {
            hr = pPrivData->GetPrivateData(pData, &cbData);
        }
    
        // Add the data to the media type.
        if (SUCCEEDED(hr))
        {
            hr = pTypeOut->SetBlob(MF_MT_USER_DATA, pData, cbData);
        }
    
        delete [] pData;
        MoFreeMediaType(&mtOut);
        SafeRelease(&pPrivData);
        return hr;
    }
    

Creare l'oggetto ContentInfo ASF

L'oggetto ASF ContentInfo è un componente a livello WMContainer progettato principalmente per archiviare le informazioni sull'oggetto intestazione ASF. Il sink del file ASF implementa l'oggetto ContentInfo per archiviare le informazioni (in un archivio proprietà) che verranno usate per scrivere l'oggetto intestazione ASF del file codificato. A tale scopo, è necessario creare e configurare il set di informazioni seguente sull'oggetto ContentInfo prima di avviare la sessione di codifica.

  1. Chiamare MFCreateASFContentInfo per creare un oggetto ContentInfo vuoto.

    Nell'esempio di codice seguente viene creato un oggetto ContentInfo vuoto.

        // Create an empty ContentInfo object
        hr = MFCreateASFContentInfo(&pContentInfo);
        if (FAILED(hr))
        {
            goto done;
        }
    
    
  2. Chiamare IMFASFContentInfo::GetEncodingConfigurationPropertyStore per ottenere l'archivio delle proprietà a livello di flusso del sink di file. In questa chiamata è necessario passare il numero di flusso assegnato per il flusso durante la creazione del profilo ASF.

  3. Impostare le proprietà di codifica a livello di flusso nell'archivio delle proprietà del flusso del sink di file. Per altre informazioni, vedere "Proprietà codifica flusso" in Impostazione delle proprietà nel sink di file.

    Nell'esempio di codice seguente vengono impostate le proprietà a livello di flusso nell'archivio delle proprietà del sink di file.

            //Get stream's encoding property
            hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamID, &pContentInfoProps);
            if (FAILED(hr))
            {
                goto done;
            }
    
            //Set the stream-level encoding properties
            hr = SetEncodingProperties(guidMajor, pContentInfoProps);       
            if (FAILED(hr))
            {
                goto done;
            }
    
    
    

    Nell'esempio di codice seguente viene illustrata l'implementazione di SetEncodingProperties. Questa funzione imposta le proprietà di codifica a livello di flusso per CBR e VBR.

    //-------------------------------------------------------------------
    //  SetEncodingProperties
    //  Create a media source from a URL.
    //
    //  guidMT:  Major type of the stream, audio or video
    //  pProps:  A pointer to the property store in which 
    //           to set the required encoding properties.
    //-------------------------------------------------------------------
    
    HRESULT SetEncodingProperties (const GUID guidMT, IPropertyStore* pProps)
    {
        if (!pProps)
        {
            return E_INVALIDARG;
        }
    
        if (EncodingMode == NONE)
        {
            return MF_E_NOT_INITIALIZED;
        }
    
        HRESULT hr = S_OK;
    
        PROPVARIANT var;
    
        switch (EncodingMode)
        {
            case CBR:
                // Set VBR to false.
                hr = InitPropVariantFromBoolean(FALSE, &var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                hr = pProps->SetValue(MFPKEY_VBRENABLED, var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                // Set the video buffer window.
                if (guidMT == MFMediaType_Video)
                {
                    hr = InitPropVariantFromInt32(VIDEO_WINDOW_MSEC, &var);
                    if (FAILED(hr))
                    {
                        goto done;
                    }
    
                    hr = pProps->SetValue(MFPKEY_VIDEOWINDOW, var);    
                    if (FAILED(hr))
                    {
                        goto done;
                    }
                }
                break;
    
            case VBR:
                //Set VBR to true.
                hr = InitPropVariantFromBoolean(TRUE, &var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                hr = pProps->SetValue(MFPKEY_VBRENABLED, var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                // Number of encoding passes is 1.
    
                hr = InitPropVariantFromInt32(1, &var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                hr = pProps->SetValue(MFPKEY_PASSESUSED, var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                // Set the quality level.
    
                if (guidMT == MFMediaType_Audio)
                {
                    hr = InitPropVariantFromUInt32(98, &var);
                    if (FAILED(hr))
                    {
                        goto done;
                    }
    
                    hr = pProps->SetValue(MFPKEY_DESIRED_VBRQUALITY, var);    
                    if (FAILED(hr))
                    {
                        goto done;
                    }
                }
                else if (guidMT == MFMediaType_Video)
                {
                    hr = InitPropVariantFromUInt32(95, &var);
                    if (FAILED(hr))
                    {
                        goto done;
                    }
    
                    hr = pProps->SetValue(MFPKEY_VBRQUALITY, var);    
                    if (FAILED(hr))
                    {
                        goto done;
                    }
                }
                break;
    
            default:
                hr = E_UNEXPECTED;
                break;
        }    
    
    done:
        PropVariantClear(&var);
        return hr;
    }
    
  4. Chiamare IMFASFContentInfo::GetEncodingConfigurationPropertyStore per ottenere l'archivio delle proprietà globali del sink di file. In questa chiamata è necessario passare 0 nel primo parametro. Per altre informazioni, vedere "Global File Sink Properties" in Setting Properties in the File Sink.For more information, see "Global File Sink Properties" in Setting Properties in the File Sink.

  5. Impostare il MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE su VARIANT_TRUE per assicurarsi che i valori di bucket persi nel multiplexer ASF siano regolati correttamente. Per informazioni su questa proprietà, vedere "Multiplexer Initialization and Leaky Bucket Impostazioni" in Creating the Multiplexer Object.For information about this property, see "Multiplexer Initialization and Leaky Bucket Impostazioni" in Creating the Multiplexer Object.

    Nell'esempio di codice seguente vengono impostate le proprietà a livello di flusso nell'archivio delle proprietà del sink di file.

        //Now we need to set global properties that will be set on the media sink
        hr = pContentInfo->GetEncodingConfigurationPropertyStore(0, &pContentInfoProps);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Auto adjust Bitrate
        var.vt = VT_BOOL;
        var.boolVal = VARIANT_TRUE;
    
        hr = pContentInfoProps->SetValue(MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE, var);
    
        //Initialize with the profile
        hr = pContentInfo->SetProfile(pProfile);
        if (FAILED(hr))
        {
            goto done;
        }
    

    Nota

    Esistono altre proprietà che è possibile impostare a livello globale per il sink di file. Per altre informazioni, vedere "Configurazione dell'oggetto ContentInfo con codificatore Impostazioni" in Impostazione delle proprietà nell'oggetto ContentInfo.

     

Si userà asf ContentInfo configurato per creare un oggetto di attivazione per il sink di file ASF (descritto nella sezione successiva).

Se si sta creando un sink di file out-of-process (MFCreateASFMediaSinkActivate), che usa un oggetto attivazione, è possibile utilizzare l'oggetto ContentInfo configurato per creare un'istanza del sink multimediale ASF (descritto nella sezione successiva). Se si sta creando un sink di file in-process (MFCreateASFMediaSink), anziché creare l'oggetto ContentInfo vuoto come descritto nel passaggio 1, ottenere un riferimento all'interfaccia IMFASFContentInfo chiamando IMFMediaSink::QueryInterface nel sink di file. È quindi necessario configurare l'oggetto ContentInfo come descritto in questa sezione.

Creare il sink di file ASF

In questo passaggio dell'esercitazione si userà l'ASF ContentInfo configurato creato nel passaggio precedente per creare un oggetto di attivazione per il sink di file ASF chiamando la funzione MFCreateASFMediaSinkActivate. Per altre informazioni, vedere Creazione del sink di file ASF.

Nell'esempio di codice seguente viene creato l'oggetto di attivazione per il sink di file.

    //Create the activation object for the  file sink
    hr = MFCreateASFMediaSinkActivate(sURL, pContentInfo, &pActivate);
    if (FAILED(hr))
    {
        goto done;
    }

Compilare la topologia di codifica parziale

Successivamente, si creerà una topologia di codifica parziale creando nodi di topologia per l'origine multimediale, i codificatori Windows Media necessari e il sink di file ASF. Dopo aver aggiunto i nodi della topologia, si connetteranno i nodi di origine, trasformazione e sink. Prima di aggiungere nodi della topologia, è necessario creare un oggetto topologia vuoto chiamando MFCreateTopology.

Creare il nodo della topologia di origine per l'origine multimediale

In questo passaggio si creerà il nodo della topologia di origine per l'origine multimediale.

Per creare questo nodo sono necessari i riferimenti seguenti:

  • Puntatore all'origine multimediale creata nel passaggio descritto nella sezione "Creare l'origine multimediale" di questa esercitazione.
  • Puntatore al descrittore di presentazione per l'origine multimediale. È possibile ottenere un riferimento all'interfaccia IMFPresentationDescriptor dell'origine multimediale chiamando IMFMediaSource::CreatePresentationDescriptor.
  • Puntatore al descrittore di flusso per ogni flusso nell'origine multimediale per cui è stato creato un flusso di destinazione nel passaggio descritto nella sezione "Creare l'oggetto profilo ASF" di questa esercitazione.

Per altre informazioni sulla creazione di nodi di origine e sull'esempio di codice, vedere Creazione di nodi di origine.

Nell'esempio di codice seguente viene creata una topologia parziale aggiungendo il nodo di origine e i nodi di trasformazione necessari. Questo codice chiama le funzioni helper AddSourceNode e AddTransformOutputNodes. Queste funzioni sono incluse più avanti in questa esercitazione.

//-------------------------------------------------------------------
//  BuildPartialTopology
//  Create a partial encoding topology by adding the source and the sink.
//
//  pSource:  A pointer to the media source to enumerate the source streams.
//  pSinkActivate: A pointer to the activation object for ASF file sink.
//  ppTopology:  Receives a pointer to the topology.
//-------------------------------------------------------------------

HRESULT BuildPartialTopology(
       IMFMediaSource *pSource, 
       IMFActivate* pSinkActivate,
       IMFTopology** ppTopology)
{
    if (!pSource || !pSinkActivate)
    {
        return E_INVALIDARG;
    }
    if (!ppTopology)
    {
        return E_POINTER;
    }

    HRESULT hr = S_OK;

    IMFPresentationDescriptor* pPD = NULL;
    IMFStreamDescriptor *pStreamDesc = NULL;
    IMFMediaTypeHandler* pMediaTypeHandler = NULL;
    IMFMediaType* pSrcType = NULL;

    IMFTopology* pTopology = NULL;
    IMFTopologyNode* pSrcNode = NULL;
    IMFTopologyNode* pEncoderNode = NULL;
    IMFTopologyNode* pOutputNode = NULL;


    DWORD cElems = 0;
    DWORD dwSrcStream = 0;
    DWORD StreamID = 0;
    GUID guidMajor = GUID_NULL;
    BOOL fSelected = FALSE;


    //Create the topology that represents the encoding pipeline
    hr = MFCreateTopology (&pTopology);
    if (FAILED(hr))
    {
        goto done;
    }

        
    hr = pSource->CreatePresentationDescriptor(&pPD);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pPD->GetStreamDescriptorCount(&dwSrcStream);
    if (FAILED(hr))
    {
        goto done;
    }

    for (DWORD iStream = 0; iStream < dwSrcStream; iStream++)
    {
        hr = pPD->GetStreamDescriptorByIndex(
            iStream, &fSelected, &pStreamDesc);
        if (FAILED(hr))
        {
            goto done;
        }

        if (!fSelected)
        {
            continue;
        }

        hr = AddSourceNode(pTopology, pSource, pPD, pStreamDesc, &pSrcNode);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pStreamDesc->GetMediaTypeHandler (&pMediaTypeHandler);
        if (FAILED(hr))
        {
            goto done;
        }
        
        hr = pStreamDesc->GetStreamIdentifier(&StreamID);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pMediaTypeHandler->GetMediaTypeByIndex(0, &pSrcType);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pSrcType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = AddTransformOutputNodes(pTopology, pSinkActivate, pSrcType, &pEncoderNode);
        if (FAILED(hr))
        {
            goto done;
        }

        //now we have the transform node, connect it to the source node
        hr = pSrcNode->ConnectOutput(0, pEncoderNode, 0);
        if (FAILED(hr))
        {
            goto done;
        }
                

        SafeRelease(&pStreamDesc);
        SafeRelease(&pMediaTypeHandler);
        SafeRelease(&pSrcType);
        SafeRelease(&pEncoderNode);
        SafeRelease(&pOutputNode);
        guidMajor = GUID_NULL;
    }

    *ppTopology = pTopology;
   (*ppTopology)->AddRef();


    wprintf_s(L"Partial Topology Built.\n");

done:
    SafeRelease(&pStreamDesc);
    SafeRelease(&pMediaTypeHandler);
    SafeRelease(&pSrcType);
    SafeRelease(&pEncoderNode);
    SafeRelease(&pOutputNode);
    SafeRelease(&pTopology);

    return hr;
}

Nell'esempio di codice seguente viene creato e aggiunto il nodo della topologia di origine alla topologia di codifica. Sono necessari puntatori a un oggetto topologia precedente, all'origine multimediale per enumerare i flussi di origine, il descrittore di presentazione dell'origine multimediale e il descrittore di flusso dell'origine multimediale. Il chiamante riceve un puntatore al nodo della topologia di origine.

// Add a source node to a topology.
HRESULT AddSourceNode(
    IMFTopology *pTopology,           // Topology.
    IMFMediaSource *pSource,          // Media source.
    IMFPresentationDescriptor *pPD,   // Presentation descriptor.
    IMFStreamDescriptor *pSD,         // Stream descriptor.
    IMFTopologyNode **ppNode)         // Receives the node pointer.
{
    IMFTopologyNode *pNode = NULL;

    // Create the node.
    HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode);
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the attributes.
    hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD);
    if (FAILED(hr))
    {
        goto done;
    }
    
    // Add the node to the topology.
    hr = pTopology->AddNode(pNode);
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the pointer to the caller.
    *ppNode = pNode;
    (*ppNode)->AddRef();

done:
    SafeRelease(&pNode);
    return hr;
}

Creare un'istanza dei codificatori necessari e creare i nodi di trasformazione

La pipeline di Media Foundation non inserisce automaticamente i codificatori Windows Media necessari per i flussi che deve codificare. L'applicazione deve aggiungere manualmente i codificatori. A tale scopo, enumerare i flussi nel profilo ASF creato nel passaggio descritto nella sezione "Creare l'oggetto profilo ASF" di questa esercitazione. Per ogni flusso nell'origine e nel flusso corrispondente nel profilo, creare un'istanza dei codificatori necessari. Per questo passaggio, è necessario un puntatore all'oggetto di attivazione per il sink di file creato nel passaggio descritto nella sezione "Creare il sink di file ASF" di questa esercitazione.

Per una panoramica sulla creazione di codificatori tramite oggetti di attivazione, vedere Uso degli oggetti attivazione di un codificatore.

La procedura seguente descrive i passaggi necessari per creare un'istanza dei codificatori necessari.

  1. Ottenere un riferimento all'oggetto ContentInfo del sink chiamando IMFActivate::ActivateObject nel sink del file e quindi eseguendo una query per IMFASFContentInfo dal sink di file chiamando QueryInterface.

  2. Ottenere il profilo ASF associato all'oggetto ContentInfo chiamando IMFASFContentInfo::GetProfile.

  3. Enumerare i flussi nel profilo. Per questo è necessario il conteggio dei flussi e un riferimento all'interfaccia IMFASFStreamConfig di ogni flusso.

    Chiamare i metodi seguenti:

  4. Per ogni flusso ottenere il tipo principale e le proprietà di codifica del flusso dall'oggetto ContentInfo.

    Chiamare i metodi seguenti:

  5. A seconda del tipo di flusso, audio o video, creare un'istanza dell'oggetto attivazione per il codificatore chiamando MFCreateWMAEncoderActivate o MFCreateWMVEncoderActivate.

    Per chiamare queste funzioni, sono necessari i riferimenti seguenti:

  6. Aggiornare il parametro bucket persa per il flusso audio.

    MFCreateWMAEncoderActivate imposta il tipo di output sul codificatore sottostante MFT per il codec audio di Windows Media. Dopo aver impostato il tipo di supporto di output, il codificatore ottiene la velocità media dei bit dal tipo di supporto di output, calcola la frequenza di bit della finestra del buffer e imposta i valori di bucket persi che verranno usati durante la sessione di codifica. È possibile aggiornare questi valori nel sink di file eseguendo una query sul codificatore o impostando valori personalizzati. Per aggiornare i valori è necessario il set di informazioni seguente:

    Creare una matrice di DWORD e impostare il valore nella proprietà MFPKEY_ASFSTREAMSINK_CORRECTED_LEAKYBUCKET del sink del flusso audio. Se non si specificano i valori aggiornati, la sessione multimediale li imposta in modo appropriato.

    Per altre informazioni, vedere Modello di buffer di bucket persi.

Gli oggetti di attivazione creati nel passaggio 5 devono essere aggiunti alla topologia come nodi della topologia di trasformazione. Per altre informazioni e un esempio di codice, vedere "Creazione di un nodo di trasformazione da un oggetto attivazione" in Creazione di nodi di trasformazione.

Nell'esempio di codice seguente viene creato e aggiunto l'attivazione del codificatore richiesto. Sono necessari puntatori a un oggetto topologia creato in precedenza, all'oggetto di attivazione del sink di file e al tipo di supporto del flusso di origine. Chiama anche AddOutputNode (vedere l'esempio di codice successivo) che crea e aggiunge il nodo sink alla topologia di codifica. Il chiamante riceve un puntatore al nodo della topologia di origine.

//-------------------------------------------------------------------
//  AddTransformOutputNodes
//  Creates and adds the sink node to the encoding topology.
//  Creates and adds the required encoder activates.

//  pTopology:  A pointer to the topology.
//  pSinkActivate:  A pointer to the file sink's activation object.
//  pSourceType: A pointer to the source stream's media type.
//  ppNode:  Receives a pointer to the topology node.
//-------------------------------------------------------------------

HRESULT AddTransformOutputNodes(
    IMFTopology* pTopology,
    IMFActivate* pSinkActivate,
    IMFMediaType* pSourceType,
    IMFTopologyNode **ppNode    // Receives the node pointer.
    )
{
    if (!pTopology || !pSinkActivate || !pSourceType)
    {
        return E_INVALIDARG;
    }

    IMFTopologyNode* pEncNode = NULL;
    IMFTopologyNode* pOutputNode = NULL;
    IMFASFContentInfo* pContentInfo = NULL;
    IMFASFProfile* pProfile = NULL;
    IMFASFStreamConfig* pStream = NULL;
    IMFMediaType* pMediaType = NULL;
    IPropertyStore* pProps = NULL;
    IMFActivate *pEncoderActivate = NULL;
    IMFMediaSink *pSink = NULL; 

    GUID guidMT = GUID_NULL;
    GUID guidMajor = GUID_NULL;

    DWORD cStreams = 0;
    WORD wStreamNumber = 0;

    HRESULT hr = S_OK;
        
    hr = pSourceType->GetMajorType(&guidMajor);
    if (FAILED(hr))
    {
        goto done;
    }

    // Create the node.
    hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &pEncNode);
    if (FAILED(hr))
    {
        goto done;
    }
    
    //Activate the sink
    hr = pSinkActivate->ActivateObject(__uuidof(IMFMediaSink), (void**)&pSink);
    if (FAILED(hr))
    {
        goto done;
    }
    //find the media type in the sink
    //Get content info from the sink
    hr = pSink->QueryInterface(__uuidof(IMFASFContentInfo), (void**)&pContentInfo);
    if (FAILED(hr))
    {
        goto done;
    }
    

    hr = pContentInfo->GetProfile(&pProfile);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pProfile->GetStreamCount(&cStreams);
    if (FAILED(hr))
    {
        goto done;
    }

    for(DWORD index = 0; index < cStreams ; index++)
    {
        hr = pProfile->GetStream(index, &wStreamNumber, &pStream);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pStream->GetMediaType(&pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pMediaType->GetMajorType(&guidMT);
        if (FAILED(hr))
        {
            goto done;
        }
        if (guidMT!=guidMajor)
        {
            SafeRelease(&pStream);
            SafeRelease(&pMediaType);
            guidMT = GUID_NULL;

            continue;
        }
        //We need to activate the encoder
        hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamNumber, &pProps);
        if (FAILED(hr))
        {
            goto done;
        }

        if (guidMT == MFMediaType_Audio)
        {
             hr = MFCreateWMAEncoderActivate(pMediaType, pProps, &pEncoderActivate);
            if (FAILED(hr))
            {
                goto done;
            }
            wprintf_s(L"Audio Encoder created. Stream Number: %d .\n", wStreamNumber);

            break;
        }
        if (guidMT == MFMediaType_Video)
        {
            hr = MFCreateWMVEncoderActivate(pMediaType, pProps, &pEncoderActivate);
            if (FAILED(hr))
            {
                goto done;
            }
            wprintf_s(L"Video Encoder created. Stream Number: %d .\n", wStreamNumber);

            break;
        }
    }

    // Set the object pointer.
    hr = pEncNode->SetObject(pEncoderActivate);
    if (FAILED(hr))
    {
        goto done;
    }

    // Add the node to the topology.
    hr = pTopology->AddNode(pEncNode);
    if (FAILED(hr))
    {
        goto done;
    }

    //Add the output node to this node.
    hr = AddOutputNode(pTopology, pSinkActivate, wStreamNumber, &pOutputNode);
    if (FAILED(hr))
    {
        goto done;
    }

    //now we have the output node, connect it to the transform node
    hr = pEncNode->ConnectOutput(0, pOutputNode, 0);
    if (FAILED(hr))
    {
        goto done;
    }

   // Return the pointer to the caller.
    *ppNode = pEncNode;
    (*ppNode)->AddRef();


done:
    SafeRelease(&pEncNode);
    SafeRelease(&pOutputNode);
    SafeRelease(&pEncoderActivate);
    SafeRelease(&pMediaType);
    SafeRelease(&pProps);
    SafeRelease(&pStream);
    SafeRelease(&pProfile);
    SafeRelease(&pContentInfo);
    SafeRelease(&pSink);
    return hr;
}

Creare i nodi della topologia di output per il sink di file

In questo passaggio si creerà il nodo della topologia di output per il sink del file ASF.

Per creare questo nodo sono necessari i riferimenti seguenti:

  • Puntatore all'oggetto di attivazione creato nel passaggio descritto nella sezione "Creare il sink di file ASF" di questa esercitazione.
  • Numeri di flusso per identificare i sink di flusso aggiunti al sink di file. I numeri di flusso corrispondono all'identificazione del flusso impostata durante la creazione del flusso.

Per altre informazioni sulla creazione di nodi di output e sull'esempio di codice, vedere "Creazione di un nodo di output da un oggetto attivazione" in Creazione di nodi di output.

Se non si usa l'oggetto attivazione per il sink di file, è necessario enumerare i sink di flusso nel sink del file ASF e impostare ogni sink di flusso come nodo di output nella topologia. Per informazioni sull'enumerazione sink di flusso, vedere "Enumerazione dei sink di flusso" in Aggiunta di informazioni sul flusso al sink del file ASF.

Nell'esempio di codice seguente viene creato e aggiunto il nodo sink alla topologia di codifica. Sono necessari puntatori a un oggetto topologia creato in precedenza, all'oggetto di attivazione del sink di file e al numero di identificazione del flusso. Il chiamante riceve un puntatore al nodo della topologia di origine.

// Add an output node to a topology.
HRESULT AddOutputNode(
    IMFTopology *pTopology,     // Topology.
    IMFActivate *pActivate,     // Media sink activation object.
    DWORD dwId,                 // Identifier of the stream sink.
    IMFTopologyNode **ppNode)   // Receives the node pointer.
{
    IMFTopologyNode *pNode = NULL;

    // Create the node.
    HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode);
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the object pointer.
    hr = pNode->SetObject(pActivate);
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the stream sink ID attribute.
    hr = pNode->SetUINT32(MF_TOPONODE_STREAMID, dwId);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
    if (FAILED(hr))
    {
        goto done;
    }

    // Add the node to the topology.
    hr = pTopology->AddNode(pNode);
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the pointer to the caller.
    *ppNode = pNode;
    (*ppNode)->AddRef();

done:
    SafeRelease(&pNode);
    return hr;
}

Nell'esempio di codice seguente vengono enumerati i sink di flusso per un sink multimediale specificato.

//-------------------------------------------------------------------
//  EnumerateStreamSinks
//  Enumerates the stream sinks within the specified media sink.
//
//  pSink:  A pointer to the media sink.
//-------------------------------------------------------------------
HRESULT EnumerateStreamSinks (IMFMediaSink* pSink)
{
    if (!pSink)
    {
        return E_INVALIDARG;
    }
    
    IMFStreamSink* pStreamSink = NULL;
    DWORD cStreamSinks = 0;

    HRESULT hr = pSink->GetStreamSinkCount(&cStreamSinks);
    if (FAILED(hr))
    {
        goto done;
    }

    for(DWORD index = 0; index < cStreamSinks; index++)
    {
        hr = pSink->GetStreamSinkByIndex (index, &pStreamSink);
        if (FAILED(hr))
        {
            goto done;
        }

        //Use the stream sink
        //Not shown.
    }
done:
    SafeRelease(&pStreamSink);

    return hr;
}

Connessione nodi di origine, trasformazione e sink

In questo passaggio si connetterà il nodo di origine al nodo di trasformazione che fa riferimento alla codifica attivata nel passaggio descritto nella sezione "Creare un'istanza dei codificatori necessari e creare i nodi di trasformazione" di questa esercitazione. Il nodo di trasformazione verrà connesso al nodo di output contenente l'oggetto attivazione per il sink di file.

Gestione della sessione di codifica

Nel passaggio verranno eseguiti i passaggi seguenti:

  1. Chiama MFCreateMediaSession per creare una sessione di codifica.

  2. Chiamare IMFMediaSession::SetTopology per impostare la topologia di codifica nella sessione. Se la chiamata viene completata, la sessione multimediale valuta i nodi della topologia e inserisce oggetti di trasformazione aggiuntivi, ad esempio un decodificatore che converte l'origine compressa specificata in campioni non compressi da inserire come input nel codificatore.

  3. Chiama IMFMediaSession::GetEvent per richiedere gli eventi generati dalla sessione multimediale.

    Nel ciclo di eventi si avvierà e si chiuderà la sessione di codifica a seconda degli eventi generati dalla sessione multimediale. La chiamata IMFMediaSession::SetTopology genera l'evento MESessionTopologyStatus con il flag MF_TOPOSTATUS_READY impostato. Tutti gli eventi vengono generati in modo asincrono e un'applicazione può recuperare questi eventi in modo sincrono o asincrono. Poiché l'applicazione in questa esercitazione è un'applicazione console e il blocco dei thread dell'interfaccia utente non è un problema, si otterranno gli eventi da Sessione multimediale in modo sincrono.

    Per ottenere gli eventi in modo asincrono, l'applicazione deve implementare l'interfaccia IMFAsyncCallback . Per altre informazioni e un'implementazione di esempio di questa interfaccia, vedere "Gestione degli eventi di sessione" in Come riprodurre file multimediali con Media Foundation.

Nel ciclo di eventi per ottenere gli eventi Media Session attendere l'evento MESessionTopologyStatus generato quando IMFMediaSession::SetTopology viene completato e la topologia viene risolta. Dopo aver ottenuto l'evento MESessionTopologyStatus, avviare la sessione di codifica chiamando IMFMediaSession::Start. La sessione multimediale genera l'evento M edizione Enterprise ndOfPresentation al termine di tutte le operazioni di codifica. Questo evento deve essere gestito per la codifica VBR ed è illustrato nella sezione successiva "Aggiornare le proprietà di codifica nel sink di file per la codifica VBR" di questa esercitazione.

La sessione multimediale genera l'oggetto intestazione ASF e finalizza il file al termine della sessione di codifica e quindi genera l'evento MESessionClosed . Questo evento deve essere gestito eseguendo operazioni di arresto appropriate nella sessione multimediale. Per avviare le operazioni di arresto, chiamare IMFMediaSession::Shutdown. Dopo la chiusura della sessione di codifica, non è possibile impostare un'altra topologia per la codifica nella stessa istanza di Sessione multimediale. Per codificare un altro file, la sessione multimediale corrente deve essere chiusa e rilasciata e la nuova topologia deve essere impostata in una sessione multimediale appena creata. Nell'esempio di codice seguente viene creata la sessione multimediale, viene impostata la topologia di codifica e vengono gestiti gli eventi media session.

L'esempio di codice seguente crea la sessione multimediale, imposta la topologia di codifica e controlla la sessione di codifica gestendo gli eventi dalla sessione multimediale.

//-------------------------------------------------------------------
//  Encode
//  Controls the encoding session and handles events from the media session.
//
//  pTopology:  A pointer to the encoding topology.
//-------------------------------------------------------------------

HRESULT Encode(IMFTopology *pTopology)
{
    if (!pTopology)
    {
        return E_INVALIDARG;
    }
    
    IMFMediaSession *pSession = NULL;
    IMFMediaEvent* pEvent = NULL;
    IMFTopology* pFullTopology = NULL;
    IUnknown* pTopoUnk = NULL;


    MediaEventType meType = MEUnknown;  // Event type

    HRESULT hr = S_OK;
    HRESULT hrStatus = S_OK;            // Event status
                
    MF_TOPOSTATUS TopoStatus = MF_TOPOSTATUS_INVALID; // Used with MESessionTopologyStatus event.    
        

    hr = MFCreateMediaSession(NULL, &pSession);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pSession->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, pTopology);
    if (FAILED(hr))
    {
        goto done;
    }

    //Get media session events synchronously
    while (1)
    {
        hr = pSession->GetEvent(0, &pEvent);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEvent->GetType(&meType);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEvent->GetStatus(&hrStatus);
        if (FAILED(hr))
        {
            goto done;
        }
        if (FAILED(hrStatus))
        {
            hr = hrStatus;
            goto done;
        }

       switch(meType)
        {
            case MESessionTopologyStatus:
                {
                    // Get the status code.
                    MF_TOPOSTATUS status = (MF_TOPOSTATUS)MFGetAttributeUINT32(
                                             pEvent, MF_EVENT_TOPOLOGY_STATUS, MF_TOPOSTATUS_INVALID);

                    if (status == MF_TOPOSTATUS_READY)
                    {
                        PROPVARIANT var;
                        PropVariantInit(&var);
                        wprintf_s(L"Topology resolved and set on the media session.\n");

                        hr = pSession->Start(NULL, &var);
                        if (FAILED(hr))
                        {
                            goto done;
                        }

                    }
                    if (status == MF_TOPOSTATUS_STARTED_SOURCE)
                    {
                        wprintf_s(L"Encoding started.\n");
                        break;
                    }
                    if (status == MF_TOPOSTATUS_ENDED)
                    {
                        wprintf_s(L"Encoding complete.\n");
                        hr = pSession->Close();
                        if (FAILED(hr))
                        {
                            goto done;
                        }

                        break;
                    }
                }
                break;


            case MESessionEnded:
                wprintf_s(L"Encoding complete.\n");
                hr = pSession->Close();
                if (FAILED(hr))
                {
                    goto done;
                }
                break;

            case MEEndOfPresentation:
                {
                    if (EncodingMode == VBR)
                    {
                        hr = pSession->GetFullTopology(MFSESSION_GETFULLTOPOLOGY_CURRENT, 0, &pFullTopology);
                        if (FAILED(hr))
                        {
                            goto done;
                        }
                        hr = PostEncodingUpdate(pFullTopology);
                        if (FAILED(hr))
                        {
                            goto done;
                        }
                        wprintf_s(L"Updated sinks for VBR. \n");
                    }
                }
                break;

            case MESessionClosed:
                wprintf_s(L"Encoding session closed.\n");

                hr = pSession->Shutdown();
                goto done;
        }
        if (FAILED(hr))
        {
            goto done;
        }

        SafeRelease(&pEvent);

    }
done:
    SafeRelease(&pEvent);
    SafeRelease(&pSession);
    SafeRelease(&pFullTopology);
    SafeRelease(&pTopoUnk);
    return hr;
}

Aggiornare le proprietà di codifica nel sink di file

Alcune proprietà di codifica, ad esempio la frequenza dei bit di codifica e i valori di bucket persi accurati, non sono noti fino al completamento della codifica, in particolare per la codifica VBR. Per ottenere i valori corretti, l'applicazione deve attendere l'evento M edizione Enterprise ndOfPresentation che indica che la sessione di codifica è stata completata. I valori di bucket persi devono essere aggiornati nel sink in modo che l'oggetto intestazione ASF possa riflettere i valori accurati.

La procedura seguente descrive i passaggi necessari per attraversare i nodi nella topologia di codifica per ottenere il nodo sink del file e impostare le proprietà del bucket di perdita necessarie.

Per aggiornare i valori delle proprietà di post-codifica nel sink del file ASF

  1. Chiamare IMFTopology::GetOutputNodeCollection per ottenere la raccolta di nodi di output dalla topologia di codifica.
  2. Per ogni nodo, ottenere un puntatore al sink di flusso nel nodo chiamando IMFTopologyNode::GetObject. Query per l'interfaccia IMFStreamSink sul puntatore IUnknown restituito da IMFTopologyNode::GetObject.
  3. Per ogni sink di flusso, ottenere il nodo downstream (codificatore) chiamando IMFTopologyNode::GetInput.
  4. Eseguire una query sul nodo per ottenere il puntatore IMFTransform dal nodo del codificatore.
  5. Eseguire una query sul codificatore per il puntatore IPropertyStore per ottenere l'archivio delle proprietà di codifica dal codificatore.
  6. Eseguire una query sul sink di flusso per il puntatore IPropertyStore per ottenere l'archivio delle proprietà del sink di flusso.
  7. Chiamare IPropertyStore::GetValue per ottenere i valori delle proprietà richiesti dall'archivio delle proprietà del codificatore e copiarli nell'archivio delle proprietà del sink di flusso chiamando IPropertyStore::SetValue.

La tabella seguente mostra i valori delle proprietà di post codifica che devono essere impostati nel sink di flusso per il flusso video.

Tipo di codifica Nome proprietà (GetValue) Nome proprietà (SetValue)
Codifica della velocità in bit costante MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
Codifica della velocità in bit variabile basata sulla qualità MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_BMAX
MFPKEY_RMAX
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
MFPKEY_STAT_BMAX
MFPKEY_STAT_RMAX

 

Nell'esempio di codice seguente vengono impostati i valori delle proprietà di post-codifica.

//-------------------------------------------------------------------
//  PostEncodingUpdate
//  Updates the file sink with encoding properties set on the encoder
//  during the encoding session.
    //1. Get the output nodes
    //2. For each node, get the downstream node
    //3. For the downstream node, get the MFT
    //4. Get the property store
    //5. Get the required values
    //6. Set them on the stream sink
//
//  pTopology: A pointer to the full topology retrieved from the media session.
//-------------------------------------------------------------------

HRESULT PostEncodingUpdate(IMFTopology *pTopology)
{
    if (!pTopology)
    {
        return E_INVALIDARG;
    }

    HRESULT hr = S_OK;

    IMFCollection* pOutputColl = NULL;
    IUnknown* pNodeUnk = NULL;
    IMFMediaType* pType = NULL;
    IMFTopologyNode* pNode = NULL;
    IUnknown* pSinkUnk = NULL;
    IMFStreamSink* pStreamSink = NULL;
    IMFTopologyNode* pEncoderNode = NULL;
    IUnknown* pEncoderUnk = NULL;
    IMFTransform* pEncoder = NULL;
    IPropertyStore* pStreamSinkProps = NULL;
    IPropertyStore* pEncoderProps = NULL;

    GUID guidMajorType = GUID_NULL;

    PROPVARIANT var;
    PropVariantInit( &var );

    DWORD cElements = 0;

    hr = pTopology->GetOutputNodeCollection( &pOutputColl);
    if (FAILED(hr))
    {
        goto done;
    }


    hr = pOutputColl->GetElementCount(&cElements);
    if (FAILED(hr))
    {
        goto done;
    }


    for(DWORD index = 0; index < cElements; index++)
    {
        hr = pOutputColl->GetElement(index, &pNodeUnk);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNodeUnk->QueryInterface(IID_IMFTopologyNode, (void**)&pNode);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNode->GetInputPrefType(0, &pType);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pType->GetMajorType( &guidMajorType );
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNode->GetObject(&pSinkUnk);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pSinkUnk->QueryInterface(IID_IMFStreamSink, (void**)&pStreamSink);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNode->GetInput( 0, &pEncoderNode, NULL );
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEncoderNode->GetObject(&pEncoderUnk);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEncoderUnk->QueryInterface(IID_IMFTransform, (void**)&pEncoder);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pStreamSink->QueryInterface(IID_IPropertyStore, (void**)&pStreamSinkProps);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEncoder->QueryInterface(IID_IPropertyStore, (void**)&pEncoderProps);
        if (FAILED(hr))
        {
            goto done;
        }

        if( guidMajorType == MFMediaType_Video )
        {            
            hr = pEncoderProps->GetValue( MFPKEY_BAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BAVG, var );
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_RAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RAVG, var);
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_BMAX, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BMAX, var);
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_RMAX, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RMAX, var);
            if (FAILED(hr))
            {
                goto done;
            }
        }
        else if( guidMajorType == MFMediaType_Audio )
        {      
            hr = pEncoderProps->GetValue( MFPKEY_STAT_BAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BAVG, var );
            if (FAILED(hr))
            {
                goto done;
            }
            
            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_STAT_RAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RAVG, var );
            if (FAILED(hr))
            {
                goto done;
            }
            
            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_STAT_BMAX, &var);
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BMAX, var);
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_STAT_RMAX, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RMAX, var );         
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_WMAENC_AVGBYTESPERSEC, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_WMAENC_AVGBYTESPERSEC, var );         
            if (FAILED(hr))
            {
                goto done;
            }
        } 
        PropVariantClear( &var ); 
   }
done:
    SafeRelease (&pOutputColl);
    SafeRelease (&pNodeUnk);
    SafeRelease (&pType);
    SafeRelease (&pNode);
    SafeRelease (&pSinkUnk);
    SafeRelease (&pStreamSink);
    SafeRelease (&pEncoderNode);
    SafeRelease (&pEncoderUnk);
    SafeRelease (&pEncoder);
    SafeRelease (&pStreamSinkProps);
    SafeRelease (&pEncoderProps);

    return hr;
   
}

Implementare main

L'esempio di codice seguente illustra la funzione principale dell'applicazione console.

int wmain(int argc, wchar_t* argv[])
{
    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);

    if (argc != 4)
    {
        wprintf_s(L"Usage: %s inputaudio.mp3, %s output.wm*, %Encoding Type: CBR, VBR\n");
        return 0;
    }


    HRESULT             hr = S_OK;

    IMFMediaSource* pSource = NULL;
    IMFTopology* pTopology = NULL;
    IMFActivate* pFileSinkActivate = NULL;

    hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (FAILED(hr))
    {
        goto done;
    }

    //Set the requested encoding mode
    if (wcscmp(argv[3], L"CBR")==0)
    {
        EncodingMode = CBR;
    }
    else if (wcscmp(argv[3], L"VBR")==0)
    {
        EncodingMode = VBR;
    }
    else
    {
        EncodingMode = CBR;
    }

    // Start up Media Foundation platform.
    hr = MFStartup(MF_VERSION);
    if (FAILED(hr))
    {
        goto done;
    }

    //Create the media source
    hr = CreateMediaSource(argv[1], &pSource);
    if (FAILED(hr))
    {
        goto done;
    }

    //Create the file sink activate
    hr = CreateMediaSink(argv[2], pSource, &pFileSinkActivate);
    if (FAILED(hr))
    {
        goto done;
    }

    //Build the encoding topology.
    hr = BuildPartialTopology(pSource, pFileSinkActivate, &pTopology);
    if (FAILED(hr))
    {
        goto done;
    }

    //Instantiate the media session and start encoding
    hr = Encode(pTopology);
    if (FAILED(hr))
    {
        goto done;
    }


done:
    // Clean up.
    SafeRelease(&pSource);
    SafeRelease(&pTopology);
    SafeRelease(&pFileSinkActivate);

    MFShutdown();
    CoUninitialize();

    if (FAILED(hr))
    {
        wprintf_s(L"Could not create the output file due to 0x%X\n", hr);
    }
    return 0;
}

Testare il file di output

Nell'elenco seguente viene descritto un elenco di controllo per il test del file codificato. Questi valori possono essere selezionati nella finestra di dialogo delle proprietà del file che è possibile visualizzare facendo clic con il pulsante destro del mouse sul file codificato e scegliendo Proprietà dal menu di scelta rapida.

  • Il percorso del file codificato è accurato.
  • Le dimensioni del file sono superiori a zero KB e la durata di riproduzione corrisponde alla durata del file di origine.
  • Per il flusso video, controllare la larghezza e l'altezza del fotogramma, la frequenza dei fotogrammi. Questi valori devono corrispondere ai valori specificati nel profilo ASF creato nel passaggio descritto nella sezione "Creare l'oggetto profilo ASF".
  • Per il flusso audio, la velocità di bit deve essere vicina al valore specificato nel tipo di supporto di destinazione.
  • Aprire il file in Lettore multimediale Windows e controllare la qualità della codifica.
  • Aprire il file ASF in ASFViewer per visualizzare la struttura di un file ASF. Questo strumento può essere scaricato da questo sito Web Microsoft.

Codici di errore comuni e debug Suggerimenti

L'elenco seguente descrive i codici di errore comuni che potrebbero ricevere e i suggerimenti per il debug.

  • La chiamata a IMFSourceResolver::CreateObjectFromURL blocca l'applicazione.

    Assicurarsi di aver inizializzato la piattaforma Media Foundation chiamando MFStartup. Questa funzione configura la piattaforma asincrona usata da tutti i metodi che avviano operazioni asincrone, ad esempio IMFSourceResolver::CreateObjectFromURL, internamente.

  • IMFSourceResolver::CreateObjectFromURL restituisce HRESULT 0x80070002 "Impossibile trovare il file specificato.

    Assicurarsi che il nome del file di input specificato dall'utente nel primo argomento esista.

  • HRESULT 0x80070020 "Il processo non può accedere al file perché viene usato da un altro processo. "

    Assicurarsi che l'input e i file di output non siano attualmente in uso da un'altra risorsa nel sistema.

  • Le chiamate ai metodi IMFTransform restituiscono MF_E_INVALIDMEDIATYPE.

    Assicurarsi che le condizioni seguenti siano vere:

    • Il tipo di input o il tipo di output specificato è compatibile con i tipi di supporto supportati dal codificatore.
    • I tipi di supporto specificati sono completi. Per il completamento dei tipi di supporti, vedere gli attributi necessari nelle sezioni "Create a Compressed Audio Media Type" (Crea un tipo di supporto audio compresso) e "Create a Compressed Video Media Type" (Crea un tipo di supporto video compresso) di questa esercitazione.
    • Assicurarsi di aver impostato la velocità di bit di destinazione sul tipo di supporto parziale a cui si aggiungono i dati privati del codec.
  • La sessione multimediale restituisce MF_E_UNSUPPORTED_D3D_TYPE nello stato dell'evento.

    Questo errore viene restituito quando il tipo di supporto dell'origine indica una modalità interlaccia mista non supportata dal codificatore Windows Media Video. Se il tipo di supporto video compresso è impostato per l'uso della modalità progressiva, la pipeline deve usare una trasformazione de-interlacciamento. Poiché la pipeline non riesce a trovare una corrispondenza (indicata da questo codice di errore), è necessario inserire manualmente un delacciatore (processore video transcodificabile) tra il decodificatore e i nodi del codificatore.

  • La sessione multimediale restituisce E_INVALIDARG nello stato dell'evento.

    Questo errore viene restituito quando gli attributi del tipo di supporto di origine non sono compatibili con gli attributi del tipo di supporto di output impostato nel codificatore Windows Media.

  • IWMCodecPrivateData::GetPrivateData restituisce HRESULT 0x80040203 "Si è verificato un errore di sintassi durante il tentativo di valutare una stringa di query"

    Assicurarsi che il tipo di input sia impostato nel codificatore MFT.

Componenti ASF del livello pipeline