当前位置: 首页 > news >正文

【MediaFoundation】OpenCV VideoCapture 读取音频源码

OpenCV 读取音频代码实例

在windows7 以及OpenCV4 过后可以使用 CAP_MSMF 读取音频,但是OpenCV没有播放音频的API。代码示例如下。 本文解析OpenCVCAP_MSMF 进行文件、设备的 音频读取,学习MediaFoundation 的使用。

#include <opencv2/core.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;int main(int argc, const char** argv)
{Mat videoFrame;Mat audioFrame;vector<vector<Mat>> audioData;VideoCapture cap;vector<int> params {    CAP_PROP_AUDIO_STREAM, 0,CAP_PROP_VIDEO_STREAM, -1,CAP_PROP_AUDIO_DATA_DEPTH, CV_32F   };//cap.open(file, CAP_MSMF, params);// 打开第一个音频输入设备cap.open(0, CAP_MSMF, params);if (!cap.isOpened()){cerr << "ERROR! Can't to open file: " + file << endl;return -1;}const int audioBaseIndex = (int)cap.get(CAP_PROP_AUDIO_BASE_INDEX);const int numberOfChannels = (int)cap.get(CAP_PROP_AUDIO_TOTAL_CHANNELS);cout << "CAP_PROP_AUDIO_DATA_DEPTH: " << depthToString((int)cap.get(CAP_PROP_AUDIO_DATA_DEPTH)) << endl;cout << "CAP_PROP_AUDIO_SAMPLES_PER_SECOND: " << cap.get(CAP_PROP_AUDIO_SAMPLES_PER_SECOND) << endl;cout << "CAP_PROP_AUDIO_TOTAL_CHANNELS: " << cap.get(CAP_PROP_AUDIO_TOTAL_CHANNELS) << endl;cout << "CAP_PROP_AUDIO_TOTAL_STREAMS: " << cap.get(CAP_PROP_AUDIO_TOTAL_STREAMS) << endl;int numberOfSamples = 0;int numberOfFrames = 0;audioData.resize(numberOfChannels);mfcap::AudioOutput audioOutput;audioOutput.Open((int)cap.get(CAP_PROP_AUDIO_TOTAL_CHANNELS),(int)cap.get(CAP_PROP_AUDIO_SAMPLES_PER_SECOND),16);for (;;){if (cap.grab()){//cap.retrieve(videoFrame);std::vector<const unsigned char*> planes;planes.resize(numberOfChannels);for (int nCh = 0; nCh < numberOfChannels; nCh++){cap.retrieve(audioFrame, audioBaseIndex+nCh);if (!audioFrame.empty()){audioData[nCh].push_back(audioFrame);//planes[nCh] = audioFrame.data + nCh * audioFrame.cols;}numberOfSamples+=audioFrame.cols;}} else { break; }}cout << "Number of audio samples: " << numberOfSamples << endl<< "Number of video frames: " << numberOfFrames << endl;return 0;
}

打开设备

bool CvCapture_MSMF::open(int index, const cv::VideoCaptureParameters* params)
{// 先重置环境close();if (index < 0)return false;if (params){// 开启硬件编解码加速,这里先省略,在后面的硬件加速上学习。configureHW(*params);/* configureStream 主要是配置是否捕获音频或视频流// 如果需要捕获音频流: audioStream = 0 否者 audioStream  = -1// 视频流同理,对应的变量为: videoStream*//* setAudioProperties // outputAudioFormat: 音频的位深, CV_16S 等// audioSamplesPerSecond 采样率// syncLastFrame: 是否需要音视频同步,OpenCV里面只支持视频文件的音视频同步*/if (!(configureStreams(*params) && setAudioProperties(*params)))return false;}// 仅支持打开音频流或者视频流,不能在一个对象里面打开或者都不打开。if (videoStream != -1 && audioStream != -1 || videoStream == -1 && audioStream == -1){CV_LOG_DEBUG(NULL, "Only one of the properties CAP_PROP_AUDIO_STREAM " << audioStream << " and " << CAP_PROP_VIDEO_STREAM << " must be different from -1");return false;}// DeviceList  主要检测当前系统中,视频或音频输入设备的个数,以及激活某一个设备。DeviceList devices;UINT32 count = 0;// 如果是读音频设备就枚举音频输入设备if (audioStream != -1)count = devices.read(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID);// 读视频设备同理。值得注意的是,opencv只支持读音频或视频设备// 如果都设置为0, 则默认读视频if (videoStream != -1)count = devices.read(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);if (count == 0 || static_cast<UINT32>(index) > count){CV_LOG_DEBUG(NULL, "Device " << index << " not found (total " << count << " devices)");return false;}/* getDefaultSourceConfig 这个主要设置硬件加速相关,这里可以先跳过*/_ComPtr<IMFAttributes> attr = getDefaultSourceConfig();/*SourceReaderCB 读取回调设置读取回调,当设置了读取的回调之后,MediaFoundation 读取Source 为异步模式这个Open函数是读设备的,还有其他重载函数用于读文件在OpenCV里面,读取设备使用异步模式,读取文件使用同步模式。*/_ComPtr<IMFSourceReaderCallback> cb = new SourceReaderCB();attr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, cb.Get());// 激活目标设备_ComPtr<IMFMediaSource> src = devices.activateSource(index);if (!src.Get() || FAILED(MFCreateSourceReaderFromMediaSource(src.Get(), attr.Get(), &videoFileSource))){CV_LOG_DEBUG(NULL, "Failed to create source reader");return false;}isOpen = true;device_status = true;camid = index;readCallback = cb;duration = 0;// 选择合适的摄像头的流,比如宽高,fps.一般一个摄像头有很多个拉流的格式// 音频设备同理,比如有不同的采样率,位深等。if (configureOutput()){frameStep = captureVideoFormat.getFrameStep();}if (isOpen && !openFinalize_(params)){close();return false;}if (isOpen){if (audioStream != -1)if (!checkAudioProperties())return false;}return isOpen;
}

选择合适的设备流 configureOutput

bool CvCapture_MSMF::configureOutput()
{// 读取设备所有的流,当然也可以读第一个流if (FAILED(videoFileSource->SetStreamSelection((DWORD)MF_SOURCE_READER_ALL_STREAMS, false))){CV_LOG_WARNING(NULL, "Failed to reset streams");return false;}bool tmp = true;// 配置视频流if (videoStream != -1)tmp = (!device_status)? configureVideoOutput(MediaType(), outputVideoFormat) : configureVideoOutput(MediaType::createDefault_Video(), outputVideoFormat);// 配置音频流if (audioStream != -1)tmp &= (!device_status)? configureAudioOutput(MediaType()) : configureAudioOutput(MediaType::createDefault_Audio());return tmp;
}

配置音频流 configureAudioOutput

bool CvCapture_MSMF::configureAudioOutput(MediaType newType)
{/*FormatStorage  存储设备的流信息,以及挑选最合适的流*/FormatStorage formats;formats.read(videoFileSource.Get());std::pair<FormatStorage::MediaID, MediaType> bestMatch;formats.countNumberOfAudioStreams(numberOfAudioStreams);if (device_status)bestMatch = formats.findBestAudioFormat(newType);elsebestMatch = formats.findAudioFormatByStream(audioStream);if (bestMatch.second.isEmpty(true)){CV_LOG_DEBUG(NULL, "Can not find audio stream with requested parameters");isOpen = false;return false;}dwAudioStreamIndex = bestMatch.first.stream;dwStreamIndices.push_back(dwAudioStreamIndex);MediaType newFormat = bestMatch.second;newFormat.majorType = MFMediaType_Audio;newFormat.nSamplesPerSec = (audioSamplesPerSecond == 0) ? 44100 : audioSamplesPerSecond;switch (outputAudioFormat){case CV_8S:newFormat.subType = MFAudioFormat_PCM;newFormat.bit_per_sample = 8;break;case CV_16S:newFormat.subType = MFAudioFormat_PCM;newFormat.bit_per_sample = 16;break;case CV_32S:newFormat.subType = MFAudioFormat_PCM;newFormat.bit_per_sample = 32;case CV_32F:newFormat.subType = MFAudioFormat_Float;newFormat.bit_per_sample = 32;break;default:break;}// 初始化流return initStream(dwAudioStreamIndex, newFormat);
}

