Dana
6 天以前 537f4895dc9b428e13c550be7f41275a6a4882b8
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraRecordManager.java
@@ -344,32 +344,44 @@
            videoEncoder.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            videoEncoder.start();
            
            // 创建音频编码器
            MediaFormat audioFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, SAMPLE_RATE, CHANNEL_COUNT);
            audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
            audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, AUDIO_BIT_RATE);
            // P2 摄像头(usbCameraId == 2)不录制音频,因为麦克风只有一个,只在 P1 时使用
            boolean enableAudio = (mediaArgu == null || mediaArgu.getUsbCameraId() != 2);
            
            audioEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
            audioEncoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            audioEncoder.start();
            // 初始化AudioRecord
            audioBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT);
            if (audioBufferSize <= 0) {
                audioBufferSize = SAMPLE_RATE * 2; // 默认缓冲区大小
            }
            audioRecord = new AudioRecord(
                    MediaRecorder.AudioSource.MIC,
                    SAMPLE_RATE,
                    CHANNEL_CONFIG,
                    AUDIO_FORMAT,
                    audioBufferSize * 2
            );
            if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
                Timber.e("AudioRecord初始化失败");
                return false;
            if (enableAudio) {
                // 创建音频编码器
                MediaFormat audioFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, SAMPLE_RATE, CHANNEL_COUNT);
                audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
                audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, AUDIO_BIT_RATE);
                audioEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
                audioEncoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
                audioEncoder.start();
                // 初始化AudioRecord
                audioBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT);
                if (audioBufferSize <= 0) {
                    audioBufferSize = SAMPLE_RATE * 2; // 默认缓冲区大小
                }
                audioRecord = new AudioRecord(
                        MediaRecorder.AudioSource.MIC,
                        SAMPLE_RATE,
                        CHANNEL_CONFIG,
                        AUDIO_FORMAT,
                        audioBufferSize * 2
                );
                if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
                    Timber.e("AudioRecord初始化失败");
                    return false;
                }
                Timber.d("音频编码器和AudioRecord初始化成功(P1模式)");
            } else {
                // P2 模式:不初始化音频相关资源
                audioEncoder = null;
                audioRecord = null;
                audioTrackIndex = -1;
                Timber.d("P2模式:跳过音频初始化,仅录制视频");
            }
            
            // 创建新的视频文件
@@ -381,11 +393,14 @@
            
            mediaMuxer = new MediaMuxer(currentVideoFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
            videoTrackIndex = -1;
            audioTrackIndex = -1;
            if (enableAudio) {
                audioTrackIndex = -1; // 只有在启用音频时才初始化为-1,否则保持-1(表示无音频轨道)
            }
            muxerStarted = false;
            currentFileStartTime = System.currentTimeMillis();
            
            Timber.d("编码器和Muxer初始化成功,文件: %s", currentVideoFile.getAbsolutePath());
            Timber.d("编码器和Muxer初始化成功,文件: %s, 音频: %s",
                    currentVideoFile.getAbsolutePath(), enableAudio ? "启用" : "禁用");
            return true;
        } catch (Exception e) {
            Timber.e(e, "初始化编码器和Muxer失败");
@@ -498,19 +513,23 @@
                    return;
                }
                
                // 启动音频录制
                if (audioRecord != null) {
                // P2模式(usbCameraId == 2)不启动音频录制
                boolean isP2Mode = (mediaArgu != null && mediaArgu.getUsbCameraId() == 2);
                if (!isP2Mode && audioRecord != null) {
                    // 启动音频录制
                    audioRecord.startRecording();
                    Timber.d("音频录制已启动");
                    Timber.d("音频录制已启动(P1模式)");
                    // 注意:不要在这里主动检查音频编码器输出格式
                    // 因为 MediaCodec 的 getOutputFormat() 在编码器启动后可能返回 null
                    // 应该等待 INFO_OUTPUT_FORMAT_CHANGED 事件
                    // 启动音频编码线程
                    audioThread = new AudioThread();
                    audioThread.start();
                } else {
                    Timber.d("P2模式:跳过音频录制和编码线程");
                }
                // 注意:不要在这里主动检查音频编码器输出格式
                // 因为 MediaCodec 的 getOutputFormat() 在编码器启动后可能返回 null
                // 应该等待 INFO_OUTPUT_FORMAT_CHANGED 事件
                // 启动音频编码线程
                audioThread = new AudioThread();
                audioThread.start();
                
                Timber.d("开始录像,分辨率: %dx%d", width, height);
                
@@ -547,12 +566,14 @@
                        // 重置开始时间
                        recordingStartTimeNs = System.nanoTime();
                        
                        // 重新启动音频录制
                        if (audioRecord != null) {
                        // P2模式不启动音频录制
                        isP2Mode = (mediaArgu != null && mediaArgu.getUsbCameraId() == 2);
                        if (!isP2Mode && audioRecord != null) {
                            // 重新启动音频录制
                            audioRecord.startRecording();
                            audioThread = new AudioThread();
                            audioThread.start();
                        }
                        audioThread = new AudioThread();
                        audioThread.start();
                        
                        lastFileChangeTime = currentTime;
                        frameCount = 0;
@@ -678,19 +699,39 @@
    }
    
    /**
     * 检查并启动Muxer(当视频和音频轨道都准备好时)
     * 检查并启动Muxer
     * P1模式:当视频和音频轨道都准备好时启动
     * P2模式:当视频轨道准备好时即可启动(无音频轨道)
     */
    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");
        boolean isP2Mode = (mediaArgu != null && mediaArgu.getUsbCameraId() == 2);
        if (isP2Mode) {
            // P2模式:只要有视频轨道就启动
            if (!muxerStarted && videoTrackIndex >= 0) {
                try {
                    mediaMuxer.start();
                    muxerStarted = true;
                    Timber.d("Muxer started (P2模式,仅视频), video track: %d", videoTrackIndex);
                } catch (Exception e) {
                    Timber.e(e, "Failed to start muxer");
                }
            } else {
                Timber.d("Muxer not started yet (P2模式), video track: %d", videoTrackIndex);
            }
        } else {
            Timber.d("Muxer not started yet, video track: %d, audio track: %d", videoTrackIndex, audioTrackIndex);
            // P1模式:需要视频和音频轨道都准备好
            if (!muxerStarted && videoTrackIndex >= 0 && audioTrackIndex >= 0) {
                try {
                    mediaMuxer.start();
                    muxerStarted = true;
                    Timber.d("Muxer started (P1模式,视频+音频), 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 (P1模式), video track: %d, audio track: %d", videoTrackIndex, audioTrackIndex);
            }
        }
    }
    
@@ -709,6 +750,12 @@
            super.run();
            Timber.d("AudioThread started");
            
            // 如果音频资源未初始化(P2模式),直接退出
            if (audioRecord == null || audioEncoder == null) {
                Timber.d("AudioThread: 音频资源未初始化,退出(可能是P2模式)");
                return;
            }
            try {
                byte[] audioBuffer = new byte[audioBufferSize];
                long totalSamplesRead = 0; // 总采样数