C#使用ffmpeg录视频和拍照

这篇具有很好参考价值的文章主要介绍了C#使用ffmpeg录视频和拍照。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

c#使用ffmpeg调用aforge引用包实现摄像设备拍照及录像

1.添加引用

AForge.dll 是框架的核心基础类库,为其他类库提供服务。
AForge.Controls.dll 包含AForge.Net的UI控件,主要用于页面显示。
AForge.Imaging.dll 主要是框架中用于图像处理的类库,主要负责图像的处理
AForge.Video.dll 主要是框架中对视频处理的类库。
AForge.Video.DirectShow.dll 主要是通过DirectShow接口访问视频资源的类库。AForge.Video.FFMPEG.dll 是一个还未正式发布的类库,通过FFMPEG类库对视频进行读写。

但是特别注意,vs的NGet目前没有AForge.Video.FFMPEG.dll,你得去AForge官网或者第三方平台http://www.aforgenet.com/framework/downloads.html里下载,可以用迅雷之类的下载器下载,他们的离线下载是可以翻墙的。

c# ffmpeg,c#,视频

选择了“Download Installer”,右键“复制链接地址”,然后放进迅雷下载。

下载下来之后是一个压缩包,AForge.Video.FFMPEG.dll就放在压缩包的Release文件夹中。

c# ffmpeg,c#,视频

2.调用(注意有坑)

引用运行报错如下:

c# ffmpeg,c#,视频

因为这个AForge.Video.FFMPEG使用VC++写的,编译的时候已经被编译成本地代码,而我们现在C#一般目标平台都是“Any CPU”,所以会发生这个问题。

解决办法不再使用“Any CPU”作为目标平台,改成“x86”或者“x64”。因为x86可以跑在x64上,而x64不能在x86上跑,所以我选择了x86。

c# ffmpeg,c#,视频

 再按次调试,又报错如下:

c# ffmpeg,c#,视频

这个AForge.Video.FFMPEG其实是在内部调用FFMPEG来工作的。所以这个FileNotFoundException其实是AForge.Video.FFMPEG找不到FFMPEG的文件所以抛出来的。AForge.Video.FFMPEG依赖的FFMPEG组件其实已经放在了刚刚下载下来的压缩包的\Externals\ffmpeg\bin目录下:

c# ffmpeg,c#,视频 

把这个8个文件复制到程序目录下,注意我们刚刚改过目标平台了,现在程序编译输出的目录已经是\bin\x86\Debug,不要复制错了。

再次调试又报错如下:

c# ffmpeg,c#,视频 

因为本项目目标框架是.net Framework 4.0,而AForge官方在编译AForge.Video.FFMPEG.dll的时候,目标框架选的是.net Framework 2.0……

解决方案有三种:

1.降低自己项目的目标.net Framework版本;

2.修改Config文件;

3.重新编译Video.FFMPEG。

这里我就讲一下方法二,

在Visual Studio中按Ctrl+Shift+A,打开“添加新项”窗口,选择“应用程序配置文件”,再点击“添加”(vs2017创建的时候已经自带了App.config无需再次添加)

打开新建的App.Config文件,在<configuration>和</configuration>标签中加入以下内容:

<startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
  </startup>

c# ffmpeg,c#,视频 

3.界面UI设计:

c# ffmpeg,c#,视频

这里解释以下界面组成及控件使用
此界面分上下两部分,其中上半部分分左,中,右三个(这里讲解左,其它类似)
上部分由一个GroupBox包裹一个AForge.NET工具包下VideoSourcePlayer控件命名为vsp_Panel组成
中间部分由一个Label,一个ComBox下拉框cb_CameraSelect,两个Button按钮btn_Connect、btn_Close组成
下部分由一个GroupBox包裹一个AForge.NET工具包下PictureBox控件命名为gb_CapturePanel组成
界面下部分由由一个GroupBox包裹5个Button按钮btn_StartVideo、btn_PauseVideo、btn_EndVideo、btn_Capture、btn_Save。以及两个label,其中录制时间: 这个label命名无所谓后续用不到,而另外一个默认显示为00:00:00的label命名为lab_Time

 4.完整代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;


