-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathextract_video.go
More file actions
108 lines (100 loc) · 2.95 KB
/
extract_video.go
File metadata and controls
108 lines (100 loc) · 2.95 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
package matroska
import (
"encoding/binary"
"fmt"
"github.com/coding-socks/matroska/internal/avi"
"github.com/coding-socks/matroska/internal/riff"
"io"
"math"
"os"
)
func extractTrackVideo(w *os.File, s *Scanner, t TrackEntry) error {
switch t.CodecID {
case VideoCodecMSCOMP:
return extractTrackMSCOMP(w, s, t)
}
return fmt.Errorf("matroska: unknown audio codec %s", t.CodecID)
}
func extractTrackMSCOMP(w io.WriterAt, s *Scanner, t TrackEntry) error {
scale := s.Info().TimestampScale
if t.Video == nil {
return fmt.Errorf("matroska: missing video stream")
}
ww, err := avi.NewWriter(w)
if err != nil {
return fmt.Errorf("matroska: could not to initiate avi file: %w", err)
}
defer ww.Close()
videoStreamID := avi.NewStreamID(0, avi.StreamTypeDC)
var totalFrames uint32 = 0
for s.Next() {
c := s.Cluster()
m := len(c.SimpleBlock) + len(c.BlockGroup)
if m == 0 {
continue
}
for i := range c.SimpleBlock {
block, err := ReadSimpleBlock(c.SimpleBlock[i], c.Timestamp)
if err != nil {
return fmt.Errorf("matroska: could not create block struct: %w", err)
}
if block.TrackNumber() != t.TrackNumber {
continue
}
frames := block.Frames()
for i := range frames {
var flags uint32 = 0
if i == 0 && ((block.Flags() & SimpleBlockFlagKeyframe) > 0) {
flags |= avi.AVIIF_KEYFRAME
}
if err := ww.WriteData(videoStreamID, frames[i], flags); err != nil {
return err
}
totalFrames++
}
}
for _, group := range c.BlockGroup {
block, err := ReadBlock(group.Block, c.Timestamp)
if err != nil {
return fmt.Errorf("matroska: could not create block struct: %w", err)
}
if block.TrackNumber() != t.TrackNumber {
continue
}
frames := block.Frames()
for i := range frames {
var flags uint32 = 0
// TODO: I'm not sure if this is correct. Maybe this is only relevant for
// RAPs (i.e., frames that don't depend on other frames).
if i == 0 && len(group.ReferenceBlock) == 0 {
flags |= avi.AVIIF_KEYFRAME
}
if err := ww.WriteData(videoStreamID, frames[i], flags); err != nil {
return err
}
totalFrames++
}
}
}
var sf = avi.StreamFormat(*t.CodecPrivate)
var handler riff.FourCC
binary.LittleEndian.PutUint32(handler[:], sf.Compression())
var mh avi.MainHeader
mh.SetMicroSecPerFrame(uint32(math.Ceil(float64(*t.DefaultDuration) / 1000.0)))
mh.SetFlags(avi.AVIF_HASINDEX | avi.AVIF_ISINTERLEAVED)
mh.SetTotalFrames(totalFrames)
mh.SetStreams(1) // number of audio streams plus one
mh.SetWidth(uint32(t.Video.PixelWidth))
mh.SetHeight(uint32(t.Video.PixelHeight))
var sh avi.StreamHeader
sh.SetType(avi.StreamTypeVIDS)
sh.SetHandler(handler)
sh.SetScale(uint32(scale))
sh.SetRate(uint32(float64(scale) / float64(*t.DefaultDuration) * 1000000000.0))
sh.SetLength(totalFrames)
sh.SetSuggestedBufferSize(ww.MaxLen())
if err := ww.WriteHeader(sh, sf); err != nil {
return fmt.Errorf("matroska: could not write header: %w", err)
}
return nil
}