C#通过调用ffmpeg调用摄像头录像拍照

本文底层逻辑是通过调用cmd命令去操作ffmpeg.exe文件,以及相应命令去控制摄像头

首先需要去官网下载ffmpeg文件

网址:https://www.ffmpeg.org/download.html

下载好以后解压,找到里面bin文件夹下的ffmpeg.exe,将文件保存到项目中,这块文件夹要记住

首先先做准备工作,先在VS中打开项目csproj文件进行编辑,在文件中加入代码:

  <Target Name="CopyFfmpegDlls" AfterTargets="Build">
	  <Copy SourceFiles="你存放ffmpeg.exe的文件的路径\ffmpeg.exe" DestinationFolder="$(OutputPath)" />
  </Target>

这段代码是将ffmpeg.exe在生成解决方案时一并弄到生成项目后的bin目录下,后期代码调用全是基于这个实现的!!!

也可以通过设置VS中文件属性去生成到bin中:

两种随便一种都可以,我两种方法一并使用了,必须保证ffmpeg.exe要在项目生成后的bin目录下!

至此准备工作完成,ffmpeg实体类代码为以下:

这块我怕文件冲突直接将名称都设为当前时间了,部分变量名可以酌情自己去自定义

namespace Test.Utils
{
    public class ffmpegManager
    {
        Process p;

        public ffmpegManager()
        {
            Init();
        }

        /// <summary>
        /// 初始化操作
        /// </summary>
        private void Init()
        {
            p = new Process();
            //设置要启动的应用程序
            p.StartInfo.FileName = "cmd.exe";
            //是否使用操作系统shell启动
            p.StartInfo.UseShellExecute = false;
            // 接受来自调用程序的输入信息
            p.StartInfo.RedirectStandardInput = true;
            //输出信息
            p.StartInfo.RedirectStandardOutput = true;
            // 输出错误
            p.StartInfo.RedirectStandardError = true;
            //不显示程序窗口
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
            p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
            p.ErrorDataReceived += new DataReceivedEventHandler(p_ErrorDataReceived);

            //启动程序
            p.Start();
            p.BeginOutputReadLine();
            p.BeginErrorReadLine();
        }
        /// <summary>
        /// 录制视频
        /// </summary>
        /// <param name="videoDeviceName">电脑摄像头的名称</param>
        /// <param name="fileName">保存视频的名称</param>

        public void RecordVideo(string videoDeviceName,  string fileName)
        {

            //可以根据需要修改命令中的参数
            //完整命令:ffmpeg -f dshow -s 320*180 -i video="HP HD Camera" -f dshow -i audio="麦克风 (USB Microphone)" -vcodec mpeg4 -acodec aac -strict -2 myVideo.mp4
            string currentDirectory = Directory.GetCurrentDirectory();
            string relativePath = "视频文件夹";
            string path = Path.Combine(currentDirectory, relativePath);
            string command = "ffmpeg -f dshow -s 1920x1080 -framerate 30 -i video=\"" + videoDeviceName + "\" -vcodec mpeg4 -acodec aac -strict -2 \"" + path + "\\"+ fileName + ".mp4\"";

            //向cmd窗口发送输入信息
            p.StandardInput.WriteLine(command);
            p.StandardInput.AutoFlush = true;
        }

        //停止录制
        public void StopRecord()
        {
            //输入q命令停止录制
            p.StandardInput.WriteLine("q");
        }

        private void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data != null)
            {
                Console.WriteLine(e.Data);
            }
        }

        private void p_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data != null)
            {
                Console.WriteLine(e.Data);
            }
        }

        //拍照
        public void takePhoto(String imageName) {


            string currentDirectory = Directory.GetCurrentDirectory();
            string relativePath = "截图文件夹";
            string path = Path.Combine(currentDirectory, relativePath);

            // 生成保存的文件路径(假设文件名为 photo.jpg)
            //string currentTime = DateTime.Now.ToString("yyyyMMdd_HHmmss");
            string filePath = Path.Combine(path, imageName + ".jpg");

            // 构造 ffmpeg 命令
            string ffmpegCommand = $"ffmpeg -f dshow -i video=\"USB Camera\" -vframes 1 \"{filePath}\"";

            //向cmd窗口发送输入信息
            p.StandardInput.WriteLine(ffmpegCommand);
            p.StandardInput.AutoFlush = true;

            // 确保不会过早调用 Close,允许系统有时间完成资源处理
            Thread.Sleep(2000);

        }


    }

}

我在基础上包装了一层工具类方便直接调用:

namespace Test.Utils
{
    public static class VideoUtils
    {

        /// <summary>
        /// 开始录制
        /// </summary>       
        public static bool startRecord = false;
        /// <summary>
        /// 暂停录制
        /// </summary>
        private static bool pauseRecord = false;
       

        private static ffmpegManager manager = new ffmpegManager();

       

