ImageReader相关函数分析

一、前提知识:

       App中的一个surface对应SufaceFlinger中的一个layout,surface需要申请GraphicBuffer来绘制UI内容,然后交给SurfaceFlinger去合成,最后显示;ImageReader中获得的image,也就是GraphicBuffer,用于绘制surface中的UI。

应用层经常会搭配如下两个函数来获取camera数据:

// 需要什么样的图像
mImageReader = ImageReader.newInstance(1920, 1080, ImageFormat.YUV_420_888, 2);
// 设置图像绘制完毕的监听
mImageReader.setOnImageAvailableListener(mmOnImageAvailableListener, mBackgroundHandler);

二、函数解析:

       首先看下ImageReader::newInstance的实现:

文件路径:Z:\Android9_beta\frameworks\base\media\java\android\media\ImageReader.java

public static ImageReader newInstance(int width, int height, int format, int maxImages) {

        return new ImageReader(width, height, format, maxImages,  

               BUFFER_USAGE_UNKNOWN);

}

接下来看看ImageReader的构造:

protected ImageReader(int width, int height, int format, int maxImages) {
        mWidth = width;
        mHeight = height;
        mFormat = format;
        mMaxImages = maxImages;
      // 检查width 、height 、mMaxImages ,且不允许申请NV21格式的buffer
        if (width < 1 || height < 1) {
            throw new IllegalArgumentException(
                "The image dimensions must be positive");
        }
        if (mMaxImages < 1) {
            throw new IllegalArgumentException(
               "Maximum outstanding image count must be at least 1");
        }
      // 不允许创建nv21格式数据
        if (format == ImageFormat.NV21) {
            throw new IllegalArgumentException(
                    "NV21 format is not supported");
        }
        mNumPlanes = ImageUtils.getNumPlanesForFormat(mFormat);
      // 下面两个是主要实现
        nativeInit(new WeakReference<>(this), width, height, format, maxImages, usage);
        mSurface = nativeGetSurface();
      // 标记申请的是image是合法的
        mIsReaderValid = true;
        mEstimatedNativeAllocBytes = ImageUtils.getEstimatedNativeAllocBytes(
                width, height, format, /*buffer count*/ 1);
        VMRuntime.getRuntime().registerNativeAllocation(mEstimatedNativeAllocBytes);
 }

这里面主要的就是执行nativeInit和nativeGetSurface两个函数,这两个函数的实现在native文件android_media_ImageReader.cpp(Image.java和ImageReader.java共同的native文件)中,路径为:Z:\Android9_beta\frameworks\base\media\jni\android_media_ImageReader.cpp

函数映射如下:

{"nativeInit",             "(Ljava/lang/Object;IIIIJ)V",  (void*)ImageReader_init },
{"nativeImageSetup",       "(Landroid/media/Image;)I",   (void*)ImageReader_imageSetup },

ImageReader_init:

static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, jint width, jint height,jint format, jint maxImages, jlong ndkUsage) {
    status_t res;
    int nativeFormat;
    android_dataspace nativeDataspace;
    ALOGV("%s: width:%d, height: %d, format: 0x%x, maxImages:%d",
         __FUNCTION__, width, height, format, maxImages);
   // 这里是将申请的格式转化成公共格式,然后获取hal层中跟该格式对应的Format
    PublicFormat publicFormat = static_cast<PublicFormat>(format);
    nativeFormat = android_view_Surface_mapPublicFormatToHalFormat(
        publicFormat);
    nativeDataspace = android_view_Surface_mapPublicFormatToHalDataspace(
        publicFormat);
    jclass clazz = env->GetObjectClass(thiz);
    if (clazz == NULL) {
        jniThrowRuntimeException(env, "Can't find android/graphics/ImageReader");
        return;
    }
    // 注意这里new出来的JNIImageReaderContext,这是管理buffer的核心。
    // 在JNIImageReaderContext的构造里,会创建一个大小为maxImages的buffer队列mBuffers,
    // 用于将ConsumerBase中获得的buffer放进此队列中。
    sp<JNIImageReaderContext> ctx(new JNIImageReaderContext(env, weakThiz, clazz, maxImages));
    sp<IGraphicBufferProducer> gbProducer;
    sp<IGraphicBufferConsumer> gbConsumer;
    // 创建生产者和消费者。
    BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
    // 创建消费者,BufferItemConsumer是ConsumerBase的子类,这里可以从 
    // BufferQueueConsumer中拿取已经填充好的buffer数据。
    sp<BufferItemConsumer> bufferConsumer;
    String8 consumerName = String8::format("ImageReader-%dx%df%xm%d-%d-%d",
            width, height, format, maxImages, getpid(),
            createProcessUniqueId());
    uint32_t consumerUsage = GRALLOC_USAGE_SW_READ_OFTEN;
    bool needUsageOverride = ndkUsage != CONSUMER_BUFFER_USAGE_UNKNOWN;
    uint64_t outProducerUsage = 0;
    uint64_t outConsumerUsage = 0;
    android_hardware_HardwareBuffer_convertToGrallocUsageBits(&outProducerUsage, &outConsumerUsage,
            ndkUsage, 0);
    if (isFormatOpaque(nativeFormat)) {
        // Use the SW_READ_NEVER usage to tell producer that this format is not for preview or video
        // encoding. The only possibility will be ZSL output.
        consumerUsage = GRALLOC_USAGE_SW_READ_NEVER;
        if (needUsageOverride) {
            consumerUsage = android_convertGralloc1To0Usage(0, outConsumerUsage);
        }
    } else if (needUsageOverride) {
        ALOGW(&#
#include "mainform.h" #include "ui_mainform.h" #include<libusb.h> #include<QLabel> #include<QThread> Mainform::Mainform(QWidget *parent) : QMainWindow(parent), ui(new Ui::Mainform) { ui->setupUi(this); } Mainform::~Mainform() { delete ui; } // // Mainform.cpp // void Mainform::handleImageReady(QImage image) { // // 将图像显示到label // ui->label->setPixmap(QPixmap::fromImage(image).scaled(640, 512)); // ui->label->setMinimumSize(640, 512); // } class subThread : public QThread { Q_OBJECT public: explicit subThread(Mainform* parent); signals: void on_pushButton_clicked(); void imageReady(QImage); // 新增图像就绪信号 protected: void run(){ //初始化libusb库 libusb_init(NULL); struct libusb_device_handle *devh=libusb_open_device_with_vid_pid(NULL,0xaa55,0x8866);//VID,PID int transferred_bytes=0; if(devh) { unsigned char buf[512];//数据头帧 libusb_bulk_transfer( devh, // 设备句柄 0x81, // 端点地址 (IN方向) buf, // 数据缓冲区 sizeof(buf), // 缓冲区大小 &transferred_bytes, // 实际传输字节数 120 // 超时时间 ); unsigned char data[655360]; int i=0; while (devh) { unsigned char buffer[16384];//每40次接收是一帧数据 libusb_bulk_transfer( devh, // 设备句柄 0x81, // 端点地址 (IN方向) buffer, // 数据缓冲区 sizeof(buffer), // 缓冲区大小 &transferred_bytes, // 实际传输字节数 120 // 超时时间 ); if(i<40) memcpy(data+i*16384,buffer,16384); if(i==40) { QImage image( 640, // 宽度(像素列数) 512, // 高度(像素行数) QImage::Format_Grayscale8 ); for (int y=0; y<512; ++y) { for (int x=0; x<640; ++x) { int idx = 2 * (y*640 + x); // 每像素占2字节 quint16 high = data[idx]; // 高位字节 quint16 low = data[idx+1]; // 低位字节 quint16 pixel = (high << 8) | low; // 合并为16位 quint8 gray = pixel >> 8; // 取高8位作为灰度值 image.setPixel(x, y, gray); } } emit imageReady(image); // ui->label->setPixmap(QPixmap::fromImage(image)); //ui->label->setText(QString::number(sizeof(data))); //ui->label->setMinimumSize(640,512); i=0; } i++; } } } }; void Mainform::on_pushButton_clicked() { } 怎么启动subThread 线程
03-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值