Search code examples
c#.net-6.0maui.net-mauimaui-windows

Maui media picker on Windows Platform error


I have created a very basic MauiApp because I wanted to try the MediaPicker on the Windows Platform.

Thus I copied the code from the official documentation and tried to run my application

However if I add <uap:Capability Name="webcam"/> to the Package.appxmanifest file as suggested in the documentaion, and run the application it gives me the following error:

Error       DEP0700: Registration of the app failed. [0x80080204] error 0xC00CE169: App 
manifest validation error: The app manifest must be valid as per schema: Line 39, Column 
21, Reason: 'webcam' violates enumeration constraint of 'documentsLibrary 
picturesLibrary videosLibrary musicLibrary enterpriseAuthentication 
sharedUserCertificates userAccountInformation removableStorage appointments contacts 
phoneCall blockedChatMessages objects3D voipCall chat'.
The attribute 'Name' with value 'webcam' failed to parse.   MauiApp3            
        

So in order to solve this problem I tried to change the capability from <uap:Capability Name="webcam"/> to <DeviceCapability Name="webcam"/>.

In this way I can run the application without errors, but photo is always null:

public async void TakePhoto(object sender, EventArgs e)
{
    if (MediaPicker.Default.IsCaptureSupported)
    {
        FileResult photo = await MediaPicker.Default.CapturePhotoAsync();
        
        if (photo != null)
        {
            // save the file into local storage
            string localFilePath = Path.Combine(FileSystem.CacheDirectory, photo.FileName);

            using Stream sourceStream = await photo.OpenReadAsync();
            using FileStream localFileStream = File.OpenWrite(localFilePath);

            await sourceStream.CopyToAsync(localFileStream);
        }
        else
        {
            // *** IT ALWAYS ENTERS IN THE ELSE CLAUSE ***
            // *** BECAUSE photo IS ALWAYS NULL ***
            CounterBtn.Text = $"Capture is supported but {photo} is null";
        }
    }
}

Note: The function above is called when I click to this button that I've defined in MainPage.xaml file:

        <Button 
            x:Name="ImageBtn"
            Text="Take Photo"
            SemanticProperties.Hint="Take Image"
            Clicked="TakePhoto"
            HorizontalOptions="Center" />

Solution

  • Unfortunately this is a old and known bug for WinUI3. microsoft/WindowsAppSDK#1034

    As a workaround you can create a custom mediapicker that inherit everything from the MAUI one for Android, iOS and Catalyst.

    Then implement a custom mediapicker for Windows, intheriting all the Capture... methods and reimplementing the CapturePhotoAsync using a custom class for camera capture:

    public async Task<FileResult> CaptureAsync(MediaPickerOptions options, bool photo)
    {
        var captureUi = new CameraCaptureUI(options);
    
        var file = await captureUi.CaptureFileAsync(photo ? CameraCaptureUIMode.Photo : CameraCaptureUIMode.Video);
    
        if (file != null)
            return new FileResult(file.Path,file.ContentType);
    
        return null;
    }
    

    This is the new CameraCaptureUI class:

    public async Task<StorageFile> CaptureFileAsync(CameraCaptureUIMode mode)
    {
        var extension = mode == CameraCaptureUIMode.Photo ? ".jpg" : ".mp4";
    
        var currentAppData = ApplicationData.Current;
        var tempLocation = currentAppData.LocalCacheFolder;
        var tempFileName = $"CCapture{extension}";
        var tempFile = await tempLocation.CreateFileAsync(tempFileName, CreationCollisionOption.GenerateUniqueName);
        var token = Windows.ApplicationModel.DataTransfer.SharedStorageAccessManager.AddFile(tempFile);
    
        var set = new ValueSet();
        if (mode == CameraCaptureUIMode.Photo)
        {
            set.Add("MediaType", "photo");
            set.Add("PhotoFileToken", token);
        }
        else
        {
            set.Add("MediaType", "video");
            set.Add("VideoFileToken", token);
        }
    
        var uri = new Uri("microsoft.windows.camera.picker:");
        var result = await Windows.System.Launcher.LaunchUriForResultsAsync(uri, _launcherOptions, set);
        if (result.Status == LaunchUriStatus.Success && result.Result != null)
        {
            return tempFile;
        }
    
        return null;
    }
    

    Also in the Package.appxmanifest add these capabilities:

    <DeviceCapability Name="microphone"/>
    <DeviceCapability Name="webcam"/>
    

    Credits for the CameraCapture class

    Edit: Modified code to allow video capturing, credits here