//go:build !disable_format_alac // +build !disable_format_alac package alac import ( "bytes" "errors" "github.com/edgeware/mp4ff/mp4" "io" ) type mp4Decoder struct { mp4 *mp4.File reader io.ReadSeekCloser trackId uint32 cookie []byte currentSegment int currentFragment int currentSample uint32 } func tryDecodeMP4(r io.ReadSeekCloser) (*mp4Decoder, error) { //TODO: mp4.DecModeLazyMdat errors in segmented files parsedMp4, err := mp4.DecodeFile(r, mp4.WithDecodeMode(mp4.DecModeNormal)) if err != nil { return nil, err } var trackId uint32 var magicCookie []byte for _, trak := range parsedMp4.Moov.Traks { if box, err := trak.Mdia.Minf.Stbl.Stsd.GetSampleDescription(0); err == nil && box.Type() == "alac" { trackId = trak.Tkhd.TrackID buf := new(bytes.Buffer) box.Encode(buf) boxBytes := buf.Bytes() boxOffset := 36 + 12 magicCookie = boxBytes[boxOffset:] break } } if magicCookie == nil { return nil, errors.New("could not find track entry") } return &mp4Decoder{ reader: r, mp4: parsedMp4, cookie: magicCookie, trackId: trackId, currentSample: 1, }, nil } func (d *mp4Decoder) Read() (samples [][]byte) { if d.mp4.IsFragmented() { if d.currentSegment >= len(d.mp4.Segments) { //EOF return nil } segment := d.mp4.Segments[d.currentSegment] if d.currentFragment >= len(segment.Fragments) { d.currentSegment++ d.currentFragment = 0 return d.Read() } frag := segment.Fragments[d.currentFragment] fullSamples, err := frag.GetFullSamples(&mp4.TrexBox{ TrackID: d.trackId, }) if err != nil { return nil } for _, sample := range fullSamples { samples = append(samples, sample.Data) } d.currentFragment++ return } else { for _, trak := range d.mp4.Moov.Traks { if trak.Tkhd.TrackID == d.trackId { buf := new(bytes.Buffer) if err := d.mp4.CopySampleData(buf, d.reader, trak, d.currentSample, d.currentSample); err != nil { return nil } d.currentSample++ return [][]byte{buf.Bytes()} } } return } }