glide icon indicating copy to clipboard operation
glide copied to clipboard

GIFs animate slowly with Glide 4.2

Open moxie0 opened this issue 6 years ago • 55 comments

Here is a screen capture of two gifs playing w/ Glide 3.7: fastgif

Here is a screen capture of the same two gifs playing w/ Glide 4.2: slowgif

      GlideApp.with(context)
              .load(url)
              .diskCacheStrategy(DiskCacheStrategy.ALL)
              .into(view);

This happens with any gif that I've tried, here's an example: https://media.giphy.com/media/3ohjV3cQ9lvPeCVLOg/giphy.gif

Glide Version: 4.2.0 Integration libraries: None Device/Android Version: Nexus 5, OnePlus One, Pixel

moxie0 avatar Oct 11 '17 23:10 moxie0

same as me. Can you give me some advise about the hand on code to load gif with 4.2.0? call my 18221650910 from china? RequestOptions o = new RequestOptions(); Glide.with(h.imageView3.getContext()) .load("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1508322038285&di=e0c474540228565f1271de3b4b2c56e7&imgtype=0&src=http%3A%2F%2Fwww.lia-edu.com%2Fupload%2Fimage%2F20170717%2F20170717151619_8233.gif").apply(o.diskCacheStrategy(DiskCacheStrategy.RESOURCE)).into(h.imageView3);

lbx2015 avatar Oct 18 '17 08:10 lbx2015

I'm not able to reproduce this on a Nexus 5X on 7.1.1 if I download the gifs you've pointed to. The GIFs are however pretty small.

Can you attach the files you're having trouble with directly to the issue to make sure we're testing using the same exact data?

Can you also provide the XML or at least the dimensions of the view you're loading into?

sjudd avatar Oct 28 '17 19:10 sjudd

Hmm, I think I must have made a mistake in my original issue report. I just tried building a sample app with a jarjar'd version of 3.7 and an unmodified 4.2 together in the same app which loads the same GIF into two adjacent ImageViews using two different versions of Glide, and I'm also not able to reproduce this.

What I am seeing, however, is that GIFs render in slow-motion with both Glide 3.7 and 4.2 on Android 5.0 and Android 6.0 devices (a Nexus 5 and OnePlus One, respectively), but render at normal speed on an Android 8.0 device (Pixel).

I tried a sample app with a single 3.7 or 4.2 production dependency, loading a single GIF, and the results are the same. Things render fine (with both 3.7 and 4.2) on an Android 8.0 device, but not an Android 5.0 or 6.0 device.

Here's a sample layout I'm using:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center">
        
        <ImageView
                android:id="@+id/image"
                android:layout_gravity="center"
                android:layout_width="210dp"
                android:layout_height="210dp"/>

</LinearLayout>

The Glide 4.2 code:

    Glide.with(this)
         .load(R.raw.giphy)
         .apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.NONE))
         .into((ImageView)findViewById(R.id.image));

An attached GIF I've tested with: giphy

moxie0 avatar Oct 30 '17 18:10 moxie0

Thanks for the follow up.

That makes sense. The GIF decoder we use isn't necessarily as fast or optimized as it could be. We've fixed a few cases where it was slower than it needed to be, but there may be more relatively simple changes we could make. Otherwise it might require a more fundamental restructuring.

One other idea we've been looking in to is adding support for rastermill/framesequence or another third party gif decoder as an integration library.

sjudd avatar Nov 01 '17 14:11 sjudd

I ran into this issue on a Nexus 5 running 6.0.1.

When running the debug Build Variant gifs perform poorly.

Once I switched to release, gifs began performing as expected. Hope this helps out...

ssessa avatar Nov 13 '17 01:11 ssessa

I haven't found anything huge so far but two things worth noting:

  1. In Glide 4.3.0, the number of threads available for GIFs decreased to 2 or 1. These threads are used only for GIF frames which avoids delays loading frames for network operations, but reduced the number of threads available for GIF frames, especially on high end devices. Limiting the number of concurrently playing GIFs will help. I'll also add an API in Glide 4.4.0 to allow you to modify the number of threads used for animations.
  2. In Glide 4.4.0, I'll add an (experimental) API called clearOnDetach (name may change, suggestions welcome). clearOnDetach will free up resources used by ViewTargets when the View is detached from it's window and automatically restart the request if the View is ever reattached. That method is primarily meant for reducing memory usage, but it will also help reduce the number of running GIFs by pausing those as soon as they go off screen. I've seen this help significantly in RecyclerView, which doesn't set visibility for off screen views and doesn't call its RecyclerListener for every view that's off screen. I may also try to just build this in to ViewTarget so that it happens automatically.

