Raspberry Pi3 B+でLED電光掲示板に動画を流してみる

※完全に備忘録なので参考にならないと思います…(多分間違ってます)
あんまり鵜呑みにしないで下さい(私も正直良く分かりません)

Raspberry Pi 3 Model B+でrpi-rgb-led-matrixというライブラリを使用してLED電光掲示板(LEDマトリクス)に動画(映像)とか流して街のデジタルサイネージ(?)みたいにしてみようという備忘録諸々です。

今回は64×64のLEDパネルを2つ購入し、128×64の解像度のLEDマトリクス再生環境を構築します。(こちらの記事とこちらのサイトをとても参考にさせて頂いています、ありがとうございます)
再生には rpi-rgb-led-matrix というフリーのライブラリを使い、ソフトを自作(改造)して再生させています。

必要なもの

※抜けてるものがあるかもしれません

  • Raspberry Pi 3 Model B+
    • 新しい機種出てたら絶対そっちの方がいいと思います
    • 別途起動に5V電源が必要です(私はiPhone用の充電器で代用していますが、普通に「ラズパイ用」とかで売られてる奴を買っても良いと思います(iPhone用でも動くというだけです・ラズパイ用のUSB電源を2つ買って1つをLED給電用に回すのもありかも))
    • あと、基本的に(セットを除き)上述のUSB電源やUSBケーブルは入って来ないので、(あると思いますが)ない場合はUSB-MicroUSBのいつものケーブルを買っておきましょう

  • LEDパネル(64×64のパネル(32S)を2個)
    • 他の解像度の場合、配線が違ってくる上に動画再生する場合ピクセルが少なすぎる印象なので、これくらいをお勧めします
    • 基本的に何故か日本国内では売ってない(売ってても非常に高い)ので、安定のChinaから注文します
    • 私はAliExpress(アリババグループの中国の電子部品メーカーが個人向けに卸してるサイト)のこちらの製品を購入しました(これが一番お安いし評価も高くて良いと思います・必ず32Sを注文してください、16Sのタイプだと64×32のパネルをくっつけているのと変わりません)
    • 他にもAliExpressで p3 64*64 とかで検索してみるといいかもです
    • どうやらLEDパネルはHUB75という業界規格に基づいて作られてるらしく、よっぽどの物でなければ別の製品でも同じような配線・ケーブルで使えるみたいです(参考・LEDドットマトリクスパネル HUB75規格について調べてみた)
    • フラットケーブル(パネル間・パネル-ラズパイ間の接続ケーブル)と後述の電源ケーブルは殆どの製品が付属します(私の製品も付属していました・2個一緒に買うと充電ケーブルをLEDパネル2個用の充電ケーブルを添付してくれました)
    • 中国郵便で注文した所2週間以上かかったので(そもそも発送までが遅い)、出来るだけ余裕のある注文をお勧めします


