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