在 JavaScript 中访问后置摄像头时出现黑屏问题

问题描述 投票:0回答:1

我正在开发一个 Web 应用程序,允许用户在其设备上的前置摄像头和后置摄像头之间切换。虽然前置摄像头工作正常,但我遇到了后置摄像头仅显示黑屏的问题。

camera.js
// wwwroot/camera.js

let currentStream = null;

async function openCamera(facingMode) {
    if (currentStream) {
        currentStream.getTracks().forEach(track => track.stop());
    }

    try {
        const constraints = {
            video: {
                facingMode: facingMode
            }
        };
        const stream = await navigator.mediaDevices.getUserMedia(constraints);
        currentStream = stream;
        document.getElementById('videoFeed').srcObject = stream;
        console.log(`Camera opened with facing mode: ${facingMode}`);
    } catch (err) {
        console.error('Error accessing camera: ', err);
    }
}

function stopCamera() {
    if (currentStream) {
        currentStream.getTracks().forEach(track => track.stop());
        currentStream = null;
    }
}


async function startVideo(src) {
    try {
        const permission = await checkCameraPermission();
        if (permission === 'prompt' || permission === 'granted') {
            navigator.getUserMedia(
                { video: true, audio: false },
                function (localMediaStream) {
                    let video = document.getElementById(src);
                    video.srcObject = localMediaStream;
                    video.onloadedmetadata = function (e) {
                        video.play();
                    };
                },
                function (err) {
                    console.error('Error accessing camera:', err);
                    throw err; // Propagate the error
                }
            );
        } else {
            console.error('Camera permission denied.');
        }
    } catch (error) {
        console.error('Error starting video:', error);
    }
}
function getFrame(src, dest, dotnetHelper) {
    let video = document.getElementById(src);
    let canvas = document.getElementById(dest);

    // Check if the video and canvas elements exist before drawing the image
    if (video && canvas) {
        canvas.getContext('2d').drawImage(video, 0, 0, 150, 150);

        // Resize the image on the canvas
        let resizedCanvas = document.createElement('canvas');
        resizedCanvas.width = 200; // Set desired width
        resizedCanvas.height = 200; // Set desired height
        let ctx = resizedCanvas.getContext('2d');
        ctx.drawImage(canvas, 0, 0, resizedCanvas.width, resizedCanvas.height);

        // Convert the resized image to base64 JPEG format
        let dataUrl = resizedCanvas.toDataURL("image/jpeg");

        // Invoke the .NET method with the resized image data
        dotnetHelper.invokeMethodAsync('ProcessImage', dataUrl);
    } else {
        console.error('Video or canvas element not found.');
    }
}


function stopVideo(src) {
    let video = document.getElementById(src);

    // Check if the video element exists before stopping
    if (video) {
        if ('srcObject' in video) {
            let tracks = video.srcObject.getTracks();
            tracks.forEach(track => track.stop());
            video.srcObject = null;
        } else {
            video.src = '';
        }
    } else {
        console.error('Video element with ID ' + src + ' not found.');
    }
}

function closeCamera() {
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
        navigator.mediaDevices.getUserMedia({ video: true })
            .then(function (stream) {
                // Stop all tracks
                let tracks = stream.getTracks();
                tracks.forEach(track => track.stop());
            })
            .catch(function (error) {
                console.error('Error closing the camera: ', error);
            });
    } else {
        console.error('getUserMedia is not supported on this browser.');
    }
}
Razorpage
@using MudBlazor
@inject IJSRuntime JSR
@inject ISnackbar Snackbar

<div>
    <MudDialog Style="overflow: hidden;">
        <DialogContent>
            <MudCard Class="pa-2">
                <MudCardContent>
                    <div style="display: flex; justify-content: center; align-items: center;">
                        <video id="videoFeed" width="600" height="300"></video>
                    </div>
                    <canvas class="d-none" id="currentFrame" width="150" height="150"></canvas>
                    <div style="display: flex; justify-content: center; margin-top: 16px;">
                        <MudIconButton Icon="@Icons.Material.Filled.CameraAlt" Color="Color.Primary" Size="Size.Large" OnClick="@Save" />
                        <MudButton OnClick="ToggleCamera" Color="Color.Primary">@buttonText</MudButton>
                        <MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Cancel" Size="Size.Large" OnClick="@Cancel" />
                    </div>
                </MudCardContent>
            </MudCard>
        </DialogContent>
    </MudDialog>
