test/simplesdl.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 26 Aug 2016 01:38:09 -0400
changeset 61 fb533bb8633e
parent 60 c6149b747a9e
permissions -rw-r--r--
Fixed incorrect video frame timestamps in various circumstances.

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