精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

從零到一:C#實現(xiàn)高性能屏幕錄制器完全指南

開發(fā) 開發(fā)工具
本文將手把手帶你用C#構(gòu)建一個完整的屏幕錄制應(yīng)用,涵蓋視頻捕獲、音頻同步、多線程優(yōu)化等核心技術(shù)。通過2000+行實戰(zhàn)代碼,你將掌握系統(tǒng)級編程的精髓,提升你的C#技術(shù)水平。

數(shù)字化辦公時代,屏幕錄制已成為開發(fā)者、產(chǎn)品經(jīng)理、教育工作者的剛需工具。市面上的錄屏軟件要么功能單一,要么性能不佳,要么收費昂貴。作為C#開發(fā)者,為什么不自己打造一個功能強大、性能卓越的錄屏神器呢?

本文將手把手帶你用C#構(gòu)建一個完整的屏幕錄制應(yīng)用,涵蓋視頻捕獲、音頻同步、多線程優(yōu)化等核心技術(shù)。通過2000+行實戰(zhàn)代碼,你將掌握系統(tǒng)級編程的精髓,提升你的C#技術(shù)水平。

痛點分析:為什么要自己造輪子?

現(xiàn)有方案的局限性

性能瓶頸:大多數(shù)錄屏工具在高幀率錄制時會出現(xiàn)卡頓、掉幀問題,影響用戶體驗。

功能限制:無法滿足特定需求,如自定義錄制區(qū)域、多音頻源混合、實時壓縮等。

成本考慮:商業(yè)軟件授權(quán)費用高昂,開源方案往往缺乏維護。

技術(shù)提升:對于C#開發(fā)者而言,這是一個絕佳的系統(tǒng)編程實戰(zhàn)項目。

核心技術(shù)架構(gòu)解析

多線程并發(fā)設(shè)計

// 高精度幀捕獲線程
private void CaptureScreenFrames(CancellationToken cancellationToken)
{
    Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;

    while (_isRecording && !cancellationToken.IsCancellationRequested)
    {
        var currentTime = _stopwatch.ElapsedTicks;

        if (currentTime >= _nextFrameTime)
        {
            var bitmap = CaptureScreenOptimized(_captureArea);

            _frameQueue.Enqueue(new FrameData
            {
                Bitmap = bitmap,
                Index = _frameIndex++,
                Timestamp = DateTime.Now
            });

            _nextFrameTime += _frameInterval;
        }
        else
        {
            // 精確等待,避免CPU空轉(zhuǎn)
            var waitMs = Math.Max(1, (_nextFrameTime - currentTime) / TimeSpan.TicksPerMillisecond);
            Thread.Sleep((int)waitMs);
        }
    }
}

核心要點

  • 使用ThreadPriority.AboveNormal確保幀捕獲優(yōu)先級
  • 高精度計時器避免幀率不穩(wěn)定
  • 并發(fā)隊列(ConcurrentQueue)實現(xiàn)生產(chǎn)者-消費者模式

?? 性能優(yōu)化:屏幕捕獲算法

private Bitmap CaptureScreenOptimized(Rectangle area)
{
    var bitmap = new Bitmap(area.Width, area.Height, PixelFormat.Format24bppRgb);

    using (var graphics = Graphics.FromImage(bitmap))
    {
        // 關(guān)鍵優(yōu)化設(shè)置
        graphics.CompositingMode = CompositingMode.SourceCopy;
        graphics.CompositingQuality = CompositingQuality.HighSpeed;
        graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
        graphics.SmoothingMode = SmoothingMode.None;
        graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;

        graphics.CopyFromScreen(area.X, area.Y, 0, 0, area.Size, 
            CopyPixelOperation.SourceCopy);
    }

    return bitmap;
}

性能提升技巧

  1. 選擇Format24bppRgb減少內(nèi)存占用
  2. 關(guān)閉圖形渲染質(zhì)量提升速度
  3. 使用SourceCopy模式避免像素混合計算

音頻同步錄制:多源混音

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualBasic;
using NAudio.Wave;

namespace AppScreenRecorder
{
    publicclass AudioRecorder : IDisposable
    {
        private WasapiLoopbackCapture _loopbackCapture;
        private WaveInEvent _micCapture;
        privatestring _systemAudioPath;
        privatestring _micAudioPath;
        private WaveFileWriter _audioWriter;
        private WaveFileWriter _micWriter;
        privatebool _isRecording = false;
        privatebool _isPaused = false;

        publicstring SystemAudioPath => _systemAudioPath;
        publicstring MicAudioPath => _micAudioPath;

        public void StartRecording(string basePath, bool recordMic = true, bool recordSystem = true)
        {
            _isRecording = true;
            _isPaused = false;

            if (recordSystem)
            {
                _systemAudioPath = Path.ChangeExtension(basePath, "_system.wav");

                _loopbackCapture = new WasapiLoopbackCapture();
                _loopbackCapture.DataAvailable += OnSystemAudioDataAvailable;
                _loopbackCapture.RecordingStopped += OnRecordingStopped;

                _audioWriter = new WaveFileWriter(_systemAudioPath, _loopbackCapture.WaveFormat);
                _loopbackCapture.StartRecording();
            }

            if (recordMic)
            {
                _micAudioPath = Path.ChangeExtension(basePath, "_mic.wav");

                _micCapture = new WaveInEvent();
                _micCapture.WaveFormat = new WaveFormat(44100, 16, 2);
                _micCapture.DataAvailable += OnMicDataAvailable;

                _micWriter = new WaveFileWriter(_micAudioPath, _micCapture.WaveFormat);
                _micCapture.StartRecording();
            }
        }

        public void StopRecording()
        {
            // 立即設(shè)置停止標(biāo)志
            _isRecording = false;
            _isPaused = false;

            try
            {
                // 立即停止錄制設(shè)備
                _loopbackCapture?.StopRecording();
                _micCapture?.StopRecording();

                // 立即刷新并關(guān)閉寫入器
                _audioWriter?.Flush();
                _audioWriter?.Dispose();
                _micWriter?.Flush();
                _micWriter?.Dispose();

                // 釋放捕獲設(shè)備
                _loopbackCapture?.Dispose();
                _micCapture?.Dispose();

                _audioWriter = null;
                _micWriter = null;
                _loopbackCapture = null;
                _micCapture = null;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"停止音頻錄制時出錯: {ex.Message}");
            }
        }

        public void PauseRecording()
        {
            _isPaused = true;
        }

        public void ResumeRecording()
        {
            _isPaused = false;
        }

        private void OnSystemAudioDataAvailable(object sender, WaveInEventArgs e)
        {
            if (_isRecording && !_isPaused && _audioWriter != null)
            {
                try
                {
                    _audioWriter.Write(e.Buffer, 0, e.BytesRecorded);
                }
                catch (ObjectDisposedException)
                {

                }
            }
        }

        private void OnMicDataAvailable(object sender, WaveInEventArgs e)
        {
            if (_isRecording && !_isPaused && _micWriter != null)
            {
                try
                {
                    _micWriter.Write(e.Buffer, 0, e.BytesRecorded);
                }
                catch (ObjectDisposedException)
                {

                }
            }
        }
        private void OnRecordingStopped(object sender, StoppedEventArgs e)
        {
            if (e.Exception != null)
            {
                Console.WriteLine($"音頻錄制出錯: {e.Exception.Message}");
            }
        }

        public void Dispose()
        {
            StopRecording();
        }
    }
}

關(guān)鍵技術(shù)點

  • WasapiLoopbackCapture實現(xiàn)系統(tǒng)音頻捕獲
  • 雙音頻流獨立錄制,后期FFmpeg合并
  • 異步寫入避免音頻丟失

