C#使用MJpeg实现视频流发送与显示

1、发送视频流:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Mjpeg
{
    public class MJpegStreamingServer
    {
        private static string _contentLengthString = "__PayloadHeaderContentLength__";
        private AsyncTcpServer _server;
        private ConcurrentDictionary<string, TcpClient> _clients;

        public MJpegStreamingServer(int listenPort)
          : this(listenPort, "--dennisgao")
        {
        }

        public MJpegStreamingServer(int listenPort, string boundary)
        {
            Port = listenPort;
            Boundary = boundary;

            _server = new AsyncTcpServer(Port);
            _server.Encoding = Encoding.ASCII;
            _clients = new ConcurrentDictionary<string, TcpClient>();
        }

        /// <summary>
        /// 监听的端口
        /// </summary>
        public int Port { get; private set; }

        /// <summary>
        /// 分隔符
        /// </summary>
        public string Boundary { get; private set; }

        /// <summary>
        /// 流头部
        /// </summary>
        public string StreamHeader
        {
            get
            {
                return "HTTP/1.1 200 OK" +
                    "\r\n" +
                         "Content-Type: multipart/x-mixed-replace; boundary=" + this.Boundary +
                          "\r\n";
            }
        }

        /// <summary>
        /// 图片头部
        /// </summary>
        public string PayloadHeader
        {
            get
            {
                return "\r\n" +
                           this.Boundary +
                               "\r\n" +
                             "Content-Type: image/jpeg" +
                             "\r\n" +
                             "Content-Length: " + _contentLengthString +
                             "\r\n\r\n";
            }
        }

        public void Start()
        {
            _server.Start(10);
            _server.ClientConnected += new EventHandler<TcpClientConnectedEventArgs>(OnClientConnected);
            _server.ClientDisconnected += new EventHandler<TcpClientDisconnectedEventArgs>(OnClientDisconnected);
        }

        public void Stop()
        {
            _server.Stop();
            _server.ClientConnected -= new EventHandler<TcpClientConnectedEventArgs>(OnClientConnected);
            _server.ClientDisconnected -= new EventHandler<TcpClientDisconnectedEventArgs>(OnClientDisconnected);
        }

        private void OnClientConnected(object sender, TcpClientConnectedEventArgs e)
        {
            _clients.AddOrUpdate(e.TcpClient.Client.RemoteEndPoint.ToString(), e.TcpClient, (n, o) => { return e.TcpClient; });
        }

        private void OnClientDisconnected(object sender, TcpClientDisconnectedEventArgs e)
        {
            TcpClient clientToBeThrowAway;
            _clients.TryRemove(e.TcpClient.Client.RemoteEndPoint.ToString(), out clientToBeThrowAway);
        }

        public void Write(Bitmap image)
        {
            if (_server.IsRunning)
            {
                byte[] payload = BytesOf(image);

                WriteStreamHeader();
                WritePayload(payload);
            }
        }

        private void WriteStreamHeader()
        {
            if (_clients.Count > 0)
            {
                foreach (var item in _clients)
                {
                    //Logger.Debug(string.Format(CultureInfo.InvariantCulture,  "Writing stream header, {0}, {1}{2}", item.Key, Environment.NewLine, StreamHeader));

                    _server.Send(item.Value, StreamHeader);

                    TcpClient clientToBeThrowAway;
                    _clients.TryRemove(item.Key, out clientToBeThrowAway);
                }
            }
        }

        private void WritePayload(byte[] payload)
        {
            string payloadHeader = this.PayloadHeader.Replace(_contentLengthString, payload.Length.ToString());
            string payloadTail = "\r\n";

            //Logger.Debug(string.Format(CultureInfo.InvariantCulture, "Writing payload header, {0}{1}", Environment.NewLine, payloadHeader));

            byte[] payloadHeaderBytes = _server.Encoding.GetBytes(payloadHeader);
            byte[] payloadTailBytes = _server.Encoding.GetBytes(payloadTail);
            byte[] packet = new byte[payloadHeaderBytes.Length + payload.Length + payloadTailBytes.Length];
            Buffer.BlockCopy(payloadHeaderBytes, 0, packet, 0, payloadHeaderBytes.Length);
            Buffer.BlockCopy(payload, 0, packet, payloadHeaderBytes.Length, payload.Length);
            Buffer.BlockCopy(payloadTailBytes, 0, packet, payloadHeaderBytes.Length + payload.Length, payloadTailBytes.Length);

            _server.SendAll(packet);
        }

        private byte[] BytesOf(Bitmap image)
        {
            MemoryStream ms = new MemoryStream();
            image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);

            byte[] payload = ms.ToArray();

            return payload;
        }
    }
}

