Mobile Development 12 min read

Memory Optimization and GPUImage‑Based Gaussian Blur for iOS Live‑Stream Images

The iOS live‑stream app was crashing from out‑of‑memory due to high‑resolution images and CPU‑based Gaussian blur, so the solution reduced download size, skipped downloads during fast scrolling, limited cached blurred images, and switched to GPUImage‑accelerated blur with a custom composite shader, cutting memory use and processing time.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Memory Optimization and GPUImage‑Based Gaussian Blur for iOS Live‑Stream Images

The project displays a cover image for each live‑stream item. Because the original image size (1000 × 2000 px, ~4 MB) does not match the container’s aspect ratio, the UI uses contentMode = aspectFill for the blurred background and aspectFit for the high‑definition overlay.

When a user scrolls through many streams (up to 1 000), the app downloads each image with SDWebImage and keeps them in memory. On an iPhone 13 running iOS 15, scrolling past roughly 400 items triggers an out‑of‑memory crash, with the stack trace pointing to the Gaussian‑blur routine.

Analysis of the memory problem :

High‑resolution images consume a lot of RAM; each 1000 × 2000 image occupies ~4 MB.

During fast scrolling, the blur operation is executed inside a block , so even images that quickly pass off‑screen continue to be blurred, wasting memory.

Two UIImageView instances load the same image, causing duplicate memory usage.

No explicit limit is set for cached blurred images; they are only released on memory‑warning, leading to crashes when the blur cannot allocate enough memory.

The blur is performed with the CPU‑based vImage API, which keeps the CPU busy and further strains memory.

Mitigation measures :

Reduce image resolution by appending size parameters to the image URL, downloading only the needed size. This avoids unnecessary client‑side processing.

Avoid downloading images that are quickly scrolled past. Use scrollViewDidScroll (frequency lower than CADisplayLink ) to detect fast scrolling and skip image download. When scrolling slows, resume downloads.

Introduce a global flag to mark “aggressive scrolling”. In cellForItem , skip download if the flag is set; otherwise, allow it.

On scrollViewDidEndDragging:willDecelerate: , trigger a download for the cells currently visible.

Cache blurred images with NSCache , limiting the cache to about 20 items (≈ 100 MB) to keep memory usage bounded.

Replace the CPU‑based vImage blur with GPU‑accelerated GPUImage . The GPU implementation processes the blur roughly twice as fast on iOS 13.

Implement a custom composite filter using GPUImageTwoInputFilter . The vertex shader passes both texture coordinates; the fragment shader decides whether to sample the high‑definition image or the blurred background based on calculated leftX , rightX , topY , and bottomY values.

Key code snippets:

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

Vertex shader string:

attribute vec4 position;
attribute vec4 inputTextureCoordinate;  // first input
attribute vec4 inputTextureCoordinate2; // second input
varying vec2 textureCoordinate;
varying vec2 textureCoordinate2;
void main() {
    gl_Position = position;
    textureCoordinate = inputTextureCoordinate.xy;
    textureCoordinate2 = inputTextureCoordinate2.xy;
}

Fragment‑shader coordinate calculation (simplified):

if (textureCoordinate2.x >= leftX && textureCoordinate2.x <= rightX &&
    textureCoordinate2.y >= topY && textureCoordinate2.y <= bottomY) {
    float offsetX = textureCoordinate2.x - leftX;
    float x = (offsetX * drawableW) / targetW;
    float offsetY = textureCoordinate2.y - topY;
    float y = (offsetY * drawableH) / targetH;
    gl_FragColor = texture2D(inputImageTexture, vec2(x, y));
}

Finally, the composite image is rendered on a background thread and the resulting UIImage is displayed on the main thread. After applying all optimizations, the page’s memory consumption decreased by about 10 MB per view and the per‑image processing time dropped by roughly 8 ms.

iOSlive streamingimage-processingMemory OptimizationGaussian BlurGPUImage
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.