FICUSONLINE F9E
Raspberry Pi 向けLinphoneをYoctoでビルド(I2S)
組込みLinux向けに様々なツールや開発メソッドを提供しているYocto projectを利用して、Raspberry Pi 2向けにSIP VoIPアプリであるLinphoneに特化したイメージを作成します。構成パーツはRaspberry Pi 2本体の他、GPIOのI2Sに接続するMEMSマイクとDACスピーカ、カメラです。LAN接続は有線としますが、後日、USB-WiFiドングルにも対応したビルドイメージも作成します。
Takanobu FuseAdministrator

3 weeks ago

Linux

概要

製作意図はRaspberry PiをSIP電話またはインターホンシステムのエントリーパネル(ドアホン)として利用(試験用)するためです。 SIP-VoIP機能により、SIP電話として利用する場合は、室内モニターや家族との通話手段として、ドアホンとして利用する場合は、訪問者の確認や訪問者との通話、ドアロックの解除などを遠隔から行えます。組み込み向けLinuxディストリビューションを構築できるYoctoを使用して、Raspberry Pi 2 向けのカスタム専用イメージを作成します。モバイル側のアプリにはLinhomeを使用します。

Raspberry Pi 2

  • コンピュータボード:Raspberry Pi 2(カメラ、ボタン、ドアスイッチ、マイク、スピーカ)

  • モバイルアプリ:Android Linhome(ドアの開閉、ビデオによる訪問者の確認、通話)

マイクとスピーカの接続にはI2S(Inter-IC Sound)インターフェイスを利用するため、I2S音声入出力に対応したサウンドデバイス googlevoicehat-soundcard を採用します。( hifiberry-dac はI2Sの音声入力に対応していないため採用しません。)

注)ボタンおよびドアスイッチは接続せず、SSH接続によるリモートからのGPIOピンのステータス確認や操作で代替します。


Yocto Project

バージョン5.0.4 Scarthgap を使用。

Yocto Overview

The Yocto Project ® 5.0.4 documentation

10 Images — The Yocto Project ® 5.0.4 documentation

11 Features — The Yocto Project ® 5.0.4 documentation

ビルドにはビルドツールであるPokyとビルドに必要な レイヤー(meta-xxx) を組入れたDockerコンテナを利用します。

Yoctoで使用されるレシピとは、機能ごとまたは機種ごとに分割されたビルト条件と手順が記述されたファイル群で、これを纏めたものがレイヤーとなります。


Dockerイメージの作成

Linphoneのビルドに必要なレシピとビルド環境を一つのコンテナに纏めます。このためのdockerfileを作成します。

以下、任意で作成したディレクトリ scarthgap-raspberrypi2/ の中での作業とします。

scarthgap-raspberrypi2/dockerfile

FROM debian:11

ARG YOCTO_VERSION=scarthgap
ARG BITBAKE_TARGET=core-image-sato

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get -y upgrade

# Required Packages for the Host Development System
# https://docs.yoctoproject.org/ref-manual/system-requirements.html#required-packages-for-the-build-host
RUN apt-get update && \ 
    apt-get install -y gawk wget git diffstat unzip texinfo gcc build-essential chrpath socat \
    cpio python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping python3-git \
    python3-jinja2 libegl1-mesa libsdl1.2-dev pylint3 xterm python3-subunit mesa-common-dev \
    zstd liblz4-tool screen nano  && \
    apt-get clean

# Additional host packages needed for build
RUN apt-get update && \
    apt-get install -y file && \
    apt-get clean
    
# Additional host packages needed for qemu
RUN apt-get update && \
    apt-get install -y iproute2 && \
    apt-get clean
    
# Additional host packages required by poky/scripts/wic
RUN apt-get update && \
    apt-get install -y curl dosfstools mtools parted syslinux tree zip && \
    apt-get clean

# Create a non-root user that will perform the actual build
RUN id build 2>/dev/null || useradd --uid 30000 --create-home build
RUN apt-get install -y sudo
RUN echo "build ALL=(ALL) NOPASSWD: ALL" | tee -a /etc/sudoers

