Utilisation du lecteur source pour traiter les données multimédias

Cette rubrique explique comment utiliser le lecteur source pour traiter les données multimédias.

Pour utiliser le lecteur source, procédez comme suit :

  1. Créez un instance du lecteur source.
  2. Énumérez les formats de sortie possibles.
  3. Définissez le format de sortie réel pour chaque flux.
  4. Traiter les données à partir de la source.

Le reste de cette rubrique décrit ces étapes en détail.

Création du lecteur source

Pour créer une instance du lecteur source, appelez l’une des fonctions suivantes :

Fonction Description
MFCreateSourceReaderFromURL
Prend une URL comme entrée. Cette fonction utilise le programme de résolution de source pour créer une source multimédia à partir de l’URL.
MFCreateSourceReaderFromByteStream
Prend un pointeur vers un flux d’octets. Cette fonction utilise également le programme de résolution de source pour créer la source multimédia.
MFCreateSourceReaderFromMediaSource
Prend un pointeur vers une source multimédia qui a déjà été créée. Cette fonction est utile pour les sources multimédias que le programme de résolution de source ne peut pas créer, telles que les périphériques de capture ou les sources multimédias personnalisées.

 

En règle générale, pour les fichiers multimédias, utilisez MFCreateSourceReaderFromURL. Pour les appareils, tels que les webcams, utilisez MFCreateSourceReaderFromMediaSource. (Pour plus d’informations sur les appareils de capture dans Microsoft Media Foundation, consultez Capture audio/vidéo.)

Chacune de ces fonctions prend un pointeur IMFAttributes facultatif, qui est utilisé pour définir différentes options sur le lecteur source, comme décrit dans les rubriques de référence pour ces fonctions. Pour obtenir le comportement par défaut, définissez ce paramètre sur NULL. Chaque fonction retourne un pointeur IMFSourceReader en tant que paramètre de sortie. Vous devez appeler les fonctions CoInitialize(Ex) et MFStartup avant d’appeler l’une de ces fonctions.

Le code suivant crée le lecteur source à partir d’une URL.

int __cdecl wmain(int argc, __in_ecount(argc) PCWSTR* argv)
{
    if (argc < 2)
    {
        return 1;
    }

    const WCHAR *pszURL = argv[1];

    // Initialize the COM runtime.
    HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (SUCCEEDED(hr))
    {
        // Initialize the Media Foundation platform.
        hr = MFStartup(MF_VERSION);
        if (SUCCEEDED(hr))
        {
            // Create the source reader.
            IMFSourceReader *pReader;
            hr = MFCreateSourceReaderFromURL(pszURL, NULL, &pReader);
            if (SUCCEEDED(hr))
            {
                ReadMediaFile(pReader);
                pReader->Release();
            }
            // Shut down Media Foundation.
            MFShutdown();
        }
        CoUninitialize();
    }
}

Énumération des formats de sortie

Chaque source multimédia a au moins un flux. Par exemple, un fichier vidéo peut contenir un flux vidéo et un flux audio. Le format de chaque flux est décrit à l’aide d’un type de média, représenté par l’interface IMFMediaType . Pour plus d’informations sur les types de médias, consultez Types de médias. Vous devez examiner le type de média pour comprendre le format des données que vous obtenez à partir du lecteur source.

Initialement, chaque flux a un format par défaut, que vous pouvez trouver en appelant la méthode IMFSourceReader::GetCurrentMediaType :

Pour chaque flux, la source multimédia offre une liste des types de médias possibles pour ce flux. Le nombre de types dépend de la source. Si la source représente un fichier multimédia, il n’existe généralement qu’un seul type par flux. En revanche, une webcam peut être en mesure de diffuser des vidéos dans différents formats. Dans ce cas, l’application peut sélectionner le format à utiliser dans la liste des types de médias.

Pour obtenir l’un des types multimédias d’un flux, appelez la méthode IMFSourceReader::GetNativeMediaType . Cette méthode prend deux paramètres d’index : l’index du flux et un index dans la liste des types de médias pour le flux. Pour énumérer tous les types d’un flux, incrémentez l’index de liste tout en conservant la constante de l’index de flux. Lorsque l’index de liste sort des limites, GetNativeMediaType retourne MF_E_NO_MORE_TYPES.

