欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

FFmpeg+Qt实现摄像头(rtsp)实时显示

程序员文章站 2022-07-06 20:20:45
...

最近,由于项目需要实时显示摄像头的图像,就学习了FFmpeg的相关知识。其实,在之前利用VLC-QT库已经实现了摄像头的实时显示,但延迟时间太长(1.5秒),因此就转而学习了FFmpeg,最终的延迟时间为0.6s,如果哪位大神有减小延迟的方法,还望不吝赐教。现将自己的实现思路展示出来,以供需要的人参考。

这里主要将项目的主*分videoplayer.cpp文件列出。所有源代码可以去http://git.oschina.net/git-lizhen/FFmpeg-QT-rtsp下载。


1、程序流程图

程序流程图如图1所示,图中所示的主函数部分主要完成界面的构建、播放线程的建立以及参考坐标系的建立。图1展示了整个程序的运行流程。
FFmpeg+Qt实现摄像头(rtsp)实时显示

2、运行界面

FFmpeg+Qt实现摄像头(rtsp)实时显示

3、主要实现的功能

该界面主要实现四个功能:
(1)读取摄像头视频流(rtsp),并实时显示到主界面上(注:存在0.7s左右的延时,延时测试过程如图3所示);
FFmpeg+Qt实现摄像头(rtsp)实时显示
(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