use std::{io::Write, time::Duration}; use hbb_common::{bail, ResultType}; #[cfg(target_os = "android")] use ndk::media::media_codec::{MediaCodec, MediaCodecDirection, MediaFormat}; use crate::{ codec::{EncoderApi, EncoderCfg}, I420ToARGB, }; pub struct MediaCodecEncoder { encoder: MediaCodec, } impl EncoderApi for MediaCodecEncoder { fn new(cfg: EncoderCfg) -> ResultType where Self: Sized, { if let EncoderCfg::HW(cfg) = cfg { create_media_codec(&cfg.codec_name, MediaCodecDirection::Encoder) } else { bail!("encoder type mismatch") } } fn encode_to_message( &mut self, frame: &[u8], ms: i64, ) -> ResultType { todo!() } fn use_yuv(&self) -> bool { todo!() } fn set_bitrate(&mut self, bitrate: u32) -> ResultType<()> { todo!() } } pub struct MediaCodecDecoder { decoder: MediaCodec, // pub info: CodecInfo, } pub struct MediaCodecDecoders { pub h264: Option, pub h265: Option, } // "video/x-vnd.on2.vp8" - VP8 video (i.e. video in .webm) // "video/x-vnd.on2.vp9" - VP9 video (i.e. video in .webm) // "video/avc" - H.264/AVC video // "video/hevc" - H.265/HEVC video impl MediaCodecDecoder { pub fn new_decoders() -> MediaCodecDecoders { // 直接生成 h264 和 h265 // 264 let h264 = create_media_codec("video/avc", MediaCodecDirection::Decoder) .map(|decoder| MediaCodecDecoder { decoder }); let h265 = create_media_codec("video/hevc", MediaCodecDirection::Decoder) .map(|decoder| MediaCodecDecoder { decoder }); MediaCodecDecoders { h264, h265 } } pub fn decode(&mut self, data: &[u8], rgb: &mut Vec) -> ResultType { log::debug!("start dequeue_input"); match self .decoder .dequeue_input_buffer(Duration::from_millis(10)) .unwrap() { Some(mut input_buffer) => { let mut buf = input_buffer.buffer_mut(); log::debug!( "dequeue_input success:buf ptr:{:?},len:{}", buf.as_ptr(), buf.len() ); if data.len() > buf.len() { log::error!("break! res.len()>buf.len()"); bail!("break! res.len()>buf.len()"); } buf.write_all(&data).unwrap(); if let Err(e) = self .decoder .queue_input_buffer(input_buffer, 0, data.len(), 0, 0) { log::debug!("debug queue_input_buffer:{:?}", e); }; } None => { log::debug!("dequeue_input_buffer fail :None"); } }; return match self .decoder .dequeue_output_buffer(Duration::from_millis(100)) { Ok(Some(output_buffer)) => { log::debug!("dequeue_output success"); // let res_format = output_buffer.format(); let res_format = self.decoder.output_format(); log::debug!("res_format:{:?}", res_format.str("mime")); log::debug!("res_color:{:?}", res_format.i32("color-format")); log::debug!("stride:{:?}", res_format.i32("stride")); let w = res_format.i32("width").unwrap() as usize; let h = res_format.i32("height").unwrap() as usize; let stride = res_format.i32("stride").unwrap(); // todo // let w = 1920; // let h = 1080; // let stride = 1920; // todo let buf = output_buffer.buffer(); log::debug!("output_buffer ptr:{:?} len:{}", buf.as_ptr(), buf.len()); let bps = 4; let u = buf.len() * 2 / 3; let v = buf.len() * 5 / 6; rgb.resize(h * w * bps, 0); log::debug!("start I420ToARGB,u:{},v:{},w:{},h:{}", u, v, w, h); let y_ptr = buf.as_ptr(); let u_ptr = buf[u..].as_ptr(); let v_ptr = buf[v..].as_ptr(); log::debug!("ptr,y:{:?},u:{:?},v:{:?}", y_ptr, u_ptr, v_ptr); unsafe { I420ToARGB( y_ptr, stride, u_ptr, stride / 2, v_ptr, stride / 2, rgb.as_mut_ptr(), (w * bps) as _, w as _, h as _, ); } log::debug!("end I420ToARGB"); log::debug!("release_output_buffer"); self.decoder .release_output_buffer(output_buffer, false) .unwrap(); log::debug!("return true"); Ok(true) } Ok(None) => { log::debug!("dequeue_output fail :None"); Ok(false) } Err(e) => { log::debug!("dequeue_output fail :error:{:?}", e); Ok(false) } }; } } fn create_media_codec(name: &str, direction: MediaCodecDirection) -> Option { let codec = MediaCodec::from_decoder_type(name).unwrap(); log::debug!("start init"); let media_format = MediaFormat::new(); media_format.set_str("mime", name); media_format.set_i32("width", 0); media_format.set_i32("height", 0); media_format.set_i32("color-format", 19); // COLOR_FormatYUV420Planar if let Err(e) = codec.configure(&media_format, None, direction) { log::error!("failed to decoder.init:{:?}", e); return None; }; log::error!("decoder init success"); if let Err(e) = codec.start() { log::error!("failed to decoder.start:{:?}", e); return None; }; log::debug!("init decoder successed!:{:?}", name); return Some(codec); }