I am trying to create multiple thumbnails (8-12) from a video source. I followed the example in the gilab and am able to successfully produce a thumbnail with a height of 180.
However I am unable to create multiple thumbnails from the same pipeline, as I am unaware of any reliable way of tracking a seek being finished. I checked the message API on gstreamer and did not find anything. Without a way of knowing if seek is done (and a thumbnail sample captured) all the seeks flush each other and only the last one would generate a thumbnail.
Currently I am spawning 12 threads, each of which create their own pipeline, seek once and then destroy it. This takes ~4 secs for a 23 minute video. I ran a profiler and my 2 thumbnail functions (launch threads and the example code) are both < 1% of the total runtime with most of it being gstreamer stuff. Of patricular note were the following
- 18.5%
gst_pad_push
(15.8%gst_base_parse_push_frame
) - 11.6%
gst_data_deque_pop
- 24%
libgstmatroska
The following is my thread spawning code. The create_thumbnail function is essentially the code from the example I posted prior.
fn thumbnail_thread(video_uri: String, video_duration: ClockTime) {
let step = video_duration.mseconds() / (NUM_THUMBNAILS + 2); // + 2 so first and last frame not chosen
let barrier = Arc::new(Barrier::new((NUM_THUMBNAILS + 1) as usize));
for i in 0..NUM_THUMBNAILS {
let uri = video_uri.clone();
let barrier = barrier.clone();
thread::spawn(move || {
let save_path = std::path::PathBuf::from(format!("/{}/thumbnail_{}.jpg", THUMBNAIL_PATH, i));
let timestamp = gst::GenericFormattedValue::from(ClockTime::from_mseconds(step + (step * i)));
let pipeline = VideoPlayerModel::create_thumbnail(save_path, uri).expect("could not create thumbnail pipeline");
pipeline.set_state(gst::State::Paused).unwrap();
let bus = pipeline.bus().expect("Pipeline without a bus.");
let mut seeked = false;
for msg in bus.iter_timed(ClockTime::NONE) {
use gst::MessageView;
match msg.view() {
MessageView::AsyncDone(..) => {
if !seeked {
if pipeline.seek_simple(SeekFlags::FLUSH | SeekFlags::KEY_UNIT, timestamp).is_err()
{
println!("Failed to seek");
}
pipeline.set_state(gst::State::Playing).unwrap();
seeked = true;
}
}
MessageView::Eos(..) => break,
_ => ()
}
}
pipeline.set_state(gst::State::Null).unwrap();
barrier.wait();
});
}
barrier.wait();
}
I think an solution to any of the following would help me:
- a way to know when a seek is done
- speed up pipeline creation/initialization
- get samples at timestamps another way then seeking