Set stream for MediaElement from Http request - c#

I have a code for recording some music/video file from online stream (video stream or radio).
How can I modify it for setting the SetSource property to my MediaElement (from IBuffer or Bytes[],... for example)?
(I cannot use the variant like this:
mediaElement1.Source = new Uri(urlLinkToOnlineStream);
mediaElement1.Play();
)
I need to set SetSource directly from already opened stream (I need write to file and record the same bytes[] from stream in the same moment or with a small pause).
HttpClientHandler aHandler = new HttpClientHandler();
aHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
HttpClient aClient = new HttpClient(aHandler);
aClient.DefaultRequestHeaders.ExpectContinue = false;
HttpResponseMessage response = await aClient.GetAsync(urlLinkToOnlineStream, HttpCompletionOption.ResponseHeadersRead);
var destinationFile = await KnownFolders.MusicLibrary.CreateFileAsync(#"recorded.mp3", CreationCollisionOption.ReplaceExisting);
var fileStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite);
Stream stream = await response.Content.ReadAsStreamAsync();
IInputStream inputStream = stream.AsInputStream();
ulong totalBytesRead = 0;
while (true)
{
// Read from the web.
IBuffer buffer = new Windows.Storage.Streams.Buffer(1024);
buffer = await inputStream.ReadAsync(buffer, buffer.Capacity, InputStreamOptions.None);
if (buffer.Length == 0)
{
break;
}
totalBytesRead += buffer.Length;
await fileStream.WriteAsync(buffer);
}
inputStream.Dispose();
fs.Dispose();

Are you developing the Universal Windows Platform App or Windows Phone 8.1 App? The UWP supports the media stream protocol like HTTP Live Stream. You can assign the URL to the media element directly.
UWP:
media.Source = new Uri("http://amssamples.streaming.mediaservices.windows.net/49b57c87-f5f3-48b3-ba22-c55cfdffa9cb/Sintel.ism/manifest(format=m3u8-aapl)");
For Windows Phone 8.1, you can use the open source stream media library.
For example: Windows Phone Streaming Media

I know this is old, however I have a working example for a single audio file that can be adapted for a stream.
InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream();
HttpClient hc = new HttpClient();
HttpResponseMessage msg = await hc.GetAsync(urlLinkToOnlineStream);
await RandomAccessStream.CopyAsync(await msg.Content.ReadAsInputStreamAsync(), stream);
stream.Seek(0);
myMediaElement.SetSource(stream, msg.Content.Headers.ContentType.ToString());
You must remember to call Dispose() on your InMemoryRandomAccessStream and HttpClient when you are sure you have finished with them. Otherwise you will most likely end up with a memory leak.
I'm not entirely sure how you intend to do the buffering, but you if you call Dispose() on the stream (in my example) playback will fail. It will also start playing the new chunk of data if you call SetSource before current chunk has finished.

Related

Video Progressive Download - can't not seek in chrome browser

