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

直播流??內存優化?案

開發 架構
通?降低分辨率?式為采用圖片云開發系統提供的服務。在圖片 url 中加入分辨率的參數, 直接下載相應分辨率的圖?。分辨率的設置,以圖片清晰為標準,?般設置為展 示 ImageView 的大小。這樣不消耗客戶端的資源,不會給 CPU 帶來額外的工作。

01項目背景介紹

項目中直播流每場直播由一張直播圖片作為展示入口,用于提示用戶此直播的概要。如下圖: 

圖片圖片

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

圖片圖片

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

02分析問題

經過初步分析,得出影響內存的原因有以下幾方面。

  • 圖片分辨率高,高斯模糊占用的內存越高。因為需要對進行大量模糊計算;
  • 用戶快速滑動直播流,正常的下載圖片速度會展示所有劃過的圖片,高斯模糊在圖片下載完成 block 中執行,即使劃過的直播圖,也會繼續高斯模糊直至返回。這樣會導致用戶大量無意義圖片占用大量內存;
  • 兩張 ImageView 需要在內存中加載兩張一樣的圖片,是一種內存浪費;
  • 為了用戶查看圖片的及時性和流暢性,項目中沒有設置存儲高斯模糊圖片最大占用內存。這會導致圖片內存只會在內存警告時被清除。導致高斯模糊不能獲取足夠的內存而崩潰;
  • 高斯模糊采用 vImage 方案,占用 CPU 進行高斯模糊計算,CPU 繁忙不能及時釋放內存,進一步加劇內存緊張。

03針對問題,采取措施

降低圖片分辨率

  • 通?降低分辨率?式為采用圖片云開發系統提供的服務。在圖片 url 中加入分辨率的參數, 直接下載相應分辨率的圖?。分辨率的設置,以圖片清晰為標準,?般設置為展 示 ImageView 的大小。這樣不消耗客戶端的資源,不會給 CPU 帶來額外的工作。

避免下載快速劃過的圖片


  • 判斷用戶是否快速劃過,無需下載圖片。scrollViewDidScroll 的回調頻率是小于 CADisplayLink 回調頻率的,在滾動緩慢的狀態下,離散取整可能導致 contentOffset 在某次刷新中不發生變化,也就是說 didScroll 的兩次打點間隔有一定可能大于0.0167s,是2個或者3個刷新周期。低速狀態下本身差值的差別就不大,所以使用 didScroll 打點,默認間隔是0.0167s即可。在 scrollviewDidScroll 中記錄兩次 scrollview 移動的差值,經實驗證明,速速大于60pt,流視圖圖片視覺上呈模糊狀態,故以60pt作為暴力滑動的臨界點;

  • 下一步,暴力滾動停止劃過圖片的下載。創建全局變量標記是否暴力滾動,在系統調用 cellForItem 方法時,判斷標記為是則不進行下載。非暴力滾動下,標記為否, cell 根據標記開啟下載當前的圖片;

  • 以上適用于自然減速停止的滑動。然而暴力滾動之后用戶手動突然停止,此時標記雖已及時改為否,但展示在屏幕上的圖片都處于不下載的狀態。我們在 scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate: 代理表示停止拖拽,在此時機,將展示在屏幕上的 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方法中下載圖片,設置 yesToLoad為 Yes ,即可開啟下載當屏的圖片這種情況;

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

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

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


a. 初始化高斯濾鏡,并設置參數。

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

說明:blurRadiusInPixels 決定高斯的卷積數。卷積數越高,模糊效果越明顯。forceProcessingAtSize:設置高斯過濾器的目標輸出分辨率

b. 初始始化原始圖?。GPUImage中靜態圖?的源對象為 GPUImagePicture類

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

通過傳?原圖的 image 對象?成 GPUImagePicture 。

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

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

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

圖片圖片

相應代碼如下:

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;
      }

片元著色器通過 gl_FragColor = texture2D ( 參數1:輸?對象的紋理,參數2:輸入對象的坐標) 得到當前坐標的紋理。參數1由直接取值,參數2需要計算。坐標的計算分2種情況,?清圖和?斯背景,當畫布坐標處于 leftX 和 rightX , topY 和 bottomY 之間,則繪制?清圖,應取?清圖的相應坐標。相應坐標的計算為繪制的 點從畫布坐標換算到在?清圖上的坐標(坐標都為0-1區間值)如圖:

圖片圖片

目標為計算出高清圖的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的間距在畫布的占比。
lowp  float x = (offsetX * drawableW)/targetW; //X的距離在深灰高清圖中距離。
lowp  float offsetY = textureCoordinate2.y - topY;
lowp  float y = (offsetY * drawableH)/targetH;
}

由公式 offX* drawableW = x *targetW 左右兩邊都是 X 線段的實際距離,從而得出 x 的值。同理得出 Y 的值。當畫布的坐標落在上圖淺灰色區域,采點高斯。高斯展示方式為 aspectFill. 如下圖:

圖片圖片

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

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 的計算方式和 y 值相同。到此,片元著色器的工作完成。

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

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

自此,多張合成圖片的方案就結束了,結合線上數據,本頁內存消耗節約10M左右,每張合成圖片時間節約8ms左右。

責任編輯:武曉燕 來源: 搜狐技術產品
相關推薦

2018-07-23 09:26:08

iOS內存優化

2017-02-14 17:00:39

iOSApp內存優化

2010-05-21 13:48:36

MySQL數據庫

2010-05-13 10:47:44

MySQL數據庫查詢

2022-07-20 22:55:39

直播OOM抖動

2010-08-10 10:00:57

Flex內存

2010-08-10 10:17:44

Flex內存

2010-06-11 12:32:57

MySQL數據庫查詢

2010-06-17 09:15:02

MySQL數據庫查詢

2011-07-28 10:01:19

IOS 內存優化

2011-08-10 09:06:44

內存內存優化

2023-11-03 17:02:18

抖音直播畫質優化

2013-09-16 16:56:09

AndroidBitmap內存優化

2017-03-14 18:48:06

Android性能優化內存優化

2022-07-05 08:41:03

Redis保存大數據

2021-11-23 16:00:56

DevOps價值流價值流優化

2015-09-16 15:21:23

Android性能優化內存

2016-12-22 17:21:11

Android性能優化內存泄漏

2017-12-14 14:32:30

.Net內存代碼

2016-12-13 14:16:28

直播
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91xxx在线观看 | www.日韩系列 | 亚洲精精品 | 在线免费观看黄a | 国产激情91久久精品导航 | 亚洲免费三区 | 午夜一区 | 成人午夜视频在线观看 | 日本不卡免费新一二三区 | 成人免费视频 | 成年人在线观看视频 | 四虎影院新网址 | 蜜桃视频在线观看免费视频网站www | 欧美日韩三级 | 亚洲日本欧美日韩高观看 | 国产三级国产精品 | 欧美成人h版在线观看 | 久久国产精品99久久久大便 | 欧州一区二区三区 | 国产馆| 成人精品免费视频 | 久草久草久草 | 亚洲一区精品在线 | 曰批视频在线观看 | 一区二区三区免费 | 美日韩免费视频 | 性色综合| 亚洲激情网站 | 久久久久久蜜桃一区二区 | 国产一级网站 | 精品成人一区二区 | 涩涩导航 | 中文日韩在线视频 | 国产日韩久久 | 国产999精品久久久 午夜天堂精品久久久久 | 亚洲1区 | 亚洲视频在线看 | 国产在线一区二区三区 | 国产免费xxx| 中文字幕一区二区三区在线观看 | www.国产|