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

     1  package fmp4
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/abema/go-mp4"
     7  
     8  	"github.com/bluenviron/mediacommon/pkg/codecs/av1"
     9  	"github.com/bluenviron/mediacommon/pkg/codecs/h264"
    10  	"github.com/bluenviron/mediacommon/pkg/codecs/h265"
    11  )
    12  
    13  func boolToUint8(v bool) uint8 {
    14  	if v {
    15  		return 1
    16  	}
    17  	return 0
    18  }
    19  
    20  // InitTrack is a track of Init.
    21  type InitTrack struct {
    22  	ID        int
    23  	TimeScale uint32
    24  	Codec     Codec
    25  }
    26  
    27  func (it *InitTrack) marshal(w *mp4Writer) error {
    28  	/*
    29  		|trak|
    30  		|    |tkhd|
    31  		|    |mdia|
    32  		|    |    |mdhd|
    33  		|    |    |hdlr|
    34  		|    |    |minf|
    35  		|    |    |    |vmhd| (video)
    36  		|    |    |    |smhd| (audio)
    37  		|    |    |    |dinf|
    38  		|    |    |    |    |dref|
    39  		|    |    |    |    |    |url|
    40  		|    |    |    |stbl|
    41  		|    |    |    |    |stsd|
    42  		|    |    |    |    |    |av01| (AV1)
    43  		|    |    |    |    |    |    |av1C|
    44  		|    |    |    |    |    |    |btrt|
    45  		|    |    |    |    |    |vp09| (VP9)
    46  		|    |    |    |    |    |    |vpcC|
    47  		|    |    |    |    |    |    |btrt|
    48  		|    |    |    |    |    |hev1| (H265)
    49  		|    |    |    |    |    |    |hvcC|
    50  		|    |    |    |    |    |    |btrt|
    51  		|    |    |    |    |    |avc1| (H264)
    52  		|    |    |    |    |    |    |avcC|
    53  		|    |    |    |    |    |    |btrt|
    54  		|    |    |    |    |    |mp4v| (MPEG-4/2/1 video, MJPEG)
    55  		|    |    |    |    |    |    |esds|
    56  		|    |    |    |    |    |    |btrt|
    57  		|    |    |    |    |    |Opus| (Opus)
    58  		|    |    |    |    |    |    |dOps|
    59  		|    |    |    |    |    |    |btrt|
    60  		|    |    |    |    |    |mp4a| (MPEG-4/1 audio)
    61  		|    |    |    |    |    |    |esds|
    62  		|    |    |    |    |    |    |btrt|
    63  		|    |    |    |    |    |ac-3| (AC-3)
    64  		|    |    |    |    |    |    |dac3|
    65  		|    |    |    |    |    |    |btrt|
    66  		|    |    |    |    |    |ipcm| (LPCM)
    67  		|    |    |    |    |    |    |pcmC|
    68  		|    |    |    |    |    |    |btrt|
    69  		|    |    |    |    |stts|
    70  		|    |    |    |    |stsc|
    71  		|    |    |    |    |stsz|
    72  		|    |    |    |    |stco|
    73  	*/
    74  
    75  	_, err := w.writeBoxStart(&mp4.Trak{}) // <trak>
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	var av1SequenceHeader *av1.SequenceHeader
    81  	var h265SPS *h265.SPS
    82  	var h264SPS *h264.SPS
    83  
    84  	var width int
    85  	var height int
    86  
    87  	switch codec := it.Codec.(type) {
    88  	case *CodecAV1:
    89  		av1SequenceHeader = &av1.SequenceHeader{}
    90  		err = av1SequenceHeader.Unmarshal(codec.SequenceHeader)
    91  		if err != nil {
    92  			return fmt.Errorf("unable to parse AV1 sequence header: %w", err)
    93  		}
    94  
    95  		width = av1SequenceHeader.Width()
    96  		height = av1SequenceHeader.Height()
    97  
    98  	case *CodecVP9:
    99  		if codec.Width == 0 {
   100  			return fmt.Errorf("VP9 parameters not provided")
   101  		}
   102  
   103  		width = codec.Width
   104  		height = codec.Height
   105  
   106  	case *CodecH265:
   107  		if len(codec.VPS) == 0 || len(codec.SPS) == 0 || len(codec.PPS) == 0 {
   108  			return fmt.Errorf("H265 parameters not provided")
   109  		}
   110  
   111  		h265SPS = &h265.SPS{}
   112  		err = h265SPS.Unmarshal(codec.SPS)
   113  		if err != nil {
   114  			return fmt.Errorf("unable to parse H265 SPS: %w", err)
   115  		}
   116  
   117  		width = h265SPS.Width()
   118  		height = h265SPS.Height()
   119  
   120  	case *CodecH264:
   121  		if len(codec.SPS) == 0 || len(codec.PPS) == 0 {
   122  			return fmt.Errorf("H264 parameters not provided")
   123  		}
   124  
   125  		h264SPS = &h264.SPS{}
   126  		err = h264SPS.Unmarshal(codec.SPS)
   127  		if err != nil {
   128  			return fmt.Errorf("unable to parse H264 SPS: %w", err)
   129  		}
   130  
   131  		width = h264SPS.Width()
   132  		height = h264SPS.Height()
   133  
   134  	case *CodecMPEG4Video:
   135  		if len(codec.Config) == 0 {
   136  			return fmt.Errorf("MPEG-4 Video config not provided")
   137  		}
   138  
   139  		// TODO: parse config and use real values
   140  		width = 800
   141  		height = 600
   142  
   143  	case *CodecMPEG1Video:
   144  		if len(codec.Config) == 0 {
   145  			return fmt.Errorf("MPEG-1/2 Video config not provided")
   146  		}
   147  
   148  		// TODO: parse config and use real values
   149  		width = 800
   150  		height = 600
   151  
   152  	case *CodecMJPEG:
   153  		if codec.Width == 0 {
   154  			return fmt.Errorf("M-JPEG parameters not provided")
   155  		}
   156  
   157  		width = codec.Width
   158  		height = codec.Height
   159  	}
   160  
   161  	if it.Codec.IsVideo() {
   162  		_, err = w.writeBox(&mp4.Tkhd{ // <tkhd/>
   163  			FullBox: mp4.FullBox{
   164  				Flags: [3]byte{0, 0, 3},
   165  			},
   166  			TrackID: uint32(it.ID),
   167  			Width:   uint32(width * 65536),
   168  			Height:  uint32(height * 65536),
   169  			Matrix:  [9]int32{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000},
   170  		})
   171  		if err != nil {
   172  			return err
   173  		}
   174  	} else {
   175  		_, err = w.writeBox(&mp4.Tkhd{ // <tkhd/>
   176  			FullBox: mp4.FullBox{
   177  				Flags: [3]byte{0, 0, 3},
   178  			},
   179  			TrackID:        uint32(it.ID),
   180  			AlternateGroup: 1,
   181  			Volume:         256,
   182  			Matrix:         [9]int32{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000},
   183  		})
   184  		if err != nil {
   185  			return err
   186  		}
   187  	}
   188  
   189  	_, err = w.writeBoxStart(&mp4.Mdia{}) // <mdia>
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	_, err = w.writeBox(&mp4.Mdhd{ // <mdhd/>
   195  		Timescale: it.TimeScale,
   196  		Language:  [3]byte{'u', 'n', 'd'},
   197  	})
   198  	if err != nil {
   199  		return err
   200  	}
   201  
   202  	if it.Codec.IsVideo() {
   203  		_, err = w.writeBox(&mp4.Hdlr{ // <hdlr/>
   204  			HandlerType: [4]byte{'v', 'i', 'd', 'e'},
   205  			Name:        "VideoHandler",
   206  		})
   207  		if err != nil {
   208  			return err
   209  		}
   210  	} else {
   211  		_, err = w.writeBox(&mp4.Hdlr{ // <hdlr/>
   212  			HandlerType: [4]byte{'s', 'o', 'u', 'n'},
   213  			Name:        "SoundHandler",
   214  		})
   215  		if err != nil {
   216  			return err
   217  		}
   218  	}
   219  
   220  	_, err = w.writeBoxStart(&mp4.Minf{}) // <minf>
   221  	if err != nil {
   222  		return err
   223  	}
   224  
   225  	if it.Codec.IsVideo() {
   226  		_, err = w.writeBox(&mp4.Vmhd{ // <vmhd/>
   227  			FullBox: mp4.FullBox{
   228  				Flags: [3]byte{0, 0, 1},
   229  			},
   230  		})
   231  		if err != nil {
   232  			return err
   233  		}
   234  	} else {
   235  		_, err = w.writeBox(&mp4.Smhd{}) // <smhd/>
   236  		if err != nil {
   237  			return err
   238  		}
   239  	}
   240  
   241  	_, err = w.writeBoxStart(&mp4.Dinf{}) // <dinf>
   242  	if err != nil {
   243  		return err
   244  	}
   245  
   246  	_, err = w.writeBoxStart(&mp4.Dref{ // <dref>
   247  		EntryCount: 1,
   248  	})
   249  	if err != nil {
   250  		return err
   251  	}
   252  
   253  	_, err = w.writeBox(&mp4.Url{ // <url/>
   254  		FullBox: mp4.FullBox{
   255  			Flags: [3]byte{0, 0, 1},
   256  		},
   257  	})
   258  	if err != nil {
   259  		return err
   260  	}
   261  
   262  	err = w.writeBoxEnd() // </dref>
   263  	if err != nil {
   264  		return err
   265  	}
   266  
   267  	err = w.writeBoxEnd() // </dinf>
   268  	if err != nil {
   269  		return err
   270  	}
   271  
   272  	_, err = w.writeBoxStart(&mp4.Stbl{}) // <stbl>
   273  	if err != nil {
   274  		return err
   275  	}
   276  
   277  	_, err = w.writeBoxStart(&mp4.Stsd{ // <stsd>
   278  		EntryCount: 1,
   279  	})
   280  	if err != nil {
   281  		return err
   282  	}
   283  
   284  	switch codec := it.Codec.(type) {
   285  	case *CodecAV1:
   286  		_, err = w.writeBoxStart(&mp4.VisualSampleEntry{ // <av01>
   287  			SampleEntry: mp4.SampleEntry{
   288  				AnyTypeBox: mp4.AnyTypeBox{
   289  					Type: mp4.BoxTypeAv01(),
   290  				},
   291  				DataReferenceIndex: 1,
   292  			},
   293  			Width:           uint16(width),
   294  			Height:          uint16(height),
   295  			Horizresolution: 4718592,
   296  			Vertresolution:  4718592,
   297  			FrameCount:      1,
   298  			Depth:           24,
   299  			PreDefined3:     -1,
   300  		})
   301  		if err != nil {
   302  			return err
   303  		}
   304  
   305  		bs, err := av1.BitstreamMarshal([][]byte{codec.SequenceHeader})
   306  		if err != nil {
   307  			return err
   308  		}
   309  
   310  		_, err = w.writeBox(&mp4.Av1C{ // <av1C/>
   311  			Marker:               1,
   312  			Version:              1,
   313  			SeqProfile:           av1SequenceHeader.SeqProfile,
   314  			SeqLevelIdx0:         av1SequenceHeader.SeqLevelIdx[0],
   315  			SeqTier0:             boolToUint8(av1SequenceHeader.SeqTier[0]),
   316  			HighBitdepth:         boolToUint8(av1SequenceHeader.ColorConfig.HighBitDepth),
   317  			TwelveBit:            boolToUint8(av1SequenceHeader.ColorConfig.TwelveBit),
   318  			Monochrome:           boolToUint8(av1SequenceHeader.ColorConfig.MonoChrome),
   319  			ChromaSubsamplingX:   boolToUint8(av1SequenceHeader.ColorConfig.SubsamplingX),
   320  			ChromaSubsamplingY:   boolToUint8(av1SequenceHeader.ColorConfig.SubsamplingY),
   321  			ChromaSamplePosition: uint8(av1SequenceHeader.ColorConfig.ChromaSamplePosition),
   322  			ConfigOBUs:           bs,
   323  		})
   324  		if err != nil {
   325  			return err
   326  		}
   327  
   328  	case *CodecVP9:
   329  		_, err = w.writeBoxStart(&mp4.VisualSampleEntry{ // <vp09>
   330  			SampleEntry: mp4.SampleEntry{
   331  				AnyTypeBox: mp4.AnyTypeBox{
   332  					Type: mp4.BoxTypeVp09(),
   333  				},
   334  				DataReferenceIndex: 1,
   335  			},
   336  			Width:           uint16(width),
   337  			Height:          uint16(height),
   338  			Horizresolution: 4718592,
   339  			Vertresolution:  4718592,
   340  			FrameCount:      1,
   341  			Depth:           24,
   342  			PreDefined3:     -1,
   343  		})
   344  		if err != nil {
   345  			return err
   346  		}
   347  
   348  		_, err = w.writeBox(&mp4.VpcC{ // <vpcC/>
   349  			FullBox: mp4.FullBox{
   350  				Version: 1,
   351  			},
   352  			Profile:            codec.Profile,
   353  			Level:              10, // level 1
   354  			BitDepth:           codec.BitDepth,
   355  			ChromaSubsampling:  codec.ChromaSubsampling,
   356  			VideoFullRangeFlag: boolToUint8(codec.ColorRange),
   357  		})
   358  		if err != nil {
   359  			return err
   360  		}
   361  
   362  	case *CodecH265:
   363  		_, err = w.writeBoxStart(&mp4.VisualSampleEntry{ // <hev1>
   364  			SampleEntry: mp4.SampleEntry{
   365  				AnyTypeBox: mp4.AnyTypeBox{
   366  					Type: mp4.BoxTypeHev1(),
   367  				},
   368  				DataReferenceIndex: 1,
   369  			},
   370  			Width:           uint16(width),
   371  			Height:          uint16(height),
   372  			Horizresolution: 4718592,
   373  			Vertresolution:  4718592,
   374  			FrameCount:      1,
   375  			Depth:           24,
   376  			PreDefined3:     -1,
   377  		})
   378  		if err != nil {
   379  			return err
   380  		}
   381  
   382  		_, err = w.writeBox(&mp4.HvcC{ // <hvcC/>
   383  			ConfigurationVersion:        1,
   384  			GeneralProfileIdc:           h265SPS.ProfileTierLevel.GeneralProfileIdc,
   385  			GeneralProfileCompatibility: h265SPS.ProfileTierLevel.GeneralProfileCompatibilityFlag,
   386  			GeneralConstraintIndicator: [6]uint8{
   387  				codec.SPS[7], codec.SPS[8], codec.SPS[9],
   388  				codec.SPS[10], codec.SPS[11], codec.SPS[12],
   389  			},
   390  			GeneralLevelIdc: h265SPS.ProfileTierLevel.GeneralLevelIdc,
   391  			// MinSpatialSegmentationIdc
   392  			// ParallelismType
   393  			ChromaFormatIdc:      uint8(h265SPS.ChromaFormatIdc),
   394  			BitDepthLumaMinus8:   uint8(h265SPS.BitDepthLumaMinus8),
   395  			BitDepthChromaMinus8: uint8(h265SPS.BitDepthChromaMinus8),
   396  			// AvgFrameRate
   397  			// ConstantFrameRate
   398  			NumTemporalLayers: 1,
   399  			// TemporalIdNested
   400  			LengthSizeMinusOne: 3,
   401  			NumOfNaluArrays:    3,
   402  			NaluArrays: []mp4.HEVCNaluArray{
   403  				{
   404  					NaluType: byte(h265.NALUType_VPS_NUT),
   405  					NumNalus: 1,
   406  					Nalus: []mp4.HEVCNalu{{
   407  						Length:  uint16(len(codec.VPS)),
   408  						NALUnit: codec.VPS,
   409  					}},
   410  				},
   411  				{
   412  					NaluType: byte(h265.NALUType_SPS_NUT),
   413  					NumNalus: 1,
   414  					Nalus: []mp4.HEVCNalu{{
   415  						Length:  uint16(len(codec.SPS)),
   416  						NALUnit: codec.SPS,
   417  					}},
   418  				},
   419  				{
   420  					NaluType: byte(h265.NALUType_PPS_NUT),
   421  					NumNalus: 1,
   422  					Nalus: []mp4.HEVCNalu{{
   423  						Length:  uint16(len(codec.PPS)),
   424  						NALUnit: codec.PPS,
   425  					}},
   426  				},
   427  			},
   428  		})
   429  		if err != nil {
   430  			return err
   431  		}
   432  
   433  	case *CodecH264:
   434  		_, err = w.writeBoxStart(&mp4.VisualSampleEntry{ // <avc1>
   435  			SampleEntry: mp4.SampleEntry{
   436  				AnyTypeBox: mp4.AnyTypeBox{
   437  					Type: mp4.BoxTypeAvc1(),
   438  				},
   439  				DataReferenceIndex: 1,
   440  			},
   441  			Width:           uint16(width),
   442  			Height:          uint16(height),
   443  			Horizresolution: 4718592,
   444  			Vertresolution:  4718592,
   445  			FrameCount:      1,
   446  			Depth:           24,
   447  			PreDefined3:     -1,
   448  		})
   449  		if err != nil {
   450  			return err
   451  		}
   452  
   453  		_, err = w.writeBox(&mp4.AVCDecoderConfiguration{ // <avcc/>
   454  			AnyTypeBox: mp4.AnyTypeBox{
   455  				Type: mp4.BoxTypeAvcC(),
   456  			},
   457  			ConfigurationVersion:       1,
   458  			Profile:                    h264SPS.ProfileIdc,
   459  			ProfileCompatibility:       codec.SPS[2],
   460  			Level:                      h264SPS.LevelIdc,
   461  			LengthSizeMinusOne:         3,
   462  			NumOfSequenceParameterSets: 1,
   463  			SequenceParameterSets: []mp4.AVCParameterSet{
   464  				{
   465  					Length:  uint16(len(codec.SPS)),
   466  					NALUnit: codec.SPS,
   467  				},
   468  			},
   469  			NumOfPictureParameterSets: 1,
   470  			PictureParameterSets: []mp4.AVCParameterSet{
   471  				{
   472  					Length:  uint16(len(codec.PPS)),
   473  					NALUnit: codec.PPS,
   474  				},
   475  			},
   476  		})
   477  		if err != nil {
   478  			return err
   479  		}
   480  
   481  	case *CodecMPEG4Video: //nolint:dupl
   482  		_, err = w.writeBoxStart(&mp4.VisualSampleEntry{ // <mp4v>
   483  			SampleEntry: mp4.SampleEntry{
   484  				AnyTypeBox: mp4.AnyTypeBox{
   485  					Type: mp4.BoxTypeMp4v(),
   486  				},
   487  				DataReferenceIndex: 1,
   488  			},
   489  			Width:           uint16(width),
   490  			Height:          uint16(height),
   491  			Horizresolution: 4718592,
   492  			Vertresolution:  4718592,
   493  			FrameCount:      1,
   494  			Depth:           24,
   495  			PreDefined3:     -1,
   496  		})
   497  		if err != nil {
   498  			return err
   499  		}
   500  
   501  		_, err = w.writeBox(&mp4.Esds{ // <esds/>
   502  			Descriptors: []mp4.Descriptor{
   503  				{
   504  					Tag:  mp4.ESDescrTag,
   505  					Size: 32 + uint32(len(codec.Config)),
   506  					ESDescriptor: &mp4.ESDescriptor{
   507  						ESID: uint16(it.ID),
   508  					},
   509  				},
   510  				{
   511  					Tag:  mp4.DecoderConfigDescrTag,
   512  					Size: 18 + uint32(len(codec.Config)),
   513  					DecoderConfigDescriptor: &mp4.DecoderConfigDescriptor{
   514  						ObjectTypeIndication: objectTypeIndicationVisualISO14496part2,
   515  						StreamType:           streamTypeVisualStream,
   516  						Reserved:             true,
   517  						MaxBitrate:           1000000,
   518  						AvgBitrate:           1000000,
   519  					},
   520  				},
   521  				{
   522  					Tag:  mp4.DecSpecificInfoTag,
   523  					Size: uint32(len(codec.Config)),
   524  					Data: codec.Config,
   525  				},
   526  				{
   527  					Tag:  mp4.SLConfigDescrTag,
   528  					Size: 1,
   529  					Data: []byte{0x02},
   530  				},
   531  			},
   532  		})
   533  		if err != nil {
   534  			return err
   535  		}
   536  
   537  	case *CodecMPEG1Video: //nolint:dupl
   538  		_, err = w.writeBoxStart(&mp4.VisualSampleEntry{ // <mp4v>
   539  			SampleEntry: mp4.SampleEntry{
   540  				AnyTypeBox: mp4.AnyTypeBox{
   541  					Type: mp4.BoxTypeMp4v(),
   542  				},
   543  				DataReferenceIndex: 1,
   544  			},
   545  			Width:           uint16(width),
   546  			Height:          uint16(height),
   547  			Horizresolution: 4718592,
   548  			Vertresolution:  4718592,
   549  			FrameCount:      1,
   550  			Depth:           24,
   551  			PreDefined3:     -1,
   552  		})
   553  		if err != nil {
   554  			return err
   555  		}
   556  
   557  		_, err = w.writeBox(&mp4.Esds{ // <esds/>
   558  			Descriptors: []mp4.Descriptor{
   559  				{
   560  					Tag:  mp4.ESDescrTag,
   561  					Size: 32 + uint32(len(codec.Config)),
   562  					ESDescriptor: &mp4.ESDescriptor{
   563  						ESID: uint16(it.ID),
   564  					},
   565  				},
   566  				{
   567  					Tag:  mp4.DecoderConfigDescrTag,
   568  					Size: 18 + uint32(len(codec.Config)),
   569  					DecoderConfigDescriptor: &mp4.DecoderConfigDescriptor{
   570  						ObjectTypeIndication: objectTypeIndicationVisualISO1318part2Main,
   571  						StreamType:           streamTypeVisualStream,
   572  						Reserved:             true,
   573  						MaxBitrate:           1000000,
   574  						AvgBitrate:           1000000,
   575  					},
   576  				},
   577  				{
   578  					Tag:  mp4.DecSpecificInfoTag,
   579  					Size: uint32(len(codec.Config)),
   580  					Data: codec.Config,
   581  				},
   582  				{
   583  					Tag:  mp4.SLConfigDescrTag,
   584  					Size: 1,
   585  					Data: []byte{0x02},
   586  				},
   587  			},
   588  		})
   589  		if err != nil {
   590  			return err
   591  		}
   592  
   593  	case *CodecMJPEG: //nolint:dupl
   594  		_, err = w.writeBoxStart(&mp4.VisualSampleEntry{ // <mp4v>
   595  			SampleEntry: mp4.SampleEntry{
   596  				AnyTypeBox: mp4.AnyTypeBox{
   597  					Type: mp4.BoxTypeMp4v(),
   598  				},
   599  				DataReferenceIndex: 1,
   600  			},
   601  			Width:           uint16(width),
   602  			Height:          uint16(height),
   603  			Horizresolution: 4718592,
   604  			Vertresolution:  4718592,
   605  			FrameCount:      1,
   606  			Depth:           24,
   607  			PreDefined3:     -1,
   608  		})
   609  		if err != nil {
   610  			return err
   611  		}
   612  
   613  		_, err = w.writeBox(&mp4.Esds{ // <esds/>
   614  			Descriptors: []mp4.Descriptor{
   615  				{
   616  					Tag:  mp4.ESDescrTag,
   617  					Size: 27,
   618  					ESDescriptor: &mp4.ESDescriptor{
   619  						ESID: uint16(it.ID),
   620  					},
   621  				},
   622  				{
   623  					Tag:  mp4.DecoderConfigDescrTag,
   624  					Size: 13,
   625  					DecoderConfigDescriptor: &mp4.DecoderConfigDescriptor{
   626  						ObjectTypeIndication: objectTypeIndicationVisualISO10918part1,
   627  						StreamType:           streamTypeVisualStream,
   628  						Reserved:             true,
   629  						MaxBitrate:           1000000,
   630  						AvgBitrate:           1000000,
   631  					},
   632  				},
   633  				{
   634  					Tag:  mp4.SLConfigDescrTag,
   635  					Size: 1,
   636  					Data: []byte{0x02},
   637  				},
   638  			},
   639  		})
   640  		if err != nil {
   641  			return err
   642  		}
   643  
   644  	case *CodecOpus:
   645  		_, err = w.writeBoxStart(&mp4.AudioSampleEntry{ // <Opus>
   646  			SampleEntry: mp4.SampleEntry{
   647  				AnyTypeBox: mp4.AnyTypeBox{
   648  					Type: mp4.BoxTypeOpus(),
   649  				},
   650  				DataReferenceIndex: 1,
   651  			},
   652  			ChannelCount: uint16(codec.ChannelCount),
   653  			SampleSize:   16,
   654  			SampleRate:   48000 * 65536,
   655  		})
   656  		if err != nil {
   657  			return err
   658  		}
   659  
   660  		_, err = w.writeBox(&mp4.DOps{ // <dOps/>
   661  			OutputChannelCount: uint8(codec.ChannelCount),
   662  			PreSkip:            312,
   663  			InputSampleRate:    48000,
   664  		})
   665  		if err != nil {
   666  			return err
   667  		}
   668  
   669  	case *CodecMPEG4Audio:
   670  		_, err = w.writeBoxStart(&mp4.AudioSampleEntry{ // <mp4a>
   671  			SampleEntry: mp4.SampleEntry{
   672  				AnyTypeBox: mp4.AnyTypeBox{
   673  					Type: mp4.BoxTypeMp4a(),
   674  				},
   675  				DataReferenceIndex: 1,
   676  			},
   677  			ChannelCount: uint16(codec.ChannelCount),
   678  			SampleSize:   16,
   679  			SampleRate:   uint32(codec.SampleRate * 65536),
   680  		})
   681  		if err != nil {
   682  			return err
   683  		}
   684  
   685  		enc, _ := codec.Config.Marshal()
   686  
   687  		_, err = w.writeBox(&mp4.Esds{ // <esds/>
   688  			Descriptors: []mp4.Descriptor{
   689  				{
   690  					Tag:  mp4.ESDescrTag,
   691  					Size: 32 + uint32(len(enc)),
   692  					ESDescriptor: &mp4.ESDescriptor{
   693  						ESID: uint16(it.ID),
   694  					},
   695  				},
   696  				{
   697  					Tag:  mp4.DecoderConfigDescrTag,
   698  					Size: 18 + uint32(len(enc)),
   699  					DecoderConfigDescriptor: &mp4.DecoderConfigDescriptor{
   700  						ObjectTypeIndication: objectTypeIndicationAudioISO14496part3,
   701  						StreamType:           streamTypeAudioStream,
   702  						Reserved:             true,
   703  						MaxBitrate:           128825,
   704  						AvgBitrate:           128825,
   705  					},
   706  				},
   707  				{
   708  					Tag:  mp4.DecSpecificInfoTag,
   709  					Size: uint32(len(enc)),
   710  					Data: enc,
   711  				},
   712  				{
   713  					Tag:  mp4.SLConfigDescrTag,
   714  					Size: 1,
   715  					Data: []byte{0x02},
   716  				},
   717  			},
   718  		})
   719  		if err != nil {
   720  			return err
   721  		}
   722  
   723  	case *CodecMPEG1Audio:
   724  		_, err = w.writeBoxStart(&mp4.AudioSampleEntry{ // <mp4a>
   725  			SampleEntry: mp4.SampleEntry{
   726  				AnyTypeBox: mp4.AnyTypeBox{
   727  					Type: mp4.BoxTypeMp4a(),
   728  				},
   729  				DataReferenceIndex: 1,
   730  			},
   731  			ChannelCount: uint16(codec.ChannelCount),
   732  			SampleSize:   16,
   733  			SampleRate:   uint32(codec.SampleRate * 65536),
   734  		})
   735  		if err != nil {
   736  			return err
   737  		}
   738  
   739  		_, err = w.writeBox(&mp4.Esds{ // <esds/>
   740  			Descriptors: []mp4.Descriptor{
   741  				{
   742  					Tag:  mp4.ESDescrTag,
   743  					Size: 27,
   744  					ESDescriptor: &mp4.ESDescriptor{
   745  						ESID: uint16(it.ID),
   746  					},
   747  				},
   748  				{
   749  					Tag:  mp4.DecoderConfigDescrTag,
   750  					Size: 13,
   751  					DecoderConfigDescriptor: &mp4.DecoderConfigDescriptor{
   752  						ObjectTypeIndication: objectTypeIndicationAudioISO11172part3,
   753  						StreamType:           streamTypeAudioStream,
   754  						Reserved:             true,
   755  						MaxBitrate:           128825,
   756  						AvgBitrate:           128825,
   757  					},
   758  				},
   759  				{
   760  					Tag:  mp4.SLConfigDescrTag,
   761  					Size: 1,
   762  					Data: []byte{0x02},
   763  				},
   764  			},
   765  		})
   766  		if err != nil {
   767  			return err
   768  		}
   769  
   770  	case *CodecAC3:
   771  		_, err = w.writeBoxStart(&mp4.AudioSampleEntry{ // <ac-3>
   772  			SampleEntry: mp4.SampleEntry{
   773  				AnyTypeBox: mp4.AnyTypeBox{
   774  					Type: mp4.BoxTypeAC3(),
   775  				},
   776  				DataReferenceIndex: 1,
   777  			},
   778  			ChannelCount: uint16(codec.ChannelCount),
   779  			SampleSize:   16,
   780  			SampleRate:   uint32(codec.SampleRate * 65536),
   781  		})
   782  		if err != nil {
   783  			return err
   784  		}
   785  
   786  		_, err = w.writeBox(&mp4.Dac3{ // <dac3/>
   787  			Fscod: codec.Fscod,
   788  			Bsid:  codec.Bsid,
   789  			Bsmod: codec.Bsmod,
   790  			Acmod: codec.Acmod,
   791  			LfeOn: func() uint8 {
   792  				if codec.LfeOn {
   793  					return 1
   794  				}
   795  				return 0
   796  			}(),
   797  			BitRateCode: codec.BitRateCode,
   798  		})
   799  		if err != nil {
   800  			return err
   801  		}
   802  
   803  	case *CodecLPCM:
   804  		_, err = w.writeBoxStart(&mp4.AudioSampleEntry{ // <ipcm>
   805  			SampleEntry: mp4.SampleEntry{
   806  				AnyTypeBox: mp4.AnyTypeBox{
   807  					Type: mp4.BoxTypeIpcm(),
   808  				},
   809  				DataReferenceIndex: 1,
   810  			},
   811  			ChannelCount: uint16(codec.ChannelCount),
   812  			SampleSize:   uint16(codec.BitDepth), // FFmpeg leaves this to 16 instead of using real bit depth
   813  			SampleRate:   uint32(codec.SampleRate * 65536),
   814  		})
   815  		if err != nil {
   816  			return err
   817  		}
   818  
   819  		_, err = w.writeBox(&mp4.PcmC{ // <pcmC/>
   820  			FormatFlags: func() uint8 {
   821  				if codec.LittleEndian {
   822  					return 1
   823  				}
   824  				return 0
   825  			}(),
   826  			PCMSampleSize: uint8(codec.BitDepth),
   827  		})
   828  		if err != nil {
   829  			return err
   830  		}
   831  	}
   832  
   833  	if it.Codec.IsVideo() {
   834  		_, err = w.writeBox(&mp4.Btrt{ // <btrt/>
   835  			MaxBitrate: 1000000,
   836  			AvgBitrate: 1000000,
   837  		})
   838  		if err != nil {
   839  			return err
   840  		}
   841  	} else {
   842  		_, err = w.writeBox(&mp4.Btrt{ // <btrt/>
   843  			MaxBitrate: 128825,
   844  			AvgBitrate: 128825,
   845  		})
   846  		if err != nil {
   847  			return err
   848  		}
   849  	}
   850  
   851  	err = w.writeBoxEnd() // </*>
   852  	if err != nil {
   853  		return err
   854  	}
   855  
   856  	err = w.writeBoxEnd() // </stsd>
   857  	if err != nil {
   858  		return err
   859  	}
   860  
   861  	_, err = w.writeBox(&mp4.Stts{ // <stts/>
   862  	})
   863  	if err != nil {
   864  		return err
   865  	}
   866  
   867  	_, err = w.writeBox(&mp4.Stsc{ // <stsc/>
   868  	})
   869  	if err != nil {
   870  		return err
   871  	}
   872  
   873  	_, err = w.writeBox(&mp4.Stsz{ // <stsz/>
   874  	})
   875  	if err != nil {
   876  		return err
   877  	}
   878  
   879  	_, err = w.writeBox(&mp4.Stco{ // <stco/>
   880  	})
   881  	if err != nil {
   882  		return err
   883  	}
   884  
   885  	err = w.writeBoxEnd() // </stbl>
   886  	if err != nil {
   887  		return err
   888  	}
   889  
   890  	err = w.writeBoxEnd() // </minf>
   891  	if err != nil {
   892  		return err
   893  	}
   894  
   895  	err = w.writeBoxEnd() // </mdia>
   896  	if err != nil {
   897  		return err
   898  	}
   899  
   900  	err = w.writeBoxEnd() // </trak>
   901  	if err != nil {
   902  		return err
   903  	}
   904  
   905  	return nil
   906  }