How to broadcast media stream using SignalR - sockets

We are trying to build .net core app to share screen or window. But it doesn't work.
The code is like this:
Html file:
<div class="container">
<div class="row p-1">
<h1>WebRTC Screen Sharing Demo</h1>
<hr>
<button onclick="captureScreen()">Capture</button>
<video id="local-video" muted autoplay></video>
</div>
</div>
<script src="~/js/signalr/dist/browser/signalr.js"></script>
<script src="~/js/stream.js"></script>
Stream.js:
"use strict";
var connection = new signalR.HubConnectionBuilder().withUrl("/streamHub").build();
connection.start().then(function () {
console.log("Connection started");
}).catch(function (err) {
return console.error(err.toString());
});
connection.on("ClientReceiveStream", function (stream) {
document.getElementById("local-video").srcObject = stream; // I need to set the video source to the media stream.
});
async function captureScreen() {
let mediaStream = null;
try
{
mediaStream = await navigator.mediaDevices.getDisplayMedia({
video:
{
cursor: "always"
},
audio: false
});
//Im trying to send the media stream to the server
connection.invoke("ServerSendStream", mediaStream).catch(function (err)
{
return console.error(err.toString());
});
} catch (ex) {
console.log("Error occurred", ex);
}
}
The hub:
public class StreamHub : Hub
{
public async Task ServerSendStream(object mediaStream) // I need to get the media stream here
{
await Clients.All.SendAsync("ClientReceiveStream", mediaStream);
}
}
How can we send this mediaStream object to the server, and then broadcast it?

There is no way to broadcast a stream to a client. The client needs to ask the server for a stream of data.
See the docs on how to do this: https://learn.microsoft.com/aspnet/core/signalr/streaming?view=aspnetcore-7.0#server-to-client-streaming-2
And when consuming the data on the client-side, you'll need to create some sort of stream and add the data to it, probably similar to the example in https://developer.mozilla.org/en-US/docs/Web/API/MediaSource

Related

Blazor WASM Load Data before Render Page

I would like to load some Data before I Render my Blazor Application because in depndency to the loaded data I would like to render my app (layout, navbar ...)
Now I want to use the OnInitialised method instead of OnInitialisedAsync and with no async and await keywords.
But now I had a problem to convert the data which I get back from my API.
protected override void OnInitialized()
{
try
{ Console.WriteLine("Test1Mainasync");
LoadCategories();
}
catch (Exception e)
{
jsRuntime.ToastrError(e.Message);
}
}
private void LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = categorieService.GetAllCategories();
SD.Categories = CategoriesInit1.ToList();
//foreach(var categorie in CategoriesInit){
// SD.Categories.Append(categorie);
//}
Console.WriteLine("Test1Main");
}
Has someone an idea why this converting issues happen?
I think you have this method:
public async Task<IEnumerable<CategorieDTO>> GetAllCategories()
and you should call it this way:
private async Task LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = await categorieService.GetAllCategories();
and:
protected override async Task OnInitializedAsync()
{
try
{ Console.WriteLine("Test1Mainasync");
await LoadCategories();
}
Has someone an idea why this converting issues happen?
In your code CatagiesInit1 is a Task, it's not a List<CategorieDTO>. You only get the List<CategorieDTO> when the task completes which you have no control over as you don't await the completion of the Task. In all likelyhood, your sync code will run to completion before that happens.
If your CategoryService returns a Task then the code that handles it must be async code. You can't escape from the async world back into the sync world without consequencies. If you want to live in the sync world then all the data pipeline code also needs to be blocking sync code.
If I understand your comments correctly, you want nothing to render until a certain set of conditions are met. If so add some standard Loading... component code to the page if it's page specific or App.razor if it's on initial load, or say MainLayout if it's application wide.
Here's a quick an dirty example:
<Router AppAssembly="#typeof(App).Assembly">
<Found Context="routeData">
#if (Loaded)
{
<RouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)" />
<FocusOnNavigate RouteData="#routeData" Selector="h1" />
}
else
{
<div class="m-2 p-5 bg-secondary text-white">
<h3>Loading.....</h3>
</div>
}
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="#typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
#code {
private bool Loaded;
protected override async Task OnInitializedAsync()
{
Loaded = false;
// simulate getting the data first
await Task.Delay(5000);
Loaded = true;
}
}
Your call to API endpoint return an awaitable task but not the IEnumerable, So you can not assign awaitable task to IEnumerable so this piece of code wont work
private void LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = categorieService.GetAllCategories();
}
You should have your LoadCategories function like this
private async Task LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = await categorieService.GetAllCategories();
}
API calls should be awaitable, else it will stuck your UI
You can use this solution as well
private void LoadCategories()
{
var t = Task.Run(() => categorieService.GetAllCategories()()).GetAwaiter();
t.OnCompleted(() =>
{
CategoriesInit1 = t.GetResult();
// you may need to call statehaschanged as well
StateHasChanged();
});
}

