共用方式為


使用 MediaCapture 進行基本相片、視訊和音訊的擷取

本文說明使用 MediaCapture 類別擷取相片和影片的最簡單方式。 MediaCapture 類別會公開一組健全的 API,提供對擷取管線的低階控制,並啟用進階擷取案例,但本文旨在協助您快速且輕鬆地將基本媒體擷取新增至您的應用程式。 若要深入瞭解 MediaCapture 提供的功能,請參閱相機

如果您只想擷取相片或影片,而不想新增任何其他媒體擷取功能,或不想建立自己的相機 UI,您可能想要使用 CameraCaptureUI 類別,這可讓您直接啟動 Windows 內建相機應用程式,並接收所擷取的相片或影片檔案。 有關詳細資訊,請參閱使用 Windows 內建相機 UI 擷取相片和影片

本文中的程式碼是從相機入門套件範例改編的。 您可以下載範例以查看內容中使用的程式碼,或使用該範例做為您自己的應用程式的起點。

將功能宣告新增至應用程式資訊清單中

為了讓您的應用程式存取裝置的相機,您必須宣告應用程式使用網路相機麥克風裝置功能。 如果您想要將擷取的相片和影片儲存至使用者的圖片或影片媒體櫃,您也必須宣告 picturesLibraryvideosLibrary 功能。

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

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

初始化 MediaCapture 物件

本文中所述的所有擷取方法都需要第一步,透過呼叫建構函式,然後呼叫 InitializeAsync 來初始化 MediaCapture 物件。 由於 MediaCapture 物件將從您應用程式中的多個位置存取,因此請宣告類別變數來保存物件。 為 MediaCapture物件的 Failed 事件實作一個處理常式,以便在擷取操作失敗時收到通知。

MediaCapture mediaCapture;
bool isPreviewing;
mediaCapture = new MediaCapture();
await mediaCapture.InitializeAsync();
mediaCapture.Failed += MediaCapture_Failed;

注意

Windows 可讓使用者在 [隱私權與安全性 -> 相機] 底下的 [Windows 設定] 應用程式中授與或拒絕裝置相機的存取權。 初始化擷取裝置時,應用程式應該檢查他們是否可以存取相機,並處理使用者拒絕存取的情況。 如需詳細資訊,請參閱 處理 Windows 相機隱私權設定

設定相機預覽

可以使用 MediaCapture 擷取相片、影片和音訊而不顯示相機預覽,但通常您希望顯示預覽串流,以便使用者可以看到正在擷取的內容。 此外,一些 MediaCapture 功能需要先執行預覽流才能啟用,包括自動對焦、自動曝光和自動白平衡。 若要查看如何設定相機預覽,請參閱顯示相機預覽

將相片擷取至 SoftwareBitmap

SoftwareBitmap 類別是在 Windows 10 中引進的,可提供跨多個功能之影像的常見表示法。 如果您想擷取相片,然後立即在應用程式中使用擷取的影像,例如在 XAML 中顯示,而不是擷取到檔案,那麼您應該擷取到 SoftwareBitmap。 您仍然可以選擇稍後將影像儲存到磁碟。

初始化 MediaCapture 物件後,您可以使用 LowLagPhotoCapture 類別將相片擷取到 SoftwareBitmap。 透過呼叫 PrepareLowLagPhotoCaptureAsync,並傳入指定所需影像格式的 ImageEncodingProperties 物件來取得此類別的執行個體。 CreateUncompressed 會使用指定的酵素格式建立未壓縮的編碼。 透過呼叫 CaptureAsync 來擷取相片,此方法會傳回 CapturedPhoto 物件。 透過存取 Frame 屬性,然後存取 SoftwareBitmap 屬性來取得 SoftwareBitmap

如有需要,您可以重複呼叫 CaptureAsync 來擷取多個相片。 當您完成擷取時,請呼叫 FinishAsync 來關閉 LowLagPhotoCapture 工作階段,並釋放相關聯的資源。 呼叫 FinishAsync 後,若要再次開始擷取相片,您需要在呼叫 CaptureAsync 之前再次呼叫 PrepareLowLagPhotoCaptureAsync,以重新初始化擷取工作階段。