</div>

@code {
    [CascadingParameter] MudDialogInstance MudDialog { get; set; }
    [Parameter] public string ImageUri { get; set; }
    private bool isFrontCamera = true;
    private string buttonText = "Open Back Camera";
    private DotNetObjectReference<CameraDialog> oCounter;
    private string frameUri;

    private bool nestedVisible = false;
    private bool isVideoStarted = false;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            await JSR.InvokeVoidAsync("startVideo", "videoFeed");
          
            isVideoStarted = true;
        }
        catch (Exception ex)
        {
            Snackbar.Add("Camera access is denied. Please allow access to the camera.", Severity.Error);
        }
    }
    private async Task ToggleCamera()
    {
        if (isFrontCamera)
        {
            await OpenCamera("environment");
            buttonText = "Open Front Camera";
        }
        else
        {
            await OpenCamera("user");
            buttonText = "Open Back Camera";
        }
        isFrontCamera = !isFrontCamera;
    }

    private async Task OpenCamera(string facingMode)
    {
        await JSR.InvokeVoidAsync("openCamera", facingMode);
    }
    private async Task Save()
    {
        if (!isVideoStarted)
        {
            Snackbar.Add("Camera access is denied. Please allow access to the camera.", Severity.Error);
            MudDialog.Cancel();
            return;
        }

        if (oCounter == null)
            oCounter = DotNetObjectReference.Create(this);

        try
        {
            await JSR.InvokeAsync<string>("getFrame", "videoFeed", "currentFrame", oCounter);
            await JSR.InvokeVoidAsync("stopVideo", "videoFeed");
            isVideoStarted = false;
            MudDialog.Close(DialogResult.Ok(frameUri));
        }
        catch (Exception ex)
        {
            if (ex.Message.Contains("Cannot read properties of null (reading 'getTracks')"))
            {
                Snackbar.Add("Camera access is denied. Please give camera permission in the browser settings.", Severity.Error);
                MudDialog.Cancel();
            }
            else
            {
                Snackbar.Add($"An error occurred while capturing the image. Please try again. Error details: {ex.Message}", Severity.Error);
                MudDialog.Cancel();
            }
        }
    }

    [JSInvokable]
    public async Task ProcessImage(string imageString)
    {
        frameUri = imageString;
        StateHasChanged();

        var parameters = new DialogParameters { { "ImageUri", frameUri } };
        var options = new DialogOptions { FullWidth = true };
    }

    private async Task Cancel()
    {
        if (isVideoStarted)
        {
            try
            {
                await JSR.InvokeVoidAsync("stopVideo", "videoFeed");
                isVideoStarted = false;
                MudDialog.Cancel();
            }
            catch (Exception ex)
            {
                if (ex.Message.Contains("Cannot read properties of null (reading 'getTracks')"))
                {
                    MudDialog.Cancel();
                }
                else
                {
                    MudDialog.Cancel();
                }
            }
        }
    }
}

我的尝试 我尝试在停止当前流和启动新流之间添加延迟。我添加了控制台日志来调试流程,但仍然面临问题。预期行为 单击“旋转相机”按钮后,后置摄像头应立即激活,而不显示黑屏。

实际行为后置摄像头要么显示黑屏,要么需要多次单击才能激活。我正在多个设备上对此进行测试,并且该问题在不同的浏览器和设备上仍然存在。相机权限已正确授予。有任何帮助或建议吗?对于解决此问题的任何帮助或建议,我将不胜感激。谢谢!

javascript html asp.net-core-webapi blazor-webassembly
1个回答
0
投票

测试结果

enter image description here

测试代码

网站.css

.camera-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 16px;
}

