みつきんのメモ

組み込みエンジニアです。Interface誌で「My オリジナルLinuxの作り方」連載中

ざっくりとALSAとPulseAudioの関係(の続きのようなもの)

はじめに

以前に書いた2015-04-04 ざっくりとALSAとPulseAudioの関係が未だにアクセスがあるようなのでちょっと振り返ってみる。

基本的な理解としては、

  1. 最終的に音を鳴らすのはデバイス
  2. バイスを叩くためにデバイスドライバ(カーネルランド)
  3. サウンド関連でカーネルランドとユーザーランド橋渡しをする部分を取りまとめているのがALSA
  4. 各アプリケーション間でサウンド関連のリソースを調停するのがPulseAudioなどのサウンドサーバ

これは変わっていない。

先述の記事では次のように書いた。

それを回避するためにPulseAudioなどのサウンドサーバはALSAのデバイスのふりをして、ALSAAPIを直接叩いているアプリケーションのサウンドリソースを横取りしてしまう。 そうして、管理下に置いたあと本物のALSAに音を流す。

ここで言う本物のALSAとは、カーネルランドにいるALSAのドライバフレームワークを主に示している。

実験環境

確認にはラズベリーパイ3 Model Bを使用。 OSはYocto Projectで作成したものを使用。(warrior/2.7)

ホストPCの環境はUbuntu 18.04。

ほんとに横取りしているのか

これを簡単に確認するにはPulseAudioが動く環境でaplayを実行してみるのがわかりやすい。

Yocto環境の設定

aplayとPulseAudioが動く環境を作るためにlocal.confに次の行を追加する。

# userland ALSA
IMAGE_INSTALL_append = " alsa-utils"

# pulseaudio
IMAGE_INSTALL_append = " pulseaudio-server"

# Enable onboard audio
RPI_EXTRA_CONFIG = ' \n \
# Enable audio (loads snd_bcm2835) \n \
dtparam=audio=on \n \
'

RPI_EXTRA_CONFIGでは、オンボードサウンドバイスを有効化している。

bitbakeでcore-image-baseを作成し、そのイメージでラズベリーパイを起動する。

横取りの確認

sshなどを使用して、ラズベリーパイのターミナルを2つ立ち上げる。

片方でpulseaudioを起動する。

$ pulseaudio --verbose

そしてもう片方でaplayを実行する。

$ aplay /usr/share/sounds/alsa/Front_Left.wav

PulseAudioのターミナルの表示が次のようになり、aplayを実行することでPulseAudioが動作していることがわかる。

I: [pulseaudio] main.c: Daemon startup complete.
I: [pulseaudio] module-suspend-on-idle.c: Sink alsa_output.platform-soc_audio.analog-mono idle for too long, suspending ...
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Device suspended...
I: [pulseaudio] client.c: Created 0 "Native client (UNIX socket client)"
I: [pulseaudio] protocol-native.c: Got credentials: uid=0 gid=0 success=1
I: [pulseaudio] sink-input.c: Trying to change sample rate
I: [pulseaudio] alsa-sink.c: Updating rate for device hw:0, new rate is 48000
I: [pulseaudio] source.c: Changed sampling rate successfully
I: [pulseaudio] sink.c: Changed format successfully
I: [pulseaudio] sink-input.c: Rate changed to 48000 Hz
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Trying resume...
I: [alsa-sink-bcm2835 ALSA] alsa-util.c: Cannot disable ALSA period wakeups
I: [alsa-sink-bcm2835 ALSA] alsa-util.c: ALSA period wakeups were not disabled
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Time scheduling watermark is 36.75ms
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Resumed successfully...
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Starting playback.
I: [pulseaudio] sink-input.c: Created input 0 "ALSA Playback" on alsa_output.platform-soc_audio.analog-mono with sample spec s16le 1ch 48000Hz and channel map mono
I: [pulseaudio] sink-input.c:     media.name = "ALSA Playback"
I: [pulseaudio] sink-input.c:     application.name = "ALSA plug-in [aplay]"
I: [pulseaudio] sink-input.c:     native-protocol.peer = "UNIX socket client"
I: [pulseaudio] sink-input.c:     native-protocol.version = "32"
I: [pulseaudio] sink-input.c:     application.process.id = "321"
I: [pulseaudio] sink-input.c:     application.process.user = "root"
I: [pulseaudio] sink-input.c:     application.process.host = "raspberrypi3"
I: [pulseaudio] sink-input.c:     application.process.binary = "aplay"
I: [pulseaudio] sink-input.c:     application.language = "C"
I: [pulseaudio] sink-input.c:     application.process.machine_id = "e08cd0ccf41446ecb446dbd2a097d362"
I: [pulseaudio] sink-input.c:     application.process.session_id = "c1"
I: [pulseaudio] sink-input.c:     module-stream-restore.id = "sink-input-by-application-name:ALSA plug-in [aplay]"
I: [pulseaudio] protocol-native.c: Requested tlength=500.00 ms, minreq=125.00 ms
I: [pulseaudio] protocol-native.c: Final latency 625.00 ms = 250.00 ms + 2*125.00 ms + 125.00 ms
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Tried rewind, but was apparently not possible.
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Tried rewind, but was apparently not possible.
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Tried rewind, but was apparently not possible.
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Tried rewind, but was apparently not possible.
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Tried rewind, but was apparently not possible.
I: [pulseaudio] sink-input.c: Freeing input 0 "ALSA Playback"
I: [pulseaudio] client.c: Freed 0 "ALSA plug-in [aplay]"
I: [pulseaudio] protocol-native.c: Connection died.
I: [pulseaudio] module-suspend-on-idle.c: Sink alsa_output.platform-soc_audio.analog-mono idle for too long, suspending ...
I: [alsa-sink-bcm2835 ALSA] alsa-sink.c: Device suspended...