ぐちゃぐちゃしていて見づらいですが、下の変換アダプタ噛ませてるケーブルがLEDパネル給電用ケーブル、奥のラズパイに接続しているケーブルがラズパイ給電用ケーブル、ラズパイからLEDパネルの方に伸びている沢山のジャンパーワイヤがLEDパネルデータ送信用ケーブルになります。

  • LED給電用
    •  USB電源(5V3A以上)
      • …と言いつつ、私は面倒でiPad用の5.1V2.1Aの充電器を使っていますが、電流が足りないとLED全体の画面を真っ白にした際にLEDを全て光らせるだけの電力を確保できず、
        電力を食う緑・青のLEDの輝きが弱まりオレンジっぽくなる現象が起こるので、つよつよな方は市販のUSB電源(そもそも5V3A以上はほとんど出回ってない)ではなくスイッチング電源かなんかを使うのが良いのでは(わからん)
    • USB-DC電源ケーブル
      • 色々ありますが、私はAmazonで売ってたこちらの製品を使っています(粗悪品もあるみたいなので注意です)
    • DCジャック-スクリュー端子台変換コネクタ
      • 私は秋月電子のこちらの製品を使っています
    • 流れとしてはUSB電源→USB-DC電源ケーブル→DCジャック-スクリュー端子台変換コネクタ→電源ケーブル(付属してるはず)という感じになるはずです
  • ラズパイ-LED接続用ジャンパーワイヤ(オス-メス):最低16本以上
    • 私は秋月電子のこちらの製品を使っています(16本以上なのでこの10本セットの奴の場合は2個買えばおkのはず)
    • ただし、ケーブルが少し安っぽいせいか、接触不良で何度か抜き差ししたら何本か壊れてしまったりしたので、1個余分に買っておくことをお勧めします…
    • 後、なんだかんだ使えるのでオス-オスの方(私はこちらの(ry)も買っておいた方がいいと思います(小並感)
  • スピーカー
    • 同時に音も鳴らせるので、適当なスピーカー(イヤホンジャックから出力できるもの)も買っておきましょう
  • (お好みで)2つのLEDパネルを固定する資材
    • 私は頻繁に持ち運ぶため結局ちょうつがいで折りたためるようにしましたが、見栄え重視ならパネルを固定して前面にアクリル板でも付けると綺麗かもしれません
  • (お好みで)ラズパイ用のディスプレイ
    • 金欠だったので買いませんでしたが、あった方が何かと便利です
    • ディスプレイを買う場合、マウスとキーボードも購入必須です
    • 私はまずSSH(WindowsならTera Termとかその辺)でラズパイに接続できる環境を用意してから、xrdpというLinux上でRDPを可能にするソフトを使ってWindows標準のリモートデスクトップからリモート出来るようにしています(VNCとかでもいいですがお好みで)
    • ただし、ラズパイのローカルネットワーク上のIPアドレスを調べないといけない(がこれがまた面倒)なので、リモートデスクトップのホスト側のPCにBonjourというソフトを入れ(iTunes入れると入ってきます)、raspberrypi.local というドメイン(ローカルネットワーク内のみで有効)でアクセスできるようにしています(リモートデスクトップの画面でraspberrypi.localと入力すると行けます・後はggって下さい(丸投げ))
  • (お好みで)LANケーブル
    • 上述の通り私はリモートデスクトップ+SSHで運用しています
    • 家の中とかWi-Fiが通じる環境なら全く問題がないのですが、例えばどっかに出し物に持っていく時などWi-Fiに繋がらない(ラズパイとホストPCが同じネットワークに存在しない)場合、ラズパイと通信できないため問題です
    • スマホでテザリングする方法とかもありますが、手っ取り早くラズパイとホストPCをLANケーブルで直接繋げると、ラズパイと通信できるようになります(この場合でもraspberrypi.localのドメインでリモートデスクトップ出来ます)
    • LANケーブル端子が付いていない場合は変換端子を買っておくといいかもしれません

他にも色々あるかもしれませんが、大体これくらいだと思います(投げやり)

配線

基本的に私はLEDパネルとの間をフラットケーブルで接続し、余ったフラットケーブルをLEDの裏側(基盤が見える方)から見て一番左側の端子に接続、もう片方の端子にジャンパーワイヤを接続するような構成としています。

LEDの規格について

配線の前にLEDの規格について(ほぼこちらの投げ売り)ですが、
LEDパネル(64×64なら4096個)全てのLEDを同時に光らせるのは現実的ではないため、
高速でLEDの点灯・消灯を繰り返して、あたかも全部ついているように見せているんだそうです(LED行先表示器を1/1000とかで撮ると切れるのはそのため)。

64×64のLEDパネルの場合、縦に64列、横に64列あるので、
全部で32セクションに列を分けます。
1セクションは一番上の横1列目と真ん中の横33列目、2セクションは横2列目と横34列目…と言った感じらしいです。
1セクションあたりのLEDの数は横に64個LED並んでいるのが2列で128個あり、セクションを切り替えながら表示していくんだとか(だから実際はLEDは一度に128個しか点灯していない)
R1・G1・B1がセクション上段の1番左のピン、R2・G2・B2がセクション下段の1番左のピンを点灯させる(同時に2つのLEDを点灯させることができる)ので、信号をあと63回送って上段64個、下段64個のLEDを点灯させたら、消灯させて次のセクションを選択、点灯、消灯…を繰り返してセクションの最後まで行ったら最初のセクションに戻ってまたセクション選択、点灯、消灯…を繰り返す、
といった流れみたいです(分かりづらい)

(恐らく)各ピンの役割は、

  • R1…LEDのR(赤)部分を点灯させるかの信号を出す(セクション上段)
  • R2…LEDのR(赤)部分を点灯させるかの信号を出す(セクション下段)
  • G1…LEDのG(緑)部分を点灯させるかの信号を出す(セクション上段)
  • G2…LEDのG(緑)部分を点灯させるかの信号を出す(セクション下段)
  • B1…LEDのB(青)部分を点灯させるかの信号を出す(セクション上段)
  • B2…LEDのB(青)部分を点灯させるかの信号を出す(セクション下段)
  • A・B・C・D・E…データを送り込むセクションの場所を決める(5Bit/32通り)
  • clock(CLK)…信号を送信するごとのクロック(らしい)
  • strobe(LAT or STB)…これでLEDが光る・表記がLAT・STBとあるらしい
  • OE-…画面表示有効のフラグらしい
  • GND…グランド

って感じみたいです(雑)

ラズパイ側接続図

太字(水色背景)がジャンパ線を接続するピンです(かなりややこしいので注意・配線を間違えると変な映像しか出なくなります)
※ピンの(かっこ)内はラズパイのピンの種類です
※ピン10のEですが、64×64のLEDパネルでのみ使われているピンなんだそうです、
64×32のパネルの場合は省略して問題ないとかなんとか…(わからん)

接続先 ピン ピン 接続先
1(3.3V) 2(5V)
3(GPIO) 4(5V)
5(GPIO) 6(GND) GND
strobe 7(GPIO) 8(GPIO)
9(GND) 10(GPIO) E
clock 11(GPIO) 12(GPIO) OE-
G1 13(GPIO) 14(GND)
A 15(GPIO) 16(GPIO) B
17(3.3V) 18(GPIO) C
B2 19(GPIO) 20(GND)
G2 21(GPIO) 22(GPIO) D
R1 23(GPIO) 24(GPIO) R2
25(GND) 26(GPIO) B1
27(ID_SD) 28(ID_SC)
29(GPIO) 30(GND)
31(GPIO) 32(GPIO)
33(GPIO) 34(GND)
35(GPIO) 36(GPIO)
37(GPIO) 38(GPIO)
39(GND) 40(GPIO)

接続ケーブル側接続図

必ずケーブルの赤いラインが引いてある方を上にして接続して下さい!(上下逆にして接続して3日くらい詰んだことがあります)

接続ケーブルとLEDパネル側の端子はピンが左右反転しています!注意してください。
下の表は接続ケーブル用なので、LEDパネル側の端子に直接接続させる場合は左右反転させて読み替えて下さい(別途ジャンパーワイヤ(メス―メス)が必要です)

太字がジャンパ線を接続するピンです(ほぼ全て接続します)。
ピンの(かっこ)内はLEDパネル側のピンの種類です(何故か表記が合ってなかったりしますがこれで行けているので多分大丈夫だと思います)。
また、ピンの表記は私の購入したパネルでの表記なので、例えばSTBがLATだったり、Dが何故かGNDになってたりしますが、そこは各自合わせて下さい()

接続先 ピン ピン 接続先
G1 1(G1) 2(R1) R1
GND 3(GND) 4(B1) B1
G2 5(G2) 6(R2) R2
E 7(E) 8(B2) B2
B 9(B) 10(A) A
D 11(GND) 12(C) C
strobe 13(LAT) 14(CLK) clock
 – 15(GND) 16(OE) OE-

写真はこちら

LEDパネル側端子

ソフトの導入

ラズパイのセットアップから書いてたらきりがないので飛ばすとして、
必要なソフトウェアですが、

  • rpi-rgb-led-matrix(最重要ソフト・これのお陰でラズパイ上でLEDマトリクスを制御する事ができる)
    • これの導入は後述
    • 後述のLEDで動画を流すためのソフトはこのソフト(ライブラリ)を組み込んでビルドします
    • 他にもPythonからライブラリを利用するためのPythonライブラリもあるので、Python分かる方は楽しめると思います(Pythonぜんぜんわからん)
  • Python(取りあえず入れといた方がよさげ・多分プリイン済み)
  • gcc/g++(C言語ビルド用ソフト(?)・多分プリイン済み)
  • make(ビルド用ソフト・多分プリイン済み)
  • git(バージョン管理ソフト・後で使います・多分プリイン済み)
  • OpenCV(動画再生ライブラリ・後述のビルドで必須です)
  • SDL(音声再生ライブラリ・後述のビルドで必須です)

あたりが必要みたいです。

OpenCV・SDLの導入

上記のうち、OpenCVとSDLのインストール(ビルド)にかなり難儀(makeがエラーで止まる)したのですが、結局aptで楽々パッケージで入れましょうという結論になりました()

いつもお馴染みターミナル(またはSSH)から、ソフト内で利用しているOpenCV・SDLのライブラリをaptでインストールしておきます。
(もしかすると、OpenCV関連はOpenCV2.4でないと動かないかもしれません・2019年4月現在ではaptでインストールできるOpenCVは2.4のままですが、もし上手く行かない場合は適当にビルドしてください(投げやり))

おまじないとしてちゃんと

sudo apt update && sudo apt upgrade

してから、

sudo apt install libopencv-dev libsdl1.2-dev libsdl2-dev libsdl-mixer1.2-dev libsdl2-mixer-dev

と実行します。

rpi-rgb-led-matrixの導入

以下の通りに実行してrpi-rgb-led-matrixをダウンロード、ビルドします。
make(ビルド)には少し時間がかかります。
実行すると、rpi-rgb-led-matrixは /home/pi/rpi-rgb-led-matrix/ 以下にダウンロードされます。

cd ~/
git clone https://github.com/hzeller/rpi-rgb-led-matrix.git
cd rpi-rgb-led-matrix
make
make install-python

ビルドが完了すると、examples-api-useディレクトリにサンプルプログラムが色々置いてあるので、動くかテストしましょう。

sudo ~/rpi-rgb-led-matrix/examples-api-use/demo -D 0 --led-rows=64 --led-cols=64 --led-chain=2 --led-no-hardware-pulse

これで正しく虹色の正方形がぐるぐる回転していれば正常です。

他にも、公式ドキュメント(翻訳してます)より、

-Dで選択されたデモ
         0  - 回転する正方形
         1  - 画像を前方にスクロールする(-m <スクロール-ミリ秒>)
         2  - 画像を後方にスクロールする(-m <スクロール-ミリ秒>)
         3  - テスト画像:正方形
         4  - パルスカラー
         5  - グレースケールブロック
         6  - アーベリアン砂山モデル(-m <タイムステップ-ミリ秒>)
         7  - コンウェイの人生のゲーム(-m <タイムステップ-ミリ秒>)
         8  - ラングトンのアリ(-m <タイムステップ-ミリ秒>)
         9  - ボリュームバー(-m <タイムステップ-ミリ秒>)
         10 - 色の進化(-m <タイムステップ-ミリ秒>)
         11 - 輝度パルス発生器

のようなデモがあるので、色々遊んでみましょう(1・2番はコマンドの末尾に ~/rpi-rgb-led-matrix/examples-api-use/runtext.ppm (流す画像が入っているファイル)を設定する必要があります)。

全く点かない・一部が欠ける・よくわかんない乱れた映像が流れる場合はほぼ100%配線を間違えているので、配線をやり直しましょう。

ちゃんと付いた事を確認して、本番のLEDパネルでの動画再生ソフトのビルドへ入ります。

led-movie-player

led-movie-playerは、こちらの記事のプログラムを元に改造した、自作のLEDパネル用動画プレイヤー(?)です(C++もうむりしんどい)。
一応無限ループ・有限ループに対応し、少しグラフィカルに表示し、またある程度LEDパネルのパラメータを指定できるようにしてあります。
仮にビルドしたけど動かない場合に備え、念のためMakefile・ソース・実行ファイル・使い方・サンプル動画まとめて配布してあります(後述)。

まず、新たにmatrixという先ほどのライブラリのsubmoduleを作成します。

git submodule add https://github.com/hzeller/rpi-rgb-led-matrix.git matrix

その後、Makefileを作成します(既にある場合は上書きしてください)。
必ずUTF-8・LFで保存して下さい。
初心者なのでソースがかなり汚い上に非効率ですが許して()
改造再配布お好きにどうぞ(私も改造した身なので人の事言えない())

Makefile

CXX=g++

OBJS=led-movie-player.o 
TARGET=led-movie-player

CXXFLAGS=-std=c++11
RGB_INCDIR=include
RGB_LIBDIR=lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread -lopencv_core -lopencv_highgui -lopencv_imgproc -lSDL -lSDL_mixer

all: $(TARGET)

$(RGB_LIBRARY):
	$(MAKE) -C $(RGB_LIBDIR)

$(TARGET): $(OBJS) $(RGB_LIBRARY)
	$(CXX) $(CXXFLAGS) $(OBJS) -o $@ $(LDFLAGS)

%.o : %.cc
	$(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) -c -o $@ $<

install:
	cp $(TARGET) /usr/local/bin/$(TARGET)

clean:
	rm -f $(OBJECTS) $(TARGET)
	$(MAKE) clean

led-movie-player.cc

#include 
#include 
#include 
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include 
#include 
#include 
#include <SDL/SDL.h>
#include <SDL/SDL_mixer.h>
#include "led-matrix.h"
#include "graphics.h"
#include<sys/types.h>
#include<sys/stat.h>


using namespace std; 
using namespace rgb_matrix;


int play(std::string movie_file, std::string audio_file, int rows, int cols, int chain, int parallel){

    // LEDパネルのパラメータをOptionsに設定
    RGBMatrix::Options options;
    options.rows = rows;
    options.cols = cols;
    options.chain_length = chain;
    options.parallel = parallel;
    options.disable_hardware_pulsing = true; //ハードウェアパルス生成を無効にする(有効だと音声モジュールを無効化する必要がある)

    // GPIOを初期化
    GPIO io;

    if(!io.Init())
        return 1;

    // 音声の読み込み

    SDL_Init(SDL_INIT_AUDIO);
    Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 4096); //音声のサンプルレートが44100の場合 ファイルのサンプルレートと異なると音ズレする
    Mix_Music *music=Mix_LoadMUS(audio_file.c_str());
    if(!music)return 1;

    // LEDマトリクスの初期化

    RGBMatrix *matrix = new RGBMatrix(&io, options);
    matrix->set_luminance_correct(true);
    matrix->SetBrightness(100);
    matrix->SetPWMBits(11);
    FrameCanvas *canvas = matrix->CreateFrameCanvas();
    const int width = canvas->width();
    const int height = canvas->height();

    // 動画の読み込み

    cv::Mat src_img;
    cv::Mat dst_img(height, width, src_img.type());
    cv::VideoCapture cap(movie_file.c_str());

    auto fps = cap.get(CV_CAP_PROP_FPS);
    long fps_ustime = 1000000.0/(double)fps+0.5;

    const auto hoseicount = (int)fps*10;
    auto hcnt=0;

    // ここで音声を再生する

    if(Mix_PlayMusic(music, 1)==-1) {
        printf("Mix_PlayMusic: %s\n", Mix_GetError());
        return 1;
    }
    auto pre_dc=1ll;

    // ここで動画を再生する

    auto frame_cnt = cap.get(CV_CAP_PROP_FRAME_COUNT) - 1; // Magic!!!
    auto start = std::chrono::system_clock::now();
    while(true){

        auto fpos = cap.get(CV_CAP_PROP_POS_FRAMES);
        if(!(fpos < frame_cnt)){
            std::wcout << "    Stoping Play the Movie." << std::endl; break; } cap >> src_img;

        auto pos = cap.get(CV_CAP_PROP_POS_MSEC);
        auto r = (double) height / src_img.rows;
        auto w = (int) (src_img.cols * r);
        auto d = (width - w) / 2;
        cv::resize(src_img, dst_img, cv::Size(w, height), cv::INTER_AREA);
        for(int y=0; y<height; y++){
            for(int x=0; x<w; x++){
                cv::Vec3b bgr = dst_img.at(y, x);
                canvas->SetPixel(x + d, y, bgr[2], bgr[1], bgr[0]);
            }
        }
        matrix->SwapOnVSync(canvas);
        auto end = std::chrono::system_clock::now();

        auto delaytime = std::chrono::duration_cast(end - start).count();

        auto delaycount = pos * 1000 - delaytime;
        if(delaycount > 0) {
            usleep(delaycount);
        }
    }

    // 終了処理

    matrix->Clear();
    Mix_HaltMusic();
    Mix_FreeMusic(music);
    Mix_CloseAudio();
    SDL_Quit();

    delete matrix;

}