        public static void Open()
        {

            //创建监控保存路径
            //录制后的文件存到了项目的bin路径下,也可以自己自定义
            string currentDirectory = Directory.GetCurrentDirectory();
            string relativePath = "视频文件夹";
            string path = Path.Combine(currentDirectory, relativePath);

            string currentDirectory2 = Directory.GetCurrentDirectory();
            string relativePath2 = "截图文件夹";
            string path2 = Path.Combine(currentDirectory2, relativePath2);

            string folderPath = @path;
            string folderPath2 = @path2;

            // 调用方法检测和创建文件夹
            EnsureFolderExists(folderPath);
            EnsureFolderExists(folderPath2);

        }


        /// <summary>
        /// 开始录制
        /// </summary>  
        public static void Start()
        {
            if (!startRecord)
            {
                if (pauseRecord)
                {
                    startRecord = true;
                    pauseRecord = false;
                    return;
                }

                string currentTime = DateTime.Now.ToString("yyyyMMdd_HHmmss");

                startRecord = true;
                pauseRecord = false;

                manager = new ffmpegManager();
                manager.RecordVideo("USB Camera", currentTime);
            }
        }


        /// <summary>
        /// 停止录制
        /// </summary>
        public static void Stop()
        {
            startRecord = false;
            pauseRecord = false;
            // 确保不会过早调用 Close,允许系统有时间完成资源处理
            Thread.Sleep(20);

            manager.StopRecord();
        }



        /// <summary>
        /// 截图拍照
        /// </summary>   
        public static void TakePhoto(String imageName)
        {
            manager.takePhoto(imageName);

        }


        public static void EnsureFolderExists(string folderPath)
        {
            // 检测路径是否存在
            if (!Directory.Exists(folderPath))
            {
                try
                {
                    // 创建路径
                    Directory.CreateDirectory(folderPath);
                    Console.WriteLine("文件夹创建成功!");
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"创建文件夹失败:{ex.Message}");
                }
            }
            else
            {

            }
        }

    }
}


 

接下来是具体的调用,首先是调用其拍照,这里直接写点击录像的方法:

首先是前端Xaml:

        <Button  x:Name="last"  Content="录制" Style="{StaticResource ButtonStyleEllipse}"  Width="170" FontSize="22" Height="70"   Click="Start" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Grid.RowSpan="2" Background="#FFE3EFFA" FontFamily="SourceHanSansCN-Bold" Foreground="#FF0065CD" FontWeight="Bold" Margin="60,50,450,61" />


        <Button  x:Name="next" Content="停止" Style="{StaticResource ButtonStyleEllipse}"  Width="170" FontSize="20" Height="70"   Click="Stop" Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Background="#FF0065CD" FontFamily="SourceHanSansCN-Bold" Foreground="White" FontWeight="Bold" Margin="115,50,275,61" />

接下来是录制视频后端cs实现:

namespace DrugControlApp
{

    public partial class MainWindow : BaseWindow
    {
       

        public MainWindow(bool is_verify = true)
        {
            InitializeComponent();
            

            //项目启动检测是否有对应文件夹
            VideoUtils.Open();


        }
        


        private void Start(object sender, RoutedEventArgs e)
        {

            try
            {
                VideoUtils.Start();
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }


        }


        private void Stop(object sender, RoutedEventArgs e)
        {

            try
            {
                VideoUtils.Stop();
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }

        }

        
    }
}

其次是拍照截图功能,同样首先放前端xaml代码:

            <Label Name="prescribePic" Visibility="Visible"  Content="照片" Grid.Row="5" Grid.Column="5"  Grid.ColumnSpan="2" HorizontalAlignment="Left" VerticalAlignment="Top" RenderTransformOrigin="1.9,1.88" FontFamily="SourceHanSansCN" FontSize="20" FontWeight="Bold" Foreground="#0E63CD"/>
            <Button  x:Name="loadPicBtn" Content="上传图片"  Visibility="Visible" HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Row="5" Grid.Column="5"  Grid.ColumnSpan="2" FontSize="22" Width="180" Height="60" Background="#2BB669" Foreground="#FFFFFF" Style="{DynamicResource ButtonStyleEllipse}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontFamily="Source Han Sans CN" FontWeight="Bold"  Click="uploadImage" />

            <Canvas Grid.Row="5" Grid.Column="5"  Grid.ColumnSpan="3" Grid.RowSpan="2" Width="400" Height="200" Margin="0,37,80,33"  >
                <Label Name="prescribePicText" Visibility="Collapsed"  Content="请对准摄像头并点击拍照按钮" Grid.Row="5" Grid.Column="5"  Grid.ColumnSpan="3" HorizontalAlignment="Left" VerticalAlignment="Top" RenderTransformOrigin="1.9,1.88" FontFamily="SourceHanSansCN" FontSize="20" FontWeight="Bold" Foreground="#0E63CD" Grid.RowSpan="2" Canvas.Left="33" Canvas.Top="87"/>
                <Image x:Name="prescribeCamera"   Stretch="Fill" Canvas.Left="0" Canvas.Top="0" Width="400" Height="200" />
                <Rectangle   x:Name="picBorder"  Visibility="Collapsed" Width="400" Height="200" Fill="Transparent" Stroke="Gray" StrokeThickness="2" StrokeDashArray="2,2"/>
                <Image x:Name="loadPic" Visibility="Collapsed" Stretch="Fill" Canvas.Left="0" Canvas.Top="0" Width="400" Height="200"/>
            </Canvas>
            <Button x:Name="snapshot" Content="拍照" Visibility="Collapsed" Grid.Row="6" Grid.Column="7" Grid.RowSpan="1"  Style="{StaticResource ButtonStyleEllipse}"  FontFamily="SourceHanSansCN" FontSize="12" FontWeight="Bold" Background="#FF0E63CD"   Foreground="White" Width="50"  Height="25"   Click="screenshot" Margin="80,77,30,33"/>
            <Button x:Name="delSnapshot" Content="删除" Visibility="Collapsed" Grid.Row="6" Grid.Column="7" Grid.RowSpan="1"  Style="{StaticResource ButtonStyleEllipse}"  FontFamily="SourceHanSansCN" FontSize="12" FontWeight="Bold" Background="Red"   Foreground="White" Width="50"  Height="25"   Click="deleteScreenshot" Margin="80,77,30,33"/>

