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

5 篇文章 1 订阅
3 篇文章 1 订阅

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

  • 3
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Qt中使用FFmpeg进行拉流和保存视频,你可以按照以下步骤进行操作: 1. 首先,确保已经在Qt项目中集成了FFmpeg库。你可以通过在.pro文件中添加以下行来链接FFmpeg库: ``` LIBS += -lavformat -lavcodec -lavutil ``` 2. 创建一个Qt窗口应用程序,并在需要的地方包含FFmpeg的头文件: ```cpp extern "C" { #include <libavformat/avformat.h> } #include <QApplication> #include <QTimer> #include <QDebug> ``` 3. 在需要拉流和保存视频的地方,编写以下代码: ```cpp // 定义视频流地址 #define INPUT_URL "your_input_url" int main(int argc, char *argv[]) { QApplication a(argc, argv); // 初始化FFmpeg avformat_network_init(); AVFormatContext *inputFormatContext = nullptr; AVPacket packet; // 打开视频流 if (avformat_open_input(&inputFormatContext, INPUT_URL, nullptr, nullptr) != 0) { qDebug() << "Failed to open input stream"; return -1; } // 检索流信息 if (avformat_find_stream_info(inputFormatContext, nullptr) < 0) { qDebug() << "Failed to retrieve stream information"; return -1; } // 寻找视频流 int videoStreamIndex = -1; for (int i = 0; i < inputFormatContext->nb_streams; ++i) { if (inputFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStreamIndex = i; break; } } if (videoStreamIndex == -1) { qDebug() << "Failed to find video stream"; return -1; } // 打开视频输出文件 AVFormatContext *outputFormatContext = nullptr; if (avformat_alloc_output_context2(&outputFormatContext, nullptr, nullptr, "output.mp4") < 0) { qDebug() << "Failed to allocate output format context"; return -1; } AVStream *videoStream = avformat_new_stream(outputFormatContext, nullptr); if (!videoStream) { qDebug() << "Failed to create output stream"; return -1; } // 复制视频流参数 avcodec_parameters_copy(videoStream->codecpar, inputFormatContext->streams[videoStreamIndex]->codecpar); // 打开输出文件 if (!(outputFormatContext->oformat->flags & AVFMT_NOFILE)) { if (avio_open(&outputFormatContext->pb, "output.mp4", AVIO_FLAG_WRITE) < 0) { qDebug() << "Failed to open output file"; return -1; } } // 写入头部信息 if (avformat_write_header(outputFormatContext, nullptr) < 0) { qDebug() << "Failed to write header"; return -1; } // 读取并保存视频帧 while (av_read_frame(inputFormatContext, &packet) >= 0) { if (packet.stream_index == videoStreamIndex) { av_write_frame(outputFormatContext, &packet); } av_packet_unref(&packet); } // 写入尾部信息 av_write_trailer(outputFormatContext); // 释放资源 avformat_close_input(&inputFormatContext); if (outputFormatContext && !(outputFormatContext->oformat->flags & AVFMT_NOFILE)) avio_closep(&outputFormatContext->pb); avformat_free_context(outputFormatContext); return a.exec(); } ``` 请确保将`your_input_url`替换为你的视频流地址,并根据需要进行其他自定义设置。此代码将从输入视频流中读取帧数据,并将其保存到名为`output.mp4`的文件中。 以上是一个简单的示例,你可以根据实际需求进行更详细的配置和错误处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值