視頻編碼:FFMpegCore集成

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using FFMpegCore;
using FFMpegCore.Enums;

namespace AppScreenRecorder
{
    publicclass ScreenRecorder : IDisposable
    {
        privatebool _isRecording = false;
        privatebool _isPaused = false;
        privatestring _outputPath;
        private Rectangle _captureArea;
        privatestring _tempDirectory;
        privateint _frameRate = 30;
        privateint _quality = 720;
        privatestring _tempVideoPath;

        // 使用并發(fā)隊列來緩存幀
        private readonly ConcurrentQueue<FrameData> _frameQueue = new ConcurrentQueue<FrameData>();
        private Task _captureTask;
        private Task _saveTask;
        private CancellationTokenSource _cancellationTokenSource;

        // 高精度計時器
        private System.Diagnostics.Stopwatch _stopwatch;
        privatelong _nextFrameTime;
        privatelong _frameInterval;
        privateint _frameIndex = 0;

        publicstring TempVideoPath => _tempVideoPath;

        privatestruct FrameData
        {
            public Bitmap Bitmap;
            publicint Index;
            public DateTime Timestamp;
        }

        public ScreenRecorder()
        {
            _tempDirectory = Path.Combine(Path.GetTempPath(),
                "ScreenRecorder_" + Guid.NewGuid().ToString("N")[..8]);
            Directory.CreateDirectory(_tempDirectory);
        }

        public async Task StartRecording(string outputPath, Rectangle? captureArea = null,
            int frameRate = 30, int quality = 720)
        {
            _outputPath = outputPath;
            _captureArea = captureArea ?? Screen.PrimaryScreen.Bounds;
            _frameRate = frameRate;
            _quality = quality;
            _isRecording = true;
            _isPaused = false;
            _frameIndex = 0;

            // 初始化高精度計時器
            _stopwatch = System.Diagnostics.Stopwatch.StartNew();
            _frameInterval = TimeSpan.TicksPerSecond / _frameRate;
            _nextFrameTime = _frameInterval;

            _cancellationTokenSource = new CancellationTokenSource();

            // 啟動捕獲和保存任務(wù)
            _captureTask = Task.Run(() => CaptureScreenFrames(_cancellationTokenSource.Token));
            _saveTask = Task.Run(() => SaveFramesToDisk(_cancellationTokenSource.Token));

            await Task.CompletedTask;
        }

        public async Task StopRecording()
        {
            // 立即停止錄制標(biāo)志
            _isRecording = false;
            _isPaused = false;

            Console.WriteLine("開始停止錄制...");

            if (_cancellationTokenSource != null)
            {
                // 立即取消捕獲任務(wù)
                _cancellationTokenSource.Cancel();

                try
                {
                    // 等待捕獲任務(wù)完成
                    if (_captureTask != null)
                    {
                        var timeoutTask = Task.Delay(3000); // 3秒超時
                        var completedTask = await Task.WhenAny(_captureTask, timeoutTask);
                        if (completedTask == timeoutTask)
                        {
                            Console.WriteLine("警告: 捕獲任務(wù)超時");
                        }
                        else
                        {
                            Console.WriteLine("捕獲任務(wù)已完成");
                        }
                    }

                    // 等待保存任務(wù)處理完所有幀
                    if (_saveTask != null)
                    {
                        Console.WriteLine($"等待保存任務(wù)完成,隊列剩余: {_frameQueue.Count} 幀");

                        var timeoutTask = Task.Delay(10000); // 10秒超時,給保存任務(wù)更多時間
                        var completedTask = await Task.WhenAny(_saveTask, timeoutTask);
                        if (completedTask == timeoutTask)
                        {
                            Console.WriteLine("警告: 保存任務(wù)超時");
                            // 即使超時也繼續(xù),清理剩余幀
                            while (_frameQueue.TryDequeue(out var frame))
                            {
                                frame.Bitmap?.Dispose();
                            }
                        }
                        else
                        {
                            Console.WriteLine("保存任務(wù)已完成");
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"停止錄制任務(wù)時出錯: {ex.Message}");
                }
            }

            // 停止計時器
            _stopwatch?.Stop();

            // 此時所有幀應(yīng)該都已保存完畢,生成視頻
            if (_frameIndex > 0)
            {
                Console.WriteLine($"開始生成視頻,總幀數(shù): {_frameIndex}");
                var tempVideoPath = Path.Combine(_tempDirectory, "temp_video.mp4");
                await GenerateVideoOnly(tempVideoPath);
                _tempVideoPath = tempVideoPath;
                Console.WriteLine("視頻生成完成");
            }
        }

        public Task PauseRecording()
        {
            _isPaused = true;
            return Task.CompletedTask;
        }

        public Task ResumeRecording()
        {
            _isPaused = false;
            // 重置計時器以避免時間跳躍
            if (_stopwatch != null)
            {
                _stopwatch.Restart();
                _nextFrameTime = _frameInterval;
            }
            return Task.CompletedTask;
        }

        private void CaptureScreenFrames(CancellationToken cancellationToken)
        {
            Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
            Console.WriteLine("捕獲任務(wù)開始");

            while (_isRecording && !cancellationToken.IsCancellationRequested)
            {
                if (_isPaused)
                {
                    // 暫停時重置計時器
                    _stopwatch.Restart();
                    _nextFrameTime = _frameInterval;
                    Thread.Sleep(50);
                    continue;
                }

                var currentTime = _stopwatch.ElapsedTicks;

                if (currentTime >= _nextFrameTime)
                {
                    // 最后一次檢查錄制狀態(tài)
                    if (!_isRecording)
                    {
                        break;
                    }

                    try
                    {
                        var bitmap = CaptureScreenOptimized(_captureArea);

                        // 檢查隊列大小,避免內(nèi)存溢出
                        if (_frameQueue.Count < 500) 
                        {
                            _frameQueue.Enqueue(new FrameData
                            {
                                Bitmap = bitmap,
                                Index = _frameIndex++,
                                Timestamp = DateTime.Now
                            });
                        }
                        else
                        {
                            bitmap?.Dispose();
                            Console.WriteLine("隊列滿,丟棄一幀");
                        }

                        _nextFrameTime += _frameInterval;

                        if (currentTime > _nextFrameTime + _frameInterval * 2)
                        {
                            _nextFrameTime = currentTime + _frameInterval;
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"捕獲幀時出錯: {ex.Message}");
                        _nextFrameTime += _frameInterval;
                    }
                }
                else
                {
                    // 精確等待
                    var waitTicks = _nextFrameTime - currentTime;
                    var waitMs = Math.Max(1, Math.Min(waitTicks / TimeSpan.TicksPerMillisecond, 15));
                    Thread.Sleep((int)waitMs);
                }
            }

            Console.WriteLine($"捕獲任務(wù)結(jié)束,總共捕獲 {_frameIndex} 幀,隊列剩余: {_frameQueue.Count}");
        }

        private void SaveFramesToDisk(CancellationToken cancellationToken)
        {
            Thread.CurrentThread.Priority = ThreadPriority.Normal;
            var processedFrames = 0;

            Console.WriteLine("保存任務(wù)開始");

            while (true)
            {
                bool hasMoreFrames = _frameQueue.TryDequeue(out var frameData);

                if (hasMoreFrames)
                {
                    try
                    {
                        var frameFile = Path.Combine(_tempDirectory, $"frame_{frameData.Index:D6}.png");

                        using (frameData.Bitmap)
                        {
                            frameData.Bitmap.Save(frameFile, ImageFormat.Png);
                        }

                        processedFrames++;

                        if (processedFrames % 100 == 0)
                        {
                            Console.WriteLine($"已保存 {processedFrames} 幀,隊列剩余: {_frameQueue.Count}");
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"保存幀時出錯: {ex.Message}");
                        frameData.Bitmap?.Dispose();
                    }
                }
                else
                {
                    if (cancellationToken.IsCancellationRequested && !_isRecording)
                    {
                        break;
                    }
                    Thread.Sleep(10);
                }
            }

            Console.WriteLine($"保存任務(wù)結(jié)束,總共保存 {processedFrames} 幀");
        }

        private Bitmap CaptureScreenOptimized(Rectangle area)
        {
            var bitmap = new Bitmap(area.Width, area.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

            using (var graphics = Graphics.FromImage(bitmap))
            {
                graphics.CompositingMode = CompositingMode.SourceCopy;
                graphics.CompositingQuality = CompositingQuality.HighSpeed;
                graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
                graphics.SmoothingMode = SmoothingMode.None;
                graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;

                graphics.CopyFromScreen(area.X, area.Y, 0, 0, area.Size, CopyPixelOperation.SourceCopy);
            }

            return bitmap;
        }

        private async Task GenerateVideoOnly(string outputPath)
        {
            try
            {
                var inputPattern = Path.Combine(_tempDirectory, "frame_%06d.png");

                await FFMpegArguments
                    .FromFileInput(inputPattern, false, options => options
                        .WithFramerate(_frameRate))
                    .OutputToFile(outputPath, true, options => options
                        .WithVideoCodec(VideoCodec.LibX264)
                        .WithCustomArgument("-preset ultrafast") 
                        .WithCustomArgument("-tune zerolatency") 
                        .WithVideoBitrate(GetBitrateForQuality(_quality))
                        .WithFramerate(_frameRate)
                        .WithVideoFilters(filterOptions => filterOptions
                            .Scale(GetVideoSize(_quality))))
                    .ProcessAsynchronously();
            }
            catch (Exception ex)
            {
                thrownew Exception($"視頻生成失敗: {ex.Message}", ex);
            }
        }

        public static async Task MergeVideoAndAudio(string videoPath, string audioPath, string outputPath)
        {
            try
            {
                await FFMpegArguments
                    .FromFileInput(videoPath)
                    .AddFileInput(audioPath)
                    .OutputToFile(outputPath, true, options => options
                        .WithCustomArgument("-c:v copy")
                        .WithAudioCodec(AudioCodec.Aac)
                        .WithAudioBitrate(128)
                        .WithCustomArgument("-shortest"))
                    .ProcessAsynchronously();
            }
            catch (Exception ex)
            {
                thrownew Exception($"視頻音頻合并失敗: {ex.Message}", ex);
            }
        }

        public static async Task MergeVideoWithMultipleAudio(string videoPath, string systemAudioPath,
            string micAudioPath, string outputPath)
        {
            try
            {
                bool hasMicAudio = !string.IsNullOrEmpty(micAudioPath) && File.Exists(micAudioPath);

                if (hasMicAudio)
                {
                    await FFMpegArguments
                        .FromFileInput(videoPath)
                        .AddFileInput(systemAudioPath)
                        .AddFileInput(micAudioPath)
                        .OutputToFile(outputPath, true, options => options
                            .WithCustomArgument("-c:v copy")
                            .WithAudioCodec(AudioCodec.Aac)
                            .WithAudioBitrate(128)
                            .WithCustomArgument("-filter_complex [1:a][2:a]amix=inputs=2:duration=shortest[a]")
                            .WithCustomArgument("-map 0:v")
                            .WithCustomArgument("-map [a]"))
                        .ProcessAsynchronously();
                }
                else
                {
                    await MergeVideoAndAudio(videoPath, systemAudioPath, outputPath);
                }
            }
            catch (Exception ex)
            {
                thrownew Exception($"視頻多音頻合并失敗: {ex.Message}", ex);
            }
        }

        private int GetBitrateForQuality(int quality)
        {
            return quality switch
            {
                1080 => 4000,
                720 => 2000,
                480 => 1000,
                _ => 2000
            };
        }

        private VideoSize GetVideoSize(int quality)
        {
            return quality switch
            {
                1080 => VideoSize.FullHd,
                720 => VideoSize.Hd,
                480 => VideoSize.Ed,
                _ => VideoSize.Hd
            };
        }

        private void CleanupTempFiles()
        {
            try
            {
                if (Directory.Exists(_tempDirectory))
                {
                    Directory.Delete(_tempDirectory, true);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"清理臨時文件時出錯: {ex.Message}");
            }
        }

        public void Dispose()
        {
            if (_isRecording)
            {
                _isRecording = false;
            }

            _cancellationTokenSource?.Cancel();

            while (_frameQueue.TryDequeue(out var frame))
            {
                frame.Bitmap?.Dispose();
            }

            _stopwatch?.Stop();
            _cancellationTokenSource?.Dispose();
            CleanupTempFiles();
        }
    }
}

編碼優(yōu)化策略

  • ultrafast預(yù)設(shè)平衡質(zhì)量與速度
  • amix濾鏡實現(xiàn)音頻混合
  • 視頻流復(fù)制(-c:v copy)避免重復(fù)編碼

用戶界面:WPF現(xiàn)代化設(shè)計

<Window x:Class="AppScreenRecorder.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:AppScreenRecorder"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" KeyDown="MainWindow_KeyDown"
        Focusable="True">
    <Window.Resources>
        <!-- 樣式定義 -->
        <Style x:Key="ModernButtonStyle" TargetType="Button">
            <Setter Property="Background" Value="#FF2196F3"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="Padding" Value="20,10"/>
            <Setter Property="Margin" Value="5"/>
            <Setter Property="Cursor" Value="Hand"/>
            <Setter Property="FontSize" Value="14"/>
            <Setter Property="FontWeight" Value="Medium"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Background="{TemplateBinding Background}"
                                CornerRadius="5"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}">
                            <ContentPresenter HorizontalAlignment="Center" 
                                            VerticalAlignment="Center"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Background" Value="#FF1976D2"/>
                            </Trigger>
                            <Trigger Property="IsPressed" Value="True">
                                <Setter Property="Background" Value="#FF0D47A1"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style x:Key="StopButtonStyle" TargetType="Button" BasedOn="{StaticResource ModernButtonStyle}">
            <Setter Property="Background" Value="#FFF44336"/>
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="#FFD32F2F"/>
                </Trigger>
                <Trigger Property="IsPressed" Value="True">
                    <Setter Property="Background" Value="#FFB71C1C"/>
                </Trigger>
            </Style.Triggers>
        </Style>

