Dana
3 天以前 430512c7927776edcee161ee600f1ee5bf34e566
1.notifyCallback synchronized
4个文件已修改
127 ■■■■■ 已修改文件
app/src/main/java/com/safeluck/floatwindow/FloatingService.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/P2UsbCameraVideoService.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraPushManager.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraRecordManager.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/FloatingService.java
@@ -331,9 +331,10 @@
    
    /**
     * 通知回调:统一使用 ResponseVO
     * 使用 synchronized 确保 RemoteCallbackList 操作的线程安全
     * @param response ResponseVO 对象,包含 type、errCode、message
     */
    private void notifyCallback(ResponseVO response) {
    private synchronized void notifyCallback(ResponseVO response) {
        if (response == null) {
            return;
        }
app/src/main/java/com/safeluck/floatwindow/P2UsbCameraVideoService.java
@@ -173,7 +173,11 @@
        currentManagerType = ManagerType.NONE;
    }
    private void notifyCallback(ResponseVO response) {
    /**
     * 通知回调:统一使用 ResponseVO
     * 使用 synchronized 确保 RemoteCallbackList 操作的线程安全
     */
    private synchronized void notifyCallback(ResponseVO response) {
        if (response == null) return;
        int count = mCallbacks.beginBroadcast();
        for (int i = 0; i < count; i++) {
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraPushManager.java
@@ -132,7 +132,7 @@
        if (media.getM_screen() != null) {
            resolutionArr[0] = media.getM_screen().getWidth();
            resolutionArr[1] = media.getM_screen().getHeight();
            Timber.d("设置分辨率: %dx%d", resolutionArr[0], resolutionArr[1]);
            Timber.d("%s 设置分辨率: %dx%d", getCameraTag(), resolutionArr[0], resolutionArr[1]);
        }
        try {
@@ -149,11 +149,11 @@
            }
            cameraExists = true;
            Timber.d("USB摄像头打开成功");
            Timber.d("%s USB摄像头打开成功", getCameraTag());
            notifyCallback(1, 0, "推流线程已启动,等待推流状态就绪");
        } catch (Exception e) {
            Timber.e(e, "Failed to start push");
            Timber.e(e, "%s Failed to start push", getCameraTag());
            notifyCallback(1, -3, "启动推流失败: " + e.getMessage());
        }
    }
@@ -162,7 +162,7 @@
     * 停止推流
     */
    public void stopPush() {
        Timber.d("stopPush called");
        Timber.d("%s stopPush called", getCameraTag());
        stopPushThread();
//        stopAudioTransfer();
        stopWaterMaskSchedule();
@@ -225,7 +225,7 @@
                @Override
                public void surfaceCreated(SurfaceHolder holder) {
                    try {
                        Timber.d("previewSurfaceView surfaceCreated, startPreviewAysnc");
                        Timber.d("%s previewSurfaceView surfaceCreated, startPreviewAysnc", getCameraTag());
                        if (alivcPusher != null) {
                            alivcPusher.startPreviewAysnc(previewSurfaceView);
@@ -235,7 +235,7 @@
                            startPushThread();
                        }
                    } catch (Exception e) {
                        Timber.e(e, "startPreviewAysnc in surfaceCreated failed");
                            Timber.e(e, "%s startPreviewAysnc in surfaceCreated failed", getCameraTag());
                        notifyCallback(1, -3, "预览启动失败: " + e.getMessage());
                    }
                }
@@ -283,9 +283,9 @@
            // 设置监听器
            setupListeners();
            Timber.d("AlivcPusher initialized successfully");
            Timber.d("%s AlivcPusher initialized successfully", getCameraTag());
        } catch (Exception e) {
            Timber.e(e, "Failed to initialize AlivcPusher");
            Timber.e(e, "%s Failed to initialize AlivcPusher", getCameraTag());
            notifyCallback(1, -3, "初始化推流SDK失败: " + e.getMessage());
        }
    }
@@ -414,17 +414,17 @@
        alivcPusher.setLivePushInfoListener(new AlivcLivePushInfoListener() {
            @Override
            public void onPreviewStarted(AlivcLivePusher alivcLivePusher) {
                Timber.d("onPreviewStarted");
                Timber.d("%s onPreviewStarted", getCameraTag());
                mainHandler.postDelayed(()->{
                    // 预览就绪后再启动推流,避免 INIT 状态直接 startPush 报错
                    if (alivcPusher != null && pushUrl != null && !pushUrl.isEmpty()) {
                        try {
                            AlivcLivePushStats s = alivcPusher.getCurrentStatus();
                            Timber.i("onPreviewStarted, current status=%s", s != null ? s.name() : "null");
                            Timber.d("开始推流: %s", pushUrl);
                            Timber.i("%s onPreviewStarted, current status=%s", getCameraTag(), s != null ? s.name() : "null");
                            Timber.d("%s 开始推流: %s", getCameraTag(), pushUrl);
                            alivcPusher.startPushAysnc(pushUrl);
                        } catch (Exception e) {
                            Timber.e(e, "startPushAysnc failed");
                            Timber.e(e, "%s startPushAysnc failed", getCameraTag());
                            notifyCallback(1, -3, "启动推流失败: " + e.getMessage());
                        }
                    }
@@ -439,7 +439,7 @@
            @Override
            public void onPushStarted(AlivcLivePusher alivcLivePusher) {
                Timber.d("onPushStarted");
                Timber.d("%s onPushStarted", getCameraTag());
                pushStarted = true;
//                startAudioTransfer();
                notifyCallback(1, 0, "推流已开始,分辨率: " + resolutionArr[0] + "x" + resolutionArr[1]);
@@ -624,7 +624,7 @@
            int ret = -1;
            for (int i = 0; i < 3; i++) {
                ret = usbCamera.prepareCamera(cameraIds, cameraName, resolutionArr, ay_encrypt);
                Timber.d("USB摄像头第%d次打开结果: %d, 分辨率: %dx%d", i + 1, ret, resolutionArr[0], resolutionArr[1]);
                Timber.d("%s USB摄像头第%d次打开结果: %d, 分辨率: %dx%d", getCameraTag(), i + 1, ret, resolutionArr[0], resolutionArr[1]);
                if (ret == 0) {
                    break;
                }
@@ -635,7 +635,7 @@
            // 成功标准:prepareCamera 返回 0
            return ret == 0;
        } catch (Exception e) {
            Timber.e(e, "打开USB摄像头异常");
            Timber.e(e, "%s 打开USB摄像头异常", getCameraTag());
            return false;
        }
    }
@@ -648,7 +648,7 @@
            isRunning = true;
            pushThread = new PushThread();
            pushThread.start();
            Timber.d("Push thread started");
            Timber.d("%s Push thread started", getCameraTag());
        }
    }
@@ -711,7 +711,7 @@
        @Override
        public void run() {
            super.run();
            Timber.d("PushThread started");
            Timber.d("%s PushThread started", getCameraTag());
            try {
                int width = resolutionArr[0];
@@ -722,14 +722,14 @@
                int bufferSize = width * height * 3 / 2;
                byte[] buffer = new byte[bufferSize];
                Timber.d("开始推送视频数据,分辨率: %dx%d", width, height);
                Timber.d("%s 开始推送视频数据,分辨率: %dx%d", getCameraTag(), width, height);
                // 循环处理摄像头数据
                while (isRunning && cameraExists) {
                    // 处理摄像头数据
                    int processResult = usbCamera.processCamera();
                    if (processResult == -1) {
                        Timber.w("processCamera返回-1,摄像头可能断开");
                        Timber.w("%s processCamera返回-1,摄像头可能断开", getCameraTag());
                        cameraExists = false;
                        notifyCallback(1, -1, "USB摄像头断开");
                        break;
@@ -751,7 +751,7 @@
                                    0 // rotation
                            );
                        } catch (Exception e) {
                            Timber.e(e, "Error pushing frame");
                            Timber.e(e, "%s Error pushing frame", getCameraTag());
                        }
                    } else if (!pushStarted) {
                        // 等待 onPushStarted 后再喂帧,避免 SDK invalid state
@@ -763,16 +763,28 @@
                }
                
            } catch (Exception e) {
                Timber.e(e, "Error in push thread");
                Timber.e(e, "%s Error in push thread", getCameraTag());
                cameraExists = false;
                notifyCallback(1, -1, "推流线程异常: " + e.getMessage());
            } finally {
                Timber.d("PushThread ended");
                Timber.d("%s PushThread ended", getCameraTag());
            }
        }
    }
    
    /**
     * 获取摄像头ID标签,用于日志
     * @return "[P1]" 或 "[P2]"
     */
    private String getCameraTag() {
        if (mediaArgu != null) {
            int usbCameraId = mediaArgu.getUsbCameraId();
            return usbCameraId == 2 ? "[P2]" : "[P1]";
        }
        return "[P1]"; // 默认 P1
    }
    /**
     * 通知回调
     */
    private void notifyCallback(int type, int errCode, String message) {
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraRecordManager.java
@@ -122,7 +122,7 @@
        if (media.getM_screen() != null) {
            resolutionArr[0] = media.getM_screen().getWidth();
            resolutionArr[1] = media.getM_screen().getHeight();
            Timber.d("设置分辨率: %dx%d", resolutionArr[0], resolutionArr[1]);
            Timber.d("%s 设置分辨率: %dx%d", getCameraTag(), resolutionArr[0], resolutionArr[1]);
        }
        
        try {
@@ -134,14 +134,14 @@
            }
            setWaterMask();
            cameraExists = true;
            Timber.d("USB摄像头打开成功");
            Timber.d("%s USB摄像头打开成功", getCameraTag());
            
            // 启动录像线程
            startRecordThread();
            
            notifyCallback(0, 0, "录像已启动");
        } catch (Exception e) {
            Timber.e(e, "Failed to start record");
            Timber.e(e, "%s Failed to start record", getCameraTag());
            notifyCallback(0, -3, "启动录像失败: " + e.getMessage());
        }
    }
@@ -262,7 +262,7 @@
     * 停止录像
     */
    public void stopRecord() {
        Timber.d("stopRecord called");
        Timber.d("%s stopRecord called", getCameraTag());
        stopWaterMaskSchedule();
        
@@ -321,7 +321,7 @@
            int ret = -1;
            for (int i = 0; i < 3; i++) {
                ret = usbCamera.prepareCamera(cameraIds, cameraName, resolutionArr, ay_encrypt);
                Timber.d("USB录像摄像头第%d次打开结果: %d, 分辨率: %dx%d", i + 1, ret, resolutionArr[0], resolutionArr[1]);
                Timber.d("%s USB录像摄像头第%d次打开结果: %d, 分辨率: %dx%d", getCameraTag(), i + 1, ret, resolutionArr[0], resolutionArr[1]);
                if (ret == 0) {
                    break;
                }
@@ -332,7 +332,7 @@
            // 成功标准:prepareCamera 返回 0
            return ret == 0;
        } catch (Exception e) {
            Timber.e(e, "打开USB摄像头异常");
            Timber.e(e, "%s 打开USB摄像头异常", getCameraTag());
            return false;
        }
    }
@@ -384,22 +384,22 @@
                );
                
                if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
                    Timber.e("AudioRecord初始化失败");
                    Timber.e("%s AudioRecord初始化失败", getCameraTag());
                    return false;
                }
                Timber.d("音频编码器和AudioRecord初始化成功(P1模式)");
                Timber.d("%s 音频编码器和AudioRecord初始化成功(P1模式)", getCameraTag());
            } else {
                // P2 模式:不初始化音频相关资源
                audioEncoder = null;
                audioRecord = null;
                audioTrackIndex = -1;
                Timber.d("P2模式:跳过音频初始化,仅录制视频");
                Timber.d("%s P2模式:跳过音频初始化,仅录制视频", getCameraTag());
            }
            
            // 创建新的视频文件
            currentVideoFile = VideoFileUtils.getVideoFile(context, mediaArgu.getTfCardFlag(),mediaArgu.getUsbCameraId());
            if (currentVideoFile == null) {
                Timber.e("Failed to create video file");
                Timber.e("%s Failed to create video file", getCameraTag());
                return false;
            }
            
