/*special authoring functions*/ GF_EXPORT GF_BIFSConfig *gf_odf_get_bifs_config(GF_DefaultDescriptor *dsi, u8 oti) { Bool hasSize, cmd_stream; GF_BitStream *bs; GF_BIFSConfig *cfg; if (oti>=GPAC_OTI_SCENE_BIFS_EXTENDED) return NULL; if (!dsi || !dsi->data || !dsi->dataLength ) { /* Hack for T-DMB non compliant streams (OnTimeTek ?) */ cfg = (GF_BIFSConfig *) gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG); cfg->pixelMetrics = 1; cfg->version = 1; return cfg; } bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ); cfg = (GF_BIFSConfig *) gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG); if (oti==2) { /*3D Mesh Coding*/ gf_bs_read_int(bs, 1); /*PMF*/ gf_bs_read_int(bs, 1); } cfg->nodeIDbits = gf_bs_read_int(bs, 5); cfg->routeIDbits = gf_bs_read_int(bs, 5); if (oti==2) cfg->protoIDbits = gf_bs_read_int(bs, 5); cmd_stream = gf_bs_read_int(bs, 1); if (!cmd_stream) { cfg->elementaryMasks = gf_list_new(); while (1) { GF_ElementaryMask* em = (GF_ElementaryMask* ) gf_odf_New_ElemMask(); em->node_id = gf_bs_read_int(bs, cfg->nodeIDbits); gf_list_add(cfg->elementaryMasks, em); /*this assumes only FDP, BDP and IFS2D (no elem mask)*/ if (gf_bs_read_int(bs, 1) == 0) break; } gf_bs_align(bs); if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) { GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[ODF] Reading bifs config: shift in sizes (not supported)\n")); } } else { cfg->pixelMetrics = gf_bs_read_int(bs, 1); hasSize = gf_bs_read_int(bs, 1); if (hasSize) { cfg->pixelWidth = gf_bs_read_int(bs, 16); cfg->pixelHeight = gf_bs_read_int(bs, 16); } gf_bs_align(bs); if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[ODF] Reading bifs config: shift in sizes (invalid descriptor)\n")); } gf_bs_del(bs); return cfg; }
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; }
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; }
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; }
u64 FDM_GetTotalOffset(GF_FileDataMap *ptr) { if (!ptr) return 0; //the pos is not always at the end //this function is called to set up the chunks //so we need the next WRITE offset return gf_bs_get_size(ptr->bs); }
static GF_Err ParseConfig(GF_BitStream *bs, BIFSStreamInfo *info, u32 version) { Bool hasSize, cmd_stream; if (info->config.elementaryMasks) gf_list_del(info->config.elementaryMasks); info->config.elementaryMasks = NULL ; if (version==2) { info->config.Use3DMeshCoding = gf_bs_read_int(bs, 1); info->config.UsePredictiveMFField = gf_bs_read_int(bs, 1); } info->config.NodeIDBits = gf_bs_read_int(bs, 5); info->config.RouteIDBits = gf_bs_read_int(bs, 5); if (version==2) { info->config.ProtoIDBits = gf_bs_read_int(bs, 5); } cmd_stream = gf_bs_read_int(bs, 1); if (cmd_stream) { info->config.PixelMetrics = gf_bs_read_int(bs, 1); hasSize = gf_bs_read_int(bs, 1); if (hasSize) { info->config.Width = gf_bs_read_int(bs, 16); info->config.Height = gf_bs_read_int(bs, 16); } gf_bs_align(bs); if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) return GF_ODF_INVALID_DESCRIPTOR; return GF_OK; } else { info->config.BAnimRAP = gf_bs_read_int(bs, 1); info->config.elementaryMasks = gf_list_new(); while (1) { /*u32 node_id = */gf_bs_read_int(bs, info->config.NodeIDBits); /*this assumes only FDP, BDP and IFS2D (no elem mask)*/ if (gf_bs_read_int(bs, 1) == 0) break; } gf_bs_align(bs); if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) return GF_NOT_SUPPORTED; return GF_OK; } }
GF_Err Media_GetSample(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample **samp, u32 *sIDX, Bool no_data, u64 *out_offset) { GF_Err e; u32 bytesRead; u32 dataRefIndex, chunkNumber; u64 offset, new_size; u8 isEdited; GF_SampleEntryBox *entry; if (!mdia || !mdia->information->sampleTable) return GF_BAD_PARAM; //OK, here we go.... if (sampleNumber > mdia->information->sampleTable->SampleSize->sampleCount) return GF_BAD_PARAM; //get the DTS e = stbl_GetSampleDTS(mdia->information->sampleTable->TimeToSample, sampleNumber, &(*samp)->DTS); if (e) return e; //the CTS offset if (mdia->information->sampleTable->CompositionOffset) { e = stbl_GetSampleCTS(mdia->information->sampleTable->CompositionOffset , sampleNumber, &(*samp)->CTS_Offset); if (e) return e; } else { (*samp)->CTS_Offset = 0; } //the size e = stbl_GetSampleSize(mdia->information->sampleTable->SampleSize, sampleNumber, &(*samp)->dataLength); if (e) return e; //the RAP if (mdia->information->sampleTable->SyncSample) { e = stbl_GetSampleRAP(mdia->information->sampleTable->SyncSample, sampleNumber, &(*samp)->IsRAP, NULL, NULL); if (e) return e; } else { //if no SyncSample, all samples are sync (cf spec) (*samp)->IsRAP = 1; } /*overwrite sync sample with sample dep if any*/ if (mdia->information->sampleTable->SampleDep) { u32 dependsOn, dependedOn, redundant; e = stbl_GetSampleDepType(mdia->information->sampleTable->SampleDep, sampleNumber, &dependsOn, &dependedOn, &redundant); if (!e) { if (dependsOn==1) (*samp)->IsRAP = 0; else if (dependsOn==2) (*samp)->IsRAP = 1; /*if not depended upon and redundant, mark as carousel sample*/ if ((dependedOn==2) && (redundant==1)) (*samp)->IsRAP = 2; /*TODO FIXME - we must enhance the IsRAP semantics to carry disposable info ... */ } } /*get sync shadow*/ if (Media_IsSampleSyncShadow(mdia->information->sampleTable->ShadowSync, sampleNumber)) (*samp)->IsRAP = 2; //the data info if (!sIDX && !no_data) return GF_BAD_PARAM; if (!sIDX && !out_offset) return GF_OK; (*sIDX) = 0; e = stbl_GetSampleInfos(mdia->information->sampleTable, sampleNumber, &offset, &chunkNumber, sIDX, &isEdited); if (e) return e; //then get the DataRef e = Media_GetSampleDesc(mdia, *sIDX, &entry, &dataRefIndex); if (e) return e; // Open the data handler - check our mode, don't reopen in read only if this is //the same entry. In other modes we have no choice because the main data map is //divided into the original and the edition files if (mdia->mediaTrack->moov->mov->openMode == GF_ISOM_OPEN_READ) { //same as last call in read mode if (!mdia->information->dataHandler) { e = gf_isom_datamap_open(mdia, dataRefIndex, isEdited); if (e) return e; } if (mdia->information->dataEntryIndex != dataRefIndex) mdia->information->dataEntryIndex = dataRefIndex; } else { e = gf_isom_datamap_open(mdia, dataRefIndex, isEdited); if (e) return e; } if (out_offset) *out_offset = offset; if (no_data) return GF_OK; /*and finally get the data, include padding if needed*/ (*samp)->data = (char *) gf_malloc(sizeof(char) * ( (*samp)->dataLength + mdia->mediaTrack->padding_bytes) ); if (mdia->mediaTrack->padding_bytes) memset((*samp)->data + (*samp)->dataLength, 0, sizeof(char) * mdia->mediaTrack->padding_bytes); //check if we can get the sample (make sure we have enougth data...) new_size = gf_bs_get_size(mdia->information->dataHandler->bs); if (offset + (*samp)->dataLength > new_size) { //always refresh the size to avoid wrong info on http/ftp new_size = gf_bs_get_refreshed_size(mdia->information->dataHandler->bs); if (offset + (*samp)->dataLength > new_size) { mdia->BytesMissing = offset + (*samp)->dataLength - new_size; return GF_ISOM_INCOMPLETE_FILE; } } bytesRead = gf_isom_datamap_get_data(mdia->information->dataHandler, (*samp)->data, (*samp)->dataLength, offset); //if bytesRead != sampleSize, we have an IO err if (bytesRead < (*samp)->dataLength) { return GF_IO_ERR; } mdia->BytesMissing = 0; //finally rewrite the sample if this is an OD Access Unit if (mdia->handler->handlerType == GF_ISOM_MEDIA_OD) { e = Media_RewriteODFrame(mdia, *samp); if (e) return e; } /*FIXME: we don NOT rewrite sample if we have a encrypted track*/ else if (gf_isom_is_nalu_based_entry(mdia, entry) && !gf_isom_is_track_encrypted(mdia->mediaTrack->moov->mov, gf_isom_get_tracknum_from_id(mdia->mediaTrack->moov, mdia->mediaTrack->Header->trackID)) ) { e = gf_isom_nalu_sample_rewrite(mdia, *samp, sampleNumber, (GF_MPEGVisualSampleEntryBox *)entry); if (e) return e; } else if (mdia->mediaTrack->moov->mov->convert_streaming_text && ((mdia->handler->handlerType == GF_ISOM_MEDIA_TEXT) || (mdia->handler->handlerType == GF_ISOM_MEDIA_SUBT)) && (entry->type == GF_ISOM_BOX_TYPE_TX3G || entry->type == GF_ISOM_BOX_TYPE_TEXT) ) { u64 dur; if (sampleNumber == mdia->information->sampleTable->SampleSize->sampleCount) { dur = mdia->mediaHeader->duration - (*samp)->DTS; } else { stbl_GetSampleDTS(mdia->information->sampleTable->TimeToSample, sampleNumber+1, &dur); dur -= (*samp)->DTS; } e = gf_isom_rewrite_text_sample(*samp, *sIDX, (u32) dur); if (e) return e; } 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 }
/* 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); } }