рд╣реЗрдбрд▓реЗрд╕ рдХреНрд░реЛрдо рдореЗрдВ рдХрд╛рдо рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рд░рд┐рдХреЙрд░реНрдб рд╡реАрдбрд┐рдпреЛ рдпреВрдЖрдИ рдСрдЯреЛрдЯреЗрд╕реНрдЯреНрд╕

рд╕рднреА рдХреЛ рдирдорд╕реНрдХрд╛рд░!

рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ рдореИрдВ рдмрд╛рдд рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдХрд┐ рд╣реЗрдбрд▓реЗрд╕ рдХреНрд░реЛрдо рдореЗрдВ рдХрд╛рдо рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рд╡реАрдбрд┐рдпреЛ рдСрдЯреЛ-рдЯреЗрд╕реНрдЯ рдХреЛ рд░рд┐рдХреЙрд░реНрдб рдХрд░рдиреЗ рдХреА рд╕рдорд╕реНрдпрд╛ рдХреЛ рдХреИрд╕реЗ рд╣рд▓ рдХрд┐рдпрд╛ рдЧрдпрд╛ (рдЧреИрд░-рд╣реЗрдбрд▓реЗрд╕ рдореЛрдб рдореЗрдВ рд░рд┐рдХреЙрд░реНрдбрд┐рдВрдЧ рдХреЗ рд╕рд╛рде рдХреЛрдИ рд╕рдорд╕реНрдпрд╛ рдирд╣реАрдВ рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдП)ред рдХрдИ рдЕрд▓рдЧ-рдЕрд▓рдЧ рджреГрд╖реНрдЯрд┐рдХреЛрдгреЛрдВ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛, рдФрд░ рдХрдард┐рдирд╛рдЗрдпреЛрдВ рдФрд░ рдЙрдиреНрд╣реЗрдВ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рддрд░реАрдХреЗ рдмрддрд╛рдП рдЬрд╛рдПрдВрдЧреЗред

рд╕рдорд╕реНрдпрд╛ рдХрд╛ рдирд┐рд░реВрдкрдг

  1. рд╡рд┐рдВрдбреЛрдЬ рдХреЗ рддрд╣рдд рдЯреЗрд╕реНрдЯ рдЪрд▓рддреЗ рд╣реИрдВ
  2. рдЯреЗрд╕реНрдЯ рд╕реЗрд▓реЗрдирд┐рдпрдо рд╡реЗрдм рдбреНрд░рд╛рдЗрд╡рд░ + рд╣реЗрдбрд▓реЗрд╕ рдХреНрд░реЛрдо рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ
  3. рдЯреЗрд╕реНрдЯ рдХрдИ рдереНрд░реЗрдб рдореЗрдВ рдЪрд▓рддреЗ рд╣реИрдВ

рдЧрд┐рд░рд╛рдП рдЧрдП рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ рд╡реАрдбрд┐рдпреЛ рдХреЛ рдмрдЪрд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЬрдмрдХрд┐

  1. рд░рдирдЯрд╛рдЗрдо 10% рд╕реЗ рдЕрдзрд┐рдХ рдирд╣реАрдВ рдмрдврд╝рдирд╛ рдЪрд╛рд╣рд┐рдПред
  2. рд╡рд░реНрддрдорд╛рди рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдореЗрдВ рдХрдо рд╕реЗ рдХрдо рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЗ рд╕рд╛рде рдХрд░реЗрдВ

рдпрджрд┐ рдЖрдк рдПрдХ рд╕рдорд╛рдзрд╛рди рдореЗрдВ рд░реБрдЪрд┐ рд░рдЦрддреЗ рд╣реИрдВ, рддреЛ рдмрд┐рд▓реНрд▓реА рдореЗрдВ рдЖрдкрдХрд╛ рд╕реНрд╡рд╛рдЧрдд рд╣реИред

рднреЛрд▓рд╛ рджреГрд╖реНрдЯрд┐рдХреЛрдгред рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ


рд╣рдорд╛рд░реЗ рдкрд░реАрдХреНрд╖рдг рдврд╛рдВрдЪреЗ рдореЗрдВ рд╕реЗрд▓реЗрдирд┐рдпрдо рдкрд░ рдПрдХ рдирд┐рдореНрди-рд╕реНрддрд░реАрдп рдЖрд╡рд░рдг рд╣реИред рдЗрд╕рд▓рд┐рдП, рдкрд╣рд▓рд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдмрд╣реБрдд рд╣реА рд╕рд░рд▓ рдФрд░ рдмреЗрд╣рдж рднреЛрд▓рд╛ рдерд╛: рдХреЛрдб рдЙрди рд╕рднреА рд╕реНрдерд╛рдиреЛрдВ рдкрд░ рдЬреЛрдбрд╝рд╛ рдЧрдпрд╛ рдерд╛ рдЬреЛ рдкреГрд╖реНрда рдХреЛ рдмрджрд▓рддреЗ рд╣реИрдВ (рдХреНрд▓рд┐рдХ рдХрд░реЗрдВ, рд╕реЗрдЯ рдХрд░реЗрдВ рдЯреЗрдХреНрд╕реНрдЯрдмреЙрдХреНрд╕, рдиреЗрд╡рд┐рдЧреЗрдЯ рдХрд░реЗрдВ, рдЖрджрд┐), рдЬреЛ рд╕реЗрд▓реЗрдирд┐рдпрдо рд╡реЗрдм рдЪрд╛рд▓рдХ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдПрдХ рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ рдмрдЪрд╛рддрд╛ рд╣реИ

Driver.TakeScreenshot().SaveAsFile(screenshotPath);

рдЗрд╕ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХреЗ рд╕рд╛рде рдкрд░реАрдХреНрд╖рдг рдирд┐рд╖реНрдкрд╛рджрди рдХрд╛ рд╕рдордп рдХрд╛рдлреА рдмрдврд╝ рдЧрдпрд╛ рд╣реИред рдХрд╛рд░рдг: рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ рдХреЛ рдмрдЪрд╛рдиреЗ рдХрд╛ рдСрдкрд░реЗрд╢рди рдЬрд▓реНрджреА рд╕реЗ рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ - 0.5 рд╕реЗрдХрдВрдб рд╕реЗ рдХрдИ рд╕реЗрдХрдВрдб рддрдХред

рдПрдХ рдЕрд▓рдЧ рд╕реНрдЯреНрд░реАрдо рдореЗрдВ рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ


рдкреГрд╖реНрда рдХреЛ рдмрджрд▓рдиреЗ рд╡рд╛рд▓реЗ рд╕рднреА рд╕реНрдерд╛рдиреЛрдВ рдореЗрдВ рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ рдХреЛ рд╕рд╣реЗрдЬрдиреЗ рд╡рд╛рд▓реЗ рдХреЛрдб рдХреЗ рдмрдЬрд╛рдп (рдХреНрд▓рд┐рдХ рдХрд░реЗрдВ, рдЯреЗрдХреНрд╕реНрдЯ рдмреЙрдХреНрд╕ рд╕реЗрдЯ рдХрд░реЗрдВ, рдиреЗрд╡рд┐рдЧреЗрдЯ рдХрд░реЗрдВ), рдПрдХ рдХреЛрдб рдЬреЛрдбрд╝рд╛ рдЧрдпрд╛ рдерд╛ рдЬреЛ рд▓рдЧрд╛рддрд╛рд░ рдПрдХ рдЕрд▓рдЧ рд╕реНрдЯреНрд░реАрдо рдореЗрдВ рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ рдмрдЪрд╛рддрд╛ рд╣реИ

рдЫрд┐рдкрд╛ рд╣реБрдЖ рдкрд╛рда
...
var timeTillNextFrame = TimeSpan.Zero;
while (!_stopThread.WaitOne(timeTillNextFrame))
{
    var screenShotDriver = Driver as ITakesScreenshot;
    if (screenShotDriver == null)
    {
        continue;
    }

    var screenShot = screenShotDriver.GetScreenshot();
    ...
}


рдкрд░реАрдХреНрд╖рдг рдХреЗ рдирд┐рд╖реНрдкрд╛рджрди рдХрд╛ рд╕рдордп рдЕрднреА рднреА рдмрд╣реБрдд рд▓рдВрдмрд╛ рдерд╛ред рдореБрдЭреЗ рджреЗрд░реА рдХрд╛ рдХрд╛рд░рдг рд╕рдордЭ рдирд╣реАрдВ рдЖрдпрд╛ред рд╕рдмрд╕реЗ рдЕрдзрд┐рдХ рд╕рдВрднрд╛рд╡рдирд╛ рд╣реИ, рд╕реЗрд▓реЗрдирд┐рдпрдо рдХреБрдЫ рдХрд░рдиреЗ рд╕реЗ рдЗрдирдХрд╛рд░ рдХрд░рддрд╛ рд╣реИ, рдЬрдмрдХрд┐ рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ рдХреЛ рдмрдЪрд╛рдпрд╛ рдЬрд╛ рд░рд╣рд╛ рд╣реИред рд╢рд╛рдпрдж рд╕реЗрд▓реЗрдирд┐рдпрдо рдХрд╛ рдПрдХ рдФрд░ рдЙрджрд╛рд╣рд░рдг рдЙрд╕реА рд╕рддреНрд░ рдореЗрдВ рд╕рдорд╛рдкреНрдд рд╣реЛрдиреЗ рдореЗрдВ рдорджрдж рдХрд░рддрд╛ред