初始化流 initStream

bool CvCapture_MSMF::initStream(DWORD streamID, const MediaType mt)
{CV_LOG_DEBUG(NULL, "Init stream " << streamID << " with MediaType " << mt);_ComPtr<IMFMediaType> mediaTypesOut;if (mt.majorType == MFMediaType_Audio){captureAudioFormat = mt;mediaTypesOut = mt.createMediaType_Audio();}if (mt.majorType == MFMediaType_Video){captureVideoFormat = mt;mediaTypesOut = mt.createMediaType_Video();}if (FAILED(videoFileSource->SetStreamSelection(streamID, true))){CV_LOG_WARNING(NULL, "Failed to select stream " << streamID);return false;}HRESULT hr = videoFileSource->SetCurrentMediaType(streamID, NULL, mediaTypesOut.Get());if (hr == MF_E_TOPO_CODEC_NOT_FOUND){CV_LOG_WARNING(NULL, "Failed to set mediaType (stream " << streamID << ", " << mt << "(codec not found)");return false;}else if (hr == MF_E_INVALIDMEDIATYPE){CV_LOG_WARNING(NULL, "Failed to set mediaType (stream " << streamID << ", " << mt << "(unsupported media type)");return false;}else if (FAILED(hr)){CV_LOG_WARNING(NULL, "Failed to set mediaType (stream " << streamID << ", " << mt << "(HRESULT " << hr << ")");return false;}return true;
}

捕获音频帧 Garb()

捕获帧的回调 SourceReaderCB

OpenCV捕获设备数据,采用异步模式,需要自定义一个捕获帧的回调

