読者です 読者をやめる 読者になる 読者になる

😃 mattintosh note 📝

Hello Raspberry Pi!

Raspberry Pi で GStreamer(gst-launch)

Raspberry Pi GStreamer

Raspberry Pi では OpenMAX のライブラリを使うことで高速に H264 エンコードができる。

現時点での Arch Linux ARM の ffmpeg は --enable-omx-rpi オプション付きでビルドされていないためエンコーダーに h264_omx が使えない。これは単純に ffmpeg をセルフビルドすれば済むが、omxplayer や GStreamer のそれに比べるとまだまだ性能を発揮できていないようにも感じる。

Raspberry Pi 用のカメラであれば raspivid で h264 で取り込みが出来るのでそこそこ負荷はかからないが、低価格な USB Web カメラは RAW か MJPEG にしか対応していないためハードウェアエンコードがほぼ必須となってくる。そこで、GStreamer を使って USB 接続した Web カメラの映像を取り込んでみることにした。

使うカメラは Buffalo の BSW20KM11BK。

カメラとマイクへのアクセス権を変更

まず、/dev/video0 に特権なしでカメラにアクセスできるようアカウントを video グループに追加する。

sudo usermod -a -G video $USER

マイクが認識されているか確認する。

arecord -l

ここでカメラのマイクが出てこない場合は特権で実行してみる。

sudo arecord -l

ここで出てくる場合は audio グループに入っていないのでこちらもグループに追加する。

sudo usermod -a -G audio $USER

一旦、ログアウトする。

デバイスを確認する

gst-device-monitor でデバイスを確認する。ビデオだけであれば v4l2-ctl --all でも良い。

※ssh -Y で接続しているとデバイスモニターが開始できないと怒られる。

