Merge branch 'ffme'

This commit is contained in:
Paddy Xu
2017-07-29 22:35:23 +03:00
31 changed files with 6908 additions and 2637 deletions

2
.gitignore vendored
View File

@@ -17,8 +17,6 @@
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/

View File

@@ -35,7 +35,7 @@ namespace QuickLook.Plugin.ImageViewer
".orf", ".pef", ".ptx", ".pxn", ".r3d", ".raf", ".raw", ".rwl", ".rw2", ".rwz", ".sr2", ".srf", ".srw",
".tif", ".x3f",
// normal
".bmp", ".gif", ".ico", ".icon", ".jpg", ".jpeg", ".png", ".psd", ".svg", ".wdp", ".tiff", ".tga", ".webp"
".bmp", ".ggg", ".ico", ".icon", ".jpg", ".jpeg", ".png", ".psd", ".svg", ".wdp", ".tiff", ".tga", ".webp"
};
private Size _imageSize;
private ImagePanel _ip;
@@ -67,10 +67,15 @@ namespace QuickLook.Plugin.ImageViewer
context.SetPreferredSizeFit(_imageSize, 0.8);
else
context.PreferredSize = new Size(1024, 768);
Directory.SetCurrentDirectory(App.AppPath);
}
public void View(string path, ContextObject context)
{
// set dcraw.exe for Magick.NET
Directory.SetCurrentDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
_ip = new ImagePanel();
context.ViewerContent = _ip;
@@ -81,11 +86,12 @@ namespace QuickLook.Plugin.ImageViewer
LoadImage(_ip, path);
context.IsBusy = false;
Directory.SetCurrentDirectory(App.AppPath);
}
public void Cleanup()
{
Directory.SetCurrentDirectory(App.AppPath);
_ip = null;
}

View File

@@ -58,6 +58,24 @@ namespace QuickLook.Plugin.VideoViewer
}
}
public sealed class BooleanToVisibilityVisibleConverter : DependencyObject, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return Visibility.Visible;
var v = (bool) value;
return v ? Visibility.Visible : Visibility.Hidden;
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public sealed class BooleanToVisibilityHiddenConverter : DependencyObject, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
@@ -75,4 +93,41 @@ namespace QuickLook.Plugin.VideoViewer
throw new NotImplementedException();
}
}
public class TimeSpanToSecondsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is TimeSpan) return ((TimeSpan) value).TotalSeconds;
if (value is Duration)
return ((Duration) value).HasTimeSpan ? ((Duration) value).TimeSpan.TotalSeconds : 0d;
return 0d;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var result = TimeSpan.FromTicks((long) Math.Round(TimeSpan.TicksPerSecond * (double) value, 0));
if (targetType == typeof(TimeSpan)) return result;
if (targetType == typeof(Duration)) return new Duration(result);
return Activator.CreateInstance(targetType);
}
}
public class DurationToTimeSpanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var val = (Duration) value;
return val.HasTimeSpan ? val.TimeSpan : TimeSpan.Zero;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,104 @@
// Copyright © 2017 Paddy Xu
//
// This file is part of QuickLook program.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Xml.XPath;
namespace QuickLook.Plugin.VideoViewer.FFmpeg
{
internal class FFprobe
{
private static readonly string _probePath =
Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "FFmpeg\\",
App.Is64Bit ? "x64\\" : "x86\\", "ffprobe.exe");
private XPathNavigator infoNavigator;
public FFprobe(string media)
{
Run(media);
}
private bool Run(string media)
{
var result = string.Empty;
using (var p = new Process())
{
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = _probePath;
p.StartInfo.Arguments = $"-v quiet -print_format xml -show_streams -show_format \"{media}\"";
p.StartInfo.StandardOutputEncoding = Encoding.UTF8;
p.Start();
p.WaitForExit();
result = p.StandardOutput.ReadToEnd();
}
if (string.IsNullOrWhiteSpace(result))
return false;
ParseResult(result);
return true;
}
private void ParseResult(string result)
{
infoNavigator = new XPathDocument(new StringReader(result)).CreateNavigator();
}
public bool CanDecode()
{
var info = infoNavigator.SelectSingleNode("/ffprobe/format");
return info != null;
}
public string GetFormatName()
{
var format = infoNavigator.SelectSingleNode("/ffprobe/format/@format_name")?.Value;
return format ?? string.Empty;
}
public string GetFormatLongName()
{
var format = infoNavigator.SelectSingleNode("/ffprobe/format/@format_long_name")?.Value;
return format ?? string.Empty;
}
public Size GetViewSize()
{
var width = infoNavigator.SelectSingleNode("/ffprobe/streams/stream[@codec_type='video'][1]/@coded_width")
?.Value;
var height = infoNavigator.SelectSingleNode("/ffprobe/streams/stream[@codec_type='video'][1]/@coded_height")
?.Value;
if (width == null || height == null)
return Size.Empty;
return new Size(double.Parse(width), double.Parse(height));
}
}
}

