我一直在努力使用 ImageReader 捕捉手机屏幕的图像。我收到的 JPEG 无效。使用此设置:
ImageReader mImageReader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 10);
我在 onImageAvailable 中得到了这个:
W/AndroidMediaUtils: Image_getBlobSize: No JPEG/HEIC header detected, defaulting to size=width=2662272
2022-01-26 11:23:36.495 22708-22857/com.artistol.phonekast D/skia: --- Failed to create image decoder with message 'unimplemented'
如果我尝试提取位图,则位图为空:
ByteBuffer myBuffer = image.getPlanes()[0].getBuffer();
myBuffer.rewind();
int mySize = myBuffer.remaining();
frame = new byte[mySize];
myBuffer.get(frame, 0, mySize);
final Bitmap bitmap = BitmapFactory.decodeByteArray(frame, 0, frame.length);
if (bitmap==null)
Log.d(TAG, "bitmap is null");
如果我设置高度!=宽度,我将看到以下异常:
2022-01-26 11:30:29.251 23141-23194/com.name.subname A/AndroidMediaUtils: RGBA override BLOB format buffer should have height == width
2022-01-26 11:30:29.376 23141-23194/com.name.subname A/my_process: runtime.cc:655] Runtime aborting...
runtime.cc:655] Dumping all threads without mutator lock held
runtime.cc:655] All threads:
runtime.cc:655] DALVIK THREADS (27):
runtime.cc:655] "com.name.subname.MyService" prio=5 tid=23 Runnable
runtime.cc:655] | group="" sCount=0 dsCount=0 flags=0 obj=0x12e4cce8 self=0xb40000773250af00
runtime.cc:655] | sysTid=23194 nice=0 cgrp=default sched=0/0 handle=0x75b062bcc0
runtime.cc:655] | state=R schedstat=( 5329217 1502500 2 ) utm=0 stm=0 core=7 HZ=100
runtime.cc:655] | stack=0x75b0528000-0x75b052a000 stackSize=1043KB
runtime.cc:655] | held mutexes= "abort lock" "mutator lock"(shared held)
runtime.cc:655] native: #00 pc 000000000049ee3c /apex/com.android.art/lib64/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, int, BacktraceMap*, char const*, art::ArtMethod*, void*, bool)+140)
runtime.cc:655] native: #01 pc 00000000005abff8 /apex/com.android.art/lib64/libart.so (art::Thread::DumpStack(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, bool, BacktraceMap*, bool) const+376)
runtime.cc:655] native: #02 pc 00000000005c9130 /apex/com.android.art/lib64/libart.so (art::DumpCheckpoint::Run(art::Thread*)+924)
runtime.cc:655] native: #03 pc 00000000005c3070 /apex/com.android.art/lib64/libart.so (art::ThreadList::RunCheckpoint(art::Closure*, art::Closure*)+528)
runtime.cc:655] native: #04 pc 00000000005c223c /apex/com.android.art/lib64/libart.so (art::ThreadList::Dump(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, bool)+1920)
runtime.cc:655] native: #05 pc 000000000055cec4 /apex/com.android.art/lib64/libart.so (art::Runtime::Abort(char const*)+1864)
runtime.cc:655] native: #06 pc 0000000000013978 /system/lib64/libbase.so (android::base::SetAborter(std::__1::function<void (char const*)>&&)::$_3::__invoke(char const*)+76)
runtime.cc:655] native: #07 pc 0000000000006e18 /system/lib64/liblog.so (__android_log_assert+336)
runtime.cc:655] native: #08 pc 0000000000001644 /system/lib64/libmedia_jni_utils.so (android::getLockedImageInfo(android::CpuConsumer::LockedBuffer*, int, int, unsigned char**, unsigned int*, int*, int*)+1284)
runtime.cc:655] native: #09 pc 000000000003c66c /system/lib64/libmedia_jni.so (Image_createSurfacePlanes(_JNIEnv*, _jobject*, int, int)+616)
runtime.cc:655] at android.media.ImageReader$SurfaceImage.nativeCreatePlanes(Native method)
runtime.cc:655] - locked <0x004f434f> (a android.media.ImageReader$SurfaceImage)
runtime.cc:655] at android.media.ImageReader$SurfaceImage.getPlanes(ImageReader.java:912)
runtime.cc:655] at com.name.subname.MyService.processImage(MyService.java:341)
runtime.cc:655] at com.name.subname.MyService.access$600(MyService.java:60)
runtime.cc:655] at com.name.subname.MyService$2.onImageAvailable(MyService.java:293)
我花了相当多的时间来研究这个。我已经尝试更改我的 ImageReader 设置以生成 JPEG 以外的格式。当我使用 ImageFormat.FLEX_RGBA_8888 而不是 ImageFormat.JPEG 时,每次出现如下异常时:
java.lang.UnsupportedOperationException: Invalid format specified 42
at android.media.ImageUtils.getNumPlanesForFormat(ImageUtils.java:73)
at android.media.ImageReader.<init>(ImageReader.java:274)
at android.media.ImageReader.newInstance(ImageReader.java:135)
我看到 ImageFormat.FLEX_RGBA_8888 在 ImageFormat.java 中被定义为 0x29 (42)。我查看了在这里找到的 ImageUtils.java 代码:
事实上 FLEX_RGBA_8888 不是 getNumPlanesForFormat 识别的代码之一,所以我可以看到为什么这被拒绝了。我是一名 Android 新手,可能会遗漏明显的内容,但为什么 ImageFormat 会提供被 ImageReader 拒绝的值?换句话说,如果我在 ImageUtils case 语句中输入一些我认为有效的代码,例如 PixelFormat.RGBA_8888,我的代码甚至不会编译。有人告诉我:
Must be one of: ImageFormat.UNKNOWN, ImageFormat.RGB_565, ImageFormat.YV12, ImageFormat.Y8, ImageFormat.NV16, ImageFormat.NV21, ImageFormat.YUY2, ImageFormat.JPEG, ImageFormat.DEPTH_JPEG, ImageFormat.YUV_420_888, ImageFormat.YUV_422_888, ImageFormat.YUV_444_888, ImageFormat.FLEX_RGB_888, ImageFormat.FLEX_RGBA_8888, ImageFormat.RAW_SENSOR, ImageFormat.RAW_PRIVATE, ImageFormat.RAW10, ImageFormat.RAW12, ImageFormat.DEPTH16, ImageFormat.DEPTH_POINT_CLOUD, ImageFormat.PRIVATE, ImageFormat.HEIC
以下是我的设置方式:
ImageReader mImageReader = ImageReader.newInstance(width, height, ImageFormat.FLEX_RGBA_8888, 10);
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
processImage(reader);
}
}, mImageReaderCallbackHandler);
MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
mMediaProjection = mediaProjectionManager.getMediaProjection(Activity.RESULT_OK, mIntent);
mMediaProjection.registerCallback(mMediaProjectionCallback, mMediaProjectionCallbackHandler);
mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture",
width, height, density,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mImageReader.getSurface(), mVirtualDisplayCallback, mVirtualDisplayCallbackHandler);
我将其作为远程服务运行,并且一直在三星 Note 8 和 Pixel 4a 上进行测试,这两种设备都是物理设备。
编辑1:
在 ImageReader 的 onImageAvailable 中,我有以下内容:
private void processImage(ImageReader reader){
byte[] frame;
Image image = reader.acquireLatestImage();
if(image!=null) {
ByteBuffer myBuffer = image.getPlanes()[0].getBuffer();
myBuffer.rewind();
int mySize = myBuffer.remaining();
frame = new byte[mySize];
myBuffer.get(frame, 0, mySize);
final Bitmap bitmap = BitmapFactory.decodeByteArray(frame, 0, frame.length);
if (bitmap==null)
Log.d(TAG, "bitmap is null");
image.close();
}
}