vivi虛擬攝像頭驅動程式

来源:http://www.cnblogs.com/linhaostudy/archive/2017/08/14/7356839.html
-Advertisement-
Play Games

一、vivi虛擬攝像頭驅動 基於V4L2(video for linux 2)攝像頭驅動程式,我們減去不需要的ioctl_fops的函數,只增加ioctl函數增加的必要的攝像頭流查詢等函數; 二、虛擬攝像頭驅動應用程式調用過程流程圖: 虛擬攝像頭一般不用自己寫的程式,而是採用網路上提供的應用程式直接 ...


一、vivi虛擬攝像頭驅動

基於V4L2(video for linux 2)攝像頭驅動程式,我們減去不需要的ioctl_fops的函數,只增加ioctl函數增加的必要的攝像頭流查詢等函數;

  1 #include <linux/module.h>
  2 #include <linux/module.h>
  3 #include <linux/delay.h>
  4 #include <linux/errno.h>
  5 #include <linux/fs.h>
  6 #include <linux/kernel.h>
  7 #include <linux/slab.h>
  8 #include <linux/mm.h>
  9 #include <linux/ioport.h>
 10 #include <linux/init.h>
 11 #include <linux/sched.h>
 12 #include <linux/pci.h>
 13 #include <linux/random.h>
 14 #include <linux/version.h>
 15 #include <linux/mutex.h>
 16 #include <linux/videodev2.h>
 17 #include <linux/dma-mapping.h>
 18 #include <linux/interrupt.h>
 19 #include <linux/kthread.h>
 20 #include <linux/highmem.h>
 21 #include <linux/freezer.h>
 22 #include <media/videobuf-vmalloc.h>
 23 #include <media/v4l2-device.h>
 24 #include <media/v4l2-ioctl.h>
 25 
 26 static struct video_device *myvivi_device;
 27 static struct timer_list myvivi_timer;
 28 static struct list_head myvivi_vb_local_queue;
 29 
 30 static void myvivi_timer_function(unsigned long data)
 31 {
 32     struct videobuf_buffer *vb;
 33     void *vbuf;
 34     struct timeval ts;
 35     
 36     /* 1. 構造數據: 從隊列頭部取出第1個videobuf到本地隊列中, 填充數據
 37      */
 38 
 39     /* 1.1 從本地隊列取出第1個videobuf */
 40     if (list_empty(&myvivi_vb_local_queue)) {
 41         goto out;
 42     }
 43     
 44     vb = list_entry(myvivi_vb_local_queue.next,
 45              struct videobuf_buffer, queue);
 46     
 47     /* Nobody is waiting on this buffer, return */
 48     if (!waitqueue_active(&vb->done))
 49         goto out;
 50     
 51 
 52     /* 1.2 填充數據 */
 53     vbuf = videobuf_to_vmalloc(vb);
 54     memset(vbuf, 0xff, vb->size);
 55     vb->field_count++;
 56     do_gettimeofday(&ts);
 57     vb->ts = ts;
 58     vb->state = VIDEOBUF_DONE;
 59 
 60     /* 1.3 把videobuf從本地隊列中刪除 */
 61     list_del(&vb->queue);
 62 
 63     /* 2. 喚醒進程: 喚醒videobuf->done上的進程 */
 64     wake_up(&vb->done);
 65     
 66 out:
 67     /* 3. 修改timer的超時時間 : 30fps, 1秒里有30幀數據
 68      *    每1/30 秒產生一幀數據
 69      */
 70     mod_timer(&myvivi_timer, jiffies + HZ/30);
 71 }
 72 /* ------------------------------------------------------------------
 73     IOCTL vidioc handling
 74    ------------------------------------------------------------------*/
 75 static int myvivi_videoc_querycap(struct file *file, void  *priv,
 76                     struct v4l2_capability *cap)
 77 {
 78     //VIDIOC_QUERYCAP 命令通過結構 v4l2_capability 獲取設備支持的操作模式:
 79     /*
 80         struct v4l2_capability {
 81         __u8    driver[16];         i.e. "bttv" 
 82         __u8    card[32];        i.e. "Hauppauge WinTV"
 83         __u8    bus_info[32];    "PCI:" + pci_name(pci_dev) 
 84         __u32   version;        should use KERNEL_VERSION() 
 85         __u32    capabilities;     Device capabilities 
 86         __u32    reserved[4];
 87         };
 88      * 
 89      */
 90     strcpy(cap->driver, "myvivi");
 91     strcpy(cap->card, "myvivi");
 92     
 93     cap->version = 0x0001;
 94     cap->capabilities =    V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;        //V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING表示一個視
 95                                                                             //頻捕捉設備並且具有數據流控制模式
 96     
 97     return 0;
 98 }
 99 
