запись рабочего стола в видео файл (библиотека FFmpeg)

Язык Visual Basic на платформе .NET.

Модераторы: Ramzes, Sebas

COSANOSTRA
Начинающий
Начинающий
 
Сообщения: 17
Зарегистрирован: 02.10.2014 (Чт) 20:02

запись рабочего стола в видео файл (библиотека FFmpeg)

Сообщение COSANOSTRA » 13.05.2015 (Ср) 15:52

На просторах интернета нашел неплохой пример записи рабочего стола в видео файл, на основе библиотеки FFmpeg http://ffmpeg.org/

задача – как исправить скорость воспроизведения записи, выходной файл воспроизводится почти в 2 раза быстрее.

Form1
Код: Выделить всё
Imports System.IO
Imports System.Runtime.InteropServices

Public Class Form1
    <DllImport("user32.dll", CharSet:=CharSet.Auto, ExactSpelling:=True)>
    Private Shared Function GetAsyncKeyState(ByVal vkey As Integer) As Short
    End Function

    Private fcounter As Integer = 0
    Private recording As Boolean
    Private proc As New Process
    Private writer As BinaryWriter

    Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        recording = False
        Try
            writer.Close()
        Catch ex As Exception
        End Try
        If bmp IsNot Nothing Then
            bmp.Dispose()
            bmp = Nothing
        End If
    End Sub

    Private Sub Record_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Record.Click
        ' mini me on record!
        'Me.WindowState = FormWindowState.Minimized
        If bmp IsNot Nothing Then
            bmp.Dispose()
            bmp = Nothing
        End If
        bmp = New Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height) ', System.Drawing.Imaging.PixelFormat.Format24bppRgb)
        Label7.Text = "Recording"
        Application.DoEvents()
        Try
            proc.StartInfo.FileName = Application.StartupPath & "\ffmpeg.exe"
            ' note: ' -an = no audio
            proc.StartInfo.Arguments = "-f image2pipe -i pipe:.bmp -pix_fmt yuv420p -c:v libx264  -acodec flac -bufsize 60000k -b:v 1800k  -y " & Environment.GetFolderPath(Environment.SpecialFolder.Desktop) & "\test.mp4"
            proc.StartInfo.UseShellExecute = False
            proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
            proc.StartInfo.RedirectStandardInput = True
            proc.StartInfo.RedirectStandardOutput = True
            proc.StartInfo.CreateNoWindow = True
            proc.Start()
            ' start capture using BGW
            recording = True
            BackgroundWorker1.RunWorkerAsync()
        Catch ex As Exception
            recording = False
            Label7.Text = "ERROR"
            MessageBox.Show(ex.Message)
            Try
                writer.Close()
            Catch
            End Try
        End Try
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        writer = New BinaryWriter(proc.StandardInput.BaseStream)
        Do
            If GetAsyncKeyState(Keys.LButton) >= 0 Then ' mousedown = False
                bmp = ScreenCap(True, False)
            ElseIf GetAsyncKeyState(Keys.LButton) < 0 Then ' mousedown = True
                bmp = ScreenCap(True, True)
            End If
            ' save capture
            bmp.Save(writer.BaseStream, System.Drawing.Imaging.ImageFormat.Bmp)
            fcounter = fcounter + 1
            ' if ESC key pressed exit
            If GetAsyncKeyState(Keys.Escape) < 0 Then
                recording = False
                Exit Do
            End If
        Loop While recording
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        ' recording stopped
        writer.Close()
        LblFrames.Text = fcounter.ToString("000000")
        Label7.Text = "STOPPED"
        Application.DoEvents()
        If bmp IsNot Nothing Then
            bmp.Dispose()
            bmp = Nothing
        End If
        Application.DoEvents()
        Me.WindowState = FormWindowState.Normal
    End Sub

    Private Sub StopREC_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles StopREC.Click
        recording = False 'stop!
        Label7.Text = "STOPPED"
    End Sub

End Class]


ScreenCapture
Код: Выделить всё
Imports System.Runtime.InteropServices

