From 2ffc2ba1b9c7ba2439a597f10122dbbad5a44815 Mon Sep 17 00:00:00 2001 From: Mattias Wadman Date: Tue, 20 Dec 2022 12:47:18 +0100 Subject: [PATCH] wip --- format/format.go | 4 +- format/mpeg/avc_sei.go | 130 +++++++++-------- format/mpeg/mpeg_cc_data.go | 42 ++++++ format/mpeg/mpeg_itu_t35.go | 269 ++++++++++++++++++++++++++++++++++++ 4 files changed, 384 insertions(+), 61 deletions(-) create mode 100644 format/mpeg/mpeg_cc_data.go create mode 100644 format/mpeg/mpeg_itu_t35.go diff --git a/format/format.go b/format/format.go index 9fb544d6b..7cbcb4b56 100644 --- a/format/format.go +++ b/format/format.go @@ -133,11 +133,13 @@ var ( MP3_Frame_XING = &decode.Group{Name: "mp3_frame_xing"} MP4 = &decode.Group{Name: "mp4"} MPEG_ASC = &decode.Group{Name: "mpeg_asc"} + MPEG_CC_Data = &decode.Group{Name: "mpeg_cc_data"} MPEG_ES = &decode.Group{Name: "mpeg_es"} - MPES_PES = &decode.Group{Name: "mpeg_pes"} + MPEG_ITU_T35 = &decode.Group{Name: "mpeg_itu_t35"} MPEG_PES_Packet = &decode.Group{Name: "mpeg_pes_packet"} MPEG_SPU = &decode.Group{Name: "mpeg_spu"} MPEG_TS = &decode.Group{Name: "mpeg_ts"} + MPES_PES = &decode.Group{Name: "mpeg_pes"} MsgPack = &decode.Group{Name: "msgpack"} Ogg = &decode.Group{Name: "ogg"} Ogg_Page = &decode.Group{Name: "ogg_page"} diff --git a/format/mpeg/avc_sei.go b/format/mpeg/avc_sei.go index 2a410d0da..b8af8f275 100644 --- a/format/mpeg/avc_sei.go +++ b/format/mpeg/avc_sei.go @@ -7,78 +7,84 @@ import ( "github.com/wader/fq/pkg/scalar" ) +var avcSeiMpegItuT35Group decode.Group + func init() { interp.RegisterFormat( format.AVC_SEI, &decode.Format{ Description: "H.264/AVC Supplemental Enhancement Information", DecodeFn: avcSEIDecode, + Dependencies: []decode.Dependency{ + {Groups: []*decode.Group{format.MPEG_ITU_T35}, Out: &avcSeiMpegItuT35Group}, + }, }) } const ( - avcSEIUserDataUnregistered = 5 + avcSEIUserDataRegisteredItuTT35 = 4 + avcSEIUserDataUnregistered = 5 ) var seiNames = scalar.UintMapSymStr{ - 0: "buffering_period", - 1: "pic_timing", - 2: "pan_scan_rect", - 3: "filler_payload", - 4: "user_data_registered_itu_t_t35", - avcSEIUserDataUnregistered: "user_data_unregistered", - 6: "recovery_point", - 7: "dec_ref_pic_marking_repetition", - 8: "spare_pic", - 9: "scene_info", - 10: "sub_seq_info", - 11: "sub_seq_layer_characteristics", - 12: "sub_seq_characteristics", - 13: "full_frame_freeze", - 14: "full_frame_freeze_release", - 15: "full_frame_snapshot", - 16: "progressive_refinement_segment_start", - 17: "progressive_refinement_segment_end", - 18: "motion_constrained_slice_group_set", - 19: "film_grain_characteristics", - 20: "deblocking_filter_display_preference", - 21: "stereo_video_info", - 22: "post_filter_hint", - 23: "tone_mapping_info", - 24: "scalability_info", - 25: "sub_pic_scalable_layer", - 26: "non_required_layer_rep", - 27: "priority_layer_info", - 28: "layers_not_present", - 29: "layer_dependency_change", - 30: "scalable_nesting", - 31: "base_layer_temporal_hrd", - 32: "quality_layer_integrity_check", - 33: "redundant_pic_property", - 34: "tl0_dep_rep_index", - 35: "tl_switching_point", - 36: "parallel_decoding_info", - 37: "mvc_scalable_nesting", - 38: "view_scalability_info", - 39: "multiview_scene_info", - 40: "multiview_acquisition_info", - 41: "non_required_view_component", - 42: "view_dependency_change", - 43: "operation_points_not_present", - 44: "base_view_temporal_hrd", - 45: "frame_packing_arrangement", - 46: "multiview_view_position", - 47: "display_orientation", - 48: "mvcd_scalable_nesting", - 49: "mvcd_view_scalability_info", - 50: "depth_representation_info", - 51: "three_dimensional_reference_displays_info", - 52: "depth_timing", - 53: "depth_sampling_info", - 54: "constrained_depth_parameter_set_identifier", - 56: "green_metadata", - 137: "mastering_display_colour_volume", - 181: "alternative_depth_info", + 0: "buffering_period", + 1: "pic_timing", + 2: "pan_scan_rect", + 3: "filler_payload", + avcSEIUserDataRegisteredItuTT35: "user_data_registered_itu_t_t35", + avcSEIUserDataUnregistered: "user_data_unregistered", + 6: "recovery_point", + 7: "dec_ref_pic_marking_repetition", + 8: "spare_pic", + 9: "scene_info", + 10: "sub_seq_info", + 11: "sub_seq_layer_characteristics", + 12: "sub_seq_characteristics", + 13: "full_frame_freeze", + 14: "full_frame_freeze_release", + 15: "full_frame_snapshot", + 16: "progressive_refinement_segment_start", + 17: "progressive_refinement_segment_end", + 18: "motion_constrained_slice_group_set", + 19: "film_grain_characteristics", + 20: "deblocking_filter_display_preference", + 21: "stereo_video_info", + 22: "post_filter_hint", + 23: "tone_mapping_info", + 24: "scalability_info", + 25: "sub_pic_scalable_layer", + 26: "non_required_layer_rep", + 27: "priority_layer_info", + 28: "layers_not_present", + 29: "layer_dependency_change", + 30: "scalable_nesting", + 31: "base_layer_temporal_hrd", + 32: "quality_layer_integrity_check", + 33: "redundant_pic_property", + 34: "tl0_dep_rep_index", + 35: "tl_switching_point", + 36: "parallel_decoding_info", + 37: "mvc_scalable_nesting", + 38: "view_scalability_info", + 39: "multiview_scene_info", + 40: "multiview_acquisition_info", + 41: "non_required_view_component", + 42: "view_dependency_change", + 43: "operation_points_not_present", + 44: "base_view_temporal_hrd", + 45: "frame_packing_arrangement", + 46: "multiview_view_position", + 47: "display_orientation", + 48: "mvcd_scalable_nesting", + 49: "mvcd_view_scalability_info", + 50: "depth_representation_info", + 51: "three_dimensional_reference_displays_info", + 52: "depth_timing", + 53: "depth_sampling_info", + 54: "constrained_depth_parameter_set_identifier", + 56: "green_metadata", + 137: "mastering_display_colour_volume", + 181: "alternative_depth_info", } var ( @@ -108,10 +114,14 @@ func avcSEIDecode(d *decode.D) any { d.FramedFn(int64(payloadSize)*8, func(d *decode.D) { switch payloadType { + case avcSEIUserDataRegisteredItuTT35: + d.FieldFormat("data", &avcSeiMpegItuT35Group, nil) case avcSEIUserDataUnregistered: d.FieldRawLen("uuid", 16*8, userDataUnregisteredNames) + d.FieldRawLen("data", d.BitsLeft()) + default: + d.FieldRawLen("data", d.BitsLeft()) } - d.FieldRawLen("data", d.BitsLeft()) }) d.FieldRawLen("rbsp_trailing_bits", d.BitsLeft()) diff --git a/format/mpeg/mpeg_cc_data.go b/format/mpeg/mpeg_cc_data.go new file mode 100644 index 000000000..c5671ca0c --- /dev/null +++ b/format/mpeg/mpeg_cc_data.go @@ -0,0 +1,42 @@ +package mpeg + +// TODO: rename? eia something? +// EIA-708 cc_data +// https://shop.cta.tech/products/digital-television-dtv-closed-captioning + +import ( + "github.com/wader/fq/format" + "github.com/wader/fq/pkg/decode" + "github.com/wader/fq/pkg/interp" +) + +func init() { + interp.RegisterFormat( + format.MPEG_CC_Data, + &decode.Format{ + Description: "EIA-708 cc_data closed captioning data", + DecodeFn: mpegCcDataDecode, + }) +} + +func mpegCcDataDecode(d *decode.D) any { + d.FieldU1("reserved0") + d.FieldBool("process_cc_data_flag") + d.FieldU1("zero_bit") + ccCount := d.FieldU5("cc_count") + d.FieldU8("reserved1") + d.FieldArray("cc", func(d *decode.D) { + for i := 0; i < int(ccCount); i++ { + d.FieldStruct("cc", func(d *decode.D) { + d.FieldU1("one_bit") + d.FieldU4("reserved0") + d.FieldBool("cc_valid") + d.FieldU2("cc_type") + d.FieldU8("cc_data_1") + d.FieldU8("cc_data_2") + }) + } + }) + + return nil +} diff --git a/format/mpeg/mpeg_itu_t35.go b/format/mpeg/mpeg_itu_t35.go new file mode 100644 index 000000000..4b3db5610 --- /dev/null +++ b/format/mpeg/mpeg_itu_t35.go @@ -0,0 +1,269 @@ +package mpeg + +// TODO: should not be named mpeg_*? just iti_*? + +// ANSI/SCTE 128-1 2020 +// https://wagtail-prod-storage.s3.amazonaws.com/documents/ANSI_SCTE-128-1-2020-1586877225672.pdf + +import ( + "github.com/wader/fq/format" + "github.com/wader/fq/pkg/decode" + "github.com/wader/fq/pkg/interp" + "github.com/wader/fq/pkg/scalar" +) + +var ituTT35MpegCCDataGroup decode.Group + +func init() { + interp.RegisterFormat( + format.MPEG_ITU_T35, + &decode.Format{ + Description: "ITU-T.35 closed captioning data", + Dependencies: []decode.Dependency{ + {Groups: []*decode.Group{format.MPEG_CC_Data}, Out: &ituTT35MpegCCDataGroup}, + }, + DecodeFn: mpegItuT35SDecode, + }) +} + +// TODO: sym some shorter string name? +var itutt35CountryCodesMap = scalar.UintMapSymStr{ + 0x00: "Japan", + 0x01: "Albania", + 0x02: "Algeria", + 0x03: "American Samoa", + 0x04: "Germany", + 0x05: "Anguilla", + 0x06: "Antigua and Barbuda", + 0x07: "Argentina", + 0x08: "Ascension (see Saint Helena)", + 0x09: "Australia", + 0x0a: "Austria", + 0x0b: "Bahamas", + 0x0c: "Bahrain", + 0x0d: "Bangladesh", + 0x0e: "Barbados", + 0x0f: "Belgium", + 0x10: "Belize", + 0x11: "Benin", + 0x12: "Bermuda", + 0x13: "Bhutan", + 0x14: "Bolivia", + 0x15: "Botswana", + 0x16: "Brazil", + 0x17: "British Antarctic Territory", + 0x18: "British Indian Ocean Territory (Diego Garcia)", + 0x19: "British Virgin Islands", + 0x1a: "Brunei Darussalam", + 0x1b: "Bulgaria", + 0x1c: "Myanmar", + 0x1d: "Burundi", + 0x1e: "Belarus", + 0x1f: "Cameroon", + 0x20: "Canada", + 0x21: "Cape Verde", + 0x22: "Cayman Islands", + 0x23: "Central African Rep.", + 0x24: "Chad", + 0x25: "Chile", + 0x26: "China", + 0x27: "Colombia", + 0x28: "Comoros", + 0x29: "Congo", + 0x2a: "Cook Islands", + 0x2b: "Costa Rica", + 0x2c: "Cuba", + 0x2d: "Cyprus", + 0x2e: "Czech Rep.", + 0x2f: "Cambodia", + 0x30: "Dem. People's Rep. of Korea", + 0x31: "Denmark", + 0x32: "Djibouti", + 0x33: "Dominican Rep.", + 0x34: "Dominica", + 0x35: "Ecuador", + 0x36: "Egypt", + 0x37: "El Salvador", + 0x38: "Equatorial Guinea", + 0x39: "Ethiopia", + 0x3a: "Falkland Islands (Malvinas)", + 0x3b: "Fiji", + 0x3c: "Finland", + 0x3d: "France", + 0x3e: "French Polynesia", + 0x3f: "(Available)", + 0x40: "Gabon", + 0x41: "Gambia", + 0x42: "Germany", + 0x43: "Angola", + 0x44: "Ghana", + 0x45: "Gibraltar", + 0x46: "Greece", + 0x47: "Grenada", + 0x48: "Guam", + 0x49: "Guatemala", + 0x4a: "Guernsey", + 0x4b: "Guinea", + 0x4c: "Guinea-Bissau", + 0x4d: "Guyana", + 0x4e: "Haiti", + 0x4f: "Honduras", + 0x50: "Hong Kong: China", + 0x51: "Hungary", + 0x52: "Iceland", + 0x53: "India", + 0x54: "Indonesia", + 0x55: "Iran (Islamic Republic of)", + 0x56: "Iraq", + 0x57: "Ireland", + 0x58: "Israel", + 0x59: "Italy", + 0x5a: "Côte d'Ivoire", + 0x5b: "Jamaica", + 0x5c: "Afghanistan", + 0x5d: "Jersey", + 0x5e: "Jordan", + 0x5f: "Kenya", + 0x60: "Kiribati", + 0x61: "Korea (Rep. of)", + 0x62: "Kuwait", + 0x63: "Lao P.D.R.", + 0x64: "Lebanon", + 0x65: "Lesotho", + 0x66: "Liberia", + 0x67: "Libya", + 0x68: "Liechtenstein", + 0x69: "Luxembourg", + 0x6a: "Macao: China", + 0x6b: "Madagascar", + 0x6c: "Malaysia", + 0x6d: "Malawi", + 0x6e: "Maldives", + 0x6f: "Mali", + 0x70: "Malta", + 0x71: "Mauritania", + 0x72: "Mauritius", + 0x73: "Mexico", + 0x74: "Monaco", + 0x75: "Mongolia", + 0x76: "Montserrat", + 0x77: "Morocco", + 0x78: "Mozambique", + 0x79: "Nauru", + 0x7a: "Nepal", + 0x7b: "Netherlands", + 0x7c: "Curaçao", + 0x7d: "New Caledonia", + 0x7e: "New Zealand", + 0x7f: "Nicaragua", + 0x80: "Niger", + 0x81: "Nigeria", + 0x82: "Norway", + 0x83: "Oman", + 0x84: "Pakistan", + 0x85: "Panama", + 0x86: "Papua New Guinea", + 0x87: "Paraguay", + 0x88: "Peru", + 0x89: "Philippines", + 0x8a: "Poland", + 0x8b: "Portugal", + 0x8c: "Puerto Rico", + 0x8d: "Qatar", + 0x8e: "Romania", + 0x8f: "Rwanda", + 0x90: "Saint Kitts and Nevis", + 0x91: "Saint Croix", + 0x92: "Saint Helena: Ascension and Tristan da Cuhna", + 0x93: "Saint Lucia", + 0x94: "San Marino", + 0x95: "Saint Thomas", + 0x96: "Sao Tome and Principe", + 0x97: "Saint Vincent and the Grenadines", + 0x98: "Saudi Arabia", + 0x99: "Senegal", + 0x9a: "Seychelles", + 0x9b: "Sierra Leone", + 0x9c: "Singapore", + 0x9d: "Solomon Islands", + 0x9e: "Somalia", + 0x9f: "South Africa", + 0xa0: "Spain", + 0xa1: "Sri Lanka", + 0xa2: "Sudan", + 0xa3: "Suriname", + 0xa4: "Swaziland", + 0xa5: "Sweden", + 0xa6: "Switzerland", + 0xa7: "Syrian Arab Republic", + 0xa8: "Tanzania", + 0xa9: "Thailand", + 0xaa: "Togo", + 0xab: "Tonga", + 0xac: "Trinidad and Tobago", + 0xad: "Tunisia", + 0xae: "Turkey", + 0xaf: "Turks and Caicos Islands", + 0xb0: "Tuvalu", + 0xb1: "Uganda", + 0xb2: "Ukraine", + 0xb3: "United Arab Emirates", + 0xb4: "United Kingdom", + 0xb5: "United States", + 0xb6: "Burkina Faso", + 0xb7: "Uruguay", + 0xb8: "Russian Federation", + 0xb9: "Vanuatu", + 0xba: "Vatican", + 0xbb: "Venezuela", + 0xbc: "Viet Nam", + 0xbd: "Wallis and Futuna", + 0xbe: "Samoa", + 0xbf: "Yemen", + 0xc0: "Yemen", + 0xc1: "Serbia", + 0xc2: "Dem. Rep. of the Congo", + 0xc3: "Zambia", + 0xc4: "Zimbabwe", + 0xc5: "Slovakia", + 0xc6: "Slovenia", + 0xc7: "Lithuania", + 0xc8: "Montenegro", + 0xc9: "(Available)", + 0xca: "(Available)", + 0xcb: "(Available)", + 0xcc: "(Available)", + 0xcd: "(Available)", + 0xce: "(Available)", + 0xcf: "(Available)", + 0xff: "Escape code to extension list", +} + +// TODO: sym some shorter string name? +var atsc1UserDataTypeCodeMap = scalar.UintMap{ + 0x03: scalar.Uint{Sym: "dtvcc", Description: "DTVCC Captions (EIA-608, EIA-708)"}, + 0x04: scalar.Uint{Sym: "additional_608_data", Description: "Additional 608 Data"}, + 0x05: scalar.Uint{Sym: "luma_pam", Description: "Luma PAM"}, + 0x06: scalar.Uint{Sym: "afd", Description: "AFD"}, + // TODO: more and there seems to be conflicts like for 0x7 and 0x8? +} + +// 8.1.1. SEI T.35 Construct for Auxiliary Data Syntax +func mpegItuT35SDecode(d *decode.D) any { + d.FieldU8("country_code", itutt35CountryCodesMap) + d.FieldU16("provider_code") // TODO: per country code tables? + userIdentifier := d.FieldUTF8("user_identifier", 4) + + switch userIdentifier { + case "GA94": + d.FieldStruct("user_structure", func(d *decode.D) { + d.FieldU8("user_data_type_code", atsc1UserDataTypeCodeMap) + d.FieldFormat("user_data_type_structure", &ituTT35MpegCCDataGroup, nil) + }) + default: + d.FieldRawLen("user_structure", d.BitsLeft()) + + } + + return nil +}