I'm trying to play in Chrome Browser video with source from Web Api
<video id="TestVideo" class="dtm-video-element" controls="">
<source src="https://localhost:44305/Api/FilesController/Stream/Get" id="TestSource" type="video/mp4" />
</video>
In order to implement progressive downloading I'm using PushStreamContent in server response
httpResponce.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>)new StreamService(fileName,httpResponce).WriteContentToStream);
public async void WriteContentToStream(Stream outputStream, HttpContent content, TransportContext transportContext)
{
//here set the size of buffer
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
//here we re using stream to read file from db server
using (var fileStream = IOC.Container.Resolve<IMongoCommonService>().GridRecordFiles.GetFileAsStream(_fileName))
{
int totalSize = (int)fileStream.Length;
/*here we are saying read bytes from file as long as total size of file
is greater then 0*/
_response.Content.Headers.Add("Content-Length", fileStream.Length.ToString());
// _response.Content.Headers.Add("Content-Range", "bytes 0-"+ totalSize.ToString()+"/"+ fileStream.Length);
while (totalSize > 0)
{
int count = totalSize > bufferSize ? bufferSize : totalSize;
//here we are reading the buffer from orginal file
int sizeOfReadedBuffer = fileStream.Read(buffer, 0, count);
//here we are writing the readed buffer to output//
await outputStream.WriteAsync(buffer, 0, sizeOfReadedBuffer);
//and finally after writing to output stream decrementing it to total size of file.
totalSize -= sizeOfReadedBuffer;
}
}
}
After I load page video start to play immediately, but I can not seek for previous (already played) seconds of video or rewind it as well in Google Chrome browser. When I try to do this, video goes back to the beggining.
But in Firefox and Edge it's working like it should be, I can go back to already played part. I don't know how to solve this issue in Google Chrome Browser
You should use HTTP partial content. As it described here:
As it turns out, looping (or any sort of seeking, for that matter) in elements on Chrome only works if the video file was served up by a server that understands partial content requests.
So there are some articles that may help you to implement it. Try these links:
HTTP 206 Partial Content In ASP.NET Web API - Video File Streaming
How to work with HTTP Range Headers in WebAPI
Here is an implementation of responding to Range requests correctly - it reads a video from a file and returns it to the browser as a stream, so it doesnt eat up your server's ram. You get the chance to decide the security you want to apply etc in code.
[HttpGet]
public HttpResponseMessage Video(string id)
{
bool rangeMode = false;
int startByte = 0;
if (Request.Headers.Range != null)
if (Request.Headers.Range.Ranges.Any())
{
rangeMode = true;
var range = Request.Headers.Range.Ranges.First();
startByte = Convert.ToInt32(range.From ?? 0);
}
var stream = new FileStream(/* FILE NAME - convert id to file somehow */, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) {Position = startByte};
if (rangeMode)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.PartialContent)
{
Content = new ByteRangeStreamContent(stream, Request.Headers.Range, MediaTypeHeaderValue.Parse(fileDetails.MimeType))
};
response.Headers.AcceptRanges.Add("bytes");
return response;
}
else
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(stream)
};
response.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(fileDetails.MimeType);
return response;
}
}

FTP Client ul/dl in Universal Windows app

I'm struggling with uploading and downloading files to a FTP server in UWP/Windows Store apps.
The FtpWebRequest Class is missing so you have to use your own Ftp Client. I haven't found any working open-source libraries for this yet. I tried Windows 8.1 Sockets: Ftp Client Sample with Sockets in C#/Xaml; The directory listing works fine but the up/download breaks after a few bytes. Maybe a buffer issue? This is the upload stream, the download function is on the msdn site.
using (var stream = await ftpClient.OpenWriteAsync(newFilePath))
{
var bytes = System.Text.Encoding.UTF8.GetBytes(filecontent);
await stream.WriteAsync(bytes.AsBuffer());
await stream.FlushAsync();
}
or
using (var stream = await OpenWriteAsync(destination))
{
// A more efficient way, maybe a DataReader can be used here
using (var readStream = await file.OpenReadAsync())
{
var buffer = new byte[512].AsBuffer();
var resultingBuffer = new byte[0];
while (true)
{
IBuffer readBuffer = await readStream.ReadAsync(buffer, 512, InputStreamOptions.Partial);
if (readBuffer.Length == 0) break;
resultingBuffer = resultingBuffer.Concat(readBuffer.ToArray()).ToArray();
}
await stream.WriteAsync(resultingBuffer.AsBuffer());
await stream.FlushAsync();
}
}
I read that the FTP upload is not supported by the BackgroundTransfer class. Has someone found a solution for loading files to a password-protected FTP server in UWP apps?
Thanks for your help!

UWP - How to Save a Contact Thumbnail to Local Storage?

