源码下载地址: 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()<<"开始解码"<.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
#include
#include
#include
#include
}
启发:
setUrl(QString url);
这里的url 一般情况下是一个RTSP流的播放地址,如rtsp://192.168.1.123:554/stream1
但也可以是一个TCP流。
我这边测试的是一个本地的socket流,设url地址为 http://127.0.0.1:5858
可直接解码播放。