BililiveRecorder/BililiveRecorder.Flv/Pipeline/Rules/UpdateTimestampOffsetRule.cs

183 lines
6.6 KiB
C#
Raw Normal View History

2021-04-20 20:41:26 +08:00
using System;
using System.Collections.Generic;
using System.Linq;
namespace BililiveRecorder.Flv.Pipeline.Rules
{
public class UpdateTimestampOffsetRule : ISimpleProcessingRule
{
private static readonly ProcessingComment comment1 = new ProcessingComment(CommentType.Unrepairable, "GOP 内音频或视频时间戳不连续");
private static readonly ProcessingComment comment2 = new ProcessingComment(CommentType.Unrepairable, "出现了无法计算偏移量的音视频偏移");
2021-04-20 20:41:26 +08:00
public void Run(FlvProcessingContext context, Action next)
{
context.PerActionRun(this.RunPerAction);
next();
}
private bool CheckIfNormal(IEnumerable<Tag> data) => !data.Any2((a, b) => a.Timestamp > b.Timestamp);
private IEnumerable<PipelineAction?> RunPerAction(FlvProcessingContext context, PipelineAction action)
{
if (action is PipelineDataAction data)
{
var isNormal = this.CheckIfNormal(data.Tags);
if (isNormal)
{
yield return data;
yield break;
}
if (!(this.CheckIfNormal(data.Tags.Where(x => x.Type == TagType.Audio)) && this.CheckIfNormal(data.Tags.Where(x => x.Type == TagType.Video))))
{
// 音频或视频自身就有问题,没救了
yield return PipelineDisconnectAction.Instance;
2021-04-20 20:41:26 +08:00
context.AddComment(comment1);
yield break;
}
else
{
var oc = new OffsetCalculator();
2021-04-20 20:41:26 +08:00
foreach (var tag in data.Tags)
oc.AddTag(tag);
2021-04-20 20:41:26 +08:00
if (oc.Calculate(out var videoOffset))
2021-04-20 20:41:26 +08:00
{
if (videoOffset != 0)
2021-04-20 20:41:26 +08:00
{
context.AddComment(new ProcessingComment(CommentType.TimestampOffset, $"音视频时间戳偏移, D: {videoOffset}"));
2021-04-20 20:41:26 +08:00
foreach (var tag in data.Tags)
if (tag.Type == TagType.Video)
tag.Timestamp += videoOffset;
}
2021-04-20 20:41:26 +08:00
yield return data;
yield break;
}
else
{
context.AddComment(comment2);
yield return PipelineDisconnectAction.Instance;
yield break;
}
2021-04-20 20:41:26 +08:00
}
}
else
yield return action;
}
/// <summary>
/// 音视频偏差量计算
/// </summary>
private class OffsetCalculator
{
/*
*
*
* 使
* */
private Tag? lastAudio = null;
private readonly Stack<Tag> tags = new Stack<Tag>();
private int maxOffset = int.MaxValue;
private int minOffset = int.MinValue;
public void AddTag(Tag tag)
{
if (tag.Type == TagType.Audio)
{
this.ReduceOffsetRange(this.lastAudio, tag);
this.lastAudio = tag;
}
else if (tag.Type == TagType.Video)
{
this.tags.Push(tag);
}
else
throw new ArgumentException("unexpected tag type");
}
public bool Calculate(out int offset)
{
{
var last = this.lastAudio;
this.lastAudio = null;
this.ReduceOffsetRange(last, null);
}
if (this.minOffset == this.maxOffset)
{
// 理想情况允许偏移范围只有一个值
offset = this.minOffset;
return true;
}
else if (this.minOffset < this.maxOffset)
{
// 允许偏移的值是一个范围
if (this.minOffset != int.MinValue)
{
if (this.maxOffset != int.MaxValue)
{
// 有一个有效范围,取平均值
offset = (int)(((long)this.minOffset + this.maxOffset) / 2L);
return true;
}
else
{
// 无效最大偏移,以最小偏移为准
offset = this.minOffset + 1;
return true;
}
}
else
{
if (this.maxOffset != int.MaxValue)
{
// 无效最小偏移,以最大偏移为准
offset = this.maxOffset - 1;
return true;
}
else
{
// 无效结果
offset = 0;
return false;
}
}
}
else
{
// 范围无效
offset = 0;
return false;
}
}
private void ReduceOffsetRange(Tag? leftAudio, Tag? rightAudio)
{
while (this.tags.Count > 0)
{
var video = this.tags.Pop();
if (leftAudio is not null)
{
var min = leftAudio.Timestamp - video.Timestamp;
if (this.minOffset < min)
this.minOffset = min;
}
if (rightAudio is not null)
{
var max = rightAudio.Timestamp - video.Timestamp;
if (this.maxOffset > max)
this.maxOffset = max;
}
}
}
}
2021-04-20 20:41:26 +08:00
}
}