gst-device-monitor-1.0
Probing devices... Device found: name : Genius WideCam F100 Analog Stereo class : Audio/Source caps : audio/x-raw, format=(string){ S16LE, S16BE, F32LE, F32BE, S32LE, S32BE, S24LE, S24BE, S24_32LE, S24_32BE, U8 }, layout=(string)interleaved, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; audio/x-alaw, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; audio/x-mulaw, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; properties: alsa.resolution_bits = 16 device.api = alsa device.class = sound alsa.class = generic alsa.subclass = generic-mix alsa.name = "USB\ Audio" alsa.id = "USB\ Audio" alsa.subdevice = 0 alsa.subdevice_name = "subdevice\ \#0" alsa.device = 0 alsa.card = 0 alsa.card_name = USB_Camera alsa.long_card_name = "KYE\ Systems\ Corp.\ USB_Camera\ at\ usb-3f980000.usb-1.4\,\ high\ speed" alsa.driver_name = snd_usb_audio device.bus_path = platform-3f980000.usb-usb-0:1.4:1.2 sysfs.path = /devices/platform/soc/3f980000.usb/usb1/1-1/1-1.4/1-1.4:1.2/sound/card0 udev.id = usb-KYE_Systems_Corp.USBCamera_200901010001-02 device.bus = usb device.vendor.id = 0458 device.vendor.name = "KYE\ Systems\ Corp.\ \(Mouse\ Systems\)" device.product.id = 708c device.product.name = "Genius\ WideCam\ F100" device.serial = KYE_Systems_Corp.USBCamera_200901010001 device.form_factor = webcam device.string = front:0 device.buffering.buffer_size = 352800 device.buffering.fragment_size = 176400 device.access_mode = mmap+timer device.profile.name = analog-stereo device.profile.description = "Analog\ Stereo" device.description = "Genius\ WideCam\ F100\ Analog\ Stereo" alsa.mixer_name = "USB\ Mixer" alsa.components = USB0458:708c module-udev-detect.discovered = 1 device.icon_name = camera-web-usb Device found: name : Monitor of Dummy Output class : Audio/Source caps : audio/x-raw, format=(string){ S16LE, S16BE, F32LE, F32BE, S32LE, S32BE, S24LE, S24BE, S24_32LE, S24_32BE, U8 }, layout=(string)interleaved, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; audio/x-alaw, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; audio/x-mulaw, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; properties: device.description = "Monitor\ of\ Dummy\ Output" device.class = monitor device.icon_name = audio-input-microphone Device found: name : Dummy Output class : Audio/Sink caps : audio/x-raw, format=(string){ S16LE, S16BE, F32LE, F32BE, S32LE, S32BE, S24LE, S24BE, S24_32LE, S24_32BE, U8 }, layout=(string)interleaved, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; audio/x-alaw, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; audio/x-mulaw, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 32 ]; properties: device.description = "Dummy\ Output" device.class = abstract device.icon_name = audio-card Device found: name : USB_Camera class : Video/Source caps : video/x-raw, format=(string)YUY2, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, colorimetry=(string)2:4:7:1, framerate=(fraction)5/1; video/x-raw, format=(string)YUY2, width=(int)1280, height=(int)720, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, colorimetry=(string)2:4:7:1, framerate=(fraction)8/1; video/x-raw, format=(string)YUY2, width=(int)800, height=(int)600, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, colorimetry=(string)2:4:7:1, framerate=(fraction)10/1; video/x-raw, format=(string)YUY2, width=(int)640, height=(int)480, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, colorimetry=(string)2:4:7:1, framerate=(fraction)30/1; video/x-raw, format=(string)YUY2, width=(int)352, height=(int)288, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, colorimetry=(string)2:4:7:1, framerate=(fraction)30/1; video/x-raw, format=(string)YUY2, width=(int)320, height=(int)240, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, colorimetry=(string)2:4:7:1, framerate=(fraction)30/1; video/x-raw, format=(string)YUY2, width=(int)176, height=(int)144, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, colorimetry=(string)2:4:7:1, framerate=(fraction)30/1; video/x-raw, format=(string)YUY2, width=(int)160, height=(int)120, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, colorimetry=(string)2:4:7:1, framerate=(fraction)30/1; image/jpeg, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, colorimetry=(string)2:4:7:1, framerate=(fraction)30/1; image/jpeg, width=(int)1280, height=(int)720, pixel-aspect-ratio=(fraction)1/1, colorimetry=(string)2:4:7:1, framerate=(fraction)30/1; image/jpeg, width=(int)800, height=(int)600, pixel-aspect-ratio=(fraction)1/1, colorimetry=(string)2:4:7:1, framerate=(fraction)30/1; image/jpeg, width=(int)640, height=(int)480, pixel-aspect-ratio=(fraction)1/1, colorimetry=(string)2:4:7:1, framerate=(fraction)30/1; image/jpeg, width=(int)352, height=(int)288, pixel-aspect-ratio=(fraction)1/1, colorimetry=(string)2:4:7:1, framerate=(fraction)30/1; image/jpeg, width=(int)320, height=(int)240, pixel-aspect-ratio=(fraction)1/1, colorimetry=(string)2:4:7:1, framerate=(fraction)30/1; image/jpeg, width=(int)176, height=(int)144, pixel-aspect-ratio=(fraction)1/1, colorimetry=(string)2:4:7:1, framerate=(fraction)30/1; image/jpeg, width=(int)160, height=(int)120, pixel-aspect-ratio=(fraction)1/1, colorimetry=(string)2:4:7:1, framerate=(fraction)30/1; video/x-raw, format=(string)I420, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)I420, width=(int)1280, height=(int)720, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)I420, width=(int)800, height=(int)600, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)I420, width=(int)640, height=(int)480, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)I420, width=(int)352, height=(int)288, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)I420, width=(int)320, height=(int)240, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)I420, width=(int)176, height=(int)144, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)I420, width=(int)160, height=(int)120, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)YV12, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)YV12, width=(int)1280, height=(int)720, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)YV12, width=(int)800, height=(int)600, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)YV12, width=(int)640, height=(int)480, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)YV12, width=(int)352, height=(int)288, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)YV12, width=(int)320, height=(int)240, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)YV12, width=(int)176, height=(int)144, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)YV12, width=(int)160, height=(int)120, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)BGR, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)BGR, width=(int)1280, height=(int)720, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)BGR, width=(int)800, height=(int)600, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)BGR, width=(int)640, height=(int)480, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)BGR, width=(int)352, height=(int)288, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)BGR, width=(int)320, height=(int)240, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)BGR, width=(int)176, height=(int)144, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)BGR, width=(int)160, height=(int)120, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)RGB, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)RGB, width=(int)1280, height=(int)720, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)RGB, width=(int)800, height=(int)600, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)RGB, width=(int)640, height=(int)480, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)RGB, width=(int)352, height=(int)288, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)RGB, width=(int)320, height=(int)240, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)RGB, width=(int)176, height=(int)144, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; video/x-raw, format=(string)RGB, width=(int)160, height=(int)120, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1; properties: udev-probed = true device.bus_path = platform-3f980000.usb-usb-0:1.4:1.0 sysfs.path = /sys/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.4/1-1.4:1.0/video4linux/video0 device.bus = usb device.subsystem = video4linux device.vendor.id = 0458 device.vendor.name = "KYE\\x20Systems\\x20Corp." device.product.id = 708c device.product.name = USB_Camera device.serial = KYE_Systems_Corp.USBCamera_200901010001 device.capabilities = :capture: device.api = v4l2 device.path = /dev/video0 v4l2.device.driver = uvcvideo v4l2.device.card = USB_Camera v4l2.device.bus_info = usb-3f980000.usb-1.4 v4l2.device.version = 263190 (0x00040416) v4l2.device.capabilities = 2233466881 (0x85200001) v4l2.device.device_caps = 85983233 (0x05200001)

