こんな感じで
Rust で書くと
use gstreamer as gst;
use gst::prelude::*;
let pipeline = gst::parse_launch("filesrc name=filesrc ! decodebin force-sw-decoders=true name=dec ! capsfilter caps=video/x-raw ! filesink sync=false name=filesink")?;
let bus = pipeline.bus()?;
let filesrc = pipeline.by_name("filesrc")?;
filesrc.set_property("location", src_path);
let filesink = pipeline.by_name("filesink")?;
filesink.set_property("location", sink_path);
pipeline.set_state(gst::State::Playing)?;
for message in bus.iter_timed(None) {
match message.view() {
gst::MessageView::Error(err) => todo!(),
gst::MessageView::Eos(_) => break,
_ => (),
}
}
pipeline.set_state(gst::State::Null)?;
適当に書いてるので動かないかもだけど、多分こんなん
上のコードの問題点
もし任意の raw format を受け付けるなら VideoInfo などの情報もどこかに保存しておかなければならない
あと、途中で format が変わることが考慮されてない
基本的には appsink とかで、細かくイベントハンドリングすればいいと思うが、以下のように pad_probe で雑に実装することもできる。
以下のように caps と flush_stop と buffer あたりをちゃんと見ておく必要がある
use gstreamer_video as gst_video;
struct ThreadData {
video_info: Option<gst_video::VideoInfo>,
}
let pad = filesnk.static_pad("sink").unwrap();
let thread_data = Arc::new(Mutex::new(ThreadData { video_info: None }))
{
let thread_data_weak = Arc::downgrade(&thread_data);
pad.add_probe(gst::PadProbeType::EVENT_FLUSH, move |_pad, info| {
let Some(thread_data) = thread_data_weak.upgrade() else {
return gst::PadProbeReturn::Remove;
};
match &info.data {
Some(gst::PadProbeData::Event(event)) => match event.view() {
gst::EventView::FlushStop(_) => {
todo!() // seek したりなどしたときに発生し filesink で fseek(fp, 0) のようなことが起こるので、対処が必要なことがある
_ => (),
},
_ => unreachable!(),
};
});
}
{
let thread_data_weak = Arc::downgrade(&thread_data);
pad.add_probe(gst::PadProbeType::EVENT_DOWNSTREAM, move |_pad, _info| {
// caps から VideoInfo を取得して、この raw file を読む的に必要な raw format の情報を得る
let Some(thread_data) = thread_data_weak.upgrade() else {
return gst::PadProbeReturn::Remove;
};
let mut thread_data = thread_data.lock().unwrap();
match &info.data {
Some(gst::PadProbeData::Event(event)) => match event.view() {
gst::EventView::Caps(caps) => {
let caps = caps.caps_owned();
let thread_data = thread_data.deref();
let mut thread_data = thread_data.lock().unwrap();
thread_data.video_info = gst_video::VideoInfo::from_caps(&caps).unwrap();
},
_ => (),
},
_ => unreachable!(),
}
gst::PadProbeReturn::Ok
});
}
{
let thread_data_weak = Arc::downgrade(&thread_data);
pad.add_probe(gst::PadProbeType::BUFFER, move |_pad, _info| {
// buffer を保存する前に video_info が変化していないかなどの考慮が必要
// preroll 時の buffer がきて、そのあと seek が起こってすれられるパターンなどもあり、
// ここにくる buffer が全て filesink で書かれるということでもないので注意
let Some(thread_data) = thread_data_weak.upgrade() else {
return gst::PadProbeReturn::Remove;
};
let thread_data = thread_data.deref();
let mut thread_data = thread_data.lock().unwrap();
todo!(); // ここにバッファを保存するかなど video_info を見て何か処理をする
});
}
raw file を読むときは rawvideoparse で読む
let pipeline = gst::parse_launch("filesrc name=filesrc ! rawvideoparse name=rawvideoparse ! ...省略")?;
let filesrc = pipeline.by_name("filesrc")?;
filesrc.set_property("location", src_path);
// 書き出した時に caps から取得した VideoInfo を使う
let rawvideoparse = pipeline.by_name("rawvideoparse")?;
rawvideoparse.set_property("width", video_info.width() as i32);
rawvideoparse.set_property("height", video_info.height() as i32);
rawvideoparse.set_property("format", video_info.format());
let framerate = video_info.fps();
if framerate.numer() != 0 {
rawvideoparse.set_property("framerate", framerate);
}
rawvideoparse.set_property("pixel-aspect-ratio", video_info.par());
if video_info.is_interlaced() {
rawvideoparse.set_property("interlaced", true);
rawvideoparse.set_property("ttf", video_info.field_order() == gst_video::VideoFieldOrder::TopFieldFirst);
} else {
rawvideoparse.set_property("interlaced", false);
}
rawvideoparse.set_property("plane-strides", gst::Array::new(video_info.stride().into_iter().map(|n| *n as i32)).to_value());
rawvideoparse.set_property("plane-offsets", gst::Array::new(video_info.offset().into_iter().map(|n| *n as i32)).to_value());
rawvideoparse.set_property("frame-size", video_info.size() as u32);
rawvideoparse.set_property("colorimetry", video_info.colorimetry().to_string());
pipeline.set_state(gst::State::Playing)?;
for message in bus.iter_timed(None) {
match message.view() {
gst::MessageView::Error(err) => todo!(),
gst::MessageView::Eos(_) => break,
_ => (),
}
}
pipeline.set_state(gst::State::Null)?;