1、前言
ffmpeg作为开源库,具备跨平台性,被广泛使用于各大视频软件和网站,在视音频开发中占有极其重要的地位。
Qt同样支持跨平台,因此结合qt+ffmpeg制作跨平台视频播放器是比较合适的做法。
2、思路
目前有一个界面类和解码类,其中界面类负责视频的显示工作,解码类的解码过程运行在子线程中,避免解码在主线程中进行,影响界面播放效果。二者的信息交互通过qt的信号槽关联,视频的绘制则采用QPainter绘制。
3、采用技术如下
1、QThread线程类
2、QPainter绘制类
3、FFmpeg主要接口
4、界面效果
5、主要代码
1、ffmpge初始化接口
int GetVideoData::InitFFmpeg(const char *filename)
{
av_register_all();
avformat_network_init();
_pFormatCtx = avformat_alloc_context();
if (avformat_open_input(&_pFormatCtx, filename, NULL, NULL) != 0)
{
qDebug() << "avformat_open_input failed";
return -1;
}
if (avformat_find_stream_info(_pFormatCtx, NULL) < 0)
{
qDebug() << "avformat_find_stream_info failed";
return -1;
}
//视频
_videoIndex = -1;
for (int i = 0; i < _pFormatCtx->nb_streams; ++i)
{
if (_pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
_videoIndex = i;
break;
}
}
if (_videoIndex == -1)
{
qDebug() << "find video stream failed";
return -1;
}
_pCodecCtx = _pFormatCtx->streams[_videoIndex]->codec;
_pCodec = avcodec_find_decoder(_pCodecCtx->codec_id);
if (_pCodec == NULL)
{
qDebug() << "find_decoder failed !";
return -1;
}
if (avcodec_open2(_pCodecCtx, _pCodec, NULL) < 0)
{
qDebug() << "open2 decoder failed";
return -1;
}
//音频
_audioIndex = -1;
for (int i = 0; i < _pFormatCtx->nb_streams; ++i)
{
if (_pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
_audioIndex = i;
break;
}
}
if (_audioIndex == -1) {
qDebug() << "find audio stream failed";
return -1;
}
_aCodecCtx = _pFormatCtx->streams[_audioIndex]->codec;
_pAvFrame = av_frame_alloc();
_pFrameRGB32 = av_frame_alloc(); //存储解码后转换的RGB数据
//保存RGB32
int size = av_image_get_buffer_size(AV_PIX_FMT_RGB32, _pCodecCtx->width, _pCodecCtx->height, 1);
_out_buffer = (uint8_t *)av_malloc(size);
av_image_fill_arrays(_pFrameRGB32->data, _pFrameRGB32->linesize,_out_buffer,
AV_PIX_FMT_RGB32, _pCodecCtx->width, _pCodecCtx->height, 1);
_packet = (AVPacket*)malloc(sizeof(AVPacket));
qDebug() << "***********视频信息**********";
av_dump_format(_pFormatCtx, 0, filename, 0);
qDebug() << "*****************************";
return 0;
}
2、ffmpeg解码接口
void GetVideoData::getRgb32Data()
{
struct SwsContext *img_convert_ctx;
//设置sws_scale转换格式为AV_PIX_FMT_RGB32
img_convert_ctx = sws_getContext(_pCodecCtx->width, _pCodecCtx->height, _pCodecCtx->pix_fmt,
_pCodecCtx->width, _pCodecCtx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
int ret;
int got_picture_res;
while (1)
{
if (_stop)
{
continue;
}
if (av_read_frame(_pFormatCtx, _packet) >= 0)
{
if (_videoIndex == _packet->stream_index)
{
if (avcodec_decode_video2(_pCodecCtx, _pAvFrame, &got_picture_res, _packet) < 0)
{
qDebug() << "decode error";
return;
}
AVStream *avStream = _pFormatCtx->streams[_packet->stream_index];
_frame_rate = -1;
if (avStream->avg_frame_rate.den)
_frame_rate = avStream->avg_frame_rate.num / avStream->avg_frame_rate.den;
if (got_picture_res)
{
sws_scale(img_convert_ctx, (const uint8_t* const*)_pAvFrame->data, _pAvFrame->linesize, 0,
_pCodecCtx->height, _pFrameRGB32->data, _pFrameRGB32->linesize);
emit sigUpdate((uchar*)_pFrameRGB32->data[0], _pCodecCtx->width, _pCodecCtx->height);
QThread::msleep(40);
}
}
av_free_packet(_packet);
}
else
break;
}
}
3、qt界面绘制接口
void IQtFFmpeg::showVideo()
{
QPainter painter;
painter.begin(this->ui.display_video);
if (!_image.isNull())
{
painter.drawImage(0, 0, _image);
}
painter.end();
}
6、完整代码