1
1
mirror of https://github.com/wader/fq.git synced 2024-09-11 12:05:39 +03:00

Merge pull request #833 from wader/mp4-trak-traf-heir-instead-of-id

mp4: Use box structure instead of track id to keep track for sample t…
This commit is contained in:
Mattias Wadman 2023-12-11 15:00:03 +01:00 committed by GitHub
commit 1f2ef4ebb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 99 additions and 77 deletions

View File

@ -313,7 +313,7 @@ type irefBox struct {
}
type trakBox struct {
trackID int
track *track
}
type moofBox struct {
@ -321,7 +321,7 @@ type moofBox struct {
}
type trafBox struct {
trackID int
track *track
baseDataOffset int64
moof *moof
}
@ -422,7 +422,11 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) {
d.FieldU32("current_time")
d.FieldU32("next_track_id")
case "trak":
decodeBoxesWithParentData(ctx, d, &trakBox{})
t := &track{}
ctx.tracks = append(ctx.tracks, t)
decodeBoxesWithParentData(ctx, d, &trakBox{
track: t,
})
case "edts":
decodeBoxes(ctx, d)
case "elst":
@ -482,8 +486,7 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) {
d.FieldFP32("track_height")
if t := ctx.currentTrakBox(); t != nil {
t.trackID = trackID
_ = ctx.currentTrack()
t.track.id = trackID
}
case "mdia":
decodeBoxes(ctx, d)
@ -964,16 +967,17 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) {
case "moof":
offset := (d.Pos() / 8) - 8
decodeBoxesWithParentData(ctx, d, &moofBox{offset: offset})
// Track Fragment
case "traf":
decodeBoxesWithParentData(ctx, d, &trafBox{})
// Movie Fragment Header
case "mfhd":
case "traf": // Track Fragment
t := &track{fragment: true}
ctx.tracks = append(ctx.tracks, t)
decodeBoxesWithParentData(ctx, d, &trafBox{
track: t,
})
case "mfhd": // Movie Fragment Header
d.FieldU8("version")
d.FieldU24("flags")
d.FieldU32("sequence_number")
// Track Fragment Header
case "tfhd":
case "tfhd": // Track Fragment Header
d.FieldU8("version")
baseDataOffsetPresent := false
sampleDescriptionIndexPresent := false
@ -1016,15 +1020,14 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) {
}
if t := ctx.currentTrafBox(); t != nil {
t.trackID = trackID
t.track.id = trackID
t.moof = m
t.baseDataOffset = baseDataOffset
}
if t := ctx.currentTrack(); t != nil {
t.moofs = append(t.moofs, m)
}
// Track Fragment Run
case "trun":
case "trun": // Track Fragment Run
m := &moof{}
if t := ctx.currentTrafBox(); t != nil {
m = t.moof
@ -1140,9 +1143,9 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) {
d.FieldU8("version")
d.FieldU24("flags")
d.FieldU32("mfra_size")
case "iloc": // HEIC image
// TODO: item location
// HEIC image
case "iloc":
version := d.FieldU8("version")
d.FieldU24("flags")

View File

@ -134,6 +134,7 @@ type stsz struct {
type track struct {
seenHdlr bool
fragment bool
id int
sampleDescriptions []sampleDescription
subType string
@ -154,16 +155,7 @@ type pathEntry struct {
type decodeContext struct {
opts format.MP4_In
path []pathEntry
tracks map[int]*track
}
func (ctx *decodeContext) lookupTrack(id int) *track {
t, ok := ctx.tracks[id]
if !ok {
t = &track{id: id}
ctx.tracks[id] = t
}
return t
tracks []*track
}
func (ctx *decodeContext) isParent(typ string) bool {
@ -211,24 +203,49 @@ func (ctx *decodeContext) currentMetaBox() *metaBox {
func (ctx *decodeContext) currentTrack() *track {
if t := ctx.currentTrakBox(); t != nil {
return ctx.lookupTrack(t.trackID)
return t.track
}
if t := ctx.currentTrafBox(); t != nil {
return ctx.lookupTrack(t.trackID)
return t.track
}
return nil
}
func mp4Tracks(d *decode.D, ctx *decodeContext) {
// keep track order stable
var sortedTracks []*track
for _, t := range ctx.tracks {
sortedTracks = append(sortedTracks, t)
type trackCollected struct {
track *track
order int
moofss [][]*moof
}
slices.SortFunc(sortedTracks, func(a, b *track) int { return cmpex.Compare(a.id, b.id) })
var tracksCollected []*trackCollected
tracksCollectedSeen := map[int]*trackCollected{}
for i, t := range ctx.tracks {
tc, ok := tracksCollectedSeen[t.id]
if !ok {
tc = &trackCollected{
order: i,
track: t,
}
tracksCollectedSeen[t.id] = tc
tracksCollected = append(tracksCollected, tc)
}
// TODO: error if not fragmented and seen before?
tc.moofss = append(tc.moofss, t.moofs)
}
// sort by id then order in file
slices.SortStableFunc(tracksCollected, func(a, b *trackCollected) int {
if r := cmpex.Compare(a.track.id, b.track.id); r != 0 {
return r
}
return cmpex.Compare(a.order, b.order)
})
d.FieldArray("tracks", func(d *decode.D) {
for _, t := range sortedTracks {
for _, tc := range tracksCollected {
decodeSampleRange := func(d *decode.D, t *track, decodeSample bool, dataFormat string, name string, firstBit int64, nBits int64, inArg any) {
d.RangeFn(firstBit, nBits, func(d *decode.D) {
if !decodeSample {
@ -277,6 +294,8 @@ func mp4Tracks(d *decode.D, ctx *decodeContext) {
}
d.FieldStruct("track", func(d *decode.D) {
t := tc.track
d.FieldValueUint("id", uint64(t.id))
trackSDDataFormat := "unknown"
@ -361,8 +380,6 @@ func mp4Tracks(d *decode.D, ctx *decodeContext) {
}
}
// log.Println(logStrFn())
decodeSampleRange(d, t, ctx.opts.DecodeSamples, trackSDDataFormat, "sample", sampleOffset*8, stszEntry.size*8, t.formatInArg)
sampleOffset += stszEntry.size
@ -373,48 +390,50 @@ func mp4Tracks(d *decode.D, ctx *decodeContext) {
}
sampleNr := 0
for _, m := range t.moofs {
for trunNr, trun := range m.truns {
var senc senc
if trunNr < len(m.sencs) {
senc = m.sencs[trunNr]
}
sampleOffset := m.offset + trun.dataOffset
for trunSampleNr, sz := range trun.samplesSizes {
dataFormat := trackSDDataFormat
if m.defaultSampleDescriptionIndex != 0 && m.defaultSampleDescriptionIndex-1 < len(t.sampleDescriptions) {
sd := t.sampleDescriptions[m.defaultSampleDescriptionIndex-1]
dataFormat = sd.dataFormat
if sd.originalFormat != "" {
dataFormat = sd.originalFormat
for _, ms := range tc.moofss {
for _, m := range ms {
for trunNr, trun := range m.truns {
var senc senc
if trunNr < len(m.sencs) {
senc = m.sencs[trunNr]
}
sampleOffset := m.offset + trun.dataOffset
for trunSampleNr, sz := range trun.samplesSizes {
dataFormat := trackSDDataFormat
if m.defaultSampleDescriptionIndex != 0 && m.defaultSampleDescriptionIndex-1 < len(t.sampleDescriptions) {
sd := t.sampleDescriptions[m.defaultSampleDescriptionIndex-1]
dataFormat = sd.dataFormat
if sd.originalFormat != "" {
dataFormat = sd.originalFormat
}
}
// logStrFn := func() string {
// return fmt.Sprintf("%d: %s: %d: (%s): sz=%d %d+%d=%d",
// t.id,
// dataFormat,
// sampleNr,
// trackSDDataFormat,
// sz,
// m.offset,
// m.dataOffset,
// sampleOffset,
// )
// }
decodeSample := ctx.opts.DecodeSamples
if trunSampleNr < len(senc.entries) {
// TODO: encrypted
decodeSample = false
}
decodeSampleRange(d, t, decodeSample, dataFormat, "sample", sampleOffset*8, sz*8, t.formatInArg)
sampleOffset += sz
sampleNr++
}
// logStrFn := func() string {
// return fmt.Sprintf("%d: %s: %d: (%s): sz=%d %d+%d=%d",
// t.id,
// dataFormat,
// sampleNr,
// trackSDDataFormat,
// sz,
// m.offset,
// m.dataOffset,
// sampleOffset,
// )
// }
// log.Println(logStrFn())
decodeSample := ctx.opts.DecodeSamples
if trunSampleNr < len(senc.entries) {
// TODO: encrypted
decodeSample = false
}
decodeSampleRange(d, t, decodeSample, dataFormat, "sample", sampleOffset*8, sz*8, t.formatInArg)
sampleOffset += sz
sampleNr++
}
}
}
@ -431,7 +450,7 @@ func mp4Decode(d *decode.D) any {
ctx := &decodeContext{
opts: mi,
path: []pathEntry{{typ: "root"}},
tracks: map[int]*track{},
tracks: []*track{},
}
// TODO: nicer, validate functions without field?