European ASP.NET MVC Hosting

BLOG about Latest ASP.NET MVC Hosting and Its Technology - Dedicated to European Windows Hosting Customer

ASP.NET MVC Hosting - HostForLIFEASP.NET :: Route Constraints in MVC

clock August 24, 2021 07:10 by author Peter

A few extra concepts of MVC routing. Routing is a great feature of MVC, it provides a REST based URL that is very easy to remember and improves page ranking in search engines.

This article is not an introduction to Routing in MVC, but we will learn a few features of routing and by implementing them we can develop a very flexible and user-friendly application. So, let's start without wasting valuable time.

Add constraint to URL
This is very necessary for when we want to add a specific constraint to our URL. Say, for example we want a URL like this.
http://www.myhost/myconstraint/controller/action

So, we want to set some constraint string after our host name. Fine, let's see how to implement it.

It's very simple to implement, just open the RouteConfig.cs file and you will find the routing definition in that. And modify the routing entry as in the following. We will see that we have added “abc” before.

Controller name, now when we browse we need to specify the string in the URL, as in the following:

Allow only Integer parameter in controller
Sometimes it is necessary to allow only an integer parameter in an application, to do that we can make a little modification in the routing. Have a look at the following routing entry.

We have specified a pattern for the “id” entry. And the pattern will only allow an integer parameter, not a string or other data type. In that manner we are specifying the URL.

And it's hitting our targeted action. If we define any other action with some other type id and try to execute that action then it will not call it at all. I am leaving this part for your R&D.

Allow only specific controller or Action

If we wish , we can allow a specific controller or specific action to execute. Again, we need to make a small change in the Route entry. Here is the modified root entry.

We will allow only those controllers whose name starts with “H” and only “index” or “Contact” actions are allowed to execute.

So, only the following combination will satisfy.
http://www.hostname/Hone/Index
or
http://www.hostname/Hone/Contact

I hope this article will provide a little extra knowledge of routing of MVC applications. It may provide a little more flexibility in your next applications. Happy reading.



ASP.NET MVC Hosting - HostForLIFEASP.NET :: Capture Video From ASP.NET Core 3.1 MVC Using WebRTC

clock August 9, 2021 07:07 by author Peter

This article introduces how to capture video with a webcam from an ASP.NET Core 3.1 MVC Application using WebRTC.

WebRTC (Web Real-Time Communication) is a free and open-source project which enables web applications and sites to capture and optionally stream audio and/or video media, as well as to exchange arbitrary data between browsers without requiring an intermediary. The technologies behind WebRTC are implemented as an open web standard and available as regular JavaScript APIs in all major browsers.

Let's follow the steps mentioned below to capture a video from a simple ASP.NET Core 3.1 MVC application.

Step 1
Create an ASP.NET Core 3.1 MVC Web Application.

Step 2
Now go to the Solution Explorer to add a new controller, right-click Controllers > Add > Controller, and give a suitable name like this.

The following is the code snippet for 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);
    }
}

Please note that SaveRecoredFile() action method will be used to store the recorded video into a folder and handle post requests.

Now Open the VideoController.cs > Set cursor inside the Index Action Method and right-click on inside the Index action method > click on Add View as follows and make sure to check the Use a Layout page option

After adding the view, let's add some HTML code and scripts. The following is the code snippet for the Index.cshtml View.

@{
    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>

Step 3
Now create a folder where the captured video files will be stored and add the below Video.css and Video.js file also in css and js folder under wwwroot folder like this.

The following is the code snippet for 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;
}

Here is the code snippet of Video.js. This script is used to capture the video and store it in the folder.
'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;
});

Step 4
Now add the below lines in _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>

The following is the complete code snippet of _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>

Now let's build and run the application to see the output.





About HostForLIFE

HostForLIFE is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2019 Hosting, ASP.NET 5 Hosting, ASP.NET MVC 6 Hosting and SQL 2019 Hosting.


Tag cloud

Sign in