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.
This commit is contained in:
Ryan C. Gordon 2023-05-09 14:16:15 -04:00
parent 04e17d4e46
commit 7b6dabd81f
No known key found for this signature in database
GPG Key ID: FA148B892AB48044

View File

@ -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);
}