用linux装逼->mpg321

本文介绍了如何使用Linux命令行工具mpg321同时播放多个音频文件,以此创建复杂的起床闹钟场景,但也指出声音混杂的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

把装逼进行到底!提高生活质量,多用键盘,不买鼠标!


同时开了七个mpg321放不同的歌叫起床,声音好混乱啊~


find > list.txt 

A: mpg321 -@list.txt -zZv -l 0     
                                C^c = next
                                C^\ = quit
B: mpg321 -@list.txt -zZ  -l 0 &   
                                killall  mpg321



-------------------------------------------------------------------------------
MPG321(1)                                                            MPG321(1)






NAME
       mpg321 — Simple and lighweight command line MP3 player


SYNOPSIS
       mpg321 [options] file(s) | URL(s) | -


DESCRIPTION
       mpg321  is  a  free  command-line  mp3 player, which uses the mad audio
       decoding library. mpg321 was written to be a  drop-in  replacement  for
       the  (by-then)  non-free mpg123 player.  Some functions remain unimple‐
       mented, but mpg321 should function as a basic drop-in  replacement  for
       mpg123  front-ends  such as gqmpeg, and those programs which use mpg123
       to decode mp3 files (like gtoaster, and other CD-recording software).


OPTIONS
       -o devicetype
                 Set the output device type to devicetype.  devi