今回使うカメラは RAW(YUY2/I420/YV12/RGB/BGR)か MJPEG に対応している。Raspberry Pi 用のカメラやちょっとお高めのウェブカメラは H264 に対応している。

この中で omxh264enc が対応しているのは I420 だけ?

ビデオエンコード

まずは入力を接続。qtmux か faac を使う場合は -e(EOF)がいるんだったかな…。

gst-launch-1.0 -v -e v4l2src device=/dev/video0

次にビデオのフォーマットを指定。

! video/x-raw, format=I420, width=1280, height=720, framerate=30/1

ビデオエンコーダを指定。パーサーも必要。

! omxh264enc ! h264parse

そして Muxer。

! qtmux

最後に sink。

! filesink location=sample.mp4

これを繋げると下のようになる。MP4 は正しく終了しないとコンテナがおかしくなるので timeout -s INT で割り込みをかけて正しく終了させる。

timeout -s INT 10 gst-launch-1.0 -v -e v4l2src device=/dev/video0 \
! video/x-raw, format=I420, width=1280, height=720, framerate=30/1 \
! omxh264enc ! h264parse \
! qtmux \
! filesink location=sample.mp4

ビットレートを指定する場合は target-bitrate= と control-rate= を使用する。target-bitrate= だけでは動かないので注意。あと、プロパティ間に ,(カンマ)も要らない。(なぜ仕様を合わせないのか…)

