summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2024-06-04 11:19:16 +0200
committerGitHub <noreply@github.com>2024-06-04 11:19:16 +0200
commit482a942cd9e62fadee4ee7bc1fd7e8def8a85f71 (patch)
treeb88c27fd594b1c5be9d3ccdd327343bb80d509b3 /src
parent06a5db8748fd67bf852a09ae48b8f1b0e68dc084 (diff)
parent7602f93105510766a5b21878819d6dbc66dd1e01 (diff)
Merge pull request #4 from yuvadm/icy-filterv0.5.0
Use icy-metadata filtering
Diffstat (limited to 'src')
-rw-r--r--src/audio/rodio.rs75
1 files changed, 37 insertions, 38 deletions
diff --git a/src/audio/rodio.rs b/src/audio/rodio.rs
index 3dffa5e..0f9fbb2 100644
--- a/src/audio/rodio.rs
+++ b/src/audio/rodio.rs
@@ -1,49 +1,32 @@
+use icy_metadata::{IcyHeaders, IcyMetadataReader, RequestIcyMetadata};
+use rodio::{Decoder, OutputStream, Sink};
use std::num::NonZeroUsize;
+use stream_download::http::reqwest::Client;
+use stream_download::http::HttpStream;
+use stream_download::storage::bounded::BoundedStorageProvider;
+use stream_download::storage::memory::MemoryStorageProvider;
+use stream_download::{Settings, StreamDownload};
-use rodio::{Decoder, OutputStream, Sink};
-use stream_download::{
- http::{reqwest::Client, HttpStream},
- source::SourceStream,
- storage::{
- bounded::BoundedStorageProvider, memory::MemoryStorageProvider, temp::TempStorageProvider,
- },
- Settings, StreamDownload,
-};
+const AUDIO_BUFFER_SECONDS: u32 = 5;
pub struct Rodio {}
impl Rodio {
pub async fn play(&self, url: &str) {
- let reader = StreamDownload::new_http(
- url.parse().unwrap(),
- TempStorageProvider::new(),
- Settings::default(),
- )
- .await
- .unwrap();
+ let (_stream, handle) = OutputStream::try_default().unwrap();
+ let sink = Sink::try_new(&handle).unwrap();
- let _res = tokio::task::spawn_blocking(move || {
- let (_stream, handle) = OutputStream::try_default().unwrap();
- let sink = Sink::try_new(&handle).unwrap();
- sink.append(Decoder::new(reader).unwrap());
- sink.sleep_until_end();
- })
- .await;
+ // request metadata header
+ let client = Client::builder().request_icy_metadata().build().unwrap();
- let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
- let sink = rodio::Sink::try_new(&handle).unwrap();
- let stream = HttpStream::<Client>::create(url.parse().unwrap())
- .await
- .unwrap();
+ let stream = HttpStream::new(client, url.parse().unwrap()).await.unwrap();
- println!("content type={:?}", stream.content_type());
- let bitrate: u64 = stream.header("Icy-Br").unwrap().parse().unwrap();
- println!("bitrate={bitrate}");
+ let icy_headers = IcyHeaders::parse_from_headers(stream.headers());
+ // println!("Icecast headers: {icy_headers:#?}\n");
+ // println!("content type={:?}\n", stream.content_type());
- // buffer 5 seconds of audio
- // bitrate (in kilobits) / bits per byte * bytes per kilobyte * 5 seconds
- let prefetch_bytes = bitrate / 8 * 1024 * 5;
- println!("prefetch bytes={prefetch_bytes}");
+ // buffer bitrate (in kilobits) / bits per byte * bytes per kilobyte * N seconds
+ let prefetch_bytes = icy_headers.bitrate().unwrap() / 8 * 1024 * AUDIO_BUFFER_SECONDS;
let reader = StreamDownload::from_stream(
stream,
@@ -54,15 +37,31 @@ impl Rodio {
// prevent any out-of-bounds reads
NonZeroUsize::new(512 * 1024).unwrap(),
),
- Settings::default().prefetch_bytes(prefetch_bytes),
+ Settings::default().prefetch_bytes(prefetch_bytes as u64),
)
.await
.unwrap();
- sink.append(rodio::Decoder::new(reader).unwrap());
+
+ sink.append(
+ Decoder::new(IcyMetadataReader::new(
+ reader,
+ // Since we requested icy metadata, the metadata interval header should be present in the
+ // response. This will allow us to parse the metadata within the stream
+ icy_headers.metadata_interval(),
+ |metadata| {
+ if let Ok(md) = metadata {
+ if let Some(tr) = md.stream_title() {
+ println!(" {tr}");
+ }
+ }
+ },
+ ))
+ .unwrap(),
+ );
let handle = tokio::task::spawn_blocking(move || {
sink.sleep_until_end();
});
- handle.await.unwrap()
+ handle.await.unwrap();
}
}