u32 gf_isom_fdm_get_data(GF_FileDataMap *ptr, char *buffer, u32 bufferLength, u64 fileOffset) { u32 bytesRead; //can we seek till that point ??? if (fileOffset > gf_bs_get_size(ptr->bs)) return 0; //ouch, we are not at the previous location, do a seek if (ptr->curPos != fileOffset) { if (gf_bs_seek(ptr->bs, fileOffset) != GF_OK) return 0; ptr->curPos = fileOffset; } //read our data. bytesRead = gf_bs_read_data(ptr->bs, buffer, bufferLength); //update our cache if (bytesRead == bufferLength) { ptr->curPos += bytesRead; } else { //rewind to original (if seek fails, return 0 cause this means: //1- no support for seek on the platform //2- corrupted file for the OS if (ptr->stream) fflush(ptr->stream); gf_bs_seek(ptr->bs, ptr->curPos); } ptr->last_acces_was_read = 1; return bytesRead; }
u32 gf_isom_fdm_get_data(GF_FileDataMap *ptr, char *buffer, u32 bufferLength, u64 fileOffset) { u32 bytesRead; //can we seek till that point ??? if (fileOffset > gf_bs_get_size(ptr->bs)) return 0; //ouch, we are not at the previous location, do a seek if (ptr->curPos != fileOffset) { if (gf_bs_seek(ptr->bs, fileOffset) != GF_OK) return 0; ptr->curPos = fileOffset; } //read our data. bytesRead = gf_bs_read_data(ptr->bs, buffer, bufferLength); //update our cache if (bytesRead == bufferLength) { ptr->curPos += bytesRead; } else { gf_bs_get_refreshed_size(ptr->bs); gf_bs_seek(ptr->bs, fileOffset); bytesRead = gf_bs_read_data(ptr->bs, buffer, bufferLength); //update our cache if (bytesRead == bufferLength) { ptr->curPos += bytesRead; } else { gf_bs_seek(ptr->bs, ptr->curPos); bytesRead = 0; } } ptr->last_acces_was_read = 1; return bytesRead; }
GF_Err FDM_AddData(GF_FileDataMap *ptr, char *data, u32 dataSize) { u32 ret; u64 orig; if (ptr->mode == GF_ISOM_DATA_MAP_READ) return GF_BAD_PARAM; orig = gf_bs_get_size(ptr->bs); /*last access was read, seek to end of file*/ if (ptr->last_acces_was_read) { gf_bs_seek(ptr->bs, orig); ptr->last_acces_was_read = 0; } //OK, write our stuff to the datamap... //we don't use bs here cause we want to know more about what has been written ret = gf_bs_write_data(ptr->bs, data, dataSize); if (ret != dataSize) { ptr->curPos = orig; gf_bs_seek(ptr->bs, orig); return GF_IO_ERR; } ptr->curPos = gf_bs_get_position(ptr->bs); //flush the stream !! if (ptr->stream) fflush(ptr->stream); return GF_OK; }
GF_Err store_senc_info(GF_SampleEncryptionBox *ptr, GF_BitStream *bs) { GF_Err e; u64 pos, new_pos; if (!ptr->cenc_saio) return GF_OK; pos = gf_bs_get_position(bs); if (pos>0xFFFFFFFFULL) { GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] \"senc\" offset larger than 32-bits , \"saio\" box version must be 1 .\n")); } e = gf_bs_seek(bs, ptr->cenc_saio->offset_first_offset_field); if (e) return e; //force using version 1 for saio box i.e offset has 64 bits #ifndef GPAC_DISABLE_ISOM_FRAGMENTS if (ptr->traf) { new_pos = pos - ptr->traf->moof_start_in_bs; } else #endif { new_pos = pos; } if (ptr->cenc_saio->offsets_large) { u32 i; u64 old_offset = ptr->cenc_saio->offsets_large[0]; for (i=0; i<ptr->cenc_saio->entry_count; i++) { gf_bs_write_u64(bs, new_pos + ptr->cenc_saio->offsets_large[i] - old_offset); ptr->cenc_saio->offsets_large[i] = new_pos + ptr->cenc_saio->offsets_large[i] - old_offset; } } else { gf_bs_write_u64(bs, new_pos); } return gf_bs_seek(bs, pos); }
static Bool ADTS_SyncFrame(GF_BitStream *bs, Bool is_complete, ADTSHeader *hdr) { u32 val, pos, start_pos; start_pos = (u32) gf_bs_get_position(bs); while (gf_bs_available(bs)) { val = gf_bs_read_u8(bs); if (val!=0xFF) continue; val = gf_bs_read_int(bs, 4); if (val != 0x0F) { gf_bs_read_int(bs, 4); continue; } hdr->is_mp2 = gf_bs_read_int(bs, 1); gf_bs_read_int(bs, 2); hdr->no_crc = gf_bs_read_int(bs, 1); pos = (u32) gf_bs_get_position(bs) - 2; hdr->profile = 1 + gf_bs_read_int(bs, 2); hdr->sr_idx = gf_bs_read_int(bs, 4); gf_bs_read_int(bs, 1); hdr->nb_ch = gf_bs_read_int(bs, 3); gf_bs_read_int(bs, 4); hdr->frame_size = gf_bs_read_int(bs, 13); gf_bs_read_int(bs, 11); gf_bs_read_int(bs, 2); hdr->hdr_size = 7; if (!hdr->no_crc) { gf_bs_read_u16(bs); hdr->hdr_size = 9; } if (hdr->frame_size < hdr->hdr_size) { gf_bs_seek(bs, pos+1); continue; } hdr->frame_size -= hdr->hdr_size; if (is_complete && (gf_bs_available(bs) == hdr->frame_size)) return 1; else if (gf_bs_available(bs) <= hdr->frame_size) break; gf_bs_skip_bytes(bs, hdr->frame_size); val = gf_bs_read_u8(bs); if (val!=0xFF) { gf_bs_seek(bs, pos+1); continue; } val = gf_bs_read_int(bs, 4); if (val!=0x0F) { gf_bs_read_int(bs, 4); gf_bs_seek(bs, pos+1); continue; } gf_bs_seek(bs, pos+hdr->hdr_size); return 1; } gf_bs_seek(bs, start_pos); return 0; }
GF_Err senc_Parse(GF_BitStream *bs, GF_TrackBox *trak, void *traf, GF_SampleEncryptionBox *senc) #endif { GF_Err e; u32 i, j, count; u64 pos = gf_bs_get_position(bs); #ifdef GPAC_DISABLE_ISOM_FRAGMENTS if (!traf) return GF_BAD_PARAM; #endif gf_bs_seek(bs, senc->bs_offset); count = gf_bs_read_u32(bs); if (!senc->samp_aux_info) senc->samp_aux_info = gf_list_new(); for (i=0; i<count; i++) { u32 is_encrypted; u32 samp_count; GF_CENCSampleAuxInfo *sai = (GF_CENCSampleAuxInfo *)gf_malloc(sizeof(GF_CENCSampleAuxInfo)); memset(sai, 0, sizeof(GF_CENCSampleAuxInfo)); samp_count = i+1; #ifndef GPAC_DISABLE_ISOM_FRAGMENTS if (trak) samp_count += trak->sample_count_at_seg_start; #endif if (trak) { e = gf_isom_get_sample_cenc_info_ex(trak, traf, senc, samp_count, &is_encrypted, &sai->IV_size, NULL, NULL, NULL, NULL, NULL); if (e) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isobmf] could not get cenc info for sample %d: %s\n", samp_count, gf_error_to_string(e) )); return e; } } //no init movie setup (segment dump/inspaction, assume default encrypted and 16 bytes IV else { is_encrypted = GF_TRUE; sai->IV_size = 16; } if (is_encrypted) { gf_bs_read_data(bs, (char *)sai->IV, sai->IV_size); if (senc->flags & 0x00000002) { sai->subsample_count = gf_bs_read_u16(bs); sai->subsamples = (GF_CENCSubSampleEntry *)gf_malloc(sai->subsample_count*sizeof(GF_CENCSubSampleEntry)); for (j = 0; j < sai->subsample_count; j++) { sai->subsamples[j].bytes_clear_data = gf_bs_read_u16(bs); sai->subsamples[j].bytes_encrypted_data = gf_bs_read_u32(bs); } } } gf_list_add(senc->samp_aux_info, sai); } gf_bs_seek(bs, pos); 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; }
GF_EXPORT GF_Err gf_bifs_decoder_configure_stream(GF_BifsDecoder * codec, u16 ESID, char *DecoderSpecificInfo, u32 DecoderSpecificInfoLength, u32 objectTypeIndication) { GF_BitStream *bs; BIFSStreamInfo *pInfo; GF_Err e; if (!DecoderSpecificInfo) { /* Hack for T-DMB non compliant streams */ GF_SAFEALLOC(pInfo, BIFSStreamInfo); pInfo->ESID = ESID; pInfo->config.PixelMetrics = 1; pInfo->config.version = (objectTypeIndication==2) ? 1 : 2; assert( codec ); assert( codec->streamInfo ); return gf_list_add(codec->streamInfo, pInfo); } // gf_mx_p(codec->mx); assert( codec ); if (gf_bifs_dec_get_stream(codec, ESID) != NULL) { // gf_mx_v(codec->mx); return GF_BAD_PARAM; } bs = gf_bs_new(DecoderSpecificInfo, DecoderSpecificInfoLength, GF_BITSTREAM_READ); GF_SAFEALLOC(pInfo, BIFSStreamInfo); pInfo->ESID = ESID; pInfo->config.version = objectTypeIndication; /*parse config with indicated oti*/ e = ParseConfig(bs, pInfo, (u32) objectTypeIndication); if (e) { pInfo->ESID = ESID; /*some content indicates a wrong OTI, so try to parse with v1 or v2*/ gf_bs_seek(bs, 0); /*try with reverse config*/ e = ParseConfig(bs, pInfo, (objectTypeIndication==2) ? 1 : 2); pInfo->config.version = (objectTypeIndication==2) ? 1 : 2; } if (e && (e != GF_ODF_INVALID_DESCRIPTOR)) { gf_free(pInfo); gf_bs_del(bs); return GF_BIFS_UNKNOWN_VERSION; } gf_bs_del(bs); assert( codec->streamInfo ); //first stream, configure size if (!codec->ignore_size && !gf_list_count(codec->streamInfo)) { gf_sg_set_scene_size_info(codec->scenegraph, pInfo->config.Width, pInfo->config.Height, pInfo->config.PixelMetrics); } gf_list_add(codec->streamInfo, pInfo); // gf_mx_v(codec->mx); return GF_OK; }
GF_Err senc_Parse(GF_BitStream *bs, GF_TrackBox *trak, void *traf, GF_SampleEncryptionBox *ptr) #endif { GF_Err e; u32 i, j, count; u64 pos = gf_bs_get_position(bs); #ifdef GPAC_DISABLE_ISOM_FRAGMENTS if (!traf) return GF_BAD_PARAM; #endif gf_bs_seek(bs, ptr->bs_offset); count = gf_bs_read_u32(bs); if (!ptr->samp_aux_info) ptr->samp_aux_info = gf_list_new(); for (i=0; i<count; i++) { u32 is_encrypted; GF_CENCSampleAuxInfo *sai = (GF_CENCSampleAuxInfo *)gf_malloc(sizeof(GF_CENCSampleAuxInfo)); memset(sai, 0, sizeof(GF_CENCSampleAuxInfo)); e = gf_isom_get_sample_cenc_info_ex(trak, traf, (trak ? trak->sample_count_at_seg_start : 0 )+ i+1, &is_encrypted, &sai->IV_size, NULL); if (e) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isobmf] could not get cenc info for sample %d: %s\n", trak->sample_count_at_seg_start + i+1, gf_error_to_string(e) )); return e; } if (is_encrypted) { gf_bs_read_data(bs, (char *)sai->IV, sai->IV_size); if (ptr->flags & 0x00000002) { sai->subsample_count = gf_bs_read_u16(bs); sai->subsamples = (GF_CENCSubSampleEntry *)gf_malloc(sai->subsample_count*sizeof(GF_CENCSubSampleEntry)); for (j = 0; j < sai->subsample_count; j++) { sai->subsamples[j].bytes_clear_data = gf_bs_read_u16(bs); sai->subsamples[j].bytes_encrypted_data = gf_bs_read_u32(bs); } } } gf_list_add(ptr->samp_aux_info, sai); } gf_bs_seek(bs, pos); return GF_OK; }
GF_Err store_senc_info(GF_SampleEncryptionBox *ptr, GF_BitStream *bs) { GF_Err e; u64 pos; if (!ptr->cenc_saio) return GF_OK; pos = gf_bs_get_position(bs); if (pos>0xFFFFFFFFULL) { GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] \"senc\" offset larger than 32-bits , \"saio\" box version must be 1 .\n")); } e = gf_bs_seek(bs, ptr->cenc_saio->offset_first_offset_field); if (e) return e; //force using version 1 for saio box i.e offset has 64 bits if (ptr->traf) { gf_bs_write_u64(bs, pos - ptr->traf->moof_start_in_bs ); } else { gf_bs_write_u64(bs, pos); } return gf_bs_seek(bs, pos); }
GF_Err store_senc_info(GF_SampleEncryptionBox *ptr, GF_BitStream *bs) { GF_Err e; u64 pos; if (!ptr->cenc_saio) return GF_OK; pos = gf_bs_get_position(bs); if (pos>0xFFFFFFFFULL) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] \"senc\" offset larger than 32-bits , cannot store.\n")); return GF_NOT_SUPPORTED; } e = gf_bs_seek(bs, ptr->cenc_saio->offset_first_offset_field); if (e) return e; if (ptr->traf) { gf_bs_write_u32(bs, (u32) ( pos - ptr->traf->moof_start_in_bs) ); } else { gf_bs_write_u32(bs, (u32) pos); } return gf_bs_seek(bs, pos); }
GF_Err meta_Read(GF_Box *s, GF_BitStream *bs) { u64 pos = gf_bs_get_position(bs); u64 size = s->size; GF_Err e = gf_isom_box_array_read(s, bs, meta_AddBox); /*try to hack around QT files which don't use a full box for meta, rewind 4 bytes*/ if (e && (pos>4) ) { gf_bs_seek(bs, pos-4); meta_reset(s); s->size = size+4; e = gf_isom_box_array_read(s, bs, meta_AddBox); } return e; }
GF_EXPORT GF_Err gf_isom_extract_meta_item_extended(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id, const char *dump_file_name, char **out_data, u32 *out_size, const char **out_mime ) { GF_BitStream *item_bs; char szPath[1024]; GF_ItemExtentEntry *extent_entry; FILE *resource = NULL; u32 i, count; GF_ItemLocationEntry *location_entry; u32 item_num; char *item_name = NULL; GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); if (!meta || !meta->item_infos || !meta->item_locations) return GF_BAD_PARAM; if (out_mime) *out_mime = NULL; item_num = gf_isom_get_meta_item_by_id(file, root_meta, track_num, item_id); if (item_num) { GF_ItemInfoEntryBox *item_entry = (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, item_num-1); item_name = item_entry->item_name; if (out_mime) *out_mime = item_entry->content_type; } location_entry = NULL; count = gf_list_count(meta->item_locations->location_entries); for (i=0; i<count; i++) { location_entry = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i); if (location_entry->item_ID == item_id) break; location_entry = NULL; } if (!location_entry) return GF_BAD_PARAM; /*FIXME*/ if (location_entry->data_reference_index) { char *item_url = NULL, *item_urn = NULL; GF_Box *a = (GF_Box *)gf_list_get(meta->file_locations->dref->other_boxes, location_entry->data_reference_index-1); if (a->type==GF_ISOM_BOX_TYPE_URL) { item_url = ((GF_DataEntryURLBox*)a)->location; } else if (a->type==GF_ISOM_BOX_TYPE_URN) { item_url = ((GF_DataEntryURNBox*)a)->location; item_urn = ((GF_DataEntryURNBox*)a)->nameURN; } GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[IsoMedia] Item already outside the ISO file at URL: %s, URN: %s\n", (item_url?item_url:"N/A"), (item_urn?item_urn:"N/A") )); return GF_OK; } /*don't extract self-reference item*/ count = gf_list_count(location_entry->extent_entries); if (!location_entry->base_offset && (count==1)) { extent_entry = (GF_ItemExtentEntry *)gf_list_get(location_entry->extent_entries, 0); if (!extent_entry->extent_length #ifndef GPAC_DISABLE_ISOM_WRITE && !extent_entry->original_extent_offset #endif ) return GF_BAD_PARAM; } item_bs = NULL; if (out_data) { item_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); } else if (dump_file_name) { strcpy(szPath, dump_file_name); resource = gf_fopen(szPath, "wb"); item_bs = gf_bs_from_file(resource, GF_BITSTREAM_WRITE); } else { if (item_name) strcpy(szPath, item_name); else sprintf(szPath, "item_id%02d", item_id); resource = gf_fopen(szPath, "wb"); item_bs = gf_bs_from_file(resource, GF_BITSTREAM_WRITE); } for (i=0; i<count; i++) { char buf_cache[4096]; u64 remain; GF_ItemExtentEntry *extent_entry = (GF_ItemExtentEntry *)gf_list_get(location_entry->extent_entries, i); gf_bs_seek(file->movieFileMap->bs, location_entry->base_offset + extent_entry->extent_offset); remain = extent_entry->extent_length; while (remain) { u32 cache_size = (remain>4096) ? 4096 : (u32) remain; gf_bs_read_data(file->movieFileMap->bs, buf_cache, cache_size); gf_bs_write_data(item_bs, buf_cache, cache_size); remain -= cache_size; } } if (out_data) { gf_bs_get_content(item_bs, out_data, out_size); } if (resource) { gf_fclose(resource); } gf_bs_del(item_bs); return GF_OK; }
/** * A function which takes FFmpeg H264 extradata (SPS/PPS) and bring them ready to be pushed to the MP4 muxer. * @param extradata * @param extradata_size * @param dstcfg * @returns GF_OK is the extradata was parsed and is valid, other values otherwise. */ static GF_Err avc_import_ffextradata(const u8 *extradata, const u64 extradata_size, GF_AVCConfig *dstcfg) { #ifdef GPAC_DISABLE_AV_PARSERS return GF_OK; #else u8 nal_size; AVCState avc; GF_BitStream *bs; if (!extradata || (extradata_size < sizeof(u32))) return GF_BAD_PARAM; bs = gf_bs_new(extradata, extradata_size, GF_BITSTREAM_READ); if (!bs) return GF_BAD_PARAM; if (gf_bs_read_u32(bs) != 0x00000001) { gf_bs_del(bs); return GF_BAD_PARAM; } //SPS { s32 idx; char *buffer = NULL; const u64 nal_start = 4; nal_size = gf_media_nalu_next_start_code_bs(bs); if (nal_start + nal_size > extradata_size) { gf_bs_del(bs); return GF_BAD_PARAM; } buffer = (char*)gf_malloc(nal_size); gf_bs_read_data(bs, buffer, nal_size); gf_bs_seek(bs, nal_start); if ((gf_bs_read_u8(bs) & 0x1F) != GF_AVC_NALU_SEQ_PARAM) { gf_bs_del(bs); gf_free(buffer); return GF_BAD_PARAM; } idx = gf_media_avc_read_sps(buffer, nal_size, &avc, 0, NULL); if (idx < 0) { gf_bs_del(bs); gf_free(buffer); return GF_BAD_PARAM; } dstcfg->configurationVersion = 1; dstcfg->profile_compatibility = avc.sps[idx].prof_compat; dstcfg->AVCProfileIndication = avc.sps[idx].profile_idc; dstcfg->AVCLevelIndication = avc.sps[idx].level_idc; dstcfg->chroma_format = avc.sps[idx].chroma_format; dstcfg->luma_bit_depth = 8 + avc.sps[idx].luma_bit_depth_m8; dstcfg->chroma_bit_depth = 8 + avc.sps[idx].chroma_bit_depth_m8; { GF_AVCConfigSlot *slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); slc->size = nal_size; slc->id = idx; slc->data = buffer; gf_list_add(dstcfg->sequenceParameterSets, slc); } } //PPS { s32 idx; char *buffer = NULL; const u64 nal_start = 4 + nal_size + 4; gf_bs_seek(bs, nal_start); nal_size = gf_media_nalu_next_start_code_bs(bs); if (nal_start + nal_size > extradata_size) { gf_bs_del(bs); return GF_BAD_PARAM; } buffer = (char*)gf_malloc(nal_size); gf_bs_read_data(bs, buffer, nal_size); gf_bs_seek(bs, nal_start); if ((gf_bs_read_u8(bs) & 0x1F) != GF_AVC_NALU_PIC_PARAM) { gf_bs_del(bs); gf_free(buffer); return GF_BAD_PARAM; } idx = gf_media_avc_read_pps(buffer, nal_size, &avc); if (idx < 0) { gf_bs_del(bs); gf_free(buffer); return GF_BAD_PARAM; } { GF_AVCConfigSlot *slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); slc->size = nal_size; slc->id = idx; slc->data = buffer; gf_list_add(dstcfg->pictureParameterSets, slc); } } gf_bs_del(bs); return GF_OK; #endif }
void RS_Deinterleaver(GF_BitStream *bs, char *out_name) { u8 rs0[204], rs1[204], rs2[204], rs3[204], rs4[204], rs5[204], rs6[204], rs7[204], rs8[204], rs9[204], rs10[204], rs11[204]; u8 *rs[12] = { rs0, rs1, rs2, rs3, rs4, rs5, rs6, rs7, rs8, rs9, rs10, rs11 }; u8 *tmp; u8 buf[204]; u32 i; u64 bs_data; u32 k = 0; memset(rs[0], 0, 204); memset(rs[1], 0, 204); memset(rs[2], 0, 204); memset(rs[3], 0, 204); memset(rs[4], 0, 204); memset(rs[5], 0, 204); memset(rs[6], 0, 204); memset(rs[7], 0, 204); memset(rs[8], 0, 204); memset(rs[9], 0, 204); memset(rs[10], 0, 204); memset(rs[11], 0, 204); bs_data = gf_bs_available(bs); while (bs_data > 204) { u64 pos; k++; // printf("TS Packet Number: %d\r", k); pos = gf_bs_get_position(bs); gf_bs_read_data(bs, buf, 204); bs_data = gf_bs_available(bs); while ((buf[0] != 0x47) && (bs_data > 0)) { printf("error in input TS %d\n", k); //return; pos++; gf_bs_seek(bs, pos); gf_bs_read_data(bs, buf, 204); bs_data = gf_bs_available(bs); } for (i=0; i<(17*12); i+=12) { // 1 paquet rs[0][i] = buf[i]; rs[1][i+1] = buf[i+1]; rs[2][i+2] = buf[i+2]; rs[3][i+3] = buf[i+3]; rs[4][i+4] = buf[i+4]; rs[5][i+5] = buf[i+5]; rs[6][i+6] = buf[i+6]; rs[7][i+7] = buf[i+7]; rs[8][i+8] = buf[i+8]; rs[9][i+9] = buf[i+9]; rs[10][i+10] = buf[i+10]; rs[11][i+11] = buf[i+11]; } if (k >= 12) { if (rs[11][0] != 0x47) { printf("error in output TS\n"); } else { save_ts(out_name, rs[11]); } } tmp = rs[11]; rs[11] = rs[10]; rs[10] = rs[9]; rs[9] = rs[8]; rs[8] = rs[7]; rs[7] = rs[6]; rs[6] = rs[5]; rs[5] = rs[4]; rs[4] = rs[3]; rs[3] = rs[2]; rs[2] = rs[1]; rs[1] = rs[0]; rs[0] = tmp; } }
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); }
//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 DoWriteMeta(GF_ISOFile *file, GF_MetaBox *meta, GF_BitStream *bs, Bool Emulation, u64 baseOffset, u64 *mdatSize) { GF_ItemExtentEntry *entry; u64 maxExtendOffset, maxExtendSize; u32 i, j, count; maxExtendOffset = 0; maxExtendSize = 0; *mdatSize = 0; if (!meta->item_locations) return GF_OK; count = gf_list_count(meta->item_locations->location_entries); for (i=0; i<count; i++) { u64 it_size; GF_ItemLocationEntry *iloc = (GF_ItemLocationEntry *)gf_list_get(meta->item_locations->location_entries, i); /*get item info*/ GF_ItemInfoEntryBox *iinf = NULL; j=0; while ((iinf = (GF_ItemInfoEntryBox *)gf_list_enum(meta->item_infos->item_infos, &j))) { if (iinf->item_ID==iloc->item_ID) break; iinf = NULL; } if (!iloc->base_offset && (gf_list_count(iloc->extent_entries)==1)) { entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0); if (!entry->extent_length && !entry->original_extent_offset) { entry->extent_offset = 0; continue; } } it_size = 0; /*for self contained only*/ if (!iloc->data_reference_index) { iloc->base_offset = baseOffset; /*new resource*/ if (iinf->full_path) { FILE *src=NULL; if (!iinf->data_len) { src = gf_f64_open(iinf->full_path, "rb"); if (!src) continue; gf_f64_seek(src, 0, SEEK_END); it_size = gf_f64_tell(src); gf_f64_seek(src, 0, SEEK_SET); } else { it_size = iinf->data_len; } if (maxExtendSize<it_size) maxExtendSize = it_size; if (!gf_list_count(iloc->extent_entries)) { GF_SAFEALLOC(entry, GF_ItemExtentEntry); gf_list_add(iloc->extent_entries, entry); } entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0); entry->extent_offset = 0; entry->extent_length = it_size; /*OK write to mdat*/ if (!Emulation) { if (src) { char cache_data[4096]; u64 remain = entry->extent_length; while (remain) { u32 size_cache = (remain>4096) ? 4096 : (u32) remain; size_cache = fread(cache_data, sizeof(char), size_cache, src); gf_bs_write_data(bs, cache_data, size_cache); remain -= size_cache; } } else { gf_bs_write_data(bs, iinf->full_path, iinf->data_len); } } if (src) fclose(src); } else if (gf_list_count(iloc->extent_entries)) { u32 j; j=0; while ((entry = (GF_ItemExtentEntry *)gf_list_enum(iloc->extent_entries, &j))) { if (j && (maxExtendOffset<it_size) ) maxExtendOffset = it_size; /*compute new offset*/ entry->extent_offset = baseOffset + it_size; it_size += entry->extent_length; if (maxExtendSize<entry->extent_length) maxExtendSize = entry->extent_length; /*Reading from the input file*/ if (!Emulation) { char cache_data[4096]; u64 remain = entry->extent_length; gf_bs_seek(file->movieFileMap->bs, entry->original_extent_offset + iloc->original_base_offset); while (remain) { u32 size_cache = (remain>4096) ? 4096 : (u32) remain; gf_bs_read_data(file->movieFileMap->bs, cache_data, size_cache); /*Writing to the output file*/ gf_bs_write_data(bs, cache_data, size_cache); remain -= size_cache; } } } } baseOffset += it_size; *mdatSize += it_size; } else { /*we MUST have at least one extent for the dref data*/ if (!gf_list_count(iloc->extent_entries)) { GF_SAFEALLOC(entry, GF_ItemExtentEntry); gf_list_add(iloc->extent_entries, entry); } entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0); entry->extent_offset = 0; /*0 means full length of referenced file*/ entry->extent_length = 0; } } /*update offset & size length fields*/ if (baseOffset>0xFFFFFFFF) meta->item_locations->base_offset_size = 8; else if (baseOffset) meta->item_locations->base_offset_size = 4; if (maxExtendSize>0xFFFFFFFF) meta->item_locations->length_size = 8; else if (maxExtendSize) meta->item_locations->length_size = 4; if (maxExtendOffset>0xFFFFFFFF) meta->item_locations->offset_size = 8; else if (maxExtendOffset) meta->item_locations->offset_size = 4; return GF_OK; }
/** * A function which takes FFmpeg H265 extradata (SPS/PPS) and bring them ready to be pushed to the MP4 muxer. * @param extradata * @param extradata_size * @param dstcfg * @returns GF_OK is the extradata was parsed and is valid, other values otherwise. */ static GF_Err hevc_import_ffextradata(const u8 *extradata, const u64 extradata_size, GF_HEVCConfig *dst_cfg) { #ifdef GPAC_DISABLE_AV_PARSERS return GF_OK; #else HEVCState hevc; GF_HEVCParamArray *vpss = NULL, *spss = NULL, *ppss = NULL; GF_BitStream *bs; char *buffer = NULL; u32 buffer_size = 0; if (!extradata || (extradata_size < sizeof(u32))) return GF_BAD_PARAM; bs = gf_bs_new(extradata, extradata_size, GF_BITSTREAM_READ); if (!bs) return GF_BAD_PARAM; memset(&hevc, 0, sizeof(HEVCState)); hevc.sps_active_idx = -1; while (gf_bs_available(bs)) { s32 idx; GF_AVCConfigSlot *slc; u8 nal_unit_type, temporal_id, layer_id; u64 nal_start; u32 nal_size; if (gf_bs_read_u32(bs) != 0x00000001) { gf_bs_del(bs); return GF_BAD_PARAM; } nal_start = gf_bs_get_position(bs); nal_size = gf_media_nalu_next_start_code_bs(bs); if (nal_start + nal_size > extradata_size) { gf_bs_del(bs); return GF_BAD_PARAM; } if (nal_size > buffer_size) { buffer = (char*)gf_realloc(buffer, nal_size); buffer_size = nal_size; } gf_bs_read_data(bs, buffer, nal_size); gf_bs_seek(bs, nal_start); gf_media_hevc_parse_nalu(bs, &hevc, &nal_unit_type, &temporal_id, &layer_id); if (layer_id) { gf_bs_del(bs); gf_free(buffer); return GF_BAD_PARAM; } switch (nal_unit_type) { case GF_HEVC_NALU_VID_PARAM: idx = gf_media_hevc_read_vps(buffer, nal_size , &hevc); if (idx < 0) { gf_bs_del(bs); gf_free(buffer); return GF_BAD_PARAM; } assert(hevc.vps[idx].state == 1); //we don't expect multiple VPS if (hevc.vps[idx].state == 1) { hevc.vps[idx].state = 2; hevc.vps[idx].crc = gf_crc_32(buffer, nal_size); dst_cfg->avgFrameRate = hevc.vps[idx].rates[0].avg_pic_rate; dst_cfg->constantFrameRate = hevc.vps[idx].rates[0].constand_pic_rate_idc; dst_cfg->numTemporalLayers = hevc.vps[idx].max_sub_layers; dst_cfg->temporalIdNested = hevc.vps[idx].temporal_id_nesting; if (!vpss) { GF_SAFEALLOC(vpss, GF_HEVCParamArray); vpss->nalus = gf_list_new(); gf_list_add(dst_cfg->param_array, vpss); vpss->array_completeness = 1; vpss->type = GF_HEVC_NALU_VID_PARAM; } slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); slc->size = nal_size; slc->id = idx; slc->data = (char*)gf_malloc(sizeof(char)*slc->size); memcpy(slc->data, buffer, sizeof(char)*slc->size); gf_list_add(vpss->nalus, slc); } break; case GF_HEVC_NALU_SEQ_PARAM: idx = gf_media_hevc_read_sps(buffer, nal_size, &hevc); if (idx < 0) { gf_bs_del(bs); gf_free(buffer); return GF_BAD_PARAM; } assert(!(hevc.sps[idx].state & AVC_SPS_DECLARED)); //we don't expect multiple SPS if ((hevc.sps[idx].state & AVC_SPS_PARSED) && !(hevc.sps[idx].state & AVC_SPS_DECLARED)) { hevc.sps[idx].state |= AVC_SPS_DECLARED; hevc.sps[idx].crc = gf_crc_32(buffer, nal_size); } dst_cfg->configurationVersion = 1; dst_cfg->profile_space = hevc.sps[idx].ptl.profile_space; dst_cfg->tier_flag = hevc.sps[idx].ptl.tier_flag; dst_cfg->profile_idc = hevc.sps[idx].ptl.profile_idc; dst_cfg->general_profile_compatibility_flags = hevc.sps[idx].ptl.profile_compatibility_flag; dst_cfg->progressive_source_flag = hevc.sps[idx].ptl.general_progressive_source_flag; dst_cfg->interlaced_source_flag = hevc.sps[idx].ptl.general_interlaced_source_flag; dst_cfg->non_packed_constraint_flag = hevc.sps[idx].ptl.general_non_packed_constraint_flag; dst_cfg->frame_only_constraint_flag = hevc.sps[idx].ptl.general_frame_only_constraint_flag; dst_cfg->constraint_indicator_flags = hevc.sps[idx].ptl.general_reserved_44bits; dst_cfg->level_idc = hevc.sps[idx].ptl.level_idc; dst_cfg->chromaFormat = hevc.sps[idx].chroma_format_idc; dst_cfg->luma_bit_depth = hevc.sps[idx].bit_depth_luma; dst_cfg->chroma_bit_depth = hevc.sps[idx].bit_depth_chroma; if (!spss) { GF_SAFEALLOC(spss, GF_HEVCParamArray); spss->nalus = gf_list_new(); gf_list_add(dst_cfg->param_array, spss); spss->array_completeness = 1; spss->type = GF_HEVC_NALU_SEQ_PARAM; } slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); slc->size = nal_size; slc->id = idx; slc->data = (char*)gf_malloc(sizeof(char)*slc->size); memcpy(slc->data, buffer, sizeof(char)*slc->size); gf_list_add(spss->nalus, slc); break; case GF_HEVC_NALU_PIC_PARAM: idx = gf_media_hevc_read_pps(buffer, nal_size, &hevc); if (idx < 0) { gf_bs_del(bs); gf_free(buffer); return GF_BAD_PARAM; } assert(hevc.pps[idx].state == 1); //we don't expect multiple PPS if (hevc.pps[idx].state == 1) { hevc.pps[idx].state = 2; hevc.pps[idx].crc = gf_crc_32(buffer, nal_size); if (!ppss) { GF_SAFEALLOC(ppss, GF_HEVCParamArray); ppss->nalus = gf_list_new(); gf_list_add(dst_cfg->param_array, ppss); ppss->array_completeness = 1; ppss->type = GF_HEVC_NALU_PIC_PARAM; } slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); slc->size = nal_size; slc->id = idx; slc->data = (char*)gf_malloc(sizeof(char)*slc->size); memcpy(slc->data, buffer, sizeof(char)*slc->size); gf_list_add(ppss->nalus, slc); } break; default: break; } if (gf_bs_seek(bs, nal_start+nal_size)) { assert(nal_start+nal_size <= gf_bs_get_size(bs)); break; } } gf_bs_del(bs); gf_free(buffer); return GF_OK; #endif }
GF_Err gp_rtp_builder_do_mpeg4(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize) { char *sl_buffer, *payl_buffer; u32 sl_buffer_size, payl_buffer_size; u32 auh_size_tmp, bytesLeftInPacket, infoSize, pckSize; u64 pos; u8 flush_pck, no_split; flush_pck = 0; bytesLeftInPacket = data_size; /*flush everything*/ if (!data) { if (builder->payload) goto flush_packet; return GF_OK; } if (builder->payload && builder->force_flush) goto flush_packet; //go till done while (bytesLeftInPacket) { no_split = 0; if (builder->sl_header.accessUnitStartFlag) { //init SL if (builder->sl_header.compositionTimeStamp != builder->sl_header.decodingTimeStamp) { builder->sl_header.decodingTimeStampFlag = 1; } builder->sl_header.compositionTimeStampFlag = 1; builder->sl_header.accessUnitLength = FullAUSize; //init some vars - based on the available size and the TS //we decide if we go with the same RTP TS serie or not if (builder->payload) { //don't store more than what we can (that is 2^slMap->CTSDelta - 1) if ( (builder->flags & GP_RTP_PCK_SIGNAL_TS) && (builder->sl_header.compositionTimeStamp - builder->rtp_header.TimeStamp >= (u32) ( 1 << builder->slMap.CTSDeltaLength) ) ) { goto flush_packet; } //don't split AU if # TS , start a new RTP pck if (builder->sl_header.compositionTimeStamp != builder->rtp_header.TimeStamp) no_split = 1; } } /*new RTP Packet*/ if (!builder->payload) { /*first SL in RTP*/ builder->first_sl_in_rtp = 1; /*if this is the end of an AU we will set it to 0 as soon as an AU is splited*/ builder->rtp_header.Marker = 1; builder->rtp_header.PayloadType = builder->PayloadType; builder->rtp_header.SequenceNumber += 1; builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp; /*prepare the mapped headers*/ builder->pck_hdr = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); builder->payload = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); pckSize = infoSize = 0; builder->bytesInPacket = 0; /*in multiSL there is a MSLHSize structure on 2 bytes*/ builder->auh_size = 0; if (builder->has_AU_header) { builder->auh_size = 16; gf_bs_write_int(builder->pck_hdr, 0, 16); } flush_pck = 0; /*and create packet*/ builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header); } //make sure we are not interleaving too much - this should never happen actually if (builder->slMap.IndexDeltaLength && !builder->first_sl_in_rtp && (builder->sl_header.AU_sequenceNumber - builder->last_au_sn >= (u32) 1<<builder->slMap.IndexDeltaLength)) { //we cannot write this packet here goto flush_packet; } /*check max ptime*/ if (builder->max_ptime && ( (u32) builder->sl_header.compositionTimeStamp >= builder->rtp_header.TimeStamp + builder->max_ptime) ) goto flush_packet; auh_size_tmp = gf_rtp_build_au_hdr_size(builder, &builder->sl_header); infoSize = auh_size_tmp + builder->auh_size; infoSize /= 8; /*align*/ if ( (builder->auh_size + auh_size_tmp) % 8) infoSize += 1; if (bytesLeftInPacket + infoSize + builder->bytesInPacket <= builder->Path_MTU) { //End of our data chunk pckSize = bytesLeftInPacket; builder->sl_header.accessUnitEndFlag = IsAUEnd; builder->auh_size += auh_size_tmp; builder->sl_header.paddingFlag = builder->sl_header.paddingBits ? 1 : 0; } else { //AU cannot fit in packet. If no split, start a new packet if (no_split) goto flush_packet; builder->auh_size += auh_size_tmp; pckSize = builder->Path_MTU - (infoSize + builder->bytesInPacket); //that's the end of the rtp packet flush_pck = 1; //but not of the AU -> marker is 0 builder->rtp_header.Marker = 0; } gf_rtp_build_au_hdr_write(builder, pckSize, builder->rtp_header.TimeStamp); //notify the user of our data structure if (builder->OnDataReference) builder->OnDataReference(builder->cbk_obj, pckSize, data_size - bytesLeftInPacket); else gf_bs_write_data(builder->payload, data + (data_size - bytesLeftInPacket), pckSize); bytesLeftInPacket -= pckSize; builder->bytesInPacket += pckSize; /*update IV*/ builder->IV += pckSize; builder->sl_header.paddingFlag = 0; builder->sl_header.accessUnitStartFlag = 0; //we are splitting a payload, auto increment SL seq num if (bytesLeftInPacket) { builder->sl_header.packetSequenceNumber += 1; } else if (! (builder->flags & GP_RTP_PCK_USE_MULTI) ) { builder->rtp_header.Marker = 1; flush_pck = 1; } //first SL in RTP is done builder->first_sl_in_rtp = 0; //store current sl builder->last_au_sn = builder->sl_header.AU_sequenceNumber; if (!flush_pck) continue; //done with the packet flush_packet: gf_bs_align(builder->pck_hdr); /*no aux data yet*/ if (builder->slMap.AuxiliaryDataSizeLength) { //write RSLH after the MSLH gf_bs_write_int(builder->pck_hdr, 0, builder->slMap.AuxiliaryDataSizeLength); } /*rewrite the size header*/ if (builder->has_AU_header) { pos = gf_bs_get_position(builder->pck_hdr); gf_bs_seek(builder->pck_hdr, 0); builder->auh_size -= 16; gf_bs_write_int(builder->pck_hdr, builder->auh_size, 16); gf_bs_seek(builder->pck_hdr, pos); } sl_buffer = NULL; gf_bs_get_content(builder->pck_hdr, &sl_buffer, &sl_buffer_size); //delete our bitstream gf_bs_del(builder->pck_hdr); builder->pck_hdr = NULL; payl_buffer = NULL; payl_buffer_size = 0; if (!builder->OnDataReference) gf_bs_get_content(builder->payload, &payl_buffer, &payl_buffer_size); gf_bs_del(builder->payload); builder->payload = NULL; /*notify header*/ builder->OnData(builder->cbk_obj, sl_buffer, sl_buffer_size, 1); /*notify payload*/ if (payl_buffer) { builder->OnData(builder->cbk_obj, payl_buffer, payl_buffer_size, 0); gf_free(payl_buffer); } /*flush packet*/ builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header); gf_free(sl_buffer); } //packet is done, update AU markers if (IsAUEnd) { builder->sl_header.accessUnitStartFlag = 1; builder->sl_header.accessUnitEndFlag = 0; } return GF_OK; }
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 /*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); #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) { return GF_ISOM_INVALID_FILE; } return e; } else { return e; } switch (a->type) { /*MOOV box*/ case GF_ISOM_BOX_TYPE_MOOV: if (mov->moov) 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) 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); 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); 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: if (((GF_SegmentTypeBox *)a)->majorBrand == GF_4CC('i', 's', 's', 's') || ((GF_SegmentTypeBox *)a)->majorBrand == GF_4CC('i', 'm', 's', 's')) mov->is_index_segment = 1; 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: ((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) { gf_list_add(mov->TopBoxes, a); } 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); if (e) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Error merging fragment: %s\n", gf_error_to_string(e) )); } } break; #endif case GF_4CC('j','P',' ',' '): { GF_UnknownBox *box = (GF_UnknownBox*)a; u8 *c = 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; default: totSize += a->size; e = gf_list_add(mov->TopBoxes, a); break; } #ifndef GPAC_DISABLE_ISOM_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_INVALID_FILE; /*we MUST have movie header*/ if (mov->moov && !mov->moov->mvhd) return GF_ISOM_INVALID_FILE; /*we MUST have meta handler*/ if (mov->meta && !mov->meta->handler) 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 } #endif /*GPAC_DISABLE_ISOM_WRITE*/ return GF_OK; }
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; }
/* Rewrite mode: * mode = 0: playback * mode = 1: streaming */ GF_Err gf_isom_nalu_sample_rewrite(GF_MediaBox *mdia, GF_ISOSample *sample, u32 sampleNumber, GF_MPEGVisualSampleEntryBox *entry) { Bool is_hevc = 0; GF_Err e = GF_OK; GF_ISOSample *ref_samp; GF_BitStream *src_bs, *ref_bs, *dst_bs; u64 offset; u32 ref_nalu_size, data_offset, data_length, copy_size, nal_size, max_size, di, nal_unit_size_field, cur_extract_mode, extractor_mode; Bool rewrite_ps, rewrite_start_codes; u8 ref_track_ID, ref_track_num; s8 sample_offset, nal_type; u32 nal_hdr; char *buffer; GF_ISOFile *file = mdia->mediaTrack->moov->mov; src_bs = ref_bs = dst_bs = NULL; ref_samp = NULL; buffer = NULL; rewrite_ps = (mdia->mediaTrack->extractor_mode & GF_ISOM_NALU_EXTRACT_INBAND_PS_FLAG) ? 1 : 0; if (! sample->IsRAP) rewrite_ps = 0; rewrite_start_codes = (mdia->mediaTrack->extractor_mode & GF_ISOM_NALU_EXTRACT_ANNEXB_FLAG) ? 1 : 0; extractor_mode = mdia->mediaTrack->extractor_mode&0x0000FFFF; if (extractor_mode == GF_ISOM_NALU_EXTRACT_INSPECT) { if (!rewrite_ps && !rewrite_start_codes) return GF_OK; } if (!entry) return GF_BAD_PARAM; nal_unit_size_field = 0; /*if svc rewrire*/ if (entry->svc_config && entry->svc_config->config) nal_unit_size_field = entry->svc_config->config->nal_unit_size; /*if mvc rewrire*/ /*otherwise do nothing*/ else if (!rewrite_ps && !rewrite_start_codes) { return GF_OK; } if (!nal_unit_size_field) { if (entry->avc_config) nal_unit_size_field = entry->avc_config->config->nal_unit_size; else if (entry->hevc_config) { nal_unit_size_field = entry->hevc_config->config->nal_unit_size; is_hevc = 1; } } if (!nal_unit_size_field) return GF_ISOM_INVALID_FILE; dst_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); src_bs = gf_bs_new(sample->data, sample->dataLength, GF_BITSTREAM_READ); max_size = 4096; /*rewrite start code with NALU delim*/ if (rewrite_start_codes) { gf_bs_write_int(dst_bs, 1, 32); if (is_hevc) { gf_bs_write_int(dst_bs, 0, 1); gf_bs_write_int(dst_bs, GF_HEVC_NALU_ACCESS_UNIT, 6); gf_bs_write_int(dst_bs, 0, 9); /*pic-type - by default we signal all slice types possible*/ gf_bs_write_int(dst_bs, 2, 3); gf_bs_write_int(dst_bs, 0, 5); } else { gf_bs_write_int(dst_bs, (sample->data[0] & 0x60) | GF_AVC_NALU_ACCESS_UNIT, 8); gf_bs_write_int(dst_bs, 0xF0 , 8); /*7 "all supported NALUs" (=111) + rbsp trailing (10000)*/; } } if (rewrite_ps) { if (is_hevc) { u32 i, count; count = gf_list_count(entry->hevc_config->config->param_array); for (i=0; i<count; i++) { GF_HEVCParamArray *ar = gf_list_get(entry->hevc_config->config->param_array, i); rewrite_nalus_list(ar->nalus, dst_bs, rewrite_start_codes, nal_unit_size_field); } /*little optimization if we are not asked to start codes: copy over the sample*/ if (!rewrite_start_codes) { gf_bs_write_data(dst_bs, sample->data, sample->dataLength); gf_free(sample->data); sample->data = NULL; gf_bs_get_content(dst_bs, &sample->data, &sample->dataLength); gf_bs_del(src_bs); gf_bs_del(dst_bs); return GF_OK; } } else { /*this is an SVC track: get all SPS/PPS from this track down to the base layer and rewrite them*/ if (mdia->mediaTrack->has_base_layer) { u32 j; GF_List *nalu_sps = gf_list_new(); GF_List *nalu_pps = gf_list_new(); GF_TrackReferenceTypeBox *dpnd = NULL; Track_FindRef(mdia->mediaTrack, GF_ISOM_REF_SCAL, &dpnd); #if 0 /*get all upper layers with SCAL reference to this track*/ for (j = 0; j < gf_isom_get_track_count(file); j++) { if (gf_isom_has_track_reference(file, j+1, GF_ISOM_REF_SCAL, mdia->mediaTrack->Header->trackID)) { u32 tkID; GF_TrackBox *base_track; GF_MPEGVisualSampleEntryBox *base_entry; gf_isom_get_reference_ID(file, j+1, GF_ISOM_REF_SCAL, 1, &tkID); base_track = GetTrackbyID(mdia->mediaTrack->moov, tkID); base_entry = base_track ? gf_list_get(base_track->Media->information->sampleTable->SampleDescription->other_boxes, 0) : NULL; if (base_entry) merge_nalus(base_entry, nalu_sps, nalu_pps); } } #endif merge_nalus(entry, nalu_sps, nalu_pps); if (dpnd) { for (j=0; j<dpnd->trackIDCount; j++) { GF_TrackBox *base_track = GetTrackbyID(mdia->mediaTrack->moov, dpnd->trackIDs[j]); GF_MPEGVisualSampleEntryBox *base_entry = base_track ? gf_list_get(base_track->Media->information->sampleTable->SampleDescription->other_boxes, 0) : NULL; if (base_entry) merge_nalus(base_entry, nalu_sps, nalu_pps); } } //rewrite nalus rewrite_nalus_list(nalu_sps, dst_bs, rewrite_start_codes, nal_unit_size_field); rewrite_nalus_list(nalu_pps, dst_bs, rewrite_start_codes, nal_unit_size_field); gf_list_del(nalu_sps); gf_list_del(nalu_pps); } else { if (entry->avc_config) { rewrite_nalus_list(entry->avc_config->config->sequenceParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field); rewrite_nalus_list(entry->avc_config->config->pictureParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field); rewrite_nalus_list(entry->avc_config->config->sequenceParameterSetExtensions, dst_bs, rewrite_start_codes, nal_unit_size_field); } /*add svc config */ if (entry->svc_config) { rewrite_nalus_list(entry->svc_config->config->sequenceParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field); rewrite_nalus_list(entry->svc_config->config->pictureParameterSets, dst_bs, rewrite_start_codes, nal_unit_size_field); } /*little optimization if we are not asked to rewrite extractors or start codes: copy over the sample*/ if (!entry->svc_config && !rewrite_start_codes) { gf_bs_write_data(dst_bs, sample->data, sample->dataLength); gf_free(sample->data); sample->data = NULL; gf_bs_get_content(dst_bs, &sample->data, &sample->dataLength); gf_bs_del(src_bs); gf_bs_del(dst_bs); return GF_OK; } } } } buffer = (char *)gf_malloc(sizeof(char)*max_size); while (gf_bs_available(src_bs)) { nal_size = gf_bs_read_int(src_bs, 8*nal_unit_size_field); if (nal_size>max_size) { buffer = (char*) gf_realloc(buffer, sizeof(char)*nal_size); max_size = nal_size; } if (is_hevc) { nal_hdr = gf_bs_read_u16(src_bs); nal_type = (nal_hdr&0x7E00) >> 9; } else { nal_hdr = gf_bs_read_u8(src_bs); nal_type = nal_hdr & 0x1F; } if (is_hevc) { /*we already wrote this stuff*/ if (nal_type==GF_HEVC_NALU_ACCESS_UNIT) { gf_bs_skip_bytes(src_bs, nal_size-2); continue; } /*rewrite nal*/ gf_bs_read_data(src_bs, buffer, nal_size-2); if (rewrite_start_codes) gf_bs_write_u32(dst_bs, 1); else gf_bs_write_int(dst_bs, nal_size, 8*nal_unit_size_field); gf_bs_write_u16(dst_bs, nal_hdr); gf_bs_write_data(dst_bs, buffer, nal_size-2); continue; } /*we already wrote this stuff*/ if (nal_type==GF_AVC_NALU_ACCESS_UNIT) { gf_bs_skip_bytes(src_bs, nal_size-1); continue; } //extractor if (nal_type == 31) { switch (extractor_mode) { case 0: gf_bs_read_int(src_bs, 24); //3 bytes of NALUHeader in extractor ref_track_ID = gf_bs_read_u8(src_bs); sample_offset = (s8) gf_bs_read_int(src_bs, 8); data_offset = gf_bs_read_u32(src_bs); data_length = gf_bs_read_u32(src_bs); ref_track_num = gf_isom_get_track_by_id(file, ref_track_ID); if (!ref_track_num) { e = GF_BAD_PARAM; goto exit; } cur_extract_mode = gf_isom_get_nalu_extract_mode(file, ref_track_num); gf_isom_set_nalu_extract_mode(file, ref_track_num, GF_ISOM_NALU_EXTRACT_INSPECT); ref_samp = gf_isom_get_sample(file, ref_track_num, sampleNumber+sample_offset, &di); if (!ref_samp) { e = GF_IO_ERR; goto exit; } ref_bs = gf_bs_new(ref_samp->data, ref_samp->dataLength, GF_BITSTREAM_READ); offset = 0; while (gf_bs_available(ref_bs)) { if (gf_bs_get_position(ref_bs) < data_offset) { ref_nalu_size = gf_bs_read_int(ref_bs, 8*nal_unit_size_field); offset += ref_nalu_size + nal_unit_size_field; if ((offset > data_offset) || (offset >= gf_bs_get_size(ref_bs))) { e = GF_BAD_PARAM; goto exit; } e = gf_bs_seek(ref_bs, offset); if (e) goto exit; continue; } ref_nalu_size = gf_bs_read_int(ref_bs, 8*nal_unit_size_field); copy_size = data_length ? data_length : ref_nalu_size; assert(copy_size <= ref_nalu_size); nal_hdr = gf_bs_read_u8(ref_bs); //rewrite NAL type if ((copy_size-1)>max_size) { buffer = (char*)gf_realloc(buffer, sizeof(char)*(copy_size-1)); max_size = copy_size-1; } gf_bs_read_data(ref_bs, buffer, copy_size-1); if (rewrite_start_codes) gf_bs_write_u32(dst_bs, 1); else gf_bs_write_int(dst_bs, copy_size, 8*nal_unit_size_field); gf_bs_write_u8(dst_bs, nal_hdr); gf_bs_write_data(dst_bs, buffer, copy_size-1); } gf_isom_sample_del(&ref_samp); ref_samp = NULL; gf_bs_del(ref_bs); ref_bs = NULL; gf_isom_set_nalu_extract_mode(file, ref_track_num, cur_extract_mode); break; default: //skip to end of this NALU gf_bs_skip_bytes(src_bs, nal_size-1); continue; } } else { gf_bs_read_data(src_bs, buffer, nal_size-1); if (rewrite_start_codes) gf_bs_write_u32(dst_bs, 1); else gf_bs_write_int(dst_bs, nal_size, 8*nal_unit_size_field); gf_bs_write_u8(dst_bs, nal_hdr); gf_bs_write_data(dst_bs, buffer, nal_size-1); } }