i want some help to get the thumbnail image of a Contact and save it to Local Storage, i successfully got the contact Thumbnail but i can't get the actual image from the stream, this is my Code:
var contactStore = await ContactManager.RequestStoreAsync();
var contacts = await contactStore.FindContactsAsync();
var myContact = contacts[0]; //I am sure that this Contact has a Thumbnail
var stream = await myContact.Thumbnail.OpenReadAsync();
byte[] buffer = new byte[stream.Size];
var readBuffer = await stream.ReadAsync(buffer.AsBuffer(), (uint)buffer.Length, InputStreamOptions.None);
var file = await ApplicationData.Current.LocalFolder.CreateFileAsync("image.jpg", CreationCollisionOption.ReplaceExisting);
var fileStream = await file.OpenStreamForWriteAsync();
await fileStream.WriteAsync(readBuffer.ToArray(), 0, (int)readBuffer.Length);
this code creates an empty image in the local storage, any help ?
thanks for your time
The problem here is that you missed Stream.Flush method to flush the buffer to the underlying stream. You can add fileStream.Flush(); after fileStream.WriteAsync method to fix your issue.
Besides this, we also need to call Stream.Dispose Method to releases the resources used by the Stream when we finish using it. And this method disposes the stream, by writing any changes to the backing store and closing the stream to release resources. So we can just use fileStream.Dispose() after fileStream.WriteAsync method.
A recommend way to call the Dispose method is using the C# using statement like following:
var contactStore = await ContactManager.RequestStoreAsync();
var contacts = await contactStore.FindContactsAsync();
var myContact = contacts[0]; //I am sure that this Contact has a Thumbnail
using (var stream = await myContact.Thumbnail.OpenReadAsync())
{
byte[] buffer = new byte[stream.Size];
var readBuffer = await stream.ReadAsync(buffer.AsBuffer(), (uint)buffer.Length, InputStreamOptions.None);
var file = await ApplicationData.Current.LocalFolder.CreateFileAsync("image.jpg", CreationCollisionOption.ReplaceExisting);
using (var fileStream = await file.OpenStreamForWriteAsync())
{
await fileStream.WriteAsync(readBuffer.ToArray(), 0, (int)readBuffer.Length);
}
}
I believe you may have to call stream.Dispose() after you read from it or else initialize the stream with a using directive: using (var outputStream = stream.GetOutputStreamAt(0))
The following link might be useful:
https://msdn.microsoft.com/en-us/windows/uwp/files/quickstart-reading-and-writing-files?f=255&MSPPError=-2147217396

WinRT read from infinite online stream hangs

The following code gets a Stream from a URI and will read in in chunks using a loop. Note that behind the specified URI is an online radio stream, which means there is no known size.
var uri = new Uri("http://*******", UriKind.Absolute);
var http = new HttpClient();
var stream = await http.GetStreamAsync(uri);
var buffer = new byte[65536];
while (true)
{
var read = await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
Debug.WriteLine("Read: {0}", read);
}
Now while this works perfectly fine in a .NET 4.5 console app, this exactly same code does not work as expected in WinRT - it will read the first chunk, and when calling ReadAsync for the second time, it will just get stuck and never continue.
If I switch the URI to a file (of known size) everything works fine in both projects.
Any tips?
EDIT> note that this behaviour happens only on WP8.1. I just searched some more on SO and found that my question might be a duplicate of this one: WP8.1 HttpClient Stream got only 65536 bytes data If that is true, I will close my question
Use HttpClient.GetAsync() with HttpCompletionOption.ResponseHeadersRead. That returns when the headers are received, then do HttpResponse.Content.ReadAsInputStreamAsync().
Looks like your while loop is infinite. How are you ensuring that it's finite? Instead of while(true) make it something like this
var uri = new Uri("http://*******", UriKind.Absolute);
var http = new HttpClient();
var stream = http.GetStreamAsync(uri).Result;
using (var reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
{
var response = reader.ReadToEnd();
}
}

Getting a Stream from a resource file / content

Is this the correct/only way of getting a Stream from a resource file?
Uri uri = new Uri(fullPath);
StorageFile storageFile =
await Windows.Storage.StorageFile.
GetFileFromApplicationUriAsync(uri);
IRandomAccessStreamWithContentType randomAccessStream =
await storageFile.OpenReadAsync();
IInputStream resourceStream = (IInputStream)
randomAccessStream.GetInputStreamAt(0);
All my other sources (http and local storage) return a Stream object, and it is painful to have to if-else code that uses one or the other.
I've also tried to just create a MemoryStream out of it, but I can't even find a way to get the bytes out... Please help.
uint size = (uint)randomAccessStream.Size;
IBuffer buffer = new Windows.Storage.Streams.Buffer(size);
await randomAccessStream.ReadAsync(buffer, size,
InputStreamOptions.None);
Stream stream = new MemoryStream(buffer); // error takes byte[] not IBuffer
IInputStream.ReadAsync() when reading from resource:
http://msdn.microsoft.com/en-us/library/windows/apps/windows.storage.streams.iinputstream.readasync.aspx
while Stream.Read() and Stream.ReadAsync() look like this:
http://msdn.microsoft.com/en-us/library/system.io.stream.read.aspx
and
http://msdn.microsoft.com/en-us/library/hh137813.aspx
Thanks
Ok I found it!
StorageFile storageFile =
await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(uri);
var randomAccessStream = await storageFile.OpenReadAsync();
Stream stream = randomAccessStream.AsStreamForRead();
You can also do it in one less line:
Stream stream = await storageFile.OpenStreamForReadAsync();

Categories