GStreamer開發筆記(三):測試gstreamer/v4l2+sdl2/v4l2+QtOpengl打攝像頭延遲和內存

若該文為原創文章,轉載請注明原文出處
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/147714800

長沙紅胖子Qt(長沙創微智科)博文大全:開發技術集合(包含Qt實用技術、樹莓派、三維、OpenCV、OpenGL、ffmpeg、OSG、單片機、軟硬結合等等)持續更新中…

FFmpeg、SDL和流媒體開發專欄


上一篇:《GStreamer開發筆記(二):GStreamer在ubnutn平臺部署安裝,測試gstreamer/cheese/ffmpeg/fmplayer打攝像頭延遲和內存》
下一篇:持續補充中…


前言

??前面測試了多種技術路線,本篇補全剩下的2種主流技術,v4l2+sdl2(偏底層),v4l2+QtOpengl(應用),v4l2+ffmpeg+QtQImage(Image的方式轉圖低于1ms,但是從yuv格式轉到rgb格式需要ffmpeg進行轉碼耗時)。


Demo

??在這里插入圖片描述


注意

??存在色彩空間不準確,不進行細究。


延遲和內存對比

步驟一:v4l2代碼測試延遲和內存

??沒有找到命令行,只找到了v4l2-ctl可以查看和控制攝像頭的參數。
??看gsteamer的源頭就是v4l2src,隨手寫個代碼使用v4l2打開攝像頭查看延遲,其中v4l2是個框架負責操作和捕獲,無法直接進行渲染顯示,本次使用了SDL進行顯示。
??注意:這里不對v4l2介紹,會有專門的專欄去講解v4l2的多媒體開發,但是這里使用v4l2的代碼寫個簡單的程序來打開。

sudo apt-get install libsdl2-dev libsdl2-2.0-0

??然后寫代碼,代碼貼在Demo里面
??在這里插入圖片描述

??在這里插入圖片描述

步驟二:v4l2+QtOpenGL+memcpy復制一次

??在這里插入圖片描述

??查看內存:
??在這里插入圖片描述

步驟三:v4l2+QtOpenGL+共享內存

??在這里插入圖片描述


最終總結

??到這里,我們得出結論,gstreamer基本是最優秀的框架之一了,初步測試不是特別嚴謹,但是基本能反應情況(比如ffmpeg得fmplay本輪測試是最差,但是ffmpeg寫代碼可以進行ffmpeg源碼和編程代碼的優化,達到150ms左右,諸如這類情況不考慮)。
??V4l2+SDL優于gstreamer優于ffmplayer優于v4l2+QtOpenGL優于cheese優于ffmpeg。
??其中v4l2+SDL、gstreamer、fmplayer在內存占用上有點區別,延遲差不多130ms左右。Cheese和v4l2+QtOpenGL延遲差不多
到170ms。Ffmpeg的播放器延遲到500ms左右。


擴展

??這里要注意,大部分低延遲內窺鏡筆者接觸的都是buffer疊顯存的方式,少數廠家使用v4l2+QtOpenGL的方式,經過測試慢了一幀左右。


