2 * Routines for ID3v2 dissection
3 * Copyright 2022, Jeff Morriss <jeff.morriss.ws [AT] gmai.com>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
13 * ID3v2 offers a flexible way of storing audio metadata within the audio
14 * file itself. It's the de facto standard for storing metadata in MP3s
15 * but is also supported in other file types including AIFF, WAV, and MP4.
17 * This dissector was written against ID3 v2.4 and v2.3:
18 * https://id3.org/id3v2.4.0-structure
19 * https://id3.org/id3v2.4.0-frames
20 * https://id3.org/id3v2.3.0
24 #include <epan/packet.h>
25 #include <epan/expert.h>
26 #include <wsutil/array.h>
27 #include <wsutil/mpeg-audio.h>
29 void proto_reg_handoff_id3v2(void);
30 void proto_register_id3v2(void);
32 static dissector_table_t media_type_dissector_table
;
34 static int proto_id3v2
;
35 static int hf_id3v2_file_id
;
36 static int hf_id3v2_version
;
37 static int hf_id3v2_flags
;
38 static int hf_id3v2_size
;
39 static int hf_id3v2_frame
;
40 static int hf_id3v2_frame_id
;
41 static int hf_id3v2_frame_size
;
42 static int hf_id3v2_frame_flags
;
43 static int hf_id3v2_frame_text_encoding
;
44 static int hf_id3v2_frame_text_description
;
45 static int hf_id3v2_frame_text_value
;
46 static int hf_id3v2_frame_ufi_owner
;
47 static int hf_id3v2_frame_ufi_id
;
48 static int hf_id3v2_frame_apic_mime_type
;
49 static int hf_id3v2_frame_apic_picture_type
;
50 static int hf_id3v2_frame_apic_description
;
51 static int hf_id3v2_frame_private
;
52 static int hf_id3v2_frame_comment_language
;
53 static int hf_id3v2_frame_comment_description
;
54 static int hf_id3v2_frame_comment_text
;
55 static int hf_id3v2_undecoded
;
56 static int hf_id3v2_padding
;
59 static int ett_id3v2_frame
;
61 static expert_field ei_id3v2_undecoded
;
63 #define ID3V2_MIN_LENGTH 10
65 /* Technically this is version + revision; break it up into multiple fields? */
66 static const value_string id3v2_version_values
[] = {
72 /* The below are v2.3 unless otherwise noted */
73 static const string_string id3v2_tag_names
[] = {
74 { "AENC", "Audio encryption" },
75 { "APIC", "Attached picture" },
76 { "ASPI", "Audio seek point index" },
77 { "COMM", "Comments" },
78 { "COMR", "Commercial frame" },
79 { "ENCR", "Encryption method registration" },
80 { "EQUA", "Equalization" }, /* v2.3 */
81 { "EQU2", "Equalization (2)" }, /* v2.4 */
82 { "ETCO", "Event timing codes" },
83 { "GEOB", "General encapsulated object" },
84 { "GRID", "Group identification registration" },
85 { "IPLS", "Involved people list" }, /* v2.3 */
86 { "TIPL", "Involved people list" }, /* v2.4 */
87 { "LINK", "Linked information" },
88 { "MCDI", "Music CD identifier" },
89 { "MLLT", "MPEG location lookup table" },
90 { "OWNE", "Ownership frame" },
91 { "PRIV", "Private frame" },
92 { "PCNT", "Play counter" },
93 { "POPM", "Popularimeter" },
94 { "POSS", "Position synchronisation frame" },
95 { "RBUF", "Recommended buffer size" },
96 { "RVAD", "Relative volume adjustment" }, /* v2.3 */
97 { "RVA2", "Relative volume adjustment (2)" }, /* v2.4 */
99 { "SEEK", "Seek frame" }, /* v2.4 */
100 { "SIGN", "Signature frame" }, /* v2.4 */
101 { "SYLT", "Synchronized lyric/text" },
102 { "SYTC", "Synchronized tempo codes" },
103 { "TALB", "Album/Movie/Show title" },
104 { "TBPM", "BPM (beats per minute)" },
105 { "TCOM", "Composer" },
106 { "TCON", "Content type" },
107 { "TCOP", "Copyright message" },
109 { "TDEN", "Encoding time" },
110 { "TDLY", "Playlist delay" },
111 { "TDRC", "Recording time" }, /* v2.4 */
112 { "TDRL", "Release time" }, /* v2.4 */
113 { "TDTG", "Tagging time" }, /* v2.4 */
114 { "TENC", "Encoded by" },
115 { "TEXT", "Lyricist/Text writer" },
116 { "TFLT", "File type" },
118 { "TIT1", "Content group description" },
119 { "TIT2", "Title/songname/content description" },
120 { "TIT3", "Subtitle/Description refinement" },
121 { "TKEY", "Initial key" },
122 { "TLAN", "Language(s)" },
123 { "TLEN", "Length" },
124 { "TMED", "Media type" },
125 { "TMOO", "Mood" }, /* v2.4 */
126 { "TMCL", "Musicians credits list" }, /* v2.4 */
127 { "TOAL", "Original album/movie/show title" },
128 { "TOFN", "Original filename" },
129 { "TOLY", "Original lyricist(s)/text writer(s)" },
130 { "TOPE", "Original artist(s)/performer(s)" },
131 { "TORY", "Original release year" }, /* v2.3 */
132 { "TDOR", "Original release time" }, /* v2.4 */
133 { "TOWN", "File owner/licensee" },
134 { "TPE1", "Lead performer(s)/Soloist(s)" },
135 { "TPE2", "Band/orchestra/accompaniment" },
136 { "TPE3", "Conductor/performer refinement" },
137 { "TPE4", "Interpreted, remixed, or otherwise modified by" },
138 { "TPOS", "Part of a set" },
139 { "TPUB", "Publisher" },
140 { "TPRO", "Produced notice" },
141 { "TRCK", "Track number/Position in set" },
142 { "TRDA", "Recording dates" },
143 { "TRSN", "Internet radio station name" },
144 { "TRSO", "Internet radio station owner" },
145 { "TSOA", "Album sort order" }, /* v2.4 */
146 { "TSO2", "Album artist sort order" }, /* iTunes */
147 { "TSOP", "Performer sort order" }, /* v2.4 */
148 { "TSOT", "Title sort order" }, /* v2.4 */
150 { "TSRC", "ISRC (international standard recording code)" },
151 { "TSSE", "Software/Hardware and settings used for encoding" },
152 { "TSST", "Set subtitle" }, /* v2.4 */
154 { "TXXX", "User defined" },
155 { "UFID", "Unique file identifier" },
156 { "USER", "Terms of use" },
157 { "USLT", "Unsynchronized lyric/text transcription" },
158 { "WCOM", "Commercial information" },
159 { "WCOP", "Copyright/Legal information" },
160 { "WOAF", "Official audio file webpage" },
161 { "WOAR", "Official artist/performer webpage" },
162 { "WOAS", "Official audio source webpage" },
163 { "WORS", "Official internet radio station homepage" },
164 { "WPAY", "Payment" },
165 { "WPUB", "Publishers official webpage" },
166 { "WXXX", "User defined URL link frame" },
169 static const value_string id3v2_apic_types
[] = {
171 { 0x01, "32x32 pixels 'file icon' (PNG only)" },
172 { 0x02, "Other file icon" },
173 { 0x03, "Cover (front)" },
174 { 0x04, "Cover (back)" },
175 { 0x05, "Leaflet page" },
176 { 0x06, "Media (e.g. label side of CD)" },
177 { 0x07, "Lead artist/lead performer/soloist" },
178 { 0x08, "Artist/performer" },
179 { 0x09, "Conductor" },
180 { 0x0A, "Band/Orchestra" },
181 { 0x0B, "Composer" },
182 { 0x0C, "Lyricist/text writer" },
183 { 0x0D, "Recording Location" },
184 { 0x0E, "During recording" },
185 { 0x0F, "During performance" },
186 { 0x10, "Movie/video screen capture" },
187 { 0x11, "A bright coloured fish" },
188 { 0x12, "Illustration" },
189 { 0x13, "Band/artist logotype" },
190 { 0x14, "Publisher/Studio logotype" },
193 static const value_string id3v2_text_encoding_values
[] = {
194 { 0x00, "ISO-8859-1" },
195 { 0x01, "UTF-16 with BOM" },
196 { 0x02, "UTF-16BE" },
201 id3v2_decode_encoding(uint8_t id3_encoding
)
205 switch (id3_encoding
) {
207 encoding
= ENC_ISO_8859_1
;
210 encoding
= ENC_UTF_16
|ENC_BOM
|ENC_LITTLE_ENDIAN
;
213 encoding
= ENC_UTF_16
|ENC_BIG_ENDIAN
;
217 encoding
= ENC_UTF_8
;
225 id3v2_dissect_textz_item(wmem_allocator_t
*scope
, tvbuff_t
*tvb
, proto_tree
*tree
, unsigned *offset
, uint8_t id3_encoding
, int hf
)
229 unsigned text_length
;
231 encoding
= id3v2_decode_encoding(id3_encoding
);
233 text_value
= tvb_get_stringz_enc(scope
, tvb
, *offset
, &text_length
, encoding
);
234 proto_tree_add_item(tree
, hf
, tvb
, *offset
, text_length
, encoding
);
235 *offset
+= text_length
;
241 id3v2_dissect_text_item(wmem_allocator_t
*scope
, tvbuff_t
*tvb
, proto_tree
*tree
, unsigned *offset
, unsigned end
, uint8_t id3_encoding
, int hf
)
246 encoding
= id3v2_decode_encoding(id3_encoding
);
248 text_value
= tvb_get_string_enc(scope
, tvb
, *offset
, (end
- *offset
), encoding
);
249 proto_tree_add_item(tree
, hf
, tvb
, *offset
, (end
- *offset
), encoding
);
255 dissect_id3v2_comment_frame(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, unsigned offset
, unsigned length
, proto_item
*pi
)
257 uint8_t id3_encoding
;
258 unsigned end
= offset
+ length
;
261 id3_encoding
= tvb_get_uint8(tvb
, offset
);
262 proto_tree_add_item(tree
, hf_id3v2_frame_text_encoding
, tvb
, offset
, 1, ENC_NA
);
265 proto_tree_add_item(tree
, hf_id3v2_frame_comment_language
, tvb
, offset
, 3, ENC_ISO_8859_1
);
268 id3v2_dissect_textz_item(pinfo
->pool
, tvb
, tree
, &offset
, id3_encoding
, hf_id3v2_frame_comment_description
);
270 comment_value
= id3v2_dissect_text_item(pinfo
->pool
, tvb
, tree
, &offset
, end
, id3_encoding
, hf_id3v2_frame_comment_text
);
271 proto_item_append_text(pi
, ": %s", comment_value
);
275 dissect_id3v2_apic_frame(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, unsigned offset
, unsigned length
)
277 uint8_t id3_encoding
;
278 unsigned end
= offset
+ length
;
282 id3_encoding
= tvb_get_uint8(tvb
, offset
);
283 proto_tree_add_item(tree
, hf_id3v2_frame_text_encoding
, tvb
, offset
, 1, ENC_NA
);
286 mime_type
= id3v2_dissect_textz_item(pinfo
->pool
, tvb
, tree
, &offset
, id3_encoding
, hf_id3v2_frame_apic_mime_type
);
288 proto_tree_add_item(tree
, hf_id3v2_frame_apic_picture_type
, tvb
, offset
, 1, ENC_NA
);
291 id3v2_dissect_textz_item(pinfo
->pool
, tvb
, tree
, &offset
, id3_encoding
, hf_id3v2_frame_apic_description
);
293 image_tvb
= tvb_new_subset_length(tvb
, offset
, (end
- offset
));
294 dissector_try_string_with_data(media_type_dissector_table
, mime_type
, image_tvb
, pinfo
, tree
, true, NULL
);
298 dissect_id3v2_text_frame(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_item
*item
, proto_tree
*tree
, unsigned offset
, unsigned length
, bool is_txxx
)
300 uint8_t id3_encoding
;
302 unsigned end
= offset
+ length
;
304 id3_encoding
= tvb_get_uint8(tvb
, offset
);
305 proto_tree_add_item(tree
, hf_id3v2_frame_text_encoding
, tvb
, offset
, 1, ENC_NA
);
309 /* This is a user-defined text frame (contains a description and a value) */
311 text_value
= id3v2_dissect_textz_item(pinfo
->pool
, tvb
, tree
, &offset
, id3_encoding
, hf_id3v2_frame_text_description
);
312 proto_item_append_text(item
, ": %s", text_value
);
315 text_value
= id3v2_dissect_text_item(pinfo
->pool
, tvb
, tree
, &offset
, end
, id3_encoding
, hf_id3v2_frame_text_value
);
316 proto_item_append_text(item
, ": %s", text_value
);
322 dissect_id3v2_frame(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, unsigned offset
, uint8_t id3_version
)
324 proto_item
*frame_item
;
325 proto_tree
*frame_tree
;
329 frame_id
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, 4, ENC_ISO_8859_1
);
331 if (strlen(frame_id
) == 0) {
332 proto_tree_add_item(tree
, hf_id3v2_padding
, tvb
, offset
, -1, ENC_NA
);
333 return offset
+ tvb_reported_length_remaining(tvb
, offset
);
336 frame_item
= proto_tree_add_item(tree
, hf_id3v2_frame
, tvb
, offset
, -1, ENC_NA
);
337 frame_tree
= proto_item_add_subtree(frame_item
, ett_id3v2_frame
);
339 proto_tree_add_item(frame_tree
, hf_id3v2_frame_id
, tvb
, offset
, 4, ENC_ISO_8859_1
);
341 proto_item_set_text(frame_item
, "%s", str_to_str(frame_id
, id3v2_tag_names
, "Unknown: %s"));
343 if (id3_version
== 0x04)
344 size
= decode_synchsafe_int(tvb_get_uint32(tvb
, offset
, ENC_BIG_ENDIAN
));
346 size
= tvb_get_uint32(tvb
, offset
, ENC_BIG_ENDIAN
);
347 proto_tree_add_uint(frame_tree
, hf_id3v2_frame_size
, tvb
, offset
, 4, size
);
348 /* `size` does not include the 10-byte header */
349 proto_item_set_len(frame_item
, size
+10);
352 /* TODO: decode each flag */
353 proto_tree_add_item(frame_tree
, hf_id3v2_frame_flags
, tvb
, offset
, 2, ENC_NA
);
356 if (frame_id
[0] == 'T') {
359 tv
= dissect_id3v2_text_frame(tvb
, pinfo
, frame_item
, frame_tree
, offset
, size
, !strcmp(frame_id
, "TXXX"));
362 if (!strcmp(frame_id
, "TIT2"))
363 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "Title: %s, ", tv
);
364 if (!strcmp(frame_id
, "TPE1"))
365 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "Artist: %s, ", tv
);
366 } else if (!strcmp(frame_id
, "UFID")) {
367 unsigned text_length
;
370 text_value
= tvb_get_stringz_enc(pinfo
->pool
, tvb
, offset
, &text_length
, ENC_UTF_8
);
371 proto_tree_add_item(frame_tree
, hf_id3v2_frame_ufi_owner
, tvb
, offset
, text_length
, ENC_ISO_8859_1
);
372 offset
+= text_length
;
373 proto_item_append_text(frame_item
, " (Owner: %s)", text_value
);
375 DISSECTOR_ASSERT(size
>= text_length
);
376 proto_tree_add_item(frame_tree
, hf_id3v2_frame_ufi_id
, tvb
, offset
, size
-text_length
, ENC_NA
);
377 offset
+= (size
-text_length
);
378 } else if (!strcmp(frame_id
, "APIC")) {
379 dissect_id3v2_apic_frame(tvb
, pinfo
, frame_tree
, offset
, size
);
381 } else if (!strcmp(frame_id
, "COMM")) {
382 dissect_id3v2_comment_frame(tvb
, pinfo
, frame_tree
, offset
, size
, frame_item
);
384 } else if (!strcmp(frame_id
, "PRIV")) {
385 proto_tree_add_item(frame_tree
, hf_id3v2_frame_private
, tvb
, offset
, size
, ENC_NA
);
390 /* TODO: decode the rest */
391 pi
= proto_tree_add_item(frame_tree
, hf_id3v2_undecoded
, tvb
, offset
, size
, ENC_NA
);
392 expert_add_info(pinfo
, pi
, &ei_id3v2_undecoded
);
400 dissect_id3v2(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
402 proto_item
*id3v2_item
;
403 proto_tree
*id3v2_tree
;
409 /* Check that the packet is long enough for it to belong to us. */
410 if (tvb_reported_length(tvb
) < ID3V2_MIN_LENGTH
)
413 /* Check if the first 3 bytes are "ID3" */
414 if (tvb_get_uint24(tvb
, 0, ENC_BIG_ENDIAN
) != 0x494433)
417 /* Check if any of the high bits of the (synchsafe int) size are set */
418 if (tvb_get_uint8(tvb
, 7) & 0x80 ||
419 tvb_get_uint8(tvb
, 8) & 0x80 ||
420 tvb_get_uint8(tvb
, 9) & 0x80 ||
421 tvb_get_uint8(tvb
, 10) & 0x80)
424 /* Looks like this is ID3v2... */
425 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "ID3v2");
426 col_clear(pinfo
->cinfo
, COL_INFO
);
428 size
= decode_synchsafe_int(tvb_get_uint32(tvb
, 6, ENC_BIG_ENDIAN
));
429 /* `size` does not include the 10-byte header */
430 id3v2_tvb
= tvb_new_subset_length(tvb
, offset
, size
+10);
431 id3v2_item
= proto_tree_add_item(tree
, proto_id3v2
, id3v2_tvb
, offset
, tvb_captured_length(id3v2_tvb
), ENC_NA
);
432 id3v2_tree
= proto_item_add_subtree(id3v2_item
, ett_id3v2
);
434 proto_tree_add_item(id3v2_tree
, hf_id3v2_file_id
, id3v2_tvb
, offset
, 3, ENC_ISO_8859_1
);
437 id3_version
= tvb_get_uint8(tvb
, offset
); /* Fetch just the major version info */
438 proto_tree_add_item(id3v2_tree
, hf_id3v2_version
, id3v2_tvb
, offset
, 2, ENC_BIG_ENDIAN
);
441 /* TODO: decode each flag */
442 proto_tree_add_item(id3v2_tree
, hf_id3v2_flags
, id3v2_tvb
, offset
, 1, ENC_NA
);
445 proto_tree_add_uint(id3v2_tree
, hf_id3v2_size
, id3v2_tvb
, offset
, 4, size
);
448 /* TODO: detect and dissect extended header */
450 while(tvb_reported_length_remaining(id3v2_tvb
, offset
)) {
451 offset
= dissect_id3v2_frame(id3v2_tvb
, pinfo
, id3v2_tree
, offset
, id3_version
);
454 /* TODO: detect and dissect footer */
456 return tvb_reported_length(id3v2_tvb
);
460 proto_register_id3v2(void)
462 expert_module_t
*expert_id3v2
;
464 static hf_register_info hf
[] = {
466 { "File Identifier", "id3v2.file_id",
467 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}},
469 { "Version", "id3v2.version",
470 FT_UINT16
, BASE_HEX
, VALS(id3v2_version_values
), 0, NULL
, HFILL
}},
472 { "Flags", "id3v2.flags",
473 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}},
475 { "Size", "id3v2.size",
476 FT_UINT32
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}},
478 { "Frame", "id3v2.frame",
479 FT_NONE
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}},
480 { &hf_id3v2_frame_id
,
481 { "Frame Identifier", "id3v2.frame.id",
482 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}},
483 { &hf_id3v2_frame_size
,
484 { "Frame Size", "id3v2.frame.size",
485 FT_UINT8
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}},
486 { &hf_id3v2_frame_flags
,
487 { "Frame Flags", "id3v2.frame.flags",
488 FT_UINT16
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}},
489 { &hf_id3v2_frame_text_encoding
,
490 { "Text Encoding", "id3v2.frame.text_encoding",
491 FT_UINT8
, BASE_HEX
, VALS(id3v2_text_encoding_values
), 0, NULL
, HFILL
}},
492 { &hf_id3v2_frame_text_description
,
493 { "Text description", "id3v2.frame.text_description",
494 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}},
495 { &hf_id3v2_frame_text_value
,
496 { "Text value", "id3v2.frame.text_value",
497 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}},
498 { &hf_id3v2_frame_ufi_owner
,
499 { "Unique file identifier owner", "id3v2.unique_file_identifier_owner",
500 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}},
501 { &hf_id3v2_frame_ufi_id
,
502 { "Unique file identifier", "id3v2.unique_file_identifier",
503 FT_BYTES
, BASE_SHOW_UTF_8_PRINTABLE
, NULL
, 0, NULL
, HFILL
}},
504 { &hf_id3v2_frame_apic_mime_type
,
505 { "Attached picture MIME type", "id3v2.apic.mime_type",
506 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}},
507 { &hf_id3v2_frame_apic_picture_type
,
508 { "Attached picture type", "id3v2.apic.type",
509 FT_UINT8
, BASE_NONE
, VALS(id3v2_apic_types
), 0, NULL
, HFILL
}},
510 { &hf_id3v2_frame_apic_description
,
511 { "Attached picture description", "id3v2.apic.description",
512 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}},
513 { &hf_id3v2_frame_private
,
514 { "Private frame", "id3v2.private",
515 FT_BYTES
, BASE_SHOW_UTF_8_PRINTABLE
, NULL
, 0, NULL
, HFILL
}},
516 { &hf_id3v2_frame_comment_language
,
517 { "Comment language", "id3v2.comment.language",
518 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}},
519 { &hf_id3v2_frame_comment_description
,
520 { "Comment description", "id3v2.comment.description",
521 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}},
522 { &hf_id3v2_frame_comment_text
,
523 { "Comment text", "id3v2.comment.text",
524 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}},
525 { &hf_id3v2_undecoded
,
526 { "Undecoded frame", "id3v2.undecoded",
527 FT_BYTES
, BASE_SHOW_UTF_8_PRINTABLE
, NULL
, 0, NULL
, HFILL
}},
529 { "Padding", "id3v2.padding",
530 FT_BYTES
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}},
533 static int *ett
[] = {
538 static ei_register_info ei
[] = {
539 { &ei_id3v2_undecoded
,
540 { "id3v2.expert.undecoded", PI_UNDECODED
, PI_NOTE
,
541 "Undecoded frame", EXPFILL
}
545 proto_id3v2
= proto_register_protocol("ID3v2", "ID3v2", "id3v2");
547 proto_register_field_array(proto_id3v2
, hf
, array_length(hf
));
548 proto_register_subtree_array(ett
, array_length(ett
));
550 expert_id3v2
= expert_register_protocol(proto_id3v2
);
551 expert_register_field_array(expert_id3v2
, ei
, array_length(ei
));
553 /* Allow other dissectors to find this one by name. */
554 register_dissector("id3v2", dissect_id3v2
, proto_id3v2
);
558 proto_reg_handoff_id3v2(void)
560 media_type_dissector_table
= find_dissector_table("media_type");
564 * Editor modelines - https://www.wireshark.org/tools/modelines.html
569 * indent-tabs-mode: nil
572 * vi: set shiftwidth=8 tabstop=8 expandtab:
573 * :indentSize=4:tabSize=8:noTabs=true: