Custom MediaStreamSource and MediaElement.Naturalduration property - c#

i have written a custom mediastreamsource, that can play media from growing source files (mpeg transport streams).
Once it reaches the end of its mediastream, it reads the new duration from the mediafile and continues to deliver samples. The MediaElement plays continously.
Unfortunately i haven´t found a way to update the MediaElement.NaturalDuration property. Hence i cannot seek into the “reloaded” area, because ME doesn´t know about it and sets my position change to its NaturalDuration value.
I tried to call ReportOpenMediaCompleted after getting the new stream length. Then Naturalduration get´s updated, but i cannot play anymore.
Is there any other way to deal with it ?

Sometime life can be so easy :-)
I solved it giving MediaElement a "fantasy" duration value when initializing my MediaStreamSource :
protected override void OpenMediaAsync()
{
...
mediaSourceAttributes[MediaSourceAttributesKeys.Duration] = TimeSpan.FromHours(10).Ticks.ToString(CultureInfo.InvariantCulture);
this.ReportOpenMediaCompleted(mediaSourceAttributes, mediaStreamDescriptions);
}
The only thing left to do was to update my slider control with the "real" duration.
Now it works like a charm ...
Tilo

Related

Asp.Net asynchronous loop not firing

I am attempting to send an image to the browser every 16 ms (~60Hz) from a specific file on the drive which is changing constantly (also at ~60Hz). To do this I am using Response.BinaryWrite(). Below is my code (it really is quite simple)
protected void Page_Load(object sender, EventArgs e)
{
MainLoop();
}
private async Task MainLoop()
{
while (true)
{
//Update image to latest from server
Response.ContentType = "image/png";
Response.BinaryWrite(File.ReadAllBytes(Server.MapPath("/Frames/frame.png")));
Response.Flush();
await Task.Delay(16);
}
}
My problem is that it does not refresh. I have double and triple checked that the file is indeed changing, and I have also tried updating a label with the current time in milliseconds. I have found that even when doing that, it does not update at all after the page loads.
If I reload the page it displays both a new image, and a new time, so the issue doesn't seem to be in the Response writing. Rather, it's as if the page is simply ignoring the loop entirely, and only running once through.
If anybody has any advice on alternatives to try (keep in mind video is not an option due to the live nature of this) I would be glad to hear them. Perhaps I am missing something very simple here, but I just can't find it!
Thanks!
Thoughts on the problem:
So the problem is that the page doesn't render updated results every 16ms. We can't solve this issue through server side code (e.g. c# code like your snippet).
I'm sure the while loop is still running but you will always need to refresh the page manually since this is ASP.NET (traditional web app with refresh). Here is a stack overflow answer that can back me up (in case you are skeptical about what I'm saying) refresh page after 3 seconds using c#
I'm not sure what the end goal here is, but I'm assuming you want to render your page with your updated png every 16ms. Basically from my 2 points above, don't resort to trying to update the image through the server side; instead, look into other options through the client side code (e.g. javascript).
Of course, I would try to comment further on where you should look exactly, but I never really dealt with updating an image every 16ms before!
I'm just going to throw in some links that could generate some ideas:
Refresh an image in the browser every x milliseconds
Change image in HTML page every few seconds
http://www.labbookpages.co.uk/web/realtime.html
I hope this can push you along! Let me know how this goes because I'm curious on how you will do this :)
You need to not only get the new image but also ensure the browser does not use the cashed image.
So, there's four steps to the solution:
Create a controller action that returns the current image, but your controller action needs to take an argument. The argument becomes a query string parameter. Each time you call the method, the param value should be different. This way, the browser considers the URL to be new and will not use a cached image. Your url should look something like http://mysite/my controller/getimage?param=12345678927483817
Create a JavaScript method to change the SRC property of the image by pointing it to the URL of your new controller action. Use something that constantly changes for your query parameter. I suggest using the current date time in milliseconds.
After the page loads, set a timer in JavaScript to call the function you created in Step 2.
Eat a cookie, because cookies are good.

Sound clipping/clicking when lowering volume with NAudio