Puppeteer рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдПрдХ рдЕрд▓рдЧ рдзрд╛рдЧреЗ рдореЗрдВ рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ


рдпрд╣ рд╕реЗрд▓реЗрдирд┐рдпрдо рдХреЗ рджреЛ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдХреЛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд рджрд┐рд▓рдЪрд╕реНрдк рдирд╣реАрдВ рдерд╛, рдХреНрдпреЛрдВрдХрд┐ рдореИрдВ рд▓рдВрдмреЗ рд╕рдордп рд╕реЗ рдЕрднреНрдпрд╛рд╕ рдореЗрдВ рдХрдардкреБрддрд▓реА-рддреЗрдЬ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рдерд╛ - рдФрд░ рдпрд╣рд╛рдВ рдореБрдЭреЗ рдПрдХ рдЙрдкрдпреБрдХреНрдд рдХрд╛рд░рдг рдорд┐рд▓рд╛ред рд╕реЗрд▓реЗрдирд┐рдпрдо рдХреЗ рдХрд┐рдирд╛рд░реЗ рдкрд░, Puppeteer рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рдерд╛, рдЬреЛ рдмрд╕ рдХреНрд░реЛрдо рд╕реЗ рдЬреБрдбрд╝рд╛ рдерд╛, рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рд╕реЗрд▓реЗрдирд┐рдпрдо рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рдерд╛

рдЫрд┐рдкрд╛ рд╣реБрдЖ рдкрд╛рда
var options = new ConnectOptions()
{
    BrowserURL = $"http://127.0.0.1:{debugPort}"
};

_puppeteerBrowser = Puppeteer.ConnectAsync(options).GetAwaiter().GetResult();


рд╕реЗрд▓реЗрдирд┐рдпрдо рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдкрд░реАрдХреНрд╖рдг рдЕрдкрдиреЗ рддрд░реАрдХреЗ рд╕реЗ рдЪрд▓рд╛ рдЧрдпрд╛, рдФрд░ рдкреБрдкреНрддреЗрдпрд░ рдиреЗ рдПрдХ рдЕрд▓рдЧ рдзрд╛рдЧреЗ рдореЗрдВ рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ рд▓рд┐рдпрд╛

рдЫрд┐рдкрд╛ рд╣реБрдЖ рдкрд╛рда
...
var timeTillNextFrame = TimeSpan.Zero;
while (!_stopThread.WaitOne(timeTillNextFrame))
{
    var pages = _puppeteerBrowser.PagesAsync().GetAwaiter().GetResult();
    if (pages.Length <= 0)
    {
        continue;
    }
    
    var page = pages[0];
    
    page.SetViewportAsync(new ViewPortOptions
    {
        Width = screenWidth,
        Height = screenHeight
    }).GetAwaiter().GetResult();
    
    var screen = page.ScreenshotStreamAsync().GetAwaiter().GetResult();
    ...
}


рдЗрд╕ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдиреЗ рдЙрддреНрд╕рд╛рд╣рдЬрдирдХ рдкрд░рд┐рдгрд╛рдо рджрд┐рдП, рдирд┐рд╖реНрдкрд╛рджрди рд╕рдордп рдореЗрдВ 10% рдХреА рд╡реГрджреНрдзрд┐ рд╣реБрдИред

minuses

  1. Puppeteer рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ рдХреЛ рд╕рд╣реЗрдЬрдиреЗ рдХрд╛ рд╕рдордп рддрддреНрдХрд╛рд▓ рдирд╣реАрдВ рд╣реИ, рдХреБрдЫ рдлреНрд░реЗрдо рдЦреЛ рдЬрд╛рдПрдВрдЧреЗ, рдФрд░ рд╡реЗ рдкрд╛рд░реНрд╕ рдХреЗ рд▓рд┐рдП рдХреБрдЫ рджрд┐рд▓рдЪрд╕реНрдк рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВред
  2. рдпрджрд┐ рд╕реЗрд▓реЗрдирд┐рдпрдо рдЯреИрдм рд╕реНрд╡рд┐рдЪ рдХрд░рддрд╛ рд╣реИ, рддреЛ рдЖрдкрдХреЛ Puppeteer рдХреЛ рд╕реВрдЪрд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЕрдиреНрдпрдерд╛ рдпрд╣ рдКрдкрд░ рджрд┐рдП рдЧрдП рдХреЛрдб рдореЗрдВ рдкрд╣рд▓рд╛ рдЯреИрдм рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ рдХрд░реЗрдЧрд╛ (рд╢рд╛рдпрдж рд╕рдХреНрд░рд┐рдп рдЯреИрдм рдХреЛ рдЦреЛрдЬрдиреЗ рдХрд╛ рдПрдХ рддрд░реАрдХрд╛ рд╣реИ - рдЖрдкрдХреЛ рджреЗрдЦрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ)ред

