cmvr-es/cmvr-es/devices/speaker/ffmpeg_speaker/src/ffmpeg_speaker.cpp
2026-07-22 13:48:11 +08:00

934 lines
29 KiB
C++

#include "common/base/logging/logger.h"
//
// Created by xtkuang on 2025/6/9.
//
#include <filesystem>
#include <algorithm>
#include <climits>
#include <cstring>
#include "../include/ffmpeg_speaker.h"
using namespace std;
using namespace cmvr::device;
ffmpegSpeaker::ffmpegSpeaker(const config::FFMpegSpeakerConfig& cfg):config_(cfg)
{
state_.is_initialized = false;
state_.is_running = false;
state_.is_paused = false;
try {
id_ = config_.id();
memset(&sample_spec_, 0, sizeof(sample_spec_));
state_.volume = 100;
}
catch (const exception& e) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] ([ffmpegSpeaker]): Failed to parse config: " << e.what();
}
}
ffmpegSpeaker::~ffmpegSpeaker() {
is_stopping_ = true;
{
std::lock_guard<std::mutex> lock(mtx_);
state_.is_running = false;
state_.is_decoding = false;
state_.is_paused = false;
}
cv_pause_.notify_all(); // 唤醒所有等待的线程
// 等待线程结束
if (decode_thread_ && decode_thread_->joinable()) {
decode_thread_->join();
decode_thread_.reset();
}
if (play_thread_ && play_thread_->joinable()) {
play_thread_->join();
play_thread_.reset();
}
resetPlayState();
}
bool ffmpegSpeaker::init() {
try {
lock_guard lock(mtx_);
state_.is_running = false;
state_.is_paused = false;
state_.is_decoding = false;
av_log_set_level(AV_LOG_QUIET);
avformat_network_init();
state_.is_initialized = true;
CMVR_LOG(INFO) << "[ffmpegSpeaker] (init): Success, id=" << id_;
return true;
}
catch (const exception& e) {
CMVR_LOG(ERROR) <<"[ffmpegSpeaker] (init): Failed, id=" << id_ << ": " << e.what();
state_.is_initialized = false;
return false;
}
}
bool ffmpegSpeaker::start() {
return true;
}
void ffmpegSpeaker::resetPlayState()
{
releaseStreamDecoder_();
if (pulse_simple_) {
pa_simple_flush(pulse_simple_, nullptr);
pa_simple_free(pulse_simple_);
pulse_simple_ = nullptr;
}
// 清空队列
AudioFrame frame;
while (audio_queue_.pop(frame)) {
// 清空所有帧
}
state_.is_initialized = false;
is_streaming_input_ = false;
audio_path_.clear();
CMVR_LOG(INFO) << "[ffmpegSpeaker] (resetPlayState): Success, id=" << id_;
}
bool ffmpegSpeaker::stop() {
std::lock_guard<std::mutex> stop_lock(stop_mtx_);
is_stopping_ = true;
{
lock_guard lock(mtx_);
state_.is_running = false;
state_.is_decoding = false;
state_.is_paused = false;
is_streaming_input_ = false;
}
cv_pause_.notify_all();
// 等待线程结束
if (decode_thread_ && decode_thread_->joinable()) {
decode_thread_->join();
decode_thread_.reset();
}
if (play_thread_ && play_thread_->joinable()) {
play_thread_->join();
play_thread_.reset();
}
resetPlayState();
is_stopping_ = false;
return true;
}
void ffmpegSpeaker::pause() {
try {
lock_guard lock(mtx_);
if (!state_.is_running || !state_.is_initialized) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] (pause): Speaker not running";
return;
}
if (state_.is_paused) {
CMVR_LOG(WARNING) << "[ffmpegSpeaker] (pause): Speaker already paused";
return;
}
state_.is_paused = true;
CMVR_LOG(INFO) << "[ffmpegSpeaker] (pause): Success, id=" << id_;
}
catch (const exception& e) {
CMVR_LOG(ERROR) <<"[ffmpegSpeaker] (pause): Failed, id=" << id_ << ": " << e.what();
}
}
void ffmpegSpeaker::resume() {
try {
lock_guard lock(mtx_);
if (!state_.is_running || !state_.is_initialized) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] (pause): Speaker not running";
return;
}
if (!state_.is_paused) {
CMVR_LOG(WARNING) << "[ffmpegSpeaker] (pause): Speaker is not paused";
return;
}
state_.is_paused = false;
cv_pause_.notify_all();
CMVR_LOG(INFO) << "[ffmpegSpeaker] (resume): Success, id=" << id_;
}
catch (const exception& e) {
CMVR_LOG(ERROR) <<"[ffmpegSpeaker] (resume): Failed, id=" << id_ << ": " << e.what();
}
}
void ffmpegSpeaker::play(const std::string& audio_path) {
std::unique_lock<std::mutex> lock(mtx_);
try {
if (state_.is_running) {
CMVR_LOG(WARNING) << "[ffmpegSpeaker] (play): Speaker is playing " << audio_path_ << ", id=" << id_;
return;
}
if (!std::filesystem::exists(audio_path)) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] (play): Audio file not found: " << audio_path;
return;
}
// 如果正在播放,先停止
state_.is_running = false;
lock.unlock();
cv_pause_.notify_all();
if (decode_thread_ && decode_thread_->joinable()) {
decode_thread_->join();
decode_thread_.reset();
}
if (play_thread_ && play_thread_->joinable()) {
play_thread_->join();
play_thread_.reset();
}
resetPlayState();
lock.lock();
// 清空队列
AudioFrame frame;
while (audio_queue_.pop(frame)) {
// 清空所有帧
}
audio_path_ = audio_path;
// 初始化音频参数
if (!initAudioParams_(audio_path)) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] Failed to get audio parameters";
return;
}
// 初始化 PulseAudio 设备
if (!initPulseDevice_()) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] PulseAudio device initialization failed";
return;
}
// 设置状态
state_.is_running = true;
state_.is_decoding = true;
state_.is_paused = false;
// 启动解码线程
decode_thread_ = std::make_shared<std::thread>([this]() {
try {
this->decode_audio_();
} catch (const std::exception& e) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] Decode thread exception: " << e.what();
std::lock_guard<std::mutex> lock(mtx_);
state_.is_decoding = false;
}
});
// 等待解码线程开始(确保有数据)
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// 启动播放线程
play_thread_ = std::make_shared<std::thread>([this]() {
try {
this->play_audio_();
} catch (const std::exception& e) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] Play thread exception: " << e.what();
std::lock_guard<std::mutex> lock(mtx_);
state_.is_running = false;
}
});
CMVR_LOG(INFO) << "[ffmpegSpeaker] Started playing: " << audio_path;
}
catch (const std::exception& e) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] (play): Failed, id=" << id_ << ": " << e.what();
state_.is_running = false;
state_.is_decoding = false;
lock.unlock();
resetPlayState();
}
}
void ffmpegSpeaker::setVolume(const int volume) {
lock_guard lock(mtx_);
state_.volume = clamp(volume, 0, 100);
}
int ffmpegSpeaker::getVolume() const {
return state_.volume;
}
void ffmpegSpeaker::getState(SpeakerState& state) {
state.is_decoding = state_.is_decoding;
state.volume = state_.volume;
state.is_initialized = state_.is_initialized;
state.is_running = state_.is_running;
state.is_paused = state_.is_paused;
}
void ffmpegSpeaker::decode_audio_() {
using namespace ffmpeg;
if (audio_path_.empty()) {
{
std::lock_guard<std::mutex> lock(mtx_);
state_.is_decoding = false;
}
CMVR_LOG(ERROR) << "[ffmpegSpeaker] (decode_audio_): audio_path is empty";
return;
}
CMVR_LOG(INFO) << "[ffmpegSpeaker] Decoding with: rate=" << sample_rate_
<< ", channels=" << channels_;
const AVFormatContextPtr fmt_ctx = make_format_context(audio_path_);
if (!fmt_ctx) {
{
std::lock_guard<std::mutex> lock(mtx_);
state_.is_decoding = false;
}
CMVR_LOG(ERROR) << "[ffmpegSpeaker] (decode_audio_): Failed to open format context";
return;
}
AVCodec* decoder = nullptr;
const int stream_index = av_find_best_stream(fmt_ctx.get(), AVMEDIA_TYPE_AUDIO, -1, -1, &decoder, 0);
if (stream_index < 0) {
{
std::lock_guard<std::mutex> lock(mtx_);
state_.is_decoding = false;
}
CMVR_LOG(ERROR) << "[ffmpegSpeaker] (decode_audio_): No audio stream found in file";
return;
}
AVStream* audio_stream = fmt_ctx->streams[stream_index];
const AVCodecContextPtr codec_ctx = make_codec_context(audio_stream, decoder);
if (!codec_ctx) {
{
std::lock_guard<std::mutex> lock(mtx_);
state_.is_decoding = false;
}
CMVR_LOG(ERROR) << "[ffmpegSpeaker] (decode_audio_): Failed to create codec context";
return;
}
const SwrContextPtr swr_ctx = make_swr_context(codec_ctx.get(), channels_, sample_rate_);
if (!swr_ctx) {
{
std::lock_guard<std::mutex> lock(mtx_);
state_.is_decoding = false;
}
CMVR_LOG(ERROR) << "[ffmpegSpeaker] (decode_audio_): Failed to init swr context";
return;
}
const AVPacketPtr pkt(av_packet_alloc());
const AVFramePtr frame(av_frame_alloc());
// 解码循环
while (true) {
{
std::lock_guard<std::mutex> lock(mtx_);
if (!state_.is_running || !state_.is_decoding) {
break;
}
}
// 暂停处理
{
std::unique_lock<std::mutex> lock(mtx_pause_);
cv_pause_.wait(lock, [this]() {
return !state_.is_paused || !state_.is_running;
});
if (!state_.is_running) break;
}
// 控制队列大小,避免内存占用过高
if (audio_queue_.write_available() < 5) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
continue;
}
if (av_read_frame(fmt_ctx.get(), pkt.get()) < 0) {
// 文件读取结束
break;
}
if (pkt->stream_index == stream_index) {
if (avcodec_send_packet(codec_ctx.get(), pkt.get()) < 0) {
av_packet_unref(pkt.get());
continue;
}
while (true) {
{
std::lock_guard<std::mutex> lock(mtx_);
if (!state_.is_running || !state_.is_decoding) {
av_packet_unref(pkt.get());
goto end_decode;
}
}
int ret = avcodec_receive_frame(codec_ctx.get(), frame.get());
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
break;
}
// 计算输出样本数
int64_t max_samples64 = av_rescale_rnd(
swr_get_delay(swr_ctx.get(), codec_ctx->sample_rate) + frame->nb_samples,
sample_rate_, codec_ctx->sample_rate, AV_ROUND_UP);
const int max_samples = static_cast<int>(std::min<int64_t>(max_samples64, INT_MAX));
// 创建音频帧
auto buffer = std::make_shared<std::vector<int16_t>>(max_samples * channels_);
uint8_t* out[] = { reinterpret_cast<uint8_t*>(buffer->data()), nullptr };
// 重采样
const int out_samples = swr_convert(
swr_ctx.get(),
out, max_samples,
const_cast<const uint8_t**>(frame->data), frame->nb_samples);
if (out_samples <= 0) {
continue;
}
// 调整大小到实际样本数
buffer->resize(out_samples * channels_);
// 应用音量
float volume_scale;
{
std::lock_guard<std::mutex> lock(mtx_);
volume_scale = static_cast<float>(state_.volume) / 100.0f;
}
int16_t* pcm_data = buffer->data();
const int sample_count = buffer->size();
for (int i = 0; i < sample_count; ++i) {
float scaled = static_cast<float>(pcm_data[i]) * volume_scale;
pcm_data[i] = static_cast<int16_t>(std::clamp(scaled, -32768.f, 32767.f));
}
// 推入队列(使用非阻塞方式)
bool pushed = false;
int attempts = 0;
while (!pushed && attempts < 10) {
pushed = audio_queue_.push(buffer);
if (!pushed) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
attempts++;
}
}
if (!pushed) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] Failed to push audio frame to queue after 10 attempts";
}
}
}
av_packet_unref(pkt.get());
}
end_decode:
{
std::lock_guard<std::mutex> lock(mtx_);
state_.is_decoding = false;
}
CMVR_LOG(INFO) << "[ffmpegSpeaker] Decoding finished";
}
void ffmpegSpeaker::play_audio_() {
int pa_error = 0;
while (true) {
{
std::lock_guard<std::mutex> lock(mtx_);
if (!state_.is_running) {
break;
}
}
// 暂停处理
if (state_.is_paused) {
std::unique_lock<std::mutex> lock(mtx_pause_);
cv_pause_.wait(lock, [this]() {
return !state_.is_paused || !state_.is_running;
});
if (!state_.is_running) break;
}
AudioFrame frame;
// 尝试获取音频帧
if (!audio_queue_.pop(frame)) {
// 检查解码是否结束
bool decoding_finished = false;
{
std::lock_guard<std::mutex> lock(mtx_);
decoding_finished = !state_.is_decoding;
}
if (decoding_finished && audio_queue_.empty()) {
break; // 解码结束且队列为空,播放完成
}
// 短暂等待后重试
std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue;
}
// 使用 PulseAudio 播放
if (pulse_simple_ && !frame->empty()) {
const size_t data_size = frame->size() * sizeof(int16_t);
int ret = pa_simple_write(pulse_simple_, frame->data(), data_size, &pa_error);
if (ret < 0) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] (play_audio_): PulseAudio write failed: "
<< pa_strerror(pa_error);
// 尝试重新连接
resetPlayState();
// 稍等后重试初始化
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (!initPulseDevice_()) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] (play_audio_): Failed to reconnect";
{
std::lock_guard<std::mutex> lock(mtx_);
state_.is_running = false;
}
break;
}
// 重新写入当前帧
ret = pa_simple_write(pulse_simple_, frame->data(), data_size, &pa_error);
if (ret < 0) {
std::lock_guard<std::mutex> lock(mtx_);
state_.is_running = false;
break;
}
}
}
}
// 播放结束,刷新缓冲区
if (pulse_simple_) {
pa_simple_flush(pulse_simple_, &pa_error);
}
{
std::lock_guard<std::mutex> lock(mtx_);
state_.is_running = false;
}
CMVR_LOG(INFO) << "[ffmpegSpeaker] Playback finished";
}
bool ffmpegSpeaker::pushAudioFrame(const AudioStreamFrameData& frame_data)
{
if (is_stopping_) {
return false;
}
if (frame_data.data.empty()) {
return true;
}
bool need_start = false;
bool need_restart = false;
{
std::lock_guard<std::mutex> lock(mtx_);
need_start = !state_.is_running;
need_restart = state_.is_running && is_streaming_input_ &&
(frame_data.sample_rate != sample_rate_ || frame_data.channels != channels_);
if (state_.is_running && !is_streaming_input_) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] cannot stream audio while file playback is active";
return false;
}
}
if (need_restart) {
stopStreaming();
need_start = true;
}
if (need_start && !startStreamingPlayback_(frame_data)) {
return false;
}
if (frame_data.format == AudioStreamFormat::PCM ||
frame_data.codec == "pcm_s16le" ||
frame_data.codec == "pcm") {
return pushPcmFrame_(frame_data);
}
return decodeStreamFrame_(frame_data);
}
void ffmpegSpeaker::stopStreaming()
{
std::lock_guard<std::mutex> stop_lock(stop_mtx_);
is_stopping_ = true;
{
std::lock_guard<std::mutex> lock(mtx_);
if (!is_streaming_input_) {
is_stopping_ = false;
return;
}
state_.is_decoding = false;
state_.is_paused = false;
is_streaming_input_ = false;
}
cv_pause_.notify_all();
if (play_thread_ && play_thread_->joinable()) {
play_thread_->join();
play_thread_.reset();
}
resetPlayState();
is_stopping_ = false;
}
bool ffmpegSpeaker::startStreamingPlayback_(const AudioStreamFrameData& frame_data)
{
std::lock_guard<std::mutex> lock(mtx_);
sample_rate_ = frame_data.sample_rate > 0 ? frame_data.sample_rate : 44100;
channels_ = frame_data.channels > 0 ? frame_data.channels : 2;
AudioFrame queued_frame;
while (audio_queue_.pop(queued_frame)) {
}
if (!initPulseDevice_()) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] PulseAudio device initialization failed";
return false;
}
state_.is_running = true;
state_.is_decoding = true;
state_.is_paused = false;
is_streaming_input_ = true;
play_thread_ = std::make_shared<std::thread>([this]() {
try {
this->play_audio_();
} catch (const std::exception& e) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] Stream play thread exception: " << e.what();
std::lock_guard<std::mutex> lock(mtx_);
state_.is_running = false;
state_.is_decoding = false;
}
});
return true;
}
bool ffmpegSpeaker::pushPcmFrame_(const AudioStreamFrameData& frame_data)
{
const size_t sample_count = frame_data.data.size() / sizeof(int16_t);
if (sample_count == 0) {
return true;
}
auto buffer = std::make_shared<std::vector<int16_t>>(sample_count);
std::memcpy(buffer->data(), frame_data.data.data(), sample_count * sizeof(int16_t));
float volume_scale = 1.0f;
{
std::lock_guard<std::mutex> lock(mtx_);
volume_scale = static_cast<float>(state_.volume) / 100.0f;
}
for (int16_t& sample : *buffer) {
const float scaled = static_cast<float>(sample) * volume_scale;
sample = static_cast<int16_t>(std::clamp(scaled, -32768.f, 32767.f));
}
while (true) {
{
std::lock_guard<std::mutex> lock(mtx_);
if (!state_.is_running || is_stopping_) {
return false;
}
}
if (audio_queue_.push(buffer)) {
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
return true;
}
bool ffmpegSpeaker::decodeStreamFrame_(const AudioStreamFrameData& frame_data)
{
std::lock_guard<std::mutex> decode_lock(stream_decode_mtx_);
if (!stream_decoder_ctx_ ||
stream_codec_ != frame_data.codec ||
stream_input_sample_rate_ != frame_data.sample_rate ||
stream_input_channels_ != frame_data.channels) {
releaseStreamDecoderUnlocked_();
if (!initStreamDecoder_(frame_data)) {
return false;
}
}
av_packet_unref(stream_decode_packet_);
if (av_new_packet(stream_decode_packet_, static_cast<int>(frame_data.data.size())) < 0) {
return false;
}
std::memcpy(stream_decode_packet_->data, frame_data.data.data(), frame_data.data.size());
stream_decode_packet_->pts = frame_data.pts;
int ret = avcodec_send_packet(stream_decoder_ctx_, stream_decode_packet_);
av_packet_unref(stream_decode_packet_);
if (ret < 0) {
return false;
}
while ((ret = avcodec_receive_frame(stream_decoder_ctx_, stream_decode_frame_)) == 0) {
if (!stream_swr_ctx_) {
stream_swr_ctx_ = swr_alloc_set_opts(nullptr,
av_get_default_channel_layout(channels_),
AV_SAMPLE_FMT_S16,
sample_rate_,
av_get_default_channel_layout(stream_decode_frame_->channels),
static_cast<AVSampleFormat>(stream_decode_frame_->format),
stream_decode_frame_->sample_rate,
0,
nullptr);
if (!stream_swr_ctx_ || swr_init(stream_swr_ctx_) < 0) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] Failed to init stream resampler";
av_frame_unref(stream_decode_frame_);
return false;
}
}
const int64_t max_samples64 = av_rescale_rnd(
swr_get_delay(stream_swr_ctx_, stream_decode_frame_->sample_rate) + stream_decode_frame_->nb_samples,
sample_rate_,
stream_decode_frame_->sample_rate,
AV_ROUND_UP);
const int max_samples = static_cast<int>(std::min<int64_t>(max_samples64, INT_MAX));
auto buffer = std::make_shared<std::vector<int16_t>>(max_samples * channels_);
uint8_t* out[] = {reinterpret_cast<uint8_t*>(buffer->data()), nullptr};
const int out_samples = swr_convert(stream_swr_ctx_,
out,
max_samples,
const_cast<const uint8_t**>(stream_decode_frame_->data),
stream_decode_frame_->nb_samples);
av_frame_unref(stream_decode_frame_);
if (out_samples <= 0) {
continue;
}
buffer->resize(out_samples * channels_);
float volume_scale = 1.0f;
{
std::lock_guard<std::mutex> lock(mtx_);
volume_scale = static_cast<float>(state_.volume) / 100.0f;
}
for (int16_t& sample : *buffer) {
const float scaled = static_cast<float>(sample) * volume_scale;
sample = static_cast<int16_t>(std::clamp(scaled, -32768.f, 32767.f));
}
while (true) {
{
std::lock_guard<std::mutex> lock(mtx_);
if (!state_.is_running || is_stopping_) {
return false;
}
}
if (audio_queue_.push(buffer)) {
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
return ret == AVERROR(EAGAIN) || ret == AVERROR_EOF;
}
bool ffmpegSpeaker::initStreamDecoder_(const AudioStreamFrameData& frame_data)
{
const AVCodec* decoder = nullptr;
if (!frame_data.codec.empty()) {
decoder = avcodec_find_decoder_by_name(frame_data.codec.c_str());
}
if (!decoder) {
AVCodecID codec_id = AV_CODEC_ID_NONE;
if (frame_data.format == AudioStreamFormat::AAC) {
codec_id = AV_CODEC_ID_AAC;
} else if (frame_data.format == AudioStreamFormat::MP3) {
codec_id = AV_CODEC_ID_MP3;
}
if (codec_id != AV_CODEC_ID_NONE) {
decoder = avcodec_find_decoder(codec_id);
}
}
if (!decoder) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] Unsupported stream audio codec: " << frame_data.codec;
return false;
}
stream_decoder_ctx_ = avcodec_alloc_context3(decoder);
if (!stream_decoder_ctx_) {
return false;
}
stream_decoder_ctx_->sample_rate = frame_data.sample_rate > 0 ? frame_data.sample_rate : sample_rate_;
stream_decoder_ctx_->channels = frame_data.channels > 0 ? frame_data.channels : channels_;
stream_decoder_ctx_->channel_layout =
stream_decoder_ctx_->channels == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO;
if (avcodec_open2(stream_decoder_ctx_, decoder, nullptr) < 0) {
releaseStreamDecoderUnlocked_();
return false;
}
stream_decode_frame_ = av_frame_alloc();
stream_decode_packet_ = av_packet_alloc();
if (!stream_decode_frame_ || !stream_decode_packet_) {
releaseStreamDecoderUnlocked_();
return false;
}
stream_codec_ = frame_data.codec;
stream_input_sample_rate_ = frame_data.sample_rate;
stream_input_channels_ = frame_data.channels;
return true;
}
void ffmpegSpeaker::releaseStreamDecoder_()
{
std::lock_guard<std::mutex> decode_lock(stream_decode_mtx_);
releaseStreamDecoderUnlocked_();
}
void ffmpegSpeaker::releaseStreamDecoderUnlocked_()
{
if (stream_swr_ctx_) {
swr_free(&stream_swr_ctx_);
}
if (stream_decode_frame_) {
av_frame_free(&stream_decode_frame_);
}
if (stream_decode_packet_) {
av_packet_free(&stream_decode_packet_);
}
if (stream_decoder_ctx_) {
avcodec_free_context(&stream_decoder_ctx_);
}
stream_codec_.clear();
stream_input_sample_rate_ = 0;
stream_input_channels_ = 0;
}
bool ffmpegSpeaker::initPulseDevice_() {
// 验证参数
if (sample_rate_ <= 0 || channels_ <= 0) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] (initPulseDevice_): Invalid audio parameters: rate="
<< sample_rate_ << ", channels=" << channels_;
return false;
}
// 配置采样规格
sample_spec_.format = PA_SAMPLE_S16LE; // 16位有符号小端整数
sample_spec_.rate = sample_rate_; // 采样率
sample_spec_.channels = channels_; // 声道数
// 检查采样规格是否有效
if (!pa_sample_spec_valid(&sample_spec_)) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] (initPulseDevice_): Invalid sample specification: "
<< "rate=" << sample_spec_.rate
<< ", channels=" << sample_spec_.channels;
return false;
}
// 释放现有的连接
if (pulse_simple_) {
pa_simple_free(pulse_simple_);
pulse_simple_ = nullptr;
}
// 创建 PulseAudio 简单连接
int pa_error = 0;
pulse_simple_ = pa_simple_new(
nullptr, // 使用默认服务器
"ffmpegSpeaker", // 应用程序名称
PA_STREAM_PLAYBACK, // 播放流
nullptr, // 使用默认设备
"Music", // 流描述
&sample_spec_, // 采样规格
nullptr, // 使用默认通道映射
nullptr, // 缓冲区属性(使用默认)
&pa_error // 错误码
);
if (!pulse_simple_) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] (initPulseDevice_): Failed to create PulseAudio connection: "
<< pa_strerror(pa_error);
return false;
}
state_.is_initialized = true;
CMVR_LOG(INFO) << "[ffmpegSpeaker] PulseAudio initialized: rate=" << sample_rate_
<< ", channels=" << channels_;
return true;
}
bool ffmpegSpeaker::initAudioParams_(const std::string& audio_path) {
using namespace ffmpeg;
auto fmt_ctx = make_format_context(audio_path);
if (!fmt_ctx) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] Failed to open audio file: " << audio_path;
return false;
}
int stream_idx = av_find_best_stream(fmt_ctx.get(), AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
if (stream_idx < 0) {
CMVR_LOG(ERROR) << "[ffmpegSpeaker] No audio stream found";
return false;
}
auto codec_par = fmt_ctx->streams[stream_idx]->codecpar;
channels_ = codec_par->channels;
sample_rate_ = codec_par->sample_rate;
// 确保合理的参数
if (channels_ <= 0) channels_ = 2;
if (sample_rate_ <= 0) sample_rate_ = 44100;
// 检查并打印详细的音频参数
CMVR_LOG(INFO) << "[ffmpegSpeaker] Audio parameters: rate=" << sample_rate_
<< ", channels=" << channels_
<< ", format=" << av_get_sample_fmt_name((AVSampleFormat)codec_par->format)
;
return true;
}