je ne saus plus c'est vieux ;-)
This commit is contained in:
@@ -477,6 +477,7 @@ impl StreamingFlacSink {
|
||||
timestamp_offset_sec: 0.0,
|
||||
current_timestamp: Arc::new(RwLock::new(0.0)),
|
||||
pending_track_duration: None,
|
||||
pending_total_samples: None,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -432,6 +432,7 @@ impl StreamingOggFlacSink {
|
||||
timestamp_offset_sec: 0.0,
|
||||
current_timestamp: Arc::new(RwLock::new(0.0)),
|
||||
pending_track_duration: None,
|
||||
pending_total_samples: None,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ use tokio::io::{AsyncRead, ReadBuf};
|
||||
use tokio::sync::{mpsc, RwLock};
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{debug, error, trace, warn};
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
use crate::byte_stream_reader::{ByteStreamReader, PcmChunk};
|
||||
use crate::sinks::timed_broadcast::{self, TryRecvError};
|
||||
@@ -214,6 +214,7 @@ pub struct SharedSinkContext {
|
||||
pub timestamp_offset_sec: f64,
|
||||
pub current_timestamp: Arc<RwLock<f64>>,
|
||||
pub pending_track_duration: Option<Duration>,
|
||||
pub pending_total_samples: Option<u64>,
|
||||
}
|
||||
|
||||
impl SharedSinkContext {
|
||||
@@ -316,31 +317,50 @@ impl SharedSinkContext {
|
||||
metadata.get_duration().await.ok().flatten()
|
||||
};
|
||||
self.pending_track_duration = duration_opt;
|
||||
|
||||
self.pending_total_samples = {
|
||||
let metadata = metadata_lock.read().await;
|
||||
metadata.get_total_samples().await.ok().flatten()
|
||||
};
|
||||
|
||||
// Compute total_samples only when we know the sample rate.
|
||||
if let (Some(duration), Some(sr)) = (self.pending_track_duration, self.sample_rate) {
|
||||
debug!("Encoder metadata: duration: {:3}s - rate: {}Hz", Duration::as_secs_f64(&duration),sr);
|
||||
let samples = (duration.as_secs_f64() * sr as f64).round() as u64;
|
||||
self.encoder_options.total_samples = Some(samples);
|
||||
} else {
|
||||
if self.pending_track_duration.is_none() {
|
||||
debug!("Encoder metadata: duration: None");
|
||||
}
|
||||
if self.sample_rate.is_none() {
|
||||
debug!("Encoder metadata: sample rate: None");
|
||||
}
|
||||
// Avoid leaking the previous track's length.
|
||||
self.encoder_options.total_samples = None;
|
||||
}
|
||||
self.refresh_total_samples_with_sample_rate();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Refresh total_samples when the sample rate is learned after metadata was already set.
|
||||
pub fn refresh_total_samples_with_sample_rate(&mut self) {
|
||||
info!(
|
||||
"Encoder metadata: refresh_total_samples (pending_total_samples={:?}, pending_duration={:?}, sample_rate={:?})",
|
||||
self.pending_total_samples,
|
||||
self.pending_track_duration
|
||||
.as_ref()
|
||||
.map(Duration::as_secs_f64),
|
||||
self.sample_rate
|
||||
);
|
||||
|
||||
if let Some(total) = self.pending_total_samples {
|
||||
self.encoder_options.total_samples = Some(total);
|
||||
info!(
|
||||
"Encoder metadata: using provided total_samples={} from TrackBoundary",
|
||||
total
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if let (Some(duration), Some(sr)) = (self.pending_track_duration, self.sample_rate) {
|
||||
let samples = (duration.as_secs_f64() * sr as f64).round() as u64;
|
||||
self.encoder_options.total_samples = Some(samples);
|
||||
info!(
|
||||
"Encoder metadata: computed total_samples={} (duration {:.3}s @ {} Hz)",
|
||||
samples,
|
||||
duration.as_secs_f64(),
|
||||
sr
|
||||
);
|
||||
} else {
|
||||
// Avoid leaking the previous track's length.
|
||||
self.encoder_options.total_samples = None;
|
||||
info!("Encoder metadata: no duration/total_samples available; clearing total_samples in encoder options");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
use anyhow::Result;
|
||||
use pmocache::CacheConfig;
|
||||
use serde_json::Value;
|
||||
use serde_json::{Number, Value};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Configuration pour le cache audio
|
||||
@@ -144,6 +144,57 @@ pub async fn add_with_metadata_extraction(
|
||||
|
||||
// Extraire les métadonnées
|
||||
let mut metadata = crate::metadata::AudioMetadata::from_bytes(&flac_bytes)?;
|
||||
// Propager les métadonnées techniques dans la DB (sans passer par le JSON)
|
||||
if let Some(d) = metadata.duration_secs {
|
||||
let _ = cache
|
||||
.db
|
||||
.set_a_metadata(&pk, "duration_secs", Value::Number(Number::from(d)));
|
||||
}
|
||||
if let Some((sr, bps, total_samples)) = parse_flac_streaminfo(&flac_bytes) {
|
||||
let _ = cache
|
||||
.db
|
||||
.set_a_metadata(&pk, "sample_rate", Value::Number(Number::from(sr)));
|
||||
let _ = cache
|
||||
.db
|
||||
.set_a_metadata(&pk, "bits_per_sample", Value::Number(Number::from(bps)));
|
||||
let _ = cache.db.set_a_metadata(
|
||||
&pk,
|
||||
"total_samples",
|
||||
Value::Number(Number::from(total_samples)),
|
||||
);
|
||||
if metadata.duration_secs.is_none() && sr > 0 {
|
||||
let secs = (total_samples as f64 / sr as f64).round() as u64;
|
||||
let _ = cache
|
||||
.db
|
||||
.set_a_metadata(&pk, "duration_secs", Value::Number(Number::from(secs)));
|
||||
metadata.duration_secs = Some(secs);
|
||||
}
|
||||
if metadata.sample_rate.is_none() {
|
||||
metadata.sample_rate = Some(sr);
|
||||
}
|
||||
}
|
||||
|
||||
// Extraire aussi les informations FLAC de base (STREAMINFO) pour peupler TrackMetadata
|
||||
if let Some((sr, bps, total_samples)) = parse_flac_streaminfo(&flac_bytes) {
|
||||
if let Err(e) = cache
|
||||
.db
|
||||
.set_a_metadata(&pk, "sample_rate", Value::Number(Number::from(sr)))
|
||||
{
|
||||
tracing::warn!("Failed to persist sample_rate for {}: {}", pk, e);
|
||||
}
|
||||
if let Err(e) = cache
|
||||
.db
|
||||
.set_a_metadata(&pk, "bits_per_sample", Value::Number(Number::from(bps)))
|
||||
{
|
||||
tracing::warn!("Failed to persist bits_per_sample for {}: {}", pk, e);
|
||||
}
|
||||
if let Err(e) = cache
|
||||
.db
|
||||
.set_a_metadata(&pk, "total_samples", Value::Number(Number::from(total_samples)))
|
||||
{
|
||||
tracing::warn!("Failed to persist total_samples for {}: {}", pk, e);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(transform) = cache.transform_metadata(&pk).await {
|
||||
if let Some(mode) = transform.mode {
|
||||
@@ -210,3 +261,40 @@ pub fn get_metadata(cache: &Cache, pk: &str) -> Result<crate::metadata::AudioMet
|
||||
|
||||
Ok(metadata)
|
||||
}
|
||||
|
||||
/// Parse minimal FLAC STREAMINFO (first metadata block) to retrieve sample rate,
|
||||
/// bits per sample, and total samples.
|
||||
fn parse_flac_streaminfo(data: &[u8]) -> Option<(u32, u8, u64)> {
|
||||
// Expect "fLaC" + 4-byte metadata block header + 34-byte STREAMINFO
|
||||
if data.len() < 4 + 4 + 34 {
|
||||
return None;
|
||||
}
|
||||
if &data[0..4] != b"fLaC" {
|
||||
return None;
|
||||
}
|
||||
|
||||
let block_type = data[4] & 0x7F;
|
||||
let block_len = ((data[5] as usize) << 16) | ((data[6] as usize) << 8) | data[7] as usize;
|
||||
if block_type != 0 || block_len < 34 || data.len() < 8 + block_len {
|
||||
return None;
|
||||
}
|
||||
|
||||
let s = &data[8..8 + 34];
|
||||
|
||||
// sample_rate: 20 bits: bytes 10..12
|
||||
let sample_rate =
|
||||
((s[10] as u32) << 12) | ((s[11] as u32) << 4) | ((s[12] as u32 & 0xF0) >> 4);
|
||||
|
||||
// bits_per_sample: 5 bits spanning byte 12 (lsb) and byte 13 (msb)
|
||||
let bps_raw = (((s[12] & 0x01) as u8) << 4) | ((s[13] & 0xF0) >> 4);
|
||||
let bits_per_sample = bps_raw.saturating_add(1);
|
||||
|
||||
// total_samples: 36 bits: lower 4 bits of byte 13 + bytes 14..17
|
||||
let total_samples = (((s[13] & 0x0F) as u64) << 32)
|
||||
| ((s[14] as u64) << 24)
|
||||
| ((s[15] as u64) << 16)
|
||||
| ((s[16] as u64) << 8)
|
||||
| (s[17] as u64);
|
||||
|
||||
Some((sample_rate, bits_per_sample, total_samples))
|
||||
}
|
||||
|
||||
@@ -281,15 +281,6 @@ pub trait TrackMetadata: Send + Sync {
|
||||
async fn touch(&mut self) -> MetadataResult<()> {
|
||||
Err(MetadataError::NotImplemented)
|
||||
}
|
||||
|
||||
async fn set_sample_rate(&mut self, _value: Option<i32>) -> MetadataResult<()> {
|
||||
Err(MetadataError::NotImplemented)
|
||||
}
|
||||
|
||||
async fn get_sample_rate(self) -> MetadataResult<i32> {
|
||||
Err(MetadataError::NotImplemented)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Copies all available metadata from one implementation to another.
|
||||
|
||||
Reference in New Issue
Block a user