Skip to content

Commit

Permalink
ogg: Add flac support
Browse files Browse the repository at this point in the history
Refactor flac_streaminfo and flac_metadata from flac_metadatablocks
Some deocde bitbuf api cleanup
  • Loading branch information
wader committed Oct 29, 2021
1 parent 97f7317 commit 88eade9
Show file tree
Hide file tree
Showing 28 changed files with 1,445 additions and 950 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ For more usage details see [usage.md](doc/usage.md).

[./formats_list.jq]: sh-start

aac_frame, adts, adts_frame, apev2, av1_ccr, av1_frame, av1_obu, avc_annexb, avc_au, avc_dcr, avc_nalu, avc_pps, avc_sei, avc_sps, bzip2, dns, elf, exif, flac, flac_frame, flac_metadatablocks, flac_picture, gif, gzip, hevc_annexb, hevc_au, hevc_dcr, hevc_nalu, icc_profile, id3v1, id3v11, id3v2, jpeg, json, matroska, mp3, mp3_frame, mp4, mpeg_asc, mpeg_es, mpeg_pes, mpeg_pes_packet, mpeg_spu, mpeg_ts, ogg, ogg_page, opus_packet, png, protobuf, protobuf_widevine, pssh_playready, raw, tar, tiff, vorbis_comment, vorbis_packet, vp8_frame, vp9_cfm, vp9_frame, vpx_ccr, wav, webp, xing
aac_frame, adts, adts_frame, apev2, av1_ccr, av1_frame, av1_obu, avc_annexb, avc_au, avc_dcr, avc_nalu, avc_pps, avc_sei, avc_sps, bzip2, dns, elf, exif, flac, flac_frame, flac_metadatablock, flac_metadatablocks, flac_picture, flac_streaminfo, gif, gzip, hevc_annexb, hevc_au, hevc_dcr, hevc_nalu, icc_profile, id3v1, id3v11, id3v2, jpeg, json, matroska, mp3, mp3_frame, mp4, mpeg_asc, mpeg_es, mpeg_pes, mpeg_pes_packet, mpeg_spu, mpeg_ts, ogg, ogg_page, opus_packet, png, protobuf, protobuf_widevine, pssh_playready, raw, tar, tiff, vorbis_comment, vorbis_packet, vp8_frame, vp9_cfm, vp9_frame, vpx_ccr, wav, webp, xing

