Example #1
0
//WARNING: MOVIETIME IS EXPRESSED IN MEDIA TS
GF_Err GetMediaTime(GF_TrackBox *trak, Bool force_non_empty, u64 movieTime, u64 *MediaTime, s64 *SegmentStartTime, s64 *MediaOffset, u8 *useEdit, u64 *next_edit_start_plus_one)
{
#if 0
    GF_Err e;
    u32 sampleNumber, prevSampleNumber;
    u64 firstDTS;
#endif
    u32 i, count;
    Bool last_is_empty = 0;
    u64 time, lastSampleTime;
    s64 mtime;
    GF_EdtsEntry *ent;
    Double scale_ts;
    GF_SampleTableBox *stbl = trak->Media->information->sampleTable;

    if (next_edit_start_plus_one) *next_edit_start_plus_one = 0;
    *useEdit = 1;
    *MediaTime = 0;
    //no segment yet...
    *SegmentStartTime = -1;
    *MediaOffset = -1;
    if (!trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale || !stbl->SampleSize) {
        return GF_ISOM_INVALID_FILE;
    }

    //no samples...
    if (!stbl->SampleSize->sampleCount) {
        lastSampleTime = 0;
    } else {
        lastSampleTime = trak->Media->mediaHeader->duration;
    }

    //No edits, 1 to 1 mapping
    if (! trak->editBox || !trak->editBox->editList) {
        *MediaTime = movieTime;
        //check this is in our media time line
        if ((*MediaTime > lastSampleTime)
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
                && !trak->moov->mov->moof
#endif
           ) {
            *MediaTime = lastSampleTime;
        }
        *useEdit = 0;
        return GF_OK;
    }
    //browse the edit list and get the time
    scale_ts = trak->Media->mediaHeader->timeScale;
    scale_ts /= trak->moov->mvhd->timeScale;

    time = 0;
    ent = NULL;
    count=gf_list_count(trak->editBox->editList->entryList);
    for (i=0; i<count; i++) {
        ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, i);
        if ( (time + ent->segmentDuration) * scale_ts > movieTime) {
            if (!force_non_empty || (ent->mediaTime >= 0)) {
                if (next_edit_start_plus_one) *next_edit_start_plus_one = 1 + (u64) ((time + ent->segmentDuration) * scale_ts);
                goto ent_found;
            }
        }
        time += ent->segmentDuration;
        last_is_empty = ent->segmentDuration ? 0 : 1;
    }

    if (last_is_empty) {
        ent = (GF_EdtsEntry *)gf_list_last(trak->editBox->editList->entryList);
        if (ent->mediaRate==1) {
            *MediaTime = movieTime + ent->mediaTime;
        } else {
            ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, 0);
            if (ent->mediaRate==-1) {
                u64 dur = (u64) (ent->segmentDuration * scale_ts);
                *MediaTime = (movieTime > dur) ? (movieTime-dur) : 0;
            }
        }
        *useEdit = 0;
        return GF_OK;
    }


    //we had nothing in the list (strange file but compliant...)
    //return the 1 to 1 mapped vale of the last media sample
    if (!ent) {
        *MediaTime = movieTime;
        //check this is in our media time line
        if (*MediaTime > lastSampleTime) *MediaTime = lastSampleTime;
        *useEdit = 0;
        return GF_OK;
    }
    //request for a bigger time that what we can give: return the last sample (undefined behavior...)
    *MediaTime = lastSampleTime;
    return GF_OK;

ent_found:
    //OK, we found our entry, set the SegmentTime
    *SegmentStartTime = time;

    //we request an empty list, there's no media here...
    if (ent->mediaTime < 0) {
        *MediaTime = 0;
        return GF_OK;
    }
    //we request a dwell edit
    if (! ent->mediaRate) {
        *MediaTime = ent->mediaTime;
        //no media offset
        *MediaOffset = 0;
        *useEdit = 2;
        return GF_OK;
    }

    /*WARNING: this can be "-1" when doing searchForward mode (to prevent jumping to next entry)*/
    mtime = ent->mediaTime + movieTime - (time * trak->Media->mediaHeader->timeScale / trak->moov->mvhd->timeScale);
    if (mtime<0) mtime = 0;
    *MediaTime = (u64) mtime;
    *MediaOffset = ent->mediaTime;

