clippy: clear the workspace errors and apply the machine fixes
- Mark the raw-pointer interop entry points unsafe with # Safety docs (oak-core upload/download/frame-from-pixels, oak-audio convert) and satisfy the existing callers (tests). - mut_from_ref: allow with the ABI contract documented (the handle get_mut helpers in oak-timeline/oak-render/oak-task take the shared reference the C ABI passes; exclusivity is the caller's unsafe contract). - Fix the eq_op in the white-balance normalization (green / green). - Apply cargo clippy --fix across the workspace (redundant closures and field names, field reassignment, items after test modules, ...). - Revert the replace_box fix in image_effect's clip_define: a redefinition must allocate a new box, otherwise the old clip handle stays valid and the HS-map replace contract (clip != clip2) breaks. - 283 warnings remain; they are all non-machine-applicable (chunks_exact -> as_chunks needs a manual iter_mut, too_many_arguments, complex types, missing Safety docs, ...) and are tracked as the follow-up.
This commit is contained in:
@@ -42,8 +42,8 @@ use std::sync::Arc;
|
||||
|
||||
use oak_codec::encoder::{create_from_params, Encoder};
|
||||
use oak_codec::encodingparams::EncodingParams as CodecEncodingParams;
|
||||
use oak_core::videoparams::VideoParams as CommonVideoParams;
|
||||
use oak_core::texture::Texture;
|
||||
use oak_core::videoparams::VideoParams as CommonVideoParams;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::nodeops::{self, NodeRef};
|
||||
@@ -127,7 +127,6 @@ pub struct EncodingParams {
|
||||
pub color_trc: i32,
|
||||
/// AVColorSpace (SDR 709 = 1, HDR BT.2020 = 9).
|
||||
pub color_space: i32,
|
||||
|
||||
}
|
||||
|
||||
impl ExportTask {
|
||||
@@ -147,7 +146,13 @@ impl ExportTask {
|
||||
// `it_export` run).
|
||||
let video_params = nodeops::sequence_video_params(&viewer.0, viewer.1, 0)
|
||||
.or_else(|| nodeops::footage_video_params(&viewer.0, viewer.1, 0));
|
||||
let render = RenderTask::new(base, video_params, viewer.clone(), ForceParams::default(), None);
|
||||
let render = RenderTask::new(
|
||||
base,
|
||||
video_params,
|
||||
viewer.clone(),
|
||||
ForceParams::default(),
|
||||
None,
|
||||
);
|
||||
ExportTask {
|
||||
render,
|
||||
viewer_node: viewer,
|
||||
@@ -231,15 +236,13 @@ impl ExportTask {
|
||||
/// The project's pipeline color settings (working colorspace + the
|
||||
/// delivery output spec) read off the exported node's project — the
|
||||
/// export renders to the project's delivery target, not to the display.
|
||||
fn delivery_color(&self) -> (
|
||||
oak_core::colormath::WorkingColorSpace,
|
||||
oak_core::colormath::OutputColorSpec,
|
||||
fn delivery_color(
|
||||
&self,
|
||||
) -> (
|
||||
oak_core::colormath::WorkingColorSpace,
|
||||
oak_core::colormath::OutputColorSpec,
|
||||
) {
|
||||
let guard = self
|
||||
.viewer_node
|
||||
.0
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner());
|
||||
let guard = self.viewer_node.0.lock().unwrap_or_else(|e| e.into_inner());
|
||||
(guard.working_color_space(), guard.output_color_spec())
|
||||
}
|
||||
|
||||
@@ -288,25 +291,26 @@ impl ExportTask {
|
||||
let frame = match texture {
|
||||
Texture::Cpu(frame) => frame.clone(),
|
||||
Texture::Gpu { .. } => texture.to_frame().map_err(|e| {
|
||||
Error::Failed(format!("Render frame readback for the encoder failed: {e:?}"))
|
||||
Error::Failed(format!(
|
||||
"Render frame readback for the encoder failed: {e:?}"
|
||||
))
|
||||
})?,
|
||||
};
|
||||
let frame = &frame;
|
||||
let params = CommonVideoParams::new_basic(
|
||||
frame.width,
|
||||
frame.height,
|
||||
oak_core::ocioutils::PixelFormat::from_code(frame.format as i32),
|
||||
4,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
frame.width,
|
||||
frame.height,
|
||||
oak_core::ocioutils::PixelFormat::from_code(frame.format as i32),
|
||||
4,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
);
|
||||
let mut out = oak_codec::frame::Frame::with_params(params);
|
||||
out.set_timestamp(frame.timestamp);
|
||||
out.allocate().map_err(|e| {
|
||||
Error::Failed(format!("Failed to allocate encoder frame: {e:?}"))
|
||||
})?;
|
||||
out.allocate()
|
||||
.map_err(|e| Error::Failed(format!("Failed to allocate encoder frame: {e:?}")))?;
|
||||
let dst_stride = out.linesize_bytes() as usize;
|
||||
let Some(dst) = out.data_mut() else {
|
||||
return Err(Error::Failed(
|
||||
@@ -386,7 +390,7 @@ impl TaskBehavior for ExportTask {
|
||||
result?;
|
||||
|
||||
// Flush the encoder and surface any trailing error.
|
||||
if let Err(_) = encoder.flush() {
|
||||
if encoder.flush().is_err() {
|
||||
// Fall through to the error read below (the flush error is
|
||||
// surfaced through `get_error()` like the C ABI did).
|
||||
}
|
||||
@@ -411,7 +415,7 @@ impl RenderTaskBehavior for ExportTask {
|
||||
// project's delivery colorspace before encoding. (No-op in the
|
||||
// legacy sRGB working space.)
|
||||
self.apply_output_node(&mut codec_frame);
|
||||
if let Err(_) = encoder.write_video(&codec_frame) {
|
||||
if encoder.write_video(&codec_frame).is_err() {
|
||||
let err = encoder.get_error();
|
||||
task.set_error(&err);
|
||||
return Err(Error::Failed("Failed to write frame".to_string()));
|
||||
@@ -433,7 +437,10 @@ impl RenderTaskBehavior for ExportTask {
|
||||
} else {
|
||||
0
|
||||
};
|
||||
if let Err(_) = encoder.write_audio(&samples.samples, frame_count as i32) {
|
||||
if encoder
|
||||
.write_audio(&samples.samples, frame_count as i32)
|
||||
.is_err()
|
||||
{
|
||||
let err = encoder.get_error();
|
||||
task.set_error(&err);
|
||||
return Err(Error::Failed("Failed to write audio".to_string()));
|
||||
@@ -450,7 +457,7 @@ impl RenderTaskBehavior for ExportTask {
|
||||
};
|
||||
// The simplified path does not carry the subtitle block's in/out
|
||||
// times; write with 0.0/0.0 (the encoder default interval).
|
||||
if let Err(_) = encoder.write_subtitle(text, 0.0, 0.0) {
|
||||
if encoder.write_subtitle(text, 0.0, 0.0).is_err() {
|
||||
let err = encoder.get_error();
|
||||
task.set_error(&err);
|
||||
return Err(Error::Failed("Failed to write subtitle".to_string()));
|
||||
|
||||
@@ -101,6 +101,11 @@ pub unsafe fn get<T: 'static>(h: &CHandle) -> Option<&T> {
|
||||
/// # Safety
|
||||
/// `T` must be the boxed type, and the handle must not be concurrently
|
||||
/// shared mutably.
|
||||
// The shared reference is the ABI input; exclusivity is the caller's
|
||||
// `unsafe` contract above, so the lint's usual aliasing concern is
|
||||
// discharged by the caller, not by `&mut CHandle` (which the C ABI does
|
||||
// not pass).
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub unsafe fn get_mut<T: 'static>(h: &CHandle) -> Option<&mut T> {
|
||||
if h.ctx.is_null() {
|
||||
return None;
|
||||
|
||||
@@ -194,9 +194,7 @@ impl TaskManager {
|
||||
|
||||
/// Index of the task with the given address.
|
||||
pub fn find_index(&self, ptr: *const Task) -> Option<usize> {
|
||||
self.tasks
|
||||
.iter()
|
||||
.position(|t| t.task() as *const Task == ptr)
|
||||
self.tasks.iter().position(|t| std::ptr::eq(t.task(), ptr))
|
||||
}
|
||||
|
||||
/// Cancel the task with the given address (no-op when absent).
|
||||
|
||||
@@ -199,8 +199,9 @@ impl ProxyTask {
|
||||
/// 探测本机可用的硬件 H.264 编码器(每进程每 ffmpeg 路径缓存一次:
|
||||
/// `ffmpeg -hide_banner -encoders` 的输出里按平台优先级找)。
|
||||
pub fn probe_hw_encoder(ffmpeg_path: &str) -> HwEncoder {
|
||||
static CACHE: std::sync::OnceLock<std::sync::Mutex<std::collections::HashMap<String, HwEncoder>>> =
|
||||
std::sync::OnceLock::new();
|
||||
static CACHE: std::sync::OnceLock<
|
||||
std::sync::Mutex<std::collections::HashMap<String, HwEncoder>>,
|
||||
> = std::sync::OnceLock::new();
|
||||
let cache = CACHE.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()));
|
||||
let mut cache = cache.lock().unwrap_or_else(|e| e.into_inner());
|
||||
if let Some(enc) = cache.get(ffmpeg_path) {
|
||||
@@ -424,13 +425,14 @@ impl TaskBehavior for ProxyTask {
|
||||
|
||||
// Create the output directory if needed.
|
||||
if let Some(parent) = std::path::Path::new(&self.output_filename).parent() {
|
||||
if !parent.as_os_str().is_empty() && !parent.exists() {
|
||||
if std::fs::create_dir_all(parent).is_err() {
|
||||
task.set_error("Failed to create proxy output directory");
|
||||
return Err(Error::Failed(
|
||||
"Failed to create proxy output directory".to_string(),
|
||||
));
|
||||
}
|
||||
if !parent.as_os_str().is_empty()
|
||||
&& !parent.exists()
|
||||
&& std::fs::create_dir_all(parent).is_err()
|
||||
{
|
||||
task.set_error("Failed to create proxy output directory");
|
||||
return Err(Error::Failed(
|
||||
"Failed to create proxy output directory".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,7 +585,12 @@ mod tests {
|
||||
#[test]
|
||||
fn transcode_arguments_software_keeps_parity_body() {
|
||||
let args = ProxyTask::build_transcode_arguments(
|
||||
"/src.mov", 0, ¶ms(), "/dst.mp4", HwEncoder::Software, 4,
|
||||
"/src.mov",
|
||||
0,
|
||||
¶ms(),
|
||||
"/dst.mp4",
|
||||
HwEncoder::Software,
|
||||
4,
|
||||
);
|
||||
assert!(args.windows(2).any(|w| w == ["-hwaccel", "auto"]));
|
||||
assert!(args.windows(2).any(|w| w == ["-threads", "4"]));
|
||||
@@ -600,27 +607,47 @@ mod tests {
|
||||
#[test]
|
||||
fn transcode_arguments_hw_swaps_encoder() {
|
||||
let vt = ProxyTask::build_transcode_arguments(
|
||||
"/src.mov", 0, ¶ms(), "/dst.mp4", HwEncoder::VideoToolbox, 4,
|
||||
"/src.mov",
|
||||
0,
|
||||
¶ms(),
|
||||
"/dst.mp4",
|
||||
HwEncoder::VideoToolbox,
|
||||
4,
|
||||
);
|
||||
assert!(vt.windows(2).any(|w| w == ["-c:v", "h264_videotoolbox"]));
|
||||
assert!(vt.windows(2).any(|w| w == ["-q:v", "23"]));
|
||||
assert!(!vt.iter().any(|a| a == "libx264" || a == "-crf"));
|
||||
|
||||
let nv = ProxyTask::build_transcode_arguments(
|
||||
"/src.mov", 0, ¶ms(), "/dst.mp4", HwEncoder::Nvenc, 4,
|
||||
"/src.mov",
|
||||
0,
|
||||
¶ms(),
|
||||
"/dst.mp4",
|
||||
HwEncoder::Nvenc,
|
||||
4,
|
||||
);
|
||||
assert!(nv.windows(2).any(|w| w == ["-c:v", "h264_nvenc"]));
|
||||
assert!(nv.windows(2).any(|w| w == ["-preset", "p3"]));
|
||||
assert!(nv.windows(2).any(|w| w == ["-cq", "23"]));
|
||||
|
||||
let qsv = ProxyTask::build_transcode_arguments(
|
||||
"/src.mov", 0, ¶ms(), "/dst.mp4", HwEncoder::Qsv, 4,
|
||||
"/src.mov",
|
||||
0,
|
||||
¶ms(),
|
||||
"/dst.mp4",
|
||||
HwEncoder::Qsv,
|
||||
4,
|
||||
);
|
||||
assert!(qsv.windows(2).any(|w| w == ["-c:v", "h264_qsv"]));
|
||||
assert!(qsv.windows(2).any(|w| w == ["-global_quality", "23"]));
|
||||
|
||||
let amf = ProxyTask::build_transcode_arguments(
|
||||
"/src.mov", 0, ¶ms(), "/dst.mp4", HwEncoder::Amf, 4,
|
||||
"/src.mov",
|
||||
0,
|
||||
¶ms(),
|
||||
"/dst.mp4",
|
||||
HwEncoder::Amf,
|
||||
4,
|
||||
);
|
||||
assert!(amf.windows(2).any(|w| w == ["-c:v", "h264_amf"]));
|
||||
assert!(amf.windows(2).any(|w| w == ["-quality", "speed"]));
|
||||
|
||||
@@ -57,19 +57,15 @@ use oak_core::videoparams::VideoParams;
|
||||
use oak_node::footage::FootageBehavior;
|
||||
use oak_node::sequence::SequenceBehavior;
|
||||
use oak_node::track::{TrackBehavior, TrackListBehavior, TrackType};
|
||||
use oak_render::procpool::{
|
||||
bgra8_to_f32_rgba, DispatcherConfig, ProcessDispatcher, ShmFrameRef,
|
||||
};
|
||||
use oak_render::procpool::{bgra8_to_f32_rgba, DispatcherConfig, ProcessDispatcher, ShmFrameRef};
|
||||
use oak_render::ticket::{
|
||||
ticket_kind, AudioTicketParams, MontageClip, MontageEffect, TicketArena, TicketId, TicketPayload,
|
||||
TicketResult, VideoTicketParams,
|
||||
ticket_kind, AudioTicketParams, MontageClip, MontageEffect, TicketArena, TicketId,
|
||||
TicketPayload, TicketResult, VideoTicketParams,
|
||||
};
|
||||
use oak_render::worker::JobDispatch;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::nodeops::{
|
||||
find_input_footage, pixel_format_from_code, NodeRef, ProjectRef,
|
||||
};
|
||||
use crate::nodeops::{find_input_footage, pixel_format_from_code, NodeRef, ProjectRef};
|
||||
use crate::task::Task;
|
||||
use oak_core::{Rational, TimeRange};
|
||||
|
||||
@@ -112,12 +108,16 @@ pub struct ForceParams {
|
||||
pub trait RenderTaskBehavior {
|
||||
/// Called for each rendered video frame.
|
||||
fn frame_downloaded(
|
||||
&mut self,
|
||||
task: &mut Task,
|
||||
frame: &oak_core::texture::Texture,
|
||||
&mut self,
|
||||
task: &mut Task,
|
||||
frame: &oak_core::texture::Texture,
|
||||
) -> Result<()>;
|
||||
/// Called for each rendered audio buffer.
|
||||
fn audio_downloaded(&mut self, task: &mut Task, samples: &oak_render::ticket::AudioSamples) -> Result<()>;
|
||||
fn audio_downloaded(
|
||||
&mut self,
|
||||
task: &mut Task,
|
||||
samples: &oak_render::ticket::AudioSamples,
|
||||
) -> Result<()>;
|
||||
/// Called to encode a subtitle.
|
||||
fn encode_subtitle(&mut self, task: &mut Task, text: &str) -> Result<()>;
|
||||
}
|
||||
@@ -230,7 +230,10 @@ impl RenderTask {
|
||||
/// The forced output size from [`ForceParams`], or `None`.
|
||||
fn force_size(&self) -> Option<(i32, i32)> {
|
||||
if self.force_params.force_width > 0 && self.force_params.force_height > 0 {
|
||||
Some((self.force_params.force_width, self.force_params.force_height))
|
||||
Some((
|
||||
self.force_params.force_width,
|
||||
self.force_params.force_height,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -253,7 +256,10 @@ impl RenderTask {
|
||||
/// traverser's bypass pushes the effect input through unchanged).
|
||||
/// Parameters are the non-hidden, non-connection data inputs at
|
||||
/// their standard (non-keyframed) values.
|
||||
fn clip_effects(graph: &oak_node::graph::Graph, host: oak_node::id::NodeId) -> Vec<MontageEffect> {
|
||||
fn clip_effects(
|
||||
graph: &oak_node::graph::Graph,
|
||||
host: oak_node::id::NodeId,
|
||||
) -> Vec<MontageEffect> {
|
||||
// Walk the chain: from the host's effect input upstream until an
|
||||
// unconnected input or a node without an effect input; a `seen`
|
||||
// guard protects against malformed cycles.
|
||||
@@ -333,10 +339,12 @@ impl RenderTask {
|
||||
/// (bottom-most track first so the topmost track — the highest-numbered
|
||||
/// one, the list's last — composites last;
|
||||
/// `// CPP-PARITY: M12 P0 montage contract`).
|
||||
fn video_montage(project: &ProjectRef, sequence: oak_node::id::NodeId, time: Rational) -> Vec<MontageClip> {
|
||||
let guard = project
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner());
|
||||
fn video_montage(
|
||||
project: &ProjectRef,
|
||||
sequence: oak_node::id::NodeId,
|
||||
time: Rational,
|
||||
) -> Vec<MontageClip> {
|
||||
let guard = project.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let Some(entry) = guard.graph.get(sequence) else {
|
||||
return Vec::new();
|
||||
};
|
||||
@@ -374,8 +382,7 @@ impl RenderTask {
|
||||
continue;
|
||||
};
|
||||
for block_id in &track.blocks {
|
||||
let Some(core) = crate::nodeops::block_core_of(&guard.graph, *block_id)
|
||||
else {
|
||||
let Some(core) = crate::nodeops::block_core_of(&guard.graph, *block_id) else {
|
||||
continue;
|
||||
};
|
||||
if time < core.in_() || time >= core.out() {
|
||||
@@ -411,10 +418,12 @@ impl RenderTask {
|
||||
|
||||
/// Flatten the audio tracks of `sequence` into an audio montage
|
||||
/// (track order is irrelevant — the mixer accumulates gains).
|
||||
fn audio_montage(project: &ProjectRef, sequence: oak_node::id::NodeId, time: Rational) -> Vec<MontageClip> {
|
||||
let guard = project
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner());
|
||||
fn audio_montage(
|
||||
project: &ProjectRef,
|
||||
sequence: oak_node::id::NodeId,
|
||||
time: Rational,
|
||||
) -> Vec<MontageClip> {
|
||||
let guard = project.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let Some(entry) = guard.graph.get(sequence) else {
|
||||
return Vec::new();
|
||||
};
|
||||
@@ -452,8 +461,7 @@ impl RenderTask {
|
||||
continue;
|
||||
};
|
||||
for block_id in &track.blocks {
|
||||
let Some(core) = crate::nodeops::block_core_of(&guard.graph, *block_id)
|
||||
else {
|
||||
let Some(core) = crate::nodeops::block_core_of(&guard.graph, *block_id) else {
|
||||
continue;
|
||||
};
|
||||
if time < core.in_() || time >= core.out() {
|
||||
@@ -569,7 +577,8 @@ impl RenderTask {
|
||||
/// single range; the C++ submits one ticket per audio range).
|
||||
fn build_audio_ticket(&self, range: TimeRange) -> Result<AudioTicketParams> {
|
||||
let (project, viewer_id) = &self.viewer;
|
||||
let (sample_rate, channel_layout) = crate::nodeops::sequence_audio_params(project, *viewer_id);
|
||||
let (sample_rate, channel_layout) =
|
||||
crate::nodeops::sequence_audio_params(project, *viewer_id);
|
||||
let montage = Self::audio_montage(project, *viewer_id, range.in_());
|
||||
Ok(AudioTicketParams {
|
||||
viewer: viewer_id.identity(),
|
||||
@@ -595,9 +604,13 @@ impl RenderTask {
|
||||
let params = self.build_video_ticket(time)?;
|
||||
let id = arena.next_id();
|
||||
let dispatch_ptr = DispatchPtr(dispatch);
|
||||
arena.submit_video_background_with_id(id, params, Box::new(move |result| {
|
||||
push_finished(id, result, dispatch_ptr);
|
||||
}));
|
||||
arena.submit_video_background_with_id(
|
||||
id,
|
||||
params,
|
||||
Box::new(move |result| {
|
||||
push_finished(id, result, dispatch_ptr);
|
||||
}),
|
||||
);
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
@@ -612,9 +625,13 @@ impl RenderTask {
|
||||
let params = self.build_audio_ticket(range)?;
|
||||
let id = arena.next_id();
|
||||
let dispatch_ptr = DispatchPtr(dispatch);
|
||||
arena.submit_audio_with_id(id, params, Box::new(move |result| {
|
||||
push_finished(id, result, dispatch_ptr);
|
||||
}));
|
||||
arena.submit_audio_with_id(
|
||||
id,
|
||||
params,
|
||||
Box::new(move |result| {
|
||||
push_finished(id, result, dispatch_ptr);
|
||||
}),
|
||||
);
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
@@ -638,10 +655,7 @@ impl RenderTask {
|
||||
match self.submit_video_ticket(arena, time, dispatch) {
|
||||
Ok(id) => {
|
||||
in_flight.push(id);
|
||||
ticket_keys.insert(
|
||||
id,
|
||||
(TICKET_VIDEO, time.numerator(), time.denominator()),
|
||||
);
|
||||
ticket_keys.insert(id, (TICKET_VIDEO, time.numerator(), time.denominator()));
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -772,8 +786,7 @@ impl RenderTask {
|
||||
.start()
|
||||
.map_err(|e| Error::Failed(format!("render worker pool start: {e}")))?;
|
||||
let producer: oak_render::ticket::Producer = Arc::new(|time, params| {
|
||||
oak_render::eval::render_produced_frame(time, params)
|
||||
.map(TicketPayload::Video)
|
||||
oak_render::eval::render_produced_frame(time, params).map(TicketPayload::Video)
|
||||
});
|
||||
// M15 S3: the private dispatcher routes audio through the
|
||||
// worker pool too; the inline dispatcher is the fallback
|
||||
@@ -910,9 +923,8 @@ impl RenderTask {
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
result = Err(Error::Failed(format!(
|
||||
"Audio render ticket failed: {e:?}"
|
||||
)));
|
||||
result =
|
||||
Err(Error::Failed(format!("Audio render ticket failed: {e:?}")));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -956,9 +968,8 @@ impl RenderTask {
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
result = Err(Error::Failed(format!(
|
||||
"Frame render ticket failed: {e:?}"
|
||||
)));
|
||||
result =
|
||||
Err(Error::Failed(format!("Frame render ticket failed: {e:?}")));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1020,8 +1031,7 @@ impl RenderTask {
|
||||
.finished
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
.is_empty()
|
||||
&& dispatch_ref.running.load(Ordering::SeqCst) == 0
|
||||
.is_empty() && dispatch_ref.running.load(Ordering::SeqCst) == 0
|
||||
{
|
||||
// Every ticket finished and its queue copy was consumed.
|
||||
break;
|
||||
@@ -1202,7 +1212,9 @@ fn shm_frame_to_texture(frame: &ShmFrameRef) -> oak_core::texture::Texture {
|
||||
f.channels = 4;
|
||||
f.timestamp = oak_core::Rational::new(meta.time_num, meta.time_den);
|
||||
f.data = samples
|
||||
.chunks_exact(4)
|
||||
.as_chunks::<4>()
|
||||
.0
|
||||
.iter()
|
||||
.flat_map(|px| {
|
||||
let mut bytes = [0u8; 16];
|
||||
for (i, v) in px.iter().enumerate() {
|
||||
|
||||
Reference in New Issue
Block a user