Streaming with go-between in Windows 8 - c#

I want to stream data from a server into a MediaElement in my Windows 8 Store (formerly Metro) app. However, I need to "record" the stream while it is streaming, so it can be served from cache if re-requested, so I don't want to feed the URL directly into the MediaElement.
Currently, the stumbling block is that MediaElement.SetSource() accepts an IRandomAccessStream, not a System.IO.Stream, which is what I get from HttpWebResponse.GetResponseStream().
The code I have now, which does not work:
var request = WebRequest.CreateHttp(url);
request.AllowReadStreamBuffering = false;
request.BeginGetResponse(ar =>
{
var response = ((HttpWebResponse)request.EndGetResponse(ar));
// this is System.IO.Stream:
var stream = response.GetResponseStream();
// this needs IRandomAccessStream:
MediaPlayer.SetSource(stream, "audio/mp3");
}, null);
Is there a solution that allows me to stream audio, but allows me to copy the stream to disk when it has finished reading from the remote side?

I haven't experimented with the following idea, but it might work: You could start streaming the web data into a file and then, after a few seconds (for buffering), pass that file to the MediaElement.
I noticed that MediaElement can be picky about opening a file that is being written into, but I have seen it work. Though, I can't say why it sometimes work and why it sometimes doesn't.

I guess this would help you to convert Stream to IRandomAccessStream
InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream();
using (Stream stream = response.GetResponseStream();)
{
await stream.CopyToAsync(ras.AsStreamForWrite());
}

Related

How to correctly use streams to retrieve, edit, save and send image via discordbot

I am writing a discord bot using DSharp plus library.
The command I am currently writing gets an image from the discord chat I then edit the image and send the edited image back as a single frame gif.
I retrieve the image from the image url by using:
HttpClient client = new HttpClient();
Stream stream = await client.GetStreamAsync(attachments[0].Url);
Bitmap image = new Bitmap(System.Drawing.Image.FromStream(stream));
I then call my edit function and edit the image. I then save the image to my files using:
using (var stream = new FileStream("Images/output.gif", FileMode.Create))
{
imgToGif.SaveAsGif(stream);
}
where .SaveAsGif() is a function from the KGySoft.Drawing library I found online.
To send the edited image back I use:
FileStream file = new FileStream("Images/output.gif", FileMode.Open);
DiscordMessageBuilder messagefile = new DiscordMessageBuilder();
messagefile.AddFile(file);
ctx.RespondAsync(messagefile);
But this throws a "The process cannot access the file "Image/output.gif" because it is being used by another process." error.
After some googling I tried to close the FileStream which saves my image to my files using stream.close() or stream.dispose(). The problem however is that I cannot acces the stream again because it will throw the "Cannot acces closed stream error".
I also tried using FileShare.read, FileShare.ReadWrite.
Tried closing both stream and tried to use 1 stream only. So I kept the stream open and used it to send the message in discord chat but that would send a file with 0 bytes in the discord chat.
I think you closed the stream too early while sending the gif
you need to call file.Close() after you call RespondAsync() and you need to change ctx.RespondAsync(messagefile); to await ctx.RespondAsync(messagefile); because RespondAsync() is an asynchronous method if you dont use await rest of the code will continue running so the stream will close while ctx.RespondAsync(messagefile); is still running and it will give an error.
sending a gif part should look like this:
FileStream file = new FileStream("Images/output.gif", FileMode.Open);
DiscordMessageBuilder messagefile = new DiscordMessageBuilder();
messagefile.AddFile(file);
await ctx.RespondAsync(messagefile);
file.Close();
if you have done the rest of the code correct this should work.

convert stream to audio file

I send an audio file to a server API in MultiPartFormData. for this purpose, first, I convert storage File to Byte format, then I convert Byte to Stream and after that I post with MultiPartFormData request.That server answer my request in MultiPartformData format with an another Audio file too.
I receive that respond in HttpResponceMesseage, my question is how can I convert it to mp3 file?
I am using windows iot with UWP coding platform.
multipartContent.Add(new ByteArrayContent(await GetBytesAsync(storageFile)),"audio","audio.mp3");
request.Content = multipartContent;
var response = await httpClient.SendAsync(request);
var content = new StreamReader(await response.Content.ReadAsStreamAsync()).ReadToEnd();
In UWP, if you want to write to a file using stream, we will following the four-step model:
Open the file to get a stream
Get an output stream.
Create a DataWriter object and call the corresponding Write method.
Commit the data in the data writer and flush the output stream.
Please see Create, write, and read a file and Best practices for writing to files for more information.
The official File access sample for your reference.
I done it with extra following code.
first, I convert response to byte[] array, then I write bytes to file within new task thread,that because of main thread correspond to UI won't let another Async task run on it.
var response = await httpClient.SendAsync(request);
byte[] x=await response.Content.ReadAsByteArrayAsync();
await Task.Run(() =>
System.IO.File.WriteAllBytes(storageFile.Path,x));

How to send a continuous stream to a ASP Core REST API

