SDL中文论坛
标题:
iOS下,解码远程桌面视频时app崩溃
[打印本页]
作者:
ancientcc
时间:
2020-10-11 22:27
标题:
iOS下,解码远程桌面视频时app崩溃
本帖最后由 ancientcc 于 2020-10-13 19:26 编辑
崩溃直接原因是app占用大量内存,导致被系统强制退出。为什么会占用大量内存?——aio3399j的h264在编码一些场景,iOS收到这些h264数据,并解码,解码过程中内存使用量会突然大增。举个例子,一直是占用100M,突然以数百M增加,大到超过1G,于是iOS强制退出app。以下是小结出的这种场景特征:1)图像一直没变化,2)送入aio3399j的帧率极高,像50帧甚至更高。
Android也存在这个问题,只不过在这种场景时,Android没表现出突占大量内存,而是解码花去更多时间,于是看去解码像是出现了延时。
如何解决这个问题。曾试着采用从三个方面。
一、尝试过的方法
1.1、让编码、解码h264的两种设备能互相兼容
h264格式不兼容,具体反映在sps(Sequence parameter set)的前三个字节。这三个字节通常叫profile-level-id,aio3399j编码出的这三个字节是42001f,iOS能支持是42e01f。Android也是42e01f。
查下来iOS只能支持42e01f,那只能想法设置aio3399j参数,让mediacodec以profile-level-id=42e01f为参数去编码。可惜,至少目前没找到这方面的办法。
1.2、怀疑libkosapi处理codec出来码流,把它们成帧时有问题
试过直接修改aosp提供的screencap,然后存成文件。iOS直接解码这文件,同样发生app崩溃。
1.3、图像不变时,不送给mediacodec
以rose写的app,至少目前很难做到不变的图像不送给mediacodec,像浮动控件、动画。
好在测下来,图像还是不变,但降低送给mediacodec的帧率,似乎能缓解这个问题。具体这个帧率多少是安全的,目前测试下来,或许选定25帧,而android提供了方法控制送给mediacodec的最大帧率:max-fps-to-encoder。
二、对这问题,目前采用第三种方法,在两方面做修改
2.1、libkosapi.so
launcher以参数max_fps_to_encoder=25调用kosRecordScreenLoop。
static status_t prepareEncoder(float displayFps, sp<MediaCodec>* pCodec, sp<IGraphicBufferProducer>* pBufferProducer)
{
...
sp<AMessage> format = new AMessage;
format->setInt32("width", gVideoWidth);
format->setInt32("height", gVideoHeight);
format->setString("mime", kMimeTypeAvc);
format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque);
format->setInt32("bitrate", gBitRate);
// 虽然会设置max-fps-to-encoder,也必须设置frame-rate,否则MediaCodec不会输图已编码数据
format->setFloat("frame-rate", displayFps);
format->setInt32("i-frame-interval", 10);
// displayFps值就是调用kosRecordScreenLoopmax_fps_to_encoder
format->setFloat("max-fps-to-encoder", displayFps);
err = codec->configure(format, NULL, NULL, MediaCodec::CONFIGURE_FLAG_ENCODE);
...
}
复制代码
顺便说下MediaCodec如何处理max-fps-to-encoder。一旦app设置了max-fps-to-encoder,MediaCodec会创建一个FrameDropper对象,当收收到新图像时,FrameDropper的shouldDrop方法判断当前是否要丢弃该图像。这丢掉的图像,自然不会进入后绪的编码模块。
bool FrameDropper::shouldDrop(int64_t timeUs) {
if (mMinIntervalUs <= 0) {
return false;
}
if (mDesiredMinTimeUs < 0) {
mDesiredMinTimeUs = timeUs + mMinIntervalUs;
ALOGV("first frame %lld, next desired frame %lld",
(long long)timeUs, (long long)mDesiredMinTimeUs);
return false;
}
if (timeUs < (mDesiredMinTimeUs - kMaxJitterUs)) {
ALOGV("drop frame %lld, desired frame %lld, diff %lld",
(long long)timeUs, (long long)mDesiredMinTimeUs,
(long long)(mDesiredMinTimeUs - timeUs));
return true;
}
int64_t n = (timeUs - mDesiredMinTimeUs + kMaxJitterUs) / mMinIntervalUs;
mDesiredMinTimeUs += (n + 1) * mMinIntervalUs;
ALOGV("keep frame %lld, next desired frame %lld, diff %lld",
(long long)timeUs, (long long)mDesiredMinTimeUs,
(long long)(mDesiredMinTimeUs - timeUs));
return false;
}
复制代码
shouldDrop返回true表示要丢弃该图像,false则表示该图像要送入后绪编码模块进行编码。参数timeUs是以微秒为单位的当前时刻,mMinIntervalUs是根据max-fps-to-encoder算出的、以微秒为单位的间隔。mDesiredMinTimeUs表示下一帧必须>=此时刻到来,之前来的要丢弃。
mMinIntervalUs = (int64_t) (1000000.0f / maxFrameRate);
复制代码
从shouldDrop逻辑看出,送入后绪编码器的两帧之间的间隔可能小于mMinIntervalUs,但这上小于会导致紧眼帧拉大间隔,就平均来说还是会保持在最大帧率是max-fps-to-encoder。
2.1、rose
当图像没变化时,不要去调用SDL_RenderPresent。这个估计会有个漫长过程。
不管怎么说,要解决这问题,根本还是让aio3399j输出兼容iOS解码的h264码流。将来如何做到真能改aio3399j输出的h264格式,如何测试是否有效,同时做两处修改。1)rose中的CVideo::flip一定调用SDL_RenderPresent。2)max-fps-to-encoder设为一个较大值,像60。
SDP Profile-level-id解析:
https://blog.csdn.net/liang12360640/article/details/52096499
解析H264的SPS信息:
https://blog.csdn.net/lizhijian21/article/details/80982403
欢迎光临 SDL中文论坛 (http://www.libsdl.cn/bbs/)
Powered by Discuz! X3.3