[vlc-commits] [Git][videolan/vlc][3.0.x] 2 commits: audiotoolbox: fix audiotoolbox macos26 crash on missing fragmented sysex

Felix Paul Kühne (@fkuehne) gitlab at videolan.org
Tue Jul 7 10:12:39 UTC 2026



Felix Paul Kühne pushed to branch 3.0.x at VideoLAN / VLC


Commits:
c13240e1 by Serhii Bykov at 2026-07-06T11:57:06+02:00
audiotoolbox: fix audiotoolbox macos26 crash on missing fragmented sysex

(cherry picked from commit a6e8d271fa69dc7c0875f0c6955e3b6bb995f4bb)
Signed-off-by: Marvin Scholz <epirat07 at gmail.com>

- - - - -
468a41f5 by Marvin Scholz at 2026-07-06T11:59:09+02:00
NEWS: Mention MIDI fix for macOS 26+

- - - - -


2 changed files:

- NEWS
- modules/codec/audiotoolbox_midi.c


Changes:

=====================================
NEWS
=====================================
@@ -9,6 +9,7 @@ Codecs:
  * Fix Speex leaks and packetization issues
  * Fix WebVTT CSS parsing and error handling
  * Fix FLAC and HEVC packetizer edge cases
+ * Fix AudioToolbox MIDI synthesizer crash on macOS 26+
 
 Demuxers:
  * Add support for CEA-708 closed captions in MP4


=====================================
modules/codec/audiotoolbox_midi.c
=====================================
@@ -71,11 +71,16 @@ struct decoder_sys_t
     AudioUnit   synthUnit;
     AudioUnit   outputUnit;
     date_t       end_date;
+    bool         sysex_in_progress;
+    UInt32       max_frames_per_slice;
+    int64_t      sample_time;
 };
 
 static int  DecodeBlock (decoder_t *p_dec, block_t *p_block);
 static void Flush (decoder_t *);
 
+static const UInt32 kAudioUnitDefaultMaxFrames = 1024;
+
 /* MIDI constants */
 enum
 {
@@ -87,6 +92,7 @@ enum
     kMidiMessage_ChannelPressure    = 0xD0,
     kMidiMessage_PitchWheel         = 0xE0,
     kMidiMessage_SysEx              = 0xF0,
+    kMidiMessage_SysExEnd           = 0xF7,
 
     kMidiMessage_BankMSBControl     = 0,
     kMidiMessage_BankLSBControl     = 32,
@@ -163,6 +169,46 @@ bailout:
     return res;
 }
 
