|
|
|
@ -166,45 +166,10 @@ static void gif_flush_put_bits_rev(PutBitContext *s) |
|
|
|
|
|
|
|
|
|
/* !RevPutBitContext */ |
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
|
UINT8 buffer[100]; /* data chunks */ |
|
|
|
|
INT64 time, file_time; |
|
|
|
|
} GIFContext; |
|
|
|
|
|
|
|
|
|
static int gif_write_header(AVFormatContext *s) |
|
|
|
|
/* GIF header */ |
|
|
|
|
static int gif_image_write_header(ByteIOContext *pb, int width, int height) |
|
|
|
|
{ |
|
|
|
|
GIFContext *gif = s->priv_data; |
|
|
|
|
ByteIOContext *pb = &s->pb; |
|
|
|
|
AVCodecContext *enc, *video_enc; |
|
|
|
|
int i, width, height, rate; |
|
|
|
|
|
|
|
|
|
/* 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++) { |
|
|
|
|
enc = &s->streams[i]->codec; |
|
|
|
|
if (enc->codec_type != CODEC_TYPE_AUDIO) |
|
|
|
|
video_enc = enc; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!video_enc) { |
|
|
|
|
av_free(gif); |
|
|
|
|
return -1; |
|
|
|
|
} else { |
|
|
|
|
width = video_enc->width; |
|
|
|
|
height = video_enc->height; |
|
|
|
|
rate = video_enc->frame_rate; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* XXX: is it allowed ? seems to work so far... */ |
|
|
|
|
video_enc->pix_fmt = PIX_FMT_RGB24; |
|
|
|
|
|
|
|
|
|
/* GIF header */ |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
put_tag(pb, "GIF"); |
|
|
|
|
put_tag(pb, "89a"); |
|
|
|
@ -233,8 +198,6 @@ static int gif_write_header(AVFormatContext *s) |
|
|
|
|
put_byte(pb, 0x00); |
|
|
|
|
put_byte(pb, 0x00); |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
put_flush_packet(&s->pb); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -244,60 +207,28 @@ static inline unsigned char gif_clut_index(rgb_triplet *clut, UINT8 r, UINT8 g, |
|
|
|
|
return ((((r)/47)%6)*6*6+(((g)/47)%6)*6+(((b)/47)%6)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* chunk writer callback */ |
|
|
|
|
/* !!! XXX:deprecated
|
|
|
|
|
static void gif_put_chunk(void *pbctx, UINT8 *buffer, int count) |
|
|
|
|
{ |
|
|
|
|
ByteIOContext *pb = (ByteIOContext *)pbctx; |
|
|
|
|
put_byte(pb, (UINT8)count); |
|
|
|
|
put_buffer(pb, buffer, count); |
|
|
|
|
} |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
static int gif_write_video(AVFormatContext *s,
|
|
|
|
|
AVCodecContext *enc, UINT8 *buf, int size) |
|
|
|
|
static int gif_image_write_image(ByteIOContext *pb,
|
|
|
|
|
int x1, int y1, int width, int height, |
|
|
|
|
uint8_t *buf, int linesize) |
|
|
|
|
{ |
|
|
|
|
ByteIOContext *pb = &s->pb; |
|
|
|
|
GIFContext *gif = s->priv_data; |
|
|
|
|
int i, left, jiffies; |
|
|
|
|
INT64 delay; |
|
|
|
|
PutBitContext p; |
|
|
|
|
UINT8 buffer[200]; /* 100 * 9 / 8 = 113 */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* graphic control extension block */ |
|
|
|
|
put_byte(pb, 0x21); |
|
|
|
|
put_byte(pb, 0xf9); |
|
|
|
|
put_byte(pb, 0x04); /* block size */ |
|
|
|
|
put_byte(pb, 0x04); /* flags */ |
|
|
|
|
|
|
|
|
|
/* 1 jiffy is 1/70 s */ |
|
|
|
|
/* the delay_time field indicates the number of jiffies - 1 */ |
|
|
|
|
delay = gif->file_time - gif->time; |
|
|
|
|
|
|
|
|
|
/* 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 */ |
|
|
|
|
jiffies = (70*FRAME_RATE_BASE/enc->frame_rate) - 1; |
|
|
|
|
|
|
|
|
|
put_le16(pb, jiffies); |
|
|
|
|
|
|
|
|
|
put_byte(pb, 0x1f); /* transparent color index */ |
|
|
|
|
put_byte(pb, 0x00); |
|
|
|
|
|
|
|
|
|
int i, left, w; |
|
|
|
|
uint8_t *ptr; |
|
|
|
|
/* image block */ |
|
|
|
|
|
|
|
|
|
put_byte(pb, 0x2c); |
|
|
|
|
put_le16(pb, 0); |
|
|
|
|
put_le16(pb, 0); |
|
|
|
|
put_le16(pb, enc->width); |
|
|
|
|
put_le16(pb, enc->height); |
|
|
|
|
put_le16(pb, x1); |
|
|
|
|
put_le16(pb, y1); |
|
|
|
|
put_le16(pb, width); |
|
|
|
|
put_le16(pb, height); |
|
|
|
|
put_byte(pb, 0x00); /* flags */ |
|
|
|
|
/* no local clut */ |
|
|
|
|
|
|
|
|
|
put_byte(pb, 0x08); |
|
|
|
|
|
|
|
|
|
left=size/3; |
|
|
|
|
left= width * height; |
|
|
|
|
|
|
|
|
|
init_put_bits(&p, buffer, 130, NULL, NULL); |
|
|
|
|
|
|
|
|
@ -305,14 +236,20 @@ static int gif_write_video(AVFormatContext *s, |
|
|
|
|
* 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 !) |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
ptr = buf; |
|
|
|
|
w = width; |
|
|
|
|
while(left>0) { |
|
|
|
|
|
|
|
|
|
gif_put_bits_rev(&p, 9, 0x0100); /* clear code */ |
|
|
|
|
|
|
|
|
|
for(i=0;i<GIF_CHUNKS;i++) { |
|
|
|
|
gif_put_bits_rev(&p, 9, gif_clut_index(NULL, *buf, buf[1], buf[2])); |
|
|
|
|
buf+=3; |
|
|
|
|
gif_put_bits_rev(&p, 9, gif_clut_index(NULL, ptr[0], ptr[1], ptr[2])); |
|
|
|
|
ptr+=3; |
|
|
|
|
if (--w == 0) { |
|
|
|
|
w = width; |
|
|
|
|
buf += linesize; |
|
|
|
|
ptr = buf; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(left<=GIF_CHUNKS) { |
|
|
|
@ -331,6 +268,93 @@ static int gif_write_video(AVFormatContext *s, |
|
|
|
|
|
|
|
|
|
left-=GIF_CHUNKS; |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
|
INT64 time, file_time; |
|
|
|
|
UINT8 buffer[100]; /* data chunks */ |
|
|
|
|
} GIFContext; |
|
|
|
|
|
|
|
|
|
static int gif_write_header(AVFormatContext *s) |
|
|
|
|
{ |
|
|
|
|
GIFContext *gif = s->priv_data; |
|
|
|
|
ByteIOContext *pb = &s->pb; |
|
|
|
|
AVCodecContext *enc, *video_enc; |
|
|
|
|
int i, width, height, rate; |
|
|
|
|
|
|
|
|
|
/* 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++) { |
|
|
|
|
enc = &s->streams[i]->codec; |
|
|
|
|
if (enc->codec_type != CODEC_TYPE_AUDIO) |
|
|
|
|
video_enc = enc; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!video_enc) { |
|
|
|
|
av_free(gif); |
|
|
|
|
return -1; |
|
|
|
|
} else { |
|
|
|
|
width = video_enc->width; |
|
|
|
|
height = video_enc->height; |
|
|
|
|
rate = video_enc->frame_rate; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* XXX: is it allowed ? seems to work so far... */ |
|
|
|
|
video_enc->pix_fmt = PIX_FMT_RGB24; |
|
|
|
|
|
|
|
|
|
gif_image_write_header(pb, width, height); |
|
|
|
|
|
|
|
|
|
put_flush_packet(&s->pb); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* chunk writer callback */ |
|
|
|
|
/* !!! XXX:deprecated
|
|
|
|
|
static void gif_put_chunk(void *pbctx, UINT8 *buffer, int count) |
|
|
|
|
{ |
|
|
|
|
ByteIOContext *pb = (ByteIOContext *)pbctx; |
|
|
|
|
put_byte(pb, (UINT8)count); |
|
|
|
|
put_buffer(pb, buffer, count); |
|
|
|
|
} |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
static int gif_write_video(AVFormatContext *s,
|
|
|
|
|
AVCodecContext *enc, UINT8 *buf, int size) |
|
|
|
|
{ |
|
|
|
|
ByteIOContext *pb = &s->pb; |
|
|
|
|
GIFContext *gif = s->priv_data; |
|
|
|
|
int jiffies; |
|
|
|
|
INT64 delay; |
|
|
|
|
|
|
|
|
|
/* graphic control extension block */ |
|
|
|
|
put_byte(pb, 0x21); |
|
|
|
|
put_byte(pb, 0xf9); |
|
|
|
|
put_byte(pb, 0x04); /* block size */ |
|
|
|
|
put_byte(pb, 0x04); /* flags */ |
|
|
|
|
|
|
|
|
|
/* 1 jiffy is 1/70 s */ |
|
|
|
|
/* the delay_time field indicates the number of jiffies - 1 */ |
|
|
|
|
delay = gif->file_time - gif->time; |
|
|
|
|
|
|
|
|
|
/* 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 */ |
|
|
|
|
jiffies = (70*FRAME_RATE_BASE/enc->frame_rate) - 1; |
|
|
|
|
|
|
|
|
|
put_le16(pb, jiffies); |
|
|
|
|
|
|
|
|
|
put_byte(pb, 0x1f); /* transparent color index */ |
|
|
|
|
put_byte(pb, 0x00); |
|
|
|
|
|
|
|
|
|
gif_image_write_image(pb, 0, 0, enc->width, enc->height, |
|
|
|
|
buf, enc->width * 3); |
|
|
|
|
|
|
|
|
|
put_flush_packet(&s->pb); |
|
|
|
|
return 0; |
|
|
|
@ -355,6 +379,17 @@ static int gif_write_trailer(AVFormatContext *s) |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* better than nothing gif image writer */ |
|
|
|
|
int gif_write(ByteIOContext *pb, AVImageInfo *info) |
|
|
|
|
{ |
|
|
|
|
gif_image_write_header(pb, info->width, info->height); |
|
|
|
|
gif_image_write_image(pb, 0, 0, info->width, info->height,
|
|
|
|
|
info->pict.data[0], info->pict.linesize[0]); |
|
|
|
|
put_byte(pb, 0x3b); |
|
|
|
|
put_flush_packet(pb); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static AVOutputFormat gif_oformat = { |
|
|
|
|
"gif", |
|
|
|
|
"GIF Animation", |
|
|
|
@ -367,6 +402,7 @@ static AVOutputFormat gif_oformat = { |
|
|
|
|
gif_write_packet, |
|
|
|
|
gif_write_trailer, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
extern AVInputFormat gif_iformat; |
|
|
|
|
|
|
|
|
|
int gif_init(void) |
|
|
|
|