struct AudioBuffer{ char* data; ...
Created on: November 25, 2025
Answered using GPT-5.1 Thinking by Chat01
Created on: November 25, 2025
Answered using GPT-5.1 Thinking by Chat01
textstruct AudioBuffer{ char* data; int size; }; std::queue<AudioBuffer> audio_queue_hdmi, audio_queue_rgb;void std::condition_variable m_cond_hdmi, m_cond_rgb;RkMediaScreenSource2::RkMediaScreenSourcePriv2::OnDecAudioPacket(char* data,int size){ //qDebug() << QString("OnDecAudioPacket --- %1 - %2").arg(GetId()).arg(GetChnId()); if(GetId() == "1"){ PushAudioToHdmi(data, size); }else if(GetId() == "2"){ PushAudioToRgb(data, size); }else{ qDebug() << QString("--- no need to push ao"); }
}
void RkMediaScreenSource2::RkMediaScreenSourcePriv2::sendAoStreamThread(int id, int chn_id) {
qDebug() << QString("111111111111 sendAoStreamThread");
do {
auto self = shared_from_this();
std::thread th([this, self, id, chn_id] {
sendAoStream(id, chn_id);
});
th.detach();
} while (0);
}
void RkMediaScreenSource2::RkMediaScreenSourcePriv2::sendAo2StreamThread(int id, int chn_id) {
qDebug() << QString("111111111111 sendAo2StreamThread");
do {
auto self = shared_from_this();
std::thread th([this, self, id, chn_id] {
sendAo2Stream(id, chn_id);
});
th.detach();
} while (0);
}
int RkMediaScreenSource2::RkMediaScreenSourcePriv2::sendAoStream(int id, int chn_id){
RK_S32 result = RK_SUCCESS;
AUDIO_FRAME_S frame;
RK_U64 timeStamp = 0;
RK_S32 s32MilliSec = -1;
textint devId = id, chnIndex = chn_id; RK_U8 *srcData = RK_NULL; AudioBuffer buffer{0}; AO_CHN_STATE_S pstStatus; while (!m_close) { auto t1 = std::chrono::steady_clock::now(); { std::unique_lock<std::mutex> lk(m_mtx); m_cond_hdmi.wait(lk, [this] { return !audio_queue_hdmi.empty(); }); if (!audio_queue_hdmi.empty()) { buffer = audio_queue_hdmi.front(); audio_queue_hdmi.pop(); m_cond_hdmi.notify_one(); } } srcData = reinterpret_cast<RK_U8*>(calloc(buffer.size, sizeof(RK_U8))); std::memcpy(srcData, buffer.data, buffer.size); frame.u32Len = buffer.size; frame.u64TimeStamp = timeStamp++; frame.enBitWidth = AUDIO_BIT_WIDTH_16; frame.enSoundMode = AUDIO_SOUND_MODE_STEREO; frame.bBypassMbBlk = RK_FALSE; MB_EXT_CONFIG_S extConfig; memset(&extConfig, 0, sizeof(extConfig)); //extConfig.pFreeCB = freeBuffFunc; extConfig.pOpaque = srcData; extConfig.pu8VirAddr = srcData; extConfig.u64Size = buffer.size; RK_MPI_SYS_CreateMB(&(frame.pMbBlk), &extConfig); __RETRY: result = RK_MPI_AO_SendFrame(devId, chnIndex, &frame, s32MilliSec); if (result != RK_SUCCESS) { qWarning() << QString("send frame fail, result = %1, TimeStamp = %2, s32MilliSec = %3").arg(result, 0, 16).arg(frame.u64TimeStamp).arg(s32MilliSec); goto __RETRY; } RK_MPI_MB_ReleaseMB(frame.pMbBlk); // 释放内存 freeBuffFunc(srcData); auto t2 = std::chrono::steady_clock::now(); qDebug() << QString("PushAudioToRgb elapsed time:%1 ms.").arg(Util::DurationMs(t1, t2)); } RK_MPI_AO_WaitEos(devId, chnIndex, s32MilliSec); return result;
}
int RkMediaScreenSource2::RkMediaScreenSourcePriv2::sendAo2Stream(int id, int chn_id){
RK_S32 result = RK_SUCCESS;
AUDIO_FRAME_S frame;
RK_U64 timeStamp = 0;
RK_S32 s32MilliSec = -1;
textint devId = id, chnIndex = chn_id; RK_U8 *srcData = RK_NULL; AudioBuffer buffer{0}; AO_CHN_STATE_S pstStatus; while (!m_close) { auto t1 = std::chrono::steady_clock::now(); { std::unique_lock<std::mutex> lk(m_mtx); m_cond_rgb.wait(lk, [this] { return !audio_queue_rgb.empty(); }); if (!audio_queue_rgb.empty()) { buffer = audio_queue_rgb.front(); audio_queue_rgb.pop(); m_cond_rgb.notify_one(); } } srcData = reinterpret_cast<RK_U8*>(calloc(buffer.size, sizeof(RK_U8))); std::memcpy(srcData, buffer.data, buffer.size); frame.u32Len = buffer.size; frame.u64TimeStamp = timeStamp++; frame.enBitWidth = AUDIO_BIT_WIDTH_16; frame.enSoundMode = AUDIO_SOUND_MODE_STEREO; frame.bBypassMbBlk = RK_FALSE; MB_EXT_CONFIG_S extConfig; memset(&extConfig, 0, sizeof(extConfig));
extConfig.pOpaque = srcData;
extConfig.pu8VirAddr = srcData;
extConfig.u64Size = buffer.size;
RK_MPI_SYS_CreateMB(&(frame.pMbBlk), &extConfig);
__RETRY:
result = RK_MPI_AO_SendFrame(devId, chnIndex, &frame, s32MilliSec);
if (result != RK_SUCCESS) {
qWarning() << QString("send frame fail, result = %1, TimeStamp = %2, s32MilliSec = %3").arg(result, 0, 16).arg(frame.u64TimeStamp).arg(s32MilliSec);
goto __RETRY;
}
RK_MPI_MB_ReleaseMB(frame.pMbBlk);
// 释放内存
freeBuffFunc(srcData);
auto t2 = std::chrono::steady_clock::now();
qDebug() << QString("PushAudioToRgb elapsed time:%1 ms.").arg(Util::DurationMs(t1, t2));
}
RK_MPI_AO_WaitEos(devId, chnIndex, s32MilliSec);
return result;
}
OnDecAudioPacket这个函数是我的音频回调数据,data已经是处理完成的音频数据,sendAoStreamThread和sendAo2StreamThread是我的两路消费者线程,任务时将回调的音频数据写入,改如何修改设计这部分完整代码,给出你设计和修改的全部代码
先给你说结论:你现在这段代码里有几个比较危险/不太合理的点:
AudioBuffer 只是一个裸指针 + size,很容易出现悬空指针/重复释放;audio_queue_hdmi / rgb 是全局队列,和类设计不匹配,也不好管理生命周期;m_cond_xxx.wait() 没有考虑 m_close,退出条件不优雅;pop() 后再 notify_one() 没意义(目前也没有“队满”的逻辑);goto __RETRY 可能死循环,完全不退让 CPU。下面我给你一份可以直接套用的完整设计,把这块做成标准的生产者/消费者模型,并且用 RAII 管好内存。你只需要把原来相关的实现换成下面的代码,并在头文件里加上对应成员即可。
假设你有一个内部类:
cpp// RkMediaScreenSource2.h 里,简化示意 class RkMediaScreenSource2 { public: class RkMediaScreenSourcePriv2 : public std::enable_shared_from_this<RkMediaScreenSourcePriv2> { public: // 回调入口 void OnDecAudioPacket(char* data, int size); // 启动两路 AO 发送线程(HDMI / RGB) void sendAoStreamThread(int id, int chn_id); // HDMI void sendAo2StreamThread(int id, int chn_id); // RGB // 关闭 void Stop(); // 析构里也可以调用 private: // 回调里调用的生产者接口 void PushAudioToHdmi(const char* data, int size); void PushAudioToRgb(const char* data, int size); // 具体消费者 int sendAoStream(int id, int chn_id); // HDMI int sendAo2Stream(int id, int chn_id); // RGB // 通用发送实现(内部复用) int sendAoStreamImpl(int devId, int chnIndex, std::queue<std::vector<uint8_t>>& q, std::condition_variable& cv, const char* logTag); private: // ===== 音频队列相关成员 ===== // 使用 vector<uint8_t> 管理 PCM 数据,防止内存泄漏/悬空指针 std::mutex m_mtx; // 一个锁同时保护两路队列(简单直接) std::condition_variable m_cond_hdmi; std::condition_variable m_cond_rgb; std::queue<std::vector<uint8_t>> m_queue_hdmi; std::queue<std::vector<uint8_t>> m_queue_rgb; std::atomic<bool> m_close{false}; // 队列长度上限,防止堆积撑爆内存 static constexpr size_t kMaxAudioQueueSize = 128; // 你原来就有的成员(示意) // std::string GetId() const; }; };
关键点:
- 不再用
struct AudioBuffer{ char* data; int size; },而是直接用std::vector<uint8_t>;- 队列和条件变量全部放进类里,去掉全局变量。
cpp#include <queue> #include <vector> #include <mutex> #include <condition_variable> #include <thread> #include <atomic> #include <chrono> #include <cstring> // Rockchip / Qt 相关头自己包含 // #include "rkmedia_screen_source2.h" // #include "rk_mpi_ao.h" // #include "rk_mpi_mb.h" // #include <QDebug> // 假定你已有:void freeBuffFunc(void* p); // 假定你已有:double Util::DurationMs(const std::chrono::steady_clock::time_point&, // const std::chrono::steady_clock::time_point&); // ====================== 回调:生产者 ====================== void RkMediaScreenSource2::RkMediaScreenSourcePriv2::OnDecAudioPacket(char* data, int size) { // data 是解码好的 PCM,这里立即拷贝一份进队列,避免回调返回后指针失效 if (!data || size <= 0) { return; } // qDebug() << QString("OnDecAudioPacket --- id=%1 ch=%2").arg(GetId()).arg(GetChnId()); if (GetId() == "1") { // HDMI 路 PushAudioToHdmi(data, size); } else if (GetId() == "2") { // RGB 路 PushAudioToRgb(data, size); } else { qDebug() << QString("--- no need to push ao, id=%1").arg(GetId()); } } void RkMediaScreenSource2::RkMediaScreenSourcePriv2::PushAudioToHdmi(const char* data, int size) { if (m_close.load(std::memory_order_acquire)) { return; } std::vector<uint8_t> buf(size); std::memcpy(buf.data(), data, static_cast<size_t>(size)); { std::lock_guard<std::mutex> lk(m_mtx); // 简单限长策略:队列满了丢掉最老一帧,防止堆积 if (m_queue_hdmi.size() >= kMaxAudioQueueSize) { m_queue_hdmi.pop(); } m_queue_hdmi.push(std::move(buf)); } m_cond_hdmi.notify_one(); } void RkMediaScreenSource2::RkMediaScreenSourcePriv2::PushAudioToRgb(const char* data, int size) { if (m_close.load(std::memory_order_acquire)) { return; } std::vector<uint8_t> buf(size); std::memcpy(buf.data(), data, static_cast<size_t>(size)); { std::lock_guard<std::mutex> lk(m_mtx); if (m_queue_rgb.size() >= kMaxAudioQueueSize) { m_queue_rgb.pop(); } m_queue_rgb.push(std::move(buf)); } m_cond_rgb.notify_one(); } // ====================== 启动消费者线程 ====================== void RkMediaScreenSource2::RkMediaScreenSourcePriv2::sendAoStreamThread(int id, int chn_id) { qDebug() << QString("sendAoStreamThread (HDMI) start. dev=%1 chn=%2") .arg(id).arg(chn_id); auto self = shared_from_this(); std::thread th([this, self, id, chn_id]() { sendAoStream(id, chn_id); }); th.detach(); } void RkMediaScreenSource2::RkMediaScreenSourcePriv2::sendAo2StreamThread(int id, int chn_id) { qDebug() << QString("sendAo2StreamThread (RGB) start. dev=%1 chn=%2") .arg(id).arg(chn_id); auto self = shared_from_this(); std::thread th([this, self, id, chn_id]() { sendAo2Stream(id, chn_id); }); th.detach(); } // ====================== 通用发送实现 ====================== int RkMediaScreenSource2::RkMediaScreenSourcePriv2::sendAoStreamImpl( int devId, int chnIndex, std::queue<std::vector<uint8_t>>& q, std::condition_variable& cv, const char* logTag) { RK_S32 result = RK_SUCCESS; AUDIO_FRAME_S frame; std::memset(&frame, 0, sizeof(frame)); RK_U64 timeStamp = 0; RK_S32 s32MilliSec = -1; RK_U8* srcData = RK_NULL; while (!m_close.load(std::memory_order_acquire)) { // 1. 从队列取一帧 std::vector<uint8_t> buffer; { std::unique_lock<std::mutex> lk(m_mtx); cv.wait(lk, [this, &q]() { return m_close.load(std::memory_order_acquire) || !q.empty(); }); if (m_close.load(std::memory_order_acquire) && q.empty()) { break; // 收到关闭且没有数据,安全退出 } buffer = std::move(q.front()); q.pop(); } if (buffer.empty()) { continue; } auto t1 = std::chrono::steady_clock::now(); // 2. 为 RK_MPI_AO 准备 MB 数据 srcData = reinterpret_cast<RK_U8*>(calloc(buffer.size(), sizeof(RK_U8))); if (!srcData) { qWarning() << QString("[%1] calloc audio buffer failed, size=%2") .arg(logTag) .arg(buffer.size()); continue; } std::memcpy(srcData, buffer.data(), buffer.size()); std::memset(&frame, 0, sizeof(frame)); frame.u32Len = static_cast<RK_U32>(buffer.size()); frame.u64TimeStamp = timeStamp++; frame.enBitWidth = AUDIO_BIT_WIDTH_16; frame.enSoundMode = AUDIO_SOUND_MODE_STEREO; frame.bBypassMbBlk = RK_FALSE; MB_EXT_CONFIG_S extConfig; std::memset(&extConfig, 0, sizeof(extConfig)); // extConfig.pFreeCB = freeBuffFunc; // 如果你希望 RK 在 MB 释放时帮你 free,可以配置 extConfig.pOpaque = srcData; extConfig.pu8VirAddr = srcData; extConfig.u64Size = buffer.size(); RK_MPI_SYS_CreateMB(&(frame.pMbBlk), &extConfig); // 3. 发送,失败时稍微重试一下,避免死循环 const int kMaxRetry = 5; int retry = 0; do { result = RK_MPI_AO_SendFrame(devId, chnIndex, &frame, s32MilliSec); if (result == RK_SUCCESS) { break; } qWarning() << QString("[%1] send frame fail, result = 0x%2, TimeStamp = %3, s32MilliSec = %4, retry=%5") .arg(logTag) .arg(result, 0, 16) .arg(frame.u64TimeStamp) .arg(s32MilliSec) .arg(retry); if (m_close.load(std::memory_order_acquire)) { break; } std::this_thread::sleep_for(std::chrono::milliseconds(2)); } while (++retry < kMaxRetry && !m_close.load(std::memory_order_acquire)); RK_MPI_MB_ReleaseMB(frame.pMbBlk); freeBuffFunc(srcData); // 这里只释放我们自己 calloc 的缓冲 auto t2 = std::chrono::steady_clock::now(); qDebug() << QString("[%1] send audio elapsed time: %2 ms.") .arg(logTag) .arg(Util::DurationMs(t1, t2)); } RK_MPI_AO_WaitEos(devId, chnIndex, s32MilliSec); return result; } // ====================== HDMI / RGB 两个具体实现 ====================== int RkMediaScreenSource2::RkMediaScreenSourcePriv2::sendAoStream(int id, int chn_id) { return sendAoStreamImpl(id, chn_id, m_queue_hdmi, m_cond_hdmi, "HDMI"); } int RkMediaScreenSource2::RkMediaScreenSourcePriv2::sendAo2Stream(int id, int chn_id) { return sendAoStreamImpl(id, chn_id, m_queue_rgb, m_cond_rgb, "RGB"); } // ====================== 停止/析构 ====================== void RkMediaScreenSource2::RkMediaScreenSourcePriv2::Stop() { bool expected = false; if (m_close.compare_exchange_strong(expected, true, std::memory_order_acq_rel)) { // 只在第一次调用 Stop 时唤醒 m_cond_hdmi.notify_all(); m_cond_rgb.notify_all(); } } // 析构里记得调用 // RkMediaScreenSource2::RkMediaScreenSourcePriv2::~RkMediaScreenSourcePriv2() // { // Stop(); // }
数据所有权明确
AudioBuffer{ char* data; int size; } 很容易搞不清 data 是谁 malloc、谁 free。std::vector<uint8_t>,由 C++ 自动管理内存,不会悬空/重复释放。回调里只做“拷贝 + 入队 + notify”
消费者线程统一走 sendAoStreamImpl
条件变量退出逻辑正确
wait() 的谓词检查:m_close || !q.empty(),并且在 m_close && q.empty 时 break,避免线程无法退出。避免无限死循环重试
kMaxRetry 和一点 sleep,防止 RK_MPI_AO_SendFrame 一直失败时 CPU 空转。如果你愿意,我还可以再帮你把这套设计抽成一个通用的 AudioQueue 小类,这样 HDMI / RGB / 以后新增的路数都可以直接复用。