用到的AsyncTcpServer类:

using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.Reflection.Emit;
using System.Text;
using System;

namespace Mjpeg
{
    /// <summary>
    /// 异步TCP服务器
    /// </summary>
    public class AsyncTcpServer : IDisposable
    {
        #region Fields

        private TcpListener listener;
        private List<TcpClientState> clients;
        private bool disposed = false;

        #endregion

        #region Ctors

        /// <summary>
        /// 异步TCP服务器
        /// </summary>
        /// <param name="listenPort">监听的端口</param>
        public AsyncTcpServer(int listenPort)
          : this(IPAddress.Any, listenPort)
        {
        }

        /// <summary>
        /// 异步TCP服务器
        /// </summary>
        /// <param name="localEP">监听的终结点</param>
        public AsyncTcpServer(IPEndPoint localEP)
          : this(localEP.Address, localEP.Port)
        {
        }

        /// <summary>
        /// 异步TCP服务器
        /// </summary>
        /// <param name="localIPAddress">监听的IP地址</param>
        /// <param name="listenPort">监听的端口</param>
        public AsyncTcpServer(IPAddress localIPAddress, int listenPort)
        {
            Address = localIPAddress;
            Port = listenPort;
            this.Encoding = Encoding.Default;

            clients = new List<TcpClientState>();

            listener = new TcpListener(Address, Port);
            listener.AllowNatTraversal(true);
        }

        #endregion

        #region Properties

        /// <summary>
        /// 服务器是否正在运行
        /// </summary>
        public bool IsRunning { get; private set; }
        /// <summary>
        /// 监听的IP地址
        /// </summary>
        public IPAddress Address { get; private set; }
        /// <summary>
        /// 监听的端口
        /// </summary>
        public int Port { get; private set; }
        /// <summary>
        /// 通信使用的编码
        /// </summary>
        public Encoding Encoding { get; set; }

        #endregion

        #region Server

        /// <summary>
        /// 启动服务器
        /// </summary>
        /// <returns>异步TCP服务器</returns>
        public AsyncTcpServer Start()
        {
            if (!IsRunning)
            {
                IsRunning = true;
                listener.Start();
                listener.BeginAcceptTcpClient(
                  new AsyncCallback(HandleTcpClientAccepted), listener);
            }
            return this;
        }

        /// <summary>
        /// 启动服务器
        /// </summary>
        /// <param name="backlog">
        /// 服务器所允许的挂起连接序列的最大长度
        /// </param>
        /// <returns>异步TCP服务器</returns>
        public AsyncTcpServer Start(int backlog)
        {
            if (!IsRunning)
            {
                IsRunning = true;
                listener.Start(backlog);
                listener.BeginAcceptTcpClient(
                  new AsyncCallback(HandleTcpClientAccepted), listener);
            }
            return this;
        }

        /// <summary>
        /// 停止服务器
        /// </summary>
        /// <returns>异步TCP服务器</returns>
        public AsyncTcpServer Stop()
        {
            if (IsRunning)
            {
                IsRunning = false;
                listener.Stop();

                lock (this.clients)
                {
                    for (int i = 0; i < this.clients.Count; i++)
                    {
                        this.clients[i].TcpClient.Client.Disconnect(false);
                    }
                    this.clients.Clear();
                }

            }
            return this;
        }

