Audiostream_playback

本文档介绍诺基亚手机中音频流播放的相关技术和实现方法,详细解释了如何在诺基亚设备上进行音频流的播放。
Audio Stream.h class AudioDevice; typedef unsigned int app_type_t; class StreamPrimary { public: StreamPrimary(audio_io_handle_t handle, const std::set<audio_devices_t> &devices, struct audio_config *config); virtual ~StreamPrimary(); uint32_t GetSampleRate(); uint32_t GetBufferSize(); audio_format_t GetFormat(); audio_channel_mask_t GetChannelMask(); int getPalDeviceIds(const std::set<audio_devices_t> &halDeviceIds, pal_device_id_t* palOutDeviceIds); audio_io_handle_t GetHandle(); int GetUseCase(); std::mutex write_wait_mutex_; std::condition_variable write_condition_; std::mutex stream_mutex_; bool write_ready_; std::mutex drain_wait_mutex_; std::condition_variable drain_condition_; bool drain_ready_; stream_callback_t client_callback; void *client_cookie; static int GetDeviceAddress(struct str_parms *parms, int *card_id, int *device_num); int GetLookupTableIndex(const struct string_to_enum *table, const int table_size, int value); bool GetSupportedConfig(bool isOutStream, struct str_parms *query, struct str_parms *reply); virtual int RouteStream(const std::set<audio_devices_t>&, bool force_device_switch = false) = 0; bool isStarted() { return stream_started_; }; protected: struct pal_stream_attributes streamAttributes_; pal_stream_handle_t* pal_stream_handle_; audio_io_handle_t handle_; pal_device_id_t pal_device_id_; struct audio_config config_; char address_[AUDIO_DEVICE_MAX_ADDRESS_LEN]; bool stream_started_ = false; bool stream_paused_ = false; bool stream_flushed_ = false; int usecase_; struct pal_volume_data *volume_; /* used to cache volume */ std::map <audio_devices_t, pal_device_id_t> mAndroidDeviceMap; int mmap_shared_memory_fd; app_type_t app_types_; pal_param_device_capability_t *device_cap_query_; app_type_t audio_power_app_types_;/* Audio PowerSave */ }; class StreamOutPrimary : public StreamPrimary { private: // Helper function for write to open pal stream & configure. ssize_t configurePalOutputStream(); //Helper method to standby streams upon write failures and sleep for buffer duration. ssize_t onWriteError(size_t bytes, ssize_t ret); protected: struct pal_device* mPalOutDevice; private: pal_device_id_t* mPalOutDeviceIds; std::set<audio_devices_t> mAndroidOutDevices; bool mInitialized; /* fixed ear_out aux_out stereo start */ bool mIsKaraokeMuteOnCombo; /* fixed ear_out aux_out stereo end */ // [offload playspeed bool isOffloadUsecase() { return GetUseCase() == USECASE_AUDIO_PLAYBACK_OFFLOAD; } bool isOffloadSpeedSupported(); bool isValidPlaybackRate(const audio_playback_rate_t *playbackRate); bool isValidStretchMode(audio_timestretch_stretch_mode_t stretchMode); bool isValidFallbackMode(audio_timestretch_fallback_mode_t fallbackMode); int setPlaybackRateToPal(const audio_playback_rate_t *playbackRate); audio_playback_rate_t mPlaybackRate = AUDIO_PLAYBACK_RATE_INITIALIZER; // offload Playspeed] public: StreamOutPrimary(audio_io_handle_t handle, const std::set<audio_devices_t>& devices, audio_output_flags_t flags, struct audio_config *config, const char *address, offload_effects_start_output fnp_start_offload_effect, offload_effects_stop_output fnp_stop_offload_effect, visualizer_hal_start_output fnp_visualizer_start_output_, visualizer_hal_stop_output fnp_visualizer_stop_output_); ~StreamOutPrimary(); bool sendGaplessMetadata = true; bool isCompressMetadataAvail = false; void UpdatemCachedPosition(uint64_t val); virtual int Standby(); int SetVolume(float left, float right); int refactorVolumeData(float left, float right); uint64_t GetFramesWritten(struct timespec *timestamp); virtual int SetParameters(struct str_parms *parms); int Pause(); int Resume(); int Drain(audio_drain_type_t type); int Flush(); virtual int Start(); int Stop(); virtual ssize_t write(const void *buffer, size_t bytes); virtual int Open(); void GetStreamHandle(audio_stream_out** stream); uint32_t GetBufferSize(); uint32_t GetBufferSizeForLowLatency(); int GetFrames(uint64_t *frames); static pal_stream_type_t GetPalStreamType(audio_output_flags_t halStreamFlags, uint32_t sample_rate, bool isDeviceAvail); static int64_t GetRenderLatency(audio_output_flags_t halStreamFlags); int GetOutputUseCase(audio_output_flags_t halStreamFlags); int StartOffloadEffects(audio_io_handle_t, pal_stream_handle_t*); int StopOffloadEffects(audio_io_handle_t, pal_stream_handle_t*); bool CheckOffloadEffectsType(pal_stream_type_t pal_stream_type); int StartOffloadVisualizer(audio_io_handle_t, pal_stream_handle_t*); int StopOffloadVisualizer(audio_io_handle_t, pal_stream_handle_t*); audio_output_flags_t flags_; int CreateMmapBuffer(int32_t min_size_frames, struct audio_mmap_buffer_info *info); int GetMmapPosition(struct audio_mmap_position *position); bool isDeviceAvailable(pal_device_id_t deviceId); int RouteStream(const std::set<audio_devices_t>&, bool force_device_switch = false); virtual void SetMode(audio_mode_t mode) = 0; ssize_t splitAndWriteAudioHapticsStream(const void *buffer, size_t bytes); bool period_size_is_plausible_for_low_latency(int period_size); source_metadata_t btSourceMetadata; std::vector<playback_track_metadata_t> tracks; int SetAggregateSourceMetadata(bool voice_active); static std::mutex sourceMetadata_mutex_; // [offload playback speed int getPlaybackRateParameters(audio_playback_rate_t *playbackRate); int setPlaybackRateParameters(const audio_playback_rate_t *playbackRate); // offload playback speed] protected: struct timespec writeAt; int get_compressed_buffer_size(); int get_pcm_buffer_size(); int is_direct(); audio_format_t halInputFormat = AUDIO_FORMAT_DEFAULT; audio_format_t halOutputFormat = AUDIO_FORMAT_DEFAULT; uint32_t convertBufSize; uint32_t fragments_ = 0; uint32_t fragment_size_ = 0; pal_snd_dec_t palSndDec; struct pal_compr_gapless_mdata gaplessMeta = {0, 0}; uint32_t msample_rate; uint16_t mchannels; std::shared_ptr<audio_stream_out> stream_; uint64_t mBytesWritten; /* total bytes written, not cleared when entering standby */ uint64_t mCachedPosition = 0; /* cache pcm offload position when entering standby */ offload_effects_start_output fnp_offload_effect_start_output_ = nullptr; offload_effects_stop_output fnp_offload_effect_stop_output_ = nullptr; visualizer_hal_start_output fnp_visualizer_start_output_ = nullptr; visualizer_hal_stop_output fnp_visualizer_stop_output_ = nullptr; void *convertBuffer; //Haptics Usecase struct pal_stream_attributes hapticsStreamAttributes; pal_stream_handle_t* pal_haptics_stream_handle; AudioExtn AudExtn; struct pal_device* hapticsDevice; uint8_t* hapticBuffer; size_t hapticsBufSize; audio_mode_t _mode; int FillHalFnPtrs(); friend class AudioDevice; struct timespec ts_first_write = {0, 0}; }; class StreamInPrimary : public StreamPrimary{ protected: struct pal_device* mPalInDevice; private: pal_device_id_t* mPalInDeviceIds; std::set<audio_devices_t> mAndroidInDevices; bool mInitialized; //Helper method to standby streams upon read failures and sleep for buffer duration. ssize_t onReadError(size_t bytes, size_t ret); public: StreamInPrimary(audio_io_handle_t handle, const std::set<audio_devices_t> &devices, audio_input_flags_t flags, struct audio_config *config, const char *address, audio_source_t source); ~StreamInPrimary(); int Standby(); int SetGain(float gain); void GetStreamHandle(audio_stream_in** stream); virtual int Open(); int Start(); int Stop(); int SetMicMute(bool mute); ssize_t read(const void *buffer, size_t bytes); uint32_t GetBufferSize(); uint32_t GetBufferSizeForLowLatencyRecord(); pal_stream_type_t GetPalStreamType(audio_input_flags_t halStreamFlags, uint32_t sample_rate); int GetInputUseCase(audio_input_flags_t halStreamFlags, audio_source_t source); int addRemoveAudioEffect(const struct audio_stream *stream, effect_handle_t effect,bool enable); virtual int SetParameters(const char *kvpairs); bool getParameters(struct str_parms *query, struct str_parms *reply); bool is_st_session; audio_input_flags_t flags_; int CreateMmapBuffer(int32_t min_size_frames, struct audio_mmap_buffer_info *info); int GetMmapPosition(struct audio_mmap_position *position); bool isDeviceAvailable(pal_device_id_t deviceId); int RouteStream(const std::set<audio_devices_t>& new_devices, bool force_device_switch = false); int64_t GetSourceLatency(audio_input_flags_t halStreamFlags); uint64_t GetFramesRead(int64_t *time); int GetPalDeviceIds(pal_device_id_t *palDevIds, int *numPalDevs); sink_metadata_t btSinkMetadata; std::vector<record_track_metadata_t> tracks; int SetAggregateSinkMetadata(bool voice_active); static std::mutex sinkMetadata_mutex_; pal_stream_handle_t *pal_vui_handle_; protected: struct timespec readAt; uint32_t fragments_ = 0; uint32_t fragment_size_ = 0; int FillHalFnPtrs(); std::shared_ptr<audio_stream_in> stream_; audio_source_t source_; friend class AudioDevice; uint64_t mBytesRead = 0; /* total bytes read, not cleared when entering standby */ // for compress capture usecase std::unique_ptr<CompressCapture::CompressAAC> mCompressEncoder; bool isECEnabled = false; bool isNSEnabled = false; bool effects_applied_ = true; //ADD: KARAOKE bool is_karaoke_on = false; int is_karaoke_status = 0; bool is_cts_stream = false; std::mutex activeStreamMutex; //END KARAOKE // MIUI ADD: Audio_XiaoAi bool is_map_switch = false; // END Audio_XiaoAi }; AudioStream.cpp int StreamOutPrimary::Standby() { int ret = 0; /* fixed ear_out aux_out stereo start */ std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance(); std::set<audio_devices_t> new_devices; /* fixed ear_out aux_out stereo end */ AHAL_DBG("Enter"); if (adevice->is_earout_hphl_conflict && mIsKaraokeMuteOnCombo) { AHAL_DBG("routestream from combo whs to whs before standby"); mAndroidOutDevices.erase(AUDIO_DEVICE_OUT_SPEAKER); new_devices = mAndroidOutDevices; StreamOutPrimary::RouteStream(new_devices, true); } stream_mutex_.lock(); if (pal_stream_handle_) { if (streamAttributes_.type == PAL_STREAM_PCM_OFFLOAD) { /* * when ssr happens, dsp position for pcm offload could be 0, * so get written frames. Else, get frames. */ if (PAL_CARD_STATUS_DOWN(AudioDevice::sndCardState)) { struct timespec ts; // release stream lock as GetFramesWritten will lock/unlock stream mutex stream_mutex_.unlock(); mCachedPosition = GetFramesWritten(&ts); stream_mutex_.lock(); AHAL_DBG("card is offline, return written frames %lld", (long long)mCachedPosition); } else { GetFrames(&mCachedPosition); } } ret = pal_stream_stop(pal_stream_handle_); if (ret) { AHAL_ERR("failed to stop stream."); ret = -EINVAL; } if (usecase_ == USECASE_AUDIO_PLAYBACK_WITH_HAPTICS && pal_haptics_stream_handle) { ret = pal_stream_stop(pal_haptics_stream_handle); if (ret) { AHAL_ERR("failed to stop haptics stream."); } } } ret = -EINVAL; exit: stream_mutex_.unlock(); AHAL_DBG("Exit ret: %d", ret); return ret; } StreamOutPrimary::StreamOutPrimary( audio_io_handle_t handle, const std::set<audio_devices_t> &devices, audio_output_flags_t flags, struct audio_config *config, const char *address __unused, offload_effects_start_output start_offload_effect, offload_effects_stop_output stop_offload_effect, visualizer_hal_start_output visualizer_start_output, visualizer_hal_stop_output visualizer_stop_output): StreamPrimary(handle, devices, config), mAndroidOutDevices(devices), flags_(flags), btSourceMetadata{0, nullptr} { stream_ = std::shared_ptr<audio_stream_out> (new audio_stream_out()); std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance(); mInitialized = false; /* fixed ear_out aux_out stereo start */ mIsKaraokeMuteOnCombo = false; bool isCombo = false; audio_devices_t OutDevices = AudioExtn::get_device_types(mAndroidOutDevices); /* fixed ear_out aux_out stereo end */ pal_stream_handle_ = nullptr; pal_haptics_stream_handle = nullptr; mPalOutDeviceIds = nullptr; mPalOutDevice = nullptr; convertBuffer = NULL; hapticsDevice = NULL; hapticBuffer = NULL; hapticsBufSize = 0; writeAt.tv_sec = 0; writeAt.tv_nsec = 0; mBytesWritten = 0; int noPalDevices = 0; int ret = 0; /*Initialize the gaplessMeta value with 0*/ memset(&gaplessMeta,0,sizeof(struct pal_compr_gapless_mdata)); if (!stream_) { AHAL_ERR("No memory allocated for stream_"); throw std::runtime_error("No memory allocated for stream_"); } AHAL_DBG("enter: handle (%x) format(%#x) sample_rate(%d) channel_mask(%#x) devices(%zu) flags(%#x)\ address(%s)", handle, config->format, config->sample_rate, config->channel_mask, mAndroidOutDevices.size(), flags, address); return; } StreamInPrimary::StreamInPrimary(audio_io_handle_t handle, const std::set<audio_devices_t> &devices, audio_input_flags_t flags, struct audio_config *config, const char *address __unused, audio_source_t source) : StreamPrimary(handle, devices, config), mAndroidInDevices(devices), flags_(flags), btSinkMetadata{0, nullptr}, pal_vui_handle_(nullptr), mCompressEncoder(nullptr) { stream_ = std::shared_ptr<audio_stream_in> (new audio_stream_in()); std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance(); pal_stream_handle_ = NULL; mInitialized = false; int noPalDevices = 0; int ret = 0; readAt.tv_sec = 0; readAt.tv_nsec = 0; void *st_handle = nullptr; pal_param_payload *payload = nullptr; AHAL_DBG("enter: handle (%x) format(%#x) sample_rate(%d) channel_mask(%#x) devices(%zu) flags(%#x)"\ , handle, config->format, config->sample_rate, config->channel_mask, mAndroidInDevices.size(), flags); if (!(stream_.get())) { AHAL_ERR("stream_ new allocation failed"); goto error; } error: (void)FillHalFnPtrs(); AHAL_DBG("Exit"); return; } audio_format_t StreamPrimary::GetFormat() { return config_.format; } static audio_format_t astream_in_get_format(const struct audio_stream *stream) { std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance(); std::shared_ptr<StreamInPrimary> astream_in; if (adevice) astream_in = adevice->InGetStream((audio_stream_t*)stream); else AHAL_ERR("unable to get audio device"); if (astream_in) return astream_in->GetFormat(); else return AUDIO_FORMAT_DEFAULT; } 请分析上面的代码修改,修改实现在int StreamOutPrimary::Standby() 获取is_karaoke_status的值用于判断
最新发布
08-08
./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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值