View File

@@ -16,15 +16,19 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using Unosquare.FFmpegMediaElement;
using QuickLook.Plugin.VideoViewer.FFmpeg;
using Unosquare.FFME;
namespace QuickLook.Plugin.VideoViewer
{
public class Plugin : IViewer
{
private Size _mediaSize;
private ViewerPanel _vp;
public int Priority => int.MaxValue;
@@ -32,7 +36,9 @@ namespace QuickLook.Plugin.VideoViewer
public void Init()
{
MediaElement.FFmpegPaths.RegisterFFmpeg();
MediaElement.FFmpegDirectory =
Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "\\FFmpeg\\",
App.Is64Bit ? "x64\\" : "x86\\");
}
public bool CanHandle(string path)
@@ -40,24 +46,28 @@ namespace QuickLook.Plugin.VideoViewer
if (Directory.Exists(path))
return false;
var formats = new[]
var blacklist = new[]
{
".3g2", ".3gp", ".3gp2", ".3gpp", ".amv", ".asf", ".asf", ".avi", ".flv", ".m2ts", ".m4v", ".mkv",
".mov", ".mp4", ".mp4v", ".mpeg", ".mpg", ".ogv", ".qt", ".vob", ".webm", ".wmv"
".txt", ".jpg", ".bmp"
};
if (formats.Contains(Path.GetExtension(path).ToLower()))
return true;
if (blacklist.Contains(Path.GetExtension(path).ToLower()))
return false;
return false;
return new FFprobe(path).CanDecode();
}
public void Prepare(string path, ContextObject context)
{
using (var element = new MediaElement {Source = new Uri(path)})
{
context.SetPreferredSizeFit(new Size(element.NaturalVideoWidth, element.NaturalVideoHeight), 0.6);
}
var def = new Size(450, 450);
_mediaSize = new FFprobe(path).GetViewSize();
var windowSize = _mediaSize == Size.Empty ? def : _mediaSize;
windowSize.Width = Math.Max(def.Width, windowSize.Width);
windowSize.Height = Math.Max(def.Height, windowSize.Height);
context.SetPreferredSizeFit(windowSize, 0.6);
}
public void View(string path, ContextObject context)
@@ -66,11 +76,16 @@ namespace QuickLook.Plugin.VideoViewer
context.ViewerContent = _vp;
Debug.WriteLine("ViewerContent done");
_vp.LoadAndPlay(path);
Debug.WriteLine("LoadAndPlay done");
_vp.mediaElement.MediaOpened += (sender, e) => context.IsBusy = false;
var info = _mediaSize == Size.Empty ? "Audio" : $"{_mediaSize.Width}×{_mediaSize.Height}";
context.Title =
$"{Path.GetFileName(path)} ({_vp.mediaElement.NaturalVideoWidth}×{_vp.mediaElement.NaturalVideoHeight})";
context.IsBusy = false;
$"{Path.GetFileName(path)} ({info})";
}
public void Cleanup()

View File

