mirror of
https://github.com/storytold/soundcraft.git
synced 2026-10-24 10:36:46 -06:00
Compare commits
2
Commits
main
...
fix/sonar-s107
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7469834420 | ||
|
|
e220666546 |
@@ -317,7 +317,14 @@ impl<W: Write + Seek> FileEncoder<W> {
|
||||
pub fn new(out: W, opts: &EncodeOptions, channels: usize, sample_rate: u32, mask: Option<u32>) -> Result<Self> {
|
||||
let (depth, dither) = (opts.bit_depth, opts.dither);
|
||||
Ok(FileEncoder(match opts.format {
|
||||
FileFormat::Wav => Writer::Wav(wav::Writer::new(out, channels, sample_rate, depth, dither, opts.bwf.as_ref(), mask, false)?),
|
||||
FileFormat::Wav => Writer::Wav(wav::Writer::new(
|
||||
out,
|
||||
channels,
|
||||
sample_rate,
|
||||
depth,
|
||||
dither,
|
||||
wav::HeaderExtras { bwf: opts.bwf.as_ref(), mask, force_rf64: false },
|
||||
)?),
|
||||
FileFormat::Aiff => Writer::Aiff(aiff::Writer::new(out, channels, sample_rate, depth, dither)?),
|
||||
FileFormat::Flac => Writer::Flac(flac::Writer::new(out, channels, sample_rate, depth, dither)?),
|
||||
other => return Err(AudioError::Unsupported(format!("encoding {other:?} is not supported"))),
|
||||
|
||||
+11
-11
@@ -355,17 +355,17 @@ pub(crate) struct Writer<W> {
|
||||
failed: bool,
|
||||
}
|
||||
|
||||
/// The optional header parts of a WAV file: BWF metadata, speaker mask, and whether to force RF64.
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct HeaderExtras<'a> {
|
||||
pub(crate) bwf: Option<&'a BwfInfo>,
|
||||
pub(crate) mask: Option<u32>,
|
||||
pub(crate) force_rf64: bool,
|
||||
}
|
||||
|
||||
impl<W: Write + Seek> Writer<W> {
|
||||
pub(crate) fn new(
|
||||
mut out: W,
|
||||
channels: usize,
|
||||
sample_rate: u32,
|
||||
depth: BitDepth,
|
||||
dither: bool,
|
||||
bwf: Option<&BwfInfo>,
|
||||
mask: Option<u32>,
|
||||
force_rf64: bool,
|
||||
) -> Result<Self> {
|
||||
pub(crate) fn new(mut out: W, channels: usize, sample_rate: u32, depth: BitDepth, dither: bool, extras: HeaderExtras<'_>) -> Result<Self> {
|
||||
let HeaderExtras { bwf, mask, force_rf64 } = extras;
|
||||
validate_layout(channels, sample_rate)?;
|
||||
let format = sample_format(depth);
|
||||
let start = out.stream_position()?;
|
||||
@@ -449,7 +449,7 @@ pub(crate) fn encode(
|
||||
let mut out = Vec::new();
|
||||
out.try_reserve_exact(head.len().saturating_add(data_len).saturating_add(1))
|
||||
.map_err(|_| AudioError::TooLarge(format!("cannot allocate {data_len} bytes for WAV output")))?;
|
||||
let mut w = Writer::new(Cursor::new(out), channels, buf.sample_rate, depth, dither, bwf, mask, force_rf64)?;
|
||||
let mut w = Writer::new(Cursor::new(out), channels, buf.sample_rate, depth, dither, HeaderExtras { bwf, mask, force_rf64 })?;
|
||||
w.write(&buf.channels, frames)?;
|
||||
Ok(w.finish()?.into_inner())
|
||||
}
|
||||
|
||||
@@ -1656,7 +1656,14 @@ fn draw_live_take(app: &SoundApp, painter: &egui::Painter, s: &Session, track: &
|
||||
let (body, _, wave) = clip_colors([t.rec.r(), t.rec.g(), t.rec.b()], false);
|
||||
painter.rect(r, CornerRadius::same(2), body, Stroke::new(1.0, Color32::from_rgb(10, 10, 10)), StrokeKind::Inside);
|
||||
if track.view != "blocks" {
|
||||
crate::waveform::draw_live(&painter.with_clip_rect(r.intersect(lane)), s, tl, origin as f64, block_len, &take, r, wave);
|
||||
crate::waveform::draw_live(
|
||||
crate::waveform::Canvas { painter: &painter.with_clip_rect(r.intersect(lane)), s, tl },
|
||||
origin as f64,
|
||||
block_len,
|
||||
&take,
|
||||
r,
|
||||
wave,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1722,7 +1729,14 @@ fn draw_clip(app: &SoundApp, painter: &egui::Painter, s: &Session, track: &Track
|
||||
preview = Clip { fade_in, fade_out, ..clip.clone() };
|
||||
&preview
|
||||
};
|
||||
crate::waveform::draw(&painter.with_clip_rect(vis), s, tl, shown, audio, track.channels(), body_r, wave);
|
||||
crate::waveform::draw(
|
||||
crate::waveform::Canvas { painter: &painter.with_clip_rect(vis), s, tl },
|
||||
shown,
|
||||
audio,
|
||||
track.channels(),
|
||||
body_r,
|
||||
wave,
|
||||
);
|
||||
let n = track.channels().min(audio.peaks.len()).max(1);
|
||||
for k in 1..n {
|
||||
let y = body_r.min.y + body_r.height() / n as f32 * k as f32;
|
||||
@@ -1776,7 +1790,7 @@ fn draw_clip(app: &SoundApp, painter: &egui::Painter, s: &Session, track: &Track
|
||||
} else {
|
||||
clip_colors(prev.color.unwrap_or(track.color), false).2.gamma_multiply(0.6)
|
||||
};
|
||||
crate::waveform::draw(&painter.with_clip_rect(xf), s, tl, prev, audio, track.channels(), body_r, tail);
|
||||
crate::waveform::draw(crate::waveform::Canvas { painter: &painter.with_clip_rect(xf), s, tl }, prev, audio, track.channels(), body_r, tail);
|
||||
}
|
||||
// Clip gain line.
|
||||
let show_gain = s.edit.flag("view.clip.gain_line") && (!clip.gain_env.is_empty() || clip.gain_db.abs() > 0.01 || track.view == "clip_gain");
|
||||
@@ -2732,6 +2746,18 @@ fn clip_context_menu(app: &mut SoundApp, ui: &mut Ui, clip: Option<ClipId>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// One frame's inputs to [`follow_update`].
|
||||
struct FollowFrame {
|
||||
playing: bool,
|
||||
mode_follow: bool,
|
||||
playhead_visible: bool,
|
||||
cur: Samples,
|
||||
last_scroll: Samples,
|
||||
last_follow_to: Option<Samples>,
|
||||
hold: bool,
|
||||
pos: Samples,
|
||||
}
|
||||
|
||||
/// Next follow state after observing one frame: `(hold, follow_to)`.
|
||||
///
|
||||
/// The view is ours to move only while nothing else moved it: when the
|
||||
@@ -2741,16 +2767,8 @@ fn clip_context_menu(app: &mut SoundApp, ui: &mut Ui, clip: Option<ClipId>) {
|
||||
/// jump is issued. Stopping, or scrolling back to the playhead, clears the
|
||||
/// hold. Only `page`/`continuous`/`center` ever issue follow jumps while
|
||||
/// playing; `none`/`after_playback` leave the view alone.
|
||||
fn follow_update(
|
||||
playing: bool,
|
||||
mode_follow: bool,
|
||||
playhead_visible: bool,
|
||||
cur: Samples,
|
||||
last_scroll: Samples,
|
||||
last_follow_to: Option<Samples>,
|
||||
hold: bool,
|
||||
pos: Samples,
|
||||
) -> (bool, Option<Samples>) {
|
||||
fn follow_update(frame: FollowFrame) -> (bool, Option<Samples>) {
|
||||
let FollowFrame { playing, mode_follow, playhead_visible, cur, last_scroll, last_follow_to, hold, pos } = frame;
|
||||
if !playing || playhead_visible {
|
||||
return (false, None);
|
||||
}
|
||||
@@ -2828,7 +2846,16 @@ fn overlay(app: &mut SoundApp, ui: &mut Ui, tl: Rect, area: Rect) {
|
||||
let mode_follow = matches!(s.edit.scrolling.as_str(), "page" | "continuous" | "center");
|
||||
let visible = x >= tl.min.x && x <= tl.max.x;
|
||||
let layout = &mut app.edit_layout;
|
||||
let (hold, follow_to) = follow_update(true, mode_follow, visible, cur, layout.last_scroll, layout.last_follow_to, layout.follow_hold, pos);
|
||||
let (hold, follow_to) = follow_update(FollowFrame {
|
||||
playing: true,
|
||||
mode_follow,
|
||||
playhead_visible: visible,
|
||||
cur,
|
||||
last_scroll: layout.last_scroll,
|
||||
last_follow_to: layout.last_follow_to,
|
||||
hold: layout.follow_hold,
|
||||
pos,
|
||||
});
|
||||
layout.follow_hold = hold;
|
||||
layout.last_scroll = cur;
|
||||
layout.last_follow_to = follow_to;
|
||||
@@ -2955,40 +2982,160 @@ mod tests {
|
||||
#[test]
|
||||
fn follow_jumps_to_offscreen_playhead_while_playing() {
|
||||
// Steady view, follow mode, playhead left the screen: jump to it.
|
||||
assert_eq!(follow_update(true, true, false, 1_000, 1_000, None, false, 96_000), (false, Some(96_000)));
|
||||
assert_eq!(
|
||||
follow_update(FollowFrame {
|
||||
playing: true,
|
||||
mode_follow: true,
|
||||
playhead_visible: false,
|
||||
cur: 1_000,
|
||||
last_scroll: 1_000,
|
||||
last_follow_to: None,
|
||||
hold: false,
|
||||
pos: 96_000
|
||||
}),
|
||||
(false, Some(96_000))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn follow_leaves_onscreen_playhead_alone() {
|
||||
assert_eq!(follow_update(true, true, true, 1_000, 1_000, None, false, 96_000), (false, None));
|
||||
assert_eq!(
|
||||
follow_update(FollowFrame {
|
||||
playing: true,
|
||||
mode_follow: true,
|
||||
playhead_visible: true,
|
||||
cur: 1_000,
|
||||
last_scroll: 1_000,
|
||||
last_follow_to: None,
|
||||
hold: false,
|
||||
pos: 96_000
|
||||
}),
|
||||
(false, None)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn follow_never_scrolls_when_stopped_nor_in_free_modes() {
|
||||
assert_eq!(follow_update(false, true, false, 1_000, 1_000, None, false, 96_000), (false, None));
|
||||
assert_eq!(
|
||||
follow_update(FollowFrame {
|
||||
playing: false,
|
||||
mode_follow: true,
|
||||
playhead_visible: false,
|
||||
cur: 1_000,
|
||||
last_scroll: 1_000,
|
||||
last_follow_to: None,
|
||||
hold: false,
|
||||
pos: 96_000
|
||||
}),
|
||||
(false, None)
|
||||
);
|
||||
// `none`/`after_playback` never follow, even with a steady view.
|
||||
assert_eq!(follow_update(true, false, false, 1_000, 1_000, None, false, 96_000), (false, None));
|
||||
assert_eq!(follow_update(true, false, false, 9_000, 1_000, None, false, 96_000), (false, None));
|
||||
assert_eq!(
|
||||
follow_update(FollowFrame {
|
||||
playing: true,
|
||||
mode_follow: false,
|
||||
playhead_visible: false,
|
||||
cur: 1_000,
|
||||
last_scroll: 1_000,
|
||||
last_follow_to: None,
|
||||
hold: false,
|
||||
pos: 96_000
|
||||
}),
|
||||
(false, None)
|
||||
);
|
||||
assert_eq!(
|
||||
follow_update(FollowFrame {
|
||||
playing: true,
|
||||
mode_follow: false,
|
||||
playhead_visible: false,
|
||||
cur: 9_000,
|
||||
last_scroll: 1_000,
|
||||
last_follow_to: None,
|
||||
hold: false,
|
||||
pos: 96_000
|
||||
}),
|
||||
(false, None)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn outside_view_move_pauses_the_follow() {
|
||||
// The view moved and it was not our own jump: pause, no jump.
|
||||
assert_eq!(follow_update(true, true, false, 9_000, 1_000, None, false, 96_000), (true, None));
|
||||
assert_eq!(
|
||||
follow_update(FollowFrame {
|
||||
playing: true,
|
||||
mode_follow: true,
|
||||
playhead_visible: false,
|
||||
cur: 9_000,
|
||||
last_scroll: 1_000,
|
||||
last_follow_to: None,
|
||||
hold: false,
|
||||
pos: 96_000
|
||||
}),
|
||||
(true, None)
|
||||
);
|
||||
// A held view stays held while it sits still elsewhere.
|
||||
assert_eq!(follow_update(true, true, false, 9_000, 9_000, None, true, 96_000), (true, None));
|
||||
assert_eq!(
|
||||
follow_update(FollowFrame {
|
||||
playing: true,
|
||||
mode_follow: true,
|
||||
playhead_visible: false,
|
||||
cur: 9_000,
|
||||
last_scroll: 9_000,
|
||||
last_follow_to: None,
|
||||
hold: true,
|
||||
pos: 96_000
|
||||
}),
|
||||
(true, None)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn own_follow_jump_is_not_mistaken_for_a_takeover() {
|
||||
// The scroll equals the target we issued last frame: keep following.
|
||||
assert_eq!(follow_update(true, true, false, 96_000, 1_000, Some(96_000), false, 97_000), (false, Some(97_000)));
|
||||
assert_eq!(
|
||||
follow_update(FollowFrame {
|
||||
playing: true,
|
||||
mode_follow: true,
|
||||
playhead_visible: false,
|
||||
cur: 96_000,
|
||||
last_scroll: 1_000,
|
||||
last_follow_to: Some(96_000),
|
||||
hold: false,
|
||||
pos: 97_000
|
||||
}),
|
||||
(false, Some(97_000))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hold_clears_when_playhead_returns_or_transport_stops() {
|
||||
assert_eq!(follow_update(true, true, true, 9_000, 9_000, None, true, 96_000), (false, None));
|
||||
assert_eq!(follow_update(false, true, false, 9_000, 9_000, None, true, 96_000), (false, None));
|
||||
assert_eq!(
|
||||
follow_update(FollowFrame {
|
||||
playing: true,
|
||||
mode_follow: true,
|
||||
playhead_visible: true,
|
||||
cur: 9_000,
|
||||
last_scroll: 9_000,
|
||||
last_follow_to: None,
|
||||
hold: true,
|
||||
pos: 96_000
|
||||
}),
|
||||
(false, None)
|
||||
);
|
||||
assert_eq!(
|
||||
follow_update(FollowFrame {
|
||||
playing: false,
|
||||
mode_follow: true,
|
||||
playhead_visible: false,
|
||||
cur: 9_000,
|
||||
last_scroll: 9_000,
|
||||
last_follow_to: None,
|
||||
hold: true,
|
||||
pos: 96_000
|
||||
}),
|
||||
(false, None)
|
||||
);
|
||||
}
|
||||
|
||||
fn harness(n: usize) -> Harness<'static, SoundApp> {
|
||||
|
||||
@@ -4,10 +4,18 @@ use egui::{Color32, Mesh, Painter, Rect, Shape, Stroke, pos2, vec2};
|
||||
use soundcraft_model::{Clip, Session, SourceAudio};
|
||||
use soundcraft_time::Samples;
|
||||
|
||||
/// Where to draw: the painter, the session and the timeline rect that maps samples to x.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Canvas<'a> {
|
||||
pub painter: &'a Painter,
|
||||
pub s: &'a Session,
|
||||
pub tl: Rect,
|
||||
}
|
||||
|
||||
/// Draw a clip's audio across `body`, one band per channel, `channels` of them at most.
|
||||
pub fn draw(painter: &Painter, s: &Session, tl: Rect, clip: &Clip, audio: &SourceAudio, channels: usize, body: Rect, wave: Color32) {
|
||||
pub fn draw(canvas: Canvas<'_>, clip: &Clip, audio: &SourceAudio, channels: usize, body: Rect, wave: Color32) {
|
||||
for (ch, band) in bands(body, channels.min(audio.peaks.len()).max(1)).enumerate() {
|
||||
channel(painter, s, tl, clip, audio, ch, band, wave);
|
||||
channel(canvas, clip, audio, ch, band, wave);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +24,8 @@ fn bands(body: Rect, n: usize) -> impl Iterator<Item = Rect> {
|
||||
(0..n).map(move |k| Rect::from_min_size(pos2(body.min.x, body.min.y + h * k as f32), vec2(body.width(), h)))
|
||||
}
|
||||
|
||||
fn channel(painter: &Painter, s: &Session, tl: Rect, clip: &Clip, audio: &SourceAudio, ch: usize, band: Rect, wave: Color32) {
|
||||
fn channel(canvas: Canvas<'_>, clip: &Clip, audio: &SourceAudio, ch: usize, band: Rect, wave: Color32) {
|
||||
let Canvas { painter, s, tl } = canvas;
|
||||
let (Some(samples), Some(peaks)) = (audio.channel(ch), audio.peaks.get(ch)) else { return };
|
||||
let Some(g) = Grid::new(painter, s, tl, band) else { return };
|
||||
let (rectified, power, outlines) = (s.edit.flag("waveform.rectified"), s.edit.flag("waveform.power"), s.edit.flag("waveform.outlines"));
|
||||
@@ -59,7 +68,8 @@ fn channel(painter: &Painter, s: &Session, tl: Rect, clip: &Clip, audio: &Source
|
||||
/// Draw a take while it records across `body`, one band per channel: `blocks` holds each channel's
|
||||
/// (min, max) per block of input frames, the blocks `block_len` timeline samples long from
|
||||
/// timeline position `origin`.
|
||||
pub fn draw_live(painter: &Painter, s: &Session, tl: Rect, origin: f64, block_len: f64, blocks: &[Vec<(f32, f32)>], body: Rect, wave: Color32) {
|
||||
pub fn draw_live(canvas: Canvas<'_>, origin: f64, block_len: f64, blocks: &[Vec<(f32, f32)>], body: Rect, wave: Color32) {
|
||||
let Canvas { painter, s, tl } = canvas;
|
||||
let zoom = s.edit.zoom.waveform_zoom;
|
||||
for (blocks, band) in blocks.iter().zip(bands(body, blocks.len())) {
|
||||
let Some(g) = Grid::new(painter, s, tl, band) else { continue };
|
||||
|
||||
@@ -22,13 +22,13 @@ fn paint(samples_per_px: f64, f: impl Fn(&egui::Painter, &Session, Rect)) -> Vec
|
||||
fn draw(samples: Vec<f32>, samples_per_px: f64) -> Vec<Shape> {
|
||||
let clip = Clip::audio(ClipId(1), "c", SourceId(1), 0, 0, samples.len() as i64);
|
||||
let audio = SourceAudio::new(AudioBuffer { sample_rate: 48_000, channels: vec![samples] });
|
||||
paint(samples_per_px, |p, s, band| waveform::draw(p, s, band, &clip, &audio, 1, band, WAVE))
|
||||
paint(samples_per_px, |p, s, band| waveform::draw(waveform::Canvas { painter: p, s, tl: band }, &clip, &audio, 1, band, WAVE))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_take_draws_its_overview_while_it_records() {
|
||||
let blocks = vec![vec![(-0.5, 0.5); 100]];
|
||||
let shapes = paint(128.0, |p, s, band| waveform::draw_live(p, s, band, 1280.0, 128.0, &blocks, band, WAVE));
|
||||
let shapes = paint(128.0, |p, s, band| waveform::draw_live(waveform::Canvas { painter: p, s, tl: band }, 1280.0, 128.0, &blocks, band, WAVE));
|
||||
let [Shape::Mesh(mesh)] = shapes.as_slice() else { panic!("{shapes:?}") };
|
||||
let span = |f: fn(&Pos2) -> f32| mesh.vertices.iter().map(|v| f(&v.pos)).fold((f32::MAX, f32::MIN), |(a, b), v| (a.min(v), b.max(v)));
|
||||
let ((left, right), (top, bottom)) = (span(|p| p.x), span(|p| p.y));
|
||||
|
||||
+17
-13
@@ -54,20 +54,24 @@ impl Matrix {
|
||||
}
|
||||
}
|
||||
|
||||
/// The planes (and their geometry) of an 8-bit planar Y'CbCr picture for [`planar_to_rgba`].
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Planes<'a> {
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub y: &'a [u8],
|
||||
pub y_stride: usize,
|
||||
pub u: &'a [u8],
|
||||
pub v: &'a [u8],
|
||||
pub uv_stride: usize,
|
||||
pub cx: usize,
|
||||
pub cy: usize,
|
||||
}
|
||||
|
||||
/// Planar 8-bit Y'CbCr with chroma subsampled by `cx` horizontally and `cy` vertically
|
||||
/// (1 or 2 each) → packed RGBA. Missing samples (short planes) read as black / neutral.
|
||||
pub fn planar_to_rgba(
|
||||
width: usize,
|
||||
height: usize,
|
||||
y: &[u8],
|
||||
y_stride: usize,
|
||||
u: &[u8],
|
||||
v: &[u8],
|
||||
uv_stride: usize,
|
||||
cx: usize,
|
||||
cy: usize,
|
||||
m: &Matrix,
|
||||
) -> Vec<u8> {
|
||||
pub fn planar_to_rgba(p: &Planes<'_>, m: &Matrix) -> Vec<u8> {
|
||||
let Planes { width, height, y, y_stride, u, v, uv_stride, cx, cy } = *p;
|
||||
let (cx, cy) = (cx.max(1), cy.max(1));
|
||||
let mut out = vec![0u8; width.saturating_mul(height).saturating_mul(4)];
|
||||
for (row, line) in out.chunks_exact_mut(width.max(1) * 4).enumerate().take(height) {
|
||||
@@ -164,7 +168,7 @@ mod tests {
|
||||
#[test]
|
||||
fn planar_conversion_tolerates_short_planes() {
|
||||
let m = Matrix::new(true, false);
|
||||
let out = planar_to_rgba(4, 4, &[235; 3], 4, &[], &[], 2, 2, 2, &m);
|
||||
let out = planar_to_rgba(&Planes { width: 4, height: 4, y: &[235; 3], y_stride: 4, u: &[], v: &[], uv_stride: 2, cx: 2, cy: 2 }, &m);
|
||||
assert_eq!(out.len(), 64);
|
||||
assert_eq!(&out[..4], &[255, 255, 255, 255]);
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ impl Frame {
|
||||
|
||||
/// Copy the w x h window of chroma component `c` at (x0, y0) (clamped) into `out`.
|
||||
#[inline]
|
||||
pub fn chroma_window(&self, c: usize, x0: i32, y0: i32, w: usize, h: usize, out: &mut [u8], os: usize) {
|
||||
pub fn chroma_window(&self, c: usize, (x0, y0): (i32, i32), w: usize, h: usize, out: &mut [u8], os: usize) {
|
||||
let (cw, ch) = (self.cwidth, self.cheight);
|
||||
for r in 0..h {
|
||||
let yy = (y0 + r as i32).clamp(0, ch as i32 - 1) as usize;
|
||||
|
||||
@@ -236,7 +236,7 @@ fn predict_block(f: &Frame, mv: [i16; 2], px: usize, py: usize, bw: usize, bh: u
|
||||
let (fx, fy) = ((mv[0] & 7) as u32, (mv[1] & 7) as u32);
|
||||
for (comp, out) in c.into_iter().enumerate() {
|
||||
let mut w = [0u8; 16 * 9];
|
||||
f.chroma_window(comp, cx, cy, cw + 1, ch + 1, &mut w, 16);
|
||||
f.chroma_window(comp, (cx, cy), cw + 1, ch + 1, &mut w, 16);
|
||||
inter::mc_chroma_win(&w, 16, fx, fy, cw, ch, out, cs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! [`Movie`]: open an MP4/MOV, report its picture format, and decode frames at times to RGBA.
|
||||
|
||||
use crate::convert::{Matrix, planar_to_rgba, scale_rgba};
|
||||
use crate::convert::{Matrix, Planes, planar_to_rgba, scale_rgba};
|
||||
use crate::isobmff::{self, ByteSource, CodecConfig, Mp4File, TrackKind};
|
||||
use crate::{Result, VideoError};
|
||||
use std::collections::VecDeque;
|
||||
@@ -411,7 +411,7 @@ impl Movie {
|
||||
let (y, u, v) = (narrow(&f.y), narrow(&f.cb), narrow(&f.cr));
|
||||
let cx = if cw < w { 2 } else { 1 };
|
||||
let m = Matrix::for_code(f.color.matrix, f.height, false);
|
||||
let rgba = planar_to_rgba(w, h, &y, w, &u, &v, cw, cx, 1, &m);
|
||||
let rgba = planar_to_rgba(&Planes { width: w, height: h, y: &y, y_stride: w, u: &u, v: &v, uv_stride: cw, cx, cy: 1 }, &m);
|
||||
Ok(Frame { width: f.width, height: f.height, rgba, index, time })
|
||||
}
|
||||
Codec::Jpeg => {
|
||||
@@ -429,7 +429,20 @@ impl Movie {
|
||||
|
||||
fn h264_to_frame(&self, p: &crate::h264::Picture, index: usize) -> Frame {
|
||||
let m = Matrix::for_code(p.color.matrix, p.height, p.color.full_range);
|
||||
let rgba = planar_to_rgba(p.width as usize, p.height as usize, &p.y, p.y_stride, &p.u, &p.v, p.uv_stride, 2, 2, &m);
|
||||
let rgba = planar_to_rgba(
|
||||
&Planes {
|
||||
width: p.width as usize,
|
||||
height: p.height as usize,
|
||||
y: &p.y,
|
||||
y_stride: p.y_stride,
|
||||
u: &p.u,
|
||||
v: &p.v,
|
||||
uv_stride: p.uv_stride,
|
||||
cx: 2,
|
||||
cy: 2,
|
||||
},
|
||||
&m,
|
||||
);
|
||||
Frame { width: p.width, height: p.height, rgba, index, time: self.times.get(index).copied().unwrap_or(0.0) }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user