ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [iOS] Kingfisher의 AnimatedImageView(UIImageView의 GIF재생)에 대하여
    앱등이에게 살충제를 뿌린다./iOS 2017. 7. 16. 16:14



    Kingfisher를 통해서 gif재생을 한번 생각해보자.




    iOS에서 gif를 재생하기 위해서는 어떻게 해야할까?

    let imageView = UIImageView()

    let images = [UIImage(), UIImage(), UIImage(), UIImage()]

    imageView.animationImages = images

    imageView.startAnimating()

    이렇게 해야하나?

    이래도 되겠다.

    근데 이러지말자.



    샘플 앱

    Kingfisher & 갓쯔위님과 샘플을 제작해보자.

    스토리보드 + 아래 코드만 사용했다.


    private func setImages() {

        let gifs = ["http://upload2.inven.co.kr/upload/2016/07/14/bbs/i10401856776.gif",

                    "https://giant.gfycat.com/BarrenPersonalIzuthrush.gif"]


        imageViewLow.kf.setImage(with: URL(string: gifs[0])!)

        imageViewHigh.kf.setImage(with: URL(string: gifs[1])!)

    }



    이제 이미지 메타데이터를 살펴보자

         


    high쯔위는 123프레임, low쯔위는 28프레임이다.

    둘 중 어떤 쯔위가 더 많은 메모리를 차지할까?

    정답은 당연히 high쯔위다. 더 많은 프레임을 메모리에 올려놓아야하기 때문이다.


    그러면 샘플앱을 iPhone6s에서 실행했을 때의 메모리를 확인해보자.

    꾸준히 148MB정도의 메모리사용량을 보여준다.

    갓쯔위긴 하지만.. 조금 부담스러운 양의 메모리이다.





    AnimatedImageView

    하지만 쯔위 움짤을 UIImageView가 아닌 Kingfisher에서 제공하는 AnimatedImageView에 올린다면 어떨까?


    그래서 스토리보드에서 UIImageView대신 AnimatedImageView만 입력해주었다.

        


    그 결과, 메모리사용은 어떻게 되었을까?


    21MB로 굉장히 많이 줄어들었다.

    하지만 CPU사용량이 0%에서 55%로 늘어났음을 알 수 있다. 

    CPU 사용은 메모리 사용에 비해 관대하기에.. AnimatedImageView는 만족스런 결과를 보여준다고 할 수 있겠다.


    그렇다면 AnimatedImageView를 쓰면 장땡인가? 라는 의문을 가질 수 있다.
    대답은 대부분은 그렇다. 라고 할 수 있겠다.


    Kingfisher에서 gif를 셋팅하는 메커니즘을 말로 표현하자면


    UIImageView

    URL에 있는 이미지 데이터를 가져와 CoreImage데이터로 UIImage를 생성하여 메모리에 올린 뒤 gif를 재생한다.


    AnimatedImageView

    URL에 있는 이미지 데이터를 가져온다. 하지만 메모리 절약을 위해 모든 프레임의 이미지를 메모리에 올려둘 수는 없다.

    따라서 현재 화면에 보여주어야 하는 프레임과 그 근처의 프레임만 메모리에 올려두어야 한다.

    즉, 항상 현재프레임과 그 근처 프레임에 해당하는 이미지 데이터를 UIImage로 만드는 작업을 끊임없이 진행한다. 그 작업은 아래 메소드에서 확인할 수 있다.


    private func prepareFrame(at index: Int) -> AnimatedFrame {

        guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, index, nil) else {

            return AnimatedFrame.null

        }

        

        // ... 중간 생략

        

        let image = Image(cgImage: imageRef)

        let scaledImage: Image?

        

        // ... 이하 생략

    }



    왜 CPU사용량이 늘어났는지 알 수 있는 대목이다.




    AnimatedImageVIew의 한계점


    gif의 속성에는 Frame이라는 중요한 속성외에도 Duration이라는 속성이 있다. 각 Frame이 다음 Frame을 보여주기 전까지 노출되는 시간을 말한다.

    어떤 gif가 100개의 프레임을 갖고 모든 프레임이 0.1초의 duration을 갖으면 gif가 모두 재생되는데 10초가 걸릴 것이고, 0.2초의 duration을 갖으면 gif가 모두 재생되는데 20초가 걸릴 것이다.


    그렇다면 같은 Frame의 gif를 재생하더라도 duration이 짧은 gif를 재생할 때, 더 높은 CPU점유율을 보일 것이라 예상할 수 있다.

    실제로 이 예상은 '참'이다.


    좀 극단적인 상황을 가정해보자.

    gif의 duration이 너무 짧아서 duration내에 imageSource로 부터 이미지를 만들어내지 못한다면 어떻게 될까?

    실제로 아이폰5이하의 장비에서 이런 현상이 자주 발생하는걸 확인했고 이 때문에 글을 남기는 것이다.


    이를 방지하고자 Kingfisher는 아래 코드를 통해 너무 짧은 duration을 보정하고 있다.

    let frameDuration = imageSource.kf.gifProperties(at: index).flatMap {

        gifInfo -> Double? in

        

        let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as Double?

        let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as Double?

        let duration = unclampedDelayTime ?? delayTime ?? 0.0

        

        /**

         http://opensource.apple.com/source/WebCore/WebCore-7600.1.25/platform/graphics/cg/ImageSourceCG.cpp

         Many annoying ads specify a 0 duration to make an image flash as quickly as

         possible. We follow Safari and Firefox's behavior and use a duration of 100 ms

         for any frames that specify a duration of <= 10 ms.

         See <rdar://problem/7689300> and <http://webkit.org/b/36082> for more information.

         

         See also: http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser.

         */

        return duration > 0.011 ? duration : 0.100

    }


    Thresh hold를 0.011로 잡고 이 보다 짧은 duration은 0.1로 바꿔버린다.


    위 예제에서 high쯔위는 0.029, low쯔위는 0.119의 duration을 갖고 있다.

    위 앱을 아이폰5에서 실행하면 high쯔위의 재생은 심각한 버퍼링을 보일 것이다.


    아이폰5이하에서 Thresh hold를 0.4 정도로 잡으면 버퍼링 문제 자체는 해결된다. 

    하지만 근본적인 해결책은 아니므로, AnimatedImageView를 사용하지 않고 UIImageView를 사용하는 것이 낫겠다. 혹은 다른 오픈소스를 찾아보던지..


Designed by Tistory.