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 let video_receiver = options.with_video();
18
19 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 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 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 match video_receiver.blocking_recv() {
77 Some(frame) => {
78 match decoder.decode(&frame.data) {
80 Ok(Some(f)) => {
81 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}