static GF_Err ISMA_Process(GF_IPMPTool *plug, GF_IPMPEvent *evt) { ISMAEAPriv *priv = (ISMAEAPriv *)plug->udta; switch (evt->event_type) { case GF_IPMP_TOOL_SETUP: if (evt->config_data_code == GF_4CC('i','s','m','a')) return ISMA_Setup(priv, evt); #ifdef OMA_DRM_MP4MC if (evt->config_data_code == GF_4CC('o','d','r','m')) return OMA_DRM_Setup(priv, evt); #endif return GF_NOT_SUPPORTED; case GF_IPMP_TOOL_GRANT_ACCESS: case GF_IPMP_TOOL_RELEASE_ACCESS: if (priv->is_oma) { } else { return ISMA_Access(priv, evt); } break; case GF_IPMP_TOOL_PROCESS_DATA: if (priv->is_oma) { if (evt->is_encrypted) { evt->restart_requested = 1; return GF_EOS; } return GF_OK; } return ISMA_ProcessData(priv, evt); } return GF_OK; }
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_ProtectionInfoBox *sinf; e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); if (e) return e; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !trak->Media || !sampleDescriptionIndex) return GF_BAD_PARAM; sea = NULL; sinf = gf_isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_CENC_SCHEME, &sea); if (!sinf) sinf = gf_isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_CBC_SCHEME, &sea); if (!sinf) sinf = gf_isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_ISMACRYP_SCHEME, &sea); if (!sinf) sinf = gf_isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_OMADRM_SCHEME, &sea); if (!sinf) sinf = gf_isom_get_sinf_entry(trak, sampleDescriptionIndex, GF_ISOM_ADOBE_SCHEME, &sea); if (!sinf) return GF_OK; sea->type = sinf->original_format->data_format; gf_isom_box_array_del(sea->protections); sea->protections = gf_list_new(); if (sea->type == GF_4CC('2','6','4','b')) sea->type = GF_ISOM_BOX_TYPE_AVC1; if (sea->type == GF_4CC('2','6','5','b')) sea->type = GF_ISOM_BOX_TYPE_HVC1; return GF_OK; }
static GF_Err gf_isom_cenc_get_sai_by_saiz_saio(GF_MediaBox *mdia, u32 sampleNumber, u8 IV_size, GF_CENCSampleAuxInfo **sai) { GF_BitStream *bs; u32 prev_sai_size, size, i, j, nb_saio; u64 cur_position, offset; GF_Err e = GF_OK; char *buffer; nb_saio = size = prev_sai_size = 0; offset = 0; 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); if (saio->aux_info_type == GF_4CC('c', 'e', 'n', 'c')) { if (saio->entry_count == 1) offset = saio->version ? saio->offsets_large[0] : saio->offsets[0]; else offset = saio->version ? saio->offsets_large[sampleNumber-1]: saio->offsets[sampleNumber-1]; nb_saio = saio->entry_count; break; } } 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); if (saiz->aux_info_type == GF_4CC('c', 'e', 'n', 'c')) { for (j = 0; j < sampleNumber-1; j++) prev_sai_size += saiz->default_sample_info_size ? saiz->default_sample_info_size : saiz->sample_info_size[j]; size = saiz->default_sample_info_size ? saiz->default_sample_info_size : saiz->sample_info_size[sampleNumber-1]; break; } } offset += (nb_saio == 1) ? prev_sai_size : 0; cur_position = gf_bs_get_position(mdia->information->dataHandler->bs); gf_bs_seek(mdia->information->dataHandler->bs, offset); buffer = (char *)gf_malloc(size); gf_bs_read_data(mdia->information->dataHandler->bs, buffer, size); gf_bs_seek(mdia->information->dataHandler->bs, cur_position); *sai = (GF_CENCSampleAuxInfo *)gf_malloc(sizeof(GF_CENCSampleAuxInfo)); memset(*sai, 0, sizeof(GF_CENCSampleAuxInfo)); bs = gf_bs_new(buffer, size, GF_BITSTREAM_READ); gf_bs_read_data(bs, (char *)(*sai)->IV, IV_size); if (size > IV_size) { (*sai)->subsample_count = gf_bs_read_u16(bs); (*sai)->subsamples = (GF_CENCSubSampleEntry *)gf_malloc(sizeof(GF_CENCSubSampleEntry)*(*sai)->subsample_count); for (i = 0; i < (*sai)->subsample_count; i++) { (*sai)->subsamples[i].bytes_clear_data = gf_bs_read_u16(bs); (*sai)->subsamples[i].bytes_encrypted_data = gf_bs_read_u32(bs); } } gf_bs_del(bs); return e; }
static GF_Err JP2_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) { GF_BitStream *bs; JP2CTX(); if (esd->dependsOnESID || esd->decoderConfig->upstream) return GF_NOT_SUPPORTED; if (!esd->decoderConfig->decoderSpecificInfo) return GF_OK; if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_IMAGE_JPEG_2000) { bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); ctx->height = gf_bs_read_u32(bs); ctx->width = gf_bs_read_u32(bs); ctx->nb_comp = gf_bs_read_u16(bs); ctx->bpp = 1 + gf_bs_read_u8(bs); ctx->out_size = ctx->width * ctx->height * ctx->nb_comp /* * ctx->bpp / 8 */; gf_bs_del(bs); switch (ctx->nb_comp) { case 1: ctx->pixel_format = GF_PIXEL_GREYSCALE; break; case 2: ctx->pixel_format = GF_PIXEL_ALPHAGREY; break; case 3: ctx->pixel_format = GF_PIXEL_RGB_24; break; case 4: ctx->pixel_format = GF_PIXEL_RGBA; break; default: return GF_NOT_SUPPORTED; } } else { bs = gf_bs_new(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); gf_bs_read_u32(bs); ctx->width = gf_bs_read_u16(bs); ctx->height = gf_bs_read_u16(bs); gf_bs_del(bs); bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); gf_bs_write_u32(bs, 12); gf_bs_write_u32(bs, GF_4CC('j','P',' ',' ') ); gf_bs_write_u32(bs, 0x0D0A870A); gf_bs_write_u32(bs, 20); gf_bs_write_u32(bs, GF_4CC('f','t','y','p') ); gf_bs_write_u32(bs, GF_4CC('j','p','2',' ') ); gf_bs_write_u32(bs, 0); gf_bs_write_u32(bs, GF_4CC('j','p','2',' ') ); gf_bs_write_data(bs, esd->decoderConfig->decoderSpecificInfo->data+8, esd->decoderConfig->decoderSpecificInfo->dataLength-8); gf_bs_get_content(bs, &ctx->dsi, &ctx->dsi_size); gf_bs_del(bs); ctx->nb_comp = 3; ctx->out_size = 3*ctx->width*ctx->height/2; ctx->pixel_format = GF_PIXEL_YV12; } return GF_OK; }
Bool gf_isom_cenc_has_saiz_saio_full(GF_SampleTableBox *stbl, void *_traf) { u32 i; GF_List *sai_sizes, *sai_offsets; 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) { sai_sizes = stbl->sai_sizes; sai_offsets = stbl->sai_offsets; } #ifndef GPAC_DISABLE_ISOM_FRAGMENTS else if (_traf) { sai_sizes = traf->sai_sizes; sai_offsets = traf->sai_offsets; } #endif else return GF_FALSE; for (i = 0; i < gf_list_count(sai_sizes); i++) { GF_SampleAuxiliaryInfoSizeBox *saiz = (GF_SampleAuxiliaryInfoSizeBox *)gf_list_get(sai_sizes, i); if (saiz->aux_info_type == GF_4CC('c', 'e', 'n', 'c')) { has_saiz = GF_TRUE; break; } } #ifndef GPAC_DISABLE_ISOM_FRAGMENTS //assume CENC if we find a senc box - hack for some ultraviolet files :( if (!has_saiz && traf && (traf->sample_encryption || traf->piff_sample_encryption) ) has_saiz = GF_TRUE; #endif for (i = 0; i < gf_list_count(sai_offsets); i++) { GF_SampleAuxiliaryInfoOffsetBox *saio = (GF_SampleAuxiliaryInfoOffsetBox *)gf_list_get(sai_offsets, i); if (saio->aux_info_type == GF_4CC('c', 'e', 'n', 'c')) { has_saio = GF_TRUE; break; } } #ifndef GPAC_DISABLE_ISOM_FRAGMENTS //assume CENC if we find a senc box - hack for some ultraviolet files :( if (!has_saio && traf && (traf->sample_encryption || traf->piff_sample_encryption) ) has_saio = GF_TRUE; #endif return (has_saiz && has_saio); }
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_from_file(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); if (saio->aux_info_type != GF_4CC('c', 'e', 'n', 'c')) continue; saio_del((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; }
GF_EXPORT GF_ISMASample *gf_isom_get_ismacryp_sample(GF_ISOFile *the_file, u32 trackNumber, GF_ISOSample *samp, u32 sampleDescriptionIndex) { GF_TrackBox *trak; GF_ISMASampleFormatBox *fmt; GF_ProtectionInfoBox *sinf; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak) return NULL; sinf = gf_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_4CC('o','d','k','m') ) { if (!sinf->info->okms) return NULL; fmt = sinf->info->okms->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 const char *gf_isom_get_payt_info(GF_ISOFile *the_file, u32 trackNumber, u32 index, u32 *payID) { u32 i, count; GF_TrackBox *trak; GF_UserDataMap *map; GF_HintInfoBox *hinf; GF_PAYTBox *payt; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !index) return NULL; if (!CheckHintFormat(trak, GF_4CC('r', 't', 'p', ' '))) return NULL; map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HINF, NULL); if (!map) return NULL; if (gf_list_count(map->boxList) != 1) return NULL; hinf = (GF_HintInfoBox *)gf_list_get(map->boxList, 0); count = 0; i = 0; while ((payt = gf_list_enum(hinf->boxList, &i))) { if (payt->type == GF_ISOM_BOX_TYPE_PAYT) { count++; if (count == index) { if (payID) *payID=payt->payloadCode; return payt->payloadString; } } } return NULL; }
static GF_Err OMA_DRM_Setup(ISMAEAPriv *priv, GF_IPMPEvent *evt) { u32 hdr_pos; GF_OMADRM2Config *cfg = (GF_OMADRM2Config*)evt->config_data; priv->state = ISMAEA_STATE_ERROR; if (cfg->scheme_type != GF_4CC('o','d','k','m')) return GF_NOT_SUPPORTED; if (cfg->scheme_version != 0x00000200) return GF_NOT_SUPPORTED; hdr_pos = 0; while (hdr_pos<cfg->oma_drm_textual_headers_len) { u32 len; char *sep; if (!strncmp(cfg->oma_drm_textual_headers + hdr_pos, "PreviewRange", 12)) { sep = strchr(cfg->oma_drm_textual_headers + hdr_pos, ':'); if (sep) priv->preview_range = atoi(sep+1); } len = (u32) strlen(cfg->oma_drm_textual_headers + hdr_pos); hdr_pos += len+1; } priv->is_oma = GF_TRUE; /*TODO: call DRM agent, fetch keys*/ if (!cfg->kms_uri) return GF_NON_COMPLIANT_BITSTREAM; priv->state = ISMAEA_STATE_SETUP; //priv->nb_allow_play = 1; /*we have preview*/ if (priv->preview_range) return GF_OK; return GF_NOT_SUPPORTED; }
GF_EXPORT u32 gf_isom_get_payt_count(GF_ISOFile *the_file, u32 trackNumber) { u32 i, count; GF_TrackBox *trak; GF_UserDataMap *map; GF_HintInfoBox *hinf; GF_PAYTBox *payt; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak) return 0; if (!CheckHintFormat(trak, GF_4CC('r', 't', 'p', ' '))) return 0; map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_HINF, NULL); if (!map) return 0; if (gf_list_count(map->boxList) != 1) return 0; hinf = (GF_HintInfoBox *)gf_list_get(map->boxList, 0); count = 0; i = 0; while ((payt = gf_list_enum(hinf->boxList, &i))) { if (payt->type == GF_ISOM_BOX_TYPE_PAYT) count++; } return count; }
void gf_isom_cenc_merge_saiz_saio(GF_SampleEncryptionBox *senc, GF_SampleTableBox *stbl, u64 offset, u32 len) { u32 i; if (!senc->cenc_saiz) { senc->cenc_saiz = (GF_SampleAuxiliaryInfoSizeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SAIZ); senc->cenc_saiz->aux_info_type = GF_4CC('c', 'e', 'n', 'c'); senc->cenc_saiz->aux_info_type_parameter = 0; if (stbl) stbl_AddBox(stbl, (GF_Box *)senc->cenc_saiz); } if (!senc->cenc_saio) { senc->cenc_saio = (GF_SampleAuxiliaryInfoOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SAIO); //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_4CC('c', 'e', 'n', 'c'); senc->cenc_saio->aux_info_type_parameter = 0; if (stbl) stbl_AddBox(stbl, (GF_Box *)senc->cenc_saio); } if (!senc->cenc_saiz->sample_count || (senc->cenc_saiz->default_sample_info_size==len)) { senc->cenc_saiz->sample_count ++; senc->cenc_saiz->default_sample_info_size = len; } else { senc->cenc_saiz->sample_info_size = (u8*)gf_realloc(senc->cenc_saiz->sample_info_size, sizeof(u8)*(senc->cenc_saiz->sample_count+1)); 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; } senc->cenc_saiz->sample_info_size[senc->cenc_saiz->sample_count] = len; senc->cenc_saiz->sample_count++; } if (!senc->cenc_saio->entry_count) { senc->cenc_saio->offsets_large = (u64 *)gf_malloc(sizeof(u64)); senc->cenc_saio->offsets_large[0] = offset; senc->cenc_saio->entry_count ++; } else { senc->cenc_saio->offsets_large = (u64*)gf_realloc(senc->cenc_saio->offsets_large, sizeof(u64)*(senc->cenc_saio->entry_count+1)); senc->cenc_saio->offsets_large[senc->cenc_saio->entry_count] = offset; senc->cenc_saio->entry_count++; } }
GF_Err gf_isom_remove_samp_group_box(GF_ISOFile *the_file, u32 trackNumber) { u32 i; GF_SampleTableBox *stbl; GF_TrackBox *trak = gf_isom_get_track_from_file(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_4CC( 's', 'e', 'i', 'g' )) { gf_list_rem(stbl->sampleGroupsDescription, i); sgpd_del((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_4CC( 's', 'e', 'i', 'g' )) { gf_list_rem(stbl->sampleGroups, i); sbgp_del((GF_Box *) a); i--; } } if (!gf_list_count(stbl->sampleGroups)) { gf_list_del(stbl->sampleGroups); stbl->sampleGroups = NULL; } return GF_OK; }
Bool Media_IsSelfContained(GF_MediaBox *mdia, u32 StreamDescIndex) { u32 drefIndex=0; GF_FullBox *a; GF_SampleEntryBox *se = NULL; Media_GetSampleDesc(mdia, StreamDescIndex, &se, &drefIndex); if (!drefIndex) return 0; a = (GF_FullBox*)gf_list_get(mdia->information->dataInformation->dref->other_boxes, drefIndex - 1); if (!a) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] broken file: Data reference index set to %d but no data reference entry found\n", drefIndex)); return 0; } if (a->flags & 1) return 1; /*QT specific*/ if (a->type == GF_4CC('a', 'l', 'i', 's')) return 1; return 0; }
GF_Err gf_isom_remove_pssh_box(GF_ISOFile *the_file) { u32 i; for (i = 0; i < gf_list_count(the_file->moov->other_boxes); i++) { GF_Box *a = (GF_Box *)gf_list_get(the_file->moov->other_boxes, i); if (a->type == GF_4CC('p', 's', 's', 'h')) { gf_list_rem(the_file->moov->other_boxes, i); pssh_del(a); i--; } } if (!gf_list_count(the_file->moov->other_boxes)) { gf_list_del(the_file->moov->other_boxes); the_file->moov->other_boxes = NULL; } return GF_OK; }
GF_Err gf_isom_set_cenc_saio(GF_ISOFile *the_file, u32 trackNumber) { GF_SampleTableBox *stbl; GF_SampleAuxiliaryInfoOffsetBox *saio; GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak) return GF_BAD_PARAM; stbl = trak->Media->information->sampleTable; if (!stbl) return GF_BAD_PARAM; saio = (GF_SampleAuxiliaryInfoOffsetBox *)saio_New(); saio->aux_info_type = GF_4CC('c', 'e', 'n', 'c'); saio->aux_info_type_parameter = 0; if (!stbl->sai_offsets) stbl->sai_offsets = gf_list_new(); gf_list_add(stbl->sai_offsets, saio); return GF_OK; }
static GF_Err XVID_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) { GF_M4VDecSpecInfo dsi; GF_Err e; unsigned char *ptr; unsigned long pitch; XVIDCTX(); if (ctx->ES_ID && ctx->ES_ID!=esd->ESID) return GF_NOT_SUPPORTED; if (!esd->decoderConfig->decoderSpecificInfo || !esd->decoderConfig->decoderSpecificInfo->data) return GF_NON_COMPLIANT_BITSTREAM; /*decode DSI*/ e = gf_m4v_get_config((char *) esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); if (e) return e; if (!dsi.width || !dsi.height) return GF_NON_COMPLIANT_BITSTREAM; GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[XviD] Attaching Stream %d - framesize %d x %d\n", esd->ESID, dsi.width, dsi.height )); ctx->codec = InitCodec(dsi.width, dsi.height, GF_4CC('x', 'v', 'i', 'd')); if (!ctx->codec) return GF_OUT_OF_MEM; GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[XviD] Decoding DecoderSpecificInfo\n")); DecodeFrame(ctx->codec, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, ptr, ptr, ptr, pitch); /*note that this may be irrelevant when used through systems (FPS is driven by systems CTS)*/ ctx->FPS = dsi.clock_rate; ctx->FPS /= 1000; if (!ctx->FPS) ctx->FPS = 30.0f; ctx->width = dsi.width; ctx->height = dsi.height; ctx->pixel_ar = (dsi.par_num<<16) | dsi.par_den; ctx->pixel_ar = 0; ctx->ES_ID = esd->ESID; ctx->first_frame = 1; /*output in YV12 only - let the player handle conversion*/ ctx->out_size = 3 * ctx->width * ctx->height / 2; GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[XviD] Decoder setup - output size %d\n", ctx->out_size )); return GF_OK; }
GF_EXPORT Bool gf_isom_is_omadrm_media(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex) { GF_TrackBox *trak; GF_SampleEntryBox *sea; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak) return 0; Media_GetSampleDesc(trak->Media, sampleDescriptionIndex, &sea, NULL); /*non-encrypted or non-ISMA*/ if (!sea || !sea->protection_info || !sea->protection_info->scheme_type || (sea->protection_info->scheme_type->scheme_type != GF_4CC('o','d','k','m') ) || !sea->protection_info->info || !sea->protection_info->info->okms || !sea->protection_info->info->okms->hdr ) return 0; return 1; }
GF_Err gf_isom_remove_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex) { GF_TrackBox *trak; GF_Err e; GF_SampleEntryBox *sea; e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); if (e) return e; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak || !trak->Media || !StreamDescriptionIndex) return GF_BAD_PARAM; Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &sea, NULL); /*non-encrypted or non-ISMA*/ if (!sea || !sea->protection_info) return GF_BAD_PARAM; if (!sea->protection_info->scheme_type || !sea->protection_info->original_format) return GF_NON_COMPLIANT_BITSTREAM; sea->type = sea->protection_info->original_format->data_format; gf_isom_box_del((GF_Box *)sea->protection_info); sea->protection_info = NULL; if (sea->type == GF_4CC('2','6','4','b')) sea->type = GF_ISOM_BOX_TYPE_AVC1; return GF_OK; }
GF_EXPORT GF_ISMASample *gf_isom_get_ismacryp_sample(GF_ISOFile *the_file, u32 trackNumber, GF_ISOSample *samp, u32 sampleDescriptionIndex) { GF_TrackBox *trak; GF_ISMASampleFormatBox *fmt; GF_SampleEntryBox *sea; trak = gf_isom_get_track_from_file(the_file, trackNumber); if (!trak) return NULL; Media_GetSampleDesc(trak->Media, sampleDescriptionIndex, &sea, NULL); /*non-encrypted or non-ISMA*/ if (!sea || !sea->protection_info || !sea->protection_info->scheme_type || !sea->protection_info->info ) { return NULL; } /*ISMA*/ if (sea->protection_info->scheme_type->scheme_type == GF_ISOM_ISMACRYP_SCHEME) { fmt = sea->protection_info->info->isfm; if (!fmt) return NULL; return gf_isom_ismacryp_sample_from_data(samp->data, samp->dataLength, sea->protection_info->info->isfm->selective_encryption, sea->protection_info->info->isfm->key_indicator_length, sea->protection_info->info->isfm->IV_length); } /*OMA*/ else if (sea->protection_info->scheme_type->scheme_type == GF_4CC('o','d','k','m') ) { if (!sea->protection_info->info->okms) return NULL; fmt = sea->protection_info->info->okms->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, 0, 0, 128); } return NULL; }
static GF_Err WriteInterleaved(MovieWriter *mw, GF_BitStream *bs, Bool drift_inter) { GF_Err e; u32 i; GF_Box *a; u64 firstSize, finalSize, offset, finalOffset; GF_List *writers = gf_list_new(); GF_ISOFile *movie = mw->movie; //first setup the writers e = SetupWriters(mw, writers, 1); if (e) goto exit; if (movie->is_jp2) { gf_bs_write_u32(bs, 12); gf_bs_write_u32(bs, GF_4CC('j','P',' ',' ')); gf_bs_write_u32(bs, 0x0D0A870A); } if (movie->brand) { e = gf_isom_box_size((GF_Box *)movie->brand); if (e) goto exit; e = gf_isom_box_write((GF_Box *)movie->brand, bs); if (e) goto exit; } if (movie->pdin) { e = gf_isom_box_size((GF_Box *)movie->pdin); if (e) goto exit; e = gf_isom_box_write((GF_Box *)movie->pdin, bs); if (e) goto exit; } e = DoInterleave(mw, writers, bs, 1, gf_bs_get_position(bs), drift_inter); if (e) goto exit; firstSize = GetMoovAndMetaSize(movie, writers); offset = firstSize; if (movie->mdat && movie->mdat->dataSize) offset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); e = ShiftOffset(movie, writers, offset); if (e) goto exit; //get the size and see if it has changed (eg, we moved to 64 bit offsets) finalSize = GetMoovAndMetaSize(movie, writers); if (firstSize != finalSize) { //we need to remove our offsets ResetWriters(writers); finalOffset = finalSize; if (movie->mdat->dataSize) finalOffset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); //OK, now we're sure about the final size -> shift the offsets //we don't need to re-emulate, as the only thing that changed is the offset //so just shift the offset e = ShiftOffset(movie, writers, finalOffset - offset); if (e) goto exit; firstSize = GetMoovAndMetaSize(movie, writers); } //now write our stuff e = WriteMoovAndMeta(movie, writers, bs); if (e) goto exit; /*we have 8 extra bytes for large size (not computed in gf_isom_box_size) */ if (movie->mdat && movie->mdat->dataSize) { if (movie->mdat->dataSize > 0xFFFFFFFF) movie->mdat->dataSize += 8; e = gf_isom_box_size((GF_Box *)movie->mdat); if (e) goto exit; e = gf_isom_box_write((GF_Box *)movie->mdat, bs); if (e) goto exit; } //we don't need the offset as we are writing... ResetWriters(writers); e = DoInterleave(mw, writers, bs, 0, 0, drift_inter); if (e) goto exit; //then the rest i=0; while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) { switch (a->type) { case GF_ISOM_BOX_TYPE_MOOV: case GF_ISOM_BOX_TYPE_META: case GF_ISOM_BOX_TYPE_FTYP: case GF_ISOM_BOX_TYPE_PDIN: case GF_ISOM_BOX_TYPE_MDAT: break; default: e = gf_isom_box_size(a); if (e) goto exit; e = gf_isom_box_write(a, bs); if (e) goto exit; } } exit: CleanWriters(writers); gf_list_del(writers); return e; }
void gf_es_config_drm(GF_Channel *ch, GF_NetComDRMConfig *drm_cfg) { GF_Terminal *term = ch->odm->term; u32 i, count; GF_Err e; GF_IPMPEvent evt; GF_OMADRM2Config cfg; GF_OMADRM2Config isma_cfg; /*always buffer when fetching keys*/ ch_buffer_on(ch); ch->is_protected = 1; memset(&evt, 0, sizeof(GF_IPMPEvent)); evt.event_type = GF_IPMP_TOOL_SETUP; evt.channel = ch; /*push all cfg data*/ if (drm_cfg->contentID) { evt.config_data_code = GF_4CC('o','d','r','m'); memset(&cfg, 0, sizeof(cfg)); cfg.scheme_version = drm_cfg->scheme_version; cfg.scheme_type = drm_cfg->scheme_type; cfg.scheme_uri = drm_cfg->scheme_uri; cfg.kms_uri = drm_cfg->kms_uri; memcpy(cfg.hash, drm_cfg->hash, sizeof(char)*20); cfg.contentID = drm_cfg->contentID; cfg.oma_drm_crypt_type = drm_cfg->oma_drm_crypt_type; cfg.oma_drm_use_pad = drm_cfg->oma_drm_use_pad; cfg.oma_drm_use_hdr = drm_cfg->oma_drm_use_hdr; cfg.oma_drm_textual_headers = drm_cfg->oma_drm_textual_headers; cfg.oma_drm_textual_headers_len = drm_cfg->oma_drm_textual_headers_len; evt.config_data = &cfg; } else { evt.config_data_code = GF_4CC('i','s','m','a'); memset(&isma_cfg, 0, sizeof(isma_cfg)); isma_cfg.scheme_version = drm_cfg->scheme_version; isma_cfg.scheme_type = drm_cfg->scheme_type; isma_cfg.scheme_uri = drm_cfg->scheme_uri; isma_cfg.kms_uri = drm_cfg->kms_uri; evt.config_data = &isma_cfg; } if (ch->ipmp_tool) { e = ch->ipmp_tool->process(ch->ipmp_tool, &evt); if (e) gf_term_message(ch->odm->term, ch->service->url, "Error setting up DRM tool", e); ch_buffer_off(ch); return; } /*browse all available tools*/ count = gf_modules_get_count(term->user->modules); for (i=0; i< count; i++) { ch->ipmp_tool = (GF_IPMPTool *) gf_modules_load_interface(term->user->modules, i, GF_IPMP_TOOL_INTERFACE); if (!ch->ipmp_tool) continue; GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[IPMP] Checking if IPMP tool %s can handle channel protection scheme\n", ch->ipmp_tool->module_name)); e = ch->ipmp_tool->process(ch->ipmp_tool, &evt); if (e==GF_OK) { GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[IPMP] Associating IPMP tool %s to channel %d\n", ch->ipmp_tool->module_name, ch->esd->ESID)); ch_buffer_off(ch); return; } gf_modules_close_interface((GF_BaseInterface *) ch->ipmp_tool); ch->ipmp_tool = NULL; } GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[IPMP] No IPMP tool suitable to handle channel protection scheme %s (KMS URI %s)\n", drm_cfg->scheme_uri, drm_cfg->kms_uri)); ch_buffer_off(ch); }
static GF_Err CENC_Setup(ISMAEAPriv *priv, GF_IPMPEvent *evt) { GF_CENCConfig *cfg = (GF_CENCConfig*)evt->config_data; u32 i; priv->state = ISMAEA_STATE_ERROR; if ((cfg->scheme_type != GF_4CC('c', 'e', 'n', 'c')) && (cfg->scheme_type != GF_4CC('c','b','c','1'))) return GF_NOT_SUPPORTED; if (cfg->scheme_version != 0x00010000) return GF_NOT_SUPPORTED; for (i = 0; i < cfg->PSSH_count; i++) { GF_NetComDRMConfigPSSH *pssh = &cfg->PSSHs[i]; char szSystemID[33]; u32 j; memset(szSystemID, 0, 33); for (j=0; j<16; j++) { sprintf(szSystemID+j*2, "%02X", (unsigned char) pssh->SystemID[j]); } /*SystemID for GPAC Player: 67706163-6365-6E63-6472-6D746F6F6C31*/ if (strcmp(szSystemID, "6770616363656E6364726D746F6F6C31")) { GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[CENC/ISMA] System ID %s not supported\n", szSystemID)); continue; } else { u8 cypherOffset; bin128 cypherKey, cypherIV; GF_Crypt *mc; /*GPAC DRM TEST system info, used to validate cypher offset in CENC packager keyIDs as usual (before private data) URL len on 8 bits URL keys, cyphered with oyur magic key :) */ cypherOffset = pssh->private_data[0] + 1; gf_bin128_parse("0x6770616363656E6364726D746F6F6C31", cypherKey); gf_bin128_parse("0x00000000000000000000000000000001", cypherIV); mc = gf_crypt_open("AES-128", "CTR"); if (!mc) { GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[CENC/ISMA] Cannot open AES-128 CTR\n")); return GF_IO_ERR; } gf_crypt_init(mc, cypherKey, 16, cypherIV); gf_crypt_decrypt(mc, pssh->private_data+cypherOffset, pssh->private_data_size-cypherOffset); gf_crypt_close(mc); /*now we search a key*/ priv->KID_count = pssh->KID_count; if (priv->KIDs) { gf_free(priv->KIDs); priv->KIDs = NULL; } priv->KIDs = (bin128 *)gf_malloc(pssh->KID_count*sizeof(bin128)); if (priv->keys) { gf_free(priv->keys); priv->keys = NULL; } priv->keys = (bin128 *)gf_malloc(pssh->KID_count*sizeof(bin128)); memmove(priv->KIDs, pssh->KIDs, pssh->KID_count*sizeof(bin128)); memmove(priv->keys, pssh->private_data + cypherOffset, pssh->KID_count*sizeof(bin128)); } } if (cfg->scheme_type == GF_4CC('c', 'e', 'n', 'c')) priv->is_cenc = GF_TRUE; else priv->is_cbc = GF_TRUE; priv->state = ISMAEA_STATE_SETUP; //priv->nb_allow_play = 1; return GF_OK; }
static GF_Err ISMA_Setup(ISMAEAPriv *priv, GF_IPMPEvent *evt) { GF_Err e; GF_ISMACrypConfig *cfg = (GF_ISMACrypConfig*)evt->config_data; priv->state = ISMAEA_STATE_ERROR; if (cfg->scheme_type != GF_4CC('i','A','E','C')) return GF_NOT_SUPPORTED; if (cfg->scheme_version != 1) return GF_NOT_SUPPORTED; if (!cfg->kms_uri) return GF_NON_COMPLIANT_BITSTREAM; /*try to fetch the keys*/ /*base64 inband encoding*/ if (!strnicmp(cfg->kms_uri, "(key)", 5)) { char data[100]; gf_base64_decode((char*)cfg->kms_uri+5, (u32)strlen(cfg->kms_uri)-5, data, 100); memcpy(priv->key, data, sizeof(char)*16); memcpy(priv->salt, data+16, sizeof(char)*8); } /*hexadecimal inband encoding*/ else if (!strnicmp(cfg->kms_uri, "(key-hexa)", 10)) { u32 v; char szT[3], *k; u32 i; szT[2] = 0; if (strlen(cfg->kms_uri) < 10+32+16) return GF_NON_COMPLIANT_BITSTREAM; k = (char *)cfg->kms_uri + 10; for (i=0; i<16; i++) { szT[0] = k[2*i]; szT[1] = k[2*i + 1]; sscanf(szT, "%X", &v); priv->key[i] = v; } k = (char *)cfg->kms_uri + 10 + 32; for (i=0; i<8; i++) { szT[0] = k[2*i]; szT[1] = k[2*i + 1]; sscanf(szT, "%X", &v); priv->salt[i] = v; } } /*MPEG4-IP KMS*/ else if (!stricmp(cfg->kms_uri, "AudioKey") || !stricmp(cfg->kms_uri, "VideoKey")) { if (!gf_ismacryp_mpeg4ip_get_info((char *) cfg->kms_uri, priv->key, priv->salt)) { return GF_BAD_PARAM; } } /*gpac default scheme is used, fetch file from KMS and load keys*/ else if (cfg->scheme_uri && !stricmp(cfg->scheme_uri, "urn:gpac:isma:encryption_scheme")) { e = ISMA_GetGPAC_KMS(priv, evt->channel, cfg->kms_uri); if (e) return e; } /*hardcoded keys*/ else { static u8 mysalt[] = { 8,7,6,5,4,3,2,1, 0,0,0,0,0,0,0,0 }; static u8 mykey[][16] = { { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 } }; memcpy(priv->salt, mysalt, sizeof(char)*8); memcpy(priv->key, mykey, sizeof(char)*16); } priv->state = ISMAEA_STATE_SETUP; //priv->nb_allow_play = 1; return GF_OK; }
/*import cubic QTVR to mp4*/ GF_Err gf_sm_load_init_qt(GF_SceneLoader *load) { u32 i, di, w, h, tk, nb_samp; Bool has_qtvr; GF_ISOSample *samp; GF_ISOFile *src; GF_StreamContext *st; GF_AUContext *au; GF_Command *com; M_Background *back; M_NavigationInfo *ni; M_Group *gr; GF_ODUpdate *odU; GF_SceneGraph *sg; GF_ObjectDescriptor *od; GF_ESD *esd; if (!load->ctx) return GF_NOT_SUPPORTED; src = gf_isom_open(load->fileName, GF_ISOM_OPEN_READ, NULL); if (!src) return gf_qt_report(load, GF_URL_ERROR, "Opening file %s failed", load->fileName); w = h = tk = 0; nb_samp = 0; has_qtvr = 0; for (i=0; i<gf_isom_get_track_count(src); i++) { switch (gf_isom_get_media_type(src, i+1)) { case GF_ISOM_MEDIA_VISUAL: if (gf_isom_get_media_subtype(src, i+1, 1) == GF_4CC('j', 'p', 'e', 'g')) { GF_GenericSampleDescription *udesc = gf_isom_get_generic_sample_description(src, i+1, 1); if ((udesc->width>w) || (udesc->height>h)) { w = udesc->width; h = udesc->height; tk = i+1; nb_samp = gf_isom_get_sample_count(src, i+1); } if (udesc->extension_buf) gf_free(udesc->extension_buf); gf_free(udesc); } break; case GF_4CC('q','t','v','r'): has_qtvr = 1; break; } } if (!has_qtvr) { gf_isom_delete(src); return gf_qt_report(load, GF_NOT_SUPPORTED, "QTVR not found - no conversion available for this QuickTime movie"); } if (!tk) { gf_isom_delete(src); return gf_qt_report(load, GF_NON_COMPLIANT_BITSTREAM, "No associated visual track with QTVR movie"); } if (nb_samp!=6) { gf_isom_delete(src); return gf_qt_report(load, GF_NOT_SUPPORTED, "Movie %s doesn't look a Cubic QTVR - sorry...", load->fileName); } GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ("QT: Importing Cubic QTVR Movie")); /*create scene*/ sg = load->ctx->scene_graph; gr = (M_Group *) gf_node_new(sg, TAG_MPEG4_Group); gf_node_register((GF_Node *)gr, NULL); st = gf_sm_stream_new(load->ctx, 1, GF_STREAM_SCENE, 1); au = gf_sm_stream_au_new(st, 0, 0, 1); com = gf_sg_command_new(load->ctx->scene_graph, GF_SG_SCENE_REPLACE); gf_list_add(au->commands, com); com->node = (GF_Node *)gr; back = (M_Background *) gf_node_new(sg, TAG_MPEG4_Background); gf_node_list_add_child( &gr->children, (GF_Node*)back); gf_node_register((GF_Node *)back, (GF_Node *)gr); gf_sg_vrml_mf_alloc(&back->leftUrl, GF_SG_VRML_MFURL, 1); back->leftUrl.vals[0].OD_ID = 2; gf_sg_vrml_mf_alloc(&back->frontUrl, GF_SG_VRML_MFURL, 1); back->frontUrl.vals[0].OD_ID = 3; gf_sg_vrml_mf_alloc(&back->rightUrl, GF_SG_VRML_MFURL, 1); back->rightUrl.vals[0].OD_ID = 4; gf_sg_vrml_mf_alloc(&back->backUrl, GF_SG_VRML_MFURL, 1); back->backUrl.vals[0].OD_ID = 5; gf_sg_vrml_mf_alloc(&back->topUrl, GF_SG_VRML_MFURL, 1); back->topUrl.vals[0].OD_ID = 6; gf_sg_vrml_mf_alloc(&back->bottomUrl, GF_SG_VRML_MFURL, 1); back->bottomUrl.vals[0].OD_ID = 7; ni = (M_NavigationInfo *) gf_node_new(sg, TAG_MPEG4_NavigationInfo); gf_node_list_add_child(&gr->children, (GF_Node*)ni); gf_node_register((GF_Node *)ni, (GF_Node *)gr); gf_sg_vrml_mf_reset(&ni->type, GF_SG_VRML_MFSTRING); gf_sg_vrml_mf_alloc(&ni->type, GF_SG_VRML_MFSTRING, 1); ni->type.vals[0] = gf_strdup("QTVR"); /*create ODs*/ st = gf_sm_stream_new(load->ctx, 2, GF_STREAM_OD, 1); au = gf_sm_stream_au_new(st, 0, 0, 1); odU = (GF_ODUpdate*) gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); gf_list_add(au->commands, odU); for (i=0; i<6; i++) { GF_MuxInfo *mi; FILE *img; char szName[1024]; od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); od->objectDescriptorID = 2+i; esd = gf_odf_desc_esd_new(2); esd->decoderConfig->streamType = GF_STREAM_VISUAL; esd->decoderConfig->objectTypeIndication = GPAC_OTI_IMAGE_JPEG; esd->ESID = 3+i; /*extract image and remember it*/ mi = (GF_MuxInfo *) gf_odf_desc_new(GF_ODF_MUXINFO_TAG); gf_list_add(esd->extensionDescriptors, mi); mi->delete_file = 1; sprintf(szName, "%s_img%d.jpg", load->fileName, esd->ESID); mi->file_name = gf_strdup(szName); gf_list_add(od->ESDescriptors, esd); gf_list_add(odU->objectDescriptors, od); samp = gf_isom_get_sample(src, tk, i+1, &di); img = gf_f64_open(mi->file_name, "wb"); fwrite(samp->data, samp->dataLength, 1, img); fclose(img); gf_isom_sample_del(&samp); } gf_isom_delete(src); return GF_OK; }
GF_Err gf_isom_add_meta_item_extended(GF_ISOFile *file, Bool root_meta, u32 track_num, Bool self_reference, char *resource_path, const char *item_name, u32 item_id, u32 item_type, const char *mime_type, const char *content_encoding, GF_ImageItemProperties *image_props, const char *URL, const char *URN, char *data, u32 data_len, GF_List *item_extent_refs) { u32 i; GF_Err e; GF_ItemLocationEntry *location_entry; GF_ItemInfoEntryBox *infe; GF_MetaBox *meta; u32 lastItemID = 0; if (!self_reference && !resource_path && !data) return GF_BAD_PARAM; e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE); if (e) return e; meta = gf_isom_get_meta(file, root_meta, track_num); if (!meta) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Trying to add item, but missing meta box")); return GF_BAD_PARAM; } e = FlushCaptureMode(file); if (e) return e; /*check file exists */ if (!URN && !URL && !self_reference && !data) { FILE *src = gf_fopen(resource_path, "rb"); if (!src) return GF_URL_ERROR; gf_fclose(src); } if (meta->item_infos) { u32 item_count = gf_list_count(meta->item_infos->item_infos); for (i = 0; i < item_count; i++) { GF_ItemInfoEntryBox *e = (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, i); if (e->item_ID > lastItemID) lastItemID = e->item_ID; if (item_id == e->item_ID) { GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[IsoMedia] Item with id %d already exists, ignoring id\n", item_id)); item_id = 0; } } } infe = (GF_ItemInfoEntryBox *)infe_New(); if (item_id) { infe->item_ID = item_id; } else { infe->item_ID = ++lastItemID; } /*get relative name*/ if (item_name) { infe->item_name = gf_strdup(item_name); } else if (resource_path) { if (strrchr(resource_path, GF_PATH_SEPARATOR)) { infe->item_name = gf_strdup(strrchr(resource_path, GF_PATH_SEPARATOR) + 1); } else { infe->item_name = gf_strdup(resource_path); } } infe->item_type = item_type; if (mime_type) { infe->content_type = gf_strdup(mime_type); } else { infe->content_type = gf_strdup("application/octet-stream"); } if (content_encoding) infe->content_encoding = gf_strdup(content_encoding); /*Creation of the ItemLocation */ location_entry = (GF_ItemLocationEntry*)gf_malloc(sizeof(GF_ItemLocationEntry)); if (!location_entry) { gf_isom_box_del((GF_Box *)infe); return GF_OUT_OF_MEM; } memset(location_entry, 0, sizeof(GF_ItemLocationEntry)); location_entry->extent_entries = gf_list_new(); /*Creates an mdat if it does not exist*/ if (!file->mdat) { file->mdat = (GF_MediaDataBox *)mdat_New(); gf_list_add(file->TopBoxes, file->mdat); } /*Creation an ItemLocation Box if it does not exist*/ if (!meta->item_locations) meta->item_locations = (GF_ItemLocationBox *)iloc_New(); gf_list_add(meta->item_locations->location_entries, location_entry); location_entry->item_ID = infe->item_ID; if (!meta->item_infos) meta->item_infos = (GF_ItemInfoBox *)iinf_New(); e = gf_list_add(meta->item_infos->item_infos, infe); if (e) return e; if (image_props) { if (image_props->hidden) { infe->flags = 0x1; } } /*0: the current file*/ location_entry->data_reference_index = 0; if (self_reference) { GF_ItemExtentEntry *entry; GF_SAFEALLOC(entry, GF_ItemExtentEntry); gf_list_add(location_entry->extent_entries, entry); if (!infe->item_name) infe->item_name = gf_strdup(""); return GF_OK; } /*file not copied, just referenced*/ if (URL || URN) { u32 dataRefIndex; if (!meta->file_locations) meta->file_locations = (GF_DataInformationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_DINF); if (!meta->file_locations->dref) meta->file_locations->dref = (GF_DataReferenceBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_DREF); e = Media_FindDataRef(meta->file_locations->dref, (char *)URL, (char *)URN, &dataRefIndex); if (e) return e; if (!dataRefIndex) { e = Media_CreateDataRef(meta->file_locations->dref, (char *)URL, (char *)URN, &dataRefIndex); if (e) return e; } location_entry->data_reference_index = dataRefIndex; } if (item_extent_refs && gf_list_count(item_extent_refs)) { u32 refs_count; location_entry->construction_method = 2; meta->item_locations->index_size = 4; refs_count = gf_list_count(item_extent_refs); for (i = 0; i < refs_count; i++) { u32 *item_index; GF_ItemExtentEntry *entry; GF_SAFEALLOC(entry, GF_ItemExtentEntry); gf_list_add(location_entry->extent_entries, entry); item_index = (u32 *)gf_list_get(item_extent_refs, i); gf_isom_meta_add_item_ref(file, root_meta, track_num, infe->item_ID, *item_index, GF_4CC('i', 'l', 'o', 'c'), &(entry->extent_index)); } } else { /*capture mode, write to disk*/ if ((file->openMode == GF_ISOM_OPEN_WRITE) && !location_entry->data_reference_index) { FILE *src; GF_ItemExtentEntry *entry; GF_SAFEALLOC(entry, GF_ItemExtentEntry); location_entry->base_offset = gf_bs_get_position(file->editFileMap->bs); /*update base offset size*/ if (location_entry->base_offset > 0xFFFFFFFF) meta->item_locations->base_offset_size = 8; else if (location_entry->base_offset && !meta->item_locations->base_offset_size) meta->item_locations->base_offset_size = 4; entry->extent_length = 0; entry->extent_offset = 0; gf_list_add(location_entry->extent_entries, entry); if (data) { gf_bs_write_data(file->editFileMap->bs, data, data_len); /*update length size*/ if (entry->extent_length > 0xFFFFFFFF) meta->item_locations->length_size = 8; else if (entry->extent_length && !meta->item_locations->length_size) meta->item_locations->length_size = 4; } else if (resource_path) { src = gf_fopen(resource_path, "rb"); if (src) { char cache_data[4096]; u64 remain; gf_fseek(src, 0, SEEK_END); entry->extent_length = gf_ftell(src); gf_fseek(src, 0, SEEK_SET); remain = entry->extent_length; while (remain) { u32 size_cache = (remain > 4096) ? 4096 : (u32)remain; size_t read = fread(cache_data, 1, size_cache, src); if (read == (size_t)-1) break; gf_bs_write_data(file->editFileMap->bs, cache_data, (u32)read); remain -= (u32)read; } gf_fclose(src); /*update length size*/ if (entry->extent_length > 0xFFFFFFFF) meta->item_locations->length_size = 8; else if (entry->extent_length && !meta->item_locations->length_size) meta->item_locations->length_size = 4; } } } /*store full path for info*/ else if (!location_entry->data_reference_index) { if (data) { infe->full_path = (char *)gf_malloc(sizeof(char) * data_len); memcpy(infe->full_path, data, sizeof(char) * data_len); infe->data_len = data_len; } else { infe->full_path = gf_strdup(resource_path); infe->data_len = 0; } } } return GF_OK; }
//extraction of the ESD from the track GF_Err GetESD(GF_MovieBox *moov, u32 trackID, u32 StreamDescIndex, GF_ESD **outESD) { GF_Err e; GF_ESD *esd; u32 track_num = 0; GF_SampleTableBox *stbl; GF_TrackBox *trak, *OCRTrack; GF_TrackReferenceTypeBox *dpnd; GF_SLConfig *slc; GF_MPEGSampleEntryBox *entry; track_num = gf_isom_get_tracknum_from_id(moov, trackID); dpnd = NULL; *outESD = NULL; trak = gf_isom_get_track(moov, track_num); if (!trak) return GF_ISOM_INVALID_FILE; e = Media_GetESD(trak->Media, StreamDescIndex, &esd, 0); if (e) return e; e = Media_GetSampleDesc(trak->Media, StreamDescIndex, (GF_SampleEntryBox **) &entry, NULL); if (e) return e; //set the ID esd->ESID = trackID; //find stream dependencies e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_DPND , &dpnd); if (e) return e; if (dpnd) { //ONLY ONE STREAM DEPENDENCY IS ALLOWED if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; //fix the spec: where is the index located ?? esd->dependsOnESID = dpnd->trackIDs[0]; } else { esd->dependsOnESID = 0; } if (trak->udta) { GF_UserDataMap *map; u32 i = 0; while ((map = (GF_UserDataMap*)gf_list_enum(trak->udta->recordList, &i))) { if (map->boxType == GF_4CC('A','U','X','V')) { GF_Descriptor *d = gf_odf_desc_new(GF_ODF_AUX_VIDEO_DATA); gf_list_add(esd->extensionDescriptors, d); break; } } } //OK, get the OCR (in a REAL MP4File, OCR is 0 in ESD and is specified through track reference dpnd = NULL; OCRTrack = NULL; //find OCR dependencies e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_SYNC, &dpnd); if (e) return e; if (dpnd) { if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; esd->OCRESID = dpnd->trackIDs[0]; OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]); while (OCRTrack) { /*if we have a dependency on a track that doesn't have OCR dep, remove that dependency*/ e = Track_FindRef(OCRTrack, GF_ISOM_BOX_TYPE_SYNC, &dpnd); if (e || !dpnd || !dpnd->trackIDCount) { OCRTrack = NULL; goto default_sync; } /*this is explicit desync*/ if (dpnd && ((dpnd->trackIDs[0]==0) || (dpnd->trackIDs[0]==OCRTrack->Header->trackID))) break; /*loop in OCRs, break it*/ if (esd->ESID == OCRTrack->Header->trackID) { OCRTrack = NULL; goto default_sync; } /*check next*/ OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]); } if (!OCRTrack) goto default_sync; } else { default_sync: /*all tracks are sync'ed by default*/ if (trak->moov->mov->es_id_default_sync<0) { if (esd->OCRESID) trak->moov->mov->es_id_default_sync = esd->OCRESID; else trak->moov->mov->es_id_default_sync = esd->ESID; } if (trak->moov->mov->es_id_default_sync) esd->OCRESID = (u16) trak->moov->mov->es_id_default_sync; /*cf ESD writer*/ if (esd->OCRESID == esd->ESID) esd->OCRESID = 0; } //update the IPI stuff if needed if (esd->ipiPtr != NULL) { dpnd = NULL; e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_IPIR, &dpnd); if (e) return e; if (dpnd) { if (esd->ipiPtr->tag != GF_ODF_ISOM_IPI_PTR_TAG) return GF_ISOM_INVALID_FILE; //OK, retrieve the ID: the IPI_ES_Id is currently the ref track esd->ipiPtr->IPI_ES_Id = dpnd->trackIDs[esd->ipiPtr->IPI_ES_Id - 1]; //and change the tag esd->ipiPtr->tag = GF_ODF_IPI_PTR_TAG; } else { return GF_ISOM_INVALID_FILE; } } if ((trak->Media->mediaHeader->packedLanguage[0] != 'u') || (trak->Media->mediaHeader->packedLanguage[1] != 'n') || (trak->Media->mediaHeader->packedLanguage[2] != 'd') ) { if (!esd->langDesc) esd->langDesc = (GF_Language *) gf_odf_desc_new(GF_ODF_LANG_TAG); esd->langDesc->langCode = trak->Media->mediaHeader->packedLanguage[0]; esd->langDesc->langCode <<= 8; esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[1]; esd->langDesc->langCode <<= 8; esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[2]; } { u16 rvc_predefined; char *rvc_cfg_data; const char *mime_type; u32 rvc_cfg_size; e = gf_isom_get_rvc_config(moov->mov, track_num, 1, &rvc_predefined, &rvc_cfg_data, &rvc_cfg_size, &mime_type); if (e==GF_OK) { if (rvc_predefined) { esd->decoderConfig->predefined_rvc_config = rvc_predefined; } else { esd->decoderConfig->rvc_config = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); if (mime_type && !strcmp(mime_type, "application/rvc-config+xml+gz") ) { #if !defined(GPAC_DISABLE_CORE_TOOLS) && !defined(GPAC_DISABLE_ZLIB) gf_gz_decompress_payload(rvc_cfg_data, rvc_cfg_size, &esd->decoderConfig->rvc_config->data, &esd->decoderConfig->rvc_config->dataLength); gf_free(rvc_cfg_data); #endif } else { esd->decoderConfig->rvc_config->data = rvc_cfg_data; esd->decoderConfig->rvc_config->dataLength = rvc_cfg_size; } } } } /*normally all files shall be stored with predefined=SLPredef_MP4, but of course some are broken (philips) so we just check the ESD_URL. If set, use the given cfg, otherwise always rewrite it*/ if (esd->URLString != NULL) { *outESD = esd; return GF_OK; } //if we are in publishing mode and we have an SLConfig specified, use it as is switch (entry->type) { case GF_ISOM_BOX_TYPE_MP4V: slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc; break; case GF_ISOM_BOX_TYPE_MP4A: slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc; break; case GF_ISOM_BOX_TYPE_MP4S: slc = entry->slc; break; default: slc = NULL; break; } if (slc) { gf_odf_desc_del((GF_Descriptor *)esd->slConfig); gf_odf_desc_copy((GF_Descriptor *)slc, (GF_Descriptor **)&esd->slConfig); *outESD = esd; return GF_OK; } //otherwise use the regular mapping //this is a desc for a media in the file, let's rewrite some param esd->slConfig->timestampLength = 32; esd->slConfig->timestampResolution = trak->Media->mediaHeader->timeScale; //NO OCR from MP4File streams (eg, constant OC Res one) esd->slConfig->OCRLength = 0; esd->slConfig->OCRResolution = 0; // if (OCRTrack) esd->slConfig->OCRResolution = OCRTrack->Media->mediaHeader->timeScale; stbl = trak->Media->information->sampleTable; // a little optimization here: if all our samples are sync, //set the RAPOnly to true... for external users... if (! stbl->SyncSample) { esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1; esd->slConfig->useRandomAccessPointFlag = 0; } else { esd->slConfig->hasRandomAccessUnitsOnlyFlag = 0; //signal we are NOT using sync points if no info is present in the table esd->slConfig->useRandomAccessPointFlag = stbl->SyncSample->nb_entries ? 1 : 0; } //do we have DegradationPriority ? if (stbl->DegradationPriority) { esd->slConfig->degradationPriorityLength = 15; } else { esd->slConfig->degradationPriorityLength = 0; } //paddingBits if (stbl->PaddingBits) { esd->slConfig->usePaddingFlag = 1; } //change to support reflecting OD streams esd->slConfig->useAccessUnitEndFlag = 1; esd->slConfig->useAccessUnitStartFlag = 1; //signal we do have padding flag (since we only use logical SL packet //the user can decide whether to use the info or not esd->slConfig->usePaddingFlag = stbl->PaddingBits ? 1 : 0; //same with degradation priority esd->slConfig->degradationPriorityLength = stbl->DegradationPriority ? 32 : 0; //this new SL will be OUT OF THE FILE. Let's set its predefined to 0 esd->slConfig->predefined = 0; *outESD = esd; return GF_OK; }
GF_EXPORT GF_RTPHinter *gf_hinter_track_new(GF_ISOFile *file, u32 TrackNum, u32 Path_MTU, u32 max_ptime, u32 default_rtp_rate, u32 flags, u8 PayloadID, Bool copy_media, u32 InterleaveGroupID, u8 InterleaveGroupPriority, GF_Err *e) { GF_SLConfig my_sl; u32 descIndex, MinSize, MaxSize, avgTS, streamType, oti, const_dur, nb_ch, maxDTSDelta; u8 OfficialPayloadID; u32 TrackMediaSubType, TrackMediaType, hintType, nbEdts, required_rate, force_dts_delta, avc_nalu_size, PL_ID, bandwidth, IV_length, KI_length; const char *url, *urn; char *mpeg4mode; Bool is_crypted, has_mpeg4_mapping; GF_RTPHinter *tmp; GF_ESD *esd; *e = GF_BAD_PARAM; if (!file || !TrackNum || !gf_isom_get_track_id(file, TrackNum)) return NULL; if (!gf_isom_get_sample_count(file, TrackNum)) { *e = GF_OK; return NULL; } *e = GF_NOT_SUPPORTED; nbEdts = gf_isom_get_edit_segment_count(file, TrackNum); if (nbEdts>1) { u64 et, sd, mt; u8 em; gf_isom_get_edit_segment(file, TrackNum, 1, &et, &sd, &mt, &em); if ((nbEdts>2) || (em!=GF_ISOM_EDIT_EMPTY)) { GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[rtp hinter] Cannot hint track whith EditList\n")); return NULL; } } if (nbEdts) gf_isom_remove_edit_segments(file, TrackNum); if (!gf_isom_is_track_enabled(file, TrackNum)) return NULL; /*by default NO PL signaled*/ PL_ID = 0; OfficialPayloadID = 0; force_dts_delta = 0; streamType = oti = 0; mpeg4mode = NULL; required_rate = 0; is_crypted = 0; IV_length = KI_length = 0; oti = 0; nb_ch = 0; avc_nalu_size = 0; has_mpeg4_mapping = 1; TrackMediaType = gf_isom_get_media_type(file, TrackNum); TrackMediaSubType = gf_isom_get_media_subtype(file, TrackNum, 1); /*for max compatibility with QT*/ if (!default_rtp_rate) default_rtp_rate = 90000; /*timed-text is a bit special, we support multiple stream descriptions & co*/ if ( (TrackMediaType==GF_ISOM_MEDIA_TEXT) || (TrackMediaType==GF_ISOM_MEDIA_SUBT)) { hintType = GF_RTP_PAYT_3GPP_TEXT; oti = GPAC_OTI_TEXT_MPEG4; streamType = GF_STREAM_TEXT; /*fixme - this works cos there's only one PL for text in mpeg4 at the current time*/ PL_ID = 0x10; } else { if (gf_isom_get_sample_description_count(file, TrackNum) > 1) return NULL; TrackMediaSubType = gf_isom_get_media_subtype(file, TrackNum, 1); switch (TrackMediaSubType) { case GF_ISOM_SUBTYPE_MPEG4_CRYP: is_crypted = 1; case GF_ISOM_SUBTYPE_MPEG4: esd = gf_isom_get_esd(file, TrackNum, 1); hintType = GF_RTP_PAYT_MPEG4; if (esd) { streamType = esd->decoderConfig->streamType; oti = esd->decoderConfig->objectTypeIndication; if (esd->URLString) hintType = 0; /*AAC*/ if ((streamType==GF_STREAM_AUDIO) && esd->decoderConfig->decoderSpecificInfo /*(nb: we use mpeg4 for MPEG-2 AAC)*/ && ((oti==GPAC_OTI_AUDIO_AAC_MPEG4) || (oti==GPAC_OTI_AUDIO_AAC_MPEG4) || (oti==GPAC_OTI_AUDIO_AAC_MPEG2_MP) || (oti==GPAC_OTI_AUDIO_AAC_MPEG2_LCP) || (oti==GPAC_OTI_AUDIO_AAC_MPEG2_SSRP)) ) { u32 sample_rate; GF_M4ADecSpecInfo a_cfg; gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &a_cfg); nb_ch = a_cfg.nb_chan; sample_rate = a_cfg.base_sr; PL_ID = a_cfg.audioPL; switch (a_cfg.base_object_type) { case GF_M4A_AAC_MAIN: case GF_M4A_AAC_LC: if (flags & GP_RTP_PCK_USE_LATM_AAC) { hintType = GF_RTP_PAYT_LATM; break; } case GF_M4A_AAC_SBR: case GF_M4A_AAC_PS: case GF_M4A_AAC_LTP: case GF_M4A_AAC_SCALABLE: case GF_M4A_ER_AAC_LC: case GF_M4A_ER_AAC_LTP: case GF_M4A_ER_AAC_SCALABLE: mpeg4mode = "AAC"; break; case GF_M4A_CELP: case GF_M4A_ER_CELP: mpeg4mode = "CELP"; break; } required_rate = sample_rate; } /*MPEG1/2 audio*/ else if ((streamType==GF_STREAM_AUDIO) && ((oti==GPAC_OTI_AUDIO_MPEG2_PART3) || (oti==GPAC_OTI_AUDIO_MPEG1))) { u32 sample_rate; if (!is_crypted) { GF_ISOSample *samp = gf_isom_get_sample(file, TrackNum, 1, NULL); u32 hdr = GF_4CC((u8)samp->data[0], (u8)samp->data[1], (u8)samp->data[2], (u8)samp->data[3]); nb_ch = gf_mp3_num_channels(hdr); sample_rate = gf_mp3_sampling_rate(hdr); gf_isom_sample_del(&samp); hintType = GF_RTP_PAYT_MPEG12_AUDIO; /*use official RTP/AVP payload type*/ OfficialPayloadID = 14; required_rate = 90000; } /*encrypted MP3 must be sent through MPEG-4 generic to signal all ISMACryp stuff*/ else { u8 bps; gf_isom_get_audio_info(file, TrackNum, 1, &sample_rate, &nb_ch, &bps); required_rate = sample_rate; } } /*QCELP audio*/ else if ((streamType==GF_STREAM_AUDIO) && (oti==GPAC_OTI_AUDIO_13K_VOICE)) { hintType = GF_RTP_PAYT_QCELP; OfficialPayloadID = 12; required_rate = 8000; streamType = GF_STREAM_AUDIO; nb_ch = 1; } /*EVRC/SVM audio*/ else if ((streamType==GF_STREAM_AUDIO) && ((oti==GPAC_OTI_AUDIO_EVRC_VOICE) || (oti==GPAC_OTI_AUDIO_SMV_VOICE)) ) { hintType = GF_RTP_PAYT_EVRC_SMV; required_rate = 8000; streamType = GF_STREAM_AUDIO; nb_ch = 1; } /*visual streams*/ else if (streamType==GF_STREAM_VISUAL) { if (oti==GPAC_OTI_VIDEO_MPEG4_PART2) { GF_M4VDecSpecInfo dsi; gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); PL_ID = dsi.VideoPL; } /*MPEG1/2 video*/ if ( ((oti>=GPAC_OTI_VIDEO_MPEG2_SIMPLE) && (oti<=GPAC_OTI_VIDEO_MPEG2_422)) || (oti==GPAC_OTI_VIDEO_MPEG1)) { if (!is_crypted) { hintType = GF_RTP_PAYT_MPEG12_VIDEO; OfficialPayloadID = 32; } } /*for ISMA*/ if (is_crypted) { /*that's another pain with ISMACryp, even if no B-frames the DTS is signaled...*/ if (oti==GPAC_OTI_VIDEO_MPEG4_PART2) force_dts_delta = 22; else if (oti==GPAC_OTI_VIDEO_AVC) { flags &= ~GP_RTP_PCK_USE_MULTI; force_dts_delta = 22; } flags |= GP_RTP_PCK_SIGNAL_RAP | GP_RTP_PCK_SIGNAL_TS; } required_rate = default_rtp_rate; } /*systems streams*/ else if (gf_isom_has_sync_shadows(file, TrackNum) || gf_isom_has_sample_dependency(file, TrackNum)) { flags |= GP_RTP_PCK_SYSTEMS_CAROUSEL; } gf_odf_desc_del((GF_Descriptor*)esd); } break; case GF_ISOM_SUBTYPE_3GP_H263: hintType = GF_RTP_PAYT_H263; required_rate = 90000; streamType = GF_STREAM_VISUAL; OfficialPayloadID = 34; /*not 100% compliant (short header is missing) but should still work*/ oti = GPAC_OTI_VIDEO_MPEG4_PART2; PL_ID = 0x01; break; case GF_ISOM_SUBTYPE_3GP_AMR: required_rate = 8000; hintType = GF_RTP_PAYT_AMR; streamType = GF_STREAM_AUDIO; has_mpeg4_mapping = 0; nb_ch = 1; break; case GF_ISOM_SUBTYPE_3GP_AMR_WB: required_rate = 16000; hintType = GF_RTP_PAYT_AMR_WB; streamType = GF_STREAM_AUDIO; has_mpeg4_mapping = 0; nb_ch = 1; break; case GF_ISOM_SUBTYPE_AVC_H264: case GF_ISOM_SUBTYPE_AVC2_H264: case GF_ISOM_SUBTYPE_SVC_H264: { GF_AVCConfig *avcc = gf_isom_avc_config_get(file, TrackNum, 1); required_rate = 90000; /* "90 kHz clock rate MUST be used"*/ hintType = GF_RTP_PAYT_H264_AVC; streamType = GF_STREAM_VISUAL; avc_nalu_size = avcc->nal_unit_size; oti = GPAC_OTI_VIDEO_AVC; PL_ID = 0x0F; gf_odf_avc_cfg_del(avcc); } break; case GF_ISOM_SUBTYPE_3GP_QCELP: required_rate = 8000; hintType = GF_RTP_PAYT_QCELP; streamType = GF_STREAM_AUDIO; oti = GPAC_OTI_AUDIO_13K_VOICE; OfficialPayloadID = 12; nb_ch = 1; break; case GF_ISOM_SUBTYPE_3GP_EVRC: case GF_ISOM_SUBTYPE_3GP_SMV: required_rate = 8000; hintType = GF_RTP_PAYT_EVRC_SMV; streamType = GF_STREAM_AUDIO; oti = (TrackMediaSubType==GF_ISOM_SUBTYPE_3GP_EVRC) ? GPAC_OTI_AUDIO_EVRC_VOICE : GPAC_OTI_AUDIO_SMV_VOICE; nb_ch = 1; break; case GF_ISOM_SUBTYPE_3GP_DIMS: hintType = GF_RTP_PAYT_3GPP_DIMS; streamType = GF_STREAM_SCENE; break; case GF_ISOM_SUBTYPE_AC3: hintType = GF_RTP_PAYT_AC3; streamType = GF_STREAM_AUDIO; gf_isom_get_audio_info(file, TrackNum, 1, NULL, &nb_ch, NULL); break; default: /*ERROR*/ hintType = 0; break; } } /*not hintable*/ if (!hintType) return NULL; /*we only support self-contained files for hinting*/ gf_isom_get_data_reference(file, TrackNum, 1, &url, &urn); if (url || urn) return NULL; *e = GF_OUT_OF_MEM; GF_SAFEALLOC(tmp, GF_RTPHinter); if (!tmp) return NULL; /*override hinter type if requested and possible*/ if (has_mpeg4_mapping && (flags & GP_RTP_PCK_FORCE_MPEG4)) { hintType = GF_RTP_PAYT_MPEG4; avc_nalu_size = 0; } /*use static payload ID if enabled*/ else if (OfficialPayloadID && (flags & GP_RTP_PCK_USE_STATIC_ID) ) { PayloadID = OfficialPayloadID; } tmp->file = file; tmp->TrackNum = TrackNum; tmp->avc_nalu_size = avc_nalu_size; tmp->nb_chan = nb_ch; /*spatial scalability check*/ tmp->has_ctts = gf_isom_has_time_offset(file, TrackNum); /*get sample info*/ gf_media_get_sample_average_infos(file, TrackNum, &MinSize, &MaxSize, &avgTS, &maxDTSDelta, &const_dur, &bandwidth); /*systems carousel: we need at least IDX and RAP signaling*/ if (flags & GP_RTP_PCK_SYSTEMS_CAROUSEL) { flags |= GP_RTP_PCK_SIGNAL_RAP; } /*update flags in MultiSL*/ if (flags & GP_RTP_PCK_USE_MULTI) { if (MinSize != MaxSize) flags |= GP_RTP_PCK_SIGNAL_SIZE; if (!const_dur) flags |= GP_RTP_PCK_SIGNAL_TS; } if (tmp->has_ctts) flags |= GP_RTP_PCK_SIGNAL_TS; /*default SL for RTP */ InitSL_RTP(&my_sl); my_sl.timestampResolution = gf_isom_get_media_timescale(file, TrackNum); /*override clockrate if set*/ if (required_rate) { Double sc = required_rate; sc /= my_sl.timestampResolution; maxDTSDelta = (u32) (maxDTSDelta*sc); my_sl.timestampResolution = required_rate; } /*switch to RTP TS*/ max_ptime = (u32) (max_ptime * my_sl.timestampResolution / 1000); my_sl.AUSeqNumLength = gf_get_bit_size(gf_isom_get_sample_count(file, TrackNum)); my_sl.CUDuration = const_dur; if (gf_isom_has_sync_points(file, TrackNum)) { my_sl.useRandomAccessPointFlag = 1; } else { my_sl.useRandomAccessPointFlag = 0; my_sl.hasRandomAccessUnitsOnlyFlag = 1; } if (is_crypted) { Bool use_sel_enc; gf_isom_get_ismacryp_info(file, TrackNum, 1, NULL, NULL, NULL, NULL, NULL, &use_sel_enc, &IV_length, &KI_length); if (use_sel_enc) flags |= GP_RTP_PCK_SELECTIVE_ENCRYPTION; } // in case a different timescale was provided tmp->OrigTimeScale = gf_isom_get_media_timescale(file, TrackNum); tmp->rtp_p = gf_rtp_builder_new(hintType, &my_sl, flags, tmp, MP4T_OnNewPacket, MP4T_OnPacketDone, /*if copy, no data ref*/ copy_media ? NULL : MP4T_OnDataRef, MP4T_OnData); //init the builder gf_rtp_builder_init(tmp->rtp_p, PayloadID, Path_MTU, max_ptime, streamType, oti, PL_ID, MinSize, MaxSize, avgTS, maxDTSDelta, IV_length, KI_length, mpeg4mode); /*ISMA compliance is a pain...*/ if (force_dts_delta) tmp->rtp_p->slMap.DTSDeltaLength = force_dts_delta; /* Hint Track Setup */ tmp->TrackID = gf_isom_get_track_id(file, TrackNum); tmp->HintID = tmp->TrackID + 65535; while (gf_isom_get_track_by_id(file, tmp->HintID)) tmp->HintID++; tmp->HintTrack = gf_isom_new_track(file, tmp->HintID, GF_ISOM_MEDIA_HINT, my_sl.timestampResolution); gf_isom_setup_hint_track(file, tmp->HintTrack, GF_ISOM_HINT_RTP); /*create a hint description*/ gf_isom_new_hint_description(file, tmp->HintTrack, -1, -1, 0, &descIndex); gf_isom_rtp_set_timescale(file, tmp->HintTrack, descIndex, my_sl.timestampResolution); if (hintType==GF_RTP_PAYT_MPEG4) { tmp->rtp_p->slMap.ObjectTypeIndication = oti; /*set this SL for extraction.*/ gf_isom_set_extraction_slc(file, TrackNum, 1, &my_sl); } tmp->bandwidth = bandwidth; /*set interleaving*/ gf_isom_set_track_group(file, TrackNum, InterleaveGroupID); if (!copy_media) { /*if we don't copy data set hint track and media track in the same group*/ gf_isom_set_track_group(file, tmp->HintTrack, InterleaveGroupID); } else { gf_isom_set_track_group(file, tmp->HintTrack, InterleaveGroupID + OFFSET_HINT_GROUP_ID); } /*use user-secified priority*/ InterleaveGroupPriority*=2; gf_isom_set_track_priority_in_group(file, TrackNum, InterleaveGroupPriority+1); gf_isom_set_track_priority_in_group(file, tmp->HintTrack, InterleaveGroupPriority); #if 0 /*QT FF: not setting these flags = server uses a random offset*/ gf_isom_rtp_set_time_offset(file, tmp->HintTrack, 1, 0); /*we don't use seq offset for maintainance pruposes*/ gf_isom_rtp_set_time_sequence_offset(file, tmp->HintTrack, 1, 0); #endif *e = GF_OK; return tmp; }
//write the file track by track, with moov box before or after the mdat GF_Err WriteFlat(MovieWriter *mw, u8 moovFirst, GF_BitStream *bs) { GF_Err e; u32 i; u64 offset, finalOffset, totSize, begin, firstSize, finalSize; GF_Box *a; GF_List *writers = gf_list_new(); GF_ISOFile *movie = mw->movie; begin = totSize = 0; //first setup the writers e = SetupWriters(mw, writers, 0); if (e) goto exit; if (!moovFirst) { if (movie->openMode == GF_ISOM_OPEN_WRITE) { begin = 0; totSize = gf_isom_datamap_get_offset(movie->editFileMap); /*start boxes have not been written yet, do it*/ if (!totSize) { if (movie->is_jp2) { gf_bs_write_u32(movie->editFileMap->bs, 12); gf_bs_write_u32(movie->editFileMap->bs, GF_4CC('j','P',' ',' ')); gf_bs_write_u32(movie->editFileMap->bs, 0x0D0A870A); totSize += 12; begin += 12; } if (movie->brand) { e = gf_isom_box_size((GF_Box *)movie->brand); if (e) goto exit; e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs); if (e) goto exit; totSize += movie->brand->size; begin += movie->brand->size; } if (movie->pdin) { e = gf_isom_box_size((GF_Box *)movie->pdin); if (e) goto exit; e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs); if (e) goto exit; totSize += movie->pdin->size; begin += movie->pdin->size; } } else { if (movie->is_jp2) begin += 12; if (movie->brand) begin += movie->brand->size; if (movie->pdin) begin += movie->pdin->size; } totSize -= begin; } else { if (movie->is_jp2) { gf_bs_write_u32(bs, 12); gf_bs_write_u32(bs, GF_4CC('j','P',' ',' ')); gf_bs_write_u32(bs, 0x0D0A870A); } if (movie->brand) { e = gf_isom_box_size((GF_Box *)movie->brand); if (e) goto exit; e = gf_isom_box_write((GF_Box *)movie->brand, bs); if (e) goto exit; } /*then progressive download*/ if (movie->pdin) { e = gf_isom_box_size((GF_Box *)movie->pdin); if (e) goto exit; e = gf_isom_box_write((GF_Box *)movie->pdin, bs); if (e) goto exit; } } //if the moov is at the end, write directly i=0; while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) { switch (a->type) { /*written by hand*/ case GF_ISOM_BOX_TYPE_MOOV: case GF_ISOM_BOX_TYPE_META: case GF_ISOM_BOX_TYPE_FTYP: case GF_ISOM_BOX_TYPE_PDIN: break; case GF_ISOM_BOX_TYPE_MDAT: //in case we're capturing if (movie->openMode == GF_ISOM_OPEN_WRITE) { //emulate a write to recreate our tables (media data already written) e = DoWrite(mw, writers, bs, 1, begin); if (e) goto exit; continue; } //to avoid computing the size each time write always 4 + 4 + 8 bytes before begin = gf_bs_get_position(bs); gf_bs_write_u64(bs, 0); gf_bs_write_u64(bs, 0); e = DoWrite(mw, writers, bs, 0, gf_bs_get_position(bs)); if (e) goto exit; totSize = gf_bs_get_position(bs) - begin; break; default: e = gf_isom_box_size(a); if (e) goto exit; e = gf_isom_box_write(a, bs); if (e) goto exit; break; } } //OK, write the movie box. e = WriteMoovAndMeta(movie, writers, bs); if (e) goto exit; /*if data has been written, update mdat size*/ if (totSize) { offset = gf_bs_get_position(bs); e = gf_bs_seek(bs, begin); if (e) goto exit; if (totSize > 0xFFFFFFFF) { gf_bs_write_u32(bs, 1); } else { gf_bs_write_u32(bs, (u32) totSize); } gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT); if (totSize > 0xFFFFFFFF) gf_bs_write_u64(bs, totSize); e = gf_bs_seek(bs, offset); } movie->mdat->size = totSize; goto exit; } //nope, we have to write the moov first. The pb is that //1 - we don't know its size till the mdat is written //2 - we don't know the ofset at which the mdat will start... //3 - once the mdat is written, the chunkOffset table can have changed... if (movie->is_jp2) { gf_bs_write_u32(bs, 12); gf_bs_write_u32(bs, GF_4CC('j','P',' ',' ')); gf_bs_write_u32(bs, 0x0D0A870A); } if (movie->brand) { e = gf_isom_box_size((GF_Box *)movie->brand); if (e) goto exit; e = gf_isom_box_write((GF_Box *)movie->brand, bs); if (e) goto exit; } /*then progressive dnload*/ if (movie->pdin) { e = gf_isom_box_size((GF_Box *)movie->pdin); if (e) goto exit; e = gf_isom_box_write((GF_Box *)movie->pdin, bs); if (e) goto exit; } //What we will do is first emulate the write from the begining... //note: this will set the size of the mdat e = DoWrite(mw, writers, bs, 1, gf_bs_get_position(bs)); if (e) goto exit; firstSize = GetMoovAndMetaSize(movie, writers); //offset = (firstSize > 0xFFFFFFFF ? firstSize + 8 : firstSize) + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); offset = firstSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); e = ShiftOffset(movie, writers, offset); if (e) goto exit; //get the size and see if it has changed (eg, we moved to 64 bit offsets) finalSize = GetMoovAndMetaSize(movie, writers); if (firstSize != finalSize) { //we need to remove our offsets ResetWriters(writers); //finalOffset = (finalSize > 0xFFFFFFFF ? finalSize + 8 : finalSize) + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); finalOffset = finalSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); //OK, now we're sure about the final size. //we don't need to re-emulate, as the only thing that changed is the offset //so just shift the offset e = ShiftOffset(movie, writers, finalOffset - offset); if (e) goto exit; } //now write our stuff e = WriteMoovAndMeta(movie, writers, bs); if (e) goto exit; e = gf_isom_box_size((GF_Box *)movie->mdat); if (e) goto exit; e = gf_isom_box_write((GF_Box *)movie->mdat, bs); if (e) goto exit; //we don't need the offset as the moov is already written... ResetWriters(writers); e = DoWrite(mw, writers, bs, 0, 0); if (e) goto exit; //then the rest i=0; while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) { switch (a->type) { case GF_ISOM_BOX_TYPE_MOOV: case GF_ISOM_BOX_TYPE_META: case GF_ISOM_BOX_TYPE_FTYP: case GF_ISOM_BOX_TYPE_PDIN: case GF_ISOM_BOX_TYPE_MDAT: break; default: e = gf_isom_box_size(a); if (e) goto exit; e = gf_isom_box_write(a, bs); if (e) goto exit; } } exit: CleanWriters(writers); gf_list_del(writers); return e; }
GF_Err gf_isom_parse_movie_boxes(GF_ISOFile *mov, u64 *bytesMissing, Bool progressive_mode) { GF_Box *a; u64 totSize; GF_Err e = GF_OK; totSize = 0; #ifndef GPAC_DISABLE_ISOM_FRAGMENTS if (mov->single_moof_mode && mov->single_moof_state == 2) { return e; } /*restart from where we stopped last*/ totSize = mov->current_top_box_start; gf_bs_seek(mov->movieFileMap->bs, mov->current_top_box_start); #endif /*while we have some data, parse our boxes*/ while (gf_bs_available(mov->movieFileMap->bs)) { *bytesMissing = 0; #ifndef GPAC_DISABLE_ISOM_FRAGMENTS mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs); GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Current top box start before parsing %d\n", mov->current_top_box_start)); #endif e = gf_isom_parse_root_box(&a, mov->movieFileMap->bs, bytesMissing, progressive_mode); if (e >= 0) { e = GF_OK; } else if (e == GF_ISOM_INCOMPLETE_FILE) { /*our mdat is uncomplete, only valid for READ ONLY files...*/ if (mov->openMode != GF_ISOM_OPEN_READ) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Incomplete MDAT while file is not read-only\n")); return GF_ISOM_INVALID_FILE; } return e; } else { return e; } switch (a->type) { /*MOOV box*/ case GF_ISOM_BOX_TYPE_MOOV: if (mov->moov) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate MOOV detected!\n")); return GF_ISOM_INVALID_FILE; } mov->moov = (GF_MovieBox *)a; /*set our pointer to the movie*/ mov->moov->mov = mov; #ifndef GPAC_DISABLE_ISOM_FRAGMENTS if (mov->moov->mvex) mov->moov->mvex->mov = mov; #endif e = gf_list_add(mov->TopBoxes, a); if (e) { return e; } totSize += a->size; break; /*META box*/ case GF_ISOM_BOX_TYPE_META: if (mov->meta) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate META detected!\n")); return GF_ISOM_INVALID_FILE; } mov->meta = (GF_MetaBox *)a; e = gf_list_add(mov->TopBoxes, a); if (e) { return e; } totSize += a->size; break; /*we only keep the MDAT in READ for dump purposes*/ case GF_ISOM_BOX_TYPE_MDAT: totSize += a->size; if (mov->openMode == GF_ISOM_OPEN_READ) { if (!mov->mdat) { mov->mdat = (GF_MediaDataBox *) a; e = gf_list_add(mov->TopBoxes, mov->mdat); if (e) { return e; } } #ifndef GPAC_DISABLE_ISOM_FRAGMENTS else if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) gf_list_add(mov->TopBoxes, a); #endif else gf_isom_box_del(a); } /*if we don't have any MDAT yet, create one (edit-write mode) We only work with one mdat, but we're puting it at the place of the first mdat found when opening a file for editing*/ else if (!mov->mdat && (mov->openMode != GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_CAT_FRAGMENTS)) { gf_isom_box_del(a); mov->mdat = (GF_MediaDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT); e = gf_list_add(mov->TopBoxes, mov->mdat); if (e) { return e; } } else { gf_isom_box_del(a); } break; case GF_ISOM_BOX_TYPE_FTYP: /*ONE AND ONLY ONE FTYP*/ if (mov->brand) { gf_isom_box_del(a); GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate FTYP detected!\n")); return GF_ISOM_INVALID_FILE; } mov->brand = (GF_FileTypeBox *)a; totSize += a->size; e = gf_list_add(mov->TopBoxes, a); break; case GF_ISOM_BOX_TYPE_PDIN: /*ONE AND ONLY ONE PDIN*/ if (mov->pdin) { gf_isom_box_del(a); GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Duplicate PDIN detected!\n")); return GF_ISOM_INVALID_FILE; } mov->pdin = (GF_ProgressiveDownloadBox *) a; totSize += a->size; e = gf_list_add(mov->TopBoxes, a); break; #ifndef GPAC_DISABLE_ISOM_FRAGMENTS case GF_ISOM_BOX_TYPE_STYP: { u32 brand = ((GF_SegmentTypeBox *)a)->majorBrand; switch (brand) { case GF_4CC('s', 'i', 's', 'x'): case GF_4CC('r', 'i', 's', 'x'): case GF_4CC('s', 's', 's', 's'): mov->is_index_segment = GF_TRUE; break; default: break; } } /*fall-through*/ case GF_ISOM_BOX_TYPE_SIDX: totSize += a->size; if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) { e = gf_list_add(mov->TopBoxes, a); } else { gf_isom_box_del(a); } break; case GF_ISOM_BOX_TYPE_MOOF: if (!mov->moov) { GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Movie fragment but no moov (yet) - possibly broken parsing!\n")); } if (mov->single_moof_mode) { mov->single_moof_state++; if (mov->single_moof_state > 1) { gf_isom_box_del(a); return GF_OK; } } ((GF_MovieFragmentBox *)a)->mov = mov; totSize += a->size; mov->moof = (GF_MovieFragmentBox *) a; /*read & debug: store at root level*/ if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) { u32 k; gf_list_add(mov->TopBoxes, a); /*also update pointers to trex for debug*/ if (mov->moov) { for (k=0; k<gf_list_count(mov->moof->TrackList); k++) { GF_TrackFragmentBox *traf = gf_list_get(mov->moof->TrackList, k); if (traf->tfhd) { GF_TrackBox *trak = gf_isom_get_track_from_id(mov->moov, traf->tfhd->trackID); u32 j=0; while ((traf->trex = (GF_TrackExtendsBox*)gf_list_enum(mov->moov->mvex->TrackExList, &j))) { if (traf->trex->trackID == traf->tfhd->trackID) { if (!traf->trex->track) traf->trex->track = trak; break; } traf->trex = NULL; } } //we should only parse senc/psec when no saiz/saio is present, otherwise we fetch the info directly if (traf->trex && traf->trex->track && (traf->piff_sample_encryption || traf->sample_encryption)) { GF_TrackBox *trak = GetTrackbyID(mov->moov, traf->tfhd->trackID); e = senc_Parse(mov->movieFileMap->bs, trak, traf, traf->piff_sample_encryption ? (GF_SampleEncryptionBox *) traf->piff_sample_encryption : traf->sample_encryption); } } } } else if (mov->openMode==GF_ISOM_OPEN_CAT_FRAGMENTS) { mov->NextMoofNumber = mov->moof->mfhd->sequence_number+1; mov->moof = NULL; gf_isom_box_del(a); } else { /*merge all info*/ e = MergeFragment((GF_MovieFragmentBox *)a, mov); gf_isom_box_del(a); } break; #endif case GF_4CC('j','P',' ',' '): { GF_UnknownBox *box = (GF_UnknownBox*)a; u8 *c = (u8 *) box->data; if ((box->dataSize==4) && (GF_4CC(c[0],c[1],c[2],c[3])==(u32)0x0D0A870A)) mov->is_jp2 = 1; gf_isom_box_del(a); } break; case GF_ISOM_BOX_TYPE_PRFT: #ifndef GPAC_DISABLE_ISOM_FRAGMENTS if (!(mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG)) { //keep the last one read if (mov->last_producer_ref_time) gf_isom_box_del(a); else mov->last_producer_ref_time = (GF_ProducerReferenceTimeBox *)a; break; } #endif //fallthrough default: totSize += a->size; e = gf_list_add(mov->TopBoxes, a); break; } #ifndef GPAC_DISABLE_ISOM_FRAGMENTS /*remember where we left, in case we append an entire number of movie fragments*/ mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs); #endif } /*we need at least moov or meta*/ if (!mov->moov && !mov->meta #ifndef GPAC_DISABLE_ISOM_FRAGMENTS && !mov->moof && !mov->is_index_segment #endif ) { return GF_ISOM_INCOMPLETE_FILE; } /*we MUST have movie header*/ if (mov->moov && !mov->moov->mvhd) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Missing MVHD in MOOV!\n")); return GF_ISOM_INVALID_FILE; } /*we MUST have meta handler*/ if (mov->meta && !mov->meta->handler) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Missing handler in META!\n")); return GF_ISOM_INVALID_FILE; } #ifndef GPAC_DISABLE_ISOM_WRITE if (mov->moov) { /*set the default interleaving time*/ mov->interleavingTime = mov->moov->mvhd->timeScale; #ifndef GPAC_DISABLE_ISOM_FRAGMENTS /*in edit mode with successfully loaded fragments, delete all fragment signaling since file is no longer fragmented*/ if ((mov->openMode > GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_CAT_FRAGMENTS) && mov->moov->mvex) { gf_isom_box_del((GF_Box *)mov->moov->mvex); mov->moov->mvex = NULL; } #endif } //create a default mdat if none was found if (!mov->mdat && (mov->openMode != GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_CAT_FRAGMENTS)) { mov->mdat = (GF_MediaDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT); e = gf_list_add(mov->TopBoxes, mov->mdat); if (e) return e; } #endif /*GPAC_DISABLE_ISOM_WRITE*/ return GF_OK; }
GF_EXPORT void gf_img_parse(GF_BitStream *bs, u8 *OTI, u32 *mtype, u32 *width, u32 *height, char **dsi, u32 *dsi_len) { u8 b1, b2, b3; u32 size, type; u64 pos; pos = gf_bs_get_position(bs); gf_bs_seek(bs, 0); *mtype = *width = *height = 0; *OTI = 0; if (dsi) { *dsi = NULL; *dsi_len = 0; } b1 = gf_bs_read_u8(bs); b2 = gf_bs_read_u8(bs); b3 = gf_bs_read_u8(bs); /*JPEG*/ if ((b1==0xFF) && (b2==0xD8) && (b3==0xFF)) { u32 offset = 0; u32 Xdens, Ydens, nb_comp; gf_bs_read_u8(bs); gf_bs_skip_bytes(bs, 10); /*2 size, 5 JFIF\0, 2 version, 1 units*/ Xdens = gf_bs_read_int(bs, 16); Ydens = gf_bs_read_int(bs, 16); nb_comp = 0; /*get frame header FFC0*/ while (gf_bs_available(bs)) { u32 type, w, h; if (gf_bs_read_u8(bs) != 0xFF) continue; if (!offset) offset = (u32)gf_bs_get_position(bs) - 1; type = gf_bs_read_u8(bs); /*process all Start of Image markers*/ switch (type) { case 0xC0: case 0xC1: case 0xC2: case 0xC3: case 0xC5: case 0xC6: case 0xC7: case 0xC9: case 0xCA: case 0xCB: case 0xCD: case 0xCE: case 0xCF: gf_bs_skip_bytes(bs, 3); h = gf_bs_read_int(bs, 16); w = gf_bs_read_int(bs, 16); if ((w > *width) || (h > *height)) { *width = w; *height = h; } nb_comp = gf_bs_read_int(bs, 8); break; } } *OTI = GPAC_OTI_IMAGE_JPEG; *mtype = GF_4CC('j','p','e','g'); if (dsi) { GF_BitStream *bs_dsi = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); gf_bs_write_u16(bs_dsi, offset); gf_bs_write_u16(bs_dsi, Xdens); gf_bs_write_u16(bs_dsi, Ydens); gf_bs_write_u8(bs_dsi, nb_comp); gf_bs_get_content(bs_dsi, dsi, dsi_len); gf_bs_del(bs_dsi); } } /*PNG*/ else if ((b1==0x89) && (b2==0x50) && (b3==0x4E)) { /*check for PNG sig*/ if ( (gf_bs_read_u8(bs) != 0x47) || (gf_bs_read_u8(bs) != 0x0D) || (gf_bs_read_u8(bs) != 0x0A) || (gf_bs_read_u8(bs) != 0x1A) || (gf_bs_read_u8(bs) != 0x0A) ) goto exit; gf_bs_read_u32(bs); /*check for PNG IHDR*/ if ( (gf_bs_read_u8(bs) != 'I') || (gf_bs_read_u8(bs) != 'H') || (gf_bs_read_u8(bs) != 'D') || (gf_bs_read_u8(bs) != 'R')) goto exit; *width = gf_bs_read_u32(bs); *height = gf_bs_read_u32(bs); *OTI = GPAC_OTI_IMAGE_PNG; *mtype = GF_4CC('p','n','g',' '); } size = gf_bs_read_u8(bs); type = gf_bs_read_u32(bs); if ( ((size==12) && (type==GF_4CC('j','P',' ',' ') )) || (type==GF_4CC('j','p','2','h') ) ) { if (type==GF_4CC('j','p','2','h')) { *OTI = GPAC_OTI_IMAGE_JPEG_2000; *mtype = GF_4CC('j','p','2',' '); goto j2k_restart; } type = gf_bs_read_u32(bs); if (type!=0x0D0A870A) goto exit; *OTI = GPAC_OTI_IMAGE_JPEG_2000; *mtype = GF_4CC('j','p','2',' '); while (gf_bs_available(bs)) { j2k_restart: size = gf_bs_read_u32(bs); type = gf_bs_read_u32(bs); switch (type) { case GF_4CC('j','p','2','h'): goto j2k_restart; case GF_4CC('i','h','d','r'): { u16 nb_comp; u8 BPC, C, UnkC, IPR; *height = gf_bs_read_u32(bs); *width = gf_bs_read_u32(bs); nb_comp = gf_bs_read_u16(bs); BPC = gf_bs_read_u8(bs); C = gf_bs_read_u8(bs); UnkC = gf_bs_read_u8(bs); IPR = gf_bs_read_u8(bs); if (dsi) { GF_BitStream *bs_dsi = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); gf_bs_write_u32(bs_dsi, *height); gf_bs_write_u32(bs_dsi, *width); gf_bs_write_u16(bs_dsi, nb_comp); gf_bs_write_u8(bs_dsi, BPC); gf_bs_write_u8(bs_dsi, C); gf_bs_write_u8(bs_dsi, UnkC); gf_bs_write_u8(bs_dsi, IPR); gf_bs_get_content(bs_dsi, dsi, dsi_len); gf_bs_del(bs_dsi); } goto exit; } break; default: gf_bs_skip_bytes(bs, size-8); break; } } } exit: gf_bs_seek(bs, pos); }