ToolBox: Add file stats

This commit is contained in:
Genteure 2021-04-23 18:51:27 +08:00
parent afd8f7a2d3
commit 2e353da446
5 changed files with 140 additions and 6 deletions

View File

@ -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),

View File

@ -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),

View 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; }
}
}

View 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;
}
}
}

View File

@ -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}"/>