PulseAudioはフォアグラウンドで実行している時、一定時間クライアントから接続がないと終了してしまうので注意が必要。

callgrindで見てみる

実際のところ、どのようになっているのかをcallgrindで見てみる。

$ valgrind --tool=callgrind aplay /usr/share/sounds/alsa/Front_Left.wav

で作成したコールグラフをホストPCのkcachegrindで開く。

画面左上のリストからaplayを選択し、左下のリストでmainを選択する。

画面右下の方にあるCall Graphのタブをクリックしコールグラフを開く。

するとaplayのメイン関数に注目したコールグラフが表示される。

しかし肝心な部分が<cycle 3>としてまとめられてしまっていて細かいことを読み取ることができない。

f:id:mickey_happygolucky:20190830124314p:plain
aplayのコールグラフ

ツールバーにあるCycle Detectionをクリックし、cycle検出機能をオフにすると、<cycle 3>の内容が展開される。

f:id:mickey_happygolucky:20190830124323p:plain
<cycle 3>の展開

main'2からsnd_pcm_openが呼び出されていることがわかる。

コールグラフ中の関数をダブルクリックすると、注目する関数を切り替えることができる。その時に注目している関数がどのライブラリ(ELF Object)に属しているかは画面左上のリストの選択状態でわかる。

このようにして関数を調べていくと、ALSA APIのインターフェースであるlibasound.so.2.0.0ioplug_priv_transfer_areas関数からPulseAudioのモジュールであるlibasound_module_pcm_pulse.sopulse_start関数が呼び出されていることがわかった。

f:id:mickey_happygolucky:20190830124337p:plain
libasoundからPulseAudioの呼び出し

つまり、アプリケーションからALSAAPIを呼び出すと内部からpulseaudioのモジュールが呼び出されているということになる。

まとめ

aplayやcallgrindをつかってユーザー空間でのALSA APIの挙動を調べたところ、PulseAudioがALSAの処理を横取りしていることが確認できた。

f:id:mickey_happygolucky:20150404105017p:plain
横取りの図式

gstreamerでalsasinkを使う場合などでもcallgrindでみると同じようになっていることが確認できる。

PulseAudioからカーネルALSAフレームワークへの流れはまた機会があれば。

Yocto環境 callgrindでコールグラフを作成する

はじめに

Linuxのユーザー空間のアプリをデバッグする際に、関数が実際にどのように呼び出されているのかを見たいことがある。 valgrindにはcallgrindというツールがあり、これを使うとコールグラフを生成することができる。

ターゲットにRPi3 Model Bを使用して、warrior(2.7)環境を構築する。 ホスト環境はUbuntu 18.04を使用する。

吐き出したコールグラフのデータはkcachegrindというツールで可視化することができる。 実機環境はヘッドレスで運用することとして、kcachegrindはホストPC側で使用する。

kcachegrindのインストール

Ubuntu環境にkcachegrindをインストールする。

$ sudo apt install valgrind kcachegrind

