@@ -18,12 +18,12 @@ import (
18
18
19
19
//export readCallback
20
20
func readCallback (opaque unsafe.Pointer , buf * C.uint8_t , bufSize C.int ) C.int {
21
- ctx , ok := ctxMap .file (opaque )
21
+ ctx , ok := ctxMap .context (opaque )
22
22
if ! ok {
23
23
return C .int (avErrUnknown )
24
24
}
25
25
p := (* [1 << 30 ]byte )(unsafe .Pointer (buf ))[:bufSize :bufSize ]
26
- n , err := ctx .Read (p )
26
+ n , err := ctx .file . Read (p )
27
27
if n > 0 || err == nil {
28
28
return C .int (n )
29
29
}
@@ -35,39 +35,39 @@ func readCallback(opaque unsafe.Pointer, buf *C.uint8_t, bufSize C.int) C.int {
35
35
}
36
36
}
37
37
38
- //export writeCallback
39
- func writeCallback (opaque unsafe.Pointer , buf * C.uint8_t , bufSize C.int ) C.int {
40
- ctx , ok := ctxMap .file (opaque )
41
- if ! ok {
42
- return C .int (avErrUnknown )
43
- }
44
- p := (* [1 << 30 ]byte )(unsafe .Pointer (buf ))[:bufSize :bufSize ]
45
- n , err := ctx .Write (p )
46
- if err != nil {
47
- return C .int (avErrUnknown )
48
- }
49
- return C .int (n )
50
- }
38
+ //// export writeCallback
39
+ // func writeCallback(opaque unsafe.Pointer, buf *C.uint8_t, bufSize C.int) C.int {
40
+ // ctx, ok := ctxMap.file(opaque)
41
+ // if !ok {
42
+ // return C.int(avErrUnknown)
43
+ // }
44
+ // p := (*[1 << 30]byte)(unsafe.Pointer(buf))[:bufSize:bufSize]
45
+ // n, err := ctx.Write(p)
46
+ // if err != nil {
47
+ // return C.int(avErrUnknown)
48
+ // }
49
+ // return C.int(n)
50
+ // }
51
51
52
52
//export seekCallback
53
53
func seekCallback (opaque unsafe.Pointer , offset C.int64_t , whence C.int ) C.int64_t {
54
- ctx , ok := ctxMap .file (opaque )
54
+ ctx , ok := ctxMap .context (opaque )
55
55
if ! ok {
56
56
return C .int64_t (avErrUnknown )
57
57
}
58
- if ! ctx .SeekEnd && whence >= C .SEEK_END {
59
- f , ok := ctx .Reader .(* seekstream.File )
58
+ if ! ctx .file . SeekEnd && whence >= C .SEEK_END {
59
+ f , ok := ctx .file . Reader .(* seekstream.File )
60
60
if ! ok || ! f .IsDone () {
61
61
return C .int64_t (avErrUnknown )
62
62
63
63
}
64
- ctx .SeekEnd = true
65
- ctx .Size = f .Size ()
64
+ ctx .file . SeekEnd = true
65
+ ctx .file . Size = f .Size ()
66
66
}
67
- if whence == C .AVSEEK_SIZE && ctx .Size > 0 {
68
- return C .int64_t (ctx .Size )
67
+ if whence == C .AVSEEK_SIZE && ctx .file . Size > 0 {
68
+ return C .int64_t (ctx .file . Size )
69
69
}
70
- n , err := ctx .Seek (int64 (offset ), int (whence ))
70
+ n , err := ctx .file . Seek (int64 (offset ), int (whence ))
71
71
if err != nil {
72
72
return C .int64_t (avErrUnknown )
73
73
}
@@ -78,7 +78,7 @@ func seekCallback(opaque unsafe.Pointer, offset C.int64_t, whence C.int) C.int64
78
78
func interruptCallback (opaque unsafe.Pointer ) C.int {
79
79
if ctx , ok := ctxMap .context (opaque ); ok {
80
80
select {
81
- case <- ctx .Done ():
81
+ case <- ctx .context . Done ():
82
82
return 1
83
83
default :
84
84
return 0
@@ -114,7 +114,7 @@ func SetFFmpegLogLevel(logLevel AVLogLevel) {
114
114
115
115
const (
116
116
readCallbackFlag = 1 << iota
117
- writeCallbackflag
117
+ // writeCallbackflag
118
118
seekCallbackFlag
119
119
interruptCallbackFlag
120
120
)
@@ -124,27 +124,26 @@ const (
124
124
hasAudio
125
125
)
126
126
127
- type contextKey int
128
-
129
- const (
130
- fileKey contextKey = iota
131
- fmtCtxKey
132
- streamKey
133
- decCtxKey
134
- thumbCtxKey
135
- frameKey
136
- durationKey
137
- )
127
+ type avContext struct {
128
+ context context.Context
129
+ file * File
130
+ formatContext * C.AVFormatContext
131
+ stream * C.AVStream
132
+ codecContext * C.AVCodecContext
133
+ thumbContext * C.ThumbContext
134
+ frame * C.AVFrame
135
+ durationInFormat bool
136
+ }
138
137
139
138
type avError int
140
139
141
140
const (
142
- avErrNoMem avError = - C .ENOMEM
143
- avErrEOF avError = C .AVERROR_EOF
144
- avErrUnknown avError = C .AVERROR_UNKNOWN
145
- avErrDecoderNotFound avError = C .AVERROR_DECODER_NOT_FOUND
146
- avErrInvalidData avError = C .AVERROR_INVALIDDATA
147
- errTooBig avError = C .ERR_TOO_BIG
141
+ avErrNoMem = avError ( - C .ENOMEM )
142
+ avErrEOF = avError ( C .AVERROR_EOF )
143
+ avErrUnknown = avError ( C .AVERROR_UNKNOWN )
144
+ avErrDecoderNotFound = avError ( C .AVERROR_DECODER_NOT_FOUND )
145
+ avErrInvalidData = avError ( C .AVERROR_INVALIDDATA )
146
+ errTooBig = avError ( C .ERR_TOO_BIG )
148
147
)
149
148
150
149
func (e avError ) errorString () string {
@@ -167,181 +166,144 @@ func (e avError) Error() string { return "ffmpeg: " + e.errorString() }
167
166
168
167
type contextMap struct {
169
168
sync.RWMutex
170
- m map [* C.AVFormatContext ]context. Context
169
+ m map [* C.AVFormatContext ]* avContext
171
170
}
172
171
173
- func (m * contextMap ) file (opaque unsafe.Pointer ) (file * File , ok bool ) {
172
+ func (m * contextMap ) context (opaque unsafe.Pointer ) (* avContext , bool ) {
174
173
m .RLock ()
175
174
ctx , ok := m .m [(* C .AVFormatContext )(opaque )]
176
175
m .RUnlock ()
177
- if ok {
178
- file , ok = ctx .Value (fileKey ).(* File )
179
- }
180
- return
181
- }
182
-
183
- func (m * contextMap ) context (opaque unsafe.Pointer ) (ctx context.Context , ok bool ) {
184
- m .RLock ()
185
- ctx , ok = m .m [(* C .AVFormatContext )(opaque )]
186
- m .RUnlock ()
187
- return
176
+ return ctx , ok
188
177
}
189
178
190
- func (m * contextMap ) set (ctx context.Context ) {
191
- fmtCtx := ctx .Value (fmtCtxKey ).(* C.AVFormatContext )
179
+ func (m * contextMap ) set (ctx * avContext ) {
192
180
m .Lock ()
193
- m .m [fmtCtx ] = ctx
181
+ m .m [ctx . formatContext ] = ctx
194
182
m .Unlock ()
195
- return
196
183
}
197
184
198
- func (m * contextMap ) delete (ctx context.Context ) (* C.AVFormatContext , bool ) {
199
- if fmtCtx , ok := ctx .Value (fmtCtxKey ).(* C.AVFormatContext ); ok {
200
- m .Lock ()
201
- delete (m .m , fmtCtx )
202
- m .Unlock ()
203
- return fmtCtx , true
204
- }
205
- return nil , false
185
+ func (m * contextMap ) delete (ctx * avContext ) {
186
+ m .Lock ()
187
+ delete (m .m , ctx .formatContext )
188
+ m .Unlock ()
206
189
}
207
190
208
- var ctxMap = contextMap {m : make (map [* C.AVFormatContext ]context. Context )}
191
+ var ctxMap = contextMap {m : make (map [* C.AVFormatContext ]* avContext )}
209
192
210
- func freeFormatContext (ctx context.Context ) {
211
- if fmtCtx , ok := ctxMap .delete (ctx ); ok && fmtCtx != nil {
212
- C .free_format_context (fmtCtx )
213
- }
193
+ func freeFormatContext (ctx * avContext ) {
194
+ C .free_format_context (ctx .formatContext )
195
+ ctxMap .delete (ctx )
214
196
}
215
197
216
- func createFormatContext (ctx context.Context , callbackFlags C.int ) (context.Context , error ) {
217
- var fmtCtx * C.AVFormatContext
218
- intErr := C .allocate_format_context (& fmtCtx )
198
+ func createFormatContext (ctx * avContext , callbackFlags C.int ) error {
199
+ intErr := C .allocate_format_context (& ctx .formatContext )
219
200
if intErr < 0 {
220
- return nil , avError (intErr )
201
+ return avError (intErr )
221
202
}
222
- ctx = context .WithValue (ctx , fmtCtxKey , fmtCtx )
223
203
ctxMap .set (ctx )
224
- intErr = C .create_format_context (fmtCtx , callbackFlags )
204
+ intErr = C .create_format_context (ctx . formatContext , callbackFlags )
225
205
if intErr < 0 {
226
206
ctxMap .delete (ctx )
227
- return nil , avError (intErr )
207
+ return avError (intErr )
228
208
}
229
209
metaData (ctx )
230
- ctx = duration (ctx )
231
- ctx , err := findStreams (ctx )
210
+ duration (ctx )
211
+ err := findStreams (ctx )
232
212
if err != nil {
233
213
freeFormatContext (ctx )
234
214
}
235
- return ctx , err
215
+ return err
236
216
}
237
217
238
- func metaData (ctx context. Context ) {
218
+ func metaData (ctx * avContext ) {
239
219
var artist , title * C.char
240
- fmtCtx := ctx .Value (fmtCtxKey ).(* C.AVFormatContext )
241
- C .get_metadata (fmtCtx , & artist , & title )
242
- file := ctx .Value (fileKey ).(* File )
243
- file .Artist = C .GoString (artist )
244
- file .Title = C .GoString (title )
220
+ C .get_metadata (ctx .formatContext , & artist , & title )
221
+ ctx .file .Artist = C .GoString (artist )
222
+ ctx .file .Title = C .GoString (title )
245
223
}
246
224
247
- func duration (ctx context.Context ) context.Context {
248
- durationInFormat := false
249
- if fmtCtx := ctx .Value (fmtCtxKey ).(* C.AVFormatContext ); fmtCtx .duration > 0 {
250
- durationInFormat = true
251
- ctx .Value (fileKey ).(* File ).Duration = time .Duration (1000 * fmtCtx .duration )
225
+ func duration (ctx * avContext ) {
226
+ if ctx .formatContext .duration > 0 {
227
+ ctx .durationInFormat = true
228
+ ctx .file .Duration = time .Duration (1000 * ctx .formatContext .duration )
252
229
}
253
- return context .WithValue (ctx , durationKey , durationInFormat )
254
-
255
230
}
256
231
257
- func fullDuration (ctx context. Context ) error {
232
+ func fullDuration (ctx * avContext ) error {
258
233
defer freeFormatContext (ctx )
259
- if ctx .Value ( durationKey ).( bool ) {
234
+ if ctx .durationInFormat {
260
235
return nil
261
236
}
262
- newDuration := time .Duration (C .find_duration (ctx .Value ( fmtCtxKey ).( * C. AVFormatContext ) ))
237
+ newDuration := time .Duration (C .find_duration (ctx .formatContext ))
263
238
if newDuration < 0 {
264
239
return avError (newDuration )
265
240
}
266
- file := ctx .Value (fileKey ).(* File )
267
- if newDuration > file .Duration {
268
- file .Duration = newDuration
241
+ if newDuration > ctx .file .Duration {
242
+ ctx .file .Duration = newDuration
269
243
}
270
244
return nil
271
245
}
272
246
273
- func findStreams (ctx context.Context ) (context.Context , error ) {
274
- var vStream * C.AVStream
247
+ func findStreams (ctx * avContext ) error {
275
248
var orientation C.int
276
- err := C .find_streams (ctx .Value ( fmtCtxKey ).( * C. AVFormatContext ) , & vStream , & orientation )
249
+ err := C .find_streams (ctx .formatContext , & ctx . stream , & orientation )
277
250
if err < 0 {
278
- return ctx , avError (err )
251
+ return avError (err )
279
252
}
280
- ctx = context .WithValue (ctx , streamKey , vStream )
281
- file := ctx .Value (fileKey ).(* File )
282
- file .HasVideo = err & hasVideo != 0
283
- file .HasAudio = err & hasAudio != 0
284
- if ! file .HasVideo {
285
- file .Media = "audio"
253
+ ctx .file .HasVideo = err & hasVideo != 0
254
+ ctx .file .HasAudio = err & hasAudio != 0
255
+ if ! ctx .file .HasVideo {
256
+ ctx .file .Media = "audio"
286
257
} else {
287
- file .Width = int (vStream .codecpar .width )
288
- file .Height = int (vStream .codecpar .height )
289
- file .Orientation = int (orientation )
258
+ ctx . file .Width = int (ctx . stream .codecpar .width )
259
+ ctx . file .Height = int (ctx . stream .codecpar .height )
260
+ ctx . file .Orientation = int (orientation )
290
261
}
291
- return ctx , nil
262
+ return nil
292
263
}
293
264
294
- func createDecoder (ctx context.Context ) (context.Context , error ) {
295
- var decCtx * C.AVCodecContext
296
- err := C .create_codec_context (ctx .Value (streamKey ).(* C.AVStream ), & decCtx )
265
+ func createDecoder (ctx * avContext ) error {
266
+ err := C .create_codec_context (ctx .stream , & ctx .codecContext )
297
267
if err < 0 {
298
- return ctx , avError (err )
268
+ return avError (err )
299
269
}
300
- ctx = context .WithValue (ctx , decCtxKey , decCtx )
301
- defer C .avcodec_free_context (& decCtx )
270
+ defer C .avcodec_free_context (& ctx .codecContext )
302
271
return createThumbContext (ctx )
303
272
}
304
273
305
- func incrementDuration (ctx context.Context , frame * C.AVFrame ) {
306
- if ! ctx .Value (durationKey ).(bool ) && frame .pts != C .AV_NOPTS_VALUE {
307
- vStream := ctx .Value (streamKey ).(* C.AVStream )
308
- ptsToNano := C .int64_t (1000000000 * vStream .time_base .num / vStream .time_base .den )
274
+ func incrementDuration (ctx * avContext , frame * C.AVFrame ) {
275
+ if ! ctx .durationInFormat && frame .pts != C .AV_NOPTS_VALUE {
276
+ ptsToNano := C .int64_t (1000000000 * ctx .stream .time_base .num / ctx .stream .time_base .den )
309
277
newDuration := time .Duration (frame .pts * ptsToNano )
310
- file := ctx .Value (fileKey ).(* File )
311
- if newDuration > file .Duration {
312
- file .Duration = newDuration
278
+ if newDuration > ctx .file .Duration {
279
+ ctx .file .Duration = newDuration
313
280
}
314
281
}
315
282
}
316
283
317
- func populateHistogram (ctx context. Context , frames <- chan * C.AVFrame ) <- chan struct {} {
284
+ func populateHistogram (ctx * avContext , frames <- chan * C.AVFrame ) <- chan struct {} {
318
285
done := make (chan struct {})
319
- thumbCtx := ctx .Value (thumbCtxKey ).(* C.ThumbContext )
320
286
go func () {
321
287
var n C.int
322
288
for frame := range frames {
323
- C .populate_histogram (thumbCtx , n , frame )
289
+ C .populate_histogram (ctx . thumbContext , n , frame )
324
290
n ++
325
291
}
326
- thumbCtx .n = n
292
+ ctx . thumbContext .n = n
327
293
done <- struct {}{}
328
294
close (done )
329
295
}()
330
296
return done
331
297
}
332
298
333
- func createThumbContext (ctx context. Context ) (context. Context , error ) {
299
+ func createThumbContext (ctx * avContext ) error {
334
300
pkt := C .create_packet ()
335
- fmtCtx := ctx .Value (fmtCtxKey ).(* C.AVFormatContext )
336
- vStream := ctx .Value (streamKey ).(* C.AVStream )
337
- decCtx := ctx .Value (decCtxKey ).(* C.AVCodecContext )
338
301
var frame * C.AVFrame
339
- var thumbCtx * C.ThumbContext
340
- err := C .obtain_next_frame (fmtCtx , decCtx , vStream .index , & pkt , & frame )
302
+ err := C .obtain_next_frame (ctx .formatContext , ctx .codecContext , ctx .stream .index , & pkt , & frame )
341
303
if err >= 0 {
342
304
incrementDuration (ctx , frame )
343
- thumbCtx = C .create_thumb_context (vStream , frame )
344
- if thumbCtx == nil {
305
+ ctx . thumbContext = C .create_thumb_context (ctx . stream , frame )
306
+ if ctx . thumbContext == nil {
345
307
err = C .int (avErrNoMem )
346
308
}
347
309
}
@@ -352,11 +314,10 @@ func createThumbContext(ctx context.Context) (context.Context, error) {
352
314
if frame != nil {
353
315
C .av_frame_free (& frame )
354
316
}
355
- return ctx , avError (err )
317
+ return avError (err )
356
318
}
357
- ctx = context .WithValue (ctx , thumbCtxKey , thumbCtx )
358
- defer C .free_thumb_context (thumbCtx )
359
- frames := make (chan * C.AVFrame , thumbCtx .max_frames )
319
+ defer C .free_thumb_context (ctx .thumbContext )
320
+ frames := make (chan * C.AVFrame , ctx .thumbContext .max_frames )
360
321
done := populateHistogram (ctx , frames )
361
322
frames <- frame
362
323
if pkt .buf != nil {
@@ -365,16 +326,12 @@ func createThumbContext(ctx context.Context) (context.Context, error) {
365
326
return populateThumbContext (ctx , frames , done )
366
327
}
367
328
368
- func populateThumbContext (ctx context. Context , frames chan * C.AVFrame , done <- chan struct {}) (context. Context , error ) {
329
+ func populateThumbContext (ctx * avContext , frames chan * C.AVFrame , done <- chan struct {}) error {
369
330
pkt := C .create_packet ()
370
331
var frame * C.AVFrame
371
- fmtCtx := ctx .Value (fmtCtxKey ).(* C.AVFormatContext )
372
- vStream := ctx .Value (streamKey ).(* C.AVStream )
373
- decCtx := ctx .Value (decCtxKey ).(* C.AVCodecContext )
374
- thumbCtx := ctx .Value (thumbCtxKey ).(* C.ThumbContext )
375
332
var err C.int
376
- for i := C .int (1 ); i < thumbCtx .max_frames ; i ++ {
377
- err = C .obtain_next_frame (fmtCtx , decCtx , vStream .index , & pkt , & frame )
333
+ for i := C .int (1 ); i < ctx . thumbContext .max_frames ; i ++ {
334
+ err = C .obtain_next_frame (ctx . formatContext , ctx . codecContext , ctx . stream .index , & pkt , & frame )
378
335
if err < 0 {
379
336
break
380
337
}
@@ -391,48 +348,46 @@ func populateThumbContext(ctx context.Context, frames chan *C.AVFrame, done <-ch
391
348
}
392
349
<- done
393
350
if err != 0 && err != C .int (avErrEOF ) {
394
- return ctx , avError (err )
351
+ return avError (err )
395
352
}
396
353
return convertFrameToRGB (ctx )
397
354
}
398
355
399
- func convertFrameToRGB (ctx context.Context ) (context.Context , error ) {
400
- thumbCtx := ctx .Value (thumbCtxKey ).(* C.ThumbContext )
401
- outputFrame := C .convert_frame_to_rgb (C .process_frames (thumbCtx ), thumbCtx .alpha )
356
+ func convertFrameToRGB (ctx * avContext ) error {
357
+ outputFrame := C .convert_frame_to_rgb (C .process_frames (ctx .thumbContext ), ctx .thumbContext .alpha )
402
358
if outputFrame == nil {
403
- return ctx , avErrNoMem
359
+ return avErrNoMem
404
360
}
405
- ctx = context . WithValue ( ctx , frameKey , outputFrame )
406
- ctx .Value ( fileKey ).( * File ). HasAlpha = thumbCtx .alpha != 0
407
- return ctx , nil
361
+ ctx . frame = outputFrame
362
+ ctx .file . HasAlpha = ctx . thumbContext .alpha != 0
363
+ return nil
408
364
}
409
365
410
- func thumbnail (ctx context. Context ) <- chan error {
366
+ func thumbnail (ctx * avContext ) <- chan error {
411
367
errCh := make (chan error )
412
- outputFrame := ctx .Value (frameKey ).(* C.AVFrame )
413
368
go func () {
414
- err := thumbnailFromFFmpeg (ctx .Value ( fileKey ).( * File ), outputFrame .data [0 ])
415
- C .av_frame_free (& outputFrame )
369
+ err := thumbnailFromFFmpeg (ctx .file , ctx . frame .data [0 ])
370
+ C .av_frame_free (& ctx . frame )
416
371
errCh <- err
417
372
close (errCh )
418
373
}()
419
374
return errCh
420
375
}
421
376
422
- func ffmpegThumbnail (ctx context.Context , file * File ) error {
423
- ctx = context . WithValue ( ctx , fileKey , file )
377
+ func ffmpegThumbnail (context context.Context , file * File ) error {
378
+ ctx := & avContext { context : context , file : file }
424
379
callbackFlags := C .int (readCallbackFlag | interruptCallbackFlag )
425
380
if file .Seeker != nil {
426
381
callbackFlags |= seekCallbackFlag
427
382
}
428
- ctx , err := createFormatContext (ctx , callbackFlags )
383
+ err := createFormatContext (ctx , callbackFlags )
429
384
if err != nil {
430
385
return err
431
386
}
432
387
if ! file .HasVideo {
433
388
return fullDuration (ctx )
434
389
}
435
- if ctx , err = createDecoder (ctx ); err == errTooBig || err == avErrDecoderNotFound {
390
+ if err = createDecoder (ctx ); err == errTooBig || err == avErrDecoderNotFound {
436
391
return fullDuration (ctx )
437
392
}
438
393
if err != nil {
0 commit comments