Qt 实现android camera摄像头的preview和拍照

折腾了几天,终于有结果了,Qt对android开发的支持还可以,就是Qt进行android开发没有anroid studio主流,用Qt进行android开发,基本都是在写C++ ,还蛮棒的。

网上找了好一会才找到参考文献5,问题得到解决。

文件目录如下:

Qt 实现android camera摄像头的preview和拍照_第1张图片

完整代码如下:

camera_widgets_display.pro

QT       += core gui multimedia multimediawidgets

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    myvideosurface.cpp \
    widget.cpp

HEADERS += \
    myvideosurface.h \
    widget.h

FORMS += \
    widget.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

myvideosurface.h

#ifndef MYVIDEOSURFACE_H
#define MYVIDEOSURFACE_H

#include 
#include 
#include 
#include 

class MyVideoSurface : public QAbstractVideoSurface
{
    Q_OBJECT
public:
    MyVideoSurface();

    QList supportedPixelFormats(QAbstractVideoBuffer::HandleType type = QAbstractVideoBuffer::NoHandle) const Q_DECL_OVERRIDE;
    bool isFormatSupported(const QVideoSurfaceFormat &) const Q_DECL_OVERRIDE;    //将视频流中像素格式转换成格式对等的图片格式,若无对等的格式,返回QImage::Format_Invalid
    bool start(const QVideoSurfaceFormat &) Q_DECL_OVERRIDE;                       //只要摄像头开,就会调用
    bool present(const QVideoFrame &) Q_DECL_OVERRIDE;                             //每一帧画面将回到这里处理
    void stop() Q_DECL_OVERRIDE;

signals:
    void frameAvailable(QVideoFrame cloneFrame);
};

#endif // MYVIDEOSURFACE_H

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

void NV21_T_RGB(unsigned int width , unsigned int height , unsigned char *yuyv , unsigned char *rgb);

private:
    Ui::Widget *ui;
    QCamera *camera;
    QCameraImageCapture *capture;
    QCameraViewfinder *viewfinder;
    MyVideoSurface *mySurface;
    QImage videoImg;

public slots:
    void displayImage(int ,QImage image);
    void rcvFrame(QVideoFrame);                            //接收图像帧数据
    void paintEvent(QPaintEvent *event);

private slots:
    void on_pushButton_clicked();
};
#endif // WIDGET_H

main.cpp

#include "widget.h"

#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

myvideosurface.cpp

#include "myvideosurface.h"

MyVideoSurface::MyVideoSurface()
{

}

//支持的像素格式
QList MyVideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
{
    if(handleType == QAbstractVideoBuffer::NoHandle){
        return QList() << QVideoFrame::Format_RGB32
                                                 << QVideoFrame::Format_ARGB32
                                                 << QVideoFrame::Format_ARGB32_Premultiplied
                                                 << QVideoFrame::Format_RGB565
                                                 << QVideoFrame::Format_NV21
                                                 << QVideoFrame::Format_RGB555;
    }
    else {
        return QList();
    }
}

//将视频流中像素格式转换成格式对等的图片格式,若无对等的格式,返回QImage::Format_Invalid
bool MyVideoSurface::isFormatSupported(const QVideoSurfaceFormat &videoformat) const
{
    //imageFormatFromPixelFormat()-----返回与视频帧像素格式等效的图像格式
    //pixelFormat()-----返回视频流中帧的像素格式
    return QVideoFrame::imageFormatFromPixelFormat(videoformat.pixelFormat()) != QImage::Format_Invalid;
}

//这些虚函数,会自动被调用,start检测图像是否可以对等转换,每一帧有没有
bool MyVideoSurface::start(const QVideoSurfaceFormat &videoformat)
{
//    qDebug() << QVideoFrame::imageFormatFromPixelFormat(videoformat.pixelFormat());              //格式是RGB32
//    if(QVideoFrame::imageFormatFromPixelFormat(videoformat.pixelFormat()) != QImage::Format_Invalid && !videoformat.frameSize().isEmpty()){
//        QAbstractVideoSurface::start(videoformat);
//        return true;
//    }
    QAbstractVideoSurface::start(videoformat);
    return false;
}