余談だが、kcachegrindの"k"はkernelではなくてKDEの”k”らしい。

環境構築

Yoctoの環境を構築する。

ソース取得

$ mkdir -p rpi-warrior/layers
$ cd rpi-warrior/layers
$ git clone git://git.yoctoproject.org/poky.git -b warrior
$ git clone git://git.yoctoproject.org/meta-raspberrypi -b warrior
$ git clone git://git.openembedded.org/meta-openembedded -b warrior
$ cd ../

環境変数設定

$ source layers/poky/oe-init-build-env build

自動的にビルドディレクトリに移動される。 これで、bitbake関連のツールが使用可能になる。

レイヤ追加

$ bitbake-layers add-layer ../layers/meta-openembedded/meta-oe
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-python
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-networking
$ bitbake-layers add-layer ../layers/meta-raspberrypi/

local.conf

RPi3向けの基本的な設定。

MACHINE = "raspberrypi3"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES_append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

# network
IMAGE_INSTALL_append = " connman-client"
EXTRA_IMAGE_FEATURES += "ssh-server-openssh"

valgrindのための設定

local.confに次の設定を追加する。

# valgrind
IMAGE_INSTALL_append = " valgrind"
EXTRA_IMAGE_FEATURES += "dbg-pkgs"

実行環境にデバッグシンボルをインストールするためにIMAGE_FEATURESにdbg-pkgsを追加する。 これをしないと、コールグラフ中の関数名が16進数の文字列になってしまう。

ちなみにvalgrindは次のようにしても追加できる。

#  "tools-profile"  - add profiling tools (oprofile, lttng, valgrind)
EXTRA_IMAGE_FEATURES += "tools-profile"

ビルド

とりあえず最小構成をビルドする。

$ bitbake core-image-base

出来上がったイメージをddなどでSDカードに書き込む。

sudo dd if=tmp/deploy/images/raspberrypi3/core-image-base-raspberrypi3.rpi-sdimg of=/dev/sdX bs=100M

sdXは環境によりsdbやsdcなどになる。

lsのコールグラフ

lsコマンドのコールグラフを作ってみる。

$ valgrind --tool=callgrind ls

callgrind.out.xxxというファイルが生成される。xxxはプロセスIDが入る。

これをホストPCにコピーして次のようにする。

$ kcachegrind callgrind.out.xxx

f:id:mickey_happygolucky:20190830002744p:plain
kcachegrind

まとめ

Yoctoの環境でもvalgrindを使うことができる。

その時にIMAGE_FEATURESにdbg-pkgsを入れておくと、デバッグ時のシンボルを解決できる。 また、src-pkgsも一緒に入れておくと、gdbなどでのソースレベルデバッグも可能になる。

Yocto環境ではIMAGE_FEATURESにフラグを指定することで、デバッグシンボルの追加をすべてのパッケージに対し行うことができたりするので便利。

straceやgdbをターゲット上で使用したい場合は、IMAGE_FEATURESにtools-debugを追加できる。 このあたりは、local.confのExtra image configuration defaultsにコメントとして記載されているので参考にすると良い。

多くの場合ターゲットはヘッドレスで運用することが想定されるので、valgrindの解析結果のファイルはホストPC側で可視化するとよい。

ラズベリーパイ3 warriorのglimagesinkがエラー

はじめに

warrior(2.7)でcore-image-baseを作ってgstreamerで遊ぼうとしたら、次のコマンドでエラーが発生した。

$ export GST_GL_WINDOW=dispmanx ; export GST_GL_API=gles2 ; export GST_GL_PLATFORM=egl
$ gst-launch-1.0 v4l2src device=/dev/video0 ! videobox ! capsfilter caps=video/x-raw,format=AYUV,width=640,height=480,framerate=30/1 !  glimagesink

エラーの内容は次の通り。

ERROR: Pipeline doesn't want to pause.
Got context from element 'sink': gst.gl.GLDisplay=context, gst.gl.GLDisplay=(GstGLDisplay)"\(GstGLDisplayEGL\)\ gldisplayegl0";
ERROR: from element /GstPipeline:pipeline0/GstGLImageSinkBin:glimagesinkbin0/GstGLImageSink:sink: Failed to initialize egl: EGL_NOT_INITIALIZED

EGLの初期化ができていないらしい。

このコマンドはインターフェース 2019年9月号に掲載された第30回 ラズパイ早送りタイムラプス動画カメラで書かせてもらった時に、thud(2.6)で動作確認しているため、 コマンドライン自体に問題はないはず。(リスト5)

