Qt 实现android camera摄像头的preview和拍照
16lz
2021-01-26
折腾了几天,终于有结果了,Qt对android开发的支持还可以,就是Qt进行android开发没有anroid studio主流,用Qt进行android开发,基本都是在写C++ ,还蛮棒的。
网上找了好一会才找到参考文献5,问题得到解决。
文件目录如下:
完整代码如下:
camera_widgets_display.pro
QT += core gui multimedia multimediawidgetsgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += 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.0SOURCES += \ main.cpp \ myvideosurface.cpp \ widget.cppHEADERS += \ myvideosurface.h \ widget.hFORMS += \ widget.ui# Default rules for deployment.qnx: target.path = /tmp/$${TARGET}/binelse: 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_OBJECTpublic: 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_NAMESPACEnamespace Ui { class Widget; }QT_END_NAMESPACEclass Widget : public QWidget{ Q_OBJECTpublic: 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_Invalidbool 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
<?xml version="1.0" encoding="UTF-8"?> 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 AV同步详解
- 图像库 libpng 编译与实践
- Android(安卓)data分区格式F2FS改为EXT4
- android获取视频每一帧
- Android应用实例之----MifareUltralight格式的nfc标签读写
- Android(安卓)显示SVG格式图片
- Android(安卓)布局单位转换
- PX(像素)转换工具类
- Android(安卓)使用ThumbnailUtils类获取视频的缩略图