[Ffmpeg-devel] [PATCH] TIFF encoder (Google SoC qualification task)

Kamil Nowosad k.nowosad
Thu Mar 22 13:01:46 CET 2007


Writing a TIFF encoder is one of your SoC qualification tasks, and I am
now submitting what i have yet done.

Apart from libavcodec, I have also updated the libavformat, so that it
is possible to include many images in one TIFF file.
Each image is divided into strips [of size ~8kB] and then compressed
using zlib or [when configured without zlib] packbits compression.

My TiffEncoderContext is small. I don't really need [yet] more than this
one field [file_offset]. Is it ok, or should I extend the structure?

It supports now only rgb24 pixel format, but i'm going to change that
until the deadline.

Are there any features that you find especially important to be
implemented here? I can also improve the decoder. I think I could add
the multiple_imares_per_one_file support.

I'm waiting for your suggestions.

-- 
Best regards,
Kamil Nowosad
-------------- next part --------------
diff -upNr ffmpeg/Changelog ffmpeg-new/Changelog
--- ffmpeg/Changelog	2007-03-22 10:43:12.000000000 +0000
+++ ffmpeg-new/Changelog	2007-03-22 11:07:24.000000000 +0000
@@ -75,6 +75,7 @@ version <next>
 - DCA decoder
 - DXA demuxer and decoder
 - DNxHD decoder
+- TIFF picture encoder
 
 version 0.4.9-pre1:
 
diff -upNr ffmpeg/doc/ffmpeg-doc.texi ffmpeg-new/doc/ffmpeg-doc.texi
--- ffmpeg/doc/ffmpeg-doc.texi	2007-03-22 10:43:09.000000000 +0000
+++ ffmpeg-new/doc/ffmpeg-doc.texi	2007-03-22 11:07:36.000000000 +0000
@@ -901,6 +901,7 @@ library:
 @item DXA @tab    @tab X
 @tab This format is used in non-Windows version of Feeble Files game and
 different game cutscenes repacked for use with ScummVM.
+ at item TIFF @tab X @tab	 
 @end multitable
 
 @code{X} means that encoding (resp. decoding) is supported.
@@ -920,7 +921,7 @@ following image formats are supported:
 @item animated GIF @tab X @tab X @tab Only uncompressed GIFs are generated.
 @item PNG          @tab X @tab X @tab 2 bit and 4 bit/pixel not supported yet.
 @item Targa        @tab   @tab X @tab Targa (.TGA) image format.
- at item TIFF         @tab   @tab X @tab Only 24 bit/pixel images are supported.
+ at item TIFF         @tab X @tab X @tab Only 24 bit/pixel images are supported.
 @item SGI          @tab X @tab X @tab SGI RGB image format
 @end multitable
 
diff -upNr ffmpeg/libavcodec/allcodecs.c ffmpeg-new/libavcodec/allcodecs.c
--- ffmpeg/libavcodec/allcodecs.c	2007-03-22 10:43:08.000000000 +0000
+++ ffmpeg-new/libavcodec/allcodecs.c	2007-03-22 11:06:10.000000000 +0000
@@ -131,7 +131,7 @@ void avcodec_register_all(void)
     REGISTER_ENCDEC (TARGA, targa);
     REGISTER_DECODER(THEORA, theora);
     REGISTER_DECODER(TIERTEXSEQVIDEO, tiertexseqvideo);
-    REGISTER_DECODER(TIFF, tiff);
+    REGISTER_ENCDEC (TIFF, tiff);
     REGISTER_DECODER(TRUEMOTION1, truemotion1);
     REGISTER_DECODER(TRUEMOTION2, truemotion2);
     REGISTER_DECODER(TSCC, tscc);
