成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

直播流??內(nèi)存優(yōu)化?案

開(kāi)發(fā) 架構(gòu)
通?降低分辨率?式為采用圖片云開(kāi)發(fā)系統(tǒng)提供的服務(wù)。在圖片 url 中加入分辨率的參數(shù), 直接下載相應(yīng)分辨率的圖?。分辨率的設(shè)置,以圖片清晰為標(biāo)準(zhǔn),?般設(shè)置為展 示 ImageView 的大小。這樣不消耗客戶(hù)端的資源,不會(huì)給 CPU 帶來(lái)額外的工作。

01項(xiàng)目背景介紹

項(xiàng)目中直播流每場(chǎng)直播由一張直播圖片作為展示入口,用于提示用戶(hù)此直播的概要。如下圖: 

圖片圖片

然而直播圖片和容器的寬高比例出現(xiàn)不一致的情況。針對(duì)此情況,采取背景圖 contentmode展示為 aspectFill 且高斯模糊,上層高清圖為 aspectfit,給用戶(hù)一種圖片填滿(mǎn)且能清除獲取信息的視覺(jué)體驗(yàn)。如下圖:

圖片圖片

然而服務(wù)端下發(fā)直播的圖片分辨率在1000 * 2000byte左右,占用內(nèi)存大小為1000 * 2000 * 4,約為 2M 大小。資深直播用戶(hù)最多有一千場(chǎng)直播。使用 sd_webImage 下載圖片并緩存在內(nèi)存中,查看足夠多的直播封面時(shí),在iPhone 13機(jī)型,iOS15的手機(jī),滑動(dòng)到400場(chǎng)直播時(shí),就會(huì)產(chǎn)生內(nèi)存不足崩潰。且崩潰堆棧展示在進(jìn)行高斯模糊的方法中。

02分析問(wèn)題

經(jīng)過(guò)初步分析,得出影響內(nèi)存的原因有以下幾方面。

  • 圖片分辨率高,高斯模糊占用的內(nèi)存越高。因?yàn)樾枰獙?duì)進(jìn)行大量模糊計(jì)算;
  • 用戶(hù)快速滑動(dòng)直播流,正常的下載圖片速度會(huì)展示所有劃過(guò)的圖片,高斯模糊在圖片下載完成 block 中執(zhí)行,即使劃過(guò)的直播圖,也會(huì)繼續(xù)高斯模糊直至返回。這樣會(huì)導(dǎo)致用戶(hù)大量無(wú)意義圖片占用大量?jī)?nèi)存;
  • 兩張 ImageView 需要在內(nèi)存中加載兩張一樣的圖片,是一種內(nèi)存浪費(fèi);
  • 為了用戶(hù)查看圖片的及時(shí)性和流暢性,項(xiàng)目中沒(méi)有設(shè)置存儲(chǔ)高斯模糊圖片最大占用內(nèi)存。這會(huì)導(dǎo)致圖片內(nèi)存只會(huì)在內(nèi)存警告時(shí)被清除。導(dǎo)致高斯模糊不能獲取足夠的內(nèi)存而崩潰;
  • 高斯模糊采用 vImage 方案,占用 CPU 進(jìn)行高斯模糊計(jì)算,CPU 繁忙不能及時(shí)釋放內(nèi)存,進(jìn)一步加劇內(nèi)存緊張。

03針對(duì)問(wèn)題,采取措施

降低圖片分辨率

  • 通?降低分辨率?式為采用圖片云開(kāi)發(fā)系統(tǒng)提供的服務(wù)。在圖片 url 中加入分辨率的參數(shù), 直接下載相應(yīng)分辨率的圖?。分辨率的設(shè)置,以圖片清晰為標(biāo)準(zhǔn),?般設(shè)置為展 示 ImageView 的大小。這樣不消耗客戶(hù)端的資源,不會(huì)給 CPU 帶來(lái)額外的工作。

