I.はじめに
libmpvを使用するプロセスでは、mpvイベントにサブスクライブすることで、正常なファイルのオープン、再生状態の変更などの一部のイベントをより正確かつ時間どおりに知ることができます。状態を読み取るためのタイマー、特に正常なオープンは必要ありません。イベント、イベントサブスクリプションを使用しない場合、ビデオストリームがしばらくの間スタックする場合があります。たとえば、ビデオストリームがない場合やネットワークが良好でない場合、この状況を回避する方法は2つあります。vlcとffmpegのデコードでも同様です。これを実行するためにスレッドに直接開く方法があります。元々、デコードプロセスは完全なスレッドクラスであるため、フラグビットを変更することでスレッド内で直接初期化が実行されます。スタックしないようにする必要はありません。別の方法はイベントコールバックを使用して、正常に開いた後、ビデオの幅と高さの読み取りなどの他の処理を実行します。これらの情報は通常、ファイルが正常に開いた後でのみ読み取ることができます。
mpvがイベントサブスクリプションもサポートしていることは間違いありません。サブスクライブする必要があるプロパティイベントの変更は、mpv_observe_property関数を介してイベントサブスクリプションキューに追加されます。この関数には4つのパラメーターがあります。最初のパラメーターはmpvオブジェクト(mpv_createによって生成)を参照し、2番目のパラメーターはユーザーデータは、必要ない場合は直接0に記入してください。通常は不要です。3つ目のパラメーターは属性名です。属性名は公式サイト(http://mpv.io/manual/master/#)のマニュアルを参照してください。プロパティ)、4番目のパラメータはプロパティのフォーマットタイプを参照します。一般的に言えば、これらの属性イベントの変更をサブスクライブします:期間(ファイルの長さ)、時間pos(現在の再生の進行状況)。イベントがサブスクライブされたら、mpv_set_wakeup_callback関数を実行して、イベントコールバック関数の処理を設定します。
2.機能
- マルチスレッドのリアルタイム再生ビデオストリーム+ローカルビデオなど
- windows + linux + macをサポートします。
- メインインターフェイスにスタックされていないマルチスレッドディスプレイイメージ。
- Webカメラを自動的に再接続します。
- ファイルに保存するかどうか、ファイル名を設定できます。
- ファイルをmpvwidgetコントロールに直接ドラッグして再生できます。
- h265ビデオストリーム+ rtmpなどの一般的なビデオストリームをサポートします。
- 再生を一時停止して再開できます。
- 単一のビデオファイルのストレージとビデオファイルのタイミングストレージをサポートします。
- 上部のフローティングバーをカスタマイズし、クリック信号通知を送信して、それを有効にするかどうかを設定します。
- 画面のストレッチ塗りつぶしまたは同じ比率の塗りつぶしを設定できます。
- スクリーンショット(元の写真)とビデオのスクリーンショットを撮ることができます。
- ビデオファイルはMP4ファイルを格納します。
- qsv、dxva2、d3d11vaなどのハードデコードをサポートします。
3、レンダリング
4、関連サイト
- 国内サイト:https : //gitee.com/feiyangqingyun/QWidgetDemo
- 国際サイト:https : //github.com/feiyangqingyun/QWidgetDemo
- 個人ホームページ:https : //blog.csdn.net/feiyangqingyun
- Zhihuホームページ:https ://www.zhihu.com/people/feiyangqingyun/
- エクスペリエンスアドレス:https : //blog.csdn.net/feiyangqingyun/article/details/97565652
5、コアコード
//事件处理
static void handleEvents(mpv_handle *mpvPlayer, mpv_event *event, MpvThread *thread)
{
switch (event->event_id) {
case MPV_EVENT_PROPERTY_CHANGE: {
mpv_event_property *property = (mpv_event_property *)event->data;
QString propertyName = property->name;
mpv_format propertyFormat = property->format;
if (propertyName == "duration") {
//文件总长度
if (propertyFormat == MPV_FORMAT_DOUBLE) {
double time = *(double *)property->data;
uint length = time * 1000;
thread->doEvent(1, QVariantList() << length);
qDebug() << TIMEMS << "文件总长: " << length;
}
} else if (propertyName == "time-pos") {
//当前播放进度
if (propertyFormat == MPV_FORMAT_DOUBLE) {
double time = *(double *)property->data;
uint position = time * 1000;
thread->doEvent(2, QVariantList() << position);
//qDebug() << TIMEMS << "当前时间: " << position;
}
} else {
qDebug() << TIMEMS << propertyName;
if (propertyFormat == MPV_FORMAT_NODE) {
#if 0
QVariant data = qtmpv::node_to_variant((mpv_node *)property->data);
QJsonDocument json = QJsonDocument::fromVariant(data);
qDebug() << TIMEMS << json.toJson().data();
#endif
}
}
break;
}
//获取宽高
case MPV_EVENT_VIDEO_RECONFIG: {
//这里会执行两次,不知道为什么
int dwidth, dheight;
mpv_get_property(mpvPlayer, "dwidth", MPV_FORMAT_INT64, &dwidth);
mpv_get_property(mpvPlayer, "dheight", MPV_FORMAT_INT64, &dheight);
qDebug() << TIMEMS << "dwidth:" << dwidth << "dheight:" << dheight;
break;
}
//打印日志
case MPV_EVENT_LOG_MESSAGE: {
struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data;
QString data = QString("[%1] %2: %3").arg(msg->prefix).arg(msg->level).arg(msg->text);
data.replace("\r", "");
data.replace("\n", "");
//qDebug() << TIMEMS << data;
break;
}
//文件播放结束
case MPV_EVENT_END_FILE: {
thread->doEvent(0, QVariantList());
break;
}
default:
break;
}
}
//订阅事件
static void attachEvents(mpv_handle *mpvPlayer)
{
mpv_observe_property(mpvPlayer, 0, "duration", MPV_FORMAT_DOUBLE);
mpv_observe_property(mpvPlayer, 0, "time-pos", MPV_FORMAT_DOUBLE);
mpv_observe_property(mpvPlayer, 0, "track-list", MPV_FORMAT_NODE);
mpv_observe_property(mpvPlayer, 0, "chapter-list", MPV_FORMAT_NODE);
}