.video-container {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    max-width: 600px;
    margin-bottom: 16px;
}

#videoFeed {
    width: 100%;
    height: auto;
}

.button-container {
    display: flex;
    justify-content: center;
    margin-bottom: 16px;
}

.images-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    max-width: 600px;
}

.captured-image {
    width: 100%;
    height: 280px;
    margin-bottom: 16px;
}

camera.js

let currentStream = null;

async function openCamera(facingMode) {
    if (currentStream) {
        currentStream.getTracks().forEach(track => track.stop());
        await new Promise(resolve => setTimeout(resolve, 500)); 
    }

    try {
        const constraints = {
            video: {
                facingMode: facingMode
            }
        };
        const stream = await navigator.mediaDevices.getUserMedia(constraints);
        currentStream = stream;
        document.getElementById('videoFeed').srcObject = stream;
        console.log(`Camera opened with facing mode: ${facingMode}`);
    } catch (err) {
        console.error('Error accessing camera: ', err);
    }
}

function stopCamera() {
    if (currentStream) {
        currentStream.getTracks().forEach(track => track.stop());
        currentStream = null;
    }
}

async function startVideo(src) {
    try {
        const permission = await checkCameraPermission();
        if (permission === 'prompt' || permission === 'granted') {
            navigator.mediaDevices.getUserMedia(
                { video: true, audio: false }
            ).then(function (localMediaStream) {
                let video = document.getElementById(src);
                video.srcObject = localMediaStream;
                video.onloadedmetadata = function (e) {
                    video.play();
                };
            }).catch(function (err) {
                console.error('Error accessing camera:', err);
                throw err; // Propagate the error
            });
        } else {
            console.error('Camera permission denied.');
        }
    } catch (error) {
        console.error('Error starting video:', error);
    }
}

function getFrame(src, dest, dotnetHelper) {
    let video = document.getElementById(src);
    let canvas = document.getElementById(dest);

    // Check if the video and canvas elements exist before drawing the image
    if (video && canvas) {
        canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);

        // Convert the canvas image to base64 JPEG format
        let dataUrl = canvas.toDataURL("image/jpeg");

        // Invoke the .NET method with the image data
        dotnetHelper.invokeMethodAsync('ProcessImage', dataUrl);
    } else {
        console.error('Video or canvas element not found.');
    }
}

function stopVideo(src) {
    let video = document.getElementById(src);

    // Check if the video element exists before stopping
    if (video) {
        if ('srcObject' in video) {
            let tracks = video.srcObject.getTracks();
            tracks.forEach(track => track.stop());
            video.srcObject = null;
        } else {
            video.src = '';
        }
    } else {
        console.error('Video element with ID ' + src + ' not found.');
    }
}

function closeCamera() {
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
        navigator.mediaDevices.getUserMedia({ video: true })
            .then(function (stream) {
                // Stop all tracks
                let tracks = stream.getTracks();
                tracks.forEach(track => track.stop());
            })
            .catch(function (error) {
                console.error('Error closing the camera: ', error);
            });
    } else {
        console.error('getUserMedia is not supported on this browser.');
    }
}

async function checkCameraPermission() {
    try {
        let stream = await navigator.mediaDevices.getUserMedia({ video: true });
        stream.getTracks().forEach(track => track.stop());
        return 'granted';
    } catch (err) {
        if (err.name === 'NotAllowedError') {
            return 'denied';
        } else if (err.name === 'NotFoundError') {
            return 'not found';
        } else {
            return 'prompt';
        }
    }
}

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>BlazorApp1</title>
    <base href="/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link rel="stylesheet" href="css/app.css" />
    <link rel="icon" type="image/png" href="favicon.png" />
    <link href="BlazorApp1.styles.css" rel="stylesheet" />
    <link href="css/site.css" rel="stylesheet" />
    <script src="camera.js"></script>
    <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
    <link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />

</head>

<body>
    <div id="app">
        <svg class="loading-progress">
            <circle r="40%" cx="50%" cy="50%" />
            <circle r="40%" cx="50%" cy="50%" />
        </svg>
        <div class="loading-progress-text"></div>
    </div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