@@ -53,18 +53,20 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="ffme, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8ed0ff27966cae1f, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>ffme\ffme.dll</HintPath>
</Reference>
<Reference Include="FontAwesome.WPF, Version=4.7.0.37774, Culture=neutral, PublicKeyToken=0758b07a11a4f466, processorArchitecture=MSIL">
<HintPath>..\..\packages\FontAwesome.WPF.4.7.0.9\lib\net40\FontAwesome.WPF.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="Unosquare.FFmpegMediaElement, Version=1.3.1.4, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>References\Unosquare.FFmpegMediaElement.dll</HintPath>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
@@ -82,6 +84,7 @@
<Link>Properties\GitVersion.cs</Link>
</Compile>
<Compile Include="Converters.cs" />
<Compile Include="FFmpeg\FFprobe.cs" />
<Compile Include="Plugin.cs" />
<Compile Include="ViewerPanel.xaml.cs">
<DependentUpon>ViewerPanel.xaml</DependentUpon>
@@ -101,5 +104,63 @@
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="ffme\ffme.dll" />
<Resource Include="Resources\compact-disc.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Resource>
<Resource Include="ffme\ffme.xml" />
<Content Include="ffme\ffme.pdb" />
<Content Include="ffme\ffmpeg.dll" />
<Content Include="ffme\ffmpeg.pdb" />
<Content Include="FFmpeg\x64\avcodec-57.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="FFmpeg\x64\avdevice-57.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="FFmpeg\x64\avfilter-6.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="FFmpeg\x64\avformat-57.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="FFmpeg\x64\avutil-55.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="FFmpeg\x64\ffprobe.exe">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="FFmpeg\x64\swresample-2.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="FFmpeg\x64\swscale-4.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="FFmpeg\x86\avcodec-57.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="FFmpeg\x86\avdevice-57.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="FFmpeg\x86\avfilter-6.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="FFmpeg\x86\avformat-57.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="FFmpeg\x86\avutil-55.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="FFmpeg\x86\ffprobe.exe">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="FFmpeg\x86\swresample-2.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="FFmpeg\x86\swscale-4.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -4,8 +4,8 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="ControlButtonStyle" TargetType="Grid">
<Setter Property="Height" Value="50" />
<Setter Property="Width" Value="50" />
<Setter Property="Height" Value="35" />
<Setter Property="Width" Value="35" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Center" />

View File

