FFmpeg+Qt实现摄像头(rtsp)实时显示
最近,由于项目需要实时显示摄像头的图像,就学习了FFmpeg的相关知识。其实,在之前利用VLC-QT库已经实现了摄像头的实时显示,但延迟时间太长(1.5秒),因此就转而学习了FFmpeg,最终的延迟时间为0.6s,如果哪位大神有减小延迟的方法,还望不吝赐教。现将自己的实现思路展示出来,以供需要的人参考。
这里主要将项目的主*分videoplayer.cpp文件列出。所有源代码可以去http://git.oschina.net/git-lizhen/FFmpeg-QT-rtsp下载。
1、程序流程图
程序流程图如图1所示,图中所示的主函数部分主要完成界面的构建、播放线程的建立以及参考坐标系的建立。图1展示了整个程序的运行流程。
2、运行界面
3、主要实现的功能
该界面主要实现四个功能:
(1)读取摄像头视频流(rtsp),并实时显示到主界面上(注:存在0.7s左右的延时,延时测试过程如图3所示);
(2)将rtsp视频流经过FFmpeg解码后的YUV数据转化成RGB32数据,提取其中的R(红色)通道,并在界面中的小窗显示(如图2中的左上角部分);
(3)将水下机器人的横滚角反映在界面上(如图2中,中间部分的虚线“十字”为水平和竖直参考位置;实线“十字”为横滚运动后机器人相对参考位置的角度变化,图示为模拟横滚角为10度的情形)。
(4)若程序掉电,再次上电后能够自动地建立连接。
4、程序源代码
/**
* 李震
* 我的码云:https://git.oschina.net/git-lizhen
* 我的CSDN博客:http://blog.csdn.net/weixin_38215395
* 联系:QQ1039953685
*/
#include "videoplayer.h"
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"
//2017.8.9---lizhen
#include "libavutil/time.h"
#include "libavutil/mathematics.h"
}
#include <stdio.h>
#include<iostream>
using namespace std;
VideoPlayer::VideoPlayer()
{
}
VideoPlayer::~VideoPlayer()
{
}
void VideoPlayer::startPlay()
{
///调用 QThread 的start函数 将会自动执行下面的run函数 run函数是一个新的线程
this->start();
}
void VideoPlayer::run()
{
//char *file_path = mFileName.toUtf8().data();
//cout<<file_path<<endl;
AVFormatContext *pFormatCtx;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
AVFrame *pFrame, *pFrameRGB;
AVPacket *packet;
uint8_t *out_buffer;
static struct SwsContext *img_convert_ctx;
int videoStream, i, numBytes;
int ret, got_picture;
avformat_network_init(); //初始化FFmpeg网络模块,2017.8.5---lizhen
av_register_all(); //初始化FFMPEG 调用了这个才能正常适用编码器和解码器
//Allocate an AVFormatContext.
pFormatCtx = avformat_alloc_context();
//2017.8.5---lizhen
AVDictionary *avdic=NULL;
char option_key[]="rtsp_transport";
char option_value[]="tcp";
av_dict_set(&avdic,option_key,option_value,0);
char option_key2[]="max_delay";
char option_value2[]="100";
av_dict_set(&avdic,option_key2,option_value2,0);
char url[]="rtsp://admin:aaa@qq.com:554/h264/ch1/main/av_stream";
if (avformat_open_input(&pFormatCtx, url, NULL, &avdic) != 0) {
printf("can't open the file. \n");
return;
}
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
printf("Could't find stream infomation.\n");
return;
}
videoStream = -1;
///循环查找视频中包含的流信息,直到找到视频类型的流
///便将其记录下来 保存到videoStream变量中
///这里我们现在只处理视频流 音频流先不管他
for (i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
}
}
///如果videoStream为-1 说明没有找到视频流
if (videoStream == -1) {
printf("Didn't find a video stream.\n");
return;
}
///查找解码器
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
//2017.8.9---lizhen
pCodecCtx->bit_rate =0; //初始化为0
pCodecCtx->time_base.num=1; //下面两行:一秒钟25帧
pCodecCtx->time_base.den=10;
pCodecCtx->frame_number=1; //每包一个视频帧
if (pCodec == NULL) {
printf("Codec not found.\n");
return;
}
///打开解码器
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
printf("Could not open codec.\n");
return;
}
pFrame = av_frame_alloc();
pFrameRGB = av_frame_alloc();
//2017.8.7---lizhen
//cout<<pCodecCtx->width<<endl;
///这里我们改成了 将解码后的YUV数据转换成RGB32
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
numBytes = avpicture_get_size(PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);
out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
avpicture_fill((AVPicture *) pFrameRGB, out_buffer, PIX_FMT_RGB32,
pCodecCtx->width, pCodecCtx->height);
int y_size = pCodecCtx->width * pCodecCtx->height;
packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
av_new_packet(packet, y_size); //分配packet的数据
//2017.8.1---lizhen
av_dump_format(pFormatCtx, 0, url, 0); //输出视频信息
while (1)
{
if (av_read_frame(pFormatCtx, packet) < 0)
{
break; //这里认为视频读取完了
}
if (packet->stream_index == videoStream) {
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);
if (ret < 0) {
printf("decode error.\n");
return;
}
if (got_picture) {
sws_scale(img_convert_ctx,
(uint8_t const * const *) pFrame->data,
pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
pFrameRGB->linesize);
//把这个RGB数据 用QImage加载
QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
emit sig_GetOneFrame(image); //发送信号
}
}
av_free_packet(packet); //释放资源,否则内存会一直上升
//2017.8.7---lizhen
msleep(0.05); //停一停 不然放的太快了
//2017.8.9---lizhen
/*int64_t start_time=av_gettime();
AVRational time_base=pFormatCtx->streams[videoStream]->time_base;
AVRational time_base_q={1,AV_TIME_BASE};
int64_t pts_time = av_rescale_q(packet->dts, time_base, time_base_q);
int64_t now_time = av_gettime() - start_time;
if (pts_time > now_time)
av_usleep(pts_time - now_time);*/
}
av_free(out_buffer);
av_free(pFrameRGB);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
}
程序版本说明:
Qt版本:Qt 5.9.1(MSVC 2015,32bit)
Qt Creator 4.3.1
FFmpeg 2.5.2
声明:本文的代码是在参考[1]代码的基础上修改的。
参考:
[1] 从零开始学习音视频编程技术(六) FFMPEG Qt视频播放器之显示图像:http://blog.yundiantech.com/?log=blog&id=9
推荐阅读
-
Html5
-
jsp实现页面实时显示当前系统时间的方法
-
Winform中实现实时颜色拾取器显示RGB和16进制颜色(附代码下载)
-
【CLI】利用Curl下载文件实时进度条显示的实现
-
php+jQuery ajax实现的实时刷新显示数据功能示例
-
OpenCV学习笔记(一):使用opencv读取摄像头并实时显示
-
EasyRTMP实现内网摄像头RTSP拉流转码RTMP推流到RTMP服务器EasyRTMP-Android视频采集流程是什么?
-
ffmpeg推流实现实时播放监控画面(rtsp to rtmp)
-
在python3下使用OpenCV 抓取摄像头图像并实时显示3色直方图
-
javascript实现页面的实时时钟显示示例