1- // Copyright (c) 2022 David Vogel
1+ // Copyright (c) 2022-2025 David Vogel
22//
33// This software is released under the MIT License.
44// https://opensource.org/licenses/MIT
@@ -7,10 +7,12 @@ package main
77
88import (
99 "fmt"
10+ "io"
1011 "log"
1112 "os"
1213 "path/filepath"
1314
15+ "github.com/Dadido3/mxv-demuxer/mxv"
1416 "github.com/moutend/go-wav"
1517)
1618
@@ -22,34 +24,13 @@ func demuxFile(filename string) error {
2224 return fmt .Errorf ("failed to open file: %w" , err )
2325 }
2426
25- chunk , err := ParseChunk64 (file )
27+ mxvReader , err := mxv . NewReader (file )
2628 if err != nil {
27- return fmt .Errorf ("failed to parse root chunk : %w" , err )
29+ return fmt .Errorf ("failed to read MXV file : %w" , err )
2830 }
2931
30- chunkMXRIFF64 , ok := chunk .(* ChunkMXRIFF64 )
31- if ! ok {
32- return fmt .Errorf ("file doesn't contain a MXRIFF64 as root chunk" )
33- }
34-
35- // Get wave format.
36- var audioSampleRate , audioBitDepth , audioChannels int
37- var wavObject * wav.File
38- for _ , childChunk := range chunkMXRIFF64 .Chunks {
39- if wfmtChunk , ok := childChunk .(* ChunkMXWFMT64 ); ok {
40- audioSampleRate = int (wfmtChunk .ByteRate ) / int (wfmtChunk .BytesPerSample ) // Seems to be more reliable than just using the SampleRate field.
41- audioBitDepth = int (wfmtChunk .ChannelBitDepth )
42- audioChannels = int (wfmtChunk .Channels )
43- log .Printf ("Found waveform table chunk: %v" , wfmtChunk )
44- log .Printf ("Set up audio: SampleRate = %d, BitDepth = %d, Channels = %d" , audioSampleRate , audioBitDepth , audioChannels )
45-
46- // Set up empty wav object.
47- if wavObject , err = wav .New (audioSampleRate , audioBitDepth , audioChannels ); err != nil {
48- return fmt .Errorf ("failed to create wav object: %w" , err )
49- }
50-
51- break
52- }
32+ if err := mxvReader .PrepareLookupTable (); err != nil {
33+ return fmt .Errorf ("failed to prepare lookup table: %w" , err )
5334 }
5435
5536 // Create output directory.
@@ -59,54 +40,67 @@ func demuxFile(filename string) error {
5940 return fmt .Errorf ("failed to create output directory: %w" , err )
6041 }
6142
62- // Get MXLIST64 with audio and video frames (MXJVFL64).
63- var frameCounter , audioSampleCounter int
64- for _ , childChunk := range chunkMXRIFF64 .Chunks {
65- if listChunk , ok := childChunk .(* ChunkMXLIST64 ); ok && listChunk .ContentType == MXJVFL64 {
66-
67- // Go through list of video and audio frames.
68- for _ , frameChunk := range listChunk .Chunks {
69- switch frameChunk := frameChunk .(type ) {
70- case * ChunkMXJVVF64 :
71- // Create file and write image data into it.
72- videoFilename := filepath .Join (outputPath , fmt .Sprintf ("video-%06d.jpeg" , frameCounter ))
73- frameCounter ++
74-
75- if err := os .WriteFile (videoFilename , frameChunk .JPEGData , 0666 ); err != nil {
76- return err
77- }
78-
79- case * ChunkMXJVAF64 :
80- // Write audio data into wave object.
81- if wavObject != nil {
82- if _ , err := wavObject .Write (frameChunk .Data ); err != nil {
83- return err
84- }
85- audioSampleCounter += int (frameChunk .Samples )
86- } else {
87- log .Printf ("Found audio chunk, but there was no waveform table chunk." )
88- }
89-
90- }
91- }
43+ log .Printf ("MXV info: %+v." , mxvReader .Info )
9244
93- break
45+ // Video frames.
46+ log .Printf ("Extracting video frames." )
47+ for frame := range mxvReader .VideoFrames () {
48+ videoFilename := filepath .Join (outputPath , fmt .Sprintf ("video-%06d.jpeg" , frame ))
49+ file , err := os .Create (videoFilename )
50+ if err != nil {
51+ return fmt .Errorf ("failed to create file: %w" , err )
52+ }
53+ frameReader , err := mxvReader .VideoFrameData (frame )
54+ if err != nil {
55+ return fmt .Errorf ("failed to get video data stream: %w" , err )
56+ }
57+ if _ , err := io .Copy (file , frameReader ); err != nil {
58+ return fmt .Errorf ("failed to copy video data stream: %w" , err )
9459 }
9560 }
9661
97- // Write wav data into file.
98- if wavObject != nil {
62+ log .Printf ("Finished extracting video frames." )
63+
64+ if mxvReader .Info .HasAudio {
65+ // TODO: go-wav doesn't support streaming, therefore replace it
66+
67+ // Set up empty wav object.
68+ wavObject , err := wav .New (int (mxvReader .Info .AudioSampleRate ), int (mxvReader .Info .AudioChannelBitDepth ), int (mxvReader .Info .AudioChannels ))
69+ if err != nil {
70+ return fmt .Errorf ("failed to create wav object: %w" , err )
71+ }
72+
73+ // Append sample data.
74+ log .Printf ("Extracting audio samples." )
75+ for frame := range mxvReader .AudioFrames () {
76+ frameReader , _ , _ , err := mxvReader .AudioFrameData (frame )
77+ if err != nil {
78+ return fmt .Errorf ("failed to get audio data stream: %w" , err )
79+ }
80+ frameData , err := io .ReadAll (frameReader )
81+ if err != nil {
82+ return fmt .Errorf ("failed to read audio data stream: %w" , err )
83+ }
84+ if _ , err := wavObject .Write (frameData ); err != nil {
85+ return fmt .Errorf ("failed to append audio data to wave object: %w" , err )
86+ }
87+ }
88+
89+ // Write audio file to disk.
90+ audioFilename := filepath .Join (outputPath , "audio.wav" )
91+ log .Printf ("Writing extracted audio data to disk at %q." , audioFilename )
9992 wavTemp , err := wav .Marshal (wavObject )
10093 if err != nil {
10194 return fmt .Errorf ("failed to encode wave data: %w" , err )
10295 }
103- audioFilename := filepath .Join (outputPath , "audio.wav" )
10496 if err := os .WriteFile (audioFilename , wavTemp , 0666 ); err != nil {
10597 return fmt .Errorf ("failed to write audio file: %w" , err )
10698 }
99+
100+ log .Printf ("Finished writing audio data." )
107101 }
108102
109- log .Printf ("Completely demuxed %q: %d video frames, %d audio samples " , filename , frameCounter , audioSampleCounter )
103+ log .Printf ("Completely demuxed %q. " , filename )
110104
111105 return nil
112106}
0 commit comments