Strange behavior with WPF MediaElement - c#

I am current using MediaElement to play a variety of different files and I seem to have most of it working.
One thing I noticed is that Audio files (in this case mp3's specifically) refuse to play on the first attempt. Sometimes you can hear a millisecond (very unattractive) worth of sound. More like a blip and then nothing. Any subsequent attempt to load music works just fine, odd. Videos will play on the first attempt, and so will streamed media. This seems to only apply to local audio files.
The code that starts both audio and video files are pretty much identical.
private void lvVideos_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var depObj = e.OriginalSource as DependencyObject;
if (depObj != null)
{
var parent = depObj.FindVisualAncestor<ListViewItem>();
if (parent != null && lvVideos.SelectedItem != null)
{
State = PlayState.Closed;
Video video = lvVideos.SelectedItem as Video;
if (video == null) return;
lblTrackName.Text = video.Title;
MediaPlayer.Source = null;
MediaPlayer.Source = new Uri(video.Location);
CurrentMedia = MediaType.Video;
State = PlayState.Playing;
}
}
}
private void lvMusic_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var depObj = e.OriginalSource as DependencyObject;
if (depObj != null)
{
var parent = depObj.FindVisualAncestor<ListViewItem>();
if (parent != null && lvMusic.SelectedItem != null)
{
State = PlayState.Closed;
Music song = lvMusic.SelectedItem as Music;
if (song == null) return;
lblTrackName.Text = song.Title;
MediaPlayer.Source = null;
MediaPlayer.Source = new Uri(song.Location);
CurrentMedia = MediaType.Music;
State = PlayState.Playing;
}
}
}
As you can see I attempted to null the source property prior to loading the audio to no avail. I have managed to come up with a dirty hack of a workaround. Which involved setting the source to a file that is gaurenteed to fail (the app's .exe) and playing it as the app initialized. This allows for the first music file loaded to play properly.
Has anybody else come across this before? and are there any fixes?
EDIT: omg I feel stupid. apparently the culprit was mediaElement.ScrubbingEnabled = true; which (by the documentation) is a seemingly useful option, perhaps it should only be enabled for remote streams?

Apparently the culprit was mediaElement.ScrubbingEnabled = true; which (by the documentation) is a seemingly useful option, perhaps it should only be enabled for remote streams?

Related

Memory Leak playing videos on Windows IoT | UWP

I've built an app that can read video files from an USB drive and switch between them using physical buttons. The app works well for a while, but after a while the device (DragonBoard 410c, latest Windows Insider Preview Build 15051) crashes due to the fact that all memory has been consumed by the app.
Looking at the processes in the device portal, I can see the "Working Set" memory jump each time I switch a video file while the "Private Working Set" roughly stays the same (around 30MB).
Here's how I load the video file:
C#
private IReadOnlyList<StorageFile> _videofiles
// list all available video files
public void Init(){
var queryOptions = new QueryOptions();
queryOptions.FolderDepth = depth;
foreach (var fileType in fileTypes)
{
queryOptions.FileTypeFilter.Add(fileType);
}
var query = KnownFolders.RemovableDevices.CreateFileQueryWithOptions(queryOptions);
_videofiles = await query.GetFilesAsync();
}
private async void SelectVideo(int videoId)
{
StorageFile videofile = _videofiles.Where(x => x.DisplayName == videoId.ToString()).FirstOrDefault();
if (videofile != null)
{
Debug.WriteLine($"Video {videofile.DisplayName} was selected");
var stream = await videofile.OpenAsync(FileAccessMode.Read);
VideoPlayer.SetSource(stream, videofile.FileType);
}
}
// since the button interrupt is not on the UI thread, SelectVideo() is called like this
private async void SelectVideoMarshalled(int videoId)
{
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
SelectVideo(videoId);
});
}
XAML
<ContentControl x:Name="VideoPlayer" Content="{x:Bind ViewModel.VideoPlayer, Mode=OneWay}"/>
I have tried running GC.Collect() manually in several places, but no luck yet. Any ideas?
Since you have a StorageFile object, I recommend using the Source property and the file's Path instead of SetSource and opening the Stream manually.
Additionally, you should always null out the MediaElement when you're done with it (best done in OnNavigatingFrom).
Here's your code, simplified:
private void SelectVideo(string videoId)
{
var videofile = _videofiles.FirstOrDefault(x => x.DisplayName == videoId.ToString());
if (videofile == null) return;
Debug.WriteLine($"Video {videofile.DisplayName} was selected");
VideoPlayer.Source = new Uri(videofile.Path);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
VideoPlayer.Stop();
VideoPlayer.Source = null;
base.OnNavigatedFrom(e);
}
I also have a side comment, you can x:Bind event handlers to the ViewModel.
For example, if your video file list is a ListView of string:
public void VideosListView_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e?.AddedItems?.Count > 0)
{
var fileDisplayName = e.AddedItems.FirstOrDefault() as string;
if (!string.IsNullOrEmpty(fileDisplayName))
SelectVideo(fileDisplayName);
}
}
Notice I only need to change the method signature to public and then in the XAML you can do this:
<ListView ItemsSource="{x:Bind ViewModel.VideoFiles, Mode=OneTime}"
SelectionChanged="{x:Bind ViewModel.VideosListView_OnSelectionChanged}"/>
No need to marshal back to the UI thread :)
Lastly, you can check out the demo here on GitHub where I've implemented something similar to this.
Turns out my code was fine after all. I had a Windows Update stuck / failing several times which I didn't notice.
When the update finally completed successfully the memory leaks were gone.