// 需要继承 IMFSourceReaderCallback
class SourceReaderCB : public IMFSourceReaderCallback
{
public:
// 最大的缓冲帧数量static const size_t MSMF_READER_MAX_QUEUE_SIZE = 3;SourceReaderCB() :m_nRefCount(0), m_hEvent(CreateEvent(NULL, FALSE, FALSE, NULL)), m_bEOS(FALSE), m_hrStatus(S_OK), m_reader(NULL), m_dwStreamIndex(0){}// COM 接口必须的,感觉就是照着格式写// IUnknown methodsSTDMETHODIMP QueryInterface(REFIID iid, void** ppv) CV_OVERRIDE{
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4838)
#endifstatic const QITAB qit[] ={QITABENT(SourceReaderCB, IMFSourceReaderCallback),{ 0 },};
#ifdef _MSC_VER
#pragma warning(pop)
#endifreturn QISearch(this, qit, iid, ppv);}STDMETHODIMP_(ULONG) AddRef() CV_OVERRIDE{return InterlockedIncrement(&m_nRefCount);}STDMETHODIMP_(ULONG) Release() CV_OVERRIDE{ULONG uCount = InterlockedDecrement(&m_nRefCount);if (uCount == 0){delete this;}return uCount;}// 在 调用wait()的时候 被调用STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample) CV_OVERRIDE{HRESULT hr = 0;cv::AutoLock lock(m_mutex);if (SUCCEEDED(hrStatus)){if (pSample){CV_LOG_DEBUG(NULL, "videoio(MSMF): got frame at " << llTimestamp);// 如果缓冲的帧的数量大于最大的数量,需要弹出前面的帧if (m_capturedFrames.size() >= MSMF_READER_MAX_QUEUE_SIZE){
#if 0CV_LOG_DEBUG(NULL, "videoio(MSMF): drop frame (not processed). Timestamp=" << m_capturedFrames.front().timestamp);m_capturedFrames.pop();
#else// this branch reduces latency if we drop frames due to slow processing.// avoid fetching of already outdated frames from the queue's front.CV_LOG_DEBUG(NULL, "videoio(MSMF): drop previous frames (not processed): " << m_capturedFrames.size());std::queue<CapturedFrameInfo>().swap(m_capturedFrames);  // similar to missing m_capturedFrames.clean();
#endif}m_capturedFrames.emplace(CapturedFrameInfo{ llTimestamp, _ComPtr<IMFSample>(pSample), hrStatus });}}else{CV_LOG_WARNING(NULL, "videoio(MSMF): OnReadSample() is called with error status: " << hrStatus);}if (MF_SOURCE_READERF_ENDOFSTREAM & dwStreamFlags){// Reached the end of the stream.m_bEOS = true;}m_hrStatus = hrStatus;if (FAILED(hr = m_reader->ReadSample(dwStreamIndex, 0, NULL, NULL, NULL, NULL))){CV_LOG_WARNING(NULL, "videoio(MSMF): async ReadSample() call is failed with error status: " << hr);m_bEOS = true;}if (pSample || m_bEOS){SetEvent(m_hEvent);}return S_OK;}STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) CV_OVERRIDE{return S_OK;}STDMETHODIMP OnFlush(DWORD) CV_OVERRIDE{return S_OK;}HRESULT Wait(DWORD dwMilliseconds, _ComPtr<IMFSample>& mediaSample, LONGLONG& sampleTimestamp, BOOL& pbEOS){pbEOS = FALSE;for (;;){{cv::AutoLock lock(m_mutex);pbEOS = m_bEOS && m_capturedFrames.empty();if (pbEOS)return m_hrStatus;if (!m_capturedFrames.empty()){CV_Assert(!m_capturedFrames.empty());CapturedFrameInfo frameInfo = m_capturedFrames.front(); m_capturedFrames.pop();CV_LOG_DEBUG(NULL, "videoio(MSMF): handle frame at " << frameInfo.timestamp);mediaSample = frameInfo.sample;CV_Assert(mediaSample);sampleTimestamp = frameInfo.timestamp;ResetEvent(m_hEvent);  // event is auto-reset, but we need this forced reset due time gap between wait() and mutex hold.return frameInfo.hrStatus;}}CV_LOG_DEBUG(NULL, "videoio(MSMF): waiting for frame... ");DWORD dwResult = WaitForSingleObject(m_hEvent, dwMilliseconds);if (dwResult == WAIT_TIMEOUT){return E_PENDING;}else if (dwResult != WAIT_OBJECT_0){return HRESULT_FROM_WIN32(GetLastError());}}}private:// Destructor is private. Caller should call Release.virtual ~SourceReaderCB(){CV_LOG_INFO(NULL, "terminating async callback");}public:long                m_nRefCount;        // Reference count.cv::Mutex           m_mutex;HANDLE              m_hEvent;BOOL                m_bEOS;HRESULT             m_hrStatus;IMFSourceReader *m_reader;DWORD m_dwStreamIndex;struct CapturedFrameInfo {LONGLONG timestamp;_ComPtr<IMFSample> sample;HRESULT hrStatus;};std::queue<CapturedFrameInfo> m_capturedFrames;
};

相关文章:

【MediaFoundation】OpenCV VideoCapture 读取音频源码

OpenCV 读取音频代码实例 在windows7 以及OpenCV4 过后可以使用 CAP_MSMF 读取音频&#xff0c;但是OpenCV没有播放音频的API。代码示例如下。 本文解析OpenCVCAP_MSMF 进行文件、设备的 音频读取&#xff0c;学习MediaFoundation 的使用。 #include <opencv2/core.hpp>…...

2024秋招,百度测试开发工程师一面

前言 大家好&#xff0c;今天我来回顾一下秋招中的一场很重要技术面试 一面面试官深挖我的项目经历&#xff0c;并提出了很多的实际场景&#xff0c;我现在回顾依然有很多新的认识 过程 自我介绍实习工作中&#xff0c;做得最好的地方是什么&#xff1f; 我先介绍了一下实习…...

Git 使用与问题记录 二(公司快速上手版)

写在前面 记录自己学习的内容&#xff0c;方便后面忘记的时候查看。给像我一样的新手提供一点参考 正文 上一章已经安装好了Git&#xff0c;如何使用呢。我这里会分享两种办法&#xff0c;第一种是在VS2022中克隆代码&#xff0c;修改和提交&#xff1b;第二种是用命令提交。…...