HRESULT EnumerateTypesForStream(IMFSourceReader *pReader, DWORD dwStreamIndex)
{
    HRESULT hr = S_OK;
    DWORD dwMediaTypeIndex = 0;

    while (SUCCEEDED(hr))
    {
        IMFMediaType *pType = NULL;
        hr = pReader->GetNativeMediaType(dwStreamIndex, dwMediaTypeIndex, &pType);
        if (hr == MF_E_NO_MORE_TYPES)
        {
            hr = S_OK;
            break;
        }
        else if (SUCCEEDED(hr))
        {
            // Examine the media type. (Not shown.)

            pType->Release();
        }
        ++dwMediaTypeIndex;
    }
    return hr;
}

Pour énumérer les types de médias pour chaque flux, incrémentez l’index de flux. Lorsque l’index de flux sort des limites, GetNativeMediaType retourne MF_E_INVALIDSTREAMNUMBER.

HRESULT EnumerateMediaTypes(IMFSourceReader *pReader)
{
    HRESULT hr = S_OK;
    DWORD dwStreamIndex = 0;

    while (SUCCEEDED(hr))
    {
        hr = EnumerateTypesForStream(pReader, dwStreamIndex);
        if (hr == MF_E_INVALIDSTREAMNUMBER)
        {
            hr = S_OK;
            break;
        }
        ++dwStreamIndex;
    }
    return hr;
}

Définition des formats de sortie

Pour modifier le format de sortie, appelez la méthode IMFSourceReader::SetCurrentMediaType . Cette méthode prend l’index de flux et un type de média :

hr = pReader->SetCurrentMediaType(dwStreamIndex, pMediaType);

Pour le type de média, cela dépend de l’insertion ou non d’un décodeur.

  • Pour obtenir des données directement à partir de la source sans les décoder, utilisez l’un des types retournés par GetNativeMediaType.
  • Pour décoder le flux, créez un nouveau type de média qui décrit le format non compressé souhaité.

Dans le cas du décodeur, créez le type de média comme suit :

  1. Appelez MFCreateMediaType pour créer un nouveau type de média.
  2. Définissez l’attribut MF_MT_MAJOR_TYPE pour spécifier l’audio ou la vidéo.
  3. Définissez l’attribut MF_MT_SUBTYPE pour spécifier le sous-type du format de décodage. (Voir GUID de sous-type audio et GUID de sous-type vidéo.)
  4. Appelez IMFSourceReader::SetCurrentMediaType.

Le lecteur source charge automatiquement le décodeur. Pour obtenir les détails complets du format décodé, appelez IMFSourceReader::GetCurrentMediaType après l’appel à SetCurrentMediaType

Le code suivant configure le flux vidéo pour RVB-32 et le flux audio pour l’audio PCM.

HRESULT ConfigureDecoder(IMFSourceReader *pReader, DWORD dwStreamIndex)
{
    IMFMediaType *pNativeType = NULL;
    IMFMediaType *pType = NULL;

    // Find the native format of the stream.
    HRESULT hr = pReader->GetNativeMediaType(dwStreamIndex, 0, &pNativeType);
    if (FAILED(hr))
    {
        return hr;
    }

    GUID majorType, subtype;

    // Find the major type.
    hr = pNativeType->GetGUID(MF_MT_MAJOR_TYPE, &majorType);
    if (FAILED(hr))
    {
        goto done;
    }

    // Define the output type.
    hr = MFCreateMediaType(&pType);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pType->SetGUID(MF_MT_MAJOR_TYPE, majorType);
    if (FAILED(hr))
    {
        goto done;
    }

    // Select a subtype.
    if (majorType == MFMediaType_Video)
    {
        subtype= MFVideoFormat_RGB32;
    }
    else if (majorType == MFMediaType_Audio)
    {
        subtype = MFAudioFormat_PCM;
    }
    else
    {
        // Unrecognized type. Skip.
        goto done;
    }

    hr = pType->SetGUID(MF_MT_SUBTYPE, subtype);
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the uncompressed format.
    hr = pReader->SetCurrentMediaType(dwStreamIndex, NULL, pType);
    if (FAILED(hr))
    {
        goto done;
    }

done:
    SafeRelease(&pNativeType);
    SafeRelease(&pType);
    return hr;
}

Traitement des données multimédias

