RK3399平臺ffmpeg-VPU硬編碼錄制USB攝像頭視頻、H264或MJPEG編碼

文章目錄

  • 1 前言
  • 2 項目內容詳細說明
    • 2.0 功能
    • 2.1 工程文件夾說明
  • 3 代碼
    • 3.1 CameraThread類
    • 3.1 CameraThreadImpl類
  • 4 資源下載


1 前言

??在某項目中需要在RK3399平臺實現USB攝像頭畫面的實時預覽、視頻錄制、拍照存儲等功能。
??先來看需要實現的最終效果。
??

ffmpeg USB攝像頭 H264編碼錄視頻拍照


??本項目使用了搭載了RK3399芯片的嵌入式平臺,操作系統Debain10,調用ffmpeg-rkmpp庫進行H264硬編碼。
??本項目需要外接一個支持 2592x1944,1080p,720p,480p的USB攝像頭。

2 項目內容詳細說明

2.0 功能

??(1)實時預覽;
??(2)攝像頭分辨率設置,可設置為480p,720p,1080p;
??(3)編碼格式設置,可按照H264格式編碼,或直接存MJPEG;
??(4)存儲文件格式,可設置為.avi或者.mp4格式;
??(5)錄制視頻時,顯示當前錄制時間以及“REC”閃爍字樣;
??(6)拍照功能,拍攝照片分辨率為2592x1944(500萬像素);
??(7)通過F3、F4鍵移動光標,通過 ↑ 按鍵實現“開始、停止”錄視頻,通過 → 按鍵實現“確定”,通過 ← 按鍵實現“拍照”。

2.1 工程文件夾說明

??工程文件夾結構如下:
在這里插入圖片描述
??在cameraModule文件夾內的是視頻相關內容,可單獨編譯成庫,在cameravideowidget.cpp中實現代碼的調用。

3 代碼

3.1 CameraThread類

??CameraThread類為視頻錄制、拍照實現的關鍵類,其接口如下表所示。

成員類型名稱描述
構造函數CameraThread()初始化 CameraThread 對象
析構函數~CameraThread()釋放 CameraThread 對象的資源。
函數int CheckUSBCamera()檢查 USB 相機是否正常工作。返回值為 0 表示正常,其他值表示錯誤代碼。
void setParamter(int, int, int)設置視頻參數(分辨率、編碼格式、封裝格式)。必須在 OpenCamera() 之前調用。
int OpenCamera()開啟相機。返回值為 0 表示成功,其他值表示錯誤代碼。
int CloseCamera()關閉相機。返回值為 0 表示成功,其他值表示錯誤代碼。
int TakePhoto(QString name)拍照接口,調用該函數使用相機拍攝一張照片并保存至指定路徑。返回值為 0 表示成功,其他值表示錯誤代碼。
int DeletPhoto()刪除照片接口,調用該函數刪除當前拍攝的照片。返回值為 0 表示成功,其他值表示錯誤代碼。
int StartTakeVideo(QString name)開始視頻錄制并保存到指定路徑。返回值為 0 表示成功,其他值表示錯誤代碼。
int StopTakeVideo()停止當前視頻錄制操作,并完成所有未完成任務。返回值為 0 表示成功,其他值表示錯誤代碼。
信號void sendPhoto(QImage img)當拍攝照片后發出此信號,傳遞一個 QImage 類型的圖像用于UI繪制。
信號void sendRealTimePhoto(QImage img)發送實時圖像幀信號,用于傳遞實時預覽圖像。
公有變量bool isOpen標志相機是否已打開。
公有變量bool isRecording標志是否正在錄制視頻。
公有變量std::unique_ptr impl使用 PIMPL 設計模式隱藏實現細節的實現類指針。

??camerathread.cpp實現如下所示。

