using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using WPFMediaKit.DirectShow.MediaPlayers;
namespace WPFMediaKit.DirectShow.Controls;
public class VideoFrame
{
public VideoFrame(BitmapSource snapshot, TimeSpan mediaTime)
{
Snapshot = snapshot;
MediaTime = mediaTime;
snapshot.Freeze();
}
public BitmapSource Snapshot { get; private set; }
public TimeSpan MediaTime { get; private set; }
}
///
/// This control is not finished. Do not use it ;)
///
public class MediaDetectorElement : Control
{
private readonly ObservableCollection m_frames;
private readonly MediaDetector m_mediaDetector;
private bool m_cancelLoadFrames;
private double m_lastEndTime;
private int m_lastFrameCount;
private double m_lastStartTime;
private ItemsControl m_videoFrameItems;
static MediaDetectorElement()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MediaDetectorElement),
new FrameworkPropertyMetadata(typeof(MediaDetectorElement)));
}
public MediaDetectorElement()
{
m_frames = new ObservableCollection();
Frames = new ReadOnlyObservableCollection(m_frames);
m_mediaDetector = CreateMediaDetector();
Loaded += MediaDetectorElement_Loaded;
}
public ReadOnlyObservableCollection Frames { get; private set; }
#region MediaLength
private static readonly DependencyPropertyKey MediaLengthPropertyKey
= DependencyProperty.RegisterReadOnly("MediaLength", typeof(double), typeof(MediaDetectorElement),
new FrameworkPropertyMetadata((double)0));
public static readonly DependencyProperty MediaLengthProperty
= MediaLengthPropertyKey.DependencyProperty;
public double MediaLength
{
get { return (double)GetValue(MediaLengthProperty); }
}
protected void SetMediaLength(double value)
{
SetValue(MediaLengthPropertyKey, value);
}
#endregion MediaLength
#region MediaSource
public static readonly DependencyProperty MediaSourceProperty =
DependencyProperty.Register("MediaSource", typeof(Uri), typeof(MediaDetectorElement),
new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(OnMediaSourceChanged)));
public Uri MediaSource
{
get { return (Uri)GetValue(MediaSourceProperty); }
set { SetValue(MediaSourceProperty, value); }
}
private static void OnMediaSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MediaDetectorElement)d).OnMediaSourceChanged(e);
}
protected virtual void OnMediaSourceChanged(DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == null)
return;
if (IsLoaded)
LoadMediaSource();
}
#endregion MediaSource
#region VideoStartTime
public static readonly DependencyProperty VideoStartTimeProperty =
DependencyProperty.Register("VideoStartTime", typeof(double), typeof(MediaDetectorElement),
new FrameworkPropertyMetadata((double)0,
new PropertyChangedCallback(
OnVideoStartTimeChanged)));
public double VideoStartTime
{
get { return (double)GetValue(VideoStartTimeProperty); }
set { SetValue(VideoStartTimeProperty, value); }
}
private static void OnVideoStartTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MediaDetectorElement)d).OnVideoStartTimeChanged(e);
}
protected virtual void OnVideoStartTimeChanged(DependencyPropertyChangedEventArgs e)
{
if (IsLoaded)
LoadVideoFrames();
}
#endregion VideoStartTime
#region VideoEndTime
public static readonly DependencyProperty VideoEndTimeProperty =
DependencyProperty.Register("VideoEndTime", typeof(double), typeof(MediaDetectorElement),
new FrameworkPropertyMetadata((double)0,
new PropertyChangedCallback(OnVideoEndTimeChanged)));
public double VideoEndTime
{
get { return (double)GetValue(VideoEndTimeProperty); }
set { SetValue(VideoEndTimeProperty, value); }
}
private static void OnVideoEndTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MediaDetectorElement)d).OnVideoEndTimeChanged(e);
}
protected virtual void OnVideoEndTimeChanged(DependencyPropertyChangedEventArgs e)
{
if (Parent != null)
LoadVideoFrames();
}
#endregion VideoEndTime
#region VideoFrameCount
public static readonly DependencyProperty VideoFrameCountProperty =
DependencyProperty.Register("VideoFrameCount", typeof(int), typeof(MediaDetectorElement),
new FrameworkPropertyMetadata(0,
new PropertyChangedCallback(
OnVideoFrameCountChanged)));
public int VideoFrameCount
{
get { return (int)GetValue(VideoFrameCountProperty); }
set { SetValue(VideoFrameCountProperty, value); }
}
private static void OnVideoFrameCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MediaDetectorElement)d).OnVideoFrameCountChanged(e);
}
protected virtual void OnVideoFrameCountChanged(DependencyPropertyChangedEventArgs e)
{
if (IsLoaded)
LoadVideoFrames();
}
#endregion VideoFrameCount
private void LoadMediaSource()
{
if (MediaSource == null)
return;
string filename = MediaSource.OriginalString;
if (m_mediaDetector == null)
return;
m_mediaDetector.Dispatcher.BeginInvoke((Action)delegate
{
m_mediaDetector.LoadMedia(filename);
SetMediaInfo();
});
}
private void LoadVideoFrames()
{
double startTime = 0;
double endTime = 0;
int videoFrameCount = 0;
startTime = VideoStartTime;
endTime = VideoEndTime;
videoFrameCount = VideoFrameCount;
if (startTime == endTime)
videoFrameCount = 1;
if (endTime < startTime)
{
m_frames.Clear();
return;
}
if (m_lastStartTime == startTime && m_lastEndTime == endTime && videoFrameCount == m_lastFrameCount)
return;
m_lastStartTime = startTime;
m_lastEndTime = endTime;
m_lastFrameCount = videoFrameCount;
m_cancelLoadFrames = true;
double timeIncrement = (endTime - startTime) / videoFrameCount;
var times = new List();
double sec = startTime;
for (int i = 0; i < videoFrameCount; i++)
{
times.Add(TimeSpan.FromSeconds(sec));
sec += timeIncrement;
}
m_mediaDetector.Dispatcher.BeginInvoke((Action)delegate
{
m_cancelLoadFrames = false;
Dispatcher.Invoke((Action)(() => m_frames.Clear()));
for (int i = 0; i < times.Count; i++)
{
if (m_cancelLoadFrames)
return;
var frame = new VideoFrame(m_mediaDetector.GetImage(times[i]),
times[i]);
Dispatcher.Invoke((Action)(() => m_frames.Add(frame)));
}
var videoframe = new VideoFrame(m_mediaDetector.GetImage(TimeSpan.FromSeconds(endTime)),
TimeSpan.FromSeconds(endTime));
Dispatcher.Invoke((Action)(() => m_frames.Add(videoframe)));
m_cancelLoadFrames = false;
});
}
private void SetMediaInfo()
{
Dispatcher.BeginInvoke(
(Action)
delegate
{
SetMediaLength(Math.Max(m_mediaDetector.AudioStreamLength.TotalSeconds, m_mediaDetector.VideoStreamLength.TotalSeconds));
LoadVideoFrames();
});
}
private void MediaDetectorElement_Loaded(object sender, RoutedEventArgs e)
{
LoadMediaSource();
}
public override void OnApplyTemplate()
{
m_videoFrameItems = GetTemplateChild("PART_VideoFrameItems") as ItemsControl;
if (m_videoFrameItems != null)
m_videoFrameItems.ItemsSource = m_frames;
base.OnApplyTemplate();
}
private static MediaDetector CreateMediaDetector()
{
MediaDetector detector = null;
/* The reset event will block our thread while
* we create an intialize the player */
var reset = new ManualResetEventSlim(false);
/* We need to create a new thread for our Dispatcher */
var t = new Thread((ThreadStart)delegate
{
detector = new MediaDetector();
/* We queue up a method to execute
* when the Dispatcher is ran.
* This will wake up the calling thread
* that has been blocked by the reset event */
detector.Dispatcher.Invoke((Action)(() => reset.Set()));
Dispatcher.Run();
})
{
Name = "MediaDetector",
IsBackground = true
};
t.SetApartmentState(ApartmentState.STA);
/* Starts the thread and creates the object */
t.Start();
/* We wait until our object is created and
* the new Dispatcher is running */
reset.Wait();
return detector;
}
}