@@ -5,29 +5,47 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:local="clr-namespace:QuickLook.Plugin.VideoViewer"
xmlns:ffmpeg="clr-namespace:Unosquare.FFmpegMediaElement;assembly=Unosquare.FFmpegMediaElement"
xmlns:ffmpeg="clr-namespace:Unosquare.FFME;assembly=ffme"
mc:Ignorable="d"
Background="#B2454545"
d:DesignHeight="449.167" d:DesignWidth="767">
x:Name="userControl"
Background="#CC4E4E4E"
d:DesignHeight="450" d:DesignWidth="450">
<Grid>
<Grid.Resources>
<ResourceDictionary>
<local:DoubleToTimeSpanConverter x:Key="DoubleToTimeSpanConverter" />
<local:DecimalToTimeSpanConverter x:Key="DecimalToTimeSpanConverter" />
<local:TimeSpanToSecondsConverter x:Key="TimeSpanToSecondsConverter" />
<local:BooleanToVisibilityVisibleConverter x:Key="BooleanToVisibilityVisibleConverter" />
<local:BooleanToVisibilityHiddenConverter x:Key="BooleanToVisibilityHiddenConverter" />
<local:DurationToTimeSpanConverter x:Key="DurationToTimeSpanConverter" />
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="7*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<ffmpeg:MediaElement Grid.RowSpan="255" x:Name="mediaElement" />
<ffmpeg:MediaElement x:Name="mediaElement" />
<Grid Grid.Row="1">
<Image Margin="50" Source="Resources/compact-disc.png">
<Image.Style>
<Style TargetType="Image">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsOpen, ElementName=mediaElement}" Value="True" />
<Condition Binding="{Binding HasVideo, ElementName=mediaElement}" Value="False" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Visibility" Value="Visible" />
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Height="100"
Visibility="{Binding NaturalDuration.HasTimeSpan, ElementName=mediaElement, Converter={StaticResource BooleanToVisibilityVisibleConverter}}">
<Grid.Background>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Offset="0.0" Color="#00000000" />
@@ -43,40 +61,51 @@
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource Self}}"
Value="True" />
<Condition Binding="{Binding IsOpen, ElementName=mediaElement}" Value="True" />
<Condition Binding="{Binding HasVideo, ElementName=mediaElement}" Value="True" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.2" />
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.2" />
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Slider x:Name="sliderProgress" Style="{StaticResource PositionSliderStyle}"
Value="{Binding Position, ElementName=mediaElement}"
Maximum="{Binding NaturalDuration, ElementName=mediaElement, Mode=OneWay}" Grid.Row="1" />
<Slider Grid.Row="1" x:Name="sliderProgress" Style="{StaticResource PositionSliderStyle}"
IsEnabled="{Binding IsOpen, ElementName=mediaElement}"
SmallChange="{Binding FrameStepDuration, ElementName=mediaElement,Converter={StaticResource TimeSpanToSecondsConverter}}"
LargeChange="{Binding FrameStepDuration, ElementName=mediaElement, Converter={StaticResource TimeSpanToSecondsConverter}}"
Maximum="{Binding NaturalDuration, ElementName=mediaElement,Converter={StaticResource TimeSpanToSecondsConverter}}"
Value="{Binding Position, ElementName=mediaElement, Converter={StaticResource TimeSpanToSecondsConverter}}" />
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="149" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center"
Orientation="Horizontal" Margin="20,0,0,0">
<StackPanel Grid.Column="0" VerticalAlignment="Center"
Orientation="Horizontal" Margin="10,0,0,0">
<Grid x:Name="buttonMute" Style="{StaticResource ControlButtonStyle}">
<fa:ImageAwesome Height="30" Width="30" Foreground="#FFEFEFEF">
<fa:ImageAwesome Height="23" Width="23" Foreground="#FFEFEFEF">
<fa:ImageAwesome.Style>
<Style TargetType="fa:ImageAwesome">
<Setter Property="Icon" Value="VolumeOff" />
@@ -90,17 +119,18 @@
</fa:ImageAwesome.Style>
</fa:ImageAwesome>
</Grid>
<Slider Style="{StaticResource CustomSliderStyle}" Width="80" Maximum="1"
<Slider Style="{StaticResource CustomSliderStyle}" MaxWidth="75" Maximum="1"
Value="{Binding Volume, ElementName=mediaElement}"
Visibility="{Binding IsMuted, ElementName=mediaElement, Converter={StaticResource BooleanToVisibilityHiddenConverter}}" />
Visibility="{Binding IsMuted, ElementName=mediaElement, Converter={StaticResource BooleanToVisibilityHiddenConverter}}"
BorderThickness="0,0,5,0" />
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Center">
<Grid x:Name="buttonBackward" Style="{StaticResource ControlButtonStyle}">
<fa:ImageAwesome Icon="Backward" Height="27" Width="27" Foreground="#FFEFEFEF" />
<fa:ImageAwesome Icon="Backward" Height="23" Width="23" Foreground="#FFEFEFEF" />
</Grid>
<Grid Width="7" />
<Grid Width="3" />
<Grid x:Name="buttonPlayPause" Style="{StaticResource ControlButtonStyle}">
<fa:ImageAwesome Height="30" Width="30" Foreground="#FFEFEFEF">
<fa:ImageAwesome Height="23" Width="23" Foreground="#FFEFEFEF">
<fa:ImageAwesome.Style>
<Style>
<Setter Property="fa:ImageAwesome.Icon" Value="Pause" />
@@ -114,22 +144,22 @@
</fa:ImageAwesome.Style>
</fa:ImageAwesome>
</Grid>
<Grid Width="7" />
<Grid Width="3" />
<Grid x:Name="buttonStop" Style="{StaticResource ControlButtonStyle}">
<fa:ImageAwesome Icon="Stop" Height="27" Width="27" Foreground="#FFEFEFEF" />
<fa:ImageAwesome Icon="Stop" Height="21" Width="21" Foreground="#FFEFEFEF" />
</Grid>
<Grid Width="7" />
<Grid Width="3" />
<Grid x:Name="buttonForward" Style="{StaticResource ControlButtonStyle}">
<fa:ImageAwesome Icon="Forward" Height="27" Width="27" Foreground="#FFEFEFEF" />
<fa:ImageAwesome Icon="Forward" Height="23" Width="23" Foreground="#FFEFEFEF" />
</Grid>
</StackPanel>
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center"
Orientation="Horizontal" Margin="0,0,20,0">
<TextBlock FontSize="16" Foreground="#FFEFEFEF"
Text="{Binding Position, ElementName=mediaElement,Converter={StaticResource DecimalToTimeSpanConverter}}" />
<TextBlock FontSize="16" Text=" / " Foreground="#FFEFEFEF" />
<TextBlock FontSize="16" Foreground="#FFEFEFEF"
Text="{Binding NaturalDuration, StringFormat=hh:mm:ss, ElementName=mediaElement,Converter={StaticResource DoubleToTimeSpanConverter}}" />
Orientation="Horizontal" Margin="0,0,10,0">
<TextBlock FontSize="14" Foreground="#FFEFEFEF"
Text="{Binding Position, StringFormat=hh\\:mm\\:ss, ElementName=mediaElement}" />
<TextBlock FontSize="14" Text=" / " Foreground="#FFEFEFEF" />
<TextBlock FontSize="14" Foreground="#FFEFEFEF"
Text="{Binding NaturalDuration, StringFormat=hh\\:mm\\:ss, ElementName=mediaElement, Converter={StaticResource DurationToTimeSpanConverter}}" />
</StackPanel>
</Grid>
</Grid>