避免下載快速劃過(guò)的圖片


  • 判斷用戶(hù)是否快速劃過(guò),無(wú)需下載圖片。scrollViewDidScroll 的回調(diào)頻率是小于 CADisplayLink 回調(diào)頻率的,在滾動(dòng)緩慢的狀態(tài)下,離散取整可能導(dǎo)致 contentOffset 在某次刷新中不發(fā)生變化,也就是說(shuō) didScroll 的兩次打點(diǎn)間隔有一定可能大于0.0167s,是2個(gè)或者3個(gè)刷新周期。低速狀態(tài)下本身差值的差別就不大,所以使用 didScroll 打點(diǎn),默認(rèn)間隔是0.0167s即可。在 scrollviewDidScroll 中記錄兩次 scrollview 移動(dòng)的差值,經(jīng)實(shí)驗(yàn)證明,速速大于60pt,流視圖圖片視覺(jué)上呈模糊狀態(tài),故以60pt作為暴力滑動(dòng)的臨界點(diǎn);

  • 下一步,暴力滾動(dòng)停止劃過(guò)圖片的下載。創(chuàng)建全局變量標(biāo)記是否暴力滾動(dòng),在系統(tǒng)調(diào)用 cellForItem 方法時(shí),判斷標(biāo)記為是則不進(jìn)行下載。非暴力滾動(dòng)下,標(biāo)記為否, cell 根據(jù)標(biāo)記開(kāi)啟下載當(dāng)前的圖片;

  • 以上適用于自然減速停止的滑動(dòng)。然而暴力滾動(dòng)之后用戶(hù)手動(dòng)突然停止,此時(shí)標(biāo)記雖已及時(shí)改為否,但展示在屏幕上的圖片都處于不下載的狀態(tài)。我們?cè)?nbsp;scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate: 代理表示停止拖拽,在此時(shí)機(jī),將展示在屏幕上的 cell 重新下載圖片。具體代碼如下:

    [self.collectionView.visibleCells  enumerateObjectsUsingBlock:^(__kindof UICollectionViewCell * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
              SVPGCLiveCollectionViewCell *cell = obj;
              if (!cell.yesToLoad) {
                  cell.yesToLoad = YES;
              }
      }];

    自定義 cell ,setYesToLoad方法中下載圖片,設(shè)置 yesToLoad為 Yes ,即可開(kāi)啟下載當(dāng)屏的圖片這種情況;

  • 其他的滑動(dòng)情況:滾動(dòng)到流頂部,手動(dòng)設(shè)置 contenOffset:animated:經(jīng)測(cè)試,滾動(dòng)的速度屬于暴力滾動(dòng)。需要在 scrollViewDidScrollToTop 和 scrollViewDidEndScrollingAnimation 中設(shè)置當(dāng)屏圖片重新下載;

  • 內(nèi)存存儲(chǔ)高斯模糊的容器為 NSCache ,NSCache 提供最大儲(chǔ)存值。根據(jù)直播 tab 業(yè)務(wù)需要,每屏展示的數(shù)量大約為15張左右。存儲(chǔ)的數(shù)值需比15張略大,保證頁(yè)面的流暢度。初設(shè)值為20,每張占用的內(nèi)存在5M左右,20張存儲(chǔ)在100M,可接收范圍內(nèi);

  • 高斯模糊由 vImage 方式修改為 GPUImage 方式,使用 CPU 處理圖像, GPUImage 在 GPU 上處理 filter ,經(jīng)統(tǒng)計(jì),在 iOS13 上,處理圖片濾鏡時(shí)間比快約2倍。GPU 處理 filter 圖片需要運(yùn)行大量 openGL 代碼,GPUImage 封裝了 openGL,只需要調(diào)用接口就可以實(shí)現(xiàn) filter 。GPUImage 沒(méi)有現(xiàn)成的合成一張底部高斯,上層高清的圖片濾鏡。GPUImage 支持自定義頂點(diǎn)著色器和片元著色器實(shí)現(xiàn)濾鏡。整體思路為,使用混合濾鏡將高斯濾鏡的紋理和原圖紋理按照坐標(biāo)計(jì)算生成新的輸出。紋理鏈條如圖:


a. 初始化高斯濾鏡,并設(shè)置參數(shù)。

GPUImageGaussianBlurFilter *gaussionfilter =     [[GPUImageGaussianBlurFilter alloc] init];
        gaussionfilter.blurRadiusInPixels = 9;//數(shù)值越高,越糊。
        [gaussionfilter forceProcessingAtSize:highDefiniImage.size];