omxh264enc target-bitrate=2000000 control-rate=variable
gst-inspect-1.0 omxh264enc
Factory Details: Rank primary + 1 (257) Long-name OpenMAX H.264 Video Encoder Klass Codec/Encoder/Video Description Encode H.264 video streams Author Sebastian Dröge <sebastian.droege@collabora.co.uk> Plugin Details: Name omx Description GStreamer OpenMAX Plug-ins Filename /usr/lib/gstreamer-1.0/libgstomx.so Version 1.2.0 License LGPL Source module gst-omx Source release date 2014-07-23 Binary package GStreamer OpenMAX IL wrapper Plugin (ArchlinuxARM/RPi) Origin URL http://www.archlinuxarm.org/ GObject +----GInitiallyUnowned +----GstObject +----GstElement +----GstVideoEncoder +----GstOMXVideoEnc +----GstOMXH264Enc +----GstOMXH264Enc-omxh264enc Implemented Interfaces: GstPreset Pad Templates: SINK template: 'sink' Availability: Always Capabilities: video/x-raw width: [ 1, 2147483647 ] height: [ 1, 2147483647 ] framerate: [ 0/1, 2147483647/1 ] SRC template: 'src' Availability: Always Capabilities: video/x-h264 width: [ 16, 4096 ] height: [ 16, 4096 ] Element Flags: no flags set Element Implementation: Has change_state() function: gst_omx_video_enc_change_state Element has no clocking capabilities. Element has no URI handling capabilities. Pads: SINK: 'sink' Pad Template: 'sink' SRC: 'src' Pad Template: 'src' Element Properties: name : The name of the object flags: readable, writable String. Default: "omxh264enc-omxh264enc0" parent : The parent of the object flags: readable, writable Object of type "GstObject" control-rate : Bitrate control method flags: readable, writable, changeable only in NULL or READY state Enum "GstOMXVideoEncControlRate" Default: -1, "default" (0): disable - Disable (1): variable - Variable (2): constant - Constant (3): variable-skip-frames - Variable Skip Frames (4): constant-skip-frames - Constant Skip Frames (-1): default - Component Default target-bitrate : Target bitrate (0xffffffff=component default) flags: readable, writable, changeable in NULL, READY, PAUSED or PLAYING state Unsigned Integer. Range: 0 - 4294967295 Default: 4294967295 quant-i-frames : Quantization parameter for I-frames (0xffffffff=component default) flags: readable, writable, changeable only in NULL or READY state Unsigned Integer. Range: 0 - 4294967295 Default: 4294967295 quant-p-frames : Quantization parameter for P-frames (0xffffffff=component default) flags: readable, writable, changeable only in NULL or READY state Unsigned Integer. Range: 0 - 4294967295 Default: 4294967295 quant-b-frames : Quantization parameter for B-frames (0xffffffff=component default) flags: readable, writable, changeable only in NULL or READY state Unsigned Integer. Range: 0 - 4294967295 Default: 4294967295 inline-header : Inline SPS/PPS header before IDR flags: readable, writable, changeable only in NULL or READY state Boolean. Default: true periodicty-idr : Periodicity of IDR frames (0xffffffff=component default) flags: readable, writable, changeable only in NULL or READY state Unsigned Integer. Range: 0 - 4294967295 Default: 4294967295 interval-intraframes: Interval of coding Intra frames (0xffffffff=component default) flags: readable, writable, changeable only in NULL or READY state Unsigned Integer. Range: 0 - 4294967295 Default: 4294967295

次に faac。

gst-inspect-1.0 faac

これって src と sink 逆だと思うんだけど合ってるのか…?