View File

@@ -17,9 +17,9 @@
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Unosquare.FFmpegMediaElement;
namespace QuickLook.Plugin.VideoViewer
{
@@ -30,6 +30,8 @@ namespace QuickLook.Plugin.VideoViewer
{
private readonly ContextObject _context;
private bool _wasPlaying;
public ViewerPanel(ContextObject context)
{
InitializeComponent();
@@ -47,32 +49,65 @@ namespace QuickLook.Plugin.VideoViewer
buttonBackward.MouseLeftButtonUp += (sender, e) => SeekBackward();
buttonForward.MouseLeftButtonUp += (sender, e) => SeekForward();
mediaElement.MediaErrored += ShowErrorNotification;
sliderProgress.PreviewMouseDown += (sender, e) =>
{
_wasPlaying = mediaElement.IsPlaying;
mediaElement.Pause();
};
sliderProgress.PreviewMouseUp += (sender, e) =>
{
if (_wasPlaying) mediaElement.Play();
};
mediaElement.MediaFailed += ShowErrorNotification;
mediaElement.MediaEnded += (s, e) =>
{
if (!mediaElement.NaturalDuration.HasTimeSpan)
{
mediaElement.Stop();
mediaElement.Play();
}
};
}
public void Dispose()
{
mediaElement?.Stop();
mediaElement?.Dispose();
mediaElement = null;
Debug.WriteLine("dispose done");
}
private void ResumePlaying()
{
_wasPlaying = mediaElement.IsPlaying;
}
private void SeekBackward()
{
var pos = Convert.ToDouble(mediaElement.Position);
var len = mediaElement.NaturalDuration;
var delta = TimeSpan.FromSeconds(15).TotalSeconds;
_wasPlaying = mediaElement.IsPlaying;
mediaElement.Pause();
mediaElement.Position = Convert.ToDecimal(pos - delta < 0 ? 0 : pos - delta);
var pos = mediaElement.Position;
var delta = TimeSpan.FromSeconds(15);
mediaElement.Position = pos < pos - delta ? TimeSpan.Zero : pos - delta;
if (_wasPlaying) mediaElement.Play();
}
private void SeekForward()
{
var pos = Convert.ToDouble(mediaElement.Position);
var len = mediaElement.NaturalDuration;
var delta = TimeSpan.FromSeconds(15).TotalSeconds;
_wasPlaying = mediaElement.IsPlaying;
mediaElement.Pause();
mediaElement.Position = Convert.ToDecimal(pos + delta > len ? len : pos + delta);
var pos = mediaElement.Position;
var len = mediaElement.NaturalDuration.TimeSpan;
var delta = TimeSpan.FromSeconds(15);
mediaElement.Position = pos + delta > len ? len : pos + delta;
if (_wasPlaying) mediaElement.Play();
}
private void TogglePlayPause(object sender, MouseButtonEventArgs e)
@@ -84,22 +119,21 @@ namespace QuickLook.Plugin.VideoViewer
}
[DebuggerNonUserCode]
private void ShowErrorNotification(object sender, MediaErrorRoutedEventArgs e)
private void ShowErrorNotification(object sender, ExceptionRoutedEventArgs exceptionRoutedEventArgs)
{
_context.ShowNotification("", "An error occurred while loading the video.");
mediaElement.Stop();
mediaElement?.Close();
Dispose();
throw new Exception("fallback to default viewer.");
//throw new Exception("fallback to default viewer.");
}
public void LoadAndPlay(string path)
{
mediaElement.Source = new Uri(path);
mediaElement.IsMuted = true;
mediaElement.Play();
mediaElement.MediaOpened += (sender, e) => mediaElement.IsMuted = true;
}
~ViewerPanel()

File diff suppressed because it is too large Load Diff