summaryrefslogtreecommitdiff
path: root/vendor/github.com/abema/go-mp4/probe.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/abema/go-mp4/probe.go')
-rw-r--r--vendor/github.com/abema/go-mp4/probe.go684
1 files changed, 0 insertions, 684 deletions
diff --git a/vendor/github.com/abema/go-mp4/probe.go b/vendor/github.com/abema/go-mp4/probe.go
deleted file mode 100644
index 2ffaf7aca..000000000
--- a/vendor/github.com/abema/go-mp4/probe.go
+++ /dev/null
@@ -1,684 +0,0 @@
-package mp4
-
-import (
- "bytes"
- "errors"
- "io"
-
- "github.com/abema/go-mp4/internal/bitio"
-)
-
-type ProbeInfo struct {
- MajorBrand [4]byte
- MinorVersion uint32
- CompatibleBrands [][4]byte
- FastStart bool
- Timescale uint32
- Duration uint64
- Tracks Tracks
- Segments Segments
-}
-
-// Deprecated: replace with ProbeInfo
-type FraProbeInfo = ProbeInfo
-
-type Tracks []*Track
-
-// Deprecated: replace with Track
-type TrackInfo = Track
-
-type Track struct {
- TrackID uint32
- Timescale uint32
- Duration uint64
- Codec Codec
- Encrypted bool
- EditList EditList
- Samples Samples
- Chunks Chunks
- AVC *AVCDecConfigInfo
- MP4A *MP4AInfo
-}
-
-type Codec int
-
-const (
- CodecUnknown Codec = iota
- CodecAVC1
- CodecMP4A
-)
-
-type EditList []*EditListEntry
-
-type EditListEntry struct {
- MediaTime int64
- SegmentDuration uint64
-}
-
-type Samples []*Sample
-
-type Sample struct {
- Size uint32
- TimeDelta uint32
- CompositionTimeOffset int64
-}
-
-type Chunks []*Chunk
-
-type Chunk struct {
- DataOffset uint64
- SamplesPerChunk uint32
-}
-
-type AVCDecConfigInfo struct {
- ConfigurationVersion uint8
- Profile uint8
- ProfileCompatibility uint8
- Level uint8
- LengthSize uint16
- Width uint16
- Height uint16
-}
-
-type MP4AInfo struct {
- OTI uint8
- AudOTI uint8
- ChannelCount uint16
-}
-
-type Segments []*Segment
-
-// Deprecated: replace with Segment
-type SegmentInfo = Segment
-
-type Segment struct {
- TrackID uint32
- MoofOffset uint64
- BaseMediaDecodeTime uint64
- DefaultSampleDuration uint32
- SampleCount uint32
- Duration uint32
- CompositionTimeOffset int32
- Size uint32
-}
-
-// Probe probes MP4 file
-func Probe(r io.ReadSeeker) (*ProbeInfo, error) {
- probeInfo := &ProbeInfo{
- Tracks: make([]*Track, 0, 8),
- Segments: make([]*Segment, 0, 8),
- }
- bis, err := ExtractBoxes(r, nil, []BoxPath{
- {BoxTypeFtyp()},
- {BoxTypeMoov()},
- {BoxTypeMoov(), BoxTypeMvhd()},
- {BoxTypeMoov(), BoxTypeTrak()},
- {BoxTypeMoof()},
- {BoxTypeMdat()},
- })
- if err != nil {
- return nil, err
- }
- var mdatAppeared bool
- for _, bi := range bis {
- switch bi.Type {
- case BoxTypeFtyp():
- var ftyp Ftyp
- if _, err := bi.SeekToPayload(r); err != nil {
- return nil, err
- }
- if _, err := Unmarshal(r, bi.Size-bi.HeaderSize, &ftyp, bi.Context); err != nil {
- return nil, err
- }
- probeInfo.MajorBrand = ftyp.MajorBrand
- probeInfo.MinorVersion = ftyp.MinorVersion
- probeInfo.CompatibleBrands = make([][4]byte, 0, len(ftyp.CompatibleBrands))
- for _, entry := range ftyp.CompatibleBrands {
- probeInfo.CompatibleBrands = append(probeInfo.CompatibleBrands, entry.CompatibleBrand)
- }
- case BoxTypeMoov():
- probeInfo.FastStart = !mdatAppeared
- case BoxTypeMvhd():
- var mvhd Mvhd
- if _, err := bi.SeekToPayload(r); err != nil {
- return nil, err
- }
- if _, err := Unmarshal(r, bi.Size-bi.HeaderSize, &mvhd, bi.Context); err != nil {
- return nil, err
- }
- probeInfo.Timescale = mvhd.Timescale
- if mvhd.GetVersion() == 0 {
- probeInfo.Duration = uint64(mvhd.DurationV0)
- } else {
- probeInfo.Duration = mvhd.DurationV1
- }
- case BoxTypeTrak():
- track, err := probeTrak(r, bi)
- if err != nil {
- return nil, err
- }
- probeInfo.Tracks = append(probeInfo.Tracks, track)
- case BoxTypeMoof():
- segment, err := probeMoof(r, bi)
- if err != nil {
- return nil, err
- }
- probeInfo.Segments = append(probeInfo.Segments, segment)
- case BoxTypeMdat():
- mdatAppeared = true
- }
- }
- return probeInfo, nil
-}
-
-// ProbeFra probes fragmented MP4 file
-// Deprecated: replace with Probe
-func ProbeFra(r io.ReadSeeker) (*FraProbeInfo, error) {
- probeInfo, err := Probe(r)
- return (*FraProbeInfo)(probeInfo), err
-}
-
-func probeTrak(r io.ReadSeeker, bi *BoxInfo) (*Track, error) {
- track := new(Track)
-
- bips, err := ExtractBoxesWithPayload(r, bi, []BoxPath{
- {BoxTypeTkhd()},
- {BoxTypeEdts(), BoxTypeElst()},
- {BoxTypeMdia(), BoxTypeMdhd()},
- {BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsd(), BoxTypeAvc1()},
- {BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsd(), BoxTypeAvc1(), BoxTypeAvcC()},
- {BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsd(), BoxTypeEncv()},
- {BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsd(), BoxTypeEncv(), BoxTypeAvcC()},
- {BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsd(), BoxTypeMp4a()},
- {BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsd(), BoxTypeMp4a(), BoxTypeEsds()},
- {BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsd(), BoxTypeMp4a(), BoxTypeWave(), BoxTypeEsds()},
- {BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsd(), BoxTypeEnca()},
- {BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsd(), BoxTypeEnca(), BoxTypeEsds()},
- {BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStco()},
- {BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeCo64()},
- {BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStts()},
- {BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeCtts()},
- {BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsc()},
- {BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsz()},
- })
- if err != nil {
- return nil, err
- }
- var tkhd *Tkhd
- var elst *Elst
- var mdhd *Mdhd
- var avc1 *VisualSampleEntry
- var avcC *AVCDecoderConfiguration
- var audioSampleEntry *AudioSampleEntry
- var esds *Esds
- var stco *Stco
- var stts *Stts
- var stsc *Stsc
- var ctts *Ctts
- var stsz *Stsz
- var co64 *Co64
- for _, bip := range bips {
- switch bip.Info.Type {
- case BoxTypeTkhd():
- tkhd = bip.Payload.(*Tkhd)
- case BoxTypeElst():
- elst = bip.Payload.(*Elst)
- case BoxTypeMdhd():
- mdhd = bip.Payload.(*Mdhd)
- case BoxTypeAvc1():
- track.Codec = CodecAVC1
- avc1 = bip.Payload.(*VisualSampleEntry)
- case BoxTypeAvcC():
- avcC = bip.Payload.(*AVCDecoderConfiguration)
- case BoxTypeEncv():
- track.Codec = CodecAVC1
- track.Encrypted = true
- case BoxTypeMp4a():
- track.Codec = CodecMP4A
- audioSampleEntry = bip.Payload.(*AudioSampleEntry)
- case BoxTypeEnca():
- track.Codec = CodecMP4A
- track.Encrypted = true
- audioSampleEntry = bip.Payload.(*AudioSampleEntry)
- case BoxTypeEsds():
- esds = bip.Payload.(*Esds)
- case BoxTypeStco():
- stco = bip.Payload.(*Stco)
- case BoxTypeStts():
- stts = bip.Payload.(*Stts)
- case BoxTypeStsc():
- stsc = bip.Payload.(*Stsc)
- case BoxTypeCtts():
- ctts = bip.Payload.(*Ctts)
- case BoxTypeStsz():
- stsz = bip.Payload.(*Stsz)
- case BoxTypeCo64():
- co64 = bip.Payload.(*Co64)
- }
- }
-
- if tkhd == nil {
- return nil, errors.New("tkhd box not found")
- }
- track.TrackID = tkhd.TrackID
-
- if elst != nil {
- editList := make([]*EditListEntry, 0, len(elst.Entries))
- for i := range elst.Entries {
- editList = append(editList, &EditListEntry{
- MediaTime: elst.GetMediaTime(i),
- SegmentDuration: elst.GetSegmentDuration(i),
- })
- }
- track.EditList = editList
- }
-
- if mdhd == nil {
- return nil, errors.New("mdhd box not found")
- }
- track.Timescale = mdhd.Timescale
- track.Duration = mdhd.GetDuration()
-
- if avc1 != nil && avcC != nil {
- track.AVC = &AVCDecConfigInfo{
- ConfigurationVersion: avcC.ConfigurationVersion,
- Profile: avcC.Profile,
- ProfileCompatibility: avcC.ProfileCompatibility,
- Level: avcC.Level,
- LengthSize: uint16(avcC.LengthSizeMinusOne) + 1,
- Width: avc1.Width,
- Height: avc1.Height,
- }
- }
-
- if audioSampleEntry != nil && esds != nil {
- oti, audOTI, err := detectAACProfile(esds)
- if err != nil {
- return nil, err
- }
- track.MP4A = &MP4AInfo{
- OTI: oti,
- AudOTI: audOTI,
- ChannelCount: audioSampleEntry.ChannelCount,
- }
- }
-
- track.Chunks = make([]*Chunk, 0)
- if stco != nil {
- for _, offset := range stco.ChunkOffset {
- track.Chunks = append(track.Chunks, &Chunk{
- DataOffset: uint64(offset),
- })
- }
- } else if co64 != nil {
- for _, offset := range co64.ChunkOffset {
- track.Chunks = append(track.Chunks, &Chunk{
- DataOffset: offset,
- })
- }
- } else {
- return nil, errors.New("stco/co64 box not found")
- }
-
- if stts == nil {
- return nil, errors.New("stts box not found")
- }
- track.Samples = make([]*Sample, 0)
- for _, entry := range stts.Entries {
- for i := uint32(0); i < entry.SampleCount; i++ {
- track.Samples = append(track.Samples, &Sample{
- TimeDelta: entry.SampleDelta,
- })
- }
- }
-
- if stsc == nil {
- return nil, errors.New("stsc box not found")
- }
- for si, entry := range stsc.Entries {
- end := uint32(len(track.Chunks))
- if si != len(stsc.Entries)-1 && stsc.Entries[si+1].FirstChunk-1 < end {
- end = stsc.Entries[si+1].FirstChunk - 1
- }
- for ci := entry.FirstChunk - 1; ci < end; ci++ {
- track.Chunks[ci].SamplesPerChunk = entry.SamplesPerChunk
- }
- }
-
- if ctts != nil {
- var si uint32
- for ci, entry := range ctts.Entries {
- for i := uint32(0); i < entry.SampleCount; i++ {
- if si >= uint32(len(track.Samples)) {
- break
- }
- track.Samples[si].CompositionTimeOffset = ctts.GetSampleOffset(ci)
- si++
- }
- }
- }
-
- if stsz != nil {
- for i := 0; i < len(stsz.EntrySize) && i < len(track.Samples); i++ {
- track.Samples[i].Size = stsz.EntrySize[i]
- }
- }
-
- return track, nil
-}
-
-func detectAACProfile(esds *Esds) (oti, audOTI uint8, err error) {
- configDscr := findDescriptorByTag(esds.Descriptors, DecoderConfigDescrTag)
- if configDscr == nil || configDscr.DecoderConfigDescriptor == nil {
- return 0, 0, nil
- }
- if configDscr.DecoderConfigDescriptor.ObjectTypeIndication != 0x40 {
- return configDscr.DecoderConfigDescriptor.ObjectTypeIndication, 0, nil
- }
-
- specificDscr := findDescriptorByTag(esds.Descriptors, DecSpecificInfoTag)
- if specificDscr == nil {
- return 0, 0, errors.New("DecoderSpecificationInfoDescriptor not found")
- }
-
- r := bitio.NewReader(bytes.NewReader(specificDscr.Data))
- remaining := len(specificDscr.Data) * 8
-
- // audio object type
- audioObjectType, read, err := getAudioObjectType(r)
- if err != nil {
- return 0, 0, err
- }
- remaining -= read
-
- // sampling frequency index
- samplingFrequencyIndex, err := r.ReadBits(4)
- if err != nil {
- return 0, 0, err
- }
- remaining -= 4
- if samplingFrequencyIndex[0] == 0x0f {
- if _, err = r.ReadBits(24); err != nil {
- return 0, 0, err
- }
- remaining -= 24
- }
-
- if audioObjectType == 2 && remaining >= 20 {
- if _, err = r.ReadBits(4); err != nil {
- return 0, 0, err
- }
- remaining -= 4
- syncExtensionType, err := r.ReadBits(11)
- if err != nil {
- return 0, 0, err
- }
- remaining -= 11
- if syncExtensionType[0] == 0x2 && syncExtensionType[1] == 0xb7 {
- extAudioObjectType, _, err := getAudioObjectType(r)
- if err != nil {
- return 0, 0, err
- }
- if extAudioObjectType == 5 || extAudioObjectType == 22 {
- sbr, err := r.ReadBits(1)
- if err != nil {
- return 0, 0, err
- }
- remaining--
- if sbr[0] != 0 {
- if extAudioObjectType == 5 {
- sfi, err := r.ReadBits(4)
- if err != nil {
- return 0, 0, err
- }
- remaining -= 4
- if sfi[0] == 0xf {
- if _, err := r.ReadBits(24); err != nil {
- return 0, 0, err
- }
- remaining -= 24
- }
- if remaining >= 12 {
- syncExtensionType, err := r.ReadBits(11)
- if err != nil {
- return 0, 0, err
- }
- if syncExtensionType[0] == 0x5 && syncExtensionType[1] == 0x48 {
- ps, err := r.ReadBits(1)
- if err != nil {
- return 0, 0, err
- }
- if ps[0] != 0 {
- return 0x40, 29, nil
- }
- }
- }
- }
- return 0x40, 5, nil
- }
- }
- }
- }
- return 0x40, audioObjectType, nil
-}
-
-func findDescriptorByTag(dscrs []Descriptor, tag int8) *Descriptor {
- for _, dscr := range dscrs {
- if dscr.Tag == tag {
- return &dscr
- }
- }
- return nil
-}
-
-func getAudioObjectType(r bitio.Reader) (byte, int, error) {
- audioObjectType, err := r.ReadBits(5)
- if err != nil {
- return 0, 0, err
- }
- if audioObjectType[0] != 0x1f {
- return audioObjectType[0], 5, nil
- }
- audioObjectType, err = r.ReadBits(6)
- if err != nil {
- return 0, 0, err
- }
- return audioObjectType[0] + 32, 11, nil
-}
-
-func probeMoof(r io.ReadSeeker, bi *BoxInfo) (*Segment, error) {
- bips, err := ExtractBoxesWithPayload(r, bi, []BoxPath{
- {BoxTypeTraf(), BoxTypeTfhd()},
- {BoxTypeTraf(), BoxTypeTfdt()},
- {BoxTypeTraf(), BoxTypeTrun()},
- })
- if err != nil {
- return nil, err
- }
-
- var tfhd *Tfhd
- var tfdt *Tfdt
- var trun *Trun
-
- segment := &Segment{
- MoofOffset: bi.Offset,
- }
- for _, bip := range bips {
- switch bip.Info.Type {
- case BoxTypeTfhd():
- tfhd = bip.Payload.(*Tfhd)
- case BoxTypeTfdt():
- tfdt = bip.Payload.(*Tfdt)
- case BoxTypeTrun():
- trun = bip.Payload.(*Trun)
- }
- }
-
- if tfhd == nil {
- return nil, errors.New("tfhd not found")
- }
- segment.TrackID = tfhd.TrackID
- segment.DefaultSampleDuration = tfhd.DefaultSampleDuration
-
- if tfdt != nil {
- if tfdt.Version == 0 {
- segment.BaseMediaDecodeTime = uint64(tfdt.BaseMediaDecodeTimeV0)
- } else {
- segment.BaseMediaDecodeTime = tfdt.BaseMediaDecodeTimeV1
- }
- }
-
- if trun != nil {
- segment.SampleCount = trun.SampleCount
-
- if trun.CheckFlag(0x000100) {
- segment.Duration = 0
- for ei := range trun.Entries {
- segment.Duration += trun.Entries[ei].SampleDuration
- }
- } else {
- segment.Duration = tfhd.DefaultSampleDuration * segment.SampleCount
- }
-
- if trun.CheckFlag(0x000200) {
- segment.Size = 0
- for ei := range trun.Entries {
- segment.Size += trun.Entries[ei].SampleSize
- }
- } else {
- segment.Size = tfhd.DefaultSampleSize * segment.SampleCount
- }
-
- var duration uint32
- for ei := range trun.Entries {
- offset := int32(duration) + int32(trun.GetSampleCompositionTimeOffset(ei))
- if ei == 0 || offset < segment.CompositionTimeOffset {
- segment.CompositionTimeOffset = offset
- }
- if trun.CheckFlag(0x000100) {
- duration += trun.Entries[ei].SampleDuration
- } else {
- duration += tfhd.DefaultSampleDuration
- }
- }
- }
-
- return segment, nil
-}
-
-func FindIDRFrames(r io.ReadSeeker, trackInfo *TrackInfo) ([]int, error) {
- if trackInfo.AVC == nil {
- return nil, nil
- }
- lengthSize := uint32(trackInfo.AVC.LengthSize)
-
- var si int
- idxs := make([]int, 0, 8)
- for _, chunk := range trackInfo.Chunks {
- end := si + int(chunk.SamplesPerChunk)
- dataOffset := chunk.DataOffset
- for ; si < end && si < len(trackInfo.Samples); si++ {
- sample := trackInfo.Samples[si]
- if sample.Size == 0 {
- continue
- }
- for nalOffset := uint32(0); nalOffset+lengthSize+1 <= sample.Size; {
- if _, err := r.Seek(int64(dataOffset+uint64(nalOffset)), io.SeekStart); err != nil {
- return nil, err
- }
- data := make([]byte, lengthSize+1)
- if _, err := io.ReadFull(r, data); err != nil {
- return nil, err
- }
- var length uint32
- for i := 0; i < int(lengthSize); i++ {
- length = (length << 8) + uint32(data[i])
- }
- nalHeader := data[lengthSize]
- nalType := nalHeader & 0x1f
- if nalType == 5 {
- idxs = append(idxs, si)
- break
- }
- nalOffset += lengthSize + length
- }
- dataOffset += uint64(sample.Size)
- }
- }
- return idxs, nil
-}
-
-func (samples Samples) GetBitrate(timescale uint32) uint64 {
- var totalSize uint64
- var totalDuration uint64
- for _, sample := range samples {
- totalSize += uint64(sample.Size)
- totalDuration += uint64(sample.TimeDelta)
- }
- if totalDuration == 0 {
- return 0
- }
- return 8 * totalSize * uint64(timescale) / totalDuration
-}
-
-func (samples Samples) GetMaxBitrate(timescale uint32, timeDelta uint64) uint64 {
- if timeDelta == 0 {
- return 0
- }
- var maxBitrate uint64
- var size uint64
- var duration uint64
- var begin int
- var end int
- for end < len(samples) {
- for {
- size += uint64(samples[end].Size)
- duration += uint64(samples[end].TimeDelta)
- end++
- if duration >= timeDelta || end == len(samples) {
- break
- }
- }
- bitrate := 8 * size * uint64(timescale) / duration
- if bitrate > maxBitrate {
- maxBitrate = bitrate
- }
- for {
- size -= uint64(samples[begin].Size)
- duration -= uint64(samples[begin].TimeDelta)
- begin++
- if duration < timeDelta {
- break
- }
- }
- }
- return maxBitrate
-}
-
-func (segments Segments) GetBitrate(trackID uint32, timescale uint32) uint64 {
- var totalSize uint64
- var totalDuration uint64
- for _, segment := range segments {
- if segment.TrackID == trackID {
- totalSize += uint64(segment.Size)
- totalDuration += uint64(segment.Duration)
- }
- }
- if totalDuration == 0 {
- return 0
- }
- return 8 * totalSize * uint64(timescale) / totalDuration
-}
-
-func (segments Segments) GetMaxBitrate(trackID uint32, timescale uint32) uint64 {
- var maxBitrate uint64
- for _, segment := range segments {
- if segment.TrackID == trackID && segment.Duration != 0 {
- bitrate := 8 * uint64(segment.Size) * uint64(timescale) / uint64(segment.Duration)
- if bitrate > maxBitrate {
- maxBitrate = bitrate
- }
- }
- }
- return maxBitrate
-}