1 /*
2 * Copyright 2008-2014 Arsen Chaloyan
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * $Id: mpf_scheduler.c 2136 2014-07-04 06:33:36Z achaloyan@gmail.com $
17 */
18
19 #include "mpf_scheduler.h"
20
21 #ifdef WIN32
22 #define ENABLE_MULTIMEDIA_TIMERS
23 #endif
24
25 #ifdef ENABLE_MULTIMEDIA_TIMERS
26
27 #pragma warning(disable:4201)
28 #include <mmsystem.h>
29 #include <windows.h>
30
31 #ifndef TIME_KILL_SYNCHRONOUS
32 #define TIME_KILL_SYNCHRONOUS 0x0100
33 #endif
34
35 #else
36 #include <apr_thread_proc.h>
37 #endif
38
39
40 struct mpf_scheduler_t {
41 apr_pool_t *pool;
42 unsigned long resolution; /* scheduler resolution */
43
44 unsigned long media_resolution;
45 mpf_scheduler_proc_f media_proc;
46 void *media_obj;
47
48 unsigned long timer_resolution;
49 unsigned long timer_elapsed_time;
50 mpf_scheduler_proc_f timer_proc;
51 void *timer_obj;
52
53 #ifdef ENABLE_MULTIMEDIA_TIMERS
54 unsigned int timer_id;
55 #else
56 apr_thread_t *thread;
57 apt_bool_t running;
58 #endif
59 };
60
61 static APR_INLINE void mpf_scheduler_init(mpf_scheduler_t *scheduler);
62
63 /** Create scheduler */
mpf_scheduler_create(apr_pool_t * pool)64 MPF_DECLARE(mpf_scheduler_t*) mpf_scheduler_create(apr_pool_t *pool)
65 {
66 mpf_scheduler_t *scheduler = apr_palloc(pool,sizeof(mpf_scheduler_t));
67 mpf_scheduler_init(scheduler);
68 scheduler->pool = pool;
69 scheduler->resolution = 0;
70
71 scheduler->media_resolution = 0;
72 scheduler->media_obj = NULL;
73 scheduler->media_proc = NULL;
74
75 scheduler->timer_resolution = 0;
76 scheduler->timer_elapsed_time = 0;
77 scheduler->timer_obj = NULL;
78 scheduler->timer_proc = NULL;
79 return scheduler;
80 }
81
82 /** Destroy scheduler */
mpf_scheduler_destroy(mpf_scheduler_t * scheduler)83 MPF_DECLARE(void) mpf_scheduler_destroy(mpf_scheduler_t *scheduler)
84 {
85 /* nothing to destroy */
86 }
87
88 /** Set media processing clock */
mpf_scheduler_media_clock_set(mpf_scheduler_t * scheduler,unsigned long resolution,mpf_scheduler_proc_f proc,void * obj)89 MPF_DECLARE(apt_bool_t) mpf_scheduler_media_clock_set(
90 mpf_scheduler_t *scheduler,
91 unsigned long resolution,
92 mpf_scheduler_proc_f proc,
93 void *obj)
94 {
95 scheduler->media_resolution = resolution;
96 scheduler->media_proc = proc;
97 scheduler->media_obj = obj;
98 return TRUE;
99 }
100
101 /** Set timer clock */
mpf_scheduler_timer_clock_set(mpf_scheduler_t * scheduler,unsigned long resolution,mpf_scheduler_proc_f proc,void * obj)102 MPF_DECLARE(apt_bool_t) mpf_scheduler_timer_clock_set(
103 mpf_scheduler_t *scheduler,
104 unsigned long resolution,
105 mpf_scheduler_proc_f proc,
106 void *obj)
107 {
108 scheduler->timer_resolution = resolution;
109 scheduler->timer_elapsed_time = 0;
110 scheduler->timer_proc = proc;
111 scheduler->timer_obj = obj;
112 return TRUE;
113 }
114
115 /** Set scheduler rate (n times faster than real-time) */
mpf_scheduler_rate_set(mpf_scheduler_t * scheduler,unsigned long rate)116 MPF_DECLARE(apt_bool_t) mpf_scheduler_rate_set(
117 mpf_scheduler_t *scheduler,
118 unsigned long rate)
119 {
120 if(rate == 0 || rate > 10) {
121 /* rate shows how many times scheduler should be faster than real-time,
122 1 is the defualt and probably the only reasonable value,
123 however, the rates up to 10 times faster should be acceptable */
124 rate = 1;
125 }
126
127 scheduler->media_resolution /= rate;
128 scheduler->timer_resolution /= rate;
129 return TRUE;
130 }
131
mpf_scheduler_resolution_set(mpf_scheduler_t * scheduler)132 static APR_INLINE void mpf_scheduler_resolution_set(mpf_scheduler_t *scheduler)
133 {
134 if(scheduler->media_resolution) {
135 scheduler->resolution = scheduler->media_resolution;
136 }
137 else if(scheduler->timer_resolution) {
138 scheduler->resolution = scheduler->timer_resolution;
139 }
140 }
141
142
143
144 #ifdef ENABLE_MULTIMEDIA_TIMERS
145
mpf_scheduler_init(mpf_scheduler_t * scheduler)146 static APR_INLINE void mpf_scheduler_init(mpf_scheduler_t *scheduler)
147 {
148 scheduler->timer_id = 0;
149 }
150
mm_timer_proc(UINT uID,UINT uMsg,DWORD_PTR dwUser,DWORD_PTR dw1,DWORD_PTR dw2)151 static void CALLBACK mm_timer_proc(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
152 {
153 mpf_scheduler_t *scheduler = (mpf_scheduler_t*) dwUser;
154 if(scheduler->media_proc) {
155 scheduler->media_proc(scheduler,scheduler->media_obj);
156 }
157
158 if(scheduler->timer_proc) {
159 scheduler->timer_elapsed_time += scheduler->resolution;
160 if(scheduler->timer_elapsed_time >= scheduler->timer_resolution) {
161 scheduler->timer_elapsed_time = 0;
162 scheduler->timer_proc(scheduler,scheduler->timer_obj);
163 }
164 }
165 }
166
167 /** Start scheduler */
mpf_scheduler_start(mpf_scheduler_t * scheduler)168 MPF_DECLARE(apt_bool_t) mpf_scheduler_start(mpf_scheduler_t *scheduler)
169 {
170 mpf_scheduler_resolution_set(scheduler);
171 scheduler->timer_id = timeSetEvent(
172 scheduler->resolution, 0, mm_timer_proc, (DWORD_PTR) scheduler,
173 TIME_PERIODIC | TIME_CALLBACK_FUNCTION | TIME_KILL_SYNCHRONOUS);
174 return scheduler->timer_id ? TRUE : FALSE;
175 }
176
177 /** Stop scheduler */
mpf_scheduler_stop(mpf_scheduler_t * scheduler)178 MPF_DECLARE(apt_bool_t) mpf_scheduler_stop(mpf_scheduler_t *scheduler)
179 {
180 if(!scheduler) {
181 return FALSE;
182 }
183
184 timeKillEvent(scheduler->timer_id);
185 scheduler->timer_id = 0;
186 return TRUE;
187 }
188
189 #else
190
191 #include "apt_task.h"
192
mpf_scheduler_init(mpf_scheduler_t * scheduler)193 static APR_INLINE void mpf_scheduler_init(mpf_scheduler_t *scheduler)
194 {
195 scheduler->thread = NULL;
196 scheduler->running = FALSE;
197 }
198
timer_thread_proc(apr_thread_t * thread,void * data)199 static void* APR_THREAD_FUNC timer_thread_proc(apr_thread_t *thread, void *data)
200 {
201 mpf_scheduler_t *scheduler = data;
202 apr_interval_time_t timeout = scheduler->resolution * 1000;
203 apr_interval_time_t time_drift = 0;
204 apr_time_t time_now, time_last;
205
206 #if APR_HAS_SETTHREADNAME
207 apr_thread_name_set("MPF Scheduler");
208 #endif
209 time_now = apr_time_now();
210 while(scheduler->running == TRUE) {
211 time_last = time_now;
212
213 if(scheduler->media_proc) {
214 scheduler->media_proc(scheduler,scheduler->media_obj);
215 }
216
217 if(scheduler->timer_proc) {
218 scheduler->timer_elapsed_time += scheduler->resolution;
219 if(scheduler->timer_elapsed_time >= scheduler->timer_resolution) {
220 scheduler->timer_elapsed_time = 0;
221 scheduler->timer_proc(scheduler,scheduler->timer_obj);
222 }
223 }
224
225 if(timeout > time_drift) {
226 apr_sleep(timeout - time_drift);
227 }
228
229 time_now = apr_time_now();
230 time_drift += time_now - time_last - timeout;
231 #if 0
232 printf("time_drift=%d\n",time_drift);
233 #endif
234 }
235
236 apr_thread_exit(thread,APR_SUCCESS);
237 return NULL;
238 }
239
mpf_scheduler_start(mpf_scheduler_t * scheduler)240 MPF_DECLARE(apt_bool_t) mpf_scheduler_start(mpf_scheduler_t *scheduler)
241 {
242 mpf_scheduler_resolution_set(scheduler);
243
244 scheduler->running = TRUE;
245 if(apr_thread_create(&scheduler->thread,NULL,timer_thread_proc,scheduler,scheduler->pool) != APR_SUCCESS) {
246 scheduler->running = FALSE;
247 return FALSE;
248 }
249 return TRUE;
250 }
251
mpf_scheduler_stop(mpf_scheduler_t * scheduler)252 MPF_DECLARE(apt_bool_t) mpf_scheduler_stop(mpf_scheduler_t *scheduler)
253 {
254 if(!scheduler) {
255 return FALSE;
256 }
257
258 scheduler->running = FALSE;
259 if(scheduler->thread) {
260 apr_status_t s;
261 apr_thread_join(&s,scheduler->thread);
262 scheduler->thread = NULL;
263 }
264 return TRUE;
265 }
266
267 #endif
268