bool MyVideoSurface::present(const QVideoFrame &frame)
{
//    qDebug() << frame.size();
    if (frame.isValid()){
        QVideoFrame cloneFrame(frame);                                      //每一帧视频都会进入present中,内部机制
        emit frameAvailable(cloneFrame);                                    //直接把视频帧发送出去
        return true;
    }
    stop();
    return false;
}

void MyVideoSurface::stop()
{
    QAbstractVideoSurface::stop();
}


widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    camera=new QCamera;//摄像头
    capture = new QCameraImageCapture(camera);
    viewfinder=new QCameraViewfinder(this);//取景器

    QObject::connect(capture, SIGNAL(imageCaptured(int,QImage)), this, SLOT(displayImage(int,QImage)));
    camera->setCaptureMode(QCamera::CaptureStillImage);
//    camera->setViewfinder(viewfinder);
    mySurface = new MyVideoSurface();
    camera->setViewfinder(mySurface);
    //处理myvideosurface中每一帧视频
    connect(mySurface, SIGNAL(frameAvailable(QVideoFrame)), this, SLOT(rcvFrame(QVideoFrame)), Qt::DirectConnection);

    camera->start(); //启动摄像头

    //获取摄像头支持的分辨率、帧率等参数
//    QList ViewSets = camera->supportedViewfinderSettings();
//    int i = 0;
//    qDebug() << "viewfinderResolutions sizes.len = " << ViewSets.length();
//    foreach (QCameraViewfinderSettings ViewSet, ViewSets) {
//        qDebug() << i++ <<" max rate = " << ViewSet.maximumFrameRate() << "min rate = "<< ViewSet.minimumFrameRate() << "resolution "<setViewfinderSettings(camerasettings);

}

Widget::~Widget()
{
    delete ui;
}


void Widget::displayImage(int ,QImage image)
{
    image=image.convertToFormat(QImage::Format_RGB888);
    ui->label->setPixmap(QPixmap::fromImage(image));
    qDebug() << image.size();
}

void Widget::rcvFrame(QVideoFrame m_currentFrame)
{
    qDebug() << "received" << m_currentFrame.size();

    m_currentFrame.map(QAbstractVideoBuffer::ReadOnly);
//    unsigned char *pix_ptr = m_currentFrame.bits();
//    qDebug("%d %d %d %d", *pix_ptr, *(pix_ptr+1), *(pix_ptr+2), *(pix_ptr+3));
        //将视频帧转化成QImage,devicePixelRatio设备像素比,bytesPerLine一行的像素字节(1280*4=5120)

    videoImg = QImage(m_currentFrame.width(), m_currentFrame.height(), QImage::Format_RGB888);
    NV21_T_RGB(m_currentFrame.width(), m_currentFrame.height(), m_currentFrame.bits(), videoImg.bits());
//    videoImg =  QImage(m_currentFrame.bits(),
//                   m_currentFrame.width(),
//                   m_currentFrame.height(),
//                   QImage::Format_Grayscale8).copy();       //这里要做一个copy,因为char* pdata在emit后释放了
//    videoImg = videoImg.mirrored(true, false);                          //水平翻转,原始图片是反的

//    qDebug() <<  "image" << videoImg;  //可以看看输出啥东西
//    QString currentTime = QDateTime::currentDateTime().toString("yyyyMMddhhmmss");
//    QString savefile = QString("E:/study/QT/opencamera/opencamera/%1.jpg").arg(currentTime);
//    qDebug() << videoImg.save(savefile);
    m_currentFrame.unmap();                                         //释放map拷贝的内存
    QCameraInfo cameraInfo(*camera); // needed to get the camera sensor position and orientation

//    // Get the current display orientation
//    const QScreen *screen = QGuiApplication::primaryScreen();
//    const int screenAngle = screen->angleBetween(screen->nativeOrientation(), screen->orientation());

//    int rotation;
//    if (cameraInfo.position() == QCamera::BackFace) {
//        rotation = (cameraInfo.orientation() - screenAngle) % 360;
//    } else {
//        // Front position, compensate the mirror
//        rotation = (360 - cameraInfo.orientation() + screenAngle) % 360;
//    }

//    // Rotate the frame so it always shows in the correct orientation
    videoImg = videoImg.transformed(QTransform().rotate(90));

    QWidget::update();                                              //更新了,就会触发paintEvent画图
}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    QRect rect(0, 0, 480, 640);

    //设置画笔
    QPen pen;
    pen.setWidth(5);                    //线宽
    pen.setColor(Qt::red);
    pen.setStyle(Qt::SolidLine);        //实线
    pen.setCapStyle(Qt::FlatCap);       //线端点样式
    pen.setJoinStyle(Qt::BevelJoin);    //线连接点样式
    painter.setPen(pen);
    painter.drawRect(rect);

    //qDebug() <<  "image" << videoImg;     //第一次输出的QImage(null)
    //只要窗口启动,就会触发paintEvent,所以第一次是null,不可以画图。
    if(videoImg != QImage(nullptr)){
        painter.drawImage(rect, videoImg);
    }
}


