Line data Source code
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 / Media Tools 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/media_dev.h>
27 : #include <gpac/base_coding.h>
28 : #include <gpac/mpeg4_odf.h>
29 : #include <gpac/constants.h>
30 : #include <gpac/maths.h>
31 : #include <gpac/internal/ietf_dev.h>
32 :
33 : #ifndef GPAC_DISABLE_ISOM
34 :
35 73 : void gf_media_get_sample_average_infos(GF_ISOFile *file, u32 Track, u32 *avgSize, u32 *MaxSize, u32 *TimeDelta, u32 *maxCTSDelta, u32 *const_duration, u32 *bandwidth)
36 : {
37 : u32 i, count, ts_diff;
38 : u64 prevTS, tdelta;
39 : Double bw;
40 : GF_ISOSample *samp;
41 :
42 73 : *avgSize = *MaxSize = 0;
43 73 : *TimeDelta = 0;
44 73 : *maxCTSDelta = 0;
45 : bw = 0;
46 : prevTS = 0;
47 : tdelta = 0;
48 :
49 73 : count = gf_isom_get_sample_count(file, Track);
50 73 : if (!count) return;
51 73 : *const_duration = 0;
52 :
53 65447 : for (i=0; i<count; i++) {
54 65301 : samp = gf_isom_get_sample_info(file, Track, i+1, NULL, NULL);
55 65301 : if (!samp) break;
56 :
57 : //get the size
58 65301 : *avgSize += samp->dataLength;
59 65301 : if (*MaxSize < samp->dataLength) *MaxSize = samp->dataLength;
60 65301 : ts_diff = (u32) (samp->DTS+samp->CTS_Offset - prevTS);
61 : //get the time
62 65301 : tdelta += ts_diff;
63 :
64 65301 : if (i==1) {
65 66 : *const_duration = ts_diff;
66 65235 : } else if ( (i<count-1) && (*const_duration != ts_diff) ) {
67 10444 : *const_duration = 0;
68 : }
69 :
70 65301 : prevTS = samp->DTS+samp->CTS_Offset;
71 65301 : bw += 8*samp->dataLength;
72 :
73 : //get the CTS delta
74 65301 : if ((samp->CTS_Offset>=0) && ((u32)samp->CTS_Offset > *maxCTSDelta))
75 47 : *maxCTSDelta = samp->CTS_Offset;
76 65301 : gf_isom_sample_del(&samp);
77 : }
78 73 : if (count>1) *TimeDelta = (u32) (tdelta/ (count-1) );
79 7 : else *TimeDelta = (u32) tdelta;
80 73 : *avgSize /= count;
81 73 : bw *= gf_isom_get_media_timescale(file, Track);
82 73 : bw /= (s64) gf_isom_get_media_duration(file, Track);
83 73 : bw /= 1000;
84 73 : (*bandwidth) = (u32) (bw+0.5);
85 :
86 : //delta is NOT an average, we need to know exactly how many bits are
87 : //needed to encode CTS-DTS for ANY samples
88 : }
89 :
90 :
91 : #ifndef GPAC_DISABLE_ISOM_HINTING
92 :
93 : /*RTP track hinter*/
94 : struct __tag_isom_hinter
95 : {
96 : GF_ISOFile *file;
97 : /*IDs are kept for mp4 hint sample building*/
98 : u32 TrackNum, TrackID, HintTrack, HintID;
99 : /*current Hint sample and associated RTP time*/
100 : u32 HintSample, RTPTime;
101 :
102 : /*track has composition time offset*/
103 : Bool has_ctts;
104 : /*remember if first SL packet in RTP packet is RAP*/
105 : u8 SampleIsRAP;
106 : u32 base_offset_in_sample;
107 : u32 OrigTimeScale;
108 : /*rtp builder*/
109 : GP_RTPPacketizer *rtp_p;
110 :
111 : u32 bandwidth, nb_chan;
112 :
113 : /*NALU size for H264/AVC*/
114 : u32 avc_nalu_size;
115 :
116 : /*stats*/
117 : u32 TotalSample, CurrentSample;
118 : };
119 :
120 :
121 : /*
122 : offset for group ID for hint tracks in SimpleAV mode when all media data
123 : is copied to the hint track (no use interleaving hint and original in this case)
124 : this offset is applied internally by the track hinter. Thus you shouldn't
125 : specify a GroupID >= OFFSET_HINT_GROUP_ID if you want the lib to perform efficient
126 : interleaving in any cases (referenced or copied media)
127 : */
128 : #define OFFSET_HINT_GROUP_ID 0x8000
129 :
130 0 : void InitSL_RTP(GF_SLConfig *slc)
131 : {
132 : memset(slc, 0, sizeof(GF_SLConfig));
133 73 : slc->tag = GF_ODF_SLC_TAG;
134 73 : slc->useTimestampsFlag = 1;
135 73 : slc->timestampLength = 32;
136 0 : }
137 :
138 0 : void InitSL_NULL(GF_SLConfig *slc)
139 : {
140 : memset(slc, 0, sizeof(GF_SLConfig));
141 2 : slc->tag = GF_ODF_SLC_TAG;
142 0 : slc->predefined = 0x01;
143 0 : }
144 :
145 :
146 :
147 68137 : void MP4T_OnPacketDone(void *cbk, GF_RTPHeader *header)
148 : {
149 : u8 disposable;
150 : GF_RTPHinter *tkHint = (GF_RTPHinter *)cbk;
151 68137 : if (!tkHint || !tkHint->HintSample) return;
152 : assert(header->TimeStamp == tkHint->RTPTime);
153 :
154 : disposable = 0;
155 68137 : if (tkHint->avc_nalu_size) {
156 5679 : disposable = tkHint->rtp_p->avc_non_idr ? 1 : 0;
157 : }
158 : /*for all other, assume that CTS=DTS means B-frame -> disposable*/
159 62458 : else if (tkHint->has_ctts && (tkHint->rtp_p->sl_header.compositionTimeStamp==tkHint->rtp_p->sl_header.decodingTimeStamp)) {
160 : disposable = 1;
161 : }
162 :
163 68137 : gf_isom_rtp_packet_set_flags(tkHint->file, tkHint->HintTrack, 0, 0, header->Marker, disposable, 0);
164 : }
165 :
166 :
167 75229 : void MP4T_OnDataRef(void *cbk, u32 payload_size, u32 offset_from_orig)
168 : {
169 : GF_RTPHinter *tkHint = (GF_RTPHinter *)cbk;
170 75229 : if (!tkHint || !payload_size) return;
171 :
172 : /*add reference*/
173 75229 : gf_isom_hint_sample_data(tkHint->file, tkHint->HintTrack, tkHint->TrackID,
174 75229 : tkHint->CurrentSample, (u16) payload_size, offset_from_orig + tkHint->base_offset_in_sample,
175 : NULL, 0);
176 : }
177 :
178 59250 : void MP4T_OnData(void *cbk, u8 *data, u32 data_size, Bool is_header)
179 : {
180 : u8 at_begin;
181 : GF_RTPHinter *tkHint = (GF_RTPHinter *)cbk;
182 59250 : if (!data_size) return;
183 :
184 49105 : at_begin = is_header ? 1 : 0;
185 49105 : if (data_size <= 14) {
186 49022 : gf_isom_hint_direct_data(tkHint->file, tkHint->HintTrack, data, data_size, at_begin);
187 : } else {
188 83 : gf_isom_hint_sample_data(tkHint->file, tkHint->HintTrack, tkHint->HintID, 0, (u16) data_size, 0, data, at_begin);
189 : }
190 : }
191 :
192 :
193 68137 : void MP4T_OnNewPacket(void *cbk, GF_RTPHeader *header)
194 : {
195 : s32 res;
196 : GF_RTPHinter *tkHint = (GF_RTPHinter *)cbk;
197 68137 : if (!tkHint) return;
198 :
199 68137 : res = (s32) (tkHint->rtp_p->sl_header.compositionTimeStamp - tkHint->rtp_p->sl_header.decodingTimeStamp);
200 : assert( !res || tkHint->has_ctts);
201 : /*do we need a new sample*/
202 68137 : if (!tkHint->HintSample || (tkHint->RTPTime != header->TimeStamp)) {
203 : /*close current sample*/
204 59170 : if (tkHint->HintSample) gf_isom_end_hint_sample(tkHint->file, tkHint->HintTrack, tkHint->SampleIsRAP);
205 :
206 : /*start new sample: We use DTS as the sampling instant (RTP TS) to make sure
207 : all packets are sent in order*/
208 59170 : gf_isom_begin_hint_sample(tkHint->file, tkHint->HintTrack, 1, header->TimeStamp-res);
209 59170 : tkHint->HintSample ++;
210 59170 : tkHint->RTPTime = header->TimeStamp;
211 59170 : tkHint->SampleIsRAP = tkHint->rtp_p->sl_config.hasRandomAccessUnitsOnlyFlag ? 1 : tkHint->rtp_p->sl_header.randomAccessPointFlag;
212 : }
213 : /*create an RTP Packet with the appropriated marker flag - note: the flags are temp ones,
214 : they are set when the full packet is signaled (to handle multi AUs per RTP)*/
215 68137 : gf_isom_rtp_packet_begin(tkHint->file, tkHint->HintTrack, 0, 0, 0, header->Marker, header->PayloadType, 0, 0, header->SequenceNumber);
216 : /*Add the delta TS to make sure RTP TS is indeed the CTS (sampling time)*/
217 68137 : if (res) gf_isom_rtp_packet_set_offset(tkHint->file, tkHint->HintTrack, res);
218 : }
219 :
220 :
221 : GF_EXPORT
222 74 : GF_RTPHinter *gf_hinter_track_new(GF_ISOFile *file, u32 TrackNum,
223 : u32 Path_MTU, u32 max_ptime, u32 default_rtp_rate, u32 flags, u8 PayloadID,
224 : Bool copy_media, u32 InterleaveGroupID, u8 InterleaveGroupPriority, GF_Err *e)
225 : {
226 :
227 : GF_SLConfig my_sl;
228 : u32 descIndex, MinSize, MaxSize, avgTS, streamType, codecid, const_dur, nb_ch, maxDTSDelta;
229 : u8 OfficialPayloadID;
230 : u32 TrackMediaSubType, TrackMediaType, hintType, nbEdts, required_rate, force_dts_delta, avc_nalu_size, PL_ID, bandwidth, IV_length, KI_length;
231 : const char *url, *urn;
232 : char *mpeg4mode;
233 : Bool is_crypted, has_mpeg4_mapping;
234 : GF_RTPHinter *tmp;
235 : GF_ESD *esd;
236 :
237 74 : *e = GF_BAD_PARAM;
238 74 : if (!file || !TrackNum || !gf_isom_get_track_id(file, TrackNum)) return NULL;
239 :
240 74 : if (!gf_isom_get_sample_count(file, TrackNum)) {
241 0 : *e = GF_OK;
242 0 : return NULL;
243 : }
244 74 : *e = GF_NOT_SUPPORTED;
245 74 : nbEdts = gf_isom_get_edits_count(file, TrackNum);
246 74 : if (nbEdts>1) {
247 : u64 et, sd, mt;
248 : GF_ISOEditType em;
249 0 : gf_isom_get_edit(file, TrackNum, 1, &et, &sd, &mt, &em);
250 0 : if ((nbEdts>2) || (em!=GF_ISOM_EDIT_EMPTY)) {
251 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[rtp hinter] Cannot hint track whith EditList\n"));
252 0 : return NULL;
253 : }
254 : }
255 74 : if (nbEdts) gf_isom_remove_edits(file, TrackNum);
256 :
257 74 : if (!gf_isom_is_track_enabled(file, TrackNum)) return NULL;
258 :
259 : /*by default NO PL signaled*/
260 : PL_ID = 0;
261 : OfficialPayloadID = 0;
262 : force_dts_delta = 0;
263 : streamType = 0;
264 : mpeg4mode = NULL;
265 74 : required_rate = 0;
266 : is_crypted = 0;
267 74 : IV_length = KI_length = 0;
268 : codecid = 0;
269 74 : nb_ch = 0;
270 : avc_nalu_size = 0;
271 : has_mpeg4_mapping = 1;
272 74 : const_dur = 0;
273 74 : bandwidth=0;
274 74 : TrackMediaType = gf_isom_get_media_type(file, TrackNum);
275 :
276 : /*for max compatibility with QT*/
277 74 : if (!default_rtp_rate) default_rtp_rate = 90000;
278 :
279 : /*timed-text is a bit special, we support multiple stream descriptions & co*/
280 74 : if ( (TrackMediaType==GF_ISOM_MEDIA_TEXT) || (TrackMediaType==GF_ISOM_MEDIA_SUBT)) {
281 : hintType = GF_RTP_PAYT_3GPP_TEXT;
282 : codecid = GF_CODECID_TEXT_MPEG4;
283 : streamType = GF_STREAM_TEXT;
284 : /*fixme - this works cos there's only one PL for text in mpeg4 at the current time*/
285 : PL_ID = 0x10;
286 : } else {
287 67 : if (gf_isom_get_sample_description_count(file, TrackNum) > 1) return NULL;
288 :
289 67 : TrackMediaSubType = gf_isom_get_media_subtype(file, TrackNum, 1);
290 67 : switch (TrackMediaSubType) {
291 5 : case GF_ISOM_SUBTYPE_MPEG4_CRYP:
292 : is_crypted = 1;
293 41 : case GF_ISOM_SUBTYPE_MPEG4:
294 41 : esd = gf_isom_get_esd(file, TrackNum, 1);
295 : hintType = GF_RTP_PAYT_MPEG4;
296 41 : if (esd && esd->decoderConfig) {
297 41 : streamType = esd->decoderConfig->streamType;
298 41 : codecid = esd->decoderConfig->objectTypeIndication;
299 41 : if (esd->URLString) hintType = 0;
300 : /*AAC*/
301 41 : if ((streamType==GF_STREAM_AUDIO)
302 16 : && esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data
303 : /*(nb: we use mpeg4 for MPEG-2 AAC)*/
304 31 : && ((codecid==GF_CODECID_AAC_MPEG4) || (codecid==GF_CODECID_AAC_MPEG2_MP) || (codecid==GF_CODECID_AAC_MPEG2_LCP) || (codecid==GF_CODECID_AAC_MPEG2_SSRP)) ) {
305 :
306 : u32 sample_rate;
307 : GF_M4ADecSpecInfo a_cfg;
308 15 : gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &a_cfg);
309 15 : nb_ch = a_cfg.nb_chan;
310 15 : sample_rate = a_cfg.base_sr;
311 15 : PL_ID = a_cfg.audioPL;
312 15 : switch (a_cfg.base_object_type) {
313 14 : case GF_M4A_AAC_MAIN:
314 : case GF_M4A_AAC_LC:
315 14 : if (flags & GP_RTP_PCK_USE_LATM_AAC) {
316 : hintType = GF_RTP_PAYT_LATM;
317 : break;
318 : }
319 : case GF_M4A_AAC_SBR:
320 : case GF_M4A_AAC_PS:
321 : case GF_M4A_AAC_LTP:
322 : case GF_M4A_AAC_SCALABLE:
323 : case GF_M4A_ER_AAC_LC:
324 : case GF_M4A_ER_AAC_LTP:
325 : case GF_M4A_ER_AAC_SCALABLE:
326 : mpeg4mode = "AAC";
327 14 : break;
328 : case GF_M4A_CELP:
329 : case GF_M4A_ER_CELP:
330 : mpeg4mode = "CELP";
331 : break;
332 : }
333 15 : required_rate = sample_rate;
334 : }
335 : /*MPEG1/2 audio*/
336 26 : else if ((streamType==GF_STREAM_AUDIO) && ((codecid==GF_CODECID_MPEG2_PART3) || (codecid==GF_CODECID_MPEG_AUDIO))) {
337 0 : GF_ISOSample *samp = NULL;
338 0 : if (!is_crypted)
339 0 : samp = gf_isom_get_sample(file, TrackNum, 1, NULL);
340 :
341 0 : if (samp && (samp->dataLength>3)) {
342 0 : u32 hdr = GF_4CC((u32)samp->data[0], (u8)samp->data[1], (u8)samp->data[2], (u8)samp->data[3]);
343 0 : nb_ch = gf_mp3_num_channels(hdr);
344 : hintType = GF_RTP_PAYT_MPEG12_AUDIO;
345 : /*use official RTP/AVP payload type*/
346 : OfficialPayloadID = 14;
347 0 : required_rate = 90000;
348 : }
349 : /*encrypted MP3 must be sent through MPEG-4 generic to signal all ISMACryp stuff*/
350 : else {
351 : u32 sample_rate;
352 0 : gf_isom_get_audio_info(file, TrackNum, 1, &sample_rate, &nb_ch, NULL);
353 0 : required_rate = sample_rate;
354 : }
355 0 : if (samp)
356 0 : gf_isom_sample_del(&samp);
357 :
358 : }
359 : /*QCELP audio*/
360 26 : else if ((streamType==GF_STREAM_AUDIO) && (codecid==GF_CODECID_QCELP)) {
361 : hintType = GF_RTP_PAYT_QCELP;
362 : OfficialPayloadID = 12;
363 0 : required_rate = 8000;
364 : streamType = GF_STREAM_AUDIO;
365 0 : nb_ch = 1;
366 : }
367 : /*EVRC/SVM audio*/
368 26 : else if ((streamType==GF_STREAM_AUDIO) && ((codecid==GF_CODECID_EVRC) || (codecid==GF_CODECID_SMV)) ) {
369 : hintType = GF_RTP_PAYT_EVRC_SMV;
370 0 : required_rate = 8000;
371 : streamType = GF_STREAM_AUDIO;
372 0 : nb_ch = 1;
373 : }
374 : /*visual streams*/
375 26 : else if (streamType==GF_STREAM_VISUAL) {
376 19 : if ((codecid==GF_CODECID_MPEG4_PART2) && esd->decoderConfig->decoderSpecificInfo) {
377 : GF_M4VDecSpecInfo dsi;
378 5 : gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi);
379 5 : PL_ID = dsi.VideoPL;
380 : }
381 : /*MPEG1/2 video*/
382 19 : if ( ((codecid>=GF_CODECID_MPEG2_SIMPLE) && (codecid<=GF_CODECID_MPEG2_422)) || (codecid==GF_CODECID_MPEG1)) {
383 2 : if (!is_crypted) {
384 : hintType = GF_RTP_PAYT_MPEG12_VIDEO;
385 : OfficialPayloadID = 32;
386 : }
387 : }
388 : /*for ISMA*/
389 19 : if (is_crypted) {
390 : /*that's another pain with ISMACryp, even if no B-frames the DTS is signaled...*/
391 4 : if (codecid==GF_CODECID_MPEG4_PART2) force_dts_delta = 22;
392 4 : else if ((codecid==GF_CODECID_AVC) || (codecid==GF_CODECID_SVC)) {
393 1 : flags &= ~GP_RTP_PCK_USE_MULTI;
394 : force_dts_delta = 22;
395 : }
396 4 : flags |= GP_RTP_PCK_SIGNAL_RAP | GP_RTP_PCK_SIGNAL_TS;
397 : }
398 :
399 19 : required_rate = default_rtp_rate;
400 : }
401 : /*systems streams*/
402 7 : else if (gf_isom_has_sync_shadows(file, TrackNum) || gf_isom_has_sample_dependency(file, TrackNum)) {
403 0 : flags |= GP_RTP_PCK_SYSTEMS_CAROUSEL;
404 : }
405 : }
406 41 : if (esd)
407 41 : gf_odf_desc_del((GF_Descriptor*)esd);
408 : break;
409 1 : case GF_ISOM_SUBTYPE_3GP_H263:
410 : hintType = GF_RTP_PAYT_H263;
411 1 : required_rate = 90000;
412 : streamType = GF_STREAM_VISUAL;
413 : OfficialPayloadID = 34;
414 : /*not 100% compliant (short header is missing) but should still work*/
415 : codecid = GF_CODECID_MPEG4_PART2;
416 : PL_ID = 0x01;
417 1 : break;
418 1 : case GF_ISOM_SUBTYPE_3GP_AMR:
419 1 : required_rate = 8000;
420 : hintType = GF_RTP_PAYT_AMR;
421 : streamType = GF_STREAM_AUDIO;
422 : has_mpeg4_mapping = 0;
423 1 : nb_ch = 1;
424 1 : break;
425 1 : case GF_ISOM_SUBTYPE_3GP_AMR_WB:
426 1 : required_rate = 16000;
427 : hintType = GF_RTP_PAYT_AMR_WB;
428 : streamType = GF_STREAM_AUDIO;
429 : has_mpeg4_mapping = 0;
430 1 : nb_ch = 1;
431 1 : break;
432 11 : case GF_ISOM_SUBTYPE_AVC_H264:
433 : case GF_ISOM_SUBTYPE_AVC2_H264:
434 : case GF_ISOM_SUBTYPE_AVC3_H264:
435 : case GF_ISOM_SUBTYPE_AVC4_H264:
436 : case GF_ISOM_SUBTYPE_SVC_H264:
437 : case GF_ISOM_SUBTYPE_MVC_H264:
438 : {
439 11 : GF_AVCConfig *avcc = gf_isom_avc_config_get(file, TrackNum, 1);
440 11 : GF_AVCConfig *svcc = gf_isom_svc_config_get(file, TrackNum, 1);
441 11 : GF_AVCConfig *mvcc = gf_isom_mvc_config_get(file, TrackNum, 1);
442 :
443 11 : if (!avcc && !svcc && !mvcc) {
444 0 : *e = GF_NON_COMPLIANT_BITSTREAM;
445 0 : return NULL;
446 : }
447 :
448 11 : required_rate = 90000; /* "90 kHz clock rate MUST be used"*/
449 : hintType = GF_RTP_PAYT_H264_AVC;
450 11 : if (TrackMediaSubType==GF_ISOM_SUBTYPE_SVC_H264)
451 : hintType = GF_RTP_PAYT_H264_SVC;
452 11 : else if (TrackMediaSubType==GF_ISOM_SUBTYPE_MVC_H264)
453 : hintType = GF_RTP_PAYT_H264_SVC;
454 : streamType = GF_STREAM_VISUAL;
455 11 : avc_nalu_size = avcc ? avcc->nal_unit_size : svcc ? svcc->nal_unit_size : mvcc->nal_unit_size;
456 : codecid = GF_CODECID_AVC;
457 : PL_ID = 0x0F;
458 11 : gf_odf_avc_cfg_del(avcc);
459 11 : gf_odf_avc_cfg_del(svcc);
460 : }
461 11 : break;
462 1 : case GF_ISOM_SUBTYPE_HVC1:
463 : case GF_ISOM_SUBTYPE_HEV1:
464 : case GF_ISOM_SUBTYPE_HVC2:
465 : case GF_ISOM_SUBTYPE_HEV2:
466 : {
467 1 : GF_HEVCConfig *hevcc = gf_isom_hevc_config_get(file, TrackNum, 1);
468 1 : if (!hevcc) {
469 0 : *e = GF_NON_COMPLIANT_BITSTREAM;
470 0 : return NULL;
471 : }
472 1 : required_rate = 90000; /* "90 kHz clock rate MUST be used"*/
473 : hintType = GF_RTP_PAYT_HEVC;
474 : streamType = GF_STREAM_VISUAL;
475 1 : avc_nalu_size = hevcc->nal_unit_size;
476 : codecid = GF_CODECID_HEVC;
477 : PL_ID = 0x0F;
478 1 : flags |= GP_RTP_PCK_USE_MULTI;
479 1 : gf_odf_hevc_cfg_del(hevcc);
480 1 : break;
481 : }
482 : break;
483 1 : case GF_ISOM_SUBTYPE_3GP_QCELP:
484 1 : required_rate = 8000;
485 : hintType = GF_RTP_PAYT_QCELP;
486 : streamType = GF_STREAM_AUDIO;
487 : codecid = GF_CODECID_QCELP;
488 : OfficialPayloadID = 12;
489 1 : nb_ch = 1;
490 1 : break;
491 1 : case GF_ISOM_SUBTYPE_3GP_EVRC:
492 : case GF_ISOM_SUBTYPE_3GP_SMV:
493 1 : required_rate = 8000;
494 : hintType = GF_RTP_PAYT_EVRC_SMV;
495 : streamType = GF_STREAM_AUDIO;
496 1 : codecid = (TrackMediaSubType==GF_ISOM_SUBTYPE_3GP_EVRC) ? GF_CODECID_EVRC : GF_CODECID_SMV;
497 1 : nb_ch = 1;
498 1 : break;
499 0 : case GF_ISOM_SUBTYPE_3GP_DIMS:
500 : #if GPAC_ENABLE_3GPP_DIMS_RTP
501 : hintType = GF_RTP_PAYT_3GPP_DIMS;
502 : streamType = GF_STREAM_SCENE;
503 : #else
504 : hintType = 0;
505 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTP Packetizer] 3GPP DIMS over RTP disabled in build\n", streamType));
506 : #endif
507 : break;
508 1 : case GF_ISOM_SUBTYPE_AC3:
509 : hintType = GF_RTP_PAYT_AC3;
510 : streamType = GF_STREAM_AUDIO;
511 1 : gf_isom_get_audio_info(file, TrackNum, 1, NULL, &nb_ch, NULL);
512 1 : break;
513 7 : case GF_ISOM_SUBTYPE_MP3:
514 : {
515 7 : GF_ISOSample *samp = gf_isom_get_sample(file, TrackNum, 1, NULL);
516 14 : if (samp && (samp->dataLength>3)) {
517 7 : u32 hdr = GF_4CC((u32)samp->data[0], (u8)samp->data[1], (u8)samp->data[2], (u8)samp->data[3]);
518 7 : nb_ch = gf_mp3_num_channels(hdr);
519 : } else {
520 : u32 bps;
521 0 : gf_isom_get_audio_info(file, TrackNum, 1, &required_rate, &nb_ch, &bps);
522 : }
523 : hintType = GF_RTP_PAYT_MPEG12_AUDIO;
524 : /*use official RTP/AVP payload type*/
525 : OfficialPayloadID = 14;
526 7 : required_rate = 90000;
527 :
528 7 : if (samp)
529 7 : gf_isom_sample_del(&samp);
530 : }
531 7 : break;
532 : default:
533 : /*ERROR*/
534 : hintType = 0;
535 : break;
536 : }
537 : }
538 :
539 : /*not hintable*/
540 74 : if (!hintType) return NULL;
541 : /*we only support self-contained files for hinting*/
542 73 : gf_isom_get_data_reference(file, TrackNum, 1, &url, &urn);
543 73 : if (url || urn) return NULL;
544 :
545 73 : *e = GF_OUT_OF_MEM;
546 73 : GF_SAFEALLOC(tmp, GF_RTPHinter);
547 73 : if (!tmp) return NULL;
548 :
549 : /*override hinter type if requested and possible*/
550 73 : if (has_mpeg4_mapping && (flags & GP_RTP_PCK_FORCE_MPEG4)) {
551 : hintType = GF_RTP_PAYT_MPEG4;
552 : avc_nalu_size = 0;
553 : }
554 : /*use static payload ID if enabled*/
555 73 : else if (OfficialPayloadID && (flags & GP_RTP_PCK_USE_STATIC_ID) ) {
556 : PayloadID = OfficialPayloadID;
557 : }
558 :
559 73 : tmp->file = file;
560 73 : tmp->TrackNum = TrackNum;
561 73 : tmp->avc_nalu_size = avc_nalu_size;
562 73 : tmp->nb_chan = nb_ch;
563 :
564 : /*spatial scalability check*/
565 73 : tmp->has_ctts = gf_isom_has_time_offset(file, TrackNum);
566 :
567 : /*get sample info*/
568 73 : gf_media_get_sample_average_infos(file, TrackNum, &MinSize, &MaxSize, &avgTS, &maxDTSDelta, &const_dur, &bandwidth);
569 :
570 : /*systems carousel: we need at least IDX and RAP signaling*/
571 73 : if (flags & GP_RTP_PCK_SYSTEMS_CAROUSEL) {
572 0 : flags |= GP_RTP_PCK_SIGNAL_RAP;
573 : }
574 :
575 : /*update flags in MultiSL*/
576 73 : if (flags & GP_RTP_PCK_USE_MULTI) {
577 1 : if (MinSize != MaxSize) flags |= GP_RTP_PCK_SIGNAL_SIZE;
578 1 : if (!const_dur) flags |= GP_RTP_PCK_SIGNAL_TS;
579 : }
580 73 : if (tmp->has_ctts) flags |= GP_RTP_PCK_SIGNAL_TS;
581 :
582 : /*default SL for RTP */
583 : InitSL_RTP(&my_sl);
584 :
585 73 : my_sl.timestampResolution = gf_isom_get_media_timescale(file, TrackNum);
586 : /*override clockrate if set*/
587 73 : if (required_rate) {
588 58 : Double sc = required_rate;
589 58 : sc /= my_sl.timestampResolution;
590 58 : maxDTSDelta = (u32) (maxDTSDelta*sc);
591 58 : my_sl.timestampResolution = required_rate;
592 : }
593 : /*switch to RTP TS*/
594 73 : max_ptime = (u32) (max_ptime * my_sl.timestampResolution / 1000);
595 :
596 73 : my_sl.AUSeqNumLength = gf_get_bit_size(gf_isom_get_sample_count(file, TrackNum));
597 73 : if (my_sl.AUSeqNumLength>16) my_sl.AUSeqNumLength=16;
598 :
599 73 : my_sl.CUDuration = const_dur;
600 :
601 73 : if (gf_isom_has_sync_points(file, TrackNum)) {
602 31 : my_sl.useRandomAccessPointFlag = 1;
603 : } else {
604 42 : my_sl.useRandomAccessPointFlag = 0;
605 42 : my_sl.hasRandomAccessUnitsOnlyFlag = 1;
606 : }
607 :
608 73 : if (is_crypted) {
609 : Bool use_sel_enc;
610 5 : gf_isom_get_ismacryp_info(file, TrackNum, 1, NULL, NULL, NULL, NULL, NULL, &use_sel_enc, &IV_length, &KI_length);
611 5 : if (use_sel_enc) flags |= GP_RTP_PCK_SELECTIVE_ENCRYPTION;
612 : }
613 :
614 : // in case a different timescale was provided
615 73 : tmp->OrigTimeScale = gf_isom_get_media_timescale(file, TrackNum);
616 73 : tmp->rtp_p = gf_rtp_builder_new(hintType, &my_sl, flags, tmp,
617 : MP4T_OnNewPacket, MP4T_OnPacketDone,
618 : /*if copy, no data ref*/
619 : copy_media ? NULL : MP4T_OnDataRef,
620 : MP4T_OnData);
621 :
622 : //init the builder
623 73 : gf_rtp_builder_init(tmp->rtp_p, PayloadID, Path_MTU, max_ptime,
624 : streamType, codecid, PL_ID, MinSize, MaxSize, avgTS, maxDTSDelta, IV_length, KI_length, mpeg4mode);
625 :
626 : /*ISMA compliance is a pain...*/
627 73 : if (force_dts_delta) tmp->rtp_p->slMap.DTSDeltaLength = force_dts_delta;
628 :
629 :
630 : /* Hint Track Setup */
631 73 : tmp->TrackID = gf_isom_get_track_id(file, TrackNum);
632 73 : tmp->HintID = tmp->TrackID + 65535;
633 73 : while (gf_isom_get_track_by_id(file, tmp->HintID)) tmp->HintID++;
634 :
635 73 : tmp->HintTrack = gf_isom_new_track(file, tmp->HintID, GF_ISOM_MEDIA_HINT, my_sl.timestampResolution);
636 73 : gf_isom_setup_hint_track(file, tmp->HintTrack, GF_ISOM_HINT_RTP);
637 : /*create a hint description*/
638 73 : gf_isom_new_hint_description(file, tmp->HintTrack, -1, -1, 0, &descIndex);
639 73 : gf_isom_rtp_set_timescale(file, tmp->HintTrack, descIndex, my_sl.timestampResolution);
640 :
641 73 : if (hintType==GF_RTP_PAYT_MPEG4) {
642 39 : tmp->rtp_p->slMap.CodecID = codecid;
643 : /*set this SL for extraction.*/
644 39 : *e = gf_isom_set_extraction_slc(file, TrackNum, 1, &my_sl);
645 39 : if (*e) {
646 0 : gf_hinter_track_del(tmp);
647 0 : return NULL;
648 : }
649 : }
650 73 : tmp->bandwidth = bandwidth;
651 :
652 : /*set interleaving*/
653 73 : gf_isom_set_track_interleaving_group(file, TrackNum, InterleaveGroupID);
654 73 : if (!copy_media) {
655 : /*if we don't copy data set hint track and media track in the same group*/
656 70 : gf_isom_set_track_interleaving_group(file, tmp->HintTrack, InterleaveGroupID);
657 : } else {
658 3 : gf_isom_set_track_interleaving_group(file, tmp->HintTrack, InterleaveGroupID + OFFSET_HINT_GROUP_ID);
659 : }
660 : /*use user-secified priority*/
661 73 : InterleaveGroupPriority*=2;
662 73 : gf_isom_set_track_priority_in_group(file, TrackNum, InterleaveGroupPriority+1);
663 73 : gf_isom_set_track_priority_in_group(file, tmp->HintTrack, InterleaveGroupPriority);
664 :
665 73 : *e = GF_OK;
666 73 : return tmp;
667 : }
668 :
669 : GF_EXPORT
670 1 : GF_Err gf_hinter_track_force_no_offsets(GF_RTPHinter *tkHinter)
671 : {
672 : GF_Err e;
673 1 : if (!tkHinter) return GF_BAD_PARAM;
674 1 : e = gf_isom_rtp_set_time_offset(tkHinter->file, tkHinter->HintTrack, 1, 0);
675 1 : if (e) return e;
676 1 : return gf_isom_rtp_set_time_sequence_offset(tkHinter->file, tkHinter->HintTrack, 1, 0);
677 : }
678 :
679 : GF_EXPORT
680 73 : u32 gf_hinter_track_get_bandwidth(GF_RTPHinter *tkHinter)
681 : {
682 73 : return tkHinter->bandwidth;
683 : }
684 :
685 : GF_EXPORT
686 73 : u32 gf_hinter_track_get_flags(GF_RTPHinter *tkHinter)
687 : {
688 73 : return tkHinter->rtp_p->flags;
689 : }
690 : GF_EXPORT
691 73 : void gf_hinter_track_get_payload_name(GF_RTPHinter *tkHinter, char *payloadName)
692 : {
693 : char mediaName[30];
694 73 : gf_rtp_builder_get_payload_name(tkHinter->rtp_p, payloadName, mediaName);
695 73 : }
696 :
697 : GF_EXPORT
698 73 : void gf_hinter_track_del(GF_RTPHinter *tkHinter)
699 : {
700 73 : if (!tkHinter) return;
701 :
702 73 : if (tkHinter->rtp_p) gf_rtp_builder_del(tkHinter->rtp_p);
703 73 : gf_free(tkHinter);
704 : }
705 :
706 : GF_EXPORT
707 73 : GF_Err gf_hinter_track_process(GF_RTPHinter *tkHint)
708 : {
709 : GF_Err e;
710 : u32 i, descIndex, duration;
711 : u64 ts;
712 : u8 PadBits;
713 : GF_Fraction ft;
714 : GF_ISOSample *samp;
715 :
716 73 : tkHint->HintSample = tkHint->RTPTime = 0;
717 :
718 73 : tkHint->TotalSample = gf_isom_get_sample_count(tkHint->file, tkHint->TrackNum);
719 73 : ft.num = tkHint->rtp_p->sl_config.timestampResolution;
720 73 : ft.den = tkHint->OrigTimeScale;
721 :
722 : e = GF_OK;
723 65447 : for (i=0; i<tkHint->TotalSample; i++) {
724 65301 : samp = gf_isom_get_sample(tkHint->file, tkHint->TrackNum, i+1, &descIndex);
725 65301 : if (!samp) return gf_isom_last_error(tkHint->file);
726 :
727 : //setup SL
728 65301 : tkHint->CurrentSample = i + 1;
729 :
730 : /*keep same AU indicator if sync shadow - TODO FIXME: this assumes shadows are placed interleaved with
731 : the track content which is the case for GPAC scene carousel generation, but may not always be true*/
732 65301 : if (samp->IsRAP==RAP_REDUNDANT) {
733 0 : tkHint->rtp_p->sl_header.AU_sequenceNumber -= 1;
734 0 : samp->IsRAP = RAP;
735 : }
736 :
737 65301 : ts = ft.num * (samp->DTS+samp->CTS_Offset) / ft.den;
738 65301 : tkHint->rtp_p->sl_header.compositionTimeStamp = ts;
739 :
740 65301 : ts = ft.num * samp->DTS / ft.den;
741 65301 : tkHint->rtp_p->sl_header.decodingTimeStamp = ts;
742 65301 : tkHint->rtp_p->sl_header.randomAccessPointFlag = samp->IsRAP;
743 :
744 65301 : tkHint->base_offset_in_sample = 0;
745 : /*crypted*/
746 65301 : if (tkHint->rtp_p->slMap.IV_length) {
747 932 : GF_ISMASample *s = gf_isom_get_ismacryp_sample(tkHint->file, tkHint->TrackNum, samp, descIndex);
748 : /*one byte take for selective_enc flag*/
749 932 : if (s->flags & GF_ISOM_ISMA_USE_SEL_ENC) tkHint->base_offset_in_sample += 1;
750 932 : if (s->flags & GF_ISOM_ISMA_IS_ENCRYPTED) tkHint->base_offset_in_sample += s->IV_length + s->KI_length;
751 932 : gf_free(samp->data);
752 932 : samp->data = s->data;
753 932 : samp->dataLength = s->dataLength;
754 932 : gf_rtp_builder_set_cryp_info(tkHint->rtp_p, s->IV, (char*)s->key_indicator, (s->flags & GF_ISOM_ISMA_IS_ENCRYPTED) ? 1 : 0);
755 932 : s->data = NULL;
756 932 : s->dataLength = 0;
757 932 : gf_isom_ismacryp_delete_sample(s);
758 : }
759 :
760 65301 : if (tkHint->rtp_p->sl_config.usePaddingFlag) {
761 0 : gf_isom_get_sample_padding_bits(tkHint->file, tkHint->TrackNum, i+1, &PadBits);
762 0 : tkHint->rtp_p->sl_header.paddingBits = PadBits;
763 : } else {
764 65301 : tkHint->rtp_p->sl_header.paddingBits = 0;
765 : }
766 :
767 65301 : duration = gf_isom_get_sample_duration(tkHint->file, tkHint->TrackNum, i+1);
768 : // ts = (u32) (ft * (s64) (duration));
769 :
770 : /*unpack nal units*/
771 65301 : if (tkHint->avc_nalu_size) {
772 : u32 v, size;
773 2309 : u32 remain = samp->dataLength;
774 2309 : char *ptr = samp->data;
775 :
776 2309 : tkHint->rtp_p->sl_header.accessUnitStartFlag = 1;
777 2309 : tkHint->rtp_p->sl_header.accessUnitEndFlag = 0;
778 10709 : while (remain) {
779 : size = 0;
780 6091 : v = tkHint->avc_nalu_size;
781 6091 : if (v>remain) {
782 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[rtp hinter] Broken AVC nalu encapsulation: NALU size length is %d but only %d bytes left in sample %d\n", v, remain, tkHint->CurrentSample));
783 : break;
784 : }
785 30455 : while (v) {
786 24364 : size |= (u8) *ptr;
787 24364 : ptr++;
788 24364 : remain--;
789 24364 : v-=1;
790 24364 : if (v) size<<=8;
791 : }
792 6091 : tkHint->base_offset_in_sample = samp->dataLength-remain;
793 6091 : if (remain < size) {
794 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[rtp hinter] Broken AVC nalu encapsulation: NALU size is %d but only %d bytes left in sample %d\n", size, remain, tkHint->CurrentSample));
795 : break;
796 : }
797 6091 : remain -= size;
798 6091 : tkHint->rtp_p->sl_header.accessUnitEndFlag = remain ? 0 : 1;
799 6091 : if (!size) {
800 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[rtp hinter] Broken AVC nalu encapsulation: NALU size is 0, ignoring it\n", size));
801 : } else {
802 6091 : e = gf_rtp_builder_process(tkHint->rtp_p, ptr, size, (u8) !remain, samp->dataLength, duration, (u8) (descIndex + GF_RTP_TX3G_SIDX_OFFSET) );
803 6091 : ptr += size;
804 : }
805 6091 : tkHint->rtp_p->sl_header.accessUnitStartFlag = 0;
806 : }
807 : } else {
808 62992 : e = gf_rtp_builder_process(tkHint->rtp_p, samp->data, samp->dataLength, 1, samp->dataLength, duration, (u8) (descIndex + GF_RTP_TX3G_SIDX_OFFSET) );
809 : }
810 65301 : tkHint->rtp_p->sl_header.packetSequenceNumber += 1;
811 :
812 : //signal some progress
813 65301 : gf_set_progress("Hinting", tkHint->CurrentSample, tkHint->TotalSample);
814 :
815 65301 : tkHint->rtp_p->sl_header.AU_sequenceNumber += 1;
816 65301 : gf_isom_sample_del(&samp);
817 :
818 65301 : if (e) return e;
819 : }
820 :
821 : //flush
822 73 : gf_rtp_builder_process(tkHint->rtp_p, NULL, 0, 1, 0, 0, 0);
823 :
824 73 : gf_isom_end_hint_sample(tkHint->file, tkHint->HintTrack, (u8) tkHint->SampleIsRAP);
825 73 : return GF_OK;
826 : }
827 :
828 35 : static u32 write_nalu_config_array(char *sdpLine, GF_List *nalus)
829 : {
830 : u32 i, count, b64s;
831 : char b64[200];
832 :
833 35 : count = gf_list_count(nalus);
834 94 : for (i=0; i<count; i++) {
835 24 : GF_NALUFFParam *sl = (GF_NALUFFParam *)gf_list_get(nalus, i);
836 24 : b64s = gf_base64_encode(sl->data, sl->size, b64, 200);
837 24 : b64[b64s]=0;
838 : strcat(sdpLine, b64);
839 24 : if (i+1<count) strcat(sdpLine, ",");
840 : }
841 35 : return count;
842 : }
843 :
844 11 : static void write_avc_config(char *sdpLine, GF_AVCConfig *avcc, GF_AVCConfig *svcc)
845 : {
846 : u32 count = 0;
847 :
848 11 : if (avcc) count += gf_list_count(avcc->sequenceParameterSets) + gf_list_count(avcc->pictureParameterSets) + gf_list_count(avcc->sequenceParameterSetExtensions);
849 11 : if (svcc) count += gf_list_count(svcc->sequenceParameterSets) + gf_list_count(svcc->pictureParameterSets);
850 11 : if (!count) return;
851 :
852 : strcat(sdpLine, "; sprop-parameter-sets=");
853 :
854 11 : if (avcc) {
855 11 : count = write_nalu_config_array(sdpLine, avcc->sequenceParameterSets);
856 11 : if (count) strcat(sdpLine, ",");
857 11 : count = write_nalu_config_array(sdpLine, avcc->sequenceParameterSetExtensions);
858 11 : if (count) strcat(sdpLine, ",");
859 11 : count = write_nalu_config_array(sdpLine, avcc->pictureParameterSets);
860 11 : if (count) strcat(sdpLine, ",");
861 : }
862 :
863 11 : if (svcc) {
864 1 : count = write_nalu_config_array(sdpLine, svcc->sequenceParameterSets);
865 1 : if (count) strcat(sdpLine, ",");
866 1 : count = write_nalu_config_array(sdpLine, svcc->pictureParameterSets);
867 1 : if (count) strcat(sdpLine, ",");
868 : }
869 11 : count = (u32) strlen(sdpLine);
870 11 : if (sdpLine[count-1] == ',')
871 11 : sdpLine[count-1] = 0;
872 : }
873 :
874 : GF_EXPORT
875 73 : GF_Err gf_hinter_track_finalize(GF_RTPHinter *tkHint, Bool AddSystemInfo)
876 : {
877 : u32 Width, Height;
878 : GF_ESD *esd;
879 : char sdpLine[20000];
880 : char mediaName[30], payloadName[30];
881 : u32 mtype;
882 :
883 73 : Width = Height = 0;
884 73 : gf_isom_sdp_clean_track(tkHint->file, tkHint->TrackNum);
885 73 : mtype = gf_isom_get_media_type(tkHint->file, tkHint->TrackNum);
886 73 : if (gf_isom_is_video_handler_type(mtype))
887 32 : gf_isom_get_visual_info(tkHint->file, tkHint->TrackNum, 1, &Width, &Height);
888 :
889 73 : gf_rtp_builder_get_payload_name(tkHint->rtp_p, payloadName, mediaName);
890 :
891 : /*TODO- extract out of rtp_p for future live tools*/
892 73 : sprintf(sdpLine, "m=%s 0 RTP/%s %d", mediaName, tkHint->rtp_p->slMap.IV_length ? "SAVP" : "AVP", tkHint->rtp_p->PayloadType);
893 73 : gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine);
894 73 : if (tkHint->bandwidth) {
895 : sprintf(sdpLine, "b=AS:%d", tkHint->bandwidth);
896 63 : gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine);
897 : }
898 73 : if (tkHint->nb_chan) {
899 27 : sprintf(sdpLine, "a=rtpmap:%d %s/%d/%d", tkHint->rtp_p->PayloadType, payloadName, tkHint->rtp_p->sl_config.timestampResolution, tkHint->nb_chan);
900 : } else {
901 46 : sprintf(sdpLine, "a=rtpmap:%d %s/%d", tkHint->rtp_p->PayloadType, payloadName, tkHint->rtp_p->sl_config.timestampResolution);
902 : }
903 73 : gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine);
904 : /*control for MPEG-4*/
905 73 : if (AddSystemInfo) {
906 17 : sprintf(sdpLine, "a=mpeg4-esid:%d", gf_isom_get_track_id(tkHint->file, tkHint->TrackNum));
907 17 : gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine);
908 : }
909 : /*control for QTSS/DSS*/
910 73 : sprintf(sdpLine, "a=control:trackID=%d", gf_isom_get_track_id(tkHint->file, tkHint->HintTrack));
911 73 : gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine);
912 :
913 : /*H263 extensions*/
914 73 : if (tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_H263) {
915 1 : sprintf(sdpLine, "a=cliprect:0,0,%d,%d", Height, Width);
916 1 : gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine);
917 : }
918 : /*AMR*/
919 72 : else if ((tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_AMR) || (tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_AMR_WB)) {
920 2 : sprintf(sdpLine, "a=fmtp:%d octet-align=1", tkHint->rtp_p->PayloadType);
921 2 : gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine);
922 : }
923 : /*Text*/
924 70 : else if (tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_3GPP_TEXT) {
925 : u32 w, h, i, m_w, m_h;
926 : s32 tx, ty;
927 : s16 l;
928 :
929 7 : gf_isom_get_track_layout_info(tkHint->file, tkHint->TrackNum, &w, &h, &tx, &ty, &l);
930 7 : m_w = w;
931 7 : m_h = h;
932 28 : for (i=0; i<gf_isom_get_track_count(tkHint->file); i++) {
933 14 : switch (gf_isom_get_media_type(tkHint->file, i+1)) {
934 0 : case GF_ISOM_MEDIA_SCENE:
935 : case GF_ISOM_MEDIA_VISUAL:
936 : case GF_ISOM_MEDIA_AUXV:
937 : case GF_ISOM_MEDIA_PICT:
938 0 : gf_isom_get_track_layout_info(tkHint->file, i+1, &w, &h, &tx, &ty, &l);
939 0 : if (w>m_w) m_w = w;
940 0 : if (h>m_h) m_h = h;
941 : break;
942 : default:
943 : break;
944 : }
945 : }
946 :
947 7 : gf_media_format_ttxt_sdp(tkHint->rtp_p, payloadName, sdpLine, w, h, tx, ty, l, m_w, m_h, NULL);
948 :
949 : strcat(sdpLine, "; tx3g=");
950 21 : for (i=0; i<gf_isom_get_sample_description_count(tkHint->file, tkHint->TrackNum); i++) {
951 : u8 *tx3g;
952 : char buffer[2000];
953 : u32 tx3g_len, len;
954 7 : gf_isom_text_get_encoded_tx3g(tkHint->file, tkHint->TrackNum, i+1, GF_RTP_TX3G_SIDX_OFFSET, &tx3g, &tx3g_len);
955 7 : len = gf_base64_encode(tx3g, tx3g_len, buffer, 2000);
956 7 : gf_free(tx3g);
957 7 : buffer[len] = 0;
958 7 : if (i) strcat(sdpLine, ", ");
959 : strcat(sdpLine, buffer);
960 : }
961 7 : gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine);
962 : }
963 : /*EVRC/SMV in non header-free mode*/
964 63 : else if ((tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_EVRC_SMV) && (tkHint->rtp_p->auh_size>1)) {
965 0 : sprintf(sdpLine, "a=fmtp:%d maxptime=%d", tkHint->rtp_p->PayloadType, tkHint->rtp_p->auh_size*20);
966 0 : gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine);
967 : }
968 : /*H264/AVC*/
969 63 : else if ((tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_H264_AVC) || (tkHint->rtp_p->rtp_payt == GF_RTP_PAYT_H264_SVC)) {
970 11 : GF_AVCConfig *avcc = gf_isom_avc_config_get(tkHint->file, tkHint->TrackNum, 1);
971 11 : GF_AVCConfig *svcc = gf_isom_svc_config_get(tkHint->file, tkHint->TrackNum, 1);
972 : /*TODO - check syntax for SVC (might be some extra signaling)*/
973 :
974 11 : if (avcc) {
975 11 : sprintf(sdpLine, "a=fmtp:%d profile-level-id=%02X%02X%02X; packetization-mode=1", tkHint->rtp_p->PayloadType, avcc->AVCProfileIndication, avcc->profile_compatibility, avcc->AVCLevelIndication);
976 : } else {
977 0 : if (!svcc)
978 : return GF_ISOM_INVALID_FILE;
979 0 : sprintf(sdpLine, "a=fmtp:%d profile-level-id=%02X%02X%02X; packetization-mode=1", tkHint->rtp_p->PayloadType, svcc->AVCProfileIndication, svcc->profile_compatibility, svcc->AVCLevelIndication);
980 : }
981 :
982 11 : write_avc_config(sdpLine, avcc, svcc);
983 :
984 11 : gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine);
985 11 : gf_odf_avc_cfg_del(avcc);
986 11 : gf_odf_avc_cfg_del(svcc);
987 : }
988 : /*MPEG-4 decoder config*/
989 52 : else if (tkHint->rtp_p->rtp_payt==GF_RTP_PAYT_MPEG4) {
990 39 : esd = gf_isom_get_esd(tkHint->file, tkHint->TrackNum, 1);
991 :
992 39 : if (esd && esd->decoderConfig && esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) {
993 32 : gf_rtp_builder_format_sdp(tkHint->rtp_p, payloadName, sdpLine, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength);
994 : } else {
995 7 : gf_rtp_builder_format_sdp(tkHint->rtp_p, payloadName, sdpLine, NULL, 0);
996 : }
997 39 : if (esd) gf_odf_desc_del((GF_Descriptor *)esd);
998 :
999 39 : if (tkHint->rtp_p->slMap.IV_length) {
1000 : const char *kms;
1001 5 : gf_isom_get_ismacryp_info(tkHint->file, tkHint->TrackNum, 1, NULL, NULL, NULL, NULL, &kms, NULL, NULL, NULL);
1002 5 : if (!strnicmp(kms, "(key)", 5) || !strnicmp(kms, "(ipmp)", 6) || !strnicmp(kms, "(uri)", 5)) {
1003 : strcat(sdpLine, "; ISMACrypKey=");
1004 : } else {
1005 : strcat(sdpLine, "; ISMACrypKey=(uri)");
1006 : }
1007 : strcat(sdpLine, kms);
1008 : }
1009 :
1010 39 : gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine);
1011 : }
1012 : /*MPEG-4 Audio LATM*/
1013 13 : else if (tkHint->rtp_p->rtp_payt==GF_RTP_PAYT_LATM) {
1014 : GF_BitStream *bs;
1015 : u8 *config_bytes;
1016 : u32 config_size;
1017 :
1018 : /* form config string */
1019 0 : bs = gf_bs_new(NULL, 32, GF_BITSTREAM_WRITE);
1020 0 : gf_bs_write_int(bs, 0, 1); /* AudioMuxVersion */
1021 0 : gf_bs_write_int(bs, 1, 1); /* all streams same time */
1022 0 : gf_bs_write_int(bs, 0, 6); /* numSubFrames */
1023 0 : gf_bs_write_int(bs, 0, 4); /* numPrograms */
1024 0 : gf_bs_write_int(bs, 0, 3); /* numLayer */
1025 :
1026 : /* audio-specific config */
1027 0 : esd = gf_isom_get_esd(tkHint->file, tkHint->TrackNum, 1);
1028 0 : if (esd && esd->decoderConfig && esd->decoderConfig->decoderSpecificInfo) {
1029 : /*PacketVideo patch: don't signal SBR and PS stuff, not allowed in LATM with audioMuxVersion=0*/
1030 0 : gf_bs_write_data(bs, esd->decoderConfig->decoderSpecificInfo->data, MIN(esd->decoderConfig->decoderSpecificInfo->dataLength, 2) );
1031 : }
1032 0 : if (esd) gf_odf_desc_del((GF_Descriptor *)esd);
1033 :
1034 : /* other data */
1035 0 : gf_bs_write_int(bs, 0, 3); /* frameLengthType */
1036 0 : gf_bs_write_int(bs, 0xff, 8); /* latmBufferFullness */
1037 0 : gf_bs_write_int(bs, 0, 1); /* otherDataPresent */
1038 0 : gf_bs_write_int(bs, 0, 1); /* crcCheckPresent */
1039 0 : gf_bs_get_content(bs, &config_bytes, &config_size);
1040 0 : gf_bs_del(bs);
1041 :
1042 0 : gf_rtp_builder_format_sdp(tkHint->rtp_p, payloadName, sdpLine, config_bytes, config_size);
1043 0 : gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine);
1044 0 : gf_free(config_bytes);
1045 : }
1046 : #if GPAC_ENABLE_3GPP_DIMS_RTP
1047 : /*3GPP DIMS*/
1048 : else if (tkHint->rtp_p->rtp_payt==GF_RTP_PAYT_3GPP_DIMS) {
1049 : GF_DIMSDescription dims;
1050 : gf_isom_get_visual_info(tkHint->file, tkHint->TrackNum, 1, &Width, &Height);
1051 :
1052 : gf_isom_get_dims_description(tkHint->file, tkHint->TrackNum, 1, &dims);
1053 : sprintf(sdpLine, "a=fmtp:%d Version-profile=%d", tkHint->rtp_p->PayloadType, dims.profile);
1054 : if (! dims.fullRequestHost) {
1055 : char fmt[200];
1056 : strcat(sdpLine, ";useFullRequestHost=0");
1057 : sprintf(fmt, ";pathComponents=%d", dims.pathComponents);
1058 : strcat(sdpLine, fmt);
1059 : }
1060 : if (!dims.streamType) strcat(sdpLine, ";stream-type=secondary");
1061 : if (dims.containsRedundant == 1) strcat(sdpLine, ";contains-redundant=main");
1062 : else if (dims.containsRedundant == 2) strcat(sdpLine, ";contains-redundant=redundant");
1063 :
1064 : if (dims.textEncoding && strlen(dims.textEncoding)) {
1065 : strcat(sdpLine, ";text-encoding=");
1066 : strcat(sdpLine, dims.textEncoding);
1067 : }
1068 : if (dims.contentEncoding && strlen(dims.contentEncoding)) {
1069 : strcat(sdpLine, ";content-coding=");
1070 : strcat(sdpLine, dims.contentEncoding);
1071 : }
1072 : if (dims.contentEncoding && dims.content_script_types && strlen(dims.content_script_types) ) {
1073 : strcat(sdpLine, ";content-script-types=");
1074 : strcat(sdpLine, dims.contentEncoding);
1075 : }
1076 : gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine);
1077 : }
1078 : #endif
1079 : /*extensions for some mobile phones*/
1080 73 : if (Width && Height) {
1081 32 : sprintf(sdpLine, "a=framesize:%d %d-%d", tkHint->rtp_p->PayloadType, Width, Height);
1082 32 : gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine);
1083 : }
1084 :
1085 73 : esd = gf_isom_get_esd(tkHint->file, tkHint->TrackNum, 1);
1086 73 : if (esd && esd->decoderConfig && (esd->decoderConfig->rvc_config || esd->decoderConfig->predefined_rvc_config)) {
1087 0 : if (esd->decoderConfig->predefined_rvc_config) {
1088 0 : sprintf(sdpLine, "a=rvc-config-predef:%d", esd->decoderConfig->predefined_rvc_config);
1089 : } else {
1090 : /*temporary ...*/
1091 0 : if ((esd->decoderConfig->objectTypeIndication==GF_CODECID_AVC) || (esd->decoderConfig->objectTypeIndication==GF_CODECID_SVC)) {
1092 : sprintf(sdpLine, "a=rvc-config:%s", "http://download.tsi.telecom-paristech.fr/gpac/RVC/rvc_config_avc.xml");
1093 : } else {
1094 : sprintf(sdpLine, "a=rvc-config:%s", "http://download.tsi.telecom-paristech.fr/gpac/RVC/rvc_config_sp.xml");
1095 : }
1096 : }
1097 0 : gf_isom_sdp_add_track_line(tkHint->file, tkHint->HintTrack, sdpLine);
1098 : }
1099 73 : if (esd) gf_odf_desc_del((GF_Descriptor *)esd);
1100 :
1101 73 : gf_isom_set_track_enabled(tkHint->file, tkHint->HintTrack, GF_TRUE);
1102 73 : return GF_OK;
1103 : }
1104 :
1105 : GF_EXPORT
1106 8 : Bool gf_hinter_can_embbed_data(u8 *data, u32 data_size, u32 streamType)
1107 : {
1108 : char data64[5000];
1109 : u32 size64;
1110 :
1111 8 : size64 = gf_base64_encode(data, data_size, data64, 5000);
1112 8 : if (!size64) return 0;
1113 8 : switch (streamType) {
1114 4 : case GF_STREAM_OD:
1115 4 : size64 += (u32) strlen("data:application/mpeg4-od-au;base64,");
1116 4 : break;
1117 4 : case GF_STREAM_SCENE:
1118 4 : size64 += (u32) strlen("data:application/mpeg4-bifs-au;base64,");
1119 4 : break;
1120 0 : default:
1121 : /*NOT NORMATIVE*/
1122 0 : size64 += (u32) strlen("data:application/mpeg4-es-au;base64,");
1123 0 : break;
1124 : }
1125 8 : if (size64>=255) return 0;
1126 8 : return 1;
1127 : }
1128 :
1129 :
1130 : GF_EXPORT
1131 55 : GF_Err gf_hinter_finalize(GF_ISOFile *file, GF_SDP_IODProfile IOD_Profile, u32 bandwidth)
1132 : {
1133 : u32 i, sceneT, odT, descIndex, size, size64;
1134 : GF_InitialObjectDescriptor *iod;
1135 : GF_SLConfig slc;
1136 : GF_ISOSample *samp;
1137 : Bool remove_ocr;
1138 : u8 *buffer;
1139 : char buf64[5000], sdpLine[5100];
1140 :
1141 :
1142 55 : gf_isom_sdp_clean(file);
1143 :
1144 55 : if (bandwidth) {
1145 : sprintf(buf64, "b=AS:%d", bandwidth);
1146 48 : gf_isom_sdp_add_line(file, buf64);
1147 : }
1148 : //xtended attribute for copyright
1149 55 : if (gf_sys_is_test_mode()) {
1150 : sprintf(buf64, "a=x-copyright: %s", "MP4/3GP File hinted with GPAC - (c) Telecom ParisTech (http://gpac.io)");
1151 : } else {
1152 0 : sprintf(buf64, "a=x-copyright: MP4/3GP File hinted with GPAC %s - %s", gf_gpac_version(), gf_gpac_copyright() );
1153 : }
1154 55 : gf_isom_sdp_add_line(file, buf64);
1155 :
1156 55 : if (IOD_Profile == GF_SDP_IOD_NONE) return GF_OK;
1157 :
1158 : odT = sceneT = 0;
1159 47 : for (i=0; i<gf_isom_get_track_count(file); i++) {
1160 42 : if (!gf_isom_is_track_in_root_od(file, i+1)) continue;
1161 10 : switch (gf_isom_get_media_type(file,i+1)) {
1162 5 : case GF_ISOM_MEDIA_OD:
1163 : odT = i+1;
1164 5 : break;
1165 5 : case GF_ISOM_MEDIA_SCENE:
1166 : sceneT = i+1;
1167 5 : break;
1168 : }
1169 : }
1170 : remove_ocr = 0;
1171 5 : if (IOD_Profile == GF_SDP_IOD_ISMA_STRICT) {
1172 : IOD_Profile = GF_SDP_IOD_ISMA;
1173 : remove_ocr = 1;
1174 : }
1175 :
1176 : /*if we want ISMA like iods, we need at least BIFS */
1177 5 : if ( (IOD_Profile == GF_SDP_IOD_ISMA) && !sceneT ) return GF_BAD_PARAM;
1178 :
1179 : /*do NOT change PLs, we assume they are correct*/
1180 5 : iod = (GF_InitialObjectDescriptor *) gf_isom_get_root_od(file);
1181 5 : if (!iod) return GF_NOT_SUPPORTED;
1182 :
1183 : /*rewrite an IOD with good SL config - embbed data if possible*/
1184 5 : if (IOD_Profile == GF_SDP_IOD_ISMA) {
1185 : GF_ESD *esd;
1186 : Bool is_ok = 1;
1187 15 : while (gf_list_count(iod->ESDescriptors)) {
1188 10 : esd = (GF_ESD*)gf_list_get(iod->ESDescriptors, 0);
1189 10 : gf_odf_desc_del((GF_Descriptor *) esd);
1190 10 : gf_list_rem(iod->ESDescriptors, 0);
1191 : }
1192 :
1193 :
1194 : /*get OD esd, and embbed stream data if possible*/
1195 5 : if (odT) {
1196 5 : esd = gf_isom_get_esd(file, odT, 1);
1197 5 : if (gf_isom_get_sample_count(file, odT)==1) {
1198 2 : samp = gf_isom_get_sample(file, odT, 1, &descIndex);
1199 2 : if (samp && gf_hinter_can_embbed_data(samp->data, samp->dataLength, GF_STREAM_OD)) {
1200 : InitSL_NULL(&slc);
1201 2 : slc.predefined = 0;
1202 2 : slc.hasRandomAccessUnitsOnlyFlag = 1;
1203 2 : slc.timeScale = slc.timestampResolution = gf_isom_get_media_timescale(file, odT);
1204 2 : slc.OCRResolution = 1000;
1205 2 : slc.startCTS = samp->DTS+samp->CTS_Offset;
1206 2 : slc.startDTS = samp->DTS;
1207 : //set the SL for future extraction
1208 2 : gf_isom_set_extraction_slc(file, odT, 1, &slc);
1209 :
1210 2 : size64 = gf_base64_encode(samp->data, samp->dataLength, buf64, 2000);
1211 2 : buf64[size64] = 0;
1212 : sprintf(sdpLine, "data:application/mpeg4-od-au;base64,%s", buf64);
1213 :
1214 2 : esd->decoderConfig->avgBitrate = 0;
1215 2 : esd->decoderConfig->bufferSizeDB = samp->dataLength;
1216 2 : esd->decoderConfig->maxBitrate = 0;
1217 2 : size64 = (u32) strlen(sdpLine)+1;
1218 2 : esd->URLString = (char*)gf_malloc(sizeof(char) * size64);
1219 : strcpy(esd->URLString, sdpLine);
1220 : } else {
1221 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[rtp hinter] OD sample too large to be embedded in IOD - ISMA disabled\n"));
1222 : is_ok = 0;
1223 : }
1224 2 : gf_isom_sample_del(&samp);
1225 : }
1226 5 : if (remove_ocr) esd->OCRESID = 0;
1227 5 : else if (esd->OCRESID == esd->ESID) esd->OCRESID = 0;
1228 :
1229 : //OK, add this to our IOD
1230 5 : gf_list_add(iod->ESDescriptors, esd);
1231 : }
1232 :
1233 5 : esd = gf_isom_get_esd(file, sceneT, 1);
1234 5 : if (gf_isom_get_sample_count(file, sceneT)==1) {
1235 2 : samp = gf_isom_get_sample(file, sceneT, 1, &descIndex);
1236 2 : if (gf_hinter_can_embbed_data(samp->data, samp->dataLength, GF_STREAM_SCENE)) {
1237 :
1238 2 : slc.timeScale = slc.timestampResolution = gf_isom_get_media_timescale(file, sceneT);
1239 2 : slc.OCRResolution = 1000;
1240 2 : slc.startCTS = samp->DTS+samp->CTS_Offset;
1241 2 : slc.startDTS = samp->DTS;
1242 : //set the SL for future extraction
1243 2 : gf_isom_set_extraction_slc(file, sceneT, 1, &slc);
1244 : //encode in Base64 the sample
1245 2 : size64 = gf_base64_encode(samp->data, samp->dataLength, buf64, 2000);
1246 2 : buf64[size64] = 0;
1247 : sprintf(sdpLine, "data:application/mpeg4-bifs-au;base64,%s", buf64);
1248 :
1249 2 : esd->decoderConfig->avgBitrate = 0;
1250 2 : esd->decoderConfig->bufferSizeDB = samp->dataLength;
1251 2 : esd->decoderConfig->maxBitrate = 0;
1252 2 : esd->URLString = (char*)gf_malloc(sizeof(char) * (strlen(sdpLine)+1));
1253 : strcpy(esd->URLString, sdpLine);
1254 : } else {
1255 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[rtp hinter] Scene description sample too large to be embedded in IOD - ISMA disabled\n"));
1256 : is_ok = 0;
1257 : }
1258 2 : gf_isom_sample_del(&samp);
1259 : }
1260 5 : if (remove_ocr) esd->OCRESID = 0;
1261 5 : else if (esd->OCRESID == esd->ESID) esd->OCRESID = 0;
1262 :
1263 5 : gf_list_add(iod->ESDescriptors, esd);
1264 :
1265 5 : if (is_ok) {
1266 : u32 has_a, has_v, has_i_a, has_i_v;
1267 : has_a = has_v = has_i_a = has_i_v = 0;
1268 47 : for (i=0; i<gf_isom_get_track_count(file); i++) {
1269 42 : esd = gf_isom_get_esd(file, i+1, 1);
1270 42 : if (!esd) continue;
1271 25 : if (esd->decoderConfig->streamType==GF_STREAM_VISUAL) {
1272 9 : if (esd->decoderConfig->objectTypeIndication==GF_CODECID_MPEG4_PART2) has_i_v ++;
1273 9 : else has_v++;
1274 16 : } else if (esd->decoderConfig->streamType==GF_STREAM_AUDIO) {
1275 2 : if (esd->decoderConfig->objectTypeIndication==GF_CODECID_AAC_MPEG4) has_i_a ++;
1276 0 : else has_a++;
1277 : }
1278 25 : gf_odf_desc_del((GF_Descriptor *)esd);
1279 : }
1280 : /*only 1 MPEG-4 visual max and 1 MPEG-4 audio max for ISMA compliancy*/
1281 5 : if (!has_v && !has_a && (has_i_v<=1) && (has_i_a<=1)) {
1282 : sprintf(sdpLine, "a=isma-compliance:1,1.0,1");
1283 0 : gf_isom_sdp_add_line(file, sdpLine);
1284 : }
1285 : }
1286 : }
1287 :
1288 : //encode the IOD
1289 5 : buffer = NULL;
1290 5 : size = 0;
1291 5 : gf_odf_desc_write((GF_Descriptor *) iod, &buffer, &size);
1292 5 : gf_odf_desc_del((GF_Descriptor *)iod);
1293 :
1294 : //encode in Base64 the iod
1295 5 : size64 = gf_base64_encode(buffer, size, buf64, 2000);
1296 5 : buf64[size64] = 0;
1297 5 : gf_free(buffer);
1298 :
1299 : sprintf(sdpLine, "a=mpeg4-iod:\"data:application/mpeg4-iod;base64,%s\"", buf64);
1300 5 : gf_isom_sdp_add_line(file, sdpLine);
1301 :
1302 5 : return GF_OK;
1303 : }
1304 :
1305 :
1306 : #endif /*GPAC_DISABLE_ISOM_HINTING*/
1307 :
1308 : #endif /*GPAC_DISABLE_ISOM*/
|