[文章]【鸿蒙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视频流解码与播放主要代码

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视频流播放的比较流畅。

1.png

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

3.2 嵌入式Linux平台测试

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

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

2.png

3.3 演示视频

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

4 总结

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

回帖

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