#![allow(unused_imports)] #![allow(unused_variables)] use clap::{App, Arg}; use himpp::{ hifb, hifb::{FrameBuffer, PixelFormat as HifbPixFmt}, himipi::{ComboDevAttrBuilder, DataType as MipiDataType, LaneDivideMode, MipiReceiver}, hitde::{ TdeCanvas, TdeColor, TdeColorFmt, TdeDev, TdeJob, TdeLine, TdeMemory, TdeOptBuilder, TdeOutAlphaFrom, TdePatternFillOpt, TdeRect, TdeSurfaceLoader, }, mpi::hdmi::{HdmiCore, HdmiId, HdmiPortBuilder}, mpi::sys::{SysBinder, SysManager, ViVpssMode}, mpi::types::{DynamicRange, PixelFormat, Rect, Size, VideoInterface, VideoStandard, WdrMode}, mpi::vb::{VbConfigBuilder, VbManager, VbRemapMode}, mpi::vi::{ ViChn, ViChnAttrBuilder, ViDev, ViDevAttrBuilder, ViDumpType, ViPipe, ViPipeAttrBuilder, ViRephaseMode, }, mpi::vo::{VoChn, VoChnAttrBuilder, VoDev, VoLayer, VoPubAttrBuilder, VoVideoLayerAttrBuilder}, mpi::vpss::{ VpssChn, VpssChnAttrBuilder, VpssChnMode, VpssCropInfo, VpssGrp, VpssGrpAttrBuilder, }, mpi::{sys, vb}, }; use pavo_traits::AlignUpwards; use poller::{EventContext, Events, Poller}; use std::error::Error; use std::fs::File; use std::io::prelude::*; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use std::thread::{sleep, spawn, Builder as ThreadBuilder, JoinHandle, Thread}; use std::time::Duration; enum MoveDirection { Left, Right, Up, Down, } fn move_direction(rect: &Rect, clip: &Rect) -> MoveDirection { let tl1 = rect.top_left(); let tl2 = clip.top_left(); let tr1 = rect.top_right(); let tr2 = clip.top_right(); let bl1 = rect.bottom_left(); let bl2 = clip.bottom_left(); let br1 = rect.bottom_right(); let br2 = clip.bottom_right(); if tl1 == tl2 || (tr1.x < tr2.x && tr1.y == tr2.y) { MoveDirection::Right } else if tr1 == tr2 || (br1.y < br2.y && br1.x == br2.x) { MoveDirection::Down } else if br1 == br2 || (bl1.x > bl2.x && bl1.y == bl2.y) { MoveDirection::Left } else if bl1 == bl2 || (tl1.y > tl2.y && tl1.x == tl2.x) { MoveDirection::Up } else { MoveDirection::Right } } fn move_rect(rect: &Rect, clip: &Rect, speed: isize) -> Rect { match move_direction(rect, clip) { MoveDirection::Left => rect.translated(-speed, 0), MoveDirection::Right => rect.translated(speed, 0), MoveDirection::Up => rect.translated(0, -speed), MoveDirection::Down => rect.translated(0, speed), } } fn tde_render() {} struct TdeRenderer { fb: Arc>, tde: Arc>, pic: TdeSurfaceLoader, pic_rect: TdeRect, moved_rect: TdeRect, time: usize, thread: Option>, } impl TdeRenderer { fn new(fb: Arc>, tde: Arc>) -> Result> { let pic = TdeSurfaceLoader::from_bytes( include_bytes!("data/images/dog_bike_car.jpg"), Some(TdeColorFmt::Argb8888), )?; let pic_rect = Rect::with_size(pic.size()); let fixed_rect = pic_rect.translated(10, 10); let moved_rect = pic_rect; Ok(Self { fb, tde, pic, pic_rect, moved_rect, time: 0, thread: None, }) } fn render_frame(&mut self) -> Result<(), Box> { let mut fb = self.fb.lock().unwrap(); let tde = self.tde.lock().unwrap(); // Create the surface from hifb for drawing. let dst = TdeSurfaceLoader::from_hifb(&fb)?; let dst_rect = Rect::with_size(dst.size()); // Move the dynamic picture display rect. let speed = ((self.time as f32).to_radians().sin() * 10.0f32).abs() as isize; self.moved_rect = move_rect(&self.moved_rect, &dst_rect, speed + 1).clipped(&dst_rect); // Wait for vblank ready. // fb.vblank()?; // Create a new canvas to drawing. let mut canvas = TdeCanvas::new(&tde, &dst, &dst_rect); // Clear the canvas with blue color. canvas.clear(None, TdeColor::packed(0x0000_0000))?; // // Drw fixed picture with box-shadow. // canvas.draw_pixmap(&pic, &fixed_rect, 0xff)?; // canvas.box_shadow(&fixed_rect, 0xf0)?; // Drw moved picture with box-shadow. canvas.draw_pixmap(&self.pic, &self.moved_rect, 0xf0)?; canvas.box_shadow(&self.moved_rect, 0xd0)?; // Draw cross lines. let color = TdeColor::packed(0xb0ff_ff00); let x = (dst_rect.width() / 4) as i32; let y = (dst_rect.height() / 2) as i32; let w = (dst_rect.width() / 2) as i32; canvas.draw_straight_line(&TdeLine::new_hline(x, y, w, color, 3))?; let x = (dst_rect.width() / 2) as i32; let y = (dst_rect.height() / 4) as i32; let h = (dst_rect.height() / 2) as i32; canvas.draw_straight_line(&TdeLine::new_vline(x, y, h, color, 3))?; // Refresh display buffers. fb.refresh()?; self.time += 1; Ok(()) } fn wait_for_vblank(&self) -> Result<(), Box> { let fb = self.fb.lock().unwrap(); fb.vblank()?; //.map_err(|err| err.into()) Ok(()) } } enum TdeRenderEvent { Pause, Refresh, Terminate, } struct TdeRenderThread { renderer: Arc>, alive: Arc, handle: Option>, events: Option>, } impl TdeRenderThread { pub fn new(renderer: Arc>) -> Self { Self { renderer, alive: Arc::new(AtomicBool::new(false)), handle: None, events: None, } } fn start(&mut self) { self.alive.store(true, Ordering::SeqCst); let alive = Arc::clone(&self.alive); let renderer = Arc::clone(&self.renderer); let (tx, rx) = channel(); self.events = Some(tx); self.handle = Some( ThreadBuilder::new() .name("TdeRender".into()) .spawn(move || { 'outer: while alive.load(Ordering::SeqCst) { let mut rdr = renderer.lock().unwrap(); rdr.wait_for_vblank().unwrap(); for ev in rx.try_iter() { match ev { TdeRenderEvent::Refresh => { rdr.render_frame().unwrap(); } TdeRenderEvent::Terminate => { break 'outer; } _ => {} } } } }) .unwrap(), ); } fn stop(&mut self) { self.alive.store(false, Ordering::SeqCst); self.handle .take() .expect("Called stop on non-running thread") .join() .expect("Could not join spawned thread"); } fn post_event(&self, event: TdeRenderEvent) { if let Some(ref tx) = self.events { tx.send(event).unwrap(); } } } fn main() -> Result<(), Box> { let matches = App::new("vi-vpss-vo") .version("1.0") .author("Varphone Wong ") .about("The example of pipeline: VI -> VPSS -> VO") .arg( Arg::with_name("framerate") .short("f") .long("framerate") .help("Set the Video Framerate") .takes_value(true), ) .arg( Arg::with_name("vi-size") .short("i") .long("vi-size") .help("Set the Video Input Size") .takes_value(true), ) .get_matches(); let fps: u32 = matches .value_of("framerate") .unwrap_or("25") .parse() .unwrap(); let vi_size = Size::from(matches.value_of("vi-size").unwrap_or("1280x720")); let vb_pool1 = (vi_size.width * vi_size.height * 3 / 2).align_upwards(64); let vb_pool2 = (vi_size.width * vi_size.height * 2).align_upwards(64); let vb_pool3: u32 = (1920 * 1080 * 3 / 2).align_upwards(64); let vb_conf = VbConfigBuilder::new() .pool(vb_pool1.into(), 8, VbRemapMode::None, "") .pool(vb_pool2.into(), 16, VbRemapMode::None, "") .pool(vb_pool3.into(), 3, VbRemapMode::None, "") .build(); let _vb_mgr = VbManager::new(&vb_conf, &[]); let _sys_mgr = SysManager::with_align(64, Some(_vb_mgr)); let vi_vpss_modes = [ ViVpssMode::BothOffline, // OnlineOffline ViVpssMode::BothOffline, ViVpssMode::BothOffline, ViVpssMode::BothOffline, ViVpssMode::BothOffline, ViVpssMode::BothOffline, ViVpssMode::BothOffline, ViVpssMode::BothOffline, ]; _sys_mgr.set_vi_vpss_mode(&vi_vpss_modes).unwrap(); let vo_size = Size::new(1920, 1080); let vo_attr = VoPubAttrBuilder::new() .with_bg_color(0x0000ffff) .with_video_interface(VideoInterface::Hdmi) .with_video_standard(VideoStandard::Hd1080p60) .build(); let vo_dev0 = Arc::new(VoDev::new(0, &vo_attr)); let vo_vl_attr = VoVideoLayerAttrBuilder::with_current(0) .with_disp_rect(Rect::with_size(vo_size)) .with_image_size(vo_size) .with_disp_framerate(60) .with_pixel_format(PixelFormat::YvuSemiPlanar420) .with_double_frame(false) .with_cluster_mode(false) .with_dynamic_range(DynamicRange::Sdr8) .build(); let vo_vl0 = Arc::new(VoLayer::new(0, &vo_vl_attr, Arc::clone(&vo_dev0))); vo_vl0.set_buf_len(3).unwrap(); vo_vl0.enable().unwrap(); let vo_chn0_attr = VoChnAttrBuilder::with_current(0, &vo_vl0) .with_priority(0) .with_disp_rect(Rect::new(0, 0, 960, 540)) .build(); let vo_chn0 = Arc::new(VoChn::new(0, &vo_chn0_attr, Arc::clone(&vo_vl0))); vo_chn0.show()?; let vo_chn1_attr = VoChnAttrBuilder::with_current(1, &vo_vl0) .with_priority(0) .with_disp_rect(Rect::new(960, 0, 960, 540)) .build(); let vo_chn1 = Arc::new(VoChn::new(1, &vo_chn1_attr, Arc::clone(&vo_vl0))); vo_chn1.show()?; let vo_chn2_attr = VoChnAttrBuilder::with_current(2, &vo_vl0) .with_priority(0) .with_disp_rect(Rect::new(0, 540, 960, 540)) .build(); let vo_chn2 = Arc::new(VoChn::new(2, &vo_chn2_attr, Arc::clone(&vo_vl0))); vo_chn2.show()?; let vo_chn3_attr = VoChnAttrBuilder::with_current(3, &vo_vl0) .with_priority(0) .with_disp_rect(Rect::new(960, 540, 960, 540)) .build(); let vo_chn3 = Arc::new(VoChn::new(3, &vo_chn3_attr, Arc::clone(&vo_vl0))); vo_chn3.show()?; let hdmi_core = Arc::new(HdmiCore::new()); let hdmi_port = HdmiPortBuilder::with_dvi_1080p60(HdmiId::Hdmi0, Arc::clone(&hdmi_core)).build(); hdmi_port.start().unwrap(); assert_eq!(Arc::strong_count(&hdmi_core), 2); let mut fb = FrameBuffer::new_ext("/dev/fb0").unwrap(); fb.set_pixel_format(HifbPixFmt::Argb8888).unwrap(); fb.set_resolution(vo_size.width, vo_size.height).unwrap(); fb.set_num_buffers(2).unwrap(); fb.set_compression(false).unwrap(); fb.enable_alpha().unwrap(); fb.enable_alpha_channel(0xff).unwrap(); fb.show().unwrap(); let fb = Arc::new(Mutex::new(fb)); let tde = Arc::new(Mutex::new(TdeDev::new())); let rdr = TdeRenderer::new(fb, tde)?; let mut trt = TdeRenderThread::new(Arc::new(Mutex::new(rdr))); let mipi_rx = MipiReceiver::new(); assert!(mipi_rx.set_hs_mode(LaneDivideMode::Mode8)); // 4+4+4+2+2 assert!(mipi_rx.enable_mipi_clock(0)); assert!(mipi_rx.reset_mipi(0)); assert!(mipi_rx.enable_sensor_clock(0)); assert!(mipi_rx.reset_sensor(0)); let mipi_attr = ComboDevAttrBuilder::new(0) .with_mipi() .with_size(vi_size.width, vi_size.height) .with_data_type(MipiDataType::Yuv4228Bit) .with_lanes([0, 1, 2, 3, -1, -1, -1, -1]) .build(); assert!(mipi_rx.set_dev_attr(&mipi_attr)); assert!(mipi_rx.unreset_mipi(0)); assert!(mipi_rx.unreset_sensor(0)); let vi_dev_attr = ViDevAttrBuilder::new() .with_mipi_yuv422() .with_multiplex(1) .with_component_mask([0xff000000, 0x00ff0000]) .with_progressive() .with_yuv() .with_yuv_uyvy() .with_size(vi_size) .with_bas_scale(vi_size) .with_bas_rephase(ViRephaseMode::None, ViRephaseMode::None) .with_wdr(WdrMode::None, vi_size.height) .build(); let vi_dev0 = Arc::new(ViDev::new(0, Some(&vi_dev_attr))); #[cfg(not(feature = "hi3559av100"))] vi_dev0.bind_mipi(0).unwrap(); vi_dev0.bind_pipes(&[0, 1, 2, 3]).unwrap(); let vi_pipe_attr = ViPipeAttrBuilder::new_yuv422_3dnr_rfr() .with_max_size(vi_size) .build(); let vi_pipe0 = Arc::new(ViPipe::new(0, &vi_pipe_attr)); let vi_pipe1 = Arc::new(ViPipe::new(1, &vi_pipe_attr)); let vi_pipe2 = Arc::new(ViPipe::new(2, &vi_pipe_attr)); let vi_pipe3 = Arc::new(ViPipe::new(3, &vi_pipe_attr)); let vi_chn_attr = ViChnAttrBuilder::new_yuv422_sdr8_linear() .with_size(vi_size) .build(); let vi_chn0 = ViChn::new(0, Some(&vi_chn_attr), Arc::clone(&vi_pipe0)); let vi_chn1 = ViChn::new(0, Some(&vi_chn_attr), Arc::clone(&vi_pipe1)); let vi_chn2 = ViChn::new(0, Some(&vi_chn_attr), Arc::clone(&vi_pipe2)); let vi_chn3 = ViChn::new(0, Some(&vi_chn_attr), Arc::clone(&vi_pipe3)); // vi_pipe0.set_dump_attr(true, 3, ViDumpType::Yuv); vi_pipe0.set_vc_number(0).unwrap(); vi_pipe0.start().unwrap(); // vi_pipe1.set_dump_attr(true, 3, ViDumpType::Yuv); vi_pipe1.set_vc_number(1).unwrap(); vi_pipe1.start().unwrap(); // vi_pipe2.set_dump_attr(true, 3, ViDumpType::Yuv); vi_pipe2.set_vc_number(2).unwrap(); vi_pipe2.start().unwrap(); // vi_pipe3.set_dump_attr(true, 3, ViDumpType::Yuv); vi_pipe3.set_vc_number(3).unwrap(); vi_pipe3.start().unwrap(); let vpss_grp_attr = VpssGrpAttrBuilder::new().with_max_size(vi_size).build(); let vpss_grp0 = Arc::new(VpssGrp::new(0, &vpss_grp_attr)); let vpss_grp1 = Arc::new(VpssGrp::new(1, &vpss_grp_attr)); let vpss_grp2 = Arc::new(VpssGrp::new(2, &vpss_grp_attr)); let vpss_grp3 = Arc::new(VpssGrp::new(3, &vpss_grp_attr)); let vpss_crop = VpssCropInfo::with_abs(Rect::with_size(vi_size)); vpss_grp0.set_crop(&vpss_crop).unwrap(); vpss_grp1.set_crop(&vpss_crop).unwrap(); vpss_grp2.set_crop(&vpss_crop).unwrap(); vpss_grp3.set_crop(&vpss_crop).unwrap(); let vpss_chn_attr = VpssChnAttrBuilder::new() .with_mode(VpssChnMode::User) .with_size(vi_size) .build(); let vpss_chn0 = Arc::new(VpssChn::new(0, &vpss_chn_attr, Arc::clone(&vpss_grp0))); let vpss_chn1 = Arc::new(VpssChn::new(0, &vpss_chn_attr, Arc::clone(&vpss_grp1))); let vpss_chn2 = Arc::new(VpssChn::new(0, &vpss_chn_attr, Arc::clone(&vpss_grp2))); let vpss_chn3 = Arc::new(VpssChn::new(0, &vpss_chn_attr, Arc::clone(&vpss_grp3))); vpss_grp0.start().unwrap(); vpss_grp1.start().unwrap(); vpss_grp2.start().unwrap(); vpss_grp3.start().unwrap(); let _b10 = SysBinder::new(&vi_pipe0, &vpss_chn0); let _b11 = SysBinder::new(&vi_pipe1, &vpss_chn1); let _b12 = SysBinder::new(&vi_pipe2, &vpss_chn2); let _b13 = SysBinder::new(&vi_pipe3, &vpss_chn3); let _b20 = SysBinder::new(&vpss_chn0, &vo_chn0); let _b21 = SysBinder::new(&vpss_chn1, &vo_chn1); let _b22 = SysBinder::new(&vpss_chn2, &vo_chn2); let _b23 = SysBinder::new(&vpss_chn3, &vo_chn3); let mut poller = Poller::new()?; poller.add(0, Events::new().read(), None).unwrap(); poller .add( vpss_chn0.get_fd().unwrap(), Events::new().read(), Some(Arc::clone(&vpss_chn0) as EventContext), ) .unwrap(); poller .add( vpss_chn1.get_fd().unwrap(), Events::new().read(), Some(Arc::clone(&vpss_chn1) as EventContext), ) .unwrap(); poller .add( vpss_chn2.get_fd().unwrap(), Events::new().read(), Some(Arc::clone(&vpss_chn2) as EventContext), ) .unwrap(); poller .add( vpss_chn3.get_fd().unwrap(), Events::new().read(), Some(Arc::clone(&vpss_chn3) as EventContext), ) .unwrap(); trt.start(); println!("Press enter to exit ..."); 'outer: loop { let events = poller.pull_events(1000).unwrap(); for (_fd, _events, ctx) in events.iter() { match ctx { Some(v) => { if let Some(chn) = v.downcast_ref::() { let info = chn.get_frame(0).unwrap(); let vf = info.video_frame(); // let mut file = File::create("/tmp/foo.yuv").unwrap(); let planes = vf.plane_memories(); // println!( // "New Frame: chn={}, size={}, pts={}", // chn, // vf.size(), // vf.pts() // ); for a in 0..planes.len() { // file.write(planes[a].as_bytes()).unwrap(); } if chn.group_id() == 0 { trt.post_event(TdeRenderEvent::Refresh); } } } None => { if _fd == &0 { break 'outer; } } } } } trt.stop(); Ok(()) }