ImageReader
允许应用程序直接获取渲染到surface
的图形数据,并转换为图片,这里我用Presentation
+VirtualDisplay
+ImageReader
做一个Demo来探讨ImageReader
的实现原理。
Demo实现的功能是:Presentation
指定显示在VirtualDisplay
上,然后由ImageReader
去获取VirtualDisplay
上渲染的Presentation
图像,获取之后将其转换为Bitmap
,设置到一个ImageView
中,并且可以通过修改Presentation
的UI数据实现ImageView
的刷新。
MainActivity onCreate
:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDisplayManager = (DisplayManager)this.getSystemService(Context.DISPLAY_SERVICE);
getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//测试ImageReader
testVirtualDisplay();
}
});
imageView = findViewById(R.id.image);
}
testVirtualDisplay
:
private void testVirtualDisplay(){
final int screenWidth = outMetrics.widthPixels;
final int screenHeight = outMetrics.heightPixels;
@SuppressLint("WrongConstant") ImageReader imageReader = ImageReader.newInstance(
screenWidth,
screenHeight,
PixelFormat.RGBA_8888, 1);
//创建虚拟屏,传入ImageReader的Surface
VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay("testVirtualDisplay",
WIDTH, HEIGHT, DENSITY, imageReader.getSurface(), DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION);
//设置回调
imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
//回调中获取Surface的渲染数据
@Override
public void onImageAvailable(ImageReader reader) {
mImageReaderLock.lock();
try {
// Get the latest buffer.
Image image = reader.acquireLatestImage();
Log.d("dongjiao", "11image = :"+image);
if (image != null) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * screenWidth;
Bitmap bitmap = Bitmap.createBitmap(screenWidth + rowPadding / pixelStride,
screenHeight, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
//virtualDisplay.release();
imageView.setImageBitmap(bitmap);
image.close();
}
} finally {
mImageReaderLock.unlock();
}
}
}, handler);
DisplayManager displayManager = (DisplayManager) this.getSystemService(Context.DISPLAY_SERVICE);
Display[] presentationDisplays = displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
if (presentationDisplays.length > 0) {
Display display = presentationDisplays[0];
//TestPresentation指定显示在虚拟屏
TestPresentation presentation = new TestPresentation(this, display);
presentation.show();
findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//更新TestPresentation的UI
presentation.changeText();
}
});
}
}
Demo代码很简单,首先MainActivity
有两个button
,BUTTON
创建ImageReader
,创建虚拟屏,设置ImageReader
回调,创建TestPresentation
,并将TestPresentation
显示在虚拟屏上,此时ImageReader
回调中就会收到TestPresentation
渲染的UI数据,拿到数据之后将其转换为Bitmap
,并设置到ImageView
中。
BUTTON1
会更新TestPresentation
的UI数据,更新之后同样会回调ImageReader
,进而再次更新ImageView
。
来看TestPresentation
的图像是如何传递到ImageReader
的。
protected ImageReader(int width, int height, int format, int maxImages, long usage) {
......
nativeInit(new WeakReference<>(this), width, height, format, maxImages, usage);
mSurface = nativeGetSurface();
......
}
这里关注两个方法,nativeInit
和nativeGetSurface
,maxImages
代码能同时获取buffer
的数量,最小为1,
static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, jint width, jint height,
jint format, jint maxImages, jlong ndkUsage)
{
status_t res;
...
sp<JNIImageReaderContext> ctx(new JNIImageReaderContext(env, weakThiz, clazz, maxImages));
sp<IGraphicBufferProducer> gbProducer;
sp<IGraphicBufferConsumer> gbConsumer;
//创建BufferQueue
BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
//自定义Consumer
sp<BufferItemConsumer> bufferConsumer;
String8 consumerName = String8::format("ImageReader-%dx%df%xm%d-%d-%d",
width, height, format, maxImages, getpid(),
createProcessUniqueId());
......
ctx->setBufferConsumer(bufferConsumer);
bufferConsumer->setName(consumerName);
ctx->setProducer(gbProducer);
bufferConsumer->setFrameAvailableListener(ctx);
ImageReader_setNativeContext(env, thiz, ctx);
ctx->setBufferFormat(nativeFormat);
ctx->setBufferDataspace(nativeDataspace);
ctx->setBufferWidth(width);
ctx->setBufferHeight(height);
....
}
}
这里核心是创建了ImageReader
的native层对象JNIImageReaderContext
,并且创建了一套生产者消费者模型,自定义了Consumer(BufferItemConsumer)
,
最重要的是setFrameAvailableListener
的对象是JNIImageReaderContext
,说明JNIImageReaderContext
实现了onFrameAvailable
接口,看看其具体实现:
void JNIImageReaderContext::onFrameAvailable(const BufferItem& /*item*/)
{
ALOGV("%s: frame available", __FUNCTION__);
bool needsDetach = false;
JNIEnv* env = getJNIEnv(&needsDetach);
if (env != NULL) {
env->CallStaticVoidMethod(mClazz, gImageReaderClassInfo.postEventFromNative, mWeakThiz);
} else {
ALOGW("onFrameAvailable event will not posted");
}
if (needsDetach) {
detachJNI();
}
}
onFrameAvailable
会回调到java层ImageReader
的postEventFromNative
方法,等下在回到java层看它的具体实现,先看Surface
是怎么构建的:
static jobject ImageReader_getSurface(JNIEnv* env, jobject thiz)
{
ALOGV("%s: ", __FUNCTION__);
//从JNIImageReaderContext中获取IGraphicBufferProducer
IGraphicBufferProducer* gbp = ImageReader_getProducer(env, thiz);
if (gbp == NULL) {
jniThrowRuntimeException(env, "Buffer consumer is uninitialized");
return NULL;
}
// 用Surface对IGraphicBufferProducer进行包装
return android_view_Surface_createFromIGraphicBufferProducer(env, gbp);
}
jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env,
const sp<IGraphicBufferProducer>& bufferProducer) {
if (bufferProducer == NULL) {
return NULL;
}
sp<Surface> surface(new Surface(bufferProducer, true));
return android_view_Surface_createFromSurface(env, surface);
}
这里就很简单了,new了一个Surface
对IGraphicBufferProducer
进行包装,这个Surface
最终就是传给虚拟屏的。
接着回到java层来看postEventFromNative
的实现:
private static void postEventFromNative(Object selfRef) {
@SuppressWarnings("unchecked")
WeakReference<ImageReader> weakSelf = (WeakReference<ImageReader>)selfRef;
final ImageReader ir = weakSelf.get();
if (ir == null) {
return;
}
final Handler handler;
synchronized (ir.mListenerLock) {
handler = ir.mListenerHandler;
}
if (handler != null) {
handler.sendEmptyMessage(0);
}
}
这里通过ListenerHandler
发送消息:
private final class ListenerHandler extends Handler {
public ListenerHandler(Looper looper) {
super(looper, null, true /*async*/);
}
@Override
public void handleMessage(Message msg) {
OnImageAvailableListener listener;
synchronized (mListenerLock) {
listener = mListener;
}
// It's dangerous to fire onImageAvailable() callback when the ImageReader is being
// closed, as application could acquire next image in the onImageAvailable() callback.
boolean isReaderValid = false;
synchronized (mCloseLock) {
isReaderValid = mIsReaderValid;
}
if (listener != null && isReaderValid) {
listener.onImageAvailable(ImageReader.this);
}
}
}
看到了吗,回调了OnImageAvailableListener
的onImageAvailable
方法,这个就是前面Demo中我们给ImageReader
设置的回调,回过头再看Demo,在onImageAvailable
方法中做了什么:
ImageReader.OnImageAvailableListener() {
//回调中获取Surface的渲染数据
@Override
public void onImageAvailable(ImageReader reader) {
mImageReaderLock.lock();
try {
Image image = reader.acquireLatestImage();
Log.d("dongjiao", "11image = :"+image);
if (image != null) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * screenWidth;
Bitmap bitmap = Bitmap.createBitmap(screenWidth + rowPadding / pixelStride,
screenHeight, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
//virtualDisplay.release();
imageView.setImageBitmap(bitmap);
image.close();
}
} finally {
mImageReaderLock.unlock();
}
}
}, handler);
调用了acquireLatestImage
获取数据:
public Image acquireLatestImage() {
Image image = acquireNextImage();
if (image == null) {
return null;
}
try {
for (;;) {
Image next = acquireNextImageNoThrowISE();
if (next == null) {
Image result = image;
image = null;
return result;
}
image.close();
image = next;
}
} finally {
if (image != null) {
image.close();
}
}
}
acquireLatestImage
会获取最新的一帧,所以里面有个循环,直到下一帧为空才返回,acquireNextImage
和acquireNextImageNoThrowISE
最终都是调用的acquireNextSurfaceImage
,
private int acquireNextSurfaceImage(SurfaceImage si) {
synchronized (mCloseLock) {
// A null image will eventually be returned if ImageReader is already closed.
int status = ACQUIRE_NO_BUFS;
if (mIsReaderValid) {
status = nativeImageSetup(si);
}
switch (status) {
case ACQUIRE_SUCCESS:
si.mIsImageValid = true;
case ACQUIRE_NO_BUFS:
case ACQUIRE_MAX_IMAGES:
break;
default:
throw new AssertionError("Unknown nativeImageSetup return code " + status);
}
// Only keep track the successfully acquired image, as the native buffer is only mapped
// for such case.
if (status == ACQUIRE_SUCCESS) {
mAcquiredImages.add(si);
}
return status;
}
}
调用到native层去获取帧的数据,参数SurfaceImage
用于保存帧的指针,
static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, jobject image) {
JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz);
if (ctx == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"ImageReader is not initialized or was already closed");
return -1;
}
//获取自定义BufferItemConsumer
BufferItemConsumer* bufferConsumer = ctx->getBufferConsumer();
//保证maxImages的作用,同一时间能够获取的maxImages数量的buffer
//当buffer为空时,需要Image close之后才能继续获取
BufferItem* buffer = ctx->getBufferItem();
if (buffer == NULL) {
ALOGW("Unable to acquire a buffer item, very likely client tried to acquire more than"
" maxImages buffers");
return ACQUIRE_MAX_IMAGES;
}
//获取buffer
status_t res = bufferConsumer->acquireBuffer(buffer, 0);
......
// 将获取到的buffer指针保存到java层传下来的SurfaceImage中
Image_setBufferItem(env, image, buffer);
//将buffer的一些参数保存到SurfaceImage中
env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp,
static_cast<jlong>(buffer->mTimestamp));
auto transform = buffer->mTransform;
if (buffer->mTransformToDisplayInverse) {
transform |= NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
}
env->SetIntField(image, gSurfaceImageClassInfo.mTransform,
static_cast<jint>(transform));
env->SetIntField(image, gSurfaceImageClassInfo.mScalingMode,
static_cast<jint>(buffer->mScalingMode));
return ACQUIRE_SUCCESS;
}
acquireBuffer
用于获取当前可用的buffer
status_t BufferItemConsumer::acquireBuffer(BufferItem *item,
nsecs_t presentWhen, bool waitForFence) {
status_t err;
if (!item) return BAD_VALUE;
Mutex::Autolock _l(mMutex);
//获取buffer
err = acquireBufferLocked(item, presentWhen);
if (err != OK) {
if (err != NO_BUFFER_AVAILABLE) {
BI_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
}
return err;
}
//传入的waitForFence等于0,不需要等Fence
if (waitForFence) {
err = item->mFence->waitForever("BufferItemConsumer::acquireBuffer");
if (err != OK) {
BI_LOGE("Failed to wait for fence of acquired buffer: %s (%d)",
strerror(-err), err);
return err;
}
}
item->mGraphicBuffer = mSlots[item->mSlot].mGraphicBuffer;
return OK;
}
真正获取buffer还是通过父类ConsumerBase
,之后就到SurfaceFlinger
流程了,后续buffer到java层之后还需要各种格式转换等处理,变成一个 ByteBuffer
,就可以写入Bitmap
了,最后将此Bitmap
设置到ImageView
就完成了Demo的效果,Demo中的刷新UI不过是再走了一遍整个流程。
再来整理一遍ImageReader
的数据流:
首先native层JNIImageReaderContext
依靠onFrameAvailable
接收到BufferQueue
中有可用buffer的信号,接着通过JNI调用postEventFromNative
告知java层ImageReader
,postEventFromNative
又继续通过回调onImageAvailable
告知某个注册了OnImageAvailableListener
的客户端,客户端在onImageAvailable
中通过acquireLatestImage
去获取BufferQueue
中可用的buffer,最终是通过acquireBufferLocked
获取,拿到的buffer之后就可以做自己的处理了。
这是ImageReader
的数据流,那么数据是怎么流向ImageReader
的呢?
ImageReader
的数据来自VirtualDisplay
,流向是这样的:
首先VirtualDisplay
创建时接收了ImageReader
的Surface
,APP(Presentation
)是一个可以显示在其他Display
的Dialog
,当它显示在VirtualDisplay
时,SurfaceFlinger
就会将它合成的数据渲染在VirtualDisplay
的Surface
上,因为此时VirtualDisplay
的Surface
来自ImageReader
,自然渲染的数据就被放入了ImageReader
的Surface
的BufferQueue
中,所以ImageReader
就收到了
onFrameAvailable
回调。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)