說(shuō)明:blurRadiusInPixels 決定高斯的卷積數(shù)。卷積數(shù)越高,模糊效果越明顯。forceProcessingAtSize:設(shè)置高斯過(guò)濾器的目標(biāo)輸出分辨率

b. 初始始化原始圖?。GPUImage中靜態(tài)圖?的源對(duì)象為 GPUImagePicture類(lèi)

GPUImagePicture *highImage = [[GPUImagePicture alloc] initWithImage:highDefiniImage];

通過(guò)傳?原圖的 image 對(duì)象?成 GPUImagePicture 。

c. 自定義頂點(diǎn)著色器和片元著色器創(chuàng)建自定義混合濾鏡。頂點(diǎn)著色器,是對(duì)頂點(diǎn)進(jìn)行一系列操作的著色器,頂點(diǎn)除了有最基本的位置屬性,還包含其他屬性,比如紋理,法線等。通過(guò)頂點(diǎn)著色器,顯卡就知道頂點(diǎn)應(yīng)該繪制在具體什么位置。針對(duì)每個(gè)頂點(diǎn),頂點(diǎn)著色器都會(huì)執(zhí)行一次。首先收到系統(tǒng)傳給他的數(shù)據(jù)(位置坐標(biāo)),將數(shù)據(jù)處理成后續(xù)我們需要的數(shù)據(jù)。系統(tǒng)對(duì)頂點(diǎn)著色器輸出的頂點(diǎn)數(shù)據(jù)進(jìn)行插值,并將插值結(jié)果傳遞給片段著色器。片段著色器根據(jù)插值結(jié)果計(jì)算最后屏幕上的像素顏色。 GPUImageTwoInputFilter 提供頂點(diǎn)著色器的代碼滿(mǎn)足需求,直接使用即可。頂點(diǎn)著色器的代碼如下所示:

NSString *const kGPUImageTwoInputTextureVertexShaderString = SHADER_STRING
(
 attribute vec4 position;
 attribute vec4 inputTextureCoordinate; //attribute  標(biāo)注屬性為輸入變量,inputTextureCoordinate為第一個(gè)輸入對(duì)象的坐標(biāo)
 attribute vec4 inputTextureCoordinate2; //inputTextureCoordinate2 為第二個(gè)輸入變量的坐標(biāo)
 varying vec2 textureCoordinate; // varying 標(biāo)注屬性為在vertex shader和fragment shader之間傳遞數(shù)據(jù),表示將第一個(gè)輸入對(duì)象的坐標(biāo)傳遞給片段著色器
 varying vec2 textureCoordinate2; //表示將第二個(gè)輸入對(duì)象的坐標(biāo)傳遞給片段著色器
 void main()
 {
     gl_Position = position;
     textureCoordinate = inputTextureCoordinate.xy;
     textureCoordinate2 = inputTextureCoordinate2.xy;
 }
);

GPUImage 包裝頂點(diǎn)著色器為 string,便于加載。每一行代碼的作用標(biāo)注在注釋里。片元著色器,是接收頂點(diǎn)著色器傳過(guò)來(lái)的數(shù)據(jù),進(jìn)行像素顏色計(jì)算。首先,需要傳入畫(huà)布和原圖的大小,用于計(jì)算畫(huà)布從原圖采點(diǎn)的坐標(biāo)。其次,計(jì)算變量,我們需要確定上層高清圖在畫(huà)布上的leftX 和 rightX ,topY 和 bottomY,高度 targetH,寬度 targetW 。以及底部高斯背景圖片設(shè)置為 aspectFill 的高度和寬度。以上數(shù)值的計(jì)算涉及高清圖橫圖和豎圖。見(jiàn)下圖,左圖為豎圖,右圖為橫圖:

圖片圖片

相應(yīng)代碼如下:

if (drawableW/drawableH > imageW/imageH ){//豎版
          targetW = imageW * (drawableH/imageH);

          lowp  float  left = (drawableW - targetW)/float(2);

          leftX = left/drawableW;

          rightX = (left+targetW)/drawableW;

          targetH = drawableH;

          bottomY = 1.0;

          targetHFill = imageHFill * drawableW / imageWFill;

          targetWFill = drawableW;
      }else{//橫版
          targetH = imageH * (drawableW/imageW);

          lowp float top = (drawableH - targetH)/float(2);

          topY = top/drawableH;

          bottomY = (top+targetH)/drawableH;

          targetW  = drawableW;

          rightX = 1.0;

          targetWFill = imageWFill * drawableH / imageHFill;

          targetHFill = drawableH;
      }