More to come as I find it. If anyone else has any suggestions or can reproduce any particularly bad cases, I'd welcome the help.

sjudd avatar Nov 20 '17 06:11 sjudd

@ssessa Are you using proguard? Do you have optimizations enabled? If you disable proguard but otherwise leave you release variant the same, do you still see GIF performance improve on those builds?

sjudd avatar Nov 21 '17 18:11 sjudd

I've pushed a series of (hopefully) safe changes. Referencing the intermediate arrays in local variables seems to make a really large difference on API 23 devices, though I don't see the same difference on API 26. The rest of the changes are much smaller improvements.

I have a couple of larger fixes including automatic pausing of detached animatables and an option to cache color table indices for all GIF frames (one byte per changed pixel per frame vs four bytes per pixel for all pixels per frame if we cache the entire frame). I may hold off on those until 4.5 to give us some more time to test.

GIFs are difficult to test and there's lots of expected but not to spec behavior that can make things that seem like they ought to be safe unsafe.

sjudd avatar Nov 22 '17 06:11 sjudd

Realised that we are looking at the wrong place on this GIF issue. Problem isn't with Glide, but it how Android is recognising the.gif file. Try this.

Add this url into your Glide load, and it will load nicely. Although, in reality it's 7.2MB. https://media.giphy.com/media/3o6Zt7YExBfE89p6Yo/giphy.gif

In fact, because the API that Giphy uses for that image was specify the content-type as image/gif. Thus, somehow made Android recognise.gif file and handles them respectively.

TLDR Specifying content-type as image/gif will have your buttery smooth .gif. Simple indicator will be to enter your link in your browser and see whether it hosts the image on the page or prompt you to download.

  • If the .gif is hosted on the page, it'll work fine.
  • If the .gif prompted for a download, it'll have that lag we all have been dreading about.

delacrixmorgan avatar Jan 30 '18 12:01 delacrixmorgan

Hi,

I have the same problem too. In Glide 3, we fix this problem by this way.

builder.setDiskCacheService(new FifoPriorityThreadPoolExecutor(4));
builder.setResizeService(new FifoPriorityThreadPoolExecutor(4));

But we can't found these APIs in Glide 4.

KimiChiu avatar Mar 11 '18 09:03 KimiChiu

@KimiChiu http://bumptech.github.io/glide/javadocs/460/com/bumptech/glide/GlideBuilder.html#setAnimationExecutor-com.bumptech.glide.load.engine.executor.GlideExecutor-

sjudd avatar Mar 12 '18 16:03 sjudd

For me I did some invesgate Obviously this is related to hardware I tested a high resolution gif (1920*1080 120 frames 867KB) on several devices Perfect performance on: XiaoMi 6 Qualcomm 845 published 2017 OnePlus 5 Qualcomm 845 published 2017 Normal performance on: Samsung S5 Qualcomm 800 published 2014 Bad performance on: Oppo R7 plus Qualcomm 615 published 2015

The test is not only on glide , i also try to play this gif with the default way the device offered.

But if i choose another gif (400*300, 7.2MB), it performance perfectly on all devices above.

So try to compress your gif or choose play it as movie will partly solve this problem.

Sorry for my poor English, I'll repeat my words in Chinese

gif动画帧率低的问题应该不只是glide的问题,各位如果能把设备信息和gif信息一起贴上来可能会更快的明确问题是关于设备还是这个库。 我的任务是为app添加开屏动画,动画尺寸是1080p,120帧,帧率60,文件大小867kb 在小米6,一加5上面完美播放 三星s5略有延迟,大概2.1-2.2秒才能播放完毕 oppo r7 plus 需要4-5秒才能播放完毕 而且调用系统相册播放gif也是相同的结果 而我换了一个400*300分辨率,7.2mb大小的gif文件,无论是用glide还是用系统的相册播放,都很流畅。所以我猜测这个问题应该是cpu解码gif的速度跟不上播放速度导致的

最终我的解决方案是用movie来播放gif,这样可能会导致丢帧,但是至少时间长度是一致的

Eggplant-Cui avatar Apr 02 '18 02:04 Eggplant-Cui

I tried this,

builder.setAnimationExecutor( GlideExecutor.newAnimationExecutor(4, GlideExecutor.UncaughtThrowableStrategy.DEFAULT) );
builder.setDiskCacheExecutor( GlideExecutor.newDiskCacheExecutor(4, "glideDiskCacheExecutor", GlideExecutor.UncaughtThrowableStrategy.DEFAULT) );
builder.setSourceExecutor( GlideExecutor.newSourceExecutor(4, "glideSourceExecutor", GlideExecutor.UncaughtThrowableStrategy.DEFAULT) );

But it still plays in slow motion in RecyclerView. The ViewPager doesn't have this problem. The test device is Nokia 6 with Android 8.

