6.Qt5 FFmpeg本地摄像头采集预览实战


***【在线视频教程】***

好文章,来自【福优学苑@音视频+流媒体】

6.Qt5+FFmpeg本地摄像头采集预览实战

源码工程:S26_Test2


FFmpeg命令行处理摄像头

ffmpeg -list_devices true -f dshow -i dummy

命令执行后输出的结果如下(注:中文的设备会出现乱码的情况)。

列表显示设备的名称很重要,输入的时候都是使用“-f dshow -i video="{设备名}"”的方式。

获取摄像头数据(保存为本地文件或者发送实时流)

image.png



编码为H.264,保存为本地文件

下面这条命令,实现了从摄像头读取数据并编码为H.264,最后保存成mycamera.mkv。


ffmpeg -f dshow -i video="Lenovo EasyCamera" -vcodec libx264  mycamera001.flv


ffplay直接播放摄像头的数据

使用ffplay可以直接播放摄像头的数据,命令如下:

ffplay -f dshow -i video="Lenovo EasyCamera"

如果设备名称正确的话,会直接打开本机的摄像头,



FFmpeg+SDL2.0显示本地摄像头


/***********************

步骤是什么?

回忆:FFmpeg大体架构、流程

1.打开流协议:(本地文件、rtsp、rtmp、摄像头)

2.分析流信息:(video,audio,subtitle,......)

3.封装、解封装:

4.编码、解码:

5.渲染、推流:


---------》数据结构、API:

AVFormatContext:大管家婆

avformat_open_input

avformat_find_stream_info

av_read_packet: AVPacket, AVFrame


avcodec_send_packet

avcodec_receive_frame:AVCodecContext


sws_scale(,,,:::yuv422,420, yuyv, uyvy, yv12,yu12, nv12,nv21)

:SwsContext

***********************/



工程配置文件:.pro

:添加头文件目录、库文件


INCLUDEPATH += $$PWD/../FFmpeg431dev/include

INCLUDEPATH += $$PWD/../SDL2/include


LIBS    +=  $$PWD/../FFmpeg431dev/lib/avcodec.lib \

            $$PWD/../FFmpeg431dev/lib/avdevice.lib \

            $$PWD/../FFmpeg431dev/lib/avfilter.lib \

            $$PWD/../FFmpeg431dev/lib/avformat.lib \

            $$PWD/../FFmpeg431dev/lib/avutil.lib \

            $$PWD/../FFmpeg431dev/lib/postproc.lib \

            $$PWD/../FFmpeg431dev/lib/swresample.lib \

            $$PWD/../FFmpeg431dev/lib/swscale.lib


LIBS    +=  $$PWD/../SDL2/lib/x86/SDL2.lib

LIBS    +=  $$PWD/../SDL2/lib/x86/SDL2main.lib



一个坑:

undefined reference to `vtable for’”问题的原因及解决方法


QT中,类要支持信号与槽机制,需要继承自QObject并在头文件开头添加Q_OBJECT宏.

如果使用QtCreator创建类时,没有选择继承自QObject类或其子类,

而在创建后手工修改继承自QObject并手工添加Q_OBJECT宏,

则在编译时有可能会出现”undefined reference to `vtable for’…….”错误.


解决方法: 把新创建的类从项目中移除(注意:不要从磁盘上删除),

然后再添加进来,QtCreator就会重新解析此类,

再编译就不再会出现上述错误.


又一个坑2:

16:58:37: 程序异常结束。

16:58:37: The process was ended forcefully.

原因:运行时缺少dll



又一个坑3:

no documents matching "ui"

无法添加槽函数,导致信号发出后槽函数接收不到信号


原因1:未在类定义中添加Q_OBJECT

原因2:信号或槽指向的对象未实例化

原因3: 多处包含相同的"ui_XXX.h"头文件


查找/添加槽错误

在UI文件中右键转到槽发生错误,弹窗提示如下:


查找或添加槽错误

The class containing "UI::XXX" could mot be found in :\..\.XXX.cpp.Please verify the #include_directives.

可将.pro文件中,SOURCE+=后面的.cpp文件名全部删除,HEADERS+=后面的.h文件名全部删除重新构建后,再次右键项目文件选择“Add Existing Directory”重新加载文件并重新编译工程。

此时再次添加槽函数

弹窗提示如下:


查找/添加槽错误

No documents matching "ui_XXX.h" could be found .Rebuilding the project might help.

此时并不需要重新编译,直接重启Qt Creator即可。



添加头文件:.h


//必须加以下内容,否则编译不能通过,为了兼容C和C99标准
#ifndef INT64_C
#define INT64_C
#define UINT64_C
#endif
//引入ffmpeg头文件
#include <iostream>
extern "C"
{
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libswscale/swscale.h>
    #include <libavdevice/avdevice.h>
    #include <libavfilter/avfilter.h>
    #include <libavutil/imgutils.h>
    #include <SDL.h>
    #include <SDL_main.h>
};
#undef main
#include <QObject>
#include <qmutex.h>
#include <qstring.h>
本地摄像头的信息
printf("------------av_dump_format-------------------\n");
    av_dump_format(pAVFmtCtx, 0, NULL, 0);
    /*
    Input #0, dshow, from '(null)':
        Duration: N/A, bitrate: N/A
        Stream #0:0: Video: rawvideo (YUY2 / 0x32595559), yuyv422, 640x480, 30 fps, 30 tbr, 10000k tbn
    */
///// 1. open local camera
///// 2. find stream info
///// 3. open codec : decoder
///// 4. sws_scale :yuyv422--->yuv420p
///// 5. SDL2.0 
///// 6. demux , decode 
///// 7. render

// calculate the bytes of one image

av_frame_alloc

av_image_get_buffer_size

av_image_fill_arrays



问题:为什么第二次Start就无法显示窗口呢?

objFmpg.SetStopped(0);





FFmpeg+Qt显示摄像头


image.png



mutex.lock();

sws_scale(pSwsContext2,

(const uint8_t* const )pFrameYUV->data,

pFrameYUV->linesize,0,

videoHeight, pAVPicture.data, pAVPicture.linesize);


 //发送获取一帧图像信号

QImage image(pAVPicture.data[0],videoWidth,videoHeight,QImage::Format_RGB888);

emit GetImage(image);

mutex.unlock();



为什么不显示呢? 

connect:信号、槽,绑定


为什么窗口可以放大,不能缩小呢?




///作业:请大家自行修改下边这段代码,改造成不用SDL

    //一帧一帧读取视频

//    int frameFinished=0;

//    while (true){

//        if (av_read_frame(pFormatCtx, packet) >= 0){

//            if(pAVPacket.stream_index == videoStreamIndex){

//                qDebug()<<"开始解码"<<QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");

//                decode(pCodecCtx, packet, pFrame, pFrameYUV, img_convert_ctx/*, fp_yu*/);

//                /////avcodec_decode_video2(pAVCodecContext, pAVFrame, &frameFinished, &pAVPacket);

//                mutex.lock();

//                sws_scale(pSwsContext2,(const uint8_t* const *)pFrameYUV->data,pFrameYUV->linesize,0,videoHeight,pAVPicture.data,pAVPicture.linesize);

//                //发送获取一帧图像信号

//                QImage image(pAVPicture.data[0],videoWidth,videoHeight,QImage::Format_RGB888);

//                emit GetImage(image);

//                mutex.unlock();



//                /// -re  : 帧率

//                QThread::msleep(33);

//            }

//        }

//        av_free_packet(packet);//释放资源,否则内存会一直上升

//    }


好文章,来自【福优学苑@音视频+流媒体】
***【在线视频教程】***