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

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

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


1、程序流程图

程序流程图如图1所示,图中所示的主函数部分主要完成界面的构建、播放线程的建立以及参考坐标系的建立。图1展示了整个程序的运行流程。
图1  程序流程图

2、运行界面

图2  运行界面

3、主要实现的功能

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

  • 16
    点赞
  • 169
    收藏
    觉得还不错? 一键收藏
  • 50
    评论
qt是一种跨平台的开发框架,ffmpeg是一套音视频编解码的库,rtsp是一种用于传输音视频流的协议。所以如果想要实现摄像头实时显示的功能,可以通过qt结合ffmpeg实现。 首先,我们需要使用ffmpeg获取每个摄像头rtsp流并解码。可以使用ffmpeg提供的函数来打开rtsp流并将其解码成原始的音视频数据。通过设置ffmpeg的参数,我们可以指定使用不同的摄像头,并可以同时从多个摄像头获取音视频数据。 接着,我们可以使用qt的图像显示控件来显示摄像头解码得到的视频帧。可以使用qt提供的QGraphicsView、QLabel等控件,将视频帧数据转换成qt能够识别的格式并在界面上实时显示。 为了实现摄像头实时显示,我们可以在qt中使用多线程来同时处理多个摄像头的数据。可以为每个摄像头开启一个线程,用于获取摄像头rtsp流并解码。然后将解码得到的视频帧数据通过线程间的通信机制传递给主线程,然后在主线程中更新界面并显示视频帧。 另外,为了提高实时性,我们可以对视频帧进行硬件加速处理,比如使用OpenGL进行渲染,这样可以减少CPU的使用率,提高视频的播放效果。 总结来说,实现qtffmpeg结合实现摄像头实时显示的功能,主要是通过ffmpeg获取rtsp流并解码,然后通过qt的图像显示控件实时显示解码得到的视频帧,在多线程中同时处理多个摄像头的数据,最终实现摄像头实时显示效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 50
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值