共用方式為


使用 MediaFrameReader 處理音訊畫面

本文說明如何使用 MediaFrameReader 搭配 MediaCapture 從媒體畫面來源取得音訊資料。 若要瞭解如何使用 MediaFrameReader 來取得影像資料,例如從色彩、紅外線或深度相機,請參閱使用 MediaFrameReader 處理媒體畫面。 本文提供畫面讀取器使用模式的一般概觀,並討論 MediaFrameReader 類別的一些其他功能,例如使用 MediaFrameSourceGroup 同時從多個來源擷取畫面。

注意

本文討論的功能僅從 Windows 10 版本 1803 開始可用。

注意

有一個通用 Windows 應用程式範例示範如何使用 MediaFrameReader 來顯示來自不同畫面來源的畫面,包括色彩、深度和紅外相機。 如需詳細資訊,請參閱ClaimsAwareWebService

設定您的專案

取得音訊畫面的程序與取得其他類型的媒體畫面大致相同。 如同任何使用 MediaCapture 的應用程式,您必須先宣告您的應用程式使用網路攝影機功能,才能嘗試存取任何攝影機裝置。 如果您的應用程式會從音訊裝置擷取,您也應該宣告麥克風裝置功能。

將功能新增到應用程式資訊清單

  1. 在 Microsoft Visual Studio 的 [方案總管] 中,按兩下 package.appxmanifest 項目,開啟應用程式資訊清單的設計工具。
  2. 選取 [功能] 索引標籤。
  3. 核取 [網路相機] 方塊和 [麥克風] 方塊。
  4. 若要存取圖片和影片媒體櫃,請勾選圖片媒體櫃的方塊和影片媒體櫃的方塊。

選取畫面來源和畫面來源群組

擷取音訊畫面的第一個步驟是初始化 MediaFrameSource,此來源代表音訊資料的來源,例如麥克風或其他音訊擷取裝置。 若要這樣做,您必須建立 MediaCapture 物件的新執行個體。 在此範例中,MediaCapture 的唯一初始化設定是將 StreamingCaptureMode 設定為指出我們想要從擷取裝置串流音訊。

呼叫 MediaCapture.InitializeAsync 之後,您可以使用 FrameSources 屬性取得可存取的媒體畫面來源清單。 此範例使用 Linq 查詢來選擇所有畫面來源,其中描述畫面來源的 MediaFrameSourceInfoMediaStreamTypeAudio,指示媒體源產生音訊資料。

如果查詢傳回一或多個畫面來源,您可以檢查 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

透過呼叫 MediaCapture.CreateFrameReaderAsync 並傳遞您在上一個步驟中選取的 MediaFrameSource 物件,以取得 MediaFrameReader 的新執行個體。 預設情況下,音訊畫面是在緩衝模式下獲取的,因此畫面丟失的可能性較小,但如果您處理音訊畫面的速度不夠快並填滿系統分配的記憶體緩衝區,這種情況仍然可能發生。

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 事件處理常式中,呼叫 MediaFrameReader 物件上的 TryAcquireLatestFrame,做為傳送者傳遞至處理常式,以嘗試擷取最新媒體畫面的參考。 請注意,這個物件可以是 Null,因此您應該一律先檢查,再使用物件。 從 TryAcquireLatestFrame 傳回之 MediaFrameReference 中包裝的媒體畫面類型取決於您設定畫面讀取器取得的畫面來源或來源類型。 由於此範例中的畫面讀取器已設定為取得音訊畫面,因此它會使用 AudioMediaFrame 屬性取得基礎畫面。

下列範例中的這個 ProcessAudioFrame 協助程式方法示範如何取得 AudioFrame,其提供畫面時間戳記等資訊,以及它是否與 AudioMediaFrame 物件不連續。 若要讀取或處理音訊範例資料,您必須從 AudioMediaFrame 物件取得 AudioBuffer 物件、建立 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;

    }
}

注意

若要對音訊資料執行作業,您必須存取原生記憶體緩衝區。 若要這樣做,您必須透過包含下面的程式碼來使用 IMemoryBufferByteAccess COM 介面。 原生緩衝區上的作業必須在使用 unsafe 關鍵字的方法中執行。 您也需要選取專案 >- 屬性對話方塊的建置索引標籤中的核取方塊以允許不安全程式碼。

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

搭配音訊資料使用 MediaFrameReader 的其他資訊

您可以透過存取 MediaFrameSource.Controller 屬性來擷取與音訊畫面來源關聯的 AudioDeviceController。 這個物件可用來取得或設定擷取裝置的串流屬性,或控制擷取層級。 下列範例會將音訊裝置設為靜音,讓畫面繼續由畫面讀取器取得,但所有樣本的值為 0。

audioDeviceController.Muted = true;

您可以使用 AudioFrame 物件將媒體畫面來源擷取的音訊資料傳遞到 AudioGraph。 將畫面傳遞到 AudioFrameInputNodeAddFrame 方法中。 有關使用音訊圖擷取、處理和混合音訊訊號的更多資訊,請參閱音訊圖