./audio_stream 禁用重采样 使用URL: https://ting8.yymp3.com/new27/liyugang6/6.mp3 正在下载音频文件... 等待音频数据... 等待缓冲: 2.0秒 (当前: 0.0秒) 缓冲中: 0.0秒/2.0秒 (0.0%) 文件大小: 3.05 MB 缓冲中: 1.1秒/2.0秒 (52.8%) 缓冲完成: 2.3秒/2.0秒 检测到新音频格式: 44100 Hz, 2 声道 播放格式: 44100 Hz, 2 声道 (原始: 44100 Hz) [重采样禁用] 缓冲: 186.6s | 速度: 1.020 | 队列: 2915.2kb 下载完成, 总大小: 3.05 MB 缓冲: 0.7s | 速度: 1.015 | 队列: 10.8kbbbbb 缓冲不足 (0.7秒 < 1.0秒), 重新缓冲... 等待缓冲: 2.0秒 (当前: 0.7秒) 下载完成,剩余数据: 0.7秒 缓冲恢复,继续播放 缓冲不足 (0.7秒 < 1.0秒), 重新缓冲... 等待缓冲: 2.0秒 (当前: 0.7秒) 下载完成,剩余数据: 0.7秒 缓冲恢复,继续播放 缓冲: 0.0s | 速度: 1.008 | 队列: 0.0kb 缓冲不足 (0.0秒 < 1.0秒), 重新缓冲... 等待缓冲: 2.0秒 (当前: 0.0秒) 下载完成,剩余数据: 0.0秒 缓冲恢复,继续播放 播放线程退出 播放结束 MP3文件而不是流媒体,已经播放完毕就不需要再去缓冲了,直接停止播放就行了,只有播放持续不断的流媒体才会尝试去缓冲,再优化一下 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <curl/curl.h> #include <mpg123.h> #include <pulse/simple.h> #include <pulse/error.h> #include <unistd.h> #include <samplerate.h> #include <time.h> #include <math.h> #include <malloc.h> #include <errno.h> #define BUFFER_SIZE 8192 #define USE_RESAMPLING 0 #define INITIAL_BUFFER_SECONDS 2.0 #define MIN_BUFFER_SECONDS 1.0 #define TARGET_BUFFER_SECONDS 2.0 #define BYTES_PER_SECOND (128 * 1000 / 8) // 共享数据结构 typedef struct { mpg123_handle *mh; pa_simple *pa_stream; pthread_mutex_t mutex; int stop; int format_initialized; int channels; long rate; int buffering; size_t buffer_bytes; double buffer_level; double playback_speed; #if USE_RESAMPLING SRC_STATE *resampler; float *resample_in_buf; float *resample_out_buf; size_t resample_in_size; size_t resample_out_size; long target_rate; #endif } PlayerState; // 自定义结构体用于存储音频数据块 struct data_chunk { unsigned char *data; size_t size; struct data_chunk *next; }; // 线程安全队列 struct data_queue { struct data_chunk *head; struct data_chunk *tail; pthread_mutex_t mutex; pthread_cond_t cond; pthread_cond_t buffer_cond; size_t total_size; size_t target_size; int finished; }; // 下载状态结构 struct DownloadStatus { size_t total_size; // 文件总大小 size_t downloaded_size; // 已下载大小 double last_update; // 最后更新时间 int display_active; // 是否显示进度 }; // 初始化队列 void queue_init(struct data_queue *q) { q->head = q->tail = NULL; pthread_mutex_init(&q->mutex, NULL); pthread_cond_init(&q->cond, NULL); pthread_cond_init(&q->buffer_cond, NULL); q->total_size = 0; q->target_size = 0; q->finished = 0; } // 添加数据到队列 void queue_push(struct data_queue *q, unsigned char *data, size_t size) { struct data_chunk *chunk = malloc(sizeof(struct data_chunk)); if (!chunk) return; chunk->data = malloc(size); if (!chunk->data) { free(chunk); return; } memcpy(chunk->data, data, size); chunk->size = size; chunk->next = NULL; pthread_mutex_lock(&q->mutex); if (q->tail) { q->tail->next = chunk; } else { q->head = chunk; } q->tail = chunk; q->total_size += size; if (q->target_size > 0 && q->total_size >= q->target_size) { pthread_cond_signal(&q->buffer_cond); } pthread_cond_signal(&q->cond); pthread_mutex_unlock(&q->mutex); } // 标记下载完成 void queue_finish(struct data_queue *q) { pthread_mutex_lock(&q->mutex); q->finished = 1; pthread_cond_signal(&q->cond); pthread_cond_signal(&q->buffer_cond); pthread_mutex_unlock(&q->mutex); } // 从队列获取数据 struct data_chunk *queue_pop(struct data_queue *q) { pthread_mutex_lock(&q->mutex); // 等待直到有数据或下载完成 while (!q->head && !q->finished) { pthread_cond_wait(&q->cond, &q->mutex); } // 如果队列为空且下载完成,返回NULL if (!q->head) { pthread_mutex_unlock(&q->mutex); return NULL; } struct data_chunk *chunk = q->head; q->head = chunk->next; if (!q->head) q->tail = NULL; q->total_size -= chunk->size; pthread_mutex_unlock(&q->mutex); return chunk; } // 清理队列 void queue_cleanup(struct data_queue *q) { pthread_mutex_lock(&q->mutex); struct data_chunk *chunk = q->head; while (chunk) { struct data_chunk *next = chunk->next; free(chunk->data); free(chunk); chunk = next; } q->head = q->tail = NULL; q->total_size = 0; pthread_mutex_unlock(&q->mutex); } // 设置缓冲目标并等待 void set_buffer_target(struct data_queue *q, struct DownloadStatus *status, size_t target_size) { pthread_mutex_lock(&q->mutex); q->target_size = target_size; // 暂停下载进度显示 if (status) status->display_active = 0; if (q->total_size >= target_size) { pthread_mutex_unlock(&q->mutex); if (status) status->display_active = 1; return; } printf("\n等待缓冲: %.1f秒 (当前: %.1f秒)\n", (double)target_size / BYTES_PER_SECOND, (double)q->total_size / BYTES_PER_SECOND); while (q->total_size < target_size && !q->finished) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 1; printf("缓冲中: %.1f秒/%.1f秒 (%.1f%%)\r", (double)q->total_size / BYTES_PER_SECOND, (double)target_size / BYTES_PER_SECOND, (double)q->total_size / target_size * 100); fflush(stdout); pthread_cond_timedwait(&q->buffer_cond, &q->mutex, &ts); } if (q->finished) { printf("\n下载完成,剩余数据: %.1f秒\n", (double)q->total_size / BYTES_PER_SECOND); } else { printf("\n缓冲完成: %.1f秒/%.1f秒\n", (double)q->total_size / BYTES_PER_SECOND, (double)target_size / BYTES_PER_SECOND); } // 恢复下载进度显示 if (status) status->display_active = 1; pthread_mutex_unlock(&q->mutex); } // libcurl回调函数 static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct data_queue *q = (struct data_queue *)userp; queue_push(q, (unsigned char *)contents, realsize); return realsize; } // 添加抖动处理函数 void apply_dither(short *output, float *input, size_t samples) { const float scale = 32767.0f; for (size_t i = 0; i < samples; i++) { float sample = input[i]; // 简单的抖动处理 float dither_val = (rand() / (float)RAND_MAX) * 0.0001f; sample += dither_val; // 软裁剪 if (sample > 1.0f) sample = 1.0f; if (sample < -1.0f) sample = -1.0f; output[i] = (short)(sample * scale); } } // 简单的低通滤波器 void apply_lowpass(float *buffer, size_t samples, int channels) { static float prev[8] = {0}; const float alpha = 0.15f; for (size_t i = 0; i < samples; i++) { int ch = i % channels; float current = buffer[i]; buffer[i] = alpha * current + (1 - alpha) * prev[ch]; prev[ch] = buffer[i]; } } // 初始化PulseAudio设备 int init_pulse_audio(PlayerState *state, long rate, int channels) { if (state->format_initialized && state->pa_stream) { if (state->rate == rate && state->channels == channels) { return 1; } pa_simple_free(state->pa_stream); state->pa_stream = NULL; } pa_sample_spec ss; #if USE_RESAMPLING ss.rate = state->target_rate; #else ss.rate = rate; #endif ss.format = PA_SAMPLE_S16LE; ss.channels = channels; pa_buffer_attr buffer_attr = { .maxlength = (uint32_t)-1, .tlength = (uint32_t)-1, .prebuf = (uint32_t)-1, .minreq = (uint32_t)4096, .fragsize = (uint32_t)1024 }; int error; state->pa_stream = pa_simple_new( NULL, "AudioStream", PA_STREAM_PLAYBACK, NULL, "Music", &ss, NULL, &buffer_attr, &error); if (!state->pa_stream) { fprintf(stderr, "无法创建PulseAudio连接: %s\n", pa_strerror(error)); return 0; } state->rate = rate; state->channels = channels; state->format_initialized = 1; state->playback_speed = 1.0; printf("播放格式: %d Hz, %d 声道 (原始: %ld Hz) %s\n", ss.rate, channels, rate, #if USE_RESAMPLING "[重采样启用]" #else "[重采样禁用]" #endif ); #if USE_RESAMPLING if (state->resampler) { src_delete(state->resampler); state->resampler = NULL; } int src_error; state->resampler = src_new(SRC_SINC_FASTEST, channels, &src_error); if (!state->resampler) { fprintf(stderr, "无法创建重采样器: %s\n", src_strerror(src_error)); return 0; } double src_ratio = (double)state->target_rate / rate; src_set_ratio(state->resampler, src_ratio); size_t needed_in_size = BUFFER_SIZE * sizeof(float) * channels; if (needed_in_size > state->resample_in_size) { free(state->resample_in_buf); state->resample_in_buf = malloc(needed_in_size); if (!state->resample_in_buf) { fprintf(stderr, "无法分配重采样输入缓冲区\n"); return 0; } state->resample_in_size = needed_in_size; } size_t needed_out_size = needed_in_size * 2; if (needed_out_size > state->resample_out_size) { free(state->resample_out_buf); state->resample_out_buf = malloc(needed_out_size); if (!state->resample_out_buf) { fprintf(stderr, "无法分配重采样输出缓冲区\n"); return 0; } state->resample_out_size = needed_out_size; } #endif return 1; } // 调整播放速度 void adjust_playback_speed(PlayerState *state, double buffer_level) { // 动态调整播放速度 double target_speed = 1.0; if (buffer_level < MIN_BUFFER_SECONDS) { target_speed = 0.98; } else if (buffer_level > TARGET_BUFFER_SECONDS * 1.5) { target_speed = 1.02; } else { target_speed = 1.0; } // 平滑过渡 state->playback_speed = 0.9 * state->playback_speed + 0.1 * target_speed; if (state->playback_speed < 0.9) state->playback_speed = 0.9; if (state->playback_speed > 1.1) state->playback_speed = 1.1; } // 播放线程函数 void *play_thread(void *arg) { PlayerState *state = (PlayerState *)arg; struct data_queue *q = (struct data_queue *)((char *)arg + sizeof(PlayerState)); float *buffer; size_t buffer_size = BUFFER_SIZE * sizeof(float); size_t done; int status; int error; if (posix_memalign((void**)&buffer, 16, buffer_size)) { fprintf(stderr, "内存对齐分配失败\n"); return NULL; } printf("等待音频数据...\n"); state->buffering = 1; state->buffer_level = 0.0; state->playback_speed = 1.0; size_t initial_buffer_target = BYTES_PER_SECOND * INITIAL_BUFFER_SECONDS; set_buffer_target(q, NULL, initial_buffer_target); state->buffering = 0; struct timespec last_frame, current_frame; clock_gettime(CLOCK_MONOTONIC, &last_frame); srand(time(NULL)); int download_completed = 0; // 跟踪下载状态 while (1) { pthread_mutex_lock(&state->mutex); if (state->stop) { pthread_mutex_unlock(&state->mutex); break; } pthread_mutex_unlock(&state->mutex); state->buffer_level = (double)q->total_size / BYTES_PER_SECOND; adjust_playback_speed(state, state->buffer_level); // 只有在下载未完成时才检查缓冲不足 if (!download_completed && state->buffer_level < MIN_BUFFER_SECONDS && !state->buffering) { state->buffering = 1; printf("\n缓冲不足 (%.1f秒 < %.1f秒), 重新缓冲...\n", state->buffer_level, MIN_BUFFER_SECONDS); size_t buffer_target = BYTES_PER_SECOND * TARGET_BUFFER_SECONDS; set_buffer_target(q, NULL, buffer_target); state->buffering = 0; printf("缓冲恢复,继续播放\n"); } struct data_chunk *chunk = queue_pop(q); // 检测下载完成 if (chunk == NULL) { download_completed = 1; // 标记下载已完成 // 播放解码器中剩余的数据 int more_data = 1; while (more_data) { status = mpg123_read(state->mh, (unsigned char*)buffer, buffer_size, &done); if (status == MPG123_NEED_MORE) { // 没有更多数据了 more_data = 0; } else if (status == MPG123_DONE) { printf("播放完成\n"); more_data = 0; } else if (status != MPG123_OK) { if (status != MPG123_NEW_FORMAT) { fprintf(stderr, "解码错误: %s\n", mpg123_strerror(state->mh)); more_data = 0; } } if (done > 0) { size_t frames = done / (sizeof(float) * state->channels); if (state->format_initialized) { apply_lowpass(buffer, frames * state->channels, state->channels); size_t pcm_size = frames * sizeof(short) * state->channels; short *pcm_buffer = malloc(pcm_size); apply_dither(pcm_buffer, buffer, frames * state->channels); if (pa_simple_write(state->pa_stream, pcm_buffer, pcm_size, &error) < 0) { fprintf(stderr, "PulseAudio播放失败: %s\n", pa_strerror(error)); } free(pcm_buffer); // 高精度播放控制 clock_gettime(CLOCK_MONOTONIC, &current_frame); double elapsed = (current_frame.tv_sec - last_frame.tv_sec) + (current_frame.tv_nsec - last_frame.tv_nsec) / 1e9; double expected_time = (double)frames / state->rate / state->playback_speed; if (elapsed < expected_time) { double sleep_seconds = expected_time - elapsed; usleep((useconds_t)(sleep_seconds * 1000000)); } clock_gettime(CLOCK_MONOTONIC, &last_frame); } } } // 确保所有音频数据都已播放 if (state->format_initialized && state->pa_stream) { if (pa_simple_drain(state->pa_stream, &error) < 0) { fprintf(stderr, "PulseAudio drain失败: %s\n", pa_strerror(error)); } } break; } if (mpg123_feed(state->mh, chunk->data, chunk->size) != MPG123_OK) { fprintf(stderr, "喂入数据失败: %s\n", mpg123_strerror(state->mh)); free(chunk->data); free(chunk); mpg123_close(state->mh); if (mpg123_open_feed(state->mh) != MPG123_OK) { fprintf(stderr, "无法重置解码器\n"); break; } continue; } free(chunk->data); free(chunk); while (1) { status = mpg123_read(state->mh, (unsigned char*)buffer, buffer_size, &done); if (status == MPG123_NEW_FORMAT) { long rate; int channels, encoding; if (mpg123_getformat(state->mh, &rate, &channels, &encoding) == MPG123_OK) { printf("检测到新音频格式: %ld Hz, %d 声道\n", rate, channels); if (!init_pulse_audio(state, rate, channels)) { fprintf(stderr, "无法初始化音频设备\n"); break; } } continue; } if (status == MPG123_NEED_MORE) { break; } if (status != MPG123_OK) { if (status != MPG123_DONE) { fprintf(stderr, "解码错误: %s\n", mpg123_strerror(state->mh)); mpg123_close(state->mh); if (mpg123_open_feed(state->mh) != MPG123_OK) { fprintf(stderr, "无法重置解码器\n"); break; } } break; } size_t frames = done / (sizeof(float) * state->channels); if (state->format_initialized && done > 0) { apply_lowpass(buffer, frames * state->channels, state->channels); size_t pcm_size = frames * sizeof(short) * state->channels; short *pcm_buffer = malloc(pcm_size); apply_dither(pcm_buffer, buffer, frames * state->channels); if (pa_simple_write(state->pa_stream, pcm_buffer, pcm_size, &error) < 0) { fprintf(stderr, "PulseAudio播放失败: %s\n", pa_strerror(error)); } free(pcm_buffer); clock_gettime(CLOCK_MONOTONIC, &current_frame); double elapsed = (current_frame.tv_sec - last_frame.tv_sec) + (current_frame.tv_nsec - last_frame.tv_nsec) / 1e9; double expected_time = (double)frames / state->rate / state->playback_speed; if (elapsed < expected_time) { double sleep_seconds = expected_time - elapsed; usleep((useconds_t)(sleep_seconds * 1000000)); } clock_gettime(CLOCK_MONOTONIC, &last_frame); } state->buffer_level = (double)q->total_size / BYTES_PER_SECOND; printf("缓冲: %.1fs | 速度: %.3f | 队列: %.1fkb\r", state->buffer_level, state->playback_speed, (double)q->total_size / 1024); fflush(stdout); } } free(buffer); printf("\n播放线程退出\n"); return NULL; } // 下载参数结构 struct DownloadParam { CURL *curl; struct data_queue *queue; struct DownloadStatus *status; // 下载状态 }; // 下载进度显示函数 void display_progress(struct DownloadStatus *status, size_t dlnow) { if (!status->display_active) return; double current_time = (double)clock() / CLOCKS_PER_SEC; // 限制更新频率,每秒最多更新10次 if (current_time - status->last_update < 0.1) { return; } status->last_update = current_time; if (status->total_size > 0) { double progress = (double)dlnow / status->total_size * 100.0; printf("下载进度: %.2f%% (%.2f MB/%.2f MB)\r", progress, (double)dlnow / (1024 * 1024), (double)status->total_size / (1024 * 1024)); } else { printf("已下载: %.2f MB\r", (double)dlnow / (1024 * 1024)); } fflush(stdout); } // 下载进度回调函数 static int progress_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { struct DownloadStatus *status = (struct DownloadStatus *)clientp; // 如果这是第一次获取文件大小 if (dltotal > 0 && status->total_size == 0) { status->total_size = dltotal; printf("\n文件大小: %.2f MB\n", (double)dltotal / (1024 * 1024)); } // 更新已下载大小 status->downloaded_size = dlnow; // 显示进度 display_progress(status, dlnow); return 0; } // 下载线程函数 void *download_thread_wrapper(void *arg) { struct DownloadParam *param = (struct DownloadParam *)arg; CURL *curl = param->curl; struct data_queue *q = param->queue; struct DownloadStatus *status = param->status; // 设置进度回调 curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback); curl_easy_setopt(curl, CURLOPT_XFERINFODATA, status); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); // 初始化下载状态 status->total_size = 0; status->downloaded_size = 0; status->last_update = 0; status->display_active = 1; CURLcode res = curl_easy_perform(curl); if (res != CURLE_OK) { fprintf(stderr, "\n下载失败: %s\n", curl_easy_strerror(res)); } else { // 确保显示最终进度 status->display_active = 1; display_progress(status, status->downloaded_size); printf("\n下载完成, 总大小: %.2f MB\n", (double)status->downloaded_size / (1024 * 1024)); } // 标记下载完成 queue_finish(q); return NULL; } int main() { CURL *curl; struct { PlayerState state; struct data_queue queue; } shared_data = {0}; pthread_t play_tid, download_tid; queue_init(&shared_data.queue); if (mpg123_init() != MPG123_OK) { fprintf(stderr, "无法初始化mpg123\n"); return 1; } shared_data.state.mh = mpg123_new(NULL, NULL); if (!shared_data.state.mh) { fprintf(stderr, "创建mpg123句柄失败: %s\n", mpg123_plain_strerror(mpg123_errcode(NULL))); mpg123_exit(); return 1; } if (mpg123_format_none(shared_data.state.mh) != MPG123_OK || mpg123_format(shared_data.state.mh, 44100, MPG123_STEREO, MPG123_ENC_FLOAT_32) != MPG123_OK) { fprintf(stderr, "无法设置输出格式\n"); mpg123_delete(shared_data.state.mh); mpg123_exit(); return 1; } #if USE_RESAMPLING printf("启用高质量重采样 (48kHz)\n"); shared_data.state.target_rate = 48000; #else printf("禁用重采样\n"); #endif #ifdef MPG123_QUIET mpg123_param(shared_data.state.mh, MPG123_ADD_FLAGS, MPG123_QUIET, 0); #else mpg123_param(shared_data.state.mh, MPG123_VERBOSE, 0, 0.0); #endif mpg123_param(shared_data.state.mh, MPG123_ADD_FLAGS, MPG123_SKIP_ID3V2, 0); mpg123_param(shared_data.state.mh, MPG123_ADD_FLAGS, MPG123_IGNORE_INFOFRAME, 0); mpg123_param(shared_data.state.mh, MPG123_ADD_FLAGS, MPG123_AUTO_RESAMPLE, 0); if (mpg123_open_feed(shared_data.state.mh) != MPG123_OK) { fprintf(stderr, "无法打开feed模式: %s\n", mpg123_strerror(shared_data.state.mh)); mpg123_delete(shared_data.state.mh); mpg123_exit(); return 1; } shared_data.state.format_initialized = 0; shared_data.state.stop = 0; shared_data.state.buffering = 1; shared_data.state.buffer_bytes = 0; shared_data.state.buffer_level = 0.0; shared_data.state.playback_speed = 1.0; #if USE_RESAMPLING shared_data.state.resampler = NULL; shared_data.state.resample_in_buf = NULL; shared_data.state.resample_out_buf = NULL; shared_data.state.resample_in_size = 0; shared_data.state.resample_out_size = 0; #endif if (pthread_mutex_init(&shared_data.state.mutex, NULL)) { fprintf(stderr, "无法初始化互斥锁\n"); mpg123_delete(shared_data.state.mh); mpg123_exit(); return 1; } curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if (!curl) { fprintf(stderr, "初始化libcurl失败\n"); return 1; } // 使用普通MP3文件URL const char *url = "https://ting8.yymp3.com/new27/liyugang6/6.mp3"; printf("使用URL: %s\n", url); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &shared_data.queue); curl_easy_setopt(curl, CURLOPT_USERAGENT, "Lavf/60.3.100"); struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Accept: */*"); headers = curl_slist_append(headers, "Range: bytes=0-"); headers = curl_slist_append(headers, "Connection: keep-alive"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10L); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0L); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 65536L); curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // 创建下载状态结构 struct DownloadStatus download_status = {0}; printf("正在下载音频文件...\n"); // 创建下载线程参数 struct DownloadParam param = { .curl = curl, .queue = &shared_data.queue, .status = &download_status }; // 创建下载线程 if (pthread_create(&download_tid, NULL, download_thread_wrapper, &param) != 0) { fprintf(stderr, "无法创建下载线程\n"); curl_easy_cleanup(curl); curl_global_cleanup(); return 1; } // 创建播放线程 if (pthread_create(&play_tid, NULL, play_thread, &shared_data) != 0) { fprintf(stderr, "无法创建播放线程\n"); mpg123_delete(shared_data.state.mh); mpg123_exit(); return 1; } // 等待下载线程完成 pthread_join(download_tid, NULL); // 等待播放线程结束 pthread_join(play_tid, NULL); // 清理资源 curl_slist_free_all(headers); curl_easy_cleanup(curl); curl_global_cleanup(); queue_cleanup(&shared_data.queue); if (shared_data.state.format_initialized && shared_data.state.pa_stream) { pa_simple_free(shared_data.state.pa_stream); } #if USE_RESAMPLING if (shared_data.state.resampler) { src_delete(shared_data.state.resampler); } free(shared_data.state.resample_in_buf); free(shared_data.state.resample_out_buf); #endif if (shared_data.state.mh) { mpg123_close(shared_data.state.mh); mpg123_delete(shared_data.state.mh); } mpg123_exit(); printf("播放结束\n"); return 0; }
07-04
#include "stream_decoder.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <curl/curl.h> #include <mpg123.h> #include <stdarg.h> #include <time.h> #include <stdint.h> #include <unistd.h> #include <signal.h> // 数据块结构 struct data_chunk { unsigned char *data; size_t size; struct data_chunk *next; }; // 数据队列 struct data_queue { struct data_chunk *head; struct data_chunk *tail; pthread_mutex_t mutex; pthread_cond_t cond; size_t total_size; int finished; }; // 解码器状态 struct StreamDecoder { mpg123_handle *mh; pthread_mutex_t mutex; int stop_requested; // 停止请求标志 int channels; long rate; PCMCallback pcm_callback; void* callback_userdata; LogLevel log_level; pthread_t download_tid; pthread_t decode_tid; struct data_queue queue; CURL *curl; }; // 内部日志函数 static void decoder_log(StreamDecoder *decoder, LogLevel level, const char *format, ...) { if (!decoder || level > decoder->log_level) return; const char *level_str = "DEBUG"; switch(level) { case LOG_LEVEL_ERROR: level_str = "ERROR"; break; case LOG_LEVEL_WARNING: level_str = "WARN"; break; case LOG_LEVEL_INFO: level_str = "INFO"; break; default: break; } va_list args; fprintf(stderr, "[DECODER %s] ", level_str); va_start(args, format); vfprintf(stderr, format, args); va_end(args); fprintf(stderr, "\n"); } // 初始化队列 static void queue_init(struct data_queue *q) { q->head = q->tail = NULL; pthread_mutex_init(&q->mutex, NULL); pthread_cond_init(&q->cond, NULL); q->total_size = 0; q->finished = 0; } // 向队列添加数据 static void queue_push(struct data_queue *q, unsigned char *data, size_t size) { if (!q || !data || size == 0) return; struct data_chunk *chunk = malloc(sizeof(struct data_chunk)); if (!chunk) return; chunk->data = malloc(size); if (!chunk->data) { free(chunk); return; } memcpy(chunk->data, data, size); chunk->size = size; chunk->next = NULL; pthread_mutex_lock(&q->mutex); if (q->tail) { q->tail->next = chunk; } else { q->head = chunk; } q->tail = chunk; q->total_size += size; pthread_cond_signal(&q->cond); pthread_mutex_unlock(&q->mutex); } // 标记队列完成 static void queue_finish(struct data_queue *q) { if (!q) return; pthread_mutex_lock(&q->mutex); q->finished = 1; pthread_cond_signal(&q->cond); pthread_mutex_unlock(&q->mutex); } // 从队列获取数据 static struct data_chunk *queue_pop(struct data_queue *q) { if (!q) return NULL; pthread_mutex_lock(&q->mutex); while (!q->head && !q->finished && !q->stop_requested) { pthread_cond_wait(&q->cond, &q->mutex); } if (!q->head) { pthread_mutex_unlock(&q->mutex); return NULL; } struct data_chunk *chunk = q->head; q->head = chunk->next; if (!q->head) q->tail = NULL; q->total_size -= chunk->size; pthread_mutex_unlock(&q->mutex); return chunk; } // 清理队列 static void queue_cleanup(struct data_queue *q) { if (!q) return; pthread_mutex_lock(&q->mutex); struct data_chunk *chunk = q->head; while (chunk) { struct data_chunk *next = chunk->next; free(chunk->data); free(chunk); chunk = next; } q->head = q->tail = NULL; q->total_size = 0; pthread_mutex_unlock(&q->mutex); } // CURL写入回调 static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct data_queue *q = (struct data_queue *)userp; queue_push(q, (unsigned char *)contents, realsize); return realsize; } // 应用抖动处理 static void apply_dither(short *output, float *input, size_t samples) { const float scale = 32767.0f; for (size_t i = 0; i < samples; i++) { float sample = input[i]; float dither_val = (rand() / (float)RAND_MAX) * 0.0001f; sample += dither_val; if (sample > 1.0f) sample = 1.0f; if (sample < -1.0f) sample = -1.0f; output[i] = (short)(sample * scale); } } // 解码线程 static void* decode_thread(void *arg) { StreamDecoder *decoder = (StreamDecoder *)arg; if (!decoder) return NULL; struct data_queue *q = &decoder->queue; float *buffer = NULL; size_t buffer_size = 8192 * sizeof(float); size_t done; int status; // 分配音频缓冲区 if (posix_memalign((void**)&buffer, 16, buffer_size)) { decoder_log(decoder, LOG_LEVEL_ERROR, "音频缓冲区分配失败"); return NULL; } // 分配PCM输出缓冲区 size_t max_pcm_size = 8192 * sizeof(short) * 2; // 立体声 short *pcm_buffer = malloc(max_pcm_size); if (!pcm_buffer) { decoder_log(decoder, LOG_LEVEL_ERROR, "PCM缓冲区分配失败"); free(buffer); return NULL; } srand(time(NULL)); while (1) { // 检查停止标志 pthread_mutex_lock(&decoder->mutex); if (decoder->stop_requested) { pthread_mutex_unlock(&decoder->mutex); break; } pthread_mutex_unlock(&decoder->mutex); struct data_chunk *chunk = queue_pop(q); if (chunk == NULL) { // 没有更多数据 break; } if (mpg123_feed(decoder->mh, chunk->data, chunk->size) != MPG123_OK) { decoder_log(decoder, LOG_LEVEL_WARNING, "数据供给失败"); free(chunk->data); free(chunk); continue; } free(chunk->data); free(chunk); while (1) { // 检查停止标志 pthread_mutex_lock(&decoder->mutex); if (decoder->stop_requested) { pthread_mutex_unlock(&decoder->mutex); break; } pthread_mutex_unlock(&decoder->mutex); status = mpg123_read(decoder->mh, (unsigned char*)buffer, buffer_size, &done); // 处理格式变化 if (status == MPG123_NEW_FORMAT) { long rate; int channels, encoding; if (mpg123_getformat(decoder->mh, &rate, &channels, &encoding) == MPG123_OK) { decoder->channels = channels; decoder->rate = rate; decoder_log(decoder, LOG_LEVEL_INFO, "音频格式: %d 声道, %ld Hz", channels, rate); } continue; } // 需要更多数据或错误 if (status == MPG123_NEED_MORE || status != MPG123_OK) { break; } // 处理解码后的PCM数据 if (done > 0) { size_t frames = done / (sizeof(float) * decoder->channels); size_t pcm_size = frames * sizeof(short) * decoder->channels; apply_dither(pcm_buffer, buffer, frames * decoder->channels); if (decoder->pcm_callback) { decoder->pcm_callback(pcm_buffer, pcm_size, decoder->channels, decoder->rate, decoder->callback_userdata); } } } } free(buffer); free(pcm_buffer); decoder_log(decoder, LOG_LEVEL_INFO, "解码线程退出"); return NULL; } // 下载线程 static void* download_thread(void *arg) { StreamDecoder *decoder = (StreamDecoder *)arg; if (!decoder || !decoder->curl) return NULL; decoder_log(decoder, LOG_LEVEL_INFO, "开始下载音频流"); CURLcode res = curl_easy_perform(decoder->curl); if (res != CURLE_OK) { decoder_log(decoder, LOG_LEVEL_ERROR, "下载失败: %s", curl_easy_strerror(res)); } else { decoder_log(decoder, LOG_LEVEL_INFO, "下载完成"); } queue_finish(&decoder->queue); decoder_log(decoder, LOG_LEVEL_INFO, "下载线程退出"); return NULL; } // ================== 公共接口实现 ================== StreamDecoder* stream_decoder_create() { StreamDecoder* decoder = calloc(1, sizeof(StreamDecoder)); if (!decoder) return NULL; // 初始化队列 queue_init(&decoder->queue); // 初始化mpg123 if (mpg123_init() != MPG123_OK) { free(decoder); return NULL; } decoder->mh = mpg123_new(NULL, NULL); if (!decoder->mh) { mpg123_exit(); free(decoder); return NULL; } if (mpg123_format_none(decoder->mh) != MPG123_OK || mpg123_format(decoder->mh, 44100, MPG123_STEREO, MPG123_ENC_FLOAT_32) != MPG123_OK) { mpg123_delete(decoder->mh); mpg123_exit(); free(decoder); return NULL; } if (mpg123_open_feed(decoder->mh) != MPG123_OK) { mpg123_delete(decoder->mh); mpg123_exit(); free(decoder); return NULL; } // 初始化互斥锁 pthread_mutex_init(&decoder->mutex, NULL); // 初始化解码器状态 decoder->stop_requested = 0; decoder->channels = 0; decoder->rate = 0; decoder->pcm_callback = NULL; decoder->callback_userdata = NULL; decoder->log_level = LOG_LEVEL_ERROR; decoder->curl = NULL; return decoder; } void stream_decoder_destroy(StreamDecoder* decoder) { if (!decoder) return; // 停止解码 stream_decoder_stop(decoder); // 清理队列 queue_cleanup(&decoder->queue); // 清理mpg123 if (decoder->mh) { mpg123_close(decoder->mh); mpg123_delete(decoder->mh); } mpg123_exit(); // 销毁互斥锁 pthread_mutex_destroy(&decoder->mutex); free(decoder); } void stream_decoder_set_callback(StreamDecoder* decoder, PCMCallback callback, void* userdata) { if (!decoder) return; decoder->pcm_callback = callback; decoder->callback_userdata = userdata; } void stream_decoder_set_log_level(StreamDecoder* decoder, LogLevel level) { if (!decoder) return; decoder->log_level = level; } int stream_decoder_start(StreamDecoder* decoder, const char* url) { if (!decoder || !url) return 0; decoder_log(decoder, LOG_LEVEL_INFO, "开始解码: %s", url); // 重置状态 pthread_mutex_lock(&decoder->mutex); decoder->stop_requested = 0; decoder->channels = 0; decoder->rate = 0; pthread_mutex_unlock(&decoder->mutex); // 清理队列 queue_cleanup(&decoder->queue); queue_init(&decoder->queue); // 初始化curl curl_global_init(CURL_GLOBAL_DEFAULT); decoder->curl = curl_easy_init(); if (!decoder->curl) { decoder_log(decoder, LOG_LEVEL_ERROR, "无法初始化CURL"); return 0; } // 设置CURL选项 curl_easy_setopt(decoder->curl, CURLOPT_URL, url); curl_easy_setopt(decoder->curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(decoder->curl, CURLOPT_WRITEDATA, &decoder->queue); curl_easy_setopt(decoder->curl, CURLOPT_USERAGENT, "AudioStreamDecoder/1.0"); curl_easy_setopt(decoder->curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(decoder->curl, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt(decoder->curl, CURLOPT_CONNECTTIMEOUT, 10L); curl_easy_setopt(decoder->curl, CURLOPT_TIMEOUT, 0L); // 无限超时 // 创建下载线程 if (pthread_create(&decoder->download_tid, NULL, download_thread, decoder) != 0) { decoder_log(decoder, LOG_LEVEL_ERROR, "无法创建下载线程"); curl_easy_cleanup(decoder->curl); curl_global_cleanup(); return 0; } // 创建解码线程 if (pthread_create(&decoder->decode_tid, NULL, decode_thread, decoder) != 0) { decoder_log(decoder, LOG_LEVEL_ERROR, "无法创建解码线程"); pthread_mutex_lock(&decoder->mutex); decoder->stop_requested = 1; pthread_mutex_unlock(&decoder->mutex); pthread_join(decoder->download_tid, NULL); curl_easy_cleanup(decoder->curl); curl_global_cleanup(); return 0; } return 1; } void stream_decoder_stop(StreamDecoder* decoder) { if (!decoder) return; decoder_log(decoder, LOG_LEVEL_INFO, "停止解码"); // 设置停止标志 pthread_mutex_lock(&decoder->mutex); decoder->stop_requested = 1; pthread_mutex_unlock(&decoder->mutex); // 中断CURL下载 if (decoder->curl) { curl_easy_setopt(decoder->curl, CURLOPT_TIMEOUT, 1L); // 设置超时以中断下载 } // 唤醒可能等待的线程 pthread_cond_broadcast(&decoder->queue.cond); // 等待线程结束 pthread_join(decoder->download_tid, NULL); pthread_join(decoder->decode_tid, NULL); // 清理curl if (decoder->curl) { curl_easy_cleanup(decoder->curl); curl_global_cleanup(); decoder->curl = NULL; } // 重置队列 queue_cleanup(&decoder->queue); } gcc -c stream_decoder.c -o stream_decoder.o -I. -lcurl -lmpg123 -lpthread stream_decoder.c: In function ‘queue_pop’: stream_decoder.c:121:42: error: ‘struct data_queue’ has no member named ‘stop_requested’ 121 | while (!q->head && !q->finished && !q->stop_requested) {
07-09
<think>我们分析一下错误信息:在stream_decoder.c文件的121行,尝试访问结构体`data_queue`的成员`stop_requested`,但该结构体并没有这个成员。 查看代码中`struct data_queue`的定义: struct data_queue { struct data_chunk *head; struct data_chunk *tail; pthread_mutex_t mutex; pthread_cond_t cond; size_t total_size; int finished; }; 确实没有`stop_requested`成员。 而在`queue_pop`函数中,等待条件变量的循环条件为: while (!q->head && !q->finished && !q->stop_requested) { pthread_cond_wait(&q->cond, &q->mutex); } 这里试图使用`q->stop_requested`,但`q`是`data_queue`类型,没有这个成员。 实际上,`stop_requested`是`StreamDecoder`结构体的成员。在`StreamDecoder`中定义: int stop_requested; // 停止请求标志 因此,在`queue_pop`函数中,我们不应该访问`q->stop_requested`,而应该访问`StreamDecoder`的`stop_requested`。 但是,`queue_pop`函数只接收`data_queue`指针,没有`StreamDecoder`指针。所以我们需要修改设计。 有两种解决方案: 1. 将`stop_requested`标志移到`data_queue`结构体中。 2. 修改`queue_pop`函数,增加一个参数,即指向`StreamDecoder`的指针(或者直接传递`stop_requested`标志的地址,但注意同步问题)。 考虑到`data_queue`是`StreamDecoder`的一部分,而且`stop_requested`是解码器的停止标志,它应该属于解码器而不是队列。 但是,在`queue_pop`中我们需要检查这个标志,所以我们需要让`queue_pop`知道解码器的停止状态。 然而,当前`queue_pop`只接收`data_queue*`。我们可以改变`queue_pop`的接口,但这样需要修改所有使用`queue_pop`的地方(目前只有`decode_thread`)。 另外,在`decode_thread`中,我们确实有`StreamDecoder`指针,所以我们可以将循环条件拆分,在`queue_pop`外部检查停止标志。 但是,`queue_pop`内部在等待条件变量时,如果停止标志被设置,我们希望它能够立即返回。因此,我们需要在条件等待中同时检查停止标志。 由于`queue_pop`是静态函数,只在当前文件使用,我们可以修改它的参数,传递`StreamDecoder`指针。 让我们修改`queue_pop`函数,增加一个参数`decoder`,然后在循环条件中检查`decoder->stop_requested`。 同时,在`decode_thread`中调用`queue_pop`时,将`decoder`传递进去。 具体修改如下: 1. 修改`queue_pop`的声明和定义,增加`StreamDecoder *decoder`参数。 static struct data_chunk *queue_pop(struct data_queue *q, StreamDecoder *decoder) 2. 在循环条件中,使用`decoder->stop_requested`(注意加锁?但这里我们持有队列的互斥锁,而`stop_requested`在另一个结构体中,所以我们需要在访问`decoder->stop_requested`时持有`decoder->mutex`?这样会导致嵌套锁,可能死锁。) 为了避免嵌套锁,我们可以将`stop_requested`标志移到`data_queue`中,因为队列的互斥锁已经保护了队列,而停止标志实际上与队列的生命周期相关(当停止时,我们希望队列不再被阻塞等待)。 考虑到代码的简洁性,我们选择方案1:将`stop_requested`移到`data_queue`结构体中。 修改`struct data_queue`: struct data_queue { struct data_chunk *head; struct data_chunk *tail; pthread_mutex_t mutex; pthread_cond_t cond; size_t total_size; int finished; int stop_requested; // 新增 }; 然后,在`queue_init`中初始化这个标志为0。 在`queue_cleanup`中,我们不需要特别清理这个标志,但重置时可以将其设为0。 另外,在`stream_decoder_stop`函数中,除了设置`decoder->stop_requested`(现在需要移除)之外,还需要设置队列的停止标志,并广播条件变量。 但是,注意`StreamDecoder`结构体中已经有一个`stop_requested`,我们将其移除,改为使用队列中的`stop_requested`。 因此,我们需要重构: 步骤: 1. 在`struct data_queue`中添加`int stop_requested;` 2. 在`queue_init`中,初始化`stop_requested`为0。 3. 在`StreamDecoder`结构体中,移除`int stop_requested;`(因为现在使用队列的停止标志)。 4. 修改`stream_decoder_stop`函数,在设置队列的停止标志时,需要锁定队列的互斥锁,并设置`q->stop_requested=1`,然后广播条件变量。 5. 修改`queue_pop`函数,循环条件使用`q->stop_requested`(注意:在队列的互斥锁保护下访问)。 6. 在`decode_thread`和`download_thread`中,检查停止标志时,现在应该检查队列的停止标志(但注意,这两个线程中,我们原本使用的是`decoder->stop_requested`,现在这个成员已经被移除,所以需要改为访问`decoder->queue.stop_requested`,并且需要加锁?) 但是,注意在`decode_thread`中,我们原本有: pthread_mutex_lock(&decoder->mutex); if (decoder->stop_requested) { pthread_mutex_unlock(&decoder->mutex); break; } pthread_mutex_unlock(&decoder->mutex); 现在,我们不再有`decoder->stop_requested`,而是使用`decoder->queue.stop_requested`,并且这个标志由队列的互斥锁保护。所以我们需要修改为使用队列的互斥锁。 这样,我们可能会在多个地方使用队列的互斥锁,而且原本的`decoder->mutex`现在似乎没有用了(除了在`stream_decoder_stop`中设置停止标志时,但设置停止标志时我们也要用队列的互斥锁)。 因此,我们可以移除`StreamDecoder`中的`mutex`,而统一使用队列的互斥锁来保护停止标志。 但是,`StreamDecoder`中的`mutex`还保护了`channels`和`rate`等成员。这些成员在`decode_thread`中修改(当有新的格式时),在回调时被读取(在解码线程中)。另外,在`stream_decoder_start`中重置状态时也修改了`channels`和`rate`,这些操作也需要互斥。 因此,我们有两种选择: a) 保留`StreamDecoder`中的`mutex`,用于保护`channels`和`rate`,而停止标志则使用队列的互斥锁保护(这样有两个锁)。 b) 将停止标志放回`StreamDecoder`中,但是通过另一种方式让`queue_pop`能够访问到(比如在`queue_pop`中增加参数),并且使用`StreamDecoder`的互斥锁。 考虑到时间,我们选择方案b)可能更复杂,因为`queue_pop`在等待队列条件时需要同时检查两个条件(队列非空、队列完成、解码器停止),而这两个条件由不同的锁保护。 为了避免死锁和复杂性,我们重新考虑:停止标志应该放在哪里? 实际上,停止标志应该属于整个解码器,而不是队列。因为停止整个解码过程不仅仅是队列的事情。所以,我们不应该把它放在`data_queue`中。 因此,我们回到最初的方案:修改`queue_pop`函数,增加一个参数,即指向停止标志的指针,并且这个停止标志由`StreamDecoder`的互斥锁保护。但是,在`queue_pop`内部,我们需要同时持有队列的锁和访问停止标志(通过指针),这要求我们同时持有两个锁,容易死锁。 另一种方法:在`queue_pop`中不检查停止标志,而是让`decode_thread`在调用`queue_pop`之前检查停止标志,但这样可能无法及时唤醒。 更合理的方法:在`queue_pop`中,我们只检查队列自身的条件(是否有数据,是否完成),而停止标志在`decode_thread`的主循环中检查。这样,即使`queue_pop`阻塞,当停止标志设置时,我们可以通过条件变量广播来唤醒`queue_pop`,然后`queue_pop`返回NULL(因为队列为空且未完成,但停止标志被设置,所以循环退出条件不成立?)—— 我们需要调整`queue_pop`的逻辑。 我们修改`queue_pop`的循环条件,只检查队列自身的条件(是否有数据,是否完成)。然后,在`decode_thread`中,我们这样写: while (1) { // 检查停止标志(通过解码器的互斥锁) pthread_mutex_lock(&decoder->mutex); if (decoder->stop_requested) { pthread_mutex_unlock(&decoder->mutex); break; } pthread_mutex_unlock(&decoder->mutex); chunk = queue_pop(q); ... // 处理chunk } 但是,这样有一个问题:当设置停止标志后,`decode_thread`在检查停止标志后跳出循环,但此时`queue_pop`可能阻塞在条件变量上。我们需要唤醒它,否则线程不会退出,直到有数据到来。 因此,在设置停止标志后,我们需要广播队列的条件变量,这样`queue_pop`就会返回(因为条件变量被广播,它会重新检查条件,然后发现队列还是空的,但还没有完成,所以继续等待?)—— 这样并没有解决问题。 所以,我们需要在`queue_pop`中增加一个条件:如果解码器停止,则返回NULL。 因此,我们修改`queue_pop`函数,传入`StreamDecoder *decoder`,然后在循环条件中检查`decoder->stop_requested`。但是,访问`decoder->stop_requested`需要加锁(`decoder->mutex`),而`queue_pop`内部已经持有`q->mutex`。这样,我们需要同时持有两个锁,容易导致死锁(如果其他地方以不同顺序加锁)。 为了避免死锁,我们可以规定:任何时候必须先锁`decoder->mutex`,再锁`q->mutex`。但这样会影响其他代码。 考虑到当前代码量不大,我们可以尝试这样做: 修改`queue_pop`函数: static struct data_chunk *queue_pop(struct data_queue *q, StreamDecoder *decoder) { pthread_mutex_lock(&decoder->mutex); int stop_requested = decoder->stop_requested; pthread_mutex_unlock(&decoder->mutex); pthread_mutex_lock(&q->mutex); while (!q->head && !q->finished && !stop_requested) { pthread_cond_wait(&q->cond, &q->mutex); // 每次被唤醒后,重新检查停止标志 pthread_mutex_lock(&decoder->mutex); stop_requested = decoder->stop_requested; pthread_mutex_unlock(&decoder->mutex); } if (stop_requested || (!q->head && q->finished)) { pthread_mutex_unlock(&q->mutex); return NULL; } // 取出chunk ... pthread_mutex_unlock(&q->mutex); return chunk; } 但是,这样在循环内频繁解锁和加锁`decoder->mutex`,效率不高,但可以接受。 而且,注意在循环内,我们解锁`q->mutex`(在等待条件变量时)然后重新获得,所以在这期间`stop_requested`可能被修改。 但是,上面的代码中,我们在循环条件中使用了`stop_requested`(在循环开始前获取),然后在等待后重新获取。这样,当停止标志被设置,循环条件不满足,就会退出循环。 然而,上面的代码在循环条件中使用了`stop_requested`,但它在循环内部被更新,所以我们需要将循环条件改为: while (!q->head && !q->finished && !stop_requested) { 然后在循环体内,在等待条件变量后,更新`stop_requested`。 但是,这样写,在进入循环前,我们持有`q->mutex`,然后检查条件。如果条件满足,就等待,在等待时释放`q->mutex`,当被唤醒时,重新获得`q->mutex`,然后更新`stop_requested`,再重新判断循环条件。 这样,当停止标志被设置时,循环条件不满足,退出循环。 然后,我们在循环后判断:如果停止标志被设置,或者队列已经完成且为空,则返回NULL。 但是,注意:停止标志被设置时,即使队列中还有数据,我们也应该放弃处理,立即停止。所以,我们直接返回NULL。 所以,修改后的`queue_pop`函数: static struct data_chunk *queue_pop(struct data_queue *q, StreamDecoder *decoder) { if (!q || !decoder) return NULL; pthread_mutex_lock(&q->mutex); // 初始获取停止标志 pthread_mutex_lock(&decoder->mutex); int stop_requested = decoder->stop_requested; pthread_mutex_unlock(&decoder->mutex); while (!q->head && !q->finished && !stop_requested) { pthread_cond_wait(&q->cond, &q->mutex); // 重新获取停止标志 pthread_mutex_lock(&decoder->mutex); stop_requested = decoder->stop_requested; pthread_mutex_unlock(&decoder->mutex); } // 如果停止请求,或者队列结束且没有数据,则返回NULL if (stop_requested || (!q->head && q->finished)) { pthread_mutex_unlock(&q->mutex); return NULL; } // 取出一个chunk struct data_chunk *chunk = q->head; q->head = chunk->next; if (!q->head) q->tail = NULL; q->total_size -= chunk->size; pthread_mutex_unlock(&q->mutex); return chunk; } 但是,这里有一个问题:在循环中,我们解锁`decoder->mutex`后,`stop_requested`可能被修改,但我们在循环条件中使用的`stop_requested`是更新后的,所以没问题。 现在,在`decode_thread`中,调用`queue_pop`时传入`decoder`。 同时,在`stream_decoder_stop`函数中,我们设置停止标志后,需要广播队列的条件变量,以唤醒`queue_pop`中的等待。 修改`stream_decoder_stop`: void stream_decoder_stop(StreamDecoder* decoder) { if (!decoder) return; decoder_log(decoder, LOG_LEVEL_INFO, "停止解码"); // 设置停止标志 pthread_mutex_lock(&decoder->mutex); decoder->stop_requested = 1; pthread_mutex_unlock(&decoder->mutex); // 中断CURL下载 if (decoder->curl) { curl_easy_setopt(decoder->curl, CURLOPT_TIMEOUT, 1L); // 设置超时以中断下载 } // 广播队列条件变量,唤醒等待的queue_pop pthread_mutex_lock(&decoder->queue.mutex); pthread_cond_broadcast(&decoder->queue.cond); pthread_mutex_unlock(&decoder->queue.mutex); // 等待线程结束 pthread_join(decoder->download_tid, NULL); pthread_join(decoder->decode_tid, NULL); // 清理curl if (decoder->curl) { curl_easy_cleanup(decoder->curl); curl_global_cleanup(); decoder->curl = NULL; } // 重置队列 queue_cleanup(&decoder->queue); } 这样修改后,应该可以解决问题。 但是,注意:在`queue_pop`中,我们加锁的顺序是:先锁`decoder->mutex`(在循环内),再锁`q->mutex`(在函数开头就锁了)。而在`stream_decoder_stop`中,我们先锁`decoder->mutex`,再锁`queue.mutex`。这可能导致死锁,因为顺序不一致。 为了避免死锁,我们需要统一加锁顺序:任何时候,先锁`decoder->mutex`,再锁`queue.mutex`。 在`queue_pop`函数中,我们目前是: - 先锁`q->mutex`(队列锁) - 然后在循环体内,锁`decoder->mutex` 这违反了加锁顺序(先decoder,后队列)。 因此,我们需要调整`queue_pop`函数,避免在持有`q->mutex`的情况下去锁`decoder->mutex`。 我们可以改为:在循环条件中,先释放`q->mutex`,然后去锁`decoder->mutex`,然后再重新锁`q->mutex`,然后检查条件。但是这样会使得条件检查不原子。 或者,我们可以将停止标志的访问移到加锁`q->mutex`之前,然后使用一个局部变量传递: static struct data_chunk *queue_pop(struct data_queue *q, StreamDecoder *decoder) { if (!q || !decoder) return NULL; // 先获取停止标志 pthread_mutex_lock(&decoder->mutex); int stop_requested = decoder->stop_requested; pthread_mutex_unlock(&decoder->mutex); pthread_mutex_lock(&q->mutex); while (!q->head && !q->finished && !stop_requested) { // 在等待前,我们需要解锁q->mutex,并等待条件变量 pthread_cond_wait(&q->cond, &q->mutex); // 被唤醒后,重新获取停止标志 pthread_mutex_lock(&decoder->mutex); stop_requested = decoder->stop_requested; pthread_mutex_unlock(&decoder->mutex); } if (stop_requested || (!q->head && q->finished)) { pthread_mutex_unlock(&q->mutex); return NULL; } // 取出chunk ... pthread_mutex_unlock(&q->mutex); return chunk; } 这样,在循环中,每次被唤醒后,我们都会更新`stop_requested`,然后重新判断循环条件。 但是,在循环条件中,我们使用的`stop_requested`是在循环开始前和每次唤醒后更新的,所以是及时的。 而且,加锁顺序是:先`decoder->mutex`,后`q->mutex`,符合统一顺序。 在`stream_decoder_stop`中,加锁顺序是:先`decoder->mutex`(设置停止标志),后`q->mutex`(广播条件变量),所以也是一致的。 因此,我们按照这个方案修改。 总结修改点: 1. 修改`queue_pop`函数,增加参数`StreamDecoder *decoder`。 2. 在`queue_pop`中,先获取停止标志(通过`decoder->mutex`),然后加锁`q->mutex`,然后在循环中等待,每次唤醒后重新获取停止标志。 3. 在`decode_thread`中,调用`queue_pop`时传入`decoder`。 4. 在`stream_decoder_stop`中,设置停止标志后,广播队列的条件变量(注意加锁顺序:先`decoder->mutex`,再`q->mutex`)。 另外,注意在`download_thread`中,我们也会检查`decoder->stop_requested`,现在需要改为通过`decoder->mutex`访问。 在`download_thread`中,原本有: pthread_mutex_lock(&decoder->mutex); if (decoder->stop_requested) { pthread_mutex_unlock(&decoder->mutex); break; } pthread_mutex_unlock(&decoder->mutex); 这个循环在`download_thread`中并不存在,所以这段代码是多余的?实际上,`download_thread`的主循环是`curl_easy_perform`,它内部会持续下载,直到完成或出错。所以我们无法在`download_thread`中主动检查停止标志并退出。我们是通过设置CURL的超时选项来中断下载的。 因此,`download_thread`中并没有循环检查停止标志的代码,我们可以忽略。 但是,在`decode_thread`中,我们有两处检查停止标志: - 在调用`queue_pop`之前 - 在解码循环中(每次喂数据后,循环解码) 我们需要修改这两处,使用`decoder->mutex`来访问`stop_requested`。 由于修改较大,我们 step by step 修改代码。 由于时间关系,这里我们只给出修改后的`queue_pop`函数和`decode_thread`中调用`queue_pop`的部分,以及`stream_decoder_stop`的修改。 但是,注意:原错误发生在`queue_pop`函数中,我们修改后,这个错误应该被解决。 由于我们无法直接运行,只能根据编译错误和逻辑进行修改。 下面,我们修改代码: 步骤1:在`struct data_queue`中,我们不需要添加`stop_requested`,所以保持原样。 步骤2:修改`queue_pop`函数,增加`StreamDecoder *decoder`参数,并按照上面的方案实现。 步骤3:在`decode_thread`中,将`queue_pop(q)`改为`queue_pop(q, decoder)`。 步骤4:在`stream_decoder_stop`中,设置停止标志后,广播队列的条件变量。 步骤5:在`StreamDecoder`结构体中,保留`stop_requested`,因为这是我们修改后的方案。 由于代码较长,我们只修改关键部分。 注意:在`queue_pop`函数中,我们新增了对`decoder`的访问,所以需要包含`StreamDecoder`的定义,但函数本身就是定义在`StreamDecoder`的上下文中,所以没问题。 修改后的`queue_pop`函数: static struct data_chunk *queue_pop(struct data_queue *q, StreamDecoder *decoder) { if (!q || !decoder) return NULL; // 先获取停止标志 pthread_mutex_lock(&decoder->mutex); int stop_requested = decoder->stop_requested; pthread_mutex_unlock(&decoder->mutex); pthread_mutex_lock(&q->mutex); while (!q->head && !q->finished && !stop_requested) { // 等待条件变量,会暂时释放q->mutex pthread_cond_wait(&q->cond, &q->mutex); // 被唤醒后,重新获取停止标志 pthread_mutex_lock(&decoder->mutex); stop_requested = decoder->stop_requested; pthread_mutex_unlock(&decoder->mutex); } // 如果停止请求,或者队列结束且没有数据,则返回NULL if (stop_requested || (!q->head && q->finished)) { pthread_mutex_unlock(&q->mutex); return NULL; } // 取出一个chunk struct data_chunk *chunk = q->head; q->head = chunk->next; if (!q->head) q->tail = NULL; q->total_size -= chunk->size; pthread_mutex_unlock(&q->mutex); return chunk; } 在`decode_thread`中,修改调用`queue_pop`的地方: struct data_chunk *chunk = queue_pop(q, decoder); // 原来是 queue_pop(q) 在`stream_decoder_stop`中,修改: // 设置停止标志 pthread_mutex_lock(&decoder->mutex); decoder->stop_requested = 1; pthread_mutex_unlock(&decoder->mutex); // 中断CURL下载 if (decoder->curl) { curl_easy_setopt(decoder->curl, CURLOPT_TIMEOUT, 1L); } // 广播队列条件变量 pthread_mutex_lock(&decoder->queue.mutex); pthread_cond_broadcast(&decoder->queue.cond); pthread_mutex_unlock(&decoder->queue.mutex); 注意:这里加锁顺序是先`decoder->mutex`,再`decoder->queue.mutex`,符合我们的约定。 最后,编译错误是因为`queue_pop`中访问了`q->stop_requested`,而我们将其改为`stop_requested`(来自`decoder`),所以这个错误应该被解决。 由于修改较多,我们还需要注意其他细节,比如在`decode_thread`中,除了`queue_pop`之前检查停止标志,在解码循环中也有检查。我们保留那些检查,因为它们使用`decoder->mutex`来访问`stop_requested`,是安全的。 另外,在`download_thread`中,我们没有检查停止标志,所以不需要修改。 重新编译,看是否还有错误。 由于我们无法实际编译,只能推理。如果还有其他错误,需要根据错误信息再调整。 现在,我们按照修改后的代码进行提交。</think>根据提供的代码和编译错误信息,问题出现在`queue_pop`函数中尝试访问`data_queue`结构体中不存在的`stop_requested`成员。错误原因是`data_queue`结构体定义中只有`finished`成员,没有`stop_requested`成员。以下是修复方案: ### 修复方案 1. **修改`struct data_queue`定义** 在`data_queue`结构体中添加`stop_requested`成员: ```c struct data_queue { struct data_chunk *head; struct data_chunk *tail; pthread_mutex_t mutex; pthread_cond_t cond; size_t total_size; int finished; int stop_requested; // 新增停止标志 }; ``` 2. **更新`queue_init`函数** 初始化时设置`stop_requested = 0`: ```c static void queue_init(struct data_queue *q) { q->head = q->tail = NULL; pthread_mutex_init(&q->mutex, NULL); pthread_cond_init(&q->cond, NULL); q->total_size = 0; q->finished = 0; q->stop_requested = 0; // 初始化停止标志 } ``` 3. **修改`queue_pop`函数** 使用`q->stop_requested`替代不存在的成员: ```c static struct data_chunk *queue_pop(struct data_queue *q) { pthread_mutex_lock(&q->mutex); while (!q->head && !q->finished && !q->stop_requested) { // 使用q->stop_requested pthread_cond_wait(&q->cond, &q->mutex); } // ... 后续代码不变 } ``` 4. **更新`stream_decoder_stop`函数** 设置队列的停止标志并广播条件变量: ```c void stream_decoder_stop(StreamDecoder* decoder) { // ... 其他代码 // 设置队列的停止标志 pthread_mutex_lock(&decoder->queue.mutex); decoder->queue.stop_requested = 1; // 设置队列的停止标志 pthread_cond_broadcast(&decoder->queue.cond); pthread_mutex_unlock(&decoder->queue.mutex); // ... 其他代码 } ``` ### 关键修改说明 - **停止标志迁移**:将停止标志从`StreamDecoder`移至`data_queue`中,统一通过队列的互斥锁管理线程同步。 - **条件变量广播**:在`stream_decoder_stop`中直接操作队列的停止标志,并广播条件变量唤醒阻塞线程。 - **线程安全**:所有对`stop_requested`的访问都受队列互斥锁保护,避免竞态条件。 ### 完整修复后的`queue_pop`函数 ```c static struct data_chunk *queue_pop(struct data_queue *q) { pthread_mutex_lock(&q->mutex); while (!q->head && !q->finished && !q->stop_requested) { pthread_cond_wait(&q->cond, &q->mutex); } if (!q->head) { pthread_mutex_unlock(&q->mutex); return NULL; } struct data_chunk *chunk = q->head; q->head = chunk->next; if (!q->head) q->tail = NULL; q->total_size -= chunk->size; pthread_mutex_unlock(&q->mutex); return chunk; } ``` ### 注意事项 - 编译命令需链接正确库:`gcc -c stream_decoder.c -o stream_decoder.o -I. -lcurl -lmpg123 -lpthread` - 确保所有对`stop_requested`的访问都在队列互斥锁的保护下进行 - 在`stream_decoder_destroy`中清理队列时重置`stop_requested`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值