namespace Camera
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        #region 属性
        /// <summary>
        /// 设备源
        /// 用来操作摄像头
        /// </summary>
        private AForge.Video.DirectShow.VideoCaptureDevice videoCapture;
        private AForge.Video.DirectShow.VideoCaptureDevice videoCapture2;
        private AForge.Video.DirectShow.VideoCaptureDevice videoCapture3;
        /// <summary>
        /// 摄像头设备集合
        /// </summary>
        private AForge.Video.DirectShow.FilterInfoCollection infoCollection;
        /// <summary>
        /// 图片
        /// </summary>
        private System.Drawing.Bitmap imgMap;
        private System.Drawing.Bitmap imgMap2;
        private System.Drawing.Bitmap imgMap3;
        /// <summary>
        /// 文件保存路径
        /// </summary>
        private readonly string filePath = $"C:\\Users\\wang jun\\Desktop\\工作\\德富莱智能科技股份有限公司\\项目\\摄像头测通讯\\";
        /// <summary>
        /// 文件名称
        /// </summary>
        private  string fileName = $"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}";
        private  string fileName2 = $"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}2";
        private  string fileName3 = $"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}3";
        /// <summary>
        /// 用与把每一帧图像写入到视频文件
        /// </summary>
        private AForge.Video.FFMPEG.VideoFileWriter videoWriter;
        private AForge.Video.FFMPEG.VideoFileWriter videoWriter2;
        private AForge.Video.FFMPEG.VideoFileWriter videoWriter3;
        /// <summary>
        /// 是否开始录像
        /// </summary>
        private bool IsStartVideo = false;
        /// <summary>
        /// 写入次数
        /// </summary>
        private int tick_num = 0;
        /// <summary>
        /// 录制多少小时,只是为了定时间计算录制时间使用
        /// </summary>
        private int Hour = 0;
        /// <summary>
        /// 计时器
        /// </summary>
        private System.Timers.Timer timer_count;
        #endregion
        
        #region 窗口加载与关闭
        /// <summary>
        /// 讲题加载事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            InitCamera();
            InitCameraSelect();
            this.btn_Capture.Enabled = false;
            this.btn_Save.Enabled = false;
            this.gb_CapturePanel.SizeMode = PictureBoxSizeMode.Zoom;//图片大小按比例适应控件,不加的话图片显示问题太大
            this.gb_CapturePanel.Visible = false;//默认不显示照片区域,拍照时在显示
                                                 //秒表
            this.gb_CapturePanel2.SizeMode = PictureBoxSizeMode.Zoom;
            this.gb_CapturePanel2.Visible = false;
            this.gb_CapturePanel3.SizeMode = PictureBoxSizeMode.Zoom;
            this.gb_CapturePanel3.Visible = false;
            timer_count = new System.Timers.Timer();   //实例化Timer类,设置间隔时间为1000毫秒;
            timer_count.Elapsed += new System.Timers.ElapsedEventHandler(tick_count);   //到达时间的时候执行事件;
            timer_count.AutoReset = true;   //设置是执行一次(false)还是一直执行(true);
            timer_count.Interval = 1000;
        }
        /// <summary>
        /// 窗口关闭时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            EndCamera();
        }
        #endregion

        #region 实例化
        /// <summary>
        /// 实例化摄像头
        /// </summary>
        public void InitCamera()
        {
            try
            {
                //检测电脑上的摄像头设备
                infoCollection = new AForge.Video.DirectShow.FilterInfoCollection(AForge.Video.DirectShow.FilterCategory.VideoInputDevice);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"没有找到设备:{ex.Message}", "Error");
            }

        }
        //加载摄像头选择
        public void InitCameraSelect()
        {
            this.cb_CameraSelect.DropDownStyle = ComboBoxStyle.DropDownList;
            this.cb_CameraSelect2.DropDownStyle = ComboBoxStyle.DropDownList;
            this.cb_CameraSelect3.DropDownStyle = ComboBoxStyle.DropDownList;
            //根据读取到的摄像头加载选择项
            foreach (AForge.Video.DirectShow.FilterInfo item in infoCollection)
            {
                this.cb_CameraSelect.Items.Add(item.Name);
                this.cb_CameraSelect2.Items.Add(item.Name);
                this.cb_CameraSelect3.Items.Add(item.Name);
            }
            cb_CameraSelect.Text = (string)cb_CameraSelect.Items[0];
            cb_CameraSelect2.Text = (string)cb_CameraSelect.Items[1];
            //需要改动cb_CameraSelect3.Text = (string)cb_CameraSelect.Items[2];
            cb_CameraSelect3.Text = (string)cb_CameraSelect.Items[0];
        }
        #endregion

        #region  摄像头的连接读取与关闭
        /// <summary>
        /// 连接按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Connect_Click(object sender, EventArgs e)
        {
            var CameraSelect = this.cb_CameraSelect.Text;
            var selectIndex = this.cb_CameraSelect.SelectedIndex;
            var selectIndex2 = this.cb_CameraSelect2.SelectedIndex;
            var selectIndex3 = this.cb_CameraSelect3.SelectedIndex;

            //已有连接的摄像头时先关闭
            if (videoCapture != null|| videoCapture2 !=null|| videoCapture3 !=null)
            {
                EndCamera();
            }
            videoCapture = new AForge.Video.DirectShow.VideoCaptureDevice(infoCollection[selectIndex].MonikerString);
            videoCapture2 = new AForge.Video.DirectShow.VideoCaptureDevice(infoCollection[selectIndex2].MonikerString);
            videoCapture3 = new AForge.Video.DirectShow.VideoCaptureDevice(infoCollection[selectIndex3].MonikerString);
            //启动摄像头
            this.vsp_Panel.VideoSource = videoCapture;
            this.vsp_Panel2.VideoSource = videoCapture2;
            this.vsp_Panel3.VideoSource = videoCapture3;
            this.vsp_Panel.Start();
            this.vsp_Panel2.Start();
            this.vsp_Panel3.Start();
            this.btn_Capture.Enabled = true;
            this.btn_Save.Enabled = false;
        }
        /// <summary>
        /// 关闭按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Close_Click(object sender, EventArgs e)
        {
            EndCamera();
        }
        /// <summary>
        /// 关闭摄像头
        /// </summary>
        public void EndCamera()
        {
            if (this.vsp_Panel.VideoSource != null)
            {
                //也可以用 Stop() 方法关闭
                //指示灯停止且等待
                this.vsp_Panel.SignalToStop();               
                this.vsp_Panel.WaitForStop();//停止且等待
                this.vsp_Panel.VideoSource = null;              
            }
            if (this.vsp_Panel2.VideoSource != null)
            {
                this.vsp_Panel2.SignalToStop();
                this.vsp_Panel2.WaitForStop();
                this.vsp_Panel2.VideoSource = null;
            }
            if (this.vsp_Panel3.VideoSource != null)
            {
                this.vsp_Panel3.SignalToStop();
                this.vsp_Panel3.WaitForStop();
                this.vsp_Panel3.VideoSource = null;
            }
        }

        #endregion

        #region 拍照与保存
        /// <summary>
        /// 拍照
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Capture_Click(object sender, EventArgs e)
        {
            imgMap = this.vsp_Panel.GetCurrentVideoFrame();
            imgMap2 = this.vsp_Panel2.GetCurrentVideoFrame();
            imgMap3 = this.vsp_Panel3.GetCurrentVideoFrame();
            this.gb_CapturePanel.Visible = true;
            this.gb_CapturePanel2.Visible = true;
            this.gb_CapturePanel3.Visible = true;
            this.gb_CapturePanel.Image = imgMap;
            this.gb_CapturePanel2.Image = imgMap2;
            this.gb_CapturePanel3.Image = imgMap3;
            this.btn_Save.Enabled = true;
        }
        /// <summary>
        /// 保存事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Save_Click(object sender, EventArgs e)
        {
            var saveName = $"{filePath}{fileName}.jpg";
            imgMap.Save(saveName);
            var saveName2 = $"{filePath}{fileName2}.jpg";
            imgMap2.Save(saveName2);
            var saveName3 = $"{filePath}{fileName3}.jpg";
            //需改动imgMap3.Save(saveName3);
            this.btn_Save.Enabled = false;
            fileName = $"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}";
            fileName2 = $"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}2";
            fileName3 = $"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}3";
            //MessageBox.Show($"保存成功");
        }
        #endregion
        
        #region 录像
        /// <summary>
        /// 开始录像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_StartVideo_Click(object sender, EventArgs e)
        {
            this.lab_Time.Text="00:00:00";
            string videoName = $"{filePath}{fileName}.mp4";
            string videoName2 = $"{filePath}{fileName2}.mp4";
            string videoName3 = $"{filePath}{fileName3}.mp4";
            IsStartVideo = true;
            
            //开始计时
            timer_count.Enabled = true;
            //配置录像参数(宽,高,帧率,比特率等参数)VideoCapabilities这个属性会返回摄像头支持哪些配置,从这里面选一个赋值接即可
            videoCapture.VideoResolution = videoCapture.VideoCapabilities[0];
            videoCapture2.VideoResolution = videoCapture2.VideoCapabilities[0];
            videoCapture3.VideoResolution = videoCapture3.VideoCapabilities[0];
            //设置回调,aforge会不断从这个回调推出图像数据
            videoCapture.NewFrame += Camera_NewFrame;
            videoCapture2.NewFrame += Camera_NewFrame2;
            videoCapture3.NewFrame += Camera_NewFrame3;
            videoWriter = new AForge.Video.FFMPEG.VideoFileWriter();
            videoWriter2 = new AForge.Video.FFMPEG.VideoFileWriter();
            videoWriter3 = new AForge.Video.FFMPEG.VideoFileWriter();
            //打开摄像头
            //打开录像文件(如果没有则创建,如果有也会清空),这里还有关于视频宽高、帧率、格式、比特率
            videoWriter.Open(
               videoName,
               videoCapture.VideoResolution.FrameSize.Width,
               videoCapture.VideoResolution.FrameSize.Height,
               //videoCapture.VideoResolution.AverageFrameRate,
               6,//帧率
               AForge.Video.FFMPEG.VideoCodec.MPEG4,
               videoCapture.VideoResolution.BitCount
            );
            videoWriter2.Open(
               videoName2,
               videoCapture2.VideoResolution.FrameSize.Width,
               videoCapture2.VideoResolution.FrameSize.Height,
               //videoCapture.VideoResolution.AverageFrameRate,
               6,//帧率
               AForge.Video.FFMPEG.VideoCodec.MPEG4,
               videoCapture2.VideoResolution.BitCount
            );
            videoWriter3.Open(
             videoName3,
             videoCapture3.VideoResolution.FrameSize.Width,
             videoCapture3.VideoResolution.FrameSize.Height,
             //videoCapture.VideoResolution.AverageFrameRate,
             6,//帧率
             AForge.Video.FFMPEG.VideoCodec.MPEG4,
             videoCapture3.VideoResolution.BitCount
          );
        }
        //摄像头1输出回调
        private void Camera_NewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs)
        {
            imgMap = eventArgs.Frame;
            lock (imgMap)
            {
                if (eventArgs != null && eventArgs.Frame != null)
                {
                    try
                    {
                        //写到文件
                        videoWriter.WriteVideoFrame(eventArgs.Frame);
                    }
                    catch (Exception ex)
                    {
                    }
                }
            }
        }
        //摄像头2输出回调
        private void Camera_NewFrame2(object sender, AForge.Video.NewFrameEventArgs eventArgs)
        {
            imgMap2 = eventArgs.Frame;
            lock (imgMap2)
            {
                if (eventArgs != null && eventArgs.Frame != null)
                {
                    try
                    {
                        //写到文件
                        videoWriter2.WriteVideoFrame(eventArgs.Frame);
                    }
                    catch (Exception ex)
                    {
                    }
                }
            }
        }
        //摄像头3输出回调
        private void Camera_NewFrame3(object sender, AForge.Video.NewFrameEventArgs eventArgs)
        {
            imgMap3 = eventArgs.Frame;
            lock (imgMap3)
            {
                if (eventArgs != null && eventArgs.Frame != null)
                {
                    try
                    {
                        //写到文件
                        //需改动videoWriter3.WriteVideoFrame(eventArgs.Frame);
                    }
                    catch (Exception ex)
                    {
                    }
                }
            }
        }
        /// <summary>
        /// 停止录像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_EndVideo_Click(object sender, EventArgs e)
        {
            IsStartVideo = false;
            timer_count.Enabled = false;
            videoWriter.Close();
            videoWriter2.Close();
            videoWriter3.Close();
            fileName = $"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}";
            fileName2 = $"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}2";
            fileName3 = $"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}3";
            MessageBox.Show("停止录像");
            HourStr="00";
            MinuteStr = "00";
            SecondStr = "00";
            tick_num = 0;

        }
        #endregion
        string HourStr =null;
        string MinuteStr=null;
        string SecondStr=null;
        #region 计时器响应函数
        /// <summary>
        /// 计时器
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        public void tick_count(object source, System.Timers.ElapsedEventArgs e)
        {
            tick_num++;
            int Temp = tick_num;
            int Second = Temp % 60;
            int Minute = Temp / 60;
            if (Minute % 60 == 0)
            {
                Hour = Minute / 60;
                Minute = 0;
            }
            else
            {
                Minute = Minute - Hour * 60;
            }
            HourStr = Hour < 10 ? $"0{Hour}" : Hour.ToString();
            MinuteStr = Minute < 10 ? $"0{Minute}" : Minute.ToString();
            SecondStr = Second < 10 ? $"0{Second}" : Second.ToString();
            String tick = $"{HourStr}:{MinuteStr}:{SecondStr}";
            this.Invoke((EventHandler)(delegate
            {
                this.lab_Time.Text = tick;
            }));
        }
        #endregion
    }
}

 文章来源地址https://www.toymoban.com/news/detail-761109.html

