2002-04-08 14:32:01 +02:00
/*
2006-10-23 10:57:54 +02:00
* Animated GIF muxer
2009-01-19 16:46:40 +01:00
* Copyright ( c ) 2000 Fabrice Bellard
2002-04-08 14:32:01 +02:00
*
2006-10-07 17:30:46 +02:00
* This file is part of FFmpeg .
*
* FFmpeg is free software ; you can redistribute it and / or
2002-05-26 00:34:32 +02:00
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
2006-10-07 17:30:46 +02:00
* version 2.1 of the License , or ( at your option ) any later version .
2002-04-08 14:32:01 +02:00
*
2006-10-07 17:30:46 +02:00
* FFmpeg is distributed in the hope that it will be useful ,
2002-04-08 14:32:01 +02:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2002-05-26 00:34:32 +02:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
2002-04-08 14:32:01 +02:00
*
2002-05-26 00:34:32 +02:00
* You should have received a copy of the GNU Lesser General Public
2006-10-07 17:30:46 +02:00
* License along with FFmpeg ; if not , write to the Free Software
2006-01-12 23:43:26 +01:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2002-04-08 14:32:01 +02:00
*/
/*
* First version by Francois Revol revol @ free . fr
*
* Features and limitations :
* - currently no compression is performed ,
* in fact the size of the data is 9 / 8 the size of the image in 8 bpp
* - uses only a global standard palette
* - tested with IE 5.0 , Opera for BeOS , NetPositive ( BeOS ) , and Mozilla ( BeOS ) .
*
* Reference documents :
* http : //www.goice.co.jp/member/mo/formats/gif.html
* http : //astronomy.swin.edu.au/pbourke/dataformats/gif/
* http : //www.dcs.ed.ac.uk/home/mxr/gfx/2d/GIF89a.txt
*
* this url claims to have an LZW algorithm not covered by Unisys patent :
* http : //www.msg.net/utility/whirlgif/gifencod.html
* could help reduce the size of the files _a lot_ . . .
* some sites mentions an RLE type compression also .
*/
# include "avformat.h"
2011-06-18 09:47:46 +02:00
# include "libavutil/log.h"
# include "libavutil/opt.h"
2009-01-14 01:27:43 +01:00
/* The GIF format uses reversed order for bitstreams... */
/* at least they don't use PDP_ENDIAN :) */
# define BITSTREAM_WRITER_LE
2009-04-12 10:35:26 +02:00
# include "libavcodec/put_bits.h"
2002-04-08 14:32:01 +02:00
/* bitstream minipacket size */
# define GIF_CHUNKS 100
2005-06-18 03:52:24 +02:00
/* slows down the decoding (and some browsers don't like it) */
/* update on the 'some browsers don't like it issue from above: this was probably due to missing 'Data Sub-block Terminator' (byte 19) in the app_header */
# define GIF_ADD_APP_HEADER // required to enable looping of animated gif
2002-04-08 14:32:01 +02:00
typedef struct {
unsigned char r ;
unsigned char g ;
unsigned char b ;
} rgb_triplet ;
/* we use the standard 216 color palette */
/* this script was used to create the palette:
2005-12-17 19:14:38 +01:00
* for r in 00 33 66 99 cc ff ; do for g in 00 33 66 99 cc ff ; do echo - n " " ; for b in 00 33 66 99 cc ff ; do
2002-04-08 14:32:01 +02:00
* echo - n " { 0x$r, 0x$g, 0x$b }, " ; done ; echo " " ; done ; done
*/
2002-08-09 15:04:27 +02:00
static const rgb_triplet gif_clut [ 216 ] = {
2002-04-08 14:32:01 +02:00
{ 0x00 , 0x00 , 0x00 } , { 0x00 , 0x00 , 0x33 } , { 0x00 , 0x00 , 0x66 } , { 0x00 , 0x00 , 0x99 } , { 0x00 , 0x00 , 0xcc } , { 0x00 , 0x00 , 0xff } ,
{ 0x00 , 0x33 , 0x00 } , { 0x00 , 0x33 , 0x33 } , { 0x00 , 0x33 , 0x66 } , { 0x00 , 0x33 , 0x99 } , { 0x00 , 0x33 , 0xcc } , { 0x00 , 0x33 , 0xff } ,
{ 0x00 , 0x66 , 0x00 } , { 0x00 , 0x66 , 0x33 } , { 0x00 , 0x66 , 0x66 } , { 0x00 , 0x66 , 0x99 } , { 0x00 , 0x66 , 0xcc } , { 0x00 , 0x66 , 0xff } ,
{ 0x00 , 0x99 , 0x00 } , { 0x00 , 0x99 , 0x33 } , { 0x00 , 0x99 , 0x66 } , { 0x00 , 0x99 , 0x99 } , { 0x00 , 0x99 , 0xcc } , { 0x00 , 0x99 , 0xff } ,
{ 0x00 , 0xcc , 0x00 } , { 0x00 , 0xcc , 0x33 } , { 0x00 , 0xcc , 0x66 } , { 0x00 , 0xcc , 0x99 } , { 0x00 , 0xcc , 0xcc } , { 0x00 , 0xcc , 0xff } ,
{ 0x00 , 0xff , 0x00 } , { 0x00 , 0xff , 0x33 } , { 0x00 , 0xff , 0x66 } , { 0x00 , 0xff , 0x99 } , { 0x00 , 0xff , 0xcc } , { 0x00 , 0xff , 0xff } ,
{ 0x33 , 0x00 , 0x00 } , { 0x33 , 0x00 , 0x33 } , { 0x33 , 0x00 , 0x66 } , { 0x33 , 0x00 , 0x99 } , { 0x33 , 0x00 , 0xcc } , { 0x33 , 0x00 , 0xff } ,
{ 0x33 , 0x33 , 0x00 } , { 0x33 , 0x33 , 0x33 } , { 0x33 , 0x33 , 0x66 } , { 0x33 , 0x33 , 0x99 } , { 0x33 , 0x33 , 0xcc } , { 0x33 , 0x33 , 0xff } ,
{ 0x33 , 0x66 , 0x00 } , { 0x33 , 0x66 , 0x33 } , { 0x33 , 0x66 , 0x66 } , { 0x33 , 0x66 , 0x99 } , { 0x33 , 0x66 , 0xcc } , { 0x33 , 0x66 , 0xff } ,
{ 0x33 , 0x99 , 0x00 } , { 0x33 , 0x99 , 0x33 } , { 0x33 , 0x99 , 0x66 } , { 0x33 , 0x99 , 0x99 } , { 0x33 , 0x99 , 0xcc } , { 0x33 , 0x99 , 0xff } ,
{ 0x33 , 0xcc , 0x00 } , { 0x33 , 0xcc , 0x33 } , { 0x33 , 0xcc , 0x66 } , { 0x33 , 0xcc , 0x99 } , { 0x33 , 0xcc , 0xcc } , { 0x33 , 0xcc , 0xff } ,
{ 0x33 , 0xff , 0x00 } , { 0x33 , 0xff , 0x33 } , { 0x33 , 0xff , 0x66 } , { 0x33 , 0xff , 0x99 } , { 0x33 , 0xff , 0xcc } , { 0x33 , 0xff , 0xff } ,
{ 0x66 , 0x00 , 0x00 } , { 0x66 , 0x00 , 0x33 } , { 0x66 , 0x00 , 0x66 } , { 0x66 , 0x00 , 0x99 } , { 0x66 , 0x00 , 0xcc } , { 0x66 , 0x00 , 0xff } ,
{ 0x66 , 0x33 , 0x00 } , { 0x66 , 0x33 , 0x33 } , { 0x66 , 0x33 , 0x66 } , { 0x66 , 0x33 , 0x99 } , { 0x66 , 0x33 , 0xcc } , { 0x66 , 0x33 , 0xff } ,
{ 0x66 , 0x66 , 0x00 } , { 0x66 , 0x66 , 0x33 } , { 0x66 , 0x66 , 0x66 } , { 0x66 , 0x66 , 0x99 } , { 0x66 , 0x66 , 0xcc } , { 0x66 , 0x66 , 0xff } ,
{ 0x66 , 0x99 , 0x00 } , { 0x66 , 0x99 , 0x33 } , { 0x66 , 0x99 , 0x66 } , { 0x66 , 0x99 , 0x99 } , { 0x66 , 0x99 , 0xcc } , { 0x66 , 0x99 , 0xff } ,
{ 0x66 , 0xcc , 0x00 } , { 0x66 , 0xcc , 0x33 } , { 0x66 , 0xcc , 0x66 } , { 0x66 , 0xcc , 0x99 } , { 0x66 , 0xcc , 0xcc } , { 0x66 , 0xcc , 0xff } ,
{ 0x66 , 0xff , 0x00 } , { 0x66 , 0xff , 0x33 } , { 0x66 , 0xff , 0x66 } , { 0x66 , 0xff , 0x99 } , { 0x66 , 0xff , 0xcc } , { 0x66 , 0xff , 0xff } ,
{ 0x99 , 0x00 , 0x00 } , { 0x99 , 0x00 , 0x33 } , { 0x99 , 0x00 , 0x66 } , { 0x99 , 0x00 , 0x99 } , { 0x99 , 0x00 , 0xcc } , { 0x99 , 0x00 , 0xff } ,
{ 0x99 , 0x33 , 0x00 } , { 0x99 , 0x33 , 0x33 } , { 0x99 , 0x33 , 0x66 } , { 0x99 , 0x33 , 0x99 } , { 0x99 , 0x33 , 0xcc } , { 0x99 , 0x33 , 0xff } ,
{ 0x99 , 0x66 , 0x00 } , { 0x99 , 0x66 , 0x33 } , { 0x99 , 0x66 , 0x66 } , { 0x99 , 0x66 , 0x99 } , { 0x99 , 0x66 , 0xcc } , { 0x99 , 0x66 , 0xff } ,
{ 0x99 , 0x99 , 0x00 } , { 0x99 , 0x99 , 0x33 } , { 0x99 , 0x99 , 0x66 } , { 0x99 , 0x99 , 0x99 } , { 0x99 , 0x99 , 0xcc } , { 0x99 , 0x99 , 0xff } ,
{ 0x99 , 0xcc , 0x00 } , { 0x99 , 0xcc , 0x33 } , { 0x99 , 0xcc , 0x66 } , { 0x99 , 0xcc , 0x99 } , { 0x99 , 0xcc , 0xcc } , { 0x99 , 0xcc , 0xff } ,
{ 0x99 , 0xff , 0x00 } , { 0x99 , 0xff , 0x33 } , { 0x99 , 0xff , 0x66 } , { 0x99 , 0xff , 0x99 } , { 0x99 , 0xff , 0xcc } , { 0x99 , 0xff , 0xff } ,
{ 0xcc , 0x00 , 0x00 } , { 0xcc , 0x00 , 0x33 } , { 0xcc , 0x00 , 0x66 } , { 0xcc , 0x00 , 0x99 } , { 0xcc , 0x00 , 0xcc } , { 0xcc , 0x00 , 0xff } ,
{ 0xcc , 0x33 , 0x00 } , { 0xcc , 0x33 , 0x33 } , { 0xcc , 0x33 , 0x66 } , { 0xcc , 0x33 , 0x99 } , { 0xcc , 0x33 , 0xcc } , { 0xcc , 0x33 , 0xff } ,
{ 0xcc , 0x66 , 0x00 } , { 0xcc , 0x66 , 0x33 } , { 0xcc , 0x66 , 0x66 } , { 0xcc , 0x66 , 0x99 } , { 0xcc , 0x66 , 0xcc } , { 0xcc , 0x66 , 0xff } ,
{ 0xcc , 0x99 , 0x00 } , { 0xcc , 0x99 , 0x33 } , { 0xcc , 0x99 , 0x66 } , { 0xcc , 0x99 , 0x99 } , { 0xcc , 0x99 , 0xcc } , { 0xcc , 0x99 , 0xff } ,
{ 0xcc , 0xcc , 0x00 } , { 0xcc , 0xcc , 0x33 } , { 0xcc , 0xcc , 0x66 } , { 0xcc , 0xcc , 0x99 } , { 0xcc , 0xcc , 0xcc } , { 0xcc , 0xcc , 0xff } ,
{ 0xcc , 0xff , 0x00 } , { 0xcc , 0xff , 0x33 } , { 0xcc , 0xff , 0x66 } , { 0xcc , 0xff , 0x99 } , { 0xcc , 0xff , 0xcc } , { 0xcc , 0xff , 0xff } ,
{ 0xff , 0x00 , 0x00 } , { 0xff , 0x00 , 0x33 } , { 0xff , 0x00 , 0x66 } , { 0xff , 0x00 , 0x99 } , { 0xff , 0x00 , 0xcc } , { 0xff , 0x00 , 0xff } ,
{ 0xff , 0x33 , 0x00 } , { 0xff , 0x33 , 0x33 } , { 0xff , 0x33 , 0x66 } , { 0xff , 0x33 , 0x99 } , { 0xff , 0x33 , 0xcc } , { 0xff , 0x33 , 0xff } ,
{ 0xff , 0x66 , 0x00 } , { 0xff , 0x66 , 0x33 } , { 0xff , 0x66 , 0x66 } , { 0xff , 0x66 , 0x99 } , { 0xff , 0x66 , 0xcc } , { 0xff , 0x66 , 0xff } ,
{ 0xff , 0x99 , 0x00 } , { 0xff , 0x99 , 0x33 } , { 0xff , 0x99 , 0x66 } , { 0xff , 0x99 , 0x99 } , { 0xff , 0x99 , 0xcc } , { 0xff , 0x99 , 0xff } ,
{ 0xff , 0xcc , 0x00 } , { 0xff , 0xcc , 0x33 } , { 0xff , 0xcc , 0x66 } , { 0xff , 0xcc , 0x99 } , { 0xff , 0xcc , 0xcc } , { 0xff , 0xcc , 0xff } ,
{ 0xff , 0xff , 0x00 } , { 0xff , 0xff , 0x33 } , { 0xff , 0xff , 0x66 } , { 0xff , 0xff , 0x99 } , { 0xff , 0xff , 0xcc } , { 0xff , 0xff , 0xff } ,
} ;
2003-02-03 23:52:38 +01:00
/* GIF header */
2011-02-20 11:04:12 +01:00
static int gif_image_write_header ( AVIOContext * pb ,
2005-06-18 03:52:24 +02:00
int width , int height , int loop_count ,
uint32_t * palette )
2002-04-08 14:32:01 +02:00
{
2003-02-03 23:52:38 +01:00
int i ;
2003-02-09 17:25:21 +01:00
unsigned int v ;
2002-04-08 14:32:01 +02:00
2011-02-24 07:36:04 +01:00
avio_write ( pb , " GIF " , 3 ) ;
avio_write ( pb , " 89a " , 3 ) ;
2011-02-21 19:28:17 +01:00
avio_wl16 ( pb , width ) ;
avio_wl16 ( pb , height ) ;
2002-04-08 14:32:01 +02:00
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0xf7 ) ; /* flags: global clut, 256 entries */
avio_w8 ( pb , 0x1f ) ; /* background color index */
avio_w8 ( pb , 0 ) ; /* aspect ratio */
2002-04-08 14:32:01 +02:00
/* the global palette */
2003-02-09 17:25:21 +01:00
if ( ! palette ) {
2011-02-21 19:28:17 +01:00
avio_write ( pb , ( const unsigned char * ) gif_clut , 216 * 3 ) ;
2003-02-09 17:25:21 +01:00
for ( i = 0 ; i < ( ( 256 - 216 ) * 3 ) ; i + + )
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0 ) ;
2003-02-09 17:25:21 +01:00
} else {
for ( i = 0 ; i < 256 ; i + + ) {
v = palette [ i ] ;
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , ( v > > 16 ) & 0xff ) ;
avio_w8 ( pb , ( v > > 8 ) & 0xff ) ;
avio_w8 ( pb , ( v ) & 0xff ) ;
2003-02-09 17:25:21 +01:00
}
}
2002-04-08 14:32:01 +02:00
2005-12-22 02:10:11 +01:00
/* update: this is the 'NETSCAPE EXTENSION' that allows for looped animated gif
see http : //members.aol.com/royalef/gifabout.htm#net-extension
byte 1 : 33 ( hex 0x21 ) GIF Extension code
byte 2 : 255 ( hex 0xFF ) Application Extension Label
byte 3 : 11 ( hex ( 0x0B ) Length of Application Block
( eleven bytes of data to follow )
bytes 4 to 11 : " NETSCAPE "
bytes 12 to 14 : " 2.0 "
byte 15 : 3 ( hex 0x03 ) Length of Data Sub - Block
( three bytes of data to follow )
byte 16 : 1 ( hex 0x01 )
bytes 17 to 18 : 0 to 65535 , an unsigned integer in
lo - hi byte format . This indicate the
number of iterations the loop should
be executed .
bytes 19 : 0 ( hex 0x00 ) a Data Sub - block Terminator
*/
2005-06-18 03:52:24 +02:00
2002-04-08 14:32:01 +02:00
/* application extension header */
# ifdef GIF_ADD_APP_HEADER
2005-06-18 03:52:24 +02:00
if ( loop_count > = 0 & & loop_count < = 65535 ) {
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0x21 ) ;
avio_w8 ( pb , 0xff ) ;
avio_w8 ( pb , 0x0b ) ;
2011-02-24 07:36:04 +01:00
avio_write ( pb , " NETSCAPE2.0 " , sizeof ( " NETSCAPE2.0 " ) - 1 ) ; // bytes 4 to 14
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0x03 ) ; // byte 15
avio_w8 ( pb , 0x01 ) ; // byte 16
avio_wl16 ( pb , ( uint16_t ) loop_count ) ;
avio_w8 ( pb , 0x00 ) ; // byte 19
2005-06-18 03:52:24 +02:00
}
2002-04-08 14:32:01 +02:00
# endif
return 0 ;
}
/* this is maybe slow, but allows for extensions */
2003-02-11 17:35:48 +01:00
static inline unsigned char gif_clut_index ( uint8_t r , uint8_t g , uint8_t b )
2002-04-08 14:32:01 +02:00
{
2008-05-06 11:16:36 +02:00
return ( ( ( r ) / 47 ) % 6 ) * 6 * 6 + ( ( ( g ) / 47 ) % 6 ) * 6 + ( ( ( b ) / 47 ) % 6 ) ;
2002-04-08 14:32:01 +02:00
}
2011-02-20 11:04:12 +01:00
static int gif_image_write_image ( AVIOContext * pb ,
2003-02-03 23:52:38 +01:00
int x1 , int y1 , int width , int height ,
2003-09-11 00:37:33 +02:00
const uint8_t * buf , int linesize , int pix_fmt )
2002-04-08 14:32:01 +02:00
{
PutBitContext p ;
2003-02-11 17:35:48 +01:00
uint8_t buffer [ 200 ] ; /* 100 * 9 / 8 = 113 */
2003-02-09 17:25:21 +01:00
int i , left , w , v ;
2003-09-11 00:37:33 +02:00
const uint8_t * ptr ;
2002-04-08 14:32:01 +02:00
/* image block */
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0x2c ) ;
avio_wl16 ( pb , x1 ) ;
avio_wl16 ( pb , y1 ) ;
avio_wl16 ( pb , width ) ;
avio_wl16 ( pb , height ) ;
avio_w8 ( pb , 0x00 ) ; /* flags */
2002-04-08 14:32:01 +02:00
/* no local clut */
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0x08 ) ;
2002-04-08 14:32:01 +02:00
2003-02-03 23:52:38 +01:00
left = width * height ;
2002-04-08 14:32:01 +02:00
2003-10-13 12:59:57 +02:00
init_put_bits ( & p , buffer , 130 ) ;
2002-04-08 14:32:01 +02:00
/*
* the thing here is the bitstream is written as little packets , with a size byte before
* but it ' s still the same bitstream between packets ( no flush ! )
*/
2003-02-03 23:52:38 +01:00
ptr = buf ;
w = width ;
2002-04-08 14:32:01 +02:00
while ( left > 0 ) {
2009-01-14 01:27:43 +01:00
put_bits ( & p , 9 , 0x0100 ) ; /* clear code */
2002-04-08 14:32:01 +02:00
2004-07-28 23:56:59 +02:00
for ( i = ( left < GIF_CHUNKS ) ? left : GIF_CHUNKS ; i ; i - - ) {
2003-02-09 17:25:21 +01:00
if ( pix_fmt = = PIX_FMT_RGB24 ) {
v = gif_clut_index ( ptr [ 0 ] , ptr [ 1 ] , ptr [ 2 ] ) ;
ptr + = 3 ;
} else {
v = * ptr + + ;
}
2009-01-14 01:27:43 +01:00
put_bits ( & p , 9 , v ) ;
2003-02-03 23:52:38 +01:00
if ( - - w = = 0 ) {
w = width ;
buf + = linesize ;
ptr = buf ;
}
2002-04-08 14:32:01 +02:00
}
if ( left < = GIF_CHUNKS ) {
2009-01-14 01:27:43 +01:00
put_bits ( & p , 9 , 0x101 ) ; /* end of stream */
flush_put_bits ( & p ) ;
2002-04-08 14:32:01 +02:00
}
2009-04-13 18:59:38 +02:00
if ( put_bits_ptr ( & p ) - p . buf > 0 ) {
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , put_bits_ptr ( & p ) - p . buf ) ; /* byte count of the packet */
avio_write ( pb , p . buf , put_bits_ptr ( & p ) - p . buf ) ; /* the actual buffer */
2002-04-08 14:32:01 +02:00
p . buf_ptr = p . buf ; /* dequeue the bytes off the bitstream */
}
left - = GIF_CHUNKS ;
}
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0x00 ) ; /* end of image block */
2005-12-17 19:14:38 +01:00
2003-02-03 23:52:38 +01:00
return 0 ;
}
typedef struct {
2011-06-18 09:47:46 +02:00
AVClass * class ; /** Class for private options. */
2003-02-11 17:35:48 +01:00
int64_t time , file_time ;
uint8_t buffer [ 100 ] ; /* data chunks */
2011-06-18 09:47:46 +02:00
int loop ;
2003-02-03 23:52:38 +01:00
} GIFContext ;
static int gif_write_header ( AVFormatContext * s )
{
GIFContext * gif = s - > priv_data ;
2011-02-20 11:04:12 +01:00
AVIOContext * pb = s - > pb ;
2003-02-03 23:52:38 +01:00
AVCodecContext * enc , * video_enc ;
2011-06-18 09:47:46 +02:00
int i , width , height /*, rate*/ ;
2003-02-03 23:52:38 +01:00
/* XXX: do we reject audio streams or just ignore them ?
if ( s - > nb_streams > 1 )
return - 1 ;
*/
gif - > time = 0 ;
gif - > file_time = 0 ;
video_enc = NULL ;
for ( i = 0 ; i < s - > nb_streams ; i + + ) {
2005-07-18 00:24:36 +02:00
enc = s - > streams [ i ] - > codec ;
2010-03-31 01:30:55 +02:00
if ( enc - > codec_type ! = AVMEDIA_TYPE_AUDIO )
2003-02-03 23:52:38 +01:00
video_enc = enc ;
}
if ( ! video_enc ) {
av_free ( gif ) ;
return - 1 ;
} else {
width = video_enc - > width ;
height = video_enc - > height ;
2005-04-30 23:43:59 +02:00
// rate = video_enc->time_base.den;
2003-02-03 23:52:38 +01:00
}
2006-08-02 11:48:05 +02:00
if ( video_enc - > pix_fmt ! = PIX_FMT_RGB24 ) {
av_log ( s , AV_LOG_ERROR , " ERROR: gif only handles the rgb24 pixel format. Use -pix_fmt rgb24. \n " ) ;
2007-07-19 17:23:32 +02:00
return AVERROR ( EIO ) ;
2006-08-02 11:48:05 +02:00
}
2003-02-03 23:52:38 +01:00
2011-06-18 09:47:46 +02:00
gif_image_write_header ( pb , width , height , gif - > loop , NULL ) ;
2003-02-03 23:52:38 +01:00
2011-03-14 20:39:06 +01:00
avio_flush ( s - > pb ) ;
2003-02-03 23:52:38 +01:00
return 0 ;
}
2005-12-17 19:14:38 +01:00
static int gif_write_video ( AVFormatContext * s ,
2003-09-11 00:37:33 +02:00
AVCodecContext * enc , const uint8_t * buf , int size )
2003-02-03 23:52:38 +01:00
{
2011-02-20 11:04:12 +01:00
AVIOContext * pb = s - > pb ;
2003-02-03 23:52:38 +01:00
int jiffies ;
/* graphic control extension block */
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0x21 ) ;
avio_w8 ( pb , 0xf9 ) ;
avio_w8 ( pb , 0x04 ) ; /* block size */
avio_w8 ( pb , 0x04 ) ; /* flags */
2005-12-17 19:14:38 +01:00
2003-02-03 23:52:38 +01:00
/* 1 jiffy is 1/70 s */
/* the delay_time field indicates the number of jiffies - 1 */
/* XXX: should use delay, in order to be more accurate */
/* instead of using the same rounded value each time */
/* XXX: don't even remember if I really use it for now */
2005-04-30 23:43:59 +02:00
jiffies = ( 70 * enc - > time_base . num / enc - > time_base . den ) - 1 ;
2003-02-03 23:52:38 +01:00
2011-02-21 19:28:17 +01:00
avio_wl16 ( pb , jiffies ) ;
2003-02-03 23:52:38 +01:00
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0x1f ) ; /* transparent color index */
avio_w8 ( pb , 0x00 ) ;
2003-02-03 23:52:38 +01:00
gif_image_write_image ( pb , 0 , 0 , enc - > width , enc - > height ,
2003-02-09 17:25:21 +01:00
buf , enc - > width * 3 , PIX_FMT_RGB24 ) ;
2002-04-08 14:32:01 +02:00
2011-03-14 20:39:06 +01:00
avio_flush ( s - > pb ) ;
2002-04-08 14:32:01 +02:00
return 0 ;
}
2004-05-29 04:06:32 +02:00
static int gif_write_packet ( AVFormatContext * s , AVPacket * pkt )
2002-04-08 14:32:01 +02:00
{
2005-07-18 00:24:36 +02:00
AVCodecContext * codec = s - > streams [ pkt - > stream_index ] - > codec ;
2010-03-31 01:30:55 +02:00
if ( codec - > codec_type = = AVMEDIA_TYPE_AUDIO )
2002-04-08 14:32:01 +02:00
return 0 ; /* just ignore audio */
else
2004-05-29 04:06:32 +02:00
return gif_write_video ( s , codec , pkt - > data , pkt - > size ) ;
2002-04-08 14:32:01 +02:00
}
static int gif_write_trailer ( AVFormatContext * s )
{
2011-02-20 11:04:12 +01:00
AVIOContext * pb = s - > pb ;
2002-04-08 14:32:01 +02:00
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0x3b ) ;
2011-03-14 20:39:06 +01:00
avio_flush ( s - > pb ) ;
2002-04-08 14:32:01 +02:00
return 0 ;
}
2011-06-18 09:47:46 +02:00
# define OFFSET(x) offsetof(GIFContext, x)
# define ENC AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options [ ] = {
2011-10-04 07:38:01 +02:00
{ " loop " , " Number of times to loop the output. " , OFFSET ( loop ) , AV_OPT_TYPE_INT , { 0 } , 0 , 65535 , ENC } ,
2011-06-18 09:47:46 +02:00
{ NULL } ,
} ;
static const AVClass gif_muxer_class = {
. class_name = " GIF muxer " ,
. item_name = av_default_item_name ,
. version = LIBAVUTIL_VERSION_INT ,
. option = options ,
} ;
2011-01-25 23:03:28 +01:00
AVOutputFormat ff_gif_muxer = {
2011-07-16 22:18:12 +02:00
. name = " gif " ,
. long_name = NULL_IF_CONFIG_SMALL ( " GIF Animation " ) ,
. mime_type = " image/gif " ,
. extensions = " gif " ,
. priv_data_size = sizeof ( GIFContext ) ,
. audio_codec = CODEC_ID_NONE ,
. video_codec = CODEC_ID_RAWVIDEO ,
. write_header = gif_write_header ,
. write_packet = gif_write_packet ,
. write_trailer = gif_write_trailer ,
2011-06-18 09:47:46 +02:00
. priv_class = & gif_muxer_class ,
2002-04-08 14:32:01 +02:00
} ;