Add multiple SPIFFS files to captive portal on Async web server on ESP32

I'm using simple code of modified Captive portal with asynchronous web portal (from ESPAsyncWebServer library - https://github.com/me-no-dev/ESPAsyncWebServer). It send html page from SPIFFS flash memory.
The way it is working now, is that it send index.html on any connection. I've just modified single line that in mentioned example was sending hmtl code. What I'd like to archive, is to be able to send more files, like html file and image.
So here is my code:
#include <DNSServer.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include "ESPAsyncWebServer.h"
#include <SPIFFS.h>
DNSServer dnsServer;
AsyncWebServer server(80);
class CaptiveRequestHandler : public AsyncWebHandler {
public:
CaptiveRequestHandler() {}
virtual ~CaptiveRequestHandler() {}
bool canHandle(AsyncWebServerRequest *request) {
//request->addInterestingHeader("ANY");
return true;
}
void handleRequest(AsyncWebServerRequest *request) {
request->send(SPIFFS, "/index.html", String(), false);
}
};
void setup() {
Serial.begin(115200);
if (!SPIFFS.begin()) {
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
WiFi.softAP("esp-captive");
dnsServer.start(53, "*", WiFi.softAPIP());
server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER);//only when requested from AP
server.on("/image1", HTTP_GET, [](AsyncWebServerRequest * request) {
request->send(SPIFFS, "/image1.jpg", "image/jpg"); // this part has been modified
});
server.begin();
}
void loop() {
dnsServer.processNextRequest();
}
I've tried to add
server.on("/image1", HTTP_GET, [](AsyncWebServerRequest * request) {
request->send(SPIFFS, "/image1.jpg", "image/jpg"); // this part has been modified
});
in setup section as explained here - https://randomnerdtutorials.com/display-images-esp32-esp8266-web-server/
But it's not working. I've tried messing with path changing "/" in places whare it appears, but with no luck. Further, if I change
void handleRequest(AsyncWebServerRequest *request) {
request->send(SPIFFS, "/index.html", String(), false);
}
to
void handleRequest(AsyncWebServerRequest *request) {
request->send(SPIFFS, "/image1.jpg", "image/jpg");
}
when logging to AP I get image not website, so I think paths are good.
To add more information this is my webpage code:
<!DOCTYPE html>
<html style="height: 100%">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body style="background-image: url('image1'); background-size: contain; background-color: black; background-repeat: no-repeat; background-position: 50% 0%; height=100%">
<h1 style="color:white">ESP32</h1>
</body>
</html>
and it is working fine on non-captive_portal solution (as explaind in previously mentioned tutorial).
So my question is how can I get to load not only single file on captive portal in asynchronous webserver, but more complicated (still very simple) webpage?
I struggeled with it for a while. But here is the answer. On the CaptiveRequestHandler() {} you can place your http calls.
Here is an example for you:
class CaptiveRequestHandler : public AsyncWebHandler {
public:
CaptiveRequestHandler() {
/* THIS IS WHERE YOU CAN PLACE THE CALLS */
server.onNotFound([](AsyncWebServerRequest *request){
AsyncWebServerResponse* response = request->beginResponse(SPIFFS, "/NotFound.html", "text/html");
request->send(response);
});
server.on("/Bootstrap.min.css", HTTP_GET, [](AsyncWebServerRequest *request) {
AsyncWebServerResponse* response = request->beginResponse(SPIFFS, "/Bootstrap.min.css", "text/css");
request->send(response);
});
}
virtual ~CaptiveRequestHandler() {}
bool canHandle(AsyncWebServerRequest *request) {
//request->addInterestingHeader("ANY");
return true;
}
void handleRequest(AsyncWebServerRequest *request) {
request->send(SPIFFS, "/index.html", String(), false);
}
};