рдкрд╣рд▓рд╛ рдорд╛рдЗрдирд╕ рдореЗрд░реЗ рд▓рд┐рдП рдЕрд╡рд░реБрджреНрдз рд╣реЛ рдЧрдпрд╛, рдЗрд╕рд▓рд┐рдП рд╣рдо рдЕрдЧрд▓реЗ рд╕рдорд╛рдзрд╛рди рдХреЗ рд▓рд┐рдП рдЖрдЧреЗ рдмрдврд╝рддреЗ рд╣реИрдВред

рд╕реНрдХреНрд░реАрдирдХрд╛рд╕реНрдЯ


Chrome рдореЗрдВ рдПрдХ рджрд┐рд▓рдЪрд╕реНрдк рд╡рд┐рд╢реЗрд╖рддрд╛ рд╣реИ - Page.startScreencast ред рд╡рд┐рд╡рд░рдг рдХреЗ рдЕрдиреБрд╕рд╛рд░ - рд╡рд╣ рд╕рд┐рд░реНрдл рд╡рд╣реА рдХрд░рддреА рд╣реИ рдЬрд┐рд╕рдХреА рдЬрд░реВрд░рдд рд╣реЛрддреА рд╣реИ - рд╡рд╣ рдмрджрд▓реЗ рд╣реБрдП рддрдЦреНрддреЗ рдХреЛ рдХрд╛рд╕реНрдЯ рдХрд░рддреА рд╣реИ рддрд╛рдХрд┐ рдЬреЛ рд▓реЛрдЧ рдЙрдиреНрд╣реЗрдВ рд░реЛрдХ рд╕рдХреЗрдВ рдФрд░ рдЙрдирдХреЗ рд╕рд╛рде рдХреБрдЫ рджрд┐рд▓рдЪрд╕реНрдк рдХрд░ рд╕рдХреЗрдВред

рд╕реЗрд▓реЗрдирд┐рдпрдо рдФрд░ рдХрдардкреБрддрд▓реА рджреЛрдиреЛрдВ рдореЗрдВ, рдЖрдк Page.startScreencast рд╢реБрд░реВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдЖрдк рдПрдХ рдпрд╛ рджреВрд╕рд░реЗ рдореЗрдВ рд╣реИрдВрдбрд▓рд░ рдирд╣реАрдВ рдЬреЛрдбрд╝ рд╕рдХрддреЗред рд╣реЙрдЯреЗрд▓рд░ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЖрд╡рд╛рдЬ рдЙрдард╛рдИ рдЧрдИ рд╣реИ - рд╣рдо рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд▓рд┐рдП рдЗрдВрддрдЬрд╛рд░ рдХрд░ рд░рд╣реЗ рд╣реИрдВред

рдореИрдВрдиреЗ ChromeDTTools рдкреБрд╕реНрддрдХрд╛рд▓рдп рдХреЗ рд╕рд╛рде рджреЛрд╕реНрдд рдмрдирд╛рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХреА ред рджреБрд░реНрднрд╛рдЧреНрдп рд╕реЗ, рдореИрдВ рдЙрд╕рдХреЗ рд╕рд╛рде рдЕрдЪреНрдЫреЗ рд╕рдВрдмрдВрдз рдирд╣реАрдВ рдмрдирд╛ рд╕рдХрд╛ред рдЖрдЧреЗ рдХреА рдЦреЛрдЬреЛрдВ рдХреЗ рдмрд╛рдж, рдорд╛рдлрд╝реНрд░реЗрдбреНрд░реА / рд╕реАрдбреАрдкреА рдореЗрдВ рд╕реНрдХреНрд░реАрдирдХреИрд╕реНрдЯ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рдорд╛рдзрд╛рди рдкрд╛рдпрд╛ рдЧрдпрд╛ рдерд╛ ред рдореВрд▓ рдЙрджрд╛рд╣рд░рдг рд╕реЗ, рдЕрдирд╛рд╡рд╢реНрдпрдХ рдиреЗрд╡рд┐рдЧреЗрд╢рди рдХреЛ рд╣рдЯрд╛ рджрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ рдФрд░ рдЖрд╡рд╢реНрдпрдХ рдЗрдирдкреБрдЯ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЛ рдЬреЛрдбрд╝рд╛ рдЧрдпрд╛ рдерд╛:

