Forcing MediaElement to Release Stream after playback - c#

I am creating an audio recorder control, with playback functionality.
I use the media element to play the recorded audio like this:
using (var storage = System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication())
{
using (System.IO.Stream stream = new System.IO.IsolatedStorage.IsolatedStorageFileStream(filePath, System.IO.FileMode.Open, storage))
{
player.SetSource(stream);
}
}
The problem i am facing is that when I use media element to play the recorded audio. The Stream is locked to the media element. I cannot overwrite the file or even play it again as the SetSource() method throws an exception.
Is there a way to force the media element to release the stream?

Based on #Sheridan answer this it what I came up with that works.
Whenever MediaElement is stopped using Stop() function set the Source Property to null like this:
ResetMediaElement()
{
mediaElement.Stop();
mediaElement.Source = null;
}
This will close the stream attached to the media element, so that the related resource can be used somewhere else.

If you use MediaElement, make sure you don't get bitten by this one:
http://msdn.microsoft.com/en-us/library/cc626563(v=vs.95).aspx
ArgumentNullException - The mediaStreamSource is null.
...
After calling this method, MediaElement.Source returns null. If this
is called and MediaElement.Source is set, the last operation wins.
If a MediaElement is removed from the UI tree while is has an opened
MediaStreamSource, subsequent calls to SetSource may be ignored. To
ensure featureSetSource calls will work, set the Source property to
null before detaching the MediaElement from the UI tree.
naturally one would expect, if they only use SetSource(somestream) to use SetSource(null) to release the resources. Nope, they thought "better", you have to use Source=null instead to release resources and SetSource(null) throws ArgumentNullException
that is what I call a design bug (breaks the rule of "least expected" behavior and causes bugs that bite you at runtime only [unless somebody has made a static analysis rule to catch such a thing - would need metadata of course that some argument can't be null, like in Code Contracts])
I managed to introduce this bug while refactoring some code in ClipFlair Studio's AudioRecorder control the other day :-(
Note that you can’t use at MediaElement something like Source = stream to open a Stream, since that is a Uri property (not an Object property to also accept Stream) and you have to use SetSource(stream) instead, so you’d also expect to be able to use SetSource(null) to release the resources.
Update: Fixed this in AudioRecorderView class (uses MVVM pattern) of AudioRecorderControl, at Audio property’s "set" accessor it needed the following null-guarding pattern:
if (mediaStreamSource != null)
player.SetSource(mediaStreamSource);
//must set the source once, not every time we play the same audio,
//else with Mp3MediaSource it will throw DRM error
else
player.Source = null;

mediaElement.Stop();
mediaElement.ClearValue(MediaElement.SourceProperty);

I had a similar problem with displaying images. In a control with an image, I would get a 'File is in use' error whenever the user tried to update the image. The solution was to set the BitmapImage.CacheOption property to BitmapCacheOption.OnLoad:
MSDN says Set the CacheOption to BitmapCacheOption.OnLoad if you wish to close a stream used to create the BitmapImage. The default OnDemand cache option retains access to the stream until the image is needed, and cleanup is handled by the garbage collector.
After searching for a similar property that you could use for your MediaElement, it turns out that there isn't one. However, according to an answer on the chacheoption for mediaelement post from MSDN, there is a (long winded) way to achieve this... from the relevant answer:
I am not sure if your MediaElement is in an UserControl or not. but
whatever the case you can set the UserControl or Control to
IsEnabled=false, which in turn will trigger the Event Handler
IsEnabledChanged. In it place the necessary code to stop the
MediaElement from playback ME.Stop(), then call ME.Clear() and
ME.Source = null. After that you should find no problems to delete the
source file.
ME.Source = new Uri(MediaFilePath);
ME.Play();
...
private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
ME.IsEnabled = false; // This will call the Event Handler IsEnabledChanged
System.IO.File.Delete(MediaFilePath);
// Now after the player was stopped and cleared and source set to null, it
// won't object to deleting the file
}
private void ME_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
ME.Stop();
ME.Clear();
ME.Source = null;
}
I hope that this helps.

Related

Why I'm receiving Access Violation Exception when try to dispose VideoView in LibVLCSharp

