alac/
mp4.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
extern crate mp4parse;

use self::mp4parse::{AudioCodecSpecific, AudioSampleEntry, CodecType, Error, SampleEntry};
use std::io::{ErrorKind, Read, Seek, SeekFrom};

use {invalid_data, Format, ReadError};

fn mp4_error(msg: &'static str) -> ReadError {
    ReadError::Format(Format::Mp4, invalid_data(msg))
}

impl From<Error> for ReadError {
    fn from(err: Error) -> ReadError {
        match err {
            Error::InvalidData(msg) => mp4_error(msg),
            Error::Unsupported(msg) => mp4_error(msg),
            Error::UnexpectedEOF => mp4_error("unexpected end of stream"),
            Error::Io(ref err) if err.kind() == ErrorKind::UnexpectedEof => {
                mp4_error("unexpected end of stream")
            }
            Error::Io(err) => ReadError::Io(err),
            Error::NoMoov => mp4_error("missing moov atom"),
            Error::OutOfMemory => mp4_error("out of memory"),
        }
    }
}

pub struct Mp4PacketReader<R> {
    reader: R,
    chunk_offsets: Vec<u64>,
    sample_sizes: Vec<u32>,
    sample_to_chunk: Vec<SampleToChunk>,
    sample_idx: u32,
}

#[derive(Clone, Copy)]
struct SampleToChunk {
    first_sample: u32,
    first_chunk: u32,
    samples_per_chunk: u32,
}

impl<R: Read + Seek> Mp4PacketReader<R> {
    pub fn new(mut reader: R) -> Result<(Mp4PacketReader<R>, Vec<u8>), ReadError> {
        let mut context = mp4parse::MediaContext::new();
        mp4parse::read_mp4(&mut reader, &mut context)?;

        let track = context
            .tracks
            .into_iter()
            .filter(|track| track.codec_type == CodecType::ALAC)
            .next()
            .ok_or(mp4_error("no alac tracks found"))?;

        let magic_cookie = if let Some(SampleEntry::Audio(AudioSampleEntry {
            codec_specific: AudioCodecSpecific::ALACSpecificBox(alac),
            ..
        })) = track.data
        {
            alac.data
        } else {
            return Err(mp4_error("missing sample entry atom"));
        };

        let chunk_offsets = if let Some(stco) = track.stco {
            stco.offsets
        } else {
            return Err(mp4_error("missing stco (chunk offset) atom"));
        };

        let sample_sizes = if let Some(stsz) = track.stsz {
            stsz.sample_sizes
        } else {
            return Err(mp4_error("missing stsz (sample size) atom"));
        };

        let sample_to_chunk = if let Some(stsc) = track.stsc {
            stsc.samples
        } else {
            return Err(mp4_error("missing stsc (sample to chunk) atom"));
        };

        let sample_to_chunk = sample_to_chunk
            .into_iter()
            .scan((0, 0), |&mut (ref mut samples, ref mut prev_chunk), s| {
                // s.first_chunk is 1 indexed
                let first_chunk = s.first_chunk - 1;
                *samples += (first_chunk - *prev_chunk) * s.samples_per_chunk;
                *prev_chunk = first_chunk;
                Some(SampleToChunk {
                    first_sample: *samples,
                    first_chunk,
                    samples_per_chunk: s.samples_per_chunk,
                })
            })
            .collect();

        Ok((
            Mp4PacketReader {
                reader,
                chunk_offsets,
                sample_sizes,
                sample_to_chunk,
                sample_idx: 0,
            },
            magic_cookie,
        ))
    }

    pub fn next_packet_into(&mut self, buf: &mut Vec<u8>) -> Result<(), ReadError> {
        let sample_idx = self.sample_idx;
        if sample_idx as usize == self.sample_sizes.len() {
            buf.clear();
            return Ok(());
        }

        // Find the current sample to chunk mapping
        let sample_to_chunk_idx = self.sample_to_chunk
            .binary_search_by_key(&self.sample_idx, |s| s.first_sample)
            // If we are past s.first_sample we want the index of s
            .unwrap_or_else(|i| i - 1);
        let sample_to_chunk = self.sample_to_chunk[sample_to_chunk_idx];
        let samples_per_chunk = sample_to_chunk.samples_per_chunk;

        let chunks_past_first_chunk =
            (sample_idx - sample_to_chunk.first_sample) / samples_per_chunk;
        let samples_into_chunk = sample_idx - chunks_past_first_chunk * samples_per_chunk;

        // Seek to next chunk offset if starting a new chunk
        if samples_into_chunk == 0 {
            let chunk_idx = sample_to_chunk.first_chunk + chunks_past_first_chunk;
            let chunk_offset = self.chunk_offsets[chunk_idx as usize];
            self.reader.seek(SeekFrom::Start(chunk_offset))?;
        }

        let packet_len = self.sample_sizes[sample_idx as usize] as usize;
        buf.resize(packet_len, 0);
        self.reader.read_exact(&mut buf[..])?;
        self.sample_idx += 1;
        Ok(())
    }
}