extern crate tello_edu;
use tello_edu::{TelloOptions, Tello, Result, VIDEO_WIDTH, VIDEO_HEIGHT, TelloVideoReceiver};
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::pixels::PixelFormatEnum;
use sdl2::rect::Rect;
use openh264::decoder::Decoder;
use openh264::formats::YUVSource;
fn main() {
let mut options = TelloOptions::default();
let video_receiver = options.with_video();
std::thread::spawn(move || {
let tokio_runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
tokio_runtime.block_on(async {
fly(options).await.unwrap();
});
});
run_gui(video_receiver).unwrap();
}
fn run_gui(mut video_receiver:TelloVideoReceiver) -> std::result::Result<(), String> {
let sdl_context = sdl2::init()?;
let video_subsystem = sdl_context.video()?;
let window = video_subsystem
.window("tello-edu", VIDEO_WIDTH, VIDEO_HEIGHT)
.position_centered()
.opengl()
.build()
.map_err(|e| e.to_string())?;
let mut canvas = window.into_canvas().build().map_err(|e| e.to_string())?;
let texture_creator = canvas.texture_creator();
let mut texture = texture_creator
.create_texture_streaming(PixelFormatEnum::IYUV, VIDEO_WIDTH, VIDEO_HEIGHT)
.map_err(|e| e.to_string())?;
canvas.clear();
canvas.present();
let mut event_pump = sdl_context.event_pump()?;
let mut video_channel_open = true;
let mut decoder = Decoder::new().unwrap();
'running: loop {
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => break 'running,
_ => {}
}
}
if video_channel_open {
match video_receiver.blocking_recv() {
Some(frame) => {
match decoder.decode(&frame.data) {
Ok(Some(f)) => {
texture.update_yuv(
None,
f.y(), f.y_stride() as usize,
f.u(), f.u_stride() as usize,
f.v(), f.v_stride() as usize
).expect("failed to update texture");
canvas.copy(&texture, None, Some(Rect::new(0, 0, VIDEO_WIDTH, VIDEO_HEIGHT)))?;
canvas.present();
}
Ok(None) => {
println!("incomplete frame, dropped");
}
Err(err) => {
println!("h264 decoder error: {err})");
}
}
}
None => {
println!("VIDEO END");
video_channel_open = false;
}
}
}
}
Ok(())
}
async fn fly(options:TelloOptions) -> Result<()> {
let drone = Tello::new()
.wait_for_wifi().await?;
let drone = drone.connect_with(options).await?;
drone.start_video().await?;
drone.take_off().await?;
drone.turn_clockwise(360).await?;
drone.land().await?;
Ok(())
}