所以,我正在开发一个LAN-Video-Streaming程序,它记录单个图像并将它们发送出去。因为每秒发送30张1920x1080图片太多了,为了获得30FPS,我做了一些研究并发现了JPEG压缩。问题是,当我尝试保存压缩的JPEG时,它会抛出System.Runtime.InteropServices.ExternalException
,并附加以下信息:General error in GDI+.
这是我的代码:
Private Sub Stream() Handles StreamTimer.Tick
If Streaming = True Then
Try
ScreenCap = New Bitmap(Bounds.Width, Bounds.Height)
GFX = Graphics.FromImage(ScreenCap)
GFX.CopyFromScreen(0, 0, 0, 0, ScreenCap.Size)
Dim Frame As New Bitmap(ScreenCap, Resolution.Split(";")(0), Resolution.Split(";")(1))
Dim jpgEncoder As ImageCodecInfo = GetEncoder(ImageFormat.Jpeg)
Dim myEncoder As Encoder = Encoder.Quality
Dim myEncoderParameters As New EncoderParameters(1)
Dim myEncoderParameter As New EncoderParameter(myEncoder, Compression)
myEncoderParameters.Param(0) = myEncoderParameter
Frame.Save(My.Computer.FileSystem.SpecialDirectories.Temp & "\LSSFrame.jpg", jpgEncoder, myEncoderParameters) 'Error occurs in this line
Using FS As New FileStream(My.Computer.FileSystem.SpecialDirectories.Temp & "\LSSFrame.jpg", FileMode.Open)
Frame = Image.FromStream(FS)
FrameSizeStatus.Text = Math.Round(FS.Length / 1000) & "KB"
FS.Close()
End Using
PreviewBox.Image = Frame
FPSStat += 1
FlushMemory()
If ViewerIPs.Count > 0 Then
For i = 0 To ViewerIPs.Count - 1
SendFrame(ViewerIPs(i), Frame)
Next
End If
Catch ex As Exception
LostFrames += 1
End Try
End If
End Sub
感谢任何帮助!
答案 0 :(得分:1)
部分原因是您没有处置您创建的Graphics
或Bitmap
个对象。错误消息不是很有用,但不处理这些消息会使资源无法恢复。
该程序还有很多事情要做。如果将其分解为部分,则可能更容易对性能进行微调等。
' form level objects
Private jEncParams As EncoderParameters
Private jpgEncoder As ImageCodecInfo
...
' inititalize somewhere when the process starts:
Dim quality As Int64 = 95
jpgEncoder = GetEncoder(ImageFormat.Jpeg)
Dim myjEnc As Imaging.Encoder = Imaging.Encoder.Quality
jEncParams = New EncoderParameters(1)
' quality is inverse to compression
jEncParams.Param(0) = New EncoderParameter(myjEnc, quality)
由于每个屏幕快照都不会更改编码器和质量元素,因此请创建一次并重新使用它们。然后你的计时器事件:
Dim scrBytes = GetScreenSnap(1280, 720)
' do something to send them....maybe queue them?
Console.WriteLine("image size: {0}k", (scrBytes.Length / 1024).ToString)
优化SendFrame
超出了此Q / A的范围,但获取屏幕截图与发送不同。
Private Function GetScreenSnap(w As Int32, h As Int32) As Byte()
Using bmpScrn As New Bitmap(My.Computer.Screen.Bounds.Width, My.Computer.Screen.Bounds.Height)
Using g As Graphics = Graphics.FromImage(bmpScrn)
g.CopyFromScreen(0, 0, 0, 0, bmpScrn.Size)
End Using ' done with graphics
Using bmpThumb As New Bitmap(bmpScrn, w, h),
ms As New MemoryStream
bmpThumb.Save(ms, jpgEncoder, jEncParams)
Return ms.ToArray
End Using ' dispose of bmp
End Using ' dispose of bmpScrn
End Function
没有特别的原因,我正在缩略图整个屏幕。您似乎没有使用Bounds.Width, Bounds.Height
,因为这会引用表单。如果表单最大化,它将仅用作屏幕快照。关键点:
各种质量因素的结果大小:
100 = 462
95 = 254
90 = 195
80 = 147
严格来说,您不需要编码器,bmpThumb.Save(ms, Imaging.ImageFormat.Jpeg)
也可以使用,但是单个编码器对象可以提供更精细的调整。
对于发送部分,您可能需要Stack
,Queue
或LinkedList
来存储字节数组。这将进一步隔离让图像发送它们。该集合将是一种ToDo / ToSend列表。
然后,如果有多个收件人,我会考虑将SendFrame
作为Task
,或许每次发送2-3个。可能有一点,接收者的数量会影响你抓住新的速度。