Remove alffplay's duplicate ALC_SOFT_device_clock declarations
[openal-soft.git] / examples / alffplay.cpp
blob65719807347fbaa104fb1d9aba8b493a6a678e57
1 /*
2 * An example showing how to play a stream sync'd to video, using ffmpeg.
4 * Requires C++11.
5 */
7 #include <condition_variable>
8 #include <functional>
9 #include <algorithm>
10 #include <iostream>
11 #include <iomanip>
12 #include <cstring>
13 #include <limits>
14 #include <thread>
15 #include <chrono>
16 #include <atomic>
17 #include <vector>
18 #include <mutex>
19 #include <deque>
20 #include <array>
22 extern "C" {
23 #include "libavcodec/avcodec.h"
24 #include "libavformat/avformat.h"
25 #include "libavformat/avio.h"
26 #include "libavutil/time.h"
27 #include "libavutil/pixfmt.h"
28 #include "libavutil/avstring.h"
29 #include "libavutil/channel_layout.h"
30 #include "libswscale/swscale.h"
31 #include "libswresample/swresample.h"
34 #include "SDL.h"
36 #include "AL/alc.h"
37 #include "AL/al.h"
38 #include "AL/alext.h"
40 namespace {
42 using nanoseconds = std::chrono::nanoseconds;
43 using microseconds = std::chrono::microseconds;
44 using milliseconds = std::chrono::milliseconds;
45 using seconds = std::chrono::seconds;
46 using seconds_d64 = std::chrono::duration<double>;
48 const std::string AppName("alffplay");
50 bool EnableDirectOut = false;
51 LPALGETSOURCEI64VSOFT alGetSourcei64vSOFT;
52 LPALCGETINTEGER64VSOFT alcGetInteger64vSOFT;
54 const seconds AVNoSyncThreshold(10);
56 const milliseconds VideoSyncThreshold(10);
57 #define VIDEO_PICTURE_QUEUE_SIZE 16
59 const seconds_d64 AudioSyncThreshold(0.03);
60 const milliseconds AudioSampleCorrectionMax(50);
61 /* Averaging filter coefficient for audio sync. */
62 #define AUDIO_DIFF_AVG_NB 20
63 const double AudioAvgFilterCoeff = std::pow(0.01, 1.0/AUDIO_DIFF_AVG_NB);
64 /* Per-buffer size, in time */
65 const milliseconds AudioBufferTime(20);
66 /* Buffer total size, in time (should be divisible by the buffer time) */
67 const milliseconds AudioBufferTotalTime(800);
69 #define MAX_QUEUE_SIZE (15 * 1024 * 1024) /* Bytes of compressed data to keep queued */
71 enum {
72 FF_UPDATE_EVENT = SDL_USEREVENT,
73 FF_REFRESH_EVENT,
74 FF_MOVIE_DONE_EVENT
77 enum class SyncMaster {
78 Audio,
79 Video,
80 External,
82 Default = External
86 inline microseconds get_avtime()
87 { return microseconds(av_gettime()); }
89 /* Define unique_ptrs to auto-cleanup associated ffmpeg objects. */
90 struct AVIOContextDeleter {
91 void operator()(AVIOContext *ptr) { avio_closep(&ptr); }
93 using AVIOContextPtr = std::unique_ptr<AVIOContext,AVIOContextDeleter>;
95 struct AVFormatCtxDeleter {
96 void operator()(AVFormatContext *ptr) { avformat_close_input(&ptr); }
98 using AVFormatCtxPtr = std::unique_ptr<AVFormatContext,AVFormatCtxDeleter>;
100 struct AVCodecCtxDeleter {
101 void operator()(AVCodecContext *ptr) { avcodec_free_context(&ptr); }
103 using AVCodecCtxPtr = std::unique_ptr<AVCodecContext,AVCodecCtxDeleter>;
105 struct AVFrameDeleter {
106 void operator()(AVFrame *ptr) { av_frame_free(&ptr); }
108 using AVFramePtr = std::unique_ptr<AVFrame,AVFrameDeleter>;
110 struct SwrContextDeleter {
111 void operator()(SwrContext *ptr) { swr_free(&ptr); }
113 using SwrContextPtr = std::unique_ptr<SwrContext,SwrContextDeleter>;
115 struct SwsContextDeleter {
116 void operator()(SwsContext *ptr) { sws_freeContext(ptr); }
118 using SwsContextPtr = std::unique_ptr<SwsContext,SwsContextDeleter>;
121 class PacketQueue {
122 std::deque<AVPacket> mPackets;
123 size_t mTotalSize{0};
125 public:
126 ~PacketQueue() { clear(); }
128 bool empty() const noexcept { return mPackets.empty(); }
129 size_t totalSize() const noexcept { return mTotalSize; }
131 void put(const AVPacket *pkt)
133 mPackets.push_back(AVPacket{});
134 if(av_packet_ref(&mPackets.back(), pkt) != 0)
135 mPackets.pop_back();
136 else
137 mTotalSize += mPackets.back().size;
140 AVPacket *front() noexcept
141 { return &mPackets.front(); }
143 void pop()
145 AVPacket *pkt = &mPackets.front();
146 mTotalSize -= pkt->size;
147 av_packet_unref(pkt);
148 mPackets.pop_front();
151 void clear()
153 for(AVPacket &pkt : mPackets)
154 av_packet_unref(&pkt);
155 mPackets.clear();
156 mTotalSize = 0;
161 struct MovieState;
163 struct AudioState {
164 MovieState &mMovie;
166 AVStream *mStream{nullptr};
167 AVCodecCtxPtr mCodecCtx;
169 std::mutex mQueueMtx;
170 std::condition_variable mQueueCond;
172 /* Used for clock difference average computation */
173 seconds_d64 mClockDiffAvg{0};
175 /* Time of the next sample to be buffered */
176 nanoseconds mCurrentPts{0};
178 /* Device clock time that the stream started at. */
179 nanoseconds mDeviceStartTime{nanoseconds::min()};
181 /* Decompressed sample frame, and swresample context for conversion */
182 AVFramePtr mDecodedFrame;
183 SwrContextPtr mSwresCtx;
185 /* Conversion format, for what gets fed to OpenAL */
186 int mDstChanLayout{0};
187 AVSampleFormat mDstSampleFmt{AV_SAMPLE_FMT_NONE};
189 /* Storage of converted samples */
190 uint8_t *mSamples{nullptr};
191 int mSamplesLen{0}; /* In samples */
192 int mSamplesPos{0};
193 int mSamplesMax{0};
195 /* OpenAL format */
196 ALenum mFormat{AL_NONE};
197 ALsizei mFrameSize{0};
199 std::mutex mSrcMutex;
200 ALuint mSource{0};
201 std::vector<ALuint> mBuffers;
202 ALsizei mBufferIdx{0};
204 AudioState(MovieState &movie) : mMovie(movie)
206 ~AudioState()
208 if(mSource)
209 alDeleteSources(1, &mSource);
210 if(!mBuffers.empty())
211 alDeleteBuffers(mBuffers.size(), mBuffers.data());
213 av_freep(&mSamples);
216 nanoseconds getClockNoLock();
217 nanoseconds getClock()
219 std::lock_guard<std::mutex> lock(mSrcMutex);
220 return getClockNoLock();
223 bool isBufferFilled();
224 void startPlayback();
226 int getSync();
227 int decodeFrame();
228 int readAudio(uint8_t *samples, int length);
230 int handler();
233 struct VideoState {
234 MovieState &mMovie;
236 AVStream *mStream{nullptr};
237 AVCodecCtxPtr mCodecCtx;
239 std::mutex mQueueMtx;
240 std::condition_variable mQueueCond;
242 nanoseconds mClock{0};
243 nanoseconds mFrameTimer{0};
244 nanoseconds mFrameLastPts{0};
245 nanoseconds mFrameLastDelay{0};
246 nanoseconds mCurrentPts{0};
247 /* time (av_gettime) at which we updated mCurrentPts - used to have running video pts */
248 microseconds mCurrentPtsTime{0};
250 /* Decompressed video frame, and swscale context for conversion */
251 AVFramePtr mDecodedFrame;
252 SwsContextPtr mSwscaleCtx;
254 struct Picture {
255 SDL_Texture *mImage{nullptr};
256 int mWidth{0}, mHeight{0}; /* Logical image size (actual size may be larger) */
257 std::atomic<bool> mUpdated{false};
258 nanoseconds mPts{0};
260 ~Picture()
262 if(mImage)
263 SDL_DestroyTexture(mImage);
264 mImage = nullptr;
267 std::array<Picture,VIDEO_PICTURE_QUEUE_SIZE> mPictQ;
268 size_t mPictQSize{0}, mPictQRead{0}, mPictQWrite{0};
269 std::mutex mPictQMutex;
270 std::condition_variable mPictQCond;
271 bool mFirstUpdate{true};
272 std::atomic<bool> mEOS{false};
273 std::atomic<bool> mFinalUpdate{false};
275 VideoState(MovieState &movie) : mMovie(movie) { }
277 nanoseconds getClock();
278 bool isBufferFilled();
280 static Uint32 SDLCALL sdl_refresh_timer_cb(Uint32 interval, void *opaque);
281 void schedRefresh(milliseconds delay);
282 void display(SDL_Window *screen, SDL_Renderer *renderer);
283 void refreshTimer(SDL_Window *screen, SDL_Renderer *renderer);
284 void updatePicture(SDL_Window *screen, SDL_Renderer *renderer);
285 int queuePicture(nanoseconds pts);
286 int handler();
289 struct MovieState {
290 AVIOContextPtr mIOContext;
291 AVFormatCtxPtr mFormatCtx;
293 SyncMaster mAVSyncType{SyncMaster::Default};
295 microseconds mClockBase{0};
296 std::atomic<bool> mPlaying{false};
298 std::mutex mSendMtx;
299 std::condition_variable mSendCond;
300 /* NOTE: false/clear = need data, true/set = no data needed */
301 std::atomic_flag mSendDataGood;
303 std::atomic<bool> mQuit{false};
305 AudioState mAudio;
306 VideoState mVideo;
308 std::thread mParseThread;
309 std::thread mAudioThread;
310 std::thread mVideoThread;
312 std::string mFilename;
314 MovieState(std::string fname)
315 : mAudio(*this), mVideo(*this), mFilename(std::move(fname))
317 ~MovieState()
319 mQuit = true;
320 if(mParseThread.joinable())
321 mParseThread.join();
324 static int decode_interrupt_cb(void *ctx);
325 bool prepare();
326 void setTitle(SDL_Window *window);
328 nanoseconds getClock();
330 nanoseconds getMasterClock();
332 nanoseconds getDuration();
334 int streamComponentOpen(int stream_index);
335 int parse_handler();
339 nanoseconds AudioState::getClockNoLock()
341 // The audio clock is the timestamp of the sample currently being heard.
342 if(alcGetInteger64vSOFT)
344 // If device start time = min, we aren't playing yet.
345 if(mDeviceStartTime == nanoseconds::min())
346 return nanoseconds::zero();
348 // Get the current device clock time and latency.
349 auto device = alcGetContextsDevice(alcGetCurrentContext());
350 ALCint64SOFT devtimes[2] = {0,0};
351 alcGetInteger64vSOFT(device, ALC_DEVICE_CLOCK_LATENCY_SOFT, 2, devtimes);
352 auto latency = nanoseconds(devtimes[1]);
353 auto device_time = nanoseconds(devtimes[0]);
355 // The clock is simply the current device time relative to the recorded
356 // start time. We can also subtract the latency to get more a accurate
357 // position of where the audio device actually is in the output stream.
358 return device_time - mDeviceStartTime - latency;
361 /* The source-based clock is based on 4 components:
362 * 1 - The timestamp of the next sample to buffer (mCurrentPts)
363 * 2 - The length of the source's buffer queue
364 * (AudioBufferTime*AL_BUFFERS_QUEUED)
365 * 3 - The offset OpenAL is currently at in the source (the first value
366 * from AL_SAMPLE_OFFSET_LATENCY_SOFT)
367 * 4 - The latency between OpenAL and the DAC (the second value from
368 * AL_SAMPLE_OFFSET_LATENCY_SOFT)
370 * Subtracting the length of the source queue from the next sample's
371 * timestamp gives the timestamp of the sample at the start of the source
372 * queue. Adding the source offset to that results in the timestamp for the
373 * sample at OpenAL's current position, and subtracting the source latency
374 * from that gives the timestamp of the sample currently at the DAC.
376 nanoseconds pts = mCurrentPts;
377 if(mSource)
379 ALint64SOFT offset[2];
380 ALint queued;
381 ALint status;
383 /* NOTE: The source state must be checked last, in case an underrun
384 * occurs and the source stops between retrieving the offset+latency
385 * and getting the state. */
386 if(alGetSourcei64vSOFT)
387 alGetSourcei64vSOFT(mSource, AL_SAMPLE_OFFSET_LATENCY_SOFT, offset);
388 else
390 ALint ioffset;
391 alGetSourcei(mSource, AL_SAMPLE_OFFSET, &ioffset);
392 offset[0] = (ALint64SOFT)ioffset << 32;
393 offset[1] = 0;
395 alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued);
396 alGetSourcei(mSource, AL_SOURCE_STATE, &status);
398 /* If the source is AL_STOPPED, then there was an underrun and all
399 * buffers are processed, so ignore the source queue. The audio thread
400 * will put the source into an AL_INITIAL state and clear the queue
401 * when it starts recovery. */
402 if(status != AL_STOPPED)
404 using fixed32 = std::chrono::duration<int64_t,std::ratio<1,(1ll<<32)>>;
406 pts -= AudioBufferTime*queued;
407 pts += std::chrono::duration_cast<nanoseconds>(
408 fixed32(offset[0] / mCodecCtx->sample_rate)
411 /* Don't offset by the latency if the source isn't playing. */
412 if(status == AL_PLAYING)
413 pts -= nanoseconds(offset[1]);
416 return std::max(pts, nanoseconds::zero());
419 bool AudioState::isBufferFilled()
421 /* All of OpenAL's buffer queueing happens under the mSrcMutex lock, as
422 * does the source gen. So when we're able to grab the lock and the source
423 * is valid, the queue must be full.
425 std::lock_guard<std::mutex> lock(mSrcMutex);
426 return mSource != 0;
429 void AudioState::startPlayback()
431 alSourcePlay(mSource);
432 if(alcGetInteger64vSOFT)
434 using fixed32 = std::chrono::duration<int64_t,std::ratio<1,(1ll<<32)>>;
436 // Subtract the total buffer queue time from the current pts to get the
437 // pts of the start of the queue.
438 nanoseconds startpts = mCurrentPts - AudioBufferTotalTime;
439 int64_t srctimes[2]={0,0};
440 alGetSourcei64vSOFT(mSource, AL_SAMPLE_OFFSET_CLOCK_SOFT, srctimes);
441 auto device_time = nanoseconds(srctimes[1]);
442 auto src_offset = std::chrono::duration_cast<nanoseconds>(fixed32(srctimes[0])) /
443 mCodecCtx->sample_rate;
445 // The mixer may have ticked and incremented the device time and sample
446 // offset, so subtract the source offset from the device time to get
447 // the device time the source started at. Also subtract startpts to get
448 // the device time the stream would have started at to reach where it
449 // is now.
450 mDeviceStartTime = device_time - src_offset - startpts;
454 int AudioState::getSync()
456 if(mMovie.mAVSyncType == SyncMaster::Audio)
457 return 0;
459 auto ref_clock = mMovie.getMasterClock();
460 auto diff = ref_clock - getClockNoLock();
462 if(!(diff < AVNoSyncThreshold && diff > -AVNoSyncThreshold))
464 /* Difference is TOO big; reset accumulated average */
465 mClockDiffAvg = seconds_d64::zero();
466 return 0;
469 /* Accumulate the diffs */
470 mClockDiffAvg = mClockDiffAvg*AudioAvgFilterCoeff + diff;
471 auto avg_diff = mClockDiffAvg*(1.0 - AudioAvgFilterCoeff);
472 if(avg_diff < AudioSyncThreshold/2.0 && avg_diff > -AudioSyncThreshold)
473 return 0;
475 /* Constrain the per-update difference to avoid exceedingly large skips */
476 diff = std::min<nanoseconds>(std::max<nanoseconds>(diff, -AudioSampleCorrectionMax),
477 AudioSampleCorrectionMax);
478 return (int)std::chrono::duration_cast<seconds>(diff*mCodecCtx->sample_rate).count();
481 int AudioState::decodeFrame()
483 while(!mMovie.mQuit.load(std::memory_order_relaxed))
485 std::unique_lock<std::mutex> lock(mQueueMtx);
486 int ret = avcodec_receive_frame(mCodecCtx.get(), mDecodedFrame.get());
487 if(ret == AVERROR(EAGAIN))
489 mMovie.mSendDataGood.clear(std::memory_order_relaxed);
490 std::unique_lock<std::mutex>(mMovie.mSendMtx).unlock();
491 mMovie.mSendCond.notify_one();
492 do {
493 mQueueCond.wait(lock);
494 ret = avcodec_receive_frame(mCodecCtx.get(), mDecodedFrame.get());
495 } while(ret == AVERROR(EAGAIN));
497 lock.unlock();
498 if(ret == AVERROR_EOF) break;
499 mMovie.mSendDataGood.clear(std::memory_order_relaxed);
500 mMovie.mSendCond.notify_one();
501 if(ret < 0)
503 std::cerr<< "Failed to decode frame: "<<ret <<std::endl;
504 return 0;
507 if(mDecodedFrame->nb_samples <= 0)
509 av_frame_unref(mDecodedFrame.get());
510 continue;
513 /* If provided, update w/ pts */
514 if(mDecodedFrame->best_effort_timestamp != AV_NOPTS_VALUE)
515 mCurrentPts = std::chrono::duration_cast<nanoseconds>(
516 seconds_d64(av_q2d(mStream->time_base)*mDecodedFrame->best_effort_timestamp)
519 if(mDecodedFrame->nb_samples > mSamplesMax)
521 av_freep(&mSamples);
522 av_samples_alloc(
523 &mSamples, nullptr, mCodecCtx->channels,
524 mDecodedFrame->nb_samples, mDstSampleFmt, 0
526 mSamplesMax = mDecodedFrame->nb_samples;
528 /* Return the amount of sample frames converted */
529 int data_size = swr_convert(mSwresCtx.get(), &mSamples, mDecodedFrame->nb_samples,
530 (const uint8_t**)mDecodedFrame->data, mDecodedFrame->nb_samples
533 av_frame_unref(mDecodedFrame.get());
534 return data_size;
537 return 0;
540 /* Duplicates the sample at in to out, count times. The frame size is a
541 * multiple of the template type size.
543 template<typename T>
544 static void sample_dup(uint8_t *out, const uint8_t *in, int count, int frame_size)
546 const T *sample = reinterpret_cast<const T*>(in);
547 T *dst = reinterpret_cast<T*>(out);
548 if(frame_size == sizeof(T))
549 std::fill_n(dst, count, *sample);
550 else
552 /* NOTE: frame_size is a multiple of sizeof(T). */
553 int type_mult = frame_size / sizeof(T);
554 int i = 0;
555 std::generate_n(dst, count*type_mult,
556 [sample,type_mult,&i]() -> T
558 T ret = sample[i];
559 i = (i+1)%type_mult;
560 return ret;
567 int AudioState::readAudio(uint8_t *samples, int length)
569 int sample_skip = getSync();
570 int audio_size = 0;
572 /* Read the next chunk of data, refill the buffer, and queue it
573 * on the source */
574 length /= mFrameSize;
575 while(audio_size < length)
577 if(mSamplesLen <= 0 || mSamplesPos >= mSamplesLen)
579 int frame_len = decodeFrame();
580 if(frame_len <= 0) break;
582 mSamplesLen = frame_len;
583 mSamplesPos = std::min(mSamplesLen, sample_skip);
584 sample_skip -= mSamplesPos;
586 // Adjust the device start time and current pts by the amount we're
587 // skipping/duplicating, so that the clock remains correct for the
588 // current stream position.
589 auto skip = nanoseconds(seconds(mSamplesPos)) / mCodecCtx->sample_rate;
590 mDeviceStartTime -= skip;
591 mCurrentPts += skip;
592 continue;
595 int rem = length - audio_size;
596 if(mSamplesPos >= 0)
598 int len = mSamplesLen - mSamplesPos;
599 if(rem > len) rem = len;
600 memcpy(samples, mSamples + mSamplesPos*mFrameSize, rem*mFrameSize);
602 else
604 rem = std::min(rem, -mSamplesPos);
606 /* Add samples by copying the first sample */
607 if((mFrameSize&7) == 0)
608 sample_dup<uint64_t>(samples, mSamples, rem, mFrameSize);
609 else if((mFrameSize&3) == 0)
610 sample_dup<uint32_t>(samples, mSamples, rem, mFrameSize);
611 else if((mFrameSize&1) == 0)
612 sample_dup<uint16_t>(samples, mSamples, rem, mFrameSize);
613 else
614 sample_dup<uint8_t>(samples, mSamples, rem, mFrameSize);
617 mSamplesPos += rem;
618 mCurrentPts += nanoseconds(seconds(rem)) / mCodecCtx->sample_rate;
619 samples += rem*mFrameSize;
620 audio_size += rem;
623 if(audio_size < length && audio_size > 0)
625 int rem = length - audio_size;
626 std::fill_n(samples, rem*mFrameSize,
627 (mDstSampleFmt == AV_SAMPLE_FMT_U8) ? 0x80 : 0x00);
628 mCurrentPts += nanoseconds(seconds(rem)) / mCodecCtx->sample_rate;
629 audio_size += rem;
632 return audio_size * mFrameSize;
636 int AudioState::handler()
638 std::unique_lock<std::mutex> lock(mSrcMutex);
639 ALenum fmt;
641 /* Find a suitable format for OpenAL. */
642 mDstChanLayout = 0;
643 if(mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8 || mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8P)
645 mDstSampleFmt = AV_SAMPLE_FMT_U8;
646 mFrameSize = 1;
647 if(mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1 &&
648 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
649 (fmt=alGetEnumValue("AL_FORMAT_71CHN8")) != AL_NONE && fmt != -1)
651 mDstChanLayout = mCodecCtx->channel_layout;
652 mFrameSize *= 8;
653 mFormat = fmt;
655 if((mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1 ||
656 mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) &&
657 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
658 (fmt=alGetEnumValue("AL_FORMAT_51CHN8")) != AL_NONE && fmt != -1)
660 mDstChanLayout = mCodecCtx->channel_layout;
661 mFrameSize *= 6;
662 mFormat = fmt;
664 if(mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO)
666 mDstChanLayout = mCodecCtx->channel_layout;
667 mFrameSize *= 1;
668 mFormat = AL_FORMAT_MONO8;
670 if(!mDstChanLayout)
672 mDstChanLayout = AV_CH_LAYOUT_STEREO;
673 mFrameSize *= 2;
674 mFormat = AL_FORMAT_STEREO8;
677 if((mCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLT || mCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLTP) &&
678 alIsExtensionPresent("AL_EXT_FLOAT32"))
680 mDstSampleFmt = AV_SAMPLE_FMT_FLT;
681 mFrameSize = 4;
682 if(mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1 &&
683 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
684 (fmt=alGetEnumValue("AL_FORMAT_71CHN32")) != AL_NONE && fmt != -1)
686 mDstChanLayout = mCodecCtx->channel_layout;
687 mFrameSize *= 8;
688 mFormat = fmt;
690 if((mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1 ||
691 mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) &&
692 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
693 (fmt=alGetEnumValue("AL_FORMAT_51CHN32")) != AL_NONE && fmt != -1)
695 mDstChanLayout = mCodecCtx->channel_layout;
696 mFrameSize *= 6;
697 mFormat = fmt;
699 if(mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO)
701 mDstChanLayout = mCodecCtx->channel_layout;
702 mFrameSize *= 1;
703 mFormat = AL_FORMAT_MONO_FLOAT32;
705 if(!mDstChanLayout)
707 mDstChanLayout = AV_CH_LAYOUT_STEREO;
708 mFrameSize *= 2;
709 mFormat = AL_FORMAT_STEREO_FLOAT32;
712 if(!mDstChanLayout)
714 mDstSampleFmt = AV_SAMPLE_FMT_S16;
715 mFrameSize = 2;
716 if(mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1 &&
717 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
718 (fmt=alGetEnumValue("AL_FORMAT_71CHN16")) != AL_NONE && fmt != -1)
720 mDstChanLayout = mCodecCtx->channel_layout;
721 mFrameSize *= 8;
722 mFormat = fmt;
724 if((mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1 ||
725 mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) &&
726 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
727 (fmt=alGetEnumValue("AL_FORMAT_51CHN16")) != AL_NONE && fmt != -1)
729 mDstChanLayout = mCodecCtx->channel_layout;
730 mFrameSize *= 6;
731 mFormat = fmt;
733 if(mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO)
735 mDstChanLayout = mCodecCtx->channel_layout;
736 mFrameSize *= 1;
737 mFormat = AL_FORMAT_MONO16;
739 if(!mDstChanLayout)
741 mDstChanLayout = AV_CH_LAYOUT_STEREO;
742 mFrameSize *= 2;
743 mFormat = AL_FORMAT_STEREO16;
746 ALsizei buffer_len = std::chrono::duration_cast<std::chrono::duration<int>>(
747 mCodecCtx->sample_rate * AudioBufferTime).count() * mFrameSize;
748 void *samples = av_malloc(buffer_len);
750 mSamples = NULL;
751 mSamplesMax = 0;
752 mSamplesPos = 0;
753 mSamplesLen = 0;
755 mDecodedFrame.reset(av_frame_alloc());
756 if(!mDecodedFrame)
758 std::cerr<< "Failed to allocate audio frame" <<std::endl;
759 goto finish;
762 mSwresCtx.reset(swr_alloc_set_opts(nullptr,
763 mDstChanLayout, mDstSampleFmt, mCodecCtx->sample_rate,
764 mCodecCtx->channel_layout ? mCodecCtx->channel_layout :
765 (uint64_t)av_get_default_channel_layout(mCodecCtx->channels),
766 mCodecCtx->sample_fmt, mCodecCtx->sample_rate,
767 0, nullptr
769 if(!mSwresCtx || swr_init(mSwresCtx.get()) != 0)
771 std::cerr<< "Failed to initialize audio converter" <<std::endl;
772 goto finish;
775 mBuffers.assign(AudioBufferTotalTime / AudioBufferTime, 0);
776 alGenBuffers(mBuffers.size(), mBuffers.data());
777 alGenSources(1, &mSource);
779 if(EnableDirectOut)
780 alSourcei(mSource, AL_DIRECT_CHANNELS_SOFT, AL_TRUE);
782 while(alGetError() == AL_NO_ERROR && !mMovie.mQuit.load(std::memory_order_relaxed))
784 /* First remove any processed buffers. */
785 ALint processed;
786 alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);
787 while(processed > 0)
789 std::array<ALuint,4> bids;
790 alSourceUnqueueBuffers(mSource, std::min<ALsizei>(bids.size(), processed),
791 bids.data());
792 processed -= std::min<ALsizei>(bids.size(), processed);
795 /* Refill the buffer queue. */
796 ALint queued;
797 alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued);
798 while((ALuint)queued < mBuffers.size())
800 int audio_size;
802 /* Read the next chunk of data, fill the buffer, and queue it on
803 * the source */
804 audio_size = readAudio(reinterpret_cast<uint8_t*>(samples), buffer_len);
805 if(audio_size <= 0) break;
807 ALuint bufid = mBuffers[mBufferIdx++];
808 mBufferIdx %= mBuffers.size();
810 alBufferData(bufid, mFormat, samples, audio_size, mCodecCtx->sample_rate);
811 alSourceQueueBuffers(mSource, 1, &bufid);
812 queued++;
814 if(queued == 0)
815 break;
817 /* Check that the source is playing. */
818 ALint state;
819 alGetSourcei(mSource, AL_SOURCE_STATE, &state);
820 if(state == AL_STOPPED)
822 /* AL_STOPPED means there was an underrun. Clear the buffer queue
823 * since this likely means we're late, and rewind the source to get
824 * it back into an AL_INITIAL state.
826 alSourceRewind(mSource);
827 alSourcei(mSource, AL_BUFFER, 0);
828 continue;
831 /* (re)start the source if needed, and wait for a buffer to finish */
832 if(state != AL_PLAYING && state != AL_PAUSED &&
833 mMovie.mPlaying.load(std::memory_order_relaxed))
834 startPlayback();
836 lock.unlock();
837 SDL_Delay((AudioBufferTime/3).count());
838 lock.lock();
841 alSourceRewind(mSource);
842 alSourcei(mSource, AL_BUFFER, 0);
844 finish:
845 av_freep(&samples);
847 return 0;
851 nanoseconds VideoState::getClock()
853 /* NOTE: This returns incorrect times while not playing. */
854 auto delta = get_avtime() - mCurrentPtsTime;
855 return mCurrentPts + delta;
858 bool VideoState::isBufferFilled()
860 std::unique_lock<std::mutex> lock(mPictQMutex);
861 return mPictQSize >= mPictQ.size();
864 Uint32 SDLCALL VideoState::sdl_refresh_timer_cb(Uint32 /*interval*/, void *opaque)
866 SDL_Event evt{};
867 evt.user.type = FF_REFRESH_EVENT;
868 evt.user.data1 = opaque;
869 SDL_PushEvent(&evt);
870 return 0; /* 0 means stop timer */
873 /* Schedules an FF_REFRESH_EVENT event to occur in 'delay' ms. */
874 void VideoState::schedRefresh(milliseconds delay)
876 SDL_AddTimer(delay.count(), sdl_refresh_timer_cb, this);
879 /* Called by VideoState::refreshTimer to display the next video frame. */
880 void VideoState::display(SDL_Window *screen, SDL_Renderer *renderer)
882 Picture *vp = &mPictQ[mPictQRead];
884 if(!vp->mImage)
885 return;
887 float aspect_ratio;
888 int win_w, win_h;
889 int w, h, x, y;
891 if(mCodecCtx->sample_aspect_ratio.num == 0)
892 aspect_ratio = 0.0f;
893 else
895 aspect_ratio = av_q2d(mCodecCtx->sample_aspect_ratio) * mCodecCtx->width /
896 mCodecCtx->height;
898 if(aspect_ratio <= 0.0f)
899 aspect_ratio = (float)mCodecCtx->width / (float)mCodecCtx->height;
901 SDL_GetWindowSize(screen, &win_w, &win_h);
902 h = win_h;
903 w = ((int)rint(h * aspect_ratio) + 3) & ~3;
904 if(w > win_w)
906 w = win_w;
907 h = ((int)rint(w / aspect_ratio) + 3) & ~3;
909 x = (win_w - w) / 2;
910 y = (win_h - h) / 2;
912 SDL_Rect src_rect{ 0, 0, vp->mWidth, vp->mHeight };
913 SDL_Rect dst_rect{ x, y, w, h };
914 SDL_RenderCopy(renderer, vp->mImage, &src_rect, &dst_rect);
915 SDL_RenderPresent(renderer);
918 /* FF_REFRESH_EVENT handler called on the main thread where the SDL_Renderer
919 * was created. It handles the display of the next decoded video frame (if not
920 * falling behind), and sets up the timer for the following video frame.
922 void VideoState::refreshTimer(SDL_Window *screen, SDL_Renderer *renderer)
924 if(!mStream)
926 if(mEOS)
928 mFinalUpdate = true;
929 std::unique_lock<std::mutex>(mPictQMutex).unlock();
930 mPictQCond.notify_all();
931 return;
933 schedRefresh(milliseconds(100));
934 return;
936 if(!mMovie.mPlaying.load(std::memory_order_relaxed))
938 schedRefresh(milliseconds(1));
939 return;
942 std::unique_lock<std::mutex> lock(mPictQMutex);
943 retry:
944 if(mPictQSize == 0)
946 if(mEOS)
947 mFinalUpdate = true;
948 else
949 schedRefresh(milliseconds(1));
950 lock.unlock();
951 mPictQCond.notify_all();
952 return;
955 Picture *vp = &mPictQ[mPictQRead];
956 mCurrentPts = vp->mPts;
957 mCurrentPtsTime = get_avtime();
959 /* Get delay using the frame pts and the pts from last frame. */
960 auto delay = vp->mPts - mFrameLastPts;
961 if(delay <= seconds::zero() || delay >= seconds(1))
963 /* If incorrect delay, use previous one. */
964 delay = mFrameLastDelay;
966 /* Save for next frame. */
967 mFrameLastDelay = delay;
968 mFrameLastPts = vp->mPts;
970 /* Update delay to sync to clock if not master source. */
971 if(mMovie.mAVSyncType != SyncMaster::Video)
973 auto ref_clock = mMovie.getMasterClock();
974 auto diff = vp->mPts - ref_clock;
976 /* Skip or repeat the frame. Take delay into account. */
977 auto sync_threshold = std::min<nanoseconds>(delay, VideoSyncThreshold);
978 if(!(diff < AVNoSyncThreshold && diff > -AVNoSyncThreshold))
980 if(diff <= -sync_threshold)
981 delay = nanoseconds::zero();
982 else if(diff >= sync_threshold)
983 delay *= 2;
987 mFrameTimer += delay;
988 /* Compute the REAL delay. */
989 auto actual_delay = mFrameTimer - get_avtime();
990 if(!(actual_delay >= VideoSyncThreshold))
992 /* We don't have time to handle this picture, just skip to the next one. */
993 mPictQRead = (mPictQRead+1)%mPictQ.size();
994 mPictQSize--;
995 goto retry;
997 schedRefresh(std::chrono::duration_cast<milliseconds>(actual_delay));
999 /* Show the picture! */
1000 display(screen, renderer);
1002 /* Update queue for next picture. */
1003 mPictQRead = (mPictQRead+1)%mPictQ.size();
1004 mPictQSize--;
1005 lock.unlock();
1006 mPictQCond.notify_all();
1009 /* FF_UPDATE_EVENT handler, updates the picture's texture. It's called on the
1010 * main thread where the renderer was created.
1012 void VideoState::updatePicture(SDL_Window *screen, SDL_Renderer *renderer)
1014 Picture *vp = &mPictQ[mPictQWrite];
1015 bool fmt_updated = false;
1017 /* allocate or resize the buffer! */
1018 if(!vp->mImage || vp->mWidth != mCodecCtx->width || vp->mHeight != mCodecCtx->height)
1020 fmt_updated = true;
1021 if(vp->mImage)
1022 SDL_DestroyTexture(vp->mImage);
1023 vp->mImage = SDL_CreateTexture(
1024 renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING,
1025 mCodecCtx->coded_width, mCodecCtx->coded_height
1027 if(!vp->mImage)
1028 std::cerr<< "Failed to create YV12 texture!" <<std::endl;
1029 vp->mWidth = mCodecCtx->width;
1030 vp->mHeight = mCodecCtx->height;
1032 if(mFirstUpdate && vp->mWidth > 0 && vp->mHeight > 0)
1034 /* For the first update, set the window size to the video size. */
1035 mFirstUpdate = false;
1037 int w = vp->mWidth;
1038 int h = vp->mHeight;
1039 if(mCodecCtx->sample_aspect_ratio.den != 0)
1041 double aspect_ratio = av_q2d(mCodecCtx->sample_aspect_ratio);
1042 if(aspect_ratio >= 1.0)
1043 w = (int)(w*aspect_ratio + 0.5);
1044 else if(aspect_ratio > 0.0)
1045 h = (int)(h/aspect_ratio + 0.5);
1047 SDL_SetWindowSize(screen, w, h);
1051 if(vp->mImage)
1053 AVFrame *frame = mDecodedFrame.get();
1054 void *pixels = nullptr;
1055 int pitch = 0;
1057 if(mCodecCtx->pix_fmt == AV_PIX_FMT_YUV420P)
1058 SDL_UpdateYUVTexture(vp->mImage, nullptr,
1059 frame->data[0], frame->linesize[0],
1060 frame->data[1], frame->linesize[1],
1061 frame->data[2], frame->linesize[2]
1063 else if(SDL_LockTexture(vp->mImage, nullptr, &pixels, &pitch) != 0)
1064 std::cerr<< "Failed to lock texture" <<std::endl;
1065 else
1067 // Convert the image into YUV format that SDL uses
1068 int coded_w = mCodecCtx->coded_width;
1069 int coded_h = mCodecCtx->coded_height;
1070 int w = mCodecCtx->width;
1071 int h = mCodecCtx->height;
1072 if(!mSwscaleCtx || fmt_updated)
1074 mSwscaleCtx.reset(sws_getContext(
1075 w, h, mCodecCtx->pix_fmt,
1076 w, h, AV_PIX_FMT_YUV420P, 0,
1077 nullptr, nullptr, nullptr
1081 /* point pict at the queue */
1082 uint8_t *pict_data[3];
1083 pict_data[0] = reinterpret_cast<uint8_t*>(pixels);
1084 pict_data[1] = pict_data[0] + coded_w*coded_h;
1085 pict_data[2] = pict_data[1] + coded_w*coded_h/4;
1087 int pict_linesize[3];
1088 pict_linesize[0] = pitch;
1089 pict_linesize[1] = pitch / 2;
1090 pict_linesize[2] = pitch / 2;
1092 sws_scale(mSwscaleCtx.get(), (const uint8_t**)frame->data,
1093 frame->linesize, 0, h, pict_data, pict_linesize);
1094 SDL_UnlockTexture(vp->mImage);
1098 vp->mUpdated.store(true, std::memory_order_release);
1099 std::unique_lock<std::mutex>(mPictQMutex).unlock();
1100 mPictQCond.notify_one();
1103 int VideoState::queuePicture(nanoseconds pts)
1105 /* Wait until we have space for a new pic */
1106 std::unique_lock<std::mutex> lock(mPictQMutex);
1107 while(mPictQSize >= mPictQ.size() && !mMovie.mQuit.load(std::memory_order_relaxed))
1108 mPictQCond.wait(lock);
1109 lock.unlock();
1111 if(mMovie.mQuit.load(std::memory_order_relaxed))
1112 return -1;
1114 Picture *vp = &mPictQ[mPictQWrite];
1116 /* We have to create/update the picture in the main thread */
1117 vp->mUpdated.store(false, std::memory_order_relaxed);
1118 SDL_Event evt{};
1119 evt.user.type = FF_UPDATE_EVENT;
1120 evt.user.data1 = this;
1121 SDL_PushEvent(&evt);
1123 /* Wait until the picture is updated. */
1124 lock.lock();
1125 while(!vp->mUpdated.load(std::memory_order_relaxed))
1127 if(mMovie.mQuit.load(std::memory_order_relaxed))
1128 return -1;
1129 mPictQCond.wait(lock);
1131 if(mMovie.mQuit.load(std::memory_order_relaxed))
1132 return -1;
1133 vp->mPts = pts;
1135 mPictQWrite = (mPictQWrite+1)%mPictQ.size();
1136 mPictQSize++;
1137 lock.unlock();
1139 return 0;
1142 int VideoState::handler()
1144 mDecodedFrame.reset(av_frame_alloc());
1145 while(!mMovie.mQuit.load(std::memory_order_relaxed))
1147 std::unique_lock<std::mutex> lock(mQueueMtx);
1148 /* Decode video frame */
1149 int ret = avcodec_receive_frame(mCodecCtx.get(), mDecodedFrame.get());
1150 if(ret == AVERROR(EAGAIN))
1152 mMovie.mSendDataGood.clear(std::memory_order_relaxed);
1153 std::unique_lock<std::mutex>(mMovie.mSendMtx).unlock();
1154 mMovie.mSendCond.notify_one();
1155 do {
1156 mQueueCond.wait(lock);
1157 ret = avcodec_receive_frame(mCodecCtx.get(), mDecodedFrame.get());
1158 } while(ret == AVERROR(EAGAIN));
1160 lock.unlock();
1161 if(ret == AVERROR_EOF) break;
1162 mMovie.mSendDataGood.clear(std::memory_order_relaxed);
1163 mMovie.mSendCond.notify_one();
1164 if(ret < 0)
1166 std::cerr<< "Failed to decode frame: "<<ret <<std::endl;
1167 continue;
1170 /* Get the PTS for this frame. */
1171 nanoseconds pts;
1172 if(mDecodedFrame->best_effort_timestamp != AV_NOPTS_VALUE)
1173 mClock = std::chrono::duration_cast<nanoseconds>(
1174 seconds_d64(av_q2d(mStream->time_base)*mDecodedFrame->best_effort_timestamp)
1176 pts = mClock;
1178 /* Update the video clock to the next expected PTS. */
1179 auto frame_delay = av_q2d(mCodecCtx->time_base);
1180 frame_delay += mDecodedFrame->repeat_pict * (frame_delay * 0.5);
1181 mClock += std::chrono::duration_cast<nanoseconds>(seconds_d64(frame_delay));
1183 if(queuePicture(pts) < 0)
1184 break;
1185 av_frame_unref(mDecodedFrame.get());
1187 mEOS = true;
1189 std::unique_lock<std::mutex> lock(mPictQMutex);
1190 if(mMovie.mQuit.load(std::memory_order_relaxed))
1192 mPictQRead = 0;
1193 mPictQWrite = 0;
1194 mPictQSize = 0;
1196 while(!mFinalUpdate)
1197 mPictQCond.wait(lock);
1199 return 0;
1203 int MovieState::decode_interrupt_cb(void *ctx)
1205 return reinterpret_cast<MovieState*>(ctx)->mQuit.load(std::memory_order_relaxed);
1208 bool MovieState::prepare()
1210 AVIOContext *avioctx = nullptr;
1211 AVIOInterruptCB intcb = { decode_interrupt_cb, this };
1212 if(avio_open2(&avioctx, mFilename.c_str(), AVIO_FLAG_READ, &intcb, nullptr))
1214 std::cerr<< "Failed to open "<<mFilename <<std::endl;
1215 return false;
1217 mIOContext.reset(avioctx);
1219 /* Open movie file. If avformat_open_input fails it will automatically free
1220 * this context, so don't set it onto a smart pointer yet.
1222 AVFormatContext *fmtctx = avformat_alloc_context();
1223 fmtctx->pb = mIOContext.get();
1224 fmtctx->interrupt_callback = intcb;
1225 if(avformat_open_input(&fmtctx, mFilename.c_str(), nullptr, nullptr) != 0)
1227 std::cerr<< "Failed to open "<<mFilename <<std::endl;
1228 return false;
1230 mFormatCtx.reset(fmtctx);
1232 /* Retrieve stream information */
1233 if(avformat_find_stream_info(mFormatCtx.get(), nullptr) < 0)
1235 std::cerr<< mFilename<<": failed to find stream info" <<std::endl;
1236 return false;
1239 mVideo.schedRefresh(milliseconds(40));
1241 mParseThread = std::thread(std::mem_fn(&MovieState::parse_handler), this);
1242 return true;
1245 void MovieState::setTitle(SDL_Window *window)
1247 auto pos1 = mFilename.rfind('/');
1248 auto pos2 = mFilename.rfind('\\');
1249 auto fpos = ((pos1 == std::string::npos) ? pos2 :
1250 (pos2 == std::string::npos) ? pos1 :
1251 std::max(pos1, pos2)) + 1;
1252 SDL_SetWindowTitle(window, (mFilename.substr(fpos)+" - "+AppName).c_str());
1255 nanoseconds MovieState::getClock()
1257 if(!mPlaying.load(std::memory_order_relaxed))
1258 return nanoseconds::zero();
1259 return get_avtime() - mClockBase;
1262 nanoseconds MovieState::getMasterClock()
1264 if(mAVSyncType == SyncMaster::Video)
1265 return mVideo.getClock();
1266 if(mAVSyncType == SyncMaster::Audio)
1267 return mAudio.getClock();
1268 return getClock();
1271 nanoseconds MovieState::getDuration()
1272 { return std::chrono::duration<int64_t,std::ratio<1,AV_TIME_BASE>>(mFormatCtx->duration); }
1274 int MovieState::streamComponentOpen(int stream_index)
1276 if(stream_index < 0 || (unsigned int)stream_index >= mFormatCtx->nb_streams)
1277 return -1;
1279 /* Get a pointer to the codec context for the stream, and open the
1280 * associated codec.
1282 AVCodecCtxPtr avctx(avcodec_alloc_context3(nullptr));
1283 if(!avctx) return -1;
1285 if(avcodec_parameters_to_context(avctx.get(), mFormatCtx->streams[stream_index]->codecpar))
1286 return -1;
1288 AVCodec *codec = avcodec_find_decoder(avctx->codec_id);
1289 if(!codec || avcodec_open2(avctx.get(), codec, nullptr) < 0)
1291 std::cerr<< "Unsupported codec: "<<avcodec_get_name(avctx->codec_id)
1292 << " (0x"<<std::hex<<avctx->codec_id<<std::dec<<")" <<std::endl;
1293 return -1;
1296 /* Initialize and start the media type handler */
1297 switch(avctx->codec_type)
1299 case AVMEDIA_TYPE_AUDIO:
1300 mAudio.mStream = mFormatCtx->streams[stream_index];
1301 mAudio.mCodecCtx = std::move(avctx);
1303 mAudioThread = std::thread(std::mem_fn(&AudioState::handler), &mAudio);
1304 break;
1306 case AVMEDIA_TYPE_VIDEO:
1307 mVideo.mStream = mFormatCtx->streams[stream_index];
1308 mVideo.mCodecCtx = std::move(avctx);
1310 mVideoThread = std::thread(std::mem_fn(&VideoState::handler), &mVideo);
1311 break;
1313 default:
1314 return -1;
1317 return stream_index;
1320 int MovieState::parse_handler()
1322 int video_index = -1;
1323 int audio_index = -1;
1325 /* Dump information about file onto standard error */
1326 av_dump_format(mFormatCtx.get(), 0, mFilename.c_str(), 0);
1328 /* Find the first video and audio streams */
1329 for(unsigned int i = 0;i < mFormatCtx->nb_streams;i++)
1331 auto codecpar = mFormatCtx->streams[i]->codecpar;
1332 if(codecpar->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0)
1333 video_index = streamComponentOpen(i);
1334 else if(codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0)
1335 audio_index = streamComponentOpen(i);
1338 if(video_index < 0 && audio_index < 0)
1340 std::cerr<< mFilename<<": could not open codecs" <<std::endl;
1341 mQuit = true;
1344 PacketQueue audio_queue, video_queue;
1345 bool input_finished = false;
1347 /* Main packet reading/dispatching loop */
1348 while(!mQuit.load(std::memory_order_relaxed) && !input_finished)
1350 AVPacket packet;
1351 if(av_read_frame(mFormatCtx.get(), &packet) < 0)
1352 input_finished = true;
1353 else
1355 /* Copy the packet into the queue it's meant for. */
1356 if(packet.stream_index == video_index)
1357 video_queue.put(&packet);
1358 else if(packet.stream_index == audio_index)
1359 audio_queue.put(&packet);
1360 av_packet_unref(&packet);
1363 do {
1364 /* Send whatever queued packets we have. */
1365 if(!audio_queue.empty())
1367 std::unique_lock<std::mutex> lock(mAudio.mQueueMtx);
1368 int ret;
1369 do {
1370 ret = avcodec_send_packet(mAudio.mCodecCtx.get(), audio_queue.front());
1371 if(ret != AVERROR(EAGAIN)) audio_queue.pop();
1372 } while(ret != AVERROR(EAGAIN) && !audio_queue.empty());
1373 lock.unlock();
1374 mAudio.mQueueCond.notify_one();
1376 if(!video_queue.empty())
1378 std::unique_lock<std::mutex> lock(mVideo.mQueueMtx);
1379 int ret;
1380 do {
1381 ret = avcodec_send_packet(mVideo.mCodecCtx.get(), video_queue.front());
1382 if(ret != AVERROR(EAGAIN)) video_queue.pop();
1383 } while(ret != AVERROR(EAGAIN) && !video_queue.empty());
1384 lock.unlock();
1385 mVideo.mQueueCond.notify_one();
1387 /* If the queues are completely empty, or it's not full and there's
1388 * more input to read, go get more.
1390 size_t queue_size = audio_queue.totalSize() + video_queue.totalSize();
1391 if(queue_size == 0 || (queue_size < MAX_QUEUE_SIZE && !input_finished))
1392 break;
1394 if(!mPlaying.load(std::memory_order_relaxed))
1396 if((!mAudio.mCodecCtx || mAudio.isBufferFilled()) &&
1397 (!mVideo.mCodecCtx || mVideo.isBufferFilled()))
1399 /* Set the base time 50ms ahead of the current av time. */
1400 mClockBase = get_avtime() + milliseconds(50);
1401 mVideo.mCurrentPtsTime = mClockBase;
1402 mVideo.mFrameTimer = mVideo.mCurrentPtsTime;
1403 mAudio.startPlayback();
1404 mPlaying.store(std::memory_order_release);
1407 /* Nothing to send or get for now, wait a bit and try again. */
1408 { std::unique_lock<std::mutex> lock(mSendMtx);
1409 if(mSendDataGood.test_and_set(std::memory_order_relaxed))
1410 mSendCond.wait_for(lock, milliseconds(10));
1412 } while(!mQuit.load(std::memory_order_relaxed));
1414 /* Pass a null packet to finish the send buffers (the receive functions
1415 * will get AVERROR_EOF when emptied).
1417 if(mVideo.mCodecCtx)
1419 { std::lock_guard<std::mutex> lock(mVideo.mQueueMtx);
1420 avcodec_send_packet(mVideo.mCodecCtx.get(), nullptr);
1422 mVideo.mQueueCond.notify_one();
1424 if(mAudio.mCodecCtx)
1426 { std::lock_guard<std::mutex> lock(mAudio.mQueueMtx);
1427 avcodec_send_packet(mAudio.mCodecCtx.get(), nullptr);
1429 mAudio.mQueueCond.notify_one();
1431 video_queue.clear();
1432 audio_queue.clear();
1434 /* all done - wait for it */
1435 if(mVideoThread.joinable())
1436 mVideoThread.join();
1437 if(mAudioThread.joinable())
1438 mAudioThread.join();
1440 mVideo.mEOS = true;
1441 std::unique_lock<std::mutex> lock(mVideo.mPictQMutex);
1442 while(!mVideo.mFinalUpdate)
1443 mVideo.mPictQCond.wait(lock);
1444 lock.unlock();
1446 SDL_Event evt{};
1447 evt.user.type = FF_MOVIE_DONE_EVENT;
1448 SDL_PushEvent(&evt);
1450 return 0;
1454 // Helper class+method to print the time with human-readable formatting.
1455 struct PrettyTime {
1456 seconds mTime;
1458 inline std::ostream &operator<<(std::ostream &os, const PrettyTime &rhs)
1460 using hours = std::chrono::hours;
1461 using minutes = std::chrono::minutes;
1462 using std::chrono::duration_cast;
1464 seconds t = rhs.mTime;
1465 if(t.count() < 0)
1467 os << '-';
1468 t *= -1;
1471 // Only handle up to hour formatting
1472 if(t >= hours(1))
1473 os << duration_cast<hours>(t).count() << 'h' << std::setfill('0') << std::setw(2)
1474 << (duration_cast<minutes>(t).count() % 60) << 'm';
1475 else
1476 os << duration_cast<minutes>(t).count() << 'm' << std::setfill('0');
1477 os << std::setw(2) << (duration_cast<seconds>(t).count() % 60) << 's' << std::setw(0)
1478 << std::setfill(' ');
1479 return os;
1482 } // namespace
1485 int main(int argc, char *argv[])
1487 std::unique_ptr<MovieState> movState;
1489 if(argc < 2)
1491 std::cerr<< "Usage: "<<argv[0]<<" [-device <device name>] [-direct] <files...>" <<std::endl;
1492 return 1;
1494 /* Register all formats and codecs */
1495 av_register_all();
1496 /* Initialize networking protocols */
1497 avformat_network_init();
1499 if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER))
1501 std::cerr<< "Could not initialize SDL - <<"<<SDL_GetError() <<std::endl;
1502 return 1;
1505 /* Make a window to put our video */
1506 SDL_Window *screen = SDL_CreateWindow(AppName.c_str(), 0, 0, 640, 480, SDL_WINDOW_RESIZABLE);
1507 if(!screen)
1509 std::cerr<< "SDL: could not set video mode - exiting" <<std::endl;
1510 return 1;
1512 /* Make a renderer to handle the texture image surface and rendering. */
1513 Uint32 render_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC;
1514 SDL_Renderer *renderer = SDL_CreateRenderer(screen, -1, render_flags);
1515 if(renderer)
1517 SDL_RendererInfo rinf{};
1518 bool ok = false;
1520 /* Make sure the renderer supports IYUV textures. If not, fallback to a
1521 * software renderer. */
1522 if(SDL_GetRendererInfo(renderer, &rinf) == 0)
1524 for(Uint32 i = 0;!ok && i < rinf.num_texture_formats;i++)
1525 ok = (rinf.texture_formats[i] == SDL_PIXELFORMAT_IYUV);
1527 if(!ok)
1529 std::cerr<< "IYUV pixelformat textures not supported on renderer "<<rinf.name <<std::endl;
1530 SDL_DestroyRenderer(renderer);
1531 renderer = nullptr;
1534 if(!renderer)
1536 render_flags = SDL_RENDERER_SOFTWARE | SDL_RENDERER_PRESENTVSYNC;
1537 renderer = SDL_CreateRenderer(screen, -1, render_flags);
1539 if(!renderer)
1541 std::cerr<< "SDL: could not create renderer - exiting" <<std::endl;
1542 return 1;
1544 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
1545 SDL_RenderFillRect(renderer, nullptr);
1546 SDL_RenderPresent(renderer);
1548 /* Open an audio device */
1549 int fileidx = 1;
1550 ALCdevice *device = [argc,argv,&fileidx]() -> ALCdevice*
1552 ALCdevice *dev = NULL;
1553 if(argc > 3 && strcmp(argv[1], "-device") == 0)
1555 fileidx = 3;
1556 dev = alcOpenDevice(argv[2]);
1557 if(dev) return dev;
1558 std::cerr<< "Failed to open \""<<argv[2]<<"\" - trying default" <<std::endl;
1560 return alcOpenDevice(nullptr);
1561 }();
1562 ALCcontext *context = alcCreateContext(device, nullptr);
1563 if(!context || alcMakeContextCurrent(context) == ALC_FALSE)
1565 std::cerr<< "Failed to set up audio device" <<std::endl;
1566 if(context)
1567 alcDestroyContext(context);
1568 return 1;
1571 const ALCchar *name = nullptr;
1572 if(alcIsExtensionPresent(device, "ALC_ENUMERATE_ALL_EXT"))
1573 name = alcGetString(device, ALC_ALL_DEVICES_SPECIFIER);
1574 if(!name || alcGetError(device) != AL_NO_ERROR)
1575 name = alcGetString(device, ALC_DEVICE_SPECIFIER);
1576 std::cout<< "Opened \""<<name<<"\"" <<std::endl;
1578 if(alcIsExtensionPresent(device, "ALC_SOFT_device_clock"))
1580 std::cout<< "Found ALC_SOFT_device_clock" <<std::endl;
1581 alcGetInteger64vSOFT = reinterpret_cast<LPALCGETINTEGER64VSOFT>(
1582 alcGetProcAddress(device, "alcGetInteger64vSOFT")
1586 if(alIsExtensionPresent("AL_SOFT_source_latency"))
1588 std::cout<< "Found AL_SOFT_source_latency" <<std::endl;
1589 alGetSourcei64vSOFT = reinterpret_cast<LPALGETSOURCEI64VSOFT>(
1590 alGetProcAddress("alGetSourcei64vSOFT")
1594 if(fileidx < argc && strcmp(argv[fileidx], "-direct") == 0)
1596 ++fileidx;
1597 if(!alIsExtensionPresent("AL_SOFT_direct_channels"))
1598 std::cerr<< "AL_SOFT_direct_channels not supported for direct output" <<std::endl;
1599 else
1601 std::cout<< "Found AL_SOFT_direct_channels" <<std::endl;
1602 EnableDirectOut = true;
1606 while(fileidx < argc && !movState)
1608 movState = std::unique_ptr<MovieState>(new MovieState(argv[fileidx++]));
1609 if(!movState->prepare()) movState = nullptr;
1611 if(!movState)
1613 std::cerr<< "Could not start a video" <<std::endl;
1614 return 1;
1616 movState->setTitle(screen);
1618 /* Default to going to the next movie at the end of one. */
1619 enum class EomAction {
1620 Next, Quit
1621 } eom_action = EomAction::Next;
1622 seconds last_time(-1);
1623 SDL_Event event;
1624 while(1)
1626 int have_evt = SDL_WaitEventTimeout(&event, 10);
1628 auto cur_time = std::chrono::duration_cast<seconds>(movState->getMasterClock());
1629 if(cur_time != last_time)
1631 auto end_time = std::chrono::duration_cast<seconds>(movState->getDuration());
1632 std::cout<< "\r "<<PrettyTime{cur_time}<<" / "<<PrettyTime{end_time} <<std::flush;
1633 last_time = cur_time;
1635 if(!have_evt) continue;
1637 switch(event.type)
1639 case SDL_KEYDOWN:
1640 switch(event.key.keysym.sym)
1642 case SDLK_ESCAPE:
1643 movState->mQuit = true;
1644 eom_action = EomAction::Quit;
1645 break;
1647 case SDLK_n:
1648 movState->mQuit = true;
1649 eom_action = EomAction::Next;
1650 break;
1652 default:
1653 break;
1655 break;
1657 case SDL_WINDOWEVENT:
1658 switch(event.window.event)
1660 case SDL_WINDOWEVENT_RESIZED:
1661 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
1662 SDL_RenderFillRect(renderer, nullptr);
1663 break;
1665 default:
1666 break;
1668 break;
1670 case SDL_QUIT:
1671 movState->mQuit = true;
1672 eom_action = EomAction::Quit;
1673 break;
1675 case FF_UPDATE_EVENT:
1676 reinterpret_cast<VideoState*>(event.user.data1)->updatePicture(
1677 screen, renderer
1679 break;
1681 case FF_REFRESH_EVENT:
1682 reinterpret_cast<VideoState*>(event.user.data1)->refreshTimer(
1683 screen, renderer
1685 break;
1687 case FF_MOVIE_DONE_EVENT:
1688 std::cout<<'\n';
1689 last_time = seconds(-1);
1690 if(eom_action != EomAction::Quit)
1692 movState = nullptr;
1693 while(fileidx < argc && !movState)
1695 movState = std::unique_ptr<MovieState>(new MovieState(argv[fileidx++]));
1696 if(!movState->prepare()) movState = nullptr;
1698 if(movState)
1700 movState->setTitle(screen);
1701 break;
1705 /* Nothing more to play. Shut everything down and quit. */
1706 movState = nullptr;
1708 alcMakeContextCurrent(nullptr);
1709 alcDestroyContext(context);
1710 alcCloseDevice(device);
1712 SDL_DestroyRenderer(renderer);
1713 renderer = nullptr;
1714 SDL_DestroyWindow(screen);
1715 screen = nullptr;
1717 SDL_Quit();
1718 exit(0);
1720 default:
1721 break;
1725 std::cerr<< "SDL_WaitEvent error - "<<SDL_GetError() <<std::endl;
1726 return 1;