@ -53,7 +53,9 @@
# define GIF_CHUNKS 100
# define GIF_CHUNKS 100
/* slows down the decoding (and some browsers don't like it) */
/* 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 */
/* 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
# define GIF_ADD_APP_HEADER // required to enable looping of animated gif
typedef struct {
typedef struct {
@ -65,8 +67,15 @@ typedef struct {
/* we use the standard 216 color palette */
/* we use the standard 216 color palette */
/* this script was used to create the palette:
/* this script was used to create the palette:
* 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
* for r in 00 33 66 99 cc ff ; do
* echo - n " { 0x$r, 0x$g, 0x$b }, " ; done ; echo " " ; done ; done
* for g in 00 33 66 99 cc ff ; do
* echo - n " "
* for b in 00 33 66 99 cc ff ; do
* echo - n " { 0x$r, 0x$g, 0x$b }, "
* done
* echo " "
* done
* done
*/
*/
static const rgb_triplet gif_clut [ 216 ] = {
static const rgb_triplet gif_clut [ 216 ] = {
@ -109,9 +118,8 @@ static const rgb_triplet gif_clut[216] = {
} ;
} ;
/* GIF header */
/* GIF header */
static int gif_image_write_header ( AVIOContext * pb ,
static int gif_image_write_header ( AVIOContext * pb , int width , int height ,
int width , int height , int loop_count ,
int loop_count , uint32_t * palette )
uint32_t * palette )
{
{
int i ;
int i ;
unsigned int v ;
unsigned int v ;
@ -127,44 +135,45 @@ static int gif_image_write_header(AVIOContext *pb,
/* the global palette */
/* the global palette */
if ( ! palette ) {
if ( ! palette ) {
avio_write ( pb , ( const unsigned char * ) gif_clut , 216 * 3 ) ;
avio_write ( pb , ( const unsigned char * ) gif_clut , 216 * 3 ) ;
for ( i = 0 ; i < ( ( 256 - 216 ) * 3 ) ; i + + )
for ( i = 0 ; i < ( ( 256 - 216 ) * 3 ) ; i + + )
avio_w8 ( pb , 0 ) ;
avio_w8 ( pb , 0 ) ;
} else {
} else {
for ( i = 0 ; i < 256 ; i + + ) {
for ( i = 0 ; i < 256 ; i + + ) {
v = palette [ i ] ;
v = palette [ i ] ;
avio_w8 ( pb , ( v > > 16 ) & 0xff ) ;
avio_w8 ( pb , ( v > > 16 ) & 0xff ) ;
avio_w8 ( pb , ( v > > 8 ) & 0xff ) ;
avio_w8 ( pb , ( v > > 8 ) & 0xff ) ;
avio_w8 ( pb , ( v ) & 0xff ) ;
avio_w8 ( pb , ( v ) & 0xff ) ;
}
}
}
}
/* update: this is the 'NETSCAPE EXTENSION' that allows for looped animated gif
/* update: this is the 'NETSCAPE EXTENSION' that allows for looped animated
see http : //members.aol.com/royalef/gifabout.htm#net-extension
* GIF , see http : //members.aol.com/royalef/gifabout.htm#net-extension
*
byte 1 : 33 ( hex 0x21 ) GIF Extension code
* byte 1 : 33 ( hex 0x21 ) GIF Extension code
byte 2 : 255 ( hex 0xFF ) Application Extension Label
* byte 2 : 255 ( hex 0xFF ) Application Extension Label
byte 3 : 11 ( hex ( 0x0B ) Length of Application Block
* byte 3 : 11 ( hex ( 0x0B ) Length of Application Block
( eleven bytes of data to follow )
* ( eleven bytes of data to follow )
bytes 4 to 11 : " NETSCAPE "
* bytes 4 to 11 : " NETSCAPE "
bytes 12 to 14 : " 2.0 "
* bytes 12 to 14 : " 2.0 "
byte 15 : 3 ( hex 0x03 ) Length of Data Sub - Block
* byte 15 : 3 ( hex 0x03 ) Length of Data Sub - Block
( three bytes of data to follow )
* ( three bytes of data to follow )
byte 16 : 1 ( hex 0x01 )
* byte 16 : 1 ( hex 0x01 )
bytes 17 to 18 : 0 to 65535 , an unsigned integer in
* bytes 17 to 18 : 0 to 65535 , an unsigned integer in
lo - hi byte format . This indicate the
* lo - hi byte format . This indicate the
number of iterations the loop should
* number of iterations the loop should
be executed .
* be executed .
bytes 19 : 0 ( hex 0x00 ) a Data Sub - block Terminator
* bytes 19 : 0 ( hex 0x00 ) a Data Sub - block Terminator
*/
*/
/* application extension header */
/* application extension header */
# ifdef GIF_ADD_APP_HEADER
# ifdef GIF_ADD_APP_HEADER
if ( loop_count > = 0 & & loop_count < = 65535 ) {
if ( loop_count > = 0 & & loop_count < = 65535 ) {
avio_w8 ( pb , 0x21 ) ;
avio_w8 ( pb , 0x21 ) ;
avio_w8 ( pb , 0xff ) ;
avio_w8 ( pb , 0xff ) ;
avio_w8 ( pb , 0x0b ) ;
avio_w8 ( pb , 0x0b ) ;
avio_write ( pb , " NETSCAPE2.0 " , sizeof ( " NETSCAPE2.0 " ) - 1 ) ; // bytes 4 to 14
// bytes 4 to 14
avio_write ( pb , " NETSCAPE2.0 " , sizeof ( " NETSCAPE2.0 " ) - 1 ) ;
avio_w8 ( pb , 0x03 ) ; // byte 15
avio_w8 ( pb , 0x03 ) ; // byte 15
avio_w8 ( pb , 0x01 ) ; // byte 16
avio_w8 ( pb , 0x01 ) ; // byte 16
avio_wl16 ( pb , ( uint16_t ) loop_count ) ;
avio_wl16 ( pb , ( uint16_t ) loop_count ) ;
@ -180,7 +189,6 @@ static inline unsigned char gif_clut_index(uint8_t r, uint8_t g, uint8_t b)
return ( ( ( r ) / 47 ) % 6 ) * 6 * 6 + ( ( ( g ) / 47 ) % 6 ) * 6 + ( ( ( b ) / 47 ) % 6 ) ;
return ( ( ( r ) / 47 ) % 6 ) * 6 * 6 + ( ( ( g ) / 47 ) % 6 ) * 6 + ( ( ( b ) / 47 ) % 6 ) ;
}
}
static int gif_image_write_image ( AVIOContext * pb ,
static int gif_image_write_image ( AVIOContext * pb ,
int x1 , int y1 , int width , int height ,
int x1 , int y1 , int width , int height ,
const uint8_t * buf , int linesize , int pix_fmt )
const uint8_t * buf , int linesize , int pix_fmt )
@ -201,45 +209,44 @@ static int gif_image_write_image(AVIOContext *pb,
avio_w8 ( pb , 0x08 ) ;
avio_w8 ( pb , 0x08 ) ;
left = width * height ;
left = width * height ;
init_put_bits ( & p , buffer , 130 ) ;
init_put_bits ( & p , buffer , 130 ) ;
/*
/*
* the thing here is the bitstream is written as little packets , with a size byte before
* the thing here is the bitstream is written as little packets , with a size
* but it ' s still the same bitstream between packets ( no flush ! )
* byte before b ut it ' s still the same bitstream between packets ( no flush ! )
*/
*/
ptr = buf ;
ptr = buf ;
w = width ;
w = width ;
while ( left > 0 ) {
while ( left > 0 ) {
put_bits ( & p , 9 , 0x0100 ) ; /* clear code */
put_bits ( & p , 9 , 0x0100 ) ; /* clear code */
for ( i = ( left < GIF_CHUNKS ) ? left : GIF_CHUNKS ; i ; i - - ) {
for ( i = ( left < GIF_CHUNKS ) ? left : GIF_CHUNKS ; i ; i - - ) {
if ( pix_fmt = = PIX_FMT_RGB24 ) {
if ( pix_fmt = = PIX_FMT_RGB24 ) {
v = gif_clut_index ( ptr [ 0 ] , ptr [ 1 ] , ptr [ 2 ] ) ;
v = gif_clut_index ( ptr [ 0 ] , ptr [ 1 ] , ptr [ 2 ] ) ;
ptr + = 3 ;
ptr + = 3 ;
} else {
} else {
v = * ptr + + ;
v = * ptr + + ;
}
}
put_bits ( & p , 9 , v ) ;
put_bits ( & p , 9 , v ) ;
if ( - - w = = 0 ) {
if ( - - w = = 0 ) {
w = width ;
w = width ;
buf + = linesize ;
buf + = linesize ;
ptr = buf ;
ptr = buf ;
}
}
}
}
if ( left < = GIF_CHUNKS ) {
if ( left < = GIF_CHUNKS ) {
put_bits ( & p , 9 , 0x101 ) ; /* end of stream */
put_bits ( & p , 9 , 0x101 ) ; /* end of stream */
flush_put_bits ( & p ) ;
flush_put_bits ( & p ) ;
}
}
if ( put_bits_ptr ( & p ) - p . buf > 0 ) {
if ( put_bits_ptr ( & p ) - p . buf > 0 ) {
avio_w8 ( pb , put_bits_ptr ( & p ) - p . buf ) ; /* byte count of the packet */
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 */
avio_write ( pb , p . buf , put_bits_ptr ( & p ) - p . buf ) ; /* the actual buffer */
p . buf_ptr = p . buf ; /* dequeue the bytes off the bitstream */
p . buf_ptr = p . buf ; /* dequeue the bytes off the bitstream */
}
}
left - = GIF_CHUNKS ;
left - = GIF_CHUNKS ;
}
}
avio_w8 ( pb , 0x00 ) ; /* end of image block */
avio_w8 ( pb , 0x00 ) ; /* end of image block */
@ -261,14 +268,14 @@ static int gif_write_header(AVFormatContext *s)
int i , width , height /*, rate*/ ;
int i , width , height /*, rate*/ ;
/* XXX: do we reject audio streams or just ignore them ?
/* XXX: do we reject audio streams or just ignore them ?
if ( s - > nb_streams > 1 )
* if ( s - > nb_streams > 1 )
return - 1 ;
* return - 1 ;
*/
*/
gif - > time = 0 ;
gif - > time = 0 ;
gif - > file_time = 0 ;
gif - > file_time = 0 ;
video_enc = NULL ;
video_enc = NULL ;
for ( i = 0 ; i < s - > nb_streams ; i + + ) {
for ( i = 0 ; i < s - > nb_streams ; i + + ) {
enc = s - > streams [ i ] - > codec ;
enc = s - > streams [ i ] - > codec ;
if ( enc - > codec_type ! = AVMEDIA_TYPE_AUDIO )
if ( enc - > codec_type ! = AVMEDIA_TYPE_AUDIO )
video_enc = enc ;
video_enc = enc ;
@ -278,13 +285,14 @@ static int gif_write_header(AVFormatContext *s)
av_free ( gif ) ;
av_free ( gif ) ;
return - 1 ;
return - 1 ;
} else {
} else {
width = video_enc - > width ;
width = video_enc - > width ;
height = video_enc - > height ;
height = video_enc - > height ;
// rate = video_enc->time_base.den;
// rate = video_enc->time_base.den;
}
}
if ( video_enc - > pix_fmt ! = PIX_FMT_RGB24 ) {
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 " ) ;
av_log ( s , AV_LOG_ERROR ,
" ERROR: gif only handles the rgb24 pixel format. Use -pix_fmt rgb24. \n " ) ;
return AVERROR ( EIO ) ;
return AVERROR ( EIO ) ;
}
}
@ -294,8 +302,8 @@ static int gif_write_header(AVFormatContext *s)
return 0 ;
return 0 ;
}
}
static int gif_write_video ( AVFormatContext * s ,
static int gif_write_video ( AVFormatContext * s , AVCodecContext * enc ,
AVCodecContext * enc , const uint8_t * buf , int size )
const uint8_t * buf , int size )
{
{
AVIOContext * pb = s - > pb ;
AVIOContext * pb = s - > pb ;
int jiffies ;
int jiffies ;
@ -311,7 +319,7 @@ static int gif_write_video(AVFormatContext *s,
/* XXX: should use delay, in order to be more accurate */
/* XXX: should use delay, in order to be more accurate */
/* instead of using the same rounded value each time */
/* instead of using the same rounded value each time */
/* XXX: don't even remember if I really use it for now */
/* XXX: don't even remember if I really use it for now */
jiffies = ( 70 * enc - > time_base . num / enc - > time_base . den ) - 1 ;
jiffies = ( 70 * enc - > time_base . num / enc - > time_base . den ) - 1 ;
avio_wl16 ( pb , jiffies ) ;
avio_wl16 ( pb , jiffies ) ;
@ -346,7 +354,8 @@ static int gif_write_trailer(AVFormatContext *s)
# define OFFSET(x) offsetof(GIFContext, x)
# define OFFSET(x) offsetof(GIFContext, x)
# define ENC AV_OPT_FLAG_ENCODING_PARAM
# define ENC AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options [ ] = {
static const AVOption options [ ] = {
{ " loop " , " Number of times to loop the output. " , OFFSET ( loop ) , AV_OPT_TYPE_INT , { 0 } , 0 , 65535 , ENC } ,
{ " loop " , " Number of times to loop the output. " , OFFSET ( loop ) ,
AV_OPT_TYPE_INT , { 0 } , 0 , 65535 , ENC } ,
{ NULL } ,
{ NULL } ,
} ;
} ;
@ -358,15 +367,15 @@ static const AVClass gif_muxer_class = {
} ;
} ;
AVOutputFormat ff_gif_muxer = {
AVOutputFormat ff_gif_muxer = {
. name = " gif " ,
. name = " gif " ,
. long_name = NULL_IF_CONFIG_SMALL ( " GIF Animation " ) ,
. long_name = NULL_IF_CONFIG_SMALL ( " GIF Animation " ) ,
. mime_type = " image/gif " ,
. mime_type = " image/gif " ,
. extensions = " gif " ,
. extensions = " gif " ,
. priv_data_size = sizeof ( GIFContext ) ,
. priv_data_size = sizeof ( GIFContext ) ,
. audio_codec = CODEC_ID_NONE ,
. audio_codec = CODEC_ID_NONE ,
. video_codec = CODEC_ID_RAWVIDEO ,
. video_codec = CODEC_ID_RAWVIDEO ,
. write_header = gif_write_header ,
. write_header = gif_write_header ,
. write_packet = gif_write_packet ,
. write_packet = gif_write_packet ,
. write_trailer = gif_write_trailer ,
. write_trailer = gif_write_trailer ,
. priv_class = & gif_muxer_class ,
. priv_class = & gif_muxer_class ,
} ;
} ;