1 /*
2 *			GPAC - Multimedia Framework C SDK
3 *
4 *			Authors: Jean Le Feuvre
5 *			Copyright (c) Telecom ParisTech 2000-2012
6 *					All rights reserved
7 *
8 *  This file is part of GPAC / ISO Media File Format sub-project
9 *
10 *  GPAC is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU Lesser General Public License as published by
12 *  the Free Software Foundation; either version 2, or (at your option)
13 *  any later version.
14 *
15 *  GPAC is distributed in the hope that it will be useful,
16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 *  GNU Lesser General Public License for more details.
19 *
20 *  You should have received a copy of the GNU Lesser General Public
21 *  License along with this library; see the file COPYING.  If not, write to
22 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25 
26 #include <gpac/internal/isomedia_dev.h>
27 #include <gpac/constants.h>
28 
29 #ifndef GPAC_DISABLE_ISOM
30 
Media_GetSampleDesc(GF_MediaBox * mdia,u32 SampleDescIndex,GF_SampleEntryBox ** out_entry,u32 * dataRefIndex)31 GF_Err Media_GetSampleDesc(GF_MediaBox *mdia, u32 SampleDescIndex, GF_SampleEntryBox **out_entry, u32 *dataRefIndex)
32 {
33 	GF_SampleDescriptionBox *stsd;
34 	GF_SampleEntryBox *entry = NULL;
35 
36 	if (!mdia) return GF_ISOM_INVALID_FILE;
37 
38 	stsd = mdia->information->sampleTable->SampleDescription;
39 	if (!stsd) return GF_ISOM_INVALID_FILE;
40 	if (!SampleDescIndex || (SampleDescIndex > gf_list_count(stsd->other_boxes))) return GF_BAD_PARAM;
41 
42 	entry = (GF_SampleEntryBox*)gf_list_get(stsd->other_boxes, SampleDescIndex - 1);
43 	if (!entry) return GF_ISOM_INVALID_FILE;
44 
45 	if (out_entry) *out_entry = entry;
46 	if (dataRefIndex) *dataRefIndex = entry->dataReferenceIndex;
47 	return GF_OK;
48 }
49 
Media_GetSampleDescIndex(GF_MediaBox * mdia,u64 DTS,u32 * sampleDescIndex)50 GF_Err Media_GetSampleDescIndex(GF_MediaBox *mdia, u64 DTS, u32 *sampleDescIndex)
51 {
52 	GF_Err e;
53 	u32 sampleNumber, prevSampleNumber, num;
54 	u64 offset;
55 	u8 isEdited;
56 	if (sampleDescIndex == NULL) return GF_BAD_PARAM;
57 
58 	//find the sample for this time
59 	e = stbl_findEntryForTime(mdia->information->sampleTable, (u32)DTS, 0, &sampleNumber, &prevSampleNumber);
60 	if (e) return e;
61 
62 	if (!sampleNumber && !prevSampleNumber) {
63 		//we have to assume the track was created to be used... If we have a sampleDesc, OK
64 		if (gf_list_count(mdia->information->sampleTable->SampleDescription->other_boxes)) {
65 			(*sampleDescIndex) = 1;
66 			return GF_OK;
67 		}
68 		return GF_BAD_PARAM;
69 	}
70 	return stbl_GetSampleInfos(mdia->information->sampleTable, (sampleNumber ? sampleNumber : prevSampleNumber), &offset, &num, sampleDescIndex, &isEdited);
71 }
72 
gf_isom_get_3gpp_audio_esd(GF_SampleTableBox * stbl,GF_GenericAudioSampleEntryBox * entry,GF_ESD ** out_esd)73 static GF_Err gf_isom_get_3gpp_audio_esd(GF_SampleTableBox *stbl, GF_GenericAudioSampleEntryBox *entry, GF_ESD **out_esd)
74 {
75 	GF_BitStream *bs;
76 	char szName[80];
77 
78 	(*out_esd) = gf_odf_desc_esd_new(2);
79 	(*out_esd)->decoderConfig->streamType = GF_STREAM_AUDIO;
80 	/*official mapping to MPEG-4*/
81 	switch (entry->type) {
82 	case GF_ISOM_SUBTYPE_3GP_EVRC:
83 		(*out_esd)->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_EVRC_VOICE;
84 		return GF_OK;
85 	case GF_ISOM_SUBTYPE_3GP_QCELP:
86 	{
87 		u32 block_size, sample_rate, sample_size, i;
88 		GF_SttsEntry *ent;
89 		/*only map CBR*/
90 		sample_size = stbl->SampleSize->sampleSize;
91 		(*out_esd)->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_13K_VOICE;
92 		bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
93 		gf_bs_write_data(bs, "QLCMfmt ", 8);
94 		gf_bs_write_u32_le(bs, 150);/*fmt chunk size*/
95 		gf_bs_write_u8(bs, 1);
96 		gf_bs_write_u8(bs, 0);
97 		/*QCELP GUID*/
98 		gf_bs_write_data(bs, "\x41\x6D\x7F\x5E\x15\xB1\xD0\x11\xBA\x91\x00\x80\x5F\xB4\xB9\x7E", 16);
99 		gf_bs_write_u16_le(bs, 1);
100 		memset(szName, 0, 80);
101 		strcpy(szName, "QCELP-13K(GPAC-emulated)");
102 		gf_bs_write_data(bs, szName, 80);
103 		ent = &stbl->TimeToSample->entries[0];
104 		sample_rate = entry->samplerate_hi;
105 		block_size = ent ? ent->sampleDelta : 160;
106 		gf_bs_write_u16_le(bs, 8 * sample_size*sample_rate / block_size);
107 		gf_bs_write_u16_le(bs, sample_size);
108 		gf_bs_write_u16_le(bs, block_size);
109 		gf_bs_write_u16_le(bs, sample_rate);
110 		gf_bs_write_u16_le(bs, entry->bitspersample);
111 		gf_bs_write_u32_le(bs, sample_size ? 0 : 7);
112 		/**/
113 		for (i = 0; i<7; i++) {
114 			static const u32 qcelp_r2s[] = { 0, 1, 1, 4, 2, 8, 3, 17, 4, 35, 5, 8, 14, 1 };
115 			if (sample_size) {
116 				gf_bs_write_u16(bs, 0);
117 			}
118 			else {
119 				gf_bs_write_u8(bs, qcelp_r2s[2 * i + 1]);
120 				gf_bs_write_u8(bs, qcelp_r2s[2 * i]);
121 			}
122 		}
123 		gf_bs_write_u16(bs, 0);
124 		memset(szName, 0, 80);
125 		gf_bs_write_data(bs, szName, 20);/*reserved*/
126 		gf_bs_get_content(bs, &(*out_esd)->decoderConfig->decoderSpecificInfo->data, &(*out_esd)->decoderConfig->decoderSpecificInfo->dataLength);
127 		gf_bs_del(bs);
128 	}
129 	return GF_OK;
130 	case GF_ISOM_SUBTYPE_3GP_SMV:
131 		(*out_esd)->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_SMV_VOICE;
132 		return GF_OK;
133 	default:
134 		break;
135 	}
136 	/*this is a user-reserved used in gpac - we need a std OTI for AMR/AMRWB*/
137 	(*out_esd)->decoderConfig->objectTypeIndication = GPAC_OTI_MEDIA_GENERIC;
138 	bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
139 	gf_bs_write_u32(bs, entry->type);
140 	gf_bs_write_u16(bs, entry->samplerate_hi);
141 	gf_bs_write_u16(bs, (entry->type == GF_ISOM_SUBTYPE_3GP_AMR) ? 160 : 320);
142 	gf_bs_write_u8(bs, entry->channel_count);
143 	gf_bs_write_u8(bs, entry->bitspersample);
144 	gf_bs_write_u8(bs, 0);
145 	gf_bs_get_content(bs, &(*out_esd)->decoderConfig->decoderSpecificInfo->data, &(*out_esd)->decoderConfig->decoderSpecificInfo->dataLength);
146 	gf_bs_del(bs);
147 	return GF_OK;
148 }
149 
Media_GetESD(GF_MediaBox * mdia,u32 sampleDescIndex,GF_ESD ** out_esd,Bool true_desc_only)150 GF_Err Media_GetESD(GF_MediaBox *mdia, u32 sampleDescIndex, GF_ESD **out_esd, Bool true_desc_only)
151 {
152 	GF_ESD *esd;
153 	GF_MPEGSampleEntryBox *entry = NULL;
154 	GF_ESDBox *ESDa;
155 	GF_SampleDescriptionBox *stsd = mdia->information->sampleTable->SampleDescription;
156 
157 	*out_esd = NULL;
158 	if (!stsd || !stsd->other_boxes || !sampleDescIndex || (sampleDescIndex > gf_list_count(stsd->other_boxes)))
159 		return GF_BAD_PARAM;
160 
161 	esd = NULL;
162 	entry = (GF_MPEGSampleEntryBox*)gf_list_get(stsd->other_boxes, sampleDescIndex - 1);
163 	if (!entry) return GF_ISOM_INVALID_MEDIA;
164 
165 	*out_esd = NULL;
166 	ESDa = NULL;
167 	switch (entry->type) {
168 	case GF_ISOM_BOX_TYPE_MP4V:
169 	case GF_ISOM_BOX_TYPE_ENCV:
170 		ESDa = ((GF_MPEGVisualSampleEntryBox*)entry)->esd;
171 		if (ESDa) esd = (GF_ESD *)ESDa->desc;
172 		/*avc1 encrypted*/
173 		else esd = ((GF_MPEGVisualSampleEntryBox*)entry)->emul_esd;
174 		break;
175 	case GF_ISOM_BOX_TYPE_AVC1:
176 	case GF_ISOM_BOX_TYPE_AVC2:
177 	case GF_ISOM_BOX_TYPE_AVC3:
178 	case GF_ISOM_BOX_TYPE_AVC4:
179 	case GF_ISOM_BOX_TYPE_HVC1:
180 	case GF_ISOM_BOX_TYPE_HEV1:
181 	case GF_ISOM_BOX_TYPE_HVC2:
182 	case GF_ISOM_BOX_TYPE_HEV2:
183 	case GF_ISOM_BOX_TYPE_HVT1:
184 		esd = ((GF_MPEGVisualSampleEntryBox*)entry)->emul_esd;
185 		break;
186 	case GF_ISOM_BOX_TYPE_SVC1:
187 		if ((mdia->mediaTrack->extractor_mode & 0x0000FFFF) != GF_ISOM_NALU_EXTRACT_INSPECT)
188 			AVC_RewriteESDescriptorEx((GF_MPEGVisualSampleEntryBox*)entry, mdia);
189 		else
190 			AVC_RewriteESDescriptorEx((GF_MPEGVisualSampleEntryBox*)entry, NULL);
191 		esd = ((GF_MPEGVisualSampleEntryBox*)entry)->emul_esd;
192 		break;
193 	case GF_ISOM_BOX_TYPE_LHE1:
194 	case GF_ISOM_BOX_TYPE_LHV1:
195 		if ((mdia->mediaTrack->extractor_mode & 0x0000FFFF) != GF_ISOM_NALU_EXTRACT_INSPECT)
196 			HEVC_RewriteESDescriptorEx((GF_MPEGVisualSampleEntryBox*)entry, mdia);
197 		else
198 			HEVC_RewriteESDescriptorEx((GF_MPEGVisualSampleEntryBox*)entry, NULL);
199 		esd = ((GF_MPEGVisualSampleEntryBox*)entry)->emul_esd;
200 		break;
201 	case GF_ISOM_BOX_TYPE_MP4A:
202 	case GF_ISOM_BOX_TYPE_ENCA:
203 		ESDa = ((GF_MPEGAudioSampleEntryBox*)entry)->esd;
204 		if (ESDa) esd = (GF_ESD *)ESDa->desc;
205 		break;
206 	case GF_ISOM_BOX_TYPE_MP4S:
207 	case GF_ISOM_BOX_TYPE_ENCS:
208 		ESDa = entry->esd;
209 		if (ESDa) esd = (GF_ESD *)ESDa->desc;
210 		break;
211 #ifndef GPAC_DISABLE_TTXT
212 	case GF_ISOM_BOX_TYPE_TX3G:
213 	case GF_ISOM_BOX_TYPE_TEXT:
214 		if (!true_desc_only && mdia->mediaTrack->moov->mov->convert_streaming_text) {
215 			GF_Err e = gf_isom_get_ttxt_esd(mdia, out_esd);
216 			if (e) return e;
217 			break;
218 		}
219 		else
220 			return GF_ISOM_INVALID_MEDIA;
221 #endif
222 
223 
224 	case GF_ISOM_SUBTYPE_3GP_AMR:
225 	case GF_ISOM_SUBTYPE_3GP_AMR_WB:
226 	case GF_ISOM_SUBTYPE_3GP_EVRC:
227 	case GF_ISOM_SUBTYPE_3GP_QCELP:
228 	case GF_ISOM_SUBTYPE_3GP_SMV:
229 		if (!true_desc_only) {
230 			GF_Err e = gf_isom_get_3gpp_audio_esd(mdia->information->sampleTable, (GF_GenericAudioSampleEntryBox*)entry, out_esd);
231 			if (e) return e;
232 			break;
233 		}
234 		else return GF_ISOM_INVALID_MEDIA;
235 
236 	case GF_ISOM_SUBTYPE_3GP_H263:
237 		if (true_desc_only) {
238 			return GF_ISOM_INVALID_MEDIA;
239 		}
240 		else {
241 			GF_BitStream *bs;
242 			esd = gf_odf_desc_esd_new(2);
243 			*out_esd = esd;
244 			esd->decoderConfig->streamType = GF_STREAM_VISUAL;
245 			esd->decoderConfig->objectTypeIndication = GPAC_OTI_MEDIA_GENERIC;
246 			bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
247 			gf_bs_write_u32(bs, entry->type);
248 			gf_bs_write_u16(bs, ((GF_MPEGVisualSampleEntryBox*)entry)->Width);
249 			gf_bs_write_u16(bs, ((GF_MPEGVisualSampleEntryBox*)entry)->Height);
250 			gf_bs_get_content(bs, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength);
251 			gf_bs_del(bs);
252 			break;
253 		}
254 
255 	case GF_ISOM_SUBTYPE_MP3:
256 		if (true_desc_only) {
257 			return GF_ISOM_INVALID_MEDIA;
258 		}
259 		else {
260 			esd = gf_odf_desc_esd_new(2);
261 			*out_esd = esd;
262 			esd->decoderConfig->streamType = GF_STREAM_AUDIO;
263 			esd->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_MPEG1;
264 			break;
265 		}
266 
267 	case GF_ISOM_SUBTYPE_LSR1:
268 		if (true_desc_only) {
269 			return GF_ISOM_INVALID_MEDIA;
270 		}
271 		else {
272 			GF_LASeRSampleEntryBox*ptr = (GF_LASeRSampleEntryBox*)entry;
273 			esd = gf_odf_desc_esd_new(2);
274 			*out_esd = esd;
275 			esd->decoderConfig->streamType = GF_STREAM_SCENE;
276 			esd->decoderConfig->objectTypeIndication = GPAC_OTI_SCENE_LASER;
277 			esd->decoderConfig->decoderSpecificInfo->dataLength = ptr->lsr_config->hdr_size;
278 			esd->decoderConfig->decoderSpecificInfo->data = gf_malloc(sizeof(char)*ptr->lsr_config->hdr_size);
279 			memcpy(esd->decoderConfig->decoderSpecificInfo->data, ptr->lsr_config->hdr, sizeof(char)*ptr->lsr_config->hdr_size);
280 			break;
281 		}
282 
283 	default:
284 		return GF_ISOM_INVALID_MEDIA;
285 	}
286 
287 	if (true_desc_only) {
288 		if (!esd) return GF_ISOM_INVALID_MEDIA;
289 		*out_esd = esd;
290 		return GF_OK;
291 	}
292 	else {
293 		if (!esd && !*out_esd) return GF_ISOM_INVALID_MEDIA;
294 		if (*out_esd == NULL) gf_odf_desc_copy((GF_Descriptor *)esd, (GF_Descriptor **)out_esd);
295 	}
296 	return GF_OK;
297 }
298 
Media_IsSampleSyncShadow(GF_ShadowSyncBox * stsh,u32 sampleNumber)299 Bool Media_IsSampleSyncShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber)
300 {
301 	u32 i;
302 	GF_StshEntry *ent;
303 	if (!stsh) return 0;
304 	i = 0;
305 	while ((ent = (GF_StshEntry*)gf_list_enum(stsh->entries, &i))) {
306 		if ((u32)ent->syncSampleNumber == sampleNumber) return 1;
307 		else if ((u32)ent->syncSampleNumber > sampleNumber) return 0;
308 	}
309 	return 0;
310 }
311 
Media_GetSample(GF_MediaBox * mdia,u32 sampleNumber,GF_ISOSample ** samp,u32 * sIDX,Bool no_data,u64 * out_offset)312 GF_Err Media_GetSample(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample **samp, u32 *sIDX, Bool no_data, u64 *out_offset)
313 {
314 	GF_Err e;
315 	u32 bytesRead;
316 	u32 dataRefIndex, chunkNumber;
317 	u64 offset, new_size;
318 	u8 isEdited;
319 	GF_SampleEntryBox *entry;
320 
321 	if (!mdia || !mdia->information->sampleTable) return GF_BAD_PARAM;
322 	if (!mdia->information->sampleTable->SampleSize)
323 		return GF_ISOM_INVALID_FILE;
324 
325 	//OK, here we go....
326 	if (sampleNumber > mdia->information->sampleTable->SampleSize->sampleCount) return GF_BAD_PARAM;
327 
328 	//get the DTS
329 	e = stbl_GetSampleDTS(mdia->information->sampleTable->TimeToSample, sampleNumber, &(*samp)->DTS);
330 	if (e) return e;
331 	//the CTS offset
332 	if (mdia->information->sampleTable->CompositionOffset) {
333 		e = stbl_GetSampleCTS(mdia->information->sampleTable->CompositionOffset, sampleNumber, &(*samp)->CTS_Offset);
334 		if (e) return e;
335 	}
336 	else {
337 		(*samp)->CTS_Offset = 0;
338 	}
339 	//the size
340 	e = stbl_GetSampleSize(mdia->information->sampleTable->SampleSize, sampleNumber, &(*samp)->dataLength);
341 	if (e) return e;
342 	//the RAP
343 	if (mdia->information->sampleTable->SyncSample) {
344 		e = stbl_GetSampleRAP(mdia->information->sampleTable->SyncSample, sampleNumber, &(*samp)->IsRAP, NULL, NULL);
345 		if (e) return e;
346 	}
347 	else {
348 		//if no SyncSample, all samples are sync (cf spec)
349 		(*samp)->IsRAP = RAP;
350 	}
351 	/*overwrite sync sample with sample dep if any*/
352 	if (mdia->information->sampleTable->SampleDep) {
353 		u32 isLeading, dependsOn, dependedOn, redundant;
354 		e = stbl_GetSampleDepType(mdia->information->sampleTable->SampleDep, sampleNumber, &isLeading, &dependsOn, &dependedOn, &redundant);
355 		if (!e) {
356 			if (dependsOn == 1) (*samp)->IsRAP = RAP_NO;
357 			else if (dependsOn == 2) (*samp)->IsRAP = RAP;
358 			/*if not depended upon and redundant, mark as carousel sample*/
359 			if ((dependedOn == 2) && (redundant == 1)) (*samp)->IsRAP = RAP_REDUNDANT;
360 			/*TODO FIXME - we must enhance the IsRAP semantics to carry disposable info ... */
361 		}
362 	}
363 	/*get sync shadow*/
364 	if (Media_IsSampleSyncShadow(mdia->information->sampleTable->ShadowSync, sampleNumber)) (*samp)->IsRAP = RAP_REDUNDANT;
365 
366 	//the data info
367 	if (!sIDX && !no_data) return GF_BAD_PARAM;
368 	if (!sIDX && !out_offset) return GF_OK;
369 	if (!sIDX) return GF_OK;
370 
371 	(*sIDX) = 0;
372 	e = stbl_GetSampleInfos(mdia->information->sampleTable, sampleNumber, &offset, &chunkNumber, sIDX, &isEdited);
373 	if (e) return e;
374 
375 	//then get the DataRef
376 	e = Media_GetSampleDesc(mdia, *sIDX, &entry, &dataRefIndex);
377 	if (e) return e;
378 
379 	// Open the data handler - check our mode, don't reopen in read only if this is
380 	//the same entry. In other modes we have no choice because the main data map is
381 	//divided into the original and the edition files
382 	if (mdia->mediaTrack->moov->mov->openMode == GF_ISOM_OPEN_READ) {
383 		//same as last call in read mode
384 		if (!mdia->information->dataHandler) {
385 			e = gf_isom_datamap_open(mdia, dataRefIndex, isEdited);
386 			if (e) return e;
387 		}
388 		if (mdia->information->dataEntryIndex != dataRefIndex)
389 			mdia->information->dataEntryIndex = dataRefIndex;
390 	}
391 	else {
392 		e = gf_isom_datamap_open(mdia, dataRefIndex, isEdited);
393 		if (e) return e;
394 	}
395 
396 	if (out_offset) *out_offset = offset;
397 	if (no_data) return GF_OK;
398 
399 	/*and finally get the data, include padding if needed*/
400 	(*samp)->data = (char *)gf_malloc(sizeof(char) * ((*samp)->dataLength + mdia->mediaTrack->padding_bytes));
401 	if (mdia->mediaTrack->padding_bytes)
402 		memset((*samp)->data + (*samp)->dataLength, 0, sizeof(char) * mdia->mediaTrack->padding_bytes);
403 
404 	//check if we can get the sample (make sure we have enougth data...)
405 	new_size = gf_bs_get_size(mdia->information->dataHandler->bs);
406 	if (offset + (*samp)->dataLength > new_size) {
407 		//always refresh the size to avoid wrong info on http/ftp
408 		new_size = gf_bs_get_refreshed_size(mdia->information->dataHandler->bs);
409 		if (offset + (*samp)->dataLength > new_size) {
410 			mdia->BytesMissing = offset + (*samp)->dataLength - new_size;
411 			return GF_ISOM_INCOMPLETE_FILE;
412 		}
413 	}
414 
415 	bytesRead = gf_isom_datamap_get_data(mdia->information->dataHandler, (*samp)->data, (*samp)->dataLength, offset);
416 	//if bytesRead != sampleSize, we have an IO err
417 	if (bytesRead < (*samp)->dataLength) {
418 		return GF_IO_ERR;
419 	}
420 	mdia->BytesMissing = 0;
421 	//finally rewrite the sample if this is an OD Access Unit
422 	if (mdia->handler->handlerType == GF_ISOM_MEDIA_OD) {
423 		e = Media_RewriteODFrame(mdia, *samp);
424 		if (e) return e;
425 	}
426 	/*FIXME: we do NOT rewrite sample if we have a encrypted track*/
427 	else if (gf_isom_is_nalu_based_entry(mdia, entry) &&
428 		!gf_isom_is_track_encrypted(mdia->mediaTrack->moov->mov, gf_isom_get_tracknum_from_id(mdia->mediaTrack->moov, mdia->mediaTrack->Header->trackID))
429 		) {
430 		e = gf_isom_nalu_sample_rewrite(mdia, *samp, sampleNumber, (GF_MPEGVisualSampleEntryBox *)entry);
431 		if (e) return e;
432 	}
433 	else if (mdia->mediaTrack->moov->mov->convert_streaming_text
434 		&& ((mdia->handler->handlerType == GF_ISOM_MEDIA_TEXT) || (mdia->handler->handlerType == GF_ISOM_MEDIA_SUBT))
435 		&& (entry->type == GF_ISOM_BOX_TYPE_TX3G || entry->type == GF_ISOM_BOX_TYPE_TEXT)
436 		) {
437 		u64 dur;
438 		if (sampleNumber == mdia->information->sampleTable->SampleSize->sampleCount) {
439 			dur = mdia->mediaHeader->duration - (*samp)->DTS;
440 		}
441 		else {
442 			stbl_GetSampleDTS(mdia->information->sampleTable->TimeToSample, sampleNumber + 1, &dur);
443 			dur -= (*samp)->DTS;
444 		}
445 		e = gf_isom_rewrite_text_sample(*samp, *sIDX, (u32)dur);
446 		if (e) return e;
447 	}
448 	return GF_OK;
449 }
450 
451 
452 
Media_CheckDataEntry(GF_MediaBox * mdia,u32 dataEntryIndex)453 GF_Err Media_CheckDataEntry(GF_MediaBox *mdia, u32 dataEntryIndex)
454 {
455 
456 	GF_DataEntryURLBox *entry;
457 	GF_DataMap *map;
458 	GF_Err e;
459 	if (!mdia || !dataEntryIndex || dataEntryIndex > gf_list_count(mdia->information->dataInformation->dref->other_boxes)) return GF_BAD_PARAM;
460 
461 	entry = (GF_DataEntryURLBox*)gf_list_get(mdia->information->dataInformation->dref->other_boxes, dataEntryIndex - 1);
462 	if (!entry) return GF_ISOM_INVALID_FILE;
463 	if (entry->flags == 1) return GF_OK;
464 
465 	//ok, not self contained, let's go for it...
466 	//we don't know what's a URN yet
467 	if (entry->type == GF_ISOM_BOX_TYPE_URN) return GF_NOT_SUPPORTED;
468 	if (mdia->mediaTrack->moov->mov->openMode == GF_ISOM_OPEN_WRITE) {
469 		e = gf_isom_datamap_new(entry->location, NULL, GF_ISOM_DATA_MAP_READ, &map);
470 	}
471 	else {
472 		e = gf_isom_datamap_new(entry->location, mdia->mediaTrack->moov->mov->fileName, GF_ISOM_DATA_MAP_READ, &map);
473 	}
474 	if (e) return e;
475 	gf_isom_datamap_del(map);
476 	return GF_OK;
477 }
478 
479 
Media_IsSelfContained(GF_MediaBox * mdia,u32 StreamDescIndex)480 Bool Media_IsSelfContained(GF_MediaBox *mdia, u32 StreamDescIndex)
481 {
482 	u32 drefIndex = 0;
483 	GF_FullBox *a;
484 	GF_SampleEntryBox *se = NULL;
485 
486 	Media_GetSampleDesc(mdia, StreamDescIndex, &se, &drefIndex);
487 	if (!drefIndex) return 0;
488 	a = (GF_FullBox*)gf_list_get(mdia->information->dataInformation->dref->other_boxes, drefIndex - 1);
489 	if (!a) {
490 		GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] broken file: Data reference index set to %d but no data reference entry found\n", drefIndex));
491 		return 0;
492 	}
493 	if (a->flags & 1) return 1;
494 	/*QT specific*/
495 	if (a->type == GF_4CC('a', 'l', 'i', 's')) return 1;
496 	return 0;
497 }
498 
499 
500 
501 //look for a sync sample from a given point in media time
Media_FindSyncSample(GF_SampleTableBox * stbl,u32 searchFromSample,u32 * sampleNumber,u8 mode)502 GF_Err Media_FindSyncSample(GF_SampleTableBox *stbl, u32 searchFromSample, u32 *sampleNumber, u8 mode)
503 {
504 	SAPType isRAP;
505 	u32 next, prev, next_in_sap, prev_in_sap;
506 	if (!stbl || !stbl->SyncSample) return GF_BAD_PARAM;
507 
508 	//set to current sample if we don't find a RAP
509 	*sampleNumber = searchFromSample;
510 
511 	//this is not the exact sample, but the prev move to next sample if enough samples....
512 	if ((mode == GF_ISOM_SEARCH_SYNC_FORWARD) && (searchFromSample == stbl->SampleSize->sampleCount)) {
513 		return GF_OK;
514 	}
515 	if ((mode == GF_ISOM_SEARCH_SYNC_BACKWARD) && !searchFromSample) {
516 		*sampleNumber = 1;
517 		return GF_OK;
518 	}
519 	//get the entry
520 	stbl_GetSampleRAP(stbl->SyncSample, searchFromSample, &isRAP, &prev, &next);
521 	if (isRAP) {
522 		(*sampleNumber) = searchFromSample;
523 		return GF_OK;
524 	}
525 
526 	/*check sample groups - prev & next are overwritten if RAP group is found, but are not re-initialized otherwise*/
527 	stbl_SearchSAPs(stbl, searchFromSample, &isRAP, &prev_in_sap, &next_in_sap);
528 	if (isRAP) {
529 		(*sampleNumber) = searchFromSample;
530 		return GF_OK;
531 	}
532 
533 	if (prev_in_sap > prev)
534 		prev = prev_in_sap;
535 	if (next_in_sap < next)
536 		next = next_in_sap;
537 
538 	//nothing yet, go for next time...
539 	if (mode == GF_ISOM_SEARCH_SYNC_FORWARD) {
540 		if (next) *sampleNumber = next;
541 	}
542 	else {
543 		if (prev) *sampleNumber = prev;
544 	}
545 
546 	return GF_OK;
547 }
548 
549 //create a DataReference if not existing (only for WRITE-edit mode)
Media_FindDataRef(GF_DataReferenceBox * dref,char * URLname,char * URNname,u32 * dataRefIndex)550 GF_Err Media_FindDataRef(GF_DataReferenceBox *dref, char *URLname, char *URNname, u32 *dataRefIndex)
551 {
552 	u32 i;
553 	GF_DataEntryURLBox *entry;
554 
555 	if (!dref) return GF_BAD_PARAM;
556 	*dataRefIndex = 0;
557 	i = 0;
558 	while ((entry = (GF_DataEntryURLBox*)gf_list_enum(dref->other_boxes, &i))) {
559 		if (entry->type == GF_ISOM_BOX_TYPE_URL) {
560 			//self-contained case
561 			if (entry->flags == 1) {
562 				//if nothing specified, get the dataRef
563 				if (!URLname && !URNname) {
564 					*dataRefIndex = i;
565 					return GF_OK;
566 				}
567 			}
568 			else {
569 				//OK, check if we have URL
570 				if (URLname && !strcmp(URLname, entry->location)) {
571 					*dataRefIndex = i;
572 					return GF_OK;
573 				}
574 			}
575 		}
576 		else {
577 			//this is a URN one, only check the URN name (URL optional)
578 			if (URNname && !strcmp(URNname, ((GF_DataEntryURNBox *)entry)->nameURN)) {
579 				*dataRefIndex = i;
580 				return GF_OK;
581 			}
582 		}
583 	}
584 	return GF_OK;
585 }
586 
587 //Get the total media duration based on the TimeToSample table
Media_SetDuration(GF_TrackBox * trak)588 GF_Err Media_SetDuration(GF_TrackBox *trak)
589 {
590 	GF_Err e;
591 	GF_ESD *esd;
592 	u64 DTS;
593 	GF_SttsEntry *ent;
594 	u32 nbSamp;
595 
596 	if (!trak->Media->information->sampleTable->SampleSize || !trak->Media->information->sampleTable->TimeToSample)
597 		return GF_ISOM_INVALID_FILE;
598 
599 	nbSamp = trak->Media->information->sampleTable->SampleSize->sampleCount;
600 
601 	//we need to check how many samples we have.
602 	// == 1 -> last sample duration == default duration
603 	// > 1 -> last sample duration == prev sample duration
604 	switch (nbSamp) {
605 	case 0:
606 		trak->Media->mediaHeader->duration = 0;
607 		if (Track_IsMPEG4Stream(trak->Media->handler->handlerType)) {
608 			Media_GetESD(trak->Media, 1, &esd, 1);
609 			if (esd && esd->URLString) trak->Media->mediaHeader->duration = (u64)-1;
610 		}
611 		return GF_OK;
612 
613 		//	case 1:
614 		//		trak->Media->mediaHeader->duration = trak->Media->mediaHeader->timeScale;
615 		//		return GF_OK;
616 
617 	default:
618 		//we assume a constant frame rate for the media and assume the last sample
619 		//will be hold the same time as the prev one
620 		e = stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, nbSamp, &DTS);
621 		if (e < 0) {
622 			return e;
623 		}
624 		if (trak->Media->information->sampleTable->TimeToSample->nb_entries > 0) {
625 			ent = &trak->Media->information->sampleTable->TimeToSample->entries[trak->Media->information->sampleTable->TimeToSample->nb_entries - 1];
626 		}
627 		else {
628 			ent = NULL;
629 		}
630 		trak->Media->mediaHeader->duration = DTS;
631 #ifndef	GPAC_DISABLE_ISOM_FRAGMENTS
632 		trak->Media->mediaHeader->duration += trak->dts_at_seg_start;
633 #endif
634 
635 
636 #if 1
637 		if (ent) trak->Media->mediaHeader->duration += ent->sampleDelta;
638 #else
639 		if (!ent) {
640 			u64 DTSprev;
641 			stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, nbSamp - 1, &DTSprev);
642 			trak->Media->mediaHeader->duration += (DTS - DTSprev);
643 		}
644 		else {
645 #ifndef GPAC_DISABLE_ISOM_WRITE
646 			if (trak->moov->mov->editFileMap && trak->Media->information->sampleTable->CompositionOffset) {
647 				u32 count, i;
648 				u64 max_ts;
649 				GF_DttsEntry *cts_ent;
650 				GF_CompositionOffsetBox *ctts = trak->Media->information->sampleTable->CompositionOffset;
651 				if (ctts->w_LastSampleNumber == nbSamp) {
652 					count = gf_list_count(ctts->entryList);
653 					max_ts = trak->Media->mediaHeader->duration;
654 					while (count) {
655 						count -= 1;
656 						cts_ent = gf_list_get(ctts->entryList, count);
657 						if (nbSamp<cts_ent->sampleCount) break;
658 
659 						for (i = 0; i<cts_ent->sampleCount; i++) {
660 							stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, nbSamp - i, &DTS);
661 							if ((s32)cts_ent->decodingOffset < 0) max_ts = DTS;
662 							else max_ts = DTS + cts_ent->decodingOffset;
663 							if (max_ts >= trak->Media->mediaHeader->duration) {
664 								trak->Media->mediaHeader->duration = max_ts;
665 							}
666 							else {
667 								break;
668 							}
669 						}
670 						if (max_ts<trak->Media->mediaHeader->duration) {
671 							break;
672 						}
673 						nbSamp -= cts_ent->sampleCount;
674 					}
675 				}
676 			}
677 #endif /*GPAC_DISABLE_ISOM_WRITE*/
678 			trak->Media->mediaHeader->duration += ent->sampleDelta;
679 		}
680 #endif
681 		return GF_OK;
682 	}
683 }
684 
685 
686 
687 
688 #ifndef GPAC_DISABLE_ISOM_WRITE
689 
690 
Media_CreateDataRef(GF_DataReferenceBox * dref,char * URLname,char * URNname,u32 * dataRefIndex)691 GF_Err Media_CreateDataRef(GF_DataReferenceBox *dref, char *URLname, char *URNname, u32 *dataRefIndex)
692 {
693 	GF_Err e;
694 	GF_DataEntryURLBox *entry;
695 
696 	GF_Err dref_AddDataEntry(GF_DataReferenceBox *ptr, GF_Box *entry);
697 
698 	if (!URLname && !URNname) {
699 		//THIS IS SELF CONTAIN, create a regular entry if needed
700 		entry = (GF_DataEntryURLBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_URL);
701 		entry->location = NULL;
702 		entry->flags = 0;
703 		entry->flags |= 1;
704 		e = dref_AddDataEntry(dref, (GF_Box *)entry);
705 		if (e) return e;
706 		*dataRefIndex = gf_list_count(dref->other_boxes);
707 		return GF_OK;
708 	}
709 	else if (!URNname && URLname) {
710 		//THIS IS URL
711 		entry = (GF_DataEntryURLBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_URL);
712 		entry->flags = 0;
713 		entry->location = (char*)gf_malloc(strlen(URLname) + 1);
714 		if (!entry->location) {
715 			gf_isom_box_del((GF_Box *)entry);
716 			return GF_OUT_OF_MEM;
717 		}
718 		strcpy(entry->location, URLname);
719 		e = dref_AddDataEntry(dref, (GF_Box *)entry);
720 		if (e) return e;
721 		*dataRefIndex = gf_list_count(dref->other_boxes);
722 		return GF_OK;
723 	}
724 	else {
725 		//THIS IS URN
726 		entry = (GF_DataEntryURLBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_URN);
727 		((GF_DataEntryURNBox *)entry)->flags = 0;
728 		((GF_DataEntryURNBox *)entry)->nameURN = (char*)gf_malloc(strlen(URNname) + 1);
729 		if (!((GF_DataEntryURNBox *)entry)->nameURN) {
730 			gf_isom_box_del((GF_Box *)entry);
731 			return GF_OUT_OF_MEM;
732 		}
733 		strcpy(((GF_DataEntryURNBox *)entry)->nameURN, URNname);
734 		//check for URL
735 		if (URLname) {
736 			((GF_DataEntryURNBox *)entry)->location = (char*)gf_malloc(strlen(URLname) + 1);
737 			if (!((GF_DataEntryURNBox *)entry)->location) {
738 				gf_isom_box_del((GF_Box *)entry);
739 				return GF_OUT_OF_MEM;
740 			}
741 			strcpy(((GF_DataEntryURNBox *)entry)->location, URLname);
742 		}
743 		e = dref_AddDataEntry(dref, (GF_Box *)entry);
744 		if (e) return e;
745 		*dataRefIndex = gf_list_count(dref->other_boxes);
746 		return GF_OK;
747 	}
748 	return GF_OK;
749 }
750 
751 
Media_AddSample(GF_MediaBox * mdia,u64 data_offset,const GF_ISOSample * sample,u32 StreamDescIndex,u32 syncShadowNumber)752 GF_Err Media_AddSample(GF_MediaBox *mdia, u64 data_offset, const GF_ISOSample *sample, u32 StreamDescIndex, u32 syncShadowNumber)
753 {
754 	GF_Err e;
755 	GF_SampleTableBox *stbl;
756 	u32 sampleNumber, i;
757 	if (!mdia || !sample) return GF_BAD_PARAM;
758 
759 	stbl = mdia->information->sampleTable;
760 
761 	//get a valid sampleNumber for this new guy
762 	e = stbl_AddDTS(stbl, sample->DTS, &sampleNumber, mdia->mediaHeader->timeScale);
763 	if (e) return e;
764 
765 	//add size
766 	e = stbl_AddSize(stbl->SampleSize, sampleNumber, sample->dataLength);
767 	if (e) return e;
768 
769 	//adds CTS offset
770 	if (sample->CTS_Offset) {
771 		//if we don't have a CTS table, add it...
772 		if (!stbl->CompositionOffset) stbl->CompositionOffset = (GF_CompositionOffsetBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CTTS);
773 		//then add our CTS (the prev samples with no CTS offset will be automatically added...
774 		e = stbl_AddCTS(stbl, sampleNumber, sample->CTS_Offset);
775 		if (e) return e;
776 	}
777 	else if (stbl->CompositionOffset) {
778 		e = stbl_AddCTS(stbl, sampleNumber, sample->CTS_Offset);
779 		if (e) return e;
780 	}
781 
782 	//The first non sync sample we see must create a syncTable
783 	if (sample->IsRAP) {
784 		//insert it only if we have a sync table and if we have an IDR slice
785 		if (stbl->SyncSample && (sample->IsRAP == RAP)) {
786 			e = stbl_AddRAP(stbl->SyncSample, sampleNumber);
787 			if (e) return e;
788 		}
789 	}
790 	else {
791 		//non-sync sample. Create a SyncSample table if needed
792 		if (!stbl->SyncSample) {
793 			stbl->SyncSample = (GF_SyncSampleBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STSS);
794 			//all the prev samples are sync
795 			for (i = 0; i<stbl->SampleSize->sampleCount; i++) {
796 				if (i + 1 != sampleNumber) {
797 					e = stbl_AddRAP(stbl->SyncSample, i + 1);
798 					if (e) return e;
799 				}
800 			}
801 		}
802 	}
803 	if (sample->IsRAP == RAP_REDUNDANT) {
804 		e = stbl_AddRedundant(stbl, sampleNumber);
805 		if (e) return e;
806 	}
807 
808 	//and update the chunks
809 	e = stbl_AddChunkOffset(mdia, sampleNumber, StreamDescIndex, data_offset);
810 	if (e) return e;
811 
812 	if (!syncShadowNumber) return GF_OK;
813 	if (!stbl->ShadowSync) stbl->ShadowSync = (GF_ShadowSyncBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STSH);
814 	return stbl_AddShadow(mdia->information->sampleTable->ShadowSync, sampleNumber, syncShadowNumber);
815 }
816 
817 
UpdateSample(GF_MediaBox * mdia,u32 sampleNumber,u32 size,s32 CTS,u64 offset,u8 isRap)818 static GF_Err UpdateSample(GF_MediaBox *mdia, u32 sampleNumber, u32 size, s32 CTS, u64 offset, u8 isRap)
819 {
820 	u32 i;
821 	GF_SampleTableBox *stbl = mdia->information->sampleTable;
822 
823 	//set size, offset, RAP, CTS ...
824 	stbl_SetSampleSize(stbl->SampleSize, sampleNumber, size);
825 	stbl_SetChunkOffset(mdia, sampleNumber, offset);
826 
827 	//do we have a CTS?
828 	if (stbl->CompositionOffset) {
829 		stbl_SetSampleCTS(stbl, sampleNumber, CTS);
830 	}
831 	else {
832 		//do we need one ??
833 		if (CTS) {
834 			stbl->CompositionOffset = (GF_CompositionOffsetBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CTTS);
835 			stbl_AddCTS(stbl, sampleNumber, CTS);
836 		}
837 	}
838 	//do we have a sync ???
839 	if (stbl->SyncSample) {
840 		stbl_SetSampleRAP(stbl->SyncSample, sampleNumber, isRap);
841 	}
842 	else {
843 		//do we need one
844 		if (!isRap) {
845 			stbl->SyncSample = (GF_SyncSampleBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STSS);
846 			//what a pain: all the sample we had have to be sync ...
847 			for (i = 0; i<stbl->SampleSize->sampleCount; i++) {
848 				if (i + 1 != sampleNumber) stbl_AddRAP(stbl->SyncSample, i + 1);
849 			}
850 		}
851 	}
852 	if (isRap == 2) {
853 		stbl_SetRedundant(stbl, sampleNumber);
854 	}
855 	return GF_OK;
856 }
857 
Media_UpdateSample(GF_MediaBox * mdia,u32 sampleNumber,GF_ISOSample * sample,Bool data_only)858 GF_Err Media_UpdateSample(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample *sample, Bool data_only)
859 {
860 	GF_Err e;
861 	u32 drefIndex, chunkNum, descIndex;
862 	u64 newOffset, DTS;
863 	u8 isEdited;
864 	GF_DataEntryURLBox *Dentry;
865 	GF_SampleTableBox *stbl;
866 
867 	if (!mdia || !sample || !sampleNumber || !mdia->mediaTrack->moov->mov->editFileMap)
868 		return GF_BAD_PARAM;
869 
870 	stbl = mdia->information->sampleTable;
871 
872 	if (!data_only) {
873 		//check we have the sampe dts
874 		e = stbl_GetSampleDTS(stbl->TimeToSample, sampleNumber, &DTS);
875 		if (e) return e;
876 		if (DTS != sample->DTS) return GF_BAD_PARAM;
877 	}
878 
879 	//get our infos
880 	stbl_GetSampleInfos(stbl, sampleNumber, &newOffset, &chunkNum, &descIndex, &isEdited);
881 
882 	//then check the data ref
883 	e = Media_GetSampleDesc(mdia, descIndex, NULL, &drefIndex);
884 	if (e) return e;
885 	Dentry = (GF_DataEntryURLBox*)gf_list_get(mdia->information->dataInformation->dref->other_boxes, drefIndex - 1);
886 	if (!Dentry) return GF_ISOM_INVALID_FILE;
887 
888 	if (Dentry->flags != 1) return GF_BAD_PARAM;
889 
890 	//MEDIA DATA EDIT: write this new sample to the edit temp file
891 	newOffset = gf_isom_datamap_get_offset(mdia->mediaTrack->moov->mov->editFileMap);
892 	if (sample->dataLength) {
893 		e = gf_isom_datamap_add_data(mdia->mediaTrack->moov->mov->editFileMap, sample->data, sample->dataLength);
894 		if (e) return e;
895 	}
896 
897 	if (data_only) {
898 		stbl_SetSampleSize(stbl->SampleSize, sampleNumber, sample->dataLength);
899 		return stbl_SetChunkOffset(mdia, sampleNumber, newOffset);
900 	}
901 	return UpdateSample(mdia, sampleNumber, sample->dataLength, sample->CTS_Offset, newOffset, sample->IsRAP);
902 }
903 
Media_UpdateSampleReference(GF_MediaBox * mdia,u32 sampleNumber,GF_ISOSample * sample,u64 data_offset)904 GF_Err Media_UpdateSampleReference(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample *sample, u64 data_offset)
905 {
906 	GF_Err e;
907 	u32 drefIndex, chunkNum, descIndex;
908 	u64 off, DTS;
909 	u8 isEdited;
910 	GF_DataEntryURLBox *Dentry;
911 	GF_SampleTableBox *stbl;
912 
913 	if (!mdia) return GF_BAD_PARAM;
914 	stbl = mdia->information->sampleTable;
915 
916 	//check we have the sampe dts
917 	e = stbl_GetSampleDTS(stbl->TimeToSample, sampleNumber, &DTS);
918 	if (e) return e;
919 	if (DTS != sample->DTS) return GF_BAD_PARAM;
920 
921 	//get our infos
922 	stbl_GetSampleInfos(stbl, sampleNumber, &off, &chunkNum, &descIndex, &isEdited);
923 
924 	//then check the data ref
925 	e = Media_GetSampleDesc(mdia, descIndex, NULL, &drefIndex);
926 	if (e) return e;
927 	Dentry = (GF_DataEntryURLBox*)gf_list_get(mdia->information->dataInformation->dref->other_boxes, drefIndex - 1);
928 	if (!Dentry) return GF_ISOM_INVALID_FILE;
929 
930 	//we only modify self-contained data
931 	if (Dentry->flags == 1) return GF_ISOM_INVALID_MODE;
932 
933 	//and we don't modify the media data
934 	return UpdateSample(mdia, sampleNumber, sample->dataLength, sample->CTS_Offset, data_offset, sample->IsRAP);
935 }
936 
937 
938 #endif	/*GPAC_DISABLE_ISOM_WRITE*/
939 
940 #endif /*GPAC_DISABLE_ISOM*/
941