1 /*
2  *			GPAC - Multimedia Framework C SDK
3  *
4  *			Authors: Jean Le Feuvre
5  *			Copyright (c) Telecom ParisTech 2000-2017
6  *					All rights reserved
7  *
8  *  This file scene part of GPAC / Scene Compositor sub-project
9  *
10  *  GPAC scene 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 scene 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/compositor_dev.h>
27 
gf_clock_new(GF_Compositor * compositor)28 static GF_Clock *gf_clock_new(GF_Compositor *compositor)
29 {
30 	GF_Clock *tmp;
31 	GF_SAFEALLOC(tmp, GF_Clock);
32 	if (!tmp) return NULL;
33 	tmp->mx = gf_mx_new("Clock");
34 	tmp->compositor = compositor;
35 	tmp->speed = FIX_ONE;
36 	tmp->timeline_id = 1;
37 	return tmp;
38 }
39 
gf_clock_del(GF_Clock * ck)40 void gf_clock_del(GF_Clock *ck)
41 {
42 	gf_mx_del(ck->mx);
43 	gf_free(ck);
44 }
45 
gf_clock_find(GF_List * Clocks,u16 clock_id,u16 ES_ID)46 GF_Clock *gf_clock_find(GF_List *Clocks, u16 clock_id, u16 ES_ID)
47 {
48 	u32 i;
49 	GF_Clock *tmp;
50 	i=0;
51 	while ((tmp = (GF_Clock *)gf_list_enum(Clocks, &i))) {
52 		//first check the clock ID
53 		if (tmp->clock_id == clock_id) return tmp;
54 		//then check the ES ID
55 		if (ES_ID && (tmp->clock_id == ES_ID)) return tmp;
56 	}
57 	//no clocks found...
58 	return NULL;
59 }
60 
gf_ck_look_for_clock_dep(GF_Scene * scene,u16 clock_id)61 static GF_Clock *gf_ck_look_for_clock_dep(GF_Scene *scene, u16 clock_id)
62 {
63 	u32 i, j;
64 	GF_ODMExtraPid *xpid;
65 	GF_ObjectManager *odm;
66 
67 	/*check in top OD*/
68 	if (scene->root_od->pid_id == clock_id) return scene->root_od->ck;
69 	i=0;
70 	while ((xpid = (GF_ODMExtraPid*)gf_list_enum(scene->root_od->extra_pids, &i))) {
71 		if (xpid->pid_id == clock_id) return scene->root_od->ck;
72 	}
73 	/*check in sub ODs*/
74 	j=0;
75 	while ((odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &j))) {
76 		if (odm->pid_id == clock_id) return odm->ck;
77 		i=0;
78 		while ((xpid = (GF_ODMExtraPid*)gf_list_enum(odm->extra_pids, &i))) {
79 			if (xpid->pid_id == clock_id) return odm->ck;
80 		}
81 	}
82 	return NULL;
83 }
84 
85 /*remove clocks created due to out-of-order OCR dependencies*/
gf_ck_resolve_clock_dep(GF_List * clocks,GF_Scene * scene,GF_Clock * new_ck,u16 Clock_ESID)86 static void gf_ck_resolve_clock_dep(GF_List *clocks, GF_Scene *scene, GF_Clock *new_ck, u16 Clock_ESID)
87 {
88 	u32 i;
89 	GF_Clock *clock;
90 	GF_ObjectManager *odm;
91 
92 	/*check all objects - if any uses a clock which ID == the clock_ESID then
93 	this clock shall be removed*/
94 	if (scene->root_od->ck && (scene->root_od->ck->clock_id == Clock_ESID)) {
95 		scene->root_od->ck = new_ck;
96 	}
97 	i=0;
98 	while ((odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) {
99 		if (odm->ck && (odm->ck->clock_id == Clock_ESID)) {
100 			odm->ck = new_ck;
101 		}
102 	}
103 	/*destroy clock*/
104 	i=0;
105 	while ((clock = (GF_Clock*)gf_list_enum(clocks, &i))) {
106 		if (clock->clock_id == Clock_ESID) {
107 			gf_list_rem(clocks, i-1);
108 			gf_clock_del(clock);
109 			return;
110 		}
111 	}
112 }
113 
gf_clock_attach(GF_List * clocks,GF_Scene * scene,u16 clock_id,u16 ES_ID,s32 hasOCR)114 GF_Clock *gf_clock_attach(GF_List *clocks, GF_Scene *scene, u16 clock_id, u16 ES_ID, s32 hasOCR)
115 {
116 	Bool check_dep;
117 	GF_Clock *tmp = gf_clock_find(clocks, clock_id, ES_ID);
118 	/*ck dep can only be solved if in the main service*/
119 	check_dep = (scene->root_od->scene_ns && scene->root_od->scene_ns->clocks==clocks) ? GF_TRUE : GF_FALSE;
120 
121 	/*this partly solves a->b->c*/
122 	if (!tmp && check_dep) tmp = gf_ck_look_for_clock_dep(scene, clock_id);
123 	if (!tmp) {
124 		tmp = gf_clock_new(scene->compositor);
125 		tmp->clock_id = clock_id;
126 		gf_list_add(clocks, tmp);
127 	} else {
128 		if (tmp->clock_id == ES_ID) tmp->clock_id = clock_id;
129 		/*this finally solves a->b->c*/
130 		if (check_dep && (tmp->clock_id != ES_ID)) gf_ck_resolve_clock_dep(clocks, scene, tmp, ES_ID);
131 	}
132 	return tmp;
133 }
134 
gf_clock_reset(GF_Clock * ck)135 void gf_clock_reset(GF_Clock *ck)
136 {
137 	ck->clock_init = 0;
138 	ck->audio_delay = 0;
139 	ck->speed_set_time = 0;
140 	//do NOT reset buffering flag, because RESET scene called only
141 	//for the stream owning the clock, and other streams may
142 	//have signaled buffering on this clock
143 	ck->init_timestamp = 0;
144 	ck->start_time = 0;
145 	ck->has_seen_eos = 0;
146 	ck->media_time_at_init = 0;
147 	ck->has_media_time_shift = 0;
148 	ck->timeline_id++;
149 }
150 
gf_clock_set_time(GF_Clock * ck,u32 TS)151 void gf_clock_set_time(GF_Clock *ck, u32 TS)
152 {
153 	if (!ck->clock_init) {
154 		ck->init_timestamp = TS;
155 		ck->clock_init = 1;
156 		ck->audio_delay = 0;
157 		/*update starttime and pausetime even in pause mode*/
158 		ck->pause_time = ck->start_time = gf_sc_get_clock(ck->compositor);
159 	}
160 }
161 
162 
163 
gf_clock_pause(GF_Clock * ck)164 void gf_clock_pause(GF_Clock *ck)
165 {
166 	gf_mx_p(ck->mx);
167 	if (!ck->nb_paused)
168 		ck->pause_time = gf_sc_get_clock(ck->compositor);
169 	ck->nb_paused += 1;
170 	gf_mx_v(ck->mx);
171 }
172 
gf_clock_resume(GF_Clock * ck)173 void gf_clock_resume(GF_Clock *ck)
174 {
175 	gf_mx_p(ck->mx);
176 	assert(ck->nb_paused);
177 	if (!ck->nb_paused) {
178 		assert(!ck->nb_buffering);
179 	}
180 	ck->nb_paused -= 1;
181 	//in player mode, increment the start time to reflect how long we have been buffering
182 	//in non-player mode, since we don't care about real-time, don't update the clock start time
183 	//this avoids cases where the first composed frame is dispatched while the object(s) are buffering
184 	//updating the clock would rewind the timebase in the past and won't trigger next frame fetch on these objects
185 	if (!ck->nb_paused && ck->compositor->player)
186 		ck->start_time += gf_sc_get_clock(ck->compositor) - ck->pause_time;
187 	gf_mx_v(ck->mx);
188 }
189 
190 
gf_clock_real_time(GF_Clock * ck)191 u32 gf_clock_real_time(GF_Clock *ck)
192 {
193 	u32 time;
194 	assert(ck);
195 	if (!ck->clock_init) return ck->start_time;
196 	time = ck->nb_paused > 0 ? ck->pause_time : gf_sc_get_clock(ck->compositor);
197 
198 #ifdef GPAC_FIXED_POINT
199 
200 	if ((ck->speed < 0) && ((s32) ck->init_timestamp < FIX2INT( (-ck->speed * 100) * (time - ck->start_time)) / 100 ) ) {
201 		time = 0;
202 	} else {
203 		time = ck->speed_set_time + ck->init_timestamp + (time - ck->start_time) * FIX2INT(100*ck->speed) / 100;
204 	}
205 
206 #else
207 
208 	if ((ck->speed < 0) && ((s32) ck->init_timestamp < (-ck->speed) * (time - ck->start_time))) {
209 		time = 0;
210 	} else {
211 		//DO NOT CHANGE the position of cast float->u32, otherwise we have precision issues when ck->init_timestamp
212 		//is >= 0x40000000. We know for sure that ck->speed * (time - ck->start_time) is positive
213 		time = ck->speed_set_time + ck->init_timestamp + (u32) (ck->speed * (time - ck->start_time) );
214 	}
215 
216 #endif
217 
218 	return time;
219 }
220 
221 GF_EXPORT
gf_clock_time(GF_Clock * ck)222 u32 gf_clock_time(GF_Clock *ck)
223 {
224 	u32 time = gf_clock_real_time(ck);
225 	if ((ck->audio_delay>0) && (time < (u32) ck->audio_delay)) return 0;
226 	return time - ck->audio_delay;
227 }
228 
gf_clock_to_media_time(GF_Clock * ck,u32 clock_val)229 u32 gf_clock_to_media_time(GF_Clock *ck, u32 clock_val)
230 {
231 	u32 t = clock_val;
232 	if (ck && ck->has_media_time_shift) {
233 		if (t>ck->init_timestamp) t -= ck->init_timestamp;
234 		else t=0;
235 		t += ck->media_time_at_init;
236 	}
237 	return t;
238 }
239 
gf_clock_media_time(GF_Clock * ck)240 u32 gf_clock_media_time(GF_Clock *ck)
241 {
242 	u32 t;
243 	if (!ck) return 0;
244 	if (!ck->has_seen_eos && ck->last_ts_rendered) t = ck->last_ts_rendered;
245 	else t = gf_clock_time(ck);
246 	return gf_clock_to_media_time(ck, t);
247 }
248 
gf_clock_elapsed_time(GF_Clock * ck)249 u32 gf_clock_elapsed_time(GF_Clock *ck)
250 {
251 	if (!ck || ck->nb_buffering || ck->nb_paused) return 0;
252 	return gf_sys_clock() - ck->start_time;
253 }
254 
gf_clock_is_started(GF_Clock * ck)255 Bool gf_clock_is_started(GF_Clock *ck)
256 {
257 	if (!ck || !ck->clock_init || ck->nb_buffering || ck->nb_paused) return 0;
258 	return 1;
259 }
260 
261 /*buffering scene protected by a mutex because it may be triggered by composition memory (audio or visual threads)*/
gf_clock_buffer_on(GF_Clock * ck)262 void gf_clock_buffer_on(GF_Clock *ck)
263 {
264 	gf_mx_p(ck->mx);
265 	if (!ck->nb_buffering) gf_clock_pause(ck);
266 	ck->nb_buffering += 1;
267 	gf_mx_v(ck->mx);
268 }
269 
gf_clock_buffer_off(GF_Clock * ck)270 void gf_clock_buffer_off(GF_Clock *ck)
271 {
272 	gf_mx_p(ck->mx);
273 	//assert(ck->nb_buffering);
274 	if (ck->nb_buffering) {
275 		ck->nb_buffering -= 1;
276 		if (!ck->nb_buffering)
277 			gf_clock_resume(ck);
278 	}
279 	gf_mx_v(ck->mx);
280 }
281 
282 
gf_clock_set_speed(GF_Clock * ck,Fixed speed)283 void gf_clock_set_speed(GF_Clock *ck, Fixed speed)
284 {
285 	u32 time;
286 	if (speed==ck->speed) return;
287 	time = gf_sc_get_clock(ck->compositor);
288 	/*adjust start time*/
289 	ck->speed_set_time = gf_clock_time(ck) - ck->init_timestamp;
290 	ck->pause_time = ck->start_time = time;
291 	ck->speed = speed;
292 }
293 
gf_clock_set_audio_delay(GF_Clock * ck,s32 ms_delay)294 void gf_clock_set_audio_delay(GF_Clock *ck, s32 ms_delay)
295 {
296 	if (ck) ck->audio_delay = ms_delay;
297 }
298 
299