编辑 | blame | 历史 | 原始文档
  • MediaMuxer 启动后不能再添加轨道

  • 视频和音频的时间戳需要正确同步

现在代码应该:

  • 正确等待两个轨道都准备好

  • 同步启动 MediaMuxer

  • 正确写入视频和音频数据

  • 避免状态冲突导致的错误

mp4文件明显时间加快了,现实世界1秒钟,可能mp4文件的视频就是半秒,所有录一分钟的视频mp4文件,视频只播到30秒就不再播了

检查并修复时间戳计算问题,可能是帧率或时间戳计算不正确:

修复时间戳计算:改用实际时间而非帧索引,确保时间戳准确:

修复时间戳计算,使用实际时间而非帧索引:

UsbCameraRecordManager.java

long frameCount = 0;

// 记录开始时间(纳秒,用于精确时间戳)

long recordingStartTimeNs = System.nanoTime();

long frameCount = 0;

UsbCameraRecordManager.java

if (videoEncoder != null && mediaMuxer != null) {

encodeFrame(buffer, frameCount, width, height);

frameCount++;

encodeFrame(buffer, frameCount, width, height);

if (videoEncoder != null && mediaMuxer != null) {

// 计算实际经过的时间(微秒)

long elapsedTimeUs = (System.nanoTime() - recordingStartTimeNs) / 1000;

encodeFrame(buffer, elapsedTimeUs, width, height);

frameCount++;

UsbCameraRecordManager.java

* 编码一帧数据

*/

* 编码一帧数据

* @param yuvData YUV数据

* @param presentationTimeUs 时间戳(微秒),基于实际开始时间

* @param width 宽度

* @param height 高度

*/

修复音频时间戳,使其与视频时间戳同步:

UsbCameraRecordManager.java

+3

/**

// 录制开始时间(纳秒),用于时间戳同步

private volatile long recordingStartTimeNs = 0;

/**

UsbCameraRecordManager.java

+1-1

// 记录开始时间(纳秒,用于精确时间戳)

long recordingStartTimeNs = System.nanoTime();

long frameCount = 0;

long recordingStartTimeNs = System.nanoTime();

// 记录开始时间(纳秒,用于精确时间戳)

recordingStartTimeNs = System.nanoTime();

long frameCount = 0;

UsbCameraRecordManager.java

+3-3

byte[] audioBuffer = new byte[audioBufferSize];

long audioFrameCount = 0;

// 编码音频数据

long audioFrameCount = 0;

byte[] audioBuffer = new byte[audioBufferSize];

long totalSamplesRead = 0; // 总采样数

// 编码音频数据

UsbCameraRecordManager.java

+7-2

* 编码音频数据

*/

private void encodeAudio(byte[] audioData, int size, long frameCount) {

private void encodeAudio(byte[] audioData, int size, long frameCount) {

* 编码音频数据

* @param audioData 音频数据

* @param size 数据大小(字节)

* @param totalSamples 总采样数(从开始到现在的累计采样数)

*/

在创建新文件时重置开始时间:

UsbCameraRecordManager.java

+3

// 重新启动音频录制

// 重置开始时间

recordingStartTimeNs = System.nanoTime();

// 重新启动音频录制

已修复时间戳计算问题。主要修改如下:修复内容:


  1. 视频时间戳使用实际时间:
  • 之前:使用 (frameIndex * 1000000) / FRAME_RATE 计算,假设固定帧率

  • 现在:使用 (System.nanoTime() - recordingStartTimeNs) / 1000,基于实际经过时间

  1. 音频时间戳基于采样数:
  • 使用 (totalSamples * 1000000) / SAMPLE_RATE 计算

  • totalSamples 是累计采样数,确保时间戳连续

  1. 添加了录制开始时间记录:
  • 使用 recordingStartTimeNs 记录开始时间(纳秒)

  • 在创建新文件时重置,确保每个文件的时间戳从 0 开始

  1. 确保时间戳同步:
  • 视频和音频都从同一个基准时间开始

  • 每个新文件的时间戳都从 0 开始

修复原理:

  • 问题原因:使用帧索引计算时间戳,假设固定帧率。实际采集帧率可能不同,导致时间戳不准确

  • 解决方案:使用实际经过的时间计算时间戳,确保时间戳与实际时间一致

现在:

  • 视频时间戳基于实际经过的时间

  • 音频时间戳基于实际读取的采样数

  • 两者都从 0 开始,确保同步

使用

      private var mediaAidlInterface: IMediaAidlInterface? = null
    private var serviceConnection: ServiceConnection? = null
    private var isServiceBound = false
    private val isServiceBoundState = mutableStateOf(false)
        serviceConnection = object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                mediaAidlInterface = IMediaAidlInterface.Stub.asInterface(service)
                isServiceBound = true
                isServiceBoundState.value = true
                Timber.d("FloatingService connected")
            }

            override fun onServiceDisconnected(name: ComponentName?) {
                mediaAidlInterface = null
                isServiceBound = false
                isServiceBoundState.value = false
                Timber.d("FloatingService disconnected")

       onBindService = { 
                            if (!isServiceBoundState.value && serviceConnection != null) {
                                val intent = Intent(this@MainActivity, FloatingService::class.java)
                                bindService(intent, serviceConnection!!, Context.BIND_AUTO_CREATE)
                                Timber.d("Binding FloatingService")
                            }
                        }, }
        }

    private fun startAndroidRecord() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot start Android record")
            return
        }

        try {
            val mediaArgu = MediaArgu().apply {
                isPush = false
                isUsedOutCamera = false // Android 内置摄像头
                codeRate = 0
                frameRate = 0
                m_screen = MediaArgu.ScreenSolution(640, 480) // 默认分辨率
                recordTime = 0
                tfCardFlag = 0 // 内部存储
            }

            mediaAidlInterface?.registerCallback(callback)
            mediaAidlInterface?.startMedia(mediaArgu)
            Timber.d("Started Android camera record")
        } catch (e: RemoteException) {
            Timber.e(e, "Error starting Android record")
        }
    }

    private fun stopAndroidRecord() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot stop Android record")
            return
        }

        try {
            mediaAidlInterface?.stopMedia()
            Timber.d("Stopped Android camera record")
        } catch (e: RemoteException) {
            Timber.e(e, "Error stopping Android record")
        }
    }

    private fun startUsbRecord() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot start USB record")
            return
        }

        try {
            val mediaArgu = MediaArgu().apply {
                isPush = false
                isUsedOutCamera = true // USB 摄像头
                codeRate = 0
                frameRate = 0
                m_screen = MediaArgu.ScreenSolution(640, 480) // 默认分辨率
                recordTime = 0
                tfCardFlag = 0 // 内部存储
            }

            mediaAidlInterface?.registerCallback(callback)
            mediaAidlInterface?.startMedia(mediaArgu)
            Timber.d("Started USB camera record")
        } catch (e: RemoteException) {
            Timber.e(e, "Error starting USB record")
        }
    }

    private fun stopUsbRecord() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot stop USB record")
            return
        }

        try {
            mediaAidlInterface?.stopMedia()
            Timber.d("Stopped USB camera record")
        } catch (e: RemoteException) {
            Timber.e(e, "Error stopping USB record")
        }
    }

    private fun startUsbPush() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot start USB push")
            return
        }

        try {
            val mediaArgu = MediaArgu().apply {
                isPush = true
                isUsedOutCamera = true // USB 摄像头
                codeRate = 0
                frameRate = 0
                m_screen = MediaArgu.ScreenSolution(640, 480) // 默认分辨率
                url = "rtmp://192.168.16.143/live/livestream" // TODO: 需要设置实际的推流地址
                userName = ""
                pwd = ""
            }

            mediaAidlInterface?.registerCallback(callback)
            mediaAidlInterface?.startMedia(mediaArgu)
            Timber.d("Started USB camera push")
        } catch (e: RemoteException) {
            Timber.e(e, "Error starting USB push")
        }
    }

    private fun stopUsbPush() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot stop USB push")
            return
        }

        try {
            mediaAidlInterface?.stopMedia()
            Timber.d("Stopped USB camera push")
        } catch (e: RemoteException) {
            Timber.e(e, "Error stopping USB push")
        }
    }

    private fun unbindServiceInternal() {
        if (isServiceBound && serviceConnection != null) {
            try {
                mediaAidlInterface?.unregisterCallback(callback)
            } catch (e: RemoteException) {
                Timber.e(e, "Error unregistering callback")
            }
            try {
                unbindService(serviceConnection!!)
                Timber.d("Unbinding FloatingService")
            } catch (e: Exception) {
                Timber.e(e, "Error unbinding service")
            }
            isServiceBound = false
            isServiceBoundState.value = false
            mediaAidlInterface = null
        }
    }