        <Style x:Key="GroupBoxStyle" TargetType="GroupBox">
            <Setter Property="BorderBrush" Value="#FFCCCCCC"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Margin" Value="10"/>
            <Setter Property="Padding" Value="10"/>
            <Setter Property="FontWeight" Value="Medium"/>
        </Style>

        <Style x:Key="StatusLabelStyle" TargetType="Label">
            <Setter Property="FontSize" Value="16"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="Margin" Value="10"/>
        </Style>
    </Window.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <!-- 標(biāo)題欄 -->
        <Border Grid.Row="0" Background="#FF2196F3" Padding="20,15">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="??" FontSize="24" Margin="0,0,10,0"/>
                <TextBlock Text="C# 錄屏神器" FontSize="20" FontWeight="Bold" Foreground="White" VerticalAlignment="Center"/>
                <TextBlock Text="輕松實現(xiàn)視頻音頻同步錄制" FontSize="12" Foreground="#FFBBDEFB" VerticalAlignment="Center" Margin="20,0,0,0"/>
            </StackPanel>
        </Border>

        <!-- 主要內(nèi)容區(qū)域 -->
        <ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
            <StackPanel Margin="20">

                <!-- 錄制控制區(qū)域 -->
                <GroupBox Header="?? 錄制控制" Style="{StaticResource GroupBoxStyle}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>

