Blog

Probando v4l2loopback

Probando v4l2loopback

v4l2loopback permite crear interfaces de vídeo virtuales. Las pueden leer aplicaciones compatibles con el API Video4Linux.

Mandando un fichero de vídeo mediante gstreamer a una interfaz de v4l2loopback, que aparece como una webcam Dummy video device (0x0000) en Skype.

Vamos a emplearlo para mandar un fichero de vídeo:

sudo modprobe v4l2loopback

Primero cargamos el módulo v4l2loopback, que creará una interfaz de vídeo. En mi caso es /dev/video2, porque ya tenía una webcam que registra /dev/video0 y /dev/video1:

[j@localhost lucky_star]$ ls -l /dev/video*
crw-rw----+ 1 root video 81, 0 Nov 18 18:14 /dev/video0
crw-rw----+ 1 root video 81, 1 Nov 18 18:14 /dev/video1
crw-rw----+ 1 root video 81, 2 Nov 18 18:15 /dev/video2

Vamos a pasar un vídeo mkv a /dev/video2 mediante gstreamer. La sintaxis es algo compleja y por algún motivo no me funciona con todos los mkv que he probado (dejando en estos casos la pantalla en negro). En gstreamer se aplican transformaciones mediante pipes, igual que un pipe de Linux, pero con el símbolo de exclamación. Primero parseamos el mkv con mastroska y extraemos el vídeo, parseamos éste con h264parse, lo decodificamos con avdec_h264, convertimos a formato de píxel YUY2 y finalmente lo enviamos a la interfaz de v4l2loopback. YUY2 porque es un requerimiento de ésta.

[j@localhost lucky_star]$ gst-launch-1.0 -v filesrc location='[Doki]_Lucky_Star_-_01_(1280x720_Hi10P_BD_FLAC)_[60B766F1].mkv' ! matroskademux name=demux demux.video_0 ! h264parse ! avdec_h264 ! videoconvert ! "video/x-raw,format=YUY2" ! v4l2sink device=/dev/video2
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
/GstPipeline:pipeline0/GstH264Parse:h264parse0.GstPad:src: caps = video/x-h264, level=(string)5, profile=(string)high-10, codec_data=(buffer)016e0032ffe1001a676e0032a6c7204405005ba100000303e90000bb808f1831846001000768e8438272c8b0, stream-format=(string)avc, alignment=(string)au, width=(int)1280, height=(int)720, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)24000/1001, interlace-mode=(string)progressive, chroma-format=(string)4:2:0, bit-depth-luma=(uint)10, bit-depth-chroma=(uint)10, parsed=(boolean)true
Redistribute latency...
/GstPipeline:pipeline0/avdec_h264:avdec_h264-0.GstPad:sink: caps = video/x-h264, level=(string)5, profile=(string)high-10, codec_data=(buffer)016e0032ffe1001a676e0032a6c7204405005ba100000303e90000bb808f1831846001000768e8438272c8b0, stream-format=(string)avc, alignment=(string)au, width=(int)1280, height=(int)720, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)24000/1001, interlace-mode=(string)progressive, chroma-format=(string)4:2:0, bit-depth-luma=(uint)10, bit-depth-chroma=(uint)10, parsed=(boolean)true
/GstPipeline:pipeline0/GstH264Parse:h264parse0.GstPad:sink: caps = video/x-h264, level=(string)5, profile=(string)high-10, codec_data=(buffer)016e0032ffe1001a676e0032a6c7204405005ba100000303e90000bb808f1831846001000768e8438272c8b0, stream-format=(string)avc, alignment=(string)au, width=(int)1280, height=(int)720, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)24000/1001
/GstPipeline:pipeline0/avdec_h264:avdec_h264-0.GstPad:src: caps = video/x-raw, format=(string)I420_10LE, width=(int)1280, height=(int)720, interlace-mode=(string)progressive, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono, pixel-aspect-ratio=(fraction)1/1, chroma-site=(string)mpeg2, colorimetry=(string)bt709, framerate=(fraction)24000/1001
/GstPipeline:pipeline0/GstVideoConvert:videoconvert0.GstPad:src: caps = video/x-raw, width=(int)1280, height=(int)720, interlace-mode=(string)progressive, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)24000/1001, format=(string)YUY2, colorimetry=(string)2:4:7:1
/GstPipeline:pipeline0/GstCapsFilter:capsfilter0.GstPad:src: caps = video/x-raw, width=(int)1280, height=(int)720, interlace-mode=(string)progressive, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)24000/1001, format=(string)YUY2, colorimetry=(string)2:4:7:1
/GstPipeline:pipeline0/GstV4l2Sink:v4l2sink0.GstPad:sink: caps = video/x-raw, width=(int)1280, height=(int)720, interlace-mode=(string)progressive, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)24000/1001, format=(string)YUY2, colorimetry=(string)2:4:7:1
/GstPipeline:pipeline0/GstCapsFilter:capsfilter0.GstPad:sink: caps = video/x-raw, width=(int)1280, height=(int)720, interlace-mode=(string)progressive, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)24000/1001, format=(string)YUY2, colorimetry=(string)2:4:7:1
/GstPipeline:pipeline0/GstVideoConvert:videoconvert0.GstPad:sink: caps = video/x-raw, format=(string)I420_10LE, width=(int)1280, height=(int)720, interlace-mode=(string)progressive, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono, pixel-aspect-ratio=(fraction)1/1, chroma-site=(string)mpeg2, colorimetry=(string)bt709, framerate=(fraction)24000/1001
Redistribute latency...
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock
^Chandling interrupt.
Interrupt: Stopping pipeline ...
Execution ended after 0:00:33.125487658
Setting pipeline to PAUSED ...
Setting pipeline to READY ...
Setting pipeline to NULL ...
Freeing pipeline ...

Si queremos volver a ejecutar el comando, hay que cerrar la ventana de settings de Skype primero, o de lo contrario nos saltará este error misterioso:

[j@localhost lucky_star]$ gst-launch-1.0 -v filesrc location='[Doki]_Lucky_Star_-_01_(1280x720_Hi10P_BD_FLAC)_[60B766F1].mkv' ! matroskademux name=demux demux.video_0 ! h264parse ! avdec_h264 ! videoconvert ! "video/x-raw,format=YUY2" ! v4l2sink device=/dev/video2
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
WARNING: from element /GstPipeline:pipeline0/GstMatroskaDemux:demux: Delayed linking failed.
Additional debug info:
./grammar.y(506): gst_parse_no_more_pads (): /GstPipeline:pipeline0/GstMatroskaDemux:demux:
failed delayed linking pad  video_0 of GstMatroskaDemux named demux to some pad of GstH264Parse named h264parse0
ERROR: from element /GstPipeline:pipeline0/GstMatroskaDemux:demux: Internal data stream error.
Additional debug info:
matroska-demux.c(5715): gst_matroska_demux_loop (): /GstPipeline:pipeline0/GstMatroskaDemux:demux:
streaming stopped, reason not-linked (-1)
ERROR: pipeline doesn't want to preroll.
Setting pipeline to NULL ...
Freeing pipeline ...

Ocurre lo mismo durante una videollamada. Deberemos desactivar el vídeo antes de rearrancar gstreamer.