使用 WebRTC 从 ASP.NET Core 3.1 MVC 捕获视频

本文介绍如何使用 WebRTC 从 ASP.NET Core 3.1 MVC 应用程序使用网络摄像头捕获视频。

WebRTC(Web 实时通信)是一个免费的开源项目,它使 Web 应用程序和站点能够捕获和可选地流式传输音频和/或视频媒体,以及在浏览器之间交换任意数据而无需中介。WebRTC 背后的技术作为开放网络标准实施,并在所有主要浏览器中作为常规 JavaScript API 提供。

让我们按照下面提到的步骤从一个简单的 ASP.NET Core 3.1 MVC 应用程序中捕获视频。

步骤1

创建 ASP.NET Core 3.1 MVC Web 应用程序。

第2步

现在转到Solution Explorer添加一个新控制器,右键单击Controllers > Add > Controller,然后像这样给出一个合适的名称。

以下是VideoController.cs 的代码片段。

public class VideoController : Controller
{

    [HttpGet]
    public IActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public async Task<IActionResult> SaveRecoredFile()
    {
        if( Request.Form.Files.Any())
        {
            var file = Request.Form.Files["video-blob"];
            string UploadFolder = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "UploadedFiles");
            string UniqueFileName = Guid.NewGuid().ToString() + "_" + file.FileName+ ".webm";
            string UploadPath = Path.Combine(UploadFolder, UniqueFileName);
            await file.CopyToAsync(new FileStream(UploadPath, FileMode.Create));
        }
        return Json(HttpStatusCode.OK);
    }
}

请注意,  SaveRecoredFile () 操作方法将用于将录制的视频存储到文件夹中并处理发布请求。

现在打开VideoController.cs > 在Index Action Method内设置光标并在Index action 方法内右键单击> 单击Add View  ,确保选中 Use a Layout page 选项。

添加视图后,让我们添加一些 HTML 代码和脚本。以下是Index.cshtml视图的代码片段。

@{
    ViewData["Title"] = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div id="container">

      <h1>
            <a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">ASP.NET Core 3.1 samples</a>
            <span>Audio/Video Recording</span>
      </h1>
      <p>
            For more information see the MediaStream Recording API <a href="http://w3c.github.io/mediacapture-record/MediaRecorder.html"
                                                                      title="W3C MediaStream Recording API Editor's Draft">Editor's&nbsp;Draft</a>.
        </p>
      <video id="gum" playsinline autoplay muted style="border: 1px solid rgb(15, 158, 238); height: 240px; width: 320px;"></video>
      <video id="recorded" playsinline loop style="border: 1px solid rgb(15, 158, 238); height: 240px; width: 320px;"></video>
      <div>
          <button id="start" class="btn btn-success">Start camera</button>
          <button id="record" disabled class="btn btn-info">Start Recording</button>
          <button id="play" disabled class="btn btn-warning">Play</button>
          <button id="download" disabled class="btn btn-dark">Download</button>
          <button id="stop" class="btn btn-danger">Stop camera</button>
      </div>
      <div> Recording format: <select id="codecPreferences" disabled></select> </div>
      <div>
            <h4>Media Stream Constraints options</h4>
            <p>Echo cancellation: <input type="checkbox" id="echoCancellation"></p>
      </div>
      <div><span id="errorMsg"></span></div>
</div>

步骤 3

现在创建一个将存储捕获的视频文件的文件夹,并将下面的 Video.css 和 Video.js 文件也添加到wwwroot 文件夹下的css和 j s文件夹中。

以下是Video.css的代码片段

p.borderBelow {
    margin: 0 0 20px 0;
    padding: 0 0 20px 0;
}

video {
    vertical-align: top;
    --width: 25vw;
    width: var(--width);
    height: calc(var(--width) * 0.5625);
}

video:last-of-type {
    margin: 0 0 20px 0;
}

video#gumVideo {
    margin: 0 20px 20px 0;
}

这是 Video.js 的代码片段此脚本用于捕获视频并将其存储在文件夹中。

'use strict';

/* globals MediaRecorder */

let mediaRecorder;
let recordedBlobs;

const codecPreferences = document.querySelector('#codecPreferences');

const errorMsgElement = document.querySelector('span#errorMsg');
const recordedVideo = document.querySelector('video#recorded');
const recordButton = document.querySelector('button#record');
recordButton.addEventListener('click', () => {
    if (recordButton.textContent === 'Start Recording') {
        startRecording();
    } else {
        stopRecording();
        recordButton.textContent = 'Start Recording';
        playButton.disabled = false;
        downloadButton.disabled = false;
        codecPreferences.disabled = false;
    }
});

const playButton = document.querySelector('button#play');
playButton.addEventListener('click', () => {
    const mimeType = codecPreferences.options[codecPreferences.selectedIndex].value.split(';', 1)[0];
    const superBuffer = new Blob(recordedBlobs, { type: mimeType });
    recordedVideo.src = null;
    recordedVideo.srcObject = null;
    recordedVideo.src = window.URL.createObjectURL(superBuffer);
    recordedVideo.controls = true;
    recordedVideo.play();
});