Pour obtenir des données multimédias à partir de la source, appelez la méthode IMFSourceReader::ReadSample , comme indiqué dans le code suivant.

        DWORD streamIndex, flags;
        LONGLONG llTimeStamp;

        hr = pReader->ReadSample(
            MF_SOURCE_READER_ANY_STREAM,    // Stream index.
            0,                              // Flags.
            &streamIndex,                   // Receives the actual stream index. 
            &flags,                         // Receives status flags.
            &llTimeStamp,                   // Receives the time stamp.
            &pSample                        // Receives the sample or NULL.
            );

Le premier paramètre est l’index du flux pour lequel vous souhaitez obtenir des données. Vous pouvez également spécifier MF_SOURCE_READER_ANY_STREAM pour obtenir les données disponibles suivantes à partir de n’importe quel flux. Le deuxième paramètre contient des indicateurs facultatifs ; consultez MF_SOURCE_READER_CONTROL_FLAG pour obtenir la liste de ceux-ci. Le troisième paramètre reçoit l’index du flux qui produit réellement les données. Vous aurez besoin de ces informations si vous définissez le premier paramètre sur MF_SOURCE_READER_ANY_STREAM. Le quatrième paramètre reçoit status indicateurs, indiquant différents événements qui peuvent se produire lors de la lecture des données, tels que les modifications de format dans le flux. Pour obtenir la liste des indicateurs de status, consultez MF_SOURCE_READER_FLAG.

Si la source multimédia est en mesure de produire des données pour le flux demandé, le dernier paramètre de ReadSample reçoit un pointeur vers l’interface IMFSample d’un exemple d’objet multimédia. Utilisez l’exemple multimédia pour :

  • Obtenir un pointeur vers les données multimédias.
  • Obtenez l’heure de présentation et la durée de l’exemple.
  • Obtenez des attributs qui décrivent l’entrelacement, la dominance de champ et d’autres aspects de l’exemple.

Le contenu des données multimédias dépend du format du flux. Pour un flux vidéo non compressé, chaque exemple multimédia contient une seule image vidéo. Pour un flux audio non compressé, chaque exemple de média contient une séquence d’images audio.

La méthode ReadSample peut retourner S_OK et ne pas renvoyer d’exemple de média dans le paramètre pSample . Par exemple, lorsque vous atteignez la fin du fichier, ReadSample définit l’indicateur MF_SOURCE_READERF_ENDOFSTREAM dans dwFlags et définit pSample sur NULL. Dans ce cas, la méthode ReadSample retourne S_OK car aucune erreur ne s’est produite, même si le paramètre pSample a la valeur NULL. Par conséquent, case activée toujours la valeur de pSample avant de la déréférencer.

Le code suivant montre comment appeler ReadSample dans une boucle et case activée les informations retournées par la méthode, jusqu’à ce que la fin du fichier multimédia soit atteinte.