# Fix error "Please use a locale setting which supports utf-8."
# See https://wiki.yoctoproject.org/wiki/TipsAndTricks/ResolvingLocaleIssues
RUN apt-get install -y locales
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
        echo 'LANG="en_US.UTF-8"'>/etc/default/locale && \
        dpkg-reconfigure --frontend=noninteractive locales && \
        update-locale LANG=en_US.UTF-8

ENV LC_ALL=en_US.UTF-8
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US.UTF-8

USER build
WORKDIR /home/build

# Clone yocto
RUN git clone -b "${YOCTO_VERSION}" git://git.yoctoproject.org/poky
WORKDIR poky
RUN git clone -b "${YOCTO_VERSION}" git://git.openembedded.org/meta-openembedded
RUN git clone -b "${YOCTO_VERSION}" git://git.yoctoproject.org/meta-raspberrypi

WORKDIR /home/build/poky

#RUN bash -c 'source oe-init-build-env && bitbake ${BITBAKE_TARGET}'
CMD ["bash"]

上記dockerfileでイメージ作成

$ docker build scarthgap-raspberrypi2 -t raspberrypi2

ホストとコンテナ間の共有フォルダにbuildを指定し、上記イメージからコンテナraspi2を起動、buildディレクトリのアクセス権を変更します。

$ docker run --name raspi2 -v ./build:/home/build/poky/build -ti raspberrypi2 bash
# sudo chown -R build:build build

linphone-sdkのmetaレイヤーであるmeta-bcのyoctoブランチをダウンロード( /home/build/poky 内)し、meta-bc/conf/layer.conf 内で

LAYERSERIES_COMPAT_bc = “scarthgap”

とします。

# git clone https://gitlab.linphone.org/BC/public/meta-bc.git -b feature/yocto-kirkstone

meta-bc/conf/layer.conf

LAYERSERIES_COMPAT_bc = "scarthgap"

ビルド環境の初期化コマンドを実行します。

# source oe-init-build-env 
You had no conf/local.conf file. This configuration file has therefore been
created for you from /home/build/poky/meta-poky/conf/templates/default/local.conf.sample
You may wish to edit it to, for example, select a different MACHINE (target
hardware).

You had no conf/bblayers.conf file. This configuration file has therefore been
created for you from /home/build/poky/meta-poky/conf/templates/default/bblayers.conf.sample
To add additional metadata layers into your configuration please add entries
to conf/bblayers.conf.

The Yocto Project has extensive documentation about OE including a reference
manual which can be found at:
    https://docs.yoctoproject.org

For more information about OpenEmbedded see the website:
    https://www.openembedded.org/

This is the default build configuration for the Poky reference distribution.

### Shell environment set up for builds. ###

You can now run 'bitbake <target>'

Common targets are:
    core-image-minimal
    core-image-full-cmdline
    core-image-sato
    core-image-weston
    meta-toolchain
    meta-ide-support

You can also run generated qemu images with a command like 'runqemu qemux86-64'.

Other commonly useful commands are:
 - 'devtool' and 'recipetool' handle common recipe tasks
 - 'bitbake-layers' handles common layer tasks
 - 'oe-pkgdata-util' handles common target package tasks

注)# source oe-init-build-env を実行すると自動的にhome/build/poxy/buildディレクトリに移動し、build/confディレクトリに設定ファイルが作成されます。


ビルド設定ファイルの編集

レイヤーリスト(Scarthgap Ver5.0)

レイヤー “meta-raspberrypi” レシピ(マシン)リスト(Scarthgap Ver5.0)

ホストと共有した build/conf ディレクトリに作成された bblayers.conf, local.conf ファイルを編集します。

bblayers.conf : イメージビルドに必要なmetaレイヤーを指定

build/conf/bblayers.conf

# POKY_BBLAYERS_CONF_VERSION is increased each time build/conf/bblayers.conf
# changes incompatibly
POKY_BBLAYERS_CONF_VERSION = "2"

BSPDIR := "${@os.path.abspath(os.path.dirname(d.getVar('FILE', True)) + '/../..')}"

BBPATH = "${TOPDIR}"
BBFILES ?= ""