I'm using Stylet as MVVM framework and LibVLCSharp.WPF 3.6.5 + VideoLAN.LibVLC.Windows 3.0.16 in my project for streaming camera rtsp streams.
VideoView is included in my VLCPLayer custom user control, and that control is exposed as a property in my view model. I don't know if this is a good thing, but in this way I managed to make some kind of custom full screen video with custom buttons.
Everything works fine until I try to close player, or better to say try to dispose VideoView.
I have something like this:
public void Dispose()
{
//these are custom events in my user control
VlcPlayer.ClosePlayer -= ClosePlayer;
VlcPlayer.RestorePlayer -= RestorePlayer;
VlcPlayer.MinimizePlayer -= MinimizePlayer;
//here I try to dispose player and VideoView
VlcPlayer.VideoView.MediaPlayer.Stop();
VlcPlayer.VideoView.MediaPlayer.Media.Dispose();
VlcPlayer.VideoView.MediaPlayer.Dispose();
//here I'm getting Access Violation Exception
VlcPlayer.VideoView.Dispose();
}
If I don't dispose VideoView it remains in memory and every time I start new stream new VideoView is added to memory, so if I opened and closed one stream and then opened the same or whatever stream, I'll have in memory 2 VideoViews instead of one. I have to release memory of unused VideoViews and players since application will be used for some kind of surveillance, so it will have lot of streams at the same time opened, and lot of VideoViews opened.
Here I found similar issue, but it was reportedly solved updating to newest version of LibVLCSharp, but my version is newer than from mentioned post and I still have similar problem.
I'd appreciate any kind of help on this issue. Thanks.

OutOfMemoryException # WriteableBitmap # background agent

I have a Windows Phone 8 App, which uses Background agent to:
Get data from internet;
Generate image based on a User Control that uses the data from step 1 as data source;
In the User Control I have Grid & StackPanel & some Text and Image controls;
When some of the Images use local resources from the installation folder (/Assets/images/...)
One of them which I used as a background is selected by user from the phone's photo library, so I have to set the source using C# code behind.
However, when it runs under the background, it get the OutOfMemoryException, some troubleshooting so far:
When I run the process in the "front", everything works fine;
If I comment out the update progress, and create the image directly, it also works fine;
If I don't set the background image, it also works fine;
The OutOfMemoryException was thrown out during var bmp = new WriteableBitmap(480, 800);
I already shrink the image size from 1280*768 to 800*480, I think it is the bottom line for a full screen background image, isn't it?
After some research, I found out this problem occurs because it exceeded the 11 MB limitation for a Periodic Task.
I tried use the DeviceStatus.ApplicationCurrentMemoryUsage to track the memory usage:
-- the limitation is 11,534,336 (bit)
-- when background agent started, even without any task in it, the memory usage turns to be 4,648,960
-- When get update from internet, it grew up to 5,079,040
-- when finished, it dropped back to 4,648,960
-- When the invoke started (to generate image from the User Control), it grew up to 8,499,200
Well, I guess that's the problem, there is little memory available for it to render the image via WriteableBitmap.
Any idea how to work out this problem?
Is there a better method to generate an image from a User Control / or anything else?
Actually the original image might only be 100 kb or around, however, when rendering by WriteableBitmap, the file size (as well as the required memory size I guess) might grew up to 1-2MB.
Or can I release the memory from anywhere?
==============================================================
BTW, when this Code Project article says I can use only 11MB memory in a Periodic Task;
However, this MSDN article says that I can use up to 20 MB or 25MB with Windows Phone 8 Update 3;
Which is correct? And why am I in the first situation?
==============================================================
Edit:
Speak of the debugger, it also stated in the MSDN article:
When running under the debugger, memory and timeout restrictions are suspended.
But why would I still hit the limitation?
==============================================================
Edit:
Well, I found something seems to be helpful, I will check on them for now, suggestions are still welcome.
http://writeablebitmapex.codeplex.com/
http://suchan.cz/2012/07/pro-live-tiles-for-windows-phone/
http://notebookheavy.com/2011/12/06/microsoft-style-dynamic-tiles-for-windows-phone-mango/
==============================================================
The code to generate the image:
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
var customBG = new ImageUserControl();
customBG.Measure(new Size(480, 800));
var bmp = new WriteableBitmap(480, 800); //Thrown the **OutOfMemoryException**
bmp.Render(customBG, null);
bmp.Invalidate();
using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
{
filename = "/Shared/NewBackGround.jpg";
using (var stream = isf.OpenFile(filename, System.IO.FileMode.OpenOrCreate))
{
bmp.SaveJpeg(stream, 480, 800, 0, 100);
}
}
}
The XAML code for the ImageUserControl:
<UserControl blabla... d:DesignHeight="800" d:DesignWidth="480">
<Grid x:Name="LayoutRoot">
<Image x:Name="nBackgroundSource" Stretch="UniformToFill"/>
//blabla...
</Grid>
</UserControl>
The C# code behind the ImageUserControl:
public ImageUserControl()
{
InitializeComponent();
LupdateUI();
}
public void LupdateUI()
{
DataInfo _dataInfo = new DataInfo();
LayoutRoot.DataContext = _dataInfo;
try
{
using (var isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var isoFileStream = isoStore.OpenFile("/Shared/BackgroundImage.jpg", FileMode.Open, FileAccess.Read))
{
BitmapImage bi = new BitmapImage();
bi.SetSource(isoFileStream);
nBackgroundSource.Source = bi;
}
}
}
catch (Exception) { }
}
When DataInfo is another Class within the settings page that hold data get from the internet:
public class DataInfo
{
public string Wind1 { get { return GetValueOrDefault<string>("Wind1", "N/A"); } set { if (AddOrUpdateValue("Wind1", value)) { Save(); } } }
public string Wind2 { get { return GetValueOrDefault<string>("Wind2", "N/A"); } set { if (AddOrUpdateValue("Wind2", value)) { Save(); } } }
//blabla...
}
If I comment out the update progress, and create the image directly, it also works fine I think you should focus on that part. It seems to indicate some memory isn't freed after the update. Make sure all the references used during the update process go out of scope before rendering the picture. Forcing a garbage collection can help too:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect(); // Frees the memory that was used by the finalizers
Another thing to consider is that the debugger is also using a lot of memory. Do a real test by compiling your project in "Release" mode and deploying on a phone to make sure you're running out of memory.
Still, I have already been in that situation so I know it may not be enough. The point is: some libraries in the .NET Framework are loaded lazily. For instance, if your update process involves downloading some data, then the background agent will load the network libraries. Those libraries can't be unloaded and will waste some of your agent's memory. That's why, even by freeing all the memory you used during the update process, you won't reach back the same amount of free memory you had when starting the background agent. Seeing this, what I did in one of my app was to span the workload of the background agent across two executions. Basically, when the agents executes:
Check in the isolated storage if there's pending data to be processed. If not, just execute the update process and store all needed data in the isolated storage
If there is pending data (that is, in the next execution), generate the picture and clear the data
It means the picture will be generated only once every hour instead of once every 30 minutes, so use this workaround only if everything else fail.
The larger memory limit is for background audio agents, it clearly states that in the documentation. You're stuck with 11 MB, which can really be a big pain when you're trying to do something smart with pictures in the background.
480x800 adds a MB to your memory because it takes 4 bytes for every pixels, so in the end it's around 1.22MB. When compressed in JPEG, then yes - it makes sense that it's only around 100KB. But whenever you use WriteableBitmap, it gets loaded into memory.
One of the things you could try before forcing the GC.Collect as mentioned in another answer is to null things out even before they go out of scope - whether it's a BitmapImage or a WriteableBitmap. Other than that you can try removing the Image object from the Grid programmatically when you're done and setting the Source of it also to be null.
Are there any other WriteableBitmap, BitmapImage or Image objects you're not showing us?
Also, try without the debugger. I've read somewhere that it adds another 1-2MB which is a lot when you have only 11 MB. Although, if it crashes so quickly with the debugger, I wouldn't risk it even if it suddenly seams OK without the debugger. But just for testing purposes you can give it a shot.
Do you need to use the ImageUserControl? Can you try doing step by step creating Image and all the other objects, without the XAML, so you can measure memory in each and every step to see at which point it goes through the roof?