// Prepare and capture photo
var lowLagCapture = await mediaCapture.PrepareLowLagPhotoCaptureAsync(ImageEncodingProperties.CreateUncompressed(MediaPixelFormat.Bgra8));

var capturedPhoto = await lowLagCapture.CaptureAsync();
var softwareBitmap = capturedPhoto.Frame.SoftwareBitmap;

await lowLagCapture.FinishAsync();

從 Windows 版本 1803 開始,您可以存取從 CaptureAsync 傳回的 CapturedFrame 類別的 BitmapProperties 屬性,以擷取有關擷取的相片的中繼資料。 您可以將此資料傳遞到 BitmapEncoder 以將中繼資料儲存到檔案中。 之前,無法存取未壓縮影像格式的這項資料。 您也可以存取 ControlValues 屬性以擷取描述已擷取畫面的控制項值 (例如曝光和白平衡) 之 CapturedFrameControlValues 物件。

有關使用 BitmapEncoder 和使用 SoftwareBitmap 物件的資訊 (包括如何在 XAML 頁面中顯示該物件),請參閱建立、編輯和儲存點陣圖影像

如需設定擷取裝置控制項值的詳細資訊,請參閱擷取相片和影片的裝置控制項

從 Windows 10 版本 1803 開始,您可以透過存取 MediaCapture 傳回的 CapturedFrameBitmapProperties 屬性來取得以未壓縮格式擷取的相片的中繼資料,例如 EXIF 資訊。 在舊版中,此資料只能在擷取到壓縮檔格式的相片標題中存取。 手動寫入影像檔案時,您可以將此資料提供至 BitmapEncoder。 如需編碼點陣圖的詳細資訊,請參閱建立、編輯和儲存點陣圖影像。 您也可以透過存取 ControlValues 屬性來存取擷取影像時使用的畫面控制項值,例如曝光和閃光設定。 如需詳細資訊,請參閱擷取相片和影片擷取的裝置控制項

將相片擷取至檔案

典型的攝影應用程式會將擷取的相片儲存到磁碟或雲端儲存體,而且必須將相片方向等中繼資料新增至檔案。 下列範例示範如何將相片擷取至檔案。 您仍然可以選擇稍後從影像檔案建立 SoftwareBitmap

此範例所示的技術會將相片擷取到記憶體內部串流,然後將相片從串流轉碼至磁碟上的檔案。 此範例使用 GetLibraryAsync 取得使用者的圖片媒體櫃,然後使用 SaveFolder 屬性取得參考預設儲存資料夾。 請記得將圖片媒體櫃功能新增至您的應用程式資訊清單,以存取此資料夾。 CreateFileAsync 能建立一個新的 StorageFile,相片將會儲存到其中。

建立 InMemoryRandomAccessStream,然後呼叫 CapturePhotoToStreamAsync 將相片擷取到串流中,傳入串流和指定應使用的影像格式的 ImageEncodingProperties 物件。 您可以透過自行初始化物件來建立自訂編碼屬性,但該類別提供靜態方法,例如用於常見編碼格式的 ImageEncodingProperties.CreateJpeg。 接下來,透過呼叫 OpenAsync 來建立到輸出檔案的檔案串流。 建立 BitmapDecoder 以解碼記憶體串流中的影像,然後建立 BitmapEncoder 以透過呼叫 CreateForTranscodingAsync 將影像編碼到檔案。

您可以選擇建立 BitmapPropertySet 物件,然後在影像編碼器上呼叫 SetPropertiesAsync 以在影像檔案中包含有關相片的中繼資料。 如需編碼屬性的詳細資訊,請參閱影像中繼資料。 正確處理裝置方向對於大部分攝影應用程式而言非常重要。 如需詳細資訊,請參閱使用 MediaCapture 處理裝置方向

