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

从笔记直接复制粘贴过来的,被吐槽了~_~,整理一下

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

本人新手小白

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

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毫秒之后槽函数已经在线程中运行