global_realloc.h

 
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <curl/curl.h> #include <neaacdec.h> #include <pulse/simple.h> #include <pulse/error.h> #include <arpa/inet.h> #include <libswresample/swresample.h> #include <math.h> #include <time.h> // 添加时间支持 #define HLS_URL "https://ngcdn001.cnr.cn/live/zgzs/index.m3u8" #define USER_AGENT "Mozilla/5.0 (X11; Linux x86_64)" #define MAX_SEGMENTS 20 #define OUTPUT_SAMPLE_RATE 48000 // 48000Hz 匹配AAC流 #define CHANNELS 2 #define BUFFER_DURATION_MS 200 // 增加缓冲区大小 #define TS_PACKET_SIZE 188 #define SYNC_BYTE 0x47 #define BUFFER_THRESHOLD 3 // 最少缓冲分段数 // 定义MemoryChunk结构体 typedef struct { unsigned char* data; size_t size; } MemoryChunk; typedef struct { char* url; int index; } Segment; typedef struct { CURL* curl; char* base_url; Segment segments[MAX_SEGMENTS]; int segment_count; int last_sequence; int running; pthread_mutex_t mutex; pa_simple* pulse; NeAACDecHandle aac_decoder; int aac_initialized; unsigned long sample_rate; // 实际的采样率 unsigned char channels; // 实际的声道数 SwrContext* swr_ctx; // 重采样上下文 int64_t audio_pts; // 音频时间戳 struct timespec last_play_time; // 最后播放时间 int64_t played_samples; // 已播放样本数 } PlayerState; // 内存写入回调函数 static size_t write_callback(void* contents, size_t size, size_t nmemb, void* userp) { size_t realsize = size * nmemb; MemoryChunk* mem = (MemoryChunk*)userp; // 使用临时指针避免内存泄漏 unsigned char* new_data = realloc(mem->data, mem->size + realsize + 1); if (new_data == NULL) { return 0; } mem->data = new_data; memcpy(&(mem->data[mem->size]), contents, realsize); mem->size += realsize; mem->data[mem->size] = '\0'; return realsize; } // 下载URL到内存 MemoryChunk download_url(CURL* curl, const char* url) { MemoryChunk chunk = {NULL, 0}; curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk); curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L); curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); // 启用压缩 curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); // 启用TCP保活 CURLcode res = curl_easy_perform(curl); if (res != CURLE_OK) { fprintf(stderr, "下载失败: %s - %s\n", url, curl_easy_strerror(res)); if (chunk.data) free(chunk.data); chunk.data = NULL; chunk.size = 0; } return chunk; } // 解析M3U8播放列表 int parse_m3u8(PlayerState* state) { MemoryChunk playlist = download_url(state->curl, HLS_URL); if (!playlist.data || playlist.size == 0) { fprintf(stderr, "下载M3U8播放列表失败\n"); return -1; } char* data = (char*)playlist.data; char* saveptr = NULL; char* line = strtok_r(data, "\n", &saveptr); int media_sequence = 0; int segment_count = 0; while (line != NULL) { if (strstr(line, "#EXT-X-MEDIA-SEQUENCE:") != NULL) { sscanf(line, "#EXT-X-MEDIA-SEQUENCE:%d", &media_sequence); state->last_sequence = media_sequence; } else if (strstr(line, ".ts") != NULL && !strstr(line, "#")) { size_t len = strlen(state->base_url) + strlen(line) + 1; char* full_url = malloc(len); if (full_url == NULL) { fprintf(stderr, "分配内存失败\n"); break; } snprintf(full_url, len, "%s%s", state->base_url, line); int is_new = 1; for (int i = 0; i < state->segment_count; i++) { if (state->segments[i].index == media_sequence + segment_count) { is_new = 0; break; } } if (is_new && segment_count < MAX_SEGMENTS) { state->segments[segment_count].url = full_url; state->segments[segment_count].index = media_sequence + segment_count; segment_count++; } else { free(full_url); } } line = strtok_r(NULL, "\n", &saveptr); } state->segment_count = segment_count; free(playlist.data); return segment_count; } // 从TS包中提取有效载荷 int extract_ts_payload(unsigned char* ts_data, size_t ts_size, unsigned char** payload, size_t* payload_size) { *payload = NULL; *payload_size = 0; size_t pos = 0; int packet_count = 0; int valid_packet_count = 0; while (pos + TS_PACKET_SIZE <= ts_size) { packet_count++; // 跳过非同步字节 if (ts_data[pos] != SYNC_BYTE) { // 尝试重新同步 while (pos < ts_size && ts_data[pos] != SYNC_BYTE) { pos++; } if (pos + TS_PACKET_SIZE > ts_size) break; } // 适配字段控制 unsigned char adapt_field = (ts_data[pos + 3] >> 4) & 0x03; size_t payload_start = pos + 4; // 处理适配字段 if (adapt_field == 0x02 || adapt_field == 0x03) { unsigned char adapt_len = ts_data[pos + 4]; payload_start += 1 + adapt_len; if (payload_start >= pos + TS_PACKET_SIZE) { // 没有有效载荷 payload_start = pos + TS_PACKET_SIZE; } } // 计算有效载荷大小 size_t packet_payload_size = (pos + TS_PACKET_SIZE) - payload_start; if (packet_payload_size == 0) { pos += TS_PACKET_SIZE; continue; } // 重新分配内存并复制数据 unsigned char* new_payload = realloc(*payload, *payload_size + packet_payload_size); if (!new_payload) { if (*payload) free(*payload); *payload = NULL; *payload_size = 0; return -1; } *payload = new_payload; memcpy(*payload + *payload_size, ts_data + payload_start, packet_payload_size); *payload_size += packet_payload_size; valid_packet_count++; pos += TS_PACKET_SIZE; } if (valid_packet_count == 0) { fprintf(stderr, "未找到有效TS包\n"); return -1; } return 0; } // 初始化重采样器 int init_resampler(PlayerState* state) { if (state->swr_ctx) { swr_free(&state->swr_ctx); } // 创建重采样上下文 state->swr_ctx = swr_alloc_set_opts(NULL, AV_CH_LAYOUT_STEREO, // 输出声道布局 AV_SAMPLE_FMT_S16, // 输出采样格式 OUTPUT_SAMPLE_RATE, // 输出采样率 state->channels == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO, // 输入声道布局 AV_SAMPLE_FMT_FLT, // 输入采样格式 (FAAD2输出的是浮点) state->sample_rate, // 输入采样率 0, NULL); if (!state->swr_ctx) { fprintf(stderr, "无法分配重采样上下文\n"); return -1; } // 设置重采样质量 av_opt_set_int(state->swr_ctx, "ich", state->channels, 0); av_opt_set_int(state->swr_ctx, "och", CHANNELS, 0); av_opt_set_int(state->swr_ctx, "filter_size", 32, 0); // 减少滤波器长度 av_opt_set_int(state->swr_ctx, "phase_shift", 6, 0); // 降低相位偏移 av_opt_set_double(state->swr_ctx, "cutoff", 0.8, 0); // 降低截止频率 av_opt_set_int(state->swr_ctx, "linear_resample", 1, 0); // 使用线性插值 // 初始化重采样器 if (swr_init(state->swr_ctx) < 0) { fprintf(stderr, "无法初始化重采样器\n"); swr_free(&state->swr_ctx); return -1; } printf("初始化重采样器: %dHz/%dch -> %dHz/%dch\n", state->sample_rate, state->channels, OUTPUT_SAMPLE_RATE, CHANNELS); return 0; } // 手动浮点到整型转换 void convert_float_to_s16(float* in, int16_t* out, int samples) { for (int i = 0; i < samples; i++) { float sample = in[i]; // 应用软裁剪避免削波 if (sample > 1.0f) sample = 1.0f; else if (sample < -1.0f) sample = -1.0f; out[i] = (int16_t)(sample * 32767.0f); } } // 音频同步机制 void sync_audio(PlayerState* state, int samples) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); // 如果是第一次播放,初始化时间戳 if (state->played_samples == 0) { state->last_play_time = now; state->played_samples = samples; return; } // 计算应播放的时间 double expected_time = (double)state->played_samples / OUTPUT_SAMPLE_RATE; double actual_time = (now.tv_sec - state->last_play_time.tv_sec) + (now.tv_nsec - state->last_play_time.tv_nsec) / 1e9; // 速度偏差超过5%时警告 if (fabs(actual_time - expected_time) > 0.05 * expected_time) { fprintf(stderr, "音频不同步: 预期 %.3fs 实际 %.3fs (偏差 %.1f%%)\n", expected_time, actual_time, 100*(actual_time - expected_time)/expected_time); } // 更新状态 state->played_samples += samples; state->last_play_time = now; } // 处理AAC帧并解码为PCM int decode_aac_frames(PlayerState* state, unsigned char* aac_data, size_t aac_size) { size_t pos = 0; int decoded_frames = 0; int initialization_attempts = 0; // 重采样缓冲区 int16_t* resampled_buffer = NULL; int resampled_samples = 0; int max_out_samples = 0; // 直接转换缓冲区 int16_t* convert_buffer = NULL; int convert_buffer_size = 0; while (pos < aac_size) { // 查找ADTS帧头 (0xFFFx) if (pos + 7 > aac_size) { // 剩余数据不足一个ADTS帧头 break; } // 检查同步字: 0xFFF (前12位) if (aac_data[pos] != 0xFF || (aac_data[pos+1] & 0xF0) != 0xF0) { // 检查是否为MP3格式 if (aac_data[pos] == 0xFF && (aac_data[pos+1] & 0xE0) == 0xE0) { fprintf(stderr, "检测到MP3格式,当前仅支持AAC解码\n"); return -1; } pos++; continue; } // 解析ADTS帧头长度 unsigned int frame_length = ((aac_data[pos+3] & 0x03) << 11); frame_length |= (aac_data[pos+4] << 3); frame_length |= (aac_data[pos+5] >> 5); // 检查帧是否完整 if (frame_length < 7 || pos + frame_length > aac_size) { // 帧不完整,跳过 pos++; continue; } // 初始化解码器(如果尚未初始化) if (!state->aac_initialized) { // 检查AAC特征 if ((aac_data[pos+1] & 0xF6) != 0xF0) { fprintf(stderr, "非AAC格式! 同步字: %02X%02X\n", aac_data[pos], aac_data[pos+1]); return -1; } unsigned long sr; unsigned char ch; if (NeAACDecInit(state->aac_decoder, aac_data + pos, frame_length, &sr, &ch) < 0) { fprintf(stderr, "AAC解码器初始化失败,尝试下一个帧\n"); initialization_attempts++; // 尝试5次后放弃 if (initialization_attempts > 5) { fprintf(stderr, "无法初始化解码器,跳过此分段\n"); break; } pos += frame_length; // 跳过当前帧,尝试下一个 continue; } state->sample_rate = sr; state->channels = ch; state->aac_initialized = 1; printf("AAC解码器初始化成功: %lu Hz, %d 声道\n", sr, ch); // 检查采样率是否在合理范围内 if (state->sample_rate < 8000 || state->sample_rate > 192000) { fprintf(stderr, "异常采样率: %lu Hz, 使用默认48000Hz\n", state->sample_rate); state->sample_rate = OUTPUT_SAMPLE_RATE; } // 初始化重采样器(如果需要) if (state->sample_rate != OUTPUT_SAMPLE_RATE || state->channels != CHANNELS) { if (init_resampler(state) != 0) { fprintf(stderr, "重采样器初始化失败,继续播放但可能有杂音\n"); } } else { printf("输入输出格式匹配,禁用重采样\n"); } } // 解码AAC帧 NeAACDecFrameInfo frame_info; void* pcm = NeAACDecDecode(state->aac_decoder, &frame_info, aac_data + pos, frame_length); if (frame_info.error == 0 && pcm && frame_info.samples > 0) { int in_samples = frame_info.samples; int in_channels = frame_info.channels; // 更新音频时间戳 state->audio_pts += in_samples; // 如果需要重采样 if (state->swr_ctx) { // 配置输入和输出 const uint8_t *in_data[8] = { (const uint8_t*)pcm }; // 计算最大输出样本数 max_out_samples = av_rescale_rnd(swr_get_delay(state->swr_ctx, state->sample_rate) + in_samples, OUTPUT_SAMPLE_RATE, state->sample_rate, AV_ROUND_UP); // 分配重采样缓冲区 if (!resampled_buffer) { resampled_buffer = malloc(max_out_samples * CHANNELS * sizeof(int16_t)); } uint8_t *out_data[8] = { (uint8_t*)resampled_buffer }; // 执行重采样 resampled_samples = swr_convert(state->swr_ctx, out_data, max_out_samples, in_data, in_samples); if (resampled_samples > 0) { int out_size = resampled_samples * CHANNELS * sizeof(int16_t); if (pa_simple_write(state->pulse, resampled_buffer, out_size, NULL) < 0) { fprintf(stderr, "PulseAudio写入错误\n"); } else { decoded_frames++; sync_audio(state, resampled_samples); } } } else { // 不需要重采样,但需要转换到S16 int pcm_size = in_samples * in_channels; // 确保转换缓冲区足够大 if (convert_buffer_size < pcm_size) { convert_buffer = realloc(convert_buffer, pcm_size * sizeof(int16_t)); convert_buffer_size = pcm_size; } // 手动转换浮点到S16 convert_float_to_s16((float*)pcm, convert_buffer, pcm_size); int out_size = pcm_size * sizeof(int16_t); if (pa_simple_write(state->pulse, convert_buffer, out_size, NULL) < 0) { fprintf(stderr, "PulseAudio写入错误\n"); } else { decoded_frames++; sync_audio(state, in_samples); } } } else if (frame_info.error) { fprintf(stderr, "AAC解码错误: %s\n", NeAACDecGetErrorMessage(frame_info.error)); } pos += frame_length; } // 冲刷重采样器 if (state->swr_ctx) { int remaining = swr_get_delay(state->swr_ctx, 0); if (remaining > 0) { if (!resampled_buffer) { resampled_buffer = malloc(remaining * CHANNELS * sizeof(int16_t)); } uint8_t *out_data[8] = { (uint8_t*)resampled_buffer }; resampled_samples = swr_convert(state->swr_ctx, out_data, remaining, NULL, 0); if (resampled_samples > 0) { int out_size = resampled_samples * CHANNELS * sizeof(int16_t); pa_simple_write(state->pulse, resampled_buffer, out_size, NULL); sync_audio(state, resampled_samples); } } } // 清理缓冲区 if (resampled_buffer) free(resampled_buffer); if (convert_buffer) free(convert_buffer); return decoded_frames; } // 处理单个TS分段 void process_ts_segment(PlayerState* state, const char* url) { struct timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); // 下载TS分段 MemoryChunk ts_chunk = download_url(state->curl, url); if (!ts_chunk.data || ts_chunk.size == 0) { fprintf(stderr, "下载分段失败: %s\n", url); return; } // 从TS包中提取有效载荷 unsigned char* payload = NULL; size_t payload_size = 0; if (extract_ts_payload(ts_chunk.data, ts_chunk.size, &payload, &payload_size) != 0) { fprintf(stderr, "TS解析失败: %s\n", url); free(ts_chunk.data); return; } free(ts_chunk.data); if (payload && payload_size > 0) { // 解码AAC音频 int frames = decode_aac_frames(state, payload, payload_size); if (frames == 0) { fprintf(stderr, "未解码任何AAC帧: %s\n", url); } free(payload); } else { fprintf(stderr, "未找到有效载荷: %s\n", url); } // 计算处理时间 clock_gettime(CLOCK_MONOTONIC, &end); double elapsed = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9; printf("分段处理时间: %.2fms\n", elapsed * 1000); } // 音频解码和播放线程 void* audio_thread(void* arg) { PlayerState* state = (PlayerState*)arg; // 初始化FAAD2解码器 state->aac_decoder = NeAACDecOpen(); if (!state->aac_decoder) { fprintf(stderr, "无法打开AAC解码器\n"); return NULL; } state->aac_initialized = 0; state->swr_ctx = NULL; // 初始化重采样器为NULL state->audio_pts = 0; // 初始化时间戳 state->played_samples = 0; // 配置解码器 NeAACDecConfigurationPtr config = NeAACDecGetCurrentConfiguration(state->aac_decoder); if (config) { config->outputFormat = FAAD_FMT_FLOAT; // 改为浮点输出 config->downMatrix = 1; config->dontUpSampleImplicitSBR = 1; // 防止SBR上采样 NeAACDecSetConfiguration(state->aac_decoder, config); } else { fprintf(stderr, "无法获取AAC解码器配置\n"); } // 设置音频缓冲 const int buffer_duration = BUFFER_DURATION_MS; const int buffer_size = OUTPUT_SAMPLE_RATE * CHANNELS * sizeof(int16_t) * buffer_duration / 1000; while (state->running) { pthread_mutex_lock(&state->mutex); if (state->segment_count == 0) { pthread_mutex_unlock(&state->mutex); usleep(buffer_duration * 1000); // 按缓冲区时长休眠 continue; } // 获取一个分段 Segment seg = state->segments[0]; memmove(&state->segments[0], &state->segments[1], (state->segment_count - 1) * sizeof(Segment)); state->segment_count--; pthread_mutex_unlock(&state->mutex); // 处理TS分段 process_ts_segment(state, seg.url); free(seg.url); } // 清理资源 if (state->swr_ctx) { swr_free(&state->swr_ctx); } // 清理FAAD2解码器 NeAACDecClose(state->aac_decoder); return NULL; } // 主控制线程 int main() { PlayerState state; memset(&state, 0, sizeof(PlayerState)); // 初始化CURL curl_global_init(CURL_GLOBAL_DEFAULT); state.curl = curl_easy_init(); if (!state.curl) { fprintf(stderr, "无法初始化CURL\n"); return 1; } // 设置基础URL state.base_url = "https://ngcdn001.cnr.cn/live/zgzs/"; // 初始化互斥锁 if (pthread_mutex_init(&state.mutex, NULL)) { fprintf(stderr, "无法初始化互斥锁\n"); curl_easy_cleanup(state.curl); return 1; } // 初始化PulseAudio static const pa_sample_spec ss = { .format = PA_SAMPLE_S16LE, .rate = OUTPUT_SAMPLE_RATE, .channels = CHANNELS }; // 增加缓冲区大小 (500ms) pa_buffer_attr buffer_attr = { .maxlength = (uint32_t)-1, .tlength = (uint32_t)(OUTPUT_SAMPLE_RATE * sizeof(int16_t) * CHANNELS * 0.5), // 500ms .prebuf = (uint32_t)-1, .minreq = (uint32_t)(OUTPUT_SAMPLE_RATE * sizeof(int16_t) * CHANNELS * 0.05), // 50ms .fragsize = (uint32_t)-1 }; int pa_error; state.pulse = pa_simple_new( NULL, // 默认服务器 "CNR Radio Player", // 应用名称 PA_STREAM_PLAYBACK, // 播放流 NULL, // 默认设备 "China National Radio", // 流描述 &ss, // 采样规格 NULL, // 声道映射 &buffer_attr, // 缓冲区属性 &pa_error // 错误码 ); if (!state.pulse) { fprintf(stderr, "无法创建PulseAudio连接: %s\n", pa_strerror(pa_error)); pthread_mutex_destroy(&state.mutex); curl_easy_cleanup(state.curl); return 1; } state.running = 1; // 创建音频线程 pthread_t audio_tid; if (pthread_create(&audio_tid, NULL, audio_thread, &state) != 0) { fprintf(stderr, "无法创建音频线程\n"); pa_simple_free(state.pulse); pthread_mutex_destroy(&state.mutex); curl_easy_cleanup(state.curl); return 1; } printf("开始播放中国之声 (按Ctrl+C停止)...\n"); printf("输出采样率: %d Hz, 声道: %d\n", OUTPUT_SAMPLE_RATE, CHANNELS); printf("缓冲区: %dms, 缓冲阈值: %d个分段\n", BUFFER_DURATION_MS, BUFFER_THRESHOLD); // 主循环:定期更新播放列表 while (state.running) { int count = parse_m3u8(&state); if (count > 0) { printf("获取到 %d 个新分段, 当前缓冲: %d\n", count, state.segment_count); } else if (count < 0) { fprintf(stderr, "解析M3U8失败, 10秒后重试...\n"); sleep(10); continue; } // 动态调整休眠时间 pthread_mutex_lock(&state.mutex); int current_count = state.segment_count; pthread_mutex_unlock(&state.mutex); int sleep_time = current_count > BUFFER_THRESHOLD ? 2 : 1; sleep(sleep_time); } // 清理资源 state.running = 0; pthread_join(audio_tid, NULL); // 清理剩余分段 for (int i = 0; i < state.segment_count; i++) { free(state.segments[i].url); } // 刷新PulseAudio缓冲区 pa_simple_drain(state.pulse, NULL); pa_simple_free(state.pulse); curl_easy_cleanup(state.curl); curl_global_cleanup(); pthread_mutex_destroy(&state.mutex); return 0; } 此代码使用的什么进行的m3u8解析
最新发布
07-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值