片元著色器通過(guò) gl_FragColor = texture2D ( 參數(shù)1:輸?對(duì)象的紋理,參數(shù)2:輸入對(duì)象的坐標(biāo)) 得到當(dāng)前坐標(biāo)的紋理。參數(shù)1由直接取值,參數(shù)2需要計(jì)算。坐標(biāo)的計(jì)算分2種情況,?清圖和?斯背景,當(dāng)畫(huà)布坐標(biāo)處于 leftX 和 rightX , topY 和 bottomY 之間,則繪制?清圖,應(yīng)取?清圖的相應(yīng)坐標(biāo)。相應(yīng)坐標(biāo)的計(jì)算為繪制的 點(diǎn)從畫(huà)布坐標(biāo)換算到在?清圖上的坐標(biāo)(坐標(biāo)都為0-1區(qū)間值)如圖:

圖片圖片

目標(biāo)為計(jì)算出高清圖的0.2,0.1。換算代碼如下:

if (textureCoordinate2.x >= leftX   && textureCoordinate2.x <= rightX && textureCoordinate2.y >= topY && textureCoordinate2.y <= bottomY) {
lowp  float offsetX = textureCoordinate2.x - leftX; //offsetx為圖中X的間距在畫(huà)布的占比。
lowp  float x = (offsetX * drawableW)/targetW; //X的距離在深灰高清圖中距離。
lowp  float offsetY = textureCoordinate2.y - topY;
lowp  float y = (offsetY * drawableH)/targetH;
}

由公式 offX* drawableW = x *targetW 左右兩邊都是 X 線段的實(shí)際距離,從而得出 x 的值。同理得出 Y 的值。當(dāng)畫(huà)布的坐標(biāo)落在上圖淺灰色區(qū)域,采點(diǎn)高斯。高斯展示方式為 aspectFill. 如下圖:

圖片圖片

粉色部分為高斯圖片的布局。高寬都會(huì)溢出被剪切一部分。圖中三角形為畫(huà)布中要繪制的點(diǎn),此點(diǎn)在高斯圖中的位置為 targetY/targetHfill。targetY = (textureCoordinate2.y * drawableH + (targetHFill - drawableH )/float(2)) 為高斯圖被剪裁的上高度+畫(huà)布繪制點(diǎn)據(jù)畫(huà)布頂部的距離。具體代碼如下:

lowp   float  y = (textureCoordinate2.y * drawableH + (targetHFill - drawableH )/float(2))/ targetHFill;
lowp  float x = (textureCoordinate2.x * drawableW + (targetWFill - drawableW )/float(2))/ targetWFill;
gl_FragColor = texture2D(inputImageTexture, vec2(x, y));

x 的計(jì)算方式和 y 值相同。到此,片元著色器的工作完成。

d. GPUImage加載著色器,并返回 UIImage 對(duì)象。自此,一張高斯背景疊加高清的圖片生成了。在直播流中,不止有一張需要合成,且合成是較耗時(shí)操作,需將合成操作放在子線程異步執(zhí)行。執(zhí)行完成后同步回到主線程展示。

e. 在使用 GPUImage 時(shí),有?個(gè)需要注意的地?。GPUImage 底層使?的 是 openGL , openGL 在后臺(tái)進(jìn)行渲染會(huì)導(dǎo)致 app 崩潰。所以需要我們退出后臺(tái)時(shí),停止 openGl 渲染。采取的方式有三種,第?種,將合成隊(duì)列在退出后臺(tái)前設(shè) 置 suspend = yes ,還未開(kāi)始的合成任務(wù)將不再執(zhí)行,直至進(jìn)入前臺(tái)后設(shè) 置 suspend = no 。然而,這種方式不能規(guī)避還在進(jìn)行中的任務(wù)。于是第?種,?定 義 NSOperation ,執(zhí)? operation 時(shí)。系統(tǒng)會(huì)調(diào)用 NSOperation 的 main 函數(shù), 在 main 函數(shù)中,寫(xiě)實(shí)現(xiàn)代碼。在每?句實(shí)現(xiàn)代碼前都判斷是否 cancled 。如果 cancled ,直接 return 。在系統(tǒng)即將進(jìn)?后臺(tái)時(shí),將在運(yùn)行的任務(wù) cancle 掉, operation 繼續(xù)執(zhí)行,監(jiān)測(cè)到已經(jīng) cancle 了,就會(huì) return 。然而,粒度還是不夠細(xì)。如果任務(wù)執(zhí)行到最后一行,且最后一行有 openGL 操作,那么就會(huì)攔截不到。第三種, GPUImage 的渲染都是放在??的?個(gè)隊(duì)列同步執(zhí)?,在接收到系統(tǒng)將進(jìn)入后臺(tái)的通知中,加入渲染隊(duì)列?個(gè)同步空任務(wù),則系統(tǒng)會(huì)執(zhí)行完空任務(wù)前的所有 任務(wù)之后,再進(jìn)入后臺(tái)。從而避免后臺(tái)渲染。