Demo:V4l2+SDL

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <errno.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_pixels.h>#define WIDTH 640
#define HEIGHT 480int main() {setbuf(stdout, NULL);int fd;struct v4l2_format fmt;struct v4l2_requestbuffers req;struct v4l2_buffer buf;void *buffer_start;unsigned int buffer_length;// 打開攝像頭設備fd = open("/dev/video0", O_RDWR);if (fd == -1) {perror("打開攝像頭設備失敗");return EXIT_FAILURE;}// 設置視頻格式memset(&fmt, 0, sizeof(fmt));fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt.fmt.pix.width = WIDTH;fmt.fmt.pix.height = HEIGHT;fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {perror("設置視頻格式失敗");close(fd);return EXIT_FAILURE;}// 請求緩沖區memset(&req, 0, sizeof(req));req.count = 1;req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;req.memory = V4L2_MEMORY_MMAP;if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {perror("請求緩沖區失敗");close(fd);return EXIT_FAILURE;}// 映射緩沖區memset(&buf, 0, sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = 0;if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {perror("查詢緩沖區失敗");close(fd);return EXIT_FAILURE;}buffer_length = buf.length;buffer_start = mmap(NULL, buffer_length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);if (buffer_start == MAP_FAILED) {perror("映射緩沖區失敗");close(fd);return EXIT_FAILURE;}// 將緩沖區放入隊列if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {perror("緩沖區入隊失敗");munmap(buffer_start, buffer_length);close(fd);return EXIT_FAILURE;}// 開始視頻捕獲enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {perror("開始視頻捕獲失敗");munmap(buffer_start, buffer_length);close(fd);return EXIT_FAILURE;}// 初始化 SDLif (SDL_Init(SDL_INIT_VIDEO) < 0) {fprintf(stderr, "SDL 初始化失敗: %s\n", SDL_GetError());munmap(buffer_start, buffer_length);close(fd);return EXIT_FAILURE;}SDL_Window *window = SDL_CreateWindow("V4L2 Camera", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, 0);if (!window) {fprintf(stderr, "創建 SDL 窗口失敗: %s\n", SDL_GetError());SDL_Quit();munmap(buffer_start, buffer_length);close(fd);return EXIT_FAILURE;}SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);// SDL_PIXELFORMAT_YV12 =      /**< Planar mode: Y + V + U  (3 planes) */// SDL_PIXELFORMAT_IYUV =      /**< Planar mode: Y + U + V  (3 planes) */// SDL_PIXELFORMAT_YUY2 =      /**< Packed mode: Y0+U0+Y1+V0 (1 plane) */// SDL_PIXELFORMAT_UYVY =      /**< Packed mode: U0+Y0+V0+Y1 (1 plane) */// SDL_PIXELFORMAT_YVYU =      /**< Packed mode: Y0+V0+Y1+U0 (1 plane) *///    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
//    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YUY2, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
//    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_UYVY, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
//    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YVYU, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);int running = 1;SDL_Event event;while (running) {// 處理事件while (SDL_PollEvent(&event)) {if (event.type == SDL_QUIT) {running = 0;}}// 捕獲幀memset(&buf, 0, sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) {perror("出隊緩沖區失敗");break;}// 更新 SDL 紋理SDL_UpdateTexture(texture, NULL, buffer_start, WIDTH);// 渲染紋理SDL_RenderClear(renderer);SDL_RenderCopy(renderer, texture, NULL, NULL);SDL_RenderPresent(renderer);// 將緩沖區重新入隊if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {perror("緩沖區入隊失敗");break;}}// 清理資源SDL_DestroyTexture(texture);SDL_DestroyRenderer(renderer);SDL_DestroyWindow(window);SDL_Quit();munmap(buffer_start, buffer_length);close(fd);return EXIT_SUCCESS;
}

Demo:V4l2+QtOpenGL+共享內存

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <errno.h>
#include "DisplayOpenGLWidget.h"
#include <QApplication>
#include <QElapsedTimer>#define WIDTH 640
#define HEIGHT 480#include <QDebug>
#include <QDateTime>
//#define LOG qDebug()<<__FILE__<<__LINE__
//#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__
//#define LOG qDebug()<<__FILE__<<__LINE__<<QThread()::currentThread()
//#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd")
#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz")int main(int argc, char *argv[])
{QApplication a(argc, argv);DisplayOpenGLWidget displayOpenGLWidget;displayOpenGLWidget.show();setbuf(stdout, NULL);int fd;struct v4l2_format fmt;struct v4l2_requestbuffers req;struct v4l2_buffer buf;void *buffer_start;unsigned int buffer_length;// 打開攝像頭設備fd = open("/dev/video0", O_RDWR);if (fd == -1) {perror("打開攝像頭設備失敗");return EXIT_FAILURE;}// 設置視頻格式memset(&fmt, 0, sizeof(fmt));fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt.fmt.pix.width = WIDTH;fmt.fmt.pix.height = HEIGHT;fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {perror("設置視頻格式失敗");close(fd);return EXIT_FAILURE;}// 請求緩沖區memset(&req, 0, sizeof(req));req.count = 1;req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;req.memory = V4L2_MEMORY_MMAP;if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {perror("請求緩沖區失敗");close(fd);return EXIT_FAILURE;}// 映射緩沖區memset(&buf, 0, sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = 0;if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {perror("查詢緩沖區失敗");close(fd);return EXIT_FAILURE;}buffer_length = buf.length;buffer_start = mmap(NULL, buffer_length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);if (buffer_start == MAP_FAILED) {perror("映射緩沖區失敗");close(fd);return EXIT_FAILURE;}displayOpenGLWidget.initDrawBuffer(WIDTH, HEIGHT, true, (char *)buffer_start);// 將緩沖區放入隊列if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {perror("緩沖區入隊失敗");munmap(buffer_start, buffer_length);close(fd);return EXIT_FAILURE;}// 開始視頻捕獲enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {perror("開始視頻捕獲失敗");munmap(buffer_start, buffer_length);close(fd);return EXIT_FAILURE;}int running = 1;while (running) {// 捕獲幀memset(&buf, 0, sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) {perror("出隊緩沖區失敗");break;}// 渲染
//        memcpy(drawBuffer, buffer_start, buffer_length);displayOpenGLWidget.displayVideoFrame();QApplication::processEvents();QApplication::processEvents();QApplication::processEvents();// 將緩沖區重新入隊if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {perror("緩沖區入隊失敗");break;}}// 清理資源munmap(buffer_start, buffer_length);close(fd);return EXIT_SUCCESS;
}

