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>.
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>
25 #include <SDL_thread.h>
26 #include <SDL_video.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
51 FF_UPDATE_EVENT
= SDL_USEREVENT
,
57 typedef struct PacketQueue
{
58 AVPacketList
*first_pkt
, *last_pkt
;
59 volatile int nb_packets
;
61 volatile bool flushing
;
66 typedef struct VideoPicture
{
68 int width
, height
; /* Logical image size (actual size may be larger) */
69 volatile bool updated
;
73 typedef struct AudioState
{
79 /* Used for clock difference average computation */
82 double diff_threshold
;
84 /* Time (in seconds) of the next sample to be buffered */
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 */
93 enum AVSampleFormat dst_sample_fmt
;
95 /* Storage of converted samples */
97 ssize_t samples_len
; /* In samples */
106 ALuint buffer
[AUDIO_BUFFER_QUEUE_SIZE
];
113 typedef struct VideoState
{
120 double frame_last_pts
;
121 double frame_last_delay
;
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
;
138 typedef struct MovieState
{
139 AVFormatContext
*pFormatCtx
;
140 int videoStream
, audioStream
;
142 volatile bool seek_req
;
144 volatile bool direct_req
;
148 int64_t external_clock_base
;
153 althrd_t parse_thread
;
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
)
179 if(pkt
!= &flush_pkt
&& !pkt
->buf
&& av_dup_packet(pkt
) < 0)
182 pkt1
= av_malloc(sizeof(AVPacketList
));
187 almtx_lock(&q
->mutex
);
191 q
->last_pkt
->next
= pkt1
;
194 q
->size
+= pkt1
->pkt
.size
;
195 almtx_unlock(&q
->mutex
);
197 alcnd_signal(&q
->cond
);
200 static int packet_queue_get(PacketQueue
*q
, AVPacket
*pkt
, MovieState
*state
)
205 almtx_lock(&q
->mutex
);
211 q
->first_pkt
= pkt1
->next
;
215 q
->size
-= pkt1
->pkt
.size
;
227 alcnd_wait(&q
->cond
, &q
->mutex
);
229 almtx_unlock(&q
->mutex
);
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
)
240 if(pkt
->pkt
.data
!= flush_pkt
.data
)
241 av_free_packet(&pkt
->pkt
);
248 almtx_unlock(&q
->mutex
);
250 static void packet_queue_flush(PacketQueue
*q
)
252 almtx_lock(&q
->mutex
);
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
)
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
;
288 ALdouble offset
[2] = { 0.0, 0.0 };
289 ALdouble queue_len
= 0.0;
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
);
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
;
349 if(movState
->av_sync_type
== AV_SYNC_AUDIO_MASTER
)
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;
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
)
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)
386 /* Get the next packet */
388 if((err
=packet_queue_get(&movState
->audio
.q
, pkt
, movState
)) <= 0)
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);
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
;
415 int len1
= avcodec_decode_audio4(movState
->audio
.st
->codec
, frame
,
419 av_shrink_packet(pkt
, 0);
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
);
437 if(frame
->nb_samples
> movState
->audio
.samples_max
)
439 av_freep(&movState
->audio
.samples
);
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
);
459 static int read_audio(MovieState
*movState
, uint8_t *samples
, int length
)
461 int sample_skip
= synchronize_audio(movState
);
464 /* Read the next chunk of data, refill the buffer, and queue it
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)
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
;
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
,
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 */
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
++)
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
++)
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
++)
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
++)
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
);
544 movState
->audio
.samples_pos
+= rem
;
545 movState
->audio
.current_pts
+= (double)rem
/ movState
->audio
.st
->codec
->sample_rate
;
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
;
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");
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
,
700 if(!movState
->audio
.swres_ctx
|| swr_init(movState
->audio
.swres_ctx
) != 0)
702 fprintf(stderr
, "Failed to initialize audio converter\n");
706 almtx_lock(&movState
->audio
.src_mutex
);
707 while(alGetError() == AL_NO_ERROR
&& !movState
->quit
)
709 /* First remove any processed buffers. */
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. */
716 alGetSourcei(movState
->audio
.source
, AL_BUFFERS_QUEUED
, &queued
);
717 while(queued
< AUDIO_BUFFER_QUEUE_SIZE
)
721 /* Read the next chunk of data, fill the buffer, and queue it on
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
);
735 /* Check that the source is playing. */
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
);
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
)
763 alGetSourcei(movState
->audio
.source
, AL_DIRECT_CHANNELS_SOFT
, &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");
770 movState
->direct_req
= false;
773 almtx_lock(&movState
->audio
.src_mutex
);
775 almtx_unlock(&movState
->audio
.src_mutex
);
778 av_frame_free(&movState
->audio
.decoded_aframe
);
779 swr_free(&movState
->audio
.swres_ctx
);
782 av_freep(&movState
->audio
.samples
);
784 alDeleteSources(1, &movState
->audio
.source
);
785 alDeleteBuffers(AUDIO_BUFFER_QUEUE_SIZE
, movState
->audio
.buffer
);
791 static Uint32
sdl_refresh_timer_cb(Uint32 interval
, void *opaque
)
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
];
816 if(movState
->video
.st
->codec
->sample_aspect_ratio
.num
== 0)
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
);
832 w
= ((int)rint(h
* aspect_ratio
) + 3) & ~3;
836 h
= ((int)rint(w
/ aspect_ratio
) + 3) & ~3;
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);
856 almtx_lock(&movState
->video
.pictq_mutex
);
858 if(movState
->video
.pictq_size
== 0)
859 schedule_refresh(movState
, 1);
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
)
890 else if(diff
>= sync_threshold
)
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
);
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
)
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
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;
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
);
961 AVFrame
*frame
= movState
->video
.decoded_vframe
;
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");
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 */
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
);
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
);
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
);
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
);
1042 static double synchronize_video(MovieState
*movState
, double pts
)
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
;
1059 int video_thread(void *arg
)
1061 MovieState
*movState
= (MovieState
*)arg
;
1062 AVPacket
*packet
= (AVPacket
[1]){};
1063 int64_t saved_pts
, pkt_pts
;
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();
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
);
1102 double pts
= av_q2d(movState
->video
.st
->time_base
);
1103 if(packet
->dts
!= AV_NOPTS_VALUE
)
1105 else if(movState
->video
.decoded_vframe
->opaque
)
1106 pts
*= *(int64_t*)movState
->video
.decoded_vframe
->opaque
;
1109 movState
->video
.decoded_vframe
->opaque
= NULL
;
1111 pts
= synchronize_video(movState
, pts
);
1112 if(queue_picture(movState
, pts
) < 0)
1117 sws_freeContext(movState
->video
.swscale_ctx
);
1118 movState
->video
.swscale_ctx
= NULL
;
1119 av_frame_free(&movState
->video
.decoded_vframe
);
1124 static int stream_component_open(MovieState
*movState
, int stream_index
)
1126 AVFormatContext
*pFormatCtx
= movState
->pFormatCtx
;
1127 AVCodecContext
*codecCtx
;
1130 if(stream_index
< 0 || (unsigned int)stream_index
>= pFormatCtx
->nb_streams
)
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");
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
;
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
/
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
;
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)
1211 else if(fmtCtx
->streams
[i
]->codec
->codec_type
== AVMEDIA_TYPE_AUDIO
&& audio_index
< 0)
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
);
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
);
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
)
1279 if(av_read_frame(movState
->pFormatCtx
, packet
) < 0)
1281 packet_queue_flush(&movState
->video
.q
);
1282 packet_queue_flush(&movState
->audio
.q
);
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
);
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)
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
} });
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
[])
1333 MovieState
*movState
;
1334 bool first_update
= true;
1336 SDL_Renderer
*renderer
;
1338 ALCcontext
*context
;
1343 fprintf(stderr
, "Usage: %s <file>\n", argv
[0]);
1346 /* Register all formats and codecs */
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());
1357 /* Make a window to put our video */
1358 screen
= SDL_CreateWindow("alffplay", 0, 0, 640, 480, SDL_WINDOW_RESIZABLE
);
1361 fprintf(stderr
, "SDL: could not set video mode - exiting\n");
1364 /* Make a renderer to handle the texture image surface and rendering. */
1365 renderer
= SDL_CreateRenderer(screen
, -1, SDL_RENDERER_ACCELERATED
);
1368 SDL_RendererInfo rinf
;
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
);
1380 fprintf(stderr
, "YV12 pixelformat textures not supported on renderer %s\n", rinf
.name
);
1381 SDL_DestroyRenderer(renderer
);
1386 renderer
= SDL_CreateRenderer(screen
, -1, SDL_RENDERER_SOFTWARE
);
1389 fprintf(stderr
, "SDL: could not create renderer - exiting\n");
1392 SDL_SetRenderDrawColor(renderer
, 0, 0, 0, 255);
1393 SDL_RenderFillRect(renderer
, NULL
);
1394 SDL_RenderPresent(renderer
);
1396 /* Open an audio device */
1399 if(argc
> 3 && strcmp(argv
[1], "-device") == 0)
1402 device
= alcOpenDevice(argv
[2]);
1404 fprintf(stderr
, "OpenAL: could not open \"%s\" - trying default\n", argv
[2]);
1407 device
= alcOpenDevice(NULL
);
1410 fprintf(stderr
, "OpenAL: could not open device - exiting\n");
1413 context
= alcCreateContext(device
, NULL
);
1416 fprintf(stderr
, "OpenAL: could not create context - exiting\n");
1419 if(alcMakeContextCurrent(context
) == ALC_FALSE
)
1421 fprintf(stderr
, "OpenAL: could not make context current - exiting\n");
1425 if(!alIsExtensionPresent("AL_SOFT_source_length"))
1427 fprintf(stderr
, "Required AL_SOFT_source_length not supported - exiting\n");
1431 if(!alIsExtensionPresent("AL_SOFT_direct_channels"))
1432 fprintf(stderr
, "AL_SOFT_direct_channels not supported.\n");
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");
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
);
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
);
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
);
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");
1490 while(SDL_WaitEvent(&event
) == 1)
1495 switch(event
.key
.keysym
.sym
)
1498 movState
->quit
= true;
1502 stream_seek(movState
, -10.0);
1505 stream_seek(movState
, 10.0);
1508 stream_seek(movState
, 30.0);
1511 stream_seek(movState
, -30.0);
1515 movState
->direct_req
= true;
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
);
1537 movState
->quit
= true;
1540 case FF_UPDATE_EVENT
:
1541 update_picture(event
.user
.data1
, &first_update
, screen
, renderer
);
1544 case FF_REFRESH_EVENT
:
1545 video_refresh_timer(event
.user
.data1
, screen
, renderer
);
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
);
1571 fprintf(stderr
, "SDL_WaitEvent error - %s\n", SDL_GetError());