How to check if sound effect is playing (Looping a BG Song)

Here is the setup I am rolling at the moment. Given this, how can I tell when the _BGMusic SoundEfect is over? I am running a game loop that cycles a few times a second and I ultimately want to loop this song.
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
SoundEffect _BGMUSIC;
public Page1() {
...
LoadSound("sfx/piano.wav", out _BGMUSIC);
...
}
private void LoadSound(String SoundFilePath, out SoundEffect Sound) {
// For error checking, assume we'll fail to load the file.
Sound = null;
try {
// Holds informations about a file stream.
StreamResourceInfo SoundFileInfo =
App.GetResourceStream(new Uri(SoundFilePath, UriKind.Relative));
// Create the SoundEffect from the Stream
Sound = SoundEffect.FromStream(SoundFileInfo.Stream);
FrameworkDispatcher.Update();
} catch (NullReferenceException) {
// Display an error message
MessageBox.Show("Couldn't load sound " + SoundFilePath);
}
}
From this code it looks like you have just stored the sound in Sound but you don't show where you implement the Sound.Play() method. While there isn't a Sound.IsPlaying boolean which would probably solve your problem, there is a Sound.Duration property which you may use to solve your problem, especially if you couple that with a Timer (which can be used in tandem to set off a flag to show whether or not the sound is playing.
Also something I have never used but just found is the Sound.IsDisposed property. I would definitely look into this as well because it may be exactly for what you are looking.
Alright so the answer to this question is actually very simple and outlined in Microsoft's MSDN page very nicely (for once).
To implement it with what I have up above currently you need to do the following.
Create a global SoundEffectInstance
SoundEffectInstance _BGMUSICINSTANCE;
In my example I initialize the SoundEffect in my main method so underneath it I want to initialize the SoundEffectInstance.
_BGMUSICINSTANCE = _BGMUSIC.CreateInstance();
Now that the instance is loaded and ready we can set its loop property to true. You can do this also in the main method or wherever you want. It's a global
_BGMUSICINSTANCE.IsLooped = true;
Finally play the song
_BGMUSICINSTANCE.Play();
Reference page
http://msdn.microsoft.com/en-us/library/dd940203(v=xnagamestudio.31).aspx

WIAScanner class is scanning only in flatbed. Device properties are null...What is wrong?

I am using this existing class from http://miljenkobarbir.com/using-a-scanner-without-dialogs-in-net/
and it is not working at all. It scans from the flatbed when it is supposed to scan from the document feeder. The only thing that works is that it detects my device which is wireless by the way but the device properties are null. No exception gets thrown.
P.S I am calling the Scan method from another thread not the UI thread but I used a lock like this below
List<Image> images = null;
lock(this)
{
images = WIAScanner.Scan("deviceID"); // This gets called but does not return back to control
}

InvalidOperationException - object is currently in use elsewhere - red cross

I have a C# desktop application in which one thread that I create continously gets an image from a source(it's a digital camera actually) and puts it on a panel(panel.Image = img) in the GUI(which must be another thread as it is the code-behind of a control.
The application works but on some machines I get the following error at random time intervals(unpredictable)
************** Exception Text **************
System.InvalidOperationException: The object is currently in use elsewhere.
Then the panel turns into a red cross, red X - i think this is the invalid picture icon that is editable from the properties. The application keeps working but the panel is never updated.
From what I can tell this error comes from the control's onpaint event where I draw something else on the picture.
I tried using a lock there but no luck :(
The way I call the function that puts the image on the panel is as follows:
if (this.ReceivedFrame != null)
{
Delegate[] clients = this.ReceivedFrame.GetInvocationList();
foreach (Delegate del in clients)
{
try
{
del.DynamicInvoke(new object[] { this,
new StreamEventArgs(frame)} );
}
catch { }
}
}
this is the delegate:
public delegate void ReceivedFrameEventHandler(object sender, StreamEventArgs e);
public event ReceivedFrameEventHandler ReceivedFrame;
and this is how the function inside the control code-behind registers to it:
Camera.ReceivedFrame +=
new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame);
I also tried
del.Method.Invoke(del.Target, new object[] { this, new StreamEventArgs(b) });
instead of
del.DynamicInvoke(new object[] { this, new StreamEventArgs(frame) });
but no luck
Does anyone know how I could fix this error or at least catch the error somehow and make the thread put the images on the panel once again?
This is because Gdi+ Image class is not thread safe. Hovewer you can avoid InvalidOperationException by using lock every time when you need to Image access, for example for painting or getting image size:
Image DummyImage;
// Paint
lock (DummyImage)
e.Graphics.DrawImage(DummyImage, 10, 10);
// Access Image properties
Size ImageSize;
lock (DummyImage)
ImageSize = DummyImage.Size;
BTW, invocation is not needed, if you will use the above pattern.
I had a similar problem with the same error message but try as I might, locking the bitmap didn't fix anything for me. Then I realized I was drawing a shape using a static brush. Sure enough, it was the brush that was causing the thread contention.
var location = new Rectangle(100, 100, 500, 500);
var brush = MyClass.RED_BRUSH;
lock(brush)
e.Graphics.FillRectangle(brush, location);
This worked for my case and lesson learned: Check all the reference types being used at the point where thread contention is occurring.
Seems to me, that the same Camera object is used several times.
E.g. try to use a new buffer for each received frame. It seems to me, that while the picture box is drawing the new frame, your capture library fills that buffer again. Therefore on faster machines this might not be an issue, with slower machines it might be an issue.
I've programmed something similar once, after each received frame, we had to request to receive the next frame and set the NEW frame receive buffer in that request.
If you can not do that, copy the received frame from the camera first to a new buffer and append that buffer to a queue, or just use 2 alternating buffers and check for overruns. Either use myOutPutPanel.BeginInvoke to call the camera_ReceivedFrame method, or better have a thread running, which checks the queue, when it has a new entry it calls mnyOutPutPanel.BeginInvoke to invoke your method to set the new buffer as image on the panel.
Furthermore, once you received the buffer, use the Panel Invoke Method to invoke the setting of the image (guarantee that it runs in the window thread and not the thread from your capture library).
The example below can be called from any thread (capture library or other separate thread):
void camera_ReceivedFrame(object sender, StreamEventArgs e)
{
if(myOutputPanel.InvokeRequired)
{
myOutPutPanel.BeginInvoke(
new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame),
sender,
e);
}
else
{
myOutPutPanel.Image = e.Image;
}
}
I think this is multithreading problem
Use windows golden rule and update the panel in the main thread use panel.Invoke
This should overcome cross threading exception

Categories