Determing the file type during a DragOver event : UWP Media learning project

I am trying to write a practice Drag and Drop program.
This program will display a picture (JPG or PNG or Bitmap) when one is dropped on it. It will play a music file when dropped on it and it will play a video when a video file is dropped on it.
I would like the drop Icon to indicate what kind of file is about to be dropped when the DragOver event is fired. And if the file type is not compatible with my program I want the Icon to be able to indicate that as well.
I have been going through the DragEventArgs class for weeks trying to figure out a way of being able to tell the difference between the 3 types of media file but I have come up empty. Can anyone help with this?
You can use the DragUIOverride to customize the look of the dragged object.
You can set it with the DragOver event on the target element:
private async void Grid_DragOver(object sender, DragEventArgs e)
{
e.DragUIOverride.Caption = "Some caption";
e.DragUIOverride.IsCaptionVisible = true;
e.DragUIOverride.IsContentVisible = true;
e.DragUIOverride.IsGlyphVisible = true;
e.AcceptedOperation = DataPackageOperation.Copy;
//check the type of the file
var items = await e.DataView.GetStorageItemsAsync();
if (items.Any())
{
var storageFile = items[0] as StorageFile;
if ( storageFile.FileType == ".jpg" )
{
e.DragUIOverride.SetContentFromBitmapImage(
new BitmapImage(new Uri("ms-appx:///Assets/jpgIcon.png")));
}
else if ( storageFile.FileType == "png" )
{
e.DragUIOverride.SetContentFromBitmapImage(
new BitmapImage(new Uri("ms-appx:///Assets/pngIcon.png")));
}
//...
else
{
//for disallowed file types
e.AcceptedOperation = DataPackageOperation.None;
e.DragUIOverride.SetContentFromBitmapImage(
new BitmapImage(new Uri("ms-appx:///Assets/errorIcon.png")));
}
}
}

Trying to play multiple videos in wpf but it only took the last video

I don't know if i am doing it correctly.
Here I'm trying to play all the videos from the video folder in wpf C# but it only plays the last video , how can i fix this
// Go to video folder.
var videosFolder = KnownFolders.VideosLibrary;
// Here i get all the files in the video folder.
var files = await videosFolder.GetFilesAsync();
// If there is no file do nothing.
if (files.Count == 0)
{
return;
}
// Here i am looping all the videos.
foreach (var video in files)
{
// Accessing each file in the video folder.
var file = await StorageFile.GetFileFromPathAsync(video.Path);
// ...
if (file == null)
{
return;
}
// Getting the path of a file.
var source = MediaSource.CreateFromStorageFile(file);
// ...
VideoPlayer.SetPlaybackSource(source);
}
You are not waiting for each video to complete before starting to play the next one. This is why you only see the last one actually play.
You have to subscribe to MediaEnded event from MediaElement documentations:
Occurs when the MediaElement finishes playing audio or video.
Here is a sample code (not tested!):
// subscribe once
VideoPlayer.MediaEnded += OnMediaEnded;
private void OnMediaEnded(object sender, RoutedEventArgs e)
{
var nextMediaSource = GetNextMediaSource();
if(nextMediaSource != null)
VideoPlayer.SetPlaybackSource(nextMediaSource);
}

axWindowsMediaPlayer: Black Screen on looping