const downloadButton = document.querySelector('button#download');
downloadButton.addEventListener('click', () => {
    const blob = new Blob(recordedBlobs, { type: 'video/webm' });
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.style.display = 'none';
    a.href = url;
    a.download = 'test.webm';
    document.body.appendChild(a);
    a.click();
    PostBlob(blob);
    setTimeout(() => {
        document.body.removeChild(a);
        window.URL.revokeObjectURL(url);
    }, 100);

});

function handleDataAvailable(event) {
    console.log('handleDataAvailable', event);
    if (event.data && event.data.size > 0) {
        recordedBlobs.push(event.data);
    }
}

function getSupportedMimeTypes() {
    const possibleTypes = [
        'video/webm;codecs=vp9,opus',
        'video/webm;codecs=vp8,opus',
        'video/webm;codecs=h264,opus',
        'video/mp4;codecs=h264,aac',
    ];
    return possibleTypes.filter(mimeType => {
        return MediaRecorder.isTypeSupported(mimeType);
    });
}

function startRecording() {
    recordedBlobs = [];
    const mimeType = codecPreferences.options[codecPreferences.selectedIndex].value;
    const options = { mimeType };

    try {
        mediaRecorder = new MediaRecorder(window.stream, options);
    } catch (e) {
        console.error('Exception while creating MediaRecorder:', e);
        errorMsgElement.innerHTML = `Exception while creating MediaRecorder: ${JSON.stringify(e)}`;
        return;
    }

    console.log('Created MediaRecorder', mediaRecorder, 'with options', options);
    recordButton.textContent = 'Stop Recording';
    playButton.disabled = true;
    downloadButton.disabled = true;
    codecPreferences.disabled = true;
    mediaRecorder.onstop = (event) => {
        console.log('Recorder stopped: ', event);
        console.log('Recorded Blobs: ', recordedBlobs);
    };
    mediaRecorder.ondataavailable = handleDataAvailable;
    mediaRecorder.start();
    console.log('MediaRecorder started', mediaRecorder);
}

function stopRecording() {
    mediaRecorder.stop();
}

function handleSuccess(stream) {
    recordButton.disabled = false;
    console.log('getUserMedia() got stream:', stream);
    window.stream = stream;

    const gumVideo = document.querySelector('video#gum');
    gumVideo.srcObject = stream;

    getSupportedMimeTypes().forEach(mimeType => {
        const option = document.createElement('option');
        option.value = mimeType;
        option.innerText = option.value;
        codecPreferences.appendChild(option);
    });
    codecPreferences.disabled = false;
}

function PostBlob(blob) {
    //FormData
    var formData = new FormData();
    formData.append('video-blob', blob);

    // POST the Blob
    $.ajax({
        type: 'POST',
        url: "Video/SaveRecoredFile",
        data: formData,
        cache: false,
        contentType: false,
        processData: false,
        success: function (result) {
            if (result) {
                console.log('Success');
            }
        },
        error: function (result) {
            console.log(result);
        }
    })
}

async function init(constraints) {
    try {
        const stream = await navigator.mediaDevices.getUserMedia(constraints);
        handleSuccess(stream);
    } catch (e) {
        console.error('navigator.getUserMedia error:', e);
        errorMsgElement.innerHTML = `navigator.getUserMedia error:${e.toString()}`;
    }
}

document.querySelector('button#start').addEventListener('click', async () => {
    document.querySelector('button#start').disabled = true;
    document.querySelector('button#stop').disabled = false;
    const hasEchoCancellation = document.querySelector('#echoCancellation').checked;
    const constraints = {
        audio: {
            echoCancellation: { exact: hasEchoCancellation }
        },
        video: {
            width: 1280, height: 720
        }
    };
    console.log('Using media constraints:', constraints);
    await init(constraints);
});

document.querySelector('button#stop').addEventListener('click',async () => {
    document.querySelector('button#stop').disabled = true;
    document.querySelector('button#start').disabled = false;
    const video = document.querySelector('video#gum');
    const mediaStream = video.srcObject;
    await mediaStream.getTracks().forEach(track => track.stop());
    video.srcObject = null;
});

步骤4

现在在 _Layout.cshtml 中添加以下行。

<link rel="stylesheet" href="~/css/Video.css" />
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="~/js/Video.js" asp-append-version="true" async></script>

以下是_Layout.cshtml 的完整代码片段。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Sample_Code</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
    <link rel="stylesheet" href="~/css/Video.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Sample_Code</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2021 - Sample_Code - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>

    <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
    <script src="~/js/Video.js" asp-append-version="true" async></script>
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true" ></script>

    @RenderSection("Scripts", required: false)

</body>
</html>

现在让我们构建并运行应用程序以查看输出。单击 “开始摄像头” 按钮并使用网络摄像头录制视频。

现在单击 下载按钮将 捕获的视频保存到 我们之前创建的wwwroot文件夹下的 UploadedFiles文件夹中。

概括

在这里,我们了解了如何逐步从简单的 ASP.NET Core 3.1 MVC 应用程序捕获视频。希望您喜欢这篇文章并从中获取一些信息。

参考

  • https://webrtc.github.io/samples/
  • 完整代码:https://github.com/ArnabMSDN/ASP.NET-Core-Sample

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

发表回复

登录后才能评论