FFmpeg ライブラリーを使った動画のデコード(C++ソースコード)

最近、色々忙しくなってきて、週末は時間が何かと足りなくなってます・・

では、早速本題!
FFmpeg はオープンソースで、動画や音声のエンコード、デコードなどを行うツール
です、非常に多くの人が改善してきた事で、非常に高い品質と、機能が実装されて
います。

また、このプロジェクトはマルチプラットホームなので、多くの環境で同じように
使う事が出来ます。
「FFmpeg」コマンドを使って動画をエンコードしたりする話は、既に沢山の方が書
かれています、今回の話は、「FFmpeg」が利用しているライブラリー郡を使って、
C++ のプログラムから、動画をデコードしてみようという内容です。

(1)準備
・まず、FFmpeg コマンドをインストールします。
※MSYS2 MinGW-w64 で、話を進めますが、OS-X の BREW などでも同じように出来ま
す。

pacman -S mingw-w64-x86_64-ffmpeg

この操作だけで、ffmpeg 他必要なライブラリーなど全てインストールされます。

(2)実装
・動画の中の、特定の1フレームをデコードするサンプル

・まず、必要そうなヘッダーをインクルード

#include <iostream>
#include <string>
extern "C" {
	#include <libavcodec/avcodec.h>
	#include <libavfilter/avfilter.h>
	#include <libavformat/avformat.h>
	#include <libswscale/swscale.h>
};

※これら、API のプロトタイプでは、全てC言語のみが想定されている為、C++
から使う場合は「extern "C"」が必要です。

・初期化

    avcodec_register_all();
    av_register_all();
    avfilter_register_all();

※FFmpeg 関係お約束の呼び出しですかねぇ・・

・ビデオファイルを開く


    std::string file_name = argv[1];
    AVFormatContext* pFormatCtx = NULL;
    if(avformat_open_input(&pFormatCtx, file_name.c_str(), NULL, NULL) != 0) {
        std::cerr << "ERROR: avformat_open_input(): '" << file_name << '\'' << std::endl;
        return -1;
    }

・ストリーム情報の取得と表示

    if(avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        std::cerr << "ERROR: avformat_find_stream_info(): '" << file_name << '\'' << std::endl;
        return -1;
    }

    // ストリーム情報の表示
    av_dump_format(pFormatCtx, 0, file_name.c_str(), 0);
    fflush(stderr);

・コーデックの検索とコーデックのオープン

    AVCodecContext* pCodecCtx = pFormatCtx->streams[0]->codec;
    AVCodec* pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

    // コーデックを開く
    if(avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        std::cerr << "ERROR: avcodec_open2(): '" << file_name << '\'' << std::endl;
        return -1;
    }

・ログ・レベルの設定

    av_log_set_level(1);

※デコードの過程で、非常に細かく、内部の軽微な問題を報告してくれるので、それをカット!

・バッファー関係の確保と必要な初期化

    // フレームの確保
    AVFrame* pFrame = avcodec_alloc_frame();
    // イメージの確保
    AVFrame* pImage = avcodec_alloc_frame();

    // イメージ用バッファの確保
    unsigned char* buffer = (unsigned char *)av_malloc(avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height));
    // バッファとフレームを関連付ける
    avpicture_fill((AVPicture*)pImage, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);

・スケーリング用コンテキストの取得

    struct SwsContext* pSWSCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height
        , pCodecCtx->pix_fmt
        , pCodecCtx->width, pCodecCtx->height
        , PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL);

・メイン

    int count = 0;
    AVPacket packet;

    // フレーム読み込み
    while(av_read_frame(pFormatCtx, &packet) >= 0) {

        // デコード
        int state;
        avcodec_decode_video2(pCodecCtx, pFrame, &state, &packet);
 
        // 各フレーム、デコード完了
        if(state) {
            // 特定のフレームを切り出し
            if(count == 400) {
                int bsize = pCodecCtx->width * pCodecCtx->height * 3;
                sws_scale(pSWSCtx, (const uint8_t **)pFrame->data
                    , pFrame->linesize, 0 , pCodecCtx->height
                    , pImage->data, pImage->linesize);

                 FILE *fp = fopen("rgb24.raw", "wb");
                 if(fp) {
                     std::cout << "FrameSize: " << (int)pCodecCtx->width << ", " << (int)pCodecCtx->height << std::endl;
                     fwrite(buffer, bsize, 1, fp);
                     fclose(fp);
                 }
             }
             ++count;
        }
        // パケット・メモリ解放
        av_free_packet(&packet);
    }
    std::cout << "Total frame: " << count << std::endl;

※この場合、400フレーム目を「RGB24」で「RAW」ファイルとして書き出します。

・メモリ解放

    sws_freeContext(pSWSCtx);
    av_free(buffer);
    av_free(pImage);
    av_free(pFrame);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);

・コンパイルと実行

g++ -c -O2 -std=c++14 -DHAVE_STDINT_H -DWIN32 -DNDEBUG -isystem /mingw64/include -Wall -Werror -Wno-deprecated-declarations -o release/decode.o decode.cpp
g++ -L/mingw64/lib release/decode.o -lpthread -lavdevice -lavformat -lavfilter -lavcodec -lswresample -lswscale -lavutil -lz -o ffdecode_test.exe

