Обработка аудиокадров с помощью MediaFrameReader

В этой статье показано, как использовать класс MediaFrameReader и MediaCapture для аудиоданных от источника кадров мультимедиа. Сведения об использовании MediaFrameReader для получения данных изображений, таких как цветная, инфракрасная камера или камера глубины, см. в разделе Обработка кадров мультимедиа с помощью MediaFrameReader. Эта статья содержит общие сведения о шаблоне использования средства чтения кадров и описание некоторых дополнительных возможностей класса MediaFrameReader, таких как использование MediaFrameSourceGroup для получения кадров от нескольких источников в одно и то же время.

Примечание

Описанные в этой статье функции доступны, только начиная c Windows 10 версии 1803.

Примечание

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

Настройка проекта

Процесс получения аудиокадров во многом похож на получение других типов кадров мультимедиа. Как и в любом приложении, использующем MediaCapture, перед попыткой получить доступ к камере вам необходимо объявить, что ваше приложение использует возможность webcam. Если ваше приложение получает данные от звукового устройства, рекомендуется также объявить возможность устройства microphone.

Добавление возможностей в манифест приложения

  1. В Microsoft Visual Studio откройте конструктор манифеста приложения, дважды щелкнув элемент package.appxmanifest в Обозревателе решений.
  2. Перейдите на вкладку Возможности.
  3. Выставьте флажок для пункта Веб-камера и поле для параметра Микрофон.
  4. Для доступа к библиотеке изображений и видео установите флажки Библиотека изображений и Библиотека видео.

Выбор источников кадров и групп источников кадров

Первым шагом при захвате аудиокадров — инициализация объекта MediaFrameSource, представляющего источник звуковых данных, такой как микрофон или другое устройство для записи звука. Для этого необходимо создать новый экземпляр объекта MediaCapture. В этом примере единственным параметром инициализации для MediaCapture является параметр StreamingCaptureMode, который указывается, что мы хотим осуществить потоковую передачу звука с устройства записи.

После вызова метода MediaCapture.InitializeAsync можно получить список доступных источников кадров мультимедиа с помощью свойства FrameSources. В этом примере используется запрос Linq для выбора всех источников кадров, где mediaFrameSourceInfo , описывающий источник кадров, имеет тип MediaStreamType со значениемAudio, указывающее, что источник мультимедиа создает звуковые данные.

Если запрос возвращает один или несколько источников кадров, вы можете просмотреть свойство CurrentFormat, чтобы узнать, поддерживает ли источник требуемый формат звука (в данном примере — аудио без сжатия). Проверьте свойство AudioEncodingProperties, чтобы убедиться, что источник поддерживает требуемое кодирование.

mediaCapture = new MediaCapture();
MediaCaptureInitializationSettings settings = new MediaCaptureInitializationSettings()
{
    StreamingCaptureMode = StreamingCaptureMode.Audio,
};
await mediaCapture.InitializeAsync(settings);

var audioFrameSources = mediaCapture.FrameSources.Where(x => x.Value.Info.MediaStreamType == MediaStreamType.Audio);

if (audioFrameSources.Count() == 0)
{
    Debug.WriteLine("No audio frame source was found.");
    return;
}

MediaFrameSource frameSource = audioFrameSources.FirstOrDefault().Value;

MediaFrameFormat format = frameSource.CurrentFormat;
if (format.Subtype != MediaEncodingSubtypes.Float)
{
    return;
}

if (format.AudioEncodingProperties.ChannelCount != 1
    || format.AudioEncodingProperties.SampleRate != 48000)
{
    return;
}

Создание и запуск MediaFrameReader

Получите новый экземпляр MediaFrameReader, вызвав метод MediaCapture.CreateFrameReaderAsync и передав объект MediaFrameSource, выбранный на предыдущем шаге. По умолчанию аудиокадры извлекаются в режиме буферизации, что снижает вероятность потери кадров, хотя это по-прежнему может произойти, если аудиокадры обрабатываются недостаточно быстро и заполняют доступный буфер памяти системы.

Зарегистрируйте обработчик для события MediaFrameReader.FrameArrived, которое система вызывает при поступлении нового кадра аудиоданных. Вызовите метод StartAsync, чтобы начать получать аудиокадры. Если средство чтения кадров не запускается, возвращенное значение состояния будет отличаться от Success.

mediaFrameReader = await mediaCapture.CreateFrameReaderAsync(frameSource);