        #endregion

        #region Receive

        private void HandleTcpClientAccepted(IAsyncResult ar)
        {
            if (IsRunning)
            {
                TcpListener tcpListener = (TcpListener)ar.AsyncState;

                TcpClient tcpClient = tcpListener.EndAcceptTcpClient(ar);
                byte[] buffer = new byte[tcpClient.ReceiveBufferSize];

                TcpClientState internalClient
                  = new TcpClientState(tcpClient, buffer);
                lock (this.clients)
                {
                    this.clients.Add(internalClient);
                    RaiseClientConnected(tcpClient);
                }

                NetworkStream networkStream = internalClient.NetworkStream;
                networkStream.BeginRead(
                  internalClient.Buffer,
                  0,
                  internalClient.Buffer.Length,
                  HandleDatagramReceived,
                  internalClient);

                tcpListener.BeginAcceptTcpClient(
                  new AsyncCallback(HandleTcpClientAccepted), ar.AsyncState);
            }
        }

        private void HandleDatagramReceived(IAsyncResult ar)
        {
            if (IsRunning)
            {
                TcpClientState internalClient = (TcpClientState)ar.AsyncState;
                NetworkStream networkStream = internalClient.NetworkStream;

                int numberOfReadBytes = 0;
                try
                {
                    numberOfReadBytes = networkStream.EndRead(ar);
                }
                catch
                {
                    numberOfReadBytes = 0;
                }

                if (numberOfReadBytes == 0)
                {
                    // connection has been closed
                    lock (this.clients)
                    {
                        this.clients.Remove(internalClient);
                        RaiseClientDisconnected(internalClient.TcpClient);
                        return;
                    }
                }

                // received byte and trigger event notification
                byte[] receivedBytes = new byte[numberOfReadBytes];
                Buffer.BlockCopy(
                  internalClient.Buffer, 0,
                  receivedBytes, 0, numberOfReadBytes);
                RaiseDatagramReceived(internalClient.TcpClient, receivedBytes);
                RaisePlaintextReceived(internalClient.TcpClient, receivedBytes);

                // continue listening for tcp datagram packets
                networkStream.BeginRead(
                  internalClient.Buffer,
                  0,
                  internalClient.Buffer.Length,
                  HandleDatagramReceived,
                  internalClient);
            }
        }

        #endregion

        #region Events

        /// <summary>
        /// 接收到数据报文事件
        /// </summary>
        public event EventHandler<TcpDatagramReceivedEventArgs<byte[]>> DatagramReceived;
        /// <summary>
        /// 接收到数据报文明文事件
        /// </summary>
        public event EventHandler<TcpDatagramReceivedEventArgs<string>> PlaintextReceived;

        private void RaiseDatagramReceived(TcpClient sender, byte[] datagram)
        {
            if (DatagramReceived != null)
            {
                DatagramReceived(this, new TcpDatagramReceivedEventArgs<byte[]>(sender, datagram));
            }
        }

        private void RaisePlaintextReceived(TcpClient sender, byte[] datagram)
        {
            if (PlaintextReceived != null)
            {
                PlaintextReceived(this, new TcpDatagramReceivedEventArgs<string>(
                  sender, this.Encoding.GetString(datagram, 0, datagram.Length)));
            }
        }

        /// <summary>
        /// 与客户端的连接已建立事件
        /// </summary>
        public event EventHandler<TcpClientConnectedEventArgs> ClientConnected;
        /// <summary>
        /// 与客户端的连接已断开事件
        /// </summary>
        public event EventHandler<TcpClientDisconnectedEventArgs> ClientDisconnected;

        private void RaiseClientConnected(TcpClient tcpClient)
        {
            if (ClientConnected != null)
            {
                ClientConnected(this, new TcpClientConnectedEventArgs(tcpClient));
            }
        }

        private void RaiseClientDisconnected(TcpClient tcpClient)
        {
            if (ClientDisconnected != null)
            {
                ClientDisconnected(this, new TcpClientDisconnectedEventArgs(tcpClient));
            }
        }

