Skip to content

Commit dd12d4e

Browse files
committed
feat: support for extracting "video for windows"
1 parent ffe088b commit dd12d4e

6 files changed

Lines changed: 858 additions & 56 deletions

File tree

cmd/mkdev/riff.go

Lines changed: 97 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package main
22

33
import (
44
"bytes"
5+
"encoding/binary"
56
"encoding/hex"
67
"fmt"
8+
"github.com/coding-socks/matroska/internal/avi"
79
"github.com/coding-socks/matroska/internal/riff"
810
"io"
911
"os"
@@ -23,8 +25,15 @@ func analyzeRIFFPackages(filename string) error {
2325

2426
w := os.Stdout
2527
fmt.Fprintf(w, "%s\n", fileType)
26-
if err := printRIFFDetails(w, r, " "); err != nil {
27-
return err
28+
switch fileType {
29+
default:
30+
if err := printRIFFDetails(w, r, " "); err != nil {
31+
return err
32+
}
33+
case avi.AVI:
34+
if err := printAVIDetails(w, r, " "); err != nil {
35+
return err
36+
}
2837
}
2938
return nil
3039
}
@@ -40,7 +49,22 @@ func printRIFFDetails(w io.Writer, r *riff.Reader, indent string) error {
4049
if err != nil {
4150
return err
4251
}
43-
if id == riff.LIST {
52+
buf.Reset()
53+
switch id {
54+
default:
55+
_, err := io.Copy(&buf, rr)
56+
if err != nil {
57+
return fmt.Errorf("unable to decode data: %w", err)
58+
}
59+
fmt.Fprintf(w, "%s%s ckSize %d\n", indent, id, l)
60+
fmt.Fprintf(w, "%s %.32s\n", indent, hex.EncodeToString(buf.Bytes()))
61+
case riff.JUNK:
62+
_, err := io.Copy(io.Discard, rr)
63+
if err != nil {
64+
return fmt.Errorf("unable to decode data: %w", err)
65+
}
66+
fmt.Fprintf(w, "%s%s ckSize %d\n", indent, id, l)
67+
case riff.LIST:
4468
listType, rr, err := riff.NewListReader(l, rr)
4569
if err != nil {
4670
return fmt.Errorf("unable to decode list: %w", err)
@@ -49,14 +73,81 @@ func printRIFFDetails(w io.Writer, r *riff.Reader, indent string) error {
4973
if err := printRIFFDetails(w, rr, indent+" "); err != nil {
5074
return err
5175
}
52-
} else {
53-
buf.Reset()
76+
}
77+
}
78+
79+
return nil
80+
}
81+
82+
func printAVIDetails(w io.Writer, r *riff.Reader, indent string) error {
83+
for {
84+
id, l, rr, err := r.Next()
85+
if err == io.EOF {
86+
break
87+
}
88+
if err != nil {
89+
return err
90+
}
91+
buf.Reset()
92+
switch id {
93+
default:
5494
_, err := io.Copy(&buf, rr)
5595
if err != nil {
5696
return fmt.Errorf("unable to decode data: %w", err)
5797
}
5898
fmt.Fprintf(w, "%s%s ckSize %d\n", indent, id, l)
59-
fmt.Fprintf(w, "%s %.30s\n", indent, hex.EncodeToString(buf.Bytes()))
99+
fmt.Fprintf(w, "%s %.32s\n", indent, hex.EncodeToString(buf.Bytes()))
100+
case riff.JUNK:
101+
_, err := io.Copy(io.Discard, rr)
102+
if err != nil {
103+
return fmt.Errorf("unable to decode data: %w", err)
104+
}
105+
fmt.Fprintf(w, "%s%s ckSize %d\n", indent, id, l)
106+
case riff.LIST:
107+
listType, rr, err := riff.NewListReader(l, rr)
108+
if err != nil {
109+
return fmt.Errorf("unable to decode list: %w", err)
110+
}
111+
fmt.Fprintf(w, "%s%s listSize %d listType %s\n", indent, id, l, listType)
112+
if err := printAVIDetails(w, rr, indent+" "); err != nil {
113+
return err
114+
}
115+
case avi.ChunkAVIH:
116+
_, err := io.Copy(&buf, rr)
117+
if err != nil {
118+
return fmt.Errorf("unable to decode data: %w", err)
119+
}
120+
fmt.Fprintf(w, "%s%s ckSize %d\n", indent, id, l)
121+
mh := avi.MainHeader(buf.Bytes())
122+
fmt.Fprintf(w, "%s %d\n", indent, mh.MicroSecPerFrame())
123+
fmt.Fprintf(w, "%s %s\n", indent, hex.EncodeToString(buf.Bytes()))
124+
case /*avi.ChunkAVIH, */ avi.ChunkSTRH, avi.ChunkSTRF, avi.ChunkSTRD, avi.ChunkSTRN:
125+
_, err := io.Copy(&buf, rr)
126+
if err != nil {
127+
return fmt.Errorf("unable to decode data: %w", err)
128+
}
129+
fmt.Fprintf(w, "%s%s ckSize %d\n", indent, id, l)
130+
fmt.Fprintf(w, "%s %s\n", indent, hex.EncodeToString(buf.Bytes()))
131+
case avi.ChunkIDX1:
132+
_, err := io.Copy(&buf, rr)
133+
if err != nil {
134+
return fmt.Errorf("unable to decode data: %w", err)
135+
}
136+
fmt.Fprintf(w, "%s%s ckSize %d\n", indent, id, l)
137+
b := buf.Bytes()
138+
for len(b) >= 16 {
139+
fmt.Fprintf(w, "%s %s\n", indent, riff.FourCC(b[0:4]))
140+
fmt.Fprintf(w, "%s flags %x\n", indent, binary.LittleEndian.Uint32(b[4:8]))
141+
fmt.Fprintf(w, "%s offset %d\n", indent, binary.LittleEndian.Uint32(b[8:12]))
142+
fmt.Fprintf(w, "%s size %d\n", indent, binary.LittleEndian.Uint32(b[12:16]))
143+
b = b[16:]
144+
}
145+
case riff.FourCC{'I', 'S', 'F', 'T'}:
146+
_, err := io.Copy(&buf, rr)
147+
if err != nil {
148+
return fmt.Errorf("unable to decode data: %w", err)
149+
}
150+
fmt.Fprintf(w, "%s %s\n", indent, buf.String())
60151
}
61152
}
62153

extract.go

Lines changed: 0 additions & 11 deletions
This file was deleted.

extract_video.go

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,37 @@
11
package matroska
22

33
import (
4+
"encoding/binary"
45
"fmt"
6+
"github.com/coding-socks/matroska/internal/avi"
7+
"github.com/coding-socks/matroska/internal/riff"
58
"io"
9+
"math"
10+
"os"
611
)
712

8-
func extractTrackVideo(w io.Writer, s *Scanner, t TrackEntry) error {
13+
func extractTrackVideo(w *os.File, s *Scanner, t TrackEntry) error {
914
switch t.CodecID {
1015
case VideoCodecMSCOMP:
1116
return extractTrackMSCOMP(w, s, t)
1217
}
1318
return fmt.Errorf("matroska: unknown audio codec %s", t.CodecID)
1419
}
1520

16-
func extractTrackMSCOMP(w io.Writer, s *Scanner, t TrackEntry) error {
21+
func extractTrackMSCOMP(w io.WriterAt, s *Scanner, t TrackEntry) error {
1722
scale := s.Info().TimestampScale
18-
var blocks []block
23+
if t.Video == nil {
24+
return fmt.Errorf("matroska: missing video stream")
25+
}
26+
27+
ww, err := avi.NewWriter(w)
28+
if err != nil {
29+
return fmt.Errorf("matroska: could not to initiate avi file: %w", err)
30+
}
31+
defer ww.Close()
32+
33+
videoStreamID := avi.NewStreamID(0, avi.StreamTypeDC)
34+
var totalFrames uint32 = 0
1935
for s.Next() {
2036
c := s.Cluster()
2137
m := len(c.SimpleBlock) + len(c.BlockGroup)
@@ -30,22 +46,63 @@ func extractTrackMSCOMP(w io.Writer, s *Scanner, t TrackEntry) error {
3046
if block.TrackNumber() != t.TrackNumber {
3147
continue
3248
}
33-
blocks = append(blocks, block)
49+
frames := block.Frames()
50+
for i := range frames {
51+
var flags uint32 = 0
52+
if i == 0 && ((block.Flags() & SimpleBlockFlagKeyframe) > 0) {
53+
flags |= avi.AVIIF_KEYFRAME
54+
}
55+
if err := ww.WriteData(videoStreamID, frames[i], flags); err != nil {
56+
return err
57+
}
58+
totalFrames++
59+
}
3460
}
35-
for i := range c.BlockGroup {
36-
block, err := ReadBlock(c.BlockGroup[i].Block, c.Timestamp)
61+
for _, group := range c.BlockGroup {
62+
block, err := ReadBlock(group.Block, c.Timestamp)
3763
if err != nil {
3864
return fmt.Errorf("matroska: could not create block struct: %w", err)
3965
}
4066
if block.TrackNumber() != t.TrackNumber {
4167
continue
4268
}
43-
blocks = append(blocks, block)
69+
frames := block.Frames()
70+
for i := range frames {
71+
var flags uint32 = 0
72+
// TODO: I'm not sure if this is correct. Maybe this is only relevant for
73+
// RAPs (i.e., frames that don't depend on other frames).
74+
if i == 0 && len(group.ReferenceBlock) == 0 {
75+
flags |= avi.AVIIF_KEYFRAME
76+
}
77+
if err := ww.WriteData(videoStreamID, frames[i], flags); err != nil {
78+
return err
79+
}
80+
totalFrames++
81+
}
4482
}
4583
}
46-
_ = scale
47-
for _, block := range blocks {
48-
_ = block
84+
var sf = avi.StreamFormat(*t.CodecPrivate)
85+
var handler riff.FourCC
86+
binary.LittleEndian.PutUint32(handler[:], sf.Compression())
87+
88+
var mh avi.MainHeader
89+
mh.SetMicroSecPerFrame(uint32(math.Ceil(float64(*t.DefaultDuration) / 1000.0)))
90+
mh.SetFlags(avi.AVIF_HASINDEX | avi.AVIF_ISINTERLEAVED)
91+
mh.SetTotalFrames(totalFrames)
92+
mh.SetStreams(1) // number of audio streams plus one
93+
mh.SetWidth(uint32(t.Video.PixelWidth))
94+
mh.SetHeight(uint32(t.Video.PixelHeight))
95+
96+
var sh avi.StreamHeader
97+
sh.SetType(avi.StreamTypeVIDS)
98+
sh.SetHandler(handler)
99+
sh.SetScale(uint32(scale))
100+
sh.SetRate(uint32(float64(scale) / float64(*t.DefaultDuration) * 1000000000.0))
101+
sh.SetLength(totalFrames)
102+
sh.SetSuggestedBufferSize(ww.MaxLen())
103+
104+
if err := ww.WriteHeader(sh, sf); err != nil {
105+
return fmt.Errorf("matroska: could not write header: %w", err)
49106
}
50107
return nil
51108
}

0 commit comments

Comments
 (0)