diff --git a/libs/scrap/src/common/mediacodec.rs b/libs/scrap/src/common/mediacodec.rs new file mode 100644 index 000000000..6a0282b49 --- /dev/null +++ b/libs/scrap/src/common/mediacodec.rs @@ -0,0 +1,187 @@ +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); +}