#include "camerathread.h"
#include "camerathreadimpl.h"CameraThread::CameraThread() :impl(std::make_unique<CameraThreadImpl>())
{connect(impl.get(), &CameraThreadImpl::sendPhoto, this, &CameraThread::sendPhoto);connect(impl.get(), &CameraThreadImpl::sendRealTimePhoto, this, &CameraThread::sendRealTimePhoto);qDebug()<<"libcamerathread.so_version_20250731_";
}CameraThread::~CameraThread()
{}int CameraThread::CheckUSBCamera()
{int ret = 0;qDebug()<<"CheckUSBCamera!";if(isOpen==true){ret = 0;}else{ret = impl->CheckUSBCamera();}return ret;
}void CameraThread::setParamter(int resolution, int encodeformat, int containerformat)
{qDebug()<<"resolution: "<<resolution<<" encodeformat: "<<encodeformat<<"containerformat "<<containerformat;impl->setParamter(resolution, encodeformat, containerformat);
}int CameraThread::OpenCamera()
{int ret = 0;qDebug()<<"Camera Open!";ret=impl->OpenCamera();if(!ret){isOpen = true;}qDebug()<<"OpenCamera isOpen: "<<isOpen;return ret;
}int CameraThread::CloseCamera()
{int ret = 0;qDebug()<<"Camera Close!";qDebug()<<"CloseCamera isOpen: "<<isOpen;if(isOpen == true){ret = impl->CloseCamera();}if(!ret){isOpen = false;}ret = StopTakeVideo();return ret;
}int CameraThread::TakePhoto(QString name)
{int ret = 0;ret=impl->TakePhoto(name);return ret;
}int CameraThread::DeletPhoto()
{int ret = 0;qDebug()<<"Delete Photo";ret=impl->DeletPhoto();return ret;
}int CameraThread::StartTakeVideo(QString name)
{int ret = 0;qDebug()<<"StartTakeVideo: "<<name;ret=impl->StartTakeVideo(name);isRecording = true;return ret;
}int CameraThread::StopTakeVideo()
{int ret = 0;if(isRecording){ret=impl->StopTakeVideo();}isRecording = false;return ret;
}

3.1 CameraThreadImpl類

??CameraThread類中使用CameraThreadImpl類作為內部實現類,可使得CameraThread接口不暴露具體實現方法。

camerathreadimpl.cpp具體實現如下所示。

#include "camerathreadimpl.h"CameraThreadImpl::CameraThreadImpl()
{initParam();/***************************分配緩沖隊列**************************************/video_packet_queue = new AVPacketQueue();video_packet_queue_output = new AVPacketQueue();/***************************分配緩沖隊列**************************************/video_frame_queue = new AVFrameQueue();fmtConvert_queue_output = new AVFrameQueue();}CameraThreadImpl::~CameraThreadImpl()
{delete demux_thread;delete decode_thread;delete save_thread;delete video_packet_queue_output;delete video_packet_queue;}void CameraThreadImpl::initParam()
{// 讀取INI配置文件QString configFilePath = QCoreApplication::applicationDirPath() + "/config.ini";QSettings settings(configFilePath, QSettings::IniFormat);/************************確認配置文件路徑以及是否存在配置文件***************/
//    qDebug()<<"configFilePath: "<<configFilePath;
//    qDebug()<<"File Exists: " << QFile::exists(configFilePath);                         // 確保文件存在
//    qDebug()<<"QSettings file path: " << settings.fileName();/*********************************************************************/// 設置默認值QString defaultCameraUrl;QString defaultSavePath;
#ifdef Q_OS_WIN
//    defaultCameraUrl = "video=Rmoncam FHD 1080P";       // Windows 平臺默認相機名稱defaultCameraUrl = "video=USB Video Device";       // Windows 平臺默認相機名稱defaultSavePath = "C:\\Users\\chao8\\Desktop\\";    // Windows 默認保存路徑
#else
//    defaultCameraUrl = "/dev/video10";                  // Forlinx 平臺默認相機名稱
//    defaultSavePath = "/home/forlinx/Desktop/";         // Forlinx 默認保存路徑defaultCameraUrl = "/dev/video0";                  // Debain11平臺默認相機名稱defaultSavePath = "/mnt/ums/data/";                     // Debain11 默認保存路徑
#endifdouble defaultRemainingSpace = 0.8;int defaultRecordIntervalMinutes = 1000;QString defaultResolution = "1920x1080";QString defaultBitrate = "40M";// 從INI文件讀取配置值,如果沒有配置則使用默認值savePath_ = settings.value("Parameters/savePath", defaultSavePath).toString().toStdString();url_ = settings.value("Parameters/url", defaultCameraUrl).toString().toStdString();remainingSpace = settings.value("Parameters/remainingStorageSpace", defaultRemainingSpace).toDouble();recordIntervalMinutes = settings.value("Parameters/recordIntervalMinutes", defaultRecordIntervalMinutes).toInt();Resolution = settings.value("Parameters/resolution", defaultResolution).toString();Bitrate = settings.value("Parameters/bitrate", defaultBitrate).toString();// 設置定時器時間,定時處理視頻信息,每隔一段時間自動記錄視頻timer = new QTimer(this);timer->setInterval(recordIntervalMinutes * 60 * 1000);connect(timer, &QTimer::timeout, this, &CameraThreadImpl::handleVideoData);/*****************************打印各參數配置數值*****************************/
//    qDebug()<<"savePath_: "<<QString::fromStdString(savePath_);
//    qDebug()<<"url_: "<<QString::fromStdString(url_);
//    qDebug()<<"remainingSpace: "<<remainingSpace;
//    qDebug()<<"Resolution: "<<Resolution;
//    qDebug()<<"Bitrate: "<<Bitrate;
//    qDebug()<<"recordIntervalMinutes: "<<recordIntervalMinutes;/**************************************************************************/}int CameraThreadImpl::CheckUSBCamera()
{avdevice_register_all();std::string url_str = url_;AVFormatContext *ifmt_ctx = avformat_alloc_context();char err2str[256] = {0};AVDictionary *options = nullptr;av_dict_set(&options, "fflags", "nobuffer", 0);av_dict_set(&options, "probesize", "4096", 0);av_dict_set(&options, "framerate", "30", 0);av_dict_set(&options, "video_size", "1920x1080", 0);av_dict_set(&options, "input_format", "mjpeg", 0);#ifdef Q_OS_WINconst AVInputFormat *m_inputFormat = av_find_input_format("dshow");
#elseconst AVInputFormat *m_inputFormat = av_find_input_format("v4l2");
#endifint ret = avformat_open_input(&ifmt_ctx, url_str.c_str(), m_inputFormat, &options);//    qDebug()<<"ret: "<<ret;if (ret < 0) {av_strerror(ret, err2str, sizeof(err2str));qDebug("avformat_open_input failed, ret:%d, err2str:%s", ret, err2str);return ret;}ret = avformat_find_stream_info(ifmt_ctx, nullptr);if (ret < 0) {av_strerror(ret, err2str, sizeof(err2str));qDebug("avformat_find_stream_info failed, ret:%d, err2str:%s", ret, err2str);avformat_close_input(&ifmt_ctx);return ret;}avformat_close_input(&ifmt_ctx);qDebug() << "USB camera check OK!";return ret;
}void CameraThreadImpl::ReleaseQueue()
{video_packet_queue_output->release();video_packet_queue->release();video_frame_queue->release();fmtConvert_queue_output->release();
}void CameraThreadImpl::setParamter(int resolution, int encodeformat, int containerformat)
{if(resolution==0){Resolution = "1920x1080";}else if(resolution==1){Resolution = "1280x720";}else if(resolution==2){Resolution = "640x480";}if(encodeformat==0){EncodeFormat = "MJPEG";}else if(encodeformat==1){EncodeFormat = "H264";}if(containerformat==0){VideoContainerFormat = "avi";}else if(containerformat==1){VideoContainerFormat = "mp4";}
}void CameraThreadImpl::handleVideoData()
{qDebug()<<"handleVideoData! ";demux_thread->start_pts = 0;save_thread->Stop();ReleaseQueue();save_thread->Init();save_thread->Start();}int CameraThreadImpl::OpenCamera()
{int ret = 0;/*************************************************************************************/if(EncodeFormat == "MJPEG"){demux_thread = new DemuxThread(video_packet_queue, &url_);demux_thread->setResolution(Resolution);ret = demux_thread->Start();if(ret<0){isCameraValid = false;}else{isCameraValid = true;}/**************************************************************************************/decode_thread = new DecodeThread(video_packet_queue, video_packet_queue_output, &savePath_);decode_thread->setRemainingSpace(remainingSpace);ret = decode_thread->Init(demux_thread->VideoCodecParameters());ret = decode_thread->Start();}if(EncodeFormat == "H264"){demux_thread = new DemuxThread(video_packet_queue, &url_);demux_thread->setResolution(Resolution);ret = demux_thread->Start();if(ret<0){isCameraValid = false;}else{isCameraValid = true;}/**************************************************************************************/decode_thread = new DecodeThread(video_packet_queue, video_frame_queue, &savePath_);decode_thread->setRemainingSpace(remainingSpace);ret = decode_thread->Init(demux_thread->VideoCodecParameters());ret = decode_thread->Start();}/**************************************************************************************/connect(decode_thread, &DecodeThread::frameReady, this, &CameraThreadImpl::sendPhoto);connect(decode_thread, &DecodeThread::realTimeframeReady, this, &CameraThreadImpl::sendRealTimePhoto);connect(decode_thread, &DecodeThread::frameReady, this, &CameraThreadImpl::CloseCamera);connect(decode_thread, &DecodeThread::frameReady, this, &CameraThreadImpl::setisTakePhoto);return ret;
}int CameraThreadImpl::StartTakeVideo(QString name)
{int ret = 0;std::string savefilename = name.toStdString();ReleaseQueue();CloseCamera();OpenCamera();/**************************************************************************************/if(isCameraValid){if(EncodeFormat == "MJPEG"){save_thread = new SaveThread(video_packet_queue_output, demux_thread->inputStream, &savefilename);save_thread->setRemainingSpace(remainingSpace);save_thread->setVideoContainerFormat(VideoContainerFormat);ret = save_thread->Init();ReleaseQueue();demux_thread->start_pts = 0;decode_thread->isSaveFile = true;ret = save_thread->Start();}if(EncodeFormat == "H264"){/************************************************************************/fmtConvert_thread = new FormatConvertThread(video_frame_queue, fmtConvert_queue_output);/*********************************************************************/encode_thread = new EncodeThread(fmtConvert_queue_output, video_packet_queue_output, demux_thread->inputStream);encode_thread->setBitrate(Bitrate);encode_thread->isFIlter = isFilterOn;encode_thread->EncodeFormat = "H264";ret = encode_thread->Init();ret = fmtConvert_thread->Start();ret = encode_thread->Start();save_thread = new SaveThread(video_packet_queue_output, demux_thread->inputStream, &savefilename, encode_thread->encoderContext);save_thread->setRemainingSpace(remainingSpace);save_thread->setVideoContainerFormat(VideoContainerFormat);ret = save_thread->Init();ReleaseQueue();demux_thread->start_pts = 0;decode_thread->isSaveFile = true;ret = save_thread->Start();}timer->start();}else{qDebug()<<"Camera is not Valid! Cannot video!";}return ret;
}int CameraThreadImpl::StopTakeVideo()
{if(isCameraValid){if(save_thread!=nullptr){save_thread->Stop();save_thread = nullptr;decode_thread->isSaveFile = false;if (fmtConvert_thread) fmtConvert_thread->Stop();if (encode_thread) encode_thread->Stop();ReleaseQueue();timer->stop();qDebug()<<"StopTakeVideo!";}else{qDebug()<<"No save_thread!";}}else{qDebug()<<"Camera is not Valid! Cannot video!";}return 0;
}int CameraThreadImpl::CloseCamera()
{StopTakeVideo();if(isCameraValid){decode_thread->Stop();demux_thread->Stop();isCameraValid = false;}ReleaseQueue();      //清空緩沖隊列里的東西,每次關閉相機的時候,必須要有!return 0;
}void CameraThreadImpl::setisTakePhoto()
{isTakePhoto = false;
}int CameraThreadImpl::TakePhoto(QString name)
{qDebug()<<"isTakePhoto: "<<isTakePhoto;if(!isTakePhoto){qDebug()<<"In TakePhoto!!!!!! ";isTakePhoto = true;CloseCamera();int ret = 0;demux_thread = new DemuxThread(video_packet_queue, &url_);demux_thread->setResolution("2592x1944");ret = demux_thread->Start();if(ret<0){isCameraValid = false;}else{isCameraValid = true;}/**************************************************************************************/decode_thread = new DecodeThread(video_packet_queue, video_packet_queue_output, &savePath_);decode_thread->setRemainingSpace(remainingSpace);ret = decode_thread->Init(demux_thread->VideoCodecParameters());ret = decode_thread->Start();connect(decode_thread, &DecodeThread::frameReady, this, &CameraThreadImpl::sendPhoto);connect(decode_thread, &DecodeThread::frameReady, this, &CameraThreadImpl::CloseCamera);connect(decode_thread, &DecodeThread::frameReady, this, &CameraThreadImpl::setisTakePhoto);if (decode_thread){decode_thread->takePhoto(name);}else{return -1;}return ret;}else{return 0;}}int CameraThreadImpl::DeletPhoto()
{decode_thread->DeletPhoto();return 0;
}

4 資源下載

本案例中涉及到的所有代碼請到此處下載 https://download.csdn.net/download/wang_chao118/91923444。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/96447.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/96447.shtml
英文地址,請注明出處:http://en.pswp.cn/web/96447.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

解決藍牙耳機連win11電腦畫質依托答辯問題

以wh910n藍牙耳機為例 設置-系統-聲音-輸出&#xff08;耳機&#xff09;-常規&#xff08;輸出點不允許&#xff09;然后刪除wh910n藍牙設備 重新配對藍牙耳機

獨立顯卡和集成顯卡切換電腦卡住了怎么辦?

你是不是也遇到過這種情況——正忙著切換顯卡呢&#xff0c;電腦突然就卡住了&#xff0c;鼠標不動、屏幕定格&#xff0c;怎么按都沒反應&#xff1f;其實這種問題挺常見的&#xff0c;尤其是用了雙顯卡的筆記本或者工作站。別急著強制關機&#xff0c;嗯&#xff0c;咱們一步…

Java根據模版導出PDF文件

問題 工作中經常有這樣的需求&#xff0c;將一些數據&#xff0c;導出為下圖的PDF文件&#xff0c;那Java怎么做呢&#xff1f;今天手把手教你 準備模版 模版地址&#xff1a;https://download.csdn.net/download/ZHUSHANGLIN/91923381 修改模版使用AcrobatProPortable工具…

力扣hot100:環形鏈表(快慢指針法)(141)

一、題目描述二、思路分析這是鏈表題目中的經典問題&#xff0c;核心就是 如何判斷鏈表是否有環。 常見的兩種方法有&#xff1a;哈希表法&#xff1a;用一個集合存儲訪問過的節點&#xff0c;如果再次遇到相同節點說明有環。缺點&#xff1a;需要額外的空間&#xff0c;空間復…

AI 智能編碼工具:重塑開發效率的革命,從 GitHub Copilot 到國產新秀的全面解析

目錄 引言 一、主流智能編碼工具深度測評&#xff1a;從功能到實戰 1. GitHub Copilot&#xff1a;AI 編碼的 “開山鼻祖” 核心特性與實戰代碼 優缺點總結 2. Baidu Comate&#xff1a;文心大模型加持的 “國產之光” 核心特性與實戰代碼 優缺點總結 3. 通義靈碼&…

Server 13 ,CentOS 上使用 Nginx 部署多個前端項目完整指南( 支持多端口與腳本自動化 )

目錄 前言 一、實際背景 1.1 并行部署 1.2 接口代理 1.3 刷新問題 二、安裝腳本 2.1 創建腳本 2.2 不同系統 2.3 執行完成 三、配置文件 3.1 配置文件 3.2 目錄結構 3.3 重新啟動 四、驗證訪問 五、問題排查 5.1 訪問 404 5.2 接口 502 六、本文總結 6.1 清理…

2025最新:徹底解決Docker拉取鏡像超時問題

文章目錄&#x1f433; 解決 Docker 拉取鏡像超時&#xff1a;context deadline exceeded 完整指南&#xff08;2025 親測有效&#xff09;&#x1f525; 問題描述&#x1f9e9; 根本原因分析? 解決方案匯總? 方案 1&#xff1a;配置多源鏡像加速器&#xff08;推薦&#xff…

小鵬汽車 vla 算法最新進展和模型結構細節

小鵬汽車在 VLA&#xff08;視覺 - 語言 - 動作&#xff09;算法領域的最新進展和模型結構細節&#xff0c;體現了其在端到端智駕系統和車端大模型部署上的技術突破。以下是基于 2025 年 9 月最新公開信息的深度解析&#xff1a; 一、最新進展&#xff1a;全場景 VLA 系統量產落…

斐波那契數列推廣

目錄 問題&#xff1a; 法一&#xff1a; 法二&#xff1a; 例題: 問題&#xff1a; 已知斐波那契數列的第一個和最后一個數字&#xff0c;如何求整個數列&#xff08;即第二個數字&#xff09; 法一&#xff1a; 主要是將數列拆分成兩個數列的思想 法二&#xff1a; 暴力…

基于STM32設計的智慧路燈(華為云IOT)_281

文章目錄 一、前言 1.1 項目介紹 【1】項目開發背景 【2】設計實現的功能 【3】項目硬件模塊組成 【4】設計意義 【5】國內外研究現狀 【6】摘要 1.2 設計思路 1.3 系統功能總結 1.4 開發工具的選擇 【1】設備端開發 【2】上位機開發 1.5 參考文獻 1.6 系統框架圖 1.7 系統原理…

實驗十 合理定義分布列實現性能優化-分布式表關聯

實驗介紹本實驗通過分析普通查詢過程中存在的性能瓶頸點&#xff0c;通過執行計劃的分析找到可能的性能優化點并加以實施&#xff0c;最終達到優化的效果&#xff0c;重點關注分布式關聯相關查詢語句的優化。實驗目的了解通過合理定義分布列實現分布式關聯的性能優化。實驗步驟…

C#,RabbitMQ從入門到精通,.NET8.0(路由/分布式/主題/消費重復問題 /延遲隊列和死信隊列/消息持久化 )/RabbitMQ集群模式

為什么使用消息隊列 消息隊列&#xff08;MQ&#xff09;在分布式系統中用于解耦生產者和消費者&#xff0c;提高系統的異步處理能力、削峰填谷、增強可擴展性和可靠性。通過消息隊列&#xff0c;任務可以異步執行&#xff0c;避免系統因瞬時高并發而崩潰。 消息隊列場景 異…

OpenHarmony之SELinux安全組件底層原理設計架構精講

1. 組件介紹 1.1 核心功能 **SELinux(安全增強式Linux)**是Linux歷史上杰出的安全組件,包含一組內核修改和用戶空間工具,并提供了基于安全策略的強制訪問控制機制(Mandatory Access Control,MAC)。本部件負責對文件、屬性、服務等系統資源提供強制訪問控制保護,提供n…

IIS 部署 asp.net core 項目時,出現500.19、500.31問題的解決方案

目錄 &#xff08;一&#xff09;500.19 問題 1. 問題說明 2. 原因 3. 解決 &#xff08;二&#xff09;500.31 問題 1. 問題說明 2. 原因 打開事件檢視器的3種方式&#xff1a; 3. 解決 &#xff08;一&#xff09;500.19 問題 1. 問題說明 2. 原因 Web項目發布時&am…

中大型水閘安全監測的重要性及實施方法

水閘作為水利工程體系中的關鍵性構筑物&#xff0c;其結構安全性和運行可靠性直接影響到整個水利系統的穩定運行&#xff0c;更與下游地區人民群眾的生命財產安全息息相關。作為水利樞紐工程的重要控制節點&#xff0c;水閘承擔著防洪排澇、灌溉供水、航運發電等多重功能&#…

【芯片設計-信號完整性 SI 學習 1.1.1 -- Unit Interval,比特周期】

文章目錄1. Unit Interval (UI) / 比特周期 的定義2. 舉例說明3. 在眼圖 (Eye Diagram) 中的體現4. 示意圖(a) 單比特周期(b) 不同速率下的 UI(c) 眼圖中的 UI5. 總結1. Unit Interval (UI) / 比特周期 的定義 在高速信號傳輸與 信號完整性 (SI) 測試中&#xff0c;Unit Inter…

Go語言開發工具全解析

Go 語言的開發工具生態對于提高開發效率、保證代碼質量和團隊協作至關重要。一套完善的工具鏈可以幫助開發者&#xff1a;1. 加速編碼過程代碼模板快速生成常見模式例如使用代碼片段(Snippet)快速生成HTTP服務框架自動生成測試用例模板實時語法檢查減少錯誤即時顯示類型不匹配錯…

[郵件服務器core] 安全通信(SSL/TLS) | OpenSSL庫管理 | 服務端安全SECURITY.md

第5章&#xff1a;安全通信&#xff08;SSL/TLS&#xff09; 歡迎回來 在第4章&#xff1a;服務運行中&#xff0c;我們學習了如何啟動Dovecot郵件服務器并使其運行。 現在&#xff0c;我們的服務器已經啟動并準備好處理電子郵件&#xff0c;但有一個關鍵問題&#xff1a;我…

Lodash方法總結

目錄 1. _.defaults()為對象填充默認值 基本語法 功能說明 示例代碼 注意事項 與其他類似方法的區別 2. _.pickBy()刪除對象中值為空串或 null 的屬性 實現方法 代碼說明 擴展&#xff1a;深層過濾 3._.omitBy()移除滿足條件的屬性 基本語法 核心功能 示例代碼 1…

C#---Expression(表達式)

前言&#xff1a;Expression 是C# 高級編程&#xff0c;表達式的應用場景有 ORM框架&#xff1a;Entity Framework&#xff0c;Dapper等&#xff0c;規則引擎&#xff1a;動態業務規則評估&#xff0c; 依賴注入&#xff1a;高級DI容器實現&#xff0c;測試框架&#xff1a;模擬…