• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

libavcodec/dvdsubenc.c

Go to the documentation of this file.
00001 /*
00002  * DVD subtitle encoding for ffmpeg
00003  * Copyright (c) 2005 Wolfram Gloger
00004  *
00005  * This file is part of FFmpeg.
00006  *
00007  * FFmpeg is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * FFmpeg is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with FFmpeg; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 #include "avcodec.h"
00022 #include "bytestream.h"
00023 
00024 #undef NDEBUG
00025 #include <assert.h>
00026 
00027 // ncnt is the nibble counter
00028 #define PUTNIBBLE(val)\
00029 do {\
00030     if (ncnt++ & 1)\
00031         *q++ = bitbuf | ((val) & 0x0f);\
00032     else\
00033         bitbuf = (val) << 4;\
00034 } while(0)
00035 
00036 static void dvd_encode_rle(uint8_t **pq,
00037                            const uint8_t *bitmap, int linesize,
00038                            int w, int h,
00039                            const int cmap[256])
00040 {
00041     uint8_t *q;
00042     unsigned int bitbuf = 0;
00043     int ncnt;
00044     int x, y, len, color;
00045 
00046     q = *pq;
00047 
00048     for (y = 0; y < h; ++y) {
00049         ncnt = 0;
00050         for(x = 0; x < w; x += len) {
00051             color = bitmap[x];
00052             for (len=1; x+len < w; ++len)
00053                 if (bitmap[x+len] != color)
00054                     break;
00055             color = cmap[color];
00056             assert(color < 4);
00057             if (len < 0x04) {
00058                 PUTNIBBLE((len << 2)|color);
00059             } else if (len < 0x10) {
00060                 PUTNIBBLE(len >> 2);
00061                 PUTNIBBLE((len << 2)|color);
00062             } else if (len < 0x40) {
00063                 PUTNIBBLE(0);
00064                 PUTNIBBLE(len >> 2);
00065                 PUTNIBBLE((len << 2)|color);
00066             } else if (x+len == w) {
00067                 PUTNIBBLE(0);
00068                 PUTNIBBLE(0);
00069                 PUTNIBBLE(0);
00070                 PUTNIBBLE(color);
00071             } else {
00072                 if (len > 0xff)
00073                     len = 0xff;
00074                 PUTNIBBLE(0);
00075                 PUTNIBBLE(len >> 6);
00076                 PUTNIBBLE(len >> 2);
00077                 PUTNIBBLE((len << 2)|color);
00078             }
00079         }
00080         /* end of line */
00081         if (ncnt & 1)
00082             PUTNIBBLE(0);
00083         bitmap += linesize;
00084     }
00085 
00086     *pq = q;
00087 }
00088 
00089 static int encode_dvd_subtitles(uint8_t *outbuf, int outbuf_size,
00090                                 const AVSubtitle *h)
00091 {
00092     uint8_t *q, *qq;
00093     int object_id;
00094     int offset1[20], offset2[20];
00095     int i, imax, color, alpha, rects = h->num_rects;
00096     unsigned long hmax;
00097     unsigned long hist[256];
00098     int           cmap[256];
00099 
00100     if (rects == 0 || h->rects == NULL)
00101         return -1;
00102     if (rects > 20)
00103         rects = 20;
00104 
00105     // analyze bitmaps, compress to 4 colors
00106     for (i=0; i<256; ++i) {
00107         hist[i] = 0;
00108         cmap[i] = 0;
00109     }
00110     for (object_id = 0; object_id < rects; object_id++)
00111         for (i=0; i<h->rects[object_id]->w*h->rects[object_id]->h; ++i) {
00112             color = h->rects[object_id]->pict.data[0][i];
00113             // only count non-transparent pixels
00114             alpha = ((uint32_t*)h->rects[object_id]->pict.data[1])[color] >> 24;
00115             hist[color] += alpha;
00116         }
00117     for (color=3;; --color) {
00118         hmax = 0;
00119         imax = 0;
00120         for (i=0; i<256; ++i)
00121             if (hist[i] > hmax) {
00122                 imax = i;
00123                 hmax = hist[i];
00124             }
00125         if (hmax == 0)
00126             break;
00127         if (color == 0)
00128             color = 3;
00129         av_log(NULL, AV_LOG_DEBUG, "dvd_subtitle hist[%d]=%ld -> col %d\n",
00130                imax, hist[imax], color);
00131         cmap[imax] = color;
00132         hist[imax] = 0;
00133     }
00134 
00135 
00136     // encode data block
00137     q = outbuf + 4;
00138     for (object_id = 0; object_id < rects; object_id++) {
00139         offset1[object_id] = q - outbuf;
00140         // worst case memory requirement: 1 nibble per pixel..
00141         if ((q - outbuf) + h->rects[object_id]->w*h->rects[object_id]->h/2
00142             + 17*rects + 21 > outbuf_size) {
00143             av_log(NULL, AV_LOG_ERROR, "dvd_subtitle too big\n");
00144             return -1;
00145         }
00146         dvd_encode_rle(&q, h->rects[object_id]->pict.data[0],
00147                        h->rects[object_id]->w*2,
00148                        h->rects[object_id]->w, h->rects[object_id]->h >> 1,
00149                        cmap);
00150         offset2[object_id] = q - outbuf;
00151         dvd_encode_rle(&q, h->rects[object_id]->pict.data[0] + h->rects[object_id]->w,
00152                        h->rects[object_id]->w*2,
00153                        h->rects[object_id]->w, h->rects[object_id]->h >> 1,
00154                        cmap);
00155     }
00156 
00157     // set data packet size
00158     qq = outbuf + 2;
00159     bytestream_put_be16(&qq, q - outbuf);
00160 
00161     // send start display command
00162     bytestream_put_be16(&q, (h->start_display_time*90) >> 10);
00163     bytestream_put_be16(&q, (q - outbuf) /*- 2 */ + 8 + 12*rects + 2);
00164     *q++ = 0x03; // palette - 4 nibbles
00165     *q++ = 0x03; *q++ = 0x7f;
00166     *q++ = 0x04; // alpha - 4 nibbles
00167     *q++ = 0xf0; *q++ = 0x00;
00168     //*q++ = 0x0f; *q++ = 0xff;
00169 
00170     // XXX not sure if more than one rect can really be encoded..
00171     // 12 bytes per rect
00172     for (object_id = 0; object_id < rects; object_id++) {
00173         int x2 = h->rects[object_id]->x + h->rects[object_id]->w - 1;
00174         int y2 = h->rects[object_id]->y + h->rects[object_id]->h - 1;
00175 
00176         *q++ = 0x05;
00177         // x1 x2 -> 6 nibbles
00178         *q++ = h->rects[object_id]->x >> 4;
00179         *q++ = (h->rects[object_id]->x << 4) | ((x2 >> 8) & 0xf);
00180         *q++ = x2;
00181         // y1 y2 -> 6 nibbles
00182         *q++ = h->rects[object_id]->y >> 4;
00183         *q++ = (h->rects[object_id]->y << 4) | ((y2 >> 8) & 0xf);
00184         *q++ = y2;
00185 
00186         *q++ = 0x06;
00187         // offset1, offset2
00188         bytestream_put_be16(&q, offset1[object_id]);
00189         bytestream_put_be16(&q, offset2[object_id]);
00190     }
00191     *q++ = 0x01; // start command
00192     *q++ = 0xff; // terminating command
00193 
00194     // send stop display command last
00195     bytestream_put_be16(&q, (h->end_display_time*90) >> 10);
00196     bytestream_put_be16(&q, (q - outbuf) - 2 /*+ 4*/);
00197     *q++ = 0x02; // set end
00198     *q++ = 0xff; // terminating command
00199 
00200     qq = outbuf;
00201     bytestream_put_be16(&qq, q - outbuf);
00202 
00203     av_log(NULL, AV_LOG_DEBUG, "subtitle_packet size=%td\n", q - outbuf);
00204     return q - outbuf;
00205 }
00206 
00207 static int dvdsub_encode(AVCodecContext *avctx,
00208                          unsigned char *buf, int buf_size, void *data)
00209 {
00210     //DVDSubtitleContext *s = avctx->priv_data;
00211     AVSubtitle *sub = data;
00212     int ret;
00213 
00214     ret = encode_dvd_subtitles(buf, buf_size, sub);
00215     return ret;
00216 }
00217 
00218 AVCodec dvdsub_encoder = {
00219     "dvdsub",
00220     AVMEDIA_TYPE_SUBTITLE,
00221     CODEC_ID_DVD_SUBTITLE,
00222     0,
00223     NULL,
00224     dvdsub_encode,
00225     .long_name = NULL_IF_CONFIG_SMALL("DVD subtitles"),
00226 };

Generated on Fri Sep 16 2011 17:17:35 for FFmpeg by  doxygen 1.7.1