fix(sdlplayer): handle multi-channel open failure and harden play_cb#676
Conversation
There was a problem hiding this comment.
Sorry @dengzhongyuan365-dev, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
7ed79f1 to
27931bb
Compare
When playing media with > 2 channels (e.g. 7-channel WAV/AC3), some audio sinks reject the multi-channel SDL_AudioSpec, leaving the device unopened and producing no audio or distorted output. libvlc_audio_setup_cb previously bailed out as soon as SDL_OpenAudio returned -1, so any sink that did not accept the source channel layout silently broke playback. Retry SDL_OpenAudio with channels=2 before giving up so playback still works on stereo-only outputs. Other small fixes / hardening on top of that: resolve "SDL_Init" by its real name (it was being looked up as "SDL_GetAudioStatus" and slipping through only because resolveSdlSymbol silently returned NULL), call SDL_CloseAudio before reopening in setup_cb to avoid leaking the previous device when VLC reconfigures the format, log SDL_OpenAudio failures on resume() instead of swallowing them, and lower SDL audio log priority from ERROR to WARN so useful diagnostics are no longer filtered out. Also harden libvlc_audio_play_cb against edge cases that could previously corrupt the mix buffer or read past the source samples: compute the buffer size with size_t to avoid int overflow at high sample rates / large buffers, guard against zero rate / channels / count, and split the channel-mismatch path into src>dst (truncate), src==dst (memcpy), src<dst (pad with last channel) so we never read past the source buffer. 将 SDL 播放器在多声道设备打开失败时的处理从直接报错改为自动降级 到立体声重试。原方案在 SDL_OpenAudio 拒绝多声道 spec 时 setup_cb 会立刻返回 -1,导致 7 声道 WAV/AC3 等文件在不支持多声道的输出端 完全没声音或破音。修复后即便 sink 只接受立体声,播放也能继续。 同时修正 SDL_Init 符号名误写为 SDL_GetAudioStatus 的 typo、setup 重新打开设备前先 CloseAudio 防止句柄泄漏、为 resume 路径下的 OpenAudio 失败补日志、SDL 日志等级 ERROR -> WARN 保留可观察性。 play_cb 入口加固:用 size_t 累乘 size 避免高采样率下 int 溢出, 零参数兜底,声道不匹配按 src>dst / == / < 三路分别处理,避免 越界读源 buffer。 Log: 修复多声道音频文件(如 7 声道 WAV/AC3)在仅支持立体声的音频输出设备上无声或破音的问题 PMS: BUG-362915 Influence: 修复后多声道源在不支持对应声道布局的输出设备上会自动降级到立体声继续播放;play_cb 在异常采样率/通道数下不再读越界,高采样率长 buffer 不再有 int 溢出风险。
27931bb to
8d6fba5
Compare
deepin pr auto review你好!我是CodeGeeX。我已仔细审查了你提供的 Git Diff。本次修改主要涉及 整体来看,这次修改显著提升了代码的健壮性和安全性,特别是修复了 VLA(变长数组)溢出风险、增加了 SDL 函数返回值检查、处理了多声道降级逻辑等。但仍有几处关键的安全和逻辑细节需要进一步优化。 以下是详细的审查意见: 一、 语法与逻辑1. VLA(变长数组)栈溢出风险(严重) + const size_t size = static_cast<size_t>(count) * dstChannels * bytesPerSample;
// ...
char curSamples[size]; // 危险:VLA
std::vector<char> curSamples(size);
// 后续 memcpy 的 curSamples 替换为 curSamples.data()2. + const unsigned bytesPerSample = sdlMediaPlayer->_rate / 8;
if (sdlMediaPlayer->_rate == 0 || /* 其他除数 */) return;
const unsigned bytesPerSample = sdlMediaPlayer->_rate / 8; 3. + desiredAS.channels = 2;
+ if (OpenAudio(&desiredAS, &sdlMediaPlayer->obtainedAS) < 0) {
+ qCCritical(dmMusic) << "Failed to open audio device even with stereo";
+ return -1;
+ }
二、 代码质量1. 动态库符号解析缺少空指针检查(高风险) + SDL_CloseAudio_function CloseAudio = (SDL_CloseAudio_function)VlcDynamicInstance::VlcFunctionInstance()->resolveSdlSymbol("SDL_CloseAudio");
// ...
+ CloseAudio();
if (!CloseAudio) {
qCWarning(dmMusic) << "Failed to resolve SDL_CloseAudio";
} else {
CloseAudio();
}2. 冗余的 + QByteArray ba(curSamples, static_cast<int>(size));
3. +.codegraph/
\ No newline at end of file
三、 代码性能1. + for (unsigned i = 0; i < count; ++i) {
+ for (unsigned j = 0; j < dstChannels; ++j) {
+ memcpy(curSamples + ..., static_cast<const char *>(samples) + ..., bytesPerSample);
+ }
+ }
四、 代码安全1. 源声道数越界读取风险 + // 源声道少于目的:
+ for (unsigned i = 0; i < count; ++i) {
+ for (unsigned j = 0; j < dstChannels; ++j) {
+ const unsigned srcChan = (j < srcChannels) ? j : (srcChannels - 1);
+ memcpy(curSamples + (i * dstChannels + j) * bytesPerSample,
+ static_cast<const char *>(samples) + (i * srcChannels + srcChan) * bytesPerSample,
+ bytesPerSample);
+ }
+ }
总结与修改建议代码片段针对最严重的 VLA 栈溢出和函数指针空指针问题,建议对 // libvlc_audio_play_cb 修改建议
const size_t size = static_cast<size_t>(count) * dstChannels * bytesPerSample;
if (size == 0) return;
// 使用堆内存代替栈上的 VLA,防止大 buffer 导致栈溢出
std::vector<char> curSamples(size);
if (srcChannels > dstChannels) {
// ... 原有逻辑,curSamples 替换为 curSamples.data() ...
} else if (srcChannels == dstChannels) {
memcpy(curSamples.data(), samples, size);
} else {
// ... 原有逻辑,curSamples 替换为 curSamples.data() ...
}
Q_ASSERT(size <= INT_MAX); // 确保不发生截断
QByteArray ba(curSamples.data(), static_cast<int>(size));
// libvlc_audio_setup_cb 修改建议
SDL_CloseAudio_function CloseAudio = (SDL_CloseAudio_function)VlcDynamicInstance::VlcFunctionInstance()->resolveSdlSymbol("SDL_CloseAudio");
if (CloseAudio) {
CloseAudio(); // 确保非空再调用
} else {
qCWarning(dmMusic) << "Failed to resolve SDL_CloseAudio, skipping close operation";
} |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: dengzhongyuan365-dev, wyu71 The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
/merge |
|
This pr cannot be merged! (status: blocked) |
|
/merge |
When playing media with > 2 channels (e.g. 7-channel WAV/AC3), some
audio sinks reject the multi-channel SDL_AudioSpec, leaving the device
unopened and producing no audio or distorted output. libvlc_audio_setup_cb
previously bailed out as soon as SDL_OpenAudio returned -1, so any sink
that did not accept the source channel layout silently broke playback.
Retry SDL_OpenAudio with channels=2 before giving up so playback still
works on stereo-only outputs. Other small fixes / hardening on top of
that: resolve "SDL_Init" by its real name (it was being looked up as
"SDL_GetAudioStatus" and slipping through only because resolveSdlSymbol
silently returned NULL), call SDL_CloseAudio before reopening in
setup_cb to avoid leaking the previous device when VLC reconfigures
the format, log SDL_OpenAudio failures on resume() instead of swallowing
them, and lower SDL audio log priority from ERROR to WARN so useful
diagnostics are no longer filtered out.
Also harden libvlc_audio_play_cb against edge cases that could
previously corrupt the mix buffer or read past the source samples:
compute the buffer size with size_t to avoid int overflow at high
sample rates / large buffers, guard against zero rate / channels /
count, and split the channel-mismatch path into src>dst (truncate),
src==dst (memcpy), src<dst (pad with last channel) so we never read
past the source buffer.
将 SDL 播放器在多声道设备打开失败时的处理从直接报错改为自动降级
到立体声重试。原方案在 SDL_OpenAudio 拒绝多声道 spec 时 setup_cb
会立刻返回 -1,导致 7 声道 WAV/AC3 等文件在不支持多声道的输出端
完全没声音或破音。修复后即便 sink 只接受立体声,播放也能继续。
同时修正 SDL_Init 符号名误写为 SDL_GetAudioStatus 的 typo、setup
重新打开设备前先 CloseAudio 防止句柄泄漏、为 resume 路径下的
OpenAudio 失败补日志、SDL 日志等级 ERROR -> WARN 保留可观察性。
play_cb 入口加固:用 size_t 累乘 size 避免高采样率下 int 溢出,
零参数兜底,声道不匹配按 src>dst / == / < 三路分别处理,避免
越界读源 buffer。
Log: 修复多声道音频文件(如 7 声道 WAV/AC3)在仅支持立体声的音频输出设备上无声或破音的问题
PMS: BUG-362915
Influence: 修复后多声道源在不支持对应声道布局的输出设备上会自动降级到立体声继续播放;play_cb 在异常采样率/通道数下不再读越界,高采样率长 buffer 不再有 int 溢出风险。