fix(video): feed android h264 encoder nv12
Some checks failed
Mirror to GitHub / mirror (push) Failing after 30s
Build Release Binaries / build-amd64 (push) Failing after 3m16s

This commit is contained in:
Siavash Sameni
2026-05-25 21:20:01 +04:00
parent e8f139588a
commit 0b7bf1b385
2 changed files with 61 additions and 3 deletions

View File

@@ -65,7 +65,7 @@ impl MediaCodecEncoder {
format.set_i32("bitrate", bitrate_bps as i32); format.set_i32("bitrate", bitrate_bps as i32);
format.set_i32("frame-rate", 30); format.set_i32("frame-rate", 30);
format.set_i32("i-frame-interval", 1); format.set_i32("i-frame-interval", 1);
format.set_i32("color-format", COLOR_FORMAT_YUV420_PLANAR); format.set_i32("color-format", COLOR_FORMAT_YUV420_SEMIPLANAR);
let codec = MediaCodec::from_encoder_type("video/avc").ok_or_else(|| { let codec = MediaCodec::from_encoder_type("video/avc").ok_or_else(|| {
VideoError::PlatformError("AMediaCodec_createEncoderByType failed".into()) VideoError::PlatformError("AMediaCodec_createEncoderByType failed".into())
@@ -122,10 +122,11 @@ impl VideoEncoder for MediaCodecEncoder {
} else { } else {
0 0
}; };
let input = i420_to_nv12(&frame.data, self.width as usize, self.height as usize)?;
let to_copy = { let to_copy = {
let buf = buffer.buffer_mut(); let buf = buffer.buffer_mut();
let n = frame.data.len().min(buf.len()); let n = input.len().min(buf.len());
for (d, &s) in buf[..n].iter_mut().zip(frame.data[..n].iter()) { for (d, &s) in buf[..n].iter_mut().zip(input[..n].iter()) {
d.write(s); d.write(s);
} }
n n
@@ -1149,6 +1150,31 @@ fn i420_len(width: usize, height: usize) -> Result<usize, VideoError> {
}) })
} }
#[cfg(target_os = "android")]
fn i420_to_nv12(src: &[u8], width: usize, height: usize) -> Result<Vec<u8>, VideoError> {
let y_size = width
.checked_mul(height)
.ok_or_else(|| VideoError::InvalidInput(format!("invalid frame dimensions {width}x{height}")))?;
let uv_size = y_size / 4;
let expected = y_size + uv_size * 2;
if src.len() < expected {
return Err(VideoError::InvalidInput(format!(
"I420 frame too small for NV12 conversion: {} bytes, expected {expected}",
src.len()
)));
}
let mut out = vec![0u8; expected];
out[..y_size].copy_from_slice(&src[..y_size]);
let u = &src[y_size..y_size + uv_size];
let v = &src[y_size + uv_size..y_size + uv_size * 2];
for i in 0..uv_size {
out[y_size + i * 2] = u[i];
out[y_size + i * 2 + 1] = v[i];
}
Ok(out)
}
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
fn yuv420_planar_to_tight_i420( fn yuv420_planar_to_tight_i420(
src: &[u8], src: &[u8],

View File

@@ -1958,6 +1958,22 @@ impl CallEngine {
}), }),
); );
} }
if should_log_video_sample(camera_frames, false) {
crate::emit_call_debug(
&vid_app,
"video:camera_frame_sample",
serde_json::json!({
"t_ms": vid_t0.elapsed().as_millis() as u64,
"codec": format!("{:?}", vid_codec),
"frame_no": camera_frames,
"width": f.width,
"height": f.height,
"data_bytes": f.data.len(),
"platform": "android",
"i420_sample": i420_sample(&f.data, f.width, f.height),
}),
);
}
f f
} }
Ok(None) => break, Ok(None) => break,
@@ -3099,6 +3115,22 @@ impl CallEngine {
}), }),
); );
} }
if should_log_video_sample(camera_frames, false) {
crate::emit_call_debug(
&vid_app,
"video:camera_frame_sample",
serde_json::json!({
"t_ms": vid_t0.elapsed().as_millis() as u64,
"codec": format!("{:?}", vid_codec),
"frame_no": camera_frames,
"width": f.width,
"height": f.height,
"data_bytes": f.data.len(),
"platform": "desktop",
"i420_sample": i420_sample(&f.data, f.width, f.height),
}),
);
}
f f
} }
Ok(None) => break, // sender dropped Ok(None) => break, // sender dropped