| #include <gpac/id3.h> | |
| // First 36 bytes of a Nielsen ID3 tag: "ID3\x04\x00 \x00\x00\x02\x05PRIV\x00\x00\x01{\x00\x00www.nielsen.com/" | |
| static const u32 NIELSEN_ID3_TAG_PREFIX_LEN = 36; | |
| static const u8 NIELSEN_ID3_TAG_PREFIX[] = {0x49, 0x44, 0x33, 0x04, 0x00, 0x20, | |
| 0x00, 0x00, 0x02, 0x05, 0x50, 0x52, | |
| 0x49, 0x56, 0x00, 0x00, 0x01, 0x7B, | |
| 0x00, 0x00, 0x77, 0x77, 0x77, 0x2E, | |
| 0x6E, 0x69, 0x65, 0x6C, 0x73, 0x65, | |
| 0x6E, 0x2E, 0x63, 0x6F, 0x6D, 0x2F}; | |
| static const char *ID3_PROP_SCHEME_URI = "https://aomedia.org/emsg/ID3"; | |
| static const char *ID3_PROP_VALUE_URI_NIELSEN = "www.nielsen.com:id3:v1"; | |
| static const char *ID3_PROP_VALUE_URI_DEFAULT = "https://aomedia.org/emsg/ID3"; | |
| GF_EXPORT | |
| GF_Err gf_id3_tag_new(GF_ID3_TAG *tag, u32 timescale, u64 pts, u8 *data, u32 data_length) | |
| { | |
| if (!tag) { | |
| return GF_BAD_PARAM; | |
| } | |
| if (data == NULL || data_length == 0) { | |
| return GF_BAD_PARAM; | |
| } | |
| tag->timescale = timescale; | |
| tag->pts = pts; | |
| tag->scheme_uri = gf_strdup(ID3_PROP_SCHEME_URI); | |
| tag->scheme_uri_length = (u32) strlen(ID3_PROP_SCHEME_URI) + 1; // plus null character | |
| // test if the data is a Nielsen ID3 tag | |
| if ((data_length >= NIELSEN_ID3_TAG_PREFIX_LEN) && !memcmp(data, NIELSEN_ID3_TAG_PREFIX, NIELSEN_ID3_TAG_PREFIX_LEN)) | |
| { | |
| tag->value_uri = gf_strdup(ID3_PROP_VALUE_URI_NIELSEN); | |
| } | |
| else | |
| { | |
| tag->value_uri = gf_strdup(ID3_PROP_VALUE_URI_DEFAULT); | |
| } | |
| tag->value_uri_length = (u32) strlen(tag->value_uri) + 1; // plus null character | |
| tag->data_length = data_length; | |
| tag->data = gf_malloc(data_length); | |
| memcpy(tag->data, data, data_length); | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| void gf_id3_tag_free(GF_ID3_TAG *tag) | |
| { | |
| if (!tag) { | |
| return; | |
| } | |
| if (tag->scheme_uri) { | |
| gf_free(tag->scheme_uri); | |
| tag->scheme_uri = NULL; | |
| } | |
| if (tag->value_uri) { | |
| gf_free(tag->value_uri); | |
| tag->value_uri = NULL; | |
| } | |
| if (tag->data) { | |
| gf_free(tag->data); | |
| tag->data = NULL; | |
| } | |
| } | |
| GF_EXPORT | |
| GF_Err gf_id3_to_bitstream(GF_ID3_TAG *tag, GF_BitStream *bs) | |
| { | |
| if (!tag || !bs) { | |
| return GF_BAD_PARAM; | |
| } | |
| gf_bs_write_u32(bs, tag->timescale); | |
| gf_bs_write_u64(bs, tag->pts); | |
| gf_bs_write_u32(bs, tag->scheme_uri_length); | |
| u32 bytes_read = gf_bs_write_data(bs, (const u8 *)tag->scheme_uri, tag->scheme_uri_length); | |
| if (bytes_read != tag->scheme_uri_length) { | |
| return GF_IO_ERR; | |
| } | |
| gf_bs_write_u32(bs, tag->value_uri_length); | |
| bytes_read = gf_bs_write_data(bs, (const u8 *)tag->value_uri, tag->value_uri_length); | |
| if (bytes_read != tag->value_uri_length) { | |
| return GF_IO_ERR; | |
| } | |
| gf_bs_write_u32(bs, tag->data_length); | |
| bytes_read = gf_bs_write_data(bs, tag->data, tag->data_length); | |
| if (bytes_read != tag->data_length) { | |
| return GF_IO_ERR; | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_id3_list_to_bitstream(GF_List *tag_list, GF_BitStream *bs) { | |
| GF_Err err = GF_OK; | |
| // first, write the number of tags | |
| u32 id3_count = gf_list_count(tag_list); | |
| gf_bs_write_u32(bs, id3_count); | |
| for (u32 i = 0; i < id3_count; ++i) { | |
| GF_ID3_TAG *tag = gf_list_get(tag_list, i); | |
| err = gf_id3_to_bitstream(tag, bs); | |
| if (err != GF_OK) { | |
| return err; | |
| } | |
| } | |
| return err; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_id3_from_bitstream(GF_ID3_TAG *tag, GF_BitStream *bs) | |
| { | |
| if (!tag || !bs) { | |
| return GF_BAD_PARAM; | |
| } | |
| tag->timescale = gf_bs_read_u32(bs); | |
| tag->pts = gf_bs_read_u64(bs); | |
| tag->scheme_uri_length = gf_bs_read_u32(bs); | |
| tag->scheme_uri = gf_malloc(tag->scheme_uri_length); | |
| u32 bytes_read = gf_bs_read_data(bs, tag->scheme_uri, tag->scheme_uri_length); | |
| if (bytes_read != tag->scheme_uri_length) { | |
| return GF_IO_ERR; | |
| } | |
| tag->value_uri_length = gf_bs_read_u32(bs); | |
| tag->value_uri = gf_malloc(tag->value_uri_length); | |
| bytes_read = gf_bs_read_data(bs, tag->value_uri, tag->value_uri_length); | |
| if (bytes_read != tag->value_uri_length) { | |
| return GF_IO_ERR; | |
| } | |
| tag->data_length = gf_bs_read_u32(bs); | |
| tag->data = gf_malloc(tag->data_length); | |
| bytes_read = gf_bs_read_data(bs, tag->data, tag->data_length); | |
| if (bytes_read != tag->data_length) { | |
| return GF_IO_ERR; | |
| } | |
| return GF_OK; | |
| } |