Calling Apex Class from Lwc only it is getting Saved

I have create one custom object. Using a LWC component, I try to create one record but when try to save it from apex, only ID is getting printed not the Name.
I am not getting why only Id is getting printed not the name.
Could anybody please help me ? Would be Appreciable.
LWC Component
import { LightningElement, track, api } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import insertDe from '#salesforce/apex/insertEvent.insertDe';
import Detail_OBJECT from '#salesforce/schema/Detail__c';
export default class insertEvent extends LightningElement {
// #api childName;
#track conRecord = Detail_OBJECT;
handleChildNameChange(event) {
this.conRecord.childName = event.target.value;
}
createRec() {
insertDe({
de: this.conRecord
})
.then(result => {
// Clear the user enter values
this.conRecord = {};
// Show success messsage
this.dispatchEvent(new ShowToastEvent({
title: 'Success!!',
message: 'Contact Created Successfully!!',
variant: 'success'
}), );
})
.catch(error => {
this.error = error.message;
});
}
}
<template>
<lightning-card title="Create Contact Record">
<template if:true={conRecord}>
<div class="slds-m-around--xx-large">
<div class="container-fluid">
<div class="form-group">
<lightning-input
label="Child Name"
name="childName"
type="text"
value={conRecord.childName}
onchange={handleChildNameChange}
></lightning-input>
</div>
</div>
<br />
<lightning-button label="Submit" onclick={createRec} variant="brand"></lightning-button>
</div>
</template>
</lightning-card>
</template>
Apex code
public with sharing class insertEvent {
#AuraEnabled
public static void insertDe(Detail__c de) {
try {
insert de;
} catch (Exception e) {
System.debug('--->'+e);
}
}
}
If you're using an LWC component then I suggest to also use Lightning Data Service.
To answer your specific issue, after an insert DML, only the Id field is returned. If you need other fields, then you need to run a query. This is because trigger / workflow / process builder can change some field value.
My suggestion if you want insert record directly from LWC component, you should use Lightning Data Service. But you need to execute some custom code or insert record from apex method, then you should pass only the data LWC component and create object in apex method then insert it.
public static void insertDe(String name) {
Detail__c obj = new Detail__c();
obj.childName = name;
try {
insert obj;
} catch (Exception e) {
System.debug('--->'+e);
}
}
Only pass the name from lwc component according to your posting code.

automatic_uploads in tinymce returns null for iformfile?

I want to use upload image in tinymce.
my controller code and layout scripts is there:
public async Task<IActionResult> uploadFile(IFormFile iformfile)
{
//I upload my file and return location to editor
}
<script>
tinymce.init({
selector: '#mytextarea',
automatic_uploads: true,
images_upload_url: 'uploadFile',
images_reuse_filename: true,
});
</script>
But I receive null in my action parameter in controller.
When I debug the app and break point in controller action, Action called but receive null.
I use .net core 2.1.
Every other plugins do well!
public async Task<ActionResult> UploadImage()
{
IFormFile formFile;
formFile= Request.Form.Files[0];
if (formFile == null)
return Json(new { location = "Not Found" });
....
}

Vertx3.0 Simple Form upload

