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,
23 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
26 /* structure for keeping track of our mng stream inside the callbacks */
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 */
32 ogg_page
*og_last
; /* for saving state */
35 SDL_Surface
*surface
; /* SDL display */
36 mng_uint32 delay
; /* ticks to wait before resuming decode */
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
)
54 mng_bool
mymngopenstream(mng_handle mng
)
58 /* look up our stream struct */
59 stuff
= (stuff_t
*)mng_get_userdata(mng
);
62 stuff
->file
= fopen(stuff
->filename
, "rb");
63 if (stuff
->file
== NULL
) {
64 fprintf(stderr
, "unable to open '%s'\n", stuff
->filename
);
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");
76 ogg_sync_init(stuff
->oy
);
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
);
91 mng_bool
mymngclosestream(mng_handle mng
)
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
);
103 if (stuff
->oy
!= NULL
) {
104 ogg_sync_destroy(stuff
->oy
);
110 stuff
->file
= NULL
; /* for safety */
115 /* feed data to the decoder */
116 mng_bool
mymngreadstream(mng_handle mng
, mng_ptr buffer
,
117 mng_uint32 byteswanted
, mng_uint32
*bytesread
)
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
);
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
;
151 if (*bytesread
>= byteswanted
)
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
;
168 if (*bytesread
>= byteswanted
) {
170 if (copybytes
< op
->bytes
) {
172 stuff
->op_bytes
= copybytes
;
173 fprintf(stderr
, "saving state: %d bytes left in packet number %ld\n",
174 op
->bytes
-copybytes
, op
->packetno
);
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
;
188 /* get a decoding buffer */
189 buf
= ogg_sync_buffer(stuff
->oy
, byteswanted
+4096);
191 fprintf(stderr
, "error: ogg sync returned no buffer\n");
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
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");
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 */
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");
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
);
242 if (*bytesread
>= byteswanted
) {
244 if (copybytes
< op
->bytes
) {
247 stuff
->op_bytes
= copybytes
;
248 fprintf(stderr
, "saving state: %d bytes left in packet number %ld\n", op
->bytes
-copybytes
, op
->packetno
);
250 stuff
->og_last
= NULL
;
251 stuff
->op_last
= NULL
;
253 fprintf(stderr
, " end of page %ld\n", ogg_page_pageno(og
));
262 fprintf(stderr
, "dropping page which does not match our serial number\n");
265 fprintf (stderr
, "no (more) pages: %d\n", rc
);
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
)
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());
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");
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
);
311 /* return a row pointer for the decoder to fill */
312 mng_ptr
mymnggetcanvasline(mng_handle mng
, mng_uint32 line
)
315 SDL_Surface
*surface
;
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);
331 mng_uint32
mymnggetticks(mng_handle mng
)
335 ticks
= (mng_uint32
)SDL_GetTicks();
336 //fprintf(stderr, " %d\t(returning tick count)\n",ticks);
341 mng_bool
mymngrefresh(mng_handle mng
, mng_uint32 x
, mng_uint32 y
,
342 mng_uint32 w
, mng_uint32 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");
376 /* interframe delay callback */
377 mng_bool
mymngsettimer(mng_handle mng
, mng_uint32 msecs
)
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
;
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
)
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);
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
);
419 int mymngquit(mng_handle mng
)
423 /* dereference our data so we can free it */
424 stuff
= (stuff_t
*)mng_get_userdata(mng
);
426 /* cleanup. this will call mymngclosestream */
436 int checkevents(mng_handle mng
)
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
) {
448 mymngquit(mng
); /* quit */
451 switch (event
.key
.keysym
.sym
) {
462 int main(int argc
, char *argv
[])
469 fprintf(stderr
, "usage: %s <mngfile>\n", argv
[0]);
473 /* allocate our stream data structure */
474 stuff
= (stuff_t
*)calloc(1, sizeof(*stuff
));
476 fprintf(stderr
, "could not allocate stream structure.\n");
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");
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 */
503 if (SDL_Init(SDL_INIT_VIDEO
) < 0) {
504 fprintf(stderr
, "%s: Unable to initialize SDL (%s)\n",
505 argv
[0], SDL_GetError());
508 /* arrange to call the shutdown routine before we exit */
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 */
530 mng_display_resume(mng
);
532 /* check for user input (just quit at this point) */
536 /* ¿hay alguno? pause before quitting */
537 fprintf(stderr
, "pausing before shutdown...\n");
540 /* cleanup and quit */