#if 0
    //
    //Sanity check: is the requested time valid ? This is to cope with wrong EditLists
    //we have the translated time, but we need to make sure we have a sample at this time ...
    //we have to find a COMPOSITION time
    e = findEntryForTime(stbl, (u32) *MediaTime, 1, &sampleNumber, &prevSampleNumber);
    if (e) return e;

    //first case: our time is after the last sample DTS (it's a broken editList somehow)
    //set the media time to the last sample
    if (!sampleNumber && !prevSampleNumber) {
        *MediaTime = lastSampleTime;
        return GF_OK;
    }
    //get the appropriated sample
    if (!sampleNumber) sampleNumber = prevSampleNumber;

    stbl_GetSampleDTS(stbl->TimeToSample, sampleNumber, &DTS);
    CTS = 0;
    if (stbl->CompositionOffset) stbl_GetSampleCTS(stbl->CompositionOffset, sampleNumber, &CTS);

    //now get the entry sample (the entry time gives the CTS, and we need the DTS
    e = findEntryForTime(stbl, (u32) ent->mediaTime, 0, &sampleNumber, &prevSampleNumber);
    if (e) return e;

    //oops, the mediaTime indicates a sample that is not in our media !
    if (!sampleNumber && !prevSampleNumber) {
        *MediaTime = lastSampleTime;
        return GF_ISOM_INVALID_FILE;
    }
    if (!sampleNumber) sampleNumber = prevSampleNumber;

    stbl_GetSampleDTS(stbl->TimeToSample, sampleNumber, &firstDTS);

    //and store the "time offset" of the desired sample in this segment
    //this is weird, used to rebuild the timeStamp when reading from the track, not the
    //media ...
    *MediaOffset = firstDTS;