TF 卡录制(tfCardFlag == 1)
目录:<TF 根>/AnYun_VIDEO/yyMMdd/HHmmss_... .mp4
每次新建 1 分钟 MP4 前:
基于 cleanupH264Files(现为 MP4 版本):
按日期目录统计所有 MP4 总大小。
超过 5GB 或 TF 卡剩余空间 < 1GB 时,从最早日期目录开始整目录删除。
内部 Flash 录制(tfCardFlag == 0)
目录:/sdcard/AnYun_VIDEO/yyMMdd/HHmmss_... .mp4
每次新建 1 分钟 MP4 前:
基于 ensureInternalFlashSpaceForH264(现为 MP4 版本):
如果该分区剩余空间 < 800MB:
递归收集 AnYun_VIDEO 下所有 MP4。
按 lastModified 从早到晚依次删,直到 ≥ 800MB 或没有文件。

app/build/intermediates/apk_ide_redirect_file/debug/createDebugApkListingFileRedirect/redirect 81 b
app/build/intermediates/incremental/packageDebug/tmp/debug/dex-renamer-state 1 KB
app/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def 350 b
app/build/intermediates/manifest_merge_blame_file/debug/processDebugMainManifest/manifest-merger-blame-debug-report 17 KB
app/build/intermediates/manifest_merge_blame_file/debugAndroidTest/processDebugAndroidTestManifest/manifest-merger-blame-debug-androidTest-report 9 KB
app/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport 15 b
app/build/intermediates/nested_resources_validation_report/debugAndroidTest/generateDebugAndroidTestResources/nestedResourcesValidationReport 15 b
app/build/intermediates/runtime_symbol_list/debug/processDebugResources/R 17 KB
app/build/intermediates/runtime_symbol_list/debugAndroidTest/processDebugAndroidTestResources/R 11 KB
app/build/intermediates/source_set_path_map/debug/mapDebugSourceSetPaths/file-map 8 KB
app/build/intermediates/source_set_path_map/debugAndroidTest/mapDebugAndroidTestSourceSetPaths/file-map 6 KB
app/build/intermediates/stable_resource_ids_file/debug/processDebugResources/stableIds 23 KB
app/build/intermediates/stable_resource_ids_file/debugAndroidTest/processDebugAndroidTestResources/stableIds 15 KB
app/build/intermediates/symbol_list_with_package_name/debug/processDebugResources/package-aware-r 10 KB
app/build/intermediates/symbol_list_with_package_name/debugAndroidTest/processDebugAndroidTestResources/package-aware-r 7 KB
app/build/outputs/logs/manifest-merger-debug-report 51 KB
key/Verify 6 KB
readMe 11 KB
usbcameralib/.cxx/Debug/3c3w6z14/arm64-v8a/CMakeCache 17 KB
usbcameralib/.cxx/Debug/3c3w6z14/arm64-v8a/CMakeFiles/TargetDirectories 354 b
usbcameralib/.cxx/Debug/3c3w6z14/arm64-v8a/additional_project_files 0 b
usbcameralib/.cxx/Debug/3c3w6z14/arm64-v8a/build_file_index 74 b
usbcameralib/.cxx/Debug/3c3w6z14/arm64-v8a/metadata_generation_command 1 KB
usbcameralib/.cxx/Debug/3c3w6z14/arm64-v8a/symbol_folder_index 112 b
usbcameralib/.cxx/Debug/3c3w6z14/armeabi-v7a/CMakeCache 17 KB
usbcameralib/.cxx/Debug/3c3w6z14/armeabi-v7a/CMakeFiles/TargetDirectories 360 b
usbcameralib/.cxx/Debug/3c3w6z14/armeabi-v7a/additional_project_files 0 b
usbcameralib/.cxx/Debug/3c3w6z14/armeabi-v7a/build_file_index 74 b
usbcameralib/.cxx/Debug/3c3w6z14/armeabi-v7a/metadata_generation_command 1 KB
usbcameralib/.cxx/Debug/3c3w6z14/armeabi-v7a/symbol_folder_index 114 b
usbcameralib/.cxx/Debug/3c3w6z14/hash_key 1 KB
usbcameralib/.cxx/Debug/p1d1g3y1/arm64-v8a/CMakeCache 16 KB
usbcameralib/.cxx/Debug/p1d1g3y1/arm64-v8a/CMakeFiles/TargetDirectories 372 b
usbcameralib/.cxx/Debug/p1d1g3y1/arm64-v8a/android_gradle_build_command_usbcamera-lib_arm64-v8a 207 b
usbcameralib/.cxx/Debug/p1d1g3y1/arm64-v8a/android_gradle_build_stderr_usbcamera-lib_arm64-v8a 0 b
usbcameralib/.cxx/Debug/p1d1g3y1/arm64-v8a/android_gradle_build_stdout_usbcamera-lib_arm64-v8a 5 KB
usbcameralib/.cxx/Debug/p1d1g3y1/arm64-v8a/metadata_generation_command 1 KB
usbcameralib/.cxx/Debug/p1d1g3y1/armeabi-v7a/CMakeCache 16 KB
usbcameralib/.cxx/Debug/p1d1g3y1/armeabi-v7a/CMakeFiles/TargetDirectories 378 b
usbcameralib/.cxx/Debug/p1d1g3y1/armeabi-v7a/android_gradle_build_command_usbcamera-lib_armeabi-v7a 209 b
usbcameralib/.cxx/Debug/p1d1g3y1/armeabi-v7a/android_gradle_build_stderr_usbcamera-lib_armeabi-v7a 0 b
usbcameralib/.cxx/Debug/p1d1g3y1/armeabi-v7a/android_gradle_build_stdout_usbcamera-lib_armeabi-v7a 148 b
usbcameralib/.cxx/Debug/p1d1g3y1/armeabi-v7a/metadata_generation_command 1 KB
usbcameralib/.cxx/RelWithDebInfo/615t3c1v/arm64-v8a/CMakeCache 17 KB
usbcameralib/.cxx/RelWithDebInfo/615t3c1v/arm64-v8a/CMakeFiles/TargetDirectories 381 b
usbcameralib/.cxx/RelWithDebInfo/615t3c1v/arm64-v8a/additional_project_files 0 b
usbcameralib/.cxx/RelWithDebInfo/615t3c1v/arm64-v8a/build_file_index 74 b
usbcameralib/.cxx/RelWithDebInfo/615t3c1v/arm64-v8a/metadata_generation_command 1 KB
usbcameralib/.cxx/RelWithDebInfo/615t3c1v/arm64-v8a/symbol_folder_index 121 b
usbcameralib/.cxx/RelWithDebInfo/615t3c1v/hash_key 1 KB
usbcameralib/.cxx/RelWithDebInfo/o35703h5/arm64-v8a/CMakeCache 16 KB
usbcameralib/.cxx/RelWithDebInfo/o35703h5/arm64-v8a/CMakeFiles/TargetDirectories 399 b
usbcameralib/.cxx/RelWithDebInfo/o35703h5/arm64-v8a/metadata_generation_command 1 KB
usbcameralib/CMakeLists 2 KB
usbcameralib/build/intermediates/annotations_typedef_file/debug/extractDebugAnnotations/typedefs 0 b
usbcameralib/build/intermediates/compile_symbol_list/debug/generateDebugRFile/R 24 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/arm64-v8a/build_stderr_usbcamera-lib 0 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/arm64-v8a/build_stdout_usbcamera-lib 140 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/arm64-v8a/configure_stderr 447 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/arm64-v8a/configure_stdout 166 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/arm64-v8a/generate_cxx_metadata_1747_timing 141 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/arm64-v8a/generate_cxx_metadata_2004_timing 30 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/arm64-v8a/generate_cxx_metadata_2288_timing 57 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/arm64-v8a/generate_cxx_metadata_2353_timing 258 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/arm64-v8a/generate_cxx_metadata_2835_timing 30 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/arm64-v8a/generate_cxx_metadata_3089_timing 57 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/arm64-v8a/generate_cxx_metadata_3267_timing 60 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/arm64-v8a/generate_cxx_metadata_3323_timing 30 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/arm64-v8a/generate_cxx_metadata_3485_timing 30 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/armeabi-v7a/build_stderr_usbcamera-lib 0 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/armeabi-v7a/build_stdout_usbcamera-lib 142 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/armeabi-v7a/configure_stderr 447 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/armeabi-v7a/configure_stdout 168 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/armeabi-v7a/generate_cxx_metadata_2020_timing 141 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/armeabi-v7a/generate_cxx_metadata_2340_timing 207 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/armeabi-v7a/generate_cxx_metadata_2835_timing 30 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/armeabi-v7a/generate_cxx_metadata_3083_timing 57 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/armeabi-v7a/generate_cxx_metadata_3323_timing 30 b
usbcameralib/build/intermediates/cxx/Debug/3c3w6z14/logs/armeabi-v7a/generate_cxx_metadata_3485_timing 30 b
usbcameralib/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def 71 b
usbcameralib/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report 259 b
usbcameralib/build/intermediates/manifest_merge_blame_file/debugAndroidTest/processDebugAndroidTestManifest/manifest-merger-blame-debug-androidTest-report 7 KB
usbcameralib/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport 15 b
usbcameralib/build/intermediates/nested_resources_validation_report/debugAndroidTest/generateDebugAndroidTestResources/nestedResourcesValidationReport 15 b
usbcameralib/build/intermediates/runtime_symbol_list/debugAndroidTest/processDebugAndroidTestResources/R 31 b
usbcameralib/build/intermediates/source_set_path_map/debugAndroidTest/mapDebugAndroidTestSourceSetPaths/file-map 984 b
usbcameralib/build/intermediates/stable_resource_ids_file/debugAndroidTest/processDebugAndroidTestResources/stableIds 57 b
usbcameralib/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r 39 b
usbcameralib/build/intermediates/symbol_list_with_package_name/debugAndroidTest/processDebugAndroidTestResources/package-aware-r 44 b
usbcameralib/build/outputs/logs/manifest-merger-debug-report 1 KB