#ifndef FFPLAYER_H #define FFPLAYER_H #include #include #include #if(QT_VERSION_MAJOR > 5) #include #else #include typedef QAudioOutput QAudioSink; typedef QAudioDeviceInfo QAudioDevice; #endif extern "C"{ #include #include #include #include } template class Queue { public: virtual ~Queue() { delAll(); } void add(T *node) { if(node==nullptr) { throw std::invalid_argument("node is null"); return; } if(last!=nullptr) last->next = node; else first = node; last = node; size++; } void remove() { if(first==nullptr) return; if(first==last) first = last = nullptr; else first = first->next; size--; } T *poll() { auto node = first; remove(); return node; } T *delGetNext() { delete poll(); return first; } T *removeGetNext() { remove(); return first; } void delAll() { while(first!=nullptr) delete poll(); } T *volatile first{0}; T *volatile last{0}; volatile int size{0}; }; template class ConQueue : public Queue { public: virtual ~ConQueue() { std::lock_guard lock(mtx); this->delAll(); } void lockAddNtf(T *node) { { std::lock_guard lock(mtx); this->add(node); } cv.notify_one(); } void lockRemove() { std::lock_guard lock(mtx); this->remove(); } T *lockPoll() { std::lock_guard lock(mtx); return this->poll(); } T *lockGet() { std::lock_guard lock(mtx); return this->first; } T *lockDelGetNext() { std::lock_guard lock(mtx); return this->delGetNext(); } T *lockRemoveGetNext() { std::lock_guard lock(mtx); return this->removeGetNext(); } void lockDelAll() { std::lock_guard lock(mtx); this->delAll(); } T *take(int ms = 0) { std::unique_lock lock(mtx); if(ms==0) while(this->first==nullptr) cv.wait(lock); else if(this->first==nullptr) cv.wait_for(lock, std::chrono::milliseconds(ms)); return this->poll(); } std::mutex mtx; std::condition_variable cv; }; struct Act { qint64 arg; Act *next; char act; }; struct Packet { AVPacket *pkt; Packet *next; uchar act; ~Packet() {av_packet_free(&pkt);} }; struct Img { QImage img; qint64 st; qint64 et; uint loop; Img *next; }; struct Pcm { uint8_t *data; int size; qint64 st; qint64 et; uint loop; Pcm *next; ~Pcm() {if(data!=nullptr) delete [] data;} }; class Type { public: inline bool isNull(){return idx <= -1;} int idx{-1}; int time_num{0}, time_den{0}; qint64 start{0}; AVCodecContext *ctx{0}; std::thread *thd{0}; std::atomic stop{false}; AVFrame *frm{av_frame_alloc()}; ConQueue pkts; uint deLoop{0}; ~Type() { av_frame_free(&frm); avcodec_free_context(&ctx); } }; class AVFmt { friend class FFPlayer; public: static const char Run{0}, Pause{1}, Quit{2}; AVFmt(QByteArray url); ~AVFmt(); void mainRead(); void mainViDecode(); void mainAuDecode(); bool isLoop{true}; protected: void checkAndUpd(FFPlayer *, qint64); void playAudio(Pcm *, FFPlayer *, qint64, qint64 lmt = INT64_MAX); void whiteAudio(Pcm *, FFPlayer *, bool, qint64 cur, qint64 lmt = INT64_MAX); QString errStr(int err) { if(av_strerror(err, err_str, err_size)==0) return QString::fromUtf8(err_str); return QString::number(err); } QString err; const static int err_size{255}; char err_str[err_size]{}; AVFormatContext *fmt_ctx{0}; std::thread *read_thd{0}; qint64 start{0}; Type vi, au; qint64 viLmt{0}; SwsContext *sws_ctx{0}; SwrContext *swr_ctx{0}; AVChannelLayout out_ch_layout; int sample_rate{0}, pcm_sample_size{0}; QAudioSink *sink{0}; QIODevice *sinkWriter{0}; int buf_size{0}; ConQueue acts; AVPacket *packet{av_packet_alloc()}; ConQueue vi_frms; ConQueue au_frms; qint64 start_epoch{0}; uint playLoop{0}; }; class FFPlayer : public QObject { friend class AVFmt; Q_OBJECT public: static const char Playing{0}, Paused{1}, Closed{2}; explicit FFPlayer(); ~FFPlayer(); inline void open(QByteArray url) { status = Paused; emit emOpen(url); } inline void close() {emit emClose();} inline void play() {emit emPlay();} inline void pause() {emit emPause();} inline void stop() {emit emStop();} inline void setVol(qreal vol) {emit emVol(vol);} inline void quit() {emit emQuit();} std::atomic status{Closed}; QImage img; qint64 dur{0}; int viSize{0}, auSize{0}; qint64 viCurTime{0}, auCurTime{0}; public slots: void updFrame(); signals: void emOpen(QByteArray url); void emClose(); void emPlay(); void emPause(); void emStop(); void emVol(qreal vol); void emUpd(QImage); void emError(QString err); void emQuit(); protected slots: void onOpen(QByteArray url); void onClose(); void onPlay(); void onPause(); void onStop(); void onVol(qreal vol); void onQuit(); protected: void timerEvent(QTimerEvent *event) override; void startAudioDaemon() { if(audioDaemonTimerId==0 && ! ctx->au.isNull()) audioDaemonTimerId = startTimer(20); } QThread *thread; AVFmt *ctx{0}; qint64 last_epoch{0}; int audioDaemonTimerId{0}; bool hasImg{false}; qreal mVol{1.0}; }; #endif // FFPLAYER_H