Factory Details: Rank secondary (128) Long-name AAC audio encoder Klass Codec/Encoder/Audio Description Free MPEG-2/4 AAC encoder Author Ronald Bultje <rbultje@ronald.bitfreak.net> Plugin Details: Name faac Description Free AAC Encoder (FAAC) Filename /usr/lib/gstreamer-1.0/libgstfaac.so Version 1.8.3 License LGPL Source module gst-plugins-bad Source release date 2016-08-19 Binary package GStreamer Bad Plugins (Arch Linux) Origin URL http://www.archlinux.org/ GObject +----GInitiallyUnowned +----GstObject +----GstElement +----GstAudioEncoder +----GstFaac Implemented Interfaces: GstPreset Pad Templates: SRC template: 'src' Availability: Always Capabilities: audio/mpeg mpegversion: 4 channels: [ 1, 6 ] rate: { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 } stream-format: { adts, raw } base-profile: { main, lc, ssr, ltp } framed: true audio/mpeg mpegversion: 2 channels: [ 1, 6 ] rate: { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 } stream-format: { adts, raw } profile: { main, lc } framed: true SINK template: 'sink' Availability: Always Capabilities: audio/x-raw format: S16LE layout: interleaved rate: { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 } channels: 1 audio/x-raw format: S16LE layout: interleaved rate: { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 } channels: 2 channel-mask: 0x0000000000000003 audio/x-raw format: S16LE layout: interleaved rate: { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 } channels: 3 channel-mask: 0x0000000000000007 audio/x-raw format: S16LE layout: interleaved rate: { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 } channels: 4 channel-mask: 0x0000000000000107 audio/x-raw format: S16LE layout: interleaved rate: { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 } channels: 5 channel-mask: 0x0000000000000037 audio/x-raw format: S16LE layout: interleaved rate: { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 } channels: 6 channel-mask: 0x000000000000003f Element Flags: no flags set Element Implementation: Has change_state() function: gst_audio_encoder_change_state Element has no clocking capabilities. Element has no URI handling capabilities. Pads: SINK: 'sink' Pad Template: 'sink' SRC: 'src' Pad Template: 'src' Element Properties: name : The name of the object flags: readable, writable String. Default: "faac0" parent : The parent of the object flags: readable, writable Object of type "GstObject" perfect-timestamp : Favour perfect timestamps over tracking upstream timestamps flags: readable, writable Boolean. Default: false mark-granule : Apply granule semantics to buffer metadata (implies perfect-timestamp) flags: readable Boolean. Default: false hard-resync : Perform clipping and sample flushing upon discontinuity flags: readable, writable Boolean. Default: false tolerance : Consider discontinuity if timestamp jitter/imperfection exceeds tolerance (ns) flags: readable, writable Integer64. Range: 0 - 9223372036854775807 Default: 40000000 quality : Variable bitrate (VBR) quantizer quality in % flags: readable, writable Integer. Range: 1 - 1000 Default: 100 bitrate : Average Bitrate (ABR) in bits/sec flags: readable, writable Integer. Range: 8000 - 320000 Default: 128000 rate-control : Encoding bitrate type (VBR/ABR) flags: readable, writable Enum "GstFaacBrtype" Default: 1, "VBR encoding" (1): VBR encoding - VBR (2): ABR encoding - ABR tns : Use temporal noise shaping flags: readable, writable Boolean. Default: false midside : Allow mid/side encoding flags: readable, writable Boolean. Default: true shortctl : Block type encorcing flags: readable, writable Enum "GstFaacShortCtl" Default: 0, "Normal block type" (0): Normal block type - SHORTCTL_NORMAL (1): No short blocks - SHORTCTL_NOSHORT (2): No long blocks - SHORTCTL_NOLONG

ビデオとオーディオを組み合わせる(+いくつかのフィルタ)

フレームレートの変更は videorate フィルタを挟まなくてはならない。色空間の変換は autovideoconvert あたりでいいのだろうか?

clockoverlay は現在の時間を表示する。

ビデオとオーディオを結合する場合は queue を使うことになるが、Muxer へ接続する際に次のエレメントとの間に ! は使わない。Muxer などはデフォルトで name プロパティを持っている(qtmux なら qtmux0)が、これは上書きできる。

gst-launch-1.0 -v -e \
v4l2src device=/dev/video0 \
! videorate ! video/x-raw, format=I420, width=1280, height=720, framerate=15/1 ! clockoverlay \
! omxh264enc ! h264parse ! queue ! m. \
alsasrc device=hw:0 ! audio/x-raw, format=S16LE, rate=48000, channels=2 \
! faac ! aacparse ! queue ! m. \
qtmux name=m \
! filesink location=sample.mp4

この辺はスクリプトにして変数使ったほうがいいかもしれない。

VIDEO_SOURCE="v4l2src device=/dev/video0"
VIDEO_SOURCE_FORMAT="video/x-raw, format=I420, width=1280, height=720, framerate=30/1"
VIDEO_ENC="omxh264enc ! h264parse"
AUDIO_SOURCE="alsasrc device=hw:0"
AUDIO_SOURCE_FORMAT="audio/x-raw, format=S16LE, rate=48000, channels=2"
AUDIO_ENC="faac ! aacparse"
MUXER="qtmux"
SINK="filesink location=sample.mp4"

gst-launch-1.0 -v -e \
${VIDEO_SOURCE} ! ${VIDEO_SOURCE_FORMAT} ! ${VIDEO_ENC} ! queue ! qtmux0. \
${AUDIO_SOURCE} ! ${AUDIO_SOURCE_FORMAT} ! ${AUDIO_ENC} ! queue ! qtmux0. \
${MUXER} \
! ${SINK}

どっかにキューをもっと綺麗にまとめる書き方してたサイトがあったけどどこだっけ…。


Armadillo の解説サイトを読ませてもらったけどあれがなかったら覚えられなかったくらいわかりづらい。

