CDXLPlay added with permission from Fredik Wikstrom.
[AROS-Contrib.git] / MultiMedia / cdxlplay / main.c
blob09b86110713a40ea8edcbca9df2a61cdca413e2d
1 #include "cdxlplay.h"
2 #include "usleep.h"
4 uint32_t get_frame_duration (struct player_data *pd, struct cdxl_frame *frame);
5 void start_timer (struct player_data *pd);
6 void start_audio (struct player_data *pd);
7 void stop_timer (struct player_data *pd);
8 void stop_audio (struct player_data *pd);
9 struct cdxl_frame *get_next_frame(struct list *list, struct cdxl_frame *frame, int loop);
10 void audio_callback (void *userdata, uint8_t *stream, int len);
11 uint32_t timer_callback (uint32_t interval, void *param);
12 int do_cdxl_frame (struct player_data *pd);
13 int do_cdxl_audio (struct player_data *pd);
14 int decode_cdxl_audio (struct cdxl_file *cdxl, struct cdxl_frame *frame, int8_t **pcm, int *alen);
16 int main (int argc, char *argv[]) {
17 struct player_data pd;
18 int sdl_init = 0;
19 int gl_init = 0;
20 int quit = 0;
21 SDL_Event event;
22 SDL_AudioSpec desired;
23 int audio_init = 0;
24 uint64_t t1, t2;
25 int x_factor, y_factor;
26 int min_width, min_height;
28 memset(&pd, 0, sizeof(pd));
29 pd.sdl_video_flags = SDL_ANYFORMAT|SDL_RESIZABLE|SDL_OPENGL;
30 init_list(&pd.audio_list);
31 pd.aprebuf = 1;
32 pd.fps = 50;
33 pd.freq = 11025;
34 pd.loop = 0;
35 pd.status = PLAYING;
36 pd.pixel_ratio = 0x10000;
38 if (!get_options(argc, argv, &pd)) {
39 goto out;
42 pd.cdxl = open_cdxl(pd.filename);
43 if (!pd.cdxl) {
44 goto out;
47 if (!setup_usleep()) {
48 goto out;
51 if (SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO|SDL_INIT_AUDIO) == -1) {
52 fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
53 goto out;
55 sdl_init = 1;
57 pd.frame = (struct cdxl_frame *)pd.cdxl->list.head;
58 if (pd.pixel_ratio < 0x10000UL) {
59 x_factor = 1;
60 y_factor = 0x10000UL / pd.pixel_ratio;
61 } else {
62 x_factor = pd.pixel_ratio / 0x10000UL;
63 y_factor = 1;
65 min_width = pd.frame->pan.XSize * x_factor;
66 min_height = pd.frame->pan.YSize * y_factor;
68 pd.width = MAX(pd.width, min_width);
69 pd.height = MAX(pd.height, min_height);
70 pd.screen = SDL_SetVideoMode(pd.width, pd.height, 32, pd.sdl_video_flags);
71 #ifdef WORDS_BIGENDIAN
72 pd.surf = SDL_CreateRGBSurface(SDL_SWSURFACE,
73 pd.frame->pan.XSize, pd.frame->pan.YSize, 32,
74 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
75 #else
76 pd.surf = SDL_CreateRGBSurface(SDL_SWSURFACE,
77 pd.frame->pan.XSize, pd.frame->pan.YSize, 32,
78 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
79 #endif
80 if (!pd.screen || !pd.surf) {
81 fprintf(stderr, "Video initialization failed: %s\n", SDL_GetError());
82 goto out;
85 SDL_WM_SetCaption(PROGNAME" "VSTRING, PROGNAME);
87 SDL_ShowCursor((pd.sdl_video_flags & SDL_FULLSCREEN) ? SDL_DISABLE : SDL_ENABLE);
89 GLInit(pd.width, pd.height);
90 gl_init = 1;
92 pd.audio_mutex = SDL_CreateMutex();
93 pd.audio_cond = SDL_CreateCond();
94 pd.audio_enable = 0;
95 if (!pd.audio_mutex || !pd.audio_cond) {
96 goto out;
99 memset(&desired, 0, sizeof(desired));
100 desired.freq = pd.freq;
101 desired.format = AUDIO_S8;
102 desired.channels = 2;
103 desired.samples = 1024;
104 desired.callback = audio_callback;
105 desired.userdata = &pd;
106 if (SDL_OpenAudio(&desired, NULL) == -1) {
107 fprintf(stderr, "Audio initialization failed: %s\n", SDL_GetError());
108 goto out;
110 audio_init = 1;
111 SDL_PauseAudio(1);
113 gettimeofday(&pd.tv, NULL);
114 do_cdxl_frame(&pd);
115 start_audio(&pd);
116 start_timer(&pd);
117 while (!quit) {
118 if (SDL_WaitEvent(&event)) {
119 switch (event.type) {
120 case SDL_USEREVENT:
121 if (pd.status != PLAYING) {
122 break;
124 free_cdxl_frame(pd.cdxl, pd.frame);
125 t2 = pd.tv.tv_sec * 1000000ULL + pd.tv.tv_usec;
126 gettimeofday(&pd.tv, NULL);
127 t1 = pd.tv.tv_sec * 1000000ULL + pd.tv.tv_usec;
128 t2 += get_frame_duration(&pd, pd.frame);
129 if (t2 > t1) {
130 usleep(t2 - t1);
132 pd.tv.tv_sec = t2 / 1000000ULL;
133 pd.tv.tv_usec = t2 % 1000000ULL;
134 pd.frame = get_next_frame(&pd.cdxl->list, pd.frame, pd.loop);
135 if (!pd.frame) {
136 pd.status = PAUSED;
137 quit = 1;
138 break;
140 do_cdxl_frame(&pd);
141 if (pd.cdxl->has_audio) {
142 pd.aframe = get_next_frame(&pd.cdxl->list, pd.aframe, pd.loop);
143 if (!pd.aframe) {
144 break;
146 do_cdxl_audio(&pd);
148 break;
149 case SDL_KEYUP:
150 switch (event.key.keysym.sym) {
151 case SDLK_ESCAPE:
152 pd.status = PAUSED;
153 quit = 1;
154 break;
155 case SDLK_SPACE:
156 if (pd.status == PLAYING) {
157 pd.status = PAUSED;
158 stop_audio(&pd);
159 stop_timer(&pd);
160 } else {
161 pd.status = PLAYING;
162 gettimeofday(&pd.tv, NULL);
163 do_cdxl_frame(&pd);
164 start_audio(&pd);
165 start_timer(&pd);
167 break;
168 default:
169 break;
171 break;
172 case SDL_QUIT:
173 pd.status = PAUSED;
174 quit = 1;
175 break;
176 case SDL_VIDEORESIZE:
177 GLExit();
178 gl_init = 0;
179 pd.width = MAX(event.resize.w, min_width);
180 pd.height = MAX(event.resize.h, min_height);
181 pd.screen = SDL_SetVideoMode(pd.width, pd.height, 32, pd.sdl_video_flags);
182 if (!pd.screen) {
183 fprintf(stderr, "Resize failed: %s\n", SDL_GetError());
184 goto out;
186 GLInit(pd.width, pd.height);
187 gl_init = 1;
188 break;
193 out:
194 stop_timer(&pd);
195 if (audio_init) {
196 stop_audio(&pd);
197 SDL_CloseAudio();
199 if (pd.audio_cond) SDL_DestroyCond(pd.audio_cond);
200 if (pd.audio_mutex) SDL_DestroyMutex(pd.audio_mutex);
201 if (gl_init) GLExit();
202 if (pd.surf) SDL_FreeSurface(pd.surf);
203 if (sdl_init) SDL_Quit();
204 cleanup_usleep();
205 if (pd.cdxl) close_cdxl(pd.cdxl);
206 free_options();
207 return 0;
210 uint32_t get_frame_duration (struct player_data *pd, struct cdxl_frame *frame) {
211 uint32_t usec;
212 if (!frame) {
213 return 0;
215 if (frame->audio_size > 0) {
216 uint32_t audio_size = frame->audio_size;
217 if (frame->is_stereo) audio_size >>= 1;
218 usec = audio_size * 1000000UL / pd->freq;
219 } else {
220 usec = 1000000UL / pd->fps;
222 return usec;
225 void start_timer(struct player_data *pd) {
226 struct cdxl_frame *frame = pd->frame;
227 uint32_t frame_ms;
228 frame_ms = (get_frame_duration(pd, frame) / 10000) * 10;
229 pd->timer_id = SDL_AddTimer(frame_ms, timer_callback, pd);
232 void start_audio(struct player_data *pd) {
233 int i;
234 if (!pd->cdxl->has_audio) {
235 return;
237 pd->aframe = pd->frame;
238 do_cdxl_audio(pd);
239 for (i = 0; i < pd->aprebuf; i++) {
240 pd->aframe = get_next_frame(&pd->cdxl->list, pd->aframe, pd->loop);
241 if (!pd->aframe) {
242 break;
244 do_cdxl_audio(pd);
246 pd->audio_enable = 1;
247 SDL_PauseAudio(0);
250 void stop_timer(struct player_data *pd) {
251 if (pd->timer_id) {
252 SDL_RemoveTimer(pd->timer_id);
253 pd->timer_id = 0;
257 void stop_audio(struct player_data *pd) {
258 if (!pd->cdxl->has_audio) {
259 return;
261 if (pd->audio_enable) {
262 struct audio_node *anode;
263 SDL_LockMutex(pd->audio_mutex);
264 while ((anode = (struct audio_node *)rem_head(&pd->audio_list))) {
265 if (anode->pcm) free(anode->pcm);
266 free(anode);
268 pd->audio_enable = 0;
269 SDL_CondSignal(pd->audio_cond);
270 SDL_UnlockMutex(pd->audio_mutex);
271 SDL_PauseAudio(1);
275 struct cdxl_frame *get_next_frame(struct list *list, struct cdxl_frame *frame, int loop) {
276 if (!frame) {
277 return NULL;
279 frame = (struct cdxl_frame *)frame->node.succ;
280 if (frame->node.succ) {
281 return frame;
282 } else if (loop) {
283 return (struct cdxl_frame *)list->head;
284 } else {
285 return NULL;
289 void audio_callback (void *userdata, uint8_t *stream, int len) {
290 struct player_data *pd = userdata;
291 struct audio_node *anode, *next;
292 const int8_t *source;
293 int8_t *dest = stream;
294 int alen;
295 if (len <= 0) {
296 return;
298 for (;;) {
299 if (!pd->audio_enable) {
300 memset(dest, 0, len);
301 return;
303 SDL_LockMutex(pd->audio_mutex);
304 anode = (struct audio_node *)pd->audio_list.head;
305 while (anode->node.succ) {
306 next = (struct audio_node *)anode->node.succ;
308 source = anode->pcm + anode->apos;
309 alen = anode->alen - anode->apos;
310 if (alen > len) alen = len;
311 if (anode->pcm)
312 memcpy(dest, source, alen);
313 else
314 memset(dest, 0, alen);
315 dest += alen;
316 anode->apos += alen;
317 len -= alen;
318 if (anode->apos == anode->alen) {
319 rem_node(&anode->node);
320 if (anode->pcm) free(anode->pcm);
321 free(anode);
323 if (len == 0) {
324 SDL_UnlockMutex(pd->audio_mutex);
325 return;
328 anode = next;
330 SDL_CondWait(pd->audio_cond, pd->audio_mutex);
331 SDL_UnlockMutex(pd->audio_mutex);
335 uint32_t timer_callback (uint32_t interval, void *param) {
336 struct player_data *pd = param;
337 struct cdxl_frame *frame;
338 uint32_t frame_ms;
339 SDL_Event event;
341 frame = get_next_frame(&pd->cdxl->list, pd->frame, pd->loop);
342 frame_ms = (get_frame_duration(pd, frame) / 10000) * 10;
344 event.type = SDL_USEREVENT;
345 event.user.code = 0;
346 event.user.data1 = NULL;
347 event.user.data2 = NULL;
348 SDL_PushEvent(&event);
350 return frame_ms;
353 int do_cdxl_frame (struct player_data *pd) {
354 struct cdxl_frame *frame = pd->frame;
355 if (read_cdxl_frame(pd->cdxl, frame) == -1) {
356 return -1;
358 if (decode_cdxl_frame(pd->cdxl, frame, pd->surf) == -1) {
359 return -1;
361 GLScaleSurface(pd->surf, pd->screen, pd->pixel_ratio);
362 SDL_GL_SwapBuffers();
363 return 0;
366 int do_cdxl_audio (struct player_data *pd) {
367 struct cdxl_frame *frame = pd->aframe;
368 if (!pd->cdxl->has_audio) {
369 return -1;
371 if (read_cdxl_frame(pd->cdxl, frame) == -1) {
372 return -1;
374 if (frame->audio_size > 0) {
375 struct audio_node *anode;
376 anode = malloc(sizeof(*anode));
377 if (!anode) {
378 return -1;
380 if (decode_cdxl_audio(pd->cdxl, frame, &anode->pcm, &anode->alen)) {
381 free(anode);
382 return -1;
384 anode->frame = frame->index;
385 anode->apos = 0;
386 SDL_LockMutex(pd->audio_mutex);
387 add_tail(&pd->audio_list, &anode->node);
388 SDL_CondSignal(pd->audio_cond);
389 SDL_UnlockMutex(pd->audio_mutex);
390 return 0;
391 } else {
392 struct audio_node *anode;
393 anode = malloc(sizeof(*anode));
394 if (!anode) {
395 return -1;
397 anode->pcm = NULL;
398 anode->alen = pd->freq / pd->fps;
399 if (frame->is_stereo) {
400 anode->alen <<= 1;
402 SDL_LockMutex(pd->audio_mutex);
403 add_tail(&pd->audio_list, &anode->node);
404 SDL_CondSignal(pd->audio_cond);
405 SDL_UnlockMutex(pd->audio_mutex);
406 return 0;
410 int decode_cdxl_audio (struct cdxl_file *cdxl, struct cdxl_frame *frame, int8_t **pcm, int *alen) {
411 if (cdxl && frame && frame->buffer) {
412 int len, i, samples;
413 const int8_t *src_l = (int8_t *)frame->buffer + frame->cmap_size + frame->video_size;
414 const int8_t *src_r = src_l;
415 int8_t *dest;
416 len = samples = frame->audio_size;
417 if (frame->is_stereo) {
418 len &= ~1;
419 samples >>= 1;
420 src_r += samples;
421 } else {
422 len <<= 1;
424 if (len <= 0) {
425 return -1;
427 dest = malloc(len);
428 if (!dest) {
429 return -1;
431 *pcm = dest;
432 *alen = len;
433 for (i = 0; i < samples; i++) {
434 *dest++ = *src_l++;
435 *dest++ = *src_r++;
437 return 0;
439 return -1;