最後,對編碼器物件呼叫 FlushAsync 將相片從記憶體內部串流轉碼到檔案。

var myPictures = await Windows.Storage.StorageLibrary.GetLibraryAsync(Windows.Storage.KnownLibraryId.Pictures);
StorageFile file = await myPictures.SaveFolder.CreateFileAsync("photo.jpg", CreationCollisionOption.GenerateUniqueName);

using (var captureStream = new InMemoryRandomAccessStream())
{
    await mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), captureStream);

    using (var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
    {
        var decoder = await BitmapDecoder.CreateAsync(captureStream);
        var encoder = await BitmapEncoder.CreateForTranscodingAsync(fileStream, decoder);

        var properties = new BitmapPropertySet {
            { "System.Photo.Orientation", new BitmapTypedValue(PhotoOrientation.Normal, PropertyType.UInt16) }
        };
        await encoder.BitmapProperties.SetPropertiesAsync(properties);

        await encoder.FlushAsync();
    }
}

如需使用檔案和資料夾的詳細資訊,請參閱檔案、資料夾和程式庫

擷取影片

使用 LowLagMediaRecording 類別將影片擷取快速新增到您的應用程式中。 首先,為物件宣告一個類別變數。

LowLagMediaRecording _mediaRecording;

接下來,建立一個用於儲存影片的 StorageFile。 請注意,若要儲存到使用者的影片庫 (如此範例所示),您必須將影片庫功能新增至您的應用程式資訊清單。 呼叫 PrepareLowLagRecordToStorageFileAsync 以初始化媒體錄製,傳入儲存檔案和指定影片編碼的 MediaEncodingProfile 物件。 此類別提供靜態方法,如 CreateMp4,用於建立通用影片編碼設定檔。

最後,呼叫 StartAsync 開始擷取影片。

var myVideos = await Windows.Storage.StorageLibrary.GetLibraryAsync(Windows.Storage.KnownLibraryId.Videos);
StorageFile file = await myVideos.SaveFolder.CreateFileAsync("video.mp4", CreationCollisionOption.GenerateUniqueName);
_mediaRecording = await mediaCapture.PrepareLowLagRecordToStorageFileAsync(
        MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto), file);
await _mediaRecording.StartAsync();

若要停止錄製影片,請呼叫 StopAsync

await _mediaRecording.StopAsync();

您可以繼續呼叫 StartAsyncStopAsync 來擷取其他影片。 當您完成擷取影片時,請呼叫 FinishAsync 以處置擷取工作階段並清除相關聯的資源。 在此呼叫之後,您必須再次呼叫 PrepareLowLagRecordToStorageFileAsync 以在呼叫 StartAsync 之前重新初始化擷取工作階段。

await _mediaRecording.FinishAsync();

擷取影片時,您應該為 MediaCapture 物件的 RecordLimitationExceeded 事件註冊處理常式,如果您超過單次錄製的限制 (目前為三個小時),作業系統將引發該事件。 在事件的處理常式中,您應該透過呼叫 StopAsync 來完成錄製。

mediaCapture.RecordLimitationExceeded += MediaCapture_RecordLimitationExceeded;
private async void MediaCapture_RecordLimitationExceeded(MediaCapture sender)
{
    await _mediaRecording.StopAsync();
    System.Diagnostics.Debug.WriteLine("Record limitation exceeded.");
}

播放和編輯已擷取的影片檔案

將影片擷取到檔案之後,您可能會想要載入檔案,並在應用程式的 UI 中播放。 您可以使用 MediaPlayerElement XAML 控制項和關聯的 MediaPlayer 來執行此操作。 有關在 XAML 頁面中播放媒體的資訊,請參閱使用 MediaPlayer 播放音訊和視訊

您也可以透過呼叫 CreateFromFileAsync 從影片檔案建立 MediaClip 物件。 MediaComposition 提供基本的影片編輯功能,例如排列 MediaClip 物件的序列、修剪影片長度、建立圖層、新增背景音樂和套用影片效果。 如需使用媒體組合的詳細資訊,請參閱媒體組合和編輯