あとは tcpserversink とかも覚えなければ…。ffmpeg なら -f v4l2 -i /dev/video0 とかでいいのに…ぐぬぬ。

mpv と youtube-dl を使ってブラウザを使わずに YouTube 動画を高画質で再生する

Linux mpv

mpv にはハードウェア再生支援機能が付いている(ビルドによるんだろうけど)。

まずは対応状況確認。これは昨日の段階で Git ソースからビルドしたもの。Ubuntu 版は結構バージョンが古いし、caca が使えなかった。--hwdec= オプションには help がないが、無効な値を指定すると有効な値を表示してくれる。

$ mpv --version
mpv git-4395a4f (C) 2000-2016 mpv/MPlayer/mplayer2 projects
 built on Sat Sep 17 23:59:53 JST 2016
ffmpeg library versions:
   libavutil       54.31.100
   libavcodec      56.60.100
   libavformat     56.40.101
   libswscale      3.1.101
   libavfilter     5.40.101
   libswresample   1.2.101
ffmpeg version: 2.8.6-1ubuntu2

$ mpv --vo=help; mpv --hwdec=help
Available video outputs: opengl : Extended OpenGL Renderer vdpau : VDPAU with X11 wayland : Wayland SHM video output xv : X11/Xv vaapi : VA API with X11 x11 : X11 (slow, old crap) null : Null video output image : Write video frames to image files caca : libcaca drm : Direct Rendering Manager Invalid value for option hwdec: help Valid values are: no auto yes auto-copy vdpau videotoolbox videotoolbox-copy vaapi vaapi-copy dxva2 dxva2-copy d3d11va d3d11va-copy rpi mediacodec cuda cuda-copy Error parsing option hwdec (option could not be parsed) Setting command line option '--hwdec=help' failed. Exiting... (Fatal error)

hwdec の方は --list-options を見た方がいいかもしれない。

$ mpv --list-options | grep hwdec
 --hwdec                          Choices: no auto yes auto-copy vdpau videotoolbox videotoolbox-copy vaapi vaapi-copy dxva2 dxva2-copy d3d11va d3d11va-copy rpi mediacodec cuda cuda-copy (default: no)
 --hwdec-codecs                   String (default: h264,vc1,wmv3,hevc,mpeg2video,vp9)
 --hwdec-preload                  Choices: no auto yes auto-copy vdpau videotoolbox videotoolbox-copy vaapi vaapi-copy dxva2 dxva2-copy d3d11va d3d11va-copy rpi mediacodec cuda cuda-copy (default: no)

vainfo も見ておく。

$ vainfo
libva info: VA-API version 0.39.2 libva info: va_getDriverName() returns 0 libva info: Trying to open /usr/lib/x86_64-linux-gnu/dri/i965_drv_video.so libva info: Found init function __vaDriverInit_0_39 libva info: va_openDriver() returns 0 vainfo: VA-API version: 0.39 (libva 1.7.1) vainfo: Driver version: Intel i965 driver for Intel(R) Sandybridge Mobile - 1.7.1 vainfo: Supported profile and entrypoints VAProfileMPEG2Simple : VAEntrypointVLD VAProfileMPEG2Main : VAEntrypointVLD VAProfileH264ConstrainedBaseline: VAEntrypointVLD VAProfileH264ConstrainedBaseline: VAEntrypointEncSlice VAProfileH264Main : VAEntrypointVLD VAProfileH264Main : VAEntrypointEncSlice VAProfileH264High : VAEntrypointVLD VAProfileH264High : VAEntrypointEncSlice VAProfileH264StereoHigh : VAEntrypointVLD VAProfileVC1Simple : VAEntrypointVLD VAProfileVC1Main : VAEntrypointVLD VAProfileVC1Advanced : VAEntrypointVLD VAProfileNone : VAEntrypointVideoProc

さて、いま使っているマシンは Intel CPU なので vaapi-copy が最も良さそう。--vo の方は 60 fps 動画で色々試してみて一番滑らかに再生できるものを探す。

ちょうどこれが 60 fps なので借りてくる。

www.youtube.com

