update speaker
This commit is contained in:
parent
0ac04290e4
commit
1fc5179344
@ -3,7 +3,7 @@ microphone {
|
|||||||
id: "mic1"
|
id: "mic1"
|
||||||
ffmpeg {
|
ffmpeg {
|
||||||
channels: 2
|
channels: 2
|
||||||
sampleRate: 44100
|
sampleRate: 48000
|
||||||
volume: 100
|
volume: 100
|
||||||
input_device: "default"
|
input_device: "default"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -114,291 +114,6 @@ enum class ImageType {
|
|||||||
DEPTH, // 深度图像
|
DEPTH, // 深度图像
|
||||||
GRAYSCALE // 灰度图像
|
GRAYSCALE // 灰度图像
|
||||||
};
|
};
|
||||||
|
|
||||||
class HEVCEncoder {
|
|
||||||
public:
|
|
||||||
HEVCEncoder(int width, int height, int fps, int bitrate,
|
|
||||||
const std::string& deviceName = "/dev/dri/renderD128",
|
|
||||||
ImageType imageType = ImageType::COLOR)
|
|
||||||
: width_(width), height_(height), fps_(fps), bitrate_(bitrate),
|
|
||||||
deviceName_(deviceName), imageType_(imageType) {
|
|
||||||
initialized_ = init();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool encodeFrame(const cv::Mat& image) {
|
|
||||||
if (!initialized_ || !codec_ctx_ || !frame_) {
|
|
||||||
CMVR_LOG(ERROR) << "HEVC encoder is not initialized";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!prepareFrame(image)) {
|
|
||||||
CMVR_LOG(ERROR) << "Failed to prepare frame";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avcodec_send_frame(codec_ctx_.get(), frame_.get()) < 0) {
|
|
||||||
CMVR_LOG(ERROR) << "Error sending frame to encoder";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> receivePacket(bool& is_key) {
|
|
||||||
std::vector<uint8_t> packetData;
|
|
||||||
if (!initialized_ || !codec_ctx_) {
|
|
||||||
CMVR_LOG(ERROR) << "HEVC encoder is not initialized";
|
|
||||||
return packetData;
|
|
||||||
}
|
|
||||||
FFmpegPtr<AVPacket> packet(av_packet_alloc());
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
int ret = avcodec_receive_packet(codec_ctx_.get(), packet.get());
|
|
||||||
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
|
|
||||||
break;
|
|
||||||
} else if (ret < 0) {
|
|
||||||
CMVR_LOG(ERROR) << "Error during encoding";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
is_key = (packet->flags & AV_PKT_FLAG_KEY) != 0;
|
|
||||||
// 预留足够空间,避免多次内存分配
|
|
||||||
packetData.reserve(packetData.size() + packet->size);
|
|
||||||
// 复制数据包内容到输出向量
|
|
||||||
packetData.insert(packetData.end(),
|
|
||||||
packet->data,
|
|
||||||
packet->data + packet->size);
|
|
||||||
av_packet_unref(packet.get());
|
|
||||||
}
|
|
||||||
return packetData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool init() {
|
|
||||||
// 1. 创建硬件设备上下文
|
|
||||||
AVBufferRef* hw_device_ctx = nullptr;
|
|
||||||
int ret = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI,
|
|
||||||
deviceName_.c_str(), nullptr, 0);
|
|
||||||
if (ret < 0) {
|
|
||||||
char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0};
|
|
||||||
av_strerror(ret, errbuf, sizeof(errbuf));
|
|
||||||
CMVR_LOG(ERROR) << "Failed to create VAAPI device context: " << errbuf;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
hw_device_ctx_.reset(hw_device_ctx);
|
|
||||||
|
|
||||||
// 2. 查找编码器
|
|
||||||
const AVCodec* codec = avcodec_find_encoder_by_name("hevc_vaapi");
|
|
||||||
if (!codec) {
|
|
||||||
CMVR_LOG(ERROR) << "HEVC VAAPI encoder not found";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 设置编码器上下文
|
|
||||||
codec_ctx_.reset(avcodec_alloc_context3(codec));
|
|
||||||
codec_ctx_->width = width_;
|
|
||||||
codec_ctx_->height = height_;
|
|
||||||
codec_ctx_->time_base = {1, fps_};
|
|
||||||
codec_ctx_->pix_fmt = AV_PIX_FMT_VAAPI;
|
|
||||||
codec_ctx_->bit_rate = bitrate_;
|
|
||||||
codec_ctx_->gop_size = 1;
|
|
||||||
codec_ctx_->hw_device_ctx = av_buffer_ref(hw_device_ctx_.get());
|
|
||||||
|
|
||||||
// 4. 创建硬件帧上下文
|
|
||||||
hw_frames_ctx_.reset(av_hwframe_ctx_alloc(hw_device_ctx_.get()));
|
|
||||||
if (!hw_frames_ctx_) {
|
|
||||||
CMVR_LOG(ERROR) << "Failed to allocate hardware frame context";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AVHWFramesContext* frames_ctx = (AVHWFramesContext*)hw_frames_ctx_->data;
|
|
||||||
frames_ctx->format = AV_PIX_FMT_VAAPI;
|
|
||||||
frames_ctx->sw_format = AV_PIX_FMT_NV12;
|
|
||||||
frames_ctx->width = width_;
|
|
||||||
frames_ctx->height = height_;
|
|
||||||
frames_ctx->initial_pool_size = 20;
|
|
||||||
|
|
||||||
if (av_hwframe_ctx_init(hw_frames_ctx_.get()) < 0) {
|
|
||||||
CMVR_LOG(ERROR) << "Failed to initialize hardware frame context";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
codec_ctx_->hw_frames_ctx = av_buffer_ref(hw_frames_ctx_.get());
|
|
||||||
|
|
||||||
// 5. 打开编码器
|
|
||||||
if (avcodec_open2(codec_ctx_.get(), codec, nullptr) < 0) {
|
|
||||||
CMVR_LOG(ERROR) << "Cannot open encoder";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. 初始化硬件帧
|
|
||||||
frame_.reset(av_frame_alloc());
|
|
||||||
frame_->format = AV_PIX_FMT_VAAPI;
|
|
||||||
frame_->width = width_;
|
|
||||||
frame_->height = height_;
|
|
||||||
|
|
||||||
if (av_hwframe_get_buffer(hw_frames_ctx_.get(), frame_.get(), 0) < 0) {
|
|
||||||
CMVR_LOG(ERROR) << "Could not allocate hardware frame data";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. 初始化软件帧
|
|
||||||
sw_frame_.reset(av_frame_alloc());
|
|
||||||
sw_frame_->width = width_;
|
|
||||||
sw_frame_->height = height_;
|
|
||||||
sw_frame_->format = (imageType_ == ImageType::DEPTH) ? AV_PIX_FMT_GRAY8 : AV_PIX_FMT_NV12;
|
|
||||||
|
|
||||||
if (av_frame_get_buffer(sw_frame_.get(), 0) < 0) {
|
|
||||||
CMVR_LOG(ERROR) << "Could not allocate software frame buffer";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool prepareFrame(const cv::Mat& image) {
|
|
||||||
if (imageType_ == ImageType::DEPTH) {
|
|
||||||
return prepareDepthFrame(image);
|
|
||||||
} else {
|
|
||||||
return prepareColorOrGrayscaleFrame(image);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool prepareDepthFrame(const cv::Mat& depthImage) {
|
|
||||||
cv::Mat processedDepth;
|
|
||||||
|
|
||||||
if (depthImage.channels() != 1) {
|
|
||||||
CMVR_LOG(ERROR) << "Depth image must be single-channel";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (depthImage.depth() != CV_8U) {
|
|
||||||
if (depthImage.depth() == CV_16U) {
|
|
||||||
depthImage.convertTo(processedDepth, CV_8U, 255.0 / 65535.0);
|
|
||||||
} else {
|
|
||||||
CMVR_LOG(ERROR) << "Unsupported depth image format";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
processedDepth = depthImage.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确保图像尺寸匹配
|
|
||||||
if (processedDepth.cols != width_ || processedDepth.rows != height_) {
|
|
||||||
CMVR_LOG(ERROR) << "Depth image size mismatch";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置软件帧属性
|
|
||||||
sw_frame_->pts = frame_counter_++;
|
|
||||||
sw_frame_->format = AV_PIX_FMT_GRAY8;
|
|
||||||
av_frame_make_writable(sw_frame_.get());
|
|
||||||
|
|
||||||
// 使用av_image_fill_arrays处理行对齐
|
|
||||||
av_image_fill_arrays(sw_frame_->data, sw_frame_->linesize,
|
|
||||||
processedDepth.data,
|
|
||||||
AV_PIX_FMT_GRAY8,
|
|
||||||
width_, height_, 1);
|
|
||||||
|
|
||||||
// 上传到硬件帧
|
|
||||||
if (av_hwframe_transfer_data(frame_.get(), sw_frame_.get(), 0) < 0) {
|
|
||||||
CMVR_LOG(ERROR) << "Error transferring depth data to hardware frame";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame_->pts = sw_frame_->pts;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool prepareColorOrGrayscaleFrame(const cv::Mat& image) {
|
|
||||||
if (image.cols != width_ || image.rows != height_) {
|
|
||||||
CMVR_LOG(ERROR) << "Image size mismatch: " << image.cols << "x" << image.rows
|
|
||||||
<< " vs " << width_ << "x" << height_;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. 确定输入图像的像素格式(OpenCV的Mat格式)
|
|
||||||
AVPixelFormat src_pix_fmt;
|
|
||||||
if (image.channels() == 3) {
|
|
||||||
src_pix_fmt = AV_PIX_FMT_BGR24; // OpenCV默认是BGR格式(3通道)
|
|
||||||
} else if (image.channels() == 1) {
|
|
||||||
src_pix_fmt = AV_PIX_FMT_GRAY8; // 灰度图(1通道)
|
|
||||||
} else {
|
|
||||||
CMVR_LOG(ERROR) << "Unsupported channel count: " << image.channels();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 初始化格式转换上下文(swscale)
|
|
||||||
SwsContext* sws_ctx = sws_getContext(
|
|
||||||
width_, height_, src_pix_fmt, // 源宽高和格式
|
|
||||||
width_, height_, AV_PIX_FMT_NV12, // 目标宽高和格式(NV12)
|
|
||||||
SWS_BILINEAR, // 缩放算法(可根据需求调整)
|
|
||||||
nullptr, nullptr, nullptr
|
|
||||||
);
|
|
||||||
if (!sws_ctx) {
|
|
||||||
CMVR_LOG(ERROR) << "Failed to create sws context";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 配置源数据(OpenCV的Mat)
|
|
||||||
uint8_t* src_data[4] = {nullptr};
|
|
||||||
int src_linesize[4] = {0};
|
|
||||||
if (image.channels() == 3) {
|
|
||||||
src_data[0] = image.data; // BGR数据起始地址
|
|
||||||
src_linesize[0] = image.step; // 每行字节数(含OpenCV的对齐填充)
|
|
||||||
} else {
|
|
||||||
src_data[0] = image.data; // 灰度数据起始地址
|
|
||||||
src_linesize[0] = image.step; // 灰度图每行字节数
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. 配置目标数据(NV12格式的AVFrame)
|
|
||||||
av_frame_make_writable(sw_frame_.get());
|
|
||||||
sw_frame_->format = AV_PIX_FMT_NV12;
|
|
||||||
sw_frame_->width = width_;
|
|
||||||
sw_frame_->height = height_;
|
|
||||||
|
|
||||||
// 5. 执行格式转换(BGR/GRAY -> NV12)
|
|
||||||
int ret = sws_scale(
|
|
||||||
sws_ctx,
|
|
||||||
src_data, // 源图像数据
|
|
||||||
src_linesize, // 源图像每行字节数
|
|
||||||
0, // 起始行
|
|
||||||
height_, // 转换的行数
|
|
||||||
sw_frame_->data, // 目标图像数据(sw_frame_的data指针)
|
|
||||||
sw_frame_->linesize // 目标图像每行字节数
|
|
||||||
);
|
|
||||||
if (ret <= 0) {
|
|
||||||
CMVR_LOG(ERROR) << "sws_scale failed, ret=" << ret;
|
|
||||||
sws_freeContext(sws_ctx);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. 释放转换上下文
|
|
||||||
sws_freeContext(sws_ctx);
|
|
||||||
|
|
||||||
// 7. 设置帧属性并上传到硬件
|
|
||||||
sw_frame_->pts = frame_counter_++;
|
|
||||||
if (av_hwframe_transfer_data(frame_.get(), sw_frame_.get(), 0) < 0) {
|
|
||||||
CMVR_LOG(ERROR) << "Error transferring data to hardware frame";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
frame_->pts = sw_frame_->pts;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
int width_;
|
|
||||||
int height_;
|
|
||||||
int fps_;
|
|
||||||
int bitrate_;
|
|
||||||
int frame_counter_ = 0;
|
|
||||||
std::string deviceName_;
|
|
||||||
ImageType imageType_;
|
|
||||||
bool initialized_{false};
|
|
||||||
|
|
||||||
FFmpegPtr<AVCodecContext> codec_ctx_;
|
|
||||||
FFmpegPtr<AVFrame> frame_;
|
|
||||||
FFmpegPtr<AVFrame> sw_frame_;
|
|
||||||
FFmpegPtr<AVBufferRef> hw_device_ctx_;
|
|
||||||
FFmpegPtr<AVBufferRef> hw_frames_ctx_;
|
|
||||||
};
|
|
||||||
} // namespace ffmpeg
|
} // namespace ffmpeg
|
||||||
|
|
||||||
#endif //FFMPEG_PTR_H
|
#endif //FFMPEG_PTR_H
|
||||||
|
|||||||
@ -3,10 +3,14 @@
|
|||||||
#ifndef CMVR_ES_FFMPEG_SPEAKER_H
|
#ifndef CMVR_ES_FFMPEG_SPEAKER_H
|
||||||
#define CMVR_ES_FFMPEG_SPEAKER_H
|
#define CMVR_ES_FFMPEG_SPEAKER_H
|
||||||
|
|
||||||
#include <thread>
|
#include <atomic>
|
||||||
#include <mutex>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
#include "speaker/abstract_speaker.h"
|
#include "speaker/abstract_speaker.h"
|
||||||
#include <boost/lockfree/spsc_queue.hpp>
|
#include <boost/lockfree/spsc_queue.hpp>
|
||||||
#include <pulse/simple.h>
|
#include <pulse/simple.h>
|
||||||
@ -48,6 +52,7 @@ namespace cmvr::device {
|
|||||||
bool decodeStreamFrame_(const AudioStreamFrameData& frame_data);
|
bool decodeStreamFrame_(const AudioStreamFrameData& frame_data);
|
||||||
bool initStreamDecoder_(const AudioStreamFrameData& frame_data);
|
bool initStreamDecoder_(const AudioStreamFrameData& frame_data);
|
||||||
void releaseStreamDecoder_();
|
void releaseStreamDecoder_();
|
||||||
|
void releaseStreamDecoderUnlocked_();
|
||||||
|
|
||||||
pa_simple* pulse_simple_ = nullptr; // 修改:PulseAudio 简单 API 句柄
|
pa_simple* pulse_simple_ = nullptr; // 修改:PulseAudio 简单 API 句柄
|
||||||
pa_sample_spec sample_spec_{}; // 新增:PulseAudio 采样规格
|
pa_sample_spec sample_spec_{}; // 新增:PulseAudio 采样规格
|
||||||
@ -59,6 +64,7 @@ namespace cmvr::device {
|
|||||||
std::shared_ptr<std::thread> decode_thread_;
|
std::shared_ptr<std::thread> decode_thread_;
|
||||||
std::shared_ptr<std::thread> play_thread_;
|
std::shared_ptr<std::thread> play_thread_;
|
||||||
std::mutex mtx_;
|
std::mutex mtx_;
|
||||||
|
std::mutex stop_mtx_;
|
||||||
std::string audio_path_;
|
std::string audio_path_;
|
||||||
|
|
||||||
// Boost 单生产者单消费者无锁队列
|
// Boost 单生产者单消费者无锁队列
|
||||||
@ -68,6 +74,7 @@ namespace cmvr::device {
|
|||||||
mutable std::mutex mtx_pause_;
|
mutable std::mutex mtx_pause_;
|
||||||
std::condition_variable cv_pause_;
|
std::condition_variable cv_pause_;
|
||||||
std::atomic<bool> is_paused_{false};
|
std::atomic<bool> is_paused_{false};
|
||||||
|
std::atomic<bool> is_stopping_{false};
|
||||||
|
|
||||||
// 音频时钟同步
|
// 音频时钟同步
|
||||||
std::atomic<int64_t> audio_pts_{0};
|
std::atomic<int64_t> audio_pts_{0};
|
||||||
|
|||||||
@ -31,6 +31,7 @@ ffmpegSpeaker::ffmpegSpeaker(const config::FFMpegSpeakerConfig& cfg):config_(cfg
|
|||||||
|
|
||||||
|
|
||||||
ffmpegSpeaker::~ffmpegSpeaker() {
|
ffmpegSpeaker::~ffmpegSpeaker() {
|
||||||
|
is_stopping_ = true;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mtx_);
|
std::lock_guard<std::mutex> lock(mtx_);
|
||||||
state_.is_running = false;
|
state_.is_running = false;
|
||||||
@ -101,13 +102,17 @@ void ffmpegSpeaker::resetPlayState()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ffmpegSpeaker::stop() {
|
bool ffmpegSpeaker::stop() {
|
||||||
|
std::lock_guard<std::mutex> stop_lock(stop_mtx_);
|
||||||
|
is_stopping_ = true;
|
||||||
|
|
||||||
{
|
{
|
||||||
lock_guard lock(mtx_);
|
lock_guard lock(mtx_);
|
||||||
state_.is_running = false;
|
state_.is_running = false;
|
||||||
state_.is_decoding = false;
|
state_.is_decoding = false;
|
||||||
state_.is_paused = false;
|
state_.is_paused = false;
|
||||||
|
is_streaming_input_ = false;
|
||||||
}
|
}
|
||||||
|
cv_pause_.notify_all();
|
||||||
// 等待线程结束
|
// 等待线程结束
|
||||||
if (decode_thread_ && decode_thread_->joinable()) {
|
if (decode_thread_ && decode_thread_->joinable()) {
|
||||||
decode_thread_->join();
|
decode_thread_->join();
|
||||||
@ -120,6 +125,7 @@ bool ffmpegSpeaker::stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resetPlayState();
|
resetPlayState();
|
||||||
|
is_stopping_ = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,6 +546,10 @@ void ffmpegSpeaker::play_audio_() {
|
|||||||
|
|
||||||
bool ffmpegSpeaker::pushAudioFrame(const AudioStreamFrameData& frame_data)
|
bool ffmpegSpeaker::pushAudioFrame(const AudioStreamFrameData& frame_data)
|
||||||
{
|
{
|
||||||
|
if (is_stopping_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (frame_data.data.empty()) {
|
if (frame_data.data.empty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -577,9 +587,12 @@ bool ffmpegSpeaker::pushAudioFrame(const AudioStreamFrameData& frame_data)
|
|||||||
|
|
||||||
void ffmpegSpeaker::stopStreaming()
|
void ffmpegSpeaker::stopStreaming()
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> stop_lock(stop_mtx_);
|
||||||
|
is_stopping_ = true;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mtx_);
|
std::lock_guard<std::mutex> lock(mtx_);
|
||||||
if (!is_streaming_input_) {
|
if (!is_streaming_input_) {
|
||||||
|
is_stopping_ = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state_.is_decoding = false;
|
state_.is_decoding = false;
|
||||||
@ -595,6 +608,7 @@ void ffmpegSpeaker::stopStreaming()
|
|||||||
}
|
}
|
||||||
|
|
||||||
resetPlayState();
|
resetPlayState();
|
||||||
|
is_stopping_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ffmpegSpeaker::startStreamingPlayback_(const AudioStreamFrameData& frame_data)
|
bool ffmpegSpeaker::startStreamingPlayback_(const AudioStreamFrameData& frame_data)
|
||||||
@ -653,13 +667,18 @@ bool ffmpegSpeaker::pushPcmFrame_(const AudioStreamFrameData& frame_data)
|
|||||||
sample = static_cast<int16_t>(std::clamp(scaled, -32768.f, 32767.f));
|
sample = static_cast<int16_t>(std::clamp(scaled, -32768.f, 32767.f));
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!audio_queue_.push(buffer)) {
|
while (true) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
{
|
||||||
std::lock_guard<std::mutex> lock(mtx_);
|
std::lock_guard<std::mutex> lock(mtx_);
|
||||||
if (!state_.is_running) {
|
if (!state_.is_running || is_stopping_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (audio_queue_.push(buffer)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -670,7 +689,7 @@ bool ffmpegSpeaker::decodeStreamFrame_(const AudioStreamFrameData& frame_data)
|
|||||||
stream_codec_ != frame_data.codec ||
|
stream_codec_ != frame_data.codec ||
|
||||||
stream_input_sample_rate_ != frame_data.sample_rate ||
|
stream_input_sample_rate_ != frame_data.sample_rate ||
|
||||||
stream_input_channels_ != frame_data.channels) {
|
stream_input_channels_ != frame_data.channels) {
|
||||||
releaseStreamDecoder_();
|
releaseStreamDecoderUnlocked_();
|
||||||
if (!initStreamDecoder_(frame_data)) {
|
if (!initStreamDecoder_(frame_data)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -738,13 +757,18 @@ bool ffmpegSpeaker::decodeStreamFrame_(const AudioStreamFrameData& frame_data)
|
|||||||
sample = static_cast<int16_t>(std::clamp(scaled, -32768.f, 32767.f));
|
sample = static_cast<int16_t>(std::clamp(scaled, -32768.f, 32767.f));
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!audio_queue_.push(buffer)) {
|
while (true) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
{
|
||||||
std::lock_guard<std::mutex> lock(mtx_);
|
std::lock_guard<std::mutex> lock(mtx_);
|
||||||
if (!state_.is_running) {
|
if (!state_.is_running || is_stopping_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (audio_queue_.push(buffer)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret == AVERROR(EAGAIN) || ret == AVERROR_EOF;
|
return ret == AVERROR(EAGAIN) || ret == AVERROR_EOF;
|
||||||
@ -782,14 +806,14 @@ bool ffmpegSpeaker::initStreamDecoder_(const AudioStreamFrameData& frame_data)
|
|||||||
stream_decoder_ctx_->channels == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO;
|
stream_decoder_ctx_->channels == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO;
|
||||||
|
|
||||||
if (avcodec_open2(stream_decoder_ctx_, decoder, nullptr) < 0) {
|
if (avcodec_open2(stream_decoder_ctx_, decoder, nullptr) < 0) {
|
||||||
releaseStreamDecoder_();
|
releaseStreamDecoderUnlocked_();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream_decode_frame_ = av_frame_alloc();
|
stream_decode_frame_ = av_frame_alloc();
|
||||||
stream_decode_packet_ = av_packet_alloc();
|
stream_decode_packet_ = av_packet_alloc();
|
||||||
if (!stream_decode_frame_ || !stream_decode_packet_) {
|
if (!stream_decode_frame_ || !stream_decode_packet_) {
|
||||||
releaseStreamDecoder_();
|
releaseStreamDecoderUnlocked_();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -800,6 +824,12 @@ bool ffmpegSpeaker::initStreamDecoder_(const AudioStreamFrameData& frame_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ffmpegSpeaker::releaseStreamDecoder_()
|
void ffmpegSpeaker::releaseStreamDecoder_()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> decode_lock(stream_decode_mtx_);
|
||||||
|
releaseStreamDecoderUnlocked_();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ffmpegSpeaker::releaseStreamDecoderUnlocked_()
|
||||||
{
|
{
|
||||||
if (stream_swr_ctx_) {
|
if (stream_swr_ctx_) {
|
||||||
swr_free(&stream_swr_ctx_);
|
swr_free(&stream_swr_ctx_);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user