Coder Social home page Coder Social logo

spice-record's Introduction

This is a simple utility for recording a SPICE sesion to MP4 video. It uses libvirt to connect to the VMs, SpiceClientGLib to access the graphics device, and FFmpeg to encode MP4 videos.

Usage

usage: spice-record [-h] [--vcodec VCODEC]
                    [--loglevel {DEBUG,INFO,WARNING,ERROR,CRITICAL}]
                    [-r FRAMERATE] [-c LIBVIRT_URI] [-o FILENAME]
                    DOMAIN-NAME|ID|UUID

positional arguments:
  DOMAIN-NAME|ID|UUID   Machine to record

optional arguments:
  -h, --help            show this help message and exit
  --vcodec VCODEC       Set the output video codec (see "ffmpeg -encoders" for
                        choices)
  --loglevel {DEBUG,INFO,WARNING,ERROR,CRITICAL}
                        Set the logging level (default=WARNING)
  -r FRAMERATE, --framerate FRAMERATE
  -c LIBVIRT_URI, --connect LIBVIRT_URI
                        Connect to hypervisor (e.g. qemu:///system)
  -o FILENAME, --output FILENAME
                        Output filename (defaults to <domain-name>.mp4)

Requirements

  • Python 3
  • libvirt-python (not libvirt-glib)
  • spice-glib
  • pygobject3
  • ffmpeg

If virt-manager is installed on a modern distro (which has ported all of its Python apps to Python 3), then everything should already be installed, aside from ffmpeg.

Notes

Currently, the spice server only supports a single client connection. When another connection is opened, the current one is disconnected. Thus, this utility is limited in its usability as it cannot record a user interacting with the VM, and only an automatic ongoing process. There is however, an experimental feature to enable multiple concurrent connections to a single spice server.

spice-record's People

Contributors

jonathonreinhart avatar pierre-baudry avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

spice-record's Issues

Improve large tempfile size requirements

The current scheme copies the framebuffer in 32-bit bgr0 format (4 bytes per pixel) to a temporary file for each frame.

At a resolution of 1440x900 and framerate of 24 fps, the temporary file grows at (1440 px * 900 px * 4 bytes/px * 24 frames/sec = 124416000 bytes / sec) = 119 MB / sec. This means a 60-second video would require about 7 GB of raw storage.

Is there anything we can do about this?

  • Until the recording is finished, we don't know what the final resolution will be, because it could grow at any moment.
  • Because of this, and concern that the pipe could block, we can't stream to ffmpeg.

not working on archlinux

looks like it doesn't work on archlinux, is there something missing? thanks

[huzhifeng@archlinux ~]$ neofetch
                   -`                    huzhifeng@archlinux
                  .o+`                   -------------------
                 `ooo/                   OS: Arch Linux x86_64
                `+oooo:                  Kernel: 5.10.6-arch1-1
               `+oooooo:                 Uptime: 1 day, 12 hours, 33 mins
               -+oooooo+:                Packages: 868 (pacman)
             `/:-:++oooo+:               Shell: bash 5.1.4
            `/++++/+++++++:              Resolution: 1920x1080
           `/++++++++++++++:             Terminal: /dev/pts/1
          `/+++ooooooooooooo/`           CPU: Intel i7-8700 (12) @ 4.600GHz
         ./ooosssso++osssssso+`          GPU: Intel UHD Graphics 630
        .oossssso-````/ossssss+`         Memory: 1252MiB / 15678MiB
       -osssssso.      :ssssssso.
      :osssssss/        osssso+++.
     /ossssssss/        +ssssooo/-
   `/ossssso+/:-        -:/+osssso+-
  `+sso+:-`                 `.-/+oso:
 `++:.                           `-/+/
 .`                                 `/

[huzhifeng@archlinux ~]$
[huzhifeng@archlinux ~]$ git clone https://github.com/JonathonReinhart/spice-record
[huzhifeng@archlinux ~]$ cd spice-record/
[huzhifeng@archlinux spice-record]$ sudo python setup.py install
[huzhifeng@archlinux spice-record]$ spice-record --connect qemu+ssh://[email protected]/system ubuntu18.04 --loglevel DEBUG
INFO:root:Opening connection to qemu+ssh://[email protected]/system
INFO:root:Using domain "ubuntu18.04" (4619a3cc-839f-49d9-ab84-102b41978746)
<domain type="kvm" id="4">
  <name>ubuntu18.04</name>
  <uuid>4619a3cc-839f-49d9-ab84-102b41978746</uuid>
  <memory unit="KiB">2097152</memory>
  <currentMemory unit="KiB">2097152</currentMemory>
  <vcpu placement="static">2</vcpu>
  <resource>
    <partition>/machine</partition>
  </resource>
  <os>
    <type arch="x86_64" machine="pc-i440fx-rhel7.0.0">hvm</type>
  </os>
  <features>
    <acpi />
    <apic />
  </features>
  <cpu mode="custom" match="exact" check="full">
    <model fallback="forbid">Skylake-Client-IBRS</model>
    <feature policy="require" name="hypervisor" />
    <feature policy="disable" name="arat" />
  </cpu>
  <clock offset="utc">
    <timer name="rtc" tickpolicy="catchup" />
    <timer name="pit" tickpolicy="delay" />
    <timer name="hpet" present="no" />
  </clock>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>destroy</on_crash>
  <pm>
    <suspend-to-mem enabled="no" />
    <suspend-to-disk enabled="no" />
  </pm>
  <devices>
    <emulator>/usr/libexec/qemu-kvm</emulator>
    <disk type="file" device="disk">
      <driver name="qemu" type="qcow2" />
      <source file="/data/virtm/ubuntu18.04.qcow2" />
      <backingStore />
      <target dev="vda" bus="virtio" />
      <boot order="1" />
      <alias name="virtio-disk0" />
      <address type="pci" domain="0x0000" bus="0x00" slot="0x07" function="0x0" />
    </disk>
    <disk type="file" device="cdrom">
      <driver name="qemu" />
      <target dev="hda" bus="ide" />
      <readonly />
      <alias name="ide0-0-0" />
      <address type="drive" controller="0" bus="0" target="0" unit="0" />
    </disk>
    <controller type="usb" index="0" model="ich9-ehci1">
      <alias name="usb" />
      <address type="pci" domain="0x0000" bus="0x00" slot="0x05" function="0x7" />
    </controller>
    <controller type="usb" index="0" model="ich9-uhci1">
      <alias name="usb" />
      <master startport="0" />
      <address type="pci" domain="0x0000" bus="0x00" slot="0x05" function="0x0" multifunction="on" />
    </controller>
    <controller type="usb" index="0" model="ich9-uhci2">
      <alias name="usb" />
      <master startport="2" />
      <address type="pci" domain="0x0000" bus="0x00" slot="0x05" function="0x1" />
    </controller>
    <controller type="usb" index="0" model="ich9-uhci3">
      <alias name="usb" />
      <master startport="4" />
      <address type="pci" domain="0x0000" bus="0x00" slot="0x05" function="0x2" />
    </controller>
    <controller type="pci" index="0" model="pci-root">
      <alias name="pci.0" />
    </controller>
    <controller type="ide" index="0">
      <alias name="ide" />
      <address type="pci" domain="0x0000" bus="0x00" slot="0x01" function="0x1" />
    </controller>
    <controller type="virtio-serial" index="0">
      <alias name="virtio-serial0" />
      <address type="pci" domain="0x0000" bus="0x00" slot="0x06" function="0x0" />
    </controller>
    <interface type="network">
      <mac address="52:54:00:e3:27:fa" />
      <source network="default" bridge="virbr0" />
      <target dev="vnet1" />
      <model type="virtio" />
      <alias name="net0" />
      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x0" />
    </interface>
    <serial type="pty">
      <source path="/dev/pts/6" />
      <target type="isa-serial" port="0">
        <model name="isa-serial" />
      </target>
      <alias name="serial0" />
    </serial>
    <console type="pty" tty="/dev/pts/6">
      <source path="/dev/pts/6" />
      <target type="serial" port="0" />
      <alias name="serial0" />
    </console>
    <channel type="spicevmc">
      <target type="virtio" name="com.redhat.spice.0" state="connected" />
      <alias name="channel0" />
      <address type="virtio-serial" controller="0" bus="0" port="1" />
    </channel>
    <input type="tablet" bus="usb">
      <alias name="input0" />
      <address type="usb" bus="0" port="1" />
    </input>
    <input type="mouse" bus="ps2">
      <alias name="input1" />
    </input>
    <input type="keyboard" bus="ps2">
      <alias name="input2" />
    </input>
    <graphics type="spice" port="5901" autoport="yes" listen="0.0.0.0">
      <listen type="address" address="0.0.0.0" />
    </graphics>
    <sound model="ich6">
      <alias name="sound0" />
      <address type="pci" domain="0x0000" bus="0x00" slot="0x04" function="0x0" />
    </sound>
    <video>
      <model type="qxl" ram="65536" vram="65536" vgamem="16384" heads="1" primary="yes" />
      <alias name="video0" />
      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x0" />
    </video>
    <redirdev bus="usb" type="spicevmc">
      <alias name="redir0" />
      <address type="usb" bus="0" port="2" />
    </redirdev>
    <redirdev bus="usb" type="spicevmc">
      <alias name="redir1" />
      <address type="usb" bus="0" port="3" />
    </redirdev>
    <memballoon model="virtio">
      <alias name="balloon0" />
      <address type="pci" domain="0x0000" bus="0x00" slot="0x08" function="0x0" />
    </memballoon>
  </devices>
  <seclabel type="dynamic" model="dac" relabel="yes">
    <label>+107:+107</label>
    <imagelabel>+107:+107</imagelabel>
  </seclabel>
</domain>
INFO:root:Guest graphics address is 0.0.0.0:5901
DEBUG:root:Spice connecting to host=0.0.0.0 port=5901 tlsport=None
DEBUG:root:New channel signal: channel=<SpiceClientGLib.MainChannel object at 0x7f832053bc80 (SpiceMainChannel at 0x55ed79f42750)>
Recording... Press Q to stop
DEBUG:root:Main channel <SpiceClientGLib.MainChannel object at 0x7f832053bc80 (SpiceMainChannel at 0x55ed79f42750)> event (20) <enum SPICE_CHANNEL_ERROR_CONNECT of type SpiceClientGLib.ChannelEvent>

Paused start

It would be nice if it would start in paused mode, eg. accessible via API to resume/start/stop session recording.

I'm thinking about a scenario where an admin would login, pam_exec module would trigger session recording resume.

Is it possible to record a non virt-manager QEMU instance?

i.e. what do I have to do to record a machine launched like this:

qemu-system-x86_64 -accel kvm -kernel vmlinuz -initrd initrd.img -append "root=live:http://host/images/install.img inst.text=1 inst.ks=http://host/linux/fedora/32.ks" -cpu qemu64 -m 2048M -smp 4 -drive file=fedora.qcow2,media=disk,if=virtio -spice port=5930

Doesn't record cursor

This isn't really an issue if spice-record is the only client, because it never sends any cursor position updates to the VM. (Unless I suppose someone is programatically moving the cursor.)

However, with multi-client support, it would be useful to see where the other client is moving his mouse.

It looks like we can handle here the new CursorChannel and then watch for the cursor-move signal.

It forces virt-manager to deconnect

Hi
Could it be possible to have both virt-manager & spice-record connecting to the same VM?
Else one can record with spice-record what he does in the VM with virt-manager…
See you

Stutter at t=2 sec when streaming to FFmpeg

I did however notice (regardless of this change) that about 2 seconds into
recording to x264, the write() calls begin to take a very long time. Where
they normally take 1-2 ms, they begin to take 100-400 ms, sometimes over
a second!

  • Happens regardless of -use_wallclock_as_timestamps 1

Record audio

Record audio in the .mp4 as well. This may get tricky, as we have to handle the displays coming and going, and it's possible the audio could become out of sync. Especially because I'm not sure if we can possibly miss frames between displays: #1

More accurate timing

The current use of GLib.timeout_add() for the frame capture timer is of questionable accuracy, but could potentially be easily improved.

The way timeout_add works, it schedules the next callback for the programmed duration after your callback returns. So at a framerate of 24 fps, the callback should get called every ~42 ms. But, if we spend 3 ms in the callback writing to the pipe, that means we'll actually see callbacks every 45 ms.

Instead, we could return False from our callback (indicating it shouldn't be called again), and schedule the next callback for (interval - processing_time) ms from now.

Of course, calling timeout_add itself has a cost, which should also be factored in. We could continually calibrate this, measuring the amount of time timeout_add took, and subtracting that in the next interval.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.