Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
350 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,340 @@ | ||
/** | ||
* TheoraPlay; multithreaded Ogg Theora/Ogg Vorbis decoding. | ||
* | ||
* Please see the file LICENSE.txt in the source's root directory. | ||
* | ||
* This file written by Ryan C. Gordon. | ||
*/ | ||
|
||
/* | ||
* This is meant to be a dirt simple test case. If you want a big, robust | ||
* version that handles lots of strange variations, try sdltheoraplay.c | ||
*/ | ||
|
||
#include <stdio.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
#include <assert.h> | ||
|
||
#include "theoraplay.h" | ||
#include "SDL.h" | ||
|
||
static Uint32 baseticks = 0; | ||
|
||
typedef struct AudioQueue | ||
{ | ||
const THEORAPLAY_AudioPacket *audio; | ||
int offset; | ||
struct AudioQueue *next; | ||
} AudioQueue; | ||
|
||
static volatile AudioQueue *audio_queue = NULL; | ||
static volatile AudioQueue *audio_queue_tail = NULL; | ||
|
||
static void SDLCALL audio_callback(void *userdata, Uint8 *stream, int len) | ||
{ | ||
// !!! FIXME: this should refuse to play if item->playms is in the future. | ||
//const Uint32 now = SDL_GetTicks() - baseticks; | ||
Sint16 *dst = (Sint16 *) stream; | ||
|
||
while (audio_queue && (len > 0)) | ||
{ | ||
volatile AudioQueue *item = audio_queue; | ||
AudioQueue *next = item->next; | ||
const int channels = item->audio->channels; | ||
|
||
const float *src = item->audio->samples + (item->offset * channels); | ||
int cpy = (item->audio->frames - item->offset) * channels; | ||
int i; | ||
|
||
if (cpy > (len / sizeof (Sint16))) | ||
cpy = len / sizeof (Sint16); | ||
|
||
for (i = 0; i < cpy; i++) | ||
{ | ||
const float val = *(src++); | ||
if (val < -1.0f) | ||
*(dst++) = -32768; | ||
else if (val > 1.0f) | ||
*(dst++) = 32767; | ||
else | ||
*(dst++) = (Sint16) (val * 32767.0f); | ||
} // for | ||
|
||
item->offset += (cpy / channels); | ||
len -= cpy * sizeof (Sint16); | ||
|
||
if (item->offset >= item->audio->frames) | ||
{ | ||
THEORAPLAY_freeAudio(item->audio); | ||
free((void *) item); | ||
audio_queue = next; | ||
} // if | ||
} // while | ||
|
||
if (!audio_queue) | ||
audio_queue_tail = NULL; | ||
|
||
if (len > 0) | ||
memset(dst, '\0', len); | ||
} // audio_callback | ||
|
||
|
||
static void queue_audio(const THEORAPLAY_AudioPacket *audio) | ||
{ | ||
AudioQueue *item = (AudioQueue *) malloc(sizeof (AudioQueue)); | ||
if (!item) | ||
{ | ||
THEORAPLAY_freeAudio(audio); | ||
return; // oh well. | ||
} // if | ||
|
||
item->audio = audio; | ||
item->offset = 0; | ||
item->next = NULL; | ||
|
||
SDL_LockAudio(); | ||
if (audio_queue_tail) | ||
audio_queue_tail->next = item; | ||
else | ||
audio_queue = item; | ||
audio_queue_tail = item; | ||
SDL_UnlockAudio(); | ||
} // queue_audio | ||
|
||
|
||
static void playfile(const char *fname) | ||
{ | ||
THEORAPLAY_Decoder *decoder = NULL; | ||
const THEORAPLAY_VideoFrame *video = NULL; | ||
const THEORAPLAY_AudioPacket *audio = NULL; | ||
SDL_Surface *screen = NULL; | ||
SDL_Overlay *overlay = NULL; | ||
SDL_AudioSpec spec; | ||
SDL_Event event; | ||
Uint32 framems = 0; | ||
int initfailed = 0; | ||
int quit = 0; | ||
|
||
printf("Trying file '%s' ...\n", fname); | ||
|
||
decoder = THEORAPLAY_startDecodeFile(fname, 30, THEORAPLAY_VIDFMT_IYUV); | ||
if (!decoder) | ||
{ | ||
fprintf(stderr, "Failed to start decoding '%s'!\n", fname); | ||
return; | ||
} // if | ||
|
||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) == -1) | ||
{ | ||
fprintf(stderr, "SDL_Init() failed: %s\n", SDL_GetError()); | ||
return; | ||
} // if | ||
|
||
// wait until we have video and/or audio data, so we can set up hardware. | ||
while (!audio || !video) | ||
{ | ||
if (!audio) audio = THEORAPLAY_getAudio(decoder); | ||
if (!video) video = THEORAPLAY_getVideo(decoder); | ||
SDL_Delay(10); | ||
} // if | ||
|
||
SDL_WM_SetCaption(fname, "TheoraPlay"); | ||
|
||
framems = (video->fps == 0.0) ? 0 : ((Uint32) (1000.0 / video->fps)); | ||
screen = SDL_SetVideoMode(video->width, video->height, 0, 0); | ||
if (!screen) | ||
fprintf(stderr, "SDL_SetVideoMode() failed: %s\n", SDL_GetError()); | ||
else // software surface | ||
{ | ||
// blank out the screen to start. | ||
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0)); | ||
SDL_Flip(screen); | ||
|
||
overlay = SDL_CreateYUVOverlay(video->width, video->height, | ||
SDL_IYUV_OVERLAY, screen); | ||
if (!overlay) | ||
fprintf(stderr, "YUV Overlay failed: %s\n", SDL_GetError()); | ||
} // else | ||
|
||
initfailed = quit = (!screen || !overlay); | ||
|
||
memset(&spec, '\0', sizeof (SDL_AudioSpec)); | ||
spec.freq = audio->freq; | ||
spec.format = AUDIO_S16SYS; | ||
spec.channels = audio->channels; | ||
spec.samples = 2048; | ||
spec.callback = audio_callback; | ||
initfailed = quit = (initfailed || (SDL_OpenAudio(&spec, NULL) != 0)); | ||
|
||
while (audio) | ||
{ | ||
queue_audio(audio); | ||
audio = THEORAPLAY_getAudio(decoder); | ||
} // while | ||
|
||
baseticks = SDL_GetTicks(); | ||
|
||
if (!quit) | ||
SDL_PauseAudio(0); | ||
|
||
while (!quit && THEORAPLAY_isDecoding(decoder)) | ||
{ | ||
const Uint32 now = SDL_GetTicks() - baseticks; | ||
|
||
if (!video) | ||
video = THEORAPLAY_getVideo(decoder); | ||
|
||
// Play video frames when it's time. | ||
if (video && (video->playms <= now)) | ||
{ | ||
//printf("Play video frame (%u ms)!\n", video->playms); | ||
if ( framems && ((now - video->playms) >= framems) ) | ||
{ | ||
// Skip frames to catch up, but keep track of the last one | ||
// in case we catch up to a series of dupe frames, which | ||
// means we'd have to draw that final frame and then wait for | ||
// more. | ||
const THEORAPLAY_VideoFrame *last = video; | ||
while ((video = THEORAPLAY_getVideo(decoder)) != NULL) | ||
{ | ||
THEORAPLAY_freeVideo(last); | ||
last = video; | ||
if ((now - video->playms) < framems) | ||
break; | ||
} // while | ||
|
||
if (!video) | ||
video = last; | ||
} // if | ||
|
||
if (!video) // do nothing; we're far behind and out of options. | ||
{ | ||
static int warned = 0; | ||
if (warned) | ||
{ | ||
warned = 1; | ||
fprintf(stderr, "WARNING: Playback can't keep up!\n"); | ||
} // if | ||
} // if | ||
|
||
else if (SDL_LockYUVOverlay(overlay) == -1) | ||
{ | ||
static int warned = 0; | ||
if (!warned) | ||
{ | ||
warned = 1; | ||
fprintf(stderr, "Couldn't lock YUV overlay: %s\n", SDL_GetError()); | ||
} // if | ||
} // else if | ||
|
||
else | ||
{ | ||
SDL_Rect dstrect = { 0, 0, video->width, video->height }; | ||
const int w = video->width; | ||
const int h = video->height; | ||
const Uint8 *y = (const Uint8 *) video->pixels; | ||
const Uint8 *u = y + (w * h); | ||
const Uint8 *v = u + ((w/2) * (h/2)); | ||
Uint8 *dst; | ||
int i; | ||
|
||
dst = overlay->pixels[0]; | ||
for (i = 0; i < h; i++, y += w, dst += overlay->pitches[0]) | ||
memcpy(dst, y, w); | ||
|
||
dst = overlay->pixels[1]; | ||
for (i = 0; i < h/2; i++, u += w/2, dst += overlay->pitches[1]) | ||
memcpy(dst, u, w/2); | ||
|
||
dst = overlay->pixels[2]; | ||
for (i = 0; i < h/2; i++, v += w/2, dst += overlay->pitches[1]) | ||
memcpy(dst, v, w/2); | ||
|
||
SDL_UnlockYUVOverlay(overlay); | ||
|
||
if (SDL_DisplayYUVOverlay(overlay, &dstrect) != 0) | ||
{ | ||
static int warned = 0; | ||
if (!warned) | ||
{ | ||
warned = 1; | ||
fprintf(stderr, "Couldn't display YUV overlay: %s\n", SDL_GetError()); | ||
} // if | ||
} // if | ||
} // else | ||
|
||
THEORAPLAY_freeVideo(video); | ||
video = NULL; | ||
} // if | ||
else // no new video frame? Give up some CPU. | ||
{ | ||
SDL_Delay(10); | ||
} // else | ||
|
||
while ((audio = THEORAPLAY_getAudio(decoder)) != NULL) | ||
queue_audio(audio); | ||
|
||
// Pump the event loop here. | ||
while (screen && SDL_PollEvent(&event)) | ||
{ | ||
switch (event.type) | ||
{ | ||
case SDL_VIDEOEXPOSE: | ||
if (overlay) | ||
{ | ||
SDL_Rect dstrect = { 0, 0, screen->w, screen->h }; | ||
SDL_DisplayYUVOverlay(overlay, &dstrect); | ||
} // if | ||
break; | ||
|
||
case SDL_QUIT: | ||
quit = 1; | ||
break; | ||
|
||
case SDL_KEYDOWN: | ||
if (event.key.keysym.sym == SDLK_ESCAPE) | ||
quit = 1; | ||
break; | ||
} // switch | ||
} // while | ||
} // while | ||
|
||
// Drain out the audio queue. | ||
while (!quit) | ||
{ | ||
SDL_LockAudio(); | ||
quit = (audio_queue == NULL); | ||
SDL_UnlockAudio(); | ||
if (!quit) | ||
SDL_Delay(100); // wait for final audio packets to play out. | ||
} // while | ||
|
||
if (initfailed) | ||
printf("Initialization failed!\n"); | ||
else if (THEORAPLAY_decodingError(decoder)) | ||
printf("There was an error decoding this file!\n"); | ||
else | ||
printf("done with this file!\n"); | ||
|
||
if (overlay) SDL_FreeYUVOverlay(overlay); | ||
if (video) THEORAPLAY_freeVideo(video); | ||
if (audio) THEORAPLAY_freeAudio(audio); | ||
if (decoder) THEORAPLAY_stopDecode(decoder); | ||
SDL_CloseAudio(); | ||
SDL_Quit(); | ||
} // playfile | ||
|
||
int main(int argc, char **argv) | ||
{ | ||
int i; | ||
for (i = 1; i < argc; i++) | ||
playfile(argv[i]); | ||
|
||
printf("done all files!\n"); | ||
|
||
return 0; | ||
} // main | ||
|
||
// end of sdltheoraplay.c ... | ||
|