音声付きスライド (pptx/ppsx) を1/3に圧縮する

# はじめに

PowerPoint のオーディオ録音機能を用いて作成した音声付き（ナレーション付き）のパワポファイル（pptx や ppsx）は、ファイルサイズが結構大きめになります。このファイルサイズを小さく圧縮するための簡単なツール（スクリプト）を作成します。圧縮後は約1/3のファイルサイズになります。ffmpeg を用います。

Windowsでの使い方は、[この図の流れのように](https://qiita.com/memakura/items/d74baa9b06756d4acc54#%E4%BB%95%E6%A7%98) pptx/ppsxファイル（複数可）をフォルダに入れておき、バッチファイルをダブルクリックです。

[こちらの GitHub](https://hkawash.github.io/lecrec-tools/) に、Windows用のバッチファイルと macOS用の bash のシェルスクリプトをまとめておきます。

## 遠隔オンライン授業

自宅にいながらの遠隔オンライン授業では、リアルタイム型の講義配信だけだと、受け手（学生）と送り手（教員）の通信状況（機器や電波状況、通信プラン、家族の利用時間の重複）、より広域なネットワーク帯域の制約、さらには遠隔会議サービスの提供側のリソースがあり、授業中に配信が不安定になることも考えらます。

そこで、様々な教育機関では、オンデマンド型を併用した授業方法が検討されています。授業資料の配布方法としては、動画を YouTube などのストリーミングサーバーへ上げる、PDF資料や、講義音声付き PowerPoint を学内のLMS (Learning Management System) サーバーや学外のクラウドサービスで共有するなど、様々な方法があります。

しかし、このうち音声付き PowerPoint のファイルサイズは意外と大きく、ストレージサーバーの容量を圧迫したり、アップロードのサイズ制限を受けたりします。ここではこのファイルサイズの圧縮を試みます。

## 音声付き PowerPoint スライド

音声付き PowerPoint は、PowerPoint の元々の機能で作成可能です。たとえば以下のステップで自動再生ファイルができます。[阪大の YouTube](https://youtu.be/2n-4YHGrrlY) が参考になります。

1. 各ページで「挿入 > 録音 > オーディオの録音」を使って録音
2. 各ページで、挿入されたスピーカーアイコンを選択
    - 「再生 > 開始: 自動」を選択
    - 「再生 > スライドショーを実行中にサウンドのアイコンを隠す」にチェック
3. 「スライドショー」の設定
    - ポインタの動きなどもさらに記録するなら「ナレーションの再生」にチェック
    - 自動再生するなら「タイミングを使用」にチェック
    - 「スライドショーの記録」>「先頭から記録」>「記録アイコン」
4. 音声が自動再生されるので、授業の流れでスライドを進めながら記録（オプション）
5. pptx や ppsx（配布用）で保存

1まで終わってから以下の圧縮を行ってもよいですし、5まで終わってからでもOKです。

### 4のステップをスキップして自動再生 pptx や ppsx を作成する場合

- 3のステップでは「タイミングを使用にチェック」
- 全スライドを選択し、「画面切り替え」で、自動切り替えの秒数を設定する
    - 音声があるスライドでは音声の長さと設定秒数のうち長い方のタイミング、音声のないスライドでは設定した秒数で、次のスライドへ移っていくように自動再生されます。

### （ついでに）「ファイル」> 「エクスポート」>「ビデオを作成」で動画ファイルを作成する場合

- 3、4のステップは省略して、「記録されたタイミングとナレーションは使用しない」を選んだとしても、上と同じように音声が再生されながらスライドが自動再生されます。

## 手動で行う場合

pptx や ppsx は zip ファイルですが、その中にある media というフォルダに m4a 形式のオーディオが格納されます。
そのため、手動でやるなら以下の手順になります。ビットレートの変更には ffmpeg を使うことにします。また、オーディオは音声であることを前提として、元は 170kbps 以上あるようですが、これを 64kbps に落としてみます。

1. いったんファイル名の pptx や ppsx を zip に変更します。
2. zipを展開します。
3. ppt/media/以下の各 m4a ファイルのビットレートを、それぞれ ffmpeg で64kbpsに落します： `ffmpeg -i input.m4a -ab 64k output.m4a`
4. 出力ファイルを元のファイル名に変更してファイルを差し替えます。
5. 再度zipで圧縮し、拡張子を pptx や ppsx に戻して完了です。
  - このときzipの直下に _rels や ppt, [Content_Types].xml などができるように注意
  - GUIで行うならファイルエクスプローラーの「右クリック > 新規作成 > 圧縮 (zip形式)フォルダー」でzipを作成し、それを開いて必要なファイルやフォルダをドラッグ＆ドロップ

# 音声付きスライドの圧縮スクリプト

以上の処理を自動で行うために、Windows 用にはバッチファイル、macOS 用には bash のシェルスクリプトを用意してみます。（以下単にスクリプトと呼びます。）

## 仕様

1. スクリプトと同じフォルダに `ppt-in` フォルダがあり、ここにファイルサイズを圧縮したい pptx や ppsx ファイルが入っているとします。
2. ffmpeg はパスが通っているか、スクリプトと同じフォルダに実行ファイルがあるとします。（以下のスクリプトの `PATH` を編集してパスを通してもよいです。）
3. スクリプトを実行すると、`ppt-out` フォルダに圧縮された pptx や ppsx が出力されるようにします。
4. 作業領域として `__work__` を用意し、zip へのリネームや zip ファイルの展開、ffmpeg で変換したファイルの一時的な置き場などに利用します。

フォルダ構成と使用時の流れを以下の図にまとめます。

![usage-win-ja.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55780/c2e8a20f-035b-c8f1-bf55-801ae6bc2aa9.png)

## Windows用バッチファイル

Windows 10 を前提とします。zip の展開と圧縮には powershell を利用します。

```bat:compress_pptaudio-win.bat
@echo off
setlocal enabledelayedexpansion

set PATH=%PATH%;%SYSTEMROOT%\System32
rem set PATH=%PATH%;%SYSTEMROOT%\System32;"<ffmpegのbinフォルダへのパス>"

rem Check ffmpeg
ffmpeg -version
if %ERRORLEVEL% neq 0 (
    echo Error: Cannot find ffmpeg
    pause
    exit /b 1
)

rem ===========================================================
rem Set bitrate here. (Do not insert space around '=')
set BITRATE=64k
rem ===========================================================

set INPUT_DIR=ppt-in
set OUTPUT_DIR=ppt-out
set WORK_DIR=__work__

if not exist %INPUT_DIR% (
    echo Error: Folder [%INPUT_DIR%] does not exist
    pause
    exit /b 1
)

dir %INPUT_DIR%\*pptx %INPUT_DIR%\*ppsx
if %ERRORLEVEL% neq 0 (
    echo Error: No pptx/ppsx files in [%INPUT_DIR%] folder
    pause
    exit /b 1
)

if not exist %WORK_DIR% mkdir %WORK_DIR%
if not exist %OUTPUT_DIR% mkdir %OUTPUT_DIR%

for %%f in (%INPUT_DIR%\*.pptx %INPUT_DIR%\*.ppsx) do (
    rem Setup filenames
    echo %%f
    set pptfname=%%~nxf
    set pptfbase=%%~nf
    set zipfile=%WORK_DIR%\!pptfbase!.zip
    set pptworkdir=%WORK_DIR%\!pptfbase!
    echo !zipfile!
    copy "%%f" "!zipfile!"

    rem Create folder for pptx content
    if exist "!pptworkdir!" rd /s /q "!pptworkdir!"
    mkdir "!pptworkdir!"

    rem Expand zip file
    powershell -Command Expand-Archive -Path '!zipfile!' -DestinationPath '!pptworkdir!'
    if not exist "!pptworkdir!"\ppt\media (
        echo Error: No media folder in pptx/ppsx
        pause
        exit /b 1
    )

    rem Compress audio files
    for %%a in ("!pptworkdir!"\ppt\media\*.m4a) do (
        set m4afname=%%~nxa
        ffmpeg -i "%%a" -ab %BITRATE% "%WORK_DIR%\!m4afname!"
        move /y "%WORK_DIR%\!m4afname!" "%%a"
    )

    rem Archive again
    powershell -Command Compress-Archive -Path '!pptworkdir!\*' -DestinationPath '%OUTPUT_DIR%\!pptfbase!.zip'
    move /y "%OUTPUT_DIR%\!pptfbase!.zip" "%OUTPUT_DIR%\!pptfname!"

    rem Remove temoporary files/folders
    del "!zipfile!"
    rd /s /q "!pptworkdir!"
)
```

外側の for ループでは各 pptx/ppsx ファイルについて、内側の for ループではスライド内の各m4aファイルについて回し、すべての m4a ファイルのビットレートを 64kbps に変換しています。

ファイルやフォルダ名に半角スペースが入るかもしれないので、適宜ダブルクォートで囲っていますが、powershell への引数はシングルクォートで囲む必要があるようです。

## macOS 用シェルスクリプト

bash を利用します。zip と unzip が入っていることを前提にしています。macOS 用と書いてありますが、Windows の bash や Linux でも使えると思います（ただし Windows なら `FFMPEG=./ffmpeg` を `FFMPEG=./ffmpeg.exe` とする必要があります）。

```bash:compress_pptaudio-mac.sh
#!/bin/bash

# PATH="$PATH":"<ffmpegのbinフォルダへのパス>"

FFMPEG=./ffmpeg  # windows なら .exe を付ける

# Check commands
if ! type $FFMPEG > /dev/null 2>&1; then
    echo "Error: Cannot find ffmpeg"
    exit 1
fi

if ! type zip > /dev/null 2>&1; then
    echo "Error: Install [zip] before using this script"
    exit 1
fi
if ! type unzip > /dev/null 2>&1; then
    echo "Error: Install [unzip] before using this script"
    exit 1
fi

# ===========================================================
# Set bitrate here. (Do not insert space around '=')
BITRATE=64k
# ===========================================================

INPUT_DIR=ppt-in
OUTPUT_DIR=ppt-out
WORK_DIR=__work__

if [ ! -d $INPUT_DIR ]; then
    echo "Error: Folder [$INPUT_DIR] does not exist"
    exit 1
fi

ls $INPUT_DIR/*.pp[ts]x
if [ $? -ne 0 ]; then
    echo "Error: No pptx/ppsx files in [$INPUT_DIR] folder"
    exit 1
fi

if [ ! -d $WORK_DIR ]; then
    mkdir -p $WORK_DIR
fi
if [ ! -d $OUTPUT_DIR ]; then
    mkdir -p $OUTPUT_DIR
fi

for f in $INPUT_DIR/*.pp[ts]x; do
    # Setup filenames
    echo "$f"
    pptfname="${f##*/}"
    pptfbase="${pptfname%.*}"
    zipfile=$WORK_DIR/$pptfbase.zip
    pptworkdir=$WORK_DIR/$pptfbase
    echo "$zipfile"
    echo "$pptworkdir"
    cp "$f" "$zipfile"

    # Create folder for pptx content
    if [ -d "$pptworkdir" ]; then
      rm -rf "$pptworkdir"
    fi
    mkdir "$pptworkdir"

    # Expand zip file
    unzip "$zipfile" -d "$pptworkdir"
    if [ ! -d "$pptworkdir"/ppt/media ]; then
        echo "Error: No media folder in pptx/ppsx"
        exit 1
    fi

    # Compress audio files
    for a in "$pptworkdir"/ppt/media/*.m4a; do
        m4afname="${a##*/}"
        $FFMPEG -i "$a" -ab $BITRATE "$WORK_DIR/$m4afname"
        mv "$WORK_DIR/$m4afname" "$a"
    done

    # Archive again
    (cd "$pptworkdir" && zip -9 -r "../../$OUTPUT_DIR/$pptfbase.zip" *)
    mv "$OUTPUT_DIR/$pptfbase.zip" "$OUTPUT_DIR/$pptfname"

    # Remove temoporary files/folders
    rm "$zipfile"
    rm -rf "$pptworkdir"
done
```

こちらも Windows 用のバッチファイルとまったく同じ構造です。ファイル名やフォルダ名に半角スペースが入るかもしれないので、適宜ダブルクォートで囲っています。

約2分の音声付きスライドだと 3.33MB が 1.16MB になりました。（インストールされているエンコーダーにもよるかも。）（別の環境で試していただいたところ、22MB は 7.5MB ということで、約1/3ぐらいになりそうです。）

以上をまとめたものを、[こちらの GitHub](https://hkawash.github.io/lecrec-tools/) のページに置いておきます。

もう少し ffmpeg のパラメータをいろいろと検討してもよいしれません。