mirror of
https://github.com/BililiveRecorder/BililiveRecorder.git
synced 2024-11-16 11:42:22 +08:00
ToolBox: Add file stats
This commit is contained in:
parent
afd8f7a2d3
commit
2e353da446
|
@ -12,6 +12,7 @@ using BililiveRecorder.Flv.Parser;
|
|||
using BililiveRecorder.Flv.Pipeline;
|
||||
using BililiveRecorder.Flv.Writer;
|
||||
using BililiveRecorder.Flv.Xml;
|
||||
using BililiveRecorder.ToolBox.ProcessingRules;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Serilog;
|
||||
|
||||
|
@ -31,6 +32,9 @@ namespace BililiveRecorder.ToolBox.Commands
|
|||
|
||||
public int OutputFileCount { get; set; }
|
||||
|
||||
public FlvStats? VideoStats { get; set; }
|
||||
public FlvStats? AudioStats { get; set; }
|
||||
|
||||
public int IssueTypeOther { get; set; }
|
||||
public int IssueTypeUnrepairable { get; set; }
|
||||
public int IssueTypeTimestampJump { get; set; }
|
||||
|
@ -97,7 +101,8 @@ namespace BililiveRecorder.ToolBox.Commands
|
|||
// Pipeline
|
||||
using var grouping = new TagGroupReader(tagReader);
|
||||
using var writer = new FlvProcessingContextWriter(tagWriter: tagWriter, allowMissingHeader: true);
|
||||
var pipeline = new ProcessingPipelineBuilder(new ServiceCollection().BuildServiceProvider()).AddDefault().AddRemoveFillerData().Build();
|
||||
var statsRule = new StatsRule();
|
||||
var pipeline = new ProcessingPipelineBuilder(new ServiceCollection().BuildServiceProvider()).Add(statsRule).AddDefault().AddRemoveFillerData().Build();
|
||||
|
||||
// Run
|
||||
await Task.Run(async () =>
|
||||
|
@ -133,6 +138,8 @@ namespace BililiveRecorder.ToolBox.Commands
|
|||
// Result
|
||||
var response = await Task.Run(() =>
|
||||
{
|
||||
var (videoStats, audioStats) = statsRule.GetStats();
|
||||
|
||||
var countableComments = comments.Where(x => x.T != CommentType.Logging).ToArray();
|
||||
return new AnalyzeResponse
|
||||
{
|
||||
|
@ -143,6 +150,9 @@ namespace BililiveRecorder.ToolBox.Commands
|
|||
|
||||
OutputFileCount = tagWriter.OutputFileCount,
|
||||
|
||||
VideoStats = videoStats,
|
||||
AudioStats = audioStats,
|
||||
|
||||
IssueTypeOther = countableComments.Count(x => x.T == CommentType.Other),
|
||||
IssueTypeUnrepairable = countableComments.Count(x => x.T == CommentType.Unrepairable),
|
||||
IssueTypeTimestampJump = countableComments.Count(x => x.T == CommentType.TimestampJump),
|
||||
|
|
|
@ -11,6 +11,7 @@ using BililiveRecorder.Flv.Parser;
|
|||
using BililiveRecorder.Flv.Pipeline;
|
||||
using BililiveRecorder.Flv.Writer;
|
||||
using BililiveRecorder.Flv.Xml;
|
||||
using BililiveRecorder.ToolBox.ProcessingRules;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Serilog;
|
||||
|
||||
|
@ -34,6 +35,9 @@ namespace BililiveRecorder.ToolBox.Commands
|
|||
|
||||
public int OutputFileCount { get; set; }
|
||||
|
||||
public FlvStats? VideoStats { get; set; }
|
||||
public FlvStats? AudioStats { get; set; }
|
||||
|
||||
public int IssueTypeOther { get; set; }
|
||||
public int IssueTypeUnrepairable { get; set; }
|
||||
public int IssueTypeTimestampJump { get; set; }
|
||||
|
@ -118,7 +122,8 @@ namespace BililiveRecorder.ToolBox.Commands
|
|||
// Pipeline
|
||||
using var grouping = new TagGroupReader(tagReader);
|
||||
using var writer = new FlvProcessingContextWriter(tagWriter: tagWriter, allowMissingHeader: true);
|
||||
var pipeline = new ProcessingPipelineBuilder(new ServiceCollection().BuildServiceProvider()).AddDefault().AddRemoveFillerData().Build();
|
||||
var statsRule = new StatsRule();
|
||||
var pipeline = new ProcessingPipelineBuilder(new ServiceCollection().BuildServiceProvider()).Add(statsRule).AddDefault().AddRemoveFillerData().Build();
|
||||
|
||||
// Run
|
||||
await Task.Run(async () =>
|
||||
|
@ -183,6 +188,8 @@ namespace BililiveRecorder.ToolBox.Commands
|
|||
// Result
|
||||
var response = await Task.Run(() =>
|
||||
{
|
||||
var (videoStats, audioStats) = statsRule.GetStats();
|
||||
|
||||
var countableComments = comments.Where(x => x.T != CommentType.Logging).ToArray();
|
||||
return new FixResponse
|
||||
{
|
||||
|
@ -193,6 +200,9 @@ namespace BililiveRecorder.ToolBox.Commands
|
|||
NeedFix = outputPaths.Count != 1 || countableComments.Any(),
|
||||
Unrepairable = countableComments.Any(x => x.T == CommentType.Unrepairable),
|
||||
|
||||
VideoStats = videoStats,
|
||||
AudioStats = audioStats,
|
||||
|
||||
IssueTypeOther = countableComments.Count(x => x.T == CommentType.Other),
|
||||
IssueTypeUnrepairable = countableComments.Count(x => x.T == CommentType.Unrepairable),
|
||||
IssueTypeTimestampJump = countableComments.Count(x => x.T == CommentType.TimestampJump),
|
||||
|
|
11
BililiveRecorder.ToolBox/ProcessingRules/FlvStats.cs
Normal file
11
BililiveRecorder.ToolBox/ProcessingRules/FlvStats.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace BililiveRecorder.ToolBox.ProcessingRules
|
||||
{
|
||||
public class FlvStats
|
||||
{
|
||||
public int FrameCount { get; set; }
|
||||
public double FramePerSecond { get; set; }
|
||||
public Dictionary<int, int>? FrameDurations { get; set; }
|
||||
}
|
||||
}
|
56
BililiveRecorder.ToolBox/ProcessingRules/StatsRule.cs
Normal file
56
BililiveRecorder.ToolBox/ProcessingRules/StatsRule.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BililiveRecorder.Flv;
|
||||
using BililiveRecorder.Flv.Pipeline;
|
||||
|
||||
namespace BililiveRecorder.ToolBox.ProcessingRules
|
||||
{
|
||||
public class StatsRule : ISimpleProcessingRule
|
||||
{
|
||||
private readonly List<int> audioTimestamp = new List<int>();
|
||||
private readonly List<int> videoTimestamp = new List<int>();
|
||||
|
||||
public void Run(FlvProcessingContext context, Action next)
|
||||
{
|
||||
foreach (var action in context.Actions)
|
||||
{
|
||||
if (action is PipelineDataAction data)
|
||||
{
|
||||
foreach (var tag in data.Tags)
|
||||
{
|
||||
if (tag.Type == TagType.Video)
|
||||
{
|
||||
this.videoTimestamp.Add(tag.Timestamp);
|
||||
}
|
||||
else if (tag.Type == TagType.Audio && tag.Flag == TagFlag.None)
|
||||
{
|
||||
this.audioTimestamp.Add(tag.Timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
public (FlvStats video, FlvStats audio) GetStats() => (this.CalculateOne(this.videoTimestamp), this.CalculateOne(this.audioTimestamp));
|
||||
|
||||
public FlvStats CalculateOne(List<int> timestamps)
|
||||
{
|
||||
var stat = new FlvStats
|
||||
{
|
||||
FrameCount = timestamps.Count,
|
||||
FrameDurations = timestamps.Select((time, i) => i == 0 ? 0 : time - timestamps[i - 1])
|
||||
.Skip(1)
|
||||
.GroupBy(x => x)
|
||||
.ToDictionary(x => x.Key, x => x.Count())
|
||||
.OrderByDescending(x => x.Value)
|
||||
.ThenByDescending(x => x.Key)
|
||||
.ToDictionary(x => x.Key, x => x.Value)
|
||||
};
|
||||
stat.FramePerSecond = 1000d / stat.FrameDurations.Select(x => x.Key * ((double)x.Value / timestamps.Count)).Sum();
|
||||
|
||||
return stat;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,14 +12,14 @@
|
|||
xmlns:local="clr-namespace:BililiveRecorder.WPF.Pages"
|
||||
xmlns:model="clr-namespace:BililiveRecorder.WPF.Models"
|
||||
xmlns:c="clr-namespace:BililiveRecorder.WPF.Converters"
|
||||
xmlns:tool="clr-namespace:BililiveRecorder.ToolBox.Commands;assembly=BililiveRecorder.ToolBox"
|
||||
xmlns:t="clr-namespace:BililiveRecorder.ToolBox.Commands;assembly=BililiveRecorder.ToolBox"
|
||||
xmlns:tr="clr-namespace:BililiveRecorder.ToolBox.ProcessingRules;assembly=BililiveRecorder.ToolBox"
|
||||
mc:Ignorable="d" DataContext="{x:Null}"
|
||||
d:DesignHeight="600" d:DesignWidth="900"
|
||||
Title="ToolboxAutoFixPage">
|
||||
<Border Background="Transparent" AllowDrop="True" Drop="FileNameTextBox_Drop">
|
||||
<Grid Margin="20" >
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
@ -69,15 +69,41 @@
|
|||
<TextBlock Text="注:不分析也可以进行修复操作" VerticalAlignment="Center" HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="NormalAnalyzeResult" DataType="{x:Type tool:AnalyzeResponse}">
|
||||
<DataTemplate x:Key="FlvStatsPanel" DataType="{x:Type tr:FlvStats}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" HorizontalAlignment="Center" Text="{Binding FrameCount,StringFormat=帧数量 {0} 个}"/>
|
||||
<TextBlock Grid.Row="1" HorizontalAlignment="Center" Text="{Binding FramePerSecond,StringFormat=平均FPS {0:F2}}"/>
|
||||
<ListView Grid.Row="2" ItemsSource="{Binding FrameDurations}" Margin="5" BorderThickness="1"
|
||||
BorderBrush="{DynamicResource SystemControlBackgroundAccentBrush}">
|
||||
<ListView.Resources>
|
||||
<!-- 忽略此处的一个 Error,是 Visual Studio 的 bug -->
|
||||
<!-- Ignore the following Error, caused by Visual Studio bug -->
|
||||
<ResourceDictionary Source="pack://application:,,,/ModernWpf;component/DensityStyles/Compact.xaml" />
|
||||
</ListView.Resources>
|
||||
<ListView.View>
|
||||
<GridView AllowsColumnReorder="False">
|
||||
<GridViewColumn Header="帧时长" DisplayMemberBinding="{Binding Key,StringFormat=\{0\} ms}"/>
|
||||
<GridViewColumn Header="帧数量" DisplayMemberBinding="{Binding Value}"/>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="NormalAnalyzeResult" DataType="{x:Type t:AnalyzeResponse}">
|
||||
<Grid Margin="5">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBox Grid.Row="0" IsReadOnly="True" Text="{Binding InputPath}" ui:ControlHelper.Header="文件:"/>
|
||||
<TextBox Grid.Row="0" IsReadOnly="True" Text="{Binding InputPath}"/>
|
||||
<TextBlock Grid.Row="1" HorizontalAlignment="Center" FontSize="24" Text="无需修复" Foreground="Green"
|
||||
Visibility="{Binding NeedFix,Converter={StaticResource InvertBooleanToVisibilityCollapsedConverter}}"/>
|
||||
<TextBlock Grid.Row="1" HorizontalAlignment="Center" FontSize="24" Text="需要修复" Foreground="Red"
|
||||
|
@ -96,6 +122,27 @@
|
|||
<TextBlock Text="{Binding IssueTypeOther,StringFormat=其他问题 {0} 处}"/>
|
||||
<TextBlock Text="{Binding IssueTypeUnrepairable,StringFormat=无法修复的问题 {0} 处}"/>
|
||||
</StackPanel>
|
||||
<Grid Grid.Row="4" Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel HorizontalAlignment="Center" Grid.ColumnSpan="3">
|
||||
<TextBlock HorizontalAlignment="Center" FontSize="20" Text="统计数据"/>
|
||||
<TextBlock HorizontalAlignment="Center" Text="仅供参考,有问题的文件的统计可能不准确"/>
|
||||
</StackPanel>
|
||||
<Separator Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" TextAlignment="Center" FontSize="16" Text="视频"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="2" TextAlignment="Center" FontSize="16" Text="音频"/>
|
||||
<ContentControl Grid.Row="2" Grid.Column="0" Content="{Binding VideoStats}" ContentTemplate="{StaticResource FlvStatsPanel}"/>
|
||||
<ContentControl Grid.Row="2" Grid.Column="2" Content="{Binding AudioStats}" ContentTemplate="{StaticResource FlvStatsPanel}"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<c:NullValueTemplateSelector x:Key="SelectorTemplate" Normal="{StaticResource NormalAnalyzeResult}" Null="{StaticResource NullAnalyzeResult}"/>
|
||||
|
|
Loading…
Reference in New Issue
Block a user