VLC弄ってみた#2 - 時間をミリ秒にしてみる
第2回目の今回は、VLCにちょっとした改造を加えてみたいと思います。
VLCでは再生時間が秒単位でしか表示されませんが、ミリ秒単位でのシークがしたいことってありますよね。ありますよね?
そういう需要に応えるため、VLCの再生時間をミリ秒で表示させてみます。
環境はUbuntu 15.04/14.04です。MacやWindowsではGUIの描画が若干異なるため、恐らくこれだと変わりません。
どこをいじれば良いのかを探す
まずは、どこをいじれば良いのかを探す必要があります。
とりあえず描画部分のパーツを見てみることにしましょう。今回いじりたいのはシーク中の再生時間ですが、これは2ヵ所に表示されています。一つはシークバーの左端、現在の再生時間が表示されている場所で、もう一つはカーソルを合わせた際に出てくるツールチップです。
描画部分がmodule/gui/qt4あたりに入っていることは何となく分かるので、とりあえずこの中をのぞいてみると、utilの中に"timetooltip.cpp" "timetooltip.hpp"というファイルがありました。これがツールチップ情報を持った描画クラスのようです。
/* timetooltip.hpp */ class TimeTooltip : public QWidget { Q_OBJECT public: explicit TimeTooltip( QWidget *parent = 0 ); void setTip( const QPoint& pos, const QString& time, const QString& text ); virtual void show(); protected: virtual void paintEvent( QPaintEvent * ); private: void adjustPosition(); void buildPath(); QPoint mTarget; QString mTime; QString mText; QString mDisplayedText; QFont mFont; QRect mBox; QPainterPath mPainterPath; QBitmap mMask; int mTipX; };
mTextというのが表示されているテキストで、setTip関数を通じてセットされていることが容易に想像できます。
ただこのクラスはパーツを定義しているだけなので、実際にsetTipしている場所を探さなければいけないですね。"timetooltip.hpp"をincludeしているファイルを検索すると、今度は同じディレクトリに"input_slider.cpp" "input_slider.hpp"というファイルが見つかりました。103行目で
/* Tooltip bubble */ mTimeTooltip = new TimeTooltip( this ); mTimeTooltip->setMouseTracking( true );
とあり、ビンゴ!ですね。
さて、実際にsetTipをしている場所を探すと、331行目に
if( likely( size().width() > handleLength() ) ) {
secstotimestr( psz_length, ( ( posX - margin ) * inputLength ) / ( size().width() - handleLength() ) );
mTimeTooltip->setTip( target, psz_length, chapterLabel );
}
という場所がありました。 secstotimestrという関数がキモのようですが、この関数はsrc/misc/mtime.cで次のように定義されています。
/** * Convert seconds to a time in the format h:mm:ss. * * This function is provided for any interface function which need to print a * time string in the format h:mm:ss * date. * \param secs the date to be converted * \param psz_buffer should be a buffer at least MSTRTIME_MAX_SIZE characters * \return psz_buffer is returned so this can be used as printf parameter. */ char *secstotimestr( char *psz_buffer, int32_t i_seconds ) { if( unlikely(i_seconds < 0) ) { secstotimestr( psz_buffer + 1, -i_seconds ); *psz_buffer = '-'; return psz_buffer; } div_t d; d = div( i_seconds, 60 ); i_seconds = d.rem; d = div( d.quot, 60 ); if( d.quot ) snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%u:%02u:%02u", d.quot, d.rem, i_seconds ); else snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%02u:%02u", d.rem, i_seconds ); return psz_buffer; }
見ての通り、秒数を受け取ってh:mm:ssのフォーマットで文字列を吐き出す関数です。なので、こいつをコピーして、millisecstotimestrという関数を作り、こちらを呼び出すようにしましょう。
実際にいじってみる
次のように関数を定義し、ヘッダファイルも編集しておきます。
char *millisecstotimestr( char *psz_buffer, int32_t i_milliseconds ) { if( unlikely(i_seconds < 0) ) { millisecstotimestr( psz_buffer + 1, -i_milliseconds ); *psz_buffer = '-'; return psz_buffer; } div_t d; unsigned short millisec, sec; d = div( i_milliseconds, 1000); millisec = d.rem; d = div( d.quot, 60 ); sec = d.rem; d = div( d.quot, 60 ); if( d.quot ) snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%u:%02u:%02u.%03u", d.quot, d.rem, sec, millisec ); else snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%02u:%02u.%03u", d.rem, sec, millisec ); return psz_buffer; }
とりあえず、現在位置を秒数で受け取っている分にはどうしようもないので、これをミリ秒で受け取るようにします。受け取る秒は次のように計算されているのでした。
( ( posX - margin ) * inputLength ) / ( size().width() - handleLength() )
ハンドルの位置と動画の長さから秒数を計算し、これがintにキャストされています。なので、ミリ秒を取得するためにはこれを1000倍するだけで良いでしょう。
if( likely( size().width() > handleLength() ) ) { millisecstotimestr( psz_length, ( ( posX - margin ) * inputLength * 1000) / ( size().width() - handleLength() ) ); mTimeTooltip->setTip( target, psz_length, chapterLabel ); }
動かない??
これでコンパイルを行うと、コンパイルは通りますが起動時に次のようなエラーが出ます。
VLC media player 2.2.1 Terry Pratchett (Weatherwax) Command Line Interface initialized. Type `help' for help. > [0000000001229228] [cli] lua interface error: Error loading script [vlc]/share/lua/intf/cli.luac: lua/intf/modules/host.lua:279: Interrupted. [00000000011224f8] core libvlc: Running vlc with the default interface. Use 'cvlc' to use vlc without interface. [0000000001229228] core interface error: corrupt module: [vlc dir]/modules/gui/qt4/.libs/libqt4_plugin.so [00007f194c01f458] core generic error: corrupt module: [vlc dir]/modules/gui/qt4/.libs/libqt4_plugin.so [0000000001229228] skins2 interface error: no suitable dialogs provider found (hint: compile the qt4 plugin, and make sure it is loaded properly) [0000000001229228] skins2 interface error: cannot instantiate qt4 dialogs provider [0000000001229228] [cli] lua interface: Listening on host
つまり、libqt4がライブラリとして不完全ということです。この原因を探るため、secstotimestrでgrepをかけ、millisecstotimestrとの差を見てみます。すると、なにやら怪しげなファイルにヒットします。
[vlc dir]/src/libvlccore.sym: 358 sdp_AddAttribute 359 sdp_AddMedia 360 secstotimestr 361 services_discovery_AddItem 362 services_discovery_EventManager
どうやらこのlibvlccore.symというファイルは、ライブラリに含まれるシンボルが集まったファイルのようです。 libvlccore.symで再度プロジェクト全体に対してgrepをかけてみます。すると、/src/Makefile.am内に
libvlccore_la_SOURCES = $(SOURCES_libvlc) libvlccore_la_LDFLAGS = \ $(LDFLAGS_libvlccore) \ -no-undefined \ -export-symbols $(srcdir)/libvlccore.sym \ -version-info 8:0:0 libvlccore_la_LIBADD = $(LIBS_libvlccore) \ ../compat/libcompat.la \ $(LTLIBINTL) $(LTLIBICONV) \ $(IDN_LIBS) $(LIBPTHREAD) $(SOCKET_LIBS) $(LIBDL) $(LIBM) libvlccore_la_DEPENDENCIES = libvlccore.sym if HAVE_WIN32
とあります。-export-symbolsオプションについてGNU libtoolのドキュメントを見てみると、確かに
-export-symbols symfile
Tells the linker to export only the symbols listed in symfile. The symbol file should end in .sym and must contain the name of one > symbol per line. This option has no effect on some platforms. By default all symbols are exported.
ということらしいです。
つまり、shared libraryでは、望まないシンボルをライブラリに含めないよう、シンボルの指定ができるということです。そのため、ライブラリに含まれないmillisecstotimestrを呼び出そうとするとエラーが起きていたのでした。なるほどなるほど。
ということで、ここにmillisecstotimestrを一行追加して再automake・コンパイルします。これでOKです。
実行結果
ヨッシャヨッシャ!!