FFmpeg的简单应用demo——读取摄像头数据并显示(Qt5.9)

一、看前要先了解FFmpeg和Qt的用法

从笔记直接复制粘贴过来的,被吐槽了~_~(CSDN的文本编辑器坑有点多。。。),整理一下

这只是个Demo、不要想着直接复制粘贴就能跑起来、代码仅供参考、多研究!程序员呢 !(!_!)!

本人新手小白,描述有误还请各位大神评论指点一二

雷神传送门:https://blog.csdn.net/leixiaohua1020/article

 

二、废话不多说直接上代码

1.创建一个跑线程的类,处理音视频编解码并显示这些都必须在线程中进行,不然会卡主界面GUI线程

class Worker:public QObject
{
    Q_OBJECT

​public:
    Worker();
    ~Worker();​
signals:

    void sig_GetOneFrame(QImage);

protected slots:
​
    void ffmpeg_test();
​
private:
​
};

2.利用FFmpeg提供的接口打开摄像头并获取摄像头流数据,处理并发

void Worker::ffmpeg_test()
{​
    AVFormatContext *pFormatCtx;//FFMPEG所有的操作都要通过这个AVFormatContext来进行
    AVInputFormat *ifmt;//使用libavdevice的时候,唯一的不同在于需要首先查找用于输入的设备
    int i, videoindex;
​    int numBytes;
    int ret, got_picture;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVFrame *pFrame, *pFrameRGB;    
    AVPacket *packet;
    uint8_t *out_buffer;
    qDebug() << "current thread ID --- ffmpeg_test:" << QThread::currentThreadId();

    //1_开始
    qDebug() << "Hello FFmpeg!";
    unsigned version = avcodec_version();//获取FFmpeg版本号 unsigned int 类型
    qDebug() << "version is:" << version;

    //2_初始化
    av_register_all(); //初始化FFMPEG  调用了这个才能正常适用编码器和解码器
    avformat_network_init();//初始化FFmpeg网络模块
    avdevice_register_all();//初始化libavdevice并注册所有输入和输出设备
    pFormatCtx = avformat_alloc_context();//分配一个AVFormatContext,查找用于输入的设备

    //3_使用libavdevice读取数据,和直接打开视频文件比较类似,
    //  因为系统的设备也被FFmpeg认为是一种输入的格式(即AVInputFormat)
    QList<QCameraInfo> cameras = QCameraInfo::availableCameras();//获取当前可用摄像头
    qDebug() << cameras.size();
    QString cam_name = QString("video=") + cameras.at(0).description();//格式必须为"video=Integrated Camera"
    qDebug() << cam_name;
    QByteArray char_cam_name = cam_name.toLatin1();//将QString类型数据转化为 const char* 类型

#if USE_DSHOW
    ifmt = av_find_input_format("dshow");//Libavdevice选择dshow(DirectShow)设备作为输入端

    //Set own video device's name
    if(avformat_open_input(&pFormatCtx,char_cam_name,ifmt,NULL)!=0){//打开指定设备 —— cameras.at()
        qDebug() << "Couldn't open input stream.\n";
    }else{
        qDebug() << "Success open input stream —— " << char_cam_name;
    }

#else

    AVInputFormat *ifmt=av_find_input_format("vfwcap");

    if(avformat_open_input(&pFormatCtx,"0",ifmt,NULL)!=0){
        qDebug() << "Couldn't open input stream.\n";
    }
    //使用DirectShow作为输入设备
//    pFormatCtx = avformat_alloc_context();

//    ifmt=av_find_input_format("dshow");
//    avformat_open_input(&pFormatCtx,"video=Integrated Camera",ifmt,NULL) ;
    //打开测试视频
//    pFormatCtx = avformat_alloc_context();

//    avformat_open_input(&pFormatCtx, "test.h265",NULL,NULL);
#endif

    if(avformat_find_stream_info(pFormatCtx,NULL) < 0)//读取一个媒体文件的数据包以获取流信息
    {
        qDebug() << "Couldn't find stream information.\n";
    }else{
        qDebug() << "Success find stream information!\n";
    }

    //4_循环查找数据包包含的流信息,直到找到视频类型的流
    //  便将其记录下来 保存到videoStream变量中
    videoindex = -1;
    for(i = 0; i < pFormatCtx->nb_streams; i++)
    {
        if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoindex=i;
            break;
        }
    }
    if(videoindex==-1)
    {
        qDebug() << "Couldn't find a video stream.\n";
    }else{
        qDebug() << "Success find a video stream!\n";

    }

    //5_查找对应的解码器并打开
    pCodecCtx = pFormatCtx->streams[videoindex]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