ffdecode_test test.mp4

※適当な動画を食わせれば、400フレーム目をデコードして、RGB24ビットで、ファイル「rgb24.raw」を書き出します。

decode.cpp

バイク用汎用メーター SS182 (中華製)その1

先日、アマゾンをブラブラしてたら、そこそこ安いバイク用汎用メーターを見つけた~

WR250Xは、タコメーターが無いので、回転を上げるのに気を使うから、前からタコメーターが欲しかったが、中々良い物が無い・・
※当然ながら、高くて良いものはあるにはあるが・・・
このメーターは6000円くらい(現在は4800円?)で、スピード、水温、ガソリンゲージ、各種インジケーターなど機能も豊富で、そして、意外と薄くてコンパクト(ここ大事)
~少し悩んだが、駄目元で、買ってみた。

先日届いて、早速開封したが、解説書の類が全く入って無いではないか・・・
IMG_0759s
流石中華クオリティーだけの事はある~、関心している場合では無い、まぁ、この程度は、端から予想していた範疇なので、慌てる事なく、ネットの海へ・・・
でも、中々、目的の情報にたどり着かない・・・

ふと、裏に型番が刻印してある「SS182」うーーん、一応これで検索・・・
おお!一発で、それらしい情報がーーー、流石 google 先生!、凄いです!


 1: Pink          Gear 1 (GND)
 2: Blue/Red      Gear 2 (GND)
 3: Green/Black   Gear 3 (GND)
 4: Yellow/Red    Gear 4 (GND)
 5: Brown/White   Gear 5 (GND)
 6: Yellow        5V (Speed sensor) ※速度センサーに供給する+5V電源出力
 7: Brown/Red     EFI (GND)
 8: Green/Blue    Water temprature (Resisitance) 抵抗入力
 9: Yellow/White  Fuel level (Resisitance) 抵抗入力
10: Green/Red     Neutral gear (GND) ニュートラルランプ
11: Red/White     Gear 6 (GND)
12: Orange        Left blinker (+12V) ウインカー左
13: Blue          Upper beam (+12V) ハイ・ビーム
14: Brown         Back light (+12V) ※中継コネクターに接続されていない、常に点灯
15: Green         GND
16: Lightblue     Right blinker (+12V) ウインカー右
17: Black/Yellow  Rev (signal) エンジン回転計用パルス信号入力
18: Red           Speed (signal) 速度センサーパルス入力
19: Purple        Stand-by power source (+12V) スタンバイ電源(常時接続)
20: Black         Main power source (+12V) 電源(イグニッション)

※一応、「解説書を送って!」と販売元に連絡しておいたら、PDFで1枚の説明が送られて来たので、ここに貼っておく。
SS182_Manual

で、何とか、結線のイメージは沸いたのだが、とりあえず、どんなもんかテストしてみた。

(1)まず、電源を入れてみる。
・19、20を+12Vに
※19番は、内部時計などを活性させておくのに常時バッテリーに接続しておくんだろうと思うけど、どのくらい電気食うか、少し不安材料だよなー、なにしろ中華クオリティーなので・・
・15をGNDに
※電源投入時オープニング
※お約束、ステッピングモーター式、最近の感じ~
※この感じなら、6000円なら「買い!」なのではないかと~

(2)基本機能
・アナログメーターによるエンジン回転表示
※10000rpmからレッドゾーン(固定)で右上の赤いランプが点灯
※最大15000rpm
※最高回転を自動で記録するようだ
※1、2、4気筒を設定できる
・デジタル速度表示
※最高速度を記録するようだ
※キロメーター、マイルの表示切替
※タイヤが1回転した時の長さと、1回転辺りのパルス数を設定できる
・オドメーター、トリップメーター
・リアルタイムクロック(バックアップ電源を常時接続する必要がある)
・抵抗式による燃料計表示(液晶表示による)
・燃料警告ランプ
・抵抗式による水温警告ランプ
・ニュートラルランプ
・左右ウインカーランプ
・ハイビームランプ
・液晶表示によるギアポジション(1~6)
・EFI警告ランプ(メーター中央下側)
・バッテリー警告ランプ(メーター中央下側右寄り)
※バッテリーの電圧がある程度下がると点灯するものと思われる

(3)各種ランプと、対応を調査
・Gear 1,2,3,4,5,6 はギアポジションを表示するもので、それぞれの端子をGNDに繋ぐ事で液晶画面に表示する。
※この機能必要かなぁ・・、最近のインジェクション車とかは、ギアポジションを観て、エンジンの特性を変えるとかしてるのもあるのだが、大抵は、ポジションにより抵抗が変化する仕組みと思うので、このままでは、上手く適合しないと思うけど・
・EFI は、エンジンや、EFIのトラブルなどで表示するランプ、これも GND 接続
・Left/Right blinker は、ウインカー用で、+12V で点灯する
・Neutral gear は、ギアポジションをニュートラルに入れた時に点灯する +12V
・Upper beam は、ハイビーム時に点灯 +12V
・14: Back Light は接続されてなくて、多分常時点灯なのだと思う

-----
付属品として、磁力に反応するごついセンサーが同梱されていたが、これは、使えないなぁー、自分で作る予定。

今回はここまで、続きは、次回~