// Optionally set acquisition mode. Buffered is the default mode for audio.
mediaFrameReader.AcquisitionMode = MediaFrameReaderAcquisitionMode.Buffered;

mediaFrameReader.FrameArrived += MediaFrameReader_AudioFrameArrived;

var status = await mediaFrameReader.StartAsync();

if (status != MediaFrameReaderStartStatus.Success)
{
    Debug.WriteLine("The MediaFrameReader couldn't start.");
}

В обработчике событий FrameArrived вызовите метод TryAcquireLatestFrame для объекта MediaFrameReader, который передается обработчику в качестве отправителя, чтобы попытаться получить ссылку на последний кадр мультимедиа. Обратите внимание, что этот объект может иметь значение null, поэтому всегда следует это проверять перед использованием объекта. Тип кадра мультимедиа в объекте MediaFrameReference, возвращенном TryAcquireLatestFrame, зависит от того, какой тип источника или источников кадров выбран в средстве чтения кадров. Так как средство чтения кадров в этом примере было настроено для получения аудиокадров, оно получает базовый кадр с помощью свойства AudioMediaFrame.

Во вспомогательном методе ProcessAudioFrame в примере ниже показано, как получить объект AudioFrame, который предоставляет такие сведения, как метка времени кадра и отсутствие связи с объектом AudioMediaFrame. Чтобы прочитать или обработать аудиоданные, необходимо получить объект AudioBuffer из объекта AudioMediaFrame, создать IMemoryBufferReference и вызвать COM-метод IMemoryBufferByteAccess::GetBuffer для извлечения данных. Дополнительные сведения о доступе к собственным буферам см. в примечании под примером кода.

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

private void MediaFrameReader_AudioFrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args)
{
    using (MediaFrameReference reference = sender.TryAcquireLatestFrame())
    {
        if (reference != null)
        {
            ProcessAudioFrame(reference.AudioMediaFrame);
        }
    }
}
unsafe private void ProcessAudioFrame(AudioMediaFrame audioMediaFrame)
{

    using (AudioFrame audioFrame = audioMediaFrame.GetAudioFrame())
    using (AudioBuffer buffer = audioFrame.LockBuffer(AudioBufferAccessMode.Read))
    using (IMemoryBufferReference reference = buffer.CreateReference())
    {
        byte* dataInBytes;
        uint capacityInBytes;
        float* dataInFloat;


        ((IMemoryBufferByteAccess)reference).GetBuffer(out dataInBytes, out capacityInBytes);
        
        // The requested format was float
        dataInFloat = (float*)dataInBytes;

        // Get the number of samples by multiplying the duration by sampling rate: 
        // duration [s] x sampling rate [samples/s] = # samples 

        // Duration can be gotten off the frame reference OR the audioFrame
        TimeSpan duration = audioMediaFrame.FrameReference.Duration;

        // frameDurMs is in milliseconds, while SampleRate is given per second.
        uint frameDurMs = (uint)duration.TotalMilliseconds;
        uint sampleRate = audioMediaFrame.AudioEncodingProperties.SampleRate;
        uint sampleCount = (frameDurMs * sampleRate) / 1000;

    }
}

Примечание

Для работы с аудиоданными необходимо получить доступ к собственному буферу памяти. Для этого нужно использовать COM-интерфейс IMemoryBufferByteAccess, добавив следующий код. Операции с собственным буфером должны выполняться в методе, который использует ключевое слово unsafe. Кроме того, необходимо проверка поле, чтобы разрешить небезопасный код на вкладке Сборка диалогового окна Проект —> свойства.

[ComImport]
[Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe interface IMemoryBufferByteAccess
{
    void GetBuffer(out byte* buffer, out uint capacity);
}

Дополнительные сведения об использовании MediaFrameReader с аудиоданными

Вы можете получить объект AudioDeviceController, связанный с источником аудиокадра, с помощью свойства MediaFrameSource.Controller. Этот объект можно использовать для получения или установки свойств потока устройства записи или для управления уровнем записи. Следующий пример отключает звуковое устройство, чтобы средство чтения кадров по-прежнему могло получать кадры, но все выборки имеют значение 0.

audioDeviceController.Muted = true;

Вы можете использовать объект AudioFrame для передачи аудиоданных, записанных источником кадров мультимедиа объекту AudioGraph. Передайте кадр в методу AddFrame объекта AudioFrameInputNode. Дополнительные сведения об использовании аудиографов для записи, обработки и смешивания звуковых сигналов см. в разделе Звуковые графы.