15

Suppose I have the URL for large animated gif and I wanted to make a youtube like activity that displays the animation in a streaming way. How do I

  1. stream in the image?
  2. get it do display with actual animation?

I know ImageView is not the answer as it only shows the first frame.

A bonus would be having access to its buffering status so I can synchronize streaming sound as well -- this is part of a YTMND viewer application. While I could create a service that transcodes the public gif files into a nicer format, I'd like the app to function without additional dependencies.

Janusz
  • 187,060
  • 113
  • 301
  • 369
rndmcnlly
  • 1,588
  • 1
  • 9
  • 18
  • 1
    Why are you using giant animated gifs, not MP4s? – Isaac Waller May 01 '09 at 01:48
  • Somehow giant animated gifs got to be *the thing* on YTMND. It's precisely because they are unpleasant to work with that the existing browser doesn't support them, thus the need for a specialized viewing app. – rndmcnlly May 01 '09 at 19:28
  • There is the example http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/BitmapDecode.html –  Apr 25 '11 at 01:58

4 Answers4

12

The general sketch of the solution is to use employ custom View which draws asks a Movie to draw itself to the Canvas periodically.

The first step is building the Movie instance. There is factory called decodeStream that can make a movie given an InputStream but it isn't enough to use the stream from a UrlConnection. If you try this you will get an IOException when the movie loader tries to call reset on the stream. The hack, unfortunate as it is, is to use a separated BufferedInputStream with a manually-set mark to tell it to save enough data that reset won't fail. Luckily, the URLConnection can tell us how much data to expect. I say this hack is unfortunate because it effectively requires the entire image to be buffered in memory (which is no problem for desktop apps, but it is a serious issue on a memory-constrained mobile device).

Here is a snip of the Movie setup code:

URL url = new URL(gifSource);
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
bis.mark(conn.getContentLength());
Movie movie = Movie.decodeStream(bis);
bis.close();

Next, you need to create a view that will display this Movie. A subclass of View with a custom onDraw will do the trick (assuming it has access to the Movie you created with the previous code).

@Override protected void onDraw(Canvas canvas) {
    if(movie != null) {
        long now = android.os.SystemClock.uptimeMillis();
        int dur = Math.max(movie.duration(), 1); // is it really animated?
        int pos = (int)(now % dur);
        movie.setTime(pos);
        movie.draw(canvas, x, y);
    }
}

The view won't trigger itself to be redrawn without help, and blindly calling invalidate() at the end of onDraw is just an energy waste. In another thread (probably the one you used to download the image data), you can post messages to the main thread, asking for the view to be invalidated at a steady (but not insane) pace.

Handler handler = new Handler();
new Thread() {
    @Override public void run() {
        // ... setup the movie (using the code from above)
        // ... create and display the custom view, passing the movie

        while(!Thread.currentThread().isInterrupted()) {
            handler.post(new Runnable() {
                public void run(){
                    view.invalidate();
                }
            });
            try {
                Thread.sleep(50); // yields 20 fps
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}.start();

A really nice solution would have all sorts of sweet progress bars and error checking, but the core is here.

rndmcnlly
  • 1,588
  • 1
  • 9
  • 18
  • Is this still working in android versions bigger 1.5? See http://stackoverflow.com/questions/1383246/android-animated-gif-cupcake for more info... – Janusz Jul 02 '10 at 06:47
1

Glide prove most easy and efficient way to achieve this with just one line of code :-

Glide.with(getApplicationContext())
            .load(Uri.parse("https://media1.giphy.com/media/5ziaphcUWGKPu/200.gif"))
            .asGif().placeholder(R.drawable.ic_launcher).crossFade()
            .into(imageView);

Result is here :-

enter image description here

Gif taken from here http://giphy.com/search/big-gif/3

Add jar from here :- https://github.com/bumptech/glide/releases

Hitesh Sahu
  • 41,955
  • 17
  • 205
  • 154
1

Did you try BitmapDecode?

There's an example in the API Demos here.

JRL
  • 76,767
  • 18
  • 98
  • 146
  • I used BitmapDecode when I was experimenting with the `ImageView` previously, but the use of a `Movie` and drawing directly to the `Canvas` I had not considered. I will look into this. – rndmcnlly May 02 '09 at 21:53
  • Ok, I got it to work now, but `BitmapDecode` isn't involed at all. – rndmcnlly May 05 '09 at 06:21
0

Maybe AnimationDrawable could work for you? EDIT: Not if you want to load from a URL like this post is about. Sorry

http://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html

Depending on the complexity of the GIF i.e. if it is a simple loading/progress indicator you could break the GIF apart, save each image separately and use the Android Framework's AnimationDrawable.

For simple progress bars this may be less error prone, maybe even more performant.

concept
  • 761
  • 2
  • 10
  • 16