BBLAYERS ?= " \
  ${BSPDIR}/meta \
  ${BSPDIR}/meta-poky \
  ${BSPDIR}/meta-openembedded/meta-oe \
  ${BSPDIR}/meta-openembedded/meta-networking \
  ${BSPDIR}/meta-openembedded/meta-python \
  ${BSPDIR}/meta-openembedded/meta-multimedia \
  ${BSPDIR}/meta-yocto-bsp \
  ${BSPDIR}/meta-raspberrypi \
  ${BSPDIR}/meta-bc \
  "

local.conf : イメージへの追加レシピや対象機種、ビルド条件などを設定

追加レシピ

IMAGE_INSTALL:append = …

ここでmeta-bcのlinphone-sdkをビルドするためのオプションも追加します。以下参照。

Linphone with Yocto

対象機種

MACHINE = “raspberrypi2”

ビデオ拡張機能

VIDEO_CAMERA = “1”

他の機能も必要であれば、下記 meta-raspberrypi ドキュメントを参照し有効化して下さい。

Optional build configuration

build/conf/local.conf


MACHINE = "raspberrypi2"

VIDEO_CAMERA = "1"

DISTRO ?= "poky"

PACKAGE_CLASSES ?= "package_ipk"

EXTRA_IMAGE_FEATURES ?= "debug-tweaks ssh-server-openssh"

USER_CLASSES ?= "buildstats"

PATCHRESOLVE = "noop"

BB_DISKMON_DIRS ??= "\
    STOPTASKS,${TMPDIR},1G,100K \
    STOPTASKS,${DL_DIR},1G,100K \
    STOPTASKS,${SSTATE_DIR},1G,100K \
    STOPTASKS,/tmp,100M,100K \
    HALT,${TMPDIR},100M,1K \
    HALT,${DL_DIR},100M,1K \
    HALT,${SSTATE_DIR},100M,1K \
    HALT,/tmp,10M,1K"

CONF_VERSION = "2"


# Additional configuration, added recipes of Branch: kirkstone https://layers.openembedded.org/layerindex/branch/kirkstone/recipes
IMAGE_INSTALL:append = " alsa-plugins alsa-utils alsa-lib boost gettext glew libopus libsrtp libvpx pulseaudio v4l-utils raspi-gpio linphone-sdk openssl mpg123"

LICENSE_FLAGS_ACCEPTED = "commercial"
PACKAGE_CLASSES ?= "package_ipk"
PACKAGECONFIG:append:pn-linphone-sdk = " h264 mdns"
PACKAGECONFIG:append:pn-avahi = " libdns_sd"

BB_NUMBER_THREADS ?= "8"
CORE_IMAGE_EXTRA_INSTALL += "dhcpcd"
CORE_IMAGE_EXTRA_INSTALL += "init-ifupdown"

INHERIT += "rm_work"

イメージのビルド:Bitbake

bitbakeコマンドでlinphone(-sdk)のビルドを実行します(ホストマシンのスペックにより、終了するまで数時間を要します)。ここでの単独ビルドを省略しても、linphone-sdkはlocal.confのIMAGE_INSTALL:appendで指定しているので、この作業は最終ビルドイメージ作成時に実行されます。事前に単独ビルドすることで、エラー発生時のリカバリコストを最小限に抑えることができると思います。

BitBake User Manual

# bitbake linphone-sdk

core-image-satoを指定して最終イメージをビルド

# bitbake core-image-sato

ビルドイメージは下記フォルダに出力されます。以下圧縮ファイルを展開

build/tmp/deploy/images/raspberrypi2/core-image-sato-raspberrypi2-2024xxxxxxxxxx.rootfs.wic.bz2

$ bzip2 -d core-image-sato-raspberrypi2-2024xxxxxxxxxx.rootfs.wic.bz2

SDカードへの書込み

$ sudo dd if=core-image-sato-raspberrypi2-2024xxxxxxxxxx.rootfs.wic of=/dev/sda bs=1024

イメージファイルの容量は1-2GBのため、SD容量を有効活用するため、ディスクユーティリティーでSDの最大容量までrootディレクトリの拡張を行って下さい。


googlevoicehatオーバレイの作成

