Add parsing for track specific avstream progress data

This commit is contained in:
Ingo Oppermann
2024-09-05 13:33:01 +02:00
parent 70ffb805b4
commit 7831992936
3 changed files with 492 additions and 47 deletions

View File

@@ -653,7 +653,7 @@ func (p *parser) Progress() Progress {
continue
}
progress.Input[i].AVstream = av.export()
progress.Input[i].AVstream = av.export(io.Type)
}
progress.Started = p.stats.initialized

View File

@@ -991,6 +991,378 @@ func TestParserProgressPlayout(t *testing.T) {
}, progress)
}
func TestParserProgressPlayoutVideo(t *testing.T) {
parser := New(Config{
LogLines: 20,
}).(*parser)
parser.Parse([]byte(`ffmpeg.inputs:[{"url":"playout:https://cdn.livespotting.com/vpu/e9slfpe3/z60wzayk.m3u8","format":"playout","index":0,"stream":0,"type":"video","codec":"h264","coder":"h264","bitrate_kbps":0,"duration_sec":0.000000,"language":"und","fps":20.666666,"pix_fmt":"yuvj420p","width":1280,"height":720}]`))
parser.Parse([]byte(`ffmpeg.outputs:[{"url":"/dev/null","format":"flv","index":0,"stream":0,"type":"video","codec":"h264","coder":"libx264","bitrate_kbps":0,"duration_sec":0.000000,"language":"und","fps":25.000000,"pix_fmt":"yuvj420p","width":1280,"height":720},{"url":"/dev/null","format":"mp4","index":1,"stream":0,"type":"video","codec":"h264","coder":"copy","bitrate_kbps":0,"duration_sec":0.000000,"language":"und","fps":20.666666,"pix_fmt":"yuvj420p","width":1280,"height":720}]`))
parser.Parse([]byte(`ffmpeg.progress:{"inputs":[{"index":0,"stream":0,"frame":7,"keyframe":1,"packet":11,"size_kb":226,"size_bytes":42}],"outputs":[{"index":0,"stream":0,"frame":7,"keyframe":1,"packet":0,"q":0.0,"size_kb":0,"size_bytes":5,"extradata_size_bytes":32},{"index":1,"stream":0,"frame":11,"packet":11,"q":-1.0,"size_kb":226}],"frame":7,"packet":0,"q":0.0,"size_kb":226,"time":"0h0m0.56s","speed":0.4,"dup":0,"drop":0}`))
parser.Parse([]byte(`avstream.progress:{"id":"playout:https://cdn.livespotting.com/vpu/e9slfpe3/z60wzayk.m3u8","url":"https://cdn.livespotting.com/vpu/e9slfpe3/z60wzayk.m3u8","stream":0,"queue":140,"aqueue":42,"dup":5,"drop":8,"enc":7,"looping":true,"duplicating":true,"gop":"key","mode":"live","input":{"state":"running","packet":148,"size_kb":1529,"time":5},"output":{"state":"running","packet":8,"size_kb":128,"time":1},"swap":{"url":"","status":"waiting","lasturl":"","lasterror":""},"video":{"queue":99,"dup":5,"drop":96,"enc":23,"input":{"state":"running","packet":248,"size_kb":1250,"time":8},"output":{"state":"running","packet":149,"size_kb":748,"time":5},"codec":"h264","profile":578,"level":31,"pix_fmt":"yuv420p","width":1280,"height":720},"audio":{"queue":175,"dup":0,"drop":0,"enc":1,"input":{"state":"running","packet":431,"size_kb":4,"time":8},"output":{"state":"running","packet":256,"size_kb":1,"time":5},"codec":"aac","profile":1,"level":-99,"sample_fmt":"fltp","sampling_hz":44100,"layout":"mono","channels":1}}`))
progress := parser.Progress()
require.Equal(t, Progress{
Started: true,
Input: []ProgressIO{
{
Address: "playout:https://cdn.livespotting.com/vpu/e9slfpe3/z60wzayk.m3u8",
Index: 0,
Stream: 0,
Format: "playout",
Type: "video",
Codec: "h264",
Coder: "h264",
Frame: 7,
Keyframe: 1,
FPS: 0,
Packet: 11,
PPS: 0,
Size: 42,
Bitrate: 0,
Pixfmt: "yuvj420p",
Quantizer: 0,
Width: 1280,
Height: 720,
Sampling: 0,
Layout: "",
Channels: 0,
AVstream: &AVstream{
Input: AVstreamIO{
State: "running",
Packet: 248,
Time: 8,
Size: 1250 * 1024,
},
Output: AVstreamIO{
State: "running",
Packet: 149,
Time: 5,
Size: 748 * 1024,
},
Aqueue: 0,
Queue: 99,
Dup: 5,
Drop: 96,
Enc: 23,
Looping: true,
Duplicating: true,
GOP: "key",
Mode: "live",
Swap: AVStreamSwap{
URL: "",
Status: "waiting",
LastURL: "",
LastError: "",
},
Codec: "h264",
Profile: 578,
Level: 31,
Pixfmt: "yuv420p",
Width: 1280,
Height: 720,
},
},
},
Output: []ProgressIO{
{
Address: "/dev/null",
Index: 0,
Stream: 0,
Format: "flv",
Type: "video",
Codec: "h264",
Coder: "libx264",
Frame: 7,
Keyframe: 1,
FPS: 0,
Packet: 0,
PPS: 0,
Size: 5,
Bitrate: 0,
Extradata: 32,
Pixfmt: "yuvj420p",
Quantizer: 0,
Width: 1280,
Height: 720,
Sampling: 0,
Layout: "",
Channels: 0,
AVstream: nil,
},
{
Address: "/dev/null",
Index: 1,
Stream: 0,
Format: "mp4",
Type: "video",
Codec: "h264",
Coder: "copy",
Frame: 11,
FPS: 0,
Packet: 11,
PPS: 0,
Size: 231424,
Bitrate: 0,
Pixfmt: "yuvj420p",
Quantizer: -1,
Width: 1280,
Height: 720,
Sampling: 0,
Layout: "",
Channels: 0,
AVstream: nil,
},
},
Frame: 7,
Packet: 0,
FPS: 0,
PPS: 0,
Quantizer: 0,
Size: 231424,
Time: 0.56,
Bitrate: 0,
Speed: 0.4,
Drop: 0,
Dup: 0,
}, progress)
}
func TestParserProgressPlayoutAudioVideo(t *testing.T) {
parser := New(Config{
LogLines: 20,
}).(*parser)
parser.Parse([]byte(`ffmpeg.inputs:[{"url":"playout:http://192.168.1.220/memfs/source.m3u8","format":"playout","index":0,"stream":0,"type":"video","codec":"h264","coder":"h264","bitrate_kbps":0,"duration_sec":0.000000,"language":"und","profile":578,"level":31,"fps":25.000000,"pix_fmt":"yuv420p","width":1280,"height":720},{"url":"playout:http://192.168.1.220/memfs/source.m3u8","format":"playout","index":0,"stream":1,"type":"audio","codec":"aac","coder":"aac","bitrate_kbps":0,"duration_sec":0.000000,"language":"und","profile":1,"level":-99,"sample_fmt":"fltp","sampling_hz":44100,"layout":"mono","channels":1}]`))
parser.Parse([]byte(`ffmpeg.outputs:[{"url":"pipe:","format":"null","index":0,"stream":0,"type":"video","codec":"h264","coder":"copy","bitrate_kbps":0,"duration_sec":0.000000,"language":"und","profile":578,"level":31,"fps":25.000000,"pix_fmt":"yuv420p","width":1280,"height":720},{"url":"pipe:","format":"null","index":0,"stream":1,"type":"audio","codec":"aac","coder":"copy","bitrate_kbps":0,"duration_sec":0.000000,"language":"und","profile":1,"level":-99,"sample_fmt":"fltp","sampling_hz":44100,"layout":"mono","channels":1}]`))
parser.Parse([]byte(`ffmpeg.progress:{"inputs":[{"index":0,"stream":0,"framerate":{"min":25.000,"max":25.000,"avg":25.000},"gop":{"min":50.000,"max":50.000,"avg":50.000},"frame":101,"keyframe":3,"packet":101,"size_kb":518,"size_bytes":530273},{"index":0,"stream":1,"framerate":{"min":43.083,"max":43.083,"avg":43.083},"gop":{"min":1.000,"max":1.000,"avg":1.000},"frame":174,"keyframe":174,"packet":174,"size_kb":1,"size_bytes":713}],"outputs":[{"index":0,"stream":0,"framerate":{"min":25.000,"max":25.000,"avg":25.000},"gop":{"min":50.000,"max":50.000,"avg":50.000},"frame":101,"keyframe":3,"packet":101,"q":-1.0,"size_kb":518,"size_bytes":530273,"extradata_size_bytes":0},{"index":0,"stream":1,"framerate":{"min":43.083,"max":43.083,"avg":43.083},"gop":{"min":1.000,"max":1.000,"avg":1.000},"frame":174,"keyframe":174,"packet":174,"size_kb":1,"size_bytes":713,"extradata_size_bytes":0}],"frame":101,"packet":101,"q":-1.0,"size_kb":519,"size_bytes":530986,"time":"0h0m4.3s","speed":1,"dup":0,"drop":0}`))
parser.Parse([]byte(`avstream.progress:{"id":"playout:http://192.168.1.220/memfs/source.m3u8","url":"http://192.168.1.220/memfs/source.m3u8","stream":0,"queue":124,"aqueue":218,"dup":0,"drop":0,"enc":0,"looping":true,"looping_runtime":42,"duplicating":true,"gop":"key","mode":"live","input":{"state":"running","packet":679,"size_kb":1255,"time":7},"output":{"state":"running","packet":337,"size_kb":628,"time":4},"video":{"queue":124,"dup":1,"drop":2,"enc":3,"input":{"state":"running","packet":248,"size_kb":1250,"time":7},"output":{"state":"running","packet":124,"size_kb":627,"time":4},"codec":"h264","profile":578,"level":31,"pix_fmt":"yuv420p","width":1280,"height":720},"audio":{"queue":218,"dup":5,"drop":6,"enc":1,"input":{"state":"running","packet":431,"size_kb":4,"time":7},"output":{"state":"running","packet":213,"size_kb":0,"time":4},"codec":"aac","profile":1,"level":-99,"sample_fmt":"fltp","sampling_hz":44100,"layout":"mono","channels":1},"swap":{"url":"","status":"waiting","lasturl":"","lasterror":""}}`))
progress := parser.Progress()
require.Equal(t, Progress{
Started: true,
Input: []ProgressIO{
{
Address: "playout:http://192.168.1.220/memfs/source.m3u8",
Index: 0,
Stream: 0,
Format: "playout",
Type: "video",
Codec: "h264",
Coder: "h264",
Profile: 578,
Level: 31,
Frame: 101,
Keyframe: 3,
Framerate: struct {
Min float64
Max float64
Average float64
}{25, 25, 25},
FPS: 0,
Packet: 101,
PPS: 0,
Size: 530273,
Bitrate: 0,
Pixfmt: "yuv420p",
Quantizer: 0,
Width: 1280,
Height: 720,
Samplefmt: "",
Sampling: 0,
Layout: "",
Channels: 0,
AVstream: &AVstream{
Input: AVstreamIO{
State: "running",
Packet: 248,
Time: 7,
Size: 1250 * 1024,
},
Output: AVstreamIO{
State: "running",
Packet: 124,
Time: 4,
Size: 642048,
},
Aqueue: 0,
Queue: 124,
Dup: 1,
Drop: 2,
Enc: 3,
Looping: true,
LoopingRuntime: 42,
Duplicating: true,
GOP: "key",
Mode: "live",
Swap: AVStreamSwap{
URL: "",
Status: "waiting",
LastURL: "",
LastError: "",
},
Codec: "h264",
Profile: 578,
Level: 31,
Pixfmt: "yuv420p",
Width: 1280,
Height: 720,
},
},
{
Address: "playout:http://192.168.1.220/memfs/source.m3u8",
Index: 0,
Stream: 1,
Format: "playout",
Type: "audio",
Codec: "aac",
Coder: "aac",
Profile: 1,
Level: -99,
Frame: 174,
Keyframe: 174,
Framerate: struct {
Min float64
Max float64
Average float64
}{43.083, 43.083, 43.083},
FPS: 0,
Packet: 174,
PPS: 0,
Size: 713,
Bitrate: 0,
Pixfmt: "",
Quantizer: 0,
Width: 0,
Height: 0,
Samplefmt: "fltp",
Sampling: 44100,
Layout: "mono",
Channels: 1,
AVstream: &AVstream{
Input: AVstreamIO{
State: "running",
Packet: 431,
Time: 7,
Size: 4096,
},
Output: AVstreamIO{
State: "running",
Packet: 213,
Time: 4,
Size: 0,
},
Aqueue: 0,
Queue: 218,
Dup: 5,
Drop: 6,
Enc: 1,
Looping: true,
LoopingRuntime: 42,
Duplicating: true,
GOP: "key",
Mode: "live",
Swap: AVStreamSwap{
URL: "",
Status: "waiting",
LastURL: "",
LastError: "",
},
Codec: "aac",
Profile: 1,
Level: -99,
Pixfmt: "",
Width: 0,
Height: 0,
Samplefmt: "fltp",
Sampling: 44100,
Layout: "mono",
Channels: 1,
},
},
},
Output: []ProgressIO{
{
Address: "pipe:",
Index: 0,
Stream: 0,
Format: "null",
Type: "video",
Codec: "h264",
Coder: "copy",
Profile: 578,
Level: 31,
Frame: 101,
Keyframe: 3,
Framerate: struct {
Min float64
Max float64
Average float64
}{25, 25, 25},
FPS: 0,
Packet: 101,
PPS: 0,
Size: 530273,
Bitrate: 0,
Extradata: 0,
Pixfmt: "yuv420p",
Quantizer: -1,
Width: 1280,
Height: 720,
Sampling: 0,
Layout: "",
Channels: 0,
AVstream: nil,
},
{
Address: "pipe:",
Index: 0,
Stream: 1,
Format: "null",
Type: "audio",
Codec: "aac",
Coder: "copy",
Profile: 1,
Level: -99,
Frame: 174,
Keyframe: 174,
Framerate: struct {
Min float64
Max float64
Average float64
}{43.083, 43.083, 43.083},
FPS: 0,
Packet: 174,
PPS: 0,
Size: 713,
Bitrate: 0,
Pixfmt: "",
Quantizer: 0,
Width: 0,
Height: 0,
Samplefmt: "fltp",
Sampling: 44100,
Layout: "mono",
Channels: 1,
AVstream: nil,
},
},
Frame: 101,
Packet: 101,
FPS: 0,
PPS: 0,
Quantizer: -1,
Size: 530986,
Time: 4.3,
Bitrate: 0,
Speed: 1,
Drop: 0,
Dup: 0,
}, progress)
}
func TestParserStreamMapping(t *testing.T) {
parser := New(Config{
LogLines: 20,

View File

@@ -74,43 +74,97 @@ func (avswap *ffmpegAVStreamSwap) export() AVStreamSwap {
}
}
type ffmpegAVstream struct {
Input ffmpegAVstreamIO `json:"input"`
Output ffmpegAVstreamIO `json:"output"`
Address string `json:"id"`
URL string `json:"url"`
Stream uint64 `json:"stream"`
Aqueue uint64 `json:"aqueue"`
Queue uint64 `json:"queue"`
Dup uint64 `json:"dup"`
Drop uint64 `json:"drop"`
Enc uint64 `json:"enc"`
Looping bool `json:"looping"`
LoopingRuntime uint64 `json:"looping_runtime"`
Duplicating bool `json:"duplicating"`
GOP string `json:"gop"`
Mode string `json:"mode"`
Debug interface{} `json:"debug"`
Swap ffmpegAVStreamSwap `json:"swap"`
type ffmpegAVStreamTrack struct {
Queue uint64 `json:"queue"`
Dup uint64 `json:"dup"`
Drop uint64 `json:"drop"`
Enc uint64 `json:"enc"`
Input ffmpegAVstreamIO `json:"input"`
Output ffmpegAVstreamIO `json:"output"`
Codec string `json:"codec"`
Profile int `json:"profile"`
Level int `json:"level"`
Pixfmt string `json:"pix_fmt"`
Width uint64 `json:"width"`
Height uint64 `json:"height"`
Samplefmt string `json:"sample_fmt"`
Sampling uint64 `json:"sampling_hz"`
Layout string `json:"layout"`
Channels uint64 `json:"channels"`
}
func (av *ffmpegAVstream) export() *AVstream {
return &AVstream{
Aqueue: av.Aqueue,
Queue: av.Queue,
Drop: av.Drop,
Dup: av.Dup,
Enc: av.Enc,
type ffmpegAVstream struct {
Input ffmpegAVstreamIO `json:"input"`
Output ffmpegAVstreamIO `json:"output"`
Audio ffmpegAVStreamTrack `json:"audio"`
Video ffmpegAVStreamTrack `json:"video"`
Address string `json:"id"`
URL string `json:"url"`
Stream uint64 `json:"stream"`
Aqueue uint64 `json:"aqueue"`
Queue uint64 `json:"queue"`
Dup uint64 `json:"dup"`
Drop uint64 `json:"drop"`
Enc uint64 `json:"enc"`
Looping bool `json:"looping"`
LoopingRuntime uint64 `json:"looping_runtime"`
Duplicating bool `json:"duplicating"`
GOP string `json:"gop"`
Mode string `json:"mode"`
Debug interface{} `json:"debug"`
Swap ffmpegAVStreamSwap `json:"swap"`
}
func (av *ffmpegAVstream) export(trackType string) *AVstream {
avs := &AVstream{
Looping: av.Looping,
LoopingRuntime: av.LoopingRuntime,
Duplicating: av.Duplicating,
GOP: av.GOP,
Mode: av.Mode,
Input: av.Input.export(),
Output: av.Output.export(),
Debug: av.Debug,
Swap: av.Swap.export(),
}
hasTracks := len(av.Video.Codec) != 0
if hasTracks {
var track *ffmpegAVStreamTrack = nil
if trackType == "audio" {
track = &av.Audio
} else {
track = &av.Video
}
avs.Queue = track.Queue
avs.Drop = track.Drop
avs.Dup = track.Dup
avs.Enc = track.Enc
avs.Input = track.Input.export()
avs.Output = track.Output.export()
avs.Codec = track.Codec
avs.Profile = track.Profile
avs.Level = track.Level
avs.Pixfmt = track.Pixfmt
avs.Width = track.Width
avs.Height = track.Height
avs.Samplefmt = track.Samplefmt
avs.Sampling = track.Sampling
avs.Layout = track.Layout
avs.Channels = track.Channels
} else {
avs.Queue = av.Queue
avs.Aqueue = av.Aqueue
avs.Drop = av.Drop
avs.Dup = av.Dup
avs.Enc = av.Enc
avs.Input = av.Input.export()
avs.Output = av.Output.export()
}
return avs
}
type ffmpegProgressIO struct {
@@ -218,6 +272,8 @@ type ffmpegProcessIO struct {
Type string `json:"type"`
Codec string `json:"codec"`
Coder string `json:"coder"`
Profile int `json:"profile"`
Level int `json:"level"`
// video
Pixfmt string `json:"pix_fmt"`
@@ -225,26 +281,30 @@ type ffmpegProcessIO struct {
Height uint64 `json:"height"`
// audio
Sampling uint64 `json:"sampling_hz"`
Layout string `json:"layout"`
Channels uint64 `json:"channels"`
Samplefmt string `json:"sample_fmt"`
Sampling uint64 `json:"sampling_hz"`
Layout string `json:"layout"`
Channels uint64 `json:"channels"`
}
func (io *ffmpegProcessIO) export() ProgressIO {
return ProgressIO{
Address: io.Address,
Format: io.Format,
Index: io.Index,
Stream: io.Stream,
Type: io.Type,
Codec: io.Codec,
Coder: io.Coder,
Pixfmt: io.Pixfmt,
Width: io.Width,
Height: io.Height,
Sampling: io.Sampling,
Layout: io.Layout,
Channels: io.Channels,
Address: io.Address,
Format: io.Format,
Index: io.Index,
Stream: io.Stream,
Type: io.Type,
Codec: io.Codec,
Coder: io.Coder,
Profile: io.Profile,
Level: io.Level,
Pixfmt: io.Pixfmt,
Width: io.Width,
Height: io.Height,
Samplefmt: io.Samplefmt,
Sampling: io.Sampling,
Layout: io.Layout,
Channels: io.Channels,
}
}
@@ -422,6 +482,8 @@ type ProgressIO struct {
Type string
Codec string
Coder string
Profile int
Level int
Frame uint64
Keyframe uint64
Framerate struct {
@@ -443,9 +505,10 @@ type ProgressIO struct {
Height uint64
// Audio
Sampling uint64
Layout string
Channels uint64
Samplefmt string
Sampling uint64 // Hz
Layout string // mono, stereo, ...
Channels uint64
// avstream
AVstream *AVstream
@@ -498,6 +561,16 @@ type AVstream struct {
Mode string
Debug interface{}
Swap AVStreamSwap
Codec string
Profile int
Level int
Pixfmt string
Width uint64
Height uint64
Samplefmt string
Sampling uint64
Layout string
Channels uint64
}
type Usage struct {