You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lal/pkg/httpflv/flv_file_pump.go

150 lines
3.7 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Copyright 2021, Chef. All rights reserved.
// https://github.com/q191201771/lal
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package httpflv
import (
"time"
"github.com/q191201771/naza/pkg/mock"
)
var Clock = mock.NewStdClock()
// FlvFilePumpOption
//
// 读取flv文件将tag按时间戳间隔缓慢类似于ffmpeg的-re返回
//
type FlvFilePumpOption struct {
IsRecursive bool // 如果为true则循环返回文件内容类似于ffmpeg的-stream_loop -1
}
var defaultFlvFilePumpOption = FlvFilePumpOption{
IsRecursive: false,
}
type FlvFilePump struct {
option FlvFilePumpOption
}
type ModFlvFilePumpOption func(option *FlvFilePumpOption)
func NewFlvFilePump(modOptions ...ModFlvFilePumpOption) *FlvFilePump {
option := defaultFlvFilePumpOption
for _, fn := range modOptions {
fn(&option)
}
return &FlvFilePump{option: option}
}
type OnPumpFlvTag func(tag Tag) bool
// Pump
//
// @param onFlvTag 如果回调中返回false则停止Pump
//
func (f *FlvFilePump) Pump(filename string, onFlvTag OnPumpFlvTag) error {
// 一次性将文件所有内容读入内存,后续不再读取文件
tags, err := ReadAllTagsFromFlvFile(filename)
if err != nil {
return err
}
return f.PumpWithTags(tags, onFlvTag)
}
// PumpWithTags @return error 暂时只做预留目前只会返回nil
//
func (f *FlvFilePump) PumpWithTags(tags []Tag, onFlvTag OnPumpFlvTag) error {
var totalBaseTs uint32 // 整体的基础时间戳。每轮最后更新
var hasReadThisBaseTs bool
var thisBaseTs uint32 // 每一轮的第一个tag时间戳
var prevTagTs uint32 // 上一个tag的时间戳
var hasReadTotalFirstTag bool
var totalFirstTagTs uint32 // 第一轮的第一个tag的时间戳
var totalFirstTagTick int64 // 第一轮的第一个tag的物理时间
const addTsBetweenRound = 1
// 循环一次,代表遍历文件一次
for roundIndex := 0; ; roundIndex++ {
Log.Debugf("new round. index=%d", roundIndex)
hasReadThisBaseTs = false
// 遍历所有tag数据
for _, tag := range tags {
// metadata只在第一轮发送一次
if tag.IsMetadata() {
if totalBaseTs == 0 {
tag.Header.Timestamp = 0
if !onFlvTag(tag) {
return nil
}
}
continue
}
// 修改时间戳
// 使得不同轮依然线性增长
if !hasReadThisBaseTs {
// 本轮第一个tag
thisBaseTs = tag.Header.Timestamp
hasReadThisBaseTs = true
tag.Header.Timestamp = totalBaseTs
} else {
tag.Header.Timestamp = totalBaseTs + tag.Header.Timestamp - thisBaseTs
}
// 修改时间戳
// 如果时间戳比前一个tag的还小可能发生了跳跃我们直接设置为上一包的值+1然后不sleep直接发送
if tag.Header.Timestamp < prevTagTs {
tag.Header.Timestamp = prevTagTs + 1
}
if hasReadTotalFirstTag {
// 当前时间戳与第一轮的第一个tag的时间戳差值
diffTs := tag.Header.Timestamp - totalFirstTagTs
// 当前物理时间与第一轮的第一个tag的物理时间差值
diffTick := Clock.Now().UnixNano()/1000000 - totalFirstTagTick
// 如果还没到物理时间差值就sleep
if diffTick < int64(diffTs) {
Clock.Sleep(time.Duration(int64(diffTs)-diffTick) * time.Millisecond)
}
} else {
// 第一轮的第一个tag记录下来
totalFirstTagTick = Clock.Now().UnixNano() / 1000000
totalFirstTagTs = tag.Header.Timestamp
hasReadTotalFirstTag = true
}
if !onFlvTag(tag) {
return nil
}
prevTagTs = tag.Header.Timestamp
}
totalBaseTs = prevTagTs + addTsBetweenRound
if !f.option.IsRecursive {
break
}
}
return nil
}