use revelo_core::stream::{StreamCollection, StreamKind};
#[cfg(feature = "mmap")]
use revelo_core::{MediaReadAt, MmapBackend, ReadBackend};
use revelo_dispatcher::detect;
use revelo_parsers_tag::parse_tags;
pub use revelo_core;
pub use revelo_dispatcher;
pub use revelo_parsers_tag;
pub type MediaFile<'a> = revelo_core::FileAnalyze<'a>;
pub struct Metadata {
streams: StreamCollection,
}
impl Metadata {
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
let parser = detect(bytes)?;
let mut fa = MediaFile::new(bytes);
parser(&mut fa);
parse_tags(&mut fa);
Some(Metadata { streams: fa.streams().clone() })
}
pub fn from_file(path: &str) -> Option<Self> {
#[cfg(feature = "mmap")]
{
let file = std::fs::File::open(path).ok()?;
match unsafe { memmap2::Mmap::map(&file) } {
Ok(mmap) => {
let mmap_backend = MmapBackend::new(&mmap);
let parser = detect(mmap_backend.as_contiguous()?)?;
let backend = ReadBackend::from(mmap_backend);
let mut fa = MediaFile::from_backend(backend);
parser(&mut fa);
parse_tags(&mut fa);
return Some(Metadata { streams: fa.streams().clone() });
}
Err(_) => {}
}
}
Self::from_file_owned(path)
}
pub fn from_file_owned(path: &str) -> Option<Self> {
let bytes = std::fs::read(path).ok()?;
Self::from_bytes(&bytes)
}
pub fn streams(&self) -> &StreamCollection {
&self.streams
}
fn stream_iter(&self, kind: StreamKind, pos: usize) -> impl Iterator<Item = (&str, &str)> {
self.streams
.stream(kind, pos)
.into_iter()
.flat_map(|s| s.iter().map(|(k, v)| (k.as_ref(), v.as_ref())))
}
pub fn into_streams(self) -> StreamCollection {
self.streams
}
pub fn general(&self) -> impl Iterator<Item = (&str, &str)> {
self.stream_iter(StreamKind::General, 0)
}
pub fn video(&self) -> impl Iterator<Item = (&str, &str)> {
self.stream_iter(StreamKind::Video, 0)
}
pub fn audio(&self) -> impl Iterator<Item = (&str, &str)> {
self.stream_iter(StreamKind::Audio, 0)
}
pub fn text(&self) -> impl Iterator<Item = (&str, &str)> {
self.stream_iter(StreamKind::Text, 0)
}
pub fn image(&self) -> impl Iterator<Item = (&str, &str)> {
self.stream_iter(StreamKind::Image, 0)
}
pub fn exif(&self) -> impl Iterator<Item = (&str, &str)> {
self.stream_iter(StreamKind::Exif, 0)
}
pub fn iptc(&self) -> impl Iterator<Item = (&str, &str)> {
self.stream_iter(StreamKind::Iptc, 0)
}
pub fn xmp(&self) -> impl Iterator<Item = (&str, &str)> {
self.stream_iter(StreamKind::Xmp, 0)
}
}
#[cfg(all(test, feature = "mmap"))]
mod tests {
use super::*;
use revelo_core::{ByteRange, MediaReadAt, MmapBackend};
use std::io::Write;
#[test]
fn mmap_backend_windows_match_file_bytes() {
let mut file = tempfile::NamedTempFile::new().unwrap();
file.write_all(&[40, 41, 42, 43, 44, 45]).unwrap();
file.as_file().sync_all().unwrap();
let mmap = unsafe { memmap2::Mmap::map(file.as_file()).unwrap() };
let source = MmapBackend::new(&mmap);
assert_eq!(source.len_u64(), 6);
assert_eq!(source.window_at(ByteRange::new(2, 3).unwrap()).unwrap(), &[42, 43, 44]);
assert_eq!(source.window_at_partial(ByteRange::new(4, 8).unwrap()).unwrap(), &[44, 45]);
}
}