Unsolved Animated GIF : QImage & Libav API
-
This is the way I initialize the QImage objects:
QSize size(width, height); QImage image = QImage(size, QImage::Format_RGB32);
I want to make one point clear: I already can create MOV, AVI and MP4 videos from a QImage array using this code:
int size = avpicture_get_size(PIX_FMT_YUV420P, width, height); uint8_t *pic_dat = (uint8_t *) av_malloc(size); RGBtoYUV420P(image.bits(), pic_dat, image.depth()/8, true, width, height); avpicture_fill((AVPicture *)frame, pic_dat, PIX_FMT_YUV420P, width, height) ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
My problem is ONLY related to the animated GIF format.
PS: In case you want to take a look to the whole class (without the GIF part) -> https://github.com/xtingray/tupi/blob/master/src/plugins/export/libavplugin/tlibavmoviegenerator.cpp
-
That's because these encoders supports
PIX_FMT_YUV420P
as input which is not the case for gif.One thing you could do to simplify your life a bit is to use
Format_RGB888
for your QImage so you wouldn't even need the conversion. -
Every day I'm getting closer to the solution. This is the latest GIF file I could create -> http://maefloresta.com/tmp/test.gif
Using this code:QImage img = image.convertToFormat(Format_RGB888); AVPacket pkt; av_init_packet(&pkt); pkt.flags |= AV_PKT_FLAG_KEY; pkt.stream_index = video_st->index; pkt.data = (uint8_t *) img.bits(); pkt.size = sizeof(AVFrame); av_write_frame(oc, &pkt);
As you can see it, the GIF file is not animated. Just the first frame is displayed, but the format is right. I was sneaking around inside the container using a hex editor and the 15 frames I created are there. I am missing some kind of flag or instruction to activate the animated format.
Any suggestion?
PS: I tried to use the function avcodec_encode_video2() to add the frames into the file, but it was unsuccessful. Creating my own packets (AVPacket) was the best approach.
-
I think you have to set the
loop
private property on the muxer. -
Finally, I could create an animated GIF for the first time. Nevertheless, I need to adjust the FPS parameter in some point because the animation looks too slow. I am doing it from the AVCodecContext variable, like this:
int fps = 24; AVCodecContext *c; c->time_base.den = fps; c->time_base.num = 1;
But anyway, it doesn't matter the value I set for the fps variable, the result is always the same (slow). On the other hand, this is the code I use to process every QImage object:
int got_packet = 0; AVPacket pkt; av_init_packet(&pkt); pkt.data = NULL; // packet data will be allocated by the encoder pkt.size = 0; QImage img = image.convertToFormat(Format_RGB888); avpicture_fill((AVPicture *)frame, img.bits(), AV_PIX_FMT_RGB24, w, h); int ret = avcodec_encode_video2(c, &pkt, frame, &got_packet); if (ret < 0) { tError() << "Error encoding video frame!"; return false; } if (got_packet) { pkt.stream_index = video_st->index; ret = av_interleaved_write_frame(oc, &pkt); } else { ret = 0; }
Any suggestion about how to fix the FPS issue?
-
IIRC I used something along these lines:
st->time_base.num = 1000; st->time_base.den = framerate * st->time_base.num;
st being the video AVStream for the output context and code.
The final result of the math should be the same but it gave me the correct output.
-
I was wrong! The problem wasn't the FPS parameter. The problem was the size of the GIF files, they are giants! That's the reason the browser was playing them so slowly. I am talking about tens of MBs for just few seconds.
I need to reduce the frames size,. Maybe I have to retake the sws_scale() approach, the good news is that for the first time I have a working (not so efficient) implementation :P
I guess I will have to look for more documentation about either libswscale or the scale filter. Not so sure yet if the solution is in that direction, but I will give it a try.
PS: The lack of documentation about ffmpeg/libav is intimidating :/
-
That's one good news ! :)
What are the size of your input images ?
-
The size of the input images is actually small: 500 pixels x 500 pixels. The array contains only 20 images and the size of the GIF is around 10 MB (unacceptable!).
Some guys from the FFmpeg list told me that the problem is not related to the dimension, but about either the codec or the format of the images. Additionally, I have to play with some filters to reduce the size of the outcome following some recommendations from this handy link: http://blog.pkh.me/p/21-high-quality-gif-with-ffmpeg.htmlIt seems the challenge is bigger than me, but I will keep trying to implement a better approach. As animated GIF are hot stuff again, I think there is a great potential to create this kind of files from Qt directly.
-
Thanks for sharing ! Looks pretty interesting !
I agree FFmpeg is not the easiest library to use but it is powerful. I'd recommend also digging in the implementation of the tools and examples they provide. You can get some useful hints in there and check the codecs code, you'll find also interesting stuff.
Otherwise, one thing I did once (but I've lost the code) was to write a "dumper" that gets all possible informations from the various data structure of the codecs like the possible image input formats. It gave me some insight that proved useful to tune the application and avoid e.g. useless conversions.