@@ -411,11 +411,11 @@
            muxerStarted = false;
            currentFileStartTime = System.currentTimeMillis();
            
            Timber.d("编码器和Muxer初始化成功,文件: %s, 音频: %s",
            Timber.d("%s 编码器和Muxer初始化成功,文件: %s, 音频: %s", getCameraTag(),
                    currentVideoFile.getAbsolutePath(), enableAudio ? "启用" : "禁用");
            return true;
        } catch (Exception e) {
            Timber.e(e, "初始化编码器和Muxer失败");
            Timber.e(e, "%s 初始化编码器和Muxer失败", getCameraTag());
            return false;
        }
    }
@@ -487,7 +487,7 @@
            isRunning = true;
            recordThread = new RecordThread();
            recordThread.start();
            Timber.d("Record thread started");
            Timber.d("%s Record thread started", getCameraTag());
        }
    }
    
@@ -514,7 +514,7 @@
        @Override
        public void run() {
            super.run();
            Timber.d("RecordThread started");
            Timber.d("%s RecordThread started", getCameraTag());
            
            try {
                int width = resolutionArr[0];
@@ -535,7 +535,7 @@
                if (!isP2Mode && audioRecord != null) {
                    // 启动音频录制
                    audioRecord.startRecording();
                    Timber.d("音频录制已启动(P1模式)");
                    Timber.d("%s 音频录制已启动(P1模式)", getCameraTag());
                    
                    // 注意:不要在这里主动检查音频编码器输出格式
                    // 因为 MediaCodec 的 getOutputFormat() 在编码器启动后可能返回 null
@@ -545,10 +545,10 @@
                    audioThread = new AudioThread();
                    audioThread.start();
                } else {
                    Timber.d("P2模式:跳过音频录制和编码线程");
                    Timber.d("%s P2模式:跳过音频录制和编码线程", getCameraTag());
                }
                
                Timber.d("开始录像,分辨率: %dx%d", width, height);
                Timber.d("%s 开始录像,分辨率: %dx%d", getCameraTag(), width, height);
                
                // 记录开始时间(纳秒,用于精确时间戳)
                recordingStartTimeNs = System.nanoTime();
@@ -613,7 +613,7 @@
                    // 处理摄像头数据
                    int processResult = usbCamera.processCamera();
                    if (processResult == -1) {
                        Timber.w("processCamera返回-1,摄像头可能断开");
                        Timber.w("%s processCamera返回-1,摄像头可能断开", getCameraTag());
                        cameraExists = false;
                        notifyCallback(0, -1, "USB摄像头断开");
                        break;
@@ -635,7 +635,7 @@
                }
                
            } catch (Exception e) {
                Timber.e(e, "Error in record thread");
                Timber.e(e, "%s Error in record thread,%s", getCameraTag(),e.getMessage());
                cameraExists = false;
                notifyCallback(0, -1, "录像线程异常: " + e.getMessage());
            } finally {
@@ -645,7 +645,7 @@
                    try {
                        audioThread.join(1000);
                    } catch (InterruptedException e) {
                        Timber.e(e, "Error stopping audio thread");
                        Timber.e(e, "%s Error stopping audio thread,%s", getCameraTag(),e.getMessage());
                    }
                    audioThread = null;
                }
@@ -658,7 +658,7 @@
                    completedVideoFile = null;
                }
                Timber.d("RecordThread ended");
                Timber.d("%s RecordThread ended", getCameraTag());
            }
        }
        
