184 lines
4.2 KiB
Go
184 lines
4.2 KiB
Go
package extension
|
||
|
||
import (
|
||
"sync"
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
func TestTrackTypeConversion(t *testing.T) {
|
||
// 验证字符串转TrackType
|
||
if GetTrackType("video") != TrackVideo {
|
||
t.Error("video track type conversion failed")
|
||
}
|
||
if GetTrackType("invalid") != TrackInvalid {
|
||
t.Error("invalid track type conversion failed")
|
||
}
|
||
|
||
// 验证TrackType转字符串
|
||
if TrackVideo.String() != "video" {
|
||
t.Error("TrackVideo string conversion failed")
|
||
}
|
||
}
|
||
|
||
func TestCodecIDConversion(t *testing.T) {
|
||
// 验证名称转CodecID
|
||
if GetCodecID("H264") != CodecH264 {
|
||
t.Error("H264 codec conversion failed")
|
||
}
|
||
if GetCodecID("invalid") != CodecInvalid {
|
||
t.Error("invalid codec conversion failed")
|
||
}
|
||
|
||
// 验证CodecID转名称
|
||
if CodecH264.String() != "H264" {
|
||
t.Error("CodecH264 string conversion failed")
|
||
}
|
||
}
|
||
|
||
func TestBaseFrame(t *testing.T) {
|
||
frame := &BaseFrame{
|
||
Dts: 1000,
|
||
Pts: 1005,
|
||
VPrefixSize: 4,
|
||
Codec: CodecH264,
|
||
Track: TrackVideo,
|
||
}
|
||
|
||
if frame.DTS() != 1000 {
|
||
t.Error("DTS mismatch")
|
||
}
|
||
if frame.PrefixSize() != 4 {
|
||
t.Error("PrefixSize mismatch")
|
||
}
|
||
}
|
||
|
||
func TestFrameCacheAble(t *testing.T) {
|
||
// 测试原始帧不可缓存
|
||
raw := &FrameFromBytes{
|
||
BaseFrame: &BaseFrame{Dts: 1000},
|
||
DataPtr: []byte{1, 2, 3},
|
||
}
|
||
if raw.CacheAble() {
|
||
t.Error("raw frame should not be cacheable")
|
||
}
|
||
|
||
// 测试转换后可缓存
|
||
cached := NewFrameCacheAble(raw, false)
|
||
if !cached.CacheAble() {
|
||
t.Error("cached frame should be cacheable")
|
||
}
|
||
if cached.Size() != 3 {
|
||
t.Error("data size mismatch")
|
||
}
|
||
}
|
||
|
||
func TestFrameMerger_H264Prefix(t *testing.T) {
|
||
merger := NewFrameMerger(MergeH264Prefix)
|
||
var output []byte
|
||
|
||
// 创建测试帧(无前缀)
|
||
frame1 := &FrameFromBytes{BaseFrame: &BaseFrame{Dts: 1000, VPrefixSize: 0}, DataPtr: []byte{0x67}}
|
||
frame2 := &FrameFromBytes{BaseFrame: &BaseFrame{Dts: 1000, VPrefixSize: 0}, DataPtr: []byte{0x68}}
|
||
|
||
// 合并相同时间戳的帧
|
||
merger.InputFrame(frame1, func(dts, pts uint64, buf []byte, key bool) {
|
||
output = append(output, buf...)
|
||
})
|
||
merger.InputFrame(frame2, func(dts, pts uint64, buf []byte, key bool) {
|
||
output = append(output, buf...)
|
||
})
|
||
merger.Flush(func(dts, pts uint64, buf []byte, key bool) {
|
||
output = append(output, buf...)
|
||
})
|
||
|
||
// 验证H264前缀添加
|
||
expected := []byte{0, 0, 0, 1, 0x67, 0, 0, 0, 1, 0x68}
|
||
if string(output) != string(expected) {
|
||
t.Error("H264 prefix merge failed")
|
||
}
|
||
}
|
||
|
||
func TestFrameMerger_MP4NalSize(t *testing.T) {
|
||
merger := NewFrameMerger(MergeMP4NalSize)
|
||
var output []byte
|
||
|
||
// 创建测试帧(带4字节前缀)
|
||
frame := &FrameFromBytes{
|
||
BaseFrame: &BaseFrame{VPrefixSize: 4, Dts: 1000},
|
||
DataPtr: []byte{0, 0, 0, 1, 0x67},
|
||
}
|
||
|
||
merger.InputFrame(frame, func(dts, pts uint64, buf []byte, key bool) {
|
||
output = buf
|
||
})
|
||
merger.Flush(func(dts, pts uint64, buf []byte, key bool) {
|
||
output = buf
|
||
})
|
||
|
||
// 验证MP4 NALU大小(4字节长度 + 原始数据)
|
||
if len(output) != 5 {
|
||
t.Error("MP4 nal size length mismatch")
|
||
}
|
||
if output[0] != 0 || output[1] != 0 || output[2] != 0 || output[3] != 1 {
|
||
t.Error("MP4 nal size header mismatch")
|
||
}
|
||
}
|
||
|
||
func TestFrameDispatcher(t *testing.T) {
|
||
dispatcher := NewFrameDispatcher()
|
||
statsChan := make(chan struct{})
|
||
|
||
// 添加测试代理
|
||
dispatcher.AddDelegate(&FrameWriter{
|
||
OnFrame: func(frame Frame) bool {
|
||
if frame.KeyFrame() && frame.TrackType() == TrackVideo {
|
||
close(statsChan)
|
||
}
|
||
return true
|
||
},
|
||
})
|
||
|
||
// 输入视频关键帧
|
||
dispatcher.InputFrame(&FrameFromBytes{
|
||
BaseFrame: &BaseFrame{
|
||
Dts: 1000,
|
||
Track: TrackVideo,
|
||
},
|
||
IsKey: true,
|
||
})
|
||
|
||
// 验证GOP统计
|
||
select {
|
||
case <-statsChan:
|
||
frames, keys, gopSize, _ := dispatcher.GetStats()
|
||
if frames != 1 || keys != 1 || gopSize != 1 {
|
||
t.Error("GOP statistics mismatch")
|
||
}
|
||
case <-time.After(100 * time.Millisecond):
|
||
t.Error("timeout waiting for stats")
|
||
}
|
||
}
|
||
|
||
func TestFrameDispatcher_Concurrent(t *testing.T) {
|
||
dispatcher := NewFrameDispatcher()
|
||
var done sync.WaitGroup
|
||
|
||
// 并发添加/删除代理
|
||
for i := 0; i < 10; i++ {
|
||
done.Add(1)
|
||
go func(id int) {
|
||
defer done.Done()
|
||
writer := &FrameWriter{}
|
||
dispatcher.AddDelegate(writer)
|
||
dispatcher.DelDelegate(writer)
|
||
}(i)
|
||
}
|
||
done.Wait()
|
||
|
||
// 验证线程安全
|
||
if dispatcher.Size() != 0 {
|
||
t.Error("concurrent delegate management failed")
|
||
}
|
||
}
|