HRESULT ProcessSamples(IMFSourceReader *pReader)
{
    HRESULT hr = S_OK;
    IMFSample *pSample = NULL;
    size_t  cSamples = 0;

    bool quit = false;
    while (!quit)
    {
        DWORD streamIndex, flags;
        LONGLONG llTimeStamp;

        hr = pReader->ReadSample(
            MF_SOURCE_READER_ANY_STREAM,    // Stream index.
            0,                              // Flags.
            &streamIndex,                   // Receives the actual stream index. 
            &flags,                         // Receives status flags.
            &llTimeStamp,                   // Receives the time stamp.
            &pSample                        // Receives the sample or NULL.
            );

        if (FAILED(hr))
        {
            break;
        }

        wprintf(L"Stream %d (%I64d)\n", streamIndex, llTimeStamp);
        if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
        {
            wprintf(L"\tEnd of stream\n");
            quit = true;
        }
        if (flags & MF_SOURCE_READERF_NEWSTREAM)
        {
            wprintf(L"\tNew stream\n");
        }
        if (flags & MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED)
        {
            wprintf(L"\tNative type changed\n");
        }
        if (flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
        {
            wprintf(L"\tCurrent type changed\n");
        }
        if (flags & MF_SOURCE_READERF_STREAMTICK)
        {
            wprintf(L"\tStream tick\n");
        }

        if (flags & MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED)
        {
            // The format changed. Reconfigure the decoder.
            hr = ConfigureDecoder(pReader, streamIndex);
            if (FAILED(hr))
            {
                break;
            }
        }

        if (pSample)
        {
            ++cSamples;
        }

        SafeRelease(&pSample);
    }

    if (FAILED(hr))
    {
        wprintf(L"ProcessSamples FAILED, hr = 0x%x\n", hr);
    }
    else
    {
        wprintf(L"Processed %d samples\n", cSamples);
    }
    SafeRelease(&pSample);
    return hr;
}

Drainage du pipeline de données

Pendant le traitement des données, un décodeur ou une autre transformation peut mettre en mémoire tampon des exemples d’entrée. Dans le diagramme suivant, l’application appelle ReadSample et reçoit un exemple dont le temps de présentation est égal à t1. Le décodeur contient des exemples pour t2 et t3.

illustration montrant la mise en mémoire tampon dans un décodeur.

Lors de l’appel suivant à ReadSample, le lecteur source peut donner t4 au décodeur et retourner t2 à l’application.

Si vous souhaitez décoder tous les exemples actuellement mis en mémoire tampon dans le décodeur, sans passer de nouveaux exemples au décodeur, définissez l’indicateur MF_SOURCE_READER_CONTROLF_DRAIN dans le paramètre dwControlFlags de ReadSample. Continuez à effectuer cette opération dans une boucle jusqu’à ce que ReadSample retourne un exemple de pointeur NULL . Selon la façon dont le décodeur met en mémoire tampon des exemples, cela peut se produire immédiatement ou après plusieurs appels à ReadSample.

Obtention de la durée du fichier

Pour obtenir la durée d’un fichier multimédia, appelez la méthode IMFSourceReader::GetPresentationAttribute et demandez l’attribut MF_PD_DURATION , comme indiqué dans le code suivant.

HRESULT GetDuration(IMFSourceReader *pReader, LONGLONG *phnsDuration)
{
    PROPVARIANT var;
    HRESULT hr = pReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, 
        MF_PD_DURATION, &var);
    if (SUCCEEDED(hr))
    {
        hr = PropVariantToInt64(var, phnsDuration);
        PropVariantClear(&var);
    }
    return hr;
}

La fonction illustrée ici obtient la durée en unités de 100 nanosecondes. Diviser par 10 000 000 pour obtenir la durée en secondes.

Recherche

Une source multimédia qui obtient des données à partir d’un fichier local peut généralement rechercher des positions arbitraires dans le fichier. Les appareils de capture tels que les webcams ne peuvent généralement pas rechercher, car les données sont actives. Une source qui diffuse des données sur un réseau peut être recherchée, en fonction du protocole de diffusion en continu réseau.

Pour savoir si une source multimédia peut rechercher, appelez IMFSourceReader::GetPresentationAttribute et demandez l’attribut MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS , comme indiqué dans le code suivant :

HRESULT GetSourceFlags(IMFSourceReader *pReader, ULONG *pulFlags)
{
    ULONG flags = 0;

    PROPVARIANT var;
    PropVariantInit(&var);

    HRESULT hr = pReader->GetPresentationAttribute(
        MF_SOURCE_READER_MEDIASOURCE, 
        MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS, 
        &var);

    if (SUCCEEDED(hr))
    {
        hr = PropVariantToUInt32(var, &flags);
    }
    if (SUCCEEDED(hr))
    {
        *pulFlags = flags;
    }

    PropVariantClear(&var);
    return hr;
}

Cette fonction obtient un ensemble d’indicateurs de fonctionnalités à partir de la source. Ces indicateurs sont définis dans l’énumération MFMEDIASOURCE_CHARACTERISTICS . Deux indicateurs concernent la recherche :

Indicateur Description
MFMEDIASOURCE_CAN_SEEK
La source peut rechercher.
MFMEDIASOURCE_HAS_SLOW_SEEK
La recherche peut prendre beaucoup de temps. Par exemple, la source peut avoir besoin de télécharger l’intégralité du fichier avant de pouvoir rechercher. (Il n’existe aucun critère strict pour qu’une source retourne cet indicateur.)

 

Les tests de code suivants pour l’indicateur MFMEDIASOURCE_CAN_SEEK .

BOOL SourceCanSeek(IMFSourceReader *pReader)
{
    BOOL bCanSeek = FALSE;
    ULONG flags;
    if (SUCCEEDED(GetSourceFlags(pReader, &flags)))
    {
        bCanSeek = ((flags & MFMEDIASOURCE_CAN_SEEK) == MFMEDIASOURCE_CAN_SEEK);
    }
    return bCanSeek;
}