@@ -901,11 +901,11 @@
        @Override
        public void run() {
            super.run();
            Timber.d("AudioThread started");
            Timber.d("%s AudioThread started", getCameraTag());
            
            // 如果音频资源未初始化(P2模式),直接退出
            if (audioRecord == null || audioEncoder == null) {
                Timber.d("AudioThread: 音频资源未初始化,退出(可能是P2模式)");
                Timber.d("%s AudioThread: 音频资源未初始化,退出(可能是P2模式)", getCameraTag());
                return;
            }
            
@@ -927,9 +927,9 @@
                }
                
            } catch (Exception e) {
                Timber.e(e, "Error in audio thread");
                Timber.e(e, "%s Error in audio thread", getCameraTag());
            } finally {
                Timber.d("AudioThread ended");
                Timber.d("%s AudioThread ended", getCameraTag());
            }
        }
        
@@ -1003,6 +1003,18 @@
    }
    
    /**
     * 获取摄像头ID标签,用于日志
     * @return "[P1]" 或 "[P2]"
     */
    private String getCameraTag() {
        if (mediaArgu != null) {
            int usbCameraId = mediaArgu.getUsbCameraId();
            return usbCameraId == 2 ? "[P2]" : "[P1]";
        }
        return "[P1]"; // 默认 P1
    }
    /**
     * 通知回调
     */
    private void notifyCallback(int type, int errCode, String message) {