Browse Source

audio: AudioStreams shouldn't overflow output buffers.

Before, as ConvertAudio might have expanded data in-place temporarily during
its work, this could blow up. Now if there's a chance of that, it'll
work out of an internal buffer and copy the final results to the output
buffer.

If the output format can handle the temporary expansion, we write directly
to the output buffer without the extra copy.

Fixes #7668.
Ryan C. Gordon 1 year ago
parent
commit
7b6dabd81f
1 changed files with 21 additions and 9 deletions
  1. 21 9
      src/audio/SDL_audiocvt.c

+ 21 - 9
src/audio/SDL_audiocvt.c

@@ -456,6 +456,8 @@ struct SDL_AudioStream
     int history_buffer_frames;
     int future_buffer_filled_frames;
 
+    int max_sample_frame_size;
+
     int src_sample_frame_size;
     SDL_AudioFormat src_format;
     int src_channels;
@@ -586,6 +588,7 @@ static int SetAudioStreamFormat(SDL_AudioStream *stream, SDL_AudioFormat src_for
 
     stream->resampler_padding_frames = resampler_padding_frames;
     stream->history_buffer_frames = history_buffer_frames;
+    stream->max_sample_frame_size = max_sample_frame_size;
     stream->src_sample_frame_size = src_sample_frame_size;
     stream->src_format = src_format;
     stream->src_channels = src_channels;
@@ -790,12 +793,7 @@ static int CalculateAudioStreamWorkBufSize(const SDL_AudioStream *stream, int le
     int workbuflen = len;
     int inputlen;
 
-    inputlen = workbuf_frames * stream->src_sample_frame_size;
-    if (inputlen > workbuflen) {
-        workbuflen = inputlen;
-    }
-
-    inputlen = workbuf_frames * stream->pre_resample_channels * sizeof (float);
+    inputlen = workbuf_frames * stream->max_sample_frame_size;
     if (inputlen > workbuflen) {
         workbuflen = inputlen;
     }
@@ -834,6 +832,7 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
     const int dst_channels = stream->dst_channels;
     const int dst_rate = stream->dst_rate;
     const int dst_sample_frame_size = stream->dst_sample_frame_size;
+    const int max_sample_frame_size = stream->max_sample_frame_size;
     const int pre_resample_channels = stream->pre_resample_channels;
     const int resampler_padding_frames = stream->resampler_padding_frames;
     const int history_buffer_frames = stream->history_buffer_frames;
@@ -963,7 +962,13 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
     /* Not resampling? It's an easy conversion (and maybe not even that!) */
     if (src_rate == dst_rate) {
         SDL_assert(resampler_padding_frames == 0);
-        ConvertAudio(input_frames, workbuf, src_format, src_channels, buf, dst_format, dst_channels);
+        /* see if we can do the conversion in-place (will fit in `buf` while in-progress), or if we need to do it in the workbuf and copy it over */
+        if (max_sample_frame_size <= dst_sample_frame_size) {
+            ConvertAudio(input_frames, workbuf, src_format, src_channels, buf, dst_format, dst_channels);
+        } else {
+            ConvertAudio(input_frames, workbuf, src_format, src_channels, workbuf, dst_format, dst_channels);
+            SDL_memcpy(workbuf, buf, input_frames * dst_sample_frame_size);
+        }
         return input_frames * dst_sample_frame_size;
     }
 
@@ -974,7 +979,7 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
         resample_outbuf = (float *) buf;
     } else {
         const int input_bytes = input_frames * pre_resample_channels * sizeof (float);
-        resample_outbuf = (float *) (workbuf + input_bytes);
+        resample_outbuf = (float *) ((workbuf + stream->work_buffer_allocation) - input_bytes);  /* do at the end of the buffer so we have room for final convert at front. */
     }
 
     ResampleAudio(pre_resample_channels, src_rate, dst_rate,
@@ -983,7 +988,14 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
                   resample_outbuf, output_frames);
 
     /* Get us to the final format! */
-    ConvertAudio(output_frames, resample_outbuf, SDL_AUDIO_F32, pre_resample_channels, buf, dst_format, dst_channels);
+    /* see if we can do the conversion in-place (will fit in `buf` while in-progress), or if we need to do it in the workbuf and copy it over */
+    if (max_sample_frame_size <= dst_sample_frame_size) {
+        ConvertAudio(output_frames, resample_outbuf, SDL_AUDIO_F32, pre_resample_channels, buf, dst_format, dst_channels);
+    } else {
+        ConvertAudio(output_frames, resample_outbuf, SDL_AUDIO_F32, pre_resample_channels, workbuf, dst_format, dst_channels);
+        SDL_memcpy(workbuf, buf, output_frames * dst_sample_frame_size);
+    }
+
     return (int) (output_frames * dst_sample_frame_size);
 }