Fix a couple hard-coded array sizes
[openal-soft.git] / examples / alffplay.c
blobb61828f0b1660f9e033c4521249885c90fdd5431
1 /*
2 * alffplay.c
4 * A pedagogical video player that really works! Now with seeking features.
6 * Code based on FFplay, Copyright (c) 2003 Fabrice Bellard, and a tutorial by
7 * Martin Bohme <boehme@inb.uni-luebeckREMOVETHIS.de>.
9 * Requires C99.
12 #include <stdio.h>
13 #include <math.h>
15 #include <libavcodec/avcodec.h>
16 #include <libavformat/avformat.h>
17 #include <libavformat/avio.h>
18 #include <libavutil/time.h>
19 #include <libavutil/avstring.h>
20 #include <libavutil/channel_layout.h>
21 #include <libswscale/swscale.h>
22 #include <libswresample/swresample.h>
24 #include <SDL.h>
25 #include <SDL_thread.h>
26 #include <SDL_video.h>
28 #include "threads.h"
29 #include "bool.h"
31 #include "AL/al.h"
32 #include "AL/alc.h"
33 #include "AL/alext.h"
36 static bool has_direct_out = false;
37 static bool has_latency_check = false;
38 static LPALGETSOURCEDVSOFT alGetSourcedvSOFT;
40 #define AUDIO_BUFFER_TIME 100 /* In milliseconds, per-buffer */
41 #define AUDIO_BUFFER_QUEUE_SIZE 8 /* Number of buffers to queue */
42 #define MAX_AUDIOQ_SIZE (5 * 16 * 1024) /* Bytes of compressed audio data to keep queued */
43 #define MAX_VIDEOQ_SIZE (5 * 256 * 1024) /* Bytes of compressed video data to keep queued */
44 #define AV_SYNC_THRESHOLD 0.01
45 #define AV_NOSYNC_THRESHOLD 10.0
46 #define SAMPLE_CORRECTION_MAX_DIFF 0.1
47 #define AUDIO_DIFF_AVG_NB 20
48 #define VIDEO_PICTURE_QUEUE_SIZE 16
50 enum {
51 FF_UPDATE_EVENT = SDL_USEREVENT,
52 FF_REFRESH_EVENT,
53 FF_QUIT_EVENT
57 typedef struct PacketQueue {
58 AVPacketList *first_pkt, *last_pkt;
59 volatile int nb_packets;
60 volatile int size;
61 volatile bool flushing;
62 almtx_t mutex;
63 alcnd_t cond;
64 } PacketQueue;
66 typedef struct VideoPicture {
67 SDL_Texture *bmp;
68 int width, height; /* Logical image size (actual size may be larger) */
69 volatile bool updated;
70 double pts;
71 } VideoPicture;
73 typedef struct AudioState {
74 AVStream *st;
76 PacketQueue q;
77 AVPacket pkt;
79 /* Used for clock difference average computation */
80 double diff_accum;
81 double diff_avg_coef;
82 double diff_threshold;
84 /* Time (in seconds) of the next sample to be buffered */
85 double current_pts;
87 /* Decompressed sample frame, and swresample context for conversion */
88 AVFrame *decoded_aframe;
89 struct SwrContext *swres_ctx;
91 /* Conversion format, for what gets fed to OpenAL */
92 int dst_ch_layout;
93 enum AVSampleFormat dst_sample_fmt;
95 /* Storage of converted samples */
96 uint8_t *samples;
97 ssize_t samples_len; /* In samples */
98 ssize_t samples_pos;
99 int samples_max;
101 /* OpenAL format */
102 ALenum format;
103 ALint frame_size;
105 ALuint source;
106 ALuint buffer[AUDIO_BUFFER_QUEUE_SIZE];
107 ALuint buffer_idx;
108 almtx_t src_mutex;
110 althrd_t thread;
111 } AudioState;
113 typedef struct VideoState {
114 AVStream *st;
116 PacketQueue q;
118 double clock;
119 double frame_timer;
120 double frame_last_pts;
121 double frame_last_delay;
122 double current_pts;
123 /* time (av_gettime) at which we updated current_pts - used to have running video pts */
124 int64_t current_pts_time;
126 /* Decompressed video frame, and swscale context for conversion */
127 AVFrame *decoded_vframe;
128 struct SwsContext *swscale_ctx;
130 VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE];
131 int pictq_size, pictq_rindex, pictq_windex;
132 almtx_t pictq_mutex;
133 alcnd_t pictq_cond;
135 althrd_t thread;
136 } VideoState;
138 typedef struct MovieState {
139 AVFormatContext *pFormatCtx;
140 int videoStream, audioStream;
142 volatile bool seek_req;
143 int64_t seek_pos;
144 volatile bool direct_req;
146 int av_sync_type;
148 int64_t external_clock_base;
150 AudioState audio;
151 VideoState video;
153 althrd_t parse_thread;
155 char filename[1024];
157 volatile bool quit;
158 } MovieState;
160 enum {
161 AV_SYNC_AUDIO_MASTER,
162 AV_SYNC_VIDEO_MASTER,
163 AV_SYNC_EXTERNAL_MASTER,
165 DEFAULT_AV_SYNC_TYPE = AV_SYNC_EXTERNAL_MASTER
168 static AVPacket flush_pkt = { .data = (uint8_t*)"FLUSH" };
170 static void packet_queue_init(PacketQueue *q)
172 memset(q, 0, sizeof(PacketQueue));
173 almtx_init(&q->mutex, almtx_plain);
174 alcnd_init(&q->cond);
176 static int packet_queue_put(PacketQueue *q, AVPacket *pkt)
178 AVPacketList *pkt1;
179 if(pkt != &flush_pkt && !pkt->buf && av_dup_packet(pkt) < 0)
180 return -1;
182 pkt1 = av_malloc(sizeof(AVPacketList));
183 if(!pkt1) return -1;
184 pkt1->pkt = *pkt;
185 pkt1->next = NULL;
187 almtx_lock(&q->mutex);
188 if(!q->last_pkt)
189 q->first_pkt = pkt1;
190 else
191 q->last_pkt->next = pkt1;
192 q->last_pkt = pkt1;
193 q->nb_packets++;
194 q->size += pkt1->pkt.size;
195 almtx_unlock(&q->mutex);
197 alcnd_signal(&q->cond);
198 return 0;
200 static int packet_queue_get(PacketQueue *q, AVPacket *pkt, MovieState *state)
202 AVPacketList *pkt1;
203 int ret = -1;
205 almtx_lock(&q->mutex);
206 while(!state->quit)
208 pkt1 = q->first_pkt;
209 if(pkt1)
211 q->first_pkt = pkt1->next;
212 if(!q->first_pkt)
213 q->last_pkt = NULL;
214 q->nb_packets--;
215 q->size -= pkt1->pkt.size;
216 *pkt = pkt1->pkt;
217 av_free(pkt1);
218 ret = 1;
219 break;
222 if(q->flushing)
224 ret = 0;
225 break;
227 alcnd_wait(&q->cond, &q->mutex);
229 almtx_unlock(&q->mutex);
230 return ret;
232 static void packet_queue_clear(PacketQueue *q)
234 AVPacketList *pkt, *pkt1;
236 almtx_lock(&q->mutex);
237 for(pkt = q->first_pkt;pkt != NULL;pkt = pkt1)
239 pkt1 = pkt->next;
240 if(pkt->pkt.data != flush_pkt.data)
241 av_free_packet(&pkt->pkt);
242 av_freep(&pkt);
244 q->last_pkt = NULL;
245 q->first_pkt = NULL;
246 q->nb_packets = 0;
247 q->size = 0;
248 almtx_unlock(&q->mutex);
250 static void packet_queue_flush(PacketQueue *q)
252 almtx_lock(&q->mutex);
253 q->flushing = true;
254 almtx_unlock(&q->mutex);
255 alcnd_signal(&q->cond);
257 static void packet_queue_deinit(PacketQueue *q)
259 packet_queue_clear(q);
260 alcnd_destroy(&q->cond);
261 almtx_destroy(&q->mutex);
265 static double get_audio_clock(AudioState *state)
267 double pts;
269 almtx_lock(&state->src_mutex);
270 /* The audio clock is the timestamp of the sample currently being heard.
271 * It's based on 4 components:
272 * 1 - The timestamp of the next sample to buffer (state->current_pts)
273 * 2 - The length of the source's buffer queue (AL_SEC_LENGTH_SOFT)
274 * 3 - The offset OpenAL is currently at in the source (the first value
275 * from AL_SEC_OFFSET_LATENCY_SOFT)
276 * 4 - The latency between OpenAL and the DAC (the second value from
277 * AL_SEC_OFFSET_LATENCY_SOFT)
279 * Subtracting the length of the source queue from the next sample's
280 * timestamp gives the timestamp of the sample at start of the source
281 * queue. Adding the source offset to that results in the timestamp for
282 * OpenAL's current position, and subtracting the source latency from that
283 * gives the timestamp of the sample currently at the DAC.
285 pts = state->current_pts;
286 if(state->source)
288 ALdouble offset[2] = { 0.0, 0.0 };
289 ALdouble queue_len = 0.0;
290 ALint status;
292 /* NOTE: The source state must be checked last, in case an underrun
293 * occurs and the source stops between retrieving the offset+latency
294 * and getting the state. */
295 if(has_latency_check)
297 alGetSourcedvSOFT(state->source, AL_SEC_OFFSET_LATENCY_SOFT, offset);
298 alGetSourcedvSOFT(state->source, AL_SEC_LENGTH_SOFT, &queue_len);
300 else
302 ALint ioffset, ilen;
303 alGetSourcei(state->source, AL_SAMPLE_OFFSET, &ioffset);
304 alGetSourcei(state->source, AL_SAMPLE_LENGTH_SOFT, &ilen);
305 offset[0] = (double)ioffset / state->st->codec->sample_rate;
306 queue_len = (double)ilen / state->st->codec->sample_rate;
308 alGetSourcei(state->source, AL_SOURCE_STATE, &status);
310 /* If the source is AL_STOPPED, then there was an underrun and all
311 * buffers are processed, so ignore the source queue. The audio thread
312 * will put the source into an AL_INITIAL state and clear the queue
313 * when it starts recovery. */
314 if(status != AL_STOPPED)
315 pts = pts - queue_len + offset[0];
316 if(status == AL_PLAYING)
317 pts = pts - offset[1];
319 almtx_unlock(&state->src_mutex);
321 return (pts >= 0.0) ? pts : 0.0;
323 static double get_video_clock(VideoState *state)
325 double delta = (av_gettime() - state->current_pts_time) / 1000000.0;
326 return state->current_pts + delta;
328 static double get_external_clock(MovieState *movState)
330 return (av_gettime()-movState->external_clock_base) / 1000000.0;
333 double get_master_clock(MovieState *movState)
335 if(movState->av_sync_type == AV_SYNC_VIDEO_MASTER)
336 return get_video_clock(&movState->video);
337 if(movState->av_sync_type == AV_SYNC_AUDIO_MASTER)
338 return get_audio_clock(&movState->audio);
339 return get_external_clock(movState);
342 /* Return how many samples to skip to maintain sync (negative means to
343 * duplicate samples). */
344 static int synchronize_audio(MovieState *movState)
346 double diff, avg_diff;
347 double ref_clock;
349 if(movState->av_sync_type == AV_SYNC_AUDIO_MASTER)
350 return 0;
352 ref_clock = get_master_clock(movState);
353 diff = ref_clock - get_audio_clock(&movState->audio);
355 if(!(fabs(diff) < AV_NOSYNC_THRESHOLD))
357 /* Difference is TOO big; reset diff stuff */
358 movState->audio.diff_accum = 0.0;
359 return 0;
362 /* Accumulate the diffs */
363 movState->audio.diff_accum = movState->audio.diff_accum*movState->audio.diff_avg_coef + diff;
364 avg_diff = movState->audio.diff_accum*(1.0 - movState->audio.diff_avg_coef);
365 if(fabs(avg_diff) < movState->audio.diff_threshold)
366 return 0;
368 /* Constrain the per-update difference to avoid exceedingly large skips */
369 if(!(diff <= SAMPLE_CORRECTION_MAX_DIFF))
370 diff = SAMPLE_CORRECTION_MAX_DIFF;
371 else if(!(diff >= -SAMPLE_CORRECTION_MAX_DIFF))
372 diff = -SAMPLE_CORRECTION_MAX_DIFF;
373 return (int)(diff*movState->audio.st->codec->sample_rate);
376 static int audio_decode_frame(MovieState *movState)
378 AVPacket *pkt = &movState->audio.pkt;
380 while(!movState->quit)
382 while(!movState->quit && pkt->size == 0)
384 av_free_packet(pkt);
386 /* Get the next packet */
387 int err;
388 if((err=packet_queue_get(&movState->audio.q, pkt, movState)) <= 0)
390 if(err == 0)
391 break;
392 return err;
394 if(pkt->data == flush_pkt.data)
396 avcodec_flush_buffers(movState->audio.st->codec);
397 movState->audio.diff_accum = 0.0;
398 movState->audio.current_pts = av_q2d(movState->audio.st->time_base)*pkt->pts;
400 alSourceRewind(movState->audio.source);
401 alSourcei(movState->audio.source, AL_BUFFER, 0);
403 av_new_packet(pkt, 0);
405 return -1;
408 /* If provided, update w/ pts */
409 if(pkt->pts != AV_NOPTS_VALUE)
410 movState->audio.current_pts = av_q2d(movState->audio.st->time_base)*pkt->pts;
413 AVFrame *frame = movState->audio.decoded_aframe;
414 int got_frame = 0;
415 int len1 = avcodec_decode_audio4(movState->audio.st->codec, frame,
416 &got_frame, pkt);
417 if(len1 < 0)
419 av_shrink_packet(pkt, 0);
420 continue;
423 if(len1 <= pkt->size)
425 /* Move the unread data to the front and clear the end bits */
426 int remaining = pkt->size - len1;
427 memmove(pkt->data, &pkt->data[len1], remaining);
428 av_shrink_packet(pkt, remaining);
431 if(!got_frame || frame->nb_samples <= 0)
433 av_frame_unref(frame);
434 continue;
437 if(frame->nb_samples > movState->audio.samples_max)
439 av_freep(&movState->audio.samples);
440 av_samples_alloc(
441 &movState->audio.samples, NULL, movState->audio.st->codec->channels,
442 frame->nb_samples, movState->audio.dst_sample_fmt, 0
444 movState->audio.samples_max = frame->nb_samples;
446 /* Return the amount of sample frames converted */
447 int data_size = swr_convert(movState->audio.swres_ctx,
448 &movState->audio.samples, frame->nb_samples,
449 (const uint8_t**)frame->data, frame->nb_samples
452 av_frame_unref(frame);
453 return data_size;
456 return -1;
459 static int read_audio(MovieState *movState, uint8_t *samples, int length)
461 int sample_skip = synchronize_audio(movState);
462 int audio_size = 0;
464 /* Read the next chunk of data, refill the buffer, and queue it
465 * on the source */
466 length /= movState->audio.frame_size;
467 while(audio_size < length)
469 if(movState->audio.samples_len <= 0 || movState->audio.samples_pos >= movState->audio.samples_len)
471 int frame_len = audio_decode_frame(movState);
472 if(frame_len < 0) return -1;
474 movState->audio.samples_len = frame_len;
475 if(movState->audio.samples_len == 0)
476 break;
478 movState->audio.samples_pos = (movState->audio.samples_len < sample_skip) ?
479 movState->audio.samples_len : sample_skip;
480 sample_skip -= movState->audio.samples_pos;
482 movState->audio.current_pts += (double)movState->audio.samples_pos /
483 (double)movState->audio.st->codec->sample_rate;
484 continue;
487 int rem = length - audio_size;
488 if(movState->audio.samples_pos >= 0)
490 int n = movState->audio.frame_size;
491 int len = movState->audio.samples_len - movState->audio.samples_pos;
492 if(rem > len) rem = len;
493 memcpy(samples + audio_size*n,
494 movState->audio.samples + movState->audio.samples_pos*n,
495 rem*n);
497 else
499 int n = movState->audio.frame_size;
500 int len = -movState->audio.samples_pos;
501 if(rem > len) rem = len;
503 /* Add samples by copying the first sample */
504 if(n == 1)
506 uint8_t sample = ((uint8_t*)movState->audio.samples)[0];
507 uint8_t *q = (uint8_t*)samples + audio_size;
508 for(int i = 0;i < rem;i++)
509 *(q++) = sample;
511 else if(n == 2)
513 uint16_t sample = ((uint16_t*)movState->audio.samples)[0];
514 uint16_t *q = (uint16_t*)samples + audio_size;
515 for(int i = 0;i < rem;i++)
516 *(q++) = sample;
518 else if(n == 4)
520 uint32_t sample = ((uint32_t*)movState->audio.samples)[0];
521 uint32_t *q = (uint32_t*)samples + audio_size;
522 for(int i = 0;i < rem;i++)
523 *(q++) = sample;
525 else if(n == 8)
527 uint64_t sample = ((uint64_t*)movState->audio.samples)[0];
528 uint64_t *q = (uint64_t*)samples + audio_size;
529 for(int i = 0;i < rem;i++)
530 *(q++) = sample;
532 else
534 uint8_t *sample = movState->audio.samples;
535 uint8_t *q = samples + audio_size*n;
536 for(int i = 0;i < rem;i++)
538 memcpy(q, sample, n);
539 q += n;
544 movState->audio.samples_pos += rem;
545 movState->audio.current_pts += (double)rem / movState->audio.st->codec->sample_rate;
546 audio_size += rem;
549 return audio_size * movState->audio.frame_size;
552 static int audio_thread(void *userdata)
554 MovieState *movState = (MovieState*)userdata;
555 uint8_t *samples = NULL;
556 ALsizei buffer_len;
557 ALenum fmt;
559 alGenBuffers(AUDIO_BUFFER_QUEUE_SIZE, movState->audio.buffer);
560 alGenSources(1, &movState->audio.source);
562 alSourcei(movState->audio.source, AL_SOURCE_RELATIVE, AL_TRUE);
563 alSourcei(movState->audio.source, AL_ROLLOFF_FACTOR, 0);
565 av_new_packet(&movState->audio.pkt, 0);
567 /* Find a suitable format for OpenAL. */
568 movState->audio.format = AL_NONE;
569 if(movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_U8 ||
570 movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_U8P)
572 movState->audio.dst_sample_fmt = AV_SAMPLE_FMT_U8;
573 movState->audio.frame_size = 1;
574 if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_7POINT1 &&
575 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
576 (fmt=alGetEnumValue("AL_FORMAT_71CHN8")) != AL_NONE && fmt != -1)
578 movState->audio.dst_ch_layout = movState->audio.st->codec->channel_layout;
579 movState->audio.frame_size *= 8;
580 movState->audio.format = fmt;
582 if((movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_5POINT1 ||
583 movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) &&
584 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
585 (fmt=alGetEnumValue("AL_FORMAT_51CHN8")) != AL_NONE && fmt != -1)
587 movState->audio.dst_ch_layout = movState->audio.st->codec->channel_layout;
588 movState->audio.frame_size *= 6;
589 movState->audio.format = fmt;
591 if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_MONO)
593 movState->audio.dst_ch_layout = AV_CH_LAYOUT_MONO;
594 movState->audio.frame_size *= 1;
595 movState->audio.format = AL_FORMAT_MONO8;
597 if(movState->audio.format == AL_NONE)
599 movState->audio.dst_ch_layout = AV_CH_LAYOUT_STEREO;
600 movState->audio.frame_size *= 2;
601 movState->audio.format = AL_FORMAT_STEREO8;
604 if((movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_FLT ||
605 movState->audio.st->codec->sample_fmt == AV_SAMPLE_FMT_FLTP) &&
606 alIsExtensionPresent("AL_EXT_FLOAT32"))
608 movState->audio.dst_sample_fmt = AV_SAMPLE_FMT_FLT;
609 movState->audio.frame_size = 4;
610 if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_7POINT1 &&
611 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
612 (fmt=alGetEnumValue("AL_FORMAT_71CHN32")) != AL_NONE && fmt != -1)
614 movState->audio.dst_ch_layout = movState->audio.st->codec->channel_layout;
615 movState->audio.frame_size *= 8;
616 movState->audio.format = fmt;
618 if((movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_5POINT1 ||
619 movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) &&
620 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
621 (fmt=alGetEnumValue("AL_FORMAT_51CHN32")) != AL_NONE && fmt != -1)
623 movState->audio.dst_ch_layout = movState->audio.st->codec->channel_layout;
624 movState->audio.frame_size *= 6;
625 movState->audio.format = fmt;
627 if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_MONO)
629 movState->audio.dst_ch_layout = AV_CH_LAYOUT_MONO;
630 movState->audio.frame_size *= 1;
631 movState->audio.format = AL_FORMAT_MONO_FLOAT32;
633 if(movState->audio.format == AL_NONE)
635 movState->audio.dst_ch_layout = AV_CH_LAYOUT_STEREO;
636 movState->audio.frame_size *= 2;
637 movState->audio.format = AL_FORMAT_STEREO_FLOAT32;
640 if(movState->audio.format == AL_NONE)
642 movState->audio.dst_sample_fmt = AV_SAMPLE_FMT_S16;
643 movState->audio.frame_size = 2;
644 if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_7POINT1 &&
645 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
646 (fmt=alGetEnumValue("AL_FORMAT_71CHN16")) != AL_NONE && fmt != -1)
648 movState->audio.dst_ch_layout = movState->audio.st->codec->channel_layout;
649 movState->audio.frame_size *= 8;
650 movState->audio.format = fmt;
652 if((movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_5POINT1 ||
653 movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) &&
654 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
655 (fmt=alGetEnumValue("AL_FORMAT_51CHN16")) != AL_NONE && fmt != -1)
657 movState->audio.dst_ch_layout = movState->audio.st->codec->channel_layout;
658 movState->audio.frame_size *= 6;
659 movState->audio.format = fmt;
661 if(movState->audio.st->codec->channel_layout == AV_CH_LAYOUT_MONO)
663 movState->audio.dst_ch_layout = AV_CH_LAYOUT_MONO;
664 movState->audio.frame_size *= 1;
665 movState->audio.format = AL_FORMAT_MONO16;
667 if(movState->audio.format == AL_NONE)
669 movState->audio.dst_ch_layout = AV_CH_LAYOUT_STEREO;
670 movState->audio.frame_size *= 2;
671 movState->audio.format = AL_FORMAT_STEREO16;
674 buffer_len = AUDIO_BUFFER_TIME * movState->audio.st->codec->sample_rate / 1000 *
675 movState->audio.frame_size;
676 samples = av_malloc(buffer_len);
678 movState->audio.samples = NULL;
679 movState->audio.samples_max = 0;
680 movState->audio.samples_pos = 0;
681 movState->audio.samples_len = 0;
683 if(!(movState->audio.decoded_aframe=av_frame_alloc()))
685 fprintf(stderr, "Failed to allocate audio frame\n");
686 goto finish;
689 movState->audio.swres_ctx = swr_alloc_set_opts(NULL,
690 movState->audio.dst_ch_layout,
691 movState->audio.dst_sample_fmt,
692 movState->audio.st->codec->sample_rate,
693 movState->audio.st->codec->channel_layout ?
694 movState->audio.st->codec->channel_layout :
695 (uint64_t)av_get_default_channel_layout(movState->audio.st->codec->channels),
696 movState->audio.st->codec->sample_fmt,
697 movState->audio.st->codec->sample_rate,
698 0, NULL
700 if(!movState->audio.swres_ctx || swr_init(movState->audio.swres_ctx) != 0)
702 fprintf(stderr, "Failed to initialize audio converter\n");
703 goto finish;
706 almtx_lock(&movState->audio.src_mutex);
707 while(alGetError() == AL_NO_ERROR && !movState->quit)
709 /* First remove any processed buffers. */
710 ALint processed;
711 alGetSourcei(movState->audio.source, AL_BUFFERS_PROCESSED, &processed);
712 alSourceUnqueueBuffers(movState->audio.source, processed, (ALuint[AUDIO_BUFFER_QUEUE_SIZE]){});
714 /* Refill the buffer queue. */
715 ALint queued;
716 alGetSourcei(movState->audio.source, AL_BUFFERS_QUEUED, &queued);
717 while(queued < AUDIO_BUFFER_QUEUE_SIZE)
719 int audio_size;
721 /* Read the next chunk of data, fill the buffer, and queue it on
722 * the source */
723 audio_size = read_audio(movState, samples, buffer_len);
724 if(audio_size < 0) break;
726 ALuint bufid = movState->audio.buffer[movState->audio.buffer_idx++];
727 movState->audio.buffer_idx %= AUDIO_BUFFER_QUEUE_SIZE;
729 alBufferData(bufid, movState->audio.format, samples, audio_size,
730 movState->audio.st->codec->sample_rate);
731 alSourceQueueBuffers(movState->audio.source, 1, &bufid);
732 queued++;
735 /* Check that the source is playing. */
736 ALint state;
737 alGetSourcei(movState->audio.source, AL_SOURCE_STATE, &state);
738 if(state == AL_STOPPED)
740 /* AL_STOPPED means there was an underrun. Double-check that all
741 * processed buffers are removed, then rewind the source to get it
742 * back into an AL_INITIAL state. */
743 alGetSourcei(movState->audio.source, AL_BUFFERS_PROCESSED, &processed);
744 alSourceUnqueueBuffers(movState->audio.source, processed, (ALuint[AUDIO_BUFFER_QUEUE_SIZE]){});
745 alSourceRewind(movState->audio.source);
746 continue;
749 almtx_unlock(&movState->audio.src_mutex);
751 /* (re)start the source if needed, and wait for a buffer to finish */
752 if(state != AL_PLAYING && state != AL_PAUSED)
754 alGetSourcei(movState->audio.source, AL_BUFFERS_QUEUED, &queued);
755 if(queued > 0) alSourcePlay(movState->audio.source);
757 SDL_Delay(AUDIO_BUFFER_TIME);
759 if(movState->direct_req)
761 if(has_direct_out)
763 alGetSourcei(movState->audio.source, AL_DIRECT_CHANNELS_SOFT, &state);
764 state = !state;
765 alSourcei(movState->audio.source, AL_DIRECT_CHANNELS_SOFT,
766 state ? AL_TRUE : AL_FALSE);
767 printf("Direct channels %s\n", state ? "on" : "off");
768 fflush(stdout);
770 movState->direct_req = false;
773 almtx_lock(&movState->audio.src_mutex);
775 almtx_unlock(&movState->audio.src_mutex);
777 finish:
778 av_frame_free(&movState->audio.decoded_aframe);
779 swr_free(&movState->audio.swres_ctx);
781 av_freep(&samples);
782 av_freep(&movState->audio.samples);
784 alDeleteSources(1, &movState->audio.source);
785 alDeleteBuffers(AUDIO_BUFFER_QUEUE_SIZE, movState->audio.buffer);
787 return 0;
791 static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque)
793 (void)interval;
795 SDL_PushEvent(&(SDL_Event){ .user={.type=FF_REFRESH_EVENT, .data1=opaque} });
796 return 0; /* 0 means stop timer */
799 /* Schedule a video refresh in 'delay' ms */
800 static void schedule_refresh(MovieState *movState, int delay)
802 SDL_AddTimer(delay, sdl_refresh_timer_cb, movState);
805 static void video_display(MovieState *movState, SDL_Window *screen, SDL_Renderer *renderer)
807 VideoPicture *vp = &movState->video.pictq[movState->video.pictq_rindex];
809 if(!vp->bmp)
810 return;
812 float aspect_ratio;
813 int win_w, win_h;
814 int w, h, x, y;
816 if(movState->video.st->codec->sample_aspect_ratio.num == 0)
817 aspect_ratio = 0.0f;
818 else
820 aspect_ratio = av_q2d(movState->video.st->codec->sample_aspect_ratio) *
821 movState->video.st->codec->width /
822 movState->video.st->codec->height;
824 if(aspect_ratio <= 0.0f)
826 aspect_ratio = (float)movState->video.st->codec->width /
827 (float)movState->video.st->codec->height;
830 SDL_GetWindowSize(screen, &win_w, &win_h);
831 h = win_h;
832 w = ((int)rint(h * aspect_ratio) + 3) & ~3;
833 if(w > win_w)
835 w = win_w;
836 h = ((int)rint(w / aspect_ratio) + 3) & ~3;
838 x = (win_w - w) / 2;
839 y = (win_h - h) / 2;
841 SDL_RenderCopy(renderer, vp->bmp,
842 &(SDL_Rect){ .x=0, .y=0, .w=vp->width, .h=vp->height },
843 &(SDL_Rect){ .x=x, .y=y, .w=w, .h=h }
845 SDL_RenderPresent(renderer);
848 static void video_refresh_timer(MovieState *movState, SDL_Window *screen, SDL_Renderer *renderer)
850 if(!movState->video.st)
852 schedule_refresh(movState, 100);
853 return;
856 almtx_lock(&movState->video.pictq_mutex);
857 retry:
858 if(movState->video.pictq_size == 0)
859 schedule_refresh(movState, 1);
860 else
862 VideoPicture *vp = &movState->video.pictq[movState->video.pictq_rindex];
863 double actual_delay, delay, sync_threshold, ref_clock, diff;
865 movState->video.current_pts = vp->pts;
866 movState->video.current_pts_time = av_gettime();
868 delay = vp->pts - movState->video.frame_last_pts; /* the pts from last time */
869 if(delay <= 0 || delay >= 1.0)
871 /* if incorrect delay, use previous one */
872 delay = movState->video.frame_last_delay;
874 /* save for next time */
875 movState->video.frame_last_delay = delay;
876 movState->video.frame_last_pts = vp->pts;
878 /* Update delay to sync to clock if not master source. */
879 if(movState->av_sync_type != AV_SYNC_VIDEO_MASTER)
881 ref_clock = get_master_clock(movState);
882 diff = vp->pts - ref_clock;
884 /* Skip or repeat the frame. Take delay into account. */
885 sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD;
886 if(fabs(diff) < AV_NOSYNC_THRESHOLD)
888 if(diff <= -sync_threshold)
889 delay = 0;
890 else if(diff >= sync_threshold)
891 delay = 2 * delay;
895 movState->video.frame_timer += delay;
896 /* Compute the REAL delay. */
897 actual_delay = movState->video.frame_timer - (av_gettime() / 1000000.0);
898 if(!(actual_delay >= 0.010))
900 /* We don't have time to handle this picture, just skip to the next one. */
901 movState->video.pictq_rindex = (movState->video.pictq_rindex+1)%VIDEO_PICTURE_QUEUE_SIZE;
902 movState->video.pictq_size--;
903 alcnd_signal(&movState->video.pictq_cond);
904 goto retry;
906 schedule_refresh(movState, (int)(actual_delay*1000.0 + 0.5));
908 /* Show the picture! */
909 video_display(movState, screen, renderer);
911 /* Update queue for next picture. */
912 movState->video.pictq_rindex = (movState->video.pictq_rindex+1)%VIDEO_PICTURE_QUEUE_SIZE;
913 movState->video.pictq_size--;
914 alcnd_signal(&movState->video.pictq_cond);
916 almtx_unlock(&movState->video.pictq_mutex);
920 static void update_picture(MovieState *movState, bool *first_update, SDL_Window *screen, SDL_Renderer *renderer)
922 VideoPicture *vp = &movState->video.pictq[movState->video.pictq_windex];
924 /* allocate or resize the buffer! */
925 if(!vp->bmp || vp->width != movState->video.st->codec->width ||
926 vp->height != movState->video.st->codec->height)
928 if(vp->bmp)
929 SDL_DestroyTexture(vp->bmp);
930 vp->bmp = SDL_CreateTexture(
931 renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING,
932 movState->video.st->codec->coded_width, movState->video.st->codec->coded_height
934 if(!vp->bmp)
935 fprintf(stderr, "Failed to create YV12 texture!\n");
936 vp->width = movState->video.st->codec->width;
937 vp->height = movState->video.st->codec->height;
939 if(*first_update && vp->width > 0 && vp->height > 0)
941 /* For the first update, set the window size to the video size. */
942 *first_update = false;
944 int w = vp->width;
945 int h = vp->height;
946 if(movState->video.st->codec->sample_aspect_ratio.num != 0 &&
947 movState->video.st->codec->sample_aspect_ratio.den != 0)
949 double aspect_ratio = av_q2d(movState->video.st->codec->sample_aspect_ratio);
950 if(aspect_ratio >= 1.0)
951 w = (int)(w*aspect_ratio + 0.5);
952 else if(aspect_ratio > 0.0)
953 h = (int)(h/aspect_ratio + 0.5);
955 SDL_SetWindowSize(screen, w, h);
959 if(vp->bmp)
961 AVFrame *frame = movState->video.decoded_vframe;
962 void *pixels = NULL;
963 int pitch = 0;
965 if(movState->video.st->codec->pix_fmt == AV_PIX_FMT_YUV420P)
966 SDL_UpdateYUVTexture(vp->bmp, NULL,
967 frame->data[0], frame->linesize[0],
968 frame->data[1], frame->linesize[1],
969 frame->data[2], frame->linesize[2]
971 else if(SDL_LockTexture(vp->bmp, NULL, &pixels, &pitch) != 0)
972 fprintf(stderr, "Failed to lock texture\n");
973 else
975 // Convert the image into YUV format that SDL uses
976 int coded_w = movState->video.st->codec->coded_width;
977 int coded_h = movState->video.st->codec->coded_height;
978 int w = movState->video.st->codec->width;
979 int h = movState->video.st->codec->height;
980 if(!movState->video.swscale_ctx)
981 movState->video.swscale_ctx = sws_getContext(
982 w, h, movState->video.st->codec->pix_fmt,
983 w, h, AV_PIX_FMT_YUV420P, SWS_X, NULL, NULL, NULL
986 /* point pict at the queue */
987 AVPicture pict;
988 pict.data[0] = pixels;
989 pict.data[2] = pict.data[0] + coded_w*coded_h;
990 pict.data[1] = pict.data[2] + coded_w*coded_h/4;
992 pict.linesize[0] = pitch;
993 pict.linesize[2] = pitch / 2;
994 pict.linesize[1] = pitch / 2;
996 sws_scale(movState->video.swscale_ctx, (const uint8_t**)frame->data,
997 frame->linesize, 0, h, pict.data, pict.linesize);
998 SDL_UnlockTexture(vp->bmp);
1002 almtx_lock(&movState->video.pictq_mutex);
1003 vp->updated = true;
1004 almtx_unlock(&movState->video.pictq_mutex);
1005 alcnd_signal(&movState->video.pictq_cond);
1008 static int queue_picture(MovieState *movState, double pts)
1010 /* Wait until we have space for a new pic */
1011 almtx_lock(&movState->video.pictq_mutex);
1012 while(movState->video.pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !movState->quit)
1013 alcnd_wait(&movState->video.pictq_cond, &movState->video.pictq_mutex);
1014 almtx_unlock(&movState->video.pictq_mutex);
1016 if(movState->quit)
1017 return -1;
1019 VideoPicture *vp = &movState->video.pictq[movState->video.pictq_windex];
1021 /* We have to create/update the picture in the main thread */
1022 vp->updated = false;
1023 SDL_PushEvent(&(SDL_Event){ .user={.type=FF_UPDATE_EVENT, .data1=movState} });
1025 /* Wait until the picture is updated. */
1026 almtx_lock(&movState->video.pictq_mutex);
1027 while(!vp->updated && !movState->quit)
1028 alcnd_wait(&movState->video.pictq_cond, &movState->video.pictq_mutex);
1029 almtx_unlock(&movState->video.pictq_mutex);
1030 if(movState->quit)
1031 return -1;
1032 vp->pts = pts;
1034 movState->video.pictq_windex = (movState->video.pictq_windex+1)%VIDEO_PICTURE_QUEUE_SIZE;
1035 almtx_lock(&movState->video.pictq_mutex);
1036 movState->video.pictq_size++;
1037 almtx_unlock(&movState->video.pictq_mutex);
1039 return 0;
1042 static double synchronize_video(MovieState *movState, double pts)
1044 double frame_delay;
1046 if(pts == 0.0) /* if we aren't given a pts, set it to the clock */
1047 pts = movState->video.clock;
1048 else /* if we have pts, set video clock to it */
1049 movState->video.clock = pts;
1051 /* update the video clock */
1052 frame_delay = av_q2d(movState->video.st->codec->time_base);
1053 /* if we are repeating a frame, adjust clock accordingly */
1054 frame_delay += movState->video.decoded_vframe->repeat_pict * (frame_delay * 0.5);
1055 movState->video.clock += frame_delay;
1056 return pts;
1059 int video_thread(void *arg)
1061 MovieState *movState = (MovieState*)arg;
1062 AVPacket *packet = (AVPacket[1]){};
1063 int64_t saved_pts, pkt_pts;
1064 int frameFinished;
1066 movState->video.decoded_vframe = av_frame_alloc();
1067 while(packet_queue_get(&movState->video.q, packet, movState) >= 0)
1069 if(packet->data == flush_pkt.data)
1071 avcodec_flush_buffers(movState->video.st->codec);
1073 almtx_lock(&movState->video.pictq_mutex);
1074 movState->video.pictq_size = 0;
1075 movState->video.pictq_rindex = 0;
1076 movState->video.pictq_windex = 0;
1077 almtx_unlock(&movState->video.pictq_mutex);
1079 movState->video.clock = av_q2d(movState->video.st->time_base)*packet->pts;
1080 movState->video.current_pts = movState->video.clock;
1081 movState->video.current_pts_time = av_gettime();
1082 continue;
1085 pkt_pts = packet->pts;
1087 /* Decode video frame */
1088 avcodec_decode_video2(movState->video.st->codec, movState->video.decoded_vframe,
1089 &frameFinished, packet);
1090 if(pkt_pts != AV_NOPTS_VALUE && !movState->video.decoded_vframe->opaque)
1092 /* Store the packet's original pts in the frame, in case the frame
1093 * is not finished decoding yet. */
1094 saved_pts = pkt_pts;
1095 movState->video.decoded_vframe->opaque = &saved_pts;
1098 av_free_packet(packet);
1100 if(frameFinished)
1102 double pts = av_q2d(movState->video.st->time_base);
1103 if(packet->dts != AV_NOPTS_VALUE)
1104 pts *= packet->dts;
1105 else if(movState->video.decoded_vframe->opaque)
1106 pts *= *(int64_t*)movState->video.decoded_vframe->opaque;
1107 else
1108 pts *= 0.0;
1109 movState->video.decoded_vframe->opaque = NULL;
1111 pts = synchronize_video(movState, pts);
1112 if(queue_picture(movState, pts) < 0)
1113 break;
1117 sws_freeContext(movState->video.swscale_ctx);
1118 movState->video.swscale_ctx = NULL;
1119 av_frame_free(&movState->video.decoded_vframe);
1120 return 0;
1124 static int stream_component_open(MovieState *movState, int stream_index)
1126 AVFormatContext *pFormatCtx = movState->pFormatCtx;
1127 AVCodecContext *codecCtx;
1128 AVCodec *codec;
1130 if(stream_index < 0 || (unsigned int)stream_index >= pFormatCtx->nb_streams)
1131 return -1;
1133 /* Get a pointer to the codec context for the video stream, and open the
1134 * associated codec */
1135 codecCtx = pFormatCtx->streams[stream_index]->codec;
1137 codec = avcodec_find_decoder(codecCtx->codec_id);
1138 if(!codec || avcodec_open2(codecCtx, codec, NULL) < 0)
1140 fprintf(stderr, "Unsupported codec!\n");
1141 return -1;
1144 /* Initialize and start the media type handler */
1145 switch(codecCtx->codec_type)
1147 case AVMEDIA_TYPE_AUDIO:
1148 movState->audioStream = stream_index;
1149 movState->audio.st = pFormatCtx->streams[stream_index];
1151 /* Averaging filter for audio sync */
1152 movState->audio.diff_avg_coef = exp(log(0.01) / AUDIO_DIFF_AVG_NB);
1153 /* Correct audio only if larger error than this */
1154 movState->audio.diff_threshold = 2.0 * 0.050/* 50 ms */;
1156 memset(&movState->audio.pkt, 0, sizeof(movState->audio.pkt));
1157 if(althrd_create(&movState->audio.thread, audio_thread, movState) != althrd_success)
1159 movState->audioStream = -1;
1160 movState->audio.st = NULL;
1162 break;
1164 case AVMEDIA_TYPE_VIDEO:
1165 movState->videoStream = stream_index;
1166 movState->video.st = pFormatCtx->streams[stream_index];
1168 movState->video.current_pts_time = av_gettime();
1169 movState->video.frame_timer = (double)movState->video.current_pts_time /
1170 1000000.0;
1171 movState->video.frame_last_delay = 40e-3;
1173 if(althrd_create(&movState->video.thread, video_thread, movState) != althrd_success)
1175 movState->videoStream = -1;
1176 movState->video.st = NULL;
1178 break;
1180 default:
1181 break;
1184 return 0;
1187 static int decode_interrupt_cb(void *ctx)
1189 return ((MovieState*)ctx)->quit;
1192 int decode_thread(void *arg)
1194 MovieState *movState = (MovieState *)arg;
1195 AVFormatContext *fmtCtx = movState->pFormatCtx;
1196 AVPacket *packet = (AVPacket[1]){};
1197 int video_index = -1;
1198 int audio_index = -1;
1200 movState->videoStream = -1;
1201 movState->audioStream = -1;
1203 /* Dump information about file onto standard error */
1204 av_dump_format(fmtCtx, 0, movState->filename, 0);
1206 /* Find the first video and audio streams */
1207 for(unsigned int i = 0;i < fmtCtx->nb_streams;i++)
1209 if(fmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0)
1210 video_index = i;
1211 else if(fmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0)
1212 audio_index = i;
1214 movState->external_clock_base = av_gettime();
1215 if(audio_index >= 0)
1216 stream_component_open(movState, audio_index);
1217 if(video_index >= 0)
1218 stream_component_open(movState, video_index);
1220 if(movState->videoStream < 0 && movState->audioStream < 0)
1222 fprintf(stderr, "%s: could not open codecs\n", movState->filename);
1223 goto fail;
1226 /* Main packet handling loop */
1227 while(!movState->quit)
1229 if(movState->seek_req)
1231 int64_t seek_target = movState->seek_pos;
1232 int stream_index= -1;
1234 /* Prefer seeking on the video stream. */
1235 if(movState->videoStream >= 0)
1236 stream_index = movState->videoStream;
1237 else if(movState->audioStream >= 0)
1238 stream_index = movState->audioStream;
1240 /* Get a seek timestamp for the appropriate stream. */
1241 int64_t timestamp = seek_target;
1242 if(stream_index >= 0)
1243 timestamp = av_rescale_q(seek_target, AV_TIME_BASE_Q, fmtCtx->streams[stream_index]->time_base);
1245 if(av_seek_frame(movState->pFormatCtx, stream_index, timestamp, 0) < 0)
1246 fprintf(stderr, "%s: error while seeking\n", movState->pFormatCtx->filename);
1247 else
1249 /* Seek successful, clear the packet queues and send a special
1250 * 'flush' packet with the new stream clock time. */
1251 if(movState->audioStream >= 0)
1253 packet_queue_clear(&movState->audio.q);
1254 flush_pkt.pts = av_rescale_q(seek_target, AV_TIME_BASE_Q,
1255 fmtCtx->streams[movState->audioStream]->time_base
1257 packet_queue_put(&movState->audio.q, &flush_pkt);
1259 if(movState->videoStream >= 0)
1261 packet_queue_clear(&movState->video.q);
1262 flush_pkt.pts = av_rescale_q(seek_target, AV_TIME_BASE_Q,
1263 fmtCtx->streams[movState->videoStream]->time_base
1265 packet_queue_put(&movState->video.q, &flush_pkt);
1267 movState->external_clock_base = av_gettime() - seek_target;
1269 movState->seek_req = false;
1272 if(movState->audio.q.size >= MAX_AUDIOQ_SIZE ||
1273 movState->video.q.size >= MAX_VIDEOQ_SIZE)
1275 SDL_Delay(10);
1276 continue;
1279 if(av_read_frame(movState->pFormatCtx, packet) < 0)
1281 packet_queue_flush(&movState->video.q);
1282 packet_queue_flush(&movState->audio.q);
1283 break;
1286 /* Place the packet in the queue it's meant for, or discard it. */
1287 if(packet->stream_index == movState->videoStream)
1288 packet_queue_put(&movState->video.q, packet);
1289 else if(packet->stream_index == movState->audioStream)
1290 packet_queue_put(&movState->audio.q, packet);
1291 else
1292 av_free_packet(packet);
1295 /* all done - wait for it */
1296 while(!movState->quit)
1298 if(movState->audio.q.nb_packets == 0 && movState->video.q.nb_packets == 0)
1299 break;
1300 SDL_Delay(100);
1303 fail:
1304 movState->quit = true;
1305 packet_queue_flush(&movState->video.q);
1306 packet_queue_flush(&movState->audio.q);
1308 if(movState->videoStream >= 0)
1309 althrd_join(movState->video.thread, NULL);
1310 if(movState->audioStream >= 0)
1311 althrd_join(movState->audio.thread, NULL);
1313 SDL_PushEvent(&(SDL_Event){ .user={.type=FF_QUIT_EVENT, .data1=movState} });
1315 return 0;
1319 static void stream_seek(MovieState *movState, double incr)
1321 if(!movState->seek_req)
1323 double newtime = get_master_clock(movState)+incr;
1324 if(newtime <= 0.0) movState->seek_pos = 0;
1325 else movState->seek_pos = (int64_t)(newtime * AV_TIME_BASE);
1326 movState->seek_req = true;
1330 int main(int argc, char *argv[])
1332 SDL_Event event;
1333 MovieState *movState;
1334 bool first_update = true;
1335 SDL_Window *screen;
1336 SDL_Renderer *renderer;
1337 ALCdevice *device;
1338 ALCcontext *context;
1339 int fileidx;
1341 if(argc < 2)
1343 fprintf(stderr, "Usage: %s <file>\n", argv[0]);
1344 return 1;
1346 /* Register all formats and codecs */
1347 av_register_all();
1348 /* Initialize networking protocols */
1349 avformat_network_init();
1351 if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER))
1353 fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
1354 return 1;
1357 /* Make a window to put our video */
1358 screen = SDL_CreateWindow("alffplay", 0, 0, 640, 480, SDL_WINDOW_RESIZABLE);
1359 if(!screen)
1361 fprintf(stderr, "SDL: could not set video mode - exiting\n");
1362 return 1;
1364 /* Make a renderer to handle the texture image surface and rendering. */
1365 renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_ACCELERATED);
1366 if(renderer)
1368 SDL_RendererInfo rinf;
1369 bool ok = false;
1371 /* Make sure the renderer supports YV12 textures. If not, fallback to a
1372 * software renderer. */
1373 if(SDL_GetRendererInfo(renderer, &rinf) == 0)
1375 for(Uint32 i = 0;!ok && i < rinf.num_texture_formats;i++)
1376 ok = (rinf.texture_formats[i] == SDL_PIXELFORMAT_YV12);
1378 if(!ok)
1380 fprintf(stderr, "YV12 pixelformat textures not supported on renderer %s\n", rinf.name);
1381 SDL_DestroyRenderer(renderer);
1382 renderer = NULL;
1385 if(!renderer)
1386 renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_SOFTWARE);
1387 if(!renderer)
1389 fprintf(stderr, "SDL: could not create renderer - exiting\n");
1390 return 1;
1392 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
1393 SDL_RenderFillRect(renderer, NULL);
1394 SDL_RenderPresent(renderer);
1396 /* Open an audio device */
1397 fileidx = 1;
1398 device = NULL;
1399 if(argc > 3 && strcmp(argv[1], "-device") == 0)
1401 fileidx = 3;
1402 device = alcOpenDevice(argv[2]);
1403 if(!device)
1404 fprintf(stderr, "OpenAL: could not open \"%s\" - trying default\n", argv[2]);
1406 if(!device)
1407 device = alcOpenDevice(NULL);
1408 if(!device)
1410 fprintf(stderr, "OpenAL: could not open device - exiting\n");
1411 return 1;
1413 context = alcCreateContext(device, NULL);
1414 if(!context)
1416 fprintf(stderr, "OpenAL: could not create context - exiting\n");
1417 return 1;
1419 if(alcMakeContextCurrent(context) == ALC_FALSE)
1421 fprintf(stderr, "OpenAL: could not make context current - exiting\n");
1422 return 1;
1425 if(!alIsExtensionPresent("AL_SOFT_source_length"))
1427 fprintf(stderr, "Required AL_SOFT_source_length not supported - exiting\n");
1428 return 1;
1431 if(!alIsExtensionPresent("AL_SOFT_direct_channels"))
1432 fprintf(stderr, "AL_SOFT_direct_channels not supported.\n");
1433 else
1434 has_direct_out = true;
1436 if(!alIsExtensionPresent("AL_SOFT_source_latency"))
1437 fprintf(stderr, "AL_SOFT_source_latency not supported, audio may be a bit laggy.\n");
1438 else
1440 alGetSourcedvSOFT = alGetProcAddress("alGetSourcedvSOFT");
1441 has_latency_check = true;
1445 movState = av_mallocz(sizeof(MovieState));
1447 av_strlcpy(movState->filename, argv[fileidx], sizeof(movState->filename));
1449 packet_queue_init(&movState->audio.q);
1450 packet_queue_init(&movState->video.q);
1452 almtx_init(&movState->video.pictq_mutex, almtx_plain);
1453 alcnd_init(&movState->video.pictq_cond);
1454 almtx_init(&movState->audio.src_mutex, almtx_recursive);
1456 movState->av_sync_type = DEFAULT_AV_SYNC_TYPE;
1458 movState->pFormatCtx = avformat_alloc_context();
1459 movState->pFormatCtx->interrupt_callback = (AVIOInterruptCB){.callback=decode_interrupt_cb, .opaque=movState};
1461 if(avio_open2(&movState->pFormatCtx->pb, movState->filename, AVIO_FLAG_READ,
1462 &movState->pFormatCtx->interrupt_callback, NULL))
1464 fprintf(stderr, "Failed to open %s\n", movState->filename);
1465 return 1;
1468 /* Open movie file */
1469 if(avformat_open_input(&movState->pFormatCtx, movState->filename, NULL, NULL) != 0)
1471 fprintf(stderr, "Failed to open %s\n", movState->filename);
1472 return 1;
1475 /* Retrieve stream information */
1476 if(avformat_find_stream_info(movState->pFormatCtx, NULL) < 0)
1478 fprintf(stderr, "%s: failed to find stream info\n", movState->filename);
1479 return 1;
1482 schedule_refresh(movState, 40);
1485 if(althrd_create(&movState->parse_thread, decode_thread, movState) != althrd_success)
1487 fprintf(stderr, "Failed to create parse thread!\n");
1488 return 1;
1490 while(SDL_WaitEvent(&event) == 1)
1492 switch(event.type)
1494 case SDL_KEYDOWN:
1495 switch(event.key.keysym.sym)
1497 case SDLK_ESCAPE:
1498 movState->quit = true;
1499 break;
1501 case SDLK_LEFT:
1502 stream_seek(movState, -10.0);
1503 break;
1504 case SDLK_RIGHT:
1505 stream_seek(movState, 10.0);
1506 break;
1507 case SDLK_UP:
1508 stream_seek(movState, 30.0);
1509 break;
1510 case SDLK_DOWN:
1511 stream_seek(movState, -30.0);
1512 break;
1514 case SDLK_d:
1515 movState->direct_req = true;
1516 break;
1518 default:
1519 break;
1521 break;
1523 case SDL_WINDOWEVENT:
1524 switch(event.window.event)
1526 case SDL_WINDOWEVENT_RESIZED:
1527 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
1528 SDL_RenderFillRect(renderer, NULL);
1529 break;
1531 default:
1532 break;
1534 break;
1536 case SDL_QUIT:
1537 movState->quit = true;
1538 break;
1540 case FF_UPDATE_EVENT:
1541 update_picture(event.user.data1, &first_update, screen, renderer);
1542 break;
1544 case FF_REFRESH_EVENT:
1545 video_refresh_timer(event.user.data1, screen, renderer);
1546 break;
1548 case FF_QUIT_EVENT:
1549 althrd_join(movState->parse_thread, NULL);
1551 avformat_close_input(&movState->pFormatCtx);
1553 almtx_destroy(&movState->audio.src_mutex);
1554 almtx_destroy(&movState->video.pictq_mutex);
1555 alcnd_destroy(&movState->video.pictq_cond);
1556 packet_queue_deinit(&movState->video.q);
1557 packet_queue_deinit(&movState->audio.q);
1559 alcMakeContextCurrent(NULL);
1560 alcDestroyContext(context);
1561 alcCloseDevice(device);
1563 SDL_Quit();
1564 exit(0);
1566 default:
1567 break;
1571 fprintf(stderr, "SDL_WaitEvent error - %s\n", SDL_GetError());
1572 return 1;