目录
一、问题现象
保存的本地视频非常小,无法播放。
二、问题原因分析
经打印,是编解码类没有输出数据。加强日志,经过详细的调试分析,最终定位到是ScreenEncoder的线程被意外的stop了,没有唤醒。
具体的涉案代码如下:
1.UltraSoundSDK类的reset方法
fun reset() {
...
ScreenRecordHelper.pausing.set(true)
ScreenRecordHelper.resetting_data.set(true)
ScreenRecordHelper.native_reset_finish.set(false)
UltraSoundSDKJni.getInstance().reset(handle)
//有可能这个reset方法执行很快,resetting_data就不在这里置为false了,让ImageReader自己处理,否则达不到清除缓存的作用了。
// ScreenRecordHelper.resetting_data.set(false)
ScreenRecordHelper.pausing.set(false)
ScreenRecordHelper.native_reset_finish.set(true)
...
}
ScreenRecordHelper.pausing.set(true)的时候,ScreenEncoder检测到了,会调用pauseScreenCapture方法将encoderPaused设置为true。
ScreenEncoder的run方法中,检测到encoderPaused为true会去执行pauseScreenCaptureInner方法使ScreenEncoder休眠。
如果 ScreenRecordHelper.native_reset_finish.set(true)在ScreenEncoder休眠之后再执行,会唤醒 ScreenEncoder线程,一切皆ok,但是如果不是,则ScreenEncoder在此次检查中会一直休眠,导致没有数据写入视频文件。
三、解决办法
在执行activateScreenCapture方法时记录时间戳activateScreenCaptureTime,在ScreenEncoder中判断时间差决定是否执行pauseScreenCaptureInner()方法。
if(encoderPaused){
//有可能activateScreenCaptureTime在此之前的瞬间执行了,此时不能pause!!!!!!
val detTime = abs(System.currentTimeMillis() - activateScreenCaptureTime)
if(detTime in 0..50){
encoderPaused = false
//记得重置,否则真正pause状态时则无法pause。
activateScreenCaptureTime = -1
val log = "give up to call pauseScreenCaptureInner"
Log.e(TAG, log)
CZLocalLogPrinterHolder.getInstance().localLogPrinter.forceWriteLog(log)
}else{
pauseScreenCaptureInner()
}
}
但是这样仍然会有问题,因为activateScreenCapture方法执行后,if(encoderPaused){...}可能已经开始执行了,并且已经进入到pauseScreenCaptureInner方法内部了,最后执行lock.wait()将ScreenEncoder线程休眠。
所以,要在执行pauseScreenCaptureInner执行lock.wait()前,开启一个子线程延时判断encoderPaused是否为false(activateScreenCapture设置的),并且lock.wait已经执行过了,则再次调用activateScreenCapture方法唤醒线程。
GlobalScope.launch(jobForDelay) {
delay(50L)
SuperLog.logI(TAG,"pauseScreenCaptureInner: encoderPaused is $encoderPaused")
if(!encoderPaused){
if(damonLoopThreadHandler?.isWaiting() == true){
log = "ScreenEncoder 线程意外休眠,现在唤醒它,call resumeScreenCapture。"
SuperLog.logI(TAG,log)
resumeScreenCapture()
}
}
}
log = "ScreenEncoder 线程进入休眠状态,获取锁。"
SuperLog.logI(TAG,log)
damonLoopThreadHandler?.sleep() // 线程休眠
log = "ScreenEncoder 线程被唤醒,释放锁。"
但是无法完全保证延迟子线程在damonLoopThreadHandler?.sleep()执行之后执行,这取决于延迟时间和cpu调试。
四、问题仍然存在
在reset方法里加上Thread.sleep(100ms),从源头解决时序问题。问题暂时就没有再复现了,但是根本原因还是没有找到。
fun reset() {
...
ScreenRecordHelper.pausing.set(true)
ScreenRecordHelper.resetting_data.set(true)
ScreenRecordHelper.native_reset_finish.set(false)
UltraSoundSDKJni.getInstance().reset(handle)
//有可能这个reset方法执行很快,resetting_data就不在这里置为false了,让ImageReader自己处理,否则达不到清除缓存的作用了。
//
Thread.sleep(100ms)
ScreenRecordHelper.resetting_data.set(false)
ScreenRecordHelper.pausing.set(false)
ScreenRecordHelper.native_reset_finish.set(true)
...
}
0 条评论