compileSdkVersion 27
buildToolsVersion '27.0.1'
minSdkVersion 19
targetSdkVersion 26

compile 'com.github.bumptech.glide:glide:4.6.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.6.1'
compile "com.github.bumptech.glide:okhttp3-integration:4.6.1"
compile 'com.squareup.okhttp3:okhttp:3.10.0'

KimiChiu avatar Apr 06 '18 13:04 KimiChiu

Any update on this?

Svoka avatar Apr 12 '18 12:04 Svoka

Any plans to add a third-party decoder to improve gif playback?

perracodex avatar May 01 '18 20:05 perracodex

@sjudd Any update on this?

andrefrsousa avatar Jul 04 '18 14:07 andrefrsousa

Here are my conclusions so far on this matter. I created a sample app that shows a list of gif links:

"https://media3.giphy.com/media/l4pT0Y5Zr6nsdFmZq/giphy.gif"
"https://user-images.githubusercontent.com/512439/31472012-f0ca007a-aea0-11e7-882e-2b4ef259a954.gif"
"https://media.giphy.com/media/3o6Zt7YExBfE89p6Yo/giphy.gif"
"https://media.giphy.com/media/10A06pOVcpo6hW/giphy.gif"
"https://media.giphy.com/media/10D8j2EpNCXDA4/giphy.gif"
"https://media3.giphy.com/media/l4pT0Y5Zr6nsdFmZq/giphy.gif"
"https://media0.giphy.com/media/3o7WIFB3rd67FVuyOs/giphy.gif"
"https://media1.giphy.com/media/Sb7bVRgv5NJcjbLJce/giphy.gif"
"https://user-images.githubusercontent.com/512439/31472012-f0ca007a-aea0-11e7-882e-2b4ef259a954.gif"
"https://media.giphy.com/media/3o6Zt7YExBfE89p6Yo/giphy.gif"
"https://media.giphy.com/media/10A06pOVcpo6hW/giphy.gif"
"https://media.giphy.com/media/10D8j2EpNCXDA4/giphy.gif"
"https://media3.giphy.com/media/l4pT0Y5Zr6nsdFmZq/giphy.gif"
"https://media0.giphy.com/media/3o7WIFB3rd67FVuyOs/giphy.gif"
"https://media1.giphy.com/media/Sb7bVRgv5NJcjbLJce/giphy.gif"
"https://user-images.githubusercontent.com/512439/31472012-f0ca007a-aea0-11e7-882e-2b4ef259a954.gif"
"https://media.giphy.com/media/3o6Zt7YExBfE89p6Yo/giphy.gif"
"https://media.giphy.com/media/10A06pOVcpo6hW/giphy.gif"
"https://media.giphy.com/media/10D8j2EpNCXDA4/giphy.gif"

I made the test using Glide 4.7.1 and Fresco 1.9.0 on a Galaxy S6, and the results were as follow:

Glide: https://drive.google.com/open?id=1W8_JatGQ7-On3UVpiZw9ews30oc62fCf Fresco: https://drive.google.com/open?id=1puh6yClbeiUgTAwZviYkip7_T_klJBqo

There is a noticeable performance issue when processing GIF files. @sjudd any thoughts on why such a difference?

andrefrsousa avatar Jul 05 '18 09:07 andrefrsousa

@andrefrsousa can you upload the code please? I can't get it working properly, the gif is too slow. Thanks

ghost avatar Aug 22 '18 16:08 ghost

@lucasgriotto very simple: Glide

Glide.with(holder.binding.imageView)
                .asGif()
                .load(image[position])
                .into(holder.binding.imageView)
                .clearOnDetach()

Fresco

        val hierarchy = holder.binding.imageView.hierarchy
        hierarchy.fadeDuration = 0

        holder.binding.imageView.hierarchy = hierarchy
        holder.binding.imageView.controller = Fresco.newDraweeControllerBuilder()
                .setImageRequest(ImageRequest.fromUri(image[position]))
                .setAutoPlayAnimations(true)
                .build()

andrefrsousa avatar Aug 22 '18 17:08 andrefrsousa

@andrefrsousa thanks. I don't know what is wrong I can't get it working properly.

I am using Glide.

 implementation 'com.github.bumptech.glide:glide:4.7.1'
 annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'

<ImageView
        android:id="@+id/fra_home_img_loading_burger"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

mImgLoading = view.findViewById(R.id.fra_home_img_loading_burger);


 Glide.with(mImgLoading).asGif().load("https://media.giphy.com/media/3o6Zt7YExBfE89p6Yo/giphy.gif")
.into(mImgLoading).clearOnDetach();

ghost avatar Aug 22 '18 17:08 ghost