рдЫрд┐рдкрд╛ рд╣реБрдЖ рдкрд╛рда
package main

import (
    "os"
    "context"
    "fmt"
    "io/ioutil"
    "log"
    "time"
    "flag"

    "github.com/mafredri/cdp"
    "github.com/mafredri/cdp/devtool"
    "github.com/mafredri/cdp/protocol/page"
    "github.com/mafredri/cdp/rpcc"
)

func main() {

    folderPtr := flag.String("folder", "", "folder path for screenshots: example c:\\temp\\screens\\")
    chromePtr := flag.String("chrome", "http://localhost:9222", "chrome connection - example: http://localhost:9222")
    
    widthPtr := flag.Int("width", 1280, "screencast width")
    heightPtr := flag.Int("height", 720, "screencast height")
    qualityPtr := flag.Int("quality", 100, "screencast quality")
    
    flag.Parse()

    if err := run(*folderPtr, *chromePtr, *widthPtr, *heightPtr, *qualityPtr); err != nil {
        panic(err)
    }
}

func run(folder string, chromeConnection string, width int, height int, quality int) error {
    ctx, cancel := context.WithCancel(context.TODO())
    defer cancel()
    
    chromePath := chromeConnection
    folderPath := folder

    devt := devtool.New(chromePath)

    pageTarget, err := devt.Get(ctx, devtool.Page)
    if err != nil {
        return err
    }

    conn, err := rpcc.DialContext(ctx, pageTarget.WebSocketDebuggerURL)
    if err != nil {
        return err
    }
    defer conn.Close()

    c := cdp.NewClient(conn)

    err = c.Page.Enable(ctx)
    if err != nil {
        return err
    }

    // Start listening to ScreencastFrame events.
    screencastFrame, err := c.Page.ScreencastFrame(ctx)
    if err != nil {
        return err
    }

    go func() {
        defer screencastFrame.Close()

        for {
            ev, err := screencastFrame.Recv()
            if err != nil {
                log.Printf("Failed to receive ScreencastFrame: %v", err)
                os.Exit(0)
            }
            log.Printf("Got frame with sessionID: %d: %+v", ev.SessionID, ev.Metadata)

            err = c.Page.ScreencastFrameAck(ctx, page.NewScreencastFrameAckArgs(ev.SessionID))
            if err != nil {
                log.Printf("Failed to ack ScreencastFrame: %v", err)
                os.Exit(0)
            }

            // Write to screencast_frame-[timestamp].png.
            name := fmt.Sprintf("screencast_frame-%d.png", ev.Metadata.Timestamp.Time().Unix())
            
            filePath := folderPath + name

            // Write the frame to file (without blocking).
            go func() {
                err = ioutil.WriteFile(filePath, ev.Data, 0644)
                if err != nil {
                    log.Printf("Failed to write ScreencastFrame to %q: %v", name, err)
                }
            }()
        }
    }()

    screencastArgs := page.NewStartScreencastArgs().
        SetQuality(quality).
        SetMaxWidth(width).
        SetMaxHeight(height).
        SetEveryNthFrame(1).
        SetFormat("png")
    err = c.Page.StartScreencast(ctx, screencastArgs)
    if err != nil {
        return err
    }

    // Random delay for our screencast.
    time.Sleep(600 * time.Second)

    err = c.Page.StopScreencast(ctx)
    if err != nil {
        return err
    }

    return nil
}


рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдЗрд╕ рдлрд╛рдЗрд▓ рдХреЛ рдХрдорд╛рдВрдб рджреНрд╡рд╛рд░рд╛ рд╕рдВрдХрд▓рд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛:

go build -o screencast.exe main.go

рдФрд░ рдореИрдВ рдкрд░реАрдХреНрд╖рдг рдХреЗ рд╕рд╛рде C # рд╕рдорд╛рдзрд╛рди рдореЗрдВ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдерд╛:

рдЫрд┐рдкрд╛ рд╣реБрдЖ рдкрд╛рда
var startInfo = new ProcessStartInfo(screenCastPath)
{
    WindowStyle = ProcessWindowStyle.Minimized,

    Arguments = $"-folder={_framesFolderPath} " +
                $"-chrome=http://localhost:{_debugPort} " +
                "-width=1024 " +
                "-height=576 " +
                "-quality=0"
};

