Bootstrap

Qt实现 基于ffmpeg拉流播放视频

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、完整代码

https://download.csdn.net/download/c_shell_python/13114076

;