【C语言小游戏】贪吃蛇

文章目录 1.引言2.运行图2.涉及知识3 Windows API3.1 控制台3.2 控制台屏幕坐标3.3 操作句柄3.4 控制台屏幕光标3.5 监视按键 4. 设计说明5. 完整代码 1.引言 使⽤C语⾔在Windows环境的控制台中模拟实现经典⼩游戏贪吃蛇 实现基本的功能&#xff1a; 贪吃蛇地图绘制蛇吃⻝物的…...

价值7500的在线授权网站源码支持IP+域名+双向授权全开源

PHP授权验证更新系统完整版&#xff0c;一键更新系统&#xff0c;一键卡密生成自助授权功能&#xff0c;域名ip双重验证功能等等 修复盗版检测&#xff0c;确保实时查看盗版 修复在线加密系统&#xff0c;一键加密 授权系统几乎所有的程序都能整合使用,包括您的app和计算机程序…...

haiku实现门控多头注意力模块

在多头注意力机制中&#xff0c;通常输入的数据包括查询&#xff08;Q&#xff09;、键&#xff08;K&#xff09;和值&#xff08;V&#xff09;。这些数据的维度以及权重矩阵的维度在多头注意力机制中扮演关键角色。下面对数据及权重的维度进行解释&#xff1a; 输入数据&…...

【React 常用的 TS 类型】持续更新

1&#xff09;定义样式的 TS 类型 【 React.CSSProperties 】 一般定义样式时需要的类型限制&#xff0c;如下&#xff1a; const customStyle: React.CSSProperties {color: blue,fontSize: 16px,margin: 10px,}; 2&#xff09;定义 Input Ref 属性时的 TS 类型限制 【 R…...

打破传统边界,VR技术与六西格玛设计理念的创新融合!

在科技飞速发展的今天&#xff0c;虚拟现实&#xff08;VR&#xff09;技术以其独特的沉浸式体验&#xff0c;正在改变我们的生活和工作方式。然而&#xff0c;要让VR真正成为主流&#xff0c;我们必须解决一些关键问题&#xff0c;其中最重要的就是用户体验。六西格玛设计&…...

[uniapp] uni-ui+vue3.2小程序评论列表组件 回复评论 点赞和删除