diff -upNr ffmpeg/libavcodec/avcodec.h ffmpeg-new/libavcodec/avcodec.h
--- ffmpeg/libavcodec/avcodec.h	2007-03-22 10:43:09.000000000 +0000
+++ ffmpeg-new/libavcodec/avcodec.h	2007-03-22 11:06:34.000000000 +0000
@@ -2315,6 +2315,7 @@ extern AVCodec targa_decoder;
 extern AVCodec theora_decoder;
 extern AVCodec tiertexseqvideo_decoder;
 extern AVCodec tiff_decoder;
+extern AVCodec tiff_encoder;
 extern AVCodec truemotion1_decoder;
 extern AVCodec truemotion2_decoder;
 extern AVCodec truespeech_decoder;
diff -upNr ffmpeg/libavcodec/tiff.c ffmpeg-new/libavcodec/tiff.c
--- ffmpeg/libavcodec/tiff.c	2007-03-22 10:43:09.000000000 +0000
+++ ffmpeg-new/libavcodec/tiff.c	2007-03-22 11:05:59.000000000 +0000
@@ -33,11 +33,15 @@ enum TiffTags{
     TIFF_COMPR,
     TIFF_INVERT = 0x106,
     TIFF_STRIP_OFFS = 0x111,
-    TIFF_ROWSPERSTRIP = 0x116,
+    TIFF_SAMPLESPERPIX = 0x115,
+    TIFF_ROWSPERSTRIP,
     TIFF_STRIP_SIZE,
     TIFF_PLANAR = 0x11C,
+    TIFF_XRES = 0x11A,
+    TIFF_YRES = 0x11B,
     TIFF_XPOS = 0x11E,
     TIFF_YPOS = 0x11F,
+    TIFF_RES_UNIT = 0x128,
     TIFF_PREDICTOR = 0x13D,
     TIFF_PAL = 0x140
 };
@@ -68,7 +72,7 @@ static const int type_sizes[6] = {
     0, 1, 100, 2, 4, 8
 };
 