I am trying to send a continuous stream, from a C# application, to an ASP Core REST API.
I define a continuous stream as for example someone talking into a microphone and the sound being sent directly, without being saved to a local file) to the Rest API to be saved to file.
I have been searching a lot on Google for something like that and so far could not find anything really useful.
I have been trying to emulate it by sending a large file (297MB).
This is what I have so far for the client side:
string TARGETURL = "http://localhost:58000/api/file/";
string filePath = #"G:\Voice\Samples\The Monkey's Paw.wav";
byte[] fileContent = File.ReadAllBytes(filePath);
var dummyStream = new MemoryStream(fileContent);
var inputData = new StreamContent(dummyStream);
HttpResponseMessage response = this._httpClient.PostAsync(TARGETURL, inputData).Result;
HttpContent result = response.Content;
if (response.IsSuccessStatusCode)
{
string contents = result.ReadAsStringAsync().Result;
}
else
{
// do something
}
And for the server side:
[Route("")]
[HttpPost]
public async Task<JsonResult> Post()
{
Dictionary<string, object> rv = new Dictionary<string, object>();
try
{
string file = Path.Combine(#"G:\Voice\Samples\dummy.txt");
using (FileStream fs = new FileStream(file, FileMode.Create, FileAccess.Write,
FileShare.None, 4096, useAsync: true))
{
await Request.Body.CopyToAsync(fs);
}
// complete the transaction
rv.Add("success", true);
rv.Add("error", "");
}
catch(Exception ex)
{
}
return Json(rv);
}
When I am sending the file, the server throw the following exception:
The request's Content-Length 304137380 is larger than the request body size limit 30000000.
I know that I could increase the body size limit, but that's not a longer term solution as the stream length could get longer that any limit I set.
That's why I am trying to find a solution that send the stream by chunks for the server to rebuild and write to a file.
What you probably want to do is use a different network stack. A web application is always going to try and fit everything into HTTP. This is a very specific kind of way to communicate. And REST is built on top of these ideas as well. Things are generally though of as a document with references on the Internet, and REST is an extension to this idea.
It does however sit on top of some other great technologies that might suit your need better.
There's nothing to stop you using the internet, but maybe you need to look at possibly a UDP or TCP level implementation. Be aware that you will still be sending information in packets. There is no such thing as a constant stream of bits on the internet. A sound wave in the real world is an infinite thing, but computers are rubbish at that.
Maybe start by taking a look at using sockets and a library like NAudio.

How streaming works, and how its not loaded into memory with WebAPIs

I am a little confused on exactly how C# streams work, and when and how streaming works with them. I have read various articles, but I am not sure I completely understand it still.
I have the following code:
[HttpGet]
[Route("GetImage/ImageId/{imageId:long}")]
public HttpResponseMessage GetImage(long imageId)
{
var imageStream = OpenStream(imageId);
response.Content = new StreamContent(imageStream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
return response;
}
I understand that a stream is basically a abstraction over a backing file, so when you open the stream, it is not loading the stream fully into memory yet (depending how its opened) but lets assume its opened without loading it into memory yet.
So the questions are:
1.If I took imageStream and use .CopyTo to copy it to another stream, I'm assuming this has to read the whole stream into memory to perform this function?
2.With the code above, if the client reads this stream a few bytes at a time, how does the API understand to only send pieces at a time, does the connection stay open until this streaming is done?
3.If I need the full image to do anything on the client side, is there a benefit to streaming this, or would getting the whole image via just GetStreamAsync do the same? Does GetStreamAsync pull the entire stream down into memory?

How do I seamlessly compress the data I post to a form using C# and IIS?

I have to interface with a slightly archaic system that doesn't use webservices. In order to send data to this system, I need to post an XML document into a form on the other system's website. This XML document can get very large so I would like to compress it.
The other system sits on IIS and I use C# my end. I could of course implement something that compresses the data before posting it, but that requires the other system to change so it can decompress the data. I would like to avoid changing the other system as I don't own it.
I have heard vague things about enabling compression / http 1.1 in IIS and the browser but I have no idea how to translate that to my program. Basically, is there some property I can set in my program that will make my program automatically compress the data that it is sending to IIS and for IIS to seamlessly decompress it so the receiving app doesn't even know the difference?
Here is some sample code to show roughly what I am doing;
private static void demo()
{
Stream myRequestStream = null;
Stream myResponseStream = null;
HttpWebRequest myWebRequest = (HttpWebRequest)System.Net
.WebRequest.Create("http://example.com");
byte[] bytMessage = null;
bytMessage = Encoding.ASCII.GetBytes("data=xyz");
myWebRequest.ContentLength = bytMessage.Length;
myWebRequest.Method = "POST";
// Set the content type as form so that the data
// will be posted as form
myWebRequest.ContentType = "application/x-www-form-urlencoded";
//Get Stream object
myRequestStream = myWebRequest.GetRequestStream();
//Writes a sequence of bytes to the current stream
myRequestStream.Write(bytMessage, 0, bytMessage.Length);
//Close stream
myRequestStream.Close();
WebResponse myWebResponse = myWebRequest.GetResponse();
myResponseStream = myWebResponse.GetResponseStream();
}
"data=xyz" will actually be "data=[a several MB XML document]".
I am aware that this question may ultimately fall under the non-programming banner if this is achievable through non-programmatic means so apologies in advance.
I see no way to compress the data on one side and receiving them uncompressed on the other side without actively uncompressing the data..
No idea if this will work since all of the examples I could find were for download, but you could try using gzip to compress the data, then set the Content-Encoding header on the outgoing message to gzip. I believe that the Length should be the length of the zipped message, although you may want to play with making it the length of the unencoded message if that doesn't work.
Good luck.
EDIT I think the issue is whether the ISAPI filter that supports compression is ever/always/configurably invoked on upload. I couldn't find an answer to that so I suspect that the answer is never, but you won't know until you try (or find the answer that eluded me).

Categories

Resources