[文章]【鸿蒙IPC开发板开发板体验】5-为IPC添加RTSP显示客户端

阅读量 0
1
3

上篇文章,介绍了如何将猫眼例程移植到鸿蒙IPC开发板,并实现了在VLC播放器中通过网络实时显示rtsp的视频流。

在实际的猫眼应用中,摄像头还应该单独拥有一个屏幕放在家中,便于查看门外情况。

本篇,就使用Qt设计一款rtsp显示客户端,并运行在我的一块Linux板子上,作为鸿蒙IPC开发板的显示客户端。

1 FFmpeg介绍

本篇实现rtsp视频流的播放,需要用到FFmpeg库,这里先简单介绍了FFmpeg。

FFmpeg是一套可以用来****记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。

FFmpeg在Linux平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括Windows、Mac OS X等。项目的名称来自MPEG视频编码标准,前面的"FF"代表"Fast Forward"。 FFmpeg编码库可以使用GPU加速。

ffmpeg的7个库

ffmpeg有7个library,分别是:

  • libavutil:一个实用的工具库用于辅助可移植的多媒体编程。它包含安全的可移植的字符串函数,随机数生成器,数据结构,附加的数学函数,密码学和多媒体相关功能(例如像素和样本格式的枚举)。它不是 libavcodec 和 libavformat 都需要的代码库。
  • libswscale:执行高度优化的图像缩放以及色彩空间和像素格式转换操作。
  • libswresample:执行高度优化的音频重采样,重矩阵化和样本格式转换操作。
  • libavcodec:提供了一个通用的编码/解码框架,并且包含用于音频、视频个字幕流的多个编解器和解码器。
  • libavformat:为音频、视频和字幕流的复用和解复用(muxing and demuxing)提供了一个通用框架。它包含多个用于媒体容器格式的多个复用器和解复用器。
  • libavdevice:提供了一个通用框架,用于从许多常见的多媒体输入/输出设备进行抓取和渲染,并支持多种输入和输出设备,包括 Video4Linux2、VfW、DShow 和 ALSA。
  • libavfilter:提供了一个通用的音频/视频过滤框架,其中包含多个过滤器、源和接收器。

2 程序设计

RTSP视频流解码与播放主要代码

voidVideoPlayer::run() { AVFormatContext *pFormatCtx;//音视频封装格式上下文结构体AVCodecContext *pCodecCtx;//音视频编码器上下文结构体AVCodec *pCodec;//音视频编码器结构体AVFrame *pFrame;//存储一帧解码后像素数据AVFrame *pFrameRGB; AVPacket *pPacket;//存储一帧压缩编码数据uint8_t *pOutBuffer;staticstruct SwsContext *pImgConvertCtx; avformat_network_init();//初始化FFmpeg网络模块av_register_all();//初始化FFMPEG 调用了这个才能正常适用编码器和解码器//Allocate an AVFormatContext.pFormatCtx = avformat_alloc_context();//AVDictionaryAVDictionary *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; }//查找视频中包含的流信息,音频流先不处理intvideoStreamIdx = -1;for(inti =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;//初始化为0pCodecCtx->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数据转换成RGB32pImgConvertCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, nullptr, nullptr, nullptr);intnumBytes = 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));//分配一个packetinty_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) {intgot_picture;intret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,pPacket);if(ret <0) { printf("decode error.\n");return; }if(got_picture) { sws_scale(pImgConvertCtx, (uint8_tconst*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); }

解码完一帧图像后,发送信号,让显示线程来显示画面:

voidMainWindow::slotGetOneFrame(QImage img) { m_Image = img; update();//调用update将执行paintEvent函数}voidMainWindow::paintEvent(QPaintEvent *event) { QPainter painter(this);intshowWidth = this->width() -100;intshowHeight = 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);intx = this->width() - img.width();inty = this->height() - img.height(); x /=2; y /=2; painter.drawImage(QPoint(x-40,y+20),img);//画出图像}

3 运行测试

3.1 Win平台测试

Qt程序是使用Qt Creator在Windows平台开发的,先在Windows平台运行看下实际效果。

在Windows中运行,rtsp视频流播放的比较流畅。

1.png

由于猫眼例程输出的视频流默认是上下倒置的,这里在QT客户端中增加了图像翻转功能,可实现画面的上下或左右翻转显示。

3.2 嵌入式Linux平台测试

将源码拷贝到Ubuntu中进行交叉编译,再将编译的程序拷贝到Linux板子中进行运行(需要先在Linux板子上搭建Qt运行环境与FFmpeg运行环境)。

在嵌入式Linux板子中运行,rtsp视频流播放的比较卡,需要再对程序进行优化。

2.png

3.3 演示视频

视频发布于硬声,还在审核中...

4 总结

本篇介绍了使用Qt设计一款rtsp显示客户端,并运行在我的一块Linux板子上,作为鸿蒙IPC开发板的显示客户端,从而实现一个较为完整的猫眼功能。

回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表德赢Vwin官网 网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友