鸿蒙IPC开发板-为IPC添加RTSP显示客户端

举报
码农爱学习 发表于 2022/10/24 23:53:26 2022/10/24
【摘要】 本篇介绍了使用Qt设计一款rtsp显示客户端,并运行在我的一块Linux板子上,作为鸿蒙IPC开发板的显示客户端,从而实现一个较为完整的猫眼功能。

上篇文章,介绍了如何将猫眼例程移植到鸿蒙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视频流解码与播放主要代码

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); //画出图像
}

3 运行测试

3.1 Win平台测试

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

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

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

3.2 嵌入式Linux平台测试

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

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

4 总结

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

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。