mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-08 22:28:12 -05:00
app/vid: take in account the stride when decoding.
This commit is contained in:
@@ -57,6 +57,8 @@ public class VideoDecoder {
|
||||
private int decoderId;
|
||||
private int outputColorFormat = -1;
|
||||
private Context context;
|
||||
private int stride = -1;
|
||||
private int sliceHeight = -1;
|
||||
|
||||
/** Conditional logging based on DEBUG flag */
|
||||
private void log(String fstr, Object... args) {
|
||||
@@ -234,6 +236,14 @@ public class VideoDecoder {
|
||||
outputColorFormat = newFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
|
||||
log("Format changed: colorFormat=%d", outputColorFormat);
|
||||
}
|
||||
// Extract stride and slice-height for handling padded buffers
|
||||
if (newFormat.containsKey(MediaFormat.KEY_STRIDE)) {
|
||||
stride = newFormat.getInteger(MediaFormat.KEY_STRIDE);
|
||||
}
|
||||
if (newFormat.containsKey(MediaFormat.KEY_SLICE_HEIGHT)) {
|
||||
sliceHeight = newFormat.getInteger(MediaFormat.KEY_SLICE_HEIGHT);
|
||||
}
|
||||
log("Format changed: stride=%d, slice-height=%d", stride, sliceHeight);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -245,6 +255,8 @@ public class VideoDecoder {
|
||||
* - Semi-planar (NV21): De-interleaves UV data
|
||||
* - Planar (YV12/I420): Reads U and V planes directly
|
||||
*
|
||||
* Handles stride padding when hardware decoder uses row alignment.
|
||||
*
|
||||
* @param outputBuffer Raw decoded frame data from MediaCodec
|
||||
* @param offset Offset to valid data in buffer
|
||||
* @param size Size of valid data in bytes
|
||||
@@ -260,7 +272,24 @@ public class VideoDecoder {
|
||||
byte[] uData = new byte[uvSize];
|
||||
byte[] vData = new byte[uvSize];
|
||||
|
||||
outputBuffer.get(yData, 0, ySize);
|
||||
// Read Y plane, accounting for stride if present
|
||||
int yStride = (stride > 0) ? stride : width;
|
||||
int yRows = (sliceHeight > 0) ? sliceHeight : height;
|
||||
|
||||
// Read Y plane row by row, skipping padding
|
||||
for (int row = 0; row < height && row < yRows; row++) {
|
||||
int destOffset = row * width;
|
||||
outputBuffer.get(yData, destOffset, width);
|
||||
if (row < yRows - 1) {
|
||||
// Skip padding bytes to next row
|
||||
int skip = yStride - width;
|
||||
outputBuffer.position(outputBuffer.position() + skip);
|
||||
}
|
||||
}
|
||||
|
||||
// Seek to UV plane start: Y plane ends at yStride * yRows
|
||||
int uvPlaneStart = yStride * yRows;
|
||||
outputBuffer.position(offset + uvPlaneStart);
|
||||
|
||||
if (outputColorFormat == COLOR_FORMATYUV420_PLANAR) {
|
||||
outputBuffer.get(uData, 0, uvSize);
|
||||
@@ -269,7 +298,8 @@ public class VideoDecoder {
|
||||
if (outputColorFormat != COLOR_FORMATYUV420_SEMIPLANAR) {
|
||||
Log.w("darkfi", String.format("Unknown color format %d, assuming semi-planar", outputColorFormat));
|
||||
}
|
||||
deinterleaveUV(outputBuffer, uData, vData, uvSize);
|
||||
// For semi-planar, UV plane may also have stride
|
||||
deinterleaveUV(outputBuffer, uData, vData, uvSize, yStride);
|
||||
}
|
||||
|
||||
onFrameDecoded(decoderId, yData, uData, vData, width, height);
|
||||
@@ -279,17 +309,35 @@ public class VideoDecoder {
|
||||
/**
|
||||
* De-interleaves UV data from semi-planar YUV format (NV21).
|
||||
*
|
||||
* Handles stride padding in the UV plane.
|
||||
*
|
||||
* @param outputBuffer Buffer containing interleaved UVUV... data
|
||||
* @param uData Output array for U component
|
||||
* @param vData Output array for V component
|
||||
* @param uvSize Number of UV pairs to de-interleave
|
||||
* @param yStride Luma stride (used to calculate chroma stride)
|
||||
*/
|
||||
private void deinterleaveUV(ByteBuffer outputBuffer, byte[] uData, byte[] vData, int uvSize) {
|
||||
byte[] uvInterleaved = new byte[uvSize * 2];
|
||||
outputBuffer.get(uvInterleaved);
|
||||
for (int i = 0; i < uvSize; i++) {
|
||||
uData[i] = uvInterleaved[i * 2];
|
||||
vData[i] = uvInterleaved[i * 2 + 1];
|
||||
private void deinterleaveUV(ByteBuffer outputBuffer, byte[] uData, byte[] vData, int uvSize, int yStride) {
|
||||
int uvWidth = width / 2;
|
||||
int uvHeight = height / 2;
|
||||
int uvStride = (yStride > 0) ? (yStride + 1) / 2 : uvWidth;
|
||||
|
||||
// Read UV plane row by row, deinterleaving and skipping padding
|
||||
int uvIdx = 0;
|
||||
for (int row = 0; row < uvHeight; row++) {
|
||||
// Read one row of UV pairs (interleaved)
|
||||
for (int col = 0; col < uvWidth; col++) {
|
||||
int uByte = outputBuffer.get() & 0xFF;
|
||||
int vByte = outputBuffer.get() & 0xFF;
|
||||
uData[uvIdx] = (byte) uByte;
|
||||
vData[uvIdx] = (byte) vByte;
|
||||
uvIdx++;
|
||||
}
|
||||
// Skip padding to next row
|
||||
if (row < uvHeight - 1) {
|
||||
int skip = (uvStride - uvWidth) * 2;
|
||||
outputBuffer.position(outputBuffer.position() + skip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,14 +21,14 @@ use sled_overlay::sled;
|
||||
use super::chat::populate_tree;
|
||||
use crate::{
|
||||
app::{
|
||||
node::{create_chatview, create_layer, create_text, create_vector_art},
|
||||
node::{create_chatview, create_layer, create_text, create_vector_art, create_video},
|
||||
App,
|
||||
},
|
||||
expr::{self, Compiler},
|
||||
mesh::COLOR_PURPLE,
|
||||
prop::{PropertyAtomicGuard, PropertyFloat32, Role},
|
||||
scene::SceneNodePtr,
|
||||
ui::{ChatView, Layer, Text, VectorArt, VectorShape},
|
||||
ui::{ChatView, Layer, Text, VectorArt, VectorShape, Video},
|
||||
util::i18n::I18nBabelFish,
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ mod ui_consts {
|
||||
get_appdata_path().join("chatdb")
|
||||
}
|
||||
//pub const KING_PATH: &str = "king.png";
|
||||
//pub const VID_PATH: &str = "forest_720x1280.mp4";
|
||||
pub const VID_PATH: &str = "forest_720x1280.mp4";
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
@@ -54,7 +54,7 @@ mod ui_consts {
|
||||
"chatdb".into()
|
||||
}
|
||||
//pub const KING_PATH: &str = "assets/king.png";
|
||||
//pub const VID_PATH: &str = "assets/forest_1920x1080.ivf";
|
||||
pub const VID_PATH: &str = "assets/forest_1920x1080.ivf";
|
||||
}
|
||||
|
||||
use ui_consts::*;
|
||||
@@ -238,7 +238,6 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
|
||||
*/
|
||||
|
||||
// Create KING GNU!
|
||||
/*
|
||||
let node = create_video("king");
|
||||
let prop = node.get_property("rect").unwrap();
|
||||
prop.set_f32(atom, Role::App, 0, 80.).unwrap();
|
||||
@@ -249,8 +248,8 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
|
||||
node.set_property_u32(atom, Role::App, "z_index", 1).unwrap();
|
||||
let node = node.setup(|me| Video::new(me, app.render_api.clone(), app.ex.clone())).await;
|
||||
layer_node.link(node);
|
||||
*/
|
||||
|
||||
/*
|
||||
// Create some text
|
||||
let node = create_text("label");
|
||||
let prop = node.get_property("rect").unwrap();
|
||||
@@ -367,7 +366,6 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
|
||||
.await;
|
||||
layer_node.link(node);
|
||||
|
||||
/*
|
||||
// Text edit
|
||||
let node = create_singleline_edit("editz");
|
||||
//let node = create_multiline_edit("editz");
|
||||
|
||||
@@ -72,6 +72,11 @@ pub fn spawn_decoder_thread(
|
||||
let pct_loaded = 100. * frame_idx as f32 / 150.0;
|
||||
d!("Decoded video {pct_loaded:.2}%%");
|
||||
}
|
||||
|
||||
assert!(frame_idx <= 150);
|
||||
if frame_idx == 150 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
d!("Finished decoding video: {path} in {:?}", now.elapsed());
|
||||
|
||||
Reference in New Issue
Block a user