后端cs实现:

//点击 上传照片 按钮
private void uploadImage(object sender, RoutedEventArgs e) {
    loadPicBtn.Visibility = Visibility.Collapsed;
    picBorder.Visibility = Visibility.Visible;
    snapshot.Visibility = Visibility.Visible;
    prescribeCamera.Visibility = Visibility.Visible;
    prescribePicText.Visibility = Visibility.Visible;
}





//拍照
private  void screenshot(object sender, RoutedEventArgs e) {

    string prescriptionName = PrescriptionName.Text;
    try
    {

        string currentTime = DateTime.Now.ToString("yyyyMMdd_HHmmss");
        string photoName = "名称_" + prescriptionName + "创建日期_" + currentTime;
        VideoUtils.TakePhoto(photoName);
        string currentDirectory = Directory.GetCurrentDirectory();
        string relativePath = "截图";
        string path = System.IO.Path.Combine(currentDirectory, relativePath);
        string filePath = System.IO.Path.Combine(path, photoName + ".jpg");

        loadPic.Source = getImage(filePath);
        LoggerFactory.GetLog().Info(loadPic.Source);
        loadPic.UpdateLayout(); // 强制刷新UI
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }

    prescribeCamera.Visibility = Visibility.Collapsed;
    snapshot.Visibility = Visibility.Collapsed;
    delSnapshot.Visibility = Visibility.Visible;
    loadPic.Visibility = Visibility.Visible;

}






//删除已上传的照片
private void deleteScreenshot(object sender, RoutedEventArgs e) {

    delSnapshot.Visibility = Visibility.Collapsed;
    snapshot.Visibility = Visibility.Collapsed;
    loadPic.Visibility = Visibility.Collapsed;
    loadPicBtn.Visibility = Visibility.Visible;
    picBorder.Visibility = Visibility.Collapsed;
    prescribePicText.Visibility = Visibility.Collapsed;

    // 获取BitmapImage对象
    BitmapImage bitmap = loadPic.Source as BitmapImage;
    // 从UriSource获取本地路径
    string imagePath = bitmap.UriSource.LocalPath;
    try
    {
        // 删除图片文件
        File.Delete(imagePath);
        Console.WriteLine("文件已成功删除");
        loadPic.Source = null;

    }
    catch (Exception ex)
    {
        CustomDialogTipModel tipModel = new CustomDialogTipModel();
        tipModel.InputParameter = "删除文件时发生错误,请联系管理员!";
        CustomDialogPrompt dialogPrompt = new CustomDialogPrompt();
        dialogPrompt.DataContext = tipModel;
        bool? result = dialogPrompt.ShowDialog();
        if (result == true)
        {

        }
        LoggerFactory.GetLog().Error(string.Format("删除文件时发生错误:{0}", ex.Message));
    }
}







public ImageSource getImage(string file)
{
    var bmp = new BitmapImage();
    try
    {
        bmp.BeginInit();
        bmp.CacheOption = BitmapCacheOption.OnLoad;        
        LoggerFactory.GetLog().Info(file);
        bmp.UriSource = new Uri(@file);
        bmp.EndInit();

    }
    catch (Exception ex)
    {
        LoggerFactory.GetLog().Error("Error during image loading", ex);
    }
    return bmp;
}


 

部分借鉴到的文章地址:使用ffmpeg调用摄像头录制视频(C#)_c# ffmpeng 打开网络摄像机-优快云博客

能站在巨人肩膀上开发真的很荣幸,非常 感谢上面这位大佬给出的解决。

这个难题困扰了我多个星期,从项目引用Aforge,到accord的dll文件,不断的踩坑不断的填坑,后期询问多个同事以及领导能得出的最优解决方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值