        #endregion

        #region Send

        /// <summary>
        /// 发送报文至指定的客户端
        /// </summary>
        /// <param name="tcpClient">客户端</param>
        /// <param name="datagram">报文</param>
        public void Send(TcpClient tcpClient, byte[] datagram)
        {
            if (!IsRunning)
                throw new InvalidProgramException("This TCP server has not been started.");

            if (tcpClient == null)
                throw new ArgumentNullException("tcpClient");

            if (datagram == null)
                throw new ArgumentNullException("datagram");

            tcpClient.GetStream().BeginWrite(
              datagram, 0, datagram.Length, HandleDatagramWritten, tcpClient);
        }

        private void HandleDatagramWritten(IAsyncResult ar)
        {
            ((TcpClient)ar.AsyncState).GetStream().EndWrite(ar);
        }

        /// <summary>
        /// 发送报文至指定的客户端
        /// </summary>
        /// <param name="tcpClient">客户端</param>
        /// <param name="datagram">报文</param>
        public void Send(TcpClient tcpClient, string datagram)
        {
            Send(tcpClient, this.Encoding.GetBytes(datagram));
        }

        /// <summary>
        /// 发送报文至所有客户端
        /// </summary>
        /// <param name="datagram">报文</param>
        public void SendAll(byte[] datagram)
        {
            if (!IsRunning)
                throw new InvalidProgramException("This TCP server has not been started.");

            for (int i = 0; i < this.clients.Count; i++)
            {
                Send(this.clients[i].TcpClient, datagram);
            }
        }

        /// <summary>
        /// 发送报文至所有客户端
        /// </summary>
        /// <param name="datagram">报文</param>
        public void SendAll(string datagram)
        {
            if (!IsRunning)
                throw new InvalidProgramException("This TCP server has not been started.");

            SendAll(this.Encoding.GetBytes(datagram));
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing,
        /// releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release
        /// both managed and unmanaged resources; <c>false</c>
        /// to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    try
                    {
                        Stop();

                        if (listener != null)
                        {
                            listener = null;
                        }
                    }
                    catch (SocketException ex)
                    {
                        //ExceptionHandler.Handle(ex);
                    }
                }

                disposed = true;
            }
        }

        #endregion
    }
}

调用:实现截屏发送

 MJpegStreamingServer server = new MJpegStreamingServer(1022);
            server.Start();
            Task.Run(() =>
            {
                while (true)
                {
                    try
                    {
                        var bit = CaptureActiveScreen();
                        server.Write(bit);
                        Thread.Sleep(50);
                    }
                    catch { }
                };

            });

截屏代码:

/// <summary>
        /// 截屏
        /// </summary>
        /// <returns></returns>
        public Bitmap CaptureActiveScreen()
        {
            // 创建一个和当前屏幕一样大小的Bitmap
            Rectangle bounds = Screen.GetBounds(Point.Empty);
            Bitmap bmp = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb);

            // 创建一个画布
            Graphics g = Graphics.FromImage(bmp);

            // 截取整个屏幕
            g.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);

            // 释放画布资源
            g.Dispose();

            return bmp;
        }

