Receive video from a source, preprocess and stream live preview to the client | ASP.NET Core - asp.net-core

I need to implement a server that gets video from some source for example IPCamera
then preprocess the image and streams it down to the client (if requested).
I already implemented the part with processing, it accepts a single frame (bitmap) and returns the processed bitmap. What I'm struggling with is the part of receiving video from the camera and then streaming it to the client.
What would be the right way to do it?
What libraries do you recommend using?
I use ASP.NET Core for the Server part, and Angular/React for the Client.
I tried to implement gRPC but a gRPC-Web client for typescript seems to be a pain in the ass.
Edit: 02.08.2022
What I achieved so far:
I figured out how to receive image output from the camera.
I found out RTSP Client for C#. Source: C# RTSP Client for .NET
It works pretty fine. I can receive output with small to no delay, and I use my phone to emulate the RTSP camera/server.
So RTSP Client receives raw frames (in my case H.264 IFrame/PFrame). The problem is I need to decode those frames preferably to Bitmap because I use YoloV4 ONXX Model for object detection.
Here's how I set up YoloV4 with ML.Net. Source: Machine Learning with ML.NET – Object detection with YOLO
To decode raw frames I use FFMpeg (sadly I didn't find any working FFMpeg package that would work with .NET Core, I tried AForge.Net, Accord but in both packages, the FFMPEG namespace is missing after installing for some reason, so I dug through Github and took this project FrameDecoderCore). It's not the best solution but it works. Now I can receive the output and decode it to Bitmap.
Now I'm facing three major issues:
How to detect objects without delaying the process of receiving camera output. And how to properly build an onnx model just to predict without training.
How to convert processed bitmaps back to a video stream. I also need to be able to save part of it as a video file on disk (video format doesn't matter) whenever the desired object was detected.
How to stream processed or unprocessed output to the client when the client wants to see the camera output. - I'm thinking of gRPC here and sending bitmaps and then displaying it on HTML Canvas.
Here's how my service looks at the moment:
public class CCTVService : BackgroundService
{
private readonly RtspClient _rtspClient;
private readonly ILogger<CCTVService> _logger;
private const int streamWidth = 480;
private const int streamHeight = 640;
private static readonly FrameDecoder FrameDecoder = new FrameDecoder();
private static readonly FrameTransformer FrameTransformer = new FrameTransformer(streamWidth, streamHeight);
public CCTVService(ILogger<CCTVService> logger)
{
_logger = logger;
_rtspClient = new RtspClient(new ConnectionParameters(new Uri("rtsp://192.168.0.99:5540/ch0")));
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using (_rtspClient)
{
try
{
await _rtspClient.ConnectAsync(stoppingToken);
_logger.LogInformation("Connecting to RTSP");
}
catch(RtspClientException clientException)
{
_logger.LogError(clientException.Message);
//throw;
}
_rtspClient.FrameReceived += (obj, rawFrame) =>
{
if (rawFrame is not RawVideoFrame rawVideoFrame)
return;
var decodedFrame = FrameDecoder.TryDecode(rawVideoFrame);
if (decodedFrame == null)
return;
using var bitmap = FrameTransformer.TransformToBitmap(decodedFrame);
_logger.LogInformation($"Timestamp: {new DateTimeOffset(rawFrame.Timestamp).ToUnixTimeSeconds()} Timestamp-diff: {new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds() - new DateTimeOffset(rawFrame.Timestamp).ToUnixTimeSeconds()}");
// save bitmaps | Test
//var t = new Thread(() =>
//{
// using var bitmap = FrameTransformer.TransformToBitmap(decodedFrame);
// var name = "./test/" + new DateTimeOffset(rawFrame.Timestamp).ToUnixTimeMilliseconds().ToString() + " - " + new Random().NextInt64().ToString() + ".bmp";
// bitmap.Save(name);
//});
//t.Priority = ThreadPriority.Highest;
//t.Start();
};
try
{
await _rtspClient.ReceiveAsync(stoppingToken);
}
catch
{
// swallow
}
}
}
}

So I can't really help with part 2 and 3 of your question but with ML.NET, one of the things you might consider is batching the predictions. Instead of preprocessing them one at a time, you could collect 10-20 frames and then instead of using PredictionEngine, use the Transform passing it in an IDataView instead of a Bitmap.
Here are some samples of using ONNX models inside applications. The WPF sample might be of interest since it uses a webcam to capture inputs. I believe it uses the native Windows APIs, so different than how you'd do it for web but it might be worth looking at anyway.
https://github.com/dotnet/machinelearning-samples/tree/main/samples/csharp/end-to-end-apps/ObjectDetection-Onnx

Related

How to send data piece by piece using Flux?

I play with baeldung tutorial from github.
I wanted to see how a browser retrieves data piece by piece. So I added my simple controller method:
#GetMapping("/flux")
public Flux<Employee> getFlux() {
return Flux.fromIterable(employeeRepository.employeeData.values())
.delayElements(Duration.ofMillis(2_000))
.take(3);
}
But when I look at the browser network the data is retrieved in one chunk after 6 second delay.
How to do this correctly?
This is happening because you didn't mention Media type for the response.
Try this code.
#GetMapping(value = "/test",produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
public Flux getMapping() {
return Flux.interval(Duration.ofMillis(300)).map(f -> "HI");
}
Thanks,
Vimalesh

.Net Core Image Manipulation (Crop & Resize) / File Handling

I have spent the last 5 hours trying to find a feasible way to accomplish what seems to me quite an easy task if it was a previous version of the .NET family that I was working with:
Uploading a picture
Resizing & Cropping the picture
Saving the new picture into a directory
I have come accross to couple of libraries that are either in pre-release stage or in a not-complete stage.
Has anyone at all accomplished the above tasks without specifically including the System.Drawing namespace and/or adding a dependency for an earlier version of the .NET framework?
UPDATE on 08 / 08 / 2016
I ended up using System.Drawing something which is very annoying and disappointing. If you are developing a software used by thousands of developers, and if all these developers are relying on the components of this software, I believe, one cannot just come up with a new version, "sweet talk" about it during the conferences to show off your public speaking skills rather than giving a shit about your work and on one hand proudly hold in high esteem of it, and on the other, strip away the mostly used and demanded portions of it.
I do understand and appreciate with great excitement myself - of the new era of .net with the core framework - being a loyal asp dev since the first days of classic asp - however, to me, it is just an uncomplete product causing more frustrations and dissappointment than pleasure. When there are millions of websites in today's content-driven world, completely relying on content management, you can't just come up and say, "Hey, I have this brilliant, techonology, leaner, faster blah blah" but errr, you will have some problems with "managing" your content..
It should not be forgotten that, although, MS (and us) is very excited about this new core framework, with now being open source etc, there are other languages and frameworks out there that are doing what MS is promising to do, for a very very long time now.
ImageSharp
ImageSharp is a new, fully featured, fully managed, cross-platform, 2D graphics API.
Designed to democratize image processing, ImageSharp brings you an incredibly powerful yet beautifully simple API.
Compared to System.Drawing we have been able to develop something much more flexible, easier to code against, and much, much less prone to memory leaks. Gone are system-wide process-locks; ImageSharp images are thread-safe and fully supported in web environments.
Built against .Net Standard 1.1 ImageSharp can be used in device, cloud, and embedded/IoT scenarios.
You can use the Microsoft ASP.NET Core JavaScript Services to invoke arbitrary NPM packages at runtime from .NET code which means you can choose any npm package that provide image scaling and invoke it.
The following example shows how to use JavaScriptServices to resize image
https://github.com/aspnet/JavaScriptServices/tree/dev/samples/misc/NodeServicesExamples
Hope that Helps
.NET Core Image Processing blog post (January 19, 2017) compares 6 libraries:
CoreCompat.System.Drawing
ImageSharp
Magick.NET (Win only)
SkiaSharp
FreeImage-dotnet-core
MagicScaler
Feb 26 update: post was updated, two new packages added
To complete the #Hossam Barakat answer, you can use the Microsoft ASP.NET Core JavaScript Services to invoke arbitrary NPM packages at runtime from .NET code which means you can choose any npm package that provide image scaling and invoke it.
The sample use the sharp module, which has a lot of dependecies. If you prefere, like me, to use jimp which is pure javascript:
Startup.cs
public class Startup
{
...
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Enable Node Services
services.AddNodeServices();
...
}
...
}
ImageController.cs
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.NodeServices;
using Microsoft.AspNetCore.StaticFiles;
using System.Security.Cryptography;
using System.Text;
namespace NodeServicesExamples.Controllers
{
public class ResizeImageController : Controller
{
private const int MaxDimension = 1000;
private static string[] AllowedMimeTypes = new[] { "image/jpeg", "image/png", "image/gif" };
private IHostingEnvironment _environment;
private INodeServices _nodeServices;
public ResizeImageController(IHostingEnvironment environment, INodeServices nodeServices)
{
_environment = environment;
_nodeServices = nodeServices;
}
[Route("resize/{*imagePath}")]
[ResponseCache(Duration = 3600)]
public async Task<IActionResult> Index(string imagePath, double maxWidth, double maxHeight)
{
// Validate incoming params
if (maxWidth < 0 || maxHeight < 0 || maxWidth > MaxDimension || maxHeight > MaxDimension
|| (maxWidth + maxHeight) == 0)
{
return BadRequest("Invalid dimensions");
}
var mimeType = GetContentType(imagePath);
if (Array.IndexOf(AllowedMimeTypes, mimeType) < 0)
{
return BadRequest("Disallowed image format");
}
// Locate source image on disk
var fileInfo = _environment.WebRootFileProvider.GetFileInfo(imagePath);
if (!fileInfo.Exists)
{
return NotFound();
}
var eTag = GenerateETag(Encoding.UTF8.GetBytes($"{fileInfo.LastModified.ToString("s")}-{fileInfo.Length}"));
HttpContext.Response.Headers["ETag"] = eTag;
var match = HttpContext.Request.Headers["If-None-Match"].FirstOrDefault();
if (eTag == match)
{
return StatusCode(304);
}
// Invoke Node and pipe the result to the response
var imageStream = await _nodeServices.InvokeAsync<Stream>(
"./Node/resizeImage",
fileInfo.PhysicalPath,
mimeType,
maxWidth,
maxHeight);
return File(imageStream, mimeType, fileInfo.Name);
}
private string GetContentType(string path)
{
string result;
return new FileExtensionContentTypeProvider().TryGetContentType(path, out result) ? result : null;
}
private string GenerateETag(byte[] data)
{
string ret = string.Empty;
using (var md5 = MD5.Create())
{
var hash = md5.ComputeHash(data);
string hex = BitConverter.ToString(hash);
ret = hex.Replace("-", "");
}
return ret;
}
}
}
Node\resizeImage.js
var jimp = require("jimp");
module.exports = function (result, physicalPath, mimeType, maxWidth, maxHeight) {
// Invoke the 'jimp' NPM module, and have it pipe the resulting image data back to .NET
jimp.read(physicalPath).then(function (file) {
var width = maxWidth || jimp.AUTO;
var height = maxHeight || jimp.AUTO;
file.resize(maxWidth, height)
.getBuffer(mimeType, function (err, buffer) {
var stream = result.stream;
stream.write(buffer);
stream.end();
});
}).catch(function (err) {
console.error(err);
});
};
install jimp: npm install jimp --save
Short answer is no, not yet. Most if not all current libraries rely on System.Drawing. If you need this now, I would go that route and add System.Drawing.
The .NET team is currently working through the features that are missing on the Core 1.0 stack, but this one isn't high enough on their priority list: Link
This is a library to watch as they're getting very close to a releasable API without System.Drawing. : ImageSharp
use SkiSharp, it is on the official microsoft documentation also:
https://learn.microsoft.com/en-us/dotnet/api/skiasharp?view=skiasharp-1.68.0

How to capture and record video from webcam using JavaCV

I'm new to JavaCV and I have difficult time finding good tutorials about different issues on the topics that I'm interested in. I've succeed to implement some sort of real time video streaming from my webcam but the problem is that I use this code snippet which I found on the net :
#Override
public void run() {
FrameGrabber grabber = new VideoInputFrameGrabber(0); // 1 for next
// camera
int i = 0;
try {
grabber.start();
IplImage img;
while (true) {
img = grabber.grab();
if (img != null) {
cvFlip(img, img, 1);// l-r = 90_degrees_steps_anti_clockwise
cvSaveImage((i++) + "-aa.jpg", img);
// show image on window
canvas.showImage(img);
}
that results in multiple jpg files.
What I really want to do is capture my webcam input and along with showing it I want to save it in a proper video file. I find out about FFmpegFrameRecorder but don't know how to implement it. Also I've been wondering what are the different options for the format of the video file, because flv maybe would be more useful for me.
It's been quite a journey. Still a few things that I'm not sure what's the meaning behind them, but here is a working example for capturing and recording video from a webcam using JavaCV:
import com.googlecode.javacv.CanvasFrame;
import com.googlecode.javacv.FFmpegFrameRecorder;
import com.googlecode.javacv.OpenCVFrameGrabber;
import com.googlecode.javacv.cpp.avutil;
import com.googlecode.javacv.cpp.opencv_core.IplImage;
public class CameraTest {
public static final String FILENAME = "output.mp4";
public static void main(String[] args) throws Exception {
OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(0);
grabber.start();
IplImage grabbedImage = grabber.grab();
CanvasFrame canvasFrame = new CanvasFrame("Cam");
canvasFrame.setCanvasSize(grabbedImage.width(), grabbedImage.height());
System.out.println("framerate = " + grabber.getFrameRate());
grabber.setFrameRate(grabber.getFrameRate());
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(FILENAME, grabber.getImageWidth(),grabber.getImageHeight());
recorder.setVideoCodec(13);
recorder.setFormat("mp4");
recorder.setPixelFormat(avutil.PIX_FMT_YUV420P);
recorder.setFrameRate(30);
recorder.setVideoBitrate(10 * 1024 * 1024);
recorder.start();
while (canvasFrame.isVisible() && (grabbedImage = grabber.grab()) != null) {
canvasFrame.showImage(grabbedImage);
recorder.record(grabbedImage);
}
recorder.stop();
grabber.stop();
canvasFrame.dispose();
}
}
It was somewhat hard for me to make this work so in addition to those that may have the same issue, if you follow the official guide about how to setup JavaCV on Windows 7/64bit and want to capture video using the code above you should create a new directory in C:\ : C:\ffmpeg and extract the files from the ffmped release that you've been told to download in the official guide. Then you should add C:\ffmpeg\bin to your Enviorment variable PATH and that's all. About this step all credits go to karlphillip
and his post here

How to extract class IL code from loaded assembly and save to disk?

How would I go about extracting the IL code for classes that are generated at runtime by reflection so I can save it to disk? If at all possible. I don't have control of the piece of code that generates these classes.
Eventually, I would like to load this IL code from disk into another assembly.
I know I could serialise/deserialise classes but I wish to use purely IL code. I'm not fussed with the security implications.
Running Mono 2.10.1
Or better yet, use Mono.Cecil.
It will allow you to get at the individual instructions, even manipulating them and disassembling them (with the mono decompiler addition).
Note that the decompiler is a work in progress (last time I checked it did not fully support lambda expressions and Visual Basic exception blocks), but you can have pretty decompiled output in C# pretty easily as far as you don't hit these boundary conditions. Also, work has progressed since.
Mono Cecil in general let's you write the IL to a new assembly, as well, which you can then subsequently load into your appdomain if you like to play with bleeding edge.
Update I came round to trying this. Unfortunately I think I found what problem you run into. It turns out there is seems to be no way to get at the IL bytes for a generated type unless the assembly happened to get written out somewhere you can load it from.
I assumed you could just get the bits via reflection (since the classes support the required methods), however the related methods just raise an exception The invoked member is not supported in a dynamic module. on invocation. You can try this with the code below, but in short I suppose it means that it ain't gonna happen unless you want to f*ck with Marshal::GetFunctionPointerForDelegate(). You'd have to binary dump the instructions and manually disassemble them as IL opcodes. There be dragons.
Code snippet:
using System;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
using System.Reflection.Emit;
using System.Reflection;
namespace REFLECT
{
class Program
{
private static Type EmitType()
{
var dyn = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Emitted"), AssemblyBuilderAccess.RunAndSave);
var mod = dyn.DefineDynamicModule("Emitted", "Emitted.dll");
var typ = mod.DefineType("EmittedNS.EmittedType", System.Reflection.TypeAttributes.Public);
var mth = typ.DefineMethod("SuperSecretEncryption", System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.Static, typeof(String), new [] {typeof(String)});
var il = mth.GetILGenerator();
il.EmitWriteLine("Emit was here");
il.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
il.Emit(System.Reflection.Emit.OpCodes.Ret);
var result = typ.CreateType();
dyn.Save("Emitted.dll");
return result;
}
private static Type TestEmit()
{
var result = EmitType();
var instance = Activator.CreateInstance(result);
var encrypted = instance.GetType().GetMethod("SuperSecretEncryption").Invoke(null, new [] { "Hello world" });
Console.WriteLine(encrypted); // This works happily, print "Emit was here" first
return result;
}
public static void Main (string[] args)
{
Type emitted = TestEmit();
// CRASH HERE: even if the assembly was actually for SaveAndRun _and_ it
// has actually been saved, there seems to be no way to get at the image
// directly:
var ass = AssemblyFactory.GetAssembly(emitted.Assembly.GetFiles(false)[0]);
// the rest was intended as mockup on how to isolate the interesting bits
// but I didn't get much chance to test that :)
var types = ass.Modules.Cast<ModuleDefinition>().SelectMany(m => m.Types.Cast<TypeDefinition>()).ToList();
var typ = types.FirstOrDefault(t => t.Name == emitted.Name);
var operands = typ.Methods.Cast<MethodDefinition>()
.SelectMany(m => m.Body.Instructions.Cast<Instruction>())
.Select(i => i.Operand);
var requiredTypes = operands.OfType<TypeReference>()
.Concat(operands.OfType<MethodReference>().Select(mr => mr.DeclaringType))
.Select(tr => tr.Resolve()).OfType<TypeDefinition>()
.Distinct();
var requiredAssemblies = requiredTypes
.Select(tr => tr.Module).OfType<ModuleDefinition>()
.Select(md => md.Assembly.Name as AssemblyNameReference);
foreach (var t in types.Except(requiredTypes))
ass.MainModule.Types.Remove(t);
foreach (var unused in ass.MainModule
.AssemblyReferences.Cast<AssemblyNameReference>().ToList()
.Except(requiredAssemblies))
ass.MainModule.AssemblyReferences.Remove(unused);
AssemblyFactory.SaveAssembly(ass, "/tmp/TestCecil.dll");
}
}
}
If all you want is the IL for your User class, you already have it. It's in the dll that you compiled it to.
From your other assembly, you can load the dll with the User class dynamically and use it through reflection.
UPDATE:
If what you have is a dynamic class created with Reflection.Emit, you have an AssemblyBuilder that you can use to save it to disk.
If your dynamic type was instead created with Mono.Cecil, you have an AssemblyDefinition that you can save to disk with myAssemblyDefinition.Write("MyAssembly.dll") (in Mono.Cecil 0.9).

HttpWebRequest for ShoutCast on Windows Phone7

I tring to stream shoutcast stream in my window phone 7 app
I start an async HttpWebRequest like this
//Init Request
HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create("http://ACommonoShoutCastUrl:8000");
myHttpWebRequest.Headers["Icy-MetaData"] = "1";
myHttpWebRequest.UserAgent = "WinampMPEG/5.09";
myHttpWebRequest.AllowReadStreamBuffering = true;
//Call
// Create an instance of the RequestState and assign the previous myHttpWebRequest object to its request field.
RequestState myRequestState = new RequestState();
myRequestState.request = myHttpWebRequest;
// Start the asynchronous request.
IAsyncResult result = (IAsyncResult)myHttpWebRequest.BeginGetResponse(new AsyncCallback(RespCallBack), myRequestState);
The problem is that the CallBack->RespCallBack is never called...
This code worked for me normally in other environments but not on the phone...
I tired also to use WebClient that seems to stream data,
the problem in this case is that it never call the end OpenReadCompleted because of endelss shoutcast stream
Thanks for support
any help would be appreciated
SHOUTcast implements its own protocol so you can't directly access and play it. You can use DownloadStringAsync (you will need a WebClient instance for this) to download the PLS file and read the URL with the help of RegEx.
When you get the URL, you can read the raw audio data by implementing MediaStreamSource and then use a MediaElement to play the contents.
You can find a sample implementation of MediaStreamSource here.
I just put the following on a page and the callback was called in repsonse to the button click. (I set a break point on the throw statement and it was hit.)
private HttpWebRequest myHttpWebRequest;
public MainPage()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//Init Request
//The following URI was chosen at random
myHttpWebRequest = (HttpWebRequest)WebRequest.Create("http://yp.shoutcast.com/sbin/tunein-station.pls?id=1377200");
myHttpWebRequest.Headers["Icy-MetaData"] = "1";
myHttpWebRequest.UserAgent = "WinampMPEG/5.09";
myHttpWebRequest.AllowReadStreamBuffering = true;
// Start the asynchronous request.
myHttpWebRequest.BeginGetResponse(RespCallBack, myHttpWebRequest);
}
private void RespCallBack(IAsyncResult ar)
{
throw new NotImplementedException();
}
I am trying to implement the Shoutcast streaming to my MediaElement via my own MediaStreamSource class. I have a loop in which I am downloading data from Shoutcast server and then set the data to the MediaStreamSource class - this works not perfectly for yet, but I discovered another more important issue. I made a test. I have downloaded a stream to a mp3 (stream is in mp3) file, then put this file to my application and set it to my MediaStreamSource. Here's the code for this:
private void Button_Click(object sender, RoutedEventArgs e)
{
var assembly = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
var res = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Demo1.sample.mp3");
byte[] data = new byte[res.Length];
res.Read(data, 0, data.Length);
MemoryStream ms = new MemoryStream();
ms.Write(data, 0, data.Length);
ms.Position = 0;
ShoutcastMediaStreamSource ss = new ShoutcastMediaStreamSource(ms);
player.SetSource(ss);
}
my ShoutcastMediaStreamSource based on MenagedMediaHelpers. So when I put this stream to my ShoutcastMediaStreamSource in debbuger I can see that the method OpenMediaAsync() is called properly, then the GetSampleAsync() is called in a loop by MediaElement, here also everything is fine, but when I run this app there is no sound ! Neither on the emulator nor on the device, and there are no errors. I think, the GetSampleAsync() method is running too fast, bacause for the file (and also stream) duration is about 30 sec., and this app ends after ca. 10 sec. But nevertheless, there should be a sound (scratch).
BUT whats suprising - this app works in Silverlight as Web Page ! The music is playing. I am confused.
Here is allso an app
http://timheuer.com/blog/archive/2010/08/16/download-and-store-media-for-playback-in-windows-phone-7-using-mediastreamsource.aspx
and there is a comment:
If Mp3MediaStreamSource is set as a
source for MediaElement then
MediaElement doesn't play that file
and donesn't show any error in Windows
phone 7 sdk RTM version. In previsios
version it was working but it's not
working with Windows phone 7 sdk final
release.
The callback gets called if you disable read stream buffering:
webRequest.AllowReadStreamBuffering = false;