The reason Fresco is faster is because it caches all the GIF frames, and in addition uses some native help for decoding. Source code here:

Fresco's GIF decoder

Notice how all the frames get cached. And for a fast decoding it uses the android.graphics.Movie class. If you check the source of Movie.decodeStream(), you’ll see how this one uses a native method for decoding: nativeDecodeStream

Glide doesn’t cache GIF frames to reduce memory usage, plus the decoding is done purely in Java.

I think an improvement would be to give an optional setting to cache GIF frames in memory. It makes sense to not cache them by default if the GIFs are seen in a RecyclerView with lots of other images. But once a user selects/opens one specific GIF, it would be better to use an optimized version with memory cached frames. Maybe as a caution, it could also have a parameter to specify the size for cached memory frames.

A second optimization would be to also use the Movie class, or the AnimatedImageDrawable one starting at API 28, as the Movie class has just been deprecated.

perracodex avatar Aug 22 '18 17:08 perracodex

@sjudd Do you have some plans to fix the issue?

githubzjh avatar Aug 29 '18 00:08 githubzjh

Maybe this spares some time for people in the future, so what I did was ditch Glide in favor of Fresco because it loads GIFs fast.

So what I did is add Fresco:

    implementation 'com.facebook.fresco:fresco:1.9.0'
    // For animated GIF support
    implementation 'com.facebook.fresco:animated-gif:1.9.0'
    // For WebP support, including animated WebP
    implementation 'com.facebook.fresco:animated-webp:1.9.0'
    implementation 'com.facebook.fresco:webpsupport:1.9.0'

And the Fresco.initialize(context) call and stuff that it needs, and also

class GifDraweeView : SimpleDraweeView {
    private var drawableResource = 0

    constructor(context: Context) : super(context) {
        init(context, null, 0)
    }

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        init(context, attrs, 0)
    }

    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        init(context, attrs, defStyleAttr)
    }

    private fun init(context: Context, attributeSet: AttributeSet?, defStyle: Int) {
        if (attributeSet != null) {
            val a: TypedArray? = when {
                defStyle != 0 -> context.obtainStyledAttributes(attributeSet, R.styleable.GifDraweeView, defStyle, 0)
                else -> context.obtainStyledAttributes(attributeSet, R.styleable.GifDraweeView)
            }
            drawableResource = a!!.getResourceId(R.styleable.GifDraweeView_gifResourceDrawable, 0)
            a.recycle()


            if (!isInEditMode) {
                if (drawableResource != 0) {
                    val controller = Fresco.newDraweeControllerBuilder()
                        .setUri(UriUtil.getUriForResourceId(drawableResource))
                        .setAutoPlayAnimations(true)
                        .build()
                    setController(controller)
                }
            }
        }
    }
}

and

<declare-styleable name="GifDraweeView">
        <attr name="gifResourceDrawable" format="reference"/>
</declare-styleable>

And Fresco GIF rendering is not slow, so it's ok

Make sure the actualImageScaleType is fitCenter.

Zhuinden avatar Dec 06 '18 16:12 Zhuinden

Any news about this?

@Zhuinden Can you please share a full sample of just that? I also wonder how to go over each frame instead of just playing them directly into a View.

AndroidDeveloperLB avatar Dec 22 '18 08:12 AndroidDeveloperLB

I'm pretty sure you have to implement yourself what GifFrameLoader is doing in order to go through frames one by one instead of playing them directly into a View.

Check the GifDrawable source.

I didn't try using Glide 3.8.0 (or 3.7.0) to see if performance is better, but that might be an option too...

Zhuinden avatar Dec 22 '18 10:12 Zhuinden

@Zhuinden I mean "can you please share a full sample of using Fresco?" And in the same context "I also wonder how to go over each frame instead of just playing them directly into a View, in Fresco"

AndroidDeveloperLB avatar Dec 22 '18 11:12 AndroidDeveloperLB

oh I think in Fresco you'd have even tougher luck going frame by frame, I have no idea.

Zhuinden avatar Dec 22 '18 11:12 Zhuinden

@Zhuinden I see. Can you share your sample nevertheless? I had bad experience trying it myself. I want to compare with Glide myself...

AndroidDeveloperLB avatar Dec 22 '18 11:12 AndroidDeveloperLB

GIPHY wrote a blog post explaining the details of how Fresco handles GIF better and to quote the post.

Developers made it clear that performance can’t be improved, unless they rewrite their rendering engine from scratch.

https://engineering.giphy.com/giphy-android-app-and-fresco/

delacrixmorgan avatar Jan 04 '19 02:01 delacrixmorgan

Pretty major issue, this should be fixed somehow in the next release!

idish avatar Jan 24 '19 12:01 idish