2、显示Mjpeg流:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Mjpeg
{
    public class MjpegUtils : IDisposable
    {
        public bool IsExit = false;
        /// <summary>
        /// 播放MJPEG视频流
        /// </summary>
        /// <param name="mjpegStreamUrl">MJPEG视频流地址</param>
        /// <param name="accessImageHandler">读到图片后处理</param>
        /// <param name="sleep">每次读取文件睡眠时间,太长会导致卡顿,太短可能频繁矫正流</param>
        public MjpegUtils(string mjpegStreamUrl, Action<byte[]> accessImageHandler, int sleep = 50)
        {
            HttpWebRequest hwRequest = (System.Net.HttpWebRequest)WebRequest.Create(mjpegStreamUrl);
            hwRequest.Method = "GET";
            HttpWebResponse hwResponse = (HttpWebResponse)hwRequest.GetResponse();
            string boundary = hwRequest.Headers["Content-Type:"];
            //响应流中没有响应头信息,全是响应体内容
            Stream stream = hwResponse.GetResponseStream();
            string headerName = "Content-Length:";
            StringBuilder sb = new StringBuilder();
            int len = 1024;
            while (true)
            {
                while (true)
                {
                    char c = (char)stream.ReadByte();
                    //Console.Write(c);
                    if (c == '\n')
                    {
                        break;
                    }
                    sb.Append(c);
                }
                string line = sb.ToString();
                sb.Remove(0, sb.Length);
                int i = line.IndexOf(headerName);
                if (i != -1)
                {

                    int imageFileLength = Convert.ToInt32(line.Substring(i + headerName.Length).Trim());
                    stream.Read(new byte[2], 0, 2);
                    byte[] imageFileBytes = new byte[imageFileLength];
                    stream.Read(imageFileBytes, 0, imageFileBytes.Length);
                    //Console.WriteLine("文件头:" + imageFileBytes[0].ToString("X") + " " + imageFileBytes[1].ToString("X") + " " + imageFileBytes[2].ToString("X") + " " + imageFileBytes[3].ToString("X") + " " + imageFileBytes[4].ToString("X"));
                    //Console.WriteLine("文件尾:" + imageFileBytes[imageFileLength - 2].ToString("X") + " " + imageFileBytes[imageFileLength - 1].ToString("X"));
                    if (imageFileBytes[imageFileLength - 2].ToString("X") != "FF" && imageFileBytes[imageFileLength - 1].ToString("X") != "D9")
                    {
                        //Console.WriteLine("开始矫正...");
                        //修正
                        char l = '0';
                        while (true)
                        {
                            char c = (char)stream.ReadByte();
                            if (l == '-' && c == '-')
                            {
                                break;
                            }
                            l = c;
                        }
                    }
                    else
                    {
                        //读取图片成功!
                        accessImageHandler(imageFileBytes);
                    }
                    Thread.Sleep(sleep);
                }

            }
            stream.Close();
            hwResponse.Close();
            Console.Read();
        }

        public int StreamFindContentLength(Stream stream)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append((char)stream.ReadByte());
            sb.Append((char)stream.ReadByte());
            sb.Append((char)stream.ReadByte());
            sb.Append((char)stream.ReadByte());
            Console.WriteLine("num:" + sb);
            int num = Convert.ToInt32(sb.ToString().Trim());
            //跳过\r\n
            char c = '0';
            do
            {
                c = (char)stream.ReadByte();
            } while (c == '\r' || c == '\n');
            return num;
        }

        public void Dispose()
        {
            IsExit = true;
        }



    }
}

调用:

    var thread = new Thread(new ThreadStart(delegate ()
             {
                 var mjpegUtils = new MjpegUtils("http://127.0.0.1:1022", delegate (byte[] bytes)
                   {
                       //Console.WriteLine(bytes.Length);
                       MemoryStream ms = new MemoryStream(bytes);
                       pictureBox1.Image = Image.FromStream(ms);
                       ms.Close();
                   }, 50);
             }));
            thread.IsBackground = true;
            thread.Start();

最终效果:

源码下载

 参考文章:

https://blog.csdn.net/weixin_33709364/article/details/86419401

https://www.jb51.net/article/206675.htm

https://www.cnblogs.com/gaochundong/archive/2013/04/14/csharp_async_tcp_server.html

https://www.cnblogs.com/gaochundong/archive/2013/04/14/csharp_async_tcp_client.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/752239.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

企业级数据库18条黄金设计原则(全网最详细!!!)

前言 1.名字 2.字段类型 3.字段长度 4.字段个数 5. 主键 6.存储引擎 7. NOT NULL 8.外键(不建议使用外健) 9. 索引 10.时间字段 11.金额字段 12. json字段 13.唯一索引 14.字符集 15. 排序规则 16.大字段 17.冗余字段 18.注释 前言 后端开发的日常工作&#…