自此,多張合成圖片的方案就結(jié)束了,結(jié)合線上數(shù)據(jù),本頁(yè)內(nèi)存消耗節(jié)約10M左右,每張合成圖片時(shí)間節(jié)約8ms左右。

責(zé)任編輯:武曉燕 來(lái)源: 搜狐技術(shù)產(chǎn)品
相關(guān)推薦

2018-07-23 09:26:08

iOS內(nèi)存優(yōu)化

2017-02-14 17:00:39

iOSApp內(nèi)存優(yōu)化

2010-05-13 10:47:44

MySQL數(shù)據(jù)庫(kù)查詢(xún)

2010-05-21 13:48:36

MySQL數(shù)據(jù)庫(kù)

2022-07-20 22:55:39

直播OOM抖動(dòng)

2010-06-11 12:32:57

MySQL數(shù)據(jù)庫(kù)查詢(xún)

2010-06-17 09:15:02

MySQL數(shù)據(jù)庫(kù)查詢(xún)

2010-08-10 10:00:57

Flex內(nèi)存

2010-08-10 10:17:44

Flex內(nèi)存

2011-07-28 10:01:19

IOS 內(nèi)存優(yōu)化

2011-08-10 09:06:44

內(nèi)存內(nèi)存優(yōu)化

2023-11-03 17:02:18

抖音直播畫(huà)質(zhì)優(yōu)化

2013-09-16 16:56:09

AndroidBitmap內(nèi)存優(yōu)化

2017-03-14 18:48:06

Android性能優(yōu)化內(nèi)存優(yōu)化

2022-07-05 08:41:03

Redis保存大數(shù)據(jù)

2021-11-23 16:00:56

DevOps價(jià)值流價(jià)值流優(yōu)化

2015-09-16 15:21:23

Android性能優(yōu)化內(nèi)存

2016-12-22 17:21:11

Android性能優(yōu)化內(nèi)存泄漏

2017-12-14 14:32:30

.Net內(nèi)存代碼

2016-12-13 14:16:28

直播
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产精品国产a级 | 涩涩鲁亚洲精品一区二区 | 久久久网 | 久久一区二区精品 | 91精品国产91久久久久久密臀 | 欧美一区二区激情三区 | 91视频在线观看免费 | 精品成人av | 亚洲精品视频一区二区三区 | 一区二区三区在线观看视频 | 人人九九精 | 亚洲综合一区二区三区 | 日韩中文一区二区三区 | 激情欧美一区二区三区中文字幕 | 亚洲国产精品视频一区 | 乱一性一乱一交一视频a∨ 色爱av | 免费视频一区二区 | 久久青草av | 一级一片在线观看 | 嫩草一区二区三区 | 日本久久久久久久久 | 亚洲精品一区二区在线观看 | 亚洲aⅴ| 亚洲精品一区二区三区在线 | 日韩精品免费播放 | 日韩国产专区 | 综合九九 | 五月婷婷色 | 中文久久 | 久久最新网址 | 一级做受毛片免费大片 | 日本a∨精品中文字幕在线 亚洲91视频 | 色爱综合网 | 香蕉久久久 | 亚洲精品欧美 | 欧美日韩中文在线 | 色婷婷婷婷色 | 污视频在线免费观看 | 玖草资源| www.狠狠干 | 久久这里只有精品首页 |