入坑

入坑一:v4l2打開視頻代碼不對

問題

??V4l2打開視頻代碼數據錯位
??在這里插入圖片描述

原因

??紋理格式不同,但是筆者測試了SDL所有支持的,都不行,不鉆了,是需要進行色彩空間轉換下才可以(會額外消耗一定延遲,預估10ms以內),我們選個可以的,測試延遲內存即可。

// SDL_PIXELFORMAT_YV12 =      /**< Planar mode: Y + V + U  (3 planes) */
// SDL_PIXELFORMAT_IYUV =      /**< Planar mode: Y + U + V  (3 planes) */
// SDL_PIXELFORMAT_YUY2 =      /**< Packed mode: Y0+U0+Y1+V0 (1 plane) */
// SDL_PIXELFORMAT_UYVY =      /**< Packed mode: U0+Y0+V0+Y1 (1 plane) */
// SDL_PIXELFORMAT_YVYU =      /**< Packed mode: Y0+V0+Y1+U0 (1 plane) *///    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
//    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YUY2, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
//    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_UYVY, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
//    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YVYU, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);

解決

??不解決,選擇能看清楚的就好了。


上一篇:《GStreamer開發筆記(二):GStreamer在ubnutn平臺部署安裝,測試gstreamer/cheese/ffmpeg/fmplayer打攝像頭延遲和內存》
下一篇:持續補充中…


本文章博客地址:https://blog.csdn.net/qq21497936/article/details/147714800

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

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

相關文章

CATIA高效工作指南——零件建模篇(二)

一、PowerCopy特征復用技術 1.1 智能特征封裝 通過??幾何圖形集(Geometrical Set)??構建參數化特征組&#xff0c;將關聯的草圖、曲面、實體等元素進行邏輯封裝。操作流程如下&#xff1a; 創建新幾何圖形集并完成特征建模激活PowerCopy命令&#xff0c;選擇目標幾何集定…

CentOS 7 安裝OpenJDK 17 JRE

CentOS 7 自帶的java 版本為&#xff1a;java version "1.8.0_311"&#xff0c; 有些軟件的運行需要更高的java版本。CentOS 7 自帶的默認倉庫里 沒有 OpenJDK 17&#xff0c;但是 Adoptium 項目&#xff08;前身 AdoptOpenJDK&#xff09;提供了穩定的 OpenJDK 17 版…

【c++】 我的世界

太久沒更新小游戲了 給個贊和收藏吧&#xff0c;求求了 要游戲的請私聊我 #include <iostream> #include <vector>// 定義世界大小 const int WORLD_WIDTH 20; const int WORLD_HEIGHT 10;// 定義方塊類型 enum BlockType {AIR,GRASS,DIRT,STONE };// 定義世界…

angular的cdk組件庫

目錄 一、虛擬滾動 一、虛擬滾動 <!-- itemSize相當于每個項目的高度為30px --><!-- 需要給虛擬滾動設置寬高&#xff0c;否則無法正常顯示 --> <cdk-virtual-scroll-viewport [itemSize]"40" class"view_scroll"><div class"m…

視頻編解碼學習三之顯示器

整理自&#xff1a;顯示器_百度百科&#xff0c;觸摸屏_百度百科,百度安全驗證 分為陰極射線管顯示器&#xff08;CRT&#xff09;&#xff0c;等離子顯示器PDP&#xff0c;液晶顯示器LCD 液晶顯示器的組成。一般來說&#xff0c;液晶顯示器由以下幾個部分組成&#xff1a; […

尚硅谷-硅谷甄選項目記錄

一、Vue3 1 基礎配置 1.1 路徑別名 vite.config.ts import { defineConfig } from vite import vue from vitejs/plugin-vue// 引入path&#xff0c;node提供的模塊&#xff0c;可以獲取文件或文件夾的路徑 import path from pathexport default defineConfig({plugins: […

ThinkPHP5 case when的使用

主表的類型不同&#xff0c;需要連接查詢不同的表時&#xff0c;可以使用case when 在主表(上面的表中)的模型里面&#xff0c;寫如下代碼來獲取列表&#xff1a; $res $this->alias("p")->field([p.*,"CASE WHEN p.pd_ticket_type 1 THEN (select s.s…

Android 10.0 SharedPreferences in credential encrypted storage are not avai

1.前言 在10.0的系統rom定制化開發中,在開機的過程中,由于某些應用在開機解鎖階段就開始訪問查詢短信和聯系人等功能,所以 會出現拋異常的情況出現,接下來分析下相關的情況,然后來解決這些問題 2.SharedPreferences in credential encrypted storage are not available …

LeetCode[102]二叉樹的層序遍歷

