github.com/bluenviron/mediacommon@v1.9.3/pkg/formats/fmp4/part.go (about)

     1  package fmp4
     2  
     3  import (
     4  	"io"
     5  
     6  	"github.com/abema/go-mp4"
     7  )
     8  
     9  const (
    10  	trunFlagDataOffsetPreset                       = 0x01
    11  	trunFlagSampleDurationPresent                  = 0x100
    12  	trunFlagSampleSizePresent                      = 0x200
    13  	trunFlagSampleFlagsPresent                     = 0x400
    14  	trunFlagSampleCompositionTimeOffsetPresentOrV1 = 0x800
    15  
    16  	sampleFlagIsNonSyncSample = 1 << 16
    17  )
    18  
    19  // Part is a fMP4 part.
    20  type Part struct {
    21  	SequenceNumber uint32
    22  	Tracks         []*PartTrack
    23  }
    24  
    25  // Marshal encodes a fMP4 part.
    26  func (p *Part) Marshal(w io.WriteSeeker) error {
    27  	/*
    28  		|moof|
    29  		|    |mfhd|
    30  		|    |traf|
    31  		|    |traf|
    32  		|    |....|
    33  		|mdat|
    34  	*/
    35  
    36  	mw := newMP4Writer(w)
    37  
    38  	moofOffset, err := mw.writeBoxStart(&mp4.Moof{}) // <moof>
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	_, err = mw.writeBox(&mp4.Mfhd{ // <mfhd/>
    44  		SequenceNumber: p.SequenceNumber,
    45  	})
    46  	if err != nil {
    47  		return err
    48  	}
    49  
    50  	trackLen := len(p.Tracks)
    51  	truns := make([]*mp4.Trun, trackLen)
    52  	trunOffsets := make([]int, trackLen)
    53  	dataOffsets := make([]int, trackLen)
    54  	dataSize := 0
    55  
    56  	for i, track := range p.Tracks {
    57  		trun, trunOffset, err := track.marshal(mw)
    58  		if err != nil {
    59  			return err
    60  		}
    61  
    62  		dataOffsets[i] = dataSize
    63  
    64  		for _, sample := range track.Samples {
    65  			dataSize += len(sample.Payload)
    66  		}
    67  
    68  		truns[i] = trun
    69  		trunOffsets[i] = trunOffset
    70  	}
    71  
    72  	err = mw.writeBoxEnd() // </moof>
    73  	if err != nil {
    74  		return err
    75  	}
    76  
    77  	mdat := &mp4.Mdat{} // <mdat/>
    78  	mdat.Data = make([]byte, dataSize)
    79  	pos := 0
    80  
    81  	for _, track := range p.Tracks {
    82  		for _, sample := range track.Samples {
    83  			pos += copy(mdat.Data[pos:], sample.Payload)
    84  		}
    85  	}
    86  
    87  	mdatOffset, err := mw.writeBox(mdat)
    88  	if err != nil {
    89  		return err
    90  	}
    91  
    92  	for i := range p.Tracks {
    93  		truns[i].DataOffset = int32(dataOffsets[i] + mdatOffset - moofOffset + 8)
    94  		err = mw.rewriteBox(trunOffsets[i], truns[i])
    95  		if err != nil {
    96  			return err
    97  		}
    98  	}
    99  
   100  	return nil
   101  }