Module ScreenCapture

    Public bmp As Bitmap
    <StructLayout(LayoutKind.Sequential)> _
    Private Structure POINTAPI
        Public x As Int32
        Public y As Int32
    End Structure
    <StructLayout(LayoutKind.Sequential)> _
    Private Structure CURSORINFO
        Public cbSize As Int32
        Public flags As Int32
        Public hCursor As IntPtr
        Public ptScreenPos As POINTAPI
    End Structure
    <StructLayout(LayoutKind.Sequential)> _
    Private Structure ICONINFO
        Public fIcon As Boolean
        Public xHotspot As Int32
        Public yHotspot As Int32
        Public hbmMask As IntPtr
        Public hbmColor As IntPtr
    End Structure
    <DllImport("user32.dll", EntryPoint:="GetCursorInfo")> _
    Private Function GetCursorInfo(ByRef pci As CURSORINFO) As Boolean
    End Function
    <DllImport("user32.dll")> _
    Private Function DrawIcon(hDC As IntPtr, X As Int32, Y As Int32, hIcon As IntPtr) As Boolean
    End Function
    <DllImport("user32.dll", EntryPoint:="GetIconInfo")> _
    Private Function GetIconInfo(hIcon As IntPtr, ByRef piconinfo As ICONINFO) As Boolean
    End Function
    Private Const vbSRCCOPY As Int32 = &HCC0020
    Private Const CAPTUREBLT As Int32 = &H40000000 ' capture layered windows
    <DllImport("gdi32.dll")> _
    Private Function BitBlt(ByVal hdc As IntPtr, ByVal nXDest As Int32, ByVal nYDest As Int32, ByVal nWidth As Int32, ByVal nHeight As Int32, ByVal hdcSrc As IntPtr, ByVal nXSrc As Int32, ByVal nYSrc As Int32, ByVal dwRop As Int32) As Boolean
    End Function
    <DllImport("gdi32.dll")> _
    Private Function DeleteObject(hObject As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function

    Public Function ScreenCap(IncludeMouse As Boolean, Optional DrawEllipse As Boolean = False) As Bitmap
        '*** NOTE: "bmp" is already declared as Public for screen capture app, so ignore next line! ***
        'Dim bmp As New Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)', System.Drawing.Imaging.PixelFormat.Format24bppRgb)
        Using gdest As Graphics = Graphics.FromImage(bmp)
            Dim hDestDC As IntPtr = gdest.GetHdc()
            Using gsrc As Graphics = Graphics.FromHwnd(IntPtr.Zero)
                Dim hSrcDC As IntPtr = gsrc.GetHdc()
                BitBlt(hDestDC, 0, 0, bmp.Width, bmp.Height, hSrcDC, 0, 0, vbSRCCOPY Or CAPTUREBLT)
                If IncludeMouse Then
                    Dim pcin As New CURSORINFO()
                    pcin.cbSize = Marshal.SizeOf(pcin)
                    If GetCursorInfo(pcin) Then
                        Dim piinfo As ICONINFO
                        If GetIconInfo(pcin.hCursor, piinfo) Then
                            ' updated for screen capture app >
                            If DrawEllipse Then ' mouse was left clicked
                                ' Create new graphics object using handle of device context.
                                Using newGraphics As Graphics = Graphics.FromHdc(hDestDC)
                                    'draw red circle around center of mouse pointer
                                    newGraphics.DrawEllipse(New Pen(Color.Red, 2), pcin.ptScreenPos.x - 14, pcin.ptScreenPos.y - 14, 28, 28)
                                End Using ' Release handle to device context.
                            End If
                            ' draw mouse pointer
                            DrawIcon(hDestDC, pcin.ptScreenPos.x - piinfo.xHotspot, pcin.ptScreenPos.y - piinfo.yHotspot, pcin.hCursor)
                            ' clean up
                            If Not piinfo.hbmMask.Equals(IntPtr.Zero) Then DeleteObject(piinfo.hbmMask)
                            If Not piinfo.hbmColor.Equals(IntPtr.Zero) Then DeleteObject(piinfo.hbmColor)
                        End If
                    End If
                End If
                gsrc.ReleaseHdc()
            End Using 'gsrc.Dispose()
            gdest.ReleaseHdc()
        End Using 'gdest.Dispose()
        Return bmp
    End Function