思路&#xff1a; 題目描述從左到右一層一層的進行遍歷&#xff0c;就遍歷二叉樹的這種題我更喜歡用遞歸來做&#xff0c; 我使用java來做的&#xff0c;結果集是兩個List集合&#xff0c;那么我們是不是應該每到新的一層就給這個結果集添加一個內部的List&#xff0c;那么怎么…

二種MVCC對比分析

文章目錄 前言MVCCInnodb的MVCC版本鏈回滾與提交可見性判斷 Oracle的MVCC版本鏈 PostgreSQL的MVCCMVCC實現可見性判斷特點 前言 ? MVCC&#xff08;多版本并發控制&#xff0c;Multi-Version Concurrency Control&#xff09;是一種數據庫管理系統&#xff08;DBMS&#x…

FAST-LIO筆記

1.FAST-LIO FAST-LIO 是一個計算效率高、魯棒性強的激光-慣性里程計系統。該系統通過緊耦合的迭代擴展卡爾曼濾波器&#xff08;IEKF&#xff09;將激光雷達特征點與IMU數據進行融合&#xff0c;使其在快速運動、噪聲較大或環境復雜、存在退化的情況下仍能實現穩定的導航。 1…

C++并發編程完全指南:從基礎到實踐

在當今多核處理器普及的時代&#xff0c;充分利用硬件并發能力已成為高性能編程的關鍵。C11引入的現代并發編程支持使得開發者能夠以標準化、可移植的方式編寫多線程程序。本文將全面介紹C并發編程的各個方面&#xff0c;從基礎概念到實際應用&#xff0c;幫助您掌握這一重要技…

如何使用docker配置ros-noetic環境并使用rviz,gazebo

參考鏈接&#xff1a;【Ubuntu】Docker中配置ROS并可視化Rviz及Gazebo_docker ros-CSDN博客 前言&#xff1a; 其實這個東西是相當必要的&#xff0c;因為我們有時候需要在一臺電腦上跑好幾個項目&#xff0c;每個項目都有不同的依賴&#xff0c;這些依賴沖突搞得人頭皮發麻&…

使用 Java 11 的 HttpClient 處理 RESTful Web 服務

在現代 Web 開發中,與 RESTful Web 服務交互是一項核心任務。Java 作為一種廣泛使用的編程語言,提供了多種處理 HTTP 請求的方法。在 Java 11 之前,開發者通常使用 HttpURLConnection 或第三方庫(如 Apache HttpClient)。然而,這些方法要么過于底層,要么需要額外依賴。J…

JVM——垃圾回收

垃圾回收 在Java虛擬機&#xff08;JVM&#xff09;的自動內存管理中&#xff0c;垃圾回收&#xff08;Garbage Collection, GC&#xff09;是其核心組件之一。它負責回收堆內存中不再使用的對象所占用的內存空間&#xff0c;以供新對象的分配使用。下面我們將深入探討JVM中的…

經典密碼學算法實現

# AES-128 加密算法的規范實現&#xff08;不使用外部庫&#xff09; # ECB模式S_BOX [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B,0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0x…

追蹤大型語言模型的思想(下)(來自針對Claude的分析)

多步推理 正如我們上面所討論的&#xff0c;語言模型回答復雜問題的一種方式就是簡單地記住答案。例如&#xff0c;如果問“達拉斯所在州的首府是哪里&#xff1f;”&#xff0c;一個“機械”的模型可以直接學會輸出“奧斯汀”&#xff0c;而無需知道德克薩斯州&#xff0c;達拉…

【Hive入門】Hive增量數據導入:基于Sqoop的關系型數據庫同步方案深度解析

目錄 引言 1 增量數據導入概述 1.1 增量同步與全量同步對比 1.2 增量同步技術選型矩陣 2 Sqoop增量導入原理剖析 2.1 Sqoop架構設計 2.2 增量同步核心機制 3 Sqoop增量模式詳解 3.1 append模式&#xff08;基于自增ID&#xff09; 3.2 lastmodified模式&#xff08;基…

[Windows] 藍山看圖王 1.0.3.21021

[Windows] 藍山看圖王 鏈接&#xff1a;https://pan.xunlei.com/s/VOPejo0dRLFd38dbpR7XA6djA1?pwddq9v# 由藍山軟件出品的一款免費高清看圖軟件&#xff0c;支持快速查看高清大圖&#xff0c;支持大部分市面圖片格式。 軟件特點 1、體積小巧&#xff0c;圖像顯示效果清晰…

通配符 DNS 記錄:應用場景與相關風險

隨著組織的互聯網基礎設施不斷擴展&#xff0c;其對配置、設置和決策的需求也隨之增加——從選擇一個可靠的名稱服務器&#xff0c;到確定合適的 DNS 記錄類型以及設置合適的 TTL&#xff08;生存時間&#xff09;值。其中一項關鍵決策就是是否要創建通配符 DNS 記錄&#xff0…