Track start and end position of seek in mediaelement [UWP] - c#

I have a XAML based Windows 10 UWP application which uses a MediaElement to play videos. For analytics purpose, when a user plays a video, I want to track the start and end positions of a seek operation.
The only event I can find is SeekCompleted, in which I can get the end position using mediaElement.Position but I can't see any way to identify the start position. Also CurrentStateChanged event is not fired when the seek operation starts. How do I get the start position?
Code that I am using:
mediaElement.CurrentStateChanged += MediaElement_StateChanged;
mediaElement.SeekCompleted += MediaElement_SeekCompleted;
//Play the video
var mediaSource = MediaSource.CreateFromUri(new Uri(streamUrl));
var mediaPlaybackItem = new MediaPlaybackItem(mediaSource);
mediaElement.SetPlaybackSource(mediaSource);
public void MediaElement_StateChanged(object sender, RoutedEventArgs e)
{
var mediaElement = sender as MediaElement;
var state = mediaElement.CurrentState;
var position = mediaElement.Position;
}
public void MediaElement_SeekCompleted(object sender, RoutedEventArgs e)
{
var position = mediaElement.Position;
}
Both methods get called only after the seek operation is completed and so I can get only the end position. I need the start position as well.

The default transport controls do not raise any event when you start seeking. To get this you could modify them so that such an event is raised.
There's documentation on customizing the transport controls at https://learn.microsoft.com/en-us/windows/uwp/controls-and-patterns/custom-transport-controls
These show how to get programmatic access to the Slider used for seeking. You could add a handler for a suitable event (perhaps DragStarting, Holding, or ManipulationStarted?) and then record the current position then.

Related

Update LiveTile when Song Changes

Basically, I am using this sample.
http://social.msdn.microsoft.com/Forums/en-US/43295c90-43e8-4b08-8a25-958a1c3d0a0b/explanation-on-windowsuixamlmediaxamlrenderingbackgroundtask
An XAMLrenderingBackgroundTask which updates my live tile with album art, text, etc. Works great - however I can't get it to run when the song changes. I have no clue how I can trigger this task when the song changes.
I have a separate BackgroundTask that is continuously running and receives events for Song changes, etc. But I don't know of a way to trigger this XAMLrenderingBackgroundTask myself without adding triggers like System time changed, etc. It must be possible, I see other apps updating images on tiles when a song changes.
I tried implementing the XAMLrenderingBGTask with the Media BackgroundTask and attempting to update the live tile when the media changed but it crashes due to some call being marshalled from another thread which I kind of expected.
Now I am stuck. Here is my backgroundtask for media player, MediaOpened event should trigger tile change for new song, but I can't..
public sealed class BackgroundAudioTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
systemmediatransportcontrol = SystemMediaTransportControls.GetForCurrentView();
systemmediatransportcontrol.ButtonPressed += systemmediatransportcontrol_ButtonPressed;
systemmediatransportcontrol.PropertyChanged += systemmediatransportcontrol_PropertyChanged;
systemmediatransportcontrol.IsEnabled = true;
systemmediatransportcontrol.IsPauseEnabled = true;
systemmediatransportcontrol.IsPlayEnabled = true;
systemmediatransportcontrol.IsNextEnabled = true;
systemmediatransportcontrol.IsPreviousEnabled = true;
systemmediatransportcontrol.IsFastForwardEnabled = true;
systemmediatransportcontrol.IsRewindEnabled = true;
//Add handlers for MediaPlayer
BackgroundMediaPlayer.Current.CurrentStateChanged += Current_CurrentStateChanged;
BackgroundMediaPlayer.Current.MediaOpened += MediaPlayer_MediaOpened;
BackgroundMediaPlayer.Current.MediaEnded += MediaPlayer_MediaEnded;
BackgroundMediaPlayer.Current.MediaFailed += mediaPlayer_MediaFailed;
BackgroundMediaPlayer.Current.VolumeChanged += Current_VolumeChanged;
BackgroundTaskStarted.Set();
backgroundtaskrunning = true;
}
I know it's a little bit late :D
you can use This to update tiles .
scenario 2 I think can help you .
in MediaOpened event you can update tile

