-
Notifications
You must be signed in to change notification settings - Fork 28
/
Copy pathmetadata2.rs
1227 lines (1081 loc) · 44.6 KB
/
metadata2.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Claxon -- A FLAC decoding library in Rust
// Copyright 2018 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.
//! The `metadata` module deals with metadata at the beginning of a FLAC stream.
use std::fs;
use std::io;
use std::path;
use std::slice;
use std::convert;
use std::ops;
use error::{Error, Result, fmt_err};
use input::{EmbeddedReader, ReadBytes};
/// A metadata about the FLAC stream.
pub enum MetadataBlock<'a, R: 'a + io::Read> {
/// The stream info block.
StreamInfo(StreamInfo),
/// A padding block, of a given number of `0x00` bytes.
Padding(u32),
/// A block with application-specific data.
Application(ApplicationBlock<'a, R>),
/// A lazy seek table block.
///
/// Yields the [`SeekTable`](struct.SeekTable.html) from `get()` (not implemented yet).
// TODO: get() docs when implemented.
SeekTable(LazySeekTable<'a, R>),
/// A lazy Vorbis comment block, also known as FLAC tags.
///
/// Yields the [`VorbisComment`](struct.VorbisComment.html) from
/// [`get()`](struct.LazyVorbisComment.html#method.get).
VorbisComment(LazyVorbisComment<'a, R>),
/// A lazy CUE sheet block.
///
/// Yields the `CueSheet` from `get()` (not implemented yet).
CueSheet(LazyCueSheet<'a, R>),
/// A lazy picture block.
///
/// Yields the [`Picture`](struct.Picture.html) from
/// [`get()`](struct.LazyPicture.html#method.get).
Picture(LazyPicture<'a, R>),
}
impl<'a, R: 'a + io::Read> MetadataBlock<'a, R> {
/// Skip over the block without parsing it.
///
/// This calls `discard()` on the inner metadata block.
pub fn discard(self) -> io::Result<()> {
match self {
MetadataBlock::StreamInfo(..) => Ok(()),
// TODO: Make Padding like Application, with a reader and discard.
MetadataBlock::Padding(..) => Ok(()),
// TODO: Add a discard to Application block.
MetadataBlock::Application(..) => Ok(()),
MetadataBlock::SeekTable(lazy_block) => lazy_block.discard(),
MetadataBlock::VorbisComment(lazy_block) => lazy_block.discard(),
MetadataBlock::CueSheet(lazy_block) => lazy_block.discard(),
MetadataBlock::Picture(lazy_block) => lazy_block.discard(),
}
}
}
/// The streaminfo metadata block, with important information about the stream.
#[derive(Clone, Copy, Debug)]
pub struct StreamInfo {
// TODO: "size" would better be called "duration" for clarity.
/// The minimum block size (in inter-channel samples) used in the stream.
///
/// This number is independent of the number of channels. To get the minimum
/// block duration in seconds, divide this by the sample rate.
pub min_block_size: u16,
/// The maximum block size (in inter-channel samples) used in the stream.
///
/// This number is independent of the number of channels. To get the
/// maximum block duratin in seconds, divide by the sample rate. To avoid
/// allocations during decoding, a buffer of this size times the number of
/// channels can be allocated up front and passed into
/// `FrameReader::read_next_or_eof()`.
pub max_block_size: u16,
/// The minimum frame size (in bytes) used in the stream.
pub min_frame_size: Option<u32>,
/// The maximum frame size (in bytes) used in the stream.
pub max_frame_size: Option<u32>,
/// The sample rate in Hz.
pub sample_rate: u32,
/// The number of channels.
pub channels: u32,
/// The number of bits per sample.
pub bits_per_sample: u32,
/// The total number of inter-channel samples in the stream.
// TODO: rename to `duration` for clarity?
pub samples: Option<u64>,
/// MD5 signature of the unencoded audio data.
pub md5sum: [u8; 16],
}
/// A metadata block that holds application-specific data.
pub struct ApplicationBlock<'a, R: 'a + io::Read> {
/// The application id, registered with Xiph.org.
///
/// [The list of registered ids can be found on Xiph.org][ids].
///
/// [ids]: https://xiph.org/flac/id.html
pub id: u32,
/// A reader that exposes the embedded application-specific data.
///
/// The reader is constrained to the application data, and will return EOF
/// when that data ends. The reader can safely be dropped even if it was not
/// consumed until the end.
pub reader: EmbeddedReader<'a, R>,
}
/// A seek point in the seek table.
#[derive(Clone, Copy)]
pub struct SeekPoint {
/// Sample number of the first sample in the target frame, or 2<sup>64</sup> - 1 for a placeholder.
pub sample: u64,
/// Offset in bytes from the first byte of the first frame header to the first byte of the
/// target frame's header.
pub offset: u64,
/// Number of samples in the target frame.
pub samples: u16,
}
/// A seek table to aid seeking in the stream.
pub struct SeekTable {
/// The seek points, sorted in ascending order by sample number.
#[allow(dead_code)] // TODO: Implement seeking.
seekpoints: Vec<SeekPoint>,
}
/// Vorbis comments, also known as FLAC tags (e.g. artist, title, etc.).
pub struct VorbisComment {
/// The “vendor string”, chosen by the encoder vendor.
///
/// This string usually contains the name and version of the program that
/// encoded the FLAC stream, such as `reference libFLAC 1.3.2 20170101`
/// or `Lavf57.25.100`.
pub vendor: String,
/// Name-value pairs of Vorbis comments, such as `ARTIST=Queen`.
///
/// This struct stores a raw low-level representation of tags. Use
/// [`tags()`](#method.tags) for a friendlier iterator. The tuple consists
/// of the string in `"NAME=value"` format, and the index of the `'='` into
/// that string.
///
/// The name is supposed to be interpreted case-insensitively, and is
/// guaranteed to consist of ASCII characters. Claxon does not normalize
/// the casing of the name. Use [`get_tag()`](#method.get_tag) to do a
/// case-insensitive lookup.
///
/// Names need not be unique. For instance, multiple `ARTIST` comments might
/// be present on a collaboration track.
///
/// See <https://www.xiph.org/vorbis/doc/v-comment.html> for more details.
pub comments: Vec<(String, usize)>,
}
/// The picture kind: front cover, leaflet, etc.
///
/// The FLAC format uses the picture kinds from the ID3v2 API frame.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum PictureKind {
/// Front cover.
FrontCover = 3,
/// Back cover.
BackCover = 4,
/// Leaflet page.
LeafletPage = 5,
/// Media (e.g. label side of CD).
Media = 6,
/// Lead artist, lead performer, or soloist.
LeadArtist = 7,
/// Artist or performer.
Artist = 8,
/// Conductor.
Conductor = 9,
/// Band or orchestra.
Band = 10,
/// Composer.
Composer = 11,
/// Lyricist or text writer.
Lyricist = 12,
/// Recording location.
RecordingLocation = 13,
/// Picture taken during recording.
DuringRecording = 14,
/// Picture taken during performance.
DuringPerformance = 15,
/// A screen capture of a movie or video.
VideoScreenCapture = 16,
/// Bright colored fish, presumably a xiph.org joke.
BrightColoredFish = 17,
/// Illustration.
Illustration = 18,
/// Band or artist logotype.
BandLogotype = 19,
/// Publisher or studio logotype.
PublisherLogotype = 20,
/// 32x32 pixels png file icon.
FileIcon32x32 = 1,
/// Other file icon.
FileIconOther = 2,
/// Other.
Other = 0,
}
/// Metadata about an embedded picture.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PictureMetadata {
/// The picture kind: front cover, leaflet, etc.
pub kind: PictureKind,
/// MIME type of the picture.
///
/// The type can also be `-->`, in which case the data should be interpreted
/// as a URL rather than the actual picture data.
pub mime_type: String,
/// A description of the picture. Often empty in practice.
pub description: String,
/// The width of the picture in pixels.
pub width: u32,
/// The height of the picture in pixels.
pub height: u32,
}
/// A block that embeds a picture (e.g. cover art).
pub struct Picture<'a, R: 'a + io::Read> {
/// Metadata about the picture, such as its kind and dimensions.
pub metadata: PictureMetadata,
/// A reader that exposes the embedded picture data.
///
/// The reader is constrained to the picture data, and will return EOF
/// when the picture data ends. The reader can safely be dropped even if
/// it was not consumed until the end.
pub reader: EmbeddedReader<'a, R>
}
impl VorbisComment {
/// Return name-value pairs of Vorbis comments, such as `("ARTIST", "Queen")`.
///
/// The name is supposed to be interpreted case-insensitively, and is
/// guaranteed to consist of ASCII characters. Claxon does not normalize
/// the casing of the name. Use [`get_tag()`](#method.get_tag) to do a
/// case-insensitive lookup.
///
/// Names need not be unique. For instance, multiple `ARTIST` comments might
/// be present on a collaboration track.
///
/// See <https://www.xiph.org/vorbis/doc/v-comment.html> for more details
/// about tag conventions.
pub fn tags(&self) -> Tags {
Tags::new(&self.comments)
}
/// Look up a Vorbis comment such as `ARTIST` in a case-insensitive way.
///
/// Returns an iterator, because tags may occur more than once. There could
/// be multiple `ARTIST` tags on a collaboration track, for instance.
///
/// Note that tag names are ASCII and never contain `'='`; trying to look up
/// a non-ASCII tag will return no results. Furthermore, the Vorbis comment
/// spec dictates that tag names should be handled case-insensitively, so
/// this method performs a case-insensitive lookup.
///
/// See also [`tags()`](#method.tags) for access to all tags.
/// See <https://www.xiph.org/vorbis/doc/v-comment.html> for more details
/// about tag conventions.
pub fn get_tag<'a>(&'a self, tag_name: &'a str) -> GetTag<'a> {
GetTag::new(&self.comments, tag_name)
}
}
impl<'a> IntoIterator for &'a VorbisComment {
type Item = (&'a str, &'a str);
type IntoIter = Tags<'a>;
fn into_iter(self) -> Tags<'a> {
self.tags()
}
}
/// Wrapper for `Option<VorbisComment>` with convenient tag iterators.
///
/// This struct avoids the need for a case distinction between streams that do
/// have a Vorbis comment block and streams that do not. It is always possible
/// to iterate over tags or look up a tag. If there was no Vorbis comment block,
/// those lookups will simply return no results.
///
/// [`MetadataReader::next_vorbis_comment()`][next-vorbis-comment] returns this
/// struct.
///
/// [next-vorbis-comment]: struct.MetadataReader.html#method.next_vorbis_comment
pub struct OptionalVorbisComment(pub Option<VorbisComment>);
impl OptionalVorbisComment {
/// Returns the vendor string of the Vorbis comment block, if present.
///
/// This string usually contains the name and version of the program that
/// encoded the FLAC stream, such as `reference libFLAC 1.3.2 20170101`
/// or `Lavf57.25.100`.
pub fn vendor(&self) -> Option<&str> {
self.as_ref().map(|vc| &vc.vendor[..])
}
/// Returns name-value pairs of Vorbis comments, such as `("ARTIST", "Queen")`.
///
/// See [`VorbisComment::tags()`](struct.VorbisComment.html#method.tags).
pub fn tags(&self) -> Tags {
match self.as_ref() {
Some(vc) => Tags::new(&vc.comments[..]),
None => Tags::new(&[]),
}
}
/// Look up a Vorbis comment such as `ARTIST` in a case-insensitive way.
///
/// See [`VorbisComment::get_tag()`](struct.VorbisComment.html#method.get_tag).
pub fn get_tag<'a>(&'a self, tag_name: &'a str) -> GetTag<'a> {
match self.as_ref() {
Some(vc) => GetTag::new(&vc.comments[..], tag_name),
None => GetTag::new(&[], tag_name),
}
}
}
impl convert::From<OptionalVorbisComment> for Option<VorbisComment> {
fn from(opt: OptionalVorbisComment) -> Option<VorbisComment> {
opt.0
}
}
impl<'a> IntoIterator for &'a OptionalVorbisComment {
type Item = (&'a str, &'a str);
type IntoIter = Tags<'a>;
fn into_iter(self) -> Tags<'a> {
self.tags()
}
}
impl ops::Deref for OptionalVorbisComment {
type Target = Option<VorbisComment>;
fn deref(&self) -> &Option<VorbisComment> {
&self.0
}
}
impl ops::DerefMut for OptionalVorbisComment {
fn deref_mut(&mut self) -> &mut Option<VorbisComment> {
&mut self.0
}
}
/// Iterates over Vorbis comments (FLAC tags) in a FLAC stream.
///
/// See [`VorbisComment::tags()`](struct.VorbisComment#method.tags) for more details.
pub struct Tags<'a> {
/// The underlying iterator.
iter: slice::Iter<'a, (String, usize)>,
}
impl<'a> Tags<'a> {
/// Returns a new `Tags` iterator.
#[inline]
fn new(comments: &'a [(String, usize)]) -> Tags<'a> {
Tags {
iter: comments.iter(),
}
}
}
impl<'a> Iterator for Tags<'a> {
type Item = (&'a str, &'a str);
#[inline]
fn next(&mut self) -> Option<(&'a str, &'a str)> {
return self.iter.next().map(|&(ref comment, sep_idx)| {
(&comment[..sep_idx], &comment[sep_idx+1..])
})
}
}
// TODO: `Tags` could implement `ExactSizeIterator`.
/// Iterates over Vorbis comments looking for a specific one; returns its values as `&str`.
///
/// See [`VorbisComment::get_tag()`](struct.VorbisComment#method.get_tag) for more details.
pub struct GetTag<'a> {
/// The Vorbis comments to search through.
vorbis_comments: &'a [(String, usize)],
/// The tag to look for.
needle: &'a str,
/// The index of the (name, value) pair that should be inspected next.
index: usize,
}
impl<'a> GetTag<'a> {
/// Returns a new `GetTag` iterator.
#[inline]
fn new(vorbis_comments: &'a [(String, usize)], needle: &'a str) -> GetTag<'a> {
GetTag {
vorbis_comments: vorbis_comments,
needle: needle,
index: 0,
}
}
}
impl<'a> Iterator for GetTag<'a> {
type Item = &'a str;
#[inline]
fn next(&mut self) -> Option<&'a str> {
// This import is actually required on Rust 1.13.
#[allow(unused_imports)]
use std::ascii::AsciiExt;
while self.index < self.vorbis_comments.len() {
let (ref comment, sep_idx) = self.vorbis_comments[self.index];
self.index += 1;
if comment[..sep_idx].eq_ignore_ascii_case(self.needle) {
return Some(&comment[sep_idx + 1..])
}
}
return None
}
}
macro_rules! lazy_block_impl {
($typename: ident) => {
impl <'a, R: 'a + io::Read> $typename<'a, R> {
/// Skip over this metadata block without parsing anything.
pub fn discard(mut self) -> io::Result<()> {
let len = self.len;
self.len = 0;
self.input.skip(len)
}
}
impl<'a, R: 'a + io::Read> Drop for $typename<'a, R> {
fn drop(&mut self) {
if self.len != 0 {
panic!("{} was dropped, call .discard() or .get() instead.", stringify!($typename))
}
}
}
}
}
/// An unparsed seek table.
///
/// This struct must be consumed in one of two ways:
///
/// * Call `discard()` to skip over the block.
/// * Call `get()` to read and parse the seek table. (Not implemented yet.)
///
/// **Dropping this struct without calling either will panic.**
#[must_use = "Discard using discard() or consume with get()."]
pub struct LazySeekTable<'a, R: 'a + io::Read> {
input: &'a mut R,
len: u32,
}
lazy_block_impl!(LazySeekTable);
/// An unparsed Vorbis comment (also called FLAC tags) block.
///
/// This struct must be consumed in one of two ways:
///
/// * Call `discard()` to skip over the block.
/// * Call `get()` to read and parse the Vorbis comments.
///
/// **Dropping this struct without calling either will panic.**
#[must_use = "Discard using discard() or consume with get()."]
pub struct LazyVorbisComment<'a, R: 'a + io::Read> {
input: &'a mut R,
len: u32,
}
lazy_block_impl!(LazyVorbisComment);
impl <'a, R: 'a + io::Read> LazyVorbisComment<'a, R> {
/// Read and parse the Vorbis comment block.
pub fn get(mut self) -> Result<VorbisComment> {
// Set len to zero before reading to indicate that we consumed the data,
// dropping does not panic, also if reading the vorbis comment block
// returns an error.
let len = self.len;
self.len = 0;
read_vorbis_comment_block(self.input, len)
}
}
/// An unparsed CUE sheet block.
///
/// This struct must be consumed in one of two ways:
///
/// * Call `discard()` to skip over the block.
/// * Call `get()` to read and parse the CUE sheet. (Not implemented yet.)
///
/// **Dropping this struct without calling either will panic.**
#[must_use = "Discard using discard() or consume with get()."]
pub struct LazyCueSheet<'a, R: 'a + io::Read> {
input: &'a mut R,
len: u32,
}
lazy_block_impl!(LazyCueSheet);
/// An unparsed picture block.
///
/// This struct must be consumed in one of two ways:
///
/// * Call `discard()` to skip over the block.
/// * Call `get()` to read and parse the picture metadata, and to expose the
/// inner picture data.
///
/// **Dropping this struct without calling either will panic.**
#[must_use = "Discard using discard() or consume with get()."]
pub struct LazyPicture<'a, R: 'a + io::Read> {
input: &'a mut R,
len: u32,
}
lazy_block_impl!(LazyPicture);
#[inline]
fn read_metadata_block_header<R: io::Read>(input: &mut R) -> Result<MetadataBlockHeader> {
let byte = try!(input.read_u8());
// The first bit specifies whether this is the last block, the next 7 bits
// specify the type of the metadata block to follow.
let is_last = (byte >> 7) == 1;
let block_type = byte & 0b0111_1111;
// The length field is 24 bits, or 3 bytes.
let length = try!(input.read_be_u24());
let header = MetadataBlockHeader {
is_last: is_last,
block_type: block_type,
length: length,
};
Ok(header)
}
/// Read a single metadata block header and body from the input.
///
/// When reading a regular flac stream, there is no need to use this function
/// directly; constructing a `FlacReader` will read the header and its metadata
/// blocks.
///
/// When a flac stream is embedded in a container format, this function can be
/// used to decode a single metadata block. For instance, the Ogg format embeds
/// metadata blocks including their header verbatim in packets. This function
/// can be used to decode that raw data.
#[inline]
pub fn read_metadata_block_with_header<'a, R>(input: &'a mut R) -> Result<MetadataBlock<'a, R>>
where R: 'a + io::Read {
let header = try!(read_metadata_block_header(input));
read_metadata_block(input, header.block_type, header.length)
}
/// Read a single metadata block of the given type and length from the input.
///
/// When reading a regular flac stream, there is no need to use this function
/// directly; constructing a `FlacReader` will read the header and its metadata
/// blocks.
///
/// When a flac stream is embedded in a container format, this function can be
/// used to decode a single metadata block. For instance, the MP4 format sports
/// a “FLAC Specific Box” which contains the block type and the raw data. This
/// function can be used to decode that raw data.
#[inline]
pub fn read_metadata_block<'a, R>(input: &'a mut R,
block_type: u8,
length: u32)
-> Result<MetadataBlock<'a, R>>
where R: 'a + io::Read {
match block_type {
0 => {
// The streaminfo block has a fixed size of 34 bytes.
if length == 34 {
let streaminfo = try!(read_streaminfo_block(input));
Ok(MetadataBlock::StreamInfo(streaminfo))
} else {
fmt_err("invalid streaminfo metadata block length")
}
}
1 => {
try!(input.skip(length));
Ok(MetadataBlock::Padding(length))
}
2 => {
let application_block = try!(read_application_block(input, length));
Ok(MetadataBlock::Application(application_block))
}
3 => {
let lazy_seek_table = LazySeekTable {
input: input,
len: length
};
Ok(MetadataBlock::SeekTable(lazy_seek_table))
}
4 => {
let lazy_vorbis_comment = LazyVorbisComment {
input: input,
len: length,
};
Ok(MetadataBlock::VorbisComment(lazy_vorbis_comment))
}
5 => {
let lazy_cue_sheet = LazyCueSheet {
input: input,
len: length,
};
Ok(MetadataBlock::CueSheet(lazy_cue_sheet))
}
6 => {
let lazy_picture = LazyPicture {
input: input,
len: length,
};
Ok(MetadataBlock::Picture(lazy_picture))
}
127 => {
// This code is invalid to avoid confusion with a frame sync code.
fmt_err("invalid metadata block type")
}
_ => {
// Any other block type is 'reserved' at the moment of writing.
// TODO: Add test to ensure that after a reserved block, the next
// block can be read properly.
try!(input.skip(length));
fmt_err("invalid metadata block, encountered reserved block type")
}
}
}
fn read_streaminfo_block<R: io::Read>(input: &mut R) -> Result<StreamInfo> {
let min_block_size = try!(input.read_be_u16());
let max_block_size = try!(input.read_be_u16());
// The frame size fields are 24 bits, or 3 bytes.
let min_frame_size = try!(input.read_be_u24());
let max_frame_size = try!(input.read_be_u24());
// Next up are 20 bits that determine the sample rate.
let sample_rate_msb = try!(input.read_be_u16());
let sample_rate_lsb = try!(input.read_u8());
// Stitch together the value from the first 16 bits,
// and then the 4 most significant bits of the next byte.
let sample_rate = (sample_rate_msb as u32) << 4 | (sample_rate_lsb as u32) >> 4;
// Next three bits are the number of channels - 1. Mask them out and add 1.
let n_channels_bps = sample_rate_lsb;
let n_channels = ((n_channels_bps >> 1) & 0b0000_0111) + 1;
// The final bit is the most significant of bits per sample - 1. Bits per
// sample - 1 is 5 bits in total.
let bps_msb = n_channels_bps & 1;
let bps_lsb_n_samples = try!(input.read_u8());
// Stitch together these values, add 1 because # - 1 is stored.
let bits_per_sample = (bps_msb << 4 | (bps_lsb_n_samples >> 4)) + 1;
// Number of samples in 36 bits, we have 4 already, 32 to go.
let n_samples_msb = bps_lsb_n_samples & 0b0000_1111;
let n_samples_lsb = try!(input.read_be_u32());
let n_samples = (n_samples_msb as u64) << 32 | n_samples_lsb as u64;
// Next are 128 bits (16 bytes) of MD5 signature.
let mut md5sum = [0u8; 16];
try!(input.read_exact(&mut md5sum));
// Lower bounds can never be larger than upper bounds. Note that 0 indicates
// unknown for the frame size. Also, the block size must be at least 16.
if min_block_size > max_block_size {
return fmt_err("inconsistent bounds, min block size > max block size");
}
if min_block_size < 16 {
return fmt_err("invalid block size, must be at least 16");
}
if min_frame_size > max_frame_size && max_frame_size != 0 {
return fmt_err("inconsistent bounds, min frame size > max frame size");
}
// A sample rate of 0 is invalid, and the maximum sample rate is limited by
// the structure of the frame headers to 655350 Hz.
if sample_rate == 0 || sample_rate > 655350 {
return fmt_err("invalid sample rate");
}
let stream_info = StreamInfo {
min_block_size: min_block_size,
max_block_size: max_block_size,
min_frame_size: if min_frame_size == 0 {
None
} else {
Some(min_frame_size)
},
max_frame_size: if max_frame_size == 0 {
None
} else {
Some(max_frame_size)
},
sample_rate: sample_rate,
channels: n_channels as u32,
bits_per_sample: bits_per_sample as u32,
samples: if n_samples == 0 {
None
} else {
Some(n_samples)
},
md5sum: md5sum,
};
Ok(stream_info)
}
fn read_vorbis_comment_block<R: io::Read>(input: &mut R, length: u32) -> Result<VorbisComment> {
if length < 8 {
// We expect at a minimum a 32-bit vendor string length, and a 32-bit
// comment count.
return fmt_err("Vorbis comment block is too short")
}
// Fail if the length of the Vorbis comment block is larger than 1 MiB. This
// block is full of length-prefixed strings for which we allocate memory up
// front. If there were no limit on these, a maliciously crafted file could
// cause OOM by claiming to contain large strings. But at least the strings
// cannot be longer than the size of the Vorbis comment block, and by
// limiting the size of that block, we can mitigate such DoS attacks.
//
// The typical size of a the Vorbis comment block is 1 KiB; on a corpus of
// real-world flac files, the 0.05 and 0.95 quantiles were 792 and 1257
// bytes respectively, with even the 0.99 quantile below 2 KiB. The only
// reason for having a large Vorbis comment block is when cover art is
// incorrectly embedded there, but the Vorbis comment block is not the right
// place for that anyway.
if length > 10 * 1024 * 1024 {
let msg = "Vorbis comment blocks larger than 10 MiB are not supported";
return Err(Error::Unsupported(msg))
}
// The Vorbis comment block starts with a length-prefixed "vendor string".
// It cannot be larger than the block length - 8, because there are the
// 32-bit vendor string length, and comment count.
let vendor_len = try!(input.read_le_u32());
if vendor_len > length - 8 { return fmt_err("vendor string too long") }
let mut vendor_bytes = Vec::with_capacity(vendor_len as usize);
// We can safely set the length of the vector here; the uninitialized memory
// is not exposed. If `read_exact` succeeds, it will have overwritten all
// bytes. If not, an error is returned and the memory is never exposed.
unsafe { vendor_bytes.set_len(vendor_len as usize); }
try!(input.read_exact(&mut vendor_bytes));
let vendor = try!(String::from_utf8(vendor_bytes));
// Next up is the number of comments. Because every comment is at least 4
// bytes to indicate its length, there cannot be more comments than the
// length of the block divided by 4. This is only an upper bound to ensure
// that we don't allocate a big vector, to protect against DoS attacks.
let comments_len = try!(input.read_le_u32());
if comments_len >= length / 4 {
return fmt_err("too many entries for Vorbis comment block")
}
let mut comments = Vec::with_capacity(comments_len as usize);
let mut bytes_left = length - 8 - vendor_len;
// For every comment, there is a length-prefixed string of the form
// "NAME=value".
while bytes_left >= 4 {
let comment_len = try!(input.read_le_u32());
bytes_left -= 4;
if comment_len > bytes_left {
return fmt_err("Vorbis comment too long for Vorbis comment block")
}
// For the same reason as above, setting the length is safe here.
let mut comment_bytes = Vec::with_capacity(comment_len as usize);
unsafe { comment_bytes.set_len(comment_len as usize); }
try!(input.read_exact(&mut comment_bytes));
bytes_left -= comment_len;
if let Some(sep_index) = comment_bytes.iter().position(|&x| x == b'=') {
{
let name_bytes = &comment_bytes[..sep_index];
// According to the Vorbis spec, the field name may consist of ascii
// bytes 0x20 through 0x7d, 0x3d (`=`) excluded. Verifying this has
// the advantage that if the check passes, the result is valid
// UTF-8, so the conversion to string will not fail.
if name_bytes.iter().any(|&x| x < 0x20 || x > 0x7d) {
return fmt_err("Vorbis comment field name contains invalid byte")
}
}
let comment = try!(String::from_utf8(comment_bytes));
comments.push((comment, sep_index));
} else {
return fmt_err("Vorbis comment does not contain '='")
}
}
if comments.len() != comments_len as usize {
return fmt_err("Vorbis comment block contains wrong number of entries")
}
let vorbis_comment = VorbisComment {
vendor: vendor,
comments: comments,
};
Ok(vorbis_comment)
}
fn read_padding_block<R: io::Read>(input: &mut R, length: u32) -> Result<()> {
// The specification dictates that all bits of the padding block must be 0.
// However, the reference implementation does not issue an error when this
// is not the case, and frankly, when you are going to skip over these
// bytes and do nothing with them whatsoever, why waste all those CPU
// cycles checking that the padding is valid?
Ok(try!(input.skip(length)))
}
fn read_application_block<'a, R: 'a + io::Read>(input: &'a mut R, length: u32) -> Result<ApplicationBlock<'a, R>> {
if length < 4 {
return fmt_err("application block length must be at least 4 bytes")
}
// Reject large application blocks to avoid memory-based denial-
// of-service attacks. See also the more elaborate motivation in
// `read_vorbis_comment_block()`.
// TODO: Now that we expose an EmbeddedReader, this is no longer a concern
// for Claxon. However, it might still be a concern for users who have not
// considered the issue of an incorrect length field in the header. Should
// we enfore this in the API somehow?
if length > 10 * 1024 * 1024 {
let msg = "application blocks larger than 10 MiB are not supported";
return Err(Error::Unsupported(msg))
}
let id = try!(input.read_be_u32());
let application = ApplicationBlock {
id: id,
reader: EmbeddedReader {
input: input,
cursor: 0,
// The application id took 4 bytes, the remainder is data.
len: length - 4,
},
};
Ok(application)
}
fn read_picture_block<R: io::Read>(input: &mut R, length: u32) -> Result<Picture<R>> {
if length < 32 {
// We expect at a minimum 8 all of the 32-bit fields.
return fmt_err("picture block is too short")
}
let picture_type = try!(input.read_be_u32());
let kind = match picture_type {
0 => PictureKind::Other,
1 => PictureKind::FileIcon32x32,
2 => PictureKind::FileIconOther,
3 => PictureKind::FrontCover,
4 => PictureKind::BackCover,
5 => PictureKind::LeafletPage,
6 => PictureKind::Media,
7 => PictureKind::LeadArtist,
8 => PictureKind::Artist,
9 => PictureKind::Conductor,
10 => PictureKind::Band,
11 => PictureKind::Composer,
12 => PictureKind::Lyricist,
13 => PictureKind::RecordingLocation,
14 => PictureKind::DuringRecording,
15 => PictureKind::DuringPerformance,
16 => PictureKind::VideoScreenCapture,
17 => PictureKind::BrightColoredFish,
18 => PictureKind::Illustration,
19 => PictureKind::BandLogotype,
20 => PictureKind::PublisherLogotype,
// Picture types up to 20 are valid, others are reserved.
_ => return fmt_err("invalid picture type"),
};
let mime_len = try!(input.read_be_u32());
// The mime type string must fit within the picture block. Also put a limit
// on the length, to ensure we don't allocate large strings, in order to
// prevent denial of service attacks.
if mime_len > length - 32 { return fmt_err("picture MIME type string too long") }
if mime_len > 256 {
let msg = "picture MIME types larger than 256 bytes are not supported";
return Err(Error::Unsupported(msg))
}
let mut mime_bytes = Vec::with_capacity(mime_len as usize);
// We can safely set the length of the vector here; the uninitialized memory
// is not exposed. If `read_exact` succeeds, it will have overwritten all
// bytes. If not, an error is returned and the memory is never exposed.
unsafe { mime_bytes.set_len(mime_len as usize); }
try!(input.read_exact(&mut mime_bytes));
// According to the spec, the MIME type string must consist of printable
// ASCII characters in the range 0x20-0x7e; validate that. This also means
// that we don't have to check for valid UTF-8 to turn it into a string.
if mime_bytes.iter().any(|&b| b < 0x20 || b > 0x7e) {
return fmt_err("picture mime type string contains invalid characters")
}
let mime_type = unsafe { String::from_utf8_unchecked(mime_bytes) };
let description_len = try!(input.read_be_u32());
// The description must fit within the picture block. Also put a limit
// on the length, to ensure we don't allocate large strings, in order to
// prevent denial of service attacks.
if description_len > length - 32 { return fmt_err("picture description too long") }
if description_len > 256 {
let msg = "picture descriptions larger than 256 bytes are not supported";
return Err(Error::Unsupported(msg))
}
let mut description_bytes = Vec::with_capacity(description_len as usize);
// We can safely set the length of the vector here; the uninitialized memory
// is not exposed. If `read_exact` succeeds, it will have overwritten all
// bytes. If not, an error is returned and the memory is never exposed.
unsafe { description_bytes.set_len(description_len as usize); }
try!(input.read_exact(&mut description_bytes));
let description = try!(String::from_utf8(description_bytes));
// Next are a few fields with pixel metadata. It seems a bit weird to me
// that FLAC stores the bits per pixel, and especially the number of indexed
// colors. Perhaps the idea was to allow choosing which picture to decode,
// but peeking the image data itself would be better anyway. I have no use
// case for these fields, so they are not exposed, to keep the API cleaner,
// and to save a few bytes of memory.
let width = try!(input.read_be_u32());
let height = try!(input.read_be_u32());
let _bits_per_pixel = try!(input.read_be_u32());
let _num_indexed_colors = try!(input.read_be_u32());
let data_len = try!(input.read_be_u32());
// The length field is redundant, because we already have the size of the
// block. The picture should fill up the remainder of the block.
if data_len > length - 32 - mime_len - description_len {
return fmt_err("picture data does not fit the picture block")
}
// The largers picture in my personal collection is 13_318_155 bytes; the
// 95th percentile is 3_672_912 bytes. Having larger cover art embedded kind
// of defeats the purpose, as the cover art would be larger than the audio
// data for the typical track. Hence a 100 MiB limit should be reasonable.
if data_len > 100 * 1024 * 1024 {
let msg = "pictures larger than 100 MiB are not supported";
return Err(Error::Unsupported(msg))
}
// TODO: Expose reader.