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

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


猜你喜欢

转载自blog.csdn.net/weixin_39743893/article/details/80714217