Pour rechercher, appelez la méthode IMFSourceReader::SetCurrentPosition , comme indiqué dans le code suivant.

HRESULT SetPosition(IMFSourceReader *pReader, const LONGLONG& hnsPosition)
{
    PROPVARIANT var;
    HRESULT hr = InitPropVariantFromInt64(hnsPosition, &var);
    if (SUCCEEDED(hr))
    {
        hr = pReader->SetCurrentPosition(GUID_NULL, var);
        PropVariantClear(&var);
    }
    return hr;
}

Le premier paramètre donne le format d’heure que vous utilisez pour spécifier la position de recherche. Toutes les sources multimédias dans Media Foundation doivent prendre en charge des unités de 100 nanosecondes, indiquées par la valeur GUID_NULL. Le deuxième paramètre est un PROPVARIANT qui contient la position de recherche. Pour les unités de temps de 100 nanosecondes, le type de données est LONGLONG.

N’oubliez pas que toutes les sources multimédias ne fournissent pas une recherche précise du cadre. La précision de la recherche dépend de plusieurs facteurs, tels que l’intervalle d’images clés, si le fichier multimédia contient un index et si les données ont un débit binaire constant ou variable. Par conséquent, une fois que vous recherchez une position dans un fichier, il n’y a aucune garantie que l’horodatage de l’échantillon suivant correspondra exactement à la position demandée. En règle générale, la position réelle n’est pas postérieure à la position demandée. Vous pouvez donc ignorer les échantillons jusqu’à ce que vous atteigniez le point souhaité dans le flux.

Taux de lecture

Bien que vous puissiez définir la vitesse de lecture à l’aide du lecteur source, cela n’est généralement pas très utile, pour les raisons suivantes :

  • Le lecteur source ne prend pas en charge la lecture inversée, même si la source multimédia le fait.
  • L’application contrôle les heures de présentation, de sorte que l’application peut implémenter une lecture rapide ou lente sans définir le débit sur la source.
  • Certaines sources multimédias prennent en charge le mode d’amincissement , où la source fournit moins d’exemples, généralement uniquement les images clés. Toutefois, si vous souhaitez supprimer des images non clés, vous pouvez case activée chaque exemple pour l’attribut MFSampleExtension_CleanPoint.

Pour définir le taux de lecture à l’aide du lecteur source, appelez la méthode IMFSourceReader::GetServiceForStream pour obtenir les interfaces IMFRateSupport et IMFRateControl à partir de la source multimédia.

Accélération matérielle

Le lecteur source est compatible avec Microsoft DirectX Video Acceleration (DXVA) 2.0 pour le décodage vidéo accéléré matériel. Pour utiliser DXVA avec le lecteur source, procédez comme suit.

  1. Créez un appareil Microsoft Direct3D.
  2. Appelez la fonction DXVA2CreateDirect3DDeviceManager9 pour créer le gestionnaire de périphériques Direct3D. Cette fonction obtient un pointeur vers l’interface IDirect3DDeviceManager9 .
  3. Appelez la méthode IDirect3DDeviceManager9::ResetDevice avec un pointeur vers l’appareil Direct3D.
  4. Créez un magasin d’attributs en appelant la fonction MFCreateAttributes .
  5. Créez le lecteur source. Transmettez le magasin d’attributs dans le paramètre pAttributes de la fonction de création.

Lorsque vous fournissez un appareil Direct3D, le lecteur source alloue des exemples vidéo compatibles avec l’API de processeur vidéo DXVA. Vous pouvez utiliser le traitement vidéo DXVA pour effectuer le désentlacement matériel ou le mixage vidéo. Pour plus d’informations, consultez Traitement vidéo DXVA. En outre, si le décodeur prend en charge DXVA 2.0, il utilise l’appareil Direct3D pour effectuer un décodage à accélération matérielle.

Important

À partir de Windows 8, IMFDXGIDeviceManager peut être utilisé à la place de IDirect3DDeviceManager9. Pour les applications du Windows Store, vous devez utiliser IMFDXGIDeviceManager. Pour plus d’informations, consultez les API vidéo Direct3D 11.

 

Lecteur source