1use std::{
2 cmp::{max, min},
3 fmt::{Debug, Display},
4 fs::File,
5 io::{self, Read, Seek},
6 marker::PhantomData,
7 net::TcpStream,
8 ops::Range,
9 path::Path,
10};
11
12use crate::{
13 buffer::Buffers,
14 error::{ParsedError, ParsingError, ParsingErrorState},
15 exif::{parse_exif_iter, TiffHeader},
16 file::Mime,
17 partial_vec::PartialVec,
18 skip::Skip,
19 video::parse_track_info,
20 ExifIter, Seekable, TrackInfo, Unseekable,
21};
22
23pub struct MediaSource<R, S = Seekable> {
45 pub(crate) reader: R,
46 pub(crate) buf: Vec<u8>,
47 pub(crate) mime: Mime,
48 phantom: PhantomData<S>,
49}
50
51impl<R, S: Skip<R>> Debug for MediaSource<R, S> {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 f.debug_struct("MediaSource")
54 .field("mime", &self.mime)
56 .field("seekable", &S::debug())
57 .finish_non_exhaustive()
58 }
59}
60
61const HEADER_PARSE_BUF_SIZE: usize = 128;
63
64impl<R: Read, S: Skip<R>> MediaSource<R, S> {
65 fn build(mut reader: R) -> crate::Result<Self> {
66 let mut buf = Vec::with_capacity(HEADER_PARSE_BUF_SIZE);
68 reader
69 .by_ref()
70 .take(HEADER_PARSE_BUF_SIZE as u64)
71 .read_to_end(&mut buf)?;
72 let mime: Mime = buf.as_slice().try_into()?;
73 Ok(Self {
74 reader,
75 buf,
76 mime,
77 phantom: PhantomData,
78 })
79 }
80
81 pub fn has_track(&self) -> bool {
82 match self.mime {
83 Mime::Image(_) => false,
84 Mime::Video(_) => true,
85 }
86 }
87
88 pub fn has_exif(&self) -> bool {
89 match self.mime {
90 Mime::Image(_) => true,
91 Mime::Video(_) => false,
92 }
93 }
94}
95
96impl<R: Read + Seek> MediaSource<R, Seekable> {
97 pub fn seekable(reader: R) -> crate::Result<Self> {
98 Self::build(reader)
99 }
100}
101
102impl<R: Read> MediaSource<R, Unseekable> {
103 pub fn unseekable(reader: R) -> crate::Result<Self> {
104 Self::build(reader)
105 }
106}
107
108impl MediaSource<File, Seekable> {
109 pub fn file_path<P: AsRef<Path>>(path: P) -> crate::Result<Self> {
110 Self::seekable(File::open(path)?)
111 }
112
113 pub fn file(file: File) -> crate::Result<Self> {
114 Self::seekable(file)
115 }
116}
117
118impl MediaSource<TcpStream, Unseekable> {
119 pub fn tcp_stream(stream: TcpStream) -> crate::Result<Self> {
120 Self::unseekable(stream)
121 }
122}
123
124pub(crate) const INIT_BUF_SIZE: usize = 4096;
126pub(crate) const MIN_GROW_SIZE: usize = 4096;
127pub(crate) const MAX_GROW_SIZE: usize = 63 * 1024;
129
130pub(crate) trait Buf {
131 fn buffer(&self) -> &[u8];
132 fn clear(&mut self);
133
134 fn set_position(&mut self, pos: usize);
135 #[allow(unused)]
136 fn position(&self) -> usize;
137}
138
139#[derive(Debug, Clone)]
140pub(crate) enum ParsingState {
141 TiffHeader(TiffHeader),
142 HeifExifSize(usize),
143}
144
145impl Display for ParsingState {
146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 match self {
148 ParsingState::TiffHeader(h) => Display::fmt(&format!("ParsingState: {h:?})"), f),
149 ParsingState::HeifExifSize(n) => Display::fmt(&format!("ParsingState: {n}"), f),
150 }
151 }
152}
153
154pub(crate) trait BufParser: Buf + Debug {
155 fn fill_buf<R: Read>(&mut self, reader: &mut R, size: usize) -> io::Result<usize>;
156 fn load_and_parse<R: Read, S: Skip<R>, P, O>(
157 &mut self,
158 reader: &mut R,
159 mut parse: P,
160 ) -> Result<O, ParsedError>
161 where
162 P: FnMut(&[u8], Option<ParsingState>) -> Result<O, ParsingErrorState>,
163 {
164 self.load_and_parse_with_offset::<R, S, _, _>(
165 reader,
166 |data, _, state| parse(data, state),
167 0,
168 )
169 }
170
171 #[tracing::instrument(skip_all)]
172 fn load_and_parse_with_offset<R: Read, S: Skip<R>, P, O>(
173 &mut self,
174 reader: &mut R,
175 mut parse: P,
176 offset: usize,
177 ) -> Result<O, ParsedError>
178 where
179 P: FnMut(&[u8], usize, Option<ParsingState>) -> Result<O, ParsingErrorState>,
180 {
181 if offset >= self.buffer().len() {
182 self.fill_buf(reader, MIN_GROW_SIZE)?;
183 }
184
185 let mut parsing_state: Option<ParsingState> = None;
186 loop {
187 let res = parse(self.buffer(), offset, parsing_state.take());
188 match res {
189 Ok(o) => return Ok(o),
190 Err(es) => {
191 tracing::debug!(?es);
192 parsing_state = es.state;
193
194 match es.err {
195 ParsingError::ClearAndSkip(n) => {
196 self.clear_and_skip::<R, S>(reader, n)?;
197 }
198 ParsingError::Need(i) => {
199 tracing::debug!(need = i, "need more bytes");
200 let to_read = max(i, MIN_GROW_SIZE);
201 let to_read = min(to_read, MAX_GROW_SIZE);
202
203 let n = self.fill_buf(reader, to_read)?;
204 if n == 0 {
205 return Err(ParsedError::NoEnoughBytes);
206 }
207 tracing::debug!(n, "actual read");
208 }
209 ParsingError::Failed(s) => return Err(ParsedError::Failed(s)),
210 }
211 }
212 }
213 }
214 }
215
216 #[tracing::instrument(skip(reader))]
217 fn clear_and_skip<R: Read, S: Skip<R>>(
218 &mut self,
219 reader: &mut R,
220 n: usize,
221 ) -> Result<(), ParsedError> {
222 tracing::debug!("ClearAndSkip");
223 if n <= self.buffer().len() {
224 tracing::debug!(n, "skip by set_position");
225 self.set_position(n);
226 return Ok(());
227 }
228
229 let skip_n = n - self.buffer().len();
230 tracing::debug!(skip_n, "clear and skip bytes");
231 self.clear();
232
233 let done = S::skip_by_seek(reader, skip_n.try_into().unwrap())?;
234 if !done {
235 tracing::debug!(skip_n, "skip by using our buffer");
236 let mut skipped = 0;
237 while skipped < skip_n {
238 let n = self.fill_buf(reader, skip_n - skipped)?;
239 skipped += n;
240 if skipped <= skip_n {
241 self.clear();
242 } else {
243 let remain = skipped - skip_n;
244 self.set_position(self.buffer().len() - remain);
245 break;
246 }
247 }
248 } else {
249 tracing::debug!(skip_n, "skip with seek");
250 }
251
252 if self.buffer().is_empty() {
253 self.fill_buf(reader, MIN_GROW_SIZE)?;
254 }
255 Ok(())
256 }
257}
258
259impl BufParser for MediaParser {
260 fn fill_buf<R: Read>(&mut self, reader: &mut R, size: usize) -> io::Result<usize> {
261 self.buf_mut().reserve_exact(size);
262
263 let n = reader.take(size as u64).read_to_end(self.buf_mut())?;
264 if n == 0 {
265 return Err(std::io::ErrorKind::UnexpectedEof.into());
266 }
267
268 Ok(n)
269 }
270}
271
272impl Buf for MediaParser {
273 fn buffer(&self) -> &[u8] {
274 &self.buf()[self.position..]
275 }
276
277 fn clear(&mut self) {
278 self.buf_mut().clear();
279 }
280
281 fn set_position(&mut self, pos: usize) {
282 self.position = pos;
283 }
284
285 fn position(&self) -> usize {
286 self.position
287 }
288}
289
290pub trait ParseOutput<R, S>: Sized {
291 fn parse(parser: &mut MediaParser, ms: MediaSource<R, S>) -> crate::Result<Self>;
292}
293
294impl<R: Read, S: Skip<R>> ParseOutput<R, S> for ExifIter {
295 fn parse(parser: &mut MediaParser, mut ms: MediaSource<R, S>) -> crate::Result<Self> {
296 if !ms.has_exif() {
297 return Err(crate::Error::ParseFailed("no Exif data here".into()));
298 }
299 parse_exif_iter::<R, S>(parser, ms.mime.unwrap_image(), &mut ms.reader)
300 }
301}
302
303impl<R: Read, S: Skip<R>> ParseOutput<R, S> for TrackInfo {
304 fn parse(parser: &mut MediaParser, mut ms: MediaSource<R, S>) -> crate::Result<Self> {
305 if !ms.has_track() {
306 return Err(crate::Error::ParseFailed("no track info here".into()));
307 }
308 let out = parser.load_and_parse::<R, S, _, _>(ms.reader.by_ref(), |data, _| {
309 parse_track_info(data, ms.mime.unwrap_video())
310 .map_err(|e| ParsingErrorState::new(e, None))
311 })?;
312 Ok(out)
313 }
314}
315
316pub struct MediaParser {
367 bb: Buffers,
368 buf: Option<Vec<u8>>,
369 position: usize,
370}
371
372impl Debug for MediaParser {
373 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
374 f.debug_struct("MediaParser")
375 .field("buffers", &self.bb)
376 .field("buf len", &self.buf.as_ref().map(|x| x.len()))
377 .field("position", &self.position)
378 .finish_non_exhaustive()
379 }
380}
381
382impl Default for MediaParser {
383 fn default() -> Self {
384 Self {
385 bb: Buffers::new(),
386 buf: None,
387 position: 0,
388 }
389 }
390}
391
392pub(crate) trait ShareBuf {
393 fn share_buf(&mut self, range: Range<usize>) -> PartialVec;
394}
395
396impl ShareBuf for MediaParser {
397 fn share_buf(&mut self, mut range: Range<usize>) -> PartialVec {
398 let buf = self.buf.take().unwrap();
399 let vec = self.bb.release_to_share(buf);
400 range.start += self.position;
401 range.end += self.position;
402 PartialVec::new(vec, range)
403 }
404}
405
406impl MediaParser {
407 pub fn new() -> Self {
408 Self::default()
409 }
410
411 pub fn parse<R: Read, S, O: ParseOutput<R, S>>(
431 &mut self,
432 mut ms: MediaSource<R, S>,
433 ) -> crate::Result<O> {
434 self.reset();
435 self.acquire_buf();
436
437 self.buf_mut().append(&mut ms.buf);
438 let res = self.do_parse(ms);
439
440 self.reset();
441 res
442 }
443
444 fn do_parse<R: Read, S, O: ParseOutput<R, S>>(
445 &mut self,
446 mut ms: MediaSource<R, S>,
447 ) -> Result<O, crate::Error> {
448 self.fill_buf(&mut ms.reader, INIT_BUF_SIZE)?;
449 let res = ParseOutput::parse(self, ms)?;
450 Ok(res)
451 }
452
453 fn reset(&mut self) {
454 if let Some(buf) = self.buf.take() {
456 self.bb.release(buf);
457 }
458
459 self.set_position(0);
461 }
462
463 pub(crate) fn buf(&self) -> &Vec<u8> {
464 match self.buf.as_ref() {
465 Some(b) => b,
466 None => panic!("no buf here"),
467 }
468 }
469
470 fn buf_mut(&mut self) -> &mut Vec<u8> {
471 match self.buf.as_mut() {
472 Some(b) => b,
473 None => panic!("no buf here"),
474 }
475 }
476
477 fn acquire_buf(&mut self) {
478 assert!(self.buf.is_none());
479 self.buf = Some(self.bb.acquire());
480 }
481}
482
483#[cfg(test)]
484mod tests {
485 use std::sync::{LazyLock, Mutex, MutexGuard};
486
487 use super::*;
488 use test_case::case;
489
490 enum TrackExif {
491 Track,
492 Exif,
493 NoData,
494 Invalid,
495 }
496 use TrackExif::*;
497
498 static PARSER: LazyLock<Mutex<MediaParser>> = LazyLock::new(|| Mutex::new(MediaParser::new()));
499 fn parser() -> MutexGuard<'static, MediaParser> {
500 PARSER.lock().unwrap()
501 }
502
503 #[case("3gp_640x360.3gp", Track)]
504 #[case("broken.jpg", Exif)]
505 #[case("compatible-brands-fail.heic", Invalid)]
506 #[case("compatible-brands-fail.mov", Invalid)]
507 #[case("compatible-brands.heic", NoData)]
508 #[case("compatible-brands.mov", NoData)]
509 #[case("embedded-in-heic.mov", Track)]
510 #[case("exif.heic", Exif)]
511 #[case("exif.jpg", Exif)]
512 #[case("meta.mov", Track)]
513 #[case("meta.mp4", Track)]
514 #[case("mka.mka", Track)]
515 #[case("mkv_640x360.mkv", Track)]
516 #[case("exif-one-entry.heic", Exif)]
517 #[case("no-exif.jpg", NoData)]
518 #[case("tif.tif", Exif)]
519 #[case("ramdisk.img", Invalid)]
520 #[case("webm_480.webm", Track)]
521 fn parse_media(path: &str, te: TrackExif) {
522 let mut parser = parser();
523 let ms = MediaSource::file_path(Path::new("testdata").join(path));
524 match te {
525 Track => {
526 let ms = ms.unwrap();
527 assert!(ms.has_track());
529 let _: TrackInfo = parser.parse(ms).unwrap();
530 }
531 Exif => {
532 let ms = ms.unwrap();
533 assert!(ms.has_exif());
535 let mut it: ExifIter = parser.parse(ms).unwrap();
536 let _ = it.parse_gps_info();
537
538 if path.contains("one-entry") {
539 assert!(it.next().is_some());
540 assert!(it.next().is_none());
541
542 let exif: crate::Exif = it.clone_and_rewind().into();
543 assert!(exif.get(ExifTag::Orientation).is_some());
544 } else {
545 let _: crate::Exif = it.clone_and_rewind().into();
546 }
547 }
548 NoData => {
549 let ms = ms.unwrap();
550 if ms.has_exif() {
552 let res: Result<ExifIter, _> = parser.parse(ms);
553 res.unwrap_err();
554 } else if ms.has_track() {
555 let res: Result<TrackInfo, _> = parser.parse(ms);
556 res.unwrap_err();
557 }
558 }
559 Invalid => {
560 ms.unwrap_err();
561 }
562 }
563 }
564
565 use crate::testkit::open_sample;
566 use crate::{EntryValue, ExifTag, TrackInfoTag};
567 use chrono::DateTime;
568 use test_case::test_case;
569
570 use crate::video::TrackInfoTag::*;
571
572 #[test_case("mkv_640x360.mkv", ImageWidth, 640_u32.into())]
573 #[test_case("mkv_640x360.mkv", ImageHeight, 360_u32.into())]
574 #[test_case("mkv_640x360.mkv", DurationMs, 13346_u64.into())]
575 #[test_case("mkv_640x360.mkv", CreateDate, DateTime::parse_from_str("2008-08-08T08:08:08Z", "%+").unwrap().into())]
576 #[test_case("meta.mov", Make, "Apple".into())]
577 #[test_case("meta.mov", Model, "iPhone X".into())]
578 #[test_case("meta.mov", GpsIso6709, "+27.1281+100.2508+000.000/".into())]
579 #[test_case("meta.mp4", ImageWidth, 1920_u32.into())]
580 #[test_case("meta.mp4", ImageHeight, 1080_u32.into())]
581 #[test_case("meta.mp4", DurationMs, 1063_u64.into())]
582 #[test_case("meta.mp4", GpsIso6709, "+27.2939+112.6932/".into())]
583 #[test_case("meta.mp4", CreateDate, DateTime::parse_from_str("2024-02-03T07:05:38Z", "%+").unwrap().into())]
584 fn parse_track_info(path: &str, tag: TrackInfoTag, v: EntryValue) {
585 let mut parser = parser();
586
587 let mf = MediaSource::file(open_sample(path).unwrap()).unwrap();
588 let info: TrackInfo = parser.parse(mf).unwrap();
589 assert_eq!(info.get(tag).unwrap(), &v);
590
591 let mf = MediaSource::unseekable(open_sample(path).unwrap()).unwrap();
592 let info: TrackInfo = parser.parse(mf).unwrap();
593 assert_eq!(info.get(tag).unwrap(), &v);
594 }
595}