-
Notifications
You must be signed in to change notification settings - Fork 29
/
main.go
170 lines (141 loc) · 4.24 KB
/
main.go
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package main
import (
"errors"
"flag"
"fmt"
"log"
"strings"
"github.com/asticode/go-astiav"
)
var (
filter = flag.String("f", "null", "the bit stream filter name")
input = flag.String("i", "", "the input path")
)
type stream struct {
bitStreamFilterContext *astiav.BitStreamFilterContext
bitStreamPkt *astiav.Packet
}
func main() {
// Handle ffmpeg logs
astiav.SetLogLevel(astiav.LogLevelDebug)
astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) {
var cs string
if c != nil {
if cl := c.Class(); cl != nil {
cs = " - class: " + cl.String()
}
}
log.Printf("ffmpeg log: %s%s - level: %d\n", strings.TrimSpace(msg), cs, l)
})
// Parse flags
flag.Parse()
// Usage
if *input == "" {
log.Println("Usage: <binary path> -i <input path> -f <bit stream filter name>")
return
}
// Alloc packet
pkt := astiav.AllocPacket()
defer pkt.Free()
// Alloc input format context
inputFormatContext := astiav.AllocFormatContext()
if inputFormatContext == nil {
log.Fatal(errors.New("main: input format context is nil"))
}
defer inputFormatContext.Free()
// Open input
if err := inputFormatContext.OpenInput(*input, nil, nil); err != nil {
log.Fatal(fmt.Errorf("main: opening input failed: %w", err))
}
defer inputFormatContext.CloseInput()
// Find stream info
if err := inputFormatContext.FindStreamInfo(nil); err != nil {
log.Fatal(fmt.Errorf("main: finding stream info failed: %w", err))
}
// Loop through streams
streams := make(map[int]*stream) // Indexed by input stream index
for _, is := range inputFormatContext.Streams() {
// Only process audio or video
if is.CodecParameters().MediaType() != astiav.MediaTypeAudio &&
is.CodecParameters().MediaType() != astiav.MediaTypeVideo {
continue
}
// Create stream
s := &stream{}
// Alloc packet
s.bitStreamPkt = astiav.AllocPacket()
defer s.bitStreamPkt.Free()
// Find bit stream filter
bsf := astiav.FindBitStreamFilterByName(*filter)
if bsf == nil {
log.Fatal(errors.New("main: bit stream filter is nil"))
}
// Alloc bit stream filter context
var err error
if s.bitStreamFilterContext, err = astiav.AllocBitStreamFilterContext(bsf); err != nil {
log.Fatal(fmt.Errorf("main: allocating bit stream filter context failed: %w", err))
}
defer s.bitStreamFilterContext.Free()
// Copy codec parameters
if err := is.CodecParameters().Copy(s.bitStreamFilterContext.InputCodecParameters()); err != nil {
log.Fatal(fmt.Errorf("main: copying codec parameters failed: %w", err))
}
// Update time base
s.bitStreamFilterContext.SetInputTimeBase(is.TimeBase())
// Initialize bit stream filter context
if err := s.bitStreamFilterContext.Initialize(); err != nil {
log.Fatal(fmt.Errorf("main: initializing bit stream filter context failed: %w", err))
}
// Add stream
streams[is.Index()] = s
}
// Loop through packets
for {
// Read frame
if err := inputFormatContext.ReadFrame(pkt); err != nil {
if errors.Is(err, astiav.ErrEof) {
break
}
log.Fatal(fmt.Errorf("main: reading frame failed: %w", err))
}
// Get stream
s, ok := streams[pkt.StreamIndex()]
if !ok {
continue
}
// Filter bit stream
if err := filterBitStream(pkt, s); err != nil {
log.Fatal(fmt.Errorf("main: filtering bit stream failed: %w", err))
}
}
// Loop through streams
for _, s := range streams {
// Flush bit stream filter
if err := filterBitStream(nil, s); err != nil {
log.Fatal(fmt.Errorf("main: filtering bit stream failed: %w", err))
}
}
// Success
log.Println("success")
}
func filterBitStream(pkt *astiav.Packet, s *stream) error {
// Send packet
if err := s.bitStreamFilterContext.SendPacket(pkt); err != nil && !errors.Is(err, astiav.ErrEagain) {
return fmt.Errorf("main: sending packet failed: %w", err)
}
// Loop
for {
// Receive packet
if err := s.bitStreamFilterContext.ReceivePacket(s.bitStreamPkt); err != nil {
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
break
}
return fmt.Errorf("main: receiving packet failed: %w", err)
}
// Do something with packet
log.Printf("new filtered packet: stream %d - pts: %d", s.bitStreamPkt.StreamIndex(), s.bitStreamPkt.Pts())
// Unref packet
s.bitStreamPkt.Unref()
}
return nil
}