超高速PCIe EtherCAT运动控制卡在高速异形插件机上的应用

市场应用背景 异形插件机主要应用于电子制造业中&#xff0c;专门用于自动化电子装配过程。它能够按照预设程序将编带或者供料器的电子元件通过多工位多头夹取&#xff0c;将电阻、电容、晶体管、连接器和按键等异形元件自动插装到PCB板的指定通孔上&#xff0c;极大地提高生产…

【PL理论深化】(9) Ocaml 语言:自定义类型 | 异常处理 | 模块

&#x1f4ac; 写在前面&#xff1a;本章我们将继续介绍 OCaml 的基本特性&#xff0c;自定义类型、异常处理和模块。掌握了这些内容后&#xff0c;编写基本程序应该不会有太大困难。接下来的两节将学习函数式编程中常用的两种编程风格 —— 递归函数和高阶函数。 目录 0x00 …

基于盲信号处理的声音分离——最小化增益的ICA算法

基于最小化增益的ICA算法的算法是依据混合信号经过盲信号分离会产生一定的噪声&#xff0c;为了使得分离后的信号与原信号的比值最小时&#xff0c;叫做增益最小。当增益越小时&#xff0c;分离后噪声越小&#xff0c;分离信号越接近原信号&#xff0c;分离算法的效果越好。这是…

防止多次点击,vue的按钮上做简易的防抖节流处理

话不多说,上个视频,看看是不是你要的效果 防抖节流 1.创建一个directive.js // directive.js export default {install(Vue) {// 防重复点击(指令实现)Vue.directive(repeatClick, {inserted(el, binding) {el.addEventListener(click, () > {if (!el.disabled) {el.disabl…

【Stable Diffusion】AI绘画美女,搞副业,赚钱真香!(内附高质量美女提示词)

前言 今天就直接上干货&#xff0c;给铁子们上一些生成高质量美女的提示词&#xff0c;每一种美女类型都附有魔法咒语&#xff0c;可应用于midjourney和stablediffusion&#xff0c;直接复制即可。 话不多说&#xff0c;直接上图&#xff0c;上提示词&#xff0c;请欣赏&…

三大工作流引擎技术Activiti、Flowable、Camunda选型指南

文章目录 前言1 流程引擎发展历程2 流程引擎主要概念BPM (Business Process Management)BPMN (Business Process Model and Notation)CMMN (Case Management Model and Notation)DMN (Decision Model and Notation)事件&#xff08;Event&#xff09;顺序流&#xff08;Sequenc…

控制台扫雷(C语言实现)

目录 博文目的实现思路项目创建文件解释 具体实现判断玩家进行游戏还是退出扫雷棋盘的确定地图初始化埋雷玩家扫雷的实现雷判断函数 源码game.cgame.h扫雷.c 博文目的 相信不少人都学习了c语言的函数&#xff0c;循环&#xff0c;分支那我们就可以写一个控制台的扫雷小游戏来检…

【AI落地应用实战】如何让扫描工具更会思考——智能高清滤镜2.0实战测评

一、引言 在这个信息爆炸的数字化时代&#xff0c;扫描工具已经成为我们日常工作和学习中不可或缺的助手。最近&#xff0c;扫描全能王推出了革命性的“智能高清滤镜2.0”&#xff0c;本次更新后&#xff0c;智能高清滤镜能够智能识别并优化扫描过程中的各种问题。无论是光线不…

【乐吾乐2D可视化组态编辑器】图元外观编辑

1 节点的外观样式 角度&#xff1a;设置尖角与圆角&#xff0c;值的范围&#xff1a;0~1 旋转&#xff1a;设置图形的旋转角度 进度&#xff1a;任意封闭图形&#xff0c;都可以当进度条&#xff1a;矩形、圆、svg、封闭连线、或其他任意封闭图形&#xff0c;值的范围&#…

强化学习:值函数近似【Deep Q-Network,DQN,Deep Q-learning】

