c - 通过 alsa 输出声音的函数在通过 pthread create 调用时不起作用:没有声音,100% CPU 使用率
问题描述
我有一个程序通过套接字接收消息并根据消息开始或停止播放某个声音文件。为了让“停止”消息起作用,我需要从单独的线程播放声音。我的解决方案是使用我使用 pthread_create() 调用的函数中的 alsa 播放声音,并在收到停止消息后使用 pthread_cancel() 结束线程。播放声音的函数称为 play_sound(void *args);
这是有效的:
struct args *args;
args->fp = fopen("path/to/soundfile.wav", "r");
args->volume = 1;
play_sound((void *) args);
但是一旦我尝试从一个新线程中运行该函数,我的两个线程上都没有声音和 100% 的 CPU 使用率:
struct args *args;
int sound_thread;
args->fp = fopen("path/to/soundfile.wav", "r");
args->volume = 1;
pthread_create(&sound_thread, NULL, (void *) play_sound, (void *) args);
我什至不知道从哪里开始故障排除。
我的代码如下所示:
#include <alsa/asoundlib.h>
#include <alsa/mixer.h>
#include <stdbool.h>
#include <pthread.h>
#include "server.h"
#include "sound.h"
//#include "log.h"
int sound_thread;
struct args {
FILE *fp;
float volume;
};
void init_sound ()
{
sound_thread = -1;
}
void stop_sound ()
{
if (sound_thread != -1) {
pthread_cancel(sound_thread);
keep_playing = false;
sound_thread = -1;
}
}
void dispatch_sound (FILE *fp, float volume)
{
// this function serves to create a new thread for the
// sound to be played from. This is what's giving me
// headaches.
if (sound_thread != -1) {
stop_sound();
}
struct args *args = (struct args *) malloc(sizeof(struct args));
args->fp = fp;
args->volume = volume;
if (pthread_create(&sound_thread, NULL, (void *) play_sound, args) != 0)
sound_thread = -1;
}
}
bool play_sound (void *args)
{
// This function actually plays the sound using functions
// from the alsa lib. it works when invoked regularly without
// threading.
keep_playing = true;
FILE *fp;
int volume;
bool success;
unsigned int samplerate;
int bufsz;
char *buf;
snd_pcm_t *pcm;
snd_pcm_hw_params_t *params;
snd_pcm_uframes_t frames;
samplerate = SAMPLERATE;
fp = ((struct args *) args)->fp;
volume = ((struct args *) args)->volume;
// volume is not actually used right now, since I took out
// the function that sets the volume before playing the
// audio in order to make it easier to pinpoint the issue.
if (snd_pcm_open(&pcm, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
success = false;
}
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(pcm, params);
if (snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
success = false;
}
if (snd_pcm_hw_params_set_format(pcm, params, SND_PCM_FORMAT_S16_LE) < 0) {
success = false;
}
if (snd_pcm_hw_params_set_channels(pcm, params, CHANNELS) < 0) {
success = false;
}
if (snd_pcm_hw_params_set_rate_near(pcm, params, &samplerate, 0) < 0) {
success = false;
}´
if (snd_pcm_hw_params(pcm, params) < 0) {
success = false;
}
snd_pcm_hw_params_get_period_size(params, &frames, 0);
bufsz = frames * CHANNELS * SAMPLE_SIZE;
buf = (char *) malloc(bufsz);
while (keep_playing) {
while (fread(buf, bufsz, 1, fp) != 0 && keep_playing) {
int err;
if ((err = snd_pcm_writei(pcm, buf, frames)) == -EPIPE) {
snd_pcm_prepare(pcm);
}
}
rewind(fp);
}
snd_pcm_drain(pcm);
snd_pcm_close(pcm);
free(buf);
return success;
}
解决方案
从 pthread_cancel 的手册页:
在 Linux 上,取消是使用信号实现的。在 NPTL 线程实现下,第一个实时信号(即信号 32)用于此目的。在 LinuxThreads 上,如果实时信号可用,则使用第二个实时信号,否则使用 SIGUSR2。
在您的 while(keep_playing) 循环中,您产生的线程不足以处理取消信号;在你的主线程中;您不是在等待取消请求的结果,因此两个线程都占用了 cpu。
在您调用 pthread_cancel 之后重新开始播放声音和 pthread_join() 之前的一小段延迟应该可以解决您的问题。
推荐阅读
- javascript - 在提交更改 http 参数并发出请求时
- java - Convert Java Date from one format to another without converting it into string
- php - 谷歌recaptcha和表格不起作用
- machine-learning - SelectKBest sklearn 不同的计算时间
- node.js - Facebook Messenger API 发送 Base64 图像
- polymer - 当我尝试在对象内显示数组时,dom-repeat 向我返回“无法读取未定义的属性‘应用’”
- python - python -m idlelib 无法将输出保存在文件中
- zend-framework - Zend 框架 1.11.11 与 PHP 5.4 错误
- ios - 如何在共享扩展中集成 Fabric/Crashlytics
- java - 契约体匹配——匹配节点数