Add Russian translation provided by Валерий Крувялис <valkru@mail.ru>
[xiph-mirror.git] / ogg-tools / oggplay / oggplay.c
blob16bd711f72b51ef1cb8477f51444aa829551c1f4
1 /*
2 oggplay
4 a testbed player for ogg multimedia
6 $Date: 2003/06/22 21:43:03 $
8 Ralph Giles <giles@xiph.org>
10 This program my be redistributed under the terms of the
11 GNU General Public Licence, version 2, or at your preference,
12 any later version.
15 #include <stdio.h>
16 #include <stdlib.h>
18 #include <ogg/ogg.h>
19 #include <libmng.h>
20 #include <SDL/SDL.h>
22 #ifndef MIN
23 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
24 #endif
26 /* structure for keeping track of our mng stream inside the callbacks */
27 typedef struct {
28 FILE *file; /* pointer to the file we're decoding */
29 char *filename; /* pointer to the file path/name */
30 ogg_sync_state *oy; /* ogg input stream machinery */
31 ogg_stream_state *os;
32 ogg_page *og_last; /* for saving state */
33 ogg_packet *op_last;
34 int op_bytes;
35 SDL_Surface *surface; /* SDL display */
36 mng_uint32 delay; /* ticks to wait before resuming decode */
37 } stuff_t;
39 /* callbacks for the mng decoder */
41 /* memory allocation; data must be zeroed */
42 mng_ptr mymngalloc(mng_uint32 size)
44 return (mng_ptr)malloc(size);
47 /* memory deallocation */
48 void mymngfree(mng_ptr p, mng_uint32 size)
50 free(p);
51 return;
54 mng_bool mymngopenstream(mng_handle mng)
56 stuff_t *stuff;
58 /* look up our stream struct */
59 stuff = (stuff_t*)mng_get_userdata(mng);
61 /* open the file */
62 stuff->file = fopen(stuff->filename, "rb");
63 if (stuff->file == NULL) {
64 fprintf(stderr, "unable to open '%s'\n", stuff->filename);
65 return MNG_FALSE;
68 /* initialize our ogg decoder */
69 if (stuff->oy == NULL) {
70 stuff->oy = (ogg_sync_state*)malloc(sizeof(ogg_sync_state));
71 if (stuff->oy == NULL) {
72 fprintf(stderr, "unable to allocate ogg sync state\n");
73 fclose(stuff->file);
74 return MNG_FALSE;
76 ogg_sync_init(stuff->oy);
77 } else {
78 fprintf(stderr, "warning: reusing old ogg sync state\n");
79 ogg_sync_reset(stuff->oy);
81 /* we'll set this part up once we find our substream */
82 if (stuff->os != NULL) {
83 fprintf(stderr, "warning: reusing old ogg stream state\n");
84 ogg_stream_destroy(stuff->os);
85 stuff->os = NULL;
88 return MNG_TRUE;
91 mng_bool mymngclosestream(mng_handle mng)
93 stuff_t *stuff;
95 /* look up our stream struct */
96 stuff = (stuff_t*)mng_get_userdata(mng);
98 /* free the ogg state */
99 if (stuff->os != NULL) {
100 ogg_stream_destroy(stuff->os);
101 stuff->os = NULL;
103 if (stuff->oy != NULL) {
104 ogg_sync_destroy(stuff->oy);
105 stuff->oy = NULL;
108 /* close the file */
109 fclose(stuff->file);
110 stuff->file = NULL; /* for safety */
112 return MNG_TRUE;
115 /* feed data to the decoder */
116 mng_bool mymngreadstream(mng_handle mng, mng_ptr buffer,
117 mng_uint32 byteswanted, mng_uint32 *bytesread)
119 stuff_t *stuff;
120 char *buf;
121 int rc;
122 int streambytes, copybytes;
123 ogg_page *og = calloc(sizeof(*og),1);
124 ogg_packet *op = calloc(sizeof(*op),1);
126 /* look up our stream struct */
127 stuff = (stuff_t*)mng_get_userdata(mng);
129 *bytesread = 0;
131 fprintf(stderr, "trying to get %d bytes for libmng\n", byteswanted);
133 /* do we have any packets/pages left over from the last call? */
134 if (stuff->og_last && stuff->op_last) {
135 fprintf(stderr, "found packet %ld from last time\n",
136 stuff->op_last->packetno);
137 copybytes = MIN(byteswanted, stuff->op_last->bytes - stuff->op_bytes);
138 fprintf(stderr, " submitting %d bytes to the mng decoder\n", copybytes);
140 /* copy it into the mng decode buffer */
141 memcpy(buffer, stuff->op_last->packet + stuff->op_bytes, copybytes);
142 stuff->op_bytes += copybytes;
143 *bytesread += copybytes;
145 if (stuff->op_bytes == stuff->op_last->bytes) {
146 stuff->op_last = NULL;
147 stuff->op_bytes = 0;
150 /* are we done? */
151 if (*bytesread >= byteswanted)
152 return MNG_TRUE;
155 /* we still need more data, so look for more packets in the saved page */
156 if (stuff->og_last) {
157 while (ogg_stream_packetout(stuff->os, op) > 0) {
158 fprintf(stderr, " got packet %ld", op->packetno);
159 fprintf(stderr, " (%d bytes)\n", op->bytes);
161 /* copy it into the mng decode buffer */
162 copybytes = MIN(byteswanted - *bytesread, op->bytes);
163 fprintf(stderr, " submitting %d bytes to the mng decoder\n", copybytes);
164 memcpy(buffer + (*bytesread), op->packet, copybytes);
165 *bytesread += copybytes;
167 /* are we done? */
168 if (*bytesread >= byteswanted) {
169 /* save any state */
170 if (copybytes < op->bytes) {
171 stuff->op_last = op;
172 stuff->op_bytes = copybytes;
173 fprintf(stderr, "saving state: %d bytes left in packet number %ld\n",
174 op->bytes-copybytes, op->packetno);
176 return MNG_TRUE;
179 /* end of any stored data */
180 fprintf(stderr, " end of page %ld\n", ogg_page_pageno(stuff->og_last));
181 stuff->og_last = NULL;
182 stuff->op_last = NULL;
183 stuff->op_bytes= 0;
184 free(op);
185 free(og);
188 /* get a decoding buffer */
189 buf = ogg_sync_buffer(stuff->oy, byteswanted+4096);
190 if (buf == NULL) {
191 fprintf(stderr, "error: ogg sync returned no buffer\n");
192 return MNG_FALSE;
194 /* read the ogg bitstream from the file */
195 /* we'll always be short because of the framing overhead */
196 streambytes = fread(buf, 1, byteswanted+4096, stuff->file);
197 ogg_sync_wrote(stuff->oy, streambytes);
198 /* the ogg layer now has the data and we don't have to worry
199 about it again */
200 fprintf(stderr, "read %d bytes from '%s'\n",
201 streambytes, stuff->filename);
203 fprintf(stderr, "looking for ogg pages...\n");
205 /* process any pages we found */
206 while ((rc = ogg_sync_pageout(stuff->oy, og)) > 0) {
207 fprintf(stderr, " got page %x:%ld\n",
208 ogg_page_serialno(og), ogg_page_pageno(og));
209 /* have we seen this (or any) substream before? */
210 if (stuff->os == NULL) {
211 stuff->os = (ogg_stream_state*)malloc(sizeof(ogg_stream_state));
212 if (stuff->os == NULL) {
213 fprintf(stderr, "error: couldn't allocate stream state\n");
214 return MNG_FALSE;
216 ogg_stream_init(stuff->os, ogg_page_serialno(og));
217 fprintf(stderr, "creating stream state for logical bitstream %x\n", stuff->os->serialno);
219 if (ogg_page_serialno(og) == stuff->os->serialno) {
220 /* FIXME: for now, we just assume the substream with the first
221 header page is the mng we want to see */
223 /* packetize it */
224 ogg_stream_pagein(stuff->os, og);
225 while (ogg_stream_packetout(stuff->os, op) > 0) {
226 fprintf(stderr, " got packet %ld", op->packetno);
227 fprintf(stderr, " (%d bytes)\n", op->bytes);
228 /* check for overflow -- should never happen */
229 if (*bytesread >= byteswanted) {
230 fprintf(stderr, "error: packet data bigger than we thought!\n");
231 return MNG_FALSE;
233 copybytes = MIN(op->bytes, byteswanted - *bytesread);
235 /* copy it into the mng decode buffer */
236 memcpy(buffer + (*bytesread), op->packet, copybytes);
237 *bytesread += copybytes;
239 fprintf(stderr, " submitting %d bytes to the mng decoder\n", copybytes);
241 /* are we done? */
242 if (*bytesread >= byteswanted) {
243 /* save any state */
244 if (copybytes < op->bytes) {
245 stuff->og_last = og;
246 stuff->op_last = op;
247 stuff->op_bytes = copybytes;
248 fprintf(stderr, "saving state: %d bytes left in packet number %ld\n", op->bytes-copybytes, op->packetno);
249 } else {
250 stuff->og_last = NULL;
251 stuff->op_last = NULL;
252 stuff->op_bytes= 0;
253 fprintf(stderr, " end of page %ld\n", ogg_page_pageno(og));
254 free(op);
255 free(og);
257 return MNG_TRUE;
261 } else {
262 fprintf(stderr, "dropping page which does not match our serial number\n");
265 fprintf (stderr, "no (more) pages: %d\n", rc);
267 return MNG_TRUE;
270 /* the header's been read. set up the display stuff_t */
271 mng_bool mymngprocessheader(mng_handle mng,
272 mng_uint32 width, mng_uint32 height)
274 stuff_t *stuff;
275 SDL_Surface *screen;
276 char title[256];
278 fprintf(stderr, "our mng is %dx%d\n", width,height);
280 screen = SDL_SetVideoMode(width,height, 32, SDL_SWSURFACE);
281 if (screen == NULL) {
282 fprintf(stderr, "unable to allocate %dx%d video memory: %s\n",
283 width, height, SDL_GetError());
284 return MNG_FALSE;
287 /* save the surface pointer */
288 stuff = (stuff_t*)mng_get_userdata(mng);
289 stuff->surface = screen;
291 /* set a descriptive window title */
292 snprintf(title, 256, "mngplay: %s", stuff->filename);
293 SDL_WM_SetCaption(title, "mngplay");
295 /* if necessary, lock the drawing surface to the decoder
296 can safely fill it. We'll unlock elsewhere before display */
297 if (SDL_MUSTLOCK(stuff->surface)) {
298 if ( SDL_LockSurface(stuff->surface) < 0 ) {
299 fprintf(stderr, "could not lock display surface\n");
300 exit(1);
304 /* tell the mng decoder about our bit-depth choice */
305 /* FIXME: this works on intel. is it correct in general? */
306 mng_set_canvasstyle(mng, MNG_CANVAS_BGRA8);
308 return MNG_TRUE;
311 /* return a row pointer for the decoder to fill */
312 mng_ptr mymnggetcanvasline(mng_handle mng, mng_uint32 line)
314 stuff_t *stuff;
315 SDL_Surface *surface;
316 mng_ptr row;
318 /* dereference our structure */
319 stuff = (stuff_t*)mng_get_userdata(mng);
321 /* we assume any necessary locking has happened
322 outside, in the frame level code */
323 row = stuff->surface->pixels + stuff->surface->pitch*line;
325 // fprintf(stderr, " returning pointer to line %d (%p)\n", line, row);
327 return (row);
330 /* timer */
331 mng_uint32 mymnggetticks(mng_handle mng)
333 mng_uint32 ticks;
335 ticks = (mng_uint32)SDL_GetTicks();
336 //fprintf(stderr, " %d\t(returning tick count)\n",ticks);
338 return(ticks);
341 mng_bool mymngrefresh(mng_handle mng, mng_uint32 x, mng_uint32 y,
342 mng_uint32 w, mng_uint32 h)
344 stuff_t *stuff;
345 SDL_Rect frame;
347 frame.x = x;
348 frame.y = y;
349 frame.w = w;
350 frame.h = h;
352 /* dereference our structure */
353 stuff = (stuff_t*)mng_get_userdata(mng);
355 //fprintf(stderr, "refreshing display\n");
357 /* if necessary, unlock the display */
358 if (SDL_MUSTLOCK(stuff->surface)) {
359 SDL_UnlockSurface(stuff->surface);
362 /* refresh the screen with the new frame */
363 SDL_UpdateRects(stuff->surface, 1, &frame);
365 /* in necessary, relock the drawing surface */
366 if (SDL_MUSTLOCK(stuff->surface)) {
367 if ( SDL_LockSurface(stuff->surface) < 0 ) {
368 fprintf(stderr, "could not lock display surface\n");
369 return MNG_FALSE;
373 return MNG_TRUE;
376 /* interframe delay callback */
377 mng_bool mymngsettimer(mng_handle mng, mng_uint32 msecs)
379 stuff_t *stuff;
381 // fprintf(stderr," pausing for %d ms\n", msecs);
383 /* look up our stream struct */
384 stuff = (stuff_t*)mng_get_userdata(mng);
386 /* set the timer for when the decoder wants to be woken */
387 stuff->delay = msecs;
389 return MNG_TRUE;
393 mng_bool mymngerror(mng_handle mng, mng_int32 code, mng_int8 severity,
394 mng_chunkid chunktype, mng_uint32 chunkseq,
395 mng_int32 extra1, mng_int32 extra2, mng_pchar text)
397 stuff_t *stuff;
398 char chunk[5];
400 /* dereference our data so we can get the filename */
401 stuff = (stuff_t*)mng_get_userdata(mng);
403 /* pull out the chuck type as a string */
404 // FIXME: does this assume unsigned char?
405 chunk[0] = (char)((chunktype >> 24) & 0xFF);
406 chunk[1] = (char)((chunktype >> 16) & 0xFF);
407 chunk[2] = (char)((chunktype >> 8) & 0xFF);
408 chunk[3] = (char)((chunktype ) & 0xFF);
409 chunk[4] = '\0';
411 /* output the error */
412 fprintf(stderr, "error playing '%s' chunk %s (%d):\n",
413 stuff->filename, chunk, chunkseq);
414 fprintf(stderr, "%s\n", text);
416 return (0);
419 int mymngquit(mng_handle mng)
421 stuff_t *stuff;
423 /* dereference our data so we can free it */
424 stuff = (stuff_t*)mng_get_userdata(mng);
426 /* cleanup. this will call mymngclosestream */
427 mng_cleanup(&mng);
429 /* free our data */
430 free(stuff);
432 /* quit */
433 exit(0);
436 int checkevents(mng_handle mng)
438 SDL_Event event;
440 /* check if there's an event pending */
441 if (!SDL_PollEvent(&event)) {
442 return 0; /* no events pending */
445 /* we have an event; process it */
446 switch (event.type) {
447 case SDL_QUIT:
448 mymngquit(mng); /* quit */
449 break;
450 case SDL_KEYUP:
451 switch (event.key.keysym.sym) {
452 case SDLK_ESCAPE:
453 case SDLK_q:
454 mymngquit(mng);
455 break;
457 default:
458 return 1;
462 int main(int argc, char *argv[])
464 stuff_t *stuff;
465 mng_handle mng;
466 SDL_Rect updaterect;
468 if (argc < 2) {
469 fprintf(stderr, "usage: %s <mngfile>\n", argv[0]);
470 exit(1);
473 /* allocate our stream data structure */
474 stuff = (stuff_t*)calloc(1, sizeof(*stuff));
475 if (stuff == NULL) {
476 fprintf(stderr, "could not allocate stream structure.\n");
477 exit(0);
480 /* pass the name of the file we want to play */
481 stuff->filename = argv[1];
483 /* set up the mng decoder for our stream */
484 mng = mng_initialize(stuff, mymngalloc, mymngfree, MNG_NULL);
485 if (mng == MNG_NULL) {
486 fprintf(stderr, "could not initialize libmng.\n");
487 exit(1);
490 /* set the callbacks */
491 mng_setcb_errorproc(mng, mymngerror);
492 mng_setcb_openstream(mng, mymngopenstream);
493 mng_setcb_closestream(mng, mymngclosestream);
494 mng_setcb_readdata(mng, mymngreadstream);
495 mng_setcb_gettickcount(mng, mymnggetticks);
496 mng_setcb_settimer(mng, mymngsettimer);
497 mng_setcb_processheader(mng, mymngprocessheader);
498 mng_setcb_getcanvasline(mng, mymnggetcanvasline);
499 mng_setcb_refresh(mng, mymngrefresh);
500 /* FIXME: should check for errors here */
502 /* initialize SDL */
503 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
504 fprintf(stderr, "%s: Unable to initialize SDL (%s)\n",
505 argv[0], SDL_GetError());
506 exit(1);
508 /* arrange to call the shutdown routine before we exit */
509 atexit(SDL_Quit);
511 /* restrict event handling to the relevant bits */
512 SDL_EventState(SDL_KEYDOWN, SDL_IGNORE); /* keyup only */
513 SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
514 SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_IGNORE);
515 SDL_EventState(SDL_MOUSEBUTTONUP, SDL_IGNORE);
517 // fprintf(stderr, "playing mng...maybe.\n");
519 mng_readdisplay(mng);
521 /* loop though the frames */
522 while (stuff->delay) {
523 // fprintf(stderr, " waiting for %d ms\n", stuff->delay);
524 SDL_Delay(stuff->delay);
526 /* reset the delay in case the decoder
527 doesn't update it again */
528 stuff->delay = 0;
530 mng_display_resume(mng);
532 /* check for user input (just quit at this point) */
533 checkevents(mng);
536 /* ¿hay alguno? pause before quitting */
537 fprintf(stderr, "pausing before shutdown...\n");
538 SDL_Delay(3000);
540 /* cleanup and quit */
541 mymngquit(mng);