int main(int argc, char *argv[]){

    int cols ; // 個々のLEDパネルの横幅
    int rows ; // 個々のLEDパネルの縦幅
    int chain ; // 直列に繋いでいるLEDパネルの数
    int parallel ; // 並列に繋いでいるLEDパネルの数
    int loop; // ループ回数
    std::string loopstr; // 諸問題で文字列型のループ回数
    int loopflg; // ループフラグ
    std::string version = "2.1"; // バージョン
    std::string file; // ファイル(拡張子なし)
    struct stat st;

    // 引数確認
    // 変数argvは文字列なのでatoiでint型に変換しておく

    if(argc == 2){ // 引数が3個(コマンド名と動画ファイル名指定のみ)
        // デフォルトサイズで再生
        file = argv[1];
        loopstr = "none";
        cols = 64;
        rows = 64;
        chain = 2;
        parallel = 1;

    } else if(argc == 3){ // 引数が3個(コマンド名と動画ファイル名とループ指定のみ)
        // デフォルトサイズで再生
        file = argv[1];
        loop = atoi( argv[2] );
        loopstr = argv[2]; //0が入るとNULLと判断されてしまうため敢えて文字型
        cols = 64;
        rows = 64;
        chain = 2;
        parallel = 1;

    } else if(argc == 7){ // 引数が6個(LEDパネルの幅・高さ・個数等も指定)
        // 指定サイズで再生
        file = argv[1];
        loop = atoi( argv[2] );
        loopstr = argv[2]; //0が入るとNULLと判断されてしまうため敢えて文字型
        cols = atoi( argv[3] );
        rows = atoi( argv[4] );
        chain = atoi( argv[5] );
        parallel = atoi( argv[6] );

    } else { // それ以外はエラー吐いて終了
        std::wcout << "    " << std::endl;
        std::wcout << "---------------------------------------------------" << std::endl;
        std::wcout << "  Error: Argument is missing or too many." << std::endl;
        std::wcout << "  Please Retry... m(__)m" << std::endl;
        std::wcout << "---------------------------------------------------" << std::endl;
        std::wcout << "    " << std::endl;
        return 1;
    }

    int panelwidth = cols * chain ; // LEDパネルの横幅の合計
    int panelheight = rows * parallel ; // LEDパネルの縦幅の合計

    // 検索ディレクトリ
    std::string path = "/usr/local/movieplayer/";

    // 再生するファイルを検索して代入
    std::string movie_file = path + file + ".mp4"; // 動画ファイル
    std::string audio_file = path + file + ".mp3"; // 音声ファイルMP3(M4Aなど他の形式にしてもOK)

    // sudoで実行されてなかったら(Rootでないなら)エラー吐いて終了
    if (geteuid() != 0){
        std::wcout << "    " << std::endl;
        std::wcout << "---------------------------------------------------" << std::endl;
        std::wcout << "  Error: It must be run with Root privileges." << std::endl;
        std::wcout << "  Please Please Add 'sudo' at the beginning" << std::endl;
        std::wcout << "  of the command and Retry... m(__)m." << std::endl;
        std::wcout << "---------------------------------------------------" << std::endl;
        std::wcout << "    " << std::endl;
        return 1;
    }

    // ファイルがなかったらエラー吐いて終了
	if (stat(movie_file.c_str(), &st) != 0 || stat(audio_file.c_str(), &st) != 0){
        std::wcout << "    " << std::endl;
        std::wcout << "---------------------------------------------------" << std::endl;
        std::wcout << "  Error: Specified file Not found." << std::endl;
        std::wcout << "  Please Retry... m(__)m" << std::endl;
        std::wcout << "---------------------------------------------------" << std::endl;
        std::wcout << "    " << std::endl; return 1; } // ループ判別 if (loopstr == "none"){ // ループ再生ではないとき loopflg = 0; } else if (loop == 0){ // 無限ループ loopflg = 1; } else if (loop >= 1){ // 有限ループ
        loopflg = 2;
    } else {
        loopflg = 0; //パラメータ変だったら取りあえず一回だけ
    }

    // 表示部分
    // std::wcout.imbue(std::locale("ja_JP.utf8")); 日本語表示する場合はこれをコメントアウト
    std::wcout << "    " << std::endl;
    std::wcout << "---------------------------------------------------" << std::endl;
    std::wcout << "               LED-Movie-Player v" << version.c_str() << std::endl;
    std::wcout << "---------------------------------------------------" << std::endl;
    std::wcout << "    LED Panels Chain : " << chain << std::endl;
    std::wcout << "    LED Panels Parallel : " << parallel << std::endl;
    std::wcout << "    One LED Panel Size : " << cols << "x" << rows << std::endl;
    std::wcout << "    All LED Panel Size : " << panelwidth << "x" << panelheight << std::endl;
    std::wcout << "    " << std::endl;
    if (loopflg == 0){
        std::wcout << "    Press Ctrl + C to stop the Movie." << std::endl;
    } else if (loopflg == 1 || loopflg == 2){
        std::wcout << "    If you want to stop loop, Press Ctrl + C." << std::endl;
    }
    std::wcout << "---------------------------------------------------" << std::endl;

    if (loopflg == 0){ // ループ再生しない

        // 表示
        std::wcout << "    Starting Play the Movie!" << std::endl;
        std::wcout << "    " << std::endl;
        std::wcout << "    Playing Movie..." << std::endl;
        std::wcout << "---------------------------------------------------" << std::endl;

        // 再生実行
        int result = play(movie_file, audio_file, rows, cols, chain, parallel);

        // 終了
        std::wcout << "    See You!!" << std::endl;
    
    } else if (loopflg == 1){ // 無限ループ

        while(1){ // 無限ループ実行

            // 表示
            std::wcout << "    Looping Play the Movie!" << std::endl;
            std::wcout << "    " << std::endl;
            std::wcout << "    Looping Movie..." << std::endl;
            std::wcout << "---------------------------------------------------" << std::endl;

            // 再生実行
            int result = play(movie_file, audio_file, rows, cols, chain, parallel);

            // 少し待つ
            std::wcout << "    loop Now..." << std::endl;
            std::wcout << "---------------------------------------------------" << std::endl;
            int second = 300000;
            usleep(second);
        }

    } else if (loopflg == 2){ // 有限ループ

        for (int i = 1; i <= loop; i++){ // ループ実行

            // 表示
            std::wcout << "    Looping Play the Movie!" << std::endl;
            std::wcout << "    Loop: " << loop <<  " Now Loop: "  << i << std::endl;
            std::wcout << "    " << std::endl;
            std::wcout << "    Looping Movie..." << std::endl;
            std::wcout << "---------------------------------------------------" << std::endl;

            // 再生実行
            int result = play(movie_file, audio_file, rows, cols, chain, parallel);

            if (i < loop){
                // 少し待つ
                std::wcout << "    loop Now..." << std::endl;
                std::wcout << "---------------------------------------------------" << std::endl;
                int second = 300000;
                usleep(second);
            } else if (i == loop){ // 最後のループなら
                std::wcout << "    See You!!" << std::endl;
            }
            
        }

    }

    std::wcout << "---------------------------------------------------" << std::endl;
    std::wcout << "    " << std::endl;

}

