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 }