Esempio n. 1
0
GF_EXPORT
GF_Err gf_isom_streamer_send_next_packet(GF_ISOMRTPStreamer *streamer, s32 send_ahead_delay, s32 max_sleep_time) 
{
	GF_Err e = GF_OK;
	GF_RTPTrack *track, *to_send;
	u32 time, duration;
	s32 diff;
	u64 min_ts, dts, cts;

	if (!streamer) return GF_BAD_PARAM;
	
	/*browse all sessions and locate most mature stream*/
	to_send = NULL;
	min_ts = (u64) -1;

	time = gf_sys_clock();

	/*init session timeline - all sessions are sync'ed for packet scheduling purposes*/
	if (!streamer->timelineOrigin)
		streamer->timelineOrigin = time*1000;

	track = streamer->stream;
	while (track) {
		/*load next AU*/
		if (!track->au) {
			if (track->current_au >= track->nb_aus) {
				Double scale;
				if (!streamer->loop) {
					track = track->next;
					continue;
				}
				/*increment ts offset*/
				scale = track->timescale/1000.0;
				track->ts_offset += (u32) (streamer->duration_ms * scale);
				track->microsec_ts_offset = (u32) (track->ts_offset*(1000000.0/track->timescale)) + streamer->timelineOrigin;
				track->current_au = 0;
			}

			track->au = gf_isom_get_sample(streamer->isom, track->track_num, track->current_au + 1, &track->sample_desc_index);
			track->current_au ++;
			if (track->au) {
				track->microsec_dts = (u64) (track->microsec_ts_scale * (s64) (track->au->DTS)) + track->microsec_ts_offset + streamer->timelineOrigin;
			}
		}

		/*check timing*/
		if (track->au) {
			if (min_ts > track->microsec_dts) {
				min_ts = track->microsec_dts;
				to_send = track;
			}
		}

		track = track->next;
	}

	/*no input data ...*/
	if( !to_send) return GF_EOS;
	min_ts /= 1000;

	if (max_sleep_time) {
		diff = ((u32) min_ts) - gf_sys_clock();	
		if (diff>max_sleep_time) 
			return GF_OK;
	}

	/*sleep until TS is mature*/
	while (1) {
		diff = ((u32) min_ts) - gf_sys_clock();
		
		if (diff > send_ahead_delay) {
			gf_sleep(1);
		} else {
			if (diff<10) {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("WARNING: RTP session %s stream %d - sending packet %d ms too late\n", gf_isom_get_filename(streamer->isom), to_send->track_num, -diff));
			}
			break;
		}
	}

	/*send packets*/

	dts = to_send->au->DTS + to_send->ts_offset;
	cts = to_send->au->DTS + to_send->au->CTS_Offset + to_send->ts_offset;
	duration = gf_isom_get_sample_duration(streamer->isom, to_send->track_num, to_send->current_au);

	/*unpack nal units*/
	if (to_send->avc_nalu_size) {
		Bool au_start, au_end;
		u32 v, size;
		u32 remain = to_send->au->dataLength;
		char *ptr = to_send->au->data;

		au_start = 1;
		au_end = 0;
		while (remain) {
			size = 0;
			v = to_send->avc_nalu_size;
			while (v) {
				size |= (u8) *ptr;
				ptr++;
				remain--;
				v-=1;
				if (v) size<<=8;
			}
			remain -= size;
			au_end = remain ? 0 : 1;

			e = gf_rtp_streamer_send_data(to_send->rtp, ptr, size, to_send->au->dataLength, cts, dts, to_send->au->IsRAP, au_start, au_end, to_send->current_au, duration, to_send->sample_desc_index);
			ptr += size;
			au_start = 0;
		}
	} else {
		e = gf_rtp_streamer_send_data(to_send->rtp, to_send->au->data, to_send->au->dataLength, to_send->au->dataLength, cts, dts, to_send->au->IsRAP, 1, 1, to_send->current_au, duration, to_send->sample_desc_index);
	}
	/*delete sample*/
	gf_isom_sample_del(&to_send->au);

	return e;
}
Esempio n. 2
0
GF_EXPORT
GF_Err gf_hinter_track_process(GF_RTPHinter *tkHint)
{
	GF_Err e;
	u32 i, descIndex, duration;
	u64 ts;
	u8 PadBits;
	Double ft;
	GF_ISOSample *samp;

	tkHint->HintSample = tkHint->RTPTime = 0;

	tkHint->TotalSample = gf_isom_get_sample_count(tkHint->file, tkHint->TrackNum);
	ft = tkHint->rtp_p->sl_config.timestampResolution;
	ft /= tkHint->OrigTimeScale;
	
	e = GF_OK;
	for (i=0; i<tkHint->TotalSample; i++) {
		samp = gf_isom_get_sample(tkHint->file, tkHint->TrackNum, i+1, &descIndex);
		if (!samp) return GF_IO_ERR;

		//setup SL
		tkHint->CurrentSample = i + 1;

		/*keep same AU indicator if sync shadow - TODO FIXME: this assumes shadows are placed interleaved with 
		the track content which is the case for GPAC scene carousel generation, but may not always be true*/
		if (samp->IsRAP==2) {
			tkHint->rtp_p->sl_header.AU_sequenceNumber -= 1;
			samp->IsRAP = 1;
		}

		ts = (u64) (ft * (s64) (samp->DTS+samp->CTS_Offset));
		tkHint->rtp_p->sl_header.compositionTimeStamp = ts;

		ts = (u64) (ft * (s64)(samp->DTS));
		tkHint->rtp_p->sl_header.decodingTimeStamp = ts;
		tkHint->rtp_p->sl_header.randomAccessPointFlag = samp->IsRAP;

		tkHint->base_offset_in_sample = 0;
		/*crypted*/
		if (tkHint->rtp_p->slMap.IV_length) {
			GF_ISMASample *s = gf_isom_get_ismacryp_sample(tkHint->file, tkHint->TrackNum, samp, descIndex);
			/*one byte take for selective_enc flag*/
			if (s->flags & GF_ISOM_ISMA_USE_SEL_ENC) tkHint->base_offset_in_sample += 1;
			if (s->flags & GF_ISOM_ISMA_IS_ENCRYPTED) tkHint->base_offset_in_sample += s->IV_length + s->KI_length;
			gf_free(samp->data);
			samp->data = s->data;
			samp->dataLength = s->dataLength;
			gp_rtp_builder_set_cryp_info(tkHint->rtp_p, s->IV, (char*)s->key_indicator, (s->flags & GF_ISOM_ISMA_IS_ENCRYPTED) ? 1 : 0);
			s->data = NULL;
			s->dataLength = 0;
			gf_isom_ismacryp_delete_sample(s);
		}

		if (tkHint->rtp_p->sl_config.usePaddingFlag) {
			gf_isom_get_sample_padding_bits(tkHint->file, tkHint->TrackNum, i+1, &PadBits);
			tkHint->rtp_p->sl_header.paddingBits = PadBits;
		} else {
			tkHint->rtp_p->sl_header.paddingBits = 0;
		}
		
		duration = gf_isom_get_sample_duration(tkHint->file, tkHint->TrackNum, i+1);
		ts = (u32) (ft * (s64) (duration));

		/*unpack nal units*/
		if (tkHint->avc_nalu_size) {
			u32 v, size;
			u32 remain = samp->dataLength;
			char *ptr = samp->data;

			tkHint->rtp_p->sl_header.accessUnitStartFlag = 1;
			tkHint->rtp_p->sl_header.accessUnitEndFlag = 0;
			while (remain) {
				size = 0;
				v = tkHint->avc_nalu_size;
				while (v) {
					size |= (u8) *ptr;
					ptr++;
					remain--;
					v-=1;
					if (v) size<<=8;
				}
				tkHint->base_offset_in_sample = samp->dataLength-remain;
				remain -= size;
				tkHint->rtp_p->sl_header.accessUnitEndFlag = remain ? 0 : 1;
				e = gf_rtp_builder_process(tkHint->rtp_p, ptr, size, (u8) !remain, samp->dataLength, duration, (u8) (descIndex + GF_RTP_TX3G_SIDX_OFFSET) );
				ptr += size;
				tkHint->rtp_p->sl_header.accessUnitStartFlag = 0;
			}
		} else {
			e = gf_rtp_builder_process(tkHint->rtp_p, samp->data, samp->dataLength, 1, samp->dataLength, duration, (u8) (descIndex + GF_RTP_TX3G_SIDX_OFFSET) );
		}
		tkHint->rtp_p->sl_header.packetSequenceNumber += 1;

		//signal some progress
		gf_set_progress("Hinting", tkHint->CurrentSample, tkHint->TotalSample);

		tkHint->rtp_p->sl_header.AU_sequenceNumber += 1;
		gf_isom_sample_del(&samp);

		if (e) return e;
	}

	//flush
	gf_rtp_builder_process(tkHint->rtp_p, NULL, 0, 1, 0, 0, 0);

	gf_isom_end_hint_sample(tkHint->file, tkHint->HintTrack, (u8) tkHint->SampleIsRAP);
	return GF_OK;
}
Esempio n. 3
0
GF_EXPORT
GF_Err gf_isom_streamer_send_next_packet(GF_ISOMRTPStreamer *streamer, s32 send_ahead_delay, s32 max_sleep_time)
{
	GF_Err e = GF_OK;
	GF_RTPTrack *track, *to_send;
	u32 time, duration;
	s32 diff;
	u64 min_ts, dts, cts;

	if (!streamer) return GF_BAD_PARAM;

	/*browse all sessions and locate most mature stream*/
	to_send = NULL;
	min_ts = (u64) -1;

	time = gf_sys_clock();

	/*init session timeline - all sessions are sync'ed for packet scheduling purposes*/
	if (!streamer->timelineOrigin) {
		streamer->timelineOrigin = time*1000;
		GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[FileStreamer] RTP session %s initialized - time origin set to %d\n", gf_isom_get_filename(streamer->isom), time));
	}

	track = streamer->stream;
	while (track) {
		/*load next AU*/
		gf_isom_set_nalu_extract_mode(streamer->isom, track->track_num, GF_ISOM_NALU_EXTRACT_LAYER_ONLY);
		if (!track->au) {
			if (track->current_au >= track->nb_aus) {
				Double scale;
				if (!streamer->loop) {
					track = track->next;
					continue;
				}
				/*increment ts offset*/
				scale = track->timescale/1000.0;
				track->ts_offset += (u32) (streamer->duration_ms * scale);
				track->microsec_ts_offset = (u32) (track->ts_offset*(1000000.0/track->timescale)) + streamer->timelineOrigin;
				track->current_au = 0;
			}

			track->au = gf_isom_get_sample(streamer->isom, track->track_num, track->current_au + 1, &track->sample_desc_index);
			track->current_au ++;
			if (track->au) {
				track->microsec_dts = (u64) (track->microsec_ts_scale * (s64) (track->au->DTS)) + track->microsec_ts_offset + streamer->timelineOrigin;
			}
		}

		/*check timing*/
		if (track->au) {
			if (min_ts > track->microsec_dts) {
				min_ts = track->microsec_dts;
				to_send = track;
			}
		}

		track = track->next;
	}

	/*no input data ...*/
	if( !to_send) return GF_EOS;


	/*we are about to send scalable base: trigger RTCP reports with the same NTP. This avoids
	NTP drift due to system clock precision which could break sync decoding*/
	if (!streamer->first_RTCP_sent || (streamer->base_track && streamer->base_track==to_send->track_num)) {
		u32 ntp_sec, ntp_frac;
		/*force sending RTCP SR every RAP ? - not really compliant but we cannot perform scalable tuning otherwise*/
		u32 ntp_type = to_send->au->IsRAP ? 2 : 1;
		gf_net_get_ntp(&ntp_sec, &ntp_frac);
		track = streamer->stream;
		while (track) {
			u32 ts = (u32) (track->au->DTS + track->au->CTS_Offset + track->ts_offset);
			gf_rtp_streamer_send_rtcp(track->rtp, GF_TRUE, ts, ntp_type, ntp_sec, ntp_frac);
			track = track->next;
		}

		streamer->first_RTCP_sent = 1;
	}

	min_ts /= 1000;

	if (max_sleep_time) {
		diff = ((u32) min_ts) - gf_sys_clock();
		if (diff>max_sleep_time)
			return GF_OK;
	}


	/*sleep until TS is mature*/
	while (1) {
		diff = ((u32) min_ts) - gf_sys_clock();

		if (diff > send_ahead_delay) {
			gf_sleep(1);
		} else {
			if (diff<10) {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("WARNING: RTP session %s stream %d - sending packet %d ms too late\n", gf_isom_get_filename(streamer->isom), to_send->track_num, -diff));
			}
			break;
		}
	}

	/*send packets*/
	dts = to_send->au->DTS + to_send->ts_offset;
	cts = to_send->au->DTS + to_send->au->CTS_Offset + to_send->ts_offset;
	duration = gf_isom_get_sample_duration(streamer->isom, to_send->track_num, to_send->current_au);

	GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[FileStreamer] Sending RTP packets for track %d AU %d/%d DTS "LLU" - CTS "LLU" - RTP TS "LLU" - size %d - RAP %d\n", to_send->track_num, to_send->current_au, to_send->nb_aus, to_send->au->DTS, to_send->au->DTS+to_send->au->CTS_Offset, cts, to_send->au->dataLength, to_send->au->IsRAP ) );

	/*unpack nal units*/
	if (to_send->avc_nalu_size) {
		Bool au_start, au_end;
		u32 v, size;
		u32 remain = to_send->au->dataLength;
		char *ptr = to_send->au->data;

		au_start = 1;
		au_end = 0;
		while (remain) {
			size = 0;
			v = to_send->avc_nalu_size;
			while (v) {
				size |= (u8) *ptr;
				ptr++;
				remain--;
				v-=1;
				if (v) size<<=8;
			}
			if (remain < size) {
				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, to_send->current_au));
				break;
			}
			remain -= size;
			au_end = remain ? 0 : 1;

			e = gf_rtp_streamer_send_data(to_send->rtp, ptr, size, to_send->au->dataLength, cts, dts, (to_send->au->IsRAP==RAP) ? 1 : 0, au_start, au_end, to_send->current_au, duration, to_send->sample_desc_index);
			ptr += size;
			au_start = 0;
		}
	} else {
		e = gf_rtp_streamer_send_data(to_send->rtp, to_send->au->data, to_send->au->dataLength, to_send->au->dataLength, cts, dts, (to_send->au->IsRAP==RAP) ? 1 : 0, 1, 1, to_send->current_au, duration, to_send->sample_desc_index);
	}
	/*delete sample*/
	gf_isom_sample_del(&to_send->au);

	return e;
}