暫停和恢復影片錄製

您可以暫停影片錄製,然後透過呼叫 PauseAsync 接著呼叫 ResumeAsync 來恢復錄製,而無需建立單獨的輸出檔案。

await _mediaRecording.PauseAsync(Windows.Media.Devices.MediaCapturePauseBehavior.ReleaseHardwareResources);
await _mediaRecording.ResumeAsync();

從 Windows 10 版本 1607 開始,您可以暫停影片錄製,並接收在錄製暫停之前擷取的最後一個畫面。 然後,您可以將此畫面疊加在相機預覽上,讓使用者在恢復錄製之前將相機與暫停的畫面對齊。 呼叫 PauseWithResultAsync 即可傳回 MediaCapturePauseResult 物件。 LastFrame 屬性是表示最後一個畫面的 VideoFrame 物件。 若要在 XAML 中顯示畫面,請取得影片畫面的 SoftwareBitmap 表示法。 目前,僅支援具有預乘或空 Alpha 色板的 BGRA8 格式的影像,因此如有必要,請呼叫 Convert 以獲得正確的格式。 建立一個新的 SoftwareBitmapSource 物件,並呼叫 SetBitmapAsync 對其進行初始化。 最後,設定 XAML Image 控制項的 Source 屬性以顯示影像。 若要使此技巧發揮作用,您的影像必須與 CaptureElement 控制項對齊,且不透明度值應小於 1。 不要忘記,您只能在 UI 執行緒上修改 UI,因此請在 RunAsync 內進行此呼叫。

PauseWithResultAsync 也會傳回在上一段錄製的視訊持續時間,以防您需要追蹤已錄製的總時間。

MediaCapturePauseResult result = 
    await _mediaRecording.PauseWithResultAsync(Windows.Media.Devices.MediaCapturePauseBehavior.RetainHardwareResources);

var pausedFrame = result.LastFrame.SoftwareBitmap;
if(pausedFrame.BitmapPixelFormat != BitmapPixelFormat.Bgra8 || pausedFrame.BitmapAlphaMode != BitmapAlphaMode.Ignore)
{
    pausedFrame = SoftwareBitmap.Convert(pausedFrame, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore);
}

var source = new SoftwareBitmapSource();
await source.SetBitmapAsync(pausedFrame);

await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
    PauseImage.Source = source;
    PauseImage.Visibility = Visibility.Visible;
});

_totalRecordedTime += result.RecordDuration;

當您繼續錄製時,您可以將影像的來源設定為 null 並加以隱藏。

await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
    PauseImage.Source = null;
    PauseImage.Visibility = Visibility.Collapsed;
});

await _mediaRecording.ResumeAsync();

請注意,您也可以透過呼叫 StopWithResultAsync 來取得停止影片時的結果畫面。

擷取音訊

您可以使用上面所示的相同技術來擷取影片,快速將音訊擷取新增至您的應用程式。 下面的範例在應用程式資料資料夾中建立一個 StorageFile。 呼叫 PrepareLowLagRecordToStorageFileAsync 以初始化擷取工作階段,傳入檔案和在此範例中由 CreateMp3 靜態方法產生的 MediaEncodingProfile。 若要開始錄製,請呼叫 StartAsync

mediaCapture.RecordLimitationExceeded += MediaCapture_RecordLimitationExceeded;

var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile file = await localFolder.CreateFileAsync("audio.mp3", CreationCollisionOption.GenerateUniqueName);
_mediaRecording = await mediaCapture.PrepareLowLagRecordToStorageFileAsync(
        MediaEncodingProfile.CreateMp3(AudioEncodingQuality.High), file);
await _mediaRecording.StartAsync();

呼叫 StopAsync 以停止音訊錄製。

await _mediaRecording.StopAsync();