void Widget::on_pushButton_clicked()
{
    capture->capture();
}

void Widget::NV21_T_RGB(unsigned int width , unsigned int height , unsigned char *yuyv , unsigned char *rgb)
{
    const int nv_start = width * height ;
    unsigned int i, j, index = 0, rgb_index = 0;
    unsigned char y, u, v;
    int r, g, b, nv_index = 0;

    for(i = 0; i < height; i++){
        for(j = 0; j < width; j ++){
            //nv_index = (rgb_index / 2 - width / 2 * ((i + 1) / 2)) * 2;
            nv_index = i / 2  * width + j - j % 2;

            y = yuyv[rgb_index];
            u = yuyv[nv_start + nv_index ];
            v = yuyv[nv_start + nv_index + 1];

            r = y + (140 * (v-128))/100;  //r
            g = y - (34 * (u-128))/100 - (71 * (v-128))/100; //g
            b = y + (177 * (u-128))/100; //b

            if(r > 255)   r = 255;
            if(g > 255)   g = 255;
            if(b > 255)   b = 255;
            if(r < 0)     r = 0;
            if(g < 0)     g = 0;
            if(b < 0)     b = 0;

            index = rgb_index % width + (height - i - 1) * width;
            //rgb[index * 3+0] = b;
            //rgb[index * 3+1] = g;
            //rgb[index * 3+2] = r;

            //颠倒图像
            //rgb[height * width * 3 - i * width * 3 - 3 * j - 1] = b;
            //rgb[height * width * 3 - i * width * 3 - 3 * j - 2] = g;
            //rgb[height * width * 3 - i * width * 3 - 3 * j - 3] = r;

            //正面图像
            rgb[i * width * 3 + 3 * j + 0] = b;
            rgb[i * width * 3 + 3 * j + 1] = g;
            rgb[i * width * 3 + 3 * j + 2] = r;

            rgb_index++;
        }
    }
//    return 0;
}

widget.ui



 Widget
 
  
   
    0
    0
    1080
    1920
   
  
  
   Widget
  
  
   
    
     0
     640
     480
     640
    
   
   
    Image
   
  
  
   
    
     40
     1520
     281
     291
    
   
   
    PushButton
   
  
 
 
 

 

参考文献:

1.NV21与I420

2.C++ NV21转RGB

3.Camera Overview

4.QAbstractVideoSurface Class

5.Qt使用QAbstractVideoSurface捕获视频帧(信号槽方式),并用QPainter画出来

 

 

你可能感兴趣的:(android,qt)