qt/LedOK/ffplayer.h
2023-04-21 11:06:47 +08:00

267 lines
6.3 KiB
C++

#ifndef FFPLAYER_H
#define FFPLAYER_H
#include <condition_variable>
#include <thread>
#include <QImage>
#if(QT_VERSION_MAJOR > 5)
#include <QAudioSink>
#else
#include <QAudioOutput>
typedef QAudioOutput QAudioSink;
typedef QAudioDeviceInfo QAudioDevice;
#endif
extern "C"{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
}
template<class T>
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 T>
class ConQueue : public Queue<T> {
public:
virtual ~ConQueue() {
std::lock_guard<std::mutex> lock(mtx);
this->delAll();
}
void lockAddNtf(T *node) {
{
std::lock_guard<std::mutex> lock(mtx);
this->add(node);
}
cv.notify_one();
}
void lockRemove() {
std::lock_guard<std::mutex> lock(mtx);
this->remove();
}
T *lockPoll() {
std::lock_guard<std::mutex> lock(mtx);
return this->poll();
}
T *lockGet() {
std::lock_guard<std::mutex> lock(mtx);
return this->first;
}
T *lockDelGetNext() {
std::lock_guard<std::mutex> lock(mtx);
return this->delGetNext();
}
T *lockRemoveGetNext() {
std::lock_guard<std::mutex> lock(mtx);
return this->removeGetNext();
}
void lockDelAll() {
std::lock_guard<std::mutex> lock(mtx);
this->delAll();
}
T *take(int ms = 0) {
std::unique_lock<std::mutex> 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<bool> stop{false};
AVFrame *frm{av_frame_alloc()};
ConQueue<Packet> pkts;
uint deLoop{0};
~Type() {
av_frame_free(&frm);
avcodec_free_context(&ctx);
}
};
class FFPlayer;
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, errbuf, AV_ERROR_MAX_STRING_SIZE)==0) return QString::fromUtf8(errbuf);
return QString::number(err);
}
QString err;
char errbuf[AV_ERROR_MAX_STRING_SIZE]{};
AVFormatContext *fmt_ctx{0};
std::thread *read_thd{0};
qint64 start{0};
Type vi, au;
qint64 frmDur{0};
qint64 viLmt{0};
SwsContext *sws_ctx{0};
int dstStride[4]{0};
SwrContext *swr_ctx{0};
AVChannelLayout out_ch_layout
#if(QT_VERSION_MAJOR > 5)
AV_CHANNEL_LAYOUT_STEREO
#endif
;
int sample_rate{0}, pcm_sample_size{0};
QAudioSink *sink{0};
QIODevice *sinkWriter{0};
int buf_size{0};
ConQueue<Act> acts;
AVPacket *packet{av_packet_alloc()};
ConQueue<Img> vi_frms;
ConQueue<Pcm> 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<char> 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