|
| 1 | +diff -ruN a/gst/isomp4/fourcc.h b/gst/isomp4/fourcc.h |
| 2 | +--- a/gst/isomp4/fourcc.h 2022-02-08 08:51:37.000000000 +0100 |
| 3 | ++++ b/gst/isomp4/fourcc.h 2022-02-02 15:56:36.000000000 +0100 |
| 4 | +@@ -392,6 +392,9 @@ |
| 5 | + #define FOURCC_tenc GST_MAKE_FOURCC('t','e','n','c') |
| 6 | + #define FOURCC_cenc GST_MAKE_FOURCC('c','e','n','c') |
| 7 | + #define FOURCC_cbcs GST_MAKE_FOURCC('c','b','c','s') |
| 8 | ++#define FOURCC_sbgp GST_MAKE_FOURCC('s','b','g','p') |
| 9 | ++#define FOURCC_sgpd GST_MAKE_FOURCC('s','g','p','d') |
| 10 | ++#define FOURCC_seig GST_MAKE_FOURCC('s','e','i','g') |
| 11 | + |
| 12 | + G_END_DECLS |
| 13 | + |
| 14 | +diff -ruN a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c |
| 15 | +--- a/gst/isomp4/qtdemux.c 2022-02-08 08:51:37.000000000 +0100 |
| 16 | ++++ b/gst/isomp4/qtdemux.c 2022-02-08 08:59:44.000000000 +0100 |
| 17 | +@@ -464,6 +464,11 @@ |
| 18 | + { |
| 19 | + GstStructure *default_properties; |
| 20 | + |
| 21 | ++ /* sample groups */ |
| 22 | ++ GPtrArray *track_group_properties; |
| 23 | ++ GPtrArray *fragment_group_properties; |
| 24 | ++ GPtrArray *sample_to_group_map; |
| 25 | ++ |
| 26 | + /* @crypto_info holds one GstStructure per sample */ |
| 27 | + GPtrArray *crypto_info; |
| 28 | + }; |
| 29 | +@@ -2729,6 +2734,12 @@ |
| 30 | + gst_structure_free (info->default_properties); |
| 31 | + if (info->crypto_info) |
| 32 | + g_ptr_array_free (info->crypto_info, TRUE); |
| 33 | ++ if (info->fragment_group_properties) |
| 34 | ++ g_ptr_array_free (info->fragment_group_properties, TRUE); |
| 35 | ++ if (info->track_group_properties) |
| 36 | ++ g_ptr_array_free (info->track_group_properties, TRUE); |
| 37 | ++ if (info->sample_to_group_map) |
| 38 | ++ g_ptr_array_free (info->sample_to_group_map, FALSE); |
| 39 | + } |
| 40 | + g_free (stream->protection_scheme_info); |
| 41 | + stream->protection_scheme_info = NULL; |
| 42 | +@@ -3834,6 +3845,7 @@ |
| 43 | + QtDemuxStream * stream, guint sample_index) |
| 44 | + { |
| 45 | + QtDemuxCencSampleSetInfo *info = NULL; |
| 46 | ++ GstStructure * properties = NULL; |
| 47 | + |
| 48 | + g_return_val_if_fail (stream != NULL, NULL); |
| 49 | + g_return_val_if_fail (stream->protected, NULL); |
| 50 | +@@ -3841,9 +3853,252 @@ |
| 51 | + |
| 52 | + info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info; |
| 53 | + |
| 54 | +- /* Currently, cenc properties for groups of samples are not supported, so |
| 55 | +- * simply return a copy of the default sample properties */ |
| 56 | +- return gst_structure_copy (info->default_properties); |
| 57 | ++ /* First check if the sample is associated with the 'seig' sample group. */ |
| 58 | ++ if (info->sample_to_group_map && (sample_index < info->sample_to_group_map->len)) |
| 59 | ++ properties = g_ptr_array_index (info->sample_to_group_map, sample_index); |
| 60 | ++ |
| 61 | ++ /* If not, use the default properties for this sample. */ |
| 62 | ++ if (!properties) |
| 63 | ++ properties = info->default_properties; |
| 64 | ++ |
| 65 | ++ return gst_structure_copy (properties); |
| 66 | ++} |
| 67 | ++ |
| 68 | ++static gboolean |
| 69 | ++qtdemux_parse_sbgp (GstQTDemux * qtdemux, QtDemuxStream * stream, |
| 70 | ++ GstByteReader * br, guint32 group, GPtrArray ** sample_to_group_array, |
| 71 | ++ GstStructure * default_properties, GPtrArray * tack_properties_array, |
| 72 | ++ GPtrArray * group_properties_array) |
| 73 | ++{ |
| 74 | ++ guint32 flags = 0; |
| 75 | ++ guint8 version = 0; |
| 76 | ++ guint32 count = 0; |
| 77 | ++ const guint8 * grouping_type_data = NULL; |
| 78 | ++ guint32 grouping_type = 0; |
| 79 | ++ |
| 80 | ++ g_return_val_if_fail (qtdemux != NULL, FALSE); |
| 81 | ++ g_return_val_if_fail (stream != NULL, FALSE); |
| 82 | ++ g_return_val_if_fail (br != NULL, FALSE); |
| 83 | ++ g_return_val_if_fail (*sample_to_group_array == NULL, FALSE); |
| 84 | ++ |
| 85 | ++ if (!gst_byte_reader_get_uint32_be (br, &flags)) |
| 86 | ++ return FALSE; |
| 87 | ++ |
| 88 | ++ if (!gst_byte_reader_get_data (br, 4, &grouping_type_data)) |
| 89 | ++ return FALSE; |
| 90 | ++ |
| 91 | ++ grouping_type = QT_FOURCC (grouping_type_data); |
| 92 | ++ if (grouping_type != group) { |
| 93 | ++ /* There may be other groups, so just log this... */ |
| 94 | ++ GST_DEBUG_OBJECT (qtdemux, "Unsupported grouping type: '%" |
| 95 | ++ GST_FOURCC_FORMAT "'", GST_FOURCC_ARGS (grouping_type)); |
| 96 | ++ return FALSE; |
| 97 | ++ } |
| 98 | ++ |
| 99 | ++ version = (flags >> 24); |
| 100 | ++ if (version > 0) { |
| 101 | ++ GST_WARNING_OBJECT (qtdemux, "Unsupported 'sbgp' box version: %hhu", |
| 102 | ++ version); |
| 103 | ++ return FALSE; |
| 104 | ++ } |
| 105 | ++ |
| 106 | ++ if (!gst_byte_reader_get_uint32_be (br, &count)) |
| 107 | ++ return FALSE; |
| 108 | ++ |
| 109 | ++ GST_LOG_OBJECT (qtdemux, "flags: %08x, type: '%" GST_FOURCC_FORMAT |
| 110 | ++ "', count: %u", flags, GST_FOURCC_ARGS (grouping_type), count); |
| 111 | ++ |
| 112 | ++ if (count > 0) |
| 113 | ++ (*sample_to_group_array) = g_ptr_array_sized_new (count); |
| 114 | ++ |
| 115 | ++ while (count--) { |
| 116 | ++ guint32 samples; |
| 117 | ++ guint32 index; |
| 118 | ++ GstStructure * properties = NULL; |
| 119 | ++ |
| 120 | ++ if (!gst_byte_reader_get_uint32_be (br, &samples)) |
| 121 | ++ return FALSE; |
| 122 | ++ |
| 123 | ++ if (!gst_byte_reader_get_uint32_be (br, &index)) |
| 124 | ++ return FALSE; |
| 125 | ++ |
| 126 | ++ if (index > 0x10000) { |
| 127 | ++ /* Index is referring the current fragment. */ |
| 128 | ++ index -= 0x10001; |
| 129 | ++ if (index < group_properties_array->len) |
| 130 | ++ properties = g_ptr_array_index (group_properties_array, index); |
| 131 | ++ else |
| 132 | ++ GST_ERROR_OBJECT (qtdemux, "invalid group index %u", index); |
| 133 | ++ } else if (index > 0) { |
| 134 | ++ /* Index is referring to the whole track. */ |
| 135 | ++ index--; |
| 136 | ++ if (index < tack_properties_array->len) |
| 137 | ++ properties = g_ptr_array_index (tack_properties_array, index); |
| 138 | ++ else |
| 139 | ++ GST_ERROR_OBJECT (qtdemux, "invalid group index %u", index); |
| 140 | ++ } else { |
| 141 | ++ /* If zero, then this range of samples does not belong to this group, |
| 142 | ++ perhaps to another one or to none at all. */ |
| 143 | ++ } |
| 144 | ++ |
| 145 | ++ GST_INFO_OBJECT (qtdemux, "assigning group '%" GST_FOURCC_FORMAT |
| 146 | ++ "' index %i for the next %i samples: %" GST_PTR_FORMAT, |
| 147 | ++ GST_FOURCC_ARGS (grouping_type), index, samples, properties); |
| 148 | ++ |
| 149 | ++ while (samples--) |
| 150 | ++ g_ptr_array_add (*sample_to_group_array, properties); |
| 151 | ++ } |
| 152 | ++ |
| 153 | ++ return TRUE; |
| 154 | ++} |
| 155 | ++ |
| 156 | ++static gboolean |
| 157 | ++qtdemux_parse_sgpd(GstQTDemux * qtdemux, QtDemuxStream * stream, |
| 158 | ++ GstByteReader * br, guint32 group, GPtrArray ** properties) |
| 159 | ++{ |
| 160 | ++ guint32 flags = 0; |
| 161 | ++ guint8 version = 0; |
| 162 | ++ guint32 default_length = 0; |
| 163 | ++ guint32 count = 0; |
| 164 | ++ const guint8 * grouping_type_data = NULL; |
| 165 | ++ guint32 grouping_type = 0; |
| 166 | ++ const guint32 min_entry_size = 20; |
| 167 | ++ |
| 168 | ++ g_return_val_if_fail (qtdemux != NULL, FALSE); |
| 169 | ++ g_return_val_if_fail (stream != NULL, FALSE); |
| 170 | ++ g_return_val_if_fail (br != NULL, FALSE); |
| 171 | ++ g_return_val_if_fail (*properties == NULL, FALSE); |
| 172 | ++ |
| 173 | ++ if (!gst_byte_reader_get_uint32_be (br, &flags)) |
| 174 | ++ return FALSE; |
| 175 | ++ |
| 176 | ++ if (!gst_byte_reader_get_data (br, 4, &grouping_type_data)) |
| 177 | ++ return FALSE; |
| 178 | ++ |
| 179 | ++ grouping_type = QT_FOURCC (grouping_type_data); |
| 180 | ++ if (grouping_type != group) { |
| 181 | ++ GST_DEBUG_OBJECT (qtdemux, "Unhandled grouping type: '%" |
| 182 | ++ GST_FOURCC_FORMAT "'", GST_FOURCC_ARGS (grouping_type)); |
| 183 | ++ return FALSE; |
| 184 | ++ } |
| 185 | ++ |
| 186 | ++ version = (flags >> 24); |
| 187 | ++ if (version == 1) { |
| 188 | ++ if (!gst_byte_reader_get_uint32_be (br, &default_length)) |
| 189 | ++ return FALSE; |
| 190 | ++ } else if (version > 1) { |
| 191 | ++ GST_WARNING_OBJECT (qtdemux, "Unsupported 'sgpd' box version: %hhu", |
| 192 | ++ version); |
| 193 | ++ return FALSE; |
| 194 | ++ } |
| 195 | ++ |
| 196 | ++ if (!gst_byte_reader_get_uint32_be (br, &count)) |
| 197 | ++ return FALSE; |
| 198 | ++ |
| 199 | ++ GST_LOG_OBJECT(qtdemux, "flags: %08x, type: '%" GST_FOURCC_FORMAT |
| 200 | ++ "', count: %u", flags, GST_FOURCC_ARGS (grouping_type), count); |
| 201 | ++ |
| 202 | ++ if (count) |
| 203 | ++ (*properties) = g_ptr_array_sized_new (count); |
| 204 | ++ |
| 205 | ++ for (guint32 index = 0; index < count; index++) { |
| 206 | ++ GstStructure *props = NULL; |
| 207 | ++ guint32 length = default_length; |
| 208 | ++ const guint8 *entry_data = NULL; |
| 209 | ++ guint8 is_encrypted = 0; |
| 210 | ++ guint8 iv_size = 0; |
| 211 | ++ guint8 constant_iv_size = 0; |
| 212 | ++ const guint8 *kid = NULL; |
| 213 | ++ guint8 crypt_byte_block = 0; |
| 214 | ++ guint8 skip_byte_block = 0; |
| 215 | ++ const guint8 *constant_iv = NULL; |
| 216 | ++ GstBuffer *kid_buf; |
| 217 | ++ |
| 218 | ++ if ((version == 1) && (length == 0)) { |
| 219 | ++ if (!gst_byte_reader_get_uint32_be (br, &length)) |
| 220 | ++ return FALSE; |
| 221 | ++ } |
| 222 | ++ |
| 223 | ++ if (G_UNLIKELY (length < min_entry_size)) { |
| 224 | ++ GST_ERROR_OBJECT (qtdemux, "Invalid entry size: %u", length); |
| 225 | ++ return FALSE; |
| 226 | ++ } |
| 227 | ++ |
| 228 | ++ if (!gst_byte_reader_get_data (br, length, &entry_data)) |
| 229 | ++ return FALSE; |
| 230 | ++ |
| 231 | ++ /* Follows tenc format... */ |
| 232 | ++ is_encrypted = QT_UINT8 (entry_data + 2); |
| 233 | ++ iv_size = QT_UINT8 (entry_data + 3); |
| 234 | ++ kid = (entry_data + 4); |
| 235 | ++ |
| 236 | ++ if (stream->protection_scheme_type == FOURCC_cbcs) { |
| 237 | ++ guint8 possible_pattern_info; |
| 238 | ++ |
| 239 | ++ if (iv_size == 0) { |
| 240 | ++ if (G_UNLIKELY (length < min_entry_size + 1)) { |
| 241 | ++ GST_ERROR_OBJECT (qtdemux, "Invalid entry size: %u", length); |
| 242 | ++ return FALSE; |
| 243 | ++ } |
| 244 | ++ |
| 245 | ++ constant_iv_size = QT_UINT8 (entry_data + 20); |
| 246 | ++ if (G_UNLIKELY ((constant_iv_size) != 8 && (constant_iv_size != 16))) { |
| 247 | ++ GST_ERROR_OBJECT (qtdemux, |
| 248 | ++ "constant IV size should be 8 or 16, not %hhu", constant_iv_size); |
| 249 | ++ return FALSE; |
| 250 | ++ } |
| 251 | ++ |
| 252 | ++ if (G_UNLIKELY (length < min_entry_size + 1 + constant_iv_size)) { |
| 253 | ++ GST_ERROR_OBJECT (qtdemux, "Invalid entry size: %u", length); |
| 254 | ++ return FALSE; |
| 255 | ++ } |
| 256 | ++ |
| 257 | ++ constant_iv = (entry_data + 21); |
| 258 | ++ } |
| 259 | ++ |
| 260 | ++ possible_pattern_info = QT_UINT8 (entry_data + 1); |
| 261 | ++ crypt_byte_block = ((possible_pattern_info >> 4) & 0x0f); |
| 262 | ++ skip_byte_block = (possible_pattern_info & 0x0f); |
| 263 | ++ } |
| 264 | ++ |
| 265 | ++ kid_buf = _gst_buffer_new_wrapped ((guint8 *) kid, 16, NULL); |
| 266 | ++ |
| 267 | ++ props = gst_structure_new ("application/x-cenc", |
| 268 | ++ "iv_size", G_TYPE_UINT, iv_size, |
| 269 | ++ "encrypted", G_TYPE_BOOLEAN, (is_encrypted == 1), |
| 270 | ++ "kid", GST_TYPE_BUFFER, kid_buf, NULL); |
| 271 | ++ |
| 272 | ++ gst_buffer_unref (kid_buf); |
| 273 | ++ |
| 274 | ++ if (stream->protection_scheme_type == FOURCC_cbcs) { |
| 275 | ++ if ((crypt_byte_block != 0) || (skip_byte_block != 0)) { |
| 276 | ++ gst_structure_set (props, |
| 277 | ++ "crypt_byte_block", G_TYPE_UINT, crypt_byte_block, |
| 278 | ++ "skip_byte_block", G_TYPE_UINT, skip_byte_block, NULL); |
| 279 | ++ } |
| 280 | ++ |
| 281 | ++ if (constant_iv != NULL) { |
| 282 | ++ GstBuffer *constant_iv_buf = _gst_buffer_new_wrapped ( |
| 283 | ++ (guint8 *) constant_iv, constant_iv_size, NULL); |
| 284 | ++ gst_structure_set (props, |
| 285 | ++ "constant_iv_size", G_TYPE_UINT, constant_iv_size, |
| 286 | ++ "iv", GST_TYPE_BUFFER, constant_iv_buf, NULL); |
| 287 | ++ gst_buffer_unref (constant_iv_buf); |
| 288 | ++ } |
| 289 | ++ |
| 290 | ++ gst_structure_set (props, "cipher-mode", G_TYPE_STRING, "cbcs", NULL); |
| 291 | ++ } else { |
| 292 | ++ gst_structure_set (props, "cipher-mode", G_TYPE_STRING, "cenc", NULL); |
| 293 | ++ } |
| 294 | ++ |
| 295 | ++ GST_INFO_OBJECT(qtdemux, "properties for group '%" |
| 296 | ++ GST_FOURCC_FORMAT "' at index %u: %" GST_PTR_FORMAT, |
| 297 | ++ GST_FOURCC_ARGS (grouping_type), index, props); |
| 298 | ++ |
| 299 | ++ g_ptr_array_add (*properties, props); |
| 300 | ++ } |
| 301 | ++ |
| 302 | ++ return TRUE; |
| 303 | + } |
| 304 | + |
| 305 | + /* Parses the sizes of sample auxiliary information contained within a stream, |
| 306 | +@@ -4210,6 +4465,50 @@ |
| 307 | + &ds_size, &ds_flags, &base_offset)) |
| 308 | + goto missing_tfhd; |
| 309 | + |
| 310 | ++ /* Sample grouping support */ |
| 311 | ++ if (stream != NULL && stream->protected && (stream->protection_scheme_type == FOURCC_cenc |
| 312 | ++ || stream->protection_scheme_type == FOURCC_cbcs)) { |
| 313 | ++ QtDemuxCencSampleSetInfo *info = stream->protection_scheme_info; |
| 314 | ++ GNode *sbgp_node, *sgpd_node; |
| 315 | ++ GstByteReader sgpd_data, sbgp_data; |
| 316 | ++ |
| 317 | ++ if (info->fragment_group_properties) { |
| 318 | ++ g_ptr_array_free (info->fragment_group_properties, TRUE); |
| 319 | ++ info->fragment_group_properties = NULL; |
| 320 | ++ } |
| 321 | ++ |
| 322 | ++ if (info->sample_to_group_map) { |
| 323 | ++ g_ptr_array_free (info->sample_to_group_map, FALSE); |
| 324 | ++ info->sample_to_group_map = NULL; |
| 325 | ++ } |
| 326 | ++ |
| 327 | ++ /* Check if sample grouping information is present for this segment. */ |
| 328 | ++ /* However look only for 'seig' (CENC encryption) grouping type... */ |
| 329 | ++ sgpd_node = qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_sgpd, |
| 330 | ++ &sgpd_data); |
| 331 | ++ while (sgpd_node) { |
| 332 | ++ if (qtdemux_parse_sgpd(qtdemux, stream, &sgpd_data, FOURCC_seig, |
| 333 | ++ &info->fragment_group_properties)) { |
| 334 | ++ /* CENC encryption grouping found, don't look further. */ |
| 335 | ++ break; |
| 336 | ++ } |
| 337 | ++ sgpd_node = qtdemux_tree_get_sibling_by_type_full (sgpd_node, |
| 338 | ++ FOURCC_sgpd, &sgpd_data); |
| 339 | ++ } |
| 340 | ++ |
| 341 | ++ sbgp_node = qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_sbgp, |
| 342 | ++ &sbgp_data); |
| 343 | ++ while (sbgp_node) { |
| 344 | ++ if (qtdemux_parse_sbgp (qtdemux, stream, &sbgp_data, FOURCC_seig, |
| 345 | ++ &info->sample_to_group_map, info->default_properties, |
| 346 | ++ info->track_group_properties, info->fragment_group_properties)) { |
| 347 | ++ break; |
| 348 | ++ } |
| 349 | ++ sbgp_node = qtdemux_tree_get_sibling_by_type_full (sbgp_node, |
| 350 | ++ FOURCC_sgpd, &sbgp_data); |
| 351 | ++ } |
| 352 | ++ } |
| 353 | ++ |
| 354 | + /* The following code assumes at most a single set of sample auxiliary |
| 355 | + * data in the fragment (consisting of a saiz box and a corresponding saio |
| 356 | + * box); in theory, however, there could be multiple sets of sample |
| 357 | +@@ -12771,7 +13070,30 @@ |
| 358 | + |
| 359 | + stsd_entry_data += len; |
| 360 | + remaining_stsd_len -= len; |
| 361 | ++ } |
| 362 | ++ |
| 363 | ++ /* Sample grouping support */ |
| 364 | ++ if (stream->protected && (stream->protection_scheme_type == FOURCC_cenc |
| 365 | ++ || stream->protection_scheme_type == FOURCC_cbcs)) { |
| 366 | ++ QtDemuxCencSampleSetInfo *info = stream->protection_scheme_info; |
| 367 | ++ GNode *sgpd_node; |
| 368 | ++ GstByteReader sgpd_data; |
| 369 | ++ |
| 370 | ++ if (info->track_group_properties) { |
| 371 | ++ g_ptr_array_free (info->fragment_group_properties, TRUE); |
| 372 | ++ info->fragment_group_properties = NULL; |
| 373 | ++ } |
| 374 | + |
| 375 | ++ sgpd_node = qtdemux_tree_get_child_by_type_full (stbl, FOURCC_sgpd, |
| 376 | ++ &sgpd_data); |
| 377 | ++ while (sgpd_node) { |
| 378 | ++ if (qtdemux_parse_sgpd(qtdemux, stream, &sgpd_data, FOURCC_seig, |
| 379 | ++ &info->track_group_properties)) { |
| 380 | ++ break; |
| 381 | ++ } |
| 382 | ++ sgpd_node = qtdemux_tree_get_sibling_by_type_full (sgpd_node, |
| 383 | ++ FOURCC_sgpd, &sgpd_data); |
| 384 | ++ } |
| 385 | + } |
| 386 | + |
| 387 | + /* collect sample information */ |
| 388 | +diff -ruN a/gst/isomp4/qtdemux_types.c b/gst/isomp4/qtdemux_types.c |
| 389 | +--- a/gst/isomp4/qtdemux_types.c 2019-04-19 11:16:25.000000000 +0200 |
| 390 | ++++ b/gst/isomp4/qtdemux_types.c 2022-02-07 11:13:17.000000000 +0100 |
| 391 | +@@ -212,6 +212,8 @@ |
| 392 | + {FOURCC_schi, "scheme information", QT_FLAG_CONTAINER}, |
| 393 | + {FOURCC_pssh, "protection system specific header", 0}, |
| 394 | + {FOURCC_tenc, "track encryption", 0}, |
| 395 | ++ {FOURCC_sgpd, "sample group description", 0}, |
| 396 | ++ {FOURCC_sbgp, "sample to group", 0}, |
| 397 | + {FOURCC_stpp, "XML subtitle sample entry", 0}, |
| 398 | + {FOURCC_clcp, "Closed Caption", 0}, |
| 399 | + {FOURCC_av01, "AV1 Sample Entry", 0}, |
0 commit comments