public/silverlight/MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Browser;
using System.Globalization;
namespace SilverlightMediaElement
{
[ScriptableType]
public partial class MainPage : UserControl
{
System.Windows.Threading.DispatcherTimer _timer;
// work arounds for src, load(), play() compatibility
bool _isLoading = false;
bool _isAttemptingToPlay = false;
// variables
string _mediaUrl;
string _jsInitFunction;
string _jsCallbackFunction;
string _preload;
string _htmlid;
bool _autoplay = false;
bool _debug = false;
int _width = 0;
int _height = 0;
int _timerRate = 0;
double _bufferedBytes = 0;
double _bufferedTime = 0;
double _volume = 1;
int _videoWidth = 0;
int _videoHeight = 0;
// state
bool _isPaused = true;
bool _isEnded = false;
// dummy
bool _firedCanPlay = false;
// mediaElement.Position updates TimelineSlider.Value, and
// updating TimelineSlider.Value updates mediaElement.Position,
// this variable helps us break the infinite loop
private bool duringTickEvent = false;
private bool playVideoWhenSliderDragIsOver = false;
public MainPage(IDictionary<string, string> initParams)
{
InitializeComponent();
HtmlPage.RegisterScriptableObject("MediaElementJS", this);
// add events
media.BufferingProgressChanged += new RoutedEventHandler(media_BufferingProgressChanged);
media.DownloadProgressChanged += new RoutedEventHandler(media_DownloadProgressChanged);
media.CurrentStateChanged += new RoutedEventHandler(media_CurrentStateChanged);
media.MediaEnded += new RoutedEventHandler(media_MediaEnded);
media.MediaFailed += new EventHandler<ExceptionRoutedEventArgs>(media_MediaFailed);
media.MediaOpened += new RoutedEventHandler(media_MediaOpened);
media.MouseLeftButtonDown += new MouseButtonEventHandler(media_MouseLeftButtonDown);
CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
transportControls.Visibility = System.Windows.Visibility.Collapsed;
// get parameters
if (initParams.ContainsKey("id"))
_htmlid = initParams["id"];
if (initParams.ContainsKey("file"))
_mediaUrl = initParams["file"];
if (initParams.ContainsKey("jsinitfunction"))
_jsInitFunction = initParams["jsinitfunction"];
if (initParams.ContainsKey("jscallbackfunction"))
_jsCallbackFunction = initParams["jscallbackfunction"];
if (initParams.ContainsKey("autoplay") && initParams["autoplay"] == "true")
_autoplay = true;
if (initParams.ContainsKey("debug") && initParams["debug"] == "true")
_debug = true;
if (initParams.ContainsKey("preload"))
_preload = initParams["preload"].ToLower();
else
_preload = "";
if (!(new string[] { "none", "metadata", "auto" }).Contains(_preload)){
_preload = "none";
}
if (initParams.ContainsKey("width"))
Int32.TryParse(initParams["width"], out _width);
if (initParams.ContainsKey("height"))
Int32.TryParse(initParams["height"], out _height);
if (initParams.ContainsKey("timerate"))
Int32.TryParse(initParams["timerrate"], out _timerRate);
if (initParams.ContainsKey("startvolume"))
Double.TryParse(initParams["startvolume"], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out _volume);
if (_timerRate == 0)
_timerRate = 250;
// timer
_timer = new System.Windows.Threading.DispatcherTimer();
_timer.Interval = new TimeSpan(0, 0, 0, 0, _timerRate); // 200 Milliseconds
_timer.Tick += new EventHandler(timer_Tick);
_timer.Stop();
//_mediaUrl = "http://local.mediaelement.com/media/jsaddington.mp4";
//_autoplay = true;
// set stage and media sizes
if (_width > 0)
LayoutRoot.Width = media.Width = this.Width = _width;
if (_height > 0)
LayoutRoot.Height = media.Height = this.Height = _height;
// debug
textBox1.Visibility = (_debug) ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
textBox1.IsEnabled = false;
textBox1.Text = "id: " + _htmlid + "\n" +
"file: " + _mediaUrl + "\n";
media.AutoPlay = _autoplay;
media.Volume = _volume;
if (!String.IsNullOrEmpty(_mediaUrl)) {
setSrc(_mediaUrl);
if (_autoplay || _preload != "none")
loadMedia();
}
media.MouseLeftButtonUp += new MouseButtonEventHandler(media_MouseLeftButtonUp);
// full screen settings
Application.Current.Host.Content.FullScreenChanged += new EventHandler(DisplaySizeInformation);
Application.Current.Host.Content.Resized += new EventHandler(DisplaySizeInformation);
//FullscreenButton.Visibility = System.Windows.Visibility.Collapsed;
// send out init call
//HtmlPage.Window.Invoke("html5_MediaPluginBridge_initPlugin", new object[] {_htmlid});
try
{
HtmlPage.Window.Eval(_jsInitFunction + "('" + _htmlid + "');");
}
catch { }
}
void media_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
switch (media.CurrentState)
{
case MediaElementState.Playing:
pauseMedia();
break;
case MediaElementState.Paused:
playMedia();
break;
case MediaElementState.Stopped:
break;
case MediaElementState.Buffering:
pauseMedia();
break;
}
}
void media_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
SendEvent("click");
}
void media_MediaOpened(object sender, RoutedEventArgs e) {
_videoWidth = Convert.ToInt32(media.NaturalVideoWidth);
_videoHeight = Convert.ToInt32(media.NaturalVideoHeight);
TimeSpan duration = media.NaturalDuration.TimeSpan;
totalTimeTextBlock.Text = TimeSpanToString(duration);
UpdateVideoSize();
playPauseButton.IsChecked = true;
SendEvent("loadedmetadata");
}
void timer_Tick(object sender, EventArgs e) {
SendEvent("timeupdate");
}
void StartTimer() {
_timer.Start();
}
void StopTimer() {
_timer.Stop();
}
void WriteDebug(string text) {
textBox1.Text += text + "\n";
}
void media_MediaFailed(object sender, ExceptionRoutedEventArgs e) {
SendEvent(e.ToString());
}
void media_MediaEnded(object sender, RoutedEventArgs e) {
_isEnded = true;
_isPaused = false;
SendEvent("ended");
StopTimer();
}
void media_CurrentStateChanged(object sender, RoutedEventArgs e) {
WriteDebug("state:" + media.CurrentState.ToString());
switch (media.CurrentState)
{
case MediaElementState.Opening:
SendEvent("loadstart");
break;
case MediaElementState.Playing:
_isEnded = false;
_isPaused = false;
_isAttemptingToPlay = false;
StartTimer();
SendEvent("play");
SendEvent("playing");
break;
case MediaElementState.Paused:
_isEnded = false;
_isPaused = true;
// special settings to allow play() to work
_isLoading = false;
WriteDebug("paused event, " + _isAttemptingToPlay);
if (_isAttemptingToPlay) {
this.playMedia();
}
StopTimer();
SendEvent("paused");
break;
case MediaElementState.Stopped:
_isEnded = false;
_isPaused = true;
StopTimer();
SendEvent("paused");
break;
case MediaElementState.Buffering:
SendEvent("progress");
break;
}
}
void media_BufferingProgressChanged(object sender, RoutedEventArgs e) {
_bufferedTime = media.DownloadProgress * media.NaturalDuration.TimeSpan.TotalSeconds;
_bufferedBytes = media.BufferingProgress;
SendEvent("progress");
}
void media_DownloadProgressChanged(object sender, RoutedEventArgs e) {
_bufferedTime = media.DownloadProgress * media.NaturalDuration.TimeSpan.TotalSeconds;
_bufferedBytes = media.BufferingProgress;
if (!_firedCanPlay) {
SendEvent("loadeddata");
SendEvent("canplay");
_firedCanPlay = true;
}
SendEvent("progress");
}
void SendEvent(string name) {
/*
* INVOKE
HtmlPage.Window.Invoke("html5_MediaPluginBridge_fireEvent",
_htmlid,
name,
@"'{" +
@"""name"": """ + name + @"""" +
@", ""currentTime"":" + (media.Position.TotalSeconds).ToString() + @"" +
@", ""duration"":" + (media.NaturalDuration.TimeSpan.TotalSeconds).ToString() + @"" +
@", ""paused"":" + (_isEnded).ToString().ToLower() + @"" +
@", ""muted"":" + (media.IsMuted).ToString().ToLower() + @"" +
@", ""ended"":" + (_isPaused).ToString().ToLower() + @"" +
@", ""volume"":" + (media.Volume).ToString() + @"" +
@", ""bufferedBytes"":" + (_bufferedBytes).ToString() + @"" +
@", ""bufferedTime"":" + (_bufferedTime).ToString() + @"" +
@"}'");
*/
/*
* EVAL
HtmlPage.Window.Eval("mejs.MediaPluginBridge.fireEvent('" + _htmlid + "','" + name + "'," +
@"{" +
@"""name"": """ + name + @"""" +
@", ""currentTime"":" + (media.Position.TotalSeconds).ToString() + @"" +
@", ""duration"":" + (media.NaturalDuration.TimeSpan.TotalSeconds).ToString() + @"" +
@", ""paused"":" + (_isEnded).ToString().ToLower() + @"" +
@", ""muted"":" + (media.IsMuted).ToString().ToLower() + @"" +
@", ""ended"":" + (_isPaused).ToString().ToLower() + @"" +
@", ""volume"":" + (media.Volume).ToString() + @"" +
@", ""bufferedBytes"":" + (_bufferedBytes).ToString() + @"" +
@", ""bufferedTime"":" + (_bufferedTime).ToString() + @"" +
@"});");
* */
// setTimeout
try {
CultureInfo invCulture = CultureInfo.InvariantCulture;
HtmlPage.Window.Invoke("setTimeout", _jsCallbackFunction + "('" + _htmlid + "','" + name + "'," +
@"{" +
@"""name"": """ + name + @"""" +
@", ""currentTime"":" + (media.Position.TotalSeconds).ToString(invCulture) + @"" +
@", ""duration"":" + (media.NaturalDuration.TimeSpan.TotalSeconds).ToString(invCulture) + @"" +
@", ""paused"":" + (_isPaused).ToString().ToLower() + @"" +
@", ""muted"":" + (media.IsMuted).ToString().ToLower() + @"" +
@", ""ended"":" + (_isEnded).ToString().ToLower() + @"" +
@", ""volume"":" + (media.Volume).ToString(invCulture) + @"" +
@", ""bufferedBytes"":" + (_bufferedBytes).ToString(invCulture) + @"" +
@", ""bufferedTime"":" + (_bufferedTime).ToString(invCulture) + @"" +
@", ""videoWidth"":" + (_videoWidth).ToString() + @"" +
@", ""videoHeight"":" + (_videoHeight).ToString() + @"" +
@"});", 0);
} catch { }
}
/* HTML5 wrapper methods */
[ScriptableMember]
public void playMedia() {
WriteDebug("method:play " + media.CurrentState);
// sometimes people forget to call load() first
if (_mediaUrl != "" && media.Source == null) {
_isAttemptingToPlay = true;
loadMedia();
}
// store and trigger with the state change above
if (media.CurrentState == MediaElementState.Closed && _isLoading) {
WriteDebug("storing _isAttemptingToPlay ");
_isAttemptingToPlay = true;
}
media.Play();
_isEnded = false;
_isPaused = false;
playPauseButton.IsChecked = true;
//StartTimer();
}
[ScriptableMember]
public void pauseMedia() {
WriteDebug("method:pause " + media.CurrentState);
_isEnded = false;
_isPaused = true;
media.Pause();
StopTimer();
playPauseButton.IsChecked = false;
}
[ScriptableMember]
public void loadMedia() {
_isLoading = true;
_firedCanPlay = false;
WriteDebug("method:load " + media.CurrentState);
WriteDebug(" - " + _mediaUrl.ToString());
media.Source = new Uri(_mediaUrl, UriKind.Absolute);
//media.Play();
//media.Stop();
}
[ScriptableMember]
public void stopMedia() {
WriteDebug("method:stop " + media.CurrentState);
_isEnded = true;
_isPaused = false;
media.Stop();
StopTimer();
playPauseButton.IsChecked = false;
}
[ScriptableMember]
public void setVolume(Double volume) {
WriteDebug("method:setvolume: " + volume.ToString());
media.Volume = volume;
SendEvent("volumechange");
}
[ScriptableMember]
public void setMuted(bool isMuted) {
WriteDebug("method:setmuted: " + isMuted.ToString());
media.IsMuted = isMuted;
muteButton.IsChecked = isMuted;
SendEvent("volumechange");
}
[ScriptableMember]
public void setCurrentTime(Double position) {
WriteDebug("method:setCurrentTime: " + position.ToString());
int milliseconds = Convert.ToInt32(position * 1000);
SendEvent("seeking");
media.Position = new TimeSpan(0, 0, 0, 0, milliseconds);
SendEvent("seeked");
}
[ScriptableMember]
public void setSrc(string url) {
_mediaUrl = url;
}
[ScriptableMember]
public void setFullscreen(bool goFullscreen) {
FullscreenButton.Visibility = System.Windows.Visibility.Visible;
}
[ScriptableMember]
public void setVideoSize(int width, int height) {
this.Width = media.Width = width;
this.Height = media.Height = height;
}
[ScriptableMember]
public void positionFullscreenButton(int x, int y,bool visibleAndAbove) {
if (visibleAndAbove)
{
//FullscreenButton.Visibility = System.Windows.Visibility.Collapsed;
}
else
{
//FullscreenButton.Visibility = System.Windows.Visibility.Visible;
}
}
private void FullscreenButton_Click(object sender, RoutedEventArgs e) {
Application.Current.Host.Content.IsFullScreen = true;
//FullscreenButton.Visibility = System.Windows.Visibility.Collapsed;
}
private void DisplaySizeInformation(Object sender, EventArgs e) {
this.Width = LayoutRoot.Width = media.Width = Application.Current.Host.Content.ActualWidth;
this.Height = LayoutRoot.Height = media.Height = Application.Current.Host.Content.ActualHeight;
UpdateVideoSize();
}
#region play button
private void BigPlayButton_Click(object sender, RoutedEventArgs e)
{
playPauseButton.IsChecked = true;
PlayPauseButton_Click(sender, e);
}
private void PlayPauseButton_Click(object sender, RoutedEventArgs e)
{
bigPlayButton.Visibility = Visibility.Collapsed;
// this will be the toggle button state after the click has been processed
if (playPauseButton.IsChecked == true)
playMedia();
else
pauseMedia();
}
#endregion
#region timelineSlider
private void Seek(double percentComplete)
{
if (duringTickEvent)
throw new Exception("Can't call Seek() now, you'll get an infinite loop");
TimeSpan duration = media.NaturalDuration.TimeSpan;
int newPosition = (int)(duration.TotalSeconds * percentComplete);
media.Position = new TimeSpan(0, 0, newPosition);
// let the next CompositionTarget.Rendering take care of updating the text blocks
}
private Slider GetSliderParent(object sender)
{
FrameworkElement element = (FrameworkElement)sender;
do
{
element = (FrameworkElement)VisualTreeHelper.GetParent(element);
} while (!(element is Slider));
return (Slider)element;
}
private void LeftTrack_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
FrameworkElement lefttrack = (sender as FrameworkElement).FindName("LeftTrack") as FrameworkElement;
FrameworkElement righttrack = (sender as FrameworkElement).FindName("RightTrack") as FrameworkElement;
double position = e.GetPosition(lefttrack).X;
double width = righttrack.TransformToVisual(lefttrack).Transform(new Point(righttrack.ActualWidth, righttrack.ActualHeight)).X;
double percent = position / width;
Slider slider = GetSliderParent(sender);
slider.Value = percent;
}
private void HorizontalThumb_DragStarted(object sender, System.Windows.Controls.Primitives.DragStartedEventArgs e)
{
if (GetSliderParent(sender) != timelineSlider) return;
bool notPlaying = (media.CurrentState == MediaElementState.Paused
|| media.CurrentState == MediaElementState.Stopped);
if (notPlaying)
{
playVideoWhenSliderDragIsOver = false;
}
else
{
playVideoWhenSliderDragIsOver = true;
media.Pause();
}
}
private void HorizontalThumb_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
{
if (playVideoWhenSliderDragIsOver)
media.Play();
}
private void TimelineSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (duringTickEvent)
return;
Seek(timelineSlider.Value);
}
#endregion
#region updating current time
private void CompositionTarget_Rendering(object sender, EventArgs e)
{
duringTickEvent = true;
TimeSpan duration = media.NaturalDuration.TimeSpan;
if (duration.TotalSeconds != 0)
{
double percentComplete = (media.Position.TotalSeconds / duration.TotalSeconds);
timelineSlider.Value = percentComplete;
string text = TimeSpanToString(media.Position);
if (this.currentTimeTextBlock.Text != text)
this.currentTimeTextBlock.Text = text;
}
duringTickEvent = false;
}
private static string TimeSpanToString(TimeSpan time)
{
return string.Format("{0:00}:{1:00}", (time.Hours * 60) + time.Minutes, time.Seconds);
}
#endregion
private void MuteButton_Click(object sender, RoutedEventArgs e)
{
//media.IsMuted = (bool)muteButton.IsChecked;
setMuted((bool)muteButton.IsChecked);
}
#region fullscreen mode
private void FullScreenButton_Click(object sender, RoutedEventArgs e)
{
var content = Application.Current.Host.Content;
content.IsFullScreen = !content.IsFullScreen;
}
private void Content_FullScreenChanged(object sender, EventArgs e)
{
UpdateVideoSize();
}
private void UpdateVideoSize()
{
if (App.Current.Host.Content.IsFullScreen)
{
transportControls.Visibility = System.Windows.Visibility.Visible;
// mediaElement takes all available space
//VideoRow.Height = new GridLength(1, GridUnitType.Star);
//VideoColumn.Width = new GridLength(1, GridUnitType.Star);
}
else
{
transportControls.Visibility = System.Windows.Visibility.Collapsed;
// mediaElement is only as big as the source video
//VideoRow.Height = new GridLength(1, GridUnitType.Auto);
//VideoColumn.Width = new GridLength(1, GridUnitType.Auto);
}
}
#endregion
}
}