この2つのファイルをmatrixディレクトリ内に配置します。

ここまで来たら、ビルドしてパスの通った場所にinstallするだけです。

sudo make && sudo make install

コマンドを実行して暫くすると、matrixディレクトリにled-movie-player(拡張子なしの実行ファイル)というファイルが生成されている筈です。
make install した際に既にパスの通った場所( /usr/local/bin )にコピーしてあるので、led-movie-playerの導入は終了です。

もしビルドに失敗した場合、こちらからled-movie-playerアーカイブをダウンロードするといけるかもしれません(Raspberry Pi 3 Model B+でビルドしたので、ほかの機種では上手く動かない可能性があります)

使い方やサンプル動画も一応入ってるので、参考にして下さい。

動画・音声のエンコード

LEDで流す動画や音声は、予めLEDやled-movie-playerで再生できる形式に変換する必要があります。

・動画… mp4形式・114×64 or 128×72(サイズは多少の誤差は許容してくれるみたいだけどそのまま突っ込んでも再生できないっぽい)
・ビットレートは何でも構わないが音声はmp3から再生するのであってもなくても可
・音声… mp3形式・128kbps・44100kHz
・サンプルレートは必ず44100kHzにする必要があります(48000kHzだと音がバグります)

GUIツールで変換する事も出来ますが、個人的にはffmpegというCUIツール(Windows・Linux両方で動きます)で変換しておくことをお勧めします。
また、Windows用のバッチファイルも同梱しておきますので、パスを設定した上で使ってみて下さい。

動画のコマンド例:

ffmpeg -i "Movie.mp4" -vf scale=128:-1 "Movie-led.mp4"

音声のコマンド例:

ffmpeg -i "Movie.mp3" -vn -ac 2 -ar 44100 -ab 128k "Movie-led.mp3"

変換が終わったら、変換後のファイルをラズパイの /usr/local/movieplayer/ 以下にコピーしてください。
ソースを弄って、ファイルの置き場所を変更することも可能です。

続く

コメント