                        <StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" Margin="0,0,0,15">
                            <Button Name="RecordButton" Content="開始錄制" 
                                    Style="{StaticResource ModernButtonStyle}"
                                    Click="RecordButton_Click" MinWidth="120"/>
                            <Button Name="StopButton" Content="停止錄制" 
                                    Style="{StaticResource StopButtonStyle}"
                                    Click="StopButton_Click" MinWidth="120"
                                    IsEnabled="False"/>
                            <Button Name="PauseButton" Content="暫停錄制" 
                                    Style="{StaticResource ModernButtonStyle}"
                                    Click="PauseButton_Click" MinWidth="120"
                                    IsEnabled="False"/>
                        </StackPanel>

                        <Label Name="StatusLabel" Content="就緒" Grid.Row="1" Grid.Column="0"
                               Style="{StaticResource StatusLabelStyle}" Foreground="#FF4CAF50"/>

                        <StackPanel Grid.Row="0" Grid.Column="1" Grid.RowSpan="2" Orientation="Vertical">
                            <TextBlock Text="錄制時長:" FontWeight="Medium" Margin="0,0,0,5"/>
                            <Label Name="RecordTimeLabel" Content="00:00:00" 
                                   FontSize="18" FontFamily="Consolas" 
                                   Background="#FFF5F5F5" Padding="10,5"
                                   HorizontalContentAlignment="Center"/>
                        </StackPanel>
                    </Grid>
                </GroupBox>

                <!-- 錄制設(shè)置區(qū)域 -->
                <GroupBox Header="?? 錄制設(shè)置" Style="{StaticResource GroupBoxStyle}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>

                        <!-- 視頻設(shè)置 -->
                        <TextBlock Text="視頻質(zhì)量:" Grid.Row="0" Grid.Column="0" Margin="0,0,0,5" FontWeight="Medium"/>
                        <ComboBox Name="VideoQualityComboBox" Grid.Row="0" Grid.Column="1" Margin="10,0,0,10">
                            <ComboBoxItem Content="高質(zhì)量 (1080p)" IsSelected="True"/>
                            <ComboBoxItem Content="標(biāo)準(zhǔn)質(zhì)量 (720p)"/>
                            <ComboBoxItem Content="低質(zhì)量 (480p)"/>
                        </ComboBox>

                        <TextBlock Text="幀率 (FPS):" Grid.Row="1" Grid.Column="0" Margin="0,0,0,5" FontWeight="Medium"/>
                        <ComboBox Name="FrameRateComboBox" Grid.Row="1" Grid.Column="1" Margin="10,0,0,10">
                            <ComboBoxItem Content="60 FPS"/>
                            <ComboBoxItem Content="30 FPS" IsSelected="True"/>
                            <ComboBoxItem Content="24 FPS"/>
                            <ComboBoxItem Content="15 FPS"/>
                        </ComboBox>

                        <!-- 音頻設(shè)置 -->
                        <CheckBox Name="RecordSystemAudioCheckBox" Content="錄制系統(tǒng)音頻" 
                                  Grid.Row="2" Grid.Column="0" IsChecked="True" Margin="0,10"/>
                        <CheckBox Name="RecordMicAudioCheckBox" Content="錄制麥克風(fēng)" 
                                  Grid.Row="2" Grid.Column="1" IsChecked="True" Margin="10,10,0,10"/>

                        <!-- 錄制區(qū)域 -->
                        <TextBlock Text="錄制區(qū)域:" Grid.Row="3" Grid.Column="0" Margin="0,0,0,5" FontWeight="Medium"/>
                        <ComboBox Name="RecordAreaComboBox" Grid.Row="3" Grid.Column="1" Margin="10,0,0,10">
                            <ComboBoxItem Content="全屏錄制" IsSelected="True"/>
                            <ComboBoxItem Content="選擇窗口"/>
                            <ComboBoxItem Content="自定義區(qū)域"/>
                        </ComboBox>

                        <!-- 輸出路徑 -->
                        <TextBlock Text="保存路徑:" Grid.Row="4" Grid.Column="0" Margin="0,10,0,5" FontWeight="Medium"/>
                        <StackPanel Grid.Row="4" Grid.Column="1" Orientation="Horizontal" Margin="10,10,0,0">
                            <TextBox Name="OutputPathTextBox" Width="200" Margin="0,0,10,0" 
                                     Text="C:\錄屏文件\錄屏_" IsReadOnly="True"/>
                            <Button Content="瀏覽..." Click="BrowseButton_Click" 
                                    Style="{StaticResource ModernButtonStyle}" Padding="10,5"/>
                        </StackPanel>
                    </Grid>
                </GroupBox>


                <!-- 快捷鍵設(shè)置 -->
                <GroupBox Header="?? 快捷鍵" Style="{StaticResource GroupBoxStyle}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>

                        <TextBlock Text="開始/停止錄制:" Grid.Row="0" Grid.Column="0" Margin="0,0,20,5" FontWeight="Medium"/>
                        <TextBlock Text="F9" Grid.Row="0" Grid.Column="1" FontFamily="Consolas" Background="#FFF0F0F0" Padding="5,2"/>

                        <TextBlock Text="暫停/恢復(fù):" Grid.Row="1" Grid.Column="0" Margin="0,0,20,5" FontWeight="Medium"/>
                        <TextBlock Text="F10" Grid.Row="1" Grid.Column="1" FontFamily="Consolas" Background="#FFF0F0F0" Padding="5,2"/>

                        <TextBlock Text="截圖:" Grid.Row="2" Grid.Column="0" Margin="0,0,20,5" FontWeight="Medium"/>
                        <TextBlock Text="F11" Grid.Row="2" Grid.Column="1" FontFamily="Consolas" Background="#FFF0F0F0" Padding="5,2"/>
                    </Grid>
                </GroupBox>

            </StackPanel>
        </ScrollViewer>

        <!-- 底部狀態(tài)欄 -->
        <Border Grid.Row="2" Background="#FFF5F5F5" BorderBrush="#FFDDDDDD" BorderThickness="0,1,0,0">
            <Grid Margin="20,10">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>

                <StackPanel Grid.Column="0" Orientation="Horizontal">
                    <TextBlock Text="狀態(tài):" Margin="0,0,10,0" VerticalAlignment="Center"/>
                    <Ellipse Name="StatusIndicator" Width="12" Height="12" Fill="#FF4CAF50" Margin="0,0,10,0" VerticalAlignment="Center"/>
                    <TextBlock Name="DetailStatusLabel" Text="系統(tǒng)就緒" VerticalAlignment="Center"/>
                </StackPanel>

                <StackPanel Grid.Column="1" Orientation="Horizontal">
                    <Button Content="?? 打開輸出文件夾" Click="OpenOutputFolderButton_Click" 
                            Style="{StaticResource ModernButtonStyle}" Padding="10,5" Margin="0,0,10,0"/>
                </StackPanel>
            </Grid>
        </Border>

    </Grid>
</Window>
using System;
using System.IO;
using System.Windows;
using System.Windows.Threading;
using System.Threading.Tasks;
using Microsoft.Win32;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using MessageBox = System.Windows.MessageBox;
using System.Windows.Input;
using KeyEventArgs = System.Windows.Input.KeyEventArgs;

namespace AppScreenRecorder
{
    public partial class MainWindow : Window
    {
        private ScreenRecorder _screenRecorder;
        private AudioRecorder _audioRecorder;
        private DispatcherTimer _recordingTimer;
        private DateTime _startTime;
        privatebool _isRecording = false;
        privatebool _isPaused = false;