#endif
    return GF_OK;
}
Example #2
0
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;
}
Example #3
0
M4Err Media_GetSample(MediaAtom *mdia, u32 sampleNumber, M4Sample **samp, u32 *sampleDescriptionIndex, Bool no_data, u64 *out_offset)
{
	M4Err e;
	u32 bytesRead;
	u32 dataRefIndex, chunkNumber;
	u64 offset, new_size;
	u8 isEdited;
	SampleEntryAtom *entry;

	(*sampleDescriptionIndex) = 0;
	
	if (!mdia || !mdia->information->sampleTable) return M4BadParam;

	//OK, here we go....
	if (sampleNumber > mdia->information->sampleTable->SampleSize->sampleCount) return M4BadParam;

	//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;
	}
	/*get sync shadow*/
	if (Media_IsSampleSyncShadow(mdia->information->sampleTable->ShadowSync, sampleNumber)) (*samp)->IsRAP = 2;

	//the data info
	e = stbl_GetSampleInfos(mdia->information->sampleTable, sampleNumber, &offset, &chunkNumber, sampleDescriptionIndex, &isEdited);
	if (e) return e;

	//then get the DataRef
	e = Media_GetSampleDesc(mdia, *sampleDescriptionIndex, &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 == M4_OPEN_READ) {	
		//same as last call in read mode
		if (!mdia->information->dataHandler || (mdia->information->dataEntryIndex != dataRefIndex)) {
			e = DataMap_Open(mdia, dataRefIndex, isEdited);
			if (e) return e;
		}
	} else {
		e = DataMap_Open(mdia, dataRefIndex, isEdited);
		if (e) return e;
	}

	if (out_offset) *out_offset = offset;
	if (no_data) return M4OK;

	/*and finally get the data, include padding if needed*/
 	(*samp)->data = (char *) 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 = BS_GetSize(mdia->information->dataHandler->bs);
	if (offset + (*samp)->dataLength > new_size) {
		//always refresh the size to avoid wrong info on http/ftp 
		new_size = BS_GetRefreshedSize(mdia->information->dataHandler->bs);
		if (offset + (*samp)->dataLength > new_size) {
			mdia->BytesMissing = offset + (*samp)->dataLength - new_size;
			return M4UncompleteFile;
		}	
	}

	bytesRead = DataMap_GetData(mdia->information->dataHandler, (*samp)->data, (*samp)->dataLength, offset);
	//if bytesRead != sampleSize, we have an IO err
	if (bytesRead < (*samp)->dataLength) {
		return M4IOErr;
	}
	mdia->BytesMissing = 0;
	//finally rewrite the sample if this is an OD Access Unit
	if (mdia->handler->handlerType == M4_ODMediaType) {
		e = Media_RewriteODFrame(mdia, *samp);
		if (e) return e;
	}
	else if (mdia->mediaTrack->moov->mov->convert_streaming_text 
		&& (mdia->handler->handlerType == M4_TimedTextMediaType) ) {
		u32 dur;
		if (sampleNumber == mdia->information->sampleTable->SampleSize->sampleCount) {
			dur = (u32) mdia->mediaHeader->duration - (*samp)->DTS;
		} else {
			stbl_GetSampleDTS(mdia->information->sampleTable->TimeToSample, sampleNumber+1, &dur);
			dur -= (*samp)->DTS;
		}
		e = M4_RewriteTextSample(*samp, *sampleDescriptionIndex, dur);
		if (e) return e;
	}
	return M4OK;
}
Example #4
0
//Get the sample number
M4Err findEntryForTime(SampleTableAtom *stbl, u32 DTS, u8 useCTS, u32 *sampleNumber, u32 *prevSampleNumber)
{
	u32 i, j, curDTS, curSampNum, CTSOffset;
	sttsEntry *ent;
	(*sampleNumber) = 0;
	(*prevSampleNumber) = 0;

	if (!stbl->CompositionOffset) useCTS = 0;

	//our cache
	if (stbl->TimeToSample->r_FirstSampleInEntry &&
		(DTS >= stbl->TimeToSample->r_CurrentDTS) ) {
		//if we're using CTS, we don't really know whether we're in the good entry or not
		//(eg, the real DTS of the sample could be in a previous entry
		i = stbl->TimeToSample->r_currentEntryIndex;
		curDTS = stbl->TimeToSample->r_CurrentDTS;
		curSampNum = stbl->TimeToSample->r_FirstSampleInEntry;
	} else {
		i = 0;
		curDTS = stbl->TimeToSample->r_CurrentDTS = 0;
		curSampNum = stbl->TimeToSample->r_FirstSampleInEntry = 1;
		stbl->TimeToSample->r_currentEntryIndex = 0;
	}

	//we need to validate our cache if we are using CTS because of B-frames and co...
	if (i && useCTS) {
		while (1) {
			stbl_GetSampleCTS(stbl->CompositionOffset, curSampNum, &CTSOffset);
			//we're too far, rewind
			if ( i && (curDTS + CTSOffset > DTS) ) {
				ent = (sttsEntry*)ChainGetEntry(stbl->TimeToSample->entryList, i);
				curSampNum -= ent->sampleCount;
				curDTS -= ent->sampleDelta * ent->sampleCount;
				i --;
			} else if (!i) {
				//begining of the table, no choice
				curDTS = stbl->TimeToSample->r_CurrentDTS = 0;
				curSampNum = stbl->TimeToSample->r_FirstSampleInEntry = 1;
				stbl->TimeToSample->r_currentEntryIndex = 0;
				break;
			} else {
				//OK now we're good
				break;
			}
		}
	}

	//look for the DTS from this entry
	for (; i<ChainGetCount(stbl->TimeToSample->entryList); i++) {
		ent = (sttsEntry*)ChainGetEntry(stbl->TimeToSample->entryList, i);
		if (useCTS) {
			stbl_GetSampleCTS(stbl->CompositionOffset, curSampNum, &CTSOffset);
		} else {
			CTSOffset = 0;
		}
		for (j=0; j<ent->sampleCount; j++) {
			if (curDTS + CTSOffset >= DTS) goto entry_found;
			curSampNum += 1;
			curDTS += ent->sampleDelta;
		}
		//we're switching to the next entry, update the cache!
		stbl->TimeToSample->r_CurrentDTS += ent->sampleCount * ent->sampleDelta;
		stbl->TimeToSample->r_currentEntryIndex += 1;
		stbl->TimeToSample->r_FirstSampleInEntry += ent->sampleCount;
	}
	//return as is
	return M4OK;

entry_found:
	//do we have the exact time ?
	if (curDTS + CTSOffset == DTS) {
		(*sampleNumber) = curSampNum;
	}
	//if we match the exact DTS also select this sample 
	else if (curDTS == DTS) {
		(*sampleNumber) = curSampNum;
	} else {
		//exception for the first sample (we need to "load" the playback)
		if (curSampNum != 1) {
			(*prevSampleNumber) = curSampNum - 1;
		} else {
			(*prevSampleNumber) = 1;
		}
	}
	return M4OK;
}