//    AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);//软编码
//    AVCodec * codec = avcodec_find_encoder_by_name("nvenc_h264");//硬编码
    if(pCodec == NULL)
    {
        qDebug() << ("Codec not found.\n");
    }else{

        qDebug() << "Codec found Successfuly!\n";
    }
//    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,NULL)<0)//打开解码器
    {
        qDebug() << ("Could not open codec.\n");
    }else{

        qDebug() << "Success open codec!\n";

    //6_开始准备读取视频
    pFrame = av_frame_alloc();//分配一个AVFrame并将其字段设置为默认值
    pFrameRGB = av_frame_alloc();
    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
                pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
                AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);//分配和返回一个SwsContext你需要它来执行使用swsscale()的缩放/转换操作
    numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);
    qDebug() <<  numBytes;
    out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *) pFrameRGB, out_buffer, AV_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的数据
    av_dump_format(pFormatCtx, 0, QApplication::applicationDirPath().toLatin1(), 0); //输出视频信息

    //7_解码压缩
    while (1)
    {
       if (av_read_frame(pFormatCtx, packet) < 0)
       {

           break; //这里认为视频读取完了
       }
       if (packet->stream_index == videoindex) {
           ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);//解码一帧视频数据
           if (ret < 0) {
               qDebug() << ("decode error.");
           }
           if (got_picture) {
               sws_scale(img_convert_ctx,
                       (uint8_t const * const *) pFrame->data,
                       pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
                       pFrameRGB->linesize);//在 pFrame->data 中缩放图像切片,并将得到的缩放切片放在pFrameRGB->data图像中
 
               //把这个RGB数据 用QImage加载
               QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
               QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
               emit sig_GetOneFrame(image);  //发送信号
               QThread::msleep(10);

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

    }

    av_free(out_buffer);

    av_free(pFrameRGB);

    avcodec_close(pCodecCtx);

    avformat_close_input(&pFormatCtx);
}


 


//主界面界面显示

void CameraWidget::slot_GetOneFrame(QImage img)

{

    ui->label->setPixmap(QPixmap::fromImage(img));
}

 

3.主线程中初始化调用,开启线程

t = new QThread;
worker = new Worker;
connect(t, &QThread::finished, worker, &QObject::deleteLater);//防止内存泄漏
connect(worker, SIGNAL(sig_GetOneFrame(QImage)), this, SLOT(slot_GetOneFrame(QImage)));
worker->moveToThread(t);
t->start();
QTimer::singleShot(1,worker,SLOT(ffmpeg_test()));//1毫秒之后槽函数已经在线程中运行


三、参考链接