        public MainWindow()
        {
            InitializeComponent();
            InitializeTimer();
            SetInitialState();
            this.Focusable = true;
            this.Focus();
        }

        private void InitializeTimer()
        {
            _recordingTimer = new DispatcherTimer();
            _recordingTimer.Interval = TimeSpan.FromSeconds(1);
            _recordingTimer.Tick += UpdateRecordingTime;
        }

        private void SetInitialState()
        {
            RecordButton.IsEnabled = true;
            StopButton.IsEnabled = false;
            PauseButton.IsEnabled = false;
            StatusLabel.Content = "就緒";
            StatusLabel.Foreground = System.Windows.Media.Brushes.Green;
            RecordTimeLabel.Content = "00:00:00";
            StatusIndicator.Fill = System.Windows.Media.Brushes.Green;
            DetailStatusLabel.Text = "系統(tǒng)就緒";

            // 設(shè)置默認輸出路徑
            string defaultPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "錄屏文件");
            if (!Directory.Exists(defaultPath))
            {
                Directory.CreateDirectory(defaultPath);
            }
            OutputPathTextBox.Text = Path.Combine(defaultPath, "錄屏_");
        }

        private async void RecordButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (!_isRecording)
                {
                    await StartRecording();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"開始錄制時出錯:{ex.Message}", "錯誤", MessageBoxButton.OK, MessageBoxImage.Error);
                SetInitialState();
            }
        }

        private async void StopButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                await StopRecording();
            }
            catch (Exception ex)
            {
                MessageBox.Show($"停止錄制時出錯:{ex.Message}", "錯誤", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void PauseButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (_isPaused)
                {
                    ResumeRecording();
                }
                else
                {
                    PauseRecording();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"暫停/恢復(fù)錄制時出錯:{ex.Message}", "錯誤", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private async Task StartRecording()
        {
            // 生成輸出文件路徑
            string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
            string finalOutputPath = OutputPathTextBox.Text + timestamp + ".mp4";
            string audioBasePath = OutputPathTextBox.Text + timestamp;

            // 獲取錄制設(shè)置
            var quality = GetSelectedQuality();
            var frameRate = GetSelectedFrameRate();
            var recordArea = GetRecordArea();

            // 更新UI狀態(tài)
            _isRecording = true;
            _isPaused = false;
            _startTime = DateTime.Now;

            RecordButton.IsEnabled = false;
            StopButton.IsEnabled = true;
            PauseButton.IsEnabled = true;
            PauseButton.Content = "暫停錄制";

            StatusLabel.Content = "錄制中...";
            StatusLabel.Foreground = System.Windows.Media.Brushes.Red;
            StatusIndicator.Fill = System.Windows.Media.Brushes.Red;
            DetailStatusLabel.Text = "正在錄制屏幕...";

            // 啟動計時器
            _recordingTimer.Start();

            try
            {
                // 初始化錄制器
                _screenRecorder = new ScreenRecorder();
                _audioRecorder = new AudioRecorder();

                // 啟動音頻錄制(如果需要)
                bool recordMic = RecordMicAudioCheckBox.IsChecked ?? false;
                bool recordSystem = RecordSystemAudioCheckBox.IsChecked ?? false;

                if (recordMic || recordSystem)
                {
                    _audioRecorder.StartRecording(audioBasePath, recordMic, recordSystem);
                }

                // 啟動屏幕錄制
                await _screenRecorder.StartRecording(finalOutputPath, recordArea, frameRate, quality);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"錄制失敗:{ex.Message}", "錯誤", MessageBoxButton.OK, MessageBoxImage.Error);
                await StopRecording();
                throw;
            }
        }

        private async Task StopRecording()
        {
            if (!_isRecording) return;

            _isRecording = false;
            _recordingTimer.Stop();

            // 更新UI狀態(tài)
            StatusLabel.Content = "正在停止錄制...";
            StatusLabel.Foreground = System.Windows.Media.Brushes.Orange;
            StatusIndicator.Fill = System.Windows.Media.Brushes.Orange;
            DetailStatusLabel.Text = "正在停止錄制,請稍候...";

            try
            {
                string tempVideoPath = null;
                string systemAudioPath = null;
                string micAudioPath = null;

                // 1. 立即停止屏幕錄制
                if (_screenRecorder != null)
                {
                    await _screenRecorder.StopRecording();
                    tempVideoPath = _screenRecorder.TempVideoPath;
                }

                // 2. 立即停止音頻錄制
                if (_audioRecorder != null)
                {
                    _audioRecorder.StopRecording();
                    systemAudioPath = _audioRecorder.SystemAudioPath;
                    micAudioPath = _audioRecorder.MicAudioPath;
                }

                // 3. 減少等待時間,只等待文件系統(tǒng)同步
                await Task.Delay(200);

                // 更新UI狀態(tài)
                StatusLabel.Content = "處理中...";
                DetailStatusLabel.Text = "正在合并視頻和音頻...";

                // 4. 生成最終輸出路徑
                string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
                string finalOutputPath = OutputPathTextBox.Text + timestamp + ".mp4";

                // 5. 合并視頻和音頻
                await MergeVideoAndAudio(tempVideoPath, systemAudioPath, micAudioPath, finalOutputPath);

                // 6. 清理資源
                _screenRecorder?.Dispose();
                _audioRecorder?.Dispose();
                _screenRecorder = null;
                _audioRecorder = null;

                // 恢復(fù)UI狀態(tài)
                SetInitialState();

                MessageBox.Show($"錄制完成!\n文件保存至:{finalOutputPath}", "成功",
                    MessageBoxButton.OK, MessageBoxImage.Information);
            }
            catch (Exception ex)
            {
                _screenRecorder?.Dispose();
                _audioRecorder?.Dispose();
                _screenRecorder = null;
                _audioRecorder = null;

                MessageBox.Show($"停止錄制時出錯:{ex.Message}", "錯誤",
                    MessageBoxButton.OK, MessageBoxImage.Error);
                SetInitialState();
            }
        }

        private async Task MergeVideoAndAudio(string videoPath, string systemAudioPath,
    string micAudioPath, string outputPath)
        {
            try
            {
                if (string.IsNullOrEmpty(videoPath) || !File.Exists(videoPath))
                {
                    thrownew Exception("視頻文件不存在");
                }

                if (string.IsNullOrEmpty(systemAudioPath) && string.IsNullOrEmpty(micAudioPath))
                {
                    File.Copy(videoPath, outputPath, true);
                    return;
                }

                if (!string.IsNullOrEmpty(systemAudioPath) && File.Exists(systemAudioPath) &&
                    !string.IsNullOrEmpty(micAudioPath) && File.Exists(micAudioPath))
                {
                    await ScreenRecorder.MergeVideoWithMultipleAudio(videoPath, systemAudioPath,
                        micAudioPath, outputPath);
                }

                elseif (!string.IsNullOrEmpty(systemAudioPath) && File.Exists(systemAudioPath))
                {
                    await ScreenRecorder.MergeVideoAndAudio(videoPath, systemAudioPath, outputPath);
                }

                elseif (!string.IsNullOrEmpty(micAudioPath) && File.Exists(micAudioPath))
                {
                    await ScreenRecorder.MergeVideoAndAudio(videoPath, micAudioPath, outputPath);
                }
                else
                {
                    File.Copy(videoPath, outputPath, true);
                }

                CleanupTempFiles(videoPath, systemAudioPath, micAudioPath);
            }
            catch (Exception ex)
            {
                thrownew Exception($"合并視頻音頻失敗: {ex.Message}", ex);
            }
        }

        // 清理臨時文件的方法
        private void CleanupTempFiles(params string[] filePaths)
        {
            foreach (var filePath in filePaths)
            {
                if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
                {
                    try
                    {
                        File.Delete(filePath);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"刪除臨時文件失敗: {ex.Message}");
                    }
                }
            }
        }


        private void PauseRecording()
        {
            if (!_isRecording) return;

            _isPaused = true;
            _recordingTimer.Stop();

            _screenRecorder?.PauseRecording();
            _audioRecorder?.PauseRecording();

            PauseButton.Content = "恢復(fù)錄制";
            StatusLabel.Content = "已暫停";
            StatusLabel.Foreground = System.Windows.Media.Brushes.Orange;
            StatusIndicator.Fill = System.Windows.Media.Brushes.Orange;
            DetailStatusLabel.Text = "錄制已暫停";
        }

        private void ResumeRecording()
        {
            if (!_isRecording) return;

            _isPaused = false;
            _recordingTimer.Start();

            _screenRecorder?.ResumeRecording();
            _audioRecorder?.ResumeRecording();

            PauseButton.Content = "暫停錄制";
            StatusLabel.Content = "錄制中...";
            StatusLabel.Foreground = System.Windows.Media.Brushes.Red;
            StatusIndicator.Fill = System.Windows.Media.Brushes.Red;
            DetailStatusLabel.Text = "正在錄制屏幕...";
        }

        private void UpdateRecordingTime(object sender, EventArgs e)
        {
            if (_isRecording && !_isPaused)
            {
                TimeSpan elapsed = DateTime.Now - _startTime;
                RecordTimeLabel.Content = elapsed.ToString(@"hh\:mm\:ss");
            }
        }

        private int GetSelectedQuality()
        {
            var selectedItem = VideoQualityComboBox.SelectedItem as System.Windows.Controls.ComboBoxItem;
            return selectedItem?.Content.ToString() switch
            {
                "高質(zhì)量 (1080p)" => 1080,
                "標(biāo)準(zhǔn)質(zhì)量 (720p)" => 720,
                "低質(zhì)量 (480p)" => 480,
                _ => 720
            };
        }

        private int GetSelectedFrameRate()
        {
            var selectedItem = FrameRateComboBox.SelectedItem as System.Windows.Controls.ComboBoxItem;
            return selectedItem?.Content.ToString() switch
            {
                "60 FPS" => 60,
                "30 FPS" => 30,
                "24 FPS" => 24,
                "15 FPS" => 15,
                _ => 30
            };
        }

        private Rectangle? GetRecordArea()
        {
            var selectedItem = RecordAreaComboBox.SelectedItem as System.Windows.Controls.ComboBoxItem;
            return selectedItem?.Content.ToString() switch
            {
                "全屏錄制" => Screen.PrimaryScreen.Bounds,
                "選擇窗口" => null, // 這里可以添加窗口選擇邏輯
                "自定義區(qū)域" => null, // 這里可以添加區(qū)域選擇邏輯
                _ => Screen.PrimaryScreen.Bounds
            };
        }

        private void BrowseButton_Click(object sender, RoutedEventArgs e)
        {
            var dialog = new System.Windows.Forms.FolderBrowserDialog();
            dialog.Description = "選擇錄屏文件保存路徑";

            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                OutputPathTextBox.Text = Path.Combine(dialog.SelectedPath, "錄屏_");
            }
        }

        private void OpenOutputFolderButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                string folderPath = Path.GetDirectoryName(OutputPathTextBox.Text);
                if (Directory.Exists(folderPath))
                {
                    Process.Start("explorer.exe", folderPath);
                }
                else
                {
                    MessageBox.Show("輸出文件夾不存在!", "錯誤", MessageBoxButton.OK, MessageBoxImage.Warning);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"打開文件夾失敗:{ex.Message}", "錯誤", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private async void MainWindow_KeyDown(object sender, KeyEventArgs e)
        {
            try
            {
                switch (e.Key)
                {
                    case Key.F9:
                        await HandleF9KeyPress();
                        e.Handled = true;
                        break;
                    case Key.F10:
                        HandleF10KeyPress();
                        e.Handled = true;
                        break;
                    case Key.F11:
                        HandleF11KeyPress();
                        e.Handled = true;
                        break;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"快捷鍵操作出錯:{ex.Message}", "錯誤",
                    MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        // F9 - 開始/停止錄制
        private async Task HandleF9KeyPress()
        {
            if (_isRecording)
            {
                await StopRecording();
            }
            else
            {
                await StartRecording();
            }
        }

        // F10 - 暫停/恢復(fù)錄制
        private void HandleF10KeyPress()
        {
            if (!_isRecording)
            {
                MessageBox.Show("請先開始錄制后再使用暫停功能", "提示",
                    MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            if (_isPaused)
            {
                ResumeRecording();
            }
            else
            {
                PauseRecording();
            }
        }

        // F11 - 截圖功能
        private void HandleF11KeyPress()
        {
            try
            {
                TakeScreenshot();
            }
            catch (Exception ex)
            {
                MessageBox.Show($"截圖失敗:{ex.Message}", "錯誤",
                    MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        // 截圖功能實現(xiàn)
        private void TakeScreenshot()
        {
            try
            {
                var bounds = Screen.PrimaryScreen.Bounds;
                using (var bitmap = new Bitmap(bounds.Width, bounds.Height))
                {
                    using (var graphics = System.Drawing.Graphics.FromImage(bitmap))
                    {
                        graphics.CopyFromScreen(bounds.X, bounds.Y, 0, 0, bounds.Size);
                    }

                    // 生成截圖文件名
                    string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
                    string screenshotPath = Path.Combine(
                        Path.GetDirectoryName(OutputPathTextBox.Text),
                        $"截圖_{timestamp}.png");

                    // 確保目錄存在
                    Directory.CreateDirectory(Path.GetDirectoryName(screenshotPath));

                    // 保存截圖
                    bitmap.Save(screenshotPath, System.Drawing.Imaging.ImageFormat.Png);

                    // 顯示成功消息
                    MessageBox.Show($"截圖已保存:\n{screenshotPath}", "截圖成功",
                        MessageBoxButton.OK, MessageBoxImage.Information);
                }
            }
            catch (Exception ex)
            {
                thrownew Exception($"截圖操作失敗:{ex.Message}");
            }
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            this.Focus();
        }

        private void MainWindow_Activated(object sender, EventArgs e)
        {
            this.Focus();
        }


        protected override void OnClosed(EventArgs e)
        {
            // 確保在關(guān)閉窗口時停止錄制
            if (_isRecording)
            {
                _screenRecorder?.Dispose();
                _audioRecorder?.Dispose();
            }
            _recordingTimer?.Stop();
            base.OnClosed(e);
        }
    }
}

圖片圖片

UI設(shè)計亮點

  • 全鍵盤快捷鍵支持,提升操作效率
  • 實時狀態(tài)反饋,用戶體驗友好
  • 響應(yīng)式布局適配不同屏幕尺寸

關(guān)鍵問題與解決方案

內(nèi)存泄漏防護

public void Dispose()
{
    if (_isRecording)
    {
        _isRecording = false;
    }

    _cancellationTokenSource?.Cancel();

    // 清理幀緩存
    while (_frameQueue.TryDequeue(out var frame))
    {
        frame.Bitmap?.Dispose();
    }

    _stopwatch?.Stop();
    _cancellationTokenSource?.Dispose();
    CleanupTempFiles();
}

線程安全保障

// 使用并發(fā)集合確保線程安全
private readonly ConcurrentQueue<FrameData> _frameQueue = new ConcurrentQueue<FrameData>();

// 取消令牌統(tǒng)一管理
private CancellationTokenSource _cancellationTokenSource;

異常處理機制

private async Task StopRecording()
{
    try
    {
        // 設(shè)置超時機制,避免無限等待
        var timeoutTask = Task.Delay(3000);
        var completedTask = await Task.WhenAny(_captureTask, timeoutTask);

        if (completedTask == timeoutTask)
        {
            Console.WriteLine("警告: 捕獲任務(wù)超時");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"停止錄制時出錯: {ex.Message}");
    }
}

部署與擴展

NuGet包依賴

<PackageReference Include="FFMpegCore" Version="4.8.0" />
<PackageReference Include="NAudio" Version="2.1.0" />
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />

性能監(jiān)控

// 隊列監(jiān)控
if (_frameQueue.Count > 500)
{
    bitmap?.Dispose();
    Console.WriteLine("隊列滿,丟棄一幀");
}

// 處理進度反饋
if (processedFrames % 100 == 0)
{
    Console.WriteLine($"已保存 {processedFrames} 幀,隊列剩余: {_frameQueue.Count}");
}

核心收獲總結(jié)

通過這個屏幕錄制器項目,你掌握了:

系統(tǒng)級編程:Windows API調(diào)用、GDI+圖形處理、音頻設(shè)備訪問等底層技術(shù)

多線程并發(fā):生產(chǎn)者-消費者模式、線程優(yōu)先級管理、并發(fā)集合使用

性能優(yōu)化:內(nèi)存管理、CPU密集型任務(wù)優(yōu)化、I/O操作異步化

這不僅僅是一個錄屏工具,更是一個完整的C#系統(tǒng)編程實戰(zhàn)案例。代碼中的設(shè)計模式、性能優(yōu)化技巧、異常處理機制都可以應(yīng)用到其他項目中。

責(zé)任編輯:武曉燕 來源: 技術(shù)老小子
相關(guān)推薦

2025-11-11 09:11:57

2025-03-04 00:20:45

2025-04-07 05:00:00

2023-05-09 08:34:51

PythonWith語句

2025-01-16 10:46:31

2017-07-11 15:26:57

LocalMQ RocketMQ高性能

2025-07-09 07:10:00

2025-03-25 09:00:00

2010-11-08 10:20:18

2025-07-17 13:52:57

通配符Linux命令行

2025-08-05 07:26:06

2025-09-08 06:10:00

FastAPI開發(fā)web

2025-10-30 01:45:00

DockerSwarm集群

2012-03-31 11:25:48

C#

2025-10-31 07:10:00

裝飾器Python代碼

2025-09-12 08:43:22

2025-03-06 14:00:00

C#性能頁面

2025-08-12 07:46:29

2025-09-08 11:00:00

2021-10-28 07:10:21

rollupPlugin插件編寫
點贊
收藏

51CTO技術(shù)棧公眾號

中文字幕综合一区| 欧美人体视频xxxxx| 免费成人毛片| 国产亚洲精品自拍| 91麻豆精品国产自产在线观看一区 | 这里只有久久精品| 国产精品无码免费播放| 国产中文字幕一区二区三区| 亚洲图片欧美视频| 91深夜福利视频| 女性裸体视频网站| 成人全视频免费观看在线看| 久久蜜臀中文字幕| 日本精品视频在线播放| 噜噜噜在线视频| 男女羞羞在线观看| av电影在线观看完整版一区二区| 精品中文字幕在线2019| 国产999免费视频| 麻豆影视国产在线观看| 久久99久久99精品免视看婷婷| 中文字幕日韩免费视频| 国产激情第一页| 91福利区在线观看| 99久久婷婷国产综合精品| 91精品国产综合久久香蕉的用户体验 | 伊是香蕉大人久久| 天美星空大象mv在线观看视频| 国产三级视频在线看| 日本不卡中文字幕| www.国产精品一二区| 中文字幕第22页| 污污网站在线观看| 白白色亚洲国产精品| 91久久久久久| 老熟妇一区二区三区啪啪| 日韩伦理一区| 日韩欧美国产电影| 国产h视频在线播放| 日本视频在线观看一区二区三区| 麻豆成人精品| 久久精品国产精品| 精品无码av一区二区三区| jizz内谢中国亚洲jizz| 国产精品久久久久久久久搜平片 | 欧美高跟鞋交xxxxhd| 国产xxxxxxxxx| 一区二区在线视频观看| 欧美性猛交xxx| 一区二区欧美日韩| 少妇人妻偷人精品一区二区| 日韩精品91亚洲二区在线观看| 日韩中文字幕网| 国产调教在线观看| 成人av地址| 欧美主播一区二区三区美女| 91精品一区二区三区四区| 无码精品人妻一区二区| 久久97超碰国产精品超碰| 国产欧美亚洲视频| 一卡二卡三卡在线观看| 国产精品乱看| 欧美最近摘花xxxx摘花| 黄色a级片在线观看| 亚洲肉体裸体xxxx137| 日韩一级二级三级| 韩国中文字幕av| 成人全视频免费观看在线看| 在线不卡欧美精品一区二区三区| 久久久精品在线视频| 91中文在线| 国产女主播视频一区二区| 动漫一区二区在线| 亚洲香蕉在线视频| 亚洲一区二区免费看| 欧美中文字幕在线观看| 中文字幕av资源| 国产精品资源| 国产精品吴梦梦| 永久免费无码av网站在线观看| 欧美+日本+国产+在线a∨观看| 亚洲天堂av女优| 尤物网站在线观看| 亚洲理论电影片| 日韩在线欧美在线| 全黄一级裸体片| 日韩欧美自拍| 欧美丰满老妇厨房牲生活| 日本在线观看视频网站| 欧美午夜不卡| 欧美成人午夜影院| 亚洲熟女毛茸茸| 欧美一区二区三| 亚洲欧洲国产伦综合| 992在线观看| 尤物精品在线| 高清视频欧美一级| 欧美国产在线看| 综合激情一区| 免费97视频在线精品国自产拍| 日本午夜小视频| 麻豆精品久久久| 国产精品永久免费观看| 亚洲欧美强伦一区二区| 国产91在线|亚洲| 超碰97在线资源| 国产 日韩 欧美 综合| 久久久亚洲高清| 欧美精品一区二区视频| 男人天堂网在线| 久久精品一区蜜桃臀影院| 一级全黄肉体裸体全过程| 搞黄网站在线观看| 一区二区三区日韩精品| 成人免费在线视频播放| 黄网站在线观| 五月天丁香久久| 免费高清在线观看免费| 视频一区日韩精品| 亚洲黄色有码视频| 中文精品在线观看| 精品国产一区二区三区av片| 伊人男人综合视频网| 亚洲国产精一区二区三区性色| 久久99精品久久只有精品| 欧美日韩亚洲一区二区三区在线观看| 污污片在线免费视频| 91超碰这里只有精品国产| 国产真实乱人偷精品人妻| 韩日一区二区三区| 97在线精品视频| aaa在线视频| 精品在线亚洲视频| 亚洲国产精品日韩| 黄av在线播放| 五月天久久比比资源色| 精品人妻无码中文字幕18禁| 日韩欧美四区| 日韩在线视频免费观看高清中文| 精品一区二区无码| 国产美女精品一区二区三区| 国产一区二中文字幕在线看| 国产尤物视频在线| **欧美大码日韩| 国产日本在线播放| 国产私拍福利精品视频二区| 欧美一区二区三区白人| 99久久人妻精品免费二区| 精品视频网站| 国产精品久久久久久超碰| av资源免费看| 国产亚洲精久久久久久| 日韩一级特黄毛片| 国产综合色区在线观看| 欧美第一区第二区| 麻豆91精品91久久久| 丝袜脚交一区二区| 成人国产1314www色视频| 婷婷色在线资源| 精品剧情v国产在线观看在线| 国产伦精品一区二区三区视频女| 欧美区日韩区| 国产精品国产三级国产专区53| 国产剧情在线观看| 在线观看日韩毛片| 国产成人在线网址| 国产一区二区三区不卡在线观看 | 久久99精品久久久水蜜桃| 日本免费中文字幕在线| 高跟丝袜欧美一区| 日本55丰满熟妇厨房伦| 精品国产乱码| 国产日韩欧美黄色| 污污视频在线看| 亚洲欧美国产制服动漫| 日韩美女视频网站| 国产亚洲精品福利| 亚洲一二三不卡| 亚洲国产一区二区三区a毛片| 成人欧美一区二区三区黑人孕妇| 风间由美一区| 欧美一区二区在线视频| 日本高清www免费视频| 国产精品99久久久久久久女警| 无码免费一区二区三区免费播放 | 国产传媒在线播放| 亚洲国产精品视频在线观看| 中文字幕人妻一区二区三区视频 | 久久手机免费视频| 青青草视频在线观看免费| 中文字幕一区二区三区在线播放| 性色av蜜臀av浪潮av老女人 | 妖精视频在线观看| 亚洲精品资源| 一道本在线观看视频| 亚洲欧洲美洲国产香蕉| 亚洲在线第一页| 国产在线观看免费麻豆| 亚洲第一精品夜夜躁人人爽| 久久久国产精品黄毛片| 国产剧情在线观看一区二区| 国产97在线 | 亚洲| 国产精品久久久久久久免费观看| 国产欧美日韩高清| 川上优av中文字幕一区二区| 亚洲精品在线三区| 粉嫩aⅴ一区二区三区| 国产精品私人影院| 色乱码一区二区三区在线| 水蜜桃久久夜色精品一区| 国产精品羞羞答答| 日产福利视频在线观看| 久久91精品国产91久久跳| 91在线看黄| 91精品福利在线一区二区三区 | 欧美一区二区三区不卡| 五月激情丁香网| 欧美午夜精品在线| 久久久久香蕉视频| 最新热久久免费视频| 亚洲图片另类小说| 成人18视频日本| 国产精品丝袜久久久久久消防器材 | 欧美在线免费观看亚洲| 亚洲第一在线播放| 国产视频亚洲色图| 少妇毛片一区二区三区| 成人一级片在线观看| 免费观看美女裸体网站| 夜夜躁狠狠躁日日躁2021日韩| 91入口在线观看| 漫画在线观看av| 久久久免费精品视频| 99福利在线| 欧美成人第一页| 97影院秋霞午夜在线观看| 久久亚洲精品一区二区| 黄色一级片在线观看| 精品国产一区二区三区四区在线观看 | 国产精品午夜春色av| 亚洲精品成人无码| 国产日韩欧美激情| 欧美一区二区三区粗大| 欧美韩国日本不卡| 超碰人人干人人| 国产精品网友自拍| 久久久久久久久久97| 亚洲日本在线看| 精品人妻一区二区三区日产乱码卜| 国产成人一级电影| 欧美xxxxx少妇| 久久国产精品色| 日本黄色的视频| 亚洲永久免费| 日韩一二区视频| 牛牛国产精品| 日韩亚洲欧美视频| 先锋影音国产一区| 无码人妻精品一区二区三区66| 欧美日本亚洲韩国国产| 无码毛片aaa在线| 国产精品一区二区av日韩在线| 免费看成人片| jizz国产精品| 美日韩免费视频| 色综合久久网| 高清无码视频直接看| 99av国产精品欲麻豆| 无码人妻精品一区二区蜜桃百度| 欧美搞黄网站| www在线观看免费| 日韩成人免费在线| 一级黄色片在线免费观看| 成人精品小蝌蚪| 国产成人无码精品久久二区三| 亚洲欧洲日韩在线| 国产一卡二卡在线| 欧美性xxxxxx少妇| 日本久久综合网| 欧美日韩夫妻久久| 国产成人av免费| 日韩女同互慰一区二区| 日韩在线免费播放| 久久久国产在线视频| 国产第一页在线| 国产美女高潮久久白浆| 9l视频自拍九色9l视频成人| 国产一区二区三区无遮挡| 高清日韩欧美| 91在线免费看片| 五月天亚洲色图| 最新欧美日韩亚洲| 手机在线电影一区| 国产在线播放观看| 精品91在线| 麻豆三级在线观看| 免费亚洲电影在线| 精品www久久久久奶水| 一区二区91| 一级做a免费视频| 91丨porny丨在线| 李宗瑞91在线正在播放| 国产精品高潮久久久久无| 尤物视频在线观看国产| 91精品黄色片免费大全| 黄色软件在线观看| 午夜精品久久久久久久男人的天堂 | 精品无吗乱吗av国产爱色| 另类天堂视频在线观看| 日本在线视频一区二区| 久久国产精品亚洲va麻豆| 欧美二区视频| 久久久久久蜜桃一区二区| 91老师片黄在线观看| 久久精品波多野结衣| 欧美日韩国产在线观看| 蜜桃视频在线免费| 久久久人成影片一区二区三区观看| 四虎精品在线观看| 亚洲综合社区网| 日韩精品1区| 国产一级做a爰片久久| 久久中文娱乐网| 精品欧美一区二区三区免费观看| 91久久线看在观草草青青| 亚洲视频在线观看一区二区 | 97超碰最新| 亚洲老妇激情| 91精品视频国产| 亚洲啪啪综合av一区二区三区| 中国精品一区二区| 国产亚洲视频在线| 国产美女av在线| 国产综合色香蕉精品| 日韩精品一区二区三区免费观看| 男女视频一区二区三区| 久久久久久久性| 亚洲成人av影片| 亚洲欧美综合另类中字| 高清不卡av| 日韩视频在线播放| 国产精品mv在线观看| 丰满的少妇愉情hd高清果冻传媒| 国产精品一区三区| 69av视频在线| 日韩欧美不卡在线观看视频| 在线xxxx| 国产精品久久精品国产| 亚洲黄页一区| 玖草视频在线观看| 日韩欧美精品免费在线| 黄色av网站在线免费观看| 国产极品精品在线观看| 日韩免费精品| 欧美中日韩在线| 成人免费黄色大片| 成年免费在线观看| 亚洲人免费视频| 亚洲毛片在线免费| 欧美亚洲另类在线一区二区三区| 久久精品卡一| 天堂av网手机版| 懂色aⅴ精品一区二区三区蜜月| 亚洲欧洲视频在线观看| 国产福利精品在线| 天天久久综合| 最新版天堂资源在线| 天天av天天翘天天综合网色鬼国产| 香蕉视频免费在线看| 国产精品欧美日韩| 爽爽窝窝午夜精品一区二区| 人妻无码视频一区二区三区 | 一区二区三区av| 国产精品一区二区x88av| 黄色一级片免费看| 在线精品国产欧美| 亚洲国产欧美国产第一区| 欧美久久久久久久久久久久久| 国产午夜精品久久久久久久 | 国产精品一区二区三区99| 国产成人在线免费观看视频| 亚洲最新av在线| 97一区二区国产好的精华液| 免费大片在线观看| 最新不卡av在线| 亚洲av电影一区| 成人精品在线视频| 国产亚洲综合精品| 九九精品视频免费| 欧美日本精品一区二区三区| 国内高清免费在线视频| 色噜噜狠狠色综合网| 国产福利91精品| 中文字幕观看视频| 久久久在线免费观看| 久久伦理在线| 国产白嫩美女无套久久| 日韩欧美一区二区在线视频| 吞精囗交69激情欧美| 日本黑人久久|