#ifndef QFFMPEG_H
#define QFFMPEG_H
/*
write by :lbwave@sina.com
QT+ffmpeg
1、不用SDL的理由
SDL是为游戏开发的,大量的依赖硬件加速。不用sdl是为了能方便的将程序移植到其他的平台 。
本人受条件限制未向其他系统移植。但由于没采用QT(ffmpeg)之外的其他第三方代码,相信
移植是个很小的问题。本人曾经做过arm920+qt+linux(fambuffer)的开发。
本程序仅用了Qwideg来显示,就是为了移植方便。ffmpeg用C写的可以向多种平台移植。
2、如何实现音频视频同步
本范例采用系统时钟作为主时钟,用音频时钟校正主时钟。
3、如何实现多趋缓冲
本范例采用多线程处理机制。
1、QFfmpeg :主要负责读取数据包,存入QList列表.压缩前的数据占用空间小。缓冲大小可设,按视频帧数和声卡缓冲大小决定
2、QAudioThread:音频解码
3、QVideoThread:视频解码
4、QFfPlay :播放 (没有用定时器,定时器误差太大)
4、本范例实现QT+ffmpeg播放器的基本功能,仅出于爱好开发,未进行系统排错,用于大家参考交流。
在开发期间参考了ffplay 。
5、实现在QT4.6 QT4.7forwindows版编译运行,内存无重大泄露。
*/
#include <QThreadPool>
#include <QRunnable>
#include <QWidget>
#include <QAudioDeviceInfo>
#include <QAudioOutput>
#include <QAudioFormat>
#include <QThread>
#include <QImage>
#include <QMutex>
#include <QTime>
#include <QPainter>
#include <QIODevice>
#include <QWaitCondition>
#include <QSemaphore>
#include <QReadWriteLock>
#include <QDebug>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>//注意要包含此头文件与qDebug函数相关
#include <stdint.h>
#include <QList>
extern "C"
{
//ffmpeg相关的头文件
#include <libavcodec/avcodec.h>
#include <libavutil/common.h>
#include <libavutil/avstring.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavcodec/opt.h>
#include <libavformat/avio.h>
//#include <libavdevice/avdevice.h>
}
//播放信息
#define DEFAULT_IMAGEFMT QImage::Format_RGB32
#define DEFAULT_FRAMEFMT PIX_FMT_RGB32
#define MAX_AUDIO_DIFFTIME 1000000 //音频时间差,最大值
#define AUDIOBUFFERSIZE (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 //音频缓冲大小
#define MAX_BUFFER 50
class QMasterClock //主时钟
{
public:
QMasterClock();
void adjusttime(int us);
qint64 getuscurtime();
void setstarttime(QTime t);
protected:
QReadWriteLock m;
QTime starttime;
};
class QDecodecThread : public QThread
{
Q_OBJECT
public:
QDecodecThread(AVFormatContext *f,AVCodecContext *c,QMasterClock *cl,int index,QObject *parent=0);
~QDecodecThread();
void run()=0;
void setstreamindex(const int index);
int getstreamindex() ;
int getusdtime() ;
void setusdtime(int dt);
void setisend(const bool b);
void lockdata();
void unlockdata();
int getcount() ;
void putpacket(AVPacket *p);
void free_packet(AVPacket *p);
AVPacket* getpacket();
qint64 getus(qint64 t);
QSemaphore sempfree;
protected:
AVCodecContext *actx; //解码器
AVFormatContext *formatctx;
int stream_index;
QMasterClock *masterclock;
QSemaphore semp;
bool isend;
QList <AVPacket*> pkts;
int usdtime;//时间差值,用于修正主时钟
QMutex mutex;
qint64 basetime;
};
class QAudioThread : public QDecodecThread
{
Q_OBJECT
public:
QAudioThread(AVFormatContext *f,AVCodecContext *c,QMasterClock *cl,int index,QObject *parent=0);
~QAudioThread();
QAudioOutput* getaudio();
void run();
void play();
int ffsampleratetoint(const SampleFormat sf);
qint64 caltime(const uint64_t pts);
public slots:
void notified();
void audiostate(QAudio::State state);
protected:
int writeaudio(char *data ,const int size);
QAudioOutput *audio;
QIODevice *audioIO;
};
class QVideoThread : public QDecodecThread
{
Q_OBJECT
public:
QVideoThread(AVFormatContext *f, AVCodecContext *c,QMasterClock *cl,int index,QObject *parent=0);
~QVideoThread();
qint64 getframebuffer(char *data);
int getwidth() const;
int getheight() const;
int getframesize();
void run();
protected:
SwsContext *m_img_convert_ctx;//图像转换设置
char *framebuffer;
int framebuffersize;
qint64 pts;
QWaitCondition videowait;
private:
AVFrame *yuvframe;
AVFrame *rgbframe;
};
class QSubtitleThread : public QDecodecThread
{
Q_OBJECT
public:
QSubtitleThread(AVFormatContext *f,AVCodecContext *c,QMasterClock *cl,int index,QObject *parent=0)
:QDecodecThread(f,c,cl,index,parent)
{}
void run(){}
};
class QFfWidget : public QWidget
{
Q_OBJECT
public:
explicit QFfWidget(QWidget *parent = 0);
~QFfWidget();
void setframe(QImage *f);
void lockframe();
void unlockframe();
private:
QImage *frame;
QMutex m;
protected:
void paintEvent(QPaintEvent *);
};
class QFfplay : public QThread
{
Q_OBJECT
public:
QFfplay(QVideoThread *v,QMasterClock *c, QObject *parent);
~QFfplay();
QWidget* getwidget();
protected:
void run();
QVideoThread *video;
QMasterClock *masterclock;
QImage *frame;
char *framebuffer;
QFfWidget *widget;
};
class QFfmpeg : public QThread
{
Q_OBJECT
public:
explicit QFfmpeg(QObject *parent);
//设置参数
void seturl(QString url);
bool open();
void close();
bool play();
void stop();
//判断视频是否结束
bool atEnd();
bool IsOpen();
QWidget* getwidget();
signals:
public slots:
protected:
void run();
private:
/****解码相关******************/
char m_url[255];
SwsContext *m_img_convert_ctx;//图像转换设置
AVFormatContext *m_pFormatctx; //视频流
QAudioThread *m_audiocodec; //音频解码器
QVideoThread *m_videocodec; //视频解码器
QSubtitleThread *m_subtitlecodec; //字幕解码器
QMasterClock masterclock;
QImage *m_frame;
uint8_t* framebuffer;//图象存储区 m_rgbframe m_frame 共享
QMutex m_mutex;
QFfplay *ffplay;
bool m_isopen;
};
#endif // QFFMPEG_H