100 /* 用於列舉、獲得、測試、設置攝像頭的數據的格式 */
101 /* 列舉支持哪種格式 */
102 static int myvivi_vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
103                     struct v4l2_fmtdesc *f)
104 {
105     /*
106     *    F O R M A T   E N U M E R A T I O N
107     
108     struct v4l2_fmtdesc {
109         __u32            index;             // Format number      , 需要填充,從0開始,依次上升。
110         enum v4l2_buf_type  type;          // buffer type       Camera,則填寫V4L2_BUF_TYPE_VIDEO_CAPTURE
111         __u32               flags;            //    如果壓縮的,則Driver 填寫:V4L2_FMT_FLAG_COMPRESSED,
112         __u8            description[32];   // Description string ,image format的描述,如:YUV 4:2:2 (YUYV)
113         __u32            pixelformat;       // Format fourcc ,所支持的格式。 如:V4L2_PIX_FMT_UYVY    
114         __u32            reserved[4];
115     };
116     */
117 
118     if(f->index >= 1)
119     {
120         return -EINVAL;
121     }
122     strcpy(f->description, "4:2:2, packed, YUYV");
123     
124     //從vivi_fmt結構體中可以看見:
125     f->pixelformat = V4L2_PIX_FMT_YUYV;
126     
127     return 0;
128 }
129 struct v4l2_format myvivi_format;
130 /* 返回當前所使用的格式 */
131 static int myvivi_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
132                     struct v4l2_format *f)
133 {
134     
135     memcpy(f, &myvivi_format, sizeof(myvivi_format));
136 
137     return (0);
138 }
139 
140 /* 測試驅動程式是否支持某種格式 */
141 static int myvivi_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
142             struct v4l2_format *f)
143 {
144     unsigned int maxw, maxh;
145     enum v4l2_field field;
146     
147     
148     if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)
149         return -EINVAL;
150 
151     field = f->fmt.pix.field;
152 
153     if (field == V4L2_FIELD_ANY) {
154         field = V4L2_FIELD_INTERLACED;
155     } else if (V4L2_FIELD_INTERLACED != field) {
156         return -EINVAL;
157     }
158     
159     maxw = 1024;
160     maxh = 768;
161     
162     /*
163      v4l2_format:
164      struct v4l2_format {  
165         enum v4l2_buf_type type;  
166         union {  
167         struct v4l2_pix_format         pix;     // V4L2_BUF_TYPE_VIDEO_CAPTURE 
168         struct v4l2_window             win;     // V4L2_BUF_TYPE_VIDEO_OVERLAY   
169         struct v4l2_vbi_format         vbi;     // V4L2_BUF_TYPE_VBI_CAPTURE 
170         struct v4l2_sliced_vbi_format  sliced;  // V4L2_BUF_TYPE_SLICED_VBI_CAPTURE 
171         __u8   raw_data[200];                   // user-defined   
172     } fmt;  
173     };
174 
175     其中  
176     enum v4l2_buf_type 
177     {  
178         V4L2_BUF_TYPE_VIDEO_CAPTURE        = 1,  
179         V4L2_BUF_TYPE_VIDEO_OUTPUT         = 2,  
180         V4L2_BUF_TYPE_VIDEO_OVERLAY        = 3,  
181         ...  
182         V4L2_BUF_TYPE_PRIVATE              = 0x80,  
183     }; 
184     
185         struct v4l2_pix_format {  
186         __u32                   width;  
187         __u32                   height;  
188         __u32                   pixelformat;  
189         enum v4l2_field         field;  
190         __u32                   bytesperline;   // for padding, zero if unused 
191         __u32                   sizeimage;  
192         enum v4l2_colorspace    colorspace;  
193         __u32                   priv;           // private data, depends on pixelformat 
194     };  
195         
196     常見的捕獲模式為 V4L2_BUF_TYPE_VIDEO_CAPTURE 即視頻捕捉模式,在此模式下 fmt 聯合體採用域 v4l2_pix_format:其中 width 為
197     視頻的寬、height 為視頻的高、pixelformat 為視頻數據格式(常見的值有 V4L2_PIX_FMT_YUV422P | V4L2_PIX_FMT_RGB565)、
198     bytesperline 為一行圖像占用的位元組數、sizeimage 則為圖像占用的總位元組數、colorspace 指定設備的顏色空間。     
199     */    
200     //設置最小寬度和最大寬度等
201     /* 調整format的width, height, 
202      * 計算bytesperline, sizeimage
203      */
204     v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, &f->fmt.pix.height, 32, maxh, 0, 0);
205     
206     f->fmt.pix.bytesperline =
207         (f->fmt.pix.width * 16) >> 3;        //顏色深度支持16
208     f->fmt.pix.sizeimage =
209         f->fmt.pix.height * f->fmt.pix.bytesperline;
210     
211     
212     return 0;
213 }
214 
215 
216 static int myvivi_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
217                     struct v4l2_format *f)
218 {
219     int ret = myvivi_vidioc_try_fmt_vid_cap(file, NULL, f);
220     if (ret < 0)
221         return ret;
222 
223     memcpy(&myvivi_format, f, sizeof(myvivi_format));
224     
225     return ret;
226 }
227 /* 用於列舉、獲得、測試、設置攝像頭的數據的格式 */
228 
229 
230 
231 
232 /* 隊列操作1: 定義 */
233 static struct videobuf_queue myvivi_vb_vidqueue;
234 //自旋鎖
235 static spinlock_t myvivi_queue_slock;
236 
237 
238 /* 參考documentations/video4linux/v4l2-framework.txt:
239  *     drivers\media\video\videobuf-core.c 
240  ops->buf_setup   - calculates the size of the video buffers and avoid they
241             to waste more than some maximum limit of RAM;
242  ops->buf_prepare - fills the video buffer structs and calls
243             videobuf_iolock() to alloc and prepare mmaped memory;
244  ops->buf_queue   - advices the driver that another buffer were
245             requested (by read() or by QBUF);
246  ops->buf_release - frees any buffer that were allocated.
247  
248  *
249  */
250 
251  
252  
253  
254 /*****************buffer operations***********************/
255 /* APP調用ioctl VIDIOC_REQBUFS(videobuf_reqbufs)時會導致此函數被調用,
256  * 它重新調整count和size
257  */
258  static int myvivi_buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
259 {
260     //第一個參數為
261     *size = myvivi_format.fmt.pix.sizeimage;
262 
263     if (0 == *count)
264         *count = 32;
265 
266     return 0;
267 }
268 /* APP調用ioctl VIDIOC_QBUF時導致此函數被調用,
269  * 它會填充video_buffer結構體並調用videobuf_iolock來分配記憶體
270  * 
271  */
272 static int myvivi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
273                         enum v4l2_field field)
274 {
275     /* 1. 做些準備工作 */
276      /* 0. 設置videobuf */
277     vb->size = myvivi_format.fmt.pix.sizeimage;
278     vb->bytesperline = myvivi_format.fmt.pix.bytesperline;
279     vb->width  = myvivi_format.fmt.pix.width;
280     vb->height = myvivi_format.fmt.pix.height;
281     vb->field  = field;
282 #if 0
283     /* 2. 調用videobuf_iolock為類型為V4L2_MEMORY_USERPTR的videobuf分配記憶體 */
284     if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
285         rc = videobuf_iolock(vq, &buf->vb, NULL);
286         if (rc < 0)
287             goto fail;
288     }
289 #endif
290     /* 3. 設置狀態 */
291     vb->state = VIDEOBUF_PREPARED;
292 
293     return 0;
294 }
295 /* APP調用ioctlVIDIOC_QBUF時:
296  * 1. 先調用buf_prepare進行一些準備工作
297  * 2. 把buf放入隊列
298  * 3. 調用buf_queue(起通知作用)通知應用程式正在請求調用
299  */
300 static void myvivi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
301 {
302     vb->state = VIDEOBUF_QUEUED;
303     
304     /* 把videobuf放入本地一個隊列尾部
305      * 定時器處理函數就可以從本地隊列取出videobuf,新增內核鏈表
306      */
307     list_add_tail(&vb->queue, &myvivi_vb_local_queue);
308 }
309 /* APP不再使用隊列時, 用它來釋放記憶體 */
310 static void myvivi_buffer_release(struct videobuf_queue *vq,
311                struct videobuf_buffer *vb)
312 {
313     videobuf_vmalloc_free(vb);
314     vb->state = VIDEOBUF_NEEDS_INIT;
315 }
316 /*****************buffer operations***********************/
317 
318 
319 
320 /* 緩衝區操作: 申請/查詢/放入隊列/取出隊列 */
321 static int myvivi_vidioc_reqbufs(struct file *file, void *priv,
322               struct v4l2_requestbuffers *p)
323 {
324     return (videobuf_reqbufs(&myvivi_vb_vidqueue, p));
325 }
326 static int myvivi_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
327 {
328     return (videobuf_querybuf(&myvivi_vb_vidqueue, p));
329 }
330 static int myvivi_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
331 {
332     return (videobuf_qbuf(&myvivi_vb_vidqueue, p));
333 }
334 static int myvivi_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
335 {
336     return (videobuf_dqbuf(&myvivi_vb_vidqueue, p,
337                 file->f_flags & O_NONBLOCK));
338 }
339 /* 緩衝區操作: 申請/查詢/放入隊列/取出隊列 */
340 /*------------------------------------------------------------------*/
341 
342 
343 /*-------------------開啟與關閉--------------------------*/
344 static int myvivi_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
345 {
346     return videobuf_streamon(&myvivi_vb_vidqueue);
347 }
348 
349 static int myvivi_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
350 {
351     videobuf_streamoff(&myvivi_vb_vidqueue);
352     return 0;
353 }
354 /*-------------------開啟與關閉--------------------------*/
355 
356 
357 
358 
359 /* ------------------------------------------------------------------
360     Videobuf operations
361    ------------------------------------------------------------------*/
362 static struct videobuf_queue_ops myvivi_video_qops = {
363     .buf_setup      = myvivi_buffer_setup, /* 計算大小以免浪費 */
364     .buf_prepare    = myvivi_buffer_prepare,
365     .buf_queue      = myvivi_buffer_queue,
366     .buf_release    = myvivi_buffer_release,
367 };
368 /* ------------------------------------------------------------------
369     File operations for the device
370    ------------------------------------------------------------------*/
371 static int myvivi_open(struct file *file)
372 {
373     /* 隊列操作2: 初始化 */
374     videobuf_queue_vmalloc_init(&myvivi_vb_vidqueue, &myvivi_video_qops,
375             NULL, &myvivi_queue_slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED,
376             sizeof(struct videobuf_buffer), NULL); /* V4L2_BUF_TYPE_VIDEO_CAPTURE用於視頻捕獲設備,倒數第2個參數是buffer的頭部大小 */
377     //myvivi_video_qops這個結構體我們怎麼去處理它呢?
378     
379     
380     myvivi_timer.expires = jiffies + 1;
381     add_timer(&myvivi_timer);
382     return 0;
383 }
384 static int myvivi_close(struct file *file)
385 {
386     del_timer(&myvivi_timer);
387     videobuf_stop(&myvivi_vb_vidqueue);        //stop the buf
388     videobuf_mmap_free(&myvivi_vb_vidqueue);
389     
390     return 0;
391 }
392 static int myvivi_mmap(struct file *file, struct vm_area_struct *vma)
393 {
394     return videobuf_mmap_mapper(&myvivi_vb_vidqueue, vma);
395 }
396 static unsigned int myvivi_poll(struct file *file, struct poll_table_struct *wait)
397 {
398     return videobuf_poll_stream(file, &myvivi_vb_vidqueue, wait);
399 }
400 /*------------------------------------------------------------------*/
401 
402 
403 
404 
405 
406 
407 
408 /*
409 ------------------------------------------------------------
410 struct video_device
411     // device ops 
412     const struct v4l2_file_operations *fops;
413 ------------------------------------------------------------
414                 v4l2_file_operations=
415                 {
416                     struct module *owner;
417                     long (*ioctl) (struct file *, unsigned int, unsigned long);
418                 }
419 
420 
421 
422 ------------------------------------------------------------
423     // callbacks 
424     void (*release)(struct video_device *vdev);
425 ------------------------------------------------------------
426 */
427 static const struct v4l2_ioctl_ops myvivi_ioctl_ops = 
428 {
429     //表示它是一個攝像頭驅動
430     .vidioc_querycap = myvivi_videoc_querycap,
431     
432 
433     /* 用於列舉、獲得、測試、設置攝像頭的數據的格式 */
434     .vidioc_enum_fmt_vid_cap  = myvivi_vidioc_enum_fmt_vid_cap,
435     .vidioc_g_fmt_vid_cap     = myvivi_vidioc_g_fmt_vid_cap,
436     .vidioc_try_fmt_vid_cap   = myvivi_vidioc_try_fmt_vid_cap,
437     .vidioc_s_fmt_vid_cap     = myvivi_vidioc_s_fmt_vid_cap,
438     
439     /* 緩衝區操作: 申請/查詢/放入隊列/取出隊列 */
440     .vidioc_reqbufs       = myvivi_vidioc_reqbufs,
441     .vidioc_querybuf      = myvivi_vidioc_querybuf,
442     .vidioc_qbuf          = myvivi_vidioc_qbuf,
443     .vidioc_dqbuf         = myvivi_vidioc_dqbuf,
444     
445     // 啟動/停止
446     .vidioc_streamon      = myvivi_vidioc_streamon,
447     .vidioc_streamoff     = myvivi_vidioc_streamoff,  
448 
449 };
450 
451 
452 static const struct v4l2_file_operations myvivi_fops = 
453 {
454     .owner = THIS_MODULE,
455     .open = myvivi_open,
456     .release = myvivi_close,
457     .mmap = myvivi_mmap,
458     .poll = myvivi_poll,        //提供了poll函數,還是會出現select timeout的情況,需要構造數據喚醒隊列了,在poll_wait隊列中休眠了
459     .ioctl = video_ioctl2, /* v4l2 ioctl handler,在這個函數中設置了ioctl的用法 */
460 };
461 static void myvivi_release(struct video_device *vdev)
462 {
463     
464 }
465 
466 static int myvivi_init(void)
467 {
468     int error=0;
469     
470     /* 1. 分配一個video_device函數,分配空間 */
471     myvivi_device = video_device_alloc();
472     
473     /* 2. 設置 */
474     
475     /* 2.1 */
476     myvivi_device->release = myvivi_release;
477     /* 2.2 */
478     myvivi_device->fops    = &myvivi_fops;
479     /* 2.3 */
480     myvivi_device->ioctl_ops = &myvivi_ioctl_ops;
481 
482     
483     /* 2.4 隊列操作
484     * a.定義/初始化一個隊列(會用到一個自旋鎖)
485     */
486     spin_lock_init(&myvivi_queue_slock);
487     
488     /*
489     #define VFL_TYPE_GRABBER    0        //表明是一個圖像採集設備-包括攝像頭、調諧器
490     #define VFL_TYPE_VBI        1        //從視頻消隱的時間段取得信息的設備
491     #define VFL_TYPE_RADIO        2        //代表無線電設備
492     #define VFL_TYPE_VTX        3        //代表視傳設備
493     #define VFL_TYPE_MAX        4
494     @nr:   which device number (0 == /dev/video0, 1 == /dev/video1, ...
495              -1 == first free)
496     */
497     /* 3.註冊,第二個參數為VFL_TYPE_GRABBER, -1 為 first free */
498     error = video_register_device(myvivi_device, VFL_TYPE_GRABBER, -1);
499 
500     
501     //用定時器產生數據並喚醒進程
502     init_timer(&myvivi_timer);
503     myvivi_timer.function = myvivi_timer_function;
504     
505     
506     INIT_LIST_HEAD(&myvivi_vb_local_queue);
507     return error;
508 };
509 
510 
511 /*
512  *    出口函數
513  * 
514  */
515 static void myvivi_exit(void)
516 {
517     video_unregister_device(myvivi_device);
518     video_device_release(myvivi_device);
519 }
520 
521 
522 module_init(myvivi_init);
523 module_exit(myvivi_exit);
	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.前提是linux系統已經安裝了上一篇講的Zookeeper和jdk[1.7及以上版本]還有python[centos已經自帶,2.6及以上版本] 2.解壓storm壓縮包 3.配置storm.yaml文件 配置zookeeper(建議配置偶數個,貌似配奇數個會報錯) 配置nimbus 配置sup ...
  • 1 概述 1 概述 本篇文章主要講解SqlServer中類型轉換涉及的兩個函數:CAST和CONVERT。 2 具體內容 2 具體內容 2.1 CAST (1)作用:將一種數據類型的表達式轉換為另一種數據類型的表達式。 (2)定義: Parameters: expression:任何可轉換為 dat ...
  • 如何避免索引失效 1、全值匹配我最愛 建立幾個複合索引欄位,最好就用上幾個欄位。且按照順序來用。 2、最佳左首碼法則 如果索引了多列,要遵守最左首碼法則,指的是查詢從索引的最左前列開始,不跳過索引中間的列。 3、不再索引列上做任何操作(計算、函數、(自動or手動)類型轉換),會導致索引失效而轉向全表 ...
  • MySQL創建: 1.創建資料庫create database test2; 2.刪除資料庫drop database test2;3.創建表create table ceshi( ids int auto_increment primary key, uid varchar(20), name v ...
  • 1 概述 1 概述 本篇文章簡要分析,在SQL SERVER中,AS的基本用法。 2 具體分析 2 具體分析 2.1 定義變數類型 2.2 取別名 2.2.1 為結果集列取別名 code: result: code: result: 2.2.2 為表取別名 2.2.3 為子查詢取別名 2.3 轉換類 ...
  • 使用過oracle或者其他關係資料庫的DBA或者開發人員都有這樣的經驗,在子查詢上都認為資料庫已經做過優化,能夠很好的選擇驅動表執行,然後在把該經驗移植到mysql資料庫上,但是不幸的是,mysql在子查詢的處理上有可能會讓你大失所望,在我們的生產系統上就由於碰到了這個問題: select i_id ...
  • 在mysql伺服器高負載的情況下,必須採取一種措施給伺服器減輕壓力,減少伺服器的I/O操作。一般採用的方法是優化sql操作語句,優化伺服器的配置參數,從而提高伺服器的性能。Mysql使用了幾種記憶體緩存數據的策略來提高性能。 一、mysql的緩存機制 Mysql緩存主要包括關鍵字緩存(key cach ...
  • HBase常用命令 進入shell 表結構 1\. 創建表 語法: 創建一個User表,並且有一個info列族 3\. 查看所有表 4\. 查看表詳情 5\. 表修改 刪除指定的列族 表數據 1\. 插入數據 語法: 2\. 根據rowKey查詢某個記錄 語法: 3\. 查詢所有記錄 語法: 掃描所 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...