+static int RenderAudioUnit(decoder_t *p_dec, decoder_sys_t *p_sys, block_t *p_out,
+                           unsigned frames, int64_t base_sample_time)
+{
+    AudioTimeStamp timestamp = {};
+    timestamp.mFlags = kAudioTimeStampSampleTimeValid;
+
+    AudioBufferList bufferList;
+    bufferList.mNumberBuffers = 1;
+    bufferList.mBuffers[0].mNumberChannels = 2;
+
+    const size_t bytes_per_frame = sizeof(Float32) * 2;
+    const UInt32 max_frames = p_sys->max_frames_per_slice;
+    UInt32 rendered = 0;
+
+    while (rendered < frames) {
+        UInt32 chunk_frames = frames - rendered;
+        if (chunk_frames > max_frames)
+            chunk_frames = max_frames;
+
+        bufferList.mBuffers[0].mDataByteSize = chunk_frames * bytes_per_frame;
+        bufferList.mBuffers[0].mData =
+            (uint8_t *)p_out->p_buffer + (rendered * bytes_per_frame);
+
+        timestamp.mSampleTime = (Float64)(base_sample_time + rendered);
+
+        OSStatus status = AudioUnitRender(p_sys->outputUnit,
+                                          NULL,
+                                          &timestamp, 0,
+                                          chunk_frames, &bufferList);
+        if (status != noErr) {
+            msg_Warn(p_dec, "rendering audio unit failed: %i", (int)status);
+            return VLC_EGENERIC;
+        }
+
+        rendered += chunk_frames;
+    }
+
+    return VLC_SUCCESS;
+}
+
 static int SetSoundfont(decoder_t *p_dec, AudioUnit synthUnit, const char *sfPath) {
     if (!sfPath) {
         msg_Dbg(p_dec, "using default soundfont");
@@ -203,6 +249,9 @@ static int Open(vlc_object_t *p_this)
         return VLC_ENOMEM;
 
     p_sys->graph = NULL;
+    p_sys->sysex_in_progress = false;
+    p_sys->max_frames_per_slice = 0;
+    p_sys->sample_time = 0;
     status = CreateAUGraph(&p_sys->graph, &p_sys->synthUnit, &p_sys->outputUnit);
     if (unlikely(status != noErr)) {
         msg_Err(p_dec, "failed to create audiograph (%i)", status);
@@ -252,6 +301,18 @@ static int Open(vlc_object_t *p_this)
         goto bailout;
     }
 
+    UInt32 max_frames = 0;
+    UInt32 max_frames_size = sizeof(max_frames);
+    status = AudioUnitGetProperty(p_sys->outputUnit,
+                                  kAudioUnitProperty_MaximumFramesPerSlice,
+                                  kAudioUnitScope_Global, 0,
+                                  &max_frames, &max_frames_size);
+    if (status == noErr && max_frames > 0)
+        p_sys->max_frames_per_slice = max_frames;
+    else
+        // Use a conservative default if the AU doesn't report its limit.
+        p_sys->max_frames_per_slice = kAudioUnitDefaultMaxFrames;
+
     // Prepare the AU
     status = AUGraphInitialize (p_sys->graph);
     if (unlikely(status != noErr)) {
@@ -311,6 +372,8 @@ static void Flush (decoder_t *p_dec)
     decoder_sys_t *p_sys = p_dec->p_sys;
 
     date_Set(&p_sys->end_date, VLC_TICK_INVALID);
+    p_sys->sysex_in_progress = false;
+    p_sys->sample_time = 0;
 
     // Turn all sound on all channels off
     // else 'old' notes could still be playing
@@ -338,6 +401,7 @@ static int DecodeBlock (decoder_t *p_dec, block_t *p_block)
 
     if (p_block->i_pts > VLC_TICK_INVALID && !date_Get(&p_sys->end_date)) {
         date_Set(&p_sys->end_date, p_block->i_pts);
+        p_sys->sample_time = 0;
     } else if (p_block->i_pts < date_Get(&p_sys->end_date)) {
         msg_Warn(p_dec, "MIDI message in the past?");
         goto drop;
@@ -349,6 +413,16 @@ static int DecodeBlock (decoder_t *p_dec, block_t *p_block)
     uint8_t event = p_block->p_buffer[0];
     uint8_t data1 = (p_block->i_buffer > 1) ? (p_block->p_buffer[1]) : 0;
     uint8_t data2 = (p_block->i_buffer > 2) ? (p_block->p_buffer[2]) : 0;
+    const bool sysex_continue = p_sys->sysex_in_progress &&
+                                (event < 0x80 || event == kMidiMessage_SysExEnd);
+
+    if (event == kMidiMessage_SysEx || sysex_continue)
+    {
+        bool ends_here = (p_block->i_buffer >= 1 &&
+                          p_block->p_buffer[p_block->i_buffer - 1] == kMidiMessage_SysExEnd);
+        p_sys->sysex_in_progress = !ends_here;
+        goto drop;
+    }
 
     switch (event & 0xF0)
     {
@@ -359,15 +433,12 @@ static int DecodeBlock (decoder_t *p_dec, block_t *p_block)
         case kMidiMessage_ProgramChange:
         case kMidiMessage_ChannelPressure:
         case kMidiMessage_PitchWheel:
+            p_sys->sysex_in_progress = false;
             MusicDeviceMIDIEvent(p_sys->synthUnit, event, data1, data2, 0);
         break;
 
-        case kMidiMessage_SysEx:
-            if (p_block->i_buffer < UINT32_MAX)
-                MusicDeviceSysEx(p_sys->synthUnit, p_block->p_buffer, (UInt32)p_block->i_buffer);
-        break;
-
         default:
+            p_sys->sysex_in_progress = false;
             msg_Warn(p_dec, "unhandled MIDI event: %x", event & 0xF0);
         break;
     }
@@ -386,28 +457,12 @@ static int DecodeBlock (decoder_t *p_dec, block_t *p_block)
         goto drop;
 
     p_out->i_pts = date_Get(&p_sys->end_date );
+    const int64_t base_sample_time = p_sys->sample_time;
     p_out->i_length = date_Increment(&p_sys->end_date, frames)
                       - p_out->i_pts;
+    p_sys->sample_time += frames;
 
-    // Prepare Timestamp for the AudioUnit render call
-    AudioTimeStamp timestamp = {};
-    timestamp.mFlags = kAudioTimeStampWordClockTimeValid;
-    timestamp.mWordClockTime = p_out->i_pts;
-
-    // Prepare Buffer for the AudioUnit render call
-    AudioBufferList bufferList;
-    bufferList.mNumberBuffers = 1;
-    bufferList.mBuffers[0].mNumberChannels = 2;
-    bufferList.mBuffers[0].mDataByteSize = frames * sizeof(Float32) * 2;
-    bufferList.mBuffers[0].mData = p_out->p_buffer;
-
-    status = AudioUnitRender(p_sys->outputUnit,
-                             NULL,
-                             &timestamp, 0,
-                             frames, &bufferList);
-
-    if (status != noErr) {
-        msg_Warn(p_dec, "rendering audio unit failed: %i", status);
+    if (RenderAudioUnit(p_dec, p_sys, p_out, frames, base_sample_time) != VLC_SUCCESS) {
         block_Release(p_out);
         p_out = NULL;
     }



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/1251b4ee0958421b30cf15e0630c1750c4c15e61...468a41f57a630abc00ba926a612b6d5e900eac33

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/1251b4ee0958421b30cf15e0630c1750c4c15e61...468a41f57a630abc00ba926a612b6d5e900eac33
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help




More information about the vlc-commits mailing list