test/simplesdl.c
author Ryan C. Gordon <icculus@icculus.org>
Sat, 06 Jul 2013 20:50:31 -0400
changeset 58 058686dc593f
parent 39 d4d4999ba762
permissions -rw-r--r--
Fixed logic bug (thanks, Lionel!).
     1 /**
     2  * TheoraPlay; multithreaded Ogg Theora/Ogg Vorbis decoding.
     3  *
     4  * Please see the file LICENSE.txt in the source's root directory.
     5  *
     6  *  This file written by Ryan C. Gordon.
     7  */
     8 
     9 /*
    10  * This is meant to be a dirt simple test case. If you want a big, robust
    11  *  version that handles lots of strange variations, try sdltheoraplay.c
    12  */
    13 
    14 #include <stdio.h>
    15 #include <string.h>
    16 #include <unistd.h>
    17 #include <assert.h>
    18 
    19 #include "theoraplay.h"
    20 #include "SDL.h"
    21 
    22 static Uint32 baseticks = 0;
    23 
    24 typedef struct AudioQueue
    25 {
    26     const THEORAPLAY_AudioPacket *audio;
    27     int offset;
    28     struct AudioQueue *next;
    29 } AudioQueue;
    30 
    31 static volatile AudioQueue *audio_queue = NULL;
    32 static volatile AudioQueue *audio_queue_tail = NULL;
    33 
    34 static void SDLCALL audio_callback(void *userdata, Uint8 *stream, int len)
    35 {
    36     // !!! FIXME: this should refuse to play if item->playms is in the future.
    37     //const Uint32 now = SDL_GetTicks() - baseticks;
    38     Sint16 *dst = (Sint16 *) stream;
    39 
    40     while (audio_queue && (len > 0))
    41     {
    42         volatile AudioQueue *item = audio_queue;
    43         AudioQueue *next = item->next;
    44         const int channels = item->audio->channels;
    45 
    46         const float *src = item->audio->samples + (item->offset * channels);
    47         int cpy = (item->audio->frames - item->offset) * channels;
    48         int i;
    49 
    50         if (cpy > (len / sizeof (Sint16)))
    51             cpy = len / sizeof (Sint16);
    52 
    53         for (i = 0; i < cpy; i++)
    54         {
    55             const float val = *(src++);
    56             if (val < -1.0f)
    57                 *(dst++) = -32768;
    58             else if (val > 1.0f)
    59                 *(dst++) = 32767;
    60             else
    61                 *(dst++) = (Sint16) (val * 32767.0f);
    62         } // for
    63 
    64         item->offset += (cpy / channels);
    65         len -= cpy * sizeof (Sint16);
    66 
    67         if (item->offset >= item->audio->frames)
    68         {
    69             THEORAPLAY_freeAudio(item->audio);
    70             free((void *) item);
    71             audio_queue = next;
    72         } // if
    73     } // while
    74 
    75     if (!audio_queue)
    76         audio_queue_tail = NULL;
    77 
    78     if (len > 0)
    79         memset(dst, '\0', len);
    80 } // audio_callback
    81 
    82 
    83 static void queue_audio(const THEORAPLAY_AudioPacket *audio)
    84 {
    85     AudioQueue *item = (AudioQueue *) malloc(sizeof (AudioQueue));
    86     if (!item)
    87     {
    88         THEORAPLAY_freeAudio(audio);
    89         return;  // oh well.
    90     } // if
    91 
    92     item->audio = audio;
    93     item->offset = 0;
    94     item->next = NULL;
    95 
    96     SDL_LockAudio();
    97     if (audio_queue_tail)
    98         audio_queue_tail->next = item;
    99     else
   100         audio_queue = item;
   101     audio_queue_tail = item;
   102     SDL_UnlockAudio();
   103 } // queue_audio
   104 
   105 
   106 static void playfile(const char *fname)
   107 {
   108     THEORAPLAY_Decoder *decoder = NULL;
   109     const THEORAPLAY_VideoFrame *video = NULL;
   110     const THEORAPLAY_AudioPacket *audio = NULL;
   111     SDL_Surface *screen = NULL;
   112     SDL_Overlay *overlay = NULL;
   113     SDL_AudioSpec spec;
   114     SDL_Event event;
   115     Uint32 framems = 0;
   116     int initfailed = 0;
   117     int quit = 0;
   118 
   119     printf("Trying file '%s' ...\n", fname);
   120 
   121     decoder = THEORAPLAY_startDecodeFile(fname, 30, THEORAPLAY_VIDFMT_IYUV);
   122     if (!decoder)
   123     {
   124         fprintf(stderr, "Failed to start decoding '%s'!\n", fname);
   125         return;
   126     } // if
   127 
   128     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) == -1)
   129     {
   130         fprintf(stderr, "SDL_Init() failed: %s\n", SDL_GetError());
   131         return;
   132     } // if
   133 
   134     // wait until we have video and/or audio data, so we can set up hardware.
   135     while (!audio || !video)
   136     {
   137         if (!audio) audio = THEORAPLAY_getAudio(decoder);
   138         if (!video) video = THEORAPLAY_getVideo(decoder);
   139         SDL_Delay(10);
   140     } // if
   141 
   142     SDL_WM_SetCaption(fname, "TheoraPlay");
   143 
   144     framems = (video->fps == 0.0) ? 0 : ((Uint32) (1000.0 / video->fps));
   145     screen = SDL_SetVideoMode(video->width, video->height, 0, 0);
   146     if (!screen)
   147         fprintf(stderr, "SDL_SetVideoMode() failed: %s\n", SDL_GetError());
   148     else  // software surface
   149     {
   150         // blank out the screen to start.
   151         SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
   152         SDL_Flip(screen);
   153 
   154         overlay = SDL_CreateYUVOverlay(video->width, video->height,
   155                                        SDL_IYUV_OVERLAY, screen);
   156         if (!overlay)
   157             fprintf(stderr, "YUV Overlay failed: %s\n", SDL_GetError());
   158     } // else
   159 
   160     initfailed = quit = (!screen || !overlay);
   161 
   162     memset(&spec, '\0', sizeof (SDL_AudioSpec));
   163     spec.freq = audio->freq;
   164     spec.format = AUDIO_S16SYS;
   165     spec.channels = audio->channels;
   166     spec.samples = 2048;
   167     spec.callback = audio_callback;
   168     initfailed = quit = (initfailed || (SDL_OpenAudio(&spec, NULL) != 0));
   169 
   170     while (audio)
   171     {
   172         queue_audio(audio);
   173         audio = THEORAPLAY_getAudio(decoder);
   174     } // while
   175 
   176     baseticks = SDL_GetTicks();
   177 
   178     if (!quit)
   179         SDL_PauseAudio(0);
   180 
   181     while (!quit && THEORAPLAY_isDecoding(decoder))
   182     {
   183         const Uint32 now = SDL_GetTicks() - baseticks;
   184 
   185         if (!video)
   186             video = THEORAPLAY_getVideo(decoder);
   187 
   188         // Play video frames when it's time.
   189         if (video && (video->playms <= now))
   190         {
   191             //printf("Play video frame (%u ms)!\n", video->playms);
   192             if ( framems && ((now - video->playms) >= framems) )
   193             {
   194                 // Skip frames to catch up, but keep track of the last one
   195                 //  in case we catch up to a series of dupe frames, which
   196                 //  means we'd have to draw that final frame and then wait for
   197                 //  more.
   198                 const THEORAPLAY_VideoFrame *last = video;
   199                 while ((video = THEORAPLAY_getVideo(decoder)) != NULL)
   200                 {
   201                     THEORAPLAY_freeVideo(last);
   202                     last = video;
   203                     if ((now - video->playms) < framems)
   204                         break;
   205                 } // while
   206 
   207                 if (!video)
   208                     video = last;
   209             } // if
   210 
   211             if (!video)  // do nothing; we're far behind and out of options.
   212             {
   213                 static int warned = 0;
   214                 if (!warned)
   215                 {
   216                     warned = 1;
   217                     fprintf(stderr, "WARNING: Playback can't keep up!\n");
   218                 } // if
   219             } // if
   220 
   221             else if (SDL_LockYUVOverlay(overlay) == -1)
   222             {
   223                 static int warned = 0;
   224                 if (!warned)
   225                 {
   226                     warned = 1;
   227                     fprintf(stderr, "Couldn't lock YUV overlay: %s\n", SDL_GetError());
   228                 } // if
   229             } // else if
   230 
   231             else
   232             {
   233                 SDL_Rect dstrect = { 0, 0, video->width, video->height };
   234                 const int w = video->width;
   235                 const int h = video->height;
   236                 const Uint8 *y = (const Uint8 *) video->pixels;
   237                 const Uint8 *u = y + (w * h);
   238                 const Uint8 *v = u + ((w/2) * (h/2));
   239                 Uint8 *dst;
   240                 int i;
   241 
   242                 dst = overlay->pixels[0];
   243                 for (i = 0; i < h; i++, y += w, dst += overlay->pitches[0])
   244                     memcpy(dst, y, w);
   245 
   246                 dst = overlay->pixels[1];
   247                 for (i = 0; i < h/2; i++, u += w/2, dst += overlay->pitches[1])
   248                     memcpy(dst, u, w/2);
   249 
   250                 dst = overlay->pixels[2];
   251                 for (i = 0; i < h/2; i++, v += w/2, dst += overlay->pitches[1])
   252                     memcpy(dst, v, w/2);
   253 
   254                 SDL_UnlockYUVOverlay(overlay);
   255 
   256                 if (SDL_DisplayYUVOverlay(overlay, &dstrect) != 0)
   257                 {
   258                     static int warned = 0;
   259                     if (!warned)
   260                     {
   261                         warned = 1;
   262                         fprintf(stderr, "Couldn't display YUV overlay: %s\n", SDL_GetError());
   263                     } // if
   264                 } // if
   265             } // else
   266 
   267             THEORAPLAY_freeVideo(video);
   268             video = NULL;
   269         } // if
   270         else  // no new video frame? Give up some CPU.
   271         {
   272             SDL_Delay(10);
   273         } // else
   274 
   275         while ((audio = THEORAPLAY_getAudio(decoder)) != NULL)
   276             queue_audio(audio);
   277 
   278         // Pump the event loop here.
   279         while (screen && SDL_PollEvent(&event))
   280         {
   281             switch (event.type)
   282             {
   283                 case SDL_VIDEOEXPOSE:
   284                     if (overlay)
   285                     {
   286                         SDL_Rect dstrect = { 0, 0, screen->w, screen->h };
   287                         SDL_DisplayYUVOverlay(overlay, &dstrect);
   288                     } // if
   289                     break;
   290 
   291                 case SDL_QUIT:
   292                     quit = 1;
   293                     break;
   294 
   295                 case SDL_KEYDOWN:
   296                     if (event.key.keysym.sym == SDLK_ESCAPE)
   297                         quit = 1;
   298                     break;
   299             } // switch
   300         } // while
   301     } // while
   302 
   303     // Drain out the audio queue.
   304     while (!quit)
   305     {
   306         SDL_LockAudio();
   307         quit = (audio_queue == NULL);
   308         SDL_UnlockAudio();
   309         if (!quit)
   310             SDL_Delay(100);  // wait for final audio packets to play out.
   311     } // while
   312 
   313     if (initfailed)
   314         printf("Initialization failed!\n");
   315     else if (THEORAPLAY_decodingError(decoder))
   316         printf("There was an error decoding this file!\n");
   317     else
   318         printf("done with this file!\n");
   319 
   320     if (overlay) SDL_FreeYUVOverlay(overlay);
   321     if (video) THEORAPLAY_freeVideo(video);
   322     if (audio) THEORAPLAY_freeAudio(audio);
   323     if (decoder) THEORAPLAY_stopDecode(decoder);
   324     SDL_CloseAudio();
   325     SDL_Quit();
   326 } // playfile
   327 
   328 int main(int argc, char **argv)
   329 {
   330     int i;
   331     for (i = 1; i < argc; i++)
   332         playfile(argv[i]);
   333 
   334     printf("done all files!\n");
   335 
   336     return 0;
   337 } // main
   338 
   339 // end of sdltheoraplay.c ...
   340