using Accord.Video.FFMPEG; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ScreenBroadcast { internal class Program { //[DllImport("user32.dll", SetLastError = true)] //static extern bool GetWindowRect(int hWnd, out RECT lpRect); // For Windows Mobile, replace user32.dll with coredll.dll [DllImport("user32.dll", SetLastError = true)] private static extern int FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll", SetLastError = true)] private static extern bool GetClientRect(int hWnd, out Rect lpRect); [DllImport("user32.dll")] private static extern bool ClientToScreen(int hWnd, ref Point lpPoint); [StructLayout(LayoutKind.Sequential)] public struct Rect { public int Left; public int Top; public int Right; public int Bottom; public int Height => Bottom - Top; public int Width => Right - Left; public Size Size => new Size(Width, Height); public Point Location => new Point(Left, Top); } private static void Main() { //comfig var string wndCls = "ConsoleWindowClass"; string wndName = "管理员: 命令提示符"; int port = 2333; var frameRate = 25; var broadcastQuality = 20L; //runtime var var runing = true; int clientNum = 0; byte[] buf = null; var waits = new List(); var taskSource = Task.Factory.StartNew(() => { //image encoding params var format = ImageFormat.Jpeg; var codec = ImageCodecInfo.GetImageDecoders().First(p => p.FormatID == format.Guid); var codecParam = new EncoderParameters(1); //buffer vars Bitmap imgBuf = null; var vdoWriter = new VideoFileWriter(); var ms = new MemoryStream(); var hWnd = 0; try { while (runing) { if (hWnd == 0) hWnd = FindWindow(wndCls, wndName); Rect rectWndCa; var ptWndLoc = Point.Empty; if (GetClientRect(hWnd, out rectWndCa) && ClientToScreen(hWnd, ref ptWndLoc)) { Func ensureBy2 = v => v % 2 != 0 ? v + 1 : v; Func ensureBy4 = v => v % 4 != 0 ? v + (4 - v % 4) : v; Func ensureBy = (b, v) => v % b != 0 ? v + (b - v % b) : v; var rect = new Rectangle(ptWndLoc, new Size(ensureBy(2, rectWndCa.Size.Width), ensureBy(2, rectWndCa.Height))); if (imgBuf == null || imgBuf.Size != rect.Size) { imgBuf = new Bitmap(rect.Width, rect.Height, PixelFormat.Format24bppRgb); if (vdoWriter.IsOpen) vdoWriter.Close(); var recordPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "record"); Directory.CreateDirectory(recordPath); var filename = $"record_{DateTime.Now:yyyyMMdd_HHmmss_fffff}-{rect.Width:0000}-{rect.Height:0000}.avi"; vdoWriter.Open(Path.Combine(recordPath, filename), rect.Width, rect.Height, frameRate, VideoCodec.MPEG4, 8000000); } Graphics srcGraphics = Graphics.FromImage(imgBuf); srcGraphics.CopyFromScreen(rect.Location, Point.Empty, rect.Size); codecParam.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, broadcastQuality); ms.SetLength(0); imgBuf.Save(ms, codec, codecParam); //imgBuf.Save(ms, format); buf = ms.ToArray(); foreach (var item in waits) { item.Set(); } vdoWriter.WriteVideoFrame(imgBuf); } else { hWnd = 0; //re find win Console.WriteLine("waiting window found..."); } Thread.Sleep(1000 / frameRate); } vdoWriter.Close(); } catch (Exception ex) { throw; } }); Task.Factory.StartNew(() => { Socket Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); Server.Bind(new IPEndPoint(IPAddress.Any, port)); Server.Listen(10); Console.WriteLine("Listening..."); while (runing) { int num; var client = Server.Accept(); Console.WriteLine("Acceped socket #{0} from {1} onlines {2}", num = ++clientNum, client.RemoteEndPoint, waits.Count); Task.Factory.StartNew(() => { var wait = new AutoResetEvent(false); waits.Add(wait); try { var stream = new NetworkStream(client, true); var writer = new BinaryWriter(stream); writer.Write(Encoding.ASCII.GetBytes("HTTP / 1.1 200 OK\r\n")); writer.Write(Encoding.ASCII.GetBytes("Content-Type: multipart/x-mixed-replace; boundary=frame\r\n")); while (runing) { wait.WaitOne(); var rbuf = buf; if (rbuf != null) { writer.Write(Encoding.ASCII.GetBytes("\r\n")); writer.Write(Encoding.ASCII.GetBytes("--frame\r\n")); writer.Write(Encoding.ASCII.GetBytes("Content-Type: image/png\r\n")); writer.Write(Encoding.ASCII.GetBytes("Content-Length: " + rbuf.Length + "\r\n")); writer.Write(Encoding.ASCII.GetBytes("\r\n")); writer.Write(rbuf); } } } catch (Exception ex) { Console.WriteLine("Lost socket #{1} from {0} onlines {2} by {3}", client.RemoteEndPoint, num, waits.Count, ex.Message); } waits.Remove(wait); }); } }); ConsoleKeyInfo k; while (char.ToLower((k = Console.ReadKey(true)).KeyChar) != 'q') { if (k.Key == ConsoleKey.UpArrow) { if (broadcastQuality + 1 == 0) broadcastQuality += 2; else broadcastQuality += 1; Console.WriteLine("Quality: {0}", broadcastQuality); } else if (k.Key == ConsoleKey.DownArrow) { if (broadcastQuality - 1 == 0) broadcastQuality -= 2; else broadcastQuality -= 1; Console.WriteLine("Quality: {0}", broadcastQuality); } else { Console.WriteLine("Press Q to exit. up-arrow to inc quality, down-arrow to dec"); } } runing = false; taskSource.Wait(); } } }