Vertx3.0 http simpleform file uploader is throwing error for multiple file.
Am using vertx3.0 simple form upload. It is working fine when i upload single file. If the form has the input "multiple" and choose multiple files, The HTTPServerUpload is throwing error "Response has already been written". Since the response is end in the endhandler for 1st file, it is throwing this error for subsequent files. is there any other way for multiple files ?
Simpleform file upload using vertx3.0
public class SimpleFormUploadServer extends AbstractVerticle {
public static void main(String[] args) {
Runner.runExample(SimpleFormUploadServer.class);
}
#Override
public void start() throws Exception {
vertx.createHttpServer()
.requestHandler(req -> {
if (req.uri().equals("/")) {
// Serve the index page
req.response().sendFile("index.html");
} else if (req.uri().startsWith("/form")) {
req.setExpectMultipart(true);
req.uploadHandler(upload -> {
upload.exceptionHandler(cause -> {
req.response().setChunked(true)
.end("Upload failed");
});
upload.endHandler(v -> {
req.response()
.setChunked(true)
.end("Successfully uploaded to "
+ upload.filename());
});
// FIXME - Potential security exploit! In a real
// system you must check this filename
// to make sure you're not saving to a place where
// you don't want!
// Or better still, just use Vert.x-Web which
// controls the upload area.
upload.streamToFileSystem(upload.filename());
});
} else {
req.response().setStatusCode(404);
req.response().end();
}
}).listen(8080);
}
}
Exception :
SEVERE: Unhandled exception
java.lang.IllegalStateException: Response has already been written
at io.vertx.core.http.impl.HttpServerResponseImpl.checkWritten(HttpServerResponseImpl.java:561)
at io.vertx.core.http.impl.HttpServerResponseImpl.end0(HttpServerResponseImpl.java:389)
at io.vertx.core.http.impl.HttpServerResponseImpl.end(HttpServerResponseImpl.java:307)
at io.vertx.core.http.impl.HttpServerResponseImpl.end(HttpServerResponseImpl.java:292)
at com.nokia.doas.vertx.http.upload.SimpleFormUploadServer$1$1$2.handle(SimpleFormUploadServer.java:85)
at com.nokia.doas.vertx.http.upload.SimpleFormUploadServer$1$1$2.handle(SimpleFormUploadServer.java:1)
at io.vertx.core.http.impl.HttpServerFileUploadImpl.notifyEndHandler(HttpServerFileUploadImpl.java:213)
at io.vertx.core.http.impl.HttpServerFileUploadImpl.lambda$handleComplete$165(HttpServerFileUploadImpl.java:206)
at io.vertx.core.file.impl.AsyncFileImpl.lambda$doClose$226(AsyncFileImpl.java:470)
at io.vertx.core.impl.ContextImpl.lambda$wrapTask$16(ContextImpl.java:335)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:358)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:357)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112)
at java.lang.Thread.run(Unknown Source)
index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
</head>
<body>
<form action="/form" ENCTYPE="multipart/form-data" method="POST" name="wibble">
choose a file to upload:<input type="file" name="files" multiple="multiple"/><br>
<input type="submit"/>
</form>
</body>
</html>
You can use vertx-web to easily handle file uploads:
router.route().handler(BodyHandler.create());
router.post("/some/path/uploads").handler(routingContext -> {
Set<FileUpload> uploads = routingContext.fileUploads();
// Do something with uploads....
});
Moreover, you will take benefits of the routing facility, and you can even serve static files such as index.html.
Hope this will help.
Multiple file upload is achievable in vert.x. Use multiple upload button in HTML and use uploadHandler of HttpRequest. UploadHandler would be invoked as many times any many files have been uploaded.
HttpServerRequest request = routingContext.request();
request.setExpectMultipart(true);
request.endHandler(new Handler<Void>() {
#Override
public void handle(Void aVoid) {
MultiMap entries = request.formAttributes();
Set<String> names = entries.names();
logger.info("UPLOAD_CONTENT: fileName = "+entries.get("fileName"));
logger.info("UPLOAD_CONTENT: type = "+entries.get("type"));
logger.info("UPLOAD_CONTENT: names = "+names);
request.response().setChunked(true).end(createResponse("SUCCESS"));
}
});
// This would be called multiple times
request.uploadHandler(upload -> {
upload.exceptionHandler(new Handler<Throwable>() {
#Override
public void handle(Throwable error) {
logger.error("UPLOAD_CONTENT: Error while uploading content "+upload.filename());
logger.error("UPLOAD_CONTENT: error = "+error.toString());
error.printStackTrace();
request.response().setChunked(true).end(createResponse("FAILURE"));
}
});
upload.endHandler(new Handler<Void>() {
#Override
public void handle(Void aVoid) {
logger.info("UPLOAD_CONTENT: fileName = "+upload.filename());
logger.info("UPLOAD_CONTENT: name = "+upload.name());
logger.info("UPLOAD_CONTENT: contentType = "+upload.contentType());
logger.info("UPLOAD_CONTENT: size = "+upload.size());
UtilityFunctions.uploadToS3(upload.filename(), "testfolder");
}
});
upload.streamToFileSystem(upload.filename());
});