qq虚拟摄像头下载:安卓虚拟相机虚拟摄像头插件,IOS苹果iphone,微信QQ都支持,提供dex\hook类代码 2024-04-02 11:11:42 0 0 核心HOOK思路源码开源了,仅供学习,用的一个java类实现的,但是成品我不提供奥,就提供下实现虚拟视频hook类的java代码【我已经把dex文件里面实现hook的字节码代码转换成java代码了】,仅供大家研究,用工具是Smali将字节码代码转换为Java代码,工具下面地址可以自己网上搜。 下面是我测试一个效果视频【仅供学习】: 虚拟摄像头插件,玩玩,支持微信QQ,替换摄像头,还是开源的!!! 下面是实现HOOk的java代码,我已经用Smali导出来了: =================================================================== // // Decompiled by Jadx - 791ms // package com.exampsle.vcam; import android.graphics.ImageFormat; import android.graphics.Rect; import android.media.Image; import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaCrypto; import android.media.MediaExtractor; import android.media.MediaFormat; import android.util.Log; import android.view.Surface; import de.robv.android.xposed.XposedBridge; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.nio.ByteBuffer; import java.util.concurrent.LinkedBlockingQueue; public class VideoToFrames implements Runnable { private static final int COLOR_FormatI420 = 1; private static final int COLOR_FormatNV21 = 2; private static final long DEFAULT_TIMEOUT_US = 10000; private static final String TAG = "VideoToFrames"; private static final boolean VERBOSE = false; private Callback callback; private Thread childThread; private LinkedBlockingQueue<byte[]> mQueue; private OutputImageFormat outputImageFormat; private Surface play_surf; private Throwable throwable; private String videoFilePath; private final int decodeColorFormat = 0x7f420888; private boolean stopDecode = false; public void setCallback(Callback callback) { this.callback = callback; } public void setEnqueue(LinkedBlockingQueue<byte[]> linkedBlockingQueue) { this.mQueue = linkedBlockingQueue; } public void setSaveFrames(String str, OutputImageFormat outputImageFormat) throws IOException { this.outputImageFormat = outputImageFormat; } public void set_surfcae(Surface surface) { if (surface != null) { this.play_surf = surface; } } public void stopDecode() { this.stopDecode = true; } public void decode(String str) throws Throwable { this.videoFilePath = str; if (this.childThread == null) { Thread thread = new Thread(this, "decode"); this.childThread = thread; thread.start(); Throwable th = this.throwable; if (th != null) { throw th; } } } @Override public void run() { try { videoDecode(this.videoFilePath); } catch (Throwable th) { this.throwable = th; } } /* JADX WARN: Multi-variable type inference failed */ /* JADX WARN: Removed duplicated region for block: B:32:0x00c1 */ /* JADX WARN: Removed duplicated region for block: B:34:0x00c9 */ /* JADX WARN: Type inference failed for: r0v1, types: [android.media.MediaCodec, android.media.MediaExtractor] */ /* JADX WARN: Type inference failed for: r0v3 */ /* JADX WARN: Type inference failed for: r0v4, types: [android.media.MediaCodec] */ /* JADX WARN: Type inference failed for: r0v5 */ /* Code decompiled incorrectly, please refer to instructions dump. */ public void videoDecode(String str) throws IOException { MediaExtractor mediaExtractor; XposedBridge.log("【VCAM】【decoder】开始解码"); MediaCodec mediaCodec = 0; mediaCodec = 0; try { try { new File(str); mediaExtractor = new MediaExtractor(); try { mediaExtractor.setDataSource(str); int selectTrack = selectTrack(mediaExtractor); if (selectTrack < 0) { XposedBridge.log("【VCAM】【decoder】No video track found in " + str); } mediaExtractor.selectTrack(selectTrack); MediaFormat trackFormat = mediaExtractor.getTrackFormat(selectTrack); String string = trackFormat.getString("mime"); mediaCodec = MediaCodec.createDecoderByType(string); showSupportedColorFormat(mediaCodec.getCodecInfo().getCapabilitiesForType(string)); if (isColorFormatSupported(0x7f420888, mediaCodec.getCodecInfo().getCapabilitiesForType(string))) { trackFormat.setInteger("color-format", 0x7f420888); XposedBridge.log("【VCAM】【decoder】set decode color format to type 2135033992"); } else { Log.i(TAG, "unable to set decode color format, color format type 0x7f420888 not supported"); XposedBridge.log("【VCAM】【decoder】unable to set decode color format, color format type 0x7f420888 not supported"); } decodeFramesToImage(mediaCodec, mediaExtractor, trackFormat); mediaCodec.stop(); while (!this.stopDecode) { mediaExtractor.seekTo(0L, 0); decodeFramesToImage(mediaCodec, mediaExtractor, trackFormat); mediaCodec.stop(); } if (mediaCodec != 0) { mediaCodec.stop(); mediaCodec.release(); } } catch (Exception e) { e = e; XposedBridge.log("【VCAM】[videofile]" + e.toString()); if (mediaCodec != 0) { mediaCodec.stop(); mediaCodec.release(); } if (mediaExtractor == null) { return; } mediaExtractor.release(); } } catch (Throwable th) { th = th; if (0 != 0) { mediaCodec.stop(); mediaCodec.release(); } if (0 != 0) { mediaCodec.release(); } throw th; } } catch (Exception e2) { e = e2; mediaExtractor = null; } catch (Throwable th2) { th = th2; if (0 != 0) { } if (0 != 0) { } throw th; } mediaExtractor.release(); } private void showSupportedColorFormat(MediaCodecInfo.CodecCapabilities codecCapabilities) { System.out.print("supported color format: "); int[] iArr = codecCapabilities.colorFormats; int length = iArr.length; for (int i = 0; i < length; i += COLOR_FormatI420) { int i2 = iArr[i]; PrintStream printStream = System.out; printStream.print(i2 + "\t"); } System.out.println(); } private boolean isColorFormatSupported(int i, MediaCodecInfo.CodecCapabilities codecCapabilities) { int[] iArr = codecCapabilities.colorFormats; int length = iArr.length; for (int i2 = 0; i2 < length; i2 += COLOR_FormatI420) { if (iArr[i2] == i) { return true; } } return false; } private void decodeFramesToImage(MediaCodec mediaCodec, MediaExtractor mediaExtractor, MediaFormat mediaFormat) { long j; int dequeueInputBuffer; MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); mediaCodec.configure(mediaFormat, this.play_surf, (MediaCrypto) null, 0); mediaCodec.start(); mediaFormat.getInteger("width"); mediaFormat.getInteger("height"); boolean z = false; boolean z2 = false; int i = 0; boolean z3 = false; long j2 = 0; while (!z && !this.stopDecode) { if (z2 || (dequeueInputBuffer = mediaCodec.dequeueInputBuffer(DEFAULT_TIMEOUT_US)) < 0) { j = 10000; } else { int readSampleData = mediaExtractor.readSampleData(mediaCodec.getInputBuffer(dequeueInputBuffer), 0); if (readSampleData < 0) { j = 10000; z2 = true; mediaCodec.queueInputBuffer(dequeueInputBuffer, 0, 0, 0L, 4); } else { j = 10000; mediaCodec.queueInputBuffer(dequeueInputBuffer, 0, readSampleData, mediaExtractor.getSampleTime(), 0); mediaExtractor.advance(); } } int dequeueOutputBuffer = mediaCodec.dequeueOutputBuffer(bufferInfo, j); if (dequeueOutputBuffer >= 0) { boolean z4 = (bufferInfo.flags & 4) != 0 ? true : z; if (bufferInfo.size != 0) { i += COLOR_FormatI420; Callback callback = this.callback; if (callback != null) { callback.onDecodeFrame(i); } if (!z3) { j2 = System.currentTimeMillis(); z3 = true; } if (this.play_surf == null) { Image outputImage = mediaCodec.getOutputImage(dequeueOutputBuffer); ByteBuffer buffer = outputImage.getPlanes()[0].getBuffer(); byte[] bArr = new byte[buffer.remaining()]; buffer.get(bArr); LinkedBlockingQueue<byte[]> linkedBlockingQueue = this.mQueue; if (linkedBlockingQueue != null) { try { linkedBlockingQueue.put(bArr); } catch (InterruptedException e) { XposedBridge.log("【VCAM】" + e.toString()); } } if (this.outputImageFormat != null) { HookMain.data_buffer = getDataFromImage(outputImage, COLOR_FormatNV21); } outputImage.close(); } long currentTimeMillis = (bufferInfo.presentationTimeUs / 1000) - (System.currentTimeMillis() - j2); if (currentTimeMillis > 0) { try { Thread.sleep(currentTimeMillis); } catch (InterruptedException e2) { XposedBridge.log("【VCAM】" + e2.toString()); XposedBridge.log("【VCAM】线程延迟出错"); } } mediaCodec.releaseOutputBuffer(dequeueOutputBuffer, true); } z = z4; } } Callback callback2 = this.callback; if (callback2 != null) { callback2.onFinishDecode(); } } private static int selectTrack(MediaExtractor mediaExtractor) { int trackCount = mediaExtractor.getTrackCount(); for (int i = 0; i < trackCount; i += COLOR_FormatI420) { if (mediaExtractor.getTrackFormat(i).getString("mime").startsWith("video/")) { return i; } } return -1; } private static boolean isImageFormatSupported(Image image) { int format = image.getFormat(); return format == 17 || format == 35 || format == 0x32315659; } /* JADX WARN: Removed duplicated region for block: B:30:0x007a */ /* JADX WARN: Removed duplicated region for block: B:31:0x007d */ /* JADX WARN: Removed duplicated region for block: B:34:0x0098 */ /* Code decompiled incorrectly, please refer to instructions dump. */ private static byte[] getDataFromImage(Image image, int i) { int i2; int i3; Rect rect; int i4; int i5 = i; int i6 = COLOR_FormatNV21; int i7 = COLOR_FormatI420; if (i5 != COLOR_FormatI420 && i5 != COLOR_FormatNV21) { throw new IllegalArgumentException("only support COLOR_FormatI420 and COLOR_FormatNV21"); } if (!isImageFormatSupported(image)) { throw new RuntimeException("can't convert Image to byte array, format " + image.getFormat()); } Rect cropRect = image.getCropRect(); int format = image.getFormat(); int width = cropRect.width(); int height = cropRect.height(); Image.Plane[] planes = image.getPlanes(); int i8 = width * height; byte[] bArr = new byte[(ImageFormat.getBitsPerPixel(format) * i8) / 8]; byte[] bArr2 = new byte[planes[0].getRowStride()]; int i9 = 0; int i10 = 0; int i11 = COLOR_FormatI420; while (i9 < planes.length) { if (i9 == 0) { i10 = 0; } else if (i9 != i7) { if (i9 == i6) { if (i5 == i7) { i10 = (int) (i8 * 1.25d); } else if (i5 == i6) { i10 = i8; i11 = COLOR_FormatNV21; } } ByteBuffer buffer = planes[i9].getBuffer(); int rowStride = planes[i9].getRowStride(); int pixelStride = planes[i9].getPixelStride(); int i12 = i9 == 0 ? 0 : COLOR_FormatI420; int i13 = width >> i12; i2 = height >> i12; int i14 = width; buffer.position(((cropRect.top >> i12) * rowStride) + ((cropRect.left >> i12) * pixelStride)); i3 = 0; while (i3 < i2) { if (pixelStride == COLOR_FormatI420 && i11 == COLOR_FormatI420) { buffer.get(bArr, i10, i13); i10 += i13; rect = cropRect; i4 = i13; } else { rect = cropRect; i4 = ((i13 - 1) * pixelStride) + COLOR_FormatI420; buffer.get(bArr2, 0, i4); for (int i15 = 0; i15 < i13; i15 += COLOR_FormatI420) { bArr[i10] = bArr2[i15 * pixelStride]; i10 += i11; } } if (i3 < i2 - 1) { buffer.position((buffer.position() + rowStride) - i4); } i3 += COLOR_FormatI420; cropRect = rect; } i9 += COLOR_FormatI420; i5 = i; width = i14; i6 = COLOR_FormatNV21; i7 = COLOR_FormatI420; } else if (i5 == i7) { i10 = i8; } else { if (i5 == i6) { i10 = i8 + COLOR_FormatI420; i11 = COLOR_FormatNV21; } ByteBuffer buffer2 = planes[i9].getBuffer(); int rowStride2 = planes[i9].getRowStride(); int pixelStride2 = planes[i9].getPixelStride(); if (i9 == 0) { } int i132 = width >> i12; i2 = height >> i12; int i142 = width; buffer2.position(((cropRect.top >> i12) * rowStride2) + ((cropRect.left >> i12) * pixelStride2)); i3 = 0; while (i3 < i2) { } i9 += COLOR_FormatI420; i5 = i; width = i142; i6 = COLOR_FormatNV21; i7 = COLOR_FormatI420; } i11 = COLOR_FormatI420; ByteBuffer buffer22 = planes[i9].getBuffer(); int rowStride22 = planes[i9].getRowStride(); int pixelStride22 = planes[i9].getPixelStride(); if (i9 == 0) { } int i1322 = width >> i12; i2 = height >> i12; int i1422 = width; buffer22.position(((cropRect.top >> i12) * rowStride22) + ((cropRect.left >> i12) * pixelStride22)); i3 = 0; while (i3 < i2) { } i9 += COLOR_FormatI420; i5 = i; width = i1422; i6 = COLOR_FormatNV21; i7 = COLOR_FormatI420; } return bArr; } } ================================================================= 收藏(0)