| app/build.gradle.kts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraRecordManager.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| key/Verify.txt | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| usbcameralib/build.gradle | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
app/build.gradle.kts
@@ -21,6 +21,10 @@ versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" ndk { abiFilters += listOf("arm64-v8a", "armeabi-v7a") } } buildFeatures{ aidl =true @@ -62,6 +66,15 @@ buildFeatures { compose = true } packaging { jniLibs { useLegacyPackaging = false } resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" } } } dependencies { app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraRecordManager.java
@@ -366,6 +366,20 @@ Timber.d("音频录制已启动"); } // 主动检查音频编码器输出格式(可能在处理数据前就有格式) try { MediaFormat audioFormat = audioEncoder.getOutputFormat(); if (audioFormat != null) { synchronized (UsbCameraRecordManager.this) { audioTrackIndex = mediaMuxer.addTrack(audioFormat); Timber.d("音频轨道已添加(主动检查): %d", audioTrackIndex); checkAndStartMuxer(); } } } catch (Exception e) { Timber.d("音频编码器输出格式尚未准备好,将在处理数据时添加"); } // 启动音频编码线程 audioThread = new AudioThread(); audioThread.start(); @@ -374,6 +388,8 @@ long frameCount = 0; long lastFileChangeTime = System.currentTimeMillis(); long audioTrackWaitStartTime = System.currentTimeMillis(); final long AUDIO_TRACK_TIMEOUT_MS = 3000; // 3秒超时 // 循环处理摄像头数据 while (isRunning && cameraExists) { @@ -432,6 +448,27 @@ if (videoEncoder != null && mediaMuxer != null) { encodeFrame(buffer, frameCount, width, height); frameCount++; // 如果音频轨道超时未准备好,尝试仅用视频轨道启动 if (!muxerStarted && videoTrackIndex >= 0 && audioTrackIndex < 0) { // 重用循环中已定义的 currentTime 变量 if (currentTime - audioTrackWaitStartTime > AUDIO_TRACK_TIMEOUT_MS) { Timber.w("音频轨道超时未准备好,仅使用视频轨道启动Muxer"); synchronized (UsbCameraRecordManager.this) { // 创建一个假的音频轨道索引,或者直接启动(但MediaMuxer要求至少一个轨道) // 实际上MediaMuxer需要至少一个轨道,所以如果视频轨道准备好了就可以启动 if (videoTrackIndex >= 0) { try { mediaMuxer.start(); muxerStarted = true; Timber.d("Muxer started with video track only: %d", videoTrackIndex); } catch (Exception e) { Timber.e(e, "Failed to start muxer with video only"); } } } } } } // 控制帧率,约20fps @@ -482,12 +519,17 @@ MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); int outputBufferIndex = videoEncoder.dequeueOutputBuffer(bufferInfo, 0); while (outputBufferIndex >= 0) { while (outputBufferIndex != MediaCodec.INFO_TRY_AGAIN_LATER) { if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { // 输出格式改变,添加视频轨道 MediaFormat newFormat = videoEncoder.getOutputFormat(); synchronized (UsbCameraRecordManager.this) { videoTrackIndex = mediaMuxer.addTrack(newFormat); Timber.d("视频轨道已添加: %d", videoTrackIndex); checkAndStartMuxer(); } outputBufferIndex = videoEncoder.dequeueOutputBuffer(bufferInfo, 0); continue; } else if (outputBufferIndex >= 0) { ByteBuffer outputBuffer = videoEncoder.getOutputBuffer(outputBufferIndex); if (outputBuffer != null && muxerStarted && videoTrackIndex >= 0) { @@ -512,9 +554,15 @@ */ private synchronized void checkAndStartMuxer() { if (!muxerStarted && videoTrackIndex >= 0 && audioTrackIndex >= 0) { try { mediaMuxer.start(); muxerStarted = true; Timber.d("Muxer started, video track: %d, audio track: %d", videoTrackIndex, audioTrackIndex); } catch (Exception e) { Timber.e(e, "Failed to start muxer"); } } else { Timber.d("Muxer not started yet, video track: %d, audio track: %d", videoTrackIndex, audioTrackIndex); } } @@ -580,12 +628,17 @@ MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); int outputBufferIndex = audioEncoder.dequeueOutputBuffer(bufferInfo, 0); while (outputBufferIndex >= 0) { while (outputBufferIndex != MediaCodec.INFO_TRY_AGAIN_LATER) { if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { // 输出格式改变,添加音频轨道 MediaFormat newFormat = audioEncoder.getOutputFormat(); synchronized (UsbCameraRecordManager.this) { audioTrackIndex = mediaMuxer.addTrack(newFormat); Timber.d("音频轨道已添加: %d", audioTrackIndex); checkAndStartMuxer(); } outputBufferIndex = audioEncoder.dequeueOutputBuffer(bufferInfo, 0); continue; } else if (outputBufferIndex >= 0) { ByteBuffer outputBuffer = audioEncoder.getOutputBuffer(outputBufferIndex); if (outputBuffer != null && muxerStarted && audioTrackIndex >= 0) { key/Verify.txt
New file @@ -0,0 +1,41 @@ private static PrivateKey getKey(String key, char[] password) throws Exception { byte[] cabuf = new BASE64Decoder().decodeBuffer(key); KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(new ByteArrayInputStream(cabuf), password); Enumeration<String> aliases = keyStore.aliases(); if (!aliases.hasMoreElements()) { throw new RuntimeException("no alias found"); } String alias = aliases.nextElement(); PrivateKey privateKey = (RSAPrivateCrtKeyImpl) keyStore.getKey(alias, password); return privateKey; } public static String sign(String data, PrivateKey key) throws Exception { // MessageDigest md = MessageDigest.getInstance("SHA-256"); MessageDigest md = MessageDigest.getInstance("SHA-1"); md.update(data.getBytes("utf-8")); byte[] hash = md.digest(); // Cipher cipher = Cipher.getInstance("RSA"); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] encrypted = cipher.doFinal(hash); return HexBin.encode(encrypted); } public static String sign(String data, String keyString ,String pwd ) throws Exception { char[] password = pwd.toCharArray(); return sign(data,getKey(keyString,password)); } public static void main(String[] args) { try{ String sign = sign("xuanborobot123456nullnullnull","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","27U777"); System.out.println(sign); }catch (Exception e){ e.printStackTrace(); } } usbcameralib/build.gradle
@@ -16,6 +16,8 @@ cmake { arguments "-DANDROID_ARM_NEON=TRUE" cppFlags "" // 确保使用 c++_shared STL arguments "-DANDROID_STL=c++_shared" } } ndk { @@ -43,7 +45,9 @@ pickFirst 'lib/armeabi-v7a/libturbojpeg.so' pickFirst 'lib/arm64-v8a/libjpeg.so' pickFirst 'lib/arm64-v8a/libturbojpeg.so' // 确保 libc++_shared.so 被包含 pickFirst 'lib/armeabi-v7a/libc++_shared.so' pickFirst 'lib/arm64-v8a/libc++_shared.so' } }