https://blog.csdn.net/leixiaohua1020/article/details/39702113

  • 4
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
敬告:该系列的课程在抓紧录制更新中,敬请大家关注。敬告:本课程项目仅供学习参考,请不要直接商用,概不负责任何法律责任。 该系列的课程涉及:FFmpeg,WebRTC,SRS,Nginx,Darwin,Live555,等。包括:音视频、流媒体、直播、Android、视频监控28181、等。 我将带领大家一起来学习使用FFmpeg开发视频监控项目,并动手操练。具体内容包括: 一、视频监控的架构和流程二、FFmpeg4.3+SDL2+Qt5开发环境的搭建三、FFmpeg的SDK编程回顾总结并操练四、SDL2.0的编程回顾总结并操练五、颜色空间转换RGB和YUV的原理与实战六、Qt5+FFmpeg本地摄像头采集预览实战七、代码封装:摄像头h264/5编码并存储八、Qt5+FFmpeg单路网络摄像头采集预览九、Qt5+FFmpeg单路网络摄像头采集预览录制会看十、onvif与GB/T-28181的简介  音视频与流媒体是一门很复杂的技术,涉及的概念、原理、理论非常多,很多初学者不学 基础理论,而是直接做项目,往往会看到c/c++的代码时一头雾水,不知道代码到底是什么意思,这是为什么呢?   因为没有学习音视频和流媒体的基础理论,就比如学习英语,不学习基本单词,而是天天听英语新闻,总也听不懂。 所以呢,一定要认真学习基础理论,然后再学习播放器、转码器、非编、流媒体直播、视频监控、等等。   梅老师从事音视频与流媒体行业18年;曾在永新视博、中科大洋、百度、美国Harris广播事业部等公司就职,经验丰富;曾亲手主导广电直播全套项目,精通h.264/h.265/aac,曾亲自参与百度app上的网页播放器等实战产品。  目前全身心自主创业,主要聚焦音视频+流媒体行业,精通音视频加密、流媒体在线转码快编等热门产品。  
以下是基于FFmpeg的AVDevice样例(读取摄像头)的代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <libavdevice/avdevice.h> #include <libavformat/avformat.h> int main(int argc, char *argv[]) { AVInputFormat *inputFormat; AVFormatContext *formatCtx = NULL; AVDictionary *options = NULL; AVCodecContext *codecCtx = NULL; AVFrame *frame = NULL; AVPacket pkt; int ret; avdevice_register_all(); inputFormat = av_find_input_format("video4linux2"); av_dict_set(&options, "video_size", "640x480", 0); av_dict_set(&options, "framerate", "30", 0); ret = avformat_open_input(&formatCtx, "/dev/video0", inputFormat, &options); if (ret < 0) { fprintf(stderr, "Could not open input file\n"); return 1; } ret = avformat_find_stream_info(formatCtx, NULL); if (ret < 0) { fprintf(stderr, "Could not find stream information\n"); return 1; } codecCtx = avcodec_alloc_context3(NULL); if (!codecCtx) { fprintf(stderr, "Could not allocate codec context\n"); return 1; } ret = avcodec_parameters_to_context(codecCtx, formatCtx->streams[0]->codecpar); if (ret < 0) { fprintf(stderr, "Could not copy codec parameters to codec context\n"); return 1; } ret = avcodec_open2(codecCtx, avcodec_find_decoder(codecCtx->codec_id), NULL); if (ret < 0) { fprintf(stderr, "Could not open codec\n"); return 1; } frame = av_frame_alloc(); if (!frame) { fprintf(stderr, "Could not allocate frame\n"); return 1; } while (1) { ret = av_read_frame(formatCtx, &pkt); if (ret < 0) break; if (pkt.stream_index == 0) { ret = avcodec_send_packet(codecCtx, &pkt); if (ret < 0) { fprintf(stderr, "Could not send packet to decoder\n"); break; } while (ret >= 0) { ret = avcodec_receive_frame(codecCtx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; else if (ret < 0) { fprintf(stderr, "Error during decoding\n"); break; } printf("Frame %d (type=%c, size=%d bytes)\n", codecCtx->frame_number, av_get_picture_type_char(frame->pict_type), frame->pkt_size); av_frame_unref(frame); } } av_packet_unref(&pkt); } av_frame_free(&frame); avcodec_free_context(&codecCtx); avformat_close_input(&formatCtx); av_dict_free(&options); return 0; } ``` 这个样例使用了video4linux2作为输入格式,打开了/dev/video0设备,读取该设备的视频流,并将视频帧通过AVCodecContext解码。在解码的过程中,将每一帧的信息打印到控制台上。 请注意,使用此样例代码需要安装FFmpeg库,并且需要有摄像头设备。如果要在其他平台上运行,需要根据平台不同进行一定的修改。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值