Audio is clipping (or clicking) when trying to lower the volume of a WAV file in real time.
I've tried it on a SampleChannel, VolumeSampleProvider and WaveChannel32 instance, the source being a 32bit WAV file.
If I try it on a WaveOut instance, the clipping doesn't occur anymore, but I don't want that because it lowers the volume of all sounds in the application.
And this only happens when I lower volume, rising it doesn't cause clipping.
Is this a known problem or am I supposed to approach this differently?
Note: this is how the volume drops in real time over the given time span:
0.9523049
0.9246111
0.9199954
0.89384
0.8676848
0.8415294
0.8169126
0.7907572
0.7646018
0.739985
0.7122912
0.6892129
0.6630576
0.6369023
0.6122856
0.5861301
0.5599748
0.535358
0.5092026
0.4830474
0.456892
0.4322752
0.4061199
0.3799645
0.3553477
0.3276539
0.3030371
0.2784202
0.2522649
0.2261095
0.2014928
0.176876
0.149182
0.1245652
0.09841
0.07225461
0.04763785
0.02148246
0
Apparently it is a DesiredLatency and NumberOfBuffers issue on the WaveOut instance. The default values cause the problem, altered values fix it.
These are the values I used to fix the issue.
WaveOutDevice = new WaveOut(WaveCallbackInfo.NewWindow())
{
DesiredLatency = 700, //! adjust the value to prevent sound clipping/clicking
NumberOfBuffers = 3, //! number of buffers must be carefully selected to prevent audio clipping/clicking
};

libVLCNet player positioning (without showing the movie begining) when opening new video

I'm looking at libvlcnet 0.4.0.0 "SimplePlayer" example from http://sourceforge.net/p/libvlcnet/wiki/Home/ and I want to ask if is it possible to to open new file and play it from predefined position without needing to play the start of the movie? I use something like this:
LibVlcInterop.libvlc_media_player_play(descriptor);
LibVlcInterop.libvlc_media_player_pause(descriptor);
LibVlcInterop.libvlc_media_player_set_position(descriptor, (float)0.8);
int res = LibVlcInterop.libvlc_media_player_play(descriptor);
When trying to play new file user can notice small fraction of the beginning of the movie.
How can I position player to particular area after I load new file without showing small portion of the beginning of movie?
I don't know about that particular library, but you can do this generally by passing "media options" before you play the media. To do this use the LibVLC API function libvlc_media_add_option.
If you can do this in that library, then you can specify a start time and/or an end time - but it has to be specified as seconds rather than as a percentage position.
The options you would pass to the API function would be:
:start-time=30
:stop-time=60.5
You can actually pass fractional seconds as shown in the stop-time example.
When I do this, I do not notice any flash of content showing the beginning of the movie but I suppose that still might happen on some platforms or with some types of media.

How to set the start position for a video using AxShockwaveFlash (revised)

Revised question with added detail...
I'm trying to use AxShockwaveFlash to play a youtube video and start it at a specific location. This is within a C# winforms app. I have the basics. I can successfully start and stop the video. I cannot, however, figure out how to set the position and start at a specific time within the video. What property or method needs to be called to do this?
Here's what I have so far:
AxShockwaveFlashObjects.AxShockwaveFlash mFlashPlayer;
...
mFlashPlayer.Movie = #"http://www.youtube.com/v/9O9HfafzBPE?version=3&hl=ru_RU";
mFlashPlayer.Play();
I've tried setting various properties and calling various methods, but I'm just guessing. For example, I tried calling
mFlashPlayer.GotoFrame(200);
and it did nothing. Seems so simple, I'm starting to wonder if I'm encountering a bug. ?
I also tried using the standard form when using a web browser, which is to encode it directly in the url. For example, the following did not work either:
mFlashPlayer.Movie = #"http ...blah... #t=77s";
mFlashPlayer.Play();
Thanks in advance for any suggestions.
ANSWER
Apparently, I'm not authorized to post an answer, so I'm editing the question and adding it here...
Got it! The magic trick is to set the 'start' parameter in the url that you provide to the Movie property or the LoadMovie() method.
Embedded AS3 player:
http://www.youtube.com/v/VIDEO_ID?version=3&start=NUMBER_SECONDS
Chromeless AS3 player:
http://www.youtube.com/apiplayer?video_id=VIDEO_ID&version=3&start=NUMBER_SECONDS
Replace VIDEO_ID and NUMBER_SECONDS with the desired values.
Example:
mFlashPlayer.Movie = #"http://www.youtube.com/v/1ZKz2KW87Y4?version=3&start=100";
I found the required info here:
https://developers.google.com/youtube/player_parameters
That was one of the more frustrating hunts for info that I've been on in a while. Hope this post saves other folks the headaches.