[#]: sh-end

Expand Down
6 changes: 4 additions & 2 deletions doc/formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
|`exif` |Exchangeable&nbsp;Image&nbsp;File&nbsp;Format |<sub></sub>|
|`flac` |Free&nbsp;Lossless&nbsp;Audio&nbsp;Codec&nbsp;file |<sub>`flac_metadatablocks` `flac_frame`</sub>|
|`flac_frame` |FLAC&nbsp;frame |<sub></sub>|
|`flac_metadatablocks` |FLAC&nbsp;metadatablocks |<sub>`flac_picture` `vorbis_comment`</sub>|
|`flac_metadatablock` |FLAC&nbsp;metadatablock |<sub>`flac_streaminfo` `flac_picture` `vorbis_comment`</sub>|
|`flac_metadatablocks` |FLAC&nbsp;metadatablocks |<sub>`flac_metadatablock`</sub>|
|`flac_picture` |FLAC&nbsp;metadatablock&nbsp;picture |<sub>`image`</sub>|
|`flac_streaminfo` |FLAC&nbsp;streaminfo |<sub></sub>|
|`gif` |Graphics&nbsp;Interchange&nbsp;Format |<sub></sub>|
|`gzip` |gzip&nbsp;compression |<sub>`probe`</sub>|
|`hevc_annexb` |H.265/HEVC&nbsp;Annex&nbsp;B |<sub>`hevc_nalu`</sub>|
Expand All @@ -48,7 +50,7 @@
|`mpeg_pes_packet` |MPEG&nbsp;Packetized&nbsp;elementary&nbsp;stream&nbsp;packet |<sub></sub>|
|`mpeg_spu` |Sub&nbsp;Picture&nbsp;Unit&nbsp;(DVD&nbsp;subtitle) |<sub></sub>|
|`mpeg_ts` |MPEG&nbsp;Transport&nbsp;Stream |<sub></sub>|
|`ogg` |OGG&nbsp;file |<sub>`ogg_page` `vorbis_packet` `opus_packet`</sub>|
|`ogg` |OGG&nbsp;file |<sub>`ogg_page` `vorbis_packet` `vorbis_comment` `opus_packet` `flac_metadatablock` `flac_frame`</sub>|
|`ogg_page` |OGG&nbsp;page |<sub></sub>|
|`opus_packet` |Opus&nbsp;packet |<sub>`vorbis_comment`</sub>|
|`png` |Portable&nbsp;Network&nbsp;Graphics&nbsp;file |<sub>`icc_profile` `exif`</sub>|
Expand Down
1,427 changes: 740 additions & 687 deletions doc/formats.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion format/flac/flac.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func init() {
func flacDecode(d *decode.D, in interface{}) interface{} {
d.FieldValidateUTF8("magic", "fLaC")

var streamInfo format.FlacMetadatablockStreamInfo
var streamInfo format.FlacStreamInfo
var flacFrameIn format.FlacFrameIn
var framesNDecodedSamples uint64
var streamTotalSamples uint64
Expand Down
2 changes: 1 addition & 1 deletion format/flac/flac_frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func utf8Uint(d *decode.D) uint64 {

// in argument is an optional FlacFrameIn struct with stream info
func frameDecode(d *decode.D, in interface{}) interface{} {
var inStreamInfo *format.FlacMetadatablockStreamInfo
var inStreamInfo *format.FlacStreamInfo
ffi, ok := in.(format.FlacFrameIn)
if ok {
inStreamInfo = &ffi.StreamInfo
Expand Down
108 changes: 108 additions & 0 deletions format/flac/flac_metadatablock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package flac

// TODO: 24 bit picture length truncate warning
// TODO: Cuesheet

import (
"fmt"

"github.com/wader/fq/format"
"github.com/wader/fq/format/registry"
"github.com/wader/fq/pkg/decode"
)

var flacStreaminfoFormat []*decode.Format
var flacPicture []*decode.Format
var vorbisCommentFormat []*decode.Format

func init() {
registry.MustRegister(&decode.Format{
Name: format.FLAC_METADATABLOCK,
Description: "FLAC metadatablock",
DecodeFn: metadatablockDecode,
Dependencies: []decode.Dependency{
{Names: []string{format.FLAC_STREAMINFO}, Formats: &flacStreaminfoFormat},
{Names: []string{format.FLAC_PICTURE}, Formats: &flacPicture},
{Names: []string{format.VORBIS_COMMENT}, Formats: &vorbisCommentFormat},
},
})
}

const (
MetadataBlockStreaminfo = 0
MetadataBlockPadding = 1
MetadataBlockApplication = 2
MetadataBlockSeektable = 3
MetadataBlockVorbisComment = 4
MetadataBlockCuesheet = 5
MetadataBlockPicture = 6
)

var metadataBlockNames = map[uint]string{
MetadataBlockStreaminfo: "Streaminfo",
MetadataBlockPadding: "Padding",
MetadataBlockApplication: "Application",
MetadataBlockSeektable: "Seektable",
MetadataBlockVorbisComment: "Vorbis comment",
MetadataBlockCuesheet: "Cuesheet",
MetadataBlockPicture: "Picture",
}

func metadatablockDecode(d *decode.D, in interface{}) interface{} {
var hasStreamInfo bool
var streamInfo format.FlacStreamInfo

isLastBlock := d.FieldBool("last_block")
typ := d.FieldUFn("type", func() (uint64, decode.DisplayFormat, string) {
t := d.U7()
name := "Unknown"
if s, ok := metadataBlockNames[uint(t)]; ok {
name = s
}
return t, decode.NumberDecimal, name
})
length := d.FieldU24("length")

switch typ {
case MetadataBlockStreaminfo:
flacStreaminfoOut, ok := d.Format(flacStreaminfoFormat, nil).(format.FlacStreaminfoOut)
if !ok {
d.Invalid(fmt.Sprintf("expected FlacStreaminfoOut, got %#+v", flacStreaminfoOut))
}
hasStreamInfo = true
streamInfo = flacStreaminfoOut.StreamInfo
case MetadataBlockVorbisComment:
d.FieldFormatLen("comment", int64(length*8), vorbisCommentFormat, nil)
case MetadataBlockPicture:
d.FieldFormatLen("picture", int64(length*8), flacPicture, nil)
case MetadataBlockSeektable:
seektableCount := length / 18
d.FieldArrayFn("seekpoints", func(d *decode.D) {
for i := uint64(0); i < seektableCount; i++ {
d.FieldStructFn("seekpoint", func(d *decode.D) {
d.FieldUFn("sample_number", func() (uint64, decode.DisplayFormat, string) {
n := d.U64()
d := ""
if n == 0xffffffffffffffff {
d = "Placeholder"
}
return n, decode.NumberDecimal, d
})
d.FieldU64("offset")
d.FieldU16("number_of_samples")
})
}
})
case MetadataBlockApplication:
d.FieldUTF8("id", 4)
d.FieldBitBufLen("data", int64((length-4)*8))
default:
d.FieldBitBufLen("data", int64(length*8))
}

return format.FlacMetadatablockOut{
IsLastBlock: isLastBlock,
HasStreamInfo: hasStreamInfo,
StreamInfo: streamInfo,
}
}
112 changes: 18 additions & 94 deletions format/flac/flac_metadatablocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,120 +4,44 @@ package flac
// TODO: Cuesheet

import (
"fmt"

"github.com/wader/fq/format"
"github.com/wader/fq/format/registry"
"github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/ranges"
)

var flacPicture []*decode.Format
var vorbisCommentFormat []*decode.Format
var flacMetadatablockForamt []*decode.Format

func init() {
registry.MustRegister(&decode.Format{
Name: format.FLAC_METADATABLOCKS,
Description: "FLAC metadatablocks",
DecodeFn: metadatablocskDecode,
DecodeFn: metadatablocksDecode,
RootV: decode.Array{},
RootName: "metadatablocks",
Dependencies: []decode.Dependency{
{Names: []string{format.FLAC_PICTURE}, Formats: &flacPicture},
{Names: []string{format.VORBIS_COMMENT}, Formats: &vorbisCommentFormat},
{Names: []string{format.FLAC_METADATABLOCK}, Formats: &flacMetadatablockForamt},
},
})
}

const (
MetadataBlockStreaminfo = 0
MetadataBlockPadding = 1
MetadataBlockApplication = 2
MetadataBlockSeektable = 3
MetadataBlockVorbisComment = 4
MetadataBlockCuesheet = 5
MetadataBlockPicture = 6
)

var metadataBlockNames = map[uint]string{
MetadataBlockStreaminfo: "Streaminfo",
MetadataBlockPadding: "Padding",
MetadataBlockApplication: "Application",
MetadataBlockSeektable: "Seektable",
MetadataBlockVorbisComment: "Vorbis comment",
MetadataBlockCuesheet: "Cuesheet",
MetadataBlockPicture: "Picture",
}

func metadatablocskDecode(d *decode.D, in interface{}) interface{} {
mb := format.FlacMetadatablocksOut{}
func metadatablocksDecode(d *decode.D, in interface{}) interface{} {
flacMetadatablocksOut := format.FlacMetadatablocksOut{}

isLastBlock := false
for !isLastBlock {
d.FieldStructFn("metadatablock", func(d *decode.D) {
isLastBlock = d.FieldBool("last_block")
typ := d.FieldUFn("type", func() (uint64, decode.DisplayFormat, string) {
t := d.U7()
name := "Unknown"
if s, ok := metadataBlockNames[uint(t)]; ok {
name = s
}
return t, decode.NumberDecimal, name
})
length := d.FieldU24("length")

switch typ {
case MetadataBlockStreaminfo:
d.FieldU16("minimum_block_size")
d.FieldU16("maximum_block_size")
d.FieldU24("minimum_frame_size")
d.FieldU24("maximum_frame_size")
sampleRate := d.FieldU("sample_rate", 20)
// <3> (number of channels)-1. FLAC supports from 1 to 8 channels
d.FieldUFn("channels", func() (uint64, decode.DisplayFormat, string) { return d.U3() + 1, decode.NumberDecimal, "" })
// <5> (bits per sample)-1. FLAC supports from 4 to 32 bits per sample. Currently the reference encoder and decoders only support up to 24 bits per sample.
bitPerSample := d.FieldUFn("bits_per_sample", func() (uint64, decode.DisplayFormat, string) {
return d.U5() + 1, decode.NumberDecimal, ""
})
totalSamplesInStream := d.FieldU("total_samples_in_stream", 36)
md5Range := ranges.Range{Start: d.Pos(), Len: 16 * 8}
d.FieldBitBufLen("md5", 16*8)

mb.StreamInfo = format.FlacMetadatablockStreamInfo{
SampleRate: sampleRate,
BitPerSample: bitPerSample,
TotalSamplesInStream: totalSamplesInStream,
MD5Range: md5Range,
}
mb.HasStreamInfo = true
case MetadataBlockVorbisComment:
d.FieldFormatLen("comment", int64(length*8), vorbisCommentFormat, nil)
case MetadataBlockPicture:
d.FieldFormatLen("picture", int64(length*8), flacPicture, nil)
case MetadataBlockSeektable:
seektableCount := length / 18
d.FieldArrayFn("seekpoints", func(d *decode.D) {
for i := uint64(0); i < seektableCount; i++ {
d.FieldStructFn("seekpoint", func(d *decode.D) {
d.FieldUFn("sample_number", func() (uint64, decode.DisplayFormat, string) {
n := d.U64()
d := ""
if n == 0xffffffffffffffff {
d = "Placeholder"
}
return n, decode.NumberDecimal, d
})
d.FieldU64("offset")
d.FieldU16("number_of_samples")
})
}
})
case MetadataBlockApplication:
d.FieldUTF8("id", 4)
d.FieldBitBufLen("data", int64((length-4)*8))
default:
d.FieldBitBufLen("data", int64(length*8))
}
})
_, flacMetadatablockOutAny := d.FieldFormat("metadatablock", flacMetadatablockForamt, nil)
flacMetadatablockOut, ok := flacMetadatablockOutAny.(format.FlacMetadatablockOut)
if !ok {
d.Invalid(fmt.Sprintf("expected FlacMetadatablocksOut, got %#+v", flacMetadatablockOut))
}
isLastBlock = flacMetadatablockOut.IsLastBlock
if flacMetadatablockOut.HasStreamInfo {
flacMetadatablocksOut.HasStreamInfo = true
flacMetadatablocksOut.StreamInfo = flacMetadatablockOut.StreamInfo
}
}

return mb
return flacMetadatablocksOut
}
39 changes: 39 additions & 0 deletions format/flac/flac_streaminfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package flac

import (
"github.com/wader/fq/format"
"github.com/wader/fq/format/registry"
"github.com/wader/fq/pkg/decode"
)

func init() {
registry.MustRegister(&decode.Format{
Name: format.FLAC_STREAMINFO,
Description: "FLAC streaminfo",
DecodeFn: streaminfoDecode,
})
}

func streaminfoDecode(d *decode.D, in interface{}) interface{} {
d.FieldU16("minimum_block_size")
d.FieldU16("maximum_block_size")
d.FieldU24("minimum_frame_size")
d.FieldU24("maximum_frame_size")
sampleRate := d.FieldU("sample_rate", 20)
// <3> (number of channels)-1. FLAC supports from 1 to 8 channels
d.FieldUFn("channels", func() (uint64, decode.DisplayFormat, string) { return d.U3() + 1, decode.NumberDecimal, "" })
// <5> (bits per sample)-1. FLAC supports from 4 to 32 bits per sample. Currently the reference encoder and decoders only support up to 24 bits per sample.
bitPerSample := d.FieldUFn("bits_per_sample", func() (uint64, decode.DisplayFormat, string) {
return d.U5() + 1, decode.NumberDecimal, ""
})
totalSamplesInStream := d.FieldU("total_samples_in_stream", 36)
d.FieldBitBufLen("md5", 16*8)

return format.FlacStreaminfoOut{
StreamInfo: format.FlacStreamInfo{
SampleRate: sampleRate,
BitPerSample: bitPerSample,
TotalSamplesInStream: totalSamplesInStream,
},
}
}
8 changes: 4 additions & 4 deletions format/flac/testdata/mono16.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ $ fq -d flac verbose /mono16.flac
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|.: {} /mono16.flac (flac) 0x0-0x8597.7 (34200)
0x0000|66 4c 61 43 |fLaC | magic: "fLaC" (Correct) 0x0-0x3.7 (4)
| | | metadatablocks: [4] (flac_metadatablocks) 0x4-0x206f.7 (8300)
| | | [0]: metadatablock {} 0x4-0x29.7 (38)
| | | [0]: metadatablock {} (flac_metadatablock) 0x4-0x29.7 (38)
0x0000| 00 | . | last_block: false 0x4-0x4 (0.1)
0x0000| 00 | . | type: Streaminfo (0) 0x4.1-0x4.7 (0.7)
0x0000| 00 00 22 | .." | length: 34 0x5-0x7.7 (3)
Expand All @@ -17,7 +17,7 @@ $ fq -d flac verbose /mono16.flac
0x0010| f0 00 00 56 22 | ...V" | total_samples_in_stream: 22050 0x15.4-0x19.7 (4.4)
0x0010| 29 cf 8e b6 22 e9| )...".| md5: 29cf8eb622e9be01808ecafe817d17a6 0x1a-0x29.7 (16)
0x0020|be 01 80 8e ca fe 81 7d 17 a6 |.......}.. |
| | | [1]: metadatablock {} 0x2a-0x3f.7 (22)
| | | [1]: metadatablock {} (flac_metadatablock) 0x2a-0x3f.7 (22)
0x0020| 03 | . | last_block: false 0x2a-0x2a (0.1)
0x0020| 03 | . | type: Seektable (3) 0x2a.1-0x2a.7 (0.7)
0x0020| 00 00 12 | ... | length: 18 0x2b-0x2d.7 (3)
Expand All @@ -27,7 +27,7 @@ $ fq -d flac verbose /mono16.flac
0x0030|00 00 00 00 00 00 |...... |
0x0030| 00 00 00 00 00 00 00 00 | ........ | offset: 0 0x36-0x3d.7 (8)
0x0030| 10 00| ..| number_of_samples: 4096 0x3e-0x3f.7 (2)
| | | [2]: metadatablock {} 0x40-0x6b.7 (44)
| | | [2]: metadatablock {} (flac_metadatablock) 0x40-0x6b.7 (44)
0x0040|04 |. | last_block: false 0x40-0x40 (0.1)
0x0040|04 |. | type: Vorbis comment (4) 0x40.1-0x40.7 (0.7)
0x0040| 00 00 28 | ..( | length: 40 0x41-0x43.7 (3)
Expand All @@ -38,7 +38,7 @@ $ fq -d flac verbose /mono16.flac
0x0060|32 30 31 39 30 38 30 34 |20190804 |
0x0060| 00 00 00 00 | .... | user_comment_list_length: 0 0x68-0x6b.7 (4)
| | | user_comments: [0] 0x6c-NA (0)
| | | [3]: metadatablock {} 0x6c-0x206f.7 (8196)
| | | [3]: metadatablock {} (flac_metadatablock) 0x6c-0x206f.7 (8196)
0x0060| 81 | . | last_block: true 0x6c-0x6c (0.1)
0x0060| 81 | . | type: Padding (1) 0x6c.1-0x6c.7 (0.7)
0x0060| 00 20 00| . .| length: 8192 0x6d-0x6f.7 (3)
Expand Down

0 comments on commit 88eade9

Please sign in to comment.