您可以多次呼叫 StartAsyncStopAsync 來錄製數個音訊檔案。 當您完成擷取音訊時,請呼叫 FinishAsync 以處置擷取工作階段並清除相關聯的資源。 在此呼叫之後,您必須再次呼叫 PrepareLowLagRecordToStorageFileAsync 以在呼叫 StartAsync 之前重新初始化擷取工作階段。

await _mediaRecording.FinishAsync();

偵測並回應系統的音訊層級變更

從 Windows 10 版本 1803 開始,您的應用程式可以在系統降低,或將應用程式的音訊擷取和音訊轉譯串流的音訊層級降低或靜音時偵測到。 例如,當應用程式進入背景時,系統可能會將應用程式的串流靜音。 AudioStateMonitor 類別可讓您註冊,以在系統修改音訊串流音量時接收事件。 透過呼叫 CreateForCaptureMonitoring 來取得用於監視音訊擷取串流的 AudioStateMonitor 執行個體。 透過呼叫 CreateForRenderMonitoring 取得用於監視音訊轉譯串流的執行個體。 為每個監視器的 SoundLevelChanged 事件註冊一個處理常式,以便在系統變更對應串流類別的音訊時收到通知。

// Namespaces for monitoring audio state
using Windows.Media;
using Windows.Media.Audio;
AudioStateMonitor captureAudioStateMonitor;
AudioStateMonitor renderAudioStateMonitor;
captureAudioStateMonitor = AudioStateMonitor.CreateForCaptureMonitoring();
captureAudioStateMonitor.SoundLevelChanged += CaptureAudioStateMonitor_SoundLevelChanged; ;

renderAudioStateMonitor = AudioStateMonitor.CreateForRenderMonitoring();
renderAudioStateMonitor.SoundLevelChanged += RenderAudioStateMonitor_SoundLevelChanged; ;

在擷取串流的 SoundLevelChanged 處理常式中,您可以檢查 AudioStateMonitor 傳送者的 SoundLevel 屬性以決定新的聲音等級。 請注意,系統不應該降低擷取串流或「躲避」。 只能將其靜音或切換回最大音量。 如果音訊串流已靜音,您可以停止進行中的擷取。 如果音訊串流恢復到最大音量,您可以再次開始擷取。 以下範例使用一些布林類別變數來追蹤應用程式目前是否正在擷取音訊,以及擷取是否會因音訊狀態變更而停止。 這些變數用於確定何時適合以程式設計方式停止或開始音訊擷取。

bool isCapturingAudio = false;
bool capturingStoppedForAudioState = false;
private void CaptureAudioStateMonitor_SoundLevelChanged(AudioStateMonitor sender, object args)
{
    switch (sender.SoundLevel)
    {
        case SoundLevel.Full:
            if(capturingStoppedForAudioState)
            {
                StartAudioCapture();
                capturingStoppedForAudioState = false;
            }  
            break;
        case SoundLevel.Muted:
            if(isCapturingAudio)
            {
                StopAudioCapture();
                capturingStoppedForAudioState = true;
            }
            break;
        case SoundLevel.Low:
            // This should never happen for capture
            Debug.WriteLine("Unexpected audio state.");
            break;
    }
}

以下程式碼範例說明了用於音訊轉譯的 SoundLevelChanged 處理常式的實作。 根據您的應用程式案例以及您正在播放的內容類型,您可能希望在聲音等級降低時暫停音訊播放。 如需處理媒體播放音效等級變更的詳細資訊,請參閱使用 MediaPlayer 播放音訊和視訊

private void RenderAudioStateMonitor_SoundLevelChanged(AudioStateMonitor sender, object args)
{
    if ((sender.SoundLevel == SoundLevel.Full) ||
  (sender.SoundLevel == SoundLevel.Low && !isPodcast))
    {
        mediaPlayer.Play();
    }
    else if ((sender.SoundLevel == SoundLevel.Muted) ||
         (sender.SoundLevel == SoundLevel.Low && isPodcast))
    {
        // Pause playback if we’re muted or if we’re playing a podcast and are ducked
        mediaPlayer.Pause();
    }
}