如何在C#中用FFmpeg在PictureBox播放视频并解决Base64转换问题?
解决C#中FFmpeg流式播放视频到PictureBox的Base64转换错误
问题根源
你当前代码的核心错误是错误地将FFmpeg输出的原始二进制帧数据当成了Base64字符串处理。image2pipe输出的是未经编码的RGB24像素流,属于二进制数据,而非文本格式的Base64。用BeginOutputReadLine()读取文本流会破坏二进制数据结构,后续的Convert.FromBase64String()自然会抛出非法字符的错误。
正确实现方案
核心思路
- 直接读取FFmpeg的
StandardOutput二进制流,而非文本行 - 提前获取视频的宽高,计算单帧RGB24数据的字节数(宽×高×3)
- 在后台线程中循环读取帧数据,转换为Bitmap后跨线程更新PictureBox
- 严格管理资源,避免内存泄漏
完整修正代码
全局变量定义
private Process _ffmpegProcess; private Thread _frameReadThread; private int _videoWidth; private int _videoHeight;
播放按钮点击事件
private void button5_Click(object sender, EventArgs e) { if (_ffmpegProcess != null) return; string videoPath = @"C:/Users/google/Desktop/New folder/video.mp4"; string ffmpegPath = @"C:/Users/google/Desktop/ffmpeg.exe"; // 获取视频分辨率,用于计算单帧字节数 GetVideoResolution(ffmpegPath, videoPath, out _videoWidth, out _videoHeight); if (_videoWidth == 0 || _videoHeight == 0) { MessageBox.Show("无法获取视频分辨率"); return; } _ffmpegProcess = new Process(); _ffmpegProcess.StartInfo.FileName = ffmpegPath; // 指定输出RGB24原始帧,保持原视频帧率 _ffmpegProcess.StartInfo.Arguments = $"-i \"{videoPath}\" -vf format=rgb24 -r 30 -f rawvideo -pix_fmt rgb24 -"; _ffmpegProcess.StartInfo.RedirectStandardOutput = true; _ffmpegProcess.StartInfo.UseShellExecute = false; _ffmpegProcess.StartInfo.CreateNoWindow = true; _ffmpegProcess.Start(); // 启动后台线程读取二进制帧 _frameReadThread = new Thread(ReadFrames); _frameReadThread.IsBackground = true; _frameReadThread.Start(); }
获取视频分辨率方法
private void GetVideoResolution(string ffmpegPath, string videoPath, out int width, out int height) { width = 0; height = 0; Process process = new Process(); process.StartInfo.FileName = ffmpegPath; process.StartInfo.Arguments = $"-i \"{videoPath}\" -hide_banner"; process.StartInfo.RedirectStandardError = true; // FFmpeg信息输出到stderr process.StartInfo.UseShellExecute = false; process.StartInfo.CreateNoWindow = true; process.Start(); string output = process.StandardError.ReadToEnd(); process.WaitForExit(); // 正则解析宽高 var regex = new System.Text.RegularExpressions.Regex(@"Stream #0:0.*Video:.* (\d+)x(\d+)"); var match = regex.Match(output); if (match.Success) { width = int.Parse(match.Groups[1].Value); height = int.Parse(match.Groups[2].Value); } }
后台线程读取帧并更新UI
private void ReadFrames() { int frameSize = _videoWidth * _videoHeight * 3; // RGB24每个像素占3字节 byte[] frameBuffer = new byte[frameSize]; try { while (_ffmpegProcess != null && !_ffmpegProcess.HasExited) { // 读取完整一帧的字节数据 int bytesRead = _ffmpegProcess.StandardOutput.Read(frameBuffer, 0, frameSize); if (bytesRead != frameSize) break; // 读取不完整,视频结束或出错 // 将RGB24字节流转换为Bitmap using (Bitmap bitmap = new Bitmap(_videoWidth, _videoHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb)) { var bmpData = bitmap.LockBits( new System.Drawing.Rectangle(0, 0, _videoWidth, _videoHeight), System.Drawing.Imaging.ImageLockMode.WriteOnly, bitmap.PixelFormat); System.Runtime.InteropServices.Marshal.Copy(frameBuffer, 0, bmpData.Scan0, frameSize); bitmap.UnlockBits(bmpData); // 跨线程更新PictureBox,同时释放旧资源 pictureBox1.Invoke((MethodInvoker)(() => { pictureBox1.Image?.Dispose(); pictureBox1.Image = new Bitmap(bitmap); })); } } } catch (Exception ex) { MessageBox.Show($"读取视频帧出错:{ex.Message}"); } finally { StopVideo(); } }
停止播放及资源释放
private void StopVideo() { // 终止后台读取线程 if (_frameReadThread != null && _frameReadThread.IsAlive) { _frameReadThread.Abort(); _frameReadThread = null; } // 停止FFmpeg进程 if (_ffmpegProcess == null) return; try { if (!_ffmpegProcess.HasExited) _ffmpegProcess.Kill(); } catch { } finally { _ffmpegProcess.Dispose(); _ffmpegProcess = null; } // 清空PictureBox并释放资源 pictureBox1.Invoke((MethodInvoker)(() => { pictureBox1.Image?.Dispose(); pictureBox1.Image = null; })); }
窗体关闭时清理
private void Form1_FormClosing(object sender, FormClosingEventArgs e) { StopVideo(); }
关键注意事项
- 禁用文本读取:绝对不能用
OutputDataReceived或BeginOutputReadLine()处理二进制帧数据,必须直接读取StandardOutput的字节流 - 资源释放:每次更新PictureBox时,务必释放旧的
Image对象,否则会导致严重的内存泄漏 - 线程安全:UI控件只能在UI线程更新,必须用
Invoke或BeginInvoke切换线程上下文 - 帧率控制:FFmpeg命令中
-r 30指定输出帧率,可根据视频实际情况调整,避免丢帧或卡顿
内容的提问来源于stack exchange,提问作者Frost Dream
相关产品推荐
相关产品推荐

