1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: MIT
3
4slint::include_modules!();
5
6use ffmpeg_next::format::Pixel;
7
8mod player;
9
10fn main() {
11 let app = App::new().unwrap();
12
13 let mut to_rgba_rescaler: Option<Rescaler> = None;
14
15 let mut player = player::Player::start(
16 "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/TearsOfSteel.mp4".into(),
17 {
18 let app_weak = app.as_weak();
19
20 move |new_frame| {
21 // TODO: use OpenGL bridge
22
23 let rebuild_rescaler =
24 to_rgba_rescaler.as_ref().map_or(true, |existing_rescaler| {
25 existing_rescaler.input().format != new_frame.format()
26 });
27
28 if rebuild_rescaler {
29 to_rgba_rescaler = Some(rgba_rescaler_for_frame(new_frame));
30 }
31
32 let rescaler = to_rgba_rescaler.as_mut().unwrap();
33
34 let mut rgb_frame = ffmpeg_next::util::frame::Video::empty();
35 rescaler.run(&new_frame, &mut rgb_frame).unwrap();
36
37 let pixel_buffer = video_frame_to_pixel_buffer(&rgb_frame);
38 app_weak
39 .upgrade_in_event_loop(|app| {
40 app.set_video_frame(slint::Image::from_rgb8(pixel_buffer))
41 })
42 .unwrap();
43 }
44 },
45 {
46 let app_weak = app.as_weak();
47
48 move |playing| {
49 app_weak.upgrade_in_event_loop(move |app| app.set_playing(playing)).unwrap();
50 }
51 },
52 )
53 .unwrap();
54
55 app.on_toggle_pause_play(move || {
56 player.toggle_pause_playing();
57 });
58
59 app.run().unwrap();
60}
61
62// Work around https://github.com/zmwangx/rust-ffmpeg/issues/102
63#[derive(derive_more::Deref, derive_more::DerefMut)]
64struct Rescaler(ffmpeg_next::software::scaling::Context);
65unsafe impl std::marker::Send for Rescaler {}
66
67fn rgba_rescaler_for_frame(frame: &ffmpeg_next::util::frame::Video) -> Rescaler {
68 Rescaler(
69 ffmpeg_nextResult::software::scaling::Context::get(
70 src_format:frame.format(),
71 src_w:frame.width(),
72 src_h:frame.height(),
73 dst_format:Pixel::RGB24,
74 dst_w:frame.width(),
75 dst_h:frame.height(),
76 flags:ffmpeg_next::software::scaling::Flags::BILINEAR,
77 )
78 .unwrap(),
79 )
80}
81
82fn video_frame_to_pixel_buffer(
83 frame: &ffmpeg_next::util::frame::Video,
84) -> slint::SharedPixelBuffer<slint::Rgb8Pixel> {
85 let mut pixel_buffer: SharedPixelBuffer> =
86 slint::SharedPixelBuffer::<slint::Rgb8Pixel>::new(frame.width(), frame.height());
87
88 let ffmpeg_line_iter: ChunksExact<'_, u8> = frame.data(0).chunks_exact(chunk_size:frame.stride(index:0));
89 let slint_pixel_line_iter: ChunksMut<'_, u8> = pixel_buffer
90 .make_mut_bytes()
91 .chunks_mut(chunk_size:frame.width() as usize * core::mem::size_of::<slint::Rgb8Pixel>());
92
93 for (source_line: &[u8], dest_line: &mut [u8]) in ffmpeg_line_iter.zip(slint_pixel_line_iter) {
94 dest_line.copy_from_slice(&source_line[..dest_line.len()])
95 }
96
97 pixel_buffer
98}
99