/src/gpac/src/media_tools/id3.c
Line | Count | Source |
1 | | #include <gpac/id3.h> |
2 | | |
3 | | // First 36 bytes of a Nielsen ID3 tag: "ID3\x04\x00 \x00\x00\x02\x05PRIV\x00\x00\x01{\x00\x00www.nielsen.com/" |
4 | | static const u32 NIELSEN_ID3_TAG_PREFIX_LEN = 36; |
5 | | static const u8 NIELSEN_ID3_TAG_PREFIX[] = {0x49, 0x44, 0x33, 0x04, 0x00, 0x20, |
6 | | 0x00, 0x00, 0x02, 0x05, 0x50, 0x52, |
7 | | 0x49, 0x56, 0x00, 0x00, 0x01, 0x7B, |
8 | | 0x00, 0x00, 0x77, 0x77, 0x77, 0x2E, |
9 | | 0x6E, 0x69, 0x65, 0x6C, 0x73, 0x65, |
10 | | 0x6E, 0x2E, 0x63, 0x6F, 0x6D, 0x2F}; |
11 | | |
12 | | static const char *ID3_PROP_SCHEME_URI = "https://aomedia.org/emsg/ID3"; |
13 | | static const char *ID3_PROP_VALUE_URI_NIELSEN = "www.nielsen.com:id3:v1"; |
14 | | static const char *ID3_PROP_VALUE_URI_DEFAULT = "https://aomedia.org/emsg/ID3"; |
15 | | |
16 | | GF_EXPORT |
17 | | GF_Err gf_id3_tag_new(GF_ID3_TAG *tag, u32 timescale, u64 pts, u8 *data, u32 data_length) |
18 | 0 | { |
19 | 0 | if (!tag) { |
20 | 0 | return GF_BAD_PARAM; |
21 | 0 | } |
22 | | |
23 | 0 | if (data == NULL || data_length == 0) { |
24 | 0 | return GF_BAD_PARAM; |
25 | 0 | } |
26 | | |
27 | 0 | tag->timescale = timescale; |
28 | 0 | tag->pts = pts; |
29 | 0 | tag->scheme_uri = gf_strdup(ID3_PROP_SCHEME_URI); |
30 | 0 | tag->scheme_uri_length = (u32) strlen(ID3_PROP_SCHEME_URI) + 1; // plus null character |
31 | | |
32 | | // test if the data is a Nielsen ID3 tag |
33 | 0 | if ((data_length >= NIELSEN_ID3_TAG_PREFIX_LEN) && !memcmp(data, NIELSEN_ID3_TAG_PREFIX, NIELSEN_ID3_TAG_PREFIX_LEN)) |
34 | 0 | { |
35 | 0 | tag->value_uri = gf_strdup(ID3_PROP_VALUE_URI_NIELSEN); |
36 | 0 | } |
37 | 0 | else |
38 | 0 | { |
39 | 0 | tag->value_uri = gf_strdup(ID3_PROP_VALUE_URI_DEFAULT); |
40 | 0 | } |
41 | |
|
42 | 0 | tag->value_uri_length = (u32) strlen(tag->value_uri) + 1; // plus null character |
43 | |
|
44 | 0 | tag->data_length = data_length; |
45 | 0 | tag->data = gf_malloc(data_length); |
46 | 0 | memcpy(tag->data, data, data_length); |
47 | |
|
48 | 0 | return GF_OK; |
49 | 0 | } |
50 | | |
51 | | GF_EXPORT |
52 | | void gf_id3_tag_free(GF_ID3_TAG *tag) |
53 | 0 | { |
54 | 0 | if (!tag) { |
55 | 0 | return; |
56 | 0 | } |
57 | | |
58 | 0 | if (tag->scheme_uri) { |
59 | 0 | gf_free(tag->scheme_uri); |
60 | 0 | tag->scheme_uri = NULL; |
61 | 0 | } |
62 | |
|
63 | 0 | if (tag->value_uri) { |
64 | 0 | gf_free(tag->value_uri); |
65 | 0 | tag->value_uri = NULL; |
66 | 0 | } |
67 | |
|
68 | 0 | if (tag->data) { |
69 | 0 | gf_free(tag->data); |
70 | 0 | tag->data = NULL; |
71 | 0 | } |
72 | 0 | } |
73 | | |
74 | | GF_EXPORT |
75 | | GF_Err gf_id3_to_bitstream(GF_ID3_TAG *tag, GF_BitStream *bs) |
76 | 0 | { |
77 | 0 | if (!tag || !bs) { |
78 | 0 | return GF_BAD_PARAM; |
79 | 0 | } |
80 | | |
81 | 0 | gf_bs_write_u32(bs, tag->timescale); |
82 | 0 | gf_bs_write_u64(bs, tag->pts); |
83 | |
|
84 | 0 | gf_bs_write_u32(bs, tag->scheme_uri_length); |
85 | 0 | u32 bytes_read = gf_bs_write_data(bs, (const u8 *)tag->scheme_uri, tag->scheme_uri_length); |
86 | 0 | if (bytes_read != tag->scheme_uri_length) { |
87 | 0 | return GF_IO_ERR; |
88 | 0 | } |
89 | | |
90 | 0 | gf_bs_write_u32(bs, tag->value_uri_length); |
91 | 0 | bytes_read = gf_bs_write_data(bs, (const u8 *)tag->value_uri, tag->value_uri_length); |
92 | 0 | if (bytes_read != tag->value_uri_length) { |
93 | 0 | return GF_IO_ERR; |
94 | 0 | } |
95 | | |
96 | 0 | gf_bs_write_u32(bs, tag->data_length); |
97 | 0 | bytes_read = gf_bs_write_data(bs, tag->data, tag->data_length); |
98 | 0 | if (bytes_read != tag->data_length) { |
99 | 0 | return GF_IO_ERR; |
100 | 0 | } |
101 | | |
102 | 0 | return GF_OK; |
103 | 0 | } |
104 | | |
105 | | GF_EXPORT |
106 | 0 | GF_Err gf_id3_list_to_bitstream(GF_List *tag_list, GF_BitStream *bs) { |
107 | |
|
108 | 0 | GF_Err err = GF_OK; |
109 | | |
110 | | // first, write the number of tags |
111 | 0 | u32 id3_count = gf_list_count(tag_list); |
112 | 0 | gf_bs_write_u32(bs, id3_count); |
113 | |
|
114 | 0 | for (u32 i = 0; i < id3_count; ++i) { |
115 | 0 | GF_ID3_TAG *tag = gf_list_get(tag_list, i); |
116 | 0 | err = gf_id3_to_bitstream(tag, bs); |
117 | 0 | if (err != GF_OK) { |
118 | 0 | return err; |
119 | 0 | } |
120 | 0 | } |
121 | | |
122 | 0 | return err; |
123 | 0 | } |
124 | | |
125 | | GF_EXPORT |
126 | | GF_Err gf_id3_from_bitstream(GF_ID3_TAG *tag, GF_BitStream *bs) |
127 | 0 | { |
128 | 0 | if (!tag || !bs) { |
129 | 0 | return GF_BAD_PARAM; |
130 | 0 | } |
131 | | |
132 | 0 | tag->timescale = gf_bs_read_u32(bs); |
133 | 0 | tag->pts = gf_bs_read_u64(bs); |
134 | |
|
135 | 0 | tag->scheme_uri_length = gf_bs_read_u32(bs); |
136 | 0 | tag->scheme_uri = gf_malloc(tag->scheme_uri_length); |
137 | 0 | u32 bytes_read = gf_bs_read_data(bs, tag->scheme_uri, tag->scheme_uri_length); |
138 | 0 | if (bytes_read != tag->scheme_uri_length) { |
139 | 0 | return GF_IO_ERR; |
140 | 0 | } |
141 | | |
142 | 0 | tag->value_uri_length = gf_bs_read_u32(bs); |
143 | 0 | tag->value_uri = gf_malloc(tag->value_uri_length); |
144 | 0 | bytes_read = gf_bs_read_data(bs, tag->value_uri, tag->value_uri_length); |
145 | 0 | if (bytes_read != tag->value_uri_length) { |
146 | 0 | return GF_IO_ERR; |
147 | 0 | } |
148 | | |
149 | 0 | tag->data_length = gf_bs_read_u32(bs); |
150 | 0 | tag->data = gf_malloc(tag->data_length); |
151 | 0 | bytes_read = gf_bs_read_data(bs, tag->data, tag->data_length); |
152 | 0 | if (bytes_read != tag->data_length) { |
153 | 0 | return GF_IO_ERR; |
154 | 0 | } |
155 | | |
156 | 0 | return GF_OK; |
157 | 0 | } |