Quantcast
Channel: Windows Presentation Foundation (WPF) forum
Viewing all articles
Browse latest Browse all 18858

Weird behavior of WPF MediaElement/MediaPlayer seeking (set Position)

$
0
0

 I am making a small tool to play mp3 file, associate with lrc file.

The main functionality is to show and highlight the current playing paragraph, and loop-playing on current/specific paragraph.

I use MediaElement/MediaPlayer, or even with MediaTimeLine to control the seeking. it show a weird behavior that, after first setting of the position of MediaElement, the the voice/media stream is not sync with the timeline any more. For instance, when seek back to same position, you hear a little ahead of the expected position and when timeline show it should start next paragraph, but the current paragraph is not ended. the delta is arount 500ms ~ 1000ms , depending on the duration of the mp3 file.

I have tried different classes/methods to control it, even on differnt version of .net framework, the problem is same.

Did I misuse the class or there is a bug of it? please help. Thanks.

following the main code files for reference.

LrcTimeline.cs

using Microsoft.Win32;
using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
namespace LrcPlayer
{
    class LrcTimeline : MediaTimeline
    {
        private MediaElement me;// = new MediaElement();
        private ArrayList lrcLines = new ArrayList();
        private string mp3FileName;
        private RichTextBox lrc_txt;
        private int idx;
        private string pattern = @"^\s*\[(?<time>.*)\](?<content>.*)$";
        private string pattern_time = @"^\s*(?<minute>\d*)\:\s*(?<second>\d*)\.\s*(?<ms>\d*)$";
        private DispatcherTimer dispatcherTimer = new DispatcherTimer();
        private MainWindow mainWindow;
        public LrcTimeline(RichTextBox rtb, MediaElement m, MainWindow _mw)
        {
            lrc_txt = rtb;
            me = m;
            mainWindow = _mw;
            init();
        }
        public Duration GetNaturalDuration()
        {
            Duration d = base.GetNaturalDuration(me.Clock);
            return d;
        }
        private void startTimer()
        {
            dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
            dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 500);
            dispatcherTimer.Start();
        }
        private void stopTimer()
        {
            dispatcherTimer.Stop();
        }
        int repeat_adust = 0;
        public void repeat()
        {
        }
        private void playNext(TimeSpan?  x)
        {
            if (x == null)
            {
                x = me.Position;
            }
            //Console.WriteLine( "player position:["+x+"]");
            Lrc l = getCurrentLrc(); if (l == null)
            {
                //Console.WriteLine("index out of range:" + idx + "::" + lrcLines.Count);
                return;
            }
            TimeSpan ts = new TimeSpan(0,0, 0,0, repeat_adust);
            if (x - l.beginTime - l.duration> ts)
            {
                //Console.WriteLine("repeat[" + mp.Position + "] -> [" + l.beginTime + "]-> [" + l.duration + "]");
                if (mainWindow.repeating)
                {
                    Console.WriteLine("repeat[" + me.Position + "] -> lrc[" + idx + "]=[" + l.beginTime + "]-> [" + l.duration + "]");
                    me.Clock.Controller.Seek(l.beginTime, TimeSeekOrigin.BeginTime);
                    Console.WriteLine("repeat[" + me.Position + "] ts="+ts);
                }
                else
                {
                    idx++;
                    l = getCurrentLrc();
                    if (l !=null) Console.WriteLine("moveNex at[" + me.Position + "] -> lrc[" + idx + "]=[" + l.beginTime + "]-> [" + l.duration + "]");
                    highlight();
                }
            }
        }
        private void dispatcherTimer_Tick(object sender, EventArgs e)
        {
            playNext(null);
        }
        public void Play()
        {
            Lrc l = getCurrentLrc();
            if (l == null)
            {
                //Console.WriteLine("index out of range:" + idx + "::" + lrcLines.Count);
                return;
            }
            Console.WriteLine("Play: idx=" + idx + "::" + l);
            if ( me.Clock ==null) me.Clock= CreateClock();
            me.Clock.Controller.Begin();
        }
        public void Pause()
        {
            Console.WriteLine("Pause at:" + me.Position);
            me.Clock.Controller.Pause();
            //stopTimer();
        }
        public void Resume() { me.Clock.Controller.Resume(); }
        public void Stop() { me.Clock.Controller.Stop(); }
        public void Play(int idx) { }
        public void init()
        {
            //CurrentStateInvalidated += LrcTimeline_CurrentStateInvalidated;
            CurrentTimeInvalidated += LrcTimeline_CurrentTimeInvalidated;
            base.Completed += LrcTimeline_Completed;
        }
        void mp_MediaOpened(object sender, EventArgs e)
        {
            updateLastLrc();
        }
        private void updateLastLrc()
        {
            try
            {
                lrcLines.RemoveAt(lrcLines.Count - 1);
                lrc_txt.SelectAll();
                int end = lrc_txt.Selection.Start.GetOffsetToPosition(lrc_txt.Selection.End);
                Lrc last = new Lrc("", me.NaturalDuration.HasTimeSpan ? me.NaturalDuration.TimeSpan : new TimeSpan(0), end);
                addLrc(last);
                Console.WriteLine("updateLastLrc:" + lrcLines.Count + " =>[" + last + "]");
            }
            catch { }
        }
        public void reset()
        {
           idx = 0;
            repeat_adust = 0;
            me.Clock.Controller.Stop();
            me.Clock = null;
            Source = new Uri(@mp3FileName);
        }
        void mp_MediaEnded(object sender, EventArgs e)
        {
            Console.WriteLine("MediaEnded");
            mainWindow.reset();
        }
        void LrcTimeline_Completed(object sender, EventArgs e)
        {
            Console.WriteLine("Completed:" + idx+"/"+lrcLines.Count);
            mainWindow.reset();
        }
        void LrcTimeline_CurrentTimeInvalidated(object sender, EventArgs e)
        {
            Clock clock = (Clock)sender;
            //Console.WriteLine("TimeInvalidated: clock=" + clock.CurrentTime+"||| me="+me.Position);
            if (clock.CurrentTime == null) { }
            else
            {
                playNext(clock.CurrentTime);
            }
        }
        //void LrcTimeline_CurrentStateInvalidated(object sender, EventArgs e)
        //{
        //    Console.WriteLine("CurrentStateInvalidated:");
        //}
        private Lrc getCurrentLrc()
        {
            //Console.WriteLine("currentLrc:" + idx + " of " + lrcLines.Count);
            if (idx < lrcLines.Count)
            {
                Lrc l = (Lrc)lrcLines[idx];
                return l;
            }
            else
            {
                return null;
            }
        }
        private Lrc getLrc(int i)
        {
            if (i < lrcLines.Count)
            {
                Lrc l = (Lrc)lrcLines[i];
                return l;
            }
            else
            {
                return null;
            }
        }
        public bool load()
        {
            OpenFileDialog fileDialog = new OpenFileDialog();
            fileDialog.Title = "Select mp3 file";
            fileDialog.Filter = "mp3 files (*.mp3)|*.mp3";
            fileDialog.FilterIndex = 1;
            fileDialog.RestoreDirectory = true;
            if (fileDialog.ShowDialog() == true)
            {
                try
                {
                    String fileName = fileDialog.FileName;
                    mp3FileName = fileDialog.FileName;
                    Source = new Uri(@fileName);
                    fileName = fileName.Substring(0, fileName.Length - 3) + "lrc";
                    openLrc(fileName);
                    return true;
                }
                catch (Exception ex) { Console.WriteLine("Exception:" + ex.Message); return false; }
            }
            else
            {
                return false;
            }
        }
        private void openLrc(string fn)
        {
            try
            {
                StreamReader sr1 = new StreamReader(@fn, Encoding.GetEncoding("GBK"));
                string nextLine = null;
                Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
                lrcLines = new ArrayList();
                idx = 0;
                repeat_adust = 0;
                lrcLines.Add(new Lrc("", new TimeSpan(0), 0));
                lrc_txt.FontSize = 16;
                lrc_txt.Document.Blocks.Clear();
                while ((nextLine = sr1.ReadLine()) != null)
                {
                    nextLine = nextLine.Trim();
                    if (nextLine.Length == 0) continue;
                    MatchCollection matches = rgx.Matches(nextLine);
                    if (matches.Count > 0)
                    {
                        //Console.WriteLine("{0} ({1} matches):", nextLine, matches.Count);
                        foreach (Match match in matches)
                        {
                            TimeSpan beginT = getDuration(match.Groups["time"].Value);
                            string txt = match.Groups["content"].Value.Trim();
                            if (txt.Length == 0) continue;
                            lrc_txt.SelectAll();
                            int pos = lrc_txt.Selection.Start.GetOffsetToPosition(lrc_txt.Selection.End); //.Text.Length+2;
                            Lrc s = new Lrc(txt, beginT, pos + 2);
                            addLrc(s);
                            Paragraph p = new Paragraph();  // 
                            Run r = new Run(s.txt);      // 
                            p.Inlines.Add(r);
                            lrc_txt.Document.Blocks.Add(p);
                        }
                    }
                }
                lrc_txt.SelectAll();
                int end = lrc_txt.Selection.Start.GetOffsetToPosition(lrc_txt.Selection.End);
                Lrc last = new Lrc("", me.NaturalDuration.HasTimeSpan ? me.NaturalDuration.TimeSpan : new TimeSpan(0), end);
                addLrc(last);
                sr1.Close();
            }
            catch (Exception ex) { Console.WriteLine("Read Lrc file failed:" + ex.Message); }
        }
        private void addLrc(Lrc l)
        {
            if (lrcLines.Count > 0)
            {
                Lrc preLrc = (Lrc)lrcLines[lrcLines.Count - 1];
                preLrc.duration = l.beginTime - preLrc.beginTime;
            }
            lrcLines.Add(l);
        }
        private TimeSpan getDuration(string t)
        {
            //return TimeSpan.Parse(t);
            TimeSpan ret = new TimeSpan();
            Regex rgx_time = new Regex(pattern_time, RegexOptions.IgnoreCase);
            MatchCollection matches = rgx_time.Matches(t);
            foreach (Match match in matches)
            {
                int m = int.Parse(match.Groups["minute"].Value);
                int s = int.Parse(match.Groups["second"].Value);
                int ms = int.Parse(match.Groups["ms"].Value);
                ret = new TimeSpan(0, 0, m, s, ms * 10);
            }
            return ret;
        }
        private void selectSentence(int start, int length)
        {
            TextPointer newSelectionStartPointer = lrc_txt.Document.ContentStart.GetPositionAtOffset(start);
            TextPointer newSelectionEndPointer = newSelectionStartPointer.GetPositionAtOffset(length);
            lrc_txt.Selection.Select(newSelectionStartPointer, newSelectionEndPointer);
            Rect screenPos = lrc_txt.Selection.Start.GetCharacterRect(LogicalDirection.Forward);
            double offset = screenPos.Top + lrc_txt.VerticalOffset;
            lrc_txt.ScrollToVerticalOffset(offset - lrc_txt.ActualHeight / 2);
        }
        public void highlight()
        {
            Lrc s;
            try
            {
                if (idx > 0)
                {
                    s = (Lrc)lrcLines[idx - 1];
                    if (s != null)
                    {
                        //Console.WriteLine(s.ToString());
                        selectSentence(s.position, s.length);
                        lrc_txt.Selection.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Gray);
                        lrc_txt.Selection.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Normal);
                        lrc_txt.Selection.ApplyPropertyValue(TextElement.FontSizeProperty, 16.0);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("except 1:" + ex.StackTrace + "\r\nMessage:  " + ex.Message);
            }
            try
            {
                s = getCurrentLrc();// (Lrc)lrcLines[idx];
                if (s != null)
                {
                    //Console.WriteLine(s.ToString());
                    selectSentence(s.position, s.length);
                    lrc_txt.Selection.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Blue);
                    lrc_txt.Selection.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
                    lrc_txt.Selection.ApplyPropertyValue(TextElement.FontSizeProperty, 24.0);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("except 2:" + ex.StackTrace + "  \r\nMessage:  " + ex.Message);
            }
        }
    }
    public class Lrc
    {
        public string txt;
        public TimeSpan beginTime;
        public TimeSpan duration;
        public int position;
        public int length;
        public Lrc(string _t, TimeSpan _b, int _p)
        {
            txt = _t;
            beginTime = _b;
            duration = new TimeSpan(0);
            position = _p;
            length = _t.Length + 2;
        }
        public override string ToString()
        {
            string ret = "";
            ret += "[" + beginTime + "]";
            ret += "[" + duration + "]";
            ret += ", pos:[" + position + "]";
            ret += ", length:" + length;
            ret += "\t[" + txt + "]";
            return ret;
        }
    }
}

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.IO;
using System.Linq;
using System.Text;
//using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Win32;
using System.Collections;
using System.Windows.Media.Animation;
namespace LrcPlayer
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            lrctl = new LrcTimeline(lrc_txt, mp3player, this);
        }
        private Boolean playing = false;
        private bool paused = false;
        private LrcTimeline lrctl = null;
        private void btn_play_click(object sender, RoutedEventArgs e)
        {
            if (playing)
            {
                lrctl.Pause();
                paused = true;
                btn_play.Content = "Play";
            }
            else
            {
                if (paused)
                {
                    lrctl.Resume();
                }
                else
                {
                    lrctl.Play();
                }
                lrctl.highlight();
                btn_play.Content = "Pause";
            }
            playing = !playing;
        }
        public bool repeating = false;
        private void btn_repeat_click(object sender, RoutedEventArgs e)
        {
            if (repeating)
            {
                btn_repeat_last.Content = "Repeat Last";
            }
            else
            {
                btn_repeat_last.Content = "Stop Repeat";
            }
            repeating = !repeating;
        }
        private void btn_open_mp3_Click(object sender, RoutedEventArgs e)
        {
            reset();
            if (lrctl.load())
            {
                btn_repeat_last.IsEnabled = true;
                btn_play.IsEnabled = true;
                btn_reset.IsEnabled = true;
            }
        }
        public void reset()
        {
            try
            {
                playing = false;
                repeating = false;
                paused = false;
                btn_repeat_last.Content = "Repeat Last";
                //lrctl.Stop();
                lrctl.reset();
                btn_play.Content = "Play";
                lrc_txt.SelectAll();
                lrc_txt.Selection.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Black);
                lrc_txt.Selection.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Normal);
                lrc_txt.Selection.ApplyPropertyValue(TextElement.FontSizeProperty, 16.0);
                lrc_txt.ScrollToHome();
            }
            catch { }
        }
        private void btn_reset_Click(object sender, RoutedEventArgs e)
        {
            reset();
        }
    }
}

MainWindow.xaml

<Window x:Class="LrcPlayer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="392" Width="633"><Grid Margin="-1,1,1,-1"><Grid.RowDefinitions><RowDefinition Height="25"/><RowDefinition   /></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="101*"/><ColumnDefinition Width="24*"/></Grid.ColumnDefinitions><RichTextBox Grid.Row="1" Grid.Column="0" x:Name="lrc_txt"    HorizontalAlignment="Stretch" Margin="5,0,5,5" VerticalAlignment="Stretch"  IsReadOnly="True" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Grid.ColumnSpan="2" ><FlowDocument><Paragraph><Run Text="please load mp3 file. (lrc file should have same file name in the same folder)"/></Paragraph></FlowDocument></RichTextBox><StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" Height="24"  VerticalAlignment="Center" Width="auto" Grid.ColumnSpan="2" Margin="0,0,0,1"><Button x:Name="btn_open_mp3" Content="Load" HorizontalAlignment="Left" Margin="2" VerticalAlignment="Top" Height="20" Width="75" Click="btn_open_mp3_Click"/><Button x:Name="btn_play" Content="Play" Margin="2" HorizontalAlignment="Left" Height="20"  VerticalAlignment="Top" Width="54" Click="btn_play_click" RenderTransformOrigin="0.556,-2.308" IsEnabled="False"/><Button x:Name="btn_repeat_last" Content="Repeat Last" Margin="2" HorizontalAlignment="Left"  Height="20" VerticalAlignment="Top" Width="80" Click="btn_repeat_click" IsEnabled="False" /><Button x:Name="btn_reset" Content="Reset" Width="65" Margin="2" Click="btn_reset_Click" IsEnabled="False"/></StackPanel><MediaElement x:Name="mp3player" HorizontalAlignment="Left" Height="100" Margin="-180,75,0,0" Grid.Row="1" VerticalAlignment="Top" Width="100" LoadedBehavior="Manual"/></Grid></Window>


Viewing all articles
Browse latest Browse all 18858

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>