//nolint:revive package mpeg import ( "fmt" "github.com/wader/fq/format" "github.com/wader/fq/pkg/decode" "github.com/wader/fq/pkg/interp" "github.com/wader/fq/pkg/scalar" ) var mpegASCFormat decode.Group var vorbisPacketFormat decode.Group func init() { interp.RegisterFormat(decode.Format{ Name: format.MPEG_ES, Description: "MPEG Elementary Stream", DecodeFn: esDecode, Dependencies: []decode.Dependency{ {Names: []string{format.MPEG_ASC}, Group: &mpegASCFormat}, {Names: []string{format.VORBIS_PACKET}, Group: &vorbisPacketFormat}, }, }) } const ( Forbidden0 = 0x00 ObjectDescrTag = 0x01 InitialObjectDescrTag = 0x02 ES_DescrTag = 0x03 DecoderConfigDescrTag = 0x04 DecSpecificInfoTag = 0x05 SLConfigDescrTag = 0x06 ContentIdentDescrTag = 0x07 SupplContentIdentDescrTag = 0x08 IPI_DescrPointerTag = 0x09 IPMP_DescrPointerTag = 0x0A IPMP_DescrTag = 0x0B QoS_DescrTag = 0x0C RegistrationDescrTag = 0x0D ES_ID_IncTag = 0x0E ES_ID_RefTag = 0x0F MP4_IOD_Tag = 0x10 MP4_OD_Tag = 0x11 IPL_DescrPointerRefTag = 0x12 ExtensionProfileLevelDescrTag = 0x13 profileLevelIndicationIndexDescrTag = 0x14 ContentClassificationDescrTag = 0x40 KeyWordDescrTag = 0x41 RatingDescrTag = 0x42 LanguageDescrTag = 0x43 ShortTextualDescrTag = 0x44 ExpandedTextualDescrTag = 0x45 ContentCreatorNameDescrTag = 0x46 ContentCreationDateDescrTag = 0x47 OCICreatorNameDescrTag = 0x48 OCICreationDateDescrTag = 0x49 SmpteCameraPositionDescrTag = 0x4A SegmentDescrTag = 0x4B MediaTimeDescrTag = 0x4C IPMP_ToolsListDescrTag = 0x60 IPMP_ToolTag = 0x61 M4MuxTimingDescrTag = 0x62 M4MuxCodeTableDescrTag = 0x63 ExtSLConfigDescrTag = 0x64 M4MuxBufferSizeDescrTag = 0x65 M4MuxIdentDescrTag = 0x66 DependencyPointerTag = 0x67 DependencyMarkerTag = 0x68 M4MuxChannelDescrTag = 0x69 Forbidden1 = 0xFF ) var odTagNames = scalar.UToSymStr{ Forbidden0: "Forbidden", ObjectDescrTag: "ObjectDescrTag", InitialObjectDescrTag: "InitialObjectDescrTag", ES_DescrTag: "ES_DescrTag", DecoderConfigDescrTag: "DecoderConfigDescrTag", DecSpecificInfoTag: "DecSpecificInfoTag", SLConfigDescrTag: "SLConfigDescrTag", ContentIdentDescrTag: "ContentIdentDescrTag", SupplContentIdentDescrTag: "SupplContentIdentDescrTag", IPI_DescrPointerTag: "IPI_DescrPointerTag", IPMP_DescrPointerTag: "IPMP_DescrPointerTag", IPMP_DescrTag: "IPMP_DescrTag", QoS_DescrTag: "QoS_DescrTag", RegistrationDescrTag: "RegistrationDescrTag", ES_ID_IncTag: "ES_ID_IncTag", ES_ID_RefTag: "ES_ID_RefTag", MP4_IOD_Tag: "MP4_IOD_Tag", MP4_OD_Tag: "MP4_OD_Tag", IPL_DescrPointerRefTag: "IPL_DescrPointerRefTag", ExtensionProfileLevelDescrTag: "ExtensionProfileLevelDescrTag", profileLevelIndicationIndexDescrTag: "profileLevelIndicationIndexDescrTag", ContentClassificationDescrTag: "ContentClassificationDescrTag", KeyWordDescrTag: "KeyWordDescrTag", RatingDescrTag: "RatingDescrTag", LanguageDescrTag: "LanguageDescrTag", ShortTextualDescrTag: "ShortTextualDescrTag", ExpandedTextualDescrTag: "ExpandedTextualDescrTag", ContentCreatorNameDescrTag: "ContentCreatorNameDescrTag", ContentCreationDateDescrTag: "ContentCreationDateDescrTag", OCICreatorNameDescrTag: "OCICreatorNameDescrTag", OCICreationDateDescrTag: "OCICreationDateDescrTag", SmpteCameraPositionDescrTag: "SmpteCameraPositionDescrTag", SegmentDescrTag: "SegmentDescrTag", MediaTimeDescrTag: "MediaTimeDescrTag", IPMP_ToolsListDescrTag: "IPMP_ToolsListDescrTag", IPMP_ToolTag: "IPMP_ToolTag", M4MuxTimingDescrTag: "M4MuxTimingDescrTag", M4MuxCodeTableDescrTag: "M4MuxCodeTableDescrTag", ExtSLConfigDescrTag: "ExtSLConfigDescrTag", M4MuxBufferSizeDescrTag: "M4MuxBufferSizeDescrTag", M4MuxIdentDescrTag: "M4MuxIdentDescrTag", DependencyPointerTag: "DependencyPointerTag", DependencyMarkerTag: "DependencyMarkerTag", M4MuxChannelDescrTag: "M4MuxChannelDescrTag", Forbidden1: "Forbidden", } const ( Forbidden = 0x00 ObjectDescriptorStream = 0x01 ClockReferenceStream = 0x02 SceneDescriptionStream = 0x03 VisualStream = 0x04 AudioStream = 0x05 MPEG7Stream = 0x06 IPMPStream = 0x07 ObjectContentInfoStream = 0x08 MPEGJStream = 0x09 InteractionStream = 0x0A IPMPToolStream = 0x0B ) var streamTypeNames = scalar.UToSymStr{ Forbidden: "Forbidden", ObjectDescriptorStream: "ObjectDescriptorStream", ClockReferenceStream: "ClockReferenceStream", SceneDescriptionStream: "SceneDescriptionStream", VisualStream: "VisualStream", AudioStream: "AudioStream", MPEG7Stream: "MPEG7Stream", IPMPStream: "IPMPStream", ObjectContentInfoStream: "ObjectContentInfoStream", MPEGJStream: "MPEGJStream", InteractionStream: "InteractionStream", IPMPToolStream: "IPMPToolStream", } func esLengthEncoding(d *decode.D) uint64 { v := uint64(0) nextByte := true for nextByte { nextByte = d.Bool() v = v<<7 | d.U7() } return v } //nolint:unparam func fieldODDecodeTag(d *decode.D, edc *esDecodeContext, name string, expectedTagID int, fn func(d *decode.D)) { d.FieldStruct(name, func(d *decode.D) { odDecodeTag(d, edc, expectedTagID, fn) }) } type esDecodeContext struct { currentDecoderConfig *format.MpegDecoderConfig decoderConfigs []format.MpegDecoderConfig } func odDecodeTag(d *decode.D, edc *esDecodeContext, _ int, fn func(d *decode.D)) { odDecoders := map[uint64]func(d *decode.D){ ES_DescrTag: func(d *decode.D) { d.FieldU16("es_id") streamDependencyFlag := d.FieldBool("stream_dependency_flag") urlFlag := d.FieldBool("url_flag") ocrStreamFlag := d.FieldBool("ocr_stream_flag") d.FieldU5("stream_priority") if streamDependencyFlag { d.FieldU16("dependency_on_es_id") } if urlFlag { urlLen := d.FieldU8("url_length") d.FieldUTF8("url", int(urlLen)) } if ocrStreamFlag { d.FieldU16("ocr_es_id") } fieldODDecodeTag(d, edc, "dec_config_descr", -1, nil) fieldODDecodeTag(d, edc, "sl_config_descr", -1, nil) }, DecoderConfigDescrTag: func(d *decode.D) { objectType := d.FieldU8("object_type_indication", format.MpegObjectTypeNames) edc.decoderConfigs = append(edc.decoderConfigs, format.MpegDecoderConfig{ ObjectType: int(objectType), }) edc.currentDecoderConfig = &edc.decoderConfigs[len(edc.decoderConfigs)-1] d.FieldU6("stream_type", streamTypeNames) d.FieldBool("upstream") d.FieldBool("specific_info_flag") d.FieldU24("buffer_size_db") d.FieldU32("max_bit_rate") d.FieldU32("avg_bit_rate") // TODO: seems to be optional if d.BitsLeft() > 0 { switch objectType { // TODO: vorbis is special case? case format.MPEGObjectTypeVORBIS: fieldODDecodeTag(d, edc, "decoder_specific_info", -1, func(d *decode.D) { numPackets := d.FieldU8("num_packets") // TODO: lacing packetLengths := []int64{} // Xiph-style lacing (similar to ogg) of n-1 packets, last is reset of block d.FieldArray("laces", func(d *decode.D) { for i := uint64(0); i < numPackets; i++ { l := d.FieldUFn("lace", func(d *decode.D) uint64 { var l uint64 for { n := d.U8() l += n if n < 255 { return l } } }) packetLengths = append(packetLengths, int64(l)) } }) d.FieldArray("packets", func(d *decode.D) { for _, l := range packetLengths { d.FieldFormatLen("packet", l*8, vorbisPacketFormat, nil) } d.FieldFormatLen("packet", d.BitsLeft(), vorbisPacketFormat, nil) }) }) default: switch format.MpegObjectTypeStreamType[objectType] { case format.MPEGStreamTypeAudio: fieldODDecodeTag(d, edc, "decoder_specific_info", -1, func(d *decode.D) { dv, v := d.FieldFormat("audio_specific_config", mpegASCFormat, nil) mpegASCout, ok := v.(format.MPEGASCOut) if dv != nil && !ok { panic(fmt.Sprintf("expected MPEGASCOut got %#+v", v)) } if edc.currentDecoderConfig != nil { edc.currentDecoderConfig.ASCObjectType = mpegASCout.ObjectType } }) case format.MPEGStreamTypeVideo: // TODO: case format.MPEGStreamTypeText: // TODO: } } } }, } // TODO: expectedTagID tagID := d.FieldU8("tag_id", odTagNames) tagLen := d.FieldUFn("length", esLengthEncoding) if fn != nil { d.FramedFn(int64(tagLen)*8, fn) } else if tagDecoder, ok := odDecoders[tagID]; ok { d.FramedFn(int64(tagLen)*8, tagDecoder) } else { d.FieldRawLen("data", d.BitsLeft()) } } func esDecode(d *decode.D, _ any) any { var edc esDecodeContext odDecodeTag(d, &edc, -1, nil) return format.MpegEsOut{DecoderConfigs: edc.decoderConfigs} }