强化学习笔记 主要基于b站西湖大学赵世钰老师的【强化学习的数学原理】课程&#xff0c;个人觉得赵老师的课件深入浅出&#xff0c;很适合入门. 第一章 强化学习基本概念 第二章 贝尔曼方程 第三章 贝尔曼最优方程 第四章 值迭代和策略迭代 第五章 强化学习实例分析:GridWorld…

【升压电子烟方案】DC-DC电源升压恒压芯片FP6277+全极低功耗霍尔MH251在电子烟中的应用

电子烟是一种新型烟草制品&#xff0c;由于其健康、环保和口感多样化的特点&#xff0c;逐渐受到了消费者的青睐。 升压芯片作为电子烟的核心组件之一&#xff0c;主要作用是将输入的电压升高至合适的工作电压&#xff0c;霍尔传感器控制电子烟的使用状态&#xff0c;以确保电子…

springboot系列七: Lombok注解,Spring Initializr,yaml语法

老韩学生 LombokLombok介绍Lombok常用注解Lombok应用实例代码实现idea安装lombok插件 Spring InitializrSpring Initializr介绍Spring Initializr使用演示需求说明方式1: IDEA创建方式2: start.spring.io创建 注意事项和说明 yaml语法yaml介绍使用文档yaml基本语法数据类型字面…

鸿蒙开发HarmonyOS NEXT (二) 熟悉ArkUI

一、构造函数 构造一个商品类Item&#xff0c;然后利用foreach函数循环渲染 class Item {name: stringimage: ResourceStrprice: numberdiscount: numberconstructor(name: string, image: ResourceStr, price: number, discount: number 0) {this.name name;this.image ima…

自动化测试小技巧之Airtest-Selenium和Excel的无缝协作

一、前言 之前在问卷以及Q群上有同学有提出过能否将网页上的一些数据通过Airtest去导出生成一份Excel&#xff0c;那么我们今天一起讨论一下&#xff0c;我们应该如何去实现&#xff0c;以及当我们获取的数据类型不同的时候&#xff0c;获取的方式该怎么随之调整&#xff1f; …

MySQL InnoDB支持几种行格式

数据库表的行格式决定了一行数据是如何进行物理存储的&#xff0c;进而影响查询和DML操作的性能。 在InnoDB中&#xff0c;常见的行格式有4种&#xff1a; 1、COMPACT&#xff1a;是MySQL 5.0之前的默认格式&#xff0c;除了保存字段值外&#xff0c;还会利用空值列表保存null…

如何使用git将本地文件夹提交至云效代码库

目录 引言 一、准备工作 二、配置Git用户信息 三、克隆代码库到本地 四、添加文件至Git仓库 五、推送更改至云效代码库 六、如何删除云效文件 七、注意事项 八、总结 引言 随着团队协作开发的日益普及&#xff0c;代码版本控制系统&#xff08;VCS&#xff09;如Git已…

java+mysql图书管理系统

完整代码地址 1.运行效果图 2.主要代码 2.1.连接数据库 package com.my.homework.utils;import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException;public class JDBCUtils {public static Connection getConnection() throws Exception {…

力压GPT-4o!新王Claude 3.5 Sonnet来了,直接免费可用

如今&#xff0c;大模型领域更卷了&#xff01; 前脚 OpenAI 发布 GPT4o&#xff0c;硬控全场&#xff0c;后脚就被最大的竞争对手 Anthropic 超越了。 刚刚&#xff0c;Anthropic 发布了全新大模型 Claude 3.5 Sonnet&#xff0c;号称是迄今为止最智能的模型。 据介绍&#x…

CSS|05 继承性与优先级

继承性 一、继承性的特点&#xff1a; 1.外层元素身上的样式会被内层元素所继承 2.如果内层元素与外层元素身上的演示相同时&#xff0c;外层元素的样式会被内层元素所覆盖 二、关于继承性的问题 是不是所有样式都能被继承&#xff1f; 答&#xff1a;并不是所有样式能被继承…