Dana
6 天以前 537f4895dc9b428e13c550be7f41275a6a4882b8
1.P2录像 无音频 只有P1有

initEncoderAndMuxer() - 根据 usbCameraId 决定是否初始化音频
P1 (usbCameraId != 2):正常初始化音频编码器和 AudioRecord
P2 (usbCameraId == 2):跳过音频初始化,audioEncoder 和 audioRecord 保持为 null
2. checkAndStartMuxer() - 根据模式决定启动条件
P1:需要视频和音频轨道都准备好才启动 muxer
P2:只要有视频轨道就启动 muxer(无音频轨道)
3. RecordThread - 根据模式决定是否启动音频线程
P1:启动 AudioRecord 和 AudioThread
P2:跳过音频录制和编码线程
4. AudioThread - 增加空值检查
如果 audioRecord == null 或 audioEncoder == null(P2 模式),直接退出
5. 文件轮换时的音频处理
文件轮换时,P2 模式同样跳过重新启动音频录制
结果
P1 录像:视频 + 音频(MP4 包含音轨)
P2 录像:仅视频(MP4 无音轨)
2个文件已修改
74 ■■■■ 已修改文件
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraRecordManager.java 69 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/util/VideoFileUtils.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraRecordManager.java
@@ -344,6 +344,10 @@
            videoEncoder.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            videoEncoder.start();
            
            // P2 摄像头(usbCameraId == 2)不录制音频,因为麦克风只有一个,只在 P1 时使用
            boolean enableAudio = (mediaArgu == null || mediaArgu.getUsbCameraId() != 2);
            if (enableAudio) {
            // 创建音频编码器
            MediaFormat audioFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, SAMPLE_RATE, CHANNEL_COUNT);
            audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
@@ -371,6 +375,14 @@
                Timber.e("AudioRecord初始化失败");
                return false;
            }
                Timber.d("音频编码器和AudioRecord初始化成功(P1模式)");
            } else {
                // P2 模式:不初始化音频相关资源
                audioEncoder = null;
                audioRecord = null;
                audioTrackIndex = -1;
                Timber.d("P2模式:跳过音频初始化,仅录制视频");
            }
            
            // 创建新的视频文件
            currentVideoFile = VideoFileUtils.getVideoFile(context, mediaArgu.getTfCardFlag(),mediaArgu.getUsbCameraId());
@@ -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,11 +513,12 @@
                    return;
                }
                
                // P2模式(usbCameraId == 2)不启动音频录制
                boolean isP2Mode = (mediaArgu != null && mediaArgu.getUsbCameraId() == 2);
                if (!isP2Mode && audioRecord != null) {
                // 启动音频录制
                if (audioRecord != null) {
                    audioRecord.startRecording();
                    Timber.d("音频录制已启动");
                }
                    Timber.d("音频录制已启动(P1模式)");
                
                // 注意:不要在这里主动检查音频编码器输出格式
                // 因为 MediaCodec 的 getOutputFormat() 在编码器启动后可能返回 null
@@ -511,6 +527,9 @@
                // 启动音频编码线程
                audioThread = new AudioThread();
                audioThread.start();
                } else {
                    Timber.d("P2模式:跳过音频录制和编码线程");
                }
                
                Timber.d("开始录像,分辨率: %dx%d", width, height);
                
@@ -547,12 +566,14 @@
                        // 重置开始时间
                        recordingStartTimeNs = System.nanoTime();
                        
                        // P2模式不启动音频录制
                        isP2Mode = (mediaArgu != null && mediaArgu.getUsbCameraId() == 2);
                        if (!isP2Mode && audioRecord != null) {
                        // 重新启动音频录制
                        if (audioRecord != null) {
                            audioRecord.startRecording();
                        }
                        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) {
        boolean isP2Mode = (mediaArgu != null && mediaArgu.getUsbCameraId() == 2);
        if (isP2Mode) {
            // P2模式:只要有视频轨道就启动
            if (!muxerStarted && videoTrackIndex >= 0) {
            try {
                mediaMuxer.start();
                muxerStarted = true;
                Timber.d("Muxer started, video track: %d, audio track: %d", videoTrackIndex, audioTrackIndex);
                    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, video track: %d, audio track: %d", videoTrackIndex, audioTrackIndex);
                Timber.d("Muxer not started yet (P2模式), video track: %d", videoTrackIndex);
            }
        } else {
            // 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; // 总采样数
app/src/main/java/com/safeluck/floatwindow/util/VideoFileUtils.java
@@ -99,10 +99,11 @@
     * 获取视频文件路径字符串 只能获取到内部相机文件的路径
     * @param context 上下文
     * @param tfCardFlag 0-内部存储,1-外部存储
     * @param cameraid 0-android系统相机 1-P1 2-P2
     * @return 文件路径字符串
     */
    public static String getVideoFilePath(Context context, int tfCardFlag) {
        File file = getVideoFile(context, tfCardFlag,0);
    public static String getVideoFilePath(Context context, int tfCardFlag,int cameraid) {
        File file = getVideoFile(context, tfCardFlag,cameraid);
        return file != null ? file.getAbsolutePath() : null;
    }
}