@@ -2,13 +2,17 @@ package matroska
22
33import (
44 "fmt"
5+ "github.com/coding-socks/matroska/internal/vorbis"
56 "io"
7+ "math/rand/v2"
68)
79
810func extractTrackAudio (w io.Writer , s * Scanner , t TrackEntry ) error {
911 switch t .CodecID {
1012 case AudioCodecMP2 , AudioCodecMP3 :
1113 return extractTrackMPEG (w , s , t )
14+ case AudioCodecVORBIS :
15+ return extractTrackVORBIS (w , s , t )
1216 }
1317 return fmt .Errorf ("matroska: unknown audio codec %s" , t .CodecID )
1418}
@@ -25,23 +29,120 @@ func extractTrackMPEG(w io.Writer, s *Scanner, t TrackEntry) error {
2529 if err != nil {
2630 return fmt .Errorf ("matroska: could not create block struct: %w" , err )
2731 }
28- if block .TrackNumber () == t .TrackNumber {
29- if _ , err := io .Copy (w , block .Data ()); err != nil {
30- return err
31- }
32+ if block .TrackNumber () != t .TrackNumber {
33+ continue
34+ }
35+ if _ , err := io .Copy (w , block .Data ()); err != nil {
36+ return err
3237 }
3338 }
3439 for i := range c .BlockGroup {
3540 block , err := ReadBlock (c .BlockGroup [i ].Block , c .Timestamp )
3641 if err != nil {
3742 return fmt .Errorf ("matroska: could not create block struct: %w" , err )
3843 }
39- if block .TrackNumber () == t .TrackNumber {
40- if _ , err := io .Copy (w , block .Data ()); err != nil {
44+ if block .TrackNumber () != t .TrackNumber {
45+ continue
46+ }
47+ if _ , err := io .Copy (w , block .Data ()); err != nil {
48+ return err
49+ }
50+ }
51+ }
52+ return nil
53+ }
54+
55+ // extractTrackVORBIS is based on the Vorbis I specification created by the Xiph.Org Foundation.
56+ // See: https://xiph.org/vorbis/doc/Vorbis_I_spec.pdf
57+ func extractTrackVORBIS (w io.Writer , s * Scanner , track TrackEntry ) error {
58+ serialNum := rand .Int32 ()
59+
60+ vw := vorbis .NewWriter (w , serialNum )
61+
62+ codecPrivate := * track .CodecPrivate
63+ frames := Frames (LacingFlagXiph , codecPrivate )
64+ if len (frames ) != 3 {
65+ return fmt .Errorf ("matroska: Vorbis audio track requires 3 header pages, got %d" , len (frames ))
66+ }
67+
68+ ih := frames [0 ]
69+ if err := vw .WriteIdentHeader (ih ); err != nil {
70+ return err
71+ }
72+ iheader , err := vorbis .ParseIdentificationHeader ([30 ]byte (ih ))
73+ if err != nil {
74+ return err
75+ }
76+ blockSizes := []uint16 {
77+ iheader .Blocksize0 ,
78+ iheader .Blocksize1 ,
79+ }
80+ cheader , err := vorbis .ParseCommentHeader (frames [1 ])
81+ if err != nil {
82+ return err
83+ }
84+ _ = cheader
85+
86+ if err := vw .WriteHeaders (frames [1 ], frames [2 ]); err != nil {
87+ return err
88+ }
89+
90+ var (
91+ prevBlockSize uint64 = 0
92+ granpos uint64 = 0
93+
94+ prevFrame []byte //
95+ )
96+ frames = frames [:0 ] // clear value
97+
98+ for s .Next () {
99+ c := s .Cluster ()
100+ if (len (c .SimpleBlock ) + len (c .BlockGroup )) == 0 {
101+ continue
102+ }
103+
104+ for i := range c .SimpleBlock {
105+ block , err := ReadSimpleBlock (c .SimpleBlock [i ], c .Timestamp )
106+ if err != nil {
107+ return fmt .Errorf ("matroska: could not create block struct: %w" , err )
108+ }
109+ if block .TrackNumber () != track .TrackNumber {
110+ continue
111+ }
112+ frames = append (frames , block .Frames ()... )
113+ }
114+ for i := range c .BlockGroup {
115+ block , err := ReadBlock (c .BlockGroup [i ].Block , c .Timestamp )
116+ if err != nil {
117+ return fmt .Errorf ("matroska: could not create block struct: %w" , err )
118+ }
119+ if block .TrackNumber () != track .TrackNumber {
120+ continue
121+ }
122+ frames = append (frames , block .Frames ()... )
123+ }
124+
125+ for _ , frame := range frames {
126+ blockSize := uint64 (blockSizes [(frame [0 ]>> 1 )& 1 ])
127+
128+ if prevFrame != nil {
129+ if err := vw .Segment (prevFrame , granpos , false ); err != nil {
41130 return err
42131 }
132+
133+ // We need at least two segment to calculate this
134+ granpos += (blockSize + prevBlockSize ) / 4
43135 }
136+
137+ prevBlockSize = blockSize
138+ prevFrame = frame
44139 }
140+
141+ frames = frames [:0 ]
45142 }
46- return nil
143+ if prevFrame == nil {
144+ return nil
145+ }
146+ // only this element can be the last.
147+ return vw .Segment (prevFrame , granpos , true )
47148}
0 commit comments