I have created a Windows Form Application having axWindowsMediaPlayer control. I haven't created a playlist on it, but I have stored my .mp4 files at a particular location. I pass the path to my next video at Media Ended state. For the first time, the player receives the correct path and play. But for the second video, I can only see a black screen although the player is receiving the correct path to play.
Here is my code for Media Ended State:
private void axWindowsMediaPlayer_PlayStateChange(object sender, AxWMPLib._WMPOCXEvents_PlayStateChangeEvent e)
{
if(e.newState == 8)
{
//Getting jumpTo of selected page
var selectedElementJumpToValue = MainContentAreaBl.GetSelectedElementValue(_currentId, "jumpTo");
if (selectedElementJumpToValue != null)
{
_currentId = selectedElementJumpToValue;
if (_currentId != "menu")
{
pagination.Text = MainContentAreaBl.GetPaginationText(_currentId);
LaunchPlayer(selectedElementJumpToValue);
}
else
{
this.Hide();
this.SendToBack();
menu.BringToFront();
}
}
}
}
private void LaunchPlayer(string id)
{
string selectedElementPageTypeValue = MainContentAreaBl.GetSelectedElementPageTypeValue(id);
var playerFile = Path.Combine(Common.ContentFolderPath, MainContentAreaBl.GetSelectedElementDataPathValue(id));
if (selectedElementPageTypeValue == "video")
{
InitialiseMediaPlayer();
axShockwaveFlash.Stop();
axShockwaveFlash.Visible = false;
if (File.Exists(playerFile))
{
axWindowsMediaPlayer.URL = playerFile;
}
}
else if (selectedElementPageTypeValue == "flash")
{
InitialiseShockwaveFlash();
axWindowsMediaPlayer.close();
axWindowsMediaPlayer.Visible = false;
if (File.Exists(playerFile))
{
axShockwaveFlash.Movie = playerFile;
axShockwaveFlash.Play();
}
}
}
private void InitialiseMediaPlayer()
{
axWindowsMediaPlayer.Visible = true;
axWindowsMediaPlayer.enableContextMenu = false
axWindowsMediaPlayer.uiMode = "none";
axWindowsMediaPlayer.Dock = DockStyle.Fill;
}
When I debugged my application, I saw Media Player getting the correct path after e.newState == 10 (Ready state). What am I doing wrong?
Edit 1: I found out that after my current video enters into Media Ended state, the player is stopped from playing. Even if I write axWindowsMediaPlayer.ctlControls.play();, it doesn't affect the media player. Is this a bug from in axWindowsMediaPlayer?
I have encountered this issue before as well. The most likely cause is that you are giving the command axWindowsMediaPlayer.ctlControls.play(); while the play state is still changing ( after Media Ended, it will change to Ready state). If a command is sent to the player while the play state is changing, it won't do anything. Another possible cause of your error is that sometimes Media State 9 (transitioning) needs to be included with if(e.newState == 8) such that you have if(e.newState == 8 | e.newState==9). I have had cases where it doesn't pick up on State 8 (media ending), possibly because it happens really fast and jumps to transitioning - not completely sure of this cause but I had a player that wasn't moving to the next video in the playlist because of this happening. To solve this I did something like:
if (e.newState == 8 | e.newState== 9 | e.newState== 10)
{
if (e.newState == 8)
{ //Insert your code here
}
This would vary slightly depending on what you are trying to achieve. Another thing to watch out for is using the PlayStateChange Event to set the Video URL, this causes problems as a result of re-entry problems with WMP - see other posts for further explanation on my last comment:
here is a good one and another here. Hope this helps!

GetManifestResourceStream - how can it help caching?

i searched for embedding custom drag and drop cursor in my wpf app. i ended up with an article which i have no idea of ONE LINE in the code suggested in (it is highlighted with the comments):
private void textBlock2_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
TextBlock btn = (TextBlock)sender;
GiveFeedbackEventHandler handler = new GiveFeedbackEventHandler(DragSource_GiveFeedback);
btn.GiveFeedback += handler;
DataObject dataObj = new DataObject(btn.Text);
DragDrop.DoDragDrop(btn, dataObj, DragDropEffects.Move);
btn.GiveFeedback -= handler;
}
void DragSource_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
try
{
if (_allOpsCursor == null)
{
////////////////////////////////////////THE UNKOWN ONE LINE STARTS HERE
using (Stream cursorStream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("SimplestDragDrop.DDIcon.cur")) // AND ENDS HERE
{
_allOpsCursor = new Cursor(cursorStream);
}
}
Mouse.SetCursor(_allOpsCursor);
e.UseDefaultCursors = false;
e.Handled = true;
}
finally { }
}
i checked GetManifestResourceStream, but i still have no idea what a mainfest resource is how could it be handled and where to start to get this caching idea (mentioned in the main article) to work.
Your assembly is loaded into memory as part of the AppDomain executing under the CLR. Because of this, if the resource is embedded into the assembly as part of the compilation process, using a stream to read an in-memory byte array is faster than having to go to disk to open a file, read its contents, close the file.
Alternatives are to store a byte array representing the resource within the source code, though more or less you're arriving at the same place using GetManifestResourceStream.

Categories