-typedef struct TiffContext {
+typedef struct TiffDecoderContext {
     AVCodecContext *avctx;
     AVFrame picture;
 
@@ -84,7 +88,11 @@ typedef struct TiffContext {
     uint8_t* stripsizes;
     int stripsize, stripoff;
     LZWState *lzw;
-} TiffContext;
+} TiffDecoderContext;
+
+typedef struct TiffEncoderContext {
+    int file_offset;
+} TiffEncoderContext;
 
 static int tget_short(uint8_t **p, int le){
     int v = le ? AV_RL16(*p) : AV_RB16(*p);
@@ -107,7 +115,7 @@ static int tget(uint8_t **p, int type, i
     }
 }
 
-static int tiff_unpack_strip(TiffContext *s, uint8_t* dst, int stride, uint8_t *src, int size, int lines){
+static int tiff_unpack_strip(TiffDecoderContext *s, uint8_t* dst, int stride, uint8_t *src, int size, int lines){
     int c, line, pixels, code;
     uint8_t *ssrc = src;
     int width = s->width * (s->bpp / 8);
@@ -186,7 +194,7 @@ static int tiff_unpack_strip(TiffContext
 }
 
 
-static int tiff_decode_tag(TiffContext *s, uint8_t *start, uint8_t *buf, uint8_t *end_buf, AVFrame *pic)
+static int tiff_decode_tag(TiffDecoderContext *s, uint8_t *start, uint8_t *buf, uint8_t *end_buf, AVFrame *pic)
 {
     int tag, type, count, off, value = 0;
     uint8_t *src, *dst;
@@ -440,7 +448,7 @@ static int decode_frame(AVCodecContext *
                         void *data, int *data_size,
                         uint8_t *buf, int buf_size)
 {
-    TiffContext * const s = avctx->priv_data;
+    TiffDecoderContext * const s = avctx->priv_data;
     AVFrame *picture = data;
     AVFrame * const p= (AVFrame*)&s->picture;
     uint8_t *orig_buf = buf, *end_buf = buf + buf_size;
@@ -494,8 +502,8 @@ static int decode_frame(AVCodecContext *
     return buf_size;
 }
 
-static int tiff_init(AVCodecContext *avctx){
-    TiffContext *s = avctx->priv_data;
+static int tiff_decoder_init(AVCodecContext *avctx){
+    TiffDecoderContext *s = avctx->priv_data;
 
     s->width = 0;
     s->height = 0;
@@ -508,9 +516,9 @@ static int tiff_init(AVCodecContext *avc
     return 0;
 }
 
-static int tiff_end(AVCodecContext *avctx)
+static int tiff_decoder_end(AVCodecContext *avctx)
 {
-    TiffContext * const s = avctx->priv_data;
+    TiffDecoderContext * const s = avctx->priv_data;
 
     ff_lzw_decode_close(&s->lzw);
     if(s->picture.data[0])
@@ -518,15 +526,299 @@ static int tiff_end(AVCodecContext *avct
     return 0;
 }
 
+static void tiff_put_short(uint8_t **p, uint16_t v)
+{
+    *(*p)++ = v;
+    *(*p)++ = v >> 8;
+}
+
+static void tiff_put_long(uint8_t **p, uint32_t v)
+{
+    *(*p)++ = v;
+    *(*p)++ = v >> 8;
+    *(*p)++ = v >> 16;
+    *(*p)++ = v >> 24;
+} 
+
+static void tiff_add_ifd_entry(uint8_t **ptr, uint16_t tag, uint16_t type, uint32_t count, uint32_t value)
+{
+    tiff_put_short(ptr, tag);
+    tiff_put_short(ptr, type);
+    tiff_put_long(ptr, count);
+    tiff_put_long(ptr, value);
+}
+
+static int tiff_pack_bits(uint8_t *dst, int *dst_len, uint8_t *src, int src_len)
+{
+#define MIN(a, b) ((a)<(b) ? (a):(b))
+    uint8_t *dst_begin = dst,
+	    *last_literal,
+	     special_case_ch;
+    enum {START, RUN, LITERAL, SPECIAL} last;
+    last = START;
+    while (src_len > 0){
+	uint8_t *sp;
+	int num = 0;
+	
+	for (sp = src; (*sp == *src) && (src_len > 0); src++, src_len--)
+	    num++;
+
+	if (dst - dst_begin + (num+127)/128 + 2 >= *dst_len) // check if has enough space
+	    return -1;
+
+	if (num > 2){
+	    if (last == SPECIAL){
+		*dst++ = -1;
+		*dst++ = special_case_ch;
+	    }
+	    while (num > 2){ 
+		*dst++ = (uint8_t)(-MIN(num, 128)+1);
+		*dst++ = *sp;
+		num -= 128;
+	    }
+	    last = RUN;
+	}
+	if (num == 2){ 
+	    if (last == LITERAL && src_len){
+		special_case_ch = *sp;
+		last = SPECIAL;
+	    }
+	    else{
+		if (last == SPECIAL){
+		    *dst++ = -1;
+		    *dst++ = special_case_ch;
+		}
+		*dst++ = -1;
+		*dst++ = *sp;
+		last = RUN;
+	    }
+	    num = 0;
+	}
+	if (num > 0){
+#define CHECK_AND_ADD(x, expr){ \ 
+	    if ((expr) || *last_literal == 127){\
+		*dst = -1;\
+		last_literal = dst++;\
+	    }\
+	    (*last_literal)++;\
+	    *dst++ = x;\
+	    }
+
+	    if (last == SPECIAL){
+		CHECK_AND_ADD(special_case_ch, 0);
+		CHECK_AND_ADD(special_case_ch, 0);
+		CHECK_AND_ADD(*sp, 0);
+	    }
+	    else 
+		CHECK_AND_ADD(*sp, last != LITERAL);
+	    last = LITERAL;
+#undef CHECK_AND_ADD
+	}
+    }
+#undef MIN 
+    *dst_len = dst - dst_begin;
+    return 0;
+}
+
+static int encode_frame(AVCodecContext *avctx, unsigned char *buf, int buf_size, void *data)
+{
+    /* encoding scheme:
+     *
+     * *offset of the IFD
+     * *image data [divided into strips]
+     * *strip offsets
+     * *strip sizes
+     * *bpp info
+     * *IFD, sorted by tag [excluding the offset of the next IFD 
+     *  - it will be filled while encoding the next frame or writing the format footer]
+     */
+    TiffEncoderContext *s = avctx->priv_data;
+    AVFrame *p=data; 
+    uint8_t *entry_point;
+    uint8_t *buf_start = buf, 
+	    *buf_end = buf+buf_size;
+    int bpp_info_offset, 
+	strip_offsets_offset = 0,
+	strip_sizes_offset = 0,
+	*strip_offset;
+    int y;
+    int strip, 
+	num_of_strips, 
+	lines_per_strip; 
+    int ret;
+#ifdef CONFIG_ZLIB
+    uint8_t *zbuf_start, 
+	    *zbuf;
+    uLongf zlen;
+#endif
+   
+    switch(avctx->pix_fmt){   
+	case PIX_FMT_RGB24: break;
+	default: av_log(avctx, AV_LOG_ERROR, "only RGB24 pix_fmt supported\n");
+		 return -1;
+    }
+
+#define CURR_OFFSET (buf - buf_start + s->file_offset)
+    buf_start = buf;
+    buf_end = buf + buf_size;
+    
+    entry_point = buf;
+    buf += 4; // a place for next IFD offset [later overwritten]
+
+    /* compute the number of strips */
+    if (avctx->width == 0 || avctx->height == 0){
+	num_of_strips = 0;
+	lines_per_strip = 0;
+    }
+    else{
+	lines_per_strip = (8192 + avctx->width * 3 - 1)/(avctx->width * 3); // =ceil(height/num_of_strips)
+	num_of_strips = (avctx->height + lines_per_strip - 1)/lines_per_strip;
+    }
+
+    /* writing the image data */
+    strip_offset = (int*) av_malloc((num_of_strips + 1) * sizeof(int));
+    strip = 0;
+#ifdef CONFIG_ZLIB
+    zbuf_start = zbuf = av_malloc(3 * avctx->width * lines_per_strip);
+    if (zbuf == NULL){
+	av_log(avctx, AV_LOG_ERROR, "not enough memory\n");
+	ret = -1;
+	goto cleanup;
+    }
+    for (y = 0; y < avctx->height; y += lines_per_strip){
+	uint8_t *src;
+	int i;
+
+	for(i = 0; (i < lines_per_strip) && (y + i < avctx->height); i++){
+	    src = p->data[0] + (y + i) * p->linesize[0];
+	    memcpy(zbuf, src, 3 * avctx->width);
+	    zbuf += 3 * avctx->width;
+	} 
+	strip_offset[strip++] = CURR_OFFSET;
+	zlen = buf_end - buf;
+	if (compress(buf, &zlen, zbuf_start, zbuf - zbuf_start) != Z_OK){
+	    av_log(avctx, AV_LOG_ERROR, "ZLib compression error\n");
+	    av_free(zbuf_start);
+	    ret = -1;
+	    goto cleanup;
+	}
+	buf += zlen;
+	zbuf = zbuf_start;
+    }
+    av_free(zbuf_start);
+#else
+    for (y = 0; y < avctx->height; y += lines_per_strip){
+	uint8_t *src;
+	int i;
+
+	strip_offset[strip++] = CURR_OFFSET;
+	for(i = 0; (i < lines_per_strip) && (y + i < avctx->height); i++){
+	    int plen = buf_end - buf;
+	    src = p->data[0] + (y + i) * p->linesize[0];
+	    if (tiff_pack_bits(buf, &plen, src, 3 * avctx->width) == -1){
+		av_log(avctx, AV_LOG_ERROR, "packbits compression error\n");
+		ret = -1;
+		goto cleanup;
+	    }
+	    buf += plen;
+	} 
+    }
+#endif
+    /* writing strip data */
+    strip_offset[strip] = CURR_OFFSET; // helpful when computing strip sizes
+    if (strip > 1){ // put strip info outside the IFD
+	int st;
+	/* strip offsets */	
+	strip_offsets_offset = CURR_OFFSET;
+	for (st = 0; st < strip; st++) 
+	    tiff_put_long(&buf, strip_offset[st]);
+	/* strip sizes */
+	strip_sizes_offset=CURR_OFFSET;
+	for (st = 1; st <= strip; st++)
+	    tiff_put_long(&buf, strip_offset[st] - strip_offset[st-1]);
+    }
+
+    if (buf + 3*4 + 4 + 2 + 9*12 >= buf_end){ // is there enough space for IFD and bpp info?
+	av_log(avctx, AV_LOG_DEBUG, "buffer too short\n");
+	ret = -1;
+	goto cleanup;
+    }
+
+    /* BPP info: */
+    bpp_info_offset = CURR_OFFSET;
+    tiff_put_short(&buf, 8);
+    tiff_put_short(&buf, 8);
+    tiff_put_short(&buf, 8);
+
+    /* image file directory starts here:  */
+    tiff_put_long(&entry_point, CURR_OFFSET); 
+    
+    /* IFD entries (9): */
+    tiff_put_short(&buf, 9);  // the number of entries
+
+    tiff_add_ifd_entry(&buf, TIFF_WIDTH, TIFF_LONG, 1, avctx->width);
+    tiff_add_ifd_entry(&buf, TIFF_HEIGHT, TIFF_LONG, 1, avctx->height);
+    tiff_add_ifd_entry(&buf, TIFF_BPP, TIFF_SHORT, 3, bpp_info_offset); 
+#ifdef CONFIG_ZLIB
+    tiff_add_ifd_entry(&buf, TIFF_COMPR, TIFF_SHORT, 1, TIFF_DEFLATE);
+#else
+    tiff_add_ifd_entry(&buf, TIFF_COMPR, TIFF_SHORT, 1, TIFF_PACKBITS);
+#endif
+    tiff_add_ifd_entry(&buf, TIFF_INVERT, TIFF_SHORT, 1, 2);
+    if (strip > 1)
+	tiff_add_ifd_entry(&buf, TIFF_STRIP_OFFS, TIFF_LONG, strip, strip_offsets_offset);
+    else
+	tiff_add_ifd_entry(&buf, TIFF_STRIP_OFFS, TIFF_LONG, 1, strip_offset[0]);
+    tiff_add_ifd_entry(&buf, TIFF_SAMPLESPERPIX, TIFF_SHORT, 1, 3);
+    tiff_add_ifd_entry(&buf, TIFF_ROWSPERSTRIP, TIFF_LONG, 1, lines_per_strip);
+    if (strip > 1)
+	tiff_add_ifd_entry(&buf, TIFF_STRIP_SIZE, TIFF_LONG, strip, strip_sizes_offset);
+    else
+	tiff_add_ifd_entry(&buf, TIFF_STRIP_SIZE, TIFF_LONG, 1, avctx->width * avctx->height * 3);
+
+    /* end writing */
+    s->file_offset = CURR_OFFSET;
+    ret = buf - buf_start;
+cleanup:
+    av_free(strip_offset);
+#undef CURR_OFFSET
+    return ret;
+}
+
+static int tiff_encoder_init(AVCodecContext *avctx){
+    TiffEncoderContext *s = avctx->priv_data;
+    
+    s->file_offset=4;
+    return 0;
+}
+
+static int tiff_encoder_end(AVCodecContext *avctx)
+{
+    return 0;
+}
+
 AVCodec tiff_decoder = {
     "tiff",
     CODEC_TYPE_VIDEO,
     CODEC_ID_TIFF,
-    sizeof(TiffContext),
-    tiff_init,
+    sizeof(TiffDecoderContext),
+    tiff_decoder_init,
     NULL,
-    tiff_end,
+    tiff_decoder_end,
     decode_frame,
     0,
     NULL
 };
+
+AVCodec tiff_encoder = {
+    "tiff",
+    CODEC_TYPE_VIDEO,
+    CODEC_ID_TIFF,
+    sizeof(TiffEncoderContext),
+    tiff_encoder_init,
+    encode_frame,
+    tiff_encoder_end,
+    NULL,
+    0,
+    NULL
+};
diff -upNr ffmpeg/libavformat/allformats.c ffmpeg-new/libavformat/allformats.c
--- ffmpeg/libavformat/allformats.c	2007-03-22 10:43:12.000000000 +0000
+++ ffmpeg-new/libavformat/allformats.c	2007-03-22 11:07:00.000000000 +0000
@@ -143,6 +143,7 @@ void av_register_all(void)
     REGISTER_MUXER   (TG2, tg2);
     REGISTER_MUXER   (TGP, tgp);
     REGISTER_DEMUXER (TIERTEXSEQ, tiertexseq);
+    REGISTER_MUXER   (TIFF, tiff);
     REGISTER_DEMUXER (TTA, tta);
     REGISTER_DEMUXER (V4L2, v4l2);
     REGISTER_DEMUXER (VC1, vc1);
diff -upNr ffmpeg/libavformat/allformats.h ffmpeg-new/libavformat/allformats.h
--- ffmpeg/libavformat/allformats.h	2007-03-22 10:43:12.000000000 +0000
+++ ffmpeg-new/libavformat/allformats.h	2007-03-22 11:07:08.000000000 +0000
@@ -153,6 +153,7 @@ extern AVInputFormat smacker_demuxer;
 extern AVInputFormat sol_demuxer;
 extern AVInputFormat swf_demuxer;
 extern AVOutputFormat swf_muxer;
+extern AVOutputFormat tiff_muxer;
 extern AVInputFormat tta_demuxer;
 extern AVInputFormat v4l2_demuxer;
 extern AVInputFormat vc1_demuxer;
diff -upNr ffmpeg/libavformat/raw.c ffmpeg-new/libavformat/raw.c
--- ffmpeg/libavformat/raw.c	2007-03-22 10:43:12.000000000 +0000
+++ ffmpeg-new/libavformat/raw.c	2007-03-22 11:06:50.000000000 +0000
@@ -43,6 +43,13 @@ static int flac_write_header(struct AVFo
     return 0;
 }
 
+static int tiff_write_header(struct AVFormatContext *s)
+{
+    static const uint8_t header[4] = {0x49, 0x49, 42, 0};
+    put_buffer(&s->pb, header, 4);
+    return 0;
+}
+
 static int raw_write_packet(struct AVFormatContext *s, AVPacket *pkt)
 {
     put_buffer(&s->pb, pkt->data, pkt->size);
@@ -54,6 +61,14 @@ static int raw_write_trailer(struct AVFo
 {
     return 0;
 }
+
+static int tiff_write_trailer(struct AVFormatContext *s)
+{
+    static const uint8_t trailer[4] = {0, 0, 0, 0};
+    put_buffer(&s->pb, trailer, 4);
+    return 0;
+}
+
 #endif //CONFIG_MUXERS
 
 /* raw input */
@@ -873,6 +888,20 @@ AVOutputFormat rawvideo_muxer = {
     raw_write_trailer,
     .flags= AVFMT_NOTIMESTAMPS,
 };
+
+AVOutputFormat tiff_muxer = {
+    "tiff",
+    "tiff video format",
+    "image/tiff",
+    "tiff,tif",
+    0,
+    CODEC_ID_NONE,
+    CODEC_ID_TIFF,
+    tiff_write_header,
+    raw_write_packet,
+    tiff_write_trailer,
+    .flags = AVFMT_NOTIMESTAMPS,
+};
 #endif //CONFIG_MUXERS
 
 #ifdef CONFIG_MUXERS



More information about the ffmpeg-devel mailing list