-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdecoder_integration_test.go
More file actions
133 lines (122 loc) · 3.03 KB
/
Copy pathdecoder_integration_test.go
File metadata and controls
133 lines (122 loc) · 3.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//go:build amd64 || arm64
package ffmpeg
import (
"errors"
"io"
"os"
"os/exec"
"path/filepath"
"testing"
)
func TestDecoderReadFrameDrainsAllSelectedStreams(t *testing.T) {
if !requireFFmpeg(t) {
return
}
decoder, err := NewDecoder(createTestVideo(t), nil)
if err != nil {
t.Fatal(err)
}
defer decoder.Close()
counts := map[MediaType]int{}
lastPTS := map[MediaType]int64{}
seenPTS := map[MediaType]bool{}
for {
frame, err := decoder.ReadFrame()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
t.Fatal(err)
}
mediaType := frame.MediaType()
pts := frame.PTS()
if seenPTS[mediaType] && pts <= lastPTS[mediaType] {
t.Fatalf("%v PTS did not increase: previous=%d current=%d", mediaType, lastPTS[mediaType], pts)
}
seenPTS[mediaType] = true
lastPTS[mediaType] = pts
counts[mediaType]++
}
if counts[MediaTypeVideo] != 60 {
t.Fatalf("video frames = %d, want 60", counts[MediaTypeVideo])
}
if counts[MediaTypeAudio] != 87 {
t.Fatalf("audio frames = %d, want 87", counts[MediaTypeAudio])
}
}
func TestDecoderSingleStreamCallsPreserveInterleavedPackets(t *testing.T) {
if !requireFFmpeg(t) {
return
}
decoder, err := NewDecoder(createTestVideo(t), nil)
if err != nil {
t.Fatal(err)
}
defer decoder.Close()
videoFrames := drainDecodedFrames(t, decoder.DecodeVideo)
audioFrames := drainDecodedFrames(t, decoder.DecodeAudio)
if videoFrames != 60 {
t.Fatalf("video frames = %d, want 60", videoFrames)
}
if audioFrames != 87 {
t.Fatalf("audio frames = %d, want 87", audioFrames)
}
}
func TestDecoderPropagatesTruncatedInputError(t *testing.T) {
if !requireFFmpeg(t) {
return
}
dir := t.TempDir()
fragmented := filepath.Join(dir, "fragmented.mp4")
command := exec.Command("ffmpeg", "-loglevel", "error", "-y",
"-i", createTestVideo(t), "-c", "copy",
"-movflags", "frag_keyframe+empty_moov+default_base_moof", fragmented)
if output, err := command.CombinedOutput(); err != nil {
t.Fatalf("create fragmented fixture: %v\n%s", err, output)
}
data, err := os.ReadFile(fragmented)
if err != nil {
t.Fatal(err)
}
if len(data) <= 5000 {
t.Fatalf("fragmented fixture is unexpectedly small: %d bytes", len(data))
}
truncated := filepath.Join(dir, "truncated.mp4")
if err := os.WriteFile(truncated, data[:len(data)-5000], 0o600); err != nil {
t.Fatal(err)
}
decoder, err := NewDecoder(truncated, nil)
if err != nil {
t.Fatalf("open truncated fixture: %v", err)
}
defer decoder.Close()
frames := 0
for frames < 500 {
frame, decodeErr := decoder.ReadFrame()
if decodeErr != nil {
if frames == 0 {
t.Fatal("truncated fixture failed before producing any frame")
}
return
}
if frame == nil {
t.Fatalf("truncated input became clean EOF after %d frames", frames)
}
frames++
}
t.Fatal("truncated input did not terminate within 500 frames")
}
func drainDecodedFrames(t *testing.T, next func() (Frame, error)) int {
t.Helper()
count := 0
for {
_, err := next()
if err != nil {
if errors.Is(err, io.EOF) {
return count
}
t.Fatal(err)
}
count++
}
}