Link Media Element on WPF with Label and Slider

I have linked the import button with a media element so i can get the song to play.
// Create OpenFileDialog
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
// Set filter for file extension and default file extension
dlg.DefaultExt = ".txt";
dlg.Filter = "WAV Files (*.wav)|*.wav|MP3 Files (*.mp3)|*.mp3|MP4 Files (*.mp4)|*.mp4|WMA Files (*.wma)|*.wma|SWA (*.swa)|*.swa";
// Display OpenFileDialog by calling ShowDialog method
Nullable<bool> result = dlg.ShowDialog();
// Get the selected file name and display in a TextBox
if (result == true)
{
// Open document
meMedia1.Source = new Uri(dlg.FileName);
meMedia1.Play();
//txtFileLocation.Text = filename;
Now, the sound plays but what I want to do is link a slider so they can skip some of the song and also a label above the slider so that it read how long into the song it is. This is how my application looks now to give you an idea.
http://i.stack.imgur.com/sVtrd.png
Thank You.
EDIT : Got the seek to change the song position but I still cant get it manually moving to the time of the song, for example if I skip to the middle of the song, and let the song finish my slider will still be in the middle and I want it to be at the end.
One approach is to create a DispatcherTimer that ticks every 200-800ms (depending on your preference for update speed) that syncs the slider to the player's current Position. That code might look similar to this:
// In the class members area
private DispatcherTimer _timer = null;
// In your constructor/loaded method
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(500);
_timer.Tick += _timer_tick;
// Timer's tick method
void _timer_tick(object sender, EventArgs e)
{
// Convert duration to an integer percentage based on current position of
// playback and update the slider control
TimeSpan ts = meMedia1.NaturalDuration.TimeSpan;
int percent = int( meMedia1.Position / ts.Seconds * 100 );
mySliderControl.Value = percent;
}
Note that this assumes you have a Slider whose Min is 0 and Max is 100. You can bump it up to 0-1000 (and change the math accordingly) to get finer granularity. This also doesn't allow the slider to push user interaction back to the player, but gives you an idea of one way to get the opposite. You can add an event handler to the Slider such that when the user begins interacting, this _timer is stopped ( _timer.Stop() ) so updates to the media position stop updating the slider and instead start doing your slider -> media position updates instead. Then when the user lets go of the slider, turn the _timer back on ( _timer.Start() ).

MediaPlayer class listening for NaturalDuration.HasTimeSpan change

I am writing a custom audio player in c# using the MediaPlayer class. I have implemented a scroll bar so the user can seek through a track and this is where I am having the problem.
WHen the user selects an audio track (loaded from an xml playlist) the app calculates the length in seconds of the track and sets this as the max value for the scroll bar. This all works fine except the NaturalDuration.TimeSpan property sometimes returns 0 rather than the amount. I have proved this by adding a loop that exits when NaturalDuration.HasTimeSpan is true then returns the NaturalDuration.TimeSpan value.
My question is how can I just get the NaturalDuration.TimeSpan when the NaturalDuration.HasTimeSpan is changed to true?
The correct way to do this is to handle the MediaPlayer.MediaOpened event:
mediaPlayer = new MediaPlayer();
mediaPlayer.MediaOpened += MediaPlayer_MediaOpened;
mediaPlayer.Open(new Uri(mediaFilePath, UriKind.Absolute));
...
private void MediaPlayer_MediaOpened(object sender, EventArgs e)
{
if (mediaPlayer.NaturalDuration.HasTimeSpan)
{
SliderMaximum = mediaPlayer.NaturalDuration.TimeSpan.TotalSeconds;
mediaPlayer.Play();
}
}

WPF Multitouch - How to get all touchpoints?

We are evaluating touchscreen keyboards and for one we need to track 10 fingers at the same time. The problem is that the touchscreen driver is very wonky (and there is no fixed version). It sends out 2500+ events for the FrameReported event each second for so many fingers. There is just no way to handle all those, even if we discard 90% at the beginning. It's simply impossible to keep up and do anything meaningful with the data.
Instead of System.Windows.Input.Touch.FrameReported, I also tried to use the (Preview) TouchMove events of the window; Same problem here.
So now I wanted, instead of using events, to poll in a separate Thread, but I cannot find information on how to get all the current touchpoints.
The only thing I found is a WinForms hack, but that isn't an option, since then I will be unable to render any WPF controls in my window.
Any solutions?
Edit 1:
This is the code, that handles all the move events:
private void UserControlTouchMove(object sender, TouchEventArgs e)
{
//Update Position of the corresponding point
var touch = e.GetTouchPoint(this);
var id = touch.TouchDevice.Id;
e.Handled = true;
var position = touch.Position;
//update finger on display, quick and dirty
if (m_ShowFingers)
{
foreach (var finger in m_Fingers)
{
if (id == (int)finger.DataContext)
{
finger.RenderTransform = new TranslateTransform(position.X - HalfFingerSize, position.Y - HalfFingerSize);
break;
}
}
}
}

Is there anyway to know when the screen has been updated/refreshed (OpenGL or DirectX maybe?)

I currently have an application I'm writing in c# (using .NET) that requires me to start a timer as soon as a user sees an image on screen up until they respond with a key press.
Now I realise that practically this is very difficult given the monitor input lag and response time, the time the keyboard takes to physically send the message, the OS to process it, etc.
But I'm trying my best to reduce it down to mostly a constant error (the response time results will be used to compare one user to the next so a constant error isn't really an issue). However annoying hurdle is the variable caused by the monitor refresh rate, as I gather when my onPaint message is called and done with, it doesn't mean the image has actually been processed and sent from the graphics buffer?
Unfortunately time restrictions and other commitments would realistically restrict me to continuing this task in c# for windows.
So what I was wondering was if either handling all the drawing in OpenGL or DirectX or better still for me if it is possible to just using either OpenGL or DirectX to create an event when the screen is updated?
Another suggestion given to me previously was regarding V-Sync, if I switch this off is the image sent as soon as it is drawn? as opposed to sending images at a set rate synchronised to the monitor refresh rate?
You must render your graphic in a separate thread in order to:
Use vertical synchronisation to have a precise timing of the effective display of your image.
Get the precise timing of your user input (since user interface is not on the same thread than the render loop.
Initialise Direct3D to enable the VSync during render :
// DirectX example
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.BackBufferCount = 1;
presentParams.PresentationInterval = PresentInterval.One;
device = new Device(...
Perform the render in a separate thread:
Thread renderThread = new Thread(RenderLoop);
renderThread.Start();
shouldDisplayImageEvent = new AutoResetEvent();
Then use the following render loop:
void RenderLoop()
{
while(applicationActive)
{
device.BeginScene();
// Other rendering task
if (shouldDisplayImageEvent.WaitOne(0))
{
// Render image
// ...
userResponseStopwatch = new Stopwatch();
userResponseStopwatch.Start();
}
device.EndScene();
device.Present();
}
}
Then handle the user input :
void OnUserInput(object sender, EventArgs e)
{
if (userResponseStopwatch != null)
{
userResponseStopwatch.Stop();
float userResponseDuration = userResponseStopwatch.ElapsedMillisecond - 1000 / device.DisplayMode.RefreshRate - displayDeviceDelayConstant;
userResponseStopwatch = null;
}
}
You now use the shouldDisplayImageEvent.Set() event trigger to display the image as needed and start the stop watch.
First enable the VSync on your application idle loop :
// DirectX example
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.BackBufferCount = 1;
presentParams.PresentationInterval = PresentInterval.One;
device = new Device(...
Application.Idle += new EventHandler(OnApplicationIdle);
// More on this here : http://blogs.msdn.com/tmiller/archive/2005/05/05/415008.aspx
internal void OnApplicationIdle(object sender, EventArgs e)
{
Msg msg = new Msg();
while (true)
{
if (PeekMessage(out msg, IntPtr.Zero, 0, 0, 0))
break;
}
// Clearing render
// ...
if (displayImage)
{
// Render image
// ...
renderTime = DateTime.now();
}
device.Present();
}
With the vsync enabled, the device.Present function block until the next frame synchronisation, so if you compute the time between renderTime and the user input time and remove the display device delay + 16.67ms you should get your user response delay.

Categories

Resources