到了这里,关于C#使用ffmpeg录视频和拍照的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C#使用EmguCV播放视频

    目录 一、前言 1、简介  2、测试工程代码下载链接 3、EmguCV 库文件下载链接 二、工程环境配置 1、EmguCV控件添加引用 (1)窗口控件添加  (2)相关Dll文件添加添加引用 (3)工程运行基础文件夹添加  (4)调试运行 2、界面设计 (1)整体布局  (2)设置Image相关属性  3、

    2024年02月14日
    浏览(41)
  • C#使用PPT组件的CreateVideo方法生成视频

    目录 需求 实现 CreateVideo方法 关键代码 CreateVideoStatus 其它 我们在使用PowerPoint文档时,经常会使用其导出功能以创建视频,如下图: 手工操作下,在制作好PPT文件后,点击文件 - 导出 - 创建视频 - 设置导出选项 - 点击创建视频即可,下面我们介绍一下如何使用C#来实现这一需

    2024年02月08日
    浏览(37)
  • C#使用OpenCv(OpenCVSharp)使用摄像头视频显示和录制及图片保存、本地视频显示

    本篇实例讲解基于OpenCvSharp实现了摄像头视频显示、录制及截图、视频保存,本地视频的显示功能。 目录 创建winform项目添加控件 NuGet安装opencvsharp  代码  运行效果 实例实现过程

    2024年02月15日
    浏览(48)
  • C#开发FFMPEG例子(API方式) FFmpeg推送udp组播流

    代码及工程见https://download.csdn.net/download/daqinzl/88156926 开发工具:visual studio 2019 播放,可采用ffmpeg工具集里的ffplay.exe, 执行命令 ffplay udp://238.1.1.10:6016 也可以参考(C#开发FFMPEG例子(API方式) FFmpeg拉取udp组播流并播放) https://blog.csdn.net/daqinzl/article/details/132112075 网上用C/C++调用FFmp

    2024年02月14日
    浏览(41)
  • C#制作简易视频播放器

    1.打开vs2019新建一个窗体项目,选用框架.net framework4.7.2 2.在【工具箱】里随便选中一个控件,右键单击它 看到图片中的【选择项】了嘛,单击它,打开添加组件的一个窗体 在添加组件窗体中的【COM组件】中找到【Window Media Player】组件并勾选,然后【确定】 3.可以看到我们的

    2024年02月12日
    浏览(56)
  • Compose使用OpenGL+CameraX快速实现相机“拍视频实时滤镜“、”拍照+滤镜“

    一、前言 短视频热潮还没有褪去,写这篇文章主要是帮助大部分人,能快速上手实现类似效果,实际上是: CameraX拿相机数据,OpenGL给CameraX提供一个Surface,数据放到OpenGL渲染的线程上去做图像相关操作 OpenGL滤镜来自 aserbao_AndroidCamera 视频录制核心代码参考改造自 Google 的 g

    2023年04月17日
    浏览(47)
  • c# winform播放MP4视频

    1、在所有windows窗体中添加windows media player控件     2、拖入windows media player控件,编写代码  

    2024年02月12日
    浏览(44)
  • 测试C#调用Vlc.DotNet组件播放视频

      除了Windows Media Player组件,在百度上搜索到还有不少文章介绍采用Vlc.DotNet组件播放视频,关于Vlc.DotNet的详细介绍见参考文献1,本文学习Vlc.DotNet的基本用法。   VS2022中新建基于.net core的winform程序,在Nuget包管理器中搜索Vlc.DotNet,选择其中的Vlc.DotNet.Forms包,该包用于

    2024年02月06日
    浏览(42)
  • c# 视频播放之Windows Media Player

    最近想给软件加个视频播放功能,在网上看有好几个方式,最后决定用 Windows Media Player 和Vlc.DotNet.Forms。 这篇文章主要讲Windows Media Player,它的优点:代码简单,视频操作功能都有,能播放网络和本地视频。缺点:需要电脑安装视频对应的解码器,适应性很差,只适合自己玩

    2024年01月23日
    浏览(48)
  • C#读取RTSP流并且录制显示视频(PictrueBox)

    下载Nuget包:EMGU.CV 引用Emgu.CV; 参考资料: https://www.cnblogs.com/LCLBook/p/16649870.html 另一种使用方法:挂载事件操作(速度稍慢) 参考数据:http://t.zoukankan.com/chengNet-p-11724429.html

    2024年02月11日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包