ビルド環境の確認

EGL関連で少し思い当たるフシがあるためビルド環境の環境変数を確認。

thud

$ bitbake core-image-base -e | grep '^MACHINE_FEATURES='
MACHINE_FEATURES=" apm usbhost keyboard vfat ext2 screen touchscreen alsa bluetooth wifi sdio qemu-usermode"

warrior

$ bitbake core-image-base -e | grep '^MACHINE_FEATURES='
MACHINE_FEATURES=" apm usbhost keyboard vfat ext2 screen touchscreen alsa bluetooth wifi sdio vc4graphics qemu-usermode"

やはり、デフォルトでvc4graphicsが有効化されている。

これはX環境でもOpenGLが使えるようにするドライバを有効にするためのフラグで、オンになっているとドライバがOpenGLのリソースを先に取ってしまうようになる。

そのため、直接EGLを叩くglimagesinkのようなものを動かそうとすると、リソースが取れずに初期化が失敗する。

これを無効化するにはlocal.confに次の行を追加する。

MACHINE_FEATURES_remove = "vc4graphics"

これで環境変数を再度確認する。

$ bitbake core-image-base -e | grep '^MACHINE_FEATURES='
MACHINE_FEATURES=" apm usbhost keyboard vfat ext2 screen touchscreen alsa bluetooth wifi sdio  qemu-usermode"

すでにビルドしてしまった状態のビルドディレクトリでbitbakeを実行するとファイルが競合するため、一度tmp以下をすべて削除する。

$ rm -rf ./sstate-cache/ tmp/ cache/ bitbake-cookerdaemon.log

-c cleansstateだとゴミが残るようだし-c cleanallだとダウンロード済みのソースまで消えてしまうので、あえて手動で消している。(これもっとスマートに解消できる方法ないかなぁ。。。)

まとめ

meta-raspberrypiのwarriorからは、MACHINE_FEATURESにvc4graphicsがデフォルトで入るようになっているので注意が必要。

Emacs shell-script-modeのフック

はじめに

(を入力すると対応する)を自動で入力してくれるsmartparens-modeシェルスクリプトを書く時にも便利なので、init.elでshell-script-modeにadd-hookしたいと思ったが、 少しハマった。

shell-script-modeのフック

最初は次のようにhookを追加しようとした。

(add-hook 'shell-script--mode-hook 'smartparens-mode)

しかし、シェルスクリプトファイルを開いても自動的にsmartparens-modeは有効化しない。

webで調べた結果次のようなことがわかった。

shell-script-mode is an alias for sh-mode. I haven't checked, but I would suspect that only the hook variable for the 'real' function name is evaluated, so I think sh-mode-hook would be the one to use.

つまりshell-script-modesh-modeエイリアスで、正式なフックはsh-mode-hookとなるはずだ。と。

(add-hook 'sh-mode-hook 'smartparens-mode)

これで試したところ、フックが効くようになった。

まとめ

shell-script-modeにフックを追加したい場合はsh-mode-hookを使う。

DE0-Nano-SoCをyocto(warrior)で動かす

はじめに

Cyclone-Vのカスタムボードを動かす必要に迫られたが、Cyclone-Vでの環境構築が明るくない上に、 ボードがうまく動かなかったため勢いに任せて既製品のボードを買ってしまった。

カスタムボードが動かなかったのは結局のところハードウェア不良が原因だったらしく、 ソフトウェアの環境構築手順には問題はなかったらしい。

ただ、Cyclone-V自体が息の長い製品らしく、古い情報を参考にしても無駄が多かったので、 最近のyoctoprojectでの環境構築手順をまとめる。

昔の手順

古い情報を参考にすると次のような手順を踏む必要があるとなっている。

yoctoで環境構築を下としても次のような手順が必要だったらしい。

  1. preloaderの作成
  2. u-bootの作成
  3. kernelの作成
  4. rootfs(core-imgae-minimal)の作成

preloaderはyoctoでは作成できないためSoC EDSを使用する必要があった。

最近は、u-bootのSPLを使用することでpreloaderを使用せずに起動することができるので、SoC EDSなどのインストールも含めて手順を省略できる。

warrior(yocto 2.7)環境の構築

meta-alteraはwarriorブランチが作成されていないが、LAYERSERIES_COMPAT_meta-alteraがwarriorに設定されているため、 masterブランチががwarrior相当となっている。

