源碼下載地址: http://download.csdn.net/detail/liukang325/9489952
我用的ffmpeg版本為 ffmpeg-2.1.8.tar.bz2
版本低了恐怕有些頭文件和API找不到。
在Linux下解壓后編譯,Linux下編譯很簡單,我這里生成的動態庫:
./configure –enable-shared
make
就能找到各個so動態庫文件。
移動位置后,記得手動鏈接 一下:
ln -s libavcodec.so.55 libavcodec.so ln -s libavdevice.so.55 libavdevice.so ln -s libavfilter.so.3 libavfilter.so ln -s libavformat.so.55 libavformat.so ln -s libavutil.so.52 libavutil.so ln -s libswscale.so.2 libswscale.so
QT pro文件中記得加入:
INCLUDEPATH += ffmpeg/include
// windows下用這幾個
win32: LIBS += ffmpeg/lib/libavcodec.dll.a ffmpeg/lib/libavfilter.dll.a ffmpeg/lib/libavformat.dll.a ffmpeg/lib/libswscale.dll.a ffmpeg/lib/libavutil.dll.a
// Linux下用這幾個
LIBS += -lavcodec -lavdevice -lavfilter -lavformat -lavutil -lswscale -L./ffmpeg/so
我這里對外提供三個接口:
void VideoStream::setUrl(QString url) { m_str_url = url; } void VideoStream::startStream() { videoStreamIndex=-1; av_register_all();//注冊庫中所有可用的文件格式和解碼器 avformat_network_init();//初始化網絡流格式,使用RTSP網絡流時必須先執行 pAVFormatContext = avformat_alloc_context();//申請一個AVFormatContext結構的內存,並進行簡單初始化 pAVFrame=av_frame_alloc(); if (this->Init()) { m_timerPlay->start(); } } void VideoStream::stopStream() { m_timerPlay->stop(); avformat_free_context(pAVFormatContext); av_frame_free(&pAVFrame); sws_freeContext(pSwsContext); }
里面與ffmpeg解碼相關的私有變量:
QMutex mutex;
AVPicture pAVPicture;
AVFormatContext *pAVFormatContext; AVCodecContext *pAVCodecContext; AVFrame *pAVFrame; SwsContext * pSwsContext; AVPacket pAVPacket;
在QT里,用一個QLabel的對象來顯示解碼后的視頻畫面:
connect(this,SIGNAL(GetImage(QImage)),this,SLOT(SetImageSlots(QImage)));
... void VideoStream::SetImageSlots(const QImage &image) { if (image.height()>0){ QPixmap pix = QPixmap::fromImage(image.scaled(m_i_w,m_i_h)); m_label->setPixmap(pix); } }
這里用的QTimer來進行一幀幀數據的解碼,也可以用一個線程比如QThread來進行解碼:
m_timerPlay = new QTimer;
m_timerPlay->setInterval(10); connect(m_timerPlay,SIGNAL(timeout()),this,SLOT(playSlots())); m_i_frameFinished = 0; ... bool VideoStream::Init() { if(m_str_url.isEmpty()) return false; //打開視頻流 int result=avformat_open_input(&pAVFormatContext, m_str_url.toStdString().c_str(),NULL,NULL); if (result<0){ qDebug()<<"打開視頻流失敗"; return false; } //獲取視頻流信息 result=avformat_find_stream_info(pAVFormatContext,NULL); if (result<0){ qDebug()<<"獲取視頻流信息失敗"; return false; } //獲取視頻流索引 videoStreamIndex = -1; for (uint i = 0; i < pAVFormatContext->nb_streams; i++) { if (pAVFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { videoStreamIndex = i; break; } } if (videoStreamIndex==-1){ qDebug()<<"獲取視頻流索引失敗"; return false; } //獲取視頻流的分辨率大小 pAVCodecContext = pAVFormatContext->streams[videoStreamIndex]->codec; videoWidth=pAVCodecContext->width; videoHeight=pAVCodecContext->height; avpicture_alloc(&pAVPicture,PIX_FMT_RGB24,videoWidth,videoHeight); AVCodec *pAVCodec; //獲取視頻流解碼器 pAVCodec = avcodec_find_decoder(pAVCodecContext->codec_id); pSwsContext = sws_getContext(videoWidth,videoHeight,PIX_FMT_YUV420P,videoWidth,videoHeight,PIX_FMT_RGB24,SWS_BICUBIC,0,0,0); //打開對應解碼器 result=avcodec_open2(pAVCodecContext,pAVCodec,NULL); if (result<0){ qDebug()<<"打開解碼器失敗"; return false; } qDebug()<<"初始化視頻流成功"; return true; } void VideoStream::playSlots() { //一幀一幀讀取視頻 if (av_read_frame(pAVFormatContext, &pAVPacket) >= 0){ if(pAVPacket.stream_index==videoStreamIndex){ qDebug()<<"開始解碼"<<QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"); avcodec_decode_video2(pAVCodecContext, pAVFrame, &m_i_frameFinished, &pAVPacket); if (m_i_frameFinished){ mutex.lock(); sws_scale(pSwsContext,(const uint8_t* const *)pAVFrame->data,pAVFrame->linesize,0,videoHeight,pAVPicture.data,pAVPicture.linesize); //發送獲取一幀圖像信號 QImage image(pAVPicture.data[0],videoWidth,videoHeight,QImage::Format_RGB888); emit GetImage(image); mutex.unlock(); } } } av_free_packet(&pAVPacket);//釋放資源,否則內存會一直上升 }
備注:
頭文件包含及注意事項
//必須加以下內容,否則編譯不能通過,為了兼容C和C99標准 #ifndef INT64_C #define INT64_C #define UINT64_C #endif //引入ffmpeg頭文件 extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavfilter/avfilter.h> #include <libswscale/swscale.h> #include <libavutil/frame.h> }
啟發:
setUrl(QString url);
這里的url 一般情況下是一個RTSP流的播放地址,如rtsp://192.168.1.123:554/stream1
但也可以是一個TCP流。
我這邊測試的是一個本地的socket流,設url地址為 http://127.0.0.1:5858
可直接解碼播放。