Process.Start(startInfo);


рд╕реНрдХреНрд░реАрдирд╢реЙрдЯреНрд╕ рд░рд┐рдХреЙрд░реНрдб рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЕрд▓рдЧ рд╕реНрдЯреНрд░реАрдо рдЕрдирд╛рд╡рд╢реНрдпрдХ рдХреЗ рд░реВрдк рдореЗрдВ рдмрд╛рд╣рд░ рдлреЗрдВрдХ рджрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред рдХрд╛рд░реНрдп рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдЗрд╕ рдкреНрд░рдХрд╛рд░ рдерд╛:

  1. рд╕реЗрд▓реЗрдирд┐рдпрдо рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдХреНрд░реЛрдо рд╢реБрд░реВ рдХрд░рдирд╛
  2. рд╣рдо рдЗрдХрдЯреНрдареЗ рдмрд╛рдЗрдирд░реА рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╕реНрдХреНрд░реЗрдиреНрдХрд╛рд╕реНрдЯ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВ - рдпрд╣ рдХреНрд░реЛрдо рд╕реЗ рдЬреБрдбрд╝рддрд╛ рд╣реИ рдФрд░ рд╣рдорд╛рд░реЗ рджреНрд╡рд╛рд░рд╛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдлрд╝реЛрд▓реНрдбрд░ рдореЗрдВ рдлрд╝реНрд░реЗрдо рдХреА рдзрд╛рд░рд╛ рдХреЛ рд╕рд╣реЗрдЬрдирд╛ рд╢реБрд░реВ рдХрд░рддрд╛ рд╣реИ
  3. рдкрд░реАрдХреНрд╖рдг рдХреЗ рдЕрдВрдд рдореЗрдВ, Chrome рдХреЛ рдмрдВрдж рдХрд░реЗрдВ - рдмрд╛рдЗрдирд░реА рдЕрдкрдиреЗ рдЖрдк рдмрдВрдж рд╣реЛ рдЬрд╛рддреА рд╣реИ
  4. рдпрджрд┐ рдкрд░реАрдХреНрд╖рдг рд╡рд┐рдлрд▓ рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ - рдПрдХ рд╡реАрдбрд┐рдпреЛ рдмрдирд╛рдПрдВ
  5. рд╣рдо рдлреНрд░реЗрдо рдХреЗ рд╕рд╛рде рдлрд╝реЛрд▓реНрдбрд░ рдХреЛ рд╕рд╛рдл рдХрд░рддреЗ рд╣реИрдВ

рдЗрд╕ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдиреЗ рдирд┐рд╖реНрдкрд╛рджрди рд╕рдордп рдХреЗ рд╕рдВрджрд░реНрдн рдореЗрдВ рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛ рдкрд░рд┐рдгрд╛рдо рджрд┐рдпрд╛ (рд╡реНрдпрд╛рд╡рд╣рд╛рд░рд┐рдХ рд░реВрдк рд╕реЗ рдХреЛрдИ рджреЗрд░реА рдирд╣реАрдВ рд╣реИ)ред рд╕рд╛рде рд╣реА, рдЙрдиреНрд╣реЛрдВрдиреЗ рдкрд░реАрдХреНрд╖рдг рдкрд░ рдЕрдзрд┐рдХрддрдо рдЬрд╛рдирдХрд╛рд░реА рдкреНрд░рджрд╛рди рдХреА (рд╡реНрдпрд╛рд╡рд╣рд╛рд░рд┐рдХ рд░реВрдк рд╕реЗ рдХреЛрдИ рдЦреЛрдП рд╣реБрдП рдлреНрд░реЗрдо рдирд╣реАрдВ рд╣реИрдВ)ред

рд╡рд┐рдкрдХреНрд╖

1. рд╕реНрдХреНрд░реЗрдВрдХрд╛рд╕реНрдЯ рдХреЗ рд▓рд┐рдП рдХрдо рд░рд┐рдЬрд╝реЙрд▓реНрдпреВрд╢рдиред рдпрджрд┐ рдЖрдк рдХреБрдЫ рдзрд╛рд░рд╛рдУрдВ рдореЗрдВ рдкрд░реАрдХреНрд╖рдг рдЪрд▓рд╛рддреЗ рд╣реИрдВ рдФрд░ рдХреНрд░реЛрдо рдХреЗ рд▓рд┐рдП 2560 * 1440 рдкрд░ рд░рд┐рдЬрд╝реЙрд▓реНрдпреВрд╢рди рд╕реЗрдЯ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдбреЗрдЯрд╛ рдЯреНрд░рд╛рдВрд╕рдлрд░ рдХреЗ рд▓рд┐рдП рдЖрд╡рдВрдЯрд┐рдд рдмрдлрд░ рдУрд╡рд░рдлреНрд▓реЛ рд╣реЛ рдЬрд╛рдПрдЧрд╛ ред