googlevoicehat-soundcard.dtbo は、マイクとDACをGPIOのI2Sインターフェイスで利用するためのデバイスツリーオーバレイですが、デフォルトではコンパイルされていないため、手動でデバイスツリーソースファイルからコンパイルします。

googlevouicehatdts ファイルからオーバレイファイルを手動でコンパイル(raspi2コンテナ内で実行)。

# find . -name "*.dts" | grep googlevoicehat
./build/tmp/work-shared/raspberrypi2/kernel-source/arch/arm/boot/dts/overlays/googlevoicehat-soundcard-overlay.dts
# sudo apt install device-tree-compiler
# dtc -@ -I dts -O dtb -o googlevoicehat-soundcard.dtbo build/tmp/work-shared/raspberrypi2/kernel-source/arch/arm/boot/dts/overlays/googlevoicehat-soundcard-overlay.dts

注) dtc (Device Tree Compiler) は、多くの Linux システムで利用可能なユーティリティで、デバイスツリーソース(DTS)ファイルをコンパイルしてバイナリ形式のデバイスツリーブロブ(DTB/DTBO)ファイルを生成します。

作成されたデバイスツリーオーバレイ googlevoicehat-soundcard-overlay.dtbo を SDカードの /boot/ovelays にコピーします。


config.txt による起動条件

起動時の追加条件を/boot/config.txtで設定します。

https://www.raspberrypi.com/documentation/computers/config_txt.html

ビデオメモリ指定

gpu_mem=128

オーバレイ追加

dtoverlay=googlevoicehat-soundcard


起動・動作確認(ビデオ・サウンド出力)

イメージを書き込んだSDカードをRaspberry Piにセットし起動します。

ビデオ・カメラ動作

カメラ接続確認

# v4l2-ctl --all
Driver Info:
	Driver name      : bcm2835 mmal
	Card type        : mmal service 16.1
	Bus info         : platform:bcm2835_v4l2-0
	Driver version   : 6.6.22
	Capabilities     : 0x85200005
		Video Capture
		Video Overlay
		Read/Write
		Streaming
		Extended Pix Format
		Device Capabilities
.......
.......

テストショット

# v4l2-ctl --stream-mmap --stream-count=1 --stream-to=capture.jpeg

フォーマット確認

# v4l2-ctl --list-formats
ioctl: VIDIOC_ENUM_FMT
	Type: Video Capture

	[0]: 'YU12' (Planar YUV 4:2:0)
	[1]: 'YUYV' (YUYV 4:2:2)
	[2]: 'RGB3' (24-bit RGB 8-8-8)
	[3]: 'JPEG' (JFIF JPEG, compressed)
	[4]: 'H264' (H.264, compressed)
	[5]: 'MJPG' (Motion-JPEG, compressed)
	[6]: 'YVYU' (YVYU 4:2:2)
	[7]: 'VYUY' (VYUY 4:2:2)
	[8]: 'UYVY' (UYVY 4:2:2)
	[9]: 'NV12' (Y/UV 4:2:0)
	[10]: 'BGR3' (24-bit BGR 8-8-8)
	[11]: 'YV12' (Planar YVU 4:2:0)
	[12]: 'NV21' (Y/VU 4:2:0)
	[13]: 'RX24' (32-bit XBGR 8-8-8-8)

ストリームテスト

# v4l2-ctl --stream-mmap --stream-count=10
<<<<<<< 5.55 fps
<<<

サウンド関連

Playback, Captureデバイスの確認

# aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: vc4hdmi [vc4-hdmi], device 0: MAI PCM i2s-hifi-0 [MAI PCM i2s-hifi-0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: sndrpigooglevoi [snd_rpi_googlevoicehat_soundcar], device 0: Google voiceHAT SoundCard HiFi voicehat-hifi-0 [Google voiceHAT SoundCard HiFi voicehat-hifi-0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

# arecord -l
**** List of CAPTURE Hardware Devices ****
card 1: sndrpigooglevoi [snd_rpi_googlevoicehat_soundcar], device 0: Google voiceHAT SoundCard HiFi voicehat-hifi-0 [Google voiceHAT SoundCard HiFi voicehat-hifi-0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

上記出力を受けて、ALSA設定ファイル(dmixer対応 ~/.asoundrc)をここで作成します。

.asoundrc

pcm.!default {
    type asym
    playback.pcm "plug:softvol"
    capture.pcm "plug:capture_level"
}

ctl.!default {
    type hw
    card 1
}

pcm.voicehat_playback {
    type hw
    card 1
    device 0
    format S32_LE
}

pcm.voicehat_capture {
    type hw
    card 1
    device 0
    format S32_LE
}

pcm.softvol {
    type softvol
    slave.pcm "dmixed_playback"
    control.name "Softvol Playback Volume"
    control.card 1
    min_dB -3.0
    max_dB 30.0
}

pcm.dmixed_playback {
    type dmix
    ipc_key 1024
    slave {
        pcm "voicehat_playback"
        period_time 0
        period_size 1024
        buffer_size 8192
        rate 48000
        format S32_LE
    }
    bindings {
        0 0
        1 1
    }
}

pcm.capture_level {
    type softvol
    slave.pcm "voicehat_capture"
    control.name "Level Capture"
    control.card 1
    min_dB -3.0
    max_dB 30.0
}

スピーカテスト

# speaker-test -D plughw:1,0 -c2
# speaker-test -D plughw:1,0 -c2 --test=wav -w /usr/share/sounds/alsa/Front_Center.wav

注) plughw:1,0 はカード1、デバイス0 を指定。~/.asound.conf : card 1 を指定

録音・再生テスト

# arecord -d 5 -f cd -V mono test.wav
# aplay test.wav

音量・マイク感度はalsamixerで調整して下さい。

# alsamixer

Alsamixer


Linphoneの起動

初回起動時に必要なデータベースファイル(ディレクトリ)を予め作成すること。

# cd /opt/belledonne-communications/bin/
# mkdir -p /home/root/.local/share/linphone
# touch /home/root/.local/share/linphone/x3dh.c25519.sqlite3
# touch /home/root/.local/share/linphone/linphone.db

起動

# /opt/belledonne-communications/bin/linphone-daemon
daemon-linphone>

起動オプション

# /opt/belledonne-communications/bin/linphone-daemon --help
daemon-linphone [<options>]
Licence: Commercial
where options are :
	--help                     Print this notice.
	--dump-commands-help       Dump the help of every available commands.
	--dump-commands-html-help  Dump the help of every available commands.
	--pipe <pipepath>          Create an unix server socket in the specified path to receive commands from. For Windows just use a name instead of a path.
	--log <path>               Supply a file where the log will be saved.
	--factory-config <path>    Supply a readonly linphonerc style config file to start with.
	--config <path>            Supply a linphonerc style config file to start with.
	--disable-stats-events     Do not automatically raise RTP statistics events.
	--enable-lsd               Use the linphone sound daemon.
	-C                         Enable video capture.
	-D                         Enable video display.
	--auto-answer              Automatically answer incoming calls.

ビデオキャプチャ、自動応答ON、ログ取得オプションを有効にして起動

# linphone-daemon --config linphonerc -C --auto-answer --log ./linphone_log

Linhoneデーモンヘルプ

daemon-linphone>help
Status: Ok

adaptive-jitter-compensation [audio|video] [enable|disable]
answer <call_id>
audio-codec-disable <payload_type_number>|<mime_type>|ALL
audio-codec-enable <payload_type_number>|<mime_type>|ALL
audio-codec-get <payload_type_number>|<mime_type>
audio-codec-move <payload_type_number>|<mime_type> <index>
......
......

ユーザ認証とその確認

daemon-linphone>register sip:[email protected] sip.linphone.org password
Status: Ok

Id: 1

daemon-linphone>register-status ALL
Status: Ok

Id: 1
State: LinphoneRegistrationOk

呼出

daemon-linphone>call [email protected]

呼び出し元が同一ドメインである場合:@以降省略可

daemon-linphone>call capitalfuse

Linhomeクライアントとの送受信確認

Linhomeの設定は Linhome Free SIP Service へのプロビジョニングによって固定されているため、設定を変更するには、独自のアカウントマネージャを用意し、カスタムURLを含むQRコードを生成して読み込む必要があります。

Assistant

QR

Device List

Connection