Correctly apply reverb coefficient fading over the entire fade length
[openal-soft.git] / examples / alffplay.cpp
blob5ee1da63bda6d052e2aa4247f16f82aa06936097
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>
21 #include <cmath>
22 #include <string>
24 extern "C" {
25 #include "libavcodec/avcodec.h"
26 #include "libavformat/avformat.h"
27 #include "libavformat/avio.h"
28 #include "libavutil/time.h"
29 #include "libavutil/pixfmt.h"
30 #include "libavutil/avstring.h"
31 #include "libavutil/channel_layout.h"
32 #include "libswscale/swscale.h"
33 #include "libswresample/swresample.h"
36 #include "SDL.h"
38 #include "AL/alc.h"
39 #include "AL/al.h"
40 #include "AL/alext.h"
42 extern "C" {
43 #ifndef AL_SOFT_map_buffer
44 #define AL_SOFT_map_buffer 1
45 typedef unsigned int ALbitfieldSOFT;
46 #define AL_MAP_READ_BIT_SOFT 0x00000001
47 #define AL_MAP_WRITE_BIT_SOFT 0x00000002
48 #define AL_MAP_PERSISTENT_BIT_SOFT 0x00000004
49 #define AL_PRESERVE_DATA_BIT_SOFT 0x00000008
50 typedef void (AL_APIENTRY*LPALBUFFERSTORAGESOFT)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags);
51 typedef void* (AL_APIENTRY*LPALMAPBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access);
52 typedef void (AL_APIENTRY*LPALUNMAPBUFFERSOFT)(ALuint buffer);
53 typedef void (AL_APIENTRY*LPALFLUSHMAPPEDBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length);
54 #endif
56 #ifndef AL_SOFT_events
57 #define AL_SOFT_events 1
58 #define AL_EVENT_CALLBACK_FUNCTION_SOFT 0x1220
59 #define AL_EVENT_CALLBACK_USER_PARAM_SOFT 0x1221
60 #define AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT 0x1222
61 #define AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT 0x1223
62 #define AL_EVENT_TYPE_ERROR_SOFT 0x1224
63 #define AL_EVENT_TYPE_PERFORMANCE_SOFT 0x1225
64 #define AL_EVENT_TYPE_DEPRECATED_SOFT 0x1226
65 #define AL_EVENT_TYPE_DISCONNECTED_SOFT 0x1227
66 typedef void (AL_APIENTRY*ALEVENTPROCSOFT)(ALenum eventType, ALuint object, ALuint param,
67 ALsizei length, const ALchar *message,
68 void *userParam);
69 typedef void (AL_APIENTRY*LPALEVENTCONTROLSOFT)(ALsizei count, const ALenum *types, ALboolean enable);
70 typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void *userParam);
71 typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname);
72 typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values);
73 #endif
76 namespace {
78 #ifndef M_PI
79 #define M_PI (3.14159265358979323846)
80 #endif
82 using nanoseconds = std::chrono::nanoseconds;
83 using microseconds = std::chrono::microseconds;
84 using milliseconds = std::chrono::milliseconds;
85 using seconds = std::chrono::seconds;
86 using seconds_d64 = std::chrono::duration<double>;
88 const std::string AppName("alffplay");
90 bool EnableDirectOut = false;
91 bool EnableWideStereo = false;
92 LPALGETSOURCEI64VSOFT alGetSourcei64vSOFT;
93 LPALCGETINTEGER64VSOFT alcGetInteger64vSOFT;
95 LPALBUFFERSTORAGESOFT alBufferStorageSOFT;
96 LPALMAPBUFFERSOFT alMapBufferSOFT;
97 LPALUNMAPBUFFERSOFT alUnmapBufferSOFT;
99 LPALEVENTCONTROLSOFT alEventControlSOFT;
100 LPALEVENTCALLBACKSOFT alEventCallbackSOFT;
102 const seconds AVNoSyncThreshold(10);
104 const milliseconds VideoSyncThreshold(10);
105 #define VIDEO_PICTURE_QUEUE_SIZE 16
107 const seconds_d64 AudioSyncThreshold(0.03);
108 const milliseconds AudioSampleCorrectionMax(50);
109 /* Averaging filter coefficient for audio sync. */
110 #define AUDIO_DIFF_AVG_NB 20
111 const double AudioAvgFilterCoeff = std::pow(0.01, 1.0/AUDIO_DIFF_AVG_NB);
112 /* Per-buffer size, in time */
113 const milliseconds AudioBufferTime(20);
114 /* Buffer total size, in time (should be divisible by the buffer time) */
115 const milliseconds AudioBufferTotalTime(800);
117 #define MAX_QUEUE_SIZE (15 * 1024 * 1024) /* Bytes of compressed data to keep queued */
119 enum {
120 FF_UPDATE_EVENT = SDL_USEREVENT,
121 FF_REFRESH_EVENT,
122 FF_MOVIE_DONE_EVENT
125 enum class SyncMaster {
126 Audio,
127 Video,
128 External,
130 Default = External
134 inline microseconds get_avtime()
135 { return microseconds(av_gettime()); }
137 /* Define unique_ptrs to auto-cleanup associated ffmpeg objects. */
138 struct AVIOContextDeleter {
139 void operator()(AVIOContext *ptr) { avio_closep(&ptr); }
141 using AVIOContextPtr = std::unique_ptr<AVIOContext,AVIOContextDeleter>;
143 struct AVFormatCtxDeleter {
144 void operator()(AVFormatContext *ptr) { avformat_close_input(&ptr); }
146 using AVFormatCtxPtr = std::unique_ptr<AVFormatContext,AVFormatCtxDeleter>;
148 struct AVCodecCtxDeleter {
149 void operator()(AVCodecContext *ptr) { avcodec_free_context(&ptr); }
151 using AVCodecCtxPtr = std::unique_ptr<AVCodecContext,AVCodecCtxDeleter>;
153 struct AVFrameDeleter {
154 void operator()(AVFrame *ptr) { av_frame_free(&ptr); }
156 using AVFramePtr = std::unique_ptr<AVFrame,AVFrameDeleter>;
158 struct SwrContextDeleter {
159 void operator()(SwrContext *ptr) { swr_free(&ptr); }
161 using SwrContextPtr = std::unique_ptr<SwrContext,SwrContextDeleter>;
163 struct SwsContextDeleter {
164 void operator()(SwsContext *ptr) { sws_freeContext(ptr); }
166 using SwsContextPtr = std::unique_ptr<SwsContext,SwsContextDeleter>;
169 class PacketQueue {
170 std::deque<AVPacket> mPackets;
171 size_t mTotalSize{0};
173 public:
174 ~PacketQueue() { clear(); }
176 bool empty() const noexcept { return mPackets.empty(); }
177 size_t totalSize() const noexcept { return mTotalSize; }
179 void put(const AVPacket *pkt)
181 mPackets.push_back(AVPacket{});
182 if(av_packet_ref(&mPackets.back(), pkt) != 0)
183 mPackets.pop_back();
184 else
185 mTotalSize += mPackets.back().size;
188 AVPacket *front() noexcept
189 { return &mPackets.front(); }
191 void pop()
193 AVPacket *pkt = &mPackets.front();
194 mTotalSize -= pkt->size;
195 av_packet_unref(pkt);
196 mPackets.pop_front();
199 void clear()
201 for(AVPacket &pkt : mPackets)
202 av_packet_unref(&pkt);
203 mPackets.clear();
204 mTotalSize = 0;
209 struct MovieState;
211 struct AudioState {
212 MovieState &mMovie;
214 AVStream *mStream{nullptr};
215 AVCodecCtxPtr mCodecCtx;
217 std::mutex mQueueMtx;
218 std::condition_variable mQueueCond;
220 /* Used for clock difference average computation */
221 seconds_d64 mClockDiffAvg{0};
223 /* Time of the next sample to be buffered */
224 nanoseconds mCurrentPts{0};
226 /* Device clock time that the stream started at. */
227 nanoseconds mDeviceStartTime{nanoseconds::min()};
229 /* Decompressed sample frame, and swresample context for conversion */
230 AVFramePtr mDecodedFrame;
231 SwrContextPtr mSwresCtx;
233 /* Conversion format, for what gets fed to OpenAL */
234 int mDstChanLayout{0};
235 AVSampleFormat mDstSampleFmt{AV_SAMPLE_FMT_NONE};
237 /* Storage of converted samples */
238 uint8_t *mSamples{nullptr};
239 int mSamplesLen{0}; /* In samples */
240 int mSamplesPos{0};
241 int mSamplesMax{0};
243 /* OpenAL format */
244 ALenum mFormat{AL_NONE};
245 ALsizei mFrameSize{0};
247 std::mutex mSrcMutex;
248 std::condition_variable mSrcCond;
249 std::atomic_flag mConnected;
250 ALuint mSource{0};
251 std::vector<ALuint> mBuffers;
252 ALsizei mBufferIdx{0};
254 AudioState(MovieState &movie) : mMovie(movie)
255 { mConnected.test_and_set(std::memory_order_relaxed); }
256 ~AudioState()
258 if(mSource)
259 alDeleteSources(1, &mSource);
260 if(!mBuffers.empty())
261 alDeleteBuffers(mBuffers.size(), mBuffers.data());
263 av_freep(&mSamples);
266 static void AL_APIENTRY EventCallback(ALenum eventType, ALuint object, ALuint param,
267 ALsizei length, const ALchar *message,
268 void *userParam);
270 nanoseconds getClockNoLock();
271 nanoseconds getClock()
273 std::lock_guard<std::mutex> lock(mSrcMutex);
274 return getClockNoLock();
277 bool isBufferFilled();
278 void startPlayback();
280 int getSync();
281 int decodeFrame();
282 bool readAudio(uint8_t *samples, int length);
284 int handler();
287 struct VideoState {
288 MovieState &mMovie;
290 AVStream *mStream{nullptr};
291 AVCodecCtxPtr mCodecCtx;
293 std::mutex mQueueMtx;
294 std::condition_variable mQueueCond;
296 nanoseconds mClock{0};
297 nanoseconds mFrameTimer{0};
298 nanoseconds mFrameLastPts{0};
299 nanoseconds mFrameLastDelay{0};
300 nanoseconds mCurrentPts{0};
301 /* time (av_gettime) at which we updated mCurrentPts - used to have running video pts */
302 microseconds mCurrentPtsTime{0};
304 /* Decompressed video frame, and swscale context for conversion */
305 AVFramePtr mDecodedFrame;
306 SwsContextPtr mSwscaleCtx;
308 struct Picture {
309 SDL_Texture *mImage{nullptr};
310 int mWidth{0}, mHeight{0}; /* Logical image size (actual size may be larger) */
311 std::atomic<bool> mUpdated{false};
312 nanoseconds mPts{0};
314 ~Picture()
316 if(mImage)
317 SDL_DestroyTexture(mImage);
318 mImage = nullptr;
321 std::array<Picture,VIDEO_PICTURE_QUEUE_SIZE> mPictQ;
322 size_t mPictQSize{0}, mPictQRead{0}, mPictQWrite{0};
323 std::mutex mPictQMutex;
324 std::condition_variable mPictQCond;
325 bool mFirstUpdate{true};
326 std::atomic<bool> mEOS{false};
327 std::atomic<bool> mFinalUpdate{false};
329 VideoState(MovieState &movie) : mMovie(movie) { }
331 nanoseconds getClock();
332 bool isBufferFilled();
334 static Uint32 SDLCALL sdl_refresh_timer_cb(Uint32 interval, void *opaque);
335 void schedRefresh(milliseconds delay);
336 void display(SDL_Window *screen, SDL_Renderer *renderer);
337 void refreshTimer(SDL_Window *screen, SDL_Renderer *renderer);
338 void updatePicture(SDL_Window *screen, SDL_Renderer *renderer);
339 int queuePicture(nanoseconds pts);
340 int handler();
343 struct MovieState {
344 AVIOContextPtr mIOContext;
345 AVFormatCtxPtr mFormatCtx;
347 SyncMaster mAVSyncType{SyncMaster::Default};
349 microseconds mClockBase{0};
350 std::atomic<bool> mPlaying{false};
352 std::mutex mSendMtx;
353 std::condition_variable mSendCond;
354 /* NOTE: false/clear = need data, true/set = no data needed */
355 std::atomic_flag mSendDataGood;
357 std::atomic<bool> mQuit{false};
359 AudioState mAudio;
360 VideoState mVideo;
362 std::thread mParseThread;
363 std::thread mAudioThread;
364 std::thread mVideoThread;
366 std::string mFilename;
368 MovieState(std::string fname)
369 : mAudio(*this), mVideo(*this), mFilename(std::move(fname))
371 ~MovieState()
373 mQuit = true;
374 if(mParseThread.joinable())
375 mParseThread.join();
378 static int decode_interrupt_cb(void *ctx);
379 bool prepare();
380 void setTitle(SDL_Window *window);
382 nanoseconds getClock();
384 nanoseconds getMasterClock();
386 nanoseconds getDuration();
388 int streamComponentOpen(int stream_index);
389 int parse_handler();
393 nanoseconds AudioState::getClockNoLock()
395 // The audio clock is the timestamp of the sample currently being heard.
396 if(alcGetInteger64vSOFT)
398 // If device start time = min, we aren't playing yet.
399 if(mDeviceStartTime == nanoseconds::min())
400 return nanoseconds::zero();
402 // Get the current device clock time and latency.
403 auto device = alcGetContextsDevice(alcGetCurrentContext());
404 ALCint64SOFT devtimes[2] = {0,0};
405 alcGetInteger64vSOFT(device, ALC_DEVICE_CLOCK_LATENCY_SOFT, 2, devtimes);
406 auto latency = nanoseconds(devtimes[1]);
407 auto device_time = nanoseconds(devtimes[0]);
409 // The clock is simply the current device time relative to the recorded
410 // start time. We can also subtract the latency to get more a accurate
411 // position of where the audio device actually is in the output stream.
412 return device_time - mDeviceStartTime - latency;
415 /* The source-based clock is based on 4 components:
416 * 1 - The timestamp of the next sample to buffer (mCurrentPts)
417 * 2 - The length of the source's buffer queue
418 * (AudioBufferTime*AL_BUFFERS_QUEUED)
419 * 3 - The offset OpenAL is currently at in the source (the first value
420 * from AL_SAMPLE_OFFSET_LATENCY_SOFT)
421 * 4 - The latency between OpenAL and the DAC (the second value from
422 * AL_SAMPLE_OFFSET_LATENCY_SOFT)
424 * Subtracting the length of the source queue from the next sample's
425 * timestamp gives the timestamp of the sample at the start of the source
426 * queue. Adding the source offset to that results in the timestamp for the
427 * sample at OpenAL's current position, and subtracting the source latency
428 * from that gives the timestamp of the sample currently at the DAC.
430 nanoseconds pts = mCurrentPts;
431 if(mSource)
433 ALint64SOFT offset[2];
434 ALint queued;
435 ALint status;
437 /* NOTE: The source state must be checked last, in case an underrun
438 * occurs and the source stops between retrieving the offset+latency
439 * and getting the state. */
440 if(alGetSourcei64vSOFT)
441 alGetSourcei64vSOFT(mSource, AL_SAMPLE_OFFSET_LATENCY_SOFT, offset);
442 else
444 ALint ioffset;
445 alGetSourcei(mSource, AL_SAMPLE_OFFSET, &ioffset);
446 offset[0] = (ALint64SOFT)ioffset << 32;
447 offset[1] = 0;
449 alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued);
450 alGetSourcei(mSource, AL_SOURCE_STATE, &status);
452 /* If the source is AL_STOPPED, then there was an underrun and all
453 * buffers are processed, so ignore the source queue. The audio thread
454 * will put the source into an AL_INITIAL state and clear the queue
455 * when it starts recovery. */
456 if(status != AL_STOPPED)
458 using fixed32 = std::chrono::duration<int64_t,std::ratio<1,(1ll<<32)>>;
460 pts -= AudioBufferTime*queued;
461 pts += std::chrono::duration_cast<nanoseconds>(
462 fixed32(offset[0] / mCodecCtx->sample_rate)
465 /* Don't offset by the latency if the source isn't playing. */
466 if(status == AL_PLAYING)
467 pts -= nanoseconds(offset[1]);
470 return std::max(pts, nanoseconds::zero());
473 bool AudioState::isBufferFilled()
475 /* All of OpenAL's buffer queueing happens under the mSrcMutex lock, as
476 * does the source gen. So when we're able to grab the lock and the source
477 * is valid, the queue must be full.
479 std::lock_guard<std::mutex> lock(mSrcMutex);
480 return mSource != 0;
483 void AudioState::startPlayback()
485 alSourcePlay(mSource);
486 if(alcGetInteger64vSOFT)
488 using fixed32 = std::chrono::duration<int64_t,std::ratio<1,(1ll<<32)>>;
490 // Subtract the total buffer queue time from the current pts to get the
491 // pts of the start of the queue.
492 nanoseconds startpts = mCurrentPts - AudioBufferTotalTime;
493 int64_t srctimes[2]={0,0};
494 alGetSourcei64vSOFT(mSource, AL_SAMPLE_OFFSET_CLOCK_SOFT, srctimes);
495 auto device_time = nanoseconds(srctimes[1]);
496 auto src_offset = std::chrono::duration_cast<nanoseconds>(fixed32(srctimes[0])) /
497 mCodecCtx->sample_rate;
499 // The mixer may have ticked and incremented the device time and sample
500 // offset, so subtract the source offset from the device time to get
501 // the device time the source started at. Also subtract startpts to get
502 // the device time the stream would have started at to reach where it
503 // is now.
504 mDeviceStartTime = device_time - src_offset - startpts;
508 int AudioState::getSync()
510 if(mMovie.mAVSyncType == SyncMaster::Audio)
511 return 0;
513 auto ref_clock = mMovie.getMasterClock();
514 auto diff = ref_clock - getClockNoLock();
516 if(!(diff < AVNoSyncThreshold && diff > -AVNoSyncThreshold))
518 /* Difference is TOO big; reset accumulated average */
519 mClockDiffAvg = seconds_d64::zero();
520 return 0;
523 /* Accumulate the diffs */
524 mClockDiffAvg = mClockDiffAvg*AudioAvgFilterCoeff + diff;
525 auto avg_diff = mClockDiffAvg*(1.0 - AudioAvgFilterCoeff);
526 if(avg_diff < AudioSyncThreshold/2.0 && avg_diff > -AudioSyncThreshold)
527 return 0;
529 /* Constrain the per-update difference to avoid exceedingly large skips */
530 diff = std::min<nanoseconds>(std::max<nanoseconds>(diff, -AudioSampleCorrectionMax),
531 AudioSampleCorrectionMax);
532 return (int)std::chrono::duration_cast<seconds>(diff*mCodecCtx->sample_rate).count();
535 int AudioState::decodeFrame()
537 while(!mMovie.mQuit.load(std::memory_order_relaxed))
539 std::unique_lock<std::mutex> lock(mQueueMtx);
540 int ret = avcodec_receive_frame(mCodecCtx.get(), mDecodedFrame.get());
541 if(ret == AVERROR(EAGAIN))
543 mMovie.mSendDataGood.clear(std::memory_order_relaxed);
544 std::unique_lock<std::mutex>(mMovie.mSendMtx).unlock();
545 mMovie.mSendCond.notify_one();
546 do {
547 mQueueCond.wait(lock);
548 ret = avcodec_receive_frame(mCodecCtx.get(), mDecodedFrame.get());
549 } while(ret == AVERROR(EAGAIN));
551 lock.unlock();
552 if(ret == AVERROR_EOF) break;
553 mMovie.mSendDataGood.clear(std::memory_order_relaxed);
554 mMovie.mSendCond.notify_one();
555 if(ret < 0)
557 std::cerr<< "Failed to decode frame: "<<ret <<std::endl;
558 return 0;
561 if(mDecodedFrame->nb_samples <= 0)
563 av_frame_unref(mDecodedFrame.get());
564 continue;
567 /* If provided, update w/ pts */
568 if(mDecodedFrame->best_effort_timestamp != AV_NOPTS_VALUE)
569 mCurrentPts = std::chrono::duration_cast<nanoseconds>(
570 seconds_d64(av_q2d(mStream->time_base)*mDecodedFrame->best_effort_timestamp)
573 if(mDecodedFrame->nb_samples > mSamplesMax)
575 av_freep(&mSamples);
576 av_samples_alloc(
577 &mSamples, nullptr, mCodecCtx->channels,
578 mDecodedFrame->nb_samples, mDstSampleFmt, 0
580 mSamplesMax = mDecodedFrame->nb_samples;
582 /* Return the amount of sample frames converted */
583 int data_size = swr_convert(mSwresCtx.get(), &mSamples, mDecodedFrame->nb_samples,
584 (const uint8_t**)mDecodedFrame->data, mDecodedFrame->nb_samples
587 av_frame_unref(mDecodedFrame.get());
588 return data_size;
591 return 0;
594 /* Duplicates the sample at in to out, count times. The frame size is a
595 * multiple of the template type size.
597 template<typename T>
598 static void sample_dup(uint8_t *out, const uint8_t *in, int count, int frame_size)
600 const T *sample = reinterpret_cast<const T*>(in);
601 T *dst = reinterpret_cast<T*>(out);
602 if(frame_size == sizeof(T))
603 std::fill_n(dst, count, *sample);
604 else
606 /* NOTE: frame_size is a multiple of sizeof(T). */
607 int type_mult = frame_size / sizeof(T);
608 int i = 0;
609 std::generate_n(dst, count*type_mult,
610 [sample,type_mult,&i]() -> T
612 T ret = sample[i];
613 i = (i+1)%type_mult;
614 return ret;
621 bool AudioState::readAudio(uint8_t *samples, int length)
623 int sample_skip = getSync();
624 int audio_size = 0;
626 /* Read the next chunk of data, refill the buffer, and queue it
627 * on the source */
628 length /= mFrameSize;
629 while(audio_size < length)
631 if(mSamplesLen <= 0 || mSamplesPos >= mSamplesLen)
633 int frame_len = decodeFrame();
634 if(frame_len <= 0) break;
636 mSamplesLen = frame_len;
637 mSamplesPos = std::min(mSamplesLen, sample_skip);
638 sample_skip -= mSamplesPos;
640 // Adjust the device start time and current pts by the amount we're
641 // skipping/duplicating, so that the clock remains correct for the
642 // current stream position.
643 auto skip = nanoseconds(seconds(mSamplesPos)) / mCodecCtx->sample_rate;
644 mDeviceStartTime -= skip;
645 mCurrentPts += skip;
646 continue;
649 int rem = length - audio_size;
650 if(mSamplesPos >= 0)
652 int len = mSamplesLen - mSamplesPos;
653 if(rem > len) rem = len;
654 memcpy(samples, mSamples + mSamplesPos*mFrameSize, rem*mFrameSize);
656 else
658 rem = std::min(rem, -mSamplesPos);
660 /* Add samples by copying the first sample */
661 if((mFrameSize&7) == 0)
662 sample_dup<uint64_t>(samples, mSamples, rem, mFrameSize);
663 else if((mFrameSize&3) == 0)
664 sample_dup<uint32_t>(samples, mSamples, rem, mFrameSize);
665 else if((mFrameSize&1) == 0)
666 sample_dup<uint16_t>(samples, mSamples, rem, mFrameSize);
667 else
668 sample_dup<uint8_t>(samples, mSamples, rem, mFrameSize);
671 mSamplesPos += rem;
672 mCurrentPts += nanoseconds(seconds(rem)) / mCodecCtx->sample_rate;
673 samples += rem*mFrameSize;
674 audio_size += rem;
676 if(audio_size <= 0)
677 return false;
679 if(audio_size < length)
681 int rem = length - audio_size;
682 std::fill_n(samples, rem*mFrameSize,
683 (mDstSampleFmt == AV_SAMPLE_FMT_U8) ? 0x80 : 0x00);
684 mCurrentPts += nanoseconds(seconds(rem)) / mCodecCtx->sample_rate;
685 audio_size += rem;
687 return true;
691 void AL_APIENTRY AudioState::EventCallback(ALenum eventType, ALuint object, ALuint param,
692 ALsizei length, const ALchar *message,
693 void *userParam)
695 AudioState *self = reinterpret_cast<AudioState*>(userParam);
697 if(eventType == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT)
699 /* Temporarily lock the source mutex to ensure it's not between
700 * checking the processed count and going to sleep.
702 std::unique_lock<std::mutex>(self->mSrcMutex).unlock();
703 self->mSrcCond.notify_one();
704 return;
707 std::cout<< "\n---- AL Event on AudioState "<<self<<" ----\nEvent: ";
708 switch(eventType)
710 case AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT: std::cout<< "Buffer completed"; break;
711 case AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT: std::cout<< "Source state changed"; break;
712 case AL_EVENT_TYPE_ERROR_SOFT: std::cout<< "API error"; break;
713 case AL_EVENT_TYPE_PERFORMANCE_SOFT: std::cout<< "Performance"; break;
714 case AL_EVENT_TYPE_DEPRECATED_SOFT: std::cout<< "Deprecated"; break;
715 case AL_EVENT_TYPE_DISCONNECTED_SOFT: std::cout<< "Disconnected"; break;
716 default: std::cout<< "0x"<<std::hex<<std::setw(4)<<std::setfill('0')<<eventType<<
717 std::dec<<std::setw(0)<<std::setfill(' '); break;
719 std::cout<< "\n"
720 "Object ID: "<<object<<"\n"
721 "Parameter: "<<param<<"\n"
722 "Message: "<<std::string(message, length)<<"\n----"<<
723 std::endl;
725 if(eventType == AL_EVENT_TYPE_DISCONNECTED_SOFT)
727 { std::lock_guard<std::mutex> lock(self->mSrcMutex);
728 self->mConnected.clear(std::memory_order_release);
730 std::unique_lock<std::mutex>(self->mSrcMutex).unlock();
731 self->mSrcCond.notify_one();
735 int AudioState::handler()
737 const std::array<ALenum,6> types{{
738 AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT,
739 AL_EVENT_TYPE_ERROR_SOFT, AL_EVENT_TYPE_PERFORMANCE_SOFT, AL_EVENT_TYPE_DEPRECATED_SOFT,
740 AL_EVENT_TYPE_DISCONNECTED_SOFT
742 std::unique_lock<std::mutex> lock(mSrcMutex);
743 milliseconds sleep_time = AudioBufferTime / 3;
744 ALenum fmt;
746 if(alEventControlSOFT)
748 alEventControlSOFT(types.size(), types.data(), AL_TRUE);
749 alEventCallbackSOFT(EventCallback, this);
750 sleep_time = AudioBufferTotalTime;
753 /* Find a suitable format for OpenAL. */
754 mDstChanLayout = 0;
755 if(mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8 || mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8P)
757 mDstSampleFmt = AV_SAMPLE_FMT_U8;
758 mFrameSize = 1;
759 if(mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1 &&
760 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
761 (fmt=alGetEnumValue("AL_FORMAT_71CHN8")) != AL_NONE && fmt != -1)
763 mDstChanLayout = mCodecCtx->channel_layout;
764 mFrameSize *= 8;
765 mFormat = fmt;
767 if((mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1 ||
768 mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) &&
769 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
770 (fmt=alGetEnumValue("AL_FORMAT_51CHN8")) != AL_NONE && fmt != -1)
772 mDstChanLayout = mCodecCtx->channel_layout;
773 mFrameSize *= 6;
774 mFormat = fmt;
776 if(mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO)
778 mDstChanLayout = mCodecCtx->channel_layout;
779 mFrameSize *= 1;
780 mFormat = AL_FORMAT_MONO8;
782 if(!mDstChanLayout)
784 mDstChanLayout = AV_CH_LAYOUT_STEREO;
785 mFrameSize *= 2;
786 mFormat = AL_FORMAT_STEREO8;
789 if((mCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLT || mCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLTP) &&
790 alIsExtensionPresent("AL_EXT_FLOAT32"))
792 mDstSampleFmt = AV_SAMPLE_FMT_FLT;
793 mFrameSize = 4;
794 if(mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1 &&
795 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
796 (fmt=alGetEnumValue("AL_FORMAT_71CHN32")) != AL_NONE && fmt != -1)
798 mDstChanLayout = mCodecCtx->channel_layout;
799 mFrameSize *= 8;
800 mFormat = fmt;
802 if((mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1 ||
803 mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) &&
804 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
805 (fmt=alGetEnumValue("AL_FORMAT_51CHN32")) != AL_NONE && fmt != -1)
807 mDstChanLayout = mCodecCtx->channel_layout;
808 mFrameSize *= 6;
809 mFormat = fmt;
811 if(mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO)
813 mDstChanLayout = mCodecCtx->channel_layout;
814 mFrameSize *= 1;
815 mFormat = AL_FORMAT_MONO_FLOAT32;
817 if(!mDstChanLayout)
819 mDstChanLayout = AV_CH_LAYOUT_STEREO;
820 mFrameSize *= 2;
821 mFormat = AL_FORMAT_STEREO_FLOAT32;
824 if(!mDstChanLayout)
826 mDstSampleFmt = AV_SAMPLE_FMT_S16;
827 mFrameSize = 2;
828 if(mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1 &&
829 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
830 (fmt=alGetEnumValue("AL_FORMAT_71CHN16")) != AL_NONE && fmt != -1)
832 mDstChanLayout = mCodecCtx->channel_layout;
833 mFrameSize *= 8;
834 mFormat = fmt;
836 if((mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1 ||
837 mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1_BACK) &&
838 alIsExtensionPresent("AL_EXT_MCFORMATS") &&
839 (fmt=alGetEnumValue("AL_FORMAT_51CHN16")) != AL_NONE && fmt != -1)
841 mDstChanLayout = mCodecCtx->channel_layout;
842 mFrameSize *= 6;
843 mFormat = fmt;
845 if(mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO)
847 mDstChanLayout = mCodecCtx->channel_layout;
848 mFrameSize *= 1;
849 mFormat = AL_FORMAT_MONO16;
851 if(!mDstChanLayout)
853 mDstChanLayout = AV_CH_LAYOUT_STEREO;
854 mFrameSize *= 2;
855 mFormat = AL_FORMAT_STEREO16;
858 void *samples = nullptr;
859 ALsizei buffer_len = std::chrono::duration_cast<std::chrono::duration<int>>(
860 mCodecCtx->sample_rate * AudioBufferTime).count() * mFrameSize;
862 mSamples = NULL;
863 mSamplesMax = 0;
864 mSamplesPos = 0;
865 mSamplesLen = 0;
867 mDecodedFrame.reset(av_frame_alloc());
868 if(!mDecodedFrame)
870 std::cerr<< "Failed to allocate audio frame" <<std::endl;
871 goto finish;
874 mSwresCtx.reset(swr_alloc_set_opts(nullptr,
875 mDstChanLayout, mDstSampleFmt, mCodecCtx->sample_rate,
876 mCodecCtx->channel_layout ? mCodecCtx->channel_layout :
877 (uint64_t)av_get_default_channel_layout(mCodecCtx->channels),
878 mCodecCtx->sample_fmt, mCodecCtx->sample_rate,
879 0, nullptr
881 if(!mSwresCtx || swr_init(mSwresCtx.get()) != 0)
883 std::cerr<< "Failed to initialize audio converter" <<std::endl;
884 goto finish;
887 mBuffers.assign(AudioBufferTotalTime / AudioBufferTime, 0);
888 alGenBuffers(mBuffers.size(), mBuffers.data());
889 alGenSources(1, &mSource);
891 if(EnableDirectOut)
892 alSourcei(mSource, AL_DIRECT_CHANNELS_SOFT, AL_TRUE);
893 if(EnableWideStereo)
895 ALfloat angles[2] = { (ALfloat)(M_PI/3.0), (ALfloat)(-M_PI/3.0) };
896 alSourcefv(mSource, AL_STEREO_ANGLES, angles);
899 if(alGetError() != AL_NO_ERROR)
900 goto finish;
902 if(!alBufferStorageSOFT)
903 samples = av_malloc(buffer_len);
904 else
906 for(ALuint bufid : mBuffers)
907 alBufferStorageSOFT(bufid, mFormat, nullptr, buffer_len, mCodecCtx->sample_rate,
908 AL_MAP_WRITE_BIT_SOFT);
909 if(alGetError() != AL_NO_ERROR)
911 fprintf(stderr, "Failed to use mapped buffers\n");
912 samples = av_malloc(buffer_len);
916 while(alGetError() == AL_NO_ERROR && !mMovie.mQuit.load(std::memory_order_relaxed) &&
917 mConnected.test_and_set(std::memory_order_relaxed))
919 /* First remove any processed buffers. */
920 ALint processed;
921 alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);
922 while(processed > 0)
924 std::array<ALuint,4> bids;
925 alSourceUnqueueBuffers(mSource, std::min<ALsizei>(bids.size(), processed),
926 bids.data());
927 processed -= std::min<ALsizei>(bids.size(), processed);
930 /* Refill the buffer queue. */
931 ALint queued;
932 alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued);
933 while((ALuint)queued < mBuffers.size())
935 ALuint bufid = mBuffers[mBufferIdx];
937 uint8_t *ptr = reinterpret_cast<uint8_t*>(
938 samples ? samples : alMapBufferSOFT(bufid, 0, buffer_len, AL_MAP_WRITE_BIT_SOFT)
940 if(!ptr) break;
942 /* Read the next chunk of data, filling the buffer, and queue it on
943 * the source */
944 bool got_audio = readAudio(ptr, buffer_len);
945 if(!samples) alUnmapBufferSOFT(bufid);
946 if(!got_audio) break;
948 if(samples)
949 alBufferData(bufid, mFormat, samples, buffer_len, mCodecCtx->sample_rate);
951 alSourceQueueBuffers(mSource, 1, &bufid);
952 mBufferIdx = (mBufferIdx+1) % mBuffers.size();
953 ++queued;
955 if(queued == 0)
956 break;
958 /* Check that the source is playing. */
959 ALint state;
960 alGetSourcei(mSource, AL_SOURCE_STATE, &state);
961 if(state == AL_STOPPED)
963 /* AL_STOPPED means there was an underrun. Clear the buffer queue
964 * since this likely means we're late, and rewind the source to get
965 * it back into an AL_INITIAL state.
967 alSourceRewind(mSource);
968 alSourcei(mSource, AL_BUFFER, 0);
969 continue;
972 /* (re)start the source if needed, and wait for a buffer to finish */
973 if(state != AL_PLAYING && state != AL_PAUSED &&
974 mMovie.mPlaying.load(std::memory_order_relaxed))
975 startPlayback();
977 mSrcCond.wait_for(lock, sleep_time);
980 alSourceRewind(mSource);
981 alSourcei(mSource, AL_BUFFER, 0);
983 finish:
984 av_freep(&samples);
986 if(alEventControlSOFT)
988 alEventControlSOFT(types.size(), types.data(), AL_FALSE);
989 alEventCallbackSOFT(nullptr, nullptr);
992 return 0;
996 nanoseconds VideoState::getClock()
998 /* NOTE: This returns incorrect times while not playing. */
999 auto delta = get_avtime() - mCurrentPtsTime;
1000 return mCurrentPts + delta;
1003 bool VideoState::isBufferFilled()
1005 std::unique_lock<std::mutex> lock(mPictQMutex);
1006 return mPictQSize >= mPictQ.size();
1009 Uint32 SDLCALL VideoState::sdl_refresh_timer_cb(Uint32 /*interval*/, void *opaque)
1011 SDL_Event evt{};
1012 evt.user.type = FF_REFRESH_EVENT;
1013 evt.user.data1 = opaque;
1014 SDL_PushEvent(&evt);
1015 return 0; /* 0 means stop timer */
1018 /* Schedules an FF_REFRESH_EVENT event to occur in 'delay' ms. */
1019 void VideoState::schedRefresh(milliseconds delay)
1021 SDL_AddTimer(delay.count(), sdl_refresh_timer_cb, this);
1024 /* Called by VideoState::refreshTimer to display the next video frame. */
1025 void VideoState::display(SDL_Window *screen, SDL_Renderer *renderer)
1027 Picture *vp = &mPictQ[mPictQRead];
1029 if(!vp->mImage)
1030 return;
1032 float aspect_ratio;
1033 int win_w, win_h;
1034 int w, h, x, y;
1036 if(mCodecCtx->sample_aspect_ratio.num == 0)
1037 aspect_ratio = 0.0f;
1038 else
1040 aspect_ratio = av_q2d(mCodecCtx->sample_aspect_ratio) * mCodecCtx->width /
1041 mCodecCtx->height;
1043 if(aspect_ratio <= 0.0f)
1044 aspect_ratio = (float)mCodecCtx->width / (float)mCodecCtx->height;
1046 SDL_GetWindowSize(screen, &win_w, &win_h);
1047 h = win_h;
1048 w = ((int)rint(h * aspect_ratio) + 3) & ~3;
1049 if(w > win_w)
1051 w = win_w;
1052 h = ((int)rint(w / aspect_ratio) + 3) & ~3;
1054 x = (win_w - w) / 2;
1055 y = (win_h - h) / 2;
1057 SDL_Rect src_rect{ 0, 0, vp->mWidth, vp->mHeight };
1058 SDL_Rect dst_rect{ x, y, w, h };
1059 SDL_RenderCopy(renderer, vp->mImage, &src_rect, &dst_rect);
1060 SDL_RenderPresent(renderer);
1063 /* FF_REFRESH_EVENT handler called on the main thread where the SDL_Renderer
1064 * was created. It handles the display of the next decoded video frame (if not
1065 * falling behind), and sets up the timer for the following video frame.
1067 void VideoState::refreshTimer(SDL_Window *screen, SDL_Renderer *renderer)
1069 if(!mStream)
1071 if(mEOS)
1073 mFinalUpdate = true;
1074 std::unique_lock<std::mutex>(mPictQMutex).unlock();
1075 mPictQCond.notify_all();
1076 return;
1078 schedRefresh(milliseconds(100));
1079 return;
1081 if(!mMovie.mPlaying.load(std::memory_order_relaxed))
1083 schedRefresh(milliseconds(1));
1084 return;
1087 std::unique_lock<std::mutex> lock(mPictQMutex);
1088 retry:
1089 if(mPictQSize == 0)
1091 if(mEOS)
1092 mFinalUpdate = true;
1093 else
1094 schedRefresh(milliseconds(1));
1095 lock.unlock();
1096 mPictQCond.notify_all();
1097 return;
1100 Picture *vp = &mPictQ[mPictQRead];
1101 mCurrentPts = vp->mPts;
1102 mCurrentPtsTime = get_avtime();
1104 /* Get delay using the frame pts and the pts from last frame. */
1105 auto delay = vp->mPts - mFrameLastPts;
1106 if(delay <= seconds::zero() || delay >= seconds(1))
1108 /* If incorrect delay, use previous one. */
1109 delay = mFrameLastDelay;
1111 /* Save for next frame. */
1112 mFrameLastDelay = delay;
1113 mFrameLastPts = vp->mPts;
1115 /* Update delay to sync to clock if not master source. */
1116 if(mMovie.mAVSyncType != SyncMaster::Video)
1118 auto ref_clock = mMovie.getMasterClock();
1119 auto diff = vp->mPts - ref_clock;
1121 /* Skip or repeat the frame. Take delay into account. */
1122 auto sync_threshold = std::min<nanoseconds>(delay, VideoSyncThreshold);
1123 if(!(diff < AVNoSyncThreshold && diff > -AVNoSyncThreshold))
1125 if(diff <= -sync_threshold)
1126 delay = nanoseconds::zero();
1127 else if(diff >= sync_threshold)
1128 delay *= 2;
1132 mFrameTimer += delay;
1133 /* Compute the REAL delay. */
1134 auto actual_delay = mFrameTimer - get_avtime();
1135 if(!(actual_delay >= VideoSyncThreshold))
1137 /* We don't have time to handle this picture, just skip to the next one. */
1138 mPictQRead = (mPictQRead+1)%mPictQ.size();
1139 mPictQSize--;
1140 goto retry;
1142 schedRefresh(std::chrono::duration_cast<milliseconds>(actual_delay));
1144 /* Show the picture! */
1145 display(screen, renderer);
1147 /* Update queue for next picture. */
1148 mPictQRead = (mPictQRead+1)%mPictQ.size();
1149 mPictQSize--;
1150 lock.unlock();
1151 mPictQCond.notify_all();
1154 /* FF_UPDATE_EVENT handler, updates the picture's texture. It's called on the
1155 * main thread where the renderer was created.
1157 void VideoState::updatePicture(SDL_Window *screen, SDL_Renderer *renderer)
1159 Picture *vp = &mPictQ[mPictQWrite];
1160 bool fmt_updated = false;
1162 /* allocate or resize the buffer! */
1163 if(!vp->mImage || vp->mWidth != mCodecCtx->width || vp->mHeight != mCodecCtx->height)
1165 fmt_updated = true;
1166 if(vp->mImage)
1167 SDL_DestroyTexture(vp->mImage);
1168 vp->mImage = SDL_CreateTexture(
1169 renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING,
1170 mCodecCtx->coded_width, mCodecCtx->coded_height
1172 if(!vp->mImage)
1173 std::cerr<< "Failed to create YV12 texture!" <<std::endl;
1174 vp->mWidth = mCodecCtx->width;
1175 vp->mHeight = mCodecCtx->height;
1177 if(mFirstUpdate && vp->mWidth > 0 && vp->mHeight > 0)
1179 /* For the first update, set the window size to the video size. */
1180 mFirstUpdate = false;
1182 int w = vp->mWidth;
1183 int h = vp->mHeight;
1184 if(mCodecCtx->sample_aspect_ratio.den != 0)
1186 double aspect_ratio = av_q2d(mCodecCtx->sample_aspect_ratio);
1187 if(aspect_ratio >= 1.0)
1188 w = (int)(w*aspect_ratio + 0.5);
1189 else if(aspect_ratio > 0.0)
1190 h = (int)(h/aspect_ratio + 0.5);
1192 SDL_SetWindowSize(screen, w, h);
1196 if(vp->mImage)
1198 AVFrame *frame = mDecodedFrame.get();
1199 void *pixels = nullptr;
1200 int pitch = 0;
1202 if(mCodecCtx->pix_fmt == AV_PIX_FMT_YUV420P)
1203 SDL_UpdateYUVTexture(vp->mImage, nullptr,
1204 frame->data[0], frame->linesize[0],
1205 frame->data[1], frame->linesize[1],
1206 frame->data[2], frame->linesize[2]
1208 else if(SDL_LockTexture(vp->mImage, nullptr, &pixels, &pitch) != 0)
1209 std::cerr<< "Failed to lock texture" <<std::endl;
1210 else
1212 // Convert the image into YUV format that SDL uses
1213 int coded_w = mCodecCtx->coded_width;
1214 int coded_h = mCodecCtx->coded_height;
1215 int w = mCodecCtx->width;
1216 int h = mCodecCtx->height;
1217 if(!mSwscaleCtx || fmt_updated)
1219 mSwscaleCtx.reset(sws_getContext(
1220 w, h, mCodecCtx->pix_fmt,
1221 w, h, AV_PIX_FMT_YUV420P, 0,
1222 nullptr, nullptr, nullptr
1226 /* point pict at the queue */
1227 uint8_t *pict_data[3];
1228 pict_data[0] = reinterpret_cast<uint8_t*>(pixels);
1229 pict_data[1] = pict_data[0] + coded_w*coded_h;
1230 pict_data[2] = pict_data[1] + coded_w*coded_h/4;
1232 int pict_linesize[3];
1233 pict_linesize[0] = pitch;
1234 pict_linesize[1] = pitch / 2;
1235 pict_linesize[2] = pitch / 2;
1237 sws_scale(mSwscaleCtx.get(), (const uint8_t**)frame->data,
1238 frame->linesize, 0, h, pict_data, pict_linesize);
1239 SDL_UnlockTexture(vp->mImage);
1243 vp->mUpdated.store(true, std::memory_order_release);
1244 std::unique_lock<std::mutex>(mPictQMutex).unlock();
1245 mPictQCond.notify_one();
1248 int VideoState::queuePicture(nanoseconds pts)
1250 /* Wait until we have space for a new pic */
1251 std::unique_lock<std::mutex> lock(mPictQMutex);
1252 while(mPictQSize >= mPictQ.size() && !mMovie.mQuit.load(std::memory_order_relaxed))
1253 mPictQCond.wait(lock);
1254 lock.unlock();
1256 if(mMovie.mQuit.load(std::memory_order_relaxed))
1257 return -1;
1259 Picture *vp = &mPictQ[mPictQWrite];
1261 /* We have to create/update the picture in the main thread */
1262 vp->mUpdated.store(false, std::memory_order_relaxed);
1263 SDL_Event evt{};
1264 evt.user.type = FF_UPDATE_EVENT;
1265 evt.user.data1 = this;
1266 SDL_PushEvent(&evt);
1268 /* Wait until the picture is updated. */
1269 lock.lock();
1270 while(!vp->mUpdated.load(std::memory_order_relaxed))
1272 if(mMovie.mQuit.load(std::memory_order_relaxed))
1273 return -1;
1274 mPictQCond.wait(lock);
1276 if(mMovie.mQuit.load(std::memory_order_relaxed))
1277 return -1;
1278 vp->mPts = pts;
1280 mPictQWrite = (mPictQWrite+1)%mPictQ.size();
1281 mPictQSize++;
1282 lock.unlock();
1284 return 0;
1287 int VideoState::handler()
1289 mDecodedFrame.reset(av_frame_alloc());
1290 while(!mMovie.mQuit.load(std::memory_order_relaxed))
1292 std::unique_lock<std::mutex> lock(mQueueMtx);
1293 /* Decode video frame */
1294 int ret = avcodec_receive_frame(mCodecCtx.get(), mDecodedFrame.get());
1295 if(ret == AVERROR(EAGAIN))
1297 mMovie.mSendDataGood.clear(std::memory_order_relaxed);
1298 std::unique_lock<std::mutex>(mMovie.mSendMtx).unlock();
1299 mMovie.mSendCond.notify_one();
1300 do {
1301 mQueueCond.wait(lock);
1302 ret = avcodec_receive_frame(mCodecCtx.get(), mDecodedFrame.get());
1303 } while(ret == AVERROR(EAGAIN));
1305 lock.unlock();
1306 if(ret == AVERROR_EOF) break;
1307 mMovie.mSendDataGood.clear(std::memory_order_relaxed);
1308 mMovie.mSendCond.notify_one();
1309 if(ret < 0)
1311 std::cerr<< "Failed to decode frame: "<<ret <<std::endl;
1312 continue;
1315 /* Get the PTS for this frame. */
1316 nanoseconds pts;
1317 if(mDecodedFrame->best_effort_timestamp != AV_NOPTS_VALUE)
1318 mClock = std::chrono::duration_cast<nanoseconds>(
1319 seconds_d64(av_q2d(mStream->time_base)*mDecodedFrame->best_effort_timestamp)
1321 pts = mClock;
1323 /* Update the video clock to the next expected PTS. */
1324 auto frame_delay = av_q2d(mCodecCtx->time_base);
1325 frame_delay += mDecodedFrame->repeat_pict * (frame_delay * 0.5);
1326 mClock += std::chrono::duration_cast<nanoseconds>(seconds_d64(frame_delay));
1328 if(queuePicture(pts) < 0)
1329 break;
1330 av_frame_unref(mDecodedFrame.get());
1332 mEOS = true;
1334 std::unique_lock<std::mutex> lock(mPictQMutex);
1335 if(mMovie.mQuit.load(std::memory_order_relaxed))
1337 mPictQRead = 0;
1338 mPictQWrite = 0;
1339 mPictQSize = 0;
1341 while(!mFinalUpdate)
1342 mPictQCond.wait(lock);
1344 return 0;
1348 int MovieState::decode_interrupt_cb(void *ctx)
1350 return reinterpret_cast<MovieState*>(ctx)->mQuit.load(std::memory_order_relaxed);
1353 bool MovieState::prepare()
1355 AVIOContext *avioctx = nullptr;
1356 AVIOInterruptCB intcb = { decode_interrupt_cb, this };
1357 if(avio_open2(&avioctx, mFilename.c_str(), AVIO_FLAG_READ, &intcb, nullptr))
1359 std::cerr<< "Failed to open "<<mFilename <<std::endl;
1360 return false;
1362 mIOContext.reset(avioctx);
1364 /* Open movie file. If avformat_open_input fails it will automatically free
1365 * this context, so don't set it onto a smart pointer yet.
1367 AVFormatContext *fmtctx = avformat_alloc_context();
1368 fmtctx->pb = mIOContext.get();
1369 fmtctx->interrupt_callback = intcb;
1370 if(avformat_open_input(&fmtctx, mFilename.c_str(), nullptr, nullptr) != 0)
1372 std::cerr<< "Failed to open "<<mFilename <<std::endl;
1373 return false;
1375 mFormatCtx.reset(fmtctx);
1377 /* Retrieve stream information */
1378 if(avformat_find_stream_info(mFormatCtx.get(), nullptr) < 0)
1380 std::cerr<< mFilename<<": failed to find stream info" <<std::endl;
1381 return false;
1384 mVideo.schedRefresh(milliseconds(40));
1386 mParseThread = std::thread(std::mem_fn(&MovieState::parse_handler), this);
1387 return true;
1390 void MovieState::setTitle(SDL_Window *window)
1392 auto pos1 = mFilename.rfind('/');
1393 auto pos2 = mFilename.rfind('\\');
1394 auto fpos = ((pos1 == std::string::npos) ? pos2 :
1395 (pos2 == std::string::npos) ? pos1 :
1396 std::max(pos1, pos2)) + 1;
1397 SDL_SetWindowTitle(window, (mFilename.substr(fpos)+" - "+AppName).c_str());
1400 nanoseconds MovieState::getClock()
1402 if(!mPlaying.load(std::memory_order_relaxed))
1403 return nanoseconds::zero();
1404 return get_avtime() - mClockBase;
1407 nanoseconds MovieState::getMasterClock()
1409 if(mAVSyncType == SyncMaster::Video)
1410 return mVideo.getClock();
1411 if(mAVSyncType == SyncMaster::Audio)
1412 return mAudio.getClock();
1413 return getClock();
1416 nanoseconds MovieState::getDuration()
1417 { return std::chrono::duration<int64_t,std::ratio<1,AV_TIME_BASE>>(mFormatCtx->duration); }
1419 int MovieState::streamComponentOpen(int stream_index)
1421 if(stream_index < 0 || (unsigned int)stream_index >= mFormatCtx->nb_streams)
1422 return -1;
1424 /* Get a pointer to the codec context for the stream, and open the
1425 * associated codec.
1427 AVCodecCtxPtr avctx(avcodec_alloc_context3(nullptr));
1428 if(!avctx) return -1;
1430 if(avcodec_parameters_to_context(avctx.get(), mFormatCtx->streams[stream_index]->codecpar))
1431 return -1;
1433 AVCodec *codec = avcodec_find_decoder(avctx->codec_id);
1434 if(!codec || avcodec_open2(avctx.get(), codec, nullptr) < 0)
1436 std::cerr<< "Unsupported codec: "<<avcodec_get_name(avctx->codec_id)
1437 << " (0x"<<std::hex<<avctx->codec_id<<std::dec<<")" <<std::endl;
1438 return -1;
1441 /* Initialize and start the media type handler */
1442 switch(avctx->codec_type)
1444 case AVMEDIA_TYPE_AUDIO:
1445 mAudio.mStream = mFormatCtx->streams[stream_index];
1446 mAudio.mCodecCtx = std::move(avctx);
1448 mAudioThread = std::thread(std::mem_fn(&AudioState::handler), &mAudio);
1449 break;
1451 case AVMEDIA_TYPE_VIDEO:
1452 mVideo.mStream = mFormatCtx->streams[stream_index];
1453 mVideo.mCodecCtx = std::move(avctx);
1455 mVideoThread = std::thread(std::mem_fn(&VideoState::handler), &mVideo);
1456 break;
1458 default:
1459 return -1;
1462 return stream_index;
1465 int MovieState::parse_handler()
1467 int video_index = -1;
1468 int audio_index = -1;
1470 /* Dump information about file onto standard error */
1471 av_dump_format(mFormatCtx.get(), 0, mFilename.c_str(), 0);
1473 /* Find the first video and audio streams */
1474 for(unsigned int i = 0;i < mFormatCtx->nb_streams;i++)
1476 auto codecpar = mFormatCtx->streams[i]->codecpar;
1477 if(codecpar->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0)
1478 video_index = streamComponentOpen(i);
1479 else if(codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0)
1480 audio_index = streamComponentOpen(i);
1483 if(video_index < 0 && audio_index < 0)
1485 std::cerr<< mFilename<<": could not open codecs" <<std::endl;
1486 mQuit = true;
1489 PacketQueue audio_queue, video_queue;
1490 bool input_finished = false;
1492 /* Main packet reading/dispatching loop */
1493 while(!mQuit.load(std::memory_order_relaxed) && !input_finished)
1495 AVPacket packet;
1496 if(av_read_frame(mFormatCtx.get(), &packet) < 0)
1497 input_finished = true;
1498 else
1500 /* Copy the packet into the queue it's meant for. */
1501 if(packet.stream_index == video_index)
1502 video_queue.put(&packet);
1503 else if(packet.stream_index == audio_index)
1504 audio_queue.put(&packet);
1505 av_packet_unref(&packet);
1508 do {
1509 /* Send whatever queued packets we have. */
1510 if(!audio_queue.empty())
1512 std::unique_lock<std::mutex> lock(mAudio.mQueueMtx);
1513 int ret;
1514 do {
1515 ret = avcodec_send_packet(mAudio.mCodecCtx.get(), audio_queue.front());
1516 if(ret != AVERROR(EAGAIN)) audio_queue.pop();
1517 } while(ret != AVERROR(EAGAIN) && !audio_queue.empty());
1518 lock.unlock();
1519 mAudio.mQueueCond.notify_one();
1521 if(!video_queue.empty())
1523 std::unique_lock<std::mutex> lock(mVideo.mQueueMtx);
1524 int ret;
1525 do {
1526 ret = avcodec_send_packet(mVideo.mCodecCtx.get(), video_queue.front());
1527 if(ret != AVERROR(EAGAIN)) video_queue.pop();
1528 } while(ret != AVERROR(EAGAIN) && !video_queue.empty());
1529 lock.unlock();
1530 mVideo.mQueueCond.notify_one();
1532 /* If the queues are completely empty, or it's not full and there's
1533 * more input to read, go get more.
1535 size_t queue_size = audio_queue.totalSize() + video_queue.totalSize();
1536 if(queue_size == 0 || (queue_size < MAX_QUEUE_SIZE && !input_finished))
1537 break;
1539 if(!mPlaying.load(std::memory_order_relaxed))
1541 if((!mAudio.mCodecCtx || mAudio.isBufferFilled()) &&
1542 (!mVideo.mCodecCtx || mVideo.isBufferFilled()))
1544 /* Set the base time 50ms ahead of the current av time. */
1545 mClockBase = get_avtime() + milliseconds(50);
1546 mVideo.mCurrentPtsTime = mClockBase;
1547 mVideo.mFrameTimer = mVideo.mCurrentPtsTime;
1548 mAudio.startPlayback();
1549 mPlaying.store(std::memory_order_release);
1552 /* Nothing to send or get for now, wait a bit and try again. */
1553 { std::unique_lock<std::mutex> lock(mSendMtx);
1554 if(mSendDataGood.test_and_set(std::memory_order_relaxed))
1555 mSendCond.wait_for(lock, milliseconds(10));
1557 } while(!mQuit.load(std::memory_order_relaxed));
1559 /* Pass a null packet to finish the send buffers (the receive functions
1560 * will get AVERROR_EOF when emptied).
1562 if(mVideo.mCodecCtx)
1564 { std::lock_guard<std::mutex> lock(mVideo.mQueueMtx);
1565 avcodec_send_packet(mVideo.mCodecCtx.get(), nullptr);
1567 mVideo.mQueueCond.notify_one();
1569 if(mAudio.mCodecCtx)
1571 { std::lock_guard<std::mutex> lock(mAudio.mQueueMtx);
1572 avcodec_send_packet(mAudio.mCodecCtx.get(), nullptr);
1574 mAudio.mQueueCond.notify_one();
1576 video_queue.clear();
1577 audio_queue.clear();
1579 /* all done - wait for it */
1580 if(mVideoThread.joinable())
1581 mVideoThread.join();
1582 if(mAudioThread.joinable())
1583 mAudioThread.join();
1585 mVideo.mEOS = true;
1586 std::unique_lock<std::mutex> lock(mVideo.mPictQMutex);
1587 while(!mVideo.mFinalUpdate)
1588 mVideo.mPictQCond.wait(lock);
1589 lock.unlock();
1591 SDL_Event evt{};
1592 evt.user.type = FF_MOVIE_DONE_EVENT;
1593 SDL_PushEvent(&evt);
1595 return 0;
1599 // Helper class+method to print the time with human-readable formatting.
1600 struct PrettyTime {
1601 seconds mTime;
1603 inline std::ostream &operator<<(std::ostream &os, const PrettyTime &rhs)
1605 using hours = std::chrono::hours;
1606 using minutes = std::chrono::minutes;
1607 using std::chrono::duration_cast;
1609 seconds t = rhs.mTime;
1610 if(t.count() < 0)
1612 os << '-';
1613 t *= -1;
1616 // Only handle up to hour formatting
1617 if(t >= hours(1))
1618 os << duration_cast<hours>(t).count() << 'h' << std::setfill('0') << std::setw(2)
1619 << (duration_cast<minutes>(t).count() % 60) << 'm';
1620 else
1621 os << duration_cast<minutes>(t).count() << 'm' << std::setfill('0');
1622 os << std::setw(2) << (duration_cast<seconds>(t).count() % 60) << 's' << std::setw(0)
1623 << std::setfill(' ');
1624 return os;
1627 } // namespace
1630 int main(int argc, char *argv[])
1632 std::unique_ptr<MovieState> movState;
1634 if(argc < 2)
1636 std::cerr<< "Usage: "<<argv[0]<<" [-device <device name>] [-direct] <files...>" <<std::endl;
1637 return 1;
1639 /* Register all formats and codecs */
1640 av_register_all();
1641 /* Initialize networking protocols */
1642 avformat_network_init();
1644 if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER))
1646 std::cerr<< "Could not initialize SDL - <<"<<SDL_GetError() <<std::endl;
1647 return 1;
1650 /* Make a window to put our video */
1651 SDL_Window *screen = SDL_CreateWindow(AppName.c_str(), 0, 0, 640, 480, SDL_WINDOW_RESIZABLE);
1652 if(!screen)
1654 std::cerr<< "SDL: could not set video mode - exiting" <<std::endl;
1655 return 1;
1657 /* Make a renderer to handle the texture image surface and rendering. */
1658 Uint32 render_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC;
1659 SDL_Renderer *renderer = SDL_CreateRenderer(screen, -1, render_flags);
1660 if(renderer)
1662 SDL_RendererInfo rinf{};
1663 bool ok = false;
1665 /* Make sure the renderer supports IYUV textures. If not, fallback to a
1666 * software renderer. */
1667 if(SDL_GetRendererInfo(renderer, &rinf) == 0)
1669 for(Uint32 i = 0;!ok && i < rinf.num_texture_formats;i++)
1670 ok = (rinf.texture_formats[i] == SDL_PIXELFORMAT_IYUV);
1672 if(!ok)
1674 std::cerr<< "IYUV pixelformat textures not supported on renderer "<<rinf.name <<std::endl;
1675 SDL_DestroyRenderer(renderer);
1676 renderer = nullptr;
1679 if(!renderer)
1681 render_flags = SDL_RENDERER_SOFTWARE | SDL_RENDERER_PRESENTVSYNC;
1682 renderer = SDL_CreateRenderer(screen, -1, render_flags);
1684 if(!renderer)
1686 std::cerr<< "SDL: could not create renderer - exiting" <<std::endl;
1687 return 1;
1689 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
1690 SDL_RenderFillRect(renderer, nullptr);
1691 SDL_RenderPresent(renderer);
1693 /* Open an audio device */
1694 int fileidx = 1;
1695 ALCdevice *device = [argc,argv,&fileidx]() -> ALCdevice*
1697 ALCdevice *dev = NULL;
1698 if(argc > 3 && strcmp(argv[1], "-device") == 0)
1700 fileidx = 3;
1701 dev = alcOpenDevice(argv[2]);
1702 if(dev) return dev;
1703 std::cerr<< "Failed to open \""<<argv[2]<<"\" - trying default" <<std::endl;
1705 return alcOpenDevice(nullptr);
1706 }();
1707 ALCcontext *context = alcCreateContext(device, nullptr);
1708 if(!context || alcMakeContextCurrent(context) == ALC_FALSE)
1710 std::cerr<< "Failed to set up audio device" <<std::endl;
1711 if(context)
1712 alcDestroyContext(context);
1713 return 1;
1716 const ALCchar *name = nullptr;
1717 if(alcIsExtensionPresent(device, "ALC_ENUMERATE_ALL_EXT"))
1718 name = alcGetString(device, ALC_ALL_DEVICES_SPECIFIER);
1719 if(!name || alcGetError(device) != AL_NO_ERROR)
1720 name = alcGetString(device, ALC_DEVICE_SPECIFIER);
1721 std::cout<< "Opened \""<<name<<"\"" <<std::endl;
1723 if(alcIsExtensionPresent(device, "ALC_SOFT_device_clock"))
1725 std::cout<< "Found ALC_SOFT_device_clock" <<std::endl;
1726 alcGetInteger64vSOFT = reinterpret_cast<LPALCGETINTEGER64VSOFT>(
1727 alcGetProcAddress(device, "alcGetInteger64vSOFT")
1731 if(alIsExtensionPresent("AL_SOFT_source_latency"))
1733 std::cout<< "Found AL_SOFT_source_latency" <<std::endl;
1734 alGetSourcei64vSOFT = reinterpret_cast<LPALGETSOURCEI64VSOFT>(
1735 alGetProcAddress("alGetSourcei64vSOFT")
1738 if(alIsExtensionPresent("AL_SOFTX_map_buffer"))
1740 std::cout<< "Found AL_SOFT_map_buffer" <<std::endl;
1741 alBufferStorageSOFT = reinterpret_cast<LPALBUFFERSTORAGESOFT>(
1742 alGetProcAddress("alBufferStorageSOFT"));
1743 alMapBufferSOFT = reinterpret_cast<LPALMAPBUFFERSOFT>(
1744 alGetProcAddress("alMapBufferSOFT"));
1745 alUnmapBufferSOFT = reinterpret_cast<LPALUNMAPBUFFERSOFT>(
1746 alGetProcAddress("alUnmapBufferSOFT"));
1748 if(alIsExtensionPresent("AL_SOFTX_events"))
1750 std::cout<< "Found AL_SOFT_events" <<std::endl;
1751 alEventControlSOFT = reinterpret_cast<LPALEVENTCONTROLSOFT>(
1752 alGetProcAddress("alEventControlSOFT"));
1753 alEventCallbackSOFT = reinterpret_cast<LPALEVENTCALLBACKSOFT>(
1754 alGetProcAddress("alEventCallbackSOFT"));
1757 for(;fileidx < argc;++fileidx)
1759 if(strcmp(argv[fileidx], "-direct") == 0)
1761 if(!alIsExtensionPresent("AL_SOFT_direct_channels"))
1762 std::cerr<< "AL_SOFT_direct_channels not supported for direct output" <<std::endl;
1763 else
1765 std::cout<< "Found AL_SOFT_direct_channels" <<std::endl;
1766 EnableDirectOut = true;
1769 else if(strcmp(argv[fileidx], "-wide") == 0)
1771 if(!alIsExtensionPresent("AL_EXT_STEREO_ANGLES"))
1772 std::cerr<< "AL_EXT_STEREO_ANGLES not supported for wide stereo" <<std::endl;
1773 else
1775 std::cout<< "Found AL_EXT_STEREO_ANGLES" <<std::endl;
1776 EnableWideStereo = true;
1779 else
1780 break;
1783 while(fileidx < argc && !movState)
1785 movState = std::unique_ptr<MovieState>(new MovieState(argv[fileidx++]));
1786 if(!movState->prepare()) movState = nullptr;
1788 if(!movState)
1790 std::cerr<< "Could not start a video" <<std::endl;
1791 return 1;
1793 movState->setTitle(screen);
1795 /* Default to going to the next movie at the end of one. */
1796 enum class EomAction {
1797 Next, Quit
1798 } eom_action = EomAction::Next;
1799 seconds last_time(-1);
1800 SDL_Event event;
1801 while(1)
1803 int have_evt = SDL_WaitEventTimeout(&event, 10);
1805 auto cur_time = std::chrono::duration_cast<seconds>(movState->getMasterClock());
1806 if(cur_time != last_time)
1808 auto end_time = std::chrono::duration_cast<seconds>(movState->getDuration());
1809 std::cout<< "\r "<<PrettyTime{cur_time}<<" / "<<PrettyTime{end_time} <<std::flush;
1810 last_time = cur_time;
1812 if(!have_evt) continue;
1814 switch(event.type)
1816 case SDL_KEYDOWN:
1817 switch(event.key.keysym.sym)
1819 case SDLK_ESCAPE:
1820 movState->mQuit = true;
1821 eom_action = EomAction::Quit;
1822 break;
1824 case SDLK_n:
1825 movState->mQuit = true;
1826 eom_action = EomAction::Next;
1827 break;
1829 default:
1830 break;
1832 break;
1834 case SDL_WINDOWEVENT:
1835 switch(event.window.event)
1837 case SDL_WINDOWEVENT_RESIZED:
1838 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
1839 SDL_RenderFillRect(renderer, nullptr);
1840 break;
1842 default:
1843 break;
1845 break;
1847 case SDL_QUIT:
1848 movState->mQuit = true;
1849 eom_action = EomAction::Quit;
1850 break;
1852 case FF_UPDATE_EVENT:
1853 reinterpret_cast<VideoState*>(event.user.data1)->updatePicture(
1854 screen, renderer
1856 break;
1858 case FF_REFRESH_EVENT:
1859 reinterpret_cast<VideoState*>(event.user.data1)->refreshTimer(
1860 screen, renderer
1862 break;
1864 case FF_MOVIE_DONE_EVENT:
1865 std::cout<<'\n';
1866 last_time = seconds(-1);
1867 if(eom_action != EomAction::Quit)
1869 movState = nullptr;
1870 while(fileidx < argc && !movState)
1872 movState = std::unique_ptr<MovieState>(new MovieState(argv[fileidx++]));
1873 if(!movState->prepare()) movState = nullptr;
1875 if(movState)
1877 movState->setTitle(screen);
1878 break;
1882 /* Nothing more to play. Shut everything down and quit. */
1883 movState = nullptr;
1885 alcMakeContextCurrent(nullptr);
1886 alcDestroyContext(context);
1887 alcCloseDevice(device);
1889 SDL_DestroyRenderer(renderer);
1890 renderer = nullptr;
1891 SDL_DestroyWindow(screen);
1892 screen = nullptr;
1894 SDL_Quit();
1895 exit(0);
1897 default:
1898 break;
1902 std::cerr<< "SDL_WaitEvent error - "<<SDL_GetError() <<std::endl;
1903 return 1;