2. рдмрдврд╝рддреЗ рд░рд┐рдЬрд╝реЙрд▓реНрдпреВрд╢рди рдХреЗ рд╕рд╛рде, рд╕реАрдкреАрдпреВ рдкрд░ рд▓реЛрдб рдмрдврд╝рддрд╛ рд╣реИред

рдирддреАрдЬрддрди, рд╕реНрдХреНрд░реЗрдВрдХрд╛рд╕реНрдЯ рдХреЗ рддрд╣рдд, рдореИрдВрдиреЗ 1024 * 576 рдХрд╛ рд╕рдВрдХрд▓реНрдк рдЪреБрдирд╛ - рдЗрд╕ рд╕рдВрдХрд▓реНрдк рдкрд░ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдиреЗ 6 рдереНрд░реЗрдбреНрд╕ рдореЗрдВ рдареАрдХ рдХрд╛рдо рдХрд┐рдпрд╛, рдкреНрд░реЛрд╕реЗрд╕рд░ рдиреЗ рдЖрд░рд╛рдорджрд╛рдпрдХ рдореЛрдб (6 рдХреЛрд░ i7-5820) рдореЗрдВ рдХрд╛рдо рдХрд┐рдпрд╛ред

рд╣рдо рд╡реАрдбрд┐рдпреЛ рдПрдХрддреНрд░ рдХрд░рддреЗ рд╣реИрдВ


рдпрд╣ рд╡реАрдбрд┐рдпреЛ рдореЗрдВ рдлреНрд░реЗрдо рдЗрдХрдЯреНрдард╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрдирд╛ рд╣реБрдЖ рд╣реИред рдЗрд╕рдХреЗ рд▓рд┐рдП рдореИрдВрдиреЗ SharpAvi рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдпрд╛

рдЫрд┐рдкрд╛ рд╣реБрдЖ рдкрд╛рда
private void GenerateVideoFromScreens(string videoPath)
{
    try
    {
        var videoWriter = new AviWriter(videoPath) { FramesPerSecond = 1, EmitIndex1 = true };

        var videoStream = videoWriter.AddMotionJpegVideoStream(1024, 576);

        var screens = new DirectoryInfo(_framesFolderPath).GetFiles().OrderBy(f => f.CreationTimeUtc.Ticks).ToList();
        foreach (var screen in screens)
        {
            try
            {
                using (var bmp = new Bitmap(screen.FullName))
                {
                    var bits = bmp.LockBits(new Rectangle(0, 0, videoStream.Width, videoStream.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
                    var videoFrame = new byte[videoStream.Width * videoStream.Height * 4];
                    Marshal.Copy(bits.Scan0, videoFrame, 0, videoFrame.Length);
                    bmp.UnlockBits(bits);

                    videoStream.WriteFrameAsync(
                        true,
                        videoFrame,
                        0,
                        videoFrame.Length).GetAwaiter().GetResult();
                }
            }
            catch(Exception ex)
            {
                // ignore all video related errors per frame
            }
        }

        videoWriter.Close();
    }
    catch
    {
        // ignore all video related errors per streams
    }
}


Upscale рдЪрд┐рддреНрд░


рдЪреВрдВрдХрд┐ рд╕реНрдХреНрд░реЗрдВрдХрд╛рд╕реНрдЯ рдХрд╛ рд░рд┐рдЬрд╝реЙрд▓реНрдпреВрд╢рди 1024 * 576 рдмрд╣реБрдд рдЫреЛрдЯрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЖрдкрдХреЛ рдХреНрд░реЛрдо рдХреЗ рд▓рд┐рдП рдПрдХ рдЫреЛрдЯрд╛ рд╕рд╛ рд░рд┐рдЬрд╝реЙрд▓реНрдпреВрд╢рди рд╕реЗрдЯ рдХрд░рдирд╛ рд╣реЛрдЧрд╛, рдЕрдиреНрдпрдерд╛ рдЫреЛрдЯреЗ рдЯреЗрдХреНрд╕реНрдЯ рдореЗрдВ рд╕рдорд╕реНрдпрд╛рдПрдБ рд╣реЛрдВрдЧреАред

Chrome реирелремреж * резрекрекреж -> резрежреирек рдкрд░ рдкреЗрдВрдЪрдХрд╛рд╕реНрдЯ * рел smallрем = рдЫреЛрдЯрд╛ рдкрд╛рда рд╡реНрдпрд╛рд╡рд╣рд╛рд░рд┐рдХ рд░реВрдк рд╕реЗ рдЕрдкрдардиреАрдп рд╣реИ
рдХреНрд░реЛрдо рез реп реиреж * резрежencреж -> режреиреж * рел =рем рдореЗрдВ рдкреЗрдВрдЪрдХрд╛рд╕реНрдЯ = рдЫреЛрдЯрд╛ рдкрд╛рда рдХрдард┐рдирд╛рдИ рдХреЗ рд╕рд╛рде рдкрдврд╝рд╛ рдЬрд╛рддрд╛ рд╣реИ
рдХреНрд░реЛрдо резрекреж * * - реп реи -> рез.реирежрел * рел76рем = рдЫреЛрдЯреЗ рдкрд╛рда рдХреЛ рдмрд┐рдирд╛ рдХрд┐рд╕реА рд╕рдорд╕реНрдпрд╛ рдХреЗ рдкрдврд╝рд╛ рдЬрд╛рддрд╛ рд╣реИред

рдкрд░рд┐рдгрд╛рдореА рд╡реАрдбрд┐рдпреЛ 1024 * 576 рдореЗрдВ рд╕реБрдзрд╛рд░ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ - рдЕрдЧрд░ рдлреЛрдЯреЛрд╕реНрдХреНрдпреВрд╕ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдлрд╝реНрд░реЗрдо рдХреЛ 1920 * 1080 рддрдХ рдШрдЯрд╛рдпрд╛ рдЬрд╛рдП

рдЫрд┐рдкрд╛ рд╣реБрдЖ рдкрд╛рда
public Bitmap ResizeImage(Bitmap bitmap, int width)
{
	using (var inStream = new MemoryStream())
	{
		bitmap.Save(inStream, ImageFormat.Png);
		inStream.Position = 0;
		using (MemoryStream outStream = new MemoryStream())
		{
			var settings = new ProcessImageSettings { Width = width };
			MagicImageProcessor.ProcessImage(inStream, outStream, settings);
			return new Bitmap(outStream);
		}
	}
}


рдирддреАрдЬрддрди, рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдкреИрд░рд╛рдореАрдЯрд░ рдкреНрд░рд╛рдкреНрдд рдХрд┐рдП рдЧрдП: рдХреНрд░реЛрдо 1408 * 792 рдореЗрдВ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ, 1024 * 576 рдореЗрдВ рд╕реНрдХреНрд░реАрдирдХреИрд╕реНрдЯ, 1920 * 1080 рдХреЗ рд▓рд┐рдП upscales рджреЗрдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдВрддрд┐рдо рд╡реАрдбрд┐рдпреЛред рдЕрдВрддрд┐рдо рдкрд░рд┐рдгрд╛рдо рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рджреЗрдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдпрд╣рд╛рдВ рдХреНрд▓рд┐рдХ рдХрд░реЗрдВ ред

рдзрдиреНрдпрд╡рд╛рдж


рд╕рднреА рдХреЛ рдзрдиреНрдпрд╡рд╛рдж рдЬрд┐рдиреНрд╣реЛрдВрдиреЗ рдкрдврд╝рд╛ рд╣реИ - рдпрджрд┐ рдореВрд▓ рд╕рдорд╕реНрдпрд╛ рдХрд╛ рд╕рд░рд▓ рд╕рдорд╛рдзрд╛рди рд╣реИ, рддреЛ рдХреГрдкрдпрд╛ рдЯрд┐рдкреНрдкрдгреА рдореЗрдВ рд▓рд┐рдЦреЗрдВред рдХрд┐рд╕реА рднреА рдЖрд▓реЛрдЪрдирд╛ рдХреЛ рднреА рд╕реНрд╡реАрдХрд╛рд░ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬрд┐рд╕рдореЗрдВ рдКрдкрд░ рд╡рд░реНрдгрд┐рдд рджреБрд░реНрднрд╛рд╡рдирд╛рдкреВрд░реНрдг рдЖрд▓реЛрдЪрдирд╛ рднреА рд╢рд╛рдорд┐рд▓ рд╣реИред

рд╕рднреА рд╕реНрд╡рд╛рд╕реНрдереНрдп рдФрд░ рддреЗрдЬреА рд╕реЗ рдкреНрд░рддрд┐рд╖реНрдард┐рдд рдкреНрд░рддрд┐рдмрдВрдзреЛрдВ рдХрд╛ рдЕрдВрдд!

All Articles