| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Cyril Concolato / Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2005-2026 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / ISO Media File Format sub-project | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include <gpac/internal/isomedia_dev.h> | |
| #include <gpac/constants.h> | |
| #ifndef GPAC_DISABLE_ISOM | |
| GF_ISMASample *gf_isom_ismacryp_new_sample() | |
| { | |
| GF_ISMASample *tmp = (GF_ISMASample *) gf_malloc(sizeof(GF_ISMASample)); | |
| if (!tmp) return NULL; | |
| memset(tmp, 0, sizeof(GF_ISMASample)); | |
| return tmp; | |
| } | |
| GF_EXPORT | |
| void gf_isom_ismacryp_delete_sample(GF_ISMASample *samp) | |
| { | |
| if (!samp) return; | |
| if (samp->data && samp->dataLength) gf_free(samp->data); | |
| if (samp->key_indicator) gf_free(samp->key_indicator); | |
| gf_free(samp); | |
| } | |
| GF_ISMASample *gf_isom_ismacryp_sample_from_data(u8 *data, u32 dataLength, Bool use_selective_encryption, u8 KI_length, u8 IV_length) | |
| { | |
| GF_ISMASample *s; | |
| GF_BitStream *bs; | |
| /*empty text sample*/ | |
| if (!data || !dataLength) { | |
| return gf_isom_ismacryp_new_sample(); | |
| } | |
| s = gf_isom_ismacryp_new_sample(); | |
| /*empty sample*/ | |
| if (!data || !dataLength) return s; | |
| bs = gf_bs_new(data, dataLength, GF_BITSTREAM_READ); | |
| s->dataLength = dataLength; | |
| s->IV_length = IV_length; | |
| s->KI_length = KI_length; | |
| if (use_selective_encryption) { | |
| s->flags = GF_ISOM_ISMA_USE_SEL_ENC; | |
| if (s->dataLength < 1) goto exit; | |
| if (gf_bs_read_int(bs, 1)) s->flags |= GF_ISOM_ISMA_IS_ENCRYPTED; | |
| gf_bs_read_int(bs, 7); | |
| s->dataLength -= 1; | |
| } else { | |
| s->flags = GF_ISOM_ISMA_IS_ENCRYPTED; | |
| } | |
| if (s->flags & GF_ISOM_ISMA_IS_ENCRYPTED) { | |
| if (IV_length != 0) { | |
| if (s->dataLength < IV_length) goto exit; | |
| s->IV = gf_bs_read_long_int(bs, 8*IV_length); | |
| s->dataLength -= IV_length; | |
| } | |
| if (KI_length) { | |
| if (s->dataLength < KI_length) goto exit; | |
| s->key_indicator = (u8 *)gf_malloc(KI_length); | |
| if (!s->key_indicator) goto exit; | |
| gf_bs_read_data(bs, (char*)s->key_indicator, KI_length); | |
| s->dataLength -= KI_length; | |
| } | |
| } | |
| s->data = (char*)gf_malloc(sizeof(char)*s->dataLength); | |
| if (!s->data) goto exit; | |
| gf_bs_read_data(bs, s->data, s->dataLength); | |
| gf_bs_del(bs); | |
| return s; | |
| exit: | |
| gf_isom_ismacryp_delete_sample(s); | |
| return NULL; | |
| } | |
| #if 0 //unused | |
| /*! rewrites ISMA sample as an ISO sample | |
| \param s the ISMA sample to rewrite | |
| \param dest the destination ISO sample | |
| \return error if any | |
| */ | |
| GF_Err gf_isom_ismacryp_sample_to_sample(const GF_ISMASample *s, GF_ISOSample *dest) | |
| { | |
| GF_BitStream *bs; | |
| if (!s || !dest) return GF_BAD_PARAM; | |
| bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| if (s->flags & GF_ISOM_ISMA_USE_SEL_ENC) { | |
| gf_bs_write_int(bs, (s->flags & GF_ISOM_ISMA_IS_ENCRYPTED) ? 1 : 0, 1); | |
| gf_bs_write_int(bs, 0, 7); | |
| } | |
| if (s->flags & GF_ISOM_ISMA_IS_ENCRYPTED) { | |
| if (s->IV_length) gf_bs_write_long_int(bs, (s64) s->IV, 8*s->IV_length); | |
| if (s->KI_length) gf_bs_write_data(bs, (char*)s->key_indicator, s->KI_length); | |
| } | |
| gf_bs_write_data(bs, s->data, s->dataLength); | |
| if (dest->data) gf_free(dest->data); | |
| dest->data = NULL; | |
| dest->dataLength = 0; | |
| gf_bs_get_content(bs, &dest->data, &dest->dataLength); | |
| gf_bs_del(bs); | |
| return GF_OK; | |
| } | |
| #endif | |
| static GF_ProtectionSchemeInfoBox *isom_get_sinf_entry(GF_TrackBox *trak, u32 sampleDescriptionIndex, u32 scheme_type, GF_SampleEntryBox **out_sea) | |
| { | |
| u32 i=0; | |
| GF_SampleEntryBox *sea=NULL; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| Media_GetSampleDesc(trak->Media, sampleDescriptionIndex, &sea, NULL); | |
| if (!sea) return NULL; | |
| i = 0; | |
| while ((sinf = (GF_ProtectionSchemeInfoBox*)gf_list_enum(sea->child_boxes, &i))) { | |
| if (sinf->type != GF_ISOM_BOX_TYPE_SINF) continue; | |
| if (sinf->original_format && sinf->scheme_type && sinf->info) { | |
| if (!scheme_type || (sinf->scheme_type->scheme_type == scheme_type)) { | |
| if (out_sea) | |
| *out_sea = sea; | |
| return sinf; | |
| } | |
| } | |
| } | |
| return NULL; | |
| } | |
| GF_EXPORT | |
| GF_ISMASample *gf_isom_get_ismacryp_sample(GF_ISOFile *the_file, u32 trackNumber, const GF_ISOSample *samp, u32 sampleDescriptionIndex) | |
| { | |
| GF_TrackBox *trak; | |
| GF_ISMASampleFormatBox *fmt; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return NULL; | |
| sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, 0, NULL); | |
| if (!sinf) return NULL; | |
| /*ISMA*/ | |
| if (sinf->scheme_type->scheme_type == GF_ISOM_ISMACRYP_SCHEME) { | |
| fmt = sinf->info->isfm; | |
| if (!fmt) return NULL; | |
| return gf_isom_ismacryp_sample_from_data(samp->data, samp->dataLength, sinf->info->isfm->selective_encryption, sinf->info->isfm->key_indicator_length, sinf->info->isfm->IV_length); | |
| } | |
| /*OMA*/ | |
| else if (sinf->scheme_type->scheme_type == GF_ISOM_OMADRM_SCHEME ) { | |
| if (!sinf->info->odkm) return NULL; | |
| fmt = sinf->info->odkm->fmt; | |
| if (fmt) { | |
| return gf_isom_ismacryp_sample_from_data(samp->data, samp->dataLength, fmt->selective_encryption, fmt->key_indicator_length, fmt->IV_length); | |
| } | |
| /*OMA default: no selective encryption, one key, 128 bit IV*/ | |
| return gf_isom_ismacryp_sample_from_data(samp->data, samp->dataLength, GF_FALSE, 0, 128); | |
| } | |
| return NULL; | |
| } | |
| GF_EXPORT | |
| u32 gf_isom_is_media_encrypted(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex) | |
| { | |
| GF_TrackBox *trak; | |
| u32 i, count; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak || !trak->Media || !trak->Media->information || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleDescription) | |
| return 0; | |
| count = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes); | |
| for (i=0; i<count; i++) { | |
| if (sampleDescriptionIndex && (i+1 != sampleDescriptionIndex)) | |
| continue; | |
| sinf = isom_get_sinf_entry(trak, i+1, 0, NULL); | |
| if (!sinf) continue; | |
| /*non-encrypted or non-ISMA*/ | |
| if (!sinf->scheme_type) return 0; | |
| if (sinf->scheme_type->scheme_type == GF_ISOM_PIFF_SCHEME) return GF_ISOM_CENC_SCHEME; | |
| return sinf->scheme_type->scheme_type; | |
| } | |
| return 0; | |
| } | |
| GF_EXPORT | |
| Bool gf_isom_is_ismacryp_media(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex) | |
| { | |
| GF_TrackBox *trak; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_FALSE; | |
| sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_ISMACRYP_SCHEME, NULL); | |
| if (!sinf) return GF_FALSE; | |
| /*non-encrypted or non-ISMA*/ | |
| if (!sinf->info || !sinf->info->ikms || !sinf->info->isfm ) | |
| return GF_FALSE; | |
| return GF_TRUE; | |
| } | |
| GF_EXPORT | |
| Bool gf_isom_is_omadrm_media(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex) | |
| { | |
| GF_TrackBox *trak; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_FALSE; | |
| sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_OMADRM_SCHEME, NULL); | |
| if (!sinf) return GF_FALSE; | |
| /*non-encrypted or non-OMA*/ | |
| if (!sinf->info || !sinf->info->odkm || !sinf->info->odkm->hdr) | |
| return GF_FALSE; | |
| return GF_TRUE; | |
| } | |
| /*retrieves ISMACryp info for the given track & SDI*/ | |
| GF_EXPORT | |
| GF_Err gf_isom_get_ismacryp_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex, u32 *outOriginalFormat, u32 *outSchemeType, u32 *outSchemeVersion, const char **outSchemeURI, const char **outKMS_URI, Bool *outSelectiveEncryption, u32 *outIVLength, u32 *outKeyIndicationLength) | |
| { | |
| GF_TrackBox *trak; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_BAD_PARAM; | |
| sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_ISMACRYP_SCHEME, NULL); | |
| if (!sinf) return GF_OK; | |
| if (outOriginalFormat) { | |
| *outOriginalFormat = sinf->original_format->data_format; | |
| if (IsMP4Description(sinf->original_format->data_format)) *outOriginalFormat = GF_ISOM_SUBTYPE_MPEG4; | |
| } | |
| if (outSchemeType) *outSchemeType = sinf->scheme_type->scheme_type; | |
| if (outSchemeVersion) *outSchemeVersion = sinf->scheme_type->scheme_version; | |
| if (outSchemeURI) *outSchemeURI = sinf->scheme_type->URI; | |
| if (sinf->info && sinf->info->ikms) { | |
| if (outKMS_URI) *outKMS_URI = sinf->info->ikms->URI; | |
| } else { | |
| if (outKMS_URI) *outKMS_URI = NULL; | |
| } | |
| if (sinf->info && sinf->info->isfm) { | |
| if (outSelectiveEncryption) *outSelectiveEncryption = sinf->info->isfm->selective_encryption; | |
| if (outIVLength) *outIVLength = sinf->info->isfm->IV_length; | |
| if (outKeyIndicationLength) *outKeyIndicationLength = sinf->info->isfm->key_indicator_length; | |
| } else { | |
| if (outSelectiveEncryption) *outSelectiveEncryption = GF_FALSE; | |
| if (outIVLength) *outIVLength = 0; | |
| if (outKeyIndicationLength) *outKeyIndicationLength = 0; | |
| } | |
| return GF_OK; | |
| } | |
| /*retrieves ISMACryp info for the given track & SDI*/ | |
| GF_EXPORT | |
| GF_Err gf_isom_get_omadrm_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex, u32 *outOriginalFormat, | |
| u32 *outSchemeType, u32 *outSchemeVersion, | |
| const char **outContentID, const char **outRightsIssuerURL, const char **outTextualHeaders, u32 *outTextualHeadersLen, u64 *outPlaintextLength, u32 *outEncryptionType, Bool *outSelectiveEncryption, u32 *outIVLength, u32 *outKeyIndicationLength) | |
| { | |
| GF_TrackBox *trak; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_BAD_PARAM; | |
| sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_OMADRM_SCHEME, NULL); | |
| if (!sinf) return GF_OK; | |
| if (!sinf->info || !sinf->info->odkm || !sinf->info->odkm->hdr) return GF_NON_COMPLIANT_BITSTREAM; | |
| if (outOriginalFormat) { | |
| *outOriginalFormat = sinf->original_format->data_format; | |
| if (IsMP4Description(sinf->original_format->data_format)) *outOriginalFormat = GF_ISOM_SUBTYPE_MPEG4; | |
| } | |
| if (outSchemeType) *outSchemeType = sinf->scheme_type->scheme_type; | |
| if (outSchemeVersion) *outSchemeVersion = sinf->scheme_type->scheme_version; | |
| if (outContentID) *outContentID = sinf->info->odkm->hdr->ContentID; | |
| if (outRightsIssuerURL) *outRightsIssuerURL = sinf->info->odkm->hdr->RightsIssuerURL; | |
| if (outTextualHeaders) { | |
| *outTextualHeaders = sinf->info->odkm->hdr->TextualHeaders; | |
| if (outTextualHeadersLen) *outTextualHeadersLen = sinf->info->odkm->hdr->TextualHeadersLen; | |
| } | |
| if (outPlaintextLength) *outPlaintextLength = sinf->info->odkm->hdr->PlaintextLength; | |
| if (outEncryptionType) *outEncryptionType = sinf->info->odkm->hdr->EncryptionMethod; | |
| if (sinf->info && sinf->info->odkm && sinf->info->odkm->fmt) { | |
| if (outSelectiveEncryption) *outSelectiveEncryption = sinf->info->odkm->fmt->selective_encryption; | |
| if (outIVLength) *outIVLength = sinf->info->odkm->fmt->IV_length; | |
| if (outKeyIndicationLength) *outKeyIndicationLength = sinf->info->odkm->fmt->key_indicator_length; | |
| } else { | |
| if (outSelectiveEncryption) *outSelectiveEncryption = GF_FALSE; | |
| if (outIVLength) *outIVLength = 0; | |
| if (outKeyIndicationLength) *outKeyIndicationLength = 0; | |
| } | |
| return GF_OK; | |
| } | |
| #ifndef GPAC_DISABLE_ISOM_WRITE | |
| #if 0 //unused | |
| /*! removes protection info (does not perform decryption), for ISMA, OMA and CENC of a sample description | |
| \param isom_file the target ISO file | |
| \param trackNumber the target track | |
| \param sampleDescriptionIndex the sample description index | |
| \return error if any | |
| */ | |
| GF_Err gf_isom_remove_track_protection(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex) | |
| { | |
| GF_TrackBox *trak; | |
| GF_Err e; | |
| GF_SampleEntryBox *sea; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| e = gf_isom_can_access_movie(the_file, GF_ISOM_OPEN_WRITE); | |
| if (e) return e; | |
| trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak || !trak->Media || !sampleDescriptionIndex) return GF_BAD_PARAM; | |
| sea = NULL; | |
| sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_CENC_SCHEME, &sea); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_CBC_SCHEME, &sea); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_CENS_SCHEME, &sea); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_CBCS_SCHEME, &sea); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_ISMACRYP_SCHEME, &sea); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_OMADRM_SCHEME, &sea); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_ADOBE_SCHEME, &sea); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_PIFF_SCHEME, &sea); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_SVE1_SCHEME, &sea); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_HLS_SAMPLE_AES_SCHEME, &sea); | |
| if (!sinf) return GF_OK; | |
| sea->type = sinf->original_format->data_format; | |
| while (1) { | |
| GF_Box *b = gf_isom_box_find_child(sea->child_boxes, GF_ISOM_BOX_TYPE_SINF); | |
| if (!b) break; | |
| gf_isom_box_del_parent(&sea->child_boxes, b); | |
| } | |
| if (sea->type == GF_ISOM_BOX_TYPE_264B) sea->type = GF_ISOM_BOX_TYPE_AVC1; | |
| if (sea->type == GF_ISOM_BOX_TYPE_265B) sea->type = GF_ISOM_BOX_TYPE_HVC1; | |
| return GF_OK; | |
| } | |
| #endif | |
| GF_EXPORT | |
| GF_Err gf_isom_change_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex, char *scheme_uri, char *kms_uri) | |
| { | |
| GF_TrackBox *trak; | |
| GF_Err e; | |
| GF_SampleEntryBox *sea; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| e = gf_isom_can_access_movie(the_file, GF_ISOM_OPEN_WRITE); | |
| if (e) return e; | |
| trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak || !trak->Media || !sampleDescriptionIndex) return GF_BAD_PARAM; | |
| sea = NULL; | |
| sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_ISMACRYP_SCHEME, &sea); | |
| if (!sinf) return GF_OK; | |
| if (scheme_uri) { | |
| gf_free(sinf->scheme_type->URI); | |
| sinf->scheme_type->URI = gf_strdup(scheme_uri); | |
| } | |
| if (kms_uri) { | |
| gf_free(sinf->info->ikms->URI); | |
| sinf->info->ikms->URI = gf_strdup(kms_uri); | |
| } | |
| return GF_OK; | |
| } | |
| static GF_Err isom_set_protected_entry(GF_ISOFile *the_file, u32 trackNumber, u32 desc_index, u8 version, u32 flags, | |
| u32 scheme_type, u32 scheme_version, char *scheme_uri, Bool is_isma, GF_ProtectionSchemeInfoBox **out_sinf) | |
| { | |
| u32 original_format; | |
| GF_Err e; | |
| u32 gnr_type=0; | |
| u32 *overwrite_type=NULL; | |
| GF_SampleEntryBox *sea; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_BAD_PARAM; | |
| e = Media_GetSampleDesc(trak->Media, desc_index, &sea, NULL); | |
| if (e) return e; | |
| original_format = sea->type; | |
| if (original_format==GF_ISOM_BOX_TYPE_GNRA) { | |
| gnr_type = original_format; | |
| overwrite_type = &((GF_GenericAudioSampleEntryBox*)sea)->EntryType; | |
| } else if (original_format==GF_ISOM_BOX_TYPE_GNRV) { | |
| gnr_type = original_format; | |
| overwrite_type = &((GF_GenericVisualSampleEntryBox*)sea)->EntryType; | |
| } else if (original_format==GF_ISOM_BOX_TYPE_GNRM) { | |
| gnr_type = original_format; | |
| overwrite_type = &((GF_GenericSampleEntryBox*)sea)->EntryType; | |
| } | |
| if (overwrite_type) | |
| original_format = *overwrite_type; | |
| /* Replacing the Media Type */ | |
| switch (sea->type) { | |
| case GF_ISOM_BOX_TYPE_MP4A: | |
| case GF_ISOM_BOX_TYPE_DAMR: | |
| case GF_ISOM_BOX_TYPE_DEVC: | |
| case GF_ISOM_BOX_TYPE_DQCP: | |
| case GF_ISOM_BOX_TYPE_DSMV: | |
| case GF_ISOM_BOX_TYPE_AC3: | |
| case GF_ISOM_BOX_TYPE_EC3: | |
| case GF_ISOM_BOX_TYPE_AC4: | |
| sea->type = GF_ISOM_BOX_TYPE_ENCA; | |
| break; | |
| case GF_ISOM_BOX_TYPE_MP4V: | |
| case GF_ISOM_BOX_TYPE_D263: | |
| sea->type = GF_ISOM_BOX_TYPE_ENCV; | |
| break; | |
| /*special case for AVC1*/ | |
| case GF_ISOM_BOX_TYPE_AVC1: | |
| case GF_ISOM_BOX_TYPE_AVC2: | |
| case GF_ISOM_BOX_TYPE_AVC3: | |
| case GF_ISOM_BOX_TYPE_AVC4: | |
| case GF_ISOM_BOX_TYPE_SVC1: | |
| case GF_ISOM_BOX_TYPE_MVC1: | |
| case GF_ISOM_BOX_TYPE_DVA1: | |
| case GF_ISOM_BOX_TYPE_DVAV: | |
| if (is_isma) | |
| original_format = GF_ISOM_BOX_TYPE_264B; | |
| sea->type = GF_ISOM_BOX_TYPE_ENCV; | |
| break; | |
| case GF_ISOM_BOX_TYPE_HVC1: | |
| case GF_ISOM_BOX_TYPE_HEV1: | |
| case GF_ISOM_BOX_TYPE_HVC2: | |
| case GF_ISOM_BOX_TYPE_HEV2: | |
| case GF_ISOM_BOX_TYPE_LHE1: | |
| case GF_ISOM_BOX_TYPE_LHV1: | |
| case GF_ISOM_BOX_TYPE_HVT1: | |
| case GF_ISOM_BOX_TYPE_DVHE: | |
| case GF_ISOM_BOX_TYPE_DVH1: | |
| if (is_isma) | |
| original_format = GF_ISOM_BOX_TYPE_265B; | |
| sea->type = GF_ISOM_BOX_TYPE_ENCV; | |
| break; | |
| case GF_ISOM_BOX_TYPE_VVC1: | |
| case GF_ISOM_BOX_TYPE_VVI1: | |
| sea->type = GF_ISOM_BOX_TYPE_ENCV; | |
| break; | |
| case GF_ISOM_BOX_TYPE_VP09: | |
| sea->type = GF_ISOM_BOX_TYPE_ENCV; | |
| break; | |
| case GF_ISOM_BOX_TYPE_AV01: | |
| case GF_ISOM_BOX_TYPE_DAV1: | |
| sea->type = GF_ISOM_BOX_TYPE_ENCV; | |
| break; | |
| case GF_ISOM_BOX_TYPE_MP4S: | |
| case GF_ISOM_BOX_TYPE_LSR1: | |
| sea->type = GF_ISOM_BOX_TYPE_ENCS; | |
| break; | |
| case GF_ISOM_BOX_TYPE_STXT: | |
| case GF_ISOM_BOX_TYPE_WVTT: | |
| case GF_ISOM_BOX_TYPE_STPP: | |
| sea->type = GF_ISOM_BOX_TYPE_ENCT; | |
| break; | |
| case GF_ISOM_BOX_TYPE_ENCA: | |
| case GF_ISOM_BOX_TYPE_ENCV: | |
| case GF_ISOM_BOX_TYPE_ENCT: | |
| case GF_ISOM_BOX_TYPE_ENCM: | |
| case GF_ISOM_BOX_TYPE_ENCF: | |
| case GF_ISOM_BOX_TYPE_ENCS: | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] cannot set protection entry: file is already encrypted.\n")); | |
| return GF_BAD_PARAM; | |
| default: | |
| if (sea->internal_type == GF_ISOM_SAMPLE_ENTRY_AUDIO) { | |
| if (overwrite_type) *overwrite_type = GF_ISOM_BOX_TYPE_ENCA; | |
| else sea->type = GF_ISOM_BOX_TYPE_ENCA; | |
| } else if (sea->internal_type == GF_ISOM_SAMPLE_ENTRY_VIDEO) { | |
| if (overwrite_type) *overwrite_type = GF_ISOM_BOX_TYPE_ENCV; | |
| else sea->type = GF_ISOM_BOX_TYPE_ENCV; | |
| } else { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] unsupported entry type %s for CENC.\n", gf_4cc_to_str(sea->type) )); | |
| return GF_BAD_PARAM; | |
| } | |
| } | |
| sinf = (GF_ProtectionSchemeInfoBox *)gf_isom_box_new_parent(&sea->child_boxes, GF_ISOM_BOX_TYPE_SINF); | |
| if (!sinf) return GF_OUT_OF_MEM; | |
| sinf->scheme_type = (GF_SchemeTypeBox *)gf_isom_box_new_parent(&sinf->child_boxes, GF_ISOM_BOX_TYPE_SCHM); | |
| if (!sinf->scheme_type) return GF_OUT_OF_MEM; | |
| sinf->scheme_type->version = version; | |
| sinf->scheme_type->flags = flags; | |
| sinf->scheme_type->scheme_type = scheme_type; | |
| sinf->scheme_type->scheme_version = scheme_version; | |
| if (scheme_uri && (sinf->scheme_type->flags == 1)) { | |
| sinf->scheme_type->URI = (char *)gf_malloc(sizeof(char)*strlen(scheme_uri)); | |
| if (!sinf->scheme_type->URI) return GF_OUT_OF_MEM; | |
| memmove(sinf->scheme_type->URI, scheme_uri, strlen(scheme_uri)); | |
| } | |
| sinf->original_format = (GF_OriginalFormatBox *)gf_isom_box_new_parent(&sinf->child_boxes, GF_ISOM_BOX_TYPE_FRMA); | |
| if (!sinf->original_format) return GF_OUT_OF_MEM; | |
| if (gnr_type) { | |
| sinf->original_format->data_format = gnr_type; | |
| sinf->original_format->gnr_type = original_format; | |
| } else { | |
| sinf->original_format->data_format = original_format; | |
| } | |
| //common to isma, cenc and oma | |
| sinf->info = (GF_SchemeInformationBox *)gf_isom_box_new_parent(&sinf->child_boxes, GF_ISOM_BOX_TYPE_SCHI); | |
| *out_sinf = sinf; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_set_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, u32 desc_index, u32 scheme_type, | |
| u32 scheme_version, char *scheme_uri, char *kms_URI, | |
| Bool selective_encryption, u32 KI_length, u32 IV_length) | |
| { | |
| GF_Err e; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| //setup generic protection | |
| e = isom_set_protected_entry(the_file, trackNumber, desc_index, 0, 0, scheme_type, scheme_version, NULL, GF_TRUE, &sinf); | |
| if (e) return e; | |
| if (scheme_uri) { | |
| sinf->scheme_type->flags |= 0x000001; | |
| sinf->scheme_type->URI = gf_strdup(scheme_uri); | |
| } | |
| sinf->info->ikms = (GF_ISMAKMSBox *)gf_isom_box_new_parent(&sinf->info->child_boxes, GF_ISOM_BOX_TYPE_IKMS); | |
| if (!sinf->info->ikms) return GF_OUT_OF_MEM; | |
| sinf->info->ikms->URI = gf_strdup(kms_URI); | |
| if (!sinf->info->ikms->URI) return GF_OUT_OF_MEM; | |
| sinf->info->isfm = (GF_ISMASampleFormatBox *)gf_isom_box_new_parent(&sinf->info->child_boxes, GF_ISOM_BOX_TYPE_ISFM); | |
| if (!sinf->info->isfm) return GF_OUT_OF_MEM; | |
| sinf->info->isfm->selective_encryption = selective_encryption; | |
| sinf->info->isfm->key_indicator_length = KI_length; | |
| sinf->info->isfm->IV_length = IV_length; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_set_oma_protection(GF_ISOFile *the_file, u32 trackNumber, u32 desc_index, | |
| char *contentID, char *kms_URI, u32 encryption_type, u64 plainTextLength, char *textual_headers, u32 textual_headers_len, | |
| Bool selective_encryption, u32 KI_length, u32 IV_length) | |
| { | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| GF_Err e; | |
| //setup generic protection | |
| e = isom_set_protected_entry(the_file, trackNumber, desc_index, 0, 0, GF_ISOM_OMADRM_SCHEME, 0x00000200, NULL, GF_FALSE, &sinf); | |
| if (e) return e; | |
| sinf->info->odkm = (GF_OMADRMKMSBox *)gf_isom_box_new_parent(&sinf->info->child_boxes, GF_ISOM_BOX_TYPE_ODKM); | |
| if (!sinf->info->odkm) return GF_OUT_OF_MEM; | |
| sinf->info->odkm->fmt = (GF_OMADRMAUFormatBox*)gf_isom_box_new_parent(&sinf->info->odkm->child_boxes, GF_ISOM_BOX_TYPE_ODAF); | |
| if (!sinf->info->odkm->fmt) return GF_OUT_OF_MEM; | |
| sinf->info->odkm->fmt->selective_encryption = selective_encryption; | |
| sinf->info->odkm->fmt->key_indicator_length = KI_length; | |
| sinf->info->odkm->fmt->IV_length = IV_length; | |
| sinf->info->odkm->hdr = (GF_OMADRMCommonHeaderBox*)gf_isom_box_new_parent(&sinf->info->odkm->child_boxes, GF_ISOM_BOX_TYPE_OHDR); | |
| if (!sinf->info->odkm->hdr) return GF_OUT_OF_MEM; | |
| sinf->info->odkm->hdr->EncryptionMethod = encryption_type; | |
| sinf->info->odkm->hdr->PaddingScheme = (encryption_type==0x01) ? 1 : 0; | |
| sinf->info->odkm->hdr->PlaintextLength = plainTextLength; | |
| if (contentID) sinf->info->odkm->hdr->ContentID = gf_strdup(contentID); | |
| if (kms_URI) sinf->info->odkm->hdr->RightsIssuerURL = gf_strdup(kms_URI); | |
| if (textual_headers) { | |
| sinf->info->odkm->hdr->TextualHeaders = (char*)gf_malloc(sizeof(char)*textual_headers_len); | |
| if (!sinf->info->odkm->hdr->TextualHeaders) return GF_OUT_OF_MEM; | |
| memcpy(sinf->info->odkm->hdr->TextualHeaders, textual_headers, sizeof(char)*textual_headers_len); | |
| sinf->info->odkm->hdr->TextualHeadersLen = textual_headers_len; | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_set_generic_protection(GF_ISOFile *the_file, u32 trackNumber, u32 desc_index, u32 scheme_type, u32 scheme_version, char *scheme_uri, char *kms_URI) | |
| { | |
| GF_Err e; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| //setup generic protection | |
| e = isom_set_protected_entry(the_file, trackNumber, desc_index, 0, 0, scheme_type, scheme_version, NULL, GF_TRUE, &sinf); | |
| if (e) return e; | |
| if (scheme_uri) { | |
| sinf->scheme_type->flags |= 0x000001; | |
| sinf->scheme_type->URI = gf_strdup(scheme_uri); | |
| } | |
| if (kms_URI) { | |
| sinf->info->ikms = (GF_ISMAKMSBox *)gf_isom_box_new_parent(&sinf->info->child_boxes, GF_ISOM_BOX_TYPE_IKMS); | |
| sinf->info->ikms->URI = gf_strdup(kms_URI); | |
| } | |
| return GF_OK; | |
| } | |
| #endif // GPAC_DISABLE_ISOM_WRITE | |
| GF_EXPORT | |
| GF_Err gf_isom_get_original_format_type(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex, u32 *outOriginalFormat) | |
| { | |
| GF_TrackBox *trak; | |
| GF_SampleEntryBox *sea; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| u32 i, count; | |
| trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_BAD_PARAM; | |
| count = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes); | |
| for (i=0; i<count; i++) { | |
| if (sampleDescriptionIndex && (i+1 != sampleDescriptionIndex)) continue; | |
| Media_GetSampleDesc(trak->Media, i+1, &sea, NULL); | |
| if (!sea) return GF_BAD_PARAM; | |
| sinf = (GF_ProtectionSchemeInfoBox*) gf_isom_box_find_child(sea->child_boxes, GF_ISOM_BOX_TYPE_SINF); | |
| if (!sinf) sinf = (GF_ProtectionSchemeInfoBox*) gf_isom_box_find_child(sea->child_boxes, GF_ISOM_BOX_TYPE_RINF); | |
| if (!sinf) continue; | |
| if (outOriginalFormat && sinf->original_format) { | |
| *outOriginalFormat = sinf->original_format->data_format; | |
| } | |
| return GF_OK; | |
| } | |
| if (outOriginalFormat) *outOriginalFormat = 0; | |
| return GF_OK; | |
| } | |
| /* Common Encryption*/ | |
| GF_EXPORT | |
| Bool gf_isom_is_cenc_media(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex) | |
| { | |
| GF_TrackBox *trak; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| u32 i, count; | |
| trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak || !trak->Media || !trak->Media->information || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleDescription) | |
| return GF_FALSE; | |
| count = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes); | |
| for (i=0; i<count; i++) { | |
| if (sampleDescriptionIndex && (i+1 != sampleDescriptionIndex)) continue; | |
| sinf = isom_get_sinf_entry(trak, i+1, GF_ISOM_CENC_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, i+1, GF_ISOM_CBC_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, i+1, GF_ISOM_CENS_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, i+1, GF_ISOM_CBCS_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, i+1, GF_ISOM_PIFF_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, i+1, GF_ISOM_SVE1_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, i+1, GF_HLS_SAMPLE_AES_SCHEME, NULL); | |
| if (!sinf) continue; | |
| /*non-encrypted or non-CENC*/ | |
| if (!sinf->scheme_type) | |
| return GF_FALSE; | |
| switch (sinf->scheme_type->scheme_type) { | |
| case GF_ISOM_CENC_SCHEME: | |
| case GF_ISOM_CBC_SCHEME: | |
| case GF_ISOM_CENS_SCHEME: | |
| case GF_ISOM_CBCS_SCHEME: | |
| case GF_ISOM_SVE1_SCHEME: | |
| case GF_HLS_SAMPLE_AES_SCHEME: | |
| return GF_TRUE; | |
| default: | |
| return GF_FALSE; | |
| } | |
| return GF_TRUE; | |
| } | |
| return GF_FALSE; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_get_cenc_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex, u32 *outOriginalFormat, u32 *outSchemeType, u32 *outSchemeVersion) | |
| { | |
| GF_TrackBox *trak; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_BAD_PARAM; | |
| sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_CENC_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_CBC_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_CENS_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_CBCS_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_PIFF_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_SVE1_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_HLS_SAMPLE_AES_SCHEME, NULL); | |
| if (!sinf) return GF_BAD_PARAM; | |
| if (outOriginalFormat) { | |
| *outOriginalFormat = sinf->original_format->data_format; | |
| if (IsMP4Description(sinf->original_format->data_format)) *outOriginalFormat = GF_ISOM_SUBTYPE_MPEG4; | |
| } | |
| if (outSchemeType) *outSchemeType = sinf->scheme_type->scheme_type; | |
| if (outSchemeVersion) *outSchemeVersion = sinf->scheme_type->scheme_version; | |
| return GF_OK; | |
| } | |
| #ifndef GPAC_DISABLE_ISOM_WRITE | |
| GF_EXPORT | |
| GF_Err gf_isom_set_cenc_protection(GF_ISOFile *the_file, u32 trackNumber, u32 desc_index, u32 scheme_type, | |
| u32 scheme_version, u32 default_IsEncrypted, u32 default_crypt_byte_block, u32 default_skip_byte_block, | |
| u8 *key_info, u32 key_info_size) | |
| { | |
| GF_Err e; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| if (!gf_cenc_validate_key_info(key_info, key_info_size)) | |
| return GF_BAD_PARAM; | |
| //setup generic protection | |
| e = isom_set_protected_entry(the_file, trackNumber, desc_index, 0, 0, scheme_type, scheme_version, NULL, GF_FALSE, &sinf); | |
| if (e) return e; | |
| if (scheme_type==GF_ISOM_PIFF_SCHEME) { | |
| sinf->info->piff_tenc = (GF_PIFFTrackEncryptionBox *) gf_isom_box_new_parent(&sinf->info->child_boxes, GF_ISOM_BOX_UUID_TENC); | |
| if (!sinf->info->piff_tenc) return GF_OUT_OF_MEM; | |
| sinf->info->piff_tenc->AlgorithmID = 1; | |
| sinf->info->piff_tenc->key_info[0] = 0; | |
| sinf->info->piff_tenc->key_info[1] = 0; | |
| sinf->info->piff_tenc->key_info[2] = 0; | |
| sinf->info->piff_tenc->key_info[3] = key_info[3]; | |
| memcpy(sinf->info->piff_tenc->key_info+4, key_info+4, 16*sizeof(char)); | |
| } | |
| else { | |
| if (key_info_size<20) return GF_BAD_PARAM; | |
| sinf->info->tenc = (GF_TrackEncryptionBox *)gf_isom_box_new_parent(&sinf->info->child_boxes, GF_ISOM_BOX_TYPE_TENC); | |
| if (!sinf->info->tenc) return GF_OUT_OF_MEM; | |
| sinf->info->tenc->isProtected = default_IsEncrypted; | |
| if ((scheme_type == GF_ISOM_CENS_SCHEME) || (scheme_type == GF_ISOM_CBCS_SCHEME)) { | |
| sinf->info->tenc->version = 1; | |
| sinf->info->tenc->crypt_byte_block = default_crypt_byte_block; | |
| sinf->info->tenc->skip_byte_block = default_skip_byte_block; | |
| } | |
| if (key_info_size>37) key_info_size = 37; | |
| memcpy(sinf->info->tenc->key_info, key_info, key_info_size); | |
| } | |
| return GF_OK; | |
| } | |
| #if 0 | |
| /*! removes CENC SAI size info | |
| \param isom_file the target ISO file | |
| \param trackNumber the target track | |
| \return error if any | |
| */ | |
| GF_Err gf_isom_remove_cenc_saiz(GF_ISOFile *the_file, u32 trackNumber) | |
| { | |
| u32 i; | |
| GF_SampleTableBox *stbl; | |
| GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_BAD_PARAM; | |
| stbl = trak->Media->information->sampleTable; | |
| if (!stbl) | |
| return GF_BAD_PARAM; | |
| for (i = 0; i < gf_list_count(stbl->sai_sizes); i++) { | |
| GF_SampleAuxiliaryInfoSizeBox *saiz = (GF_SampleAuxiliaryInfoSizeBox *)gf_list_get(stbl->sai_sizes, i); | |
| switch (saiz->aux_info_type) { | |
| case GF_ISOM_CENC_SCHEME: | |
| case GF_ISOM_CENS_SCHEME: | |
| case GF_ISOM_CBC_SCHEME: | |
| case GF_ISOM_CBCS_SCHEME: | |
| case 0: | |
| break; | |
| default: | |
| continue; | |
| } | |
| gf_isom_box_del_parent(&stbl->child_boxes, (GF_Box *)saiz); | |
| gf_list_rem(stbl->sai_sizes, i); | |
| i--; | |
| } | |
| if (!gf_list_count(stbl->sai_sizes)) { | |
| gf_list_del(stbl->sai_sizes); | |
| stbl->sai_sizes = NULL; | |
| } | |
| return GF_OK; | |
| } | |
| /*! removes CENC SAI offset info | |
| \param isom_file the target ISO file | |
| \param trackNumber the target track | |
| \return error if any | |
| */ | |
| GF_Err gf_isom_remove_cenc_saio(GF_ISOFile *the_file, u32 trackNumber) | |
| { | |
| u32 i; | |
| GF_SampleTableBox *stbl; | |
| GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_BAD_PARAM; | |
| stbl = trak->Media->information->sampleTable; | |
| if (!stbl) | |
| return GF_BAD_PARAM; | |
| for (i = 0; i < gf_list_count(stbl->sai_offsets); i++) { | |
| GF_SampleAuxiliaryInfoOffsetBox *saio = (GF_SampleAuxiliaryInfoOffsetBox *)gf_list_get(stbl->sai_offsets, i); | |
| switch (saio->aux_info_type) { | |
| case GF_ISOM_CENC_SCHEME: | |
| case GF_ISOM_CENS_SCHEME: | |
| case GF_ISOM_CBC_SCHEME: | |
| case GF_ISOM_CBCS_SCHEME: | |
| case 0: | |
| break; | |
| default: | |
| continue; | |
| } | |
| gf_isom_box_del_parent(&stbl->child_boxes, (GF_Box *)saio); | |
| gf_list_rem(stbl->sai_offsets, i); | |
| i--; | |
| } | |
| if (!gf_list_count(stbl->sai_offsets)) { | |
| gf_list_del(stbl->sai_offsets); | |
| stbl->sai_offsets = NULL; | |
| } | |
| return GF_OK; | |
| } | |
| #endif | |
| GF_EXPORT | |
| GF_Err gf_isom_cenc_set_pssh(GF_ISOFile *file, bin128 systemID, u32 version, u32 KID_count, bin128 *KIDs, u8 *data, u32 len, u32 pssh_mode) | |
| { | |
| GF_ProtectionSystemHeaderBox *pssh = NULL; | |
| GF_PIFFProtectionSystemHeaderBox *pssh_piff = NULL; | |
| u32 i=0; | |
| GF_Box *a; | |
| GF_List **child_boxes = NULL, *moof_pssh = NULL; | |
| if (pssh_mode==2) { | |
| if (!file->meta) return GF_BAD_PARAM; | |
| if (!file->meta->child_boxes) file->meta->child_boxes = gf_list_new(); | |
| child_boxes = &file->meta->child_boxes; | |
| } else if (file->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) { | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| if (!file->moof) return GF_BAD_PARAM; | |
| if (!file->moof->PSSHs) file->moof->PSSHs = gf_list_new(); | |
| child_boxes = &file->moof->child_boxes; | |
| moof_pssh = file->moof->PSSHs; | |
| #else | |
| return GF_NOT_SUPPORTED; | |
| #endif | |
| } else { | |
| if (!file->moov) return GF_BAD_PARAM; | |
| if (!file->moov->child_boxes) file->moov->child_boxes = gf_list_new(); | |
| child_boxes = &file->moov->child_boxes; | |
| } | |
| while ((a = gf_list_enum(*child_boxes, &i))) { | |
| GF_UUIDBox *uuid = (GF_UUIDBox *)a; | |
| if (a->type==GF_ISOM_BOX_TYPE_PSSH) { | |
| pssh = (GF_ProtectionSystemHeaderBox *)a; | |
| if (!memcmp(pssh->SystemID, systemID, sizeof(bin128))) break; | |
| pssh = NULL; | |
| } else if ((a->type==GF_ISOM_BOX_TYPE_UUID) && (uuid->internal_4cc==GF_ISOM_BOX_UUID_PSSH)) { | |
| pssh_piff = (GF_PIFFProtectionSystemHeaderBox *)a; | |
| if (!memcmp(pssh_piff->SystemID, systemID, sizeof(bin128))) break; | |
| pssh_piff = NULL; | |
| } | |
| } | |
| //we had a pssh with same ID but different private data, keep both... | |
| if (pssh && pssh->private_data && len && memcmp(pssh->private_data, data, sizeof(char)*len) ) { | |
| pssh = NULL; | |
| } | |
| else if (pssh_piff && pssh_piff->private_data && len && memcmp(pssh_piff->private_data, data, sizeof(char)*len) ) { | |
| pssh_piff = NULL; | |
| } | |
| if (!pssh && !pssh_piff) { | |
| if (pssh_mode==1) { | |
| pssh_piff = (GF_PIFFProtectionSystemHeaderBox *)gf_isom_box_new_parent(child_boxes, GF_ISOM_BOX_UUID_PSSH); | |
| if (!pssh_piff) return GF_IO_ERR; | |
| memcpy((char *)pssh_piff->SystemID, systemID, sizeof(bin128)); | |
| pssh_piff->version = version; | |
| if (moof_pssh) gf_list_add(moof_pssh, pssh_piff); | |
| } else { | |
| pssh = (GF_ProtectionSystemHeaderBox *)gf_isom_box_new_parent(child_boxes, GF_ISOM_BOX_TYPE_PSSH); | |
| if (!pssh) return GF_IO_ERR; | |
| memcpy((char *)pssh->SystemID, systemID, sizeof(bin128)); | |
| pssh->version = version; | |
| if (moof_pssh) gf_list_add(moof_pssh, pssh); | |
| } | |
| } | |
| if (pssh && KID_count) { | |
| u32 j; | |
| for (j=0; j<KID_count; j++) { | |
| Bool found = GF_FALSE; | |
| for (i=0; i<pssh->KID_count; i++) { | |
| if (!memcmp(pssh->KIDs[i], KIDs[j], sizeof(bin128))) found = GF_TRUE; | |
| } | |
| if (!found) { | |
| pssh->KIDs = gf_realloc(pssh->KIDs, sizeof(bin128) * (pssh->KID_count+1)); | |
| if (!pssh->KIDs) return GF_OUT_OF_MEM; | |
| memcpy(pssh->KIDs[pssh->KID_count], KIDs[j], sizeof(bin128)); | |
| pssh->KID_count++; | |
| } | |
| } | |
| if (!pssh->version) | |
| pssh->version = 1; | |
| } | |
| if (pssh) { | |
| if (!pssh->private_data_size) { | |
| pssh->private_data_size = len; | |
| if (len) { | |
| if (!pssh->private_data) { | |
| pssh->private_data = (u8 *)gf_malloc(pssh->private_data_size*sizeof(char)); | |
| if (!pssh->private_data) return GF_OUT_OF_MEM; | |
| } | |
| memcpy((char *)pssh->private_data, data, pssh->private_data_size); | |
| } | |
| } | |
| } else if (pssh_piff) { | |
| if (!pssh_piff->private_data_size) { | |
| pssh_piff->private_data_size = len; | |
| if (len) { | |
| if (!pssh_piff->private_data) { | |
| pssh_piff->private_data = (u8 *)gf_malloc(pssh_piff->private_data_size*sizeof(char)); | |
| if (!pssh_piff->private_data) return GF_OUT_OF_MEM; | |
| } | |
| memcpy((char *)pssh_piff->private_data, data, pssh_piff->private_data_size); | |
| } | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_remove_cenc_senc_box(GF_ISOFile *the_file, u32 trackNumber) | |
| { | |
| u32 i; | |
| GF_SampleTableBox *stbl; | |
| GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_BAD_PARAM; | |
| stbl = trak->Media->information->sampleTable; | |
| if (!stbl) | |
| return GF_BAD_PARAM; | |
| for (i = 0; i < gf_list_count(stbl->child_boxes); i++) { | |
| GF_Box *a = (GF_Box *)gf_list_get(stbl->child_boxes, i); | |
| if ((a->type ==GF_ISOM_BOX_TYPE_UUID) && (((GF_UUIDBox *)a)->internal_4cc == GF_ISOM_BOX_UUID_PSEC)) { | |
| gf_isom_box_del_parent(&stbl->child_boxes, a); | |
| i--; | |
| } | |
| else if (a->type == GF_ISOM_BOX_TYPE_SENC) { | |
| gf_isom_box_del_parent(&stbl->child_boxes, a); | |
| i--; | |
| } | |
| } | |
| if (!gf_list_count(stbl->child_boxes)) { | |
| gf_list_del(stbl->child_boxes); | |
| stbl->child_boxes = NULL; | |
| } | |
| for (i = 0; i < gf_list_count(trak->child_boxes); i++) { | |
| GF_Box *a = (GF_Box *)gf_list_get(trak->child_boxes, i); | |
| if ((a->type ==GF_ISOM_BOX_TYPE_UUID) && (((GF_UUIDBox *)a)->internal_4cc == GF_ISOM_BOX_UUID_PSEC)) { | |
| gf_isom_box_del_parent(&trak->child_boxes, a); | |
| i--; | |
| } | |
| else if (a->type == GF_ISOM_BOX_TYPE_SENC) { | |
| gf_isom_box_del_parent(&trak->child_boxes, a); | |
| i--; | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_remove_cenc_seig_sample_group(GF_ISOFile *the_file, u32 trackNumber) | |
| { | |
| u32 i; | |
| GF_SampleTableBox *stbl; | |
| GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_BAD_PARAM; | |
| stbl = trak->Media->information->sampleTable; | |
| if (!stbl) | |
| return GF_BAD_PARAM; | |
| for (i = 0; i < gf_list_count(stbl->sampleGroupsDescription); i++) { | |
| GF_SampleGroupDescriptionBox *a = (GF_SampleGroupDescriptionBox *)gf_list_get(stbl->sampleGroupsDescription, i); | |
| if (a->grouping_type == GF_ISOM_SAMPLE_GROUP_SEIG) { | |
| gf_list_rem(stbl->sampleGroupsDescription, i); | |
| gf_isom_box_del_parent(&stbl->child_boxes, (GF_Box *) a); | |
| i--; | |
| } | |
| } | |
| if (!gf_list_count(stbl->sampleGroupsDescription)) { | |
| gf_list_del(stbl->sampleGroupsDescription); | |
| stbl->sampleGroupsDescription = NULL; | |
| } | |
| for (i = 0; i < gf_list_count(stbl->sampleGroups); i++) { | |
| GF_SampleGroupBox *a = (GF_SampleGroupBox *)gf_list_get(stbl->sampleGroups, i); | |
| if (a->grouping_type == GF_ISOM_SAMPLE_GROUP_SEIG) { | |
| gf_list_rem(stbl->sampleGroups, i); | |
| gf_isom_box_del_parent(&stbl->child_boxes, (GF_Box *) a); | |
| i--; | |
| } | |
| } | |
| if (!gf_list_count(stbl->sampleGroups)) { | |
| gf_list_del(stbl->sampleGroups); | |
| stbl->sampleGroups = NULL; | |
| } | |
| return GF_OK; | |
| } | |
| #if 0 //unused | |
| /*! removes CENC PSSH box | |
| \param isom_file the target ISO file | |
| \return error if any | |
| */ | |
| GF_Err gf_isom_remove_pssh_box(GF_ISOFile *the_file) | |
| { | |
| u32 i; | |
| for (i = 0; i < gf_list_count(the_file->moov->child_boxes); i++) { | |
| GF_Box *a = (GF_Box *)gf_list_get(the_file->moov->child_boxes, i); | |
| GF_UUIDBox *uuid = (GF_UUIDBox *)a; | |
| if ((a->type == GF_ISOM_BOX_TYPE_PSSH) | |
| || ((a->type == GF_ISOM_BOX_TYPE_UUID) && (uuid->internal_4cc == GF_ISOM_BOX_UUID_PSSH)) | |
| ) { | |
| gf_isom_box_del_parent(&the_file->moov->child_boxes, a); | |
| i--; | |
| } | |
| } | |
| if (!gf_list_count(the_file->moov->child_boxes)) { | |
| gf_list_del(the_file->moov->child_boxes); | |
| the_file->moov->child_boxes = NULL; | |
| } | |
| return GF_OK; | |
| } | |
| #endif | |
| #endif /*GPAC_DISABLE_ISOM_WRITE*/ | |
| GF_SampleEncryptionBox * gf_isom_create_piff_psec_box(u8 version, u32 flags, u32 AlgorithmID, u8 IV_size, bin128 KID) | |
| { | |
| GF_SampleEncryptionBox *psec; | |
| psec = (GF_SampleEncryptionBox *) gf_isom_box_new(GF_ISOM_BOX_UUID_PSEC); | |
| if (!psec) | |
| return NULL; | |
| psec->version = version; | |
| psec->flags = flags; | |
| psec->piff_type = 1; | |
| if (psec->flags & 0x1) { | |
| psec->AlgorithmID = AlgorithmID; | |
| psec->IV_size = IV_size; | |
| memcpy((char *)psec->KID, (const char *)KID, sizeof(bin128) ); | |
| } | |
| psec->samp_aux_info = gf_list_new(); | |
| return psec; | |
| } | |
| GF_SampleEncryptionBox * gf_isom_create_samp_enc_box(u8 version, u32 flags) | |
| { | |
| GF_SampleEncryptionBox *senc; | |
| senc = (GF_SampleEncryptionBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SENC); | |
| if (!senc) | |
| return NULL; | |
| senc->version = version; | |
| senc->flags = flags; | |
| senc->samp_aux_info = gf_list_new(); | |
| return senc; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_cenc_allocate_storage(GF_ISOFile *the_file, u32 trackNumber) | |
| { | |
| GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_BAD_PARAM; | |
| if (trak->sample_encryption) return GF_OK; | |
| trak->sample_encryption = (GF_SampleEncryptionBox *)gf_isom_create_samp_enc_box(0, 0); | |
| //senc will be written and destroyed with the other boxes | |
| if (!trak->child_boxes) trak->child_boxes = gf_list_new(); | |
| return gf_list_add(trak->child_boxes, trak->sample_encryption); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_piff_allocate_storage(GF_ISOFile *the_file, u32 trackNumber, u32 AlgorithmID, u8 IV_size, bin128 KID) | |
| { | |
| GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_BAD_PARAM; | |
| if (trak->sample_encryption) return GF_OK; | |
| trak->sample_encryption = (GF_SampleEncryptionBox *)gf_isom_create_piff_psec_box(1, 0, AlgorithmID, IV_size, KID); | |
| //senc will be written and destroyed with the other boxes | |
| if (!trak->child_boxes) trak->child_boxes = gf_list_new(); | |
| return gf_list_add(trak->child_boxes, trak->sample_encryption); | |
| } | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| void gf_isom_cenc_set_saiz_saio(GF_SampleEncryptionBox *senc, GF_SampleTableBox *stbl, GF_TrackFragmentBox *traf, u32 len, Bool saio_32bits, Bool use_multikey) | |
| { | |
| u32 i; | |
| GF_List **child_boxes = stbl ? &stbl->child_boxes : &traf->child_boxes; | |
| if (!senc->cenc_saiz) { | |
| senc->cenc_saiz = (GF_SampleAuxiliaryInfoSizeBox *) gf_isom_box_new_parent(child_boxes, GF_ISOM_BOX_TYPE_SAIZ); | |
| if (!senc->cenc_saiz) return; | |
| //as per 3rd edition of cenc "so content SHOULD be created omitting these optional fields" ... | |
| senc->cenc_saiz->aux_info_type = 0; | |
| senc->cenc_saiz->aux_info_type_parameter = use_multikey ? 1 : 0; | |
| if (stbl) | |
| stbl_on_child_box((GF_Box*)stbl, (GF_Box *)senc->cenc_saiz, GF_FALSE); | |
| else | |
| traf_on_child_box((GF_Box*)traf, (GF_Box *)senc->cenc_saiz, GF_FALSE); | |
| } | |
| if (!senc->cenc_saio) { | |
| senc->cenc_saio = (GF_SampleAuxiliaryInfoOffsetBox *) gf_isom_box_new_parent(child_boxes, GF_ISOM_BOX_TYPE_SAIO); | |
| if (!senc->cenc_saio) return; | |
| //force using version 1 for saio box, it could be redundant when we use 64 bits for offset | |
| senc->cenc_saio->version = saio_32bits ? 0 : 1; | |
| //as per 3rd edition of cenc "so content SHOULD be created omitting these optional fields" ... | |
| senc->cenc_saio->aux_info_type = 0; | |
| senc->cenc_saiz->aux_info_type_parameter = use_multikey ? 1 : 0; | |
| senc->cenc_saio->entry_count = 1; | |
| if (stbl) | |
| stbl_on_child_box((GF_Box*)stbl, (GF_Box *)senc->cenc_saio, GF_FALSE); | |
| else | |
| traf_on_child_box((GF_Box*)traf, (GF_Box *)senc->cenc_saio, GF_FALSE); | |
| } | |
| if (!senc->cenc_saiz->sample_count || ((senc->cenc_saiz->default_sample_info_size==len) && len) ) { | |
| senc->cenc_saiz->sample_count ++; | |
| senc->cenc_saiz->default_sample_info_size = len; | |
| } else { | |
| if (senc->cenc_saiz->sample_count + 1 > senc->cenc_saiz->sample_alloc) { | |
| senc->cenc_saiz->sample_alloc = senc->cenc_saiz->sample_count+10; | |
| senc->cenc_saiz->sample_info_size = (u8*)gf_realloc(senc->cenc_saiz->sample_info_size, sizeof(u8)*(senc->cenc_saiz->sample_alloc)); | |
| } | |
| if (senc->cenc_saiz->default_sample_info_size || (senc->cenc_saiz->sample_count==1)) { | |
| for (i=0; i<senc->cenc_saiz->sample_count; i++) | |
| senc->cenc_saiz->sample_info_size[i] = senc->cenc_saiz->default_sample_info_size; | |
| senc->cenc_saiz->default_sample_info_size = 0; | |
| } | |
| senc->cenc_saiz->sample_info_size[senc->cenc_saiz->sample_count] = len; | |
| senc->cenc_saiz->sample_count++; | |
| } | |
| } | |
| GF_Err gf_isom_cenc_merge_saiz_saio(GF_SampleEncryptionBox *senc, GF_SampleTableBox *stbl, u32 sample_number, u64 offset, u32 len) | |
| { | |
| u32 i; | |
| Bool is_first_saiz = GF_FALSE; | |
| gf_assert(stbl); | |
| if (!senc->cenc_saiz) { | |
| senc->cenc_saiz = (GF_SampleAuxiliaryInfoSizeBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_SAIZ); | |
| if (!senc->cenc_saiz) return GF_OUT_OF_MEM; | |
| senc->cenc_saiz->aux_info_type = GF_ISOM_CENC_SCHEME; | |
| senc->cenc_saiz->aux_info_type_parameter = 0; | |
| stbl_on_child_box((GF_Box*)stbl, (GF_Box *)senc->cenc_saiz, GF_FALSE); | |
| if (!sample_number) return GF_BAD_PARAM; | |
| senc->cenc_saiz->sample_count = sample_number - 1; | |
| is_first_saiz = GF_TRUE; | |
| } | |
| if (!senc->cenc_saio) { | |
| senc->cenc_saio = (GF_SampleAuxiliaryInfoOffsetBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_SAIO); | |
| if (!senc->cenc_saio) return GF_OUT_OF_MEM; | |
| //force using version 1 for saio box, it could be redundant when we use 64 bits for offset | |
| senc->cenc_saio->version = 1; | |
| senc->cenc_saio->aux_info_type = GF_ISOM_CENC_SCHEME; | |
| senc->cenc_saio->aux_info_type_parameter = 0; | |
| stbl_on_child_box((GF_Box*)stbl, (GF_Box *)senc->cenc_saio, GF_FALSE); | |
| } | |
| if (sample_number <= senc->cenc_saiz->sample_count) | |
| return GF_BAD_PARAM; | |
| if (sample_number <= senc->cenc_saio->entry_count) | |
| return GF_BAD_PARAM; | |
| if (!senc->cenc_saiz->sample_count || (!senc->cenc_saiz->sample_alloc && (senc->cenc_saiz->default_sample_info_size==len))) { | |
| senc->cenc_saiz->sample_count ++; | |
| senc->cenc_saiz->default_sample_info_size = len; | |
| } else { | |
| u32 sample_diff = sample_number - senc->cenc_saiz->sample_count; | |
| if (senc->cenc_saiz->sample_count + sample_diff > senc->cenc_saiz->sample_alloc) { | |
| senc->cenc_saiz->sample_alloc = senc->cenc_saiz->sample_count + sample_diff + 10; | |
| senc->cenc_saiz->sample_info_size = (u8*)gf_realloc(senc->cenc_saiz->sample_info_size, sizeof(u8)*(senc->cenc_saiz->sample_alloc)); | |
| if (!senc->cenc_saiz->sample_info_size) return GF_OUT_OF_MEM; | |
| } | |
| if (senc->cenc_saiz->default_sample_info_size) { | |
| for (i=0; i<senc->cenc_saiz->sample_count; i++) | |
| senc->cenc_saiz->sample_info_size[i] = senc->cenc_saiz->default_sample_info_size; | |
| senc->cenc_saiz->default_sample_info_size = 0; | |
| } else if (is_first_saiz) { | |
| memset(senc->cenc_saiz->sample_info_size, 0, sizeof(u8)*(senc->cenc_saiz->sample_alloc)); | |
| } | |
| for (i=0; i<sample_diff-1; i++) { | |
| senc->cenc_saiz->sample_info_size[senc->cenc_saiz->sample_count + i] = 0; | |
| } | |
| senc->cenc_saiz->sample_info_size[senc->cenc_saiz->sample_count + sample_diff-1] = len; | |
| senc->cenc_saiz->sample_count += sample_diff; | |
| } | |
| if (!senc->cenc_saio->entry_count && !is_first_saiz) { | |
| senc->cenc_saio->offsets = (u64 *)gf_malloc(sizeof(u64)); | |
| if (!senc->cenc_saio->offsets) return GF_OUT_OF_MEM; | |
| senc->cenc_saio->offsets[0] = offset; | |
| senc->cenc_saio->entry_count ++; | |
| senc->cenc_saio->entry_alloc = 1; | |
| } else { | |
| u32 sample_diff; | |
| if (is_first_saiz) | |
| senc->cenc_saio->entry_count = senc->cenc_saio->entry_alloc = sample_number - 1; | |
| sample_diff = sample_number - senc->cenc_saio->entry_count; | |
| if (senc->cenc_saio->entry_count + sample_diff > senc->cenc_saio->entry_alloc) { | |
| senc->cenc_saio->entry_alloc += MAX(50, sample_diff); | |
| senc->cenc_saio->offsets = (u64*)gf_realloc(senc->cenc_saio->offsets, sizeof(u64)*(senc->cenc_saio->entry_alloc)); | |
| if (!senc->cenc_saio->offsets) return GF_OUT_OF_MEM; | |
| } | |
| if (is_first_saiz) { | |
| memset(senc->cenc_saio->offsets, 0, sizeof(u64)*(senc->cenc_saio->entry_alloc)); | |
| } | |
| for (i=0; i<sample_diff-1; i++) { | |
| senc->cenc_saio->offsets[senc->cenc_saio->entry_count + i] = 0; | |
| } | |
| senc->cenc_saio->offsets[senc->cenc_saio->entry_count + sample_diff-1] = offset; | |
| senc->cenc_saio->entry_count++; | |
| } | |
| if (offset > 0xFFFFFFFFUL) | |
| senc->cenc_saio->version=1; | |
| return GF_OK; | |
| } | |
| #endif /* GPAC_DISABLE_ISOM_FRAGMENTS */ | |
| GF_EXPORT | |
| GF_Err gf_isom_track_cenc_add_sample_info(GF_ISOFile *the_file, u32 trackNumber, u32 container_type, u8 *buf, u32 len, Bool use_subsamples, Bool use_saio_32bit, Bool use_multikey) | |
| { | |
| GF_SampleEncryptionBox *senc; | |
| GF_CENCSampleAuxInfo *sai; | |
| GF_SampleTableBox *stbl; | |
| GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_BAD_PARAM; | |
| stbl = trak->Media->information->sampleTable; | |
| if (!stbl) return GF_BAD_PARAM; | |
| switch (container_type) { | |
| case GF_ISOM_BOX_UUID_PSEC: | |
| case GF_ISOM_BOX_TYPE_SENC: | |
| case 0: | |
| senc = trak->sample_encryption; | |
| if (!senc) return GF_BAD_PARAM; | |
| break; | |
| default: | |
| return GF_NOT_SUPPORTED; | |
| } | |
| if (len && buf) { | |
| GF_SAFEALLOC(sai, GF_CENCSampleAuxInfo); | |
| if (!sai) return GF_OUT_OF_MEM; | |
| sai->cenc_data_size = len; | |
| sai->cenc_data = gf_malloc(sizeof(u8) * len); | |
| if (!sai->cenc_data) { | |
| gf_free(sai); | |
| return GF_OUT_OF_MEM; | |
| } | |
| memcpy(sai->cenc_data, buf, len); | |
| gf_list_add(senc->samp_aux_info, sai); | |
| } else { | |
| GF_SAFEALLOC(sai, GF_CENCSampleAuxInfo); | |
| if (!sai) return GF_OUT_OF_MEM; | |
| gf_list_add(senc->samp_aux_info, sai); | |
| sai->isNotProtected = 1; | |
| } | |
| if (use_subsamples) | |
| senc->flags = 0x00000002; | |
| if (use_multikey) | |
| senc->version = 1; | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| gf_isom_cenc_set_saiz_saio(senc, stbl, NULL, sai->cenc_data_size, use_saio_32bit, use_multikey); | |
| #endif | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| void gf_isom_cenc_samp_aux_info_del(GF_CENCSampleAuxInfo *samp) | |
| { | |
| if (!samp) return; | |
| if (samp->cenc_data) gf_free(samp->cenc_data); | |
| gf_free(samp); | |
| } | |
| Bool gf_isom_cenc_has_saiz_saio_full(GF_SampleTableBox *stbl, void *_traf, u32 scheme_type) | |
| { | |
| u32 i, c1, c2; | |
| GF_List *sai_sizes, *sai_offsets; | |
| u32 sinf_fmt = 0; | |
| Bool has_saiz, has_saio; | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| GF_TrackFragmentBox *traf=(GF_TrackFragmentBox *)_traf; | |
| #endif | |
| has_saiz = has_saio = GF_FALSE; | |
| if (stbl) { | |
| if (!stbl->patch_piff_psec) { | |
| stbl->patch_piff_psec = gf_opts_get_bool("core", "piff-force-subsamples") ? 2 : 1; | |
| } | |
| if (stbl->patch_piff_psec==2) | |
| return GF_FALSE; | |
| sai_sizes = stbl->sai_sizes; | |
| sai_offsets = stbl->sai_offsets; | |
| } | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| else if (_traf) { | |
| if (traf->trex && traf->trex->track && traf->trex->track->Media->information->sampleTable) { | |
| GF_SampleTableBox *_stbl = traf->trex->track->Media->information->sampleTable; | |
| if (!_stbl->patch_piff_psec) { | |
| _stbl->patch_piff_psec = gf_opts_get_bool("core", "piff-force-subsamples") ? 2 : 1; | |
| } | |
| if (_stbl->patch_piff_psec==2) | |
| return GF_FALSE; | |
| } | |
| sai_sizes = traf->sai_sizes; | |
| sai_offsets = traf->sai_offsets; | |
| } | |
| #endif | |
| else | |
| return GF_FALSE; | |
| c1 = gf_list_count(sai_sizes); | |
| c2 = gf_list_count(sai_offsets); | |
| for (i = 0; i < c1; i++) { | |
| GF_SampleAuxiliaryInfoSizeBox *saiz = (GF_SampleAuxiliaryInfoSizeBox *)gf_list_get(sai_sizes, i); | |
| u32 saiz_aux_info_type = saiz->aux_info_type; | |
| if (!saiz_aux_info_type) saiz_aux_info_type = scheme_type; | |
| if (!saiz_aux_info_type) { | |
| GF_SampleEntryBox *entry = NULL; | |
| GF_ProtectionSchemeInfoBox *sinf = NULL; | |
| if (stbl) { | |
| entry = gf_list_get(stbl->SampleDescription->child_boxes, 0); | |
| } else { | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| entry = gf_list_get(traf->trex->track->Media->information->sampleTable->SampleDescription->child_boxes, 0); | |
| #endif | |
| } | |
| if (entry) | |
| sinf = (GF_ProtectionSchemeInfoBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_SINF); | |
| if (sinf && sinf->scheme_type) { | |
| saiz_aux_info_type = sinf_fmt = sinf->scheme_type->scheme_type; | |
| } | |
| //default to cenc for smooth | |
| else if (traf->trex->track->moov->mov->is_smooth) | |
| saiz_aux_info_type = sinf_fmt = GF_ISOM_CENC_SCHEME; | |
| } | |
| if (!saiz_aux_info_type && (c1==1) && (c2==1)) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] saiz box without flags nor aux info type and no default scheme, ignoring\n")); | |
| continue; | |
| } | |
| switch (saiz_aux_info_type) { | |
| case GF_ISOM_CENC_SCHEME: | |
| case GF_ISOM_CBC_SCHEME: | |
| case GF_ISOM_CENS_SCHEME: | |
| case GF_ISOM_CBCS_SCHEME: | |
| case GF_ISOM_PIFF_SCHEME: | |
| has_saiz = GF_TRUE; | |
| break; | |
| } | |
| } | |
| for (i = 0; i < c2; i++) { | |
| GF_SampleAuxiliaryInfoOffsetBox *saio = (GF_SampleAuxiliaryInfoOffsetBox *)gf_list_get(sai_offsets, i); | |
| u32 saio_aux_info_type = saio->aux_info_type; | |
| if (!saio_aux_info_type) saio_aux_info_type = scheme_type; | |
| if (!saio_aux_info_type) saio_aux_info_type = sinf_fmt; | |
| if (!saio_aux_info_type) { | |
| GF_SampleEntryBox *entry = NULL; | |
| GF_ProtectionSchemeInfoBox *sinf = NULL; | |
| if (stbl) { | |
| entry = gf_list_get(stbl->SampleDescription->child_boxes, 0); | |
| } else { | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| entry = gf_list_get(traf->trex->track->Media->information->sampleTable->SampleDescription->child_boxes, 0); | |
| #endif | |
| } | |
| if (entry) | |
| sinf = (GF_ProtectionSchemeInfoBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_SINF); | |
| if (sinf && sinf->scheme_type) { | |
| saio_aux_info_type = sinf_fmt = sinf->scheme_type->scheme_type; | |
| } | |
| //default to cenc for smooth | |
| else if (traf->trex->track->moov->mov->is_smooth) | |
| saio_aux_info_type = sinf_fmt = GF_ISOM_CENC_SCHEME; | |
| } | |
| if (!saio_aux_info_type && (c1==1) && (c2==1)) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] saio box without flags nor aux info type and no default scheme, ignoring\n")); | |
| continue; | |
| } | |
| //special case for query on a file that has just been imported but not yet written: saio offset is NULL, we must use senc | |
| if (saio->entry_count && !saio->offsets) | |
| continue; | |
| switch (saio_aux_info_type) { | |
| case GF_ISOM_CENC_SCHEME: | |
| case GF_ISOM_CBC_SCHEME: | |
| case GF_ISOM_CENS_SCHEME: | |
| case GF_ISOM_CBCS_SCHEME: | |
| case GF_ISOM_PIFF_SCHEME: | |
| has_saio = GF_TRUE; | |
| break; | |
| } | |
| } | |
| return (has_saiz && has_saio); | |
| } | |
| Bool gf_isom_cenc_has_saiz_saio_track(GF_SampleTableBox *stbl, u32 scheme_type) | |
| { | |
| return gf_isom_cenc_has_saiz_saio_full(stbl, NULL, scheme_type); | |
| } | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| Bool gf_isom_cenc_has_saiz_saio_traf(GF_TrackFragmentBox *traf, u32 scheme_type) | |
| { | |
| return gf_isom_cenc_has_saiz_saio_full(NULL, traf, scheme_type); | |
| } | |
| #endif | |
| static GF_Err isom_cenc_get_sai_by_saiz_saio(GF_MediaBox *mdia, u32 sampleNumber, u32 scheme_type, u8 **out_buffer, u32 *out_size) | |
| { | |
| u32 prev_sai_size, size, i, j, nb_saio; | |
| u64 cur_position, offset; | |
| GF_Err e = GF_OK; | |
| GF_SampleAuxiliaryInfoOffsetBox *saio_cenc=NULL; | |
| GF_SampleAuxiliaryInfoSizeBox *saiz_cenc=NULL; | |
| nb_saio = size = prev_sai_size = 0; | |
| offset = 0; | |
| if (! out_buffer || !out_size) return GF_BAD_PARAM; | |
| for (i = 0; i < gf_list_count(mdia->information->sampleTable->sai_offsets); i++) { | |
| GF_SampleAuxiliaryInfoOffsetBox *saio = (GF_SampleAuxiliaryInfoOffsetBox *)gf_list_get(mdia->information->sampleTable->sai_offsets, i); | |
| u32 aux_info_type = saio->aux_info_type; | |
| if (!aux_info_type) aux_info_type = scheme_type; | |
| switch (aux_info_type) { | |
| case GF_ISOM_CENC_SCHEME: | |
| case GF_ISOM_CBC_SCHEME: | |
| case GF_ISOM_CENS_SCHEME: | |
| case GF_ISOM_CBCS_SCHEME: | |
| case GF_ISOM_PIFF_SCHEME: | |
| break; | |
| default: | |
| continue; | |
| } | |
| if (saio->entry_count == 1) { | |
| offset = saio->offsets[0]; | |
| } else if (sampleNumber>saio->entry_count) { | |
| return GF_ISOM_INVALID_FILE; | |
| } else { | |
| offset = saio->offsets[sampleNumber-1]; | |
| } | |
| nb_saio = saio->entry_count; | |
| saio_cenc = saio; | |
| break; | |
| } | |
| if (!saio_cenc) return GF_ISOM_INVALID_FILE; | |
| for (i = 0; i < gf_list_count(mdia->information->sampleTable->sai_sizes); i++) { | |
| GF_SampleAuxiliaryInfoSizeBox *saiz = (GF_SampleAuxiliaryInfoSizeBox *)gf_list_get(mdia->information->sampleTable->sai_sizes, i); | |
| u32 aux_info_type = saiz->aux_info_type; | |
| if (!aux_info_type) aux_info_type = scheme_type; | |
| switch (aux_info_type) { | |
| case GF_ISOM_CENC_SCHEME: | |
| case GF_ISOM_CBC_SCHEME: | |
| case GF_ISOM_CENS_SCHEME: | |
| case GF_ISOM_CBCS_SCHEME: | |
| case GF_ISOM_PIFF_SCHEME: | |
| break; | |
| default: | |
| continue; | |
| } | |
| if (sampleNumber>saiz->sample_count) { | |
| return GF_ISOM_INVALID_FILE; | |
| } | |
| if ((nb_saio==1) && !saio_cenc->total_size) { | |
| for (j = 0; j < saiz->sample_count; j++) { | |
| saio_cenc->total_size += (saiz->default_sample_info_size || !saiz->sample_info_size) ? saiz->default_sample_info_size : saiz->sample_info_size[j]; | |
| } | |
| } | |
| if (saiz->cached_sample_num+1== sampleNumber) { | |
| prev_sai_size = saiz->cached_prev_size; | |
| } else { | |
| if (sampleNumber > saiz->sample_count) | |
| return GF_ISOM_INVALID_FILE; | |
| for (j = 0; j < sampleNumber-1; j++) | |
| prev_sai_size += (saiz->default_sample_info_size || !saiz->sample_info_size) ? saiz->default_sample_info_size : saiz->sample_info_size[j]; | |
| } | |
| if (saiz->default_sample_info_size) { | |
| size = saiz->default_sample_info_size; | |
| } else if (sampleNumber > saiz->sample_count) { | |
| return GF_ISOM_INVALID_FILE; | |
| } else if (!saiz->sample_info_size) { | |
| //CBCS | |
| size = 0; | |
| } else { | |
| size = saiz->sample_info_size[sampleNumber-1]; | |
| } | |
| saiz_cenc=saiz; | |
| break; | |
| } | |
| if (!saiz_cenc) return GF_BAD_PARAM; | |
| saiz_cenc->cached_sample_num = sampleNumber; | |
| saiz_cenc->cached_prev_size = prev_sai_size + size; | |
| if (saio_cenc->total_size) { | |
| if (!saio_cenc->cached_data) { | |
| saio_cenc->cached_data = gf_malloc(sizeof(u8)*saio_cenc->total_size); | |
| if (!saio_cenc->cached_data) return GF_OUT_OF_MEM; | |
| cur_position = gf_bs_get_position(mdia->information->dataHandler->bs); | |
| //offset is as written in saio (relative to frag base data offset), compensate removed bytes | |
| gf_bs_seek(mdia->information->dataHandler->bs, offset - mdia->mediaTrack->moov->mov->bytes_removed ); | |
| gf_bs_read_data(mdia->information->dataHandler->bs, saio_cenc->cached_data, saio_cenc->total_size); | |
| gf_bs_seek(mdia->information->dataHandler->bs, cur_position); | |
| } | |
| if (out_size) { | |
| if (out_buffer) { | |
| if ((*out_size) < size) { | |
| (*out_buffer) = gf_realloc((*out_buffer), sizeof(char)*(size) ); | |
| if (! *out_buffer) return GF_OUT_OF_MEM; | |
| } | |
| memcpy((*out_buffer), saio_cenc->cached_data + prev_sai_size, size); | |
| } | |
| (*out_size) = size; | |
| } | |
| return GF_OK; | |
| } | |
| offset += (nb_saio == 1) ? prev_sai_size : 0; | |
| cur_position = gf_bs_get_position(mdia->information->dataHandler->bs); | |
| //offset is as written in saio (relative to frag base data offset), compensate removed bytes | |
| gf_bs_seek(mdia->information->dataHandler->bs, offset - mdia->mediaTrack->moov->mov->bytes_removed); | |
| if (out_buffer) { | |
| if ((*out_size) < size) { | |
| (*out_buffer) = gf_realloc((*out_buffer), sizeof(char)*(size) ); | |
| if (! *out_buffer) return GF_OUT_OF_MEM; | |
| } | |
| if ((*out_buffer) && size) | |
| gf_bs_read_data(mdia->information->dataHandler->bs, (*out_buffer), size); | |
| } | |
| (*out_size) = size; | |
| gf_bs_seek(mdia->information->dataHandler->bs, cur_position); | |
| return e; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_cenc_get_sample_aux_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u32 sampleDescIndex, u32 *container_type, u8 **out_buffer, u32 *outSize) | |
| { | |
| GF_TrackBox *trak; | |
| GF_SampleTableBox *stbl; | |
| GF_SampleEncryptionBox *senc = NULL; | |
| u32 type, scheme_type = -1; | |
| GF_CENCSampleAuxInfo *a_sai; | |
| trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_BAD_PARAM; | |
| stbl = trak->Media->information->sampleTable; | |
| if (!stbl) | |
| return GF_BAD_PARAM; | |
| type = 0; | |
| senc = trak->sample_encryption; | |
| //no senc is OK | |
| if (senc) { | |
| if ((senc->type == GF_ISOM_BOX_TYPE_UUID) && (((GF_UUIDBox *)senc)->internal_4cc == GF_ISOM_BOX_UUID_PSEC)) { | |
| type = GF_ISOM_BOX_UUID_PSEC; | |
| } else if (senc->type == GF_ISOM_BOX_TYPE_SENC) { | |
| type = GF_ISOM_BOX_TYPE_SENC; | |
| } | |
| if (container_type) *container_type = type; | |
| } | |
| if (!out_buffer) return GF_OK; /*we need only container_type*/ | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| sampleNumber -= trak->sample_count_at_seg_start; | |
| #endif | |
| gf_isom_get_cenc_info(the_file, trackNumber, sampleDescIndex, NULL, &scheme_type, NULL); | |
| /*! if we have saiz/saio use this directly*/ | |
| if (gf_isom_cenc_has_saiz_saio_track(stbl, scheme_type)) { | |
| return isom_cenc_get_sai_by_saiz_saio(trak->Media, sampleNumber, scheme_type, out_buffer, outSize); | |
| } | |
| if (!senc) | |
| return GF_OK; | |
| //senc is not loaded by default, do it now | |
| if (!gf_list_count(senc->samp_aux_info)) { | |
| GF_Err e = senc_Parse(trak->Media->information->dataHandler->bs, trak, NULL, senc, 0); | |
| if (e) return e; | |
| } | |
| a_sai = NULL; | |
| switch (type) { | |
| case GF_ISOM_BOX_UUID_PSEC: | |
| if (senc) | |
| a_sai = (GF_CENCSampleAuxInfo *)gf_list_get(senc->samp_aux_info, sampleNumber-1); | |
| break; | |
| case GF_ISOM_BOX_TYPE_SENC: | |
| if (senc) | |
| a_sai = (GF_CENCSampleAuxInfo *)gf_list_get(senc->samp_aux_info, sampleNumber-1); | |
| break; | |
| } | |
| //not present, check we use constant IV and no IV size | |
| if (!a_sai) { | |
| const u8 *key_info=NULL; | |
| u32 key_info_size=0; | |
| u8 IV_size=0, constant_IV_size=0; | |
| Bool is_Protected; | |
| #ifndef GPAC_DISABLE_ISOM_FRAGMENTS | |
| //read number of trashed samples since gf_isom_get_sample_cenc_info_internal removes them | |
| sampleNumber += trak->sample_count_at_seg_start; | |
| #endif | |
| gf_isom_get_sample_cenc_info_internal(trak, NULL, senc, sampleNumber, &is_Protected, NULL, NULL, &key_info, &key_info_size); | |
| if (!key_info) { | |
| IV_size = key_info_size; //piff default | |
| } else { | |
| IV_size = key_info[3]; | |
| if (!IV_size) | |
| constant_IV_size = key_info[20]; | |
| } | |
| if (!IV_size && constant_IV_size) | |
| return GF_OK; | |
| return GF_NOT_FOUND; | |
| } | |
| if (*outSize < a_sai->cenc_data_size) { | |
| *out_buffer = gf_realloc(*out_buffer, sizeof(char) * a_sai->cenc_data_size); | |
| if (! *out_buffer) return GF_OUT_OF_MEM; | |
| *outSize = a_sai->cenc_data_size; | |
| } | |
| memcpy(*out_buffer, a_sai->cenc_data, a_sai->cenc_data_size); | |
| return GF_OK; | |
| } | |
| void gf_isom_cenc_get_default_info_internal(GF_TrackBox *trak, u32 sampleDescriptionIndex, u32 *container_type, Bool *default_IsEncrypted, u32 *crypt_byte_block, u32 *skip_byte_block, const u8 **key_info, u32 *key_info_size) | |
| { | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| //setup all default as not encrypted | |
| if (default_IsEncrypted) *default_IsEncrypted = GF_FALSE; | |
| if (crypt_byte_block) *crypt_byte_block = 0; | |
| if (skip_byte_block) *skip_byte_block = 0; | |
| if (container_type) *container_type = 0; | |
| if (key_info) *key_info = NULL; | |
| if (key_info_size) *key_info_size = 0; | |
| sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_CENC_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_CBC_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_CENS_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_CBCS_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_PIFF_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_SVE1_SCHEME, NULL); | |
| if (!sinf) sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_HLS_SAMPLE_AES_SCHEME, NULL); | |
| if (!sinf) { | |
| u32 i, nb_stsd = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes); | |
| for (i=0; i<nb_stsd; i++) { | |
| GF_ProtectionSchemeInfoBox *a_sinf; | |
| GF_SampleEntryBox *sentry=NULL; | |
| if (i+1==sampleDescriptionIndex) continue; | |
| sentry = gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, i); | |
| a_sinf = (GF_ProtectionSchemeInfoBox *) gf_isom_box_find_child(sentry->child_boxes, GF_ISOM_BOX_TYPE_SINF); | |
| if (!a_sinf) continue; | |
| //signal default (not encrypted) | |
| return; | |
| } | |
| } | |
| if (sinf && sinf->info && sinf->info->tenc) { | |
| if (default_IsEncrypted) *default_IsEncrypted = sinf->info->tenc->isProtected; | |
| if (crypt_byte_block) *crypt_byte_block = sinf->info->tenc->crypt_byte_block; | |
| if (skip_byte_block) *skip_byte_block = sinf->info->tenc->skip_byte_block; | |
| if (key_info) *key_info = sinf->info->tenc->key_info; | |
| if (key_info_size) { | |
| *key_info_size = 20; | |
| if (!sinf->info->tenc->key_info[3]) | |
| *key_info_size += 1 + sinf->info->tenc->key_info[20]; | |
| } | |
| //set default value, overwritten below | |
| if (container_type) *container_type = GF_ISOM_BOX_TYPE_SENC; | |
| } else if (sinf && sinf->info && sinf->info->piff_tenc) { | |
| if (default_IsEncrypted) *default_IsEncrypted = GF_TRUE; | |
| if (key_info) *key_info = sinf->info->piff_tenc->key_info; | |
| if (key_info_size) *key_info_size = 19; | |
| //set default value, overwritten below | |
| if (container_type) *container_type = GF_ISOM_BOX_UUID_PSEC; | |
| } else { | |
| u32 i, count = 0; | |
| GF_CENCSampleEncryptionGroupEntry *seig_entry = NULL; | |
| if (!trak->moov->mov->is_smooth) | |
| count = gf_list_count(trak->Media->information->sampleTable->sampleGroupsDescription); | |
| for (i=0; i<count; i++) { | |
| GF_SampleGroupDescriptionBox *sgdesc = (GF_SampleGroupDescriptionBox*)gf_list_get(trak->Media->information->sampleTable->sampleGroupsDescription, i); | |
| if (sgdesc->grouping_type!=GF_ISOM_SAMPLE_GROUP_SEIG) continue; | |
| if (sgdesc->default_description_index) | |
| seig_entry = gf_list_get(sgdesc->group_descriptions, sgdesc->default_description_index-1); | |
| else | |
| seig_entry = gf_list_get(sgdesc->group_descriptions, 0); | |
| if (seig_entry && !seig_entry->key_info[0]) | |
| seig_entry = NULL; | |
| break; | |
| } | |
| if (seig_entry) { | |
| if (default_IsEncrypted) *default_IsEncrypted = seig_entry->IsProtected; | |
| if (crypt_byte_block) *crypt_byte_block = seig_entry->crypt_byte_block; | |
| if (skip_byte_block) *skip_byte_block = seig_entry->skip_byte_block; | |
| if (key_info) *key_info = seig_entry->key_info; | |
| if (key_info_size) *key_info_size = seig_entry->key_info_size; | |
| if (container_type) *container_type = GF_ISOM_BOX_TYPE_SENC; | |
| } else { | |
| if (! trak->moov->mov->is_smooth ) { | |
| trak->moov->mov->is_smooth = GF_TRUE; | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] senc box without tenc, assuming MS smooth+piff\n")); | |
| } | |
| if (default_IsEncrypted) *default_IsEncrypted = GF_TRUE; | |
| //set default value, overwritten below | |
| if (container_type) *container_type = GF_ISOM_BOX_UUID_PSEC; | |
| } | |
| } | |
| if (container_type && trak->sample_encryption) { | |
| if (trak->sample_encryption->type == GF_ISOM_BOX_TYPE_SENC) *container_type = GF_ISOM_BOX_TYPE_SENC; | |
| else if (trak->sample_encryption->type == GF_ISOM_BOX_TYPE_UUID) *container_type = ((GF_UUIDBox*)trak->sample_encryption)->internal_4cc; | |
| } | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_cenc_get_default_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex, u32 *container_type, Bool *default_IsEncrypted, u32 *crypt_byte_block, u32 *skip_byte_block, const u8 **key_info, u32 *key_info_size) | |
| { | |
| GF_TrackBox *trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_BAD_PARAM; | |
| gf_isom_cenc_get_default_info_internal(trak, sampleDescriptionIndex, container_type, default_IsEncrypted, crypt_byte_block, skip_byte_block, key_info, key_info_size); | |
| return GF_OK; | |
| } | |
| /* | |
| Adobe'protection scheme | |
| */ | |
| GF_EXPORT | |
| GF_Err gf_isom_set_adobe_protection(GF_ISOFile *the_file, u32 trackNumber, u32 desc_index, u32 scheme_type, u32 scheme_version, Bool is_selective_enc, char *metadata, u32 len) | |
| { | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| //setup generic protection | |
| #ifndef GPAC_DISABLE_ISOM_WRITE | |
| GF_Err e; | |
| e = isom_set_protected_entry(the_file, trackNumber, desc_index, 1, 0, scheme_type, scheme_version, NULL, GF_FALSE, &sinf); | |
| if (e) return e; | |
| #else | |
| return GF_NOT_SUPPORTED; | |
| #endif | |
| sinf->info->adkm = (GF_AdobeDRMKeyManagementSystemBox *)gf_isom_box_new_parent(&sinf->info->child_boxes, GF_ISOM_BOX_TYPE_ADKM); | |
| if (!sinf->info->adkm) return GF_OUT_OF_MEM; | |
| sinf->info->adkm->header = (GF_AdobeDRMHeaderBox *)gf_isom_box_new_parent(&sinf->info->adkm->child_boxes, GF_ISOM_BOX_TYPE_AHDR); | |
| if (!sinf->info->adkm->header) return GF_OUT_OF_MEM; | |
| sinf->info->adkm->header->std_enc_params = (GF_AdobeStdEncryptionParamsBox *)gf_isom_box_new_parent(& sinf->info->adkm->header->child_boxes, GF_ISOM_BOX_TYPE_APRM); | |
| if (!sinf->info->adkm->header->std_enc_params) return GF_OUT_OF_MEM; | |
| sinf->info->adkm->header->std_enc_params->enc_info = (GF_AdobeEncryptionInfoBox *)gf_isom_box_new_parent(&sinf->info->adkm->header->std_enc_params->child_boxes, GF_ISOM_BOX_TYPE_AEIB); | |
| if (!sinf->info->adkm->header->std_enc_params->enc_info) return GF_OUT_OF_MEM; | |
| sinf->info->adkm->header->std_enc_params->enc_info->enc_algo = (char *)gf_strdup("AES-CBC"); | |
| if (!sinf->info->adkm->header->std_enc_params->enc_info->enc_algo) return GF_OUT_OF_MEM; | |
| sinf->info->adkm->header->std_enc_params->enc_info->key_length = 16; | |
| sinf->info->adkm->header->std_enc_params->key_info = (GF_AdobeKeyInfoBox *)gf_isom_box_new_parent(&sinf->info->adkm->header->std_enc_params->child_boxes, GF_ISOM_BOX_TYPE_AKEY); | |
| if (!sinf->info->adkm->header->std_enc_params->key_info) return GF_OUT_OF_MEM; | |
| sinf->info->adkm->header->std_enc_params->key_info->params = (GF_AdobeFlashAccessParamsBox *)gf_isom_box_new_parent(&sinf->info->adkm->header->std_enc_params->key_info->child_boxes, GF_ISOM_BOX_TYPE_FLXS); | |
| if (!sinf->info->adkm->header->std_enc_params->key_info->params) return GF_OUT_OF_MEM; | |
| if (metadata && len) { | |
| sinf->info->adkm->header->std_enc_params->key_info->params->metadata = (char *)gf_malloc((len+1)*sizeof(char)); | |
| if (!sinf->info->adkm->header->std_enc_params->key_info->params->metadata) return GF_OUT_OF_MEM; | |
| memcpy(sinf->info->adkm->header->std_enc_params->key_info->params->metadata, metadata, len); | |
| sinf->info->adkm->header->std_enc_params->key_info->params->metadata[len] = 0; | |
| } | |
| sinf->info->adkm->au_format = (GF_AdobeDRMAUFormatBox *)gf_isom_box_new_parent(&sinf->info->adkm->child_boxes, GF_ISOM_BOX_TYPE_ADAF); | |
| if (!sinf->info->adkm->au_format) return GF_OUT_OF_MEM; | |
| sinf->info->adkm->au_format->selective_enc = is_selective_enc ? 0x10 : 0x00; | |
| sinf->info->adkm->au_format->IV_length = 16; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| Bool gf_isom_is_adobe_protection_media(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex) | |
| { | |
| GF_TrackBox *trak; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_FALSE; | |
| sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_ADOBE_SCHEME, NULL); | |
| if (!sinf) return GF_FALSE; | |
| /*non-encrypted or non-ADOBE*/ | |
| if (!sinf->info || !sinf->info->adkm) | |
| return GF_FALSE; | |
| return GF_TRUE; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_isom_get_adobe_protection_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex, u32 *outOriginalFormat, u32 *outSchemeType, u32 *outSchemeVersion, const char **outMetadata) | |
| { | |
| GF_TrackBox *trak; | |
| GF_ProtectionSchemeInfoBox *sinf; | |
| trak = gf_isom_get_track_box(the_file, trackNumber); | |
| if (!trak) return GF_BAD_PARAM; | |
| sinf = isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_ADOBE_SCHEME, NULL); | |
| if (!sinf) return GF_BAD_PARAM; | |
| if (outOriginalFormat) { | |
| *outOriginalFormat = sinf->original_format->data_format; | |
| if (IsMP4Description(sinf->original_format->data_format)) *outOriginalFormat = GF_ISOM_SUBTYPE_MPEG4; | |
| } | |
| if (outSchemeType) *outSchemeType = sinf->scheme_type->scheme_type; | |
| if (outSchemeVersion) *outSchemeVersion = sinf->scheme_type->scheme_version; | |
| if (outMetadata) { | |
| *outMetadata = NULL; | |
| if (sinf->info && sinf->info->adkm && sinf->info->adkm->header && sinf->info->adkm->header->std_enc_params && sinf->info->adkm->header->std_enc_params->key_info | |
| && sinf->info->adkm->header->std_enc_params->key_info->params && sinf->info->adkm->header->std_enc_params->key_info->params->metadata) | |
| { | |
| *outMetadata = sinf->info->adkm->header->std_enc_params->key_info->params->metadata; | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| #if 0 //unused | |
| /*! removes the IPMPX tools from files | |
| \param isom_file the target ISO file | |
| */ | |
| void gf_isom_ipmpx_remove_tool_list(GF_ISOFile *the_file) | |
| { | |
| /*remove IPMPToolList if any*/ | |
| if (the_file && the_file->moov && the_file->moov->iods && (the_file ->moov->iods->descriptor->tag == GF_ODF_ISOM_IOD_TAG) ) { | |
| GF_IsomInitialObjectDescriptor *iod = (GF_IsomInitialObjectDescriptor *)the_file ->moov->iods->descriptor; | |
| if (iod->IPMPToolList) gf_odf_desc_del((GF_Descriptor*) iod->IPMPToolList); | |
| iod->IPMPToolList = NULL; | |
| } | |
| } | |
| #endif | |
| GF_EXPORT | |
| Bool gf_cenc_validate_key_info(const u8 *key_info, u32 key_info_size) | |
| { | |
| u32 i, n_keys, kpos, nb_missing = 19; | |
| if (!key_info|| (key_info_size<19)) | |
| goto exit; | |
| n_keys = 1; | |
| if (key_info[0]) { | |
| n_keys = key_info[1]; | |
| n_keys <<= 8; | |
| n_keys |= key_info[2]; | |
| } | |
| kpos=3; | |
| for (i=0;i<n_keys; i++) { | |
| u8 iv_size; | |
| if (kpos + 17 > key_info_size) { | |
| nb_missing = kpos + 17 - key_info_size; | |
| goto exit; | |
| } | |
| iv_size = key_info[kpos]; | |
| kpos += 17; | |
| if (!iv_size) { | |
| if (kpos + 1 > key_info_size) { | |
| nb_missing = kpos + 1 - key_info_size; | |
| goto exit; | |
| } | |
| iv_size = key_info[kpos]; | |
| if (kpos + 1 + iv_size > key_info_size) { | |
| nb_missing = kpos + 1 + iv_size - key_info_size; | |
| goto exit; | |
| } | |
| kpos += 1 + iv_size; | |
| } | |
| } | |
| return GF_TRUE; | |
| exit: | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Invalid key info format, missing %d bytes\n", nb_missing)); | |
| return GF_FALSE; | |
| } | |
| #endif /*GPAC_DISABLE_ISOM*/ |