예제 #1
0
static void imagetexture_destroy(GF_Node *node, void *rs, Bool is_destroy)
{
	if (is_destroy) {
		GF_TextureHandler *txh = (GF_TextureHandler *) gf_node_get_private(node);

		/*cleanup cache if needed*/
		if (gf_node_get_tag(node)==TAG_MPEG4_CacheTexture) {
			char section[64];
			const char *opt, *file;
			Bool delete_file = 1;
			M_CacheTexture *ct = (M_CacheTexture*)node;

			sprintf(section, "@cache=%p", ct);
			file = gf_cfg_get_key(txh->compositor->user->config, section, "cacheFile");
			opt = gf_cfg_get_key(txh->compositor->user->config, section, "expireAfterNTP");

			if (opt) {
				u32 sec, frac, exp;
				sscanf(opt, "%u", &exp);
				gf_net_get_ntp(&sec, &frac);
				if (!exp || (exp>sec)) delete_file=0;
			}
			if (delete_file) {
				gf_delete_file((char*)file);
				gf_cfg_del_section(txh->compositor->user->config, section);
			}

			if (txh->data) gf_free(txh->data);
			txh->data = NULL;
		}
		gf_sc_texture_destroy(txh);
		gf_free(txh);
	}
}
예제 #2
0
void gf_storage_save(M_Storage *storage)
{
	char szID[20];
	u32 i;
	GF_Config *cfg = storage_get_cfg(storage);
	char *section = storage_get_section(storage);
	if (!cfg || !section) return;

	gf_cfg_del_section(cfg, section);

	if (storage->expireAfter) {
		u32 sec, frac;
		char szNTP[100];
		gf_net_get_ntp(&sec, &frac);
		sec += storage->expireAfter;
		sprintf(szNTP, "%u", sec);
		gf_cfg_set_key(cfg, section, "expireAfterNTP", szNTP);
	} else {
		gf_cfg_set_key(cfg, section, "expireAfterNTP", "0");
	}

	for (i=0; i<storage->storageList.count; i++) {
		char *val;
		GF_FieldInfo info;
		sprintf(szID, "%d", i);

		if (!storage->storageList.vals[i].node) break;
		if (gf_node_get_field(storage->storageList.vals[i].node, storage->storageList.vals[i].fieldIndex, &info) != GF_OK) break;

		if (gf_sg_vrml_is_sf_field(info.fieldType)) {
			val = storage_serialize_sf(info.far_ptr, info.fieldType);
		} else {
			//u32 sftype = gf_sg_vrml_get_sf_type(info.fieldType);
			char *slotval;
			void *slot;
			val = NULL;
			for (i=0; i<((GenMFField *)info.far_ptr)->count; i++) {
				if (gf_sg_vrml_mf_get_item(info.far_ptr, info.fieldType, &slot, i) != GF_OK) break;
				slotval = storage_serialize_sf(info.far_ptr, info.fieldType);
				if (!slotval) break;
				if (val) {
					val = gf_realloc(val, strlen(val) + 3 + strlen(slot));
				} else {
					val = gf_malloc(3 + strlen(slot));
					val[0] = 0;
				}
				strcat(val, "'");
				strcat(val, slotval);
				strcat(val, "'");
				gf_free(slot);
			}
		}
		if (val) {
			gf_cfg_set_key(cfg, section, szID, val);
			gf_free(val);
		}
	}
	gf_free(section);
}
예제 #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;
}
예제 #4
0
static void gf_storage_load(M_Storage *storage)
{
	const char *opt;
	char szID[20];
	u32 i, count;
	u32 sec, exp, frac;
	GF_Config *cfg = storage_get_cfg(storage);
	char *section = storage_get_section(storage);
	if (!cfg || !section) return;

	if (!gf_cfg_get_key_count(cfg, section)) {
		gf_free(section);
		return;
	}
	opt = gf_cfg_get_key(cfg, section, "expireAfterNTP");
	gf_net_get_ntp(&sec, &frac);
	sscanf(opt, "%u", &exp);
	if (exp && (exp<=sec)) {
		gf_cfg_del_section(cfg, section);
		gf_free(section);
		return;
	}

	count = gf_cfg_get_key_count(cfg, section)-1;
	if (!count || (count!=storage->storageList.count)) {
		gf_cfg_del_section(cfg, section);
		gf_free(section);
		return;
	}

	for (i=0; i<count; i++) {
		GF_FieldInfo info;
		sprintf(szID, "%d", i);
		opt = gf_cfg_get_key(cfg, section, szID);
		if (!opt) break;
		if (!storage->storageList.vals[i].node) break;
		if (gf_node_get_field(storage->storageList.vals[i].node, storage->storageList.vals[i].fieldIndex, &info) != GF_OK) break;

		if (gf_sg_vrml_is_sf_field(info.fieldType)) {
			storage_parse_sf(info.far_ptr, info.fieldType, (char *) opt);
		} else {
			u32 sftype = gf_sg_vrml_get_sf_type(info.fieldType);
			char *sep, *val;
			void *slot;
			gf_sg_vrml_mf_reset(info.far_ptr, info.fieldType);
			while (1) {
				val = strchr(opt, '\'');
				sep = val ? strchr(val+1, '\'') : NULL;
				if (!val || !sep) break;

				sep[0] = 0;
				gf_sg_vrml_mf_append(info.far_ptr, info.fieldType, &slot);
				storage_parse_sf(slot, sftype, val+1);
				sep[0] = '\'';
				opt = sep+1;
			}
		}
		gf_node_changed(storage->storageList.vals[i].node, &info);
	}
	gf_free(section);
}
예제 #5
0
static void imagetexture_update(GF_TextureHandler *txh)
{
	if (gf_node_get_tag(txh->owner)!=TAG_MPEG4_CacheTexture) {
		MFURL url = ((M_ImageTexture *) txh->owner)->url;

		/*setup texture if needed*/
		if (!txh->is_open && url.count) {
			gf_sc_texture_play(txh, &url);
		}
		gf_sc_texture_update_frame(txh, 0);

		if (
		    /*URL is present but not opened - redraw till fetch*/
		    /* (txh->stream && !txh->tx_io) && */
		    /*image has been updated*/
		    txh->needs_refresh) {
			/*mark all subtrees using this image as dirty*/
			gf_node_dirty_parents(txh->owner);
			gf_sc_invalidate(txh->compositor, NULL);
		}
		return;
	}
	/*cache texture case*/
	else {
		M_CacheTexture *ct = (M_CacheTexture *) txh->owner;

		/*decode cacheTexture data */
		if ((ct->data || ct->image.buffer) && !txh->data) {
#ifndef GPAC_DISABLE_AV_PARSERS
			u32 out_size;
			GF_Err e;

			/*BT/XMT playback: load to memory*/
			if (ct->image.buffer) {
				char *par = (char *) gf_scene_get_service_url( gf_node_get_graph(txh->owner ) );
				char *src_url = gf_url_concatenate(par, ct->image.buffer);
				FILE *test = gf_fopen( src_url ? src_url : ct->image.buffer, "rb");
				if (test) {
					fseek(test, 0, SEEK_END);
					ct->data_len = (u32) gf_ftell(test);
					ct->data = gf_malloc(sizeof(char)*ct->data_len);
					fseek(test, 0, SEEK_SET);
					if (ct->data_len != fread(ct->data, 1, ct->data_len, test)) {
						GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to load CacheTexture data from file %s: IO err\n", src_url ? src_url : ct->image.buffer ) );
						gf_free(ct->data);
						ct->data = NULL;
						ct->data_len = 0;
					}
					gf_fclose(test);
				} else {
					GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to load CacheTexture data from file %s: not found\n", src_url ? src_url : ct->image.buffer ) );
				}
				ct->image.buffer = NULL;
				if (src_url) gf_free(src_url);
			}

			/*BIFS decoded playback*/
			switch (ct->objectTypeIndication) {
			case GPAC_OTI_IMAGE_JPEG:
				out_size = 0;
				e = gf_img_jpeg_dec((char *) ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, NULL, &out_size, 3);
				if (e==GF_BUFFER_TOO_SMALL) {
					u32 BPP;
					txh->data = gf_malloc(sizeof(char) * out_size);
					if (txh->pixelformat==GF_PIXEL_GREYSCALE) BPP = 1;
					else BPP = 3;

					e = gf_img_jpeg_dec((char *) ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, txh->data, &out_size, BPP);
					if (e==GF_OK) {
						gf_sc_texture_allocate(txh);
						gf_sc_texture_set_data(txh);
						txh->needs_refresh = 1;
						txh->stride = out_size / txh->height;
					}
				}
				break;
			case GPAC_OTI_IMAGE_PNG:
				out_size = 0;
				e = gf_img_png_dec((char *) ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, NULL, &out_size);
				if (e==GF_BUFFER_TOO_SMALL) {
					txh->data = gf_malloc(sizeof(char) * out_size);
					e = gf_img_png_dec((char *) ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, txh->data, &out_size);
					if (e==GF_OK) {
						gf_sc_texture_allocate(txh);
						gf_sc_texture_set_data(txh);
						txh->needs_refresh = 1;
						txh->stride = out_size / txh->height;
					}
				}
				break;
			}

#endif // GPAC_DISABLE_AV_PARSERS

			/*cacheURL is specified, store the image*/
			if (ct->cacheURL.buffer) {
				u32 i;
				u8 hash[20];
				FILE *cached_texture;
				char szExtractName[GF_MAX_PATH], section[64], *opt, *src_url;
				opt = (char *) gf_cfg_get_key(txh->compositor->user->config, "General", "CacheDirectory");
				if (opt) {
					strcpy(szExtractName, opt);
				} else {
					opt = gf_get_default_cache_directory();
					strcpy(szExtractName, opt);
					gf_free(opt);
				}
				strcat(szExtractName, "/");
				src_url = (char *) gf_scene_get_service_url( gf_node_get_graph(txh->owner ) );

				gf_sha1_csum((u8 *)src_url, (u32) strlen(src_url), hash);
				for (i=0; i<20; i++) {
					char t[3];
					t[2] = 0;
					sprintf(t, "%02X", hash[i]);
					strcat(szExtractName, t);
				}
				strcat(szExtractName, "_");

				strcat(szExtractName, ct->cacheURL.buffer);
				cached_texture = gf_fopen(szExtractName, "wb");
				if (cached_texture) {
					gf_fwrite(ct->data, 1, ct->data_len, cached_texture);
					gf_fclose(cached_texture);
				}

				/*and write cache info*/
				if (ct->expirationDate!=0) {
					sprintf(section, "@cache=%p", ct);
					gf_cfg_set_key(txh->compositor->user->config, section, "serviceURL", src_url);
					gf_cfg_set_key(txh->compositor->user->config, section, "cacheFile", szExtractName);
					gf_cfg_set_key(txh->compositor->user->config, section, "cacheName", ct->cacheURL.buffer);

					if (ct->expirationDate>0) {
						char exp[50];
						u32 sec, frac;
						gf_net_get_ntp(&sec, &frac);
						sec += ct->expirationDate;
						sprintf(exp, "%u", sec);
						gf_cfg_set_key(txh->compositor->user->config, section, "expireAfterNTP", exp);
					} else {
						gf_cfg_set_key(txh->compositor->user->config, section, "expireAfterNTP", "0");
					}
				}
			}

			/*done with image, destroy buffer*/
			if (ct->data) gf_free(ct->data);
			ct->data = NULL;
			ct->data_len = 0;
		}
	}
}
예제 #6
0
파일: video_muxer.c 프로젝트: Bevara/GPAC
int dc_video_muxer_write(VideoOutputFile *video_output_file, int frame_nb, Bool insert_utc)
{
	u64 frame_dur;
	GF_Err ret;
	switch (video_output_file->muxer_type) {
	case FFMPEG_VIDEO_MUXER:
		return dc_ffmpeg_video_muxer_write(video_output_file);
	case RAW_VIDEO_H264:
		return dc_raw_h264_write(video_output_file);
	case GPAC_VIDEO_MUXER:
	case GPAC_INIT_VIDEO_MUXER_AVC1:
	case GPAC_INIT_VIDEO_MUXER_AVC3:
		if (video_output_file->use_source_timing) {
			if (!video_output_file->fragment_started) {
				video_output_file->fragment_started = 1;
				ret = gf_isom_start_fragment(video_output_file->isof, 1);
				if (ret < 0)
					return -1;

				video_output_file->first_dts = video_output_file->codec_ctx->coded_frame->pkt_dts;
				if (!video_output_file->segment_started) {
					u32 sec, frac;
					u64 ntpts;
					video_output_file->pts_at_segment_start = video_output_file->codec_ctx->coded_frame->pts;
					video_output_file->segment_started = 1;

					if (insert_utc) {
						gf_net_get_ntp(&sec, &frac);
						ntpts = sec;
						ntpts <<= 32;
						ntpts |= frac;
						gf_isom_set_fragment_reference_time(video_output_file->isof, video_output_file->trackID, ntpts, video_output_file->pts_at_segment_start);
					}
				}
				gf_isom_set_traf_base_media_decode_time(video_output_file->isof, video_output_file->trackID, video_output_file->first_dts);
			}

			//keep track of previous frame dur and use last dur as the default duration for next sample
			//this works fine because we perform frame rate regulation at the capture stage
			frame_dur = video_output_file->codec_ctx->coded_frame->pts - video_output_file->last_pts;
			if (frame_dur && (video_output_file->frame_dur>(u32) frame_dur)) {
				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("New frame dur detected: %d vs %d old\n", (u32) frame_dur, (u32) video_output_file->frame_dur));
				video_output_file->frame_dur = (u32)frame_dur;
			}

			if (dc_gpac_video_isom_write(video_output_file) < 0) {
				return -1;
			}
			video_output_file->last_pts = video_output_file->codec_ctx->coded_frame->pts;
			video_output_file->last_dts = video_output_file->codec_ctx->coded_frame->pkt_dts;

			if (( video_output_file->last_dts - video_output_file->first_dts + video_output_file->frame_dur) /video_output_file->timescale >= video_output_file->frag_dur / 1000) {
				gf_isom_flush_fragments(video_output_file->isof, 1);
				video_output_file->fragment_started = 0;
				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DashCast] Flushed fragment to disk at UTC "LLU" ms - last coded frame PTS %d\n", gf_net_get_utc(), video_output_file->codec_ctx->coded_frame->pts));
			}

			//we may have rounding errors on the input PTS :( add half frame dur safety
			if (1000 * ( video_output_file->last_pts - video_output_file->pts_at_segment_start + 3*video_output_file->frame_dur/2) /video_output_file->timescale >= video_output_file->seg_dur ) {
				return 1;
			}
			return 0;
		}

		if (frame_nb % video_output_file->frame_per_fragment == 0) {
			gf_isom_start_fragment(video_output_file->isof, 1);

			if (!video_output_file->segment_started) {
				u32 sec, frac;
				u64 ntpts;

				video_output_file->pts_at_segment_start = video_output_file->codec_ctx->coded_frame->pts;
				video_output_file->segment_started = 1;

				if (insert_utc) {
					time_t secs;
					struct tm t;
					gf_net_get_ntp(&sec, &frac);
					ntpts = sec;
					ntpts <<= 32;
					ntpts |= frac;
					gf_isom_set_fragment_reference_time(video_output_file->isof, video_output_file->trackID, ntpts, video_output_file->pts_at_segment_start);

					secs = sec - GF_NTP_SEC_1900_TO_1970;
					t = *gmtime(&secs);
					GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DashCast] Producer reference clock: Timestamp %d matches sender NTP time %d-%02d-%02dT%02d:%02d:%02dZ (NTP frac part %u) \n", (u32) video_output_file->pts_at_segment_start, 1900+t.tm_year, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, (u32) frac ));

				}
			}


			gf_isom_set_traf_base_media_decode_time(video_output_file->isof, video_output_file->trackID, video_output_file->first_dts);
			video_output_file->first_dts += video_output_file->frame_per_fragment;
		}

		dc_gpac_video_isom_write(video_output_file);

		if (frame_nb % video_output_file->frame_per_fragment == video_output_file->frame_per_fragment - 1) {
			gf_isom_flush_fragments(video_output_file->isof, 1);
			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DashCast] Flushed fragment to disk at UTC "LLU" ms - last coded frame PTS %d\n", gf_net_get_utc(), video_output_file->codec_ctx->coded_frame->pts));
		}

		if (frame_nb + 1 == video_output_file->frame_per_segment)
			return 1;

		return 0;
	default:
		return -2;
	}

	return -2;
}