mpv 'https://www.youtube.com/watch?v=OO3UZBwpCLM' --hwdec=vaapi-copy --vo=opengl

ヌルヌル動けばOK。

f:id:mattintosh4:20160919002537p:plain

設定が決まったら ~/.config/mpv/mpv.conf に書いておく。

hwdec=vaapi-copy
vo=opengl

youtube-dl について

youtube-dl は名前の通り、YouTube の動画をそのままダウンロードしてくるソフトウェア。これは YouTube だけに限らず、他にも多くのサイトに対応している(対応リストはここ)。

著作権や日本国内の法律の問題もあるのでここではあまり詳しくは書かない。あくまで mpv のひとつの機能として、ダウンロードではなくストリーミングとしての扱い方に絞る。

再生に関するコントロールは mpv から行えるので最高画質・音質固定や連続(ループ)再生なども容易に指定することができる。また、mpv はプレイリストも扱えるので複数 URL をシャッフルするようなことも可能。

mpv では引数に YouTube のページ URL を与えるだけで再生ができる。(動画そのものの URL ではない)

mpv {YouTube URL}

品質の設定

man mpv によれば、品質は最高画質+最高音質の bestvideo+bestaudio/best となっているが、これはカスタマイズすることができる。

bestvideo+bestaudio(ビデオ・オーディオ分割)と best(単一ファイル)は前者の方が品質が良い。

例)低解像度のビデオに変更したい

PC のスペックが足りない場合や、ネットワークの帯域に余裕が無い場合などに。

mpv --ytdl-format="worst"
mpv --ytdl-format="[height<720]" {YouTube URL}
mpv --ytdl-format="[height<=480]" {YouTube URL}

解像度を下げたいが、画質は維持したい場合(60 fps の場合はこれ)

mpv --ytdl-format="bestvideo[height<=720]+bestaudio" {YouTube URL}

例)低解像度またはビデオなしでオーディオのみ最高品質で再生したい

mpv --ytdl-format="worstvideo+bestaudio" {YouTube URL}
mpv --ytdl-format="[height<=480]+bestaudio" {YouTube URL}
mpv --ytdl-format="bestaudio" {YouTube URL}

例えば、1366x768 のディスプレイで 1920x1080 の動画なんかを拾ってきても無駄が多いので ~/.config/mpv/mpv.conf に以下のように設定しておけばよい。(もし、一時的に ~/.config/mpv/mpv.conf の内容を無効にしたければオプションに --no-config を与えればよい)

ytdl-format=bestvideo[height<=720]+bestaudio/best

Raspberry Pi を YouTube ジュークボックスに

うちの Raspberry Pi にはスピーカーを接続していないのでメイン PC に PulseAudio を使ってオーディオだけ飛ばしている。mpv が OpenMAX に対応すれば FullHD の再生も滑らかになるのだが、まだ対応していないようだ。(--vo=rpi で X なしでも再生ができるようになってはいる)

export PULSE_SERVER=${SSH_CLIENT%% *}
mpv --hwdec=rpi --vo=rpi --ytdl-format="bestvideo[height<=480]+bestaudio" --shuffle --loop=force youtube_playlist.m3u

おわりに

一日中ずっと高画質で YouTube にアップされている動画を再生し続けたいなら mpv ほどシンプルなものはあまりないと思う。とりあえずローカルなら youtube.m3u みたいなファイル作ってそこに URL を1行ごとに貼って --loop=force --shuffle とでもしておけばいいだろう。ヒーリング・ミュージックを一日中再生するような日がよくあるので mpv のおかげでかなり楽になった。

Arch Linux ARM on Raspberry Pi で LXC を使って Ubuntu を4台同時起動してみる

Raspberry Pi Ubuntu LXC 仮想化

最近、仮想化で遊んでます。Docker 前から気になってるんだけど………LXC(LinuX Containers)です。

調べてみたら Raspberry Pi でも使えるので早速トライ。

lxc と Debian 系の Ubuntu を入れるので debootstrap をインストール。Arch Linux の場合は arch-install-scripts が必要。

pacman -S lxc debootstrap 

/usr/share/lxc/templates 以下はこんな感じ。