先看效果 下载地址 uni-app官方插件市场: cc-comment组件 环境 基于vue3.2和uni-ui开发; 依赖版本参考如下: "dependencies": {"dcloudio/uni-mp-weixin": "3.0.0-3090820231124001","dcloudio/uni-ui": "^1.4.28","…...

TongLINKQ(3):TongLINKQ常用命令

启动&#xff1a; tlq 暂停&#xff1a; tlq -cabort -y -w1 查看lic信息&#xff1a; tlqstat –lic 查看队列消息&#xff1a; tlqstat -qcu qcu名 -c 查看发送连接状态&#xff1a; tlqstat -snd qcu名 -1 -ct 1 查看指定的Qcu连接状态&#xff1a; tlqsta…...

抽水马桶出水慢解决记录

今天分享一些修马桶的小心得&#xff08;雾&#xff09; 家里的马桶出水很好&#xff0c;但是水却不怎么被冲下去&#xff08;出水很慢&#xff09;&#xff0c;这会导致内容物滞留&#xff0c;造成很不好的使用体验。 出于成本考虑&#xff0c;首先选择自己维修。 首先直接…...

img标签的奇怪问题

本来只是为实现一个轮播图&#xff0c;img的url地址是从后端接口获取的&#xff0c;但不巧的是url地址的图片都过期了。 因为懒得重新到网上找图&#xff0c;就想直接用一下本地的图片&#xff0c;简单的想法遇到一堆问题。 问题一&#xff1a; 因为是springboot项目&#xf…...

深入探究Hibernate:优雅、强大的Java持久化框架

目录 1、前言 2、Hibernate简介 2.1 什么是Hibernate 2.2 为什么选择Hibernate 3、Hibernate核心概念 3.1 实体类和映射文件 3.2 数据库表和持久化类的映射 3.3 主键生成策略 3.4 持久化操作 3.5 查询语言(HQL和Criteria) 3.6 事务管理 4、Hibernate配置与连接 4…...

JavaScript高级特性详解

摘要&#xff1a;本文将深入探讨JavaScript中的一些高级特性&#xff0c;包括闭包、原型链、高阶函数和异步编程。我们将通过详细的注释和实例来帮助读者理解这些概念&#xff0c;并通过总结部分强调其在实际开发中的应用。 一、闭包 闭包是JavaScript中一个非常重要的概念&a…...

网站建设网络设计营销类网站eyouCMS模板(PC+WAP)

模板介绍&#xff1a; 本模板自带eyoucms内核&#xff0c;无需再下载eyou系统&#xff0c;原创设计、手工书写DIVCSS&#xff0c;完美兼容IE7、Firefox、Chrome、360浏览器等&#xff1b;主流浏览器&#xff1b;结构容易优化&#xff1b;多终端均可正常预览。...

迅为RK3568开发板Android11/12/Linux编译驱动到内核

在平时的驱动开发中&#xff0c;经常需要在内核中配置某种功能&#xff0c;为了方便大家开发和学习&#xff0c;本小 节讲解如何在内核中添加驱动。具体的讲解原理讲解请参考本手册的驱动教程。 Android11 源码如果想要修改内核&#xff0c;可以运行以下命令进行修改: cd ke…...

SaaS 应用深度解析:Marketo

随着数字营销的不断发展&#xff0c;企业需要强大而智能的工具来管理营销活动、吸引潜在客户、并实现销售目标。在众多营销自动化工具中&#xff0c;Marketo 是一款备受推崇的 SaaS 应用&#xff0c;为企业提供全面的营销解决方案。本文将深入了解 Marketo&#xff0c;探讨其功…...

闲聊篇-求职的点点滴滴~~

引言 求职之旅是一段充满挑战与机遇的旅程。它不仅仅是寻找工作的过程&#xff0c;更是一个自我探索和成长的过程。在这篇文章中&#xff0c;我们将探讨求职的各个方面&#xff0c;从准备简历到面试&#xff0c;再到最终拿到心仪的offer。 1. 简历&#xff1a;你的敲门砖 精…...

微软最新研究成果:使用GPT-4合成数据来训练AI模型,实现SOTA!

文本嵌入是各项NLP任务的基础&#xff0c;用于将自然语言转换为向量表示。现有的大部分方法通常采用复杂的多阶段训练流程&#xff0c;先在大规模数据上训练&#xff0c;再在小规模标注数据上微调。此过程依赖于手动收集数据制作正负样本对&#xff0c;缺乏任务的多样性和语言多…...

爬虫案例—抓取小米商店应用

爬虫案例—抓取小米商店应用 代码如下&#xff1a; # 抓取第一页的内容 import requests from lxml import etree url ‘https://app.mi.com/catTopList/0?page1’ headers { ‘User-Agent’: ‘Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (K…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

如何通过git命令查看项目连接的仓库地址?

要通过 Git 命令查看项目连接的仓库地址&#xff0c;您可以使用以下几种方法&#xff1a; 1. 查看所有远程仓库地址 使用 git remote -v 命令&#xff0c;它会显示项目中配置的所有远程仓库及其对应的 URL&#xff1a; git remote -v输出示例&#xff1a; origin https://…...

Canal环境搭建并实现和ES数据同步

作者&#xff1a;田超凡 日期&#xff1a;2025年6月7日 Canal安装&#xff0c;启动端口11111、8082&#xff1a; 安装canal-deployer服务端&#xff1a; https://github.com/alibaba/canal/releases/1.1.7/canal.deployer-1.1.7.tar.gz cd /opt/homebrew/etc mkdir canal…...

raid存储技术

1. 存储技术概念 数据存储架构是对数据存储方式、存储设备及相关组件的组织和规划&#xff0c;涵盖存储系统的布局、数据存储策略等&#xff0c;它明确数据如何存储、管理与访问&#xff0c;为数据的安全、高效使用提供支撑。 由计算机中一组存储设备、控制部件和管理信息调度的…...