/src/mozilla-central/dom/media/flac/FlacDemuxer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "FlacDemuxer.h" |
8 | | |
9 | | #include "mozilla/Maybe.h" |
10 | | #include "BitReader.h" |
11 | | #include "prenv.h" |
12 | | #include "FlacFrameParser.h" |
13 | | #include "VideoUtils.h" |
14 | | #include "TimeUnits.h" |
15 | | |
16 | | extern mozilla::LazyLogModule gMediaDemuxerLog; |
17 | | #define LOG(msg, ...) \ |
18 | 0 | DDMOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, msg, ##__VA_ARGS__) |
19 | | #define LOGV(msg, ...) \ |
20 | 0 | DDMOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, msg, ##__VA_ARGS__) |
21 | | |
22 | | using namespace mozilla::media; |
23 | | |
24 | | namespace mozilla { |
25 | | namespace flac { |
26 | | |
27 | | // flac::FrameHeader - Holds the flac frame header and its parsing |
28 | | // state. |
29 | | |
30 | | class FrameHeader |
31 | | { |
32 | | public: |
33 | 0 | const AudioInfo& Info() const { return mInfo; } |
34 | | |
35 | 0 | uint32_t Size() const { return mSize; } |
36 | | |
37 | 0 | bool IsValid() const { return mValid; } |
38 | | |
39 | | // Return the index (in samples) from the beginning of the track. |
40 | 0 | int64_t Index() const { return mIndex; } |
41 | | |
42 | | // Parse the current packet and check that it made a valid flac frame header. |
43 | | // From https://xiph.org/flac/format.html#frame_header |
44 | | // A valid header is one that can be decoded without error and that has a |
45 | | // valid CRC. |
46 | | bool Parse(const uint8_t* aPacket, size_t aBytes) |
47 | 0 | { |
48 | 0 | BitReader br(aPacket, aBytes * 8); |
49 | 0 |
|
50 | 0 | // Frame sync code. |
51 | 0 | if ((br.ReadBits(15) & 0x7fff) != 0x7ffc) { |
52 | 0 | return false; |
53 | 0 | } |
54 | 0 | |
55 | 0 | // Variable block size stream code. |
56 | 0 | mVariableBlockSize = br.ReadBit(); |
57 | 0 |
|
58 | 0 | // Block size and sample rate codes. |
59 | 0 | int bs_code = br.ReadBits(4); |
60 | 0 | int sr_code = br.ReadBits(4); |
61 | 0 |
|
62 | 0 | // Channels and decorrelation. |
63 | 0 | int ch_mode = br.ReadBits(4); |
64 | 0 | if (ch_mode < FLAC_MAX_CHANNELS) { |
65 | 0 | mInfo.mChannels = ch_mode + 1; |
66 | 0 | } else if (ch_mode < FLAC_MAX_CHANNELS + FLAC_CHMODE_MID_SIDE) { |
67 | 0 | // This is a special flac channels, we can't handle those yet. Treat it |
68 | 0 | // as stereo. |
69 | 0 | mInfo.mChannels = 2; |
70 | 0 | } else { |
71 | 0 | // invalid channel mode |
72 | 0 | return false; |
73 | 0 | } |
74 | 0 | |
75 | 0 | // Bits per sample. |
76 | 0 | int bps_code = br.ReadBits(3); |
77 | 0 | if (bps_code == 3 || bps_code == 7) { |
78 | 0 | // Invalid sample size code. |
79 | 0 | return false; |
80 | 0 | } |
81 | 0 | mInfo.mBitDepth = FlacSampleSizeTable[bps_code]; |
82 | 0 |
|
83 | 0 | // Reserved bit, must be 0. |
84 | 0 | if (br.ReadBit()) { |
85 | 0 | // Broken stream, invalid padding. |
86 | 0 | return false; |
87 | 0 | } |
88 | 0 | |
89 | 0 | // Sample or frame count. |
90 | 0 | int64_t frame_or_sample_num = br.ReadUTF8(); |
91 | 0 | if (frame_or_sample_num < 0) { |
92 | 0 | // Sample/frame number invalid. |
93 | 0 | return false; |
94 | 0 | } |
95 | 0 | |
96 | 0 | // Blocksize |
97 | 0 | if (bs_code == 0) { |
98 | 0 | // reserved blocksize code |
99 | 0 | return false; |
100 | 0 | } else if (bs_code == 6) { |
101 | 0 | mBlocksize = br.ReadBits(8) + 1; |
102 | 0 | } else if (bs_code == 7) { |
103 | 0 | mBlocksize = br.ReadBits(16) + 1; |
104 | 0 | } else { |
105 | 0 | mBlocksize = FlacBlocksizeTable[bs_code]; |
106 | 0 | } |
107 | 0 |
|
108 | 0 | // The sample index is either: |
109 | 0 | // 1- coded sample number if blocksize is variable or |
110 | 0 | // 2- coded frame number if blocksize is known. |
111 | 0 | // A frame is made of Blocksize sample. |
112 | 0 | mIndex = mVariableBlockSize ? frame_or_sample_num |
113 | 0 | : frame_or_sample_num * mBlocksize; |
114 | 0 |
|
115 | 0 | // Sample rate. |
116 | 0 | if (sr_code < 12) { |
117 | 0 | mInfo.mRate = FlacSampleRateTable[sr_code]; |
118 | 0 | } else if (sr_code == 12) { |
119 | 0 | mInfo.mRate = br.ReadBits(8) * 1000; |
120 | 0 | } else if (sr_code == 13) { |
121 | 0 | mInfo.mRate = br.ReadBits(16); |
122 | 0 | } else if (sr_code == 14) { |
123 | 0 | mInfo.mRate = br.ReadBits(16) * 10; |
124 | 0 | } else { |
125 | 0 | // Illegal sample rate code. |
126 | 0 | return false; |
127 | 0 | } |
128 | 0 | |
129 | 0 | // Header CRC-8 check. |
130 | 0 | uint8_t crc = 0; |
131 | 0 | for (uint32_t i = 0; i < br.BitCount() / 8; i++) { |
132 | 0 | crc = CRC8Table[crc ^ aPacket[i]]; |
133 | 0 | } |
134 | 0 | mValid = crc == br.ReadBits(8); |
135 | 0 | mSize = br.BitCount() / 8; |
136 | 0 |
|
137 | 0 | if (mValid) { |
138 | 0 | // Set the mimetype to make it a valid AudioInfo. |
139 | 0 | mInfo.mMimeType = "audio/flac"; |
140 | 0 | } |
141 | 0 |
|
142 | 0 | return mValid; |
143 | 0 | } |
144 | | |
145 | | private: |
146 | | friend class Frame; |
147 | | enum |
148 | | { |
149 | | FLAC_CHMODE_INDEPENDENT = 0, |
150 | | FLAC_CHMODE_LEFT_SIDE, |
151 | | FLAC_CHMODE_RIGHT_SIDE, |
152 | | FLAC_CHMODE_MID_SIDE, |
153 | | }; |
154 | | AudioInfo mInfo; |
155 | | // Index in samples from start; |
156 | | int64_t mIndex = 0; |
157 | | bool mVariableBlockSize = false; |
158 | | uint32_t mBlocksize = 0;; |
159 | | uint32_t mSize = 0; |
160 | | bool mValid = false; |
161 | | |
162 | | static const int FlacSampleRateTable[16]; |
163 | | static const int32_t FlacBlocksizeTable[16]; |
164 | | static const uint8_t FlacSampleSizeTable[8]; |
165 | | static const uint8_t CRC8Table[256]; |
166 | | }; |
167 | | |
168 | | const int FrameHeader::FlacSampleRateTable[16] = |
169 | | { |
170 | | 0, |
171 | | 88200, 176400, 192000, |
172 | | 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000, |
173 | | 0, 0, 0, 0 |
174 | | }; |
175 | | |
176 | | const int32_t FrameHeader::FlacBlocksizeTable[16] = |
177 | | { |
178 | | 0 , 192 , 576<<0, 576<<1, 576<<2, 576<<3, 0, 0, |
179 | | 256<<0, 256<<1, 256<<2, 256<<3, 256<<4, 256<<5, 256<<6, 256<<7 |
180 | | }; |
181 | | |
182 | | const uint8_t FrameHeader::FlacSampleSizeTable[8] = { 0, 8, 12, 0, 16, 20, 24, 0 }; |
183 | | |
184 | | const uint8_t FrameHeader::CRC8Table[256] = |
185 | | { |
186 | | 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, |
187 | | 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, |
188 | | 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, |
189 | | 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, |
190 | | 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, |
191 | | 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, |
192 | | 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, |
193 | | 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, |
194 | | 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, |
195 | | 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, |
196 | | 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, |
197 | | 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, |
198 | | 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, |
199 | | 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, |
200 | | 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, |
201 | | 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, |
202 | | 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, |
203 | | 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, |
204 | | 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, |
205 | | 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, |
206 | | 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, |
207 | | 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, |
208 | | 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, |
209 | | 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, |
210 | | 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, |
211 | | 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, |
212 | | 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, |
213 | | 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, |
214 | | 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, |
215 | | 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, |
216 | | 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, |
217 | | 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 |
218 | | }; |
219 | | |
220 | | // flac::Frame - Frame meta container used to parse and hold a frame |
221 | | // header and side info. |
222 | | class Frame |
223 | | { |
224 | | public: |
225 | | |
226 | | // The FLAC signature is made of 14 bits set to 1; however the 15th bit is |
227 | | // mandatorily set to 0, so we need to find either of 0xfffc or 0xfffd 2-bytes |
228 | | // signature. We first use a bitmask to see if 0xfc or 0xfd is present. And if |
229 | | // so we check for the whole signature. |
230 | | int64_t FindNext(const uint8_t* aData, const uint32_t aLength) |
231 | 0 | { |
232 | 0 | // The non-variable size of a FLAC header is 32 bits followed by variable |
233 | 0 | // size data and a 8 bits CRC. |
234 | 0 | // There's no need to read the last 4 bytes, it can never make a complete |
235 | 0 | // header. |
236 | 0 | if (aLength < 4) { |
237 | 0 | return -1; |
238 | 0 | } |
239 | 0 | uint32_t modOffset = aLength % 4; |
240 | 0 | uint32_t i, j; |
241 | 0 |
|
242 | 0 | for (i = 0; i < modOffset; i++) { |
243 | 0 | if ((BigEndian::readUint16(aData + i) & 0xfffe) == 0xfff8) { |
244 | 0 | if (mHeader.Parse(aData + i, aLength - i)) { |
245 | 0 | return i; |
246 | 0 | } |
247 | 0 | } |
248 | 0 | } |
249 | 0 |
|
250 | 0 | for (; i < aLength - 4; i += 4) { |
251 | 0 | uint32_t x = BigEndian::readUint32(aData + i); |
252 | 0 | if (((x & ~(x + 0x01010101)) & 0x80808080)) { |
253 | 0 | for (j = 0; j < 4; j++) { |
254 | 0 | if ((BigEndian::readUint16(aData + i + j) & 0xfffe) == 0xfff8) { |
255 | 0 | if (mHeader.Parse(aData + i + j, aLength - i - j)) { |
256 | 0 | return i + j; |
257 | 0 | } |
258 | 0 | } |
259 | 0 | } |
260 | 0 | } |
261 | 0 | } |
262 | 0 | return -1; |
263 | 0 | } |
264 | | |
265 | | // Find the next frame start in the current resource. |
266 | | // On exit return true, offset is set and resource points to the frame found. |
267 | | bool FindNext(MediaResourceIndex& aResource) |
268 | 0 | { |
269 | 0 | static const int BUFFER_SIZE = 4096; |
270 | 0 |
|
271 | 0 | Reset(); |
272 | 0 |
|
273 | 0 | nsTArray<char> buffer; |
274 | 0 | int64_t originalOffset = aResource.Tell(); |
275 | 0 | int64_t offset = originalOffset; |
276 | 0 | uint32_t innerOffset = 0; |
277 | 0 |
|
278 | 0 | do { |
279 | 0 | uint32_t read = 0; |
280 | 0 | buffer.SetLength(BUFFER_SIZE + innerOffset); |
281 | 0 | nsresult rv = |
282 | 0 | aResource.Read(buffer.Elements() + innerOffset, BUFFER_SIZE, &read); |
283 | 0 | if (NS_FAILED(rv)) { |
284 | 0 | return false; |
285 | 0 | } |
286 | 0 | |
287 | 0 | const size_t bufSize = read + innerOffset; |
288 | 0 | int64_t foundOffset = |
289 | 0 | FindNext(reinterpret_cast<uint8_t*>(buffer.Elements()), bufSize); |
290 | 0 |
|
291 | 0 | if (foundOffset >= 0) { |
292 | 0 | SetOffset(aResource, foundOffset + offset); |
293 | 0 | return true; |
294 | 0 | } |
295 | 0 | |
296 | 0 | if (read < BUFFER_SIZE) { |
297 | 0 | // Nothing more to try on as we had reached EOS during the previous |
298 | 0 | // read. |
299 | 0 | mEOS = true; |
300 | 0 | return false; |
301 | 0 | } |
302 | 0 | |
303 | 0 | // Scan the next block; |
304 | 0 | // We rewind a bit to re-try what could have been an incomplete packet. |
305 | 0 | // The maximum size of a FLAC header being FLAC_MAX_FRAME_HEADER_SIZE so |
306 | 0 | // we need to retry just after that amount. |
307 | 0 | offset += bufSize - (FLAC_MAX_FRAME_HEADER_SIZE + 1); |
308 | 0 | buffer.RemoveElementsAt(0, bufSize - (FLAC_MAX_FRAME_HEADER_SIZE + 1)); |
309 | 0 | innerOffset = buffer.Length(); |
310 | 0 | } while (offset - originalOffset < FLAC_MAX_FRAME_SIZE); |
311 | 0 |
|
312 | 0 | return false; |
313 | 0 | } |
314 | | |
315 | 0 | int64_t Offset() const { return mOffset; } |
316 | | |
317 | 0 | const AudioInfo& Info() const { return Header().Info(); } |
318 | | |
319 | 0 | void SetEndOffset(int64_t aOffset) { mSize = aOffset - mOffset; } |
320 | | |
321 | | void SetEndTime(int64_t aIndex) |
322 | 0 | { |
323 | 0 | if (aIndex > Header().mIndex) { |
324 | 0 | mDuration = aIndex - Header().mIndex; |
325 | 0 | } |
326 | 0 | } |
327 | | |
328 | 0 | uint32_t Size() const { return mSize; } |
329 | | |
330 | | TimeUnit Time() const |
331 | 0 | { |
332 | 0 | if (!IsValid()) { |
333 | 0 | return TimeUnit::Invalid(); |
334 | 0 | } |
335 | 0 | MOZ_ASSERT(Header().Info().mRate, "Invalid Frame. Need Header"); |
336 | 0 | return FramesToTimeUnit(Header().mIndex, Header().Info().mRate); |
337 | 0 | } |
338 | | |
339 | | TimeUnit Duration() const |
340 | 0 | { |
341 | 0 | if (!IsValid()) { |
342 | 0 | return TimeUnit(); |
343 | 0 | } |
344 | 0 | MOZ_ASSERT(Header().Info().mRate, "Invalid Frame. Need Header"); |
345 | 0 | return FramesToTimeUnit(mDuration, Header().Info().mRate); |
346 | 0 | } |
347 | | |
348 | | // Returns the parsed frame header. |
349 | 0 | const FrameHeader& Header() const { return mHeader; } |
350 | | |
351 | 0 | bool IsValid() const { return mHeader.IsValid(); } |
352 | | |
353 | 0 | bool EOS() const { return mEOS; } |
354 | | |
355 | 0 | void SetRate(uint32_t aRate) { mHeader.mInfo.mRate = aRate; }; |
356 | | |
357 | 0 | void SetBitDepth(uint32_t aBitDepth) { mHeader.mInfo.mBitDepth = aBitDepth; } |
358 | | |
359 | 0 | void SetInvalid() { mHeader.mValid = false; } |
360 | | |
361 | | // Resets the frame header and data. |
362 | 0 | void Reset() { *this = Frame(); } |
363 | | |
364 | | private: |
365 | | void SetOffset(MediaResourceIndex& aResource, int64_t aOffset) |
366 | 0 | { |
367 | 0 | mOffset = aOffset; |
368 | 0 | aResource.Seek(SEEK_SET, mOffset); |
369 | 0 | } |
370 | | |
371 | | // The offset to the start of the header. |
372 | | int64_t mOffset = 0; |
373 | | uint32_t mSize = 0; |
374 | | uint32_t mDuration = 0; |
375 | | bool mEOS = false; |
376 | | |
377 | | // The currently parsed frame header. |
378 | | FrameHeader mHeader; |
379 | | |
380 | | }; |
381 | | |
382 | | class FrameParser |
383 | | { |
384 | | public: |
385 | | |
386 | | // Returns the currently parsed frame. Reset via EndFrameSession. |
387 | 0 | const Frame& CurrentFrame() const { return mFrame; } |
388 | | |
389 | | // Returns the first parsed frame. |
390 | 0 | const Frame& FirstFrame() const { return mFirstFrame; } |
391 | | |
392 | | // Clear the last parsed frame to allow for next frame parsing |
393 | | void EndFrameSession() |
394 | 0 | { |
395 | 0 | mNextFrame.Reset(); |
396 | 0 | mFrame.Reset(); |
397 | 0 | } |
398 | | |
399 | | // Attempt to find the next frame. |
400 | | bool FindNextFrame(MediaResourceIndex& aResource) |
401 | 0 | { |
402 | 0 | mFrame = mNextFrame; |
403 | 0 | if (GetNextFrame(aResource)) { |
404 | 0 | if (!mFrame.IsValid()) { |
405 | 0 | mFrame = mNextFrame; |
406 | 0 | // We need two frames to be able to start playing (or have reached EOS). |
407 | 0 | GetNextFrame(aResource); |
408 | 0 | } |
409 | 0 | } |
410 | 0 |
|
411 | 0 | if (mFrame.IsValid()) { |
412 | 0 | if (mNextFrame.EOS()) { |
413 | 0 | mFrame.SetEndOffset(aResource.Tell()); |
414 | 0 | } else if (mNextFrame.IsValid()) { |
415 | 0 | mFrame.SetEndOffset(mNextFrame.Offset()); |
416 | 0 | mFrame.SetEndTime(mNextFrame.Header().Index()); |
417 | 0 | } |
418 | 0 | } |
419 | 0 |
|
420 | 0 | if (!mFirstFrame.IsValid()) { |
421 | 0 | mFirstFrame = mFrame; |
422 | 0 | } |
423 | 0 | return mFrame.IsValid(); |
424 | 0 | } |
425 | | |
426 | | // Convenience methods to external FlacFrameParser ones. |
427 | | bool IsHeaderBlock(const uint8_t* aPacket, size_t aLength) const |
428 | 0 | { |
429 | 0 | auto res = mParser.IsHeaderBlock(aPacket, aLength); |
430 | 0 | return res.isOk() ? res.unwrap() : false; |
431 | 0 | } |
432 | | |
433 | | uint32_t HeaderBlockLength(const uint8_t* aPacket) const |
434 | 0 | { |
435 | 0 | return mParser.HeaderBlockLength(aPacket); |
436 | 0 | } |
437 | | |
438 | | bool DecodeHeaderBlock(const uint8_t* aPacket, size_t aLength) |
439 | 0 | { |
440 | 0 | return mParser.DecodeHeaderBlock(aPacket, aLength).isOk(); |
441 | 0 | } |
442 | | |
443 | 0 | bool HasFullMetadata() const { return mParser.HasFullMetadata(); } |
444 | | |
445 | 0 | AudioInfo Info() const { return mParser.mInfo; } |
446 | | |
447 | | // Return a hash table with tag metadata. |
448 | 0 | MetadataTags* GetTags() const { return mParser.GetTags(); } |
449 | | |
450 | | private: |
451 | | bool GetNextFrame(MediaResourceIndex& aResource) |
452 | 0 | { |
453 | 0 | while (mNextFrame.FindNext(aResource)) { |
454 | 0 | // Move our offset slightly, so that we don't find the same frame at the |
455 | 0 | // next FindNext call. |
456 | 0 | aResource.Seek(SEEK_CUR, mNextFrame.Header().Size()); |
457 | 0 | if (mFrame.IsValid() && |
458 | 0 | mNextFrame.Offset() - mFrame.Offset() < FLAC_MAX_FRAME_SIZE && |
459 | 0 | !CheckCRC16AtOffset( |
460 | 0 | mFrame.Offset(), mNextFrame.Offset(), aResource)) { |
461 | 0 | // The frame doesn't match its CRC or would be too far, skip it.. |
462 | 0 | continue; |
463 | 0 | } |
464 | 0 | CheckFrameData(); |
465 | 0 | break; |
466 | 0 | } |
467 | 0 | return mNextFrame.IsValid(); |
468 | 0 | } |
469 | | |
470 | | bool CheckFrameData() |
471 | 0 | { |
472 | 0 | if (mNextFrame.Header().Info().mRate == 0 || |
473 | 0 | mNextFrame.Header().Info().mBitDepth == 0) { |
474 | 0 | if (!Info().IsValid()) { |
475 | 0 | // We can only use the STREAMINFO data if we have one. |
476 | 0 | mNextFrame.SetInvalid(); |
477 | 0 | } else { |
478 | 0 | if (mNextFrame.Header().Info().mRate == 0) { |
479 | 0 | mNextFrame.SetRate(Info().mRate); |
480 | 0 | } |
481 | 0 | if (mNextFrame.Header().Info().mBitDepth == 0) { |
482 | 0 | mNextFrame.SetBitDepth(Info().mBitDepth); |
483 | 0 | } |
484 | 0 | } |
485 | 0 | } |
486 | 0 | return mNextFrame.IsValid(); |
487 | 0 | } |
488 | | |
489 | | bool CheckCRC16AtOffset(int64_t aStart, int64_t aEnd, |
490 | | MediaResourceIndex& aResource) const |
491 | 0 | { |
492 | 0 | int64_t size = aEnd - aStart; |
493 | 0 | if (size <= 0) { |
494 | 0 | return false; |
495 | 0 | } |
496 | 0 | UniquePtr<char[]> buffer(new char[size]); |
497 | 0 | uint32_t read = 0; |
498 | 0 | if (NS_FAILED(aResource.ReadAt(aStart, buffer.get(), size, &read)) || |
499 | 0 | read != size) { |
500 | 0 | NS_WARNING("Couldn't read frame content"); |
501 | 0 | return false; |
502 | 0 | } |
503 | 0 |
|
504 | 0 | uint16_t crc = 0; |
505 | 0 | uint8_t* buf = reinterpret_cast<uint8_t*>(buffer.get()); |
506 | 0 | const uint8_t *end = buf + size; |
507 | 0 | while (buf < end) { |
508 | 0 | crc = CRC16Table[((uint8_t)crc) ^ *buf++] ^ (crc >> 8); |
509 | 0 | } |
510 | 0 | return !crc; |
511 | 0 | } |
512 | | |
513 | | const uint16_t CRC16Table[256] = |
514 | | { |
515 | | 0x0000, 0x0580, 0x0F80, 0x0A00, 0x1B80, 0x1E00, 0x1400, 0x1180, |
516 | | 0x3380, 0x3600, 0x3C00, 0x3980, 0x2800, 0x2D80, 0x2780, 0x2200, |
517 | | 0x6380, 0x6600, 0x6C00, 0x6980, 0x7800, 0x7D80, 0x7780, 0x7200, |
518 | | 0x5000, 0x5580, 0x5F80, 0x5A00, 0x4B80, 0x4E00, 0x4400, 0x4180, |
519 | | 0xC380, 0xC600, 0xCC00, 0xC980, 0xD800, 0xDD80, 0xD780, 0xD200, |
520 | | 0xF000, 0xF580, 0xFF80, 0xFA00, 0xEB80, 0xEE00, 0xE400, 0xE180, |
521 | | 0xA000, 0xA580, 0xAF80, 0xAA00, 0xBB80, 0xBE00, 0xB400, 0xB180, |
522 | | 0x9380, 0x9600, 0x9C00, 0x9980, 0x8800, 0x8D80, 0x8780, 0x8200, |
523 | | 0x8381, 0x8601, 0x8C01, 0x8981, 0x9801, 0x9D81, 0x9781, 0x9201, |
524 | | 0xB001, 0xB581, 0xBF81, 0xBA01, 0xAB81, 0xAE01, 0xA401, 0xA181, |
525 | | 0xE001, 0xE581, 0xEF81, 0xEA01, 0xFB81, 0xFE01, 0xF401, 0xF181, |
526 | | 0xD381, 0xD601, 0xDC01, 0xD981, 0xC801, 0xCD81, 0xC781, 0xC201, |
527 | | 0x4001, 0x4581, 0x4F81, 0x4A01, 0x5B81, 0x5E01, 0x5401, 0x5181, |
528 | | 0x7381, 0x7601, 0x7C01, 0x7981, 0x6801, 0x6D81, 0x6781, 0x6201, |
529 | | 0x2381, 0x2601, 0x2C01, 0x2981, 0x3801, 0x3D81, 0x3781, 0x3201, |
530 | | 0x1001, 0x1581, 0x1F81, 0x1A01, 0x0B81, 0x0E01, 0x0401, 0x0181, |
531 | | 0x0383, 0x0603, 0x0C03, 0x0983, 0x1803, 0x1D83, 0x1783, 0x1203, |
532 | | 0x3003, 0x3583, 0x3F83, 0x3A03, 0x2B83, 0x2E03, 0x2403, 0x2183, |
533 | | 0x6003, 0x6583, 0x6F83, 0x6A03, 0x7B83, 0x7E03, 0x7403, 0x7183, |
534 | | 0x5383, 0x5603, 0x5C03, 0x5983, 0x4803, 0x4D83, 0x4783, 0x4203, |
535 | | 0xC003, 0xC583, 0xCF83, 0xCA03, 0xDB83, 0xDE03, 0xD403, 0xD183, |
536 | | 0xF383, 0xF603, 0xFC03, 0xF983, 0xE803, 0xED83, 0xE783, 0xE203, |
537 | | 0xA383, 0xA603, 0xAC03, 0xA983, 0xB803, 0xBD83, 0xB783, 0xB203, |
538 | | 0x9003, 0x9583, 0x9F83, 0x9A03, 0x8B83, 0x8E03, 0x8403, 0x8183, |
539 | | 0x8002, 0x8582, 0x8F82, 0x8A02, 0x9B82, 0x9E02, 0x9402, 0x9182, |
540 | | 0xB382, 0xB602, 0xBC02, 0xB982, 0xA802, 0xAD82, 0xA782, 0xA202, |
541 | | 0xE382, 0xE602, 0xEC02, 0xE982, 0xF802, 0xFD82, 0xF782, 0xF202, |
542 | | 0xD002, 0xD582, 0xDF82, 0xDA02, 0xCB82, 0xCE02, 0xC402, 0xC182, |
543 | | 0x4382, 0x4602, 0x4C02, 0x4982, 0x5802, 0x5D82, 0x5782, 0x5202, |
544 | | 0x7002, 0x7582, 0x7F82, 0x7A02, 0x6B82, 0x6E02, 0x6402, 0x6182, |
545 | | 0x2002, 0x2582, 0x2F82, 0x2A02, 0x3B82, 0x3E02, 0x3402, 0x3182, |
546 | | 0x1382, 0x1602, 0x1C02, 0x1982, 0x0802, 0x0D82, 0x0782, 0x0202, |
547 | | }; |
548 | | |
549 | | FlacFrameParser mParser; |
550 | | // We keep the first parsed frame around for static info access |
551 | | // and the currently parsed frame. |
552 | | Frame mFirstFrame; |
553 | | Frame mNextFrame; |
554 | | Frame mFrame; |
555 | | }; |
556 | | |
557 | | } // namespace flac |
558 | | |
559 | | // FlacDemuxer |
560 | | |
561 | | FlacDemuxer::FlacDemuxer(MediaResource* aSource) |
562 | | : mSource(aSource) |
563 | 0 | { |
564 | 0 | DDLINKCHILD("source", aSource); |
565 | 0 | } |
566 | | |
567 | | bool |
568 | | FlacDemuxer::InitInternal() |
569 | 0 | { |
570 | 0 | if (!mTrackDemuxer) { |
571 | 0 | mTrackDemuxer = new FlacTrackDemuxer(mSource); |
572 | 0 | DDLINKCHILD("track demuxer", mTrackDemuxer.get()); |
573 | 0 | } |
574 | 0 | return mTrackDemuxer->Init(); |
575 | 0 | } |
576 | | |
577 | | RefPtr<FlacDemuxer::InitPromise> |
578 | | FlacDemuxer::Init() |
579 | 0 | { |
580 | 0 | if (!InitInternal()) { |
581 | 0 | LOG("Init() failure: waiting for data"); |
582 | 0 |
|
583 | 0 | return InitPromise::CreateAndReject( |
584 | 0 | NS_ERROR_DOM_MEDIA_DEMUXER_ERR, __func__); |
585 | 0 | } |
586 | 0 |
|
587 | 0 | LOG("Init() successful"); |
588 | 0 | return InitPromise::CreateAndResolve(NS_OK, __func__); |
589 | 0 | } |
590 | | |
591 | | uint32_t |
592 | | FlacDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const |
593 | 0 | { |
594 | 0 | return (aType == TrackInfo::kAudioTrack) ? 1 : 0; |
595 | 0 | } |
596 | | |
597 | | already_AddRefed<MediaTrackDemuxer> |
598 | | FlacDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber) |
599 | 0 | { |
600 | 0 | if (!mTrackDemuxer) { |
601 | 0 | return nullptr; |
602 | 0 | } |
603 | 0 | |
604 | 0 | return RefPtr<FlacTrackDemuxer>(mTrackDemuxer).forget(); |
605 | 0 | } |
606 | | |
607 | | bool |
608 | | FlacDemuxer::IsSeekable() const |
609 | 0 | { |
610 | 0 | return mTrackDemuxer && mTrackDemuxer->IsSeekable(); |
611 | 0 | } |
612 | | |
613 | | // FlacTrackDemuxer |
614 | | FlacTrackDemuxer::FlacTrackDemuxer(MediaResource* aSource) |
615 | | : mSource(aSource) |
616 | | , mParser(new flac::FrameParser()) |
617 | | , mTotalFrameLen(0) |
618 | 0 | { |
619 | 0 | DDLINKCHILD("source", aSource); |
620 | 0 | Reset(); |
621 | 0 | } |
622 | | |
623 | | FlacTrackDemuxer::~FlacTrackDemuxer() |
624 | 0 | { |
625 | 0 | } |
626 | | |
627 | | bool |
628 | | FlacTrackDemuxer::Init() |
629 | 0 | { |
630 | 0 | static const int BUFFER_SIZE = 4096; |
631 | 0 |
|
632 | 0 | // First check if we have a valid Flac start. |
633 | 0 | char buffer[BUFFER_SIZE]; |
634 | 0 | const uint8_t* ubuffer = // only needed due to type constraints of ReadAt. |
635 | 0 | reinterpret_cast<uint8_t*>(buffer); |
636 | 0 | int64_t offset = 0; |
637 | 0 |
|
638 | 0 | do { |
639 | 0 | uint32_t read = 0; |
640 | 0 | nsresult ret = mSource.ReadAt(offset, buffer, BUFFER_SIZE, &read); |
641 | 0 | if (NS_FAILED(ret) || read < BUFFER_SIZE) { |
642 | 0 | // Assume that if we can't read that many bytes while parsing the header, |
643 | 0 | // that something is wrong. |
644 | 0 | return false; |
645 | 0 | } |
646 | 0 | if (!mParser->IsHeaderBlock(ubuffer, BUFFER_SIZE)) { |
647 | 0 | // Not a header and we haven't reached the end of the metadata blocks. |
648 | 0 | // Will fall back to using the frames header instead. |
649 | 0 | break; |
650 | 0 | } |
651 | 0 | uint32_t sizeHeader = mParser->HeaderBlockLength(ubuffer); |
652 | 0 | RefPtr<MediaByteBuffer> block = |
653 | 0 | mSource.MediaReadAt(offset, sizeHeader); |
654 | 0 | if (!block || block->Length() != sizeHeader) { |
655 | 0 | break; |
656 | 0 | } |
657 | 0 | if (!mParser->DecodeHeaderBlock(block->Elements(), sizeHeader)) { |
658 | 0 | break; |
659 | 0 | } |
660 | 0 | offset += sizeHeader; |
661 | 0 | } while (!mParser->HasFullMetadata()); |
662 | 0 |
|
663 | 0 | // First flac frame is found after the metadata. |
664 | 0 | // Can seek there immediately to avoid reparsing it all. |
665 | 0 | mSource.Seek(SEEK_SET, offset); |
666 | 0 |
|
667 | 0 | // Find the first frame to fully initialise our parser. |
668 | 0 | if (mParser->FindNextFrame(mSource)) { |
669 | 0 | // Ensure that the next frame returned will be the first. |
670 | 0 | mSource.Seek(SEEK_SET, mParser->FirstFrame().Offset()); |
671 | 0 | mParser->EndFrameSession(); |
672 | 0 | } else if (!mParser->Info().IsValid() || !mParser->FirstFrame().IsValid()) { |
673 | 0 | // We must find at least a frame to determine the metadata. |
674 | 0 | // We can't play this stream. |
675 | 0 | return false; |
676 | 0 | } |
677 | 0 | |
678 | 0 | if (!mParser->Info().IsValid() || !mParser->Info().mDuration.IsPositive()) { |
679 | 0 | // Check if we can look at the last frame for the end time to determine the |
680 | 0 | // duration when we don't have any. |
681 | 0 | TimeAtEnd(); |
682 | 0 | } |
683 | 0 |
|
684 | 0 | return true; |
685 | 0 | } |
686 | | |
687 | | UniquePtr<TrackInfo> |
688 | | FlacTrackDemuxer::GetInfo() const |
689 | 0 | { |
690 | 0 | if (mParser->Info().IsValid()) { |
691 | 0 | // We have a proper metadata header. |
692 | 0 | UniquePtr<TrackInfo> info = mParser->Info().Clone(); |
693 | 0 | nsAutoPtr<MetadataTags> tags(mParser->GetTags()); |
694 | 0 | if (tags) { |
695 | 0 | for (auto iter = tags->Iter(); !iter.Done(); iter.Next()) { |
696 | 0 | info->mTags.AppendElement(MetadataTag(iter.Key(), iter.Data())); |
697 | 0 | } |
698 | 0 | } |
699 | 0 | return info; |
700 | 0 | } else if (mParser->FirstFrame().Info().IsValid()) { |
701 | 0 | // Use the first frame header. |
702 | 0 | UniquePtr<TrackInfo> info = mParser->FirstFrame().Info().Clone(); |
703 | 0 | info->mDuration = Duration(); |
704 | 0 | return info; |
705 | 0 | } |
706 | 0 | return nullptr; |
707 | 0 | } |
708 | | |
709 | | bool |
710 | | FlacTrackDemuxer::IsSeekable() const |
711 | 0 | { |
712 | 0 | // For now we only allow seeking if a STREAMINFO block was found and with |
713 | 0 | // a known number of samples (duration is set). |
714 | 0 | return mParser->Info().IsValid() && mParser->Info().mDuration.IsPositive(); |
715 | 0 | } |
716 | | |
717 | | RefPtr<FlacTrackDemuxer::SeekPromise> |
718 | | FlacTrackDemuxer::Seek(const TimeUnit& aTime) |
719 | 0 | { |
720 | 0 | // Efficiently seek to the position. |
721 | 0 | FastSeek(aTime); |
722 | 0 | // Correct seek position by scanning the next frames. |
723 | 0 | const TimeUnit seekTime = ScanUntil(aTime); |
724 | 0 |
|
725 | 0 | return SeekPromise::CreateAndResolve(seekTime, __func__); |
726 | 0 | } |
727 | | |
728 | | TimeUnit |
729 | | FlacTrackDemuxer::FastSeek(const TimeUnit& aTime) |
730 | 0 | { |
731 | 0 | LOG("FastSeek(%f) avgFrameLen=%f mParsedFramesDuration=%f offset=%" PRId64, |
732 | 0 | aTime.ToSeconds(), AverageFrameLength(), |
733 | 0 | mParsedFramesDuration.ToSeconds(), GetResourceOffset()); |
734 | 0 |
|
735 | 0 | // Invalidate current frames in the parser. |
736 | 0 | mParser->EndFrameSession(); |
737 | 0 |
|
738 | 0 | if (!mParser->FirstFrame().IsValid()) { |
739 | 0 | // Something wrong, and there's nothing to seek to anyway, so we can |
740 | 0 | // do whatever here. |
741 | 0 | mSource.Seek(SEEK_SET, 0); |
742 | 0 | return TimeUnit(); |
743 | 0 | } |
744 | 0 | |
745 | 0 | if (aTime <= mParser->FirstFrame().Time()) { |
746 | 0 | // We're attempting to seek prior the first frame, return the first frame. |
747 | 0 | mSource.Seek(SEEK_SET, mParser->FirstFrame().Offset()); |
748 | 0 | return mParser->FirstFrame().Time(); |
749 | 0 | } |
750 | 0 | |
751 | 0 | // We look for the seek position using a bisection search, starting where the |
752 | 0 | // estimated position might be using the average frame length. |
753 | 0 | // Typically, with flac such approximation is typically useless. |
754 | 0 | |
755 | 0 | // Estimate where the position might be. |
756 | 0 | int64_t pivot = |
757 | 0 | aTime.ToSeconds() * AverageFrameLength() + mParser->FirstFrame().Offset(); |
758 | 0 |
|
759 | 0 | // Time in seconds where we can stop seeking and will continue using |
760 | 0 | // ScanUntil. |
761 | 0 | static const int GAP_THRESHOLD = 5; |
762 | 0 | int64_t first = mParser->FirstFrame().Offset(); |
763 | 0 | int64_t last = mSource.GetLength(); |
764 | 0 | Maybe<int64_t> lastFoundOffset; |
765 | 0 | uint32_t iterations = 0; |
766 | 0 | TimeUnit timeSeekedTo; |
767 | 0 |
|
768 | 0 | do { |
769 | 0 | iterations++; |
770 | 0 | mSource.Seek(SEEK_SET, pivot); |
771 | 0 | flac::Frame frame; |
772 | 0 | if (!frame.FindNext(mSource)) { |
773 | 0 | NS_WARNING("We should have found a point"); |
774 | 0 | break; |
775 | 0 | } |
776 | 0 | timeSeekedTo = frame.Time(); |
777 | 0 |
|
778 | 0 | LOGV("FastSeek: interation:%u found:%f @ %" PRId64, |
779 | 0 | iterations, timeSeekedTo.ToSeconds(), frame.Offset()); |
780 | 0 |
|
781 | 0 | if (lastFoundOffset && lastFoundOffset.ref() == frame.Offset()) { |
782 | 0 | // Same frame found twice. We're done. |
783 | 0 | break; |
784 | 0 | } |
785 | 0 | lastFoundOffset = Some(frame.Offset()); |
786 | 0 |
|
787 | 0 | if (frame.Time() == aTime) { |
788 | 0 | break; |
789 | 0 | } |
790 | 0 | if (aTime > frame.Time() && |
791 | 0 | aTime - frame.Time() <= TimeUnit::FromSeconds(GAP_THRESHOLD)) { |
792 | 0 | // We're close enough to the target, experimentation shows that bisection |
793 | 0 | // search doesn't help much after that. |
794 | 0 | break; |
795 | 0 | } |
796 | 0 | if (frame.Time() > aTime) { |
797 | 0 | last = pivot; |
798 | 0 | pivot -= (pivot - first) / 2; |
799 | 0 | } else { |
800 | 0 | first = pivot; |
801 | 0 | pivot += (last - pivot) / 2; |
802 | 0 | } |
803 | 0 | } while (true); |
804 | 0 |
|
805 | 0 | if (lastFoundOffset) { |
806 | 0 | mSource.Seek(SEEK_SET, lastFoundOffset.ref()); |
807 | 0 | } |
808 | 0 |
|
809 | 0 | return timeSeekedTo; |
810 | 0 | } |
811 | | |
812 | | TimeUnit |
813 | | FlacTrackDemuxer::ScanUntil(const TimeUnit& aTime) |
814 | 0 | { |
815 | 0 | LOG("ScanUntil(%f avgFrameLen=%f mParsedFramesDuration=%f offset=%" PRId64, |
816 | 0 | aTime.ToSeconds(), AverageFrameLength(), |
817 | 0 | mParsedFramesDuration.ToSeconds(), mParser->CurrentFrame().Offset()); |
818 | 0 |
|
819 | 0 | if (!mParser->FirstFrame().IsValid() || |
820 | 0 | aTime <= mParser->FirstFrame().Time()) { |
821 | 0 | return FastSeek(aTime); |
822 | 0 | } |
823 | 0 | |
824 | 0 | int64_t previousOffset = 0; |
825 | 0 | TimeUnit previousTime; |
826 | 0 | while (FindNextFrame().IsValid() && mParser->CurrentFrame().Time() < aTime) { |
827 | 0 | previousOffset = mParser->CurrentFrame().Offset(); |
828 | 0 | previousTime = mParser->CurrentFrame().Time(); |
829 | 0 | } |
830 | 0 |
|
831 | 0 | if (!mParser->CurrentFrame().IsValid()) { |
832 | 0 | // We reached EOS. |
833 | 0 | return Duration(); |
834 | 0 | } |
835 | 0 | |
836 | 0 | // Seek back to the last frame found prior the target. |
837 | 0 | mParser->EndFrameSession(); |
838 | 0 | mSource.Seek(SEEK_SET, previousOffset); |
839 | 0 | return previousTime; |
840 | 0 | } |
841 | | |
842 | | RefPtr<FlacTrackDemuxer::SamplesPromise> |
843 | | FlacTrackDemuxer::GetSamples(int32_t aNumSamples) |
844 | 0 | { |
845 | 0 | LOGV("GetSamples(%d) Begin offset=%" PRId64 " mParsedFramesDuration=%f" |
846 | 0 | " mTotalFrameLen=%" PRIu64, |
847 | 0 | aNumSamples, GetResourceOffset(), mParsedFramesDuration.ToSeconds(), |
848 | 0 | mTotalFrameLen); |
849 | 0 |
|
850 | 0 | if (!aNumSamples) { |
851 | 0 | return SamplesPromise::CreateAndReject( |
852 | 0 | NS_ERROR_DOM_MEDIA_DEMUXER_ERR, __func__); |
853 | 0 | } |
854 | 0 | |
855 | 0 | RefPtr<SamplesHolder> frames = new SamplesHolder(); |
856 | 0 |
|
857 | 0 | while (aNumSamples--) { |
858 | 0 | RefPtr<MediaRawData> frame(GetNextFrame(FindNextFrame())); |
859 | 0 | if (!frame) |
860 | 0 | break; |
861 | 0 | |
862 | 0 | frames->mSamples.AppendElement(frame); |
863 | 0 | } |
864 | 0 |
|
865 | 0 | LOGV("GetSamples() End mSamples.Length=%zu aNumSamples=%d offset=%" PRId64 |
866 | 0 | " mParsedFramesDuration=%f mTotalFrameLen=%" PRIu64, |
867 | 0 | frames->mSamples.Length(), aNumSamples, GetResourceOffset(), |
868 | 0 | mParsedFramesDuration.ToSeconds(), mTotalFrameLen); |
869 | 0 |
|
870 | 0 | if (frames->mSamples.IsEmpty()) { |
871 | 0 | return SamplesPromise::CreateAndReject( |
872 | 0 | NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__); |
873 | 0 | } |
874 | 0 | |
875 | 0 | return SamplesPromise::CreateAndResolve(frames, __func__); |
876 | 0 | } |
877 | | |
878 | | void |
879 | | FlacTrackDemuxer::Reset() |
880 | 0 | { |
881 | 0 | LOG("Reset()"); |
882 | 0 | MOZ_ASSERT(mParser); |
883 | 0 | if (mParser->FirstFrame().IsValid()) { |
884 | 0 | mSource.Seek(SEEK_SET, mParser->FirstFrame().Offset()); |
885 | 0 | } else { |
886 | 0 | mSource.Seek(SEEK_SET, 0); |
887 | 0 | } |
888 | 0 | mParser->EndFrameSession(); |
889 | 0 | } |
890 | | |
891 | | RefPtr<FlacTrackDemuxer::SkipAccessPointPromise> |
892 | | FlacTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) |
893 | 0 | { |
894 | 0 | // Will not be called for audio-only resources. |
895 | 0 | return SkipAccessPointPromise::CreateAndReject( |
896 | 0 | SkipFailureHolder(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 0), __func__); |
897 | 0 | } |
898 | | |
899 | | int64_t |
900 | | FlacTrackDemuxer::GetResourceOffset() const |
901 | 0 | { |
902 | 0 | return mSource.Tell(); |
903 | 0 | } |
904 | | |
905 | | TimeIntervals |
906 | | FlacTrackDemuxer::GetBuffered() |
907 | 0 | { |
908 | 0 | TimeUnit duration = Duration(); |
909 | 0 |
|
910 | 0 | if (duration <= TimeUnit()) { |
911 | 0 | return TimeIntervals(); |
912 | 0 | } |
913 | 0 | |
914 | 0 | // We could simply parse the cached data instead and read the timestamps. |
915 | 0 | // However, for now this will do. |
916 | 0 | AutoPinned<MediaResource> stream(mSource.GetResource()); |
917 | 0 | return GetEstimatedBufferedTimeRanges(stream, duration.ToMicroseconds()); |
918 | 0 | } |
919 | | |
920 | | const flac::Frame& |
921 | | FlacTrackDemuxer::FindNextFrame() |
922 | 0 | { |
923 | 0 | LOGV("FindNext() Begin offset=%" PRId64 " mParsedFramesDuration=%f" |
924 | 0 | " mTotalFrameLen=%" PRIu64, |
925 | 0 | GetResourceOffset(), mParsedFramesDuration.ToSeconds(), mTotalFrameLen); |
926 | 0 |
|
927 | 0 | if (mParser->FindNextFrame(mSource)) { |
928 | 0 | // Update our current progress stats. |
929 | 0 | mParsedFramesDuration = |
930 | 0 | std::max(mParsedFramesDuration, |
931 | 0 | mParser->CurrentFrame().Time() - mParser->FirstFrame().Time() |
932 | 0 | + mParser->CurrentFrame().Duration()); |
933 | 0 | mTotalFrameLen = |
934 | 0 | std::max<uint64_t>(mTotalFrameLen, |
935 | 0 | mParser->CurrentFrame().Offset() |
936 | 0 | - mParser->FirstFrame().Offset() |
937 | 0 | + mParser->CurrentFrame().Size()); |
938 | 0 |
|
939 | 0 | LOGV("FindNext() End time=%f offset=%" PRId64 " mParsedFramesDuration=%f" |
940 | 0 | " mTotalFrameLen=%" PRIu64, |
941 | 0 | mParser->CurrentFrame().Time().ToSeconds(), GetResourceOffset(), |
942 | 0 | mParsedFramesDuration.ToSeconds(), mTotalFrameLen); |
943 | 0 | } |
944 | 0 |
|
945 | 0 | return mParser->CurrentFrame(); |
946 | 0 | } |
947 | | |
948 | | already_AddRefed<MediaRawData> |
949 | | FlacTrackDemuxer::GetNextFrame(const flac::Frame& aFrame) |
950 | 0 | { |
951 | 0 | if (!aFrame.IsValid()) { |
952 | 0 | LOG("GetNextFrame() EOS"); |
953 | 0 | return nullptr; |
954 | 0 | } |
955 | 0 |
|
956 | 0 | LOG("GetNextFrame() Begin(time=%f offset=%" PRId64 " size=%u)", |
957 | 0 | aFrame.Time().ToSeconds(), aFrame.Offset(), aFrame.Size()); |
958 | 0 |
|
959 | 0 | const int64_t offset = aFrame.Offset(); |
960 | 0 | const uint32_t size = aFrame.Size(); |
961 | 0 |
|
962 | 0 | RefPtr<MediaRawData> frame = new MediaRawData(); |
963 | 0 | frame->mOffset = offset; |
964 | 0 |
|
965 | 0 | UniquePtr<MediaRawDataWriter> frameWriter(frame->CreateWriter()); |
966 | 0 | if (!frameWriter->SetSize(size)) { |
967 | 0 | LOG("GetNext() Exit failed to allocated media buffer"); |
968 | 0 | return nullptr; |
969 | 0 | } |
970 | 0 |
|
971 | 0 | const uint32_t read = Read(frameWriter->Data(), offset, size); |
972 | 0 | if (read != size) { |
973 | 0 | LOG("GetNextFrame() Exit read=%u frame->Size=%zu", read, frame->Size()); |
974 | 0 | return nullptr; |
975 | 0 | } |
976 | 0 |
|
977 | 0 | frame->mTime = aFrame.Time(); |
978 | 0 | frame->mDuration = aFrame.Duration(); |
979 | 0 | frame->mTimecode = frame->mTime; |
980 | 0 | frame->mOffset = aFrame.Offset(); |
981 | 0 | frame->mKeyframe = true; |
982 | 0 |
|
983 | 0 | MOZ_ASSERT(!frame->mTime.IsNegative()); |
984 | 0 | MOZ_ASSERT(!frame->mDuration.IsNegative()); |
985 | 0 |
|
986 | 0 | return frame.forget(); |
987 | 0 | } |
988 | | |
989 | | int32_t |
990 | | FlacTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize) |
991 | 0 | { |
992 | 0 | uint32_t read = 0; |
993 | 0 | const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast<char*>(aBuffer), |
994 | 0 | static_cast<uint32_t>(aSize), &read); |
995 | 0 | NS_ENSURE_SUCCESS(rv, 0); |
996 | 0 | return static_cast<int32_t>(read); |
997 | 0 | } |
998 | | |
999 | | double |
1000 | | FlacTrackDemuxer::AverageFrameLength() const |
1001 | 0 | { |
1002 | 0 | if (mParsedFramesDuration.ToMicroseconds()) { |
1003 | 0 | return mTotalFrameLen / mParsedFramesDuration.ToSeconds(); |
1004 | 0 | } |
1005 | 0 | |
1006 | 0 | return 0.0; |
1007 | 0 | } |
1008 | | |
1009 | | TimeUnit |
1010 | | FlacTrackDemuxer::Duration() const |
1011 | 0 | { |
1012 | 0 | return std::max(mParsedFramesDuration, mParser->Info().mDuration); |
1013 | 0 | } |
1014 | | |
1015 | | TimeUnit |
1016 | | FlacTrackDemuxer::TimeAtEnd() |
1017 | 0 | { |
1018 | 0 | // Scan the last 128kB if available to determine the last frame. |
1019 | 0 | static const int OFFSET_FROM_END = 128 * 1024; |
1020 | 0 |
|
1021 | 0 | // Seek to the end of the file and attempt to find the last frame. |
1022 | 0 | MediaResourceIndex source(mSource.GetResource()); |
1023 | 0 | TimeUnit previousDuration; |
1024 | 0 | TimeUnit previousTime; |
1025 | 0 |
|
1026 | 0 | const int64_t streamLen = mSource.GetLength(); |
1027 | 0 | if (streamLen < 0) { |
1028 | 0 | return TimeUnit::FromInfinity(); |
1029 | 0 | } |
1030 | 0 | |
1031 | 0 | flac::FrameParser parser; |
1032 | 0 |
|
1033 | 0 | source.Seek(SEEK_SET, std::max<int64_t>(0LL, streamLen - OFFSET_FROM_END)); |
1034 | 0 | while (parser.FindNextFrame(source)) { |
1035 | 0 | // FFmpeg flac muxer can generate a last frame with earlier than the others. |
1036 | 0 | previousTime = std::max(previousTime, parser.CurrentFrame().Time()); |
1037 | 0 | if (parser.CurrentFrame().Duration() > TimeUnit()) { |
1038 | 0 | // The last frame doesn't have a duration, so only update our duration |
1039 | 0 | // if we do have one. |
1040 | 0 | previousDuration = parser.CurrentFrame().Duration(); |
1041 | 0 | } |
1042 | 0 | if (source.Tell() >= streamLen) { |
1043 | 0 | // Limit the read, in case the length change half-way. |
1044 | 0 | break; |
1045 | 0 | } |
1046 | 0 | } |
1047 | 0 |
|
1048 | 0 | // Update our current progress stats. |
1049 | 0 | mParsedFramesDuration = |
1050 | 0 | previousTime + previousDuration - mParser->FirstFrame().Time(); |
1051 | 0 | mTotalFrameLen = streamLen - mParser->FirstFrame().Offset(); |
1052 | 0 |
|
1053 | 0 | return mParsedFramesDuration; |
1054 | 0 | } |
1055 | | |
1056 | | /* static */ bool |
1057 | | FlacDemuxer::FlacSniffer(const uint8_t* aData, const uint32_t aLength) |
1058 | 0 | { |
1059 | 0 | if (aLength < FLAC_MIN_FRAME_SIZE) { |
1060 | 0 | return false; |
1061 | 0 | } |
1062 | 0 | |
1063 | 0 | flac::Frame frame; |
1064 | 0 | return frame.FindNext(aData, aLength) >= 0; |
1065 | 0 | } |
1066 | | |
1067 | | } // namespace mozilla |