</body>

</html>

相机.剃须刀

@page "/camera"
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime
@inject ISnackbar Snackbar

<div class="camera-container">
    <MudCard Class="pa-2">
        <MudCardContent>
            <div class="video-container">
                <video id="videoFeed" autoplay></video>
            </div>
            <canvas class="d-none" id="currentFrame"></canvas>
            <div class="button-container">
                <MudIconButton Icon="@Icons.Material.Filled.CameraAlt" Color="Color.Primary" Size="Size.Large" OnClick="Save" />
                <MudButton OnClick="ToggleCamera" Color="Color.Primary">@buttonText</MudButton>
                @* <MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Cancel" Size="Size.Large" OnClick="Cancel" /> *@
            </div>
            @if (capturedImages != null && capturedImages.Count > 0)
            {
                    <div class="images-container">
                    @foreach (var image in capturedImages)
                    {
                                <img src="@image" alt="Captured Image" class="captured-image" />
                    }
                    </div>
            }
        </MudCardContent>
    </MudCard>
</div>

@code {
    private bool isFrontCamera = true;
    private string buttonText = "Open Back Camera";
    private DotNetObjectReference<CameraDialog> oCounter;
    private List<string> capturedImages = new List<string>();
    private bool isVideoStarted = false;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            await JSRuntime.InvokeVoidAsync("startVideo", "videoFeed");
            isVideoStarted = true;
        }
        catch (Exception ex)
        {
            Snackbar.Add("Camera access is denied. Please allow access to the camera.", Severity.Error);
        }
    }

    private async Task ToggleCamera()
    {
        try
        {
            if (isFrontCamera)
            {
                await OpenCamera("environment");
                buttonText = "Open Front Camera";
            }
            else
            {
                await OpenCamera("user");
                buttonText = "Open Back Camera";
            }
            isFrontCamera = !isFrontCamera;
        }
        catch (Exception ex)
        {
            Snackbar.Add($"Error switching camera: {ex.Message}", Severity.Error);
        }
    }

    private async Task OpenCamera(string facingMode)
    {
        try
        {
            await JSRuntime.InvokeVoidAsync("openCamera", facingMode);
        }
        catch (Exception ex)
        {
            Snackbar.Add($"Error opening camera: {ex.Message}", Severity.Error);
        }
    }

    private async Task Save()
    {
        if (!isVideoStarted)
        {
            Snackbar.Add("Camera access is denied. Please allow access to the camera.", Severity.Error);
            return;
        }

        if (oCounter == null)
            oCounter = DotNetObjectReference.Create(new CameraDialog());

        try
        {
            await JSRuntime.InvokeAsync<string>("getFrame", "videoFeed", "currentFrame", oCounter);
            var cameraDialog = oCounter.Value;
            var imageUri = cameraDialog.CapturedImage;
            if (!string.IsNullOrEmpty(imageUri))
            {
                capturedImages.Add(imageUri);
                StateHasChanged();
            }
        }
        catch (Exception ex)
        {
            if (ex.Message.Contains("Cannot read properties of null (reading 'getTracks')"))
            {
                Snackbar.Add("Camera access is denied. Please give camera permission in the browser settings.", Severity.Error);
            }
            else
            {
                Snackbar.Add($"An error occurred while capturing the image. Please try again. Error details: {ex.Message}", Severity.Error);
            }
        }
    }

    private async Task Cancel()
    {
        if (isVideoStarted)
        {
            try
            {
                await JSRuntime.InvokeVoidAsync("stopVideo", "videoFeed");
                isVideoStarted = false;
            }
            catch (Exception ex)
            {
                // Handle cancellation errors
            }
        }
    }
}

CameraDialog.cs

using Microsoft.JSInterop;

namespace BlazorApp1
{
    public class CameraDialog
    {
        public string? CapturedImage { get; private set; }

        [JSInvokable]
        public Task ProcessImage(string imageString)
        {
            CapturedImage = imageString;
            return Task.CompletedTask;
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.