Simple Image Processing in WinRT

I'm working on a WinRT app that should require some image processing. I've done something similar so far but in Java, I would like to do some simple things in WinRT app also... But I can't seem to manage my way with the API...
Long story short, I have in xaml, on my page, an image that obtains an image with a file picker. Then when I click a "negativate" button, the image should get negativated.
Now, the method for the negativation button, I thought to look like this :
private void OnNegativateButtonClick(object sender, RoutedEventArgs e)
{
var imageToNegativate = ImagePanel.Source as WriteableBitmap ;
if (imageToNegativate == null) //Actually is ALWAYS null :(
{
//Wrong code here...
var bitmapSource = ImagePanel.Source as BitmapSource;
imageToNegativate = new WriteableBitmap(imageToNegativate.PixelWidth, imageToNegativate.PixelHeight);
}
imageToNegativate = ImageUtil.Negativate(imageToNegativate);
ImagePanel.Source = imageToNegativate;
}
This is very similar to this sample I found here but that sample project won't even load so I tried to open the files individually... My code is that method for negativation only there is something wrong with wb = new WriteableBitmap(bs); in his if (wb==null) { ... }.
What is the approach to take a WriteableBitmap from an image, do some pixel manipulation, and then set the source of the image with the new WriteableBitmap...
I'm saying about WriteableBitmap because my method for negativation uses one for input, does some processing and outputs it. (same type, WriteableBitmap.
Any suggestions or help is greatly appreciated, thank you!
The first problem is these lines of code:
var imageToNegativate = ImagePanel.Source as WriteableBitmap ;
if (imageToNegativate == null) //Actually is ALWAYS null :(
The null is telling you that ImagePanel.Source is not of type WriteableBitmap; your typecast failed. This is expected; the picker is going to give you something read-only, because read-only images are more performant (WinRT can do some optimizations if it knows the image's content isn't going to change). You only get a WriteableBitmap when you explicitly create one.
The body of your if block also doesn't make much sense -- you're trying to create a new, empty WriteableBitmap with the same size as the original image, and then you try to do an inverse-video on that empty image. Even if you got that far, you'd just get another empty image. You're not doing anything to keep the pixels from the original image.
You do need a WriteableBitmap to get access to the pixel buffer, but you need to make one that's a copy of the original image. Get rid of your cast and if block, and try this instead:
var imageToNegativate = new WriteableBitmap(ImagePanel.Source);
Joe is right in saying what is wrong with your code, but while his answer would probably work in WPF - the constructor he mentions indeed doesn't exist in WinRT/XAML. There is in fact no way to create a WriteableBitmap from a BitmapImage and the only way to attack it is to create a WriteableBitmap from the source of your BitmapImage.
One approach then would be to create a new 1x1-sized WriteableBitmap, then use its SetSource method on the source of your BitmapImage. Since the BitmapImage.UriSource property is a Uri and WriteableBitmap.SetSource() requires a Stream - you need to play with it a bit to find or download the actual image file and open a stream to read it. My toolkit has some extension methods (WriteableBitmap.FromBitmapImage()) to help you with it in case your images come from your application package and you could fairly easy expand it to also work with downloaded images.
The problem is - you get some inefficiencies that way - one is that you need to open and decode the file twice (which might be slow with large images on an ARM device - been there, done that), second is that you might need to re-download the file if it came from the network and third that there is a bug in WinRT/XAML that causes the UriSource property of a BitmapImage to become null if you set CacheMode of an associated Image control to BitmapCache, so you might need to track your sources separately anyway.
The best solution in many ways is to simply make sure you open the source image as a WriteableBitmap from the beginning, so you don't even have to create another bitmap if you want to change it anyway. In your case the user selects the image file, so there is only one file and it seems to have a pretty big chance of being processed afterwards so opening it as a WriteableBitmap sounds like the right thing to do.
Perhaps even if you want to keep both the original and processed version of the image - creating a new WriteableBitmap of same size and copying the pixels of the original might be faster (especially on an ARM device) than decoding a big image file twice.

Categories