上篇文章,介绍了如何将猫眼例程移植到鸿蒙IPC开发板,并实现了在VLC播放器中通过网络实时显示rtsp的视频流。
在实际的猫眼应用中,摄像头还应该单独拥有一个屏幕放在家中,便于查看门外情况。
本篇,就使用Qt设计一款rtsp显示客户端,并运行在我的一块Linux板子上,作为鸿蒙IPC开发板的显示客户端。
本篇实现rtsp视频流的播放,需要用到FFmpeg库,这里先简单介绍了FFmpeg。
FFmpeg是一套可以用来****记录、转换数字音频、视频,并能将其转化为流的开源计算机程序 。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。
FFmpeg在Linux平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括Windows、Mac OS X等。项目的名称来自MPEG视频编码标准,前面的"FF"代表"Fast Forward"。 FFmpeg编码库可以使用GPU加速。
ffmpeg有7个library,分别是:
RTSP视频流解码与播放主要代码
void VideoPlayer::run()
{
AVFormatContext *pFormatCtx; //音视频封装格式上下文结构体
AVCodecContext *pCodecCtx; //音视频编码器上下文结构体
AVCodec *pCodec; //音视频编码器结构体
AVFrame *pFrame; //存储一帧解码后像素数据
AVFrame *pFrameRGB;
AVPacket *pPacket; //存储一帧压缩编码数据
uint8_t *pOutBuffer;
static struct SwsContext *pImgConvertCtx;
avformat_network_init(); //初始化FFmpeg网络模块
av_register_all(); //初始化FFMPEG 调用了这个才能正常适用编码器和解码器
//Allocate an AVFormatContext.
pFormatCtx = avformat_alloc_context();
//AVDictionary
AVDictionary *avdic=nullptr;
char option_key[]="rtsp_transport";
char option_value[]="udp";
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);
if (avformat_open_input(&pFormatCtx, m_strFileName.toLocal8Bit().data(), nullptr, &avdic) != 0)
{
printf("can't open the file. \n");
return;
}
if (avformat_find_stream_info(pFormatCtx, nullptr) < 0)
{
printf("Could't find stream infomation.\n");
return;
}
//查找视频中包含的流信息,音频流先不处理
int videoStreamIdx = -1;
for (int i = 0; i < pFormatCtx->nb_streams; i++)
{
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoStreamIdx = i; //视频流
}
}
//查找解码器
qDebug("avcodec_find_decoder...");
pCodecCtx = pFormatCtx->streams[videoStreamIdx]->codec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == nullptr)
{
printf("Codec not found.\n");
return;
}
pCodecCtx->bit_rate =0; //初始化为0
pCodecCtx->time_base.num=1; //下面两行:一秒钟25帧
pCodecCtx->time_base.den=10;
pCodecCtx->frame_number=1; //每包一个视频帧
//打开解码器
if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0)
{
printf("Could not open codec.\n");
return;
}
//将解码后的YUV数据转换成RGB32
pImgConvertCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
AV_PIX_FMT_RGB32, SWS_BICUBIC, nullptr, nullptr, nullptr);
int numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);
pFrame = av_frame_alloc();
pFrameRGB = av_frame_alloc();
pOutBuffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
avpicture_fill((AVPicture *) pFrameRGB, pOutBuffer, AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height);
pPacket = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
int y_size = pCodecCtx->width * pCodecCtx->height;
av_new_packet(pPacket, y_size); //分配packet的数据
while (1)
{
if (av_read_frame(pFormatCtx, pPacket) < 0)
{
break; //这里认为视频读取完了
}
if (pPacket->stream_index == videoStreamIdx)
{
int got_picture;
int ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,pPacket);
if (ret < 0)
{
printf("decode error.\n");
return;
}
if (got_picture)
{
sws_scale(pImgConvertCtx, (uint8_t const * const *) pFrame->data, pFrame->linesize,
0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
//把这个RGB数据 用QImage加载
QImage tmpImg((uchar *)pOutBuffer, pCodecCtx->width, pCodecCtx->height, QImage::Format_RGB32);
QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
emit sig_GetOneFrame(image); //发送信号
}
}
av_free_packet(pPacket);
}
av_free(pOutBuffer);
av_free(pFrameRGB);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
}
解码完一帧图像后,发送信号,让显示线程来显示画面:
void MainWindow::slotGetOneFrame(QImage img)
{
m_Image = img;
update(); //调用update将执行paintEvent函数
}
void MainWindow::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
int showWidth = this->width() - 100;
int showHeight = this->height() - 50;
painter.setBrush(Qt::white);
painter.drawRect(0, 0, this->width(), this->height()); //先画成白色
//将图像按比例缩放
QImage img = m_Image.scaled(QSize(showWidth, showHeight),Qt::KeepAspectRatio);
img = img.mirrored(m_bHFlip, m_bVFlip);
int x = this->width() - img.width();
int y = this->height() - img.height();
x /= 2;
y /= 2;
painter.drawImage(QPoint(x-40,y+20),img); //画出图像
}
Qt程序是使用Qt Creator在Windows平台开发的,先在Windows平台运行看下实际效果。
在Windows中运行,rtsp视频流播放的比较流畅。
由于猫眼例程输出的视频流默认是上下倒置的,这里在QT客户端中增加了图像翻转功能,可实现画面的上下或左右翻转显示。
将源码拷贝到Ubuntu中进行交叉编译,再将编译的程序拷贝到Linux板子中进行运行(需要先在Linux板子上搭建Qt运行环境与FFmpeg运行环境)。
在嵌入式Linux板子中运行,rtsp视频流播放的比较卡,需要再对程序进行优化。
视频发布于硬声,还在审核中...
本篇介绍了使用Qt设计一款rtsp显示客户端,并运行在我的一块Linux板子上,作为鸿蒙IPC开发板的显示客户端,从而实现一个较为完整的猫眼功能。