mirror of
https://github.com/QL-Win/QuickLook.git
synced 2025-12-24 02:00:55 +08:00
Merge branch 'ffme'
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -17,8 +17,6 @@
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
104
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/FFmpeg/FFprobe.cs
Normal file
104
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/FFmpeg/FFprobe.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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()
|
||||
|
||||
@@ -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.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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()
|
||||
|
||||
BIN
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/ffme/ffme.dll
Normal file
BIN
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/ffme/ffme.dll
Normal file
Binary file not shown.
6525
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/ffme/ffme.xml
Normal file
6525
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/ffme/ffme.xml
Normal file
File diff suppressed because it is too large
Load Diff
BIN
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/ffme/ffmpeg.dll
Normal file
BIN
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/ffme/ffmpeg.dll
Normal file
Binary file not shown.
Reference in New Issue
Block a user