ffmpeg だと
ffmpeg -ss 100 -t 200 -i input.mp4 output.mp4
みたいな感じで 100 秒から 200 秒のメディアを切り出せる
Gstreamer だとどうするんだっけ
paused にしてから seek したりすると preroll 時に sample が一個来てしまうので、 それを sink がどう扱うかとか、element の実装をしっている必要がありそう。
結論
基本以下の流れでいい
- pipeline を作る
- pipeline を PAUSED state にする
- pipeline を seek する(ここで開始時間、終了時間を指定する)
- pipeline を PLAYING state にする
- pipeline が EOS まで行ったら NULL state にする
そして以下の 2 点だけ確認する
- sink 要素が videosink を継承したものなら show-preroll-frame=false にしないと最初の 1 フレームが混入する
- pipeline の中間で、 pad_probe などで流れる buffer を処理する場合は preroll なフレームも混ざってくるので、それなりに複雑な処理が必要。
ポイント
- basesink の実装では preroll メソッドのと render メソッドは別々の処理として実装されている
- PAUSED な状態で seek しても preroll メソッドは呼び直されるが render メソッドは呼ばれない
- その後 PLAING になれば、最後に preroll メソッドに渡された sample が render メソッドに渡される
- 基本的に basesink の子クラスは render メソッドで sample を処理していると思うので preroll された sample が render メソッドで処理されることはない
- PAUSED な状態で seek しても preroll メソッドは呼び直されるが render メソッドは呼ばれない
- 落とし穴
- videosink がベースとなっている sink element ではデフォルトで preroll メソッドも render メソッドと同じ sample 処理のフローに入る(!)。これは show-preroll-frame というプロパティが立っている場合に preroll メソッドがそのような処理をするように videosink に書かれているから
- この挙動は player アプリなどを実装する人には有用かもしれないが、ストリームの処理をするツールとかを作っている人には不要なので show-preroll-frame=false にして使うと良い。
- pad_probe とかで buffer を監視していると preroll の buffer も render されるべき buffer もくるので、その辺でちゃんと扱わなければならない。
- videosink がベースとなっている sink element ではデフォルトで preroll メソッドも render メソッドと同じ sample 処理のフローに入る(!)。これは show-preroll-frame というプロパティが立っている場合に preroll メソッドがそのような処理をするように videosink に書かれているから
以下雑な調査
色々気になる公式のドキュメントの記述を調べる
-
https://gstreamer.freedesktop.org/documentation/gstreamer/gstelement.html?gi-language=c
- gst_element_set_start_time
- Set the start time of an element. The start time of the element is the running time of the element when it last went to the PAUSED state. In READY or after a flushing seek, it is set to 0.
- running_time とかは gstreamer で厳密な定義がかる
- Toplevel elements like GstPipeline will manage the start_time and base_time on its children. Setting the start_time to GST_CLOCK_TIME_NONE on such a toplevel element will disable the distribution of the base_time to the children and can be useful if the application manages the base_time itself, for example if you want to synchronize capture from multiple pipelines, and you can also ensure that the pipelines have the same clock.
- いまいちわからん。
- PAUSED (再生直前の状態) の running_time を指しているということはわかるが、設定すると具体的にどうなるのか。どういう state の時に設定できるのかがわからん。
- コードを読んでみた
- start_time はそんなに使われてなくて、基本的には設定しても base_time をずらすくらいしか影響はないように見える。
- 試しにやってみてもいい。あとでやってもいいかな
- start_time はそんなに使われてなくて、基本的には設定しても base_time をずらすくらいしか影響はないように見える。
- Set the start time of an element. The start time of the element is the running time of the element when it last went to the PAUSED state. In READY or after a flushing seek, it is set to 0.
- gst_element_set_start_time
-
https://gstreamer.freedesktop.org/documentation/gstreamer/gstelement.html?gi-language=c#gst_element_seek_simple
- gst_element_seek_simpl
- Some elements allow for seeking in the READY state, in this case they will store the seek event and execute it when they are put to PAUSED. If the element supports seek in READY, it will always return TRUE when it receives the event in the READY state.
- READY で seek できる element もあるっぽい。 decodebin とかは dynamic に pipeline structure が変わるので、 preroll の過程が必要だとは思うし、 decodebin や parsebin が READY 時に seek できないことは過去に確認ずみ。
- simple に demux ! decode ! sink みたいな構成なら基本 READY できるのかな?そのへん、個別に decoder の実装を自分で確認する必要があるならかなり大変になりそう。
- Some elements allow for seeking in the READY state, in this case they will store the seek event and execute it when they are put to PAUSED. If the element supports seek in READY, it will always return TRUE when it receives the event in the READY state.
- gst_element_seek_simpl
-
https://gstreamer.freedesktop.org/documentation/application-development/advanced/pipeline-manipulation.html?gi-language=c#play-a-section-of-a-media-file
- ここに media ファイルのセクションを再生するサンプルコードがあるが、これだと prerolled の sample は再生されてしまいそうな気がする。
コード読む
github に mirror されてるコードの方が検索性が高いのでおすすめ。
-
https://github.com/GStreamer/gstreamer/blob/63bb0b8de7fb7c206b2d9a598226df401f3863e0/subprojects/gstreamer/libs/gst/base/gstbasesink.c#L2432
- basesink が render を呼んで、 sink が sample を処理するのが普通の流れなので、 preroll された sample が render にわたってないことを確認すれば良さそう?普通に basesink の render を実装する限りは preroll されたものが Playing なしに直ちに処理されなさそうに見える。試してみないとはっきり言えないけど
-
https://github.com/GStreamer/gstreamer/blob/63bb0b8de7fb7c206b2d9a598226df401f3863e0/subprojects/gst-plugins-base/gst-libs/gst/video/gstvideosink.c#L47
- 一方 videosink は show-preroll-frame というプロパティがあり、これが true だと render と preroll が両方とも show_frame にわたってしまう。
あ〜。そういうことか。理解した。