7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

openFrameworksで動画の再生終了を検知する

Last updated at Posted at 2018-02-18

当記事は、macOS 10.12.5 ,openFrameworks 0.9.8,Xcode8.3.3の開発環境を想定したものとなります。

「動画の再生終了後に処理を書きたいけど、再生が終わったってどうやって検知したらいいんだろう……?」

そう思い悩んだ結果、イベントリスナーを作りました。
今回参考にしたものは、ofEventのリファレンスと以下のexampleです。
・events/customEventExample
exampleではシューティングゲームの当たり判定が実装されています。

#動画再生クラスの作成
動画再生にはofVideoPlayerを用いていますが、動画再生が終了したという判定はプロパティのisPlayingと言うboolで判定するしかないようです。isPlayingtrueの時は再生中、falseの時は再生していないと言うことになります。
しかしながら、update毎にofAppで条件判定するのも使い回せないし格好悪いなぁ、と考えた結果、リスナー付きの動画再生クラスを作成することにしました。openFrameworksでは、イベント関連の処理を実装するにはofEventというクラスを利用します。
以下のように定義していきます。

EventVideo.hpp
class EventVideo{
    public:
    enum E_GIMMICK{G_P_CHAIR,G_P_BED,G_M_CHAIR,G_M_BED};
    E_GIMMICK mGimmick;
    ofEvent <E_GIMMICK> mEndEvent;
    ofVideoPlayer mPlayer;
    void setup(string path,ofLoopType type,E_GIMMICK app);
    void update();
    void draw(float x , float y , float width , float height);
    void play();
    void stop();
    void pause();
    void setSpeed(float speed);
    void setFrame(int frameNum);
    void closeMovie();
    int getTotalNumFrames();
    bool mIsPlayed;
};
EventVideo.cpp
#include "EventVideo.hpp"

void EventVideo::setup(string path , ofLoopType state , E_GIMMICK gimmick){
    mIsPlayed = false;
    mPlayer.load(path);
    mPlayer.setLoopState(state);
    mGimmick = gimmick;
}

void EventVideo::update(){
    mPlayer.update();
    bool isPlaying = mPlayer.isPlaying();
    if(mPlayer.isInitialized() && !mIsPlayed && !mPlayer.isPlaying()){
        ofNotifyEvent(mEndEvent , mGimmick);
        mIsPlayed = true;
    }
}

void EventVideo::draw(float x,float y,float width,float height){
    mPlayer.draw(x,y,width,height);
}

void EventVideo::play(){
    mPlayer.play();
    mIsPlayed = false;
}
void EventVideo::stop(){
    mPlayer.stop();
    mIsPlayed = true;
}

void EventVideo::pause(){
    mPlayer.setPaused(true);
}

void EventVideo::setSpeed(float speed){
    mPlayer.setSpeed(speed);
}

void EventVideo::setFrame(int flameNum){
    mPlayer.setFrame(flameNum);
}

void EventVideo::closeMovie(){
    mPlayer.closeMovie();
}

int EventVideo::getTotalNumFrames(){
    return mPlayer.getTotalNumFrames();
}

見てもらうとわかる通り、EventVideoクラスはofVideoPlayerクラスのインスタンスとofEventクラスのインスタンスを内包したものになります。動画を扱うクラスをもっといじって実装することも考えましたが、工数の都合上このような形で実装しました。
setupメソッドの引数であるpathは動画のパス、ofLoopTypestateは動画をループさせるのかどうか、E_GIMMICKは筆者の作品独自のenum定義で、どの種類の動画を流すのかを渡しています。
この辺りの引数については必要だと思うものを各自適宜追加してください。ただ、私見になりますが、動画再生クラスなのでpathofLoopTypeについては渡してあげた方が便利だと思います!
updateではプレイヤーの更新と、終わったかどうかの判定を行います。mIsPlayedは独自のbool変数で、動画再生前か再生後かを判別するためのものです。動画クラスのisPlayingは現在再生中かどうかの検知しかできないため、このような変数を追加しています。動画の再生が終了した場合には、ofNotifyEventで呼び出し元に通知します。
それ以降の処理は、呼び出し元からofVideoPlayerのメソッドをそのまま呼び出しています。

#呼び出し元のクラスの実装
次に、呼び出し元のクラスを実装します。Arduinoと連携する時にも使う、ofAddListener,ofRemoveListenerを使用します。

ofApp.hpp
#include "EventVideo.hpp"
class ofApp : public BaseApp{
    
	public:
		void setup();
		void update();
		void draw();
		void keyPressed(int key);
		void keyReleased(int key);
		void mouseMoved(int x, int y );
		void mouseDragged(int x, int y, int button);
		void mousePressed(int x, int y, int button);
		void mouseReleased(int x, int y, int button);
		void mouseEntered(int x, int y);
		void mouseExited(int x, int y);
		void windowResized(int w, int h);
		void dragEvent(ofDragInfo dragInfo);
		void gotMessage(ofMessage msg);
        void exit();
        void endMovie(CONST::E_GIMMICK & app);
        EventVideo mVideo;

ofApp.cpp
#include "ofApp.hpp"

void ofApp::setup(){
	mVideo.setup("動画のパス", OF_LOOP_NONE, EventVideo::G_M_BED);
        ofAddListener(mVideo->mEndEvent, this, &ofApp::endMovie);
}

void ofApp::update(){
        mVideo.update();
}

void ofApp::draw(){
        mVideo.draw(0, 0, ofGetWidth(), ofGetHeight());
}

void ofApp::endMovie(EventVideo::E_GIMMICK & app){
        //動画終了後の処理
}

void ofApp::exit(){
        ofRemoveListener(mVideo->mEndEvent, this, &ofApp::endMovie); 
}

setupで引数に情報を入れた後、ofAddListenerでイベントを設定します。引数は左から順に、対応するofEventのインスタンス、Listenerクラス(大体thisでいいと思います)、通知が来た際に実行するメソッドのアドレスを指定します。
update,drawは動画の表示のために呼びます。
そして、endMovieは動画が終了した際に呼ばれるメソッドです。例えば筆者の場合は引数からenumを受け取り、switch文にて条件分岐し、どの動画を流したかによって動画再生後の処理を分けました。

#イベント検知にはofEvent
今回は動画再生の検知に使用しましたが、exampleにもある通りシューティングの当たり判定など、様々なことに応用できます。
ポイントは、
・呼び出し先にofEventのインスタンスを定義する。
・呼び出し元に処理をお願いしたいタイミングでofNotifyEventを実行する。
・呼び出し元でofAddListenerをしてあげる。
・必要がなくなったらofRemoveListenerでリスナーの設定を削除する。

event処理になるのでdelegateのようにコールバックする形ですね。
openFrameworksで呼び出し元に処理をしてほしいけどどうやって処理してもらおう!ってなった時にはこちらの方法を試して見てはいかがでしょうか。

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?