fix(video): request android sync frames via mediacodec
This commit is contained in:
@@ -19,7 +19,7 @@ shiguredo_svt_av1 = "2026.1.0"
|
|||||||
shiguredo_video_toolbox = "2026.1"
|
shiguredo_video_toolbox = "2026.1"
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
ndk = { version = "0.9", features = ["media"] }
|
ndk = { version = "0.9", features = ["api-level-26", "media"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ const BITRATE_MODE_CBR: i32 = 2;
|
|||||||
/// AMediaCodec keyframe buffer flag.
|
/// AMediaCodec keyframe buffer flag.
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
const AMEDIACODEC_BUFFER_FLAG_KEY_FRAME: u32 = 1;
|
const AMEDIACODEC_BUFFER_FLAG_KEY_FRAME: u32 = 1;
|
||||||
|
/// MediaCodec encoder parameter key for forcing the next output frame to be a sync frame.
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
const MEDIA_CODEC_REQUEST_SYNC_FRAME: &str = "request-sync";
|
||||||
|
|
||||||
// AMediaCodec is thread-safe; the NonNull inside MediaCodec suppresses auto-Send.
|
// AMediaCodec is thread-safe; the NonNull inside MediaCodec suppresses auto-Send.
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
@@ -117,12 +120,11 @@ impl VideoEncoder for MediaCodecEncoder {
|
|||||||
.dequeue_input_buffer(std::time::Duration::from_millis(10))
|
.dequeue_input_buffer(std::time::Duration::from_millis(10))
|
||||||
{
|
{
|
||||||
Ok(ndk::media::media_codec::DequeuedInputBufferResult::Buffer(mut buffer)) => {
|
Ok(ndk::media::media_codec::DequeuedInputBufferResult::Buffer(mut buffer)) => {
|
||||||
let flags = if self.force_keyframe {
|
if self.force_keyframe {
|
||||||
AMEDIACODEC_BUFFER_FLAG_KEY_FRAME
|
self.request_sync_frame();
|
||||||
} else {
|
}
|
||||||
0
|
let input =
|
||||||
};
|
i420_to_nv12(&frame.data, self.width as usize, self.height as usize)?;
|
||||||
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 = input.len().min(buf.len());
|
let n = input.len().min(buf.len());
|
||||||
@@ -132,7 +134,7 @@ impl VideoEncoder for MediaCodecEncoder {
|
|||||||
n
|
n
|
||||||
};
|
};
|
||||||
self.codec
|
self.codec
|
||||||
.queue_input_buffer(buffer, 0, to_copy, frame.timestamp_ms as u64 * 1000, flags)
|
.queue_input_buffer(buffer, 0, to_copy, frame.timestamp_ms as u64 * 1000, 0)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
VideoError::PlatformError(format!("queue_input_buffer failed: {e}"))
|
VideoError::PlatformError(format!("queue_input_buffer failed: {e}"))
|
||||||
})?;
|
})?;
|
||||||
@@ -174,6 +176,14 @@ impl VideoEncoder for MediaCodecEncoder {
|
|||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
impl MediaCodecEncoder {
|
impl MediaCodecEncoder {
|
||||||
|
fn request_sync_frame(&self) {
|
||||||
|
let mut params = MediaFormat::new();
|
||||||
|
params.set_i32(MEDIA_CODEC_REQUEST_SYNC_FRAME, 0);
|
||||||
|
if let Err(e) = self.codec.set_parameters(params) {
|
||||||
|
tracing::warn!(error = %e, "AMediaCodec request sync frame failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Drain all available output buffers and convert from AVCC to Annex-B.
|
/// Drain all available output buffers and convert from AVCC to Annex-B.
|
||||||
fn drain_output(&mut self) -> Result<Vec<u8>, VideoError> {
|
fn drain_output(&mut self) -> Result<Vec<u8>, VideoError> {
|
||||||
let mut output = Vec::new();
|
let mut output = Vec::new();
|
||||||
|
|||||||
Reference in New Issue
Block a user