End Module


пользуясь русскоязычной справкой http://help.ubuntu.ru/wiki/ffmpeg пробовал подставлять разные значения командной строки FFmpag, не выходит
Вложения
Video Capture.rar
(73.03 Кб) Скачиваний: 402

FireFenix
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1640
Зарегистрирован: 25.05.2007 (Пт) 10:24
Откуда: Mugen no Sora

Re: запись рабочего стола в видео файл (библиотека FFmpeg)

Сообщение FireFenix » 14.05.2015 (Чт) 2:45

Вообще странная постановка вопроса и решение задачи.

Я бы смотрел в сторону OBS, проще написать плагин или стырить сырцы.

COSANOSTRA писал(а):задача – как исправить скорость воспроизведения записи, выходной файл воспроизводится почти в 2 раза быстрее.

У тебя фреймрейт не лимитирован. Количество записанных bmp в стрим ничем не лимитировано.
Если предположить, что по умолчанию 24fps видео поток кодируется, а в фоне поток выдаёт 60кадров в секунду, то вот и разница.
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる

COSANOSTRA
Начинающий
Начинающий
 
Сообщения: 17
Зарегистрирован: 02.10.2014 (Чт) 20:02

Re: запись рабочего стола в видео файл (библиотека FFmpeg)

Сообщение COSANOSTRA » 14.05.2015 (Чт) 11:51

FireFenix писал(а):У тебя фреймрейт не лимитирован. Количество записанных bmp в стрим ничем не лимитировано.
Если предположить, что по умолчанию 24fps видео поток кодируется, а в фоне поток выдаёт 60кадров в секунду, то вот и разница


Попробовал собрать все это безобразие на API таймере timeSetEvent. При интервале таймера 1 мс (якобы) и fps на выходе 7.4 добился нужной скорости видео, но это покадровка. Что бы получить на выходе 24 кадра и нормальную скорость воспроизведения мне нужно выдавать в два раза больше кадров bmp, что при данном исполнении невозможно, я правильно понял? :?

FireFenix
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1640
Зарегистрирован: 25.05.2007 (Пт) 10:24
Откуда: Mugen no Sora

Re: запись рабочего стола в видео файл (библиотека FFmpeg)

Сообщение FireFenix » 15.05.2015 (Пт) 1:41

COSANOSTRA писал(а):Попробовал собрать все это безобразие на API таймере timeSetEvent. При интервале таймера 1 мс (якобы)

у таймера минимальный интервал это 50мс

COSANOSTRA писал(а):fps на выходе 7.4 добился нужной скорости видео

чего?

COSANOSTRA писал(а):что при данном исполнении невозможно,

COSANOSTRA писал(а):выходной файл воспроизводится почти в 2 раза быстрее

если у тебя файл воспроихводился в 2 раза быстрее, а стал в 2 раза медленнее это значит, что ты что-то делаешь не так.

твоя задача разделена на 2 фронта:
1) снятие буфера экрана и передача в пайп
2) запуск пережатия секвенции картинок в видео

Т.е.
1) ты должен вначале определится, можешь ли ты выдавать скажем 24 бмп в сек в пайп или нет. Т.е. как минимум успевает ли этот алгоритм на твоей машине или нет. Для теста можно выводить на форму и считать сколько за секунду пролезло. Если всё ок, то можно просто в цикле резать по StopWatch'у в потоке, без всяких таймеров.
2) Если пункт 1 допускает скажем 24 кадра в секунду, то значит проблема в ффмпег, он не так понимает, что ты хочешь ему сообщить. В примере выше ты не юзаешь ключ -r, попробуй с ним. Вообще лучше вначале просто получить на диске набор картинок порезанных с заданным фпс и попытаться собрать через ffmpeg в требуемое видео, а уже потом работать программно с пайпом.

Вообще нафиг ты полез в винапи? в дотнете хватает своих средств, скажем для принтскрина достаточно Graphics.CopyFromScreen, а для ffmpeg есть .net обёртки.
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる


Вернуться в Visual Basic .NET

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 44

    TopList  
cron