コンテナを作ってみる。

sudo lxc-create -t ubuntu -n ubuntu01

暫く放置しておく。最期にデフォルトユーザとパスワードが出力されるので覚えておく。

##
# The default user is 'ubuntu' with password 'ubuntu'!
# Use the 'sudo' command to run tasks as root in the container.
##

ネットワークが使えるように /var/lib/lxc/ubntu01/config を編集する。ホストの Arch Linux ARM では systemd-networkd で既に br0 は作成してある。

# Template used to create this container: /usr/share/lxc/templates/lxc-ubuntu
# Parameters passed to the template:
# For additional config options, please look at lxc.container.conf(5)

# Uncomment the following line to support nesting containers:
#lxc.include = /usr/share/lxc/config/nesting.conf
# (Be aware this has security implications)


# Common configuration
lxc.include = /usr/share/lxc/config/ubuntu.common.conf

# Container specific configuration
lxc.rootfs = /var/lib/lxc/ubuntu01/rootfs
lxc.rootfs.backend = dir
lxc.utsname = ubuntu01
lxc.arch = armhf

# Network configuration
#lxc.network.type = empty
lxc.network.type = veth
lxc.network.link = br0
lxc.network.flags = up
lxc.network.ipv4 = 192.168.1.123/24
lxc.network.ipv4.gateway = 192.168.1.1
lxc.network.name = eth0

コンテナを起動する。

sudo lxc-start -n ubuntu01

ssh -X で直接コンテナに接続して glxgears を動かしてみたところ。30 fps 以上出るし母艦の Raspberry Pi にも余力がまだある。

f:id:mattintosh4:20160911163303p:plain

lxc-copy -n ubuntu01 -N ubuntu0X でコンテナを複製してみた。ご丁寧に rootfs/etc/hostname と rootfs/etc/hosts も書き換えてくれるらしい。(ただし、config の lxc.network.ipv4 は変わらない)

Ubuntu のコンテナを複製して4台同時起動してみた。

f:id:mattintosh4:20160911191855p:plain

Raspberry Pi 3 一台で複数の OS が起動できるし、ネットワークインターフェイスも個別に設定が出来るから5〜6人ならそれぞれに仮想マシンを用意させてあげることができそう。lxc-create してから少し時間はかかるけど時間あるときに作っておいてあとは lxc-copy で複製すればいいかな。

なんか IP が2つ割り当たっていて NIC の設定を間違っているような気がするので追々調整する…。


DHCP が有効だとこれだけでいいっぽい。固定 IP にしようとすると IP が二つ割り当てられるんだけどどう設定するんだろ。

lxc.network.type = macvlan
lxc.network.link = br0

↑コンテナの中の /etc/network/interfaces 編集するだけだった…。

auto eth0
iface eth0 inet static
    address 192.168.1.123
    netmask 255.255.255.0
    gateway 192.168.1.1
    dns-nameservers 8.8.8.8 8.8.4.4 192.168.1.1

ホスト側から設定ファイル修正出来るのも簡単でいいなぁ…。

Arch Linux の場合はインターフェイスが起動しないので lxc.network.flags = up にしとく。

lxc.network.type = macvlan
lxc.network.link = br0
lxc.network.flags = up

んで ip コマンド。まだやってないけど /etc/systemd/network に設定ファイル置いておけば起動するだろう。

ip a add 192.168.1.234/24 dev eth0
ip r add default via 192.168.1.1

Raspberry Pi (ARM) で i386 Linux のバイナリを実行する

Raspberry Pi QEMU Arch Linux

Raspberry Pi で Nero AAC Codec(neroAacEnc)を使えるようにしてみた。

f:id:mattintosh4:20160911023334p:plain

続きを読む

Ubuntu Linux で qaac.exe を使って AAC/ALAC にエンコードする

Linux Ubuntu qaac Wine cdrdao bchunk SoX p7zip aac Mac OS X mp4

脱 Nero AAC Enc を目指して SoX とか色々使いつつもなんか違うなぁ〜と試行錯誤。(そもそも libav の aac コーデックが好きじゃないなら何やっても無駄じゃ…)

続きを読む