show_video/
show_video.rs

1extern crate tello_edu;
2
3use tello_edu::{TelloOptions, Tello, Result, VIDEO_WIDTH, VIDEO_HEIGHT, TelloVideoReceiver};
4
5use sdl2::event::Event;
6use sdl2::keyboard::Keycode;
7use sdl2::pixels::PixelFormatEnum;
8use sdl2::rect::Rect;
9
10use openh264::decoder::Decoder;
11use openh264::formats::YUVSource;
12
13fn main() {
14    let mut options = TelloOptions::default();
15
16    // we want video...
17    let video_receiver = options.with_video();
18
19    // run async Tokio runtime in a thread...
20    std::thread::spawn(move || {
21        let tokio_runtime = tokio::runtime::Builder::new_multi_thread()
22            .enable_all()
23            .build()
24            .unwrap();
25
26        tokio_runtime.block_on(async {
27            fly(options).await.unwrap();
28        });
29    });
30
31    // ...because SDL must run on the main thread
32    run_gui(video_receiver).unwrap();
33}
34
35fn run_gui(mut video_receiver:TelloVideoReceiver) -> std::result::Result<(), String> {
36    let sdl_context = sdl2::init()?;
37    let video_subsystem = sdl_context.video()?;
38
39    let window = video_subsystem
40        .window("tello-edu", VIDEO_WIDTH, VIDEO_HEIGHT)
41        .position_centered()
42        .opengl()
43        .build()
44        .map_err(|e| e.to_string())?;
45
46    let mut canvas = window.into_canvas().build().map_err(|e| e.to_string())?;
47    let texture_creator = canvas.texture_creator();
48
49    let mut texture = texture_creator
50        .create_texture_streaming(PixelFormatEnum::IYUV, VIDEO_WIDTH, VIDEO_HEIGHT)
51        .map_err(|e| e.to_string())?;
52
53    canvas.clear();
54    canvas.present();
55
56    let mut event_pump = sdl_context.event_pump()?;
57
58    let mut video_channel_open = true;
59    let mut decoder = Decoder::new().unwrap();
60
61    'running: loop {
62        // SDL event loop
63        for event in event_pump.poll_iter() {
64            match event {
65                Event::Quit { .. }
66                | Event::KeyDown {
67                    keycode: Some(Keycode::Escape),
68                    ..
69                } => break 'running,
70                _ => {}
71            }
72        }
73
74        if video_channel_open {
75            // wait for next encoded frame of video
76            match video_receiver.blocking_recv() {
77                Some(frame) => {
78                    // decode h264 to YUV
79                    match decoder.decode(&frame.data) {
80                        Ok(Some(f)) =>  {
81                            // draw frame
82                            texture.update_yuv(
83                                None,
84                                f.y(), f.y_stride() as usize,
85                                f.u(), f.u_stride() as usize,
86                                f.v(), f.v_stride() as usize
87                            ).expect("failed to update texture");
88
89                            canvas.copy(&texture, None, Some(Rect::new(0, 0, VIDEO_WIDTH, VIDEO_HEIGHT)))?;
90                            canvas.present();
91                        }
92                        Ok(None) => {
93                            println!("incomplete frame, dropped");
94                        }
95                        Err(err) => {
96                            println!("h264 decoder error: {err})");
97                        }
98                    }
99                }
100                None => {
101                    println!("VIDEO END");
102                    video_channel_open = false;
103                }
104            }
105        }
106    }
107
108    Ok(())
109}
110
111async fn fly(options:TelloOptions) -> Result<()> {
112    let drone = Tello::new()
113        .wait_for_wifi().await?;
114
115    let drone = drone.connect_with(options).await?;
116
117    drone.start_video().await?;
118
119    drone.take_off().await?;
120    drone.turn_clockwise(360).await?;
121    drone.land().await?;
122
123    Ok(())
124}