-
Notifications
You must be signed in to change notification settings - Fork 4
/
ffmpeg.go
286 lines (237 loc) · 6.2 KB
/
ffmpeg.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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
package ffmpeg
import (
"fmt"
"sync"
"unsafe"
)
/*
#cgo CFLAGS: -I${SRCDIR}/include -Wno-deprecated -Wno-deprecated-declarations
#cgo linux LDFLAGS: -L${SRCDIR}
#cgo linux,amd64 LDFLAGS: -lffmpeg_linux_amd64
#cgo linux,arm64 LDFLAGS: -lffmpeg_linux_arm64
#cgo linux LDFLAGS: -lm -ldl
#cgo darwin LDFLAGS: -L${SRCDIR} -lm -framework ApplicationServices -framework CoreVideo -framework CoreMedia -framework VideoToolbox -framework AudioToolbox
#cgo darwin,amd64 LDFLAGS: -lffmpeg_darwin_amd64
#cgo darwin,arm64 LDFLAGS: -lffmpeg_darwin_arm64
#include <errno.h>
#include <stdlib.h>
#include <libavutil/avutil.h>
*/
import "C"
const (
ptrSize = uintptr(C.sizeof_size_t)
intSize = uintptr(C.sizeof_int)
int8Size = uintptr(C.sizeof_int8_t)
int64Size = uintptr(C.sizeof_int64_t)
)
var AVTimeBaseQ = &AVRational{value: C.AV_TIME_BASE_Q}
var (
EAgain = AVError{Code: -C.EAGAIN}
AVErrorEOF = AVError{Code: AVErrorEofConst}
)
// AVError represents a non-positive return code from FFmpeg.
type AVError struct {
Code int
}
func (e AVError) Error() string {
buf := AllocCStr(uint(AVErrorMaxStringSize))
defer buf.Free()
AVStrerror(e.Code, buf, uint64(AVErrorMaxStringSize))
return fmt.Sprintf("averror %v: %v", e.Code, buf.String())
}
// WrapErr returns a AVError if the code is less than zero, otherwise nil.
func WrapErr(code int) error {
if code >= 0 {
return nil
}
return AVError{Code: code}
}
// CStr is a string allocated in the C memory space. You may need to call Free to clean up the string depending on the
// owner and use-case.
type CStr struct {
ptr *C.char
dontFree bool
}
// AllocCStr allocates an empty string with the given length. The buffer will be initialised to 0.
func AllocCStr(len uint) *CStr {
ptr := (*C.char)(C.calloc(C.ulong(len), C.sizeof_char))
return &CStr{
ptr: ptr,
}
}
// ToCStr allocates a new CStr with the given content. The CStr will not be automatically garbage collected.
func ToCStr(val string) *CStr {
return &CStr{
ptr: C.CString(val),
}
}
var (
strMap = map[string]*CStr{}
strLock = sync.RWMutex{}
)
// GlobalCStr resolves the given string to a CStr. Multiple calls with the same input string will return the same CStr.
// You should not attempt to free the CStr returned. When passing to FFmpeg, you may need to call Dup to create a copy
// if the FFmpeg code expects to take ownership and will likely free the string.
func GlobalCStr(val string) *CStr {
var (
ptr *CStr
ok bool
)
strLock.RLock()
ptr, ok = strMap[val]
strLock.RUnlock()
if ok {
return ptr
}
strLock.Lock()
defer strLock.Unlock()
ptr, ok = strMap[val]
if ok {
return ptr
}
ptr = ToCStr(val)
ptr.dontFree = true
strMap[val] = ptr
return ptr
}
func wrapCStr(ptr *C.char) *CStr {
if ptr == nil {
return nil
}
return &CStr{
ptr: ptr,
}
}
// Dup is a wrapper for AVStrdup.
func (s *CStr) Dup() *CStr {
return AVStrdup(s)
}
// String converts the CStr to a Go string.
func (s *CStr) String() string {
return C.GoString(s.ptr)
}
// Free frees the backing memory for this string. You should only call this function if you are the owner of the memory.
func (s *CStr) Free() {
if s.dontFree {
return
}
C.free(unsafe.Pointer(s.ptr))
}
// RawPtr returns a raw reference to the underlying allocation.
func (s *CStr) RawPtr() unsafe.Pointer {
return unsafe.Pointer(s.ptr)
}
// Array is a helper utility for accessing arrays of FFmpeg types. You can not directly allocate this type, and you must
// use one of the inbuilt constructors, such as AllocAVCodecIDArray.
//
// Arrays have no inbuilt length, matching the behaviour of C code. Getting or setting an out of bound index will lead
// to undefined behaviour.
type Array[T any] struct {
ptr unsafe.Pointer
elemSize uintptr
loadPtr func(pointer unsafe.Pointer) T
storePtr func(pointer unsafe.Pointer, value T)
}
// Get returns the element at the ith offset.
func (a *Array[T]) Get(i uintptr) T {
ptr := unsafe.Add(a.ptr, i*a.elemSize)
return a.loadPtr(ptr)
}
// Set sets the element at the ith offset.
func (a *Array[T]) Set(i uintptr, value T) {
ptr := unsafe.Add(a.ptr, i*a.elemSize)
a.storePtr(ptr, value)
}
// Free deallocates the underlying memory. You should only call this if you own the array.
func (a *Array[T]) Free() {
AVFree(a.ptr)
}
// RawPtr returns a raw handle the underlying allocation.
func (a *Array[T]) RawPtr() unsafe.Pointer {
return a.ptr
}
func ToIntArray(ptr unsafe.Pointer) *Array[int] {
if ptr == nil {
return nil
}
return &Array[int]{
elemSize: intSize,
loadPtr: func(pointer unsafe.Pointer) int {
ptr := (*C.int)(pointer)
return int(*ptr)
},
ptr: ptr,
storePtr: func(pointer unsafe.Pointer, value int) {
ptr := (*C.int)(pointer)
*ptr = C.int(value)
},
}
}
func ToUint8Array(ptr unsafe.Pointer) *Array[uint8] {
if ptr == nil {
return nil
}
return &Array[uint8]{
elemSize: int8Size,
loadPtr: func(pointer unsafe.Pointer) uint8 {
ptr := (*C.uint8_t)(pointer)
return uint8(*ptr)
},
ptr: ptr,
storePtr: func(pointer unsafe.Pointer, value uint8) {
ptr := (*C.uint8_t)(pointer)
*ptr = C.uint8_t(value)
},
}
}
func ToInt64Array(ptr unsafe.Pointer) *Array[int64] {
if ptr == nil {
return nil
}
return &Array[int64]{
elemSize: int64Size,
loadPtr: func(pointer unsafe.Pointer) int64 {
ptr := (*C.int64_t)(pointer)
return int64(*ptr)
},
ptr: ptr,
storePtr: func(pointer unsafe.Pointer, value int64) {
ptr := (*C.int64_t)(pointer)
*ptr = C.int64_t(value)
},
}
}
func ToUint64Array(ptr unsafe.Pointer) *Array[uint64] {
if ptr == nil {
return nil
}
return &Array[uint64]{
elemSize: int64Size,
loadPtr: func(pointer unsafe.Pointer) uint64 {
ptr := (*C.uint64_t)(pointer)
return uint64(*ptr)
},
ptr: ptr,
storePtr: func(pointer unsafe.Pointer, value uint64) {
ptr := (*C.uint64_t)(pointer)
*ptr = C.uint64_t(value)
},
}
}
func ToUint8PtrArray(ptr unsafe.Pointer) *Array[unsafe.Pointer] {
if ptr == nil {
return nil
}
return &Array[unsafe.Pointer]{
elemSize: ptrSize,
loadPtr: func(pointer unsafe.Pointer) unsafe.Pointer {
ptr := (*unsafe.Pointer)(pointer)
return *ptr
},
ptr: ptr,
storePtr: func(pointer unsafe.Pointer, value unsafe.Pointer) {
ptr := (*unsafe.Pointer)(pointer)
*ptr = value
},
}
}