171 lines
6.0 KiB
Rust
171 lines
6.0 KiB
Rust
use hbb_common::anyhow::Error;
|
|
use hbb_common::{bail, ResultType};
|
|
use ndk::media::media_codec::{MediaCodec, MediaCodecDirection, MediaFormat};
|
|
use std::ops::Deref;
|
|
use std::{
|
|
io::Write,
|
|
sync::atomic::{AtomicBool, Ordering},
|
|
time::Duration,
|
|
};
|
|
|
|
use crate::ImageFormat;
|
|
use crate::{
|
|
codec::{EncoderApi, EncoderCfg},
|
|
I420ToABGR, I420ToARGB,
|
|
};
|
|
|
|
/// MediaCodec mime type name
|
|
const H264_MIME_TYPE: &str = "video/avc";
|
|
const H265_MIME_TYPE: &str = "video/hevc";
|
|
// const VP8_MIME_TYPE: &str = "video/x-vnd.on2.vp8";
|
|
// const VP9_MIME_TYPE: &str = "video/x-vnd.on2.vp9";
|
|
|
|
// TODO MediaCodecEncoder
|
|
|
|
pub static H264_DECODER_SUPPORT: AtomicBool = AtomicBool::new(false);
|
|
pub static H265_DECODER_SUPPORT: AtomicBool = AtomicBool::new(false);
|
|
|
|
pub struct MediaCodecDecoder {
|
|
decoder: MediaCodec,
|
|
name: String,
|
|
}
|
|
|
|
impl Deref for MediaCodecDecoder {
|
|
type Target = MediaCodec;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.decoder
|
|
}
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct MediaCodecDecoders {
|
|
pub h264: Option<MediaCodecDecoder>,
|
|
pub h265: Option<MediaCodecDecoder>,
|
|
}
|
|
|
|
impl MediaCodecDecoder {
|
|
pub fn new_decoders() -> MediaCodecDecoders {
|
|
let h264 = create_media_codec(H264_MIME_TYPE, MediaCodecDirection::Decoder);
|
|
let h265 = create_media_codec(H265_MIME_TYPE, MediaCodecDirection::Decoder);
|
|
MediaCodecDecoders { h264, h265 }
|
|
}
|
|
|
|
pub fn decode(&mut self, data: &[u8], fmt: ImageFormat, raw: &mut Vec<u8>) -> ResultType<bool> {
|
|
match self.dequeue_input_buffer(Duration::from_millis(10))? {
|
|
Some(mut input_buffer) => {
|
|
let mut buf = input_buffer.buffer_mut();
|
|
if data.len() > buf.len() {
|
|
log::error!("Failed to decode, the input data size is bigger than input buf");
|
|
bail!("The input data size is bigger than input buf");
|
|
}
|
|
buf.write_all(&data)?;
|
|
self.queue_input_buffer(input_buffer, 0, data.len(), 0, 0)?;
|
|
}
|
|
None => {
|
|
log::debug!("Failed to dequeue_input_buffer: No available input_buffer");
|
|
}
|
|
};
|
|
|
|
return match self.dequeue_output_buffer(Duration::from_millis(100))? {
|
|
Some(output_buffer) => {
|
|
let res_format = self.output_format();
|
|
let w = res_format
|
|
.i32("width")
|
|
.ok_or(Error::msg("Failed to dequeue_output_buffer, width is None"))?
|
|
as usize;
|
|
let h = res_format.i32("height").ok_or(Error::msg(
|
|
"Failed to dequeue_output_buffer, height is None",
|
|
))? as usize;
|
|
let stride = res_format.i32("stride").ok_or(Error::msg(
|
|
"Failed to dequeue_output_buffer, stride is None",
|
|
))?;
|
|
let buf = output_buffer.buffer();
|
|
let bps = 4;
|
|
let u = buf.len() * 2 / 3;
|
|
let v = buf.len() * 5 / 6;
|
|
raw.resize(h * w * bps, 0);
|
|
let y_ptr = buf.as_ptr();
|
|
let u_ptr = buf[u..].as_ptr();
|
|
let v_ptr = buf[v..].as_ptr();
|
|
unsafe {
|
|
match fmt {
|
|
ImageFormat::ARGB => {
|
|
I420ToARGB(
|
|
y_ptr,
|
|
stride,
|
|
u_ptr,
|
|
stride / 2,
|
|
v_ptr,
|
|
stride / 2,
|
|
raw.as_mut_ptr(),
|
|
(w * bps) as _,
|
|
w as _,
|
|
h as _,
|
|
);
|
|
}
|
|
ImageFormat::ARGB => {
|
|
I420ToABGR(
|
|
y_ptr,
|
|
stride,
|
|
u_ptr,
|
|
stride / 2,
|
|
v_ptr,
|
|
stride / 2,
|
|
raw.as_mut_ptr(),
|
|
(w * bps) as _,
|
|
w as _,
|
|
h as _,
|
|
);
|
|
}
|
|
_ => {
|
|
bail!("Unsupported image format");
|
|
}
|
|
}
|
|
}
|
|
self.release_output_buffer(output_buffer, false)?;
|
|
Ok(true)
|
|
}
|
|
None => {
|
|
log::debug!("Failed to dequeue_output: No available dequeue_output");
|
|
Ok(false)
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
fn create_media_codec(name: &str, direction: MediaCodecDirection) -> Option<MediaCodecDecoder> {
|
|
let codec = MediaCodec::from_decoder_type(name)?;
|
|
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 init decoder:{:?}", e);
|
|
return None;
|
|
};
|
|
log::error!("decoder init success");
|
|
if let Err(e) = codec.start() {
|
|
log::error!("Failed to start decoder:{:?}", e);
|
|
return None;
|
|
};
|
|
log::debug!("Init decoder successed!: {:?}", name);
|
|
return Some(MediaCodecDecoder {
|
|
decoder: codec,
|
|
name: name.to_owned(),
|
|
});
|
|
}
|
|
|
|
pub fn check_mediacodec() {
|
|
std::thread::spawn(move || {
|
|
// check decoders
|
|
let decoders = MediaCodecDecoder::new_decoders();
|
|
H264_DECODER_SUPPORT.swap(decoders.h264.is_some(), Ordering::SeqCst);
|
|
H265_DECODER_SUPPORT.swap(decoders.h265.is_some(), Ordering::SeqCst);
|
|
decoders.h264.map(|d| d.stop());
|
|
decoders.h265.map(|d| d.stop());
|
|
// TODO encoders
|
|
});
|
|
}
|