188 lines
6.1 KiB
Rust
188 lines
6.1 KiB
Rust
|
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<Self>
|
||
|
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<hbb_common::message_proto::Message> {
|
||
|
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<MediaCodecDecoder>,
|
||
|
pub h265: Option<MediaCodecDecoder>,
|
||
|
}
|
||
|
|
||
|
// "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<u8>) -> ResultType<bool> {
|
||
|
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<MediaCodec> {
|
||
|
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);
|
||
|
}
|