Search code examples
rubyseleniumselenium-webdrivervideo-streaminglive-streaming

Live stream video of Selenium scripts/bots


I would like to stream video footage of running Selenium scripts/bots. I'm using capybara with selenium-webdriver and I know I can use headless to capture video.

What I don't know how to do is stream the video such that I can access it at some URL and not consume disk space. Has anyone done this?

Here's a test script I use to generate video files:

require 'capybara/dsl'
require 'selenium-webdriver'
require 'headless'

Capybara.configure do |c|
  c.javascript_driver = :selenium
  c.default_driver = :selenium
end

Capybara.register_driver :selenium do |app|
  profile = Selenium::WebDriver::Chrome::Profile.new
  profile['general.useragent.override'] = Bot::USER_AGENT
  Capybara::Selenium::Driver.new(app, { 
    browser: :chrome,
    profile: profile,
    args: ['--window-size=1024,768'] 
  })
end

class Bot
  include Capybara::DSL

  USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36'

  def run
    headless = Headless.new
    headless.start
    headless.video.start_capture

    loop do
      visit('http://google.com')
      sleep 1
      visit('http://youtube.com')
      sleep 1
      headless.video.stop_and_save("video-#{Time.now.to_i}.mov")
    end
  end
end

bot = Bot.new
bot.run

Solution

  • I got it working. Here's the updated Ruby script:

    require 'capybara/dsl'
    require 'selenium-webdriver'
    require 'headless'
    
    Capybara.configure do |c|
      c.javascript_driver = :selenium
      c.default_driver = :selenium
    end
    
    Capybara.register_driver :selenium do |app|
      profile = Selenium::WebDriver::Chrome::Profile.new
      profile['general.useragent.override'] = Bot::USER_AGENT
      Capybara::Selenium::Driver.new(app, {
        browser: :chrome,
        profile: profile,
        args: ["--window-size=1024,768"]
      })
    end
    
    class Bot
      include Capybara::DSL
    
      USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36'
      FILE_PATH = File.join(__dir__, 'segment%05d.ts')
    
      def run
        headless = Headless.new(video: {
          codec: 'libx264',
          frame_rate: 25,
          tmp_file_path: FILE_PATH,
          extra: [
            '-pix_fmt yuv420p',
            '-profile:v baseline',
            '-b:v 1500k',
            '-x264opts keyint=25',
            '-s 640x360',
            '-map 0',
            '-flags',
            '-global_header',
            '-f segment',
            '-segment_list index_1500.m3u8',
            '-segment_time 1',
            '-segment_format mpeg_ts',
            '-segment_list_type m3u8',
            '-segment_list_flags +live',
            '-segment_list_size 2',
          ],
        })
        headless.start
        headless.video.start_capture
    
        loop do
          print 'loading google... '
          visit('http://google.com')
          puts 'done'
          print 'loading youtube... '
          visit('http://youtube.com')
          puts 'done'
        end
      end
    end
    
    bot = Bot.new
    bot.run
    

    Internally, headless will build this command:

    /usr/bin/avconv -y -r 25 -s 1280x1024 -f x11grab -i :99 -g 600 -vcodec libx264 -pix_fmt yuv420p -profile:v baseline -b:v 1500k -x264opts keyint=25 -s 640x360
     -map 0 -flags -global_header -f segment -segment_list index_1500.m3u8 -segment_time 1 -segment_format mpeg_ts -segment_list_type m3u8 -segment_list_flags +li
    ve -segment_list_size 2 /home/mhluska/record-test/segment%05d.ts
    

    I'm not well versed in ffmpeg so I can't explain what half of these parameters do. The end result is it will produce segment*.ts files and index_1500.m3u8 which seems to be a manifest.

    We can play it in the browser now. Create index.html:

    <!doctype html>
    <html>
      <head>
        <title>Video stream test</title>
      </head>
      <body>
        <video src="index_1500.m3u8" autoplay controls type="application/x-mpegURL"></video>
      </body>
    </html>
    

    And serve it temporarily with sudo python -m SimpleHTTPServer 80.

    Notes/caveats:

    • I had to first set up a swap file on my VPS otherwise the script would get killed
    • The video tag seems to only play on Safari and not Chrome
    • The video ratio/size seems wrong but this can be corrected by tweaking the ffmpeg parameters