2016-01-03 2 views
0

Итак, я сейчас работаю над программой LAN-Video-Streaming, которая записывает одиночные изображения и отправляет их. Поскольку было бы слишком много для отправки 30 1920x1080 снимков в секунду, чтобы получить 30FPS, я провел некоторое исследование и нашел JPEG-компрессию. Проблема заключается в том, что, когда я пытаюсь сохранить сжатый JPEG, он бросает System.Runtime.InteropServices.ExternalException, с дополнительной информацией: General error in GDI+.JPEG Сжатие вызывает GDI + Exception

Вот мой код:

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

Вы не владеете надлежащим образом, что может привести к этой ошибке. Проконсультируйтесь с должностями справа ** **. **. Я сомневаюсь, что вы когда-нибудь получите 30 снимков в секунду. – Plutonix

+0

Ну, как я могу это сделать? Я знаю, как распоряжаться вещами, но ** что я должен распоряжаться? Кстати, я тестировал id, и он даже работает до 60FPS. : D –

+0

Хорошо, спасибо. Я просмотрю его снова и исправлю свои ошибки. : D –

ответ

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, так как это относится к форме. Он будет работать только как моментальный снимок экрана, если форма будет максимальной. ключевые точки:

  • Это связано с тем, что вы можете/будете отправлять байтовый массив в поток. Таким образом, я оставляю его как байтовый массив, а не создавая BMP только для (предположительно) его последующего преобразования.
  • Нет необходимости создавать файлы на диске для получения размера. Если массив содержит закодированные байты, он будет иметь тот же размер.
  • Я никогда не использовал параметр COMPRESSION, но я знаю, что качество инвертирует сжатие.

Итоговые размеры для различных факторов качества:

100 = 462
95 = 254
90 = 195
80 = 147

Строго говоря, вам не нужен encoder, bmpThumb.Save(ms, Imaging.ImageFormat.Jpeg) также будет работать, но один объект кодера предлагает более точную настройку.


Для отправки части, вы можете захотеть Stack, Queue или LinkedList хранить массив байтов. Это будет дополнительно изолировать получение изображений от их отправки. Коллекция будет своего рода списком ToDo/ToSend.

Тогда, если есть несколько получателей, я бы посмотрел, возможно, на SendFrame в качестве Task, возможно, отправив 2-3 за раз. Там может быть точка, в которой количество приемников мешает тому, как быстро вы можете захватить новые.

Смежные вопросы