$ mkdir -p de0-nano-soc/layers
$ cd de0-nano-soc/layers
$ git clone git://git.yoctoproject.org/poky -b warrior
$ git clone https://github.com/kraj/meta-altera.git
$ git clone git://git.linaro.org/openembedded/meta-linaro.git -b warrior

環境変数の読み込み

bitbakeが実行できるように環境変数の設定を行う。

$ cd ../
$ source ./layers/poky/oe-init-build-env

コマンド実行後はbuildディレクトリに自動的に移動される。

ビルド対象へのレイヤの追加

bitbakeの実行対象にレイヤを追加する。

$ bitbake-layers add-layer ../layers/meta-altera
$ bitbake-layers add-layer ../layers/meta-linaro/meta-linaro-toolchain

local.confの修正

次の行をlocal.confに追加する。

MACHINE = "cyclone5"
UBOOT_CONFIG = "de0-nano-soc"

カーネルコンパイルエラー

dtsのファイル名が変更されたようで、カーネルのビルド中に次のようなエラーが発生する。

|   Using /home/mickey/work/yocto/de0-nano-soc/build/tmp/work-shared/cyclone5/kernel-source as source for kernel
| make[3]: *** No rule to make target 'arch/arm/boot/dts/socfpga_cyclone5_de0_sockit.dtb'.  Stop.
| /home/mickey/work/yocto/de0-nano-soc/build/tmp/work-shared/cyclone5/kernel-source/Makefile:1224: recipe for target 'socfpga_cyclone5_de0_sockit.dtb' failed
| make[2]: *** [socfpga_cyclone5_de0_sockit.dtb] Error 2
| Makefile:169: recipe for target 'sub-make' failed
| make[1]: *** [sub-make] Error 2
| ERROR: oe_runmake failed
| Makefile:15: recipe for target '__sub-make' failed
| make: *** [__sub-make] Error 2
| WARNING: exit code 1 from a shell command.
| ERROR: Function failed: do_compile (log file is located at /home/mickey/work/yocto/de0-nano-soc/build/tmp/work/cyclone5-poky-linux-gnueabi/linux-altera/5.1+gitAUTOINC+a64da520ac-r0/temp/log.do_compile.5813)

これを回避するためにlocal.confに次の行を追加する。

KERNEL_DEVICETREE = "\
            socfpga_cyclone5_socdk.dtb \
            socfpga_cyclone5_sockit.dtb \
            socfpga_cyclone5_socrates.dtb \
            socfpga_cyclone5_de0_nano_soc.dtb \
            socfpga_cyclone5_mcvevk.dtb \
            socfpga_cyclone5_sodia.dtb \
            socfpga_cyclone5_trcom.dtb \
            socfpga_cyclone5_vining_fpga.dtb \
            "

u-bootのデバイスツリーの設定

extlinux.confのFDTはUBOOT_EXTLINUX_FDT_defaultによって設定されるらしい。 local.confに次の行を追加する。

UBOOT_EXTLINUX_FDT_default = "../socfpga_cyclone5_de0_nano_soc.dtb"

ビルド

bitbakeを実行しイメージを作成する。

$ bitbake core-image-base

tmp/deploy/images/cyclone5にwicイメージが生成される。

ddコマンドなどでSDカードにwicイメージを書き込むことでブート用のSDイメージができる。

起動

UARTのUSBケーブルを接続しSDカードを挿入したあと、電源を投入する。

次のメッセージが表示された後ログが止まるので、KEY4(ボタン)を押す。

U-Boot SPL 2019.01 (Jul 16 2019 - 06:23:35 +0000)
Trying to boot from MMC1


U-Boot 2019.01 (Jul 16 2019 - 06:23:35 +0000)

CPU:   Altera SoCFPGA Platform
FPGA:  Altera Cyclone V, SE/A4 or SX/C4, version 0x0
BOOT:  SD/MMC Internal Transceiver (3.0V)
DRAM:  1 GiB

するとu-bootからカーネルがキックされ、ログイン画面まで進む。

Poky (Yocto Project Reference Distro) 2.7.1 cyclone5 /dev/ttyS0

cyclone5 login:

まとめ

最近のyoctoではwicイメージをSDにddするだけでブートできる。

de0-nano-soc向けのDTSのファイル名が変更されているのでこれの対応をしておかないとカーネルのビルドに失敗するので注意が必要。

起動時にKEY4を押さないとブートが止まってしまうのがちょっと気になる。

gitbook-templateを試す(その3)

はじめに

前回のつづき。

Dockerベースのgitbook-templateを使用していて、book.jsonでtitleに日本語を使用するとPDFに変換した時に文字化けするので調べた。

コンテナのロケール

おそらくコンテナのロケールがデフォルトのままであるため日本語が使用できないのだろうと、 コンテナのロケールを日本語に設定する方法を調べたところ、こんな記事を見つけた。

これを参考にDockerfileを次のように修正したみた。

diff --git a/Dockerfile b/Dockerfile
index 1e40043..41555f4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -4,6 +4,7 @@ LABEL maintainer="HeRoMo"
 # install apt packages
 RUN apt-get update -y && \
     apt-get install -y \
+      locales \
       bzip2 \
       calibre \
       fonts-ricty-diminished && \
@@ -19,4 +20,9 @@ RUN chmod 755 /usr/bin/start.sh
 RUN mkdir -p /opt/gitbook
 COPY ./book.json /opt/gitbook
 
+RUN locale-gen ja_JP.UTF-8
+ENV LANG ja_JP.UTF-8
+ENV LC_CTYPE ja_JP.UTF-8
+RUN localedef -f UTF-8 -i ja_JP ja_JP.utf8
+
 ENTRYPOINT ["start.sh"]

コンテナのビルドと確認

docker-compose.ymlがあるディレクトリで次のコマンドを実行する。

$ docker-compose build
$ docker-compose run --rm gitbook pdf

文字化けが直った。

まとめ

Dockerを使っていて日本語でに文字化けするようなら、コンテナのロケールを確認すると良い。

gitbook-templateを試す(その2)

はじめに

前回のつづき。

PDF出力時の日本語フォントなど、細かいところは調整が難しい感じ。

と書いたが、Dockerfileをおいてどうにかできないか試してみた。

作業環境は前回のものをそのまま引き継ぐ。

Dockerfileのリポジトリ

オリジナルのDockerfileはHeRoMo/docker-gitbookで管理されている。

これをローカルの環境に取得して、コンテナに手を入れていきたい。

docker-compose.ymlがあるディレクトリで次のコマンドを実行する。ここでは適当にブランチを切っている。

$ git clone https://github.com/HeRoMo/docker-gitbook.git
$ cd docker-gitbook
$ git checkout -b mybook

そしてdocker-compose.ymlを次のように編集する。

--- docker-compose.yml~  2019-06-19 15:19:21.237108274 +0900
+++ docker-compose.yml    2019-06-28 16:40:38.619619404 +0900
@@ -4,7 +4,7 @@
     image: 'bitjourney/plantuml-service:1.3.3'
 
   gitbook:
-    image: hero/gitbook:latest
+    build: ./docker-gitbook
     volumes:
       - ./gitbook:/gitbook
       - plugins:/gitbook/node_modules

これでローカルのDockerfileを参照してイメージがビルドできるようになる。

$ docker-compose build

フォントを追加してみる

Dockerfileを編集してフォントをイメージに追加してみる。

diff --git a/Dockerfile b/Dockerfile
index fae376d..1e40043 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -5,7 +5,8 @@ LABEL maintainer="HeRoMo"
 RUN apt-get update -y && \
     apt-get install -y \
       bzip2 \
-      calibre && \
+      calibre \
+      fonts-ricty-diminished && \
     apt-get autoremove -y && \
     apt-get clean && \
     rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/*

ここではfonts-ricty-diminishedを追加している。

gitbook/book.jsonを修正してPDF生成時に使用するフォントを設定してみる。

--- book.json~   2019-06-28 17:17:47.862386849 +0900
+++ book.json 2019-06-28 17:17:26.694060803 +0900
@@ -21,6 +21,7 @@
     }
   },
   "pdf": {
+    "fontFamily":"Ricty Diminished",
     "paperSize": "a4"
   },
   "language": "ja"

次のコマンドでPDFを生成する。

docker-compose run --rm gitbook pdf

ドキュメントビューアーで確認すると次のようになっている。

f:id:mickey_happygolucky:20190628172440p:plain
PDFのフォント情報

まとめ

ローカルにDockerfileを配置して、コンテナを自由に改変できるようになった。 フォントの埋め込みなどの、オリジナルのままではちょっと不便だと思うこともなんとかなりそう。