[Ffmpeg-devel] [Patch] DVB Subtitle decoder

Ian Caulfield imc25
Sat Jul 9 01:00:02 CEST 2005


On Fri, 8 Jul 2005, Ian Caulfield wrote:

> On Fri, 8 Jul 2005, Nico Sabbi wrote:
>
>> Ian Caulfield wrote:
>> 
>>> Hi,
>>> 
>>> The attached patch adds a DVB subtitle decoder. I've had to fiddle the 
>>> AVSubtitle struct to get it to work efficiently; the patch includes 
>>> tweaks to the existing subtitle code. I've also fiddled slightly with 
>>> the mpegts parser to allow it to detect DVB subtitles.
>>> 
>>> Issues still outstanding:
>>>  - The 'end_display_time' is currently inaccurate - it gives the page
>>>    timeout rather than the time it is replaced by the next subtitle
>>>  - The parser is a bit of a hack to reassemble PES packets. I welcome
>>>    suggestions of better ways to implement it
>>>  - If there are multiple subtitle streams on one PID, only one will be
>>>    decoded. To fix this it is necessary to associate more than one
>>>    AVStream with a pid - not sure how to do this.
>>> 
>>> Please let me know if you have any comments/suggestions.
>>> 
>> Can you upload a sample to www.mplayerhq.hu/MPlayer/incoming please?
>
> By sample, do you mean the patch files or a sample file it can decode?

Never mind, uploading sample dvbsubtest.ts now.

Also, have spotted a typo in the code (don't edit code that's #ifdef'ed 
out!) - new source file reattached.

Ian
-------------- next part --------------
/*

 * DVD subtitle decoding for ffmpeg

 * Copyright (c) 2005 Ian Caulfield.

 *

 * This library is free software; you can redistribute it and/or

 * modify it under the terms of the GNU Lesser General Public

 * License as published by the Free Software Foundation; either

 * version 2 of the License, or (at your option) any later version.

 *

 * This library is distributed in the hope that it will be useful,

 * but WITHOUT ANY WARRANTY; without even the implied warranty of

 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

 * Lesser General Public License for more details.

 *

 * You should have received a copy of the GNU Lesser General Public

 * License along with this library; if not, write to the Free Software

 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 */

#include "avcodec.h"



//#define DEBUG

//#define DEBUG_PACKET_CONTENTS

//#define DEBUG_SAVE_IMAGES



#define DVBSUB_PAGE_SEGMENT     0x10

#define DVBSUB_REGION_SEGMENT   0x11

#define DVBSUB_CLUT_SEGMENT     0x12

#define DVBSUB_OBJECT_SEGMENT   0x13

#define DVBSUB_DISPLAY_SEGMENT  0x80



static uint16_t getbe16(const uint8_t *p)

{

    return (p[0] << 8) | p[1];

}



#define MAX_NEG_CROP 1024



static unsigned char cropTbl[256 + 2 * MAX_NEG_CROP];

static unsigned char *cm;





#ifdef DEBUG_SAVE_IMAGES

#undef fprintf

#if 0

static void png_save(const char *filename, uint8_t *bitmap, int w, int h,

                     uint32_t *rgba_palette)

{

    int x, y, v;

    FILE *f;

    char fname[40], fname2[40];

    char command[1024];

    

    snprintf(fname, 40, "%s.ppm", filename);



    f = fopen(fname, "w");

    if (!f) {

        perror(fname);

        exit(1);

    }

    fprintf(f, "P6\n"

            "%d %d\n"

            "%d\n",

            w, h, 255);

    for(y = 0; y < h; y++) {

        for(x = 0; x < w; x++) {

            v = rgba_palette[bitmap[y * w + x]];

            putc((v >> 16) & 0xff, f);

            putc((v >> 8) & 0xff, f);

            putc((v >> 0) & 0xff, f);

        }

    }

    fclose(f);

    

    

    snprintf(fname2, 40, "%s-a.pgm", filename);



    f = fopen(fname2, "w");

    if (!f) {

        perror(fname2);

        exit(1);

    }

    fprintf(f, "P5\n"

            "%d %d\n"

            "%d\n",

            w, h, 255);

    for(y = 0; y < h; y++) {

        for(x = 0; x < w; x++) {

            v = rgba_palette[bitmap[y * w + x]];

            putc((v >> 24) & 0xff, f);

        }

    }

    fclose(f);

    

    snprintf(command, 1024, "pnmtopng -alpha %s %s > %s.png 2> /dev/null", fname2, fname, filename);

    system(command);

    

    snprintf(command, 1024, "rm %s %s", fname, fname2);

    system(command);

}

#endif



static void png_save2(const char *filename, uint32_t *bitmap, int w, int h)

{

    int x, y, v;

    FILE *f;

    char fname[40], fname2[40];

    char command[1024];

    

    snprintf(fname, 40, "%s.ppm", filename);



    f = fopen(fname, "w");

    if (!f) {

        perror(fname);

        exit(1);

    }

    fprintf(f, "P6\n"

            "%d %d\n"

            "%d\n",

            w, h, 255);

    for(y = 0; y < h; y++) {

        for(x = 0; x < w; x++) {

            v = bitmap[y * w + x];

            putc((v >> 16) & 0xff, f);

            putc((v >> 8) & 0xff, f);

            putc((v >> 0) & 0xff, f);

        }

    }

    fclose(f);

    

    

    snprintf(fname2, 40, "%s-a.pgm", filename);



    f = fopen(fname2, "w");

    if (!f) {

        perror(fname2);

        exit(1);

    }

    fprintf(f, "P5\n"

            "%d %d\n"

            "%d\n",

            w, h, 255);

    for(y = 0; y < h; y++) {

        for(x = 0; x < w; x++) {

            v = bitmap[y * w + x];

            putc((v >> 24) & 0xff, f);

        }

    }

    fclose(f);

    

    snprintf(command, 1024, "pnmtopng -alpha %s %s > %s.png 2> /dev/null", fname2, fname, filename);

    system(command);

    

    snprintf(command, 1024, "rm %s %s", fname, fname2);

    system(command);

}

#endif



#define RGBA(r,g,b,a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))



typedef struct DVBSubCLUT {

    uint32_t clut4[4];

    uint32_t clut16[16];

    uint32_t clut256[256];

} DVBSubCLUT;



static DVBSubCLUT default_clut;



typedef struct DVBSubObjectDisplay {

    int object_id;

    int region_id;



    int x_pos;

    int y_pos;



    int fgcolour;

    int bgcolour;

    

    struct DVBSubObjectDisplay *region_list_next;

    struct DVBSubObjectDisplay *object_list_next;   

} DVBSubObjectDisplay;



typedef struct DVBSubObject {

    int id;



    int type;

    

    int tmp_width;

    int tmp_height;

    int tmp_clut;



    struct DVBSubObjectDisplay *display_list;   

    

    struct DVBSubObject *next;

} DVBSubObject;



typedef struct DVBSubRegionDisplay {

    int region_id;



    int x_pos;

    int y_pos;



    struct DVBSubRegionDisplay *next;

} DVBSubRegionDisplay;



typedef struct DVBSubRegion {

    int width;

    int height;

    int depth;

    

    int clut;

    int bgcolour;

    

    uint8_t *pbuf;

    int buf_size;



    struct DVBSubObjectDisplay *display_list;

} DVBSubRegion;



typedef struct DVBSubContext {

    int composition_id;

    int ancillary_id;



    int time_out;

    DVBSubRegion *region[256];

    DVBSubCLUT   *clut[256];

    DVBSubObject *object;

    

    int display_list_size;

    DVBSubRegionDisplay *display_list;

} DVBSubContext;





static DVBSubObject* get_object(DVBSubObject *first, int object_id)

{

    DVBSubObject *ptr = first;



    while (ptr != NULL) {

        ptr = ptr->next;

    }



    ptr = first;



    while (ptr != NULL && ptr->id != object_id) {

        ptr = ptr->next;

    }

    

    if (ptr != NULL && ptr->id == object_id)

        return ptr;

        

    return NULL;

}



static void delete_region_display_list(DVBSubContext *ctx, DVBSubRegion *region)

{

    DVBSubObject *object, *obj2, **obj2_ptr;

    DVBSubObjectDisplay *display, *obj_disp, **obj_disp_ptr;



    while (region->display_list != NULL) {

        display = region->display_list;

        

        object = get_object(ctx->object, display->object_id);

        

        if (object != NULL) {

            obj_disp = object->display_list;

            obj_disp_ptr = &object->display_list;

            

            while (obj_disp != NULL && obj_disp != display) {

                obj_disp_ptr = &obj_disp->object_list_next;

                obj_disp = obj_disp->object_list_next;

            }

                

            if (obj_disp) {

                *obj_disp_ptr = obj_disp->object_list_next;

                

                if (object->display_list == NULL) {

                    obj2 = ctx->object;

                    obj2_ptr = &ctx->object;



                    while (obj2 != NULL && obj2 != object) {

                        obj2_ptr = &obj2->next;

                        obj2 = obj2->next;

                    }

                    

                    if (obj2 != object)

                        abort();

                        

                    *obj2_ptr = obj2->next;

                    

                    av_free(obj2);

                }

            }

        }

        

        region->display_list = display->region_list_next;

        

        av_free(display);

    }

                

}



static int dvbsub_init_decoder(AVCodecContext *avctx)

{

    int i, r, g, b, a = 0;

    DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data;



    for (i = 0; i < 256; i++)

        cropTbl[i + MAX_NEG_CROP] = i;

    for (i = 0; i < MAX_NEG_CROP; i++)

    {

        cropTbl[i] = 0;

        cropTbl[i + MAX_NEG_CROP + 256] = 255;

    }



    cm = cropTbl + MAX_NEG_CROP;



    memset(avctx->priv_data, 0, sizeof(DVBSubContext));

    

    ctx->composition_id = avctx->sub_id & 0xffff;

    ctx->ancillary_id = avctx->sub_id >> 16;



    default_clut.clut4[0] = RGBA(  0,   0,   0,   0);

    default_clut.clut4[1] = RGBA(255, 255, 255, 255);

    default_clut.clut4[2] = RGBA(  0,   0,   0, 255);

    default_clut.clut4[3] = RGBA(127, 127, 127, 255);



    default_clut.clut16[0] = RGBA(  0,   0,   0,   0);

    for (i = 1; i < 16; i++) {

        if (i < 8) {

            r = (i & 1) ? 255 : 0;

            g = (i & 2) ? 255 : 0;

            b = (i & 4) ? 255 : 0;

        } else {

            r = (i & 1) ? 127 : 0;

            g = (i & 2) ? 127 : 0;

            b = (i & 4) ? 127 : 0;

        }           

        default_clut.clut16[i] = RGBA(r, g, b, 255);

    }



    default_clut.clut256[0] = RGBA(  0,   0,   0,   0);

    for (i = 1; i < 256; i++) {

        if (i < 8) {

            r = (i & 1) ? 255 : 0;

            g = (i & 2) ? 255 : 0;

            b = (i & 4) ? 255 : 0;

            a = 63;

        } else {

            switch (i & 0x88) {

            case 0x00:

                r = ((i & 1) ? 85 : 0) + ((i & 0x10) ? 170 : 0);

                g = ((i & 2) ? 85 : 0) + ((i & 0x20) ? 170 : 0);

                b = ((i & 4) ? 85 : 0) + ((i & 0x40) ? 170 : 0);

                a = 255;

                break;

            case 0x08:

                r = ((i & 1) ? 85 : 0) + ((i & 0x10) ? 170 : 0);

                g = ((i & 2) ? 85 : 0) + ((i & 0x20) ? 170 : 0);

                b = ((i & 4) ? 85 : 0) + ((i & 0x40) ? 170 : 0);

                a = 127;

                break;

            case 0x80:

                r = 127 + ((i & 1) ? 43 : 0) + ((i & 0x10) ? 85 : 0);

                g = 127 + ((i & 2) ? 43 : 0) + ((i & 0x20) ? 85 : 0);

                b = 127 + ((i & 4) ? 43 : 0) + ((i & 0x40) ? 85 : 0);

                a = 255;

                break;

            case 0x88:

                r = ((i & 1) ? 43 : 0) + ((i & 0x10) ? 85 : 0);

                g = ((i & 2) ? 43 : 0) + ((i & 0x20) ? 85 : 0);

                b = ((i & 4) ? 43 : 0) + ((i & 0x40) ? 85 : 0);

                a = 255;

                break;

            }

        }           

        default_clut.clut256[i] = RGBA(r, g, b, a);

    }



    return 0;

}



static int dvbsub_close_decoder(AVCodecContext *avctx)

{

    return 0;

}



static int get_bits(int num, uint8_t **srcbuf, uint8_t *sbuf_end, int *bit_pos)

{

    int return_val = 0;



    if (*bit_pos > 7)

        *bit_pos &= 7;

        

    while (num > (8 - *bit_pos))

    {

        num -= (8 - *bit_pos);

        return_val |= ((**srcbuf) & ((1 << *bit_pos) - 1)) << num;

        *bit_pos = 0;

        (*srcbuf)++;

    }

        

    return_val |= ((**srcbuf) >> (8 - (*bit_pos + num))) & ((1 << num) - 1);

    *bit_pos += num;



    if (*bit_pos > 7) {

        *bit_pos &= 7;

        (*srcbuf)++;

    }

    

    return return_val;

}



static int dvbsub_read_2bit_string(uint8_t *destbuf, int dbuf_len, 

                                   uint8_t **srcbuf, uint8_t *sbuf_end, 

                                   int non_mod, uint8_t *map_table)

{

    int bit_pos = 0;

    int bits;

    int run_length;

    int pixels_read = 0;

    

    

    while (*srcbuf < sbuf_end && pixels_read < dbuf_len) {

        bits = get_bits(2, srcbuf, sbuf_end, &bit_pos);



        if (bits != 0) {

            if (non_mod != 1 || bits != 1) {

                if (map_table != NULL)

                    *destbuf++ = map_table[bits];

                else

                    *destbuf++ = bits;

            }

            pixels_read++;

        } else {

            bits = get_bits(1, srcbuf, sbuf_end, &bit_pos);

            if (bits == 1) {

                run_length = get_bits(3, srcbuf, sbuf_end, &bit_pos) + 3;

                bits = get_bits(2, srcbuf, sbuf_end, &bit_pos);

                

                if (non_mod == 1 && bits == 1)

                    pixels_read += run_length;

                else {

                    if (map_table != NULL)

                        bits = map_table[bits];

                    while (run_length-- > 0 && pixels_read < dbuf_len) {

                        *destbuf++ = bits;

                        pixels_read++;

                    }

                }

            } else {

                bits = get_bits(1, srcbuf, sbuf_end, &bit_pos);

                if (bits == 0) {

                    bits = get_bits(2, srcbuf, sbuf_end, &bit_pos);

                    if (bits == 2) {

                        run_length = get_bits(4, srcbuf, sbuf_end, &bit_pos) + 12;

                        bits = get_bits(2, srcbuf, sbuf_end, &bit_pos);



                        if (non_mod == 1 && bits == 1)

                            pixels_read += run_length;

                        else {

                            if (map_table != NULL)

                                bits = map_table[bits];

                            while (run_length-- > 0 && pixels_read < dbuf_len) {

                                *destbuf++ = bits;

                                pixels_read++;

                            }

                        }

                    } else if (bits == 3) {

                        run_length = get_bits(8, srcbuf, sbuf_end, &bit_pos) + 29;

                        bits = get_bits(2, srcbuf, sbuf_end, &bit_pos);



                        if (non_mod == 1 && bits == 1)

                            pixels_read += run_length;

                        else {

                            if (map_table != NULL)

                                bits = map_table[bits];

                            while (run_length-- > 0 && pixels_read < dbuf_len) {

                                *destbuf++ = bits;

                                pixels_read++;

                            }

                        }

                    } else if (bits == 1) {

                        pixels_read += 2;

                        if (map_table != NULL)

                            bits = map_table[0];

                        else

                            bits = 0;

                        if (pixels_read <= dbuf_len) {

                            *destbuf++ = bits;

                            *destbuf++ = bits;

                        }

                    } else {

                        if (bit_pos != 0)

                            (*srcbuf)++;

                        return pixels_read;

                    }

                } else {

                    if (map_table != NULL)

                        bits = map_table[0];

                    else

                        bits = 0;

                    *destbuf++ = bits;

                    pixels_read++;

                }

            }

        }

    }

    

    if (get_bits(6, srcbuf, sbuf_end, &bit_pos) != 0)

        av_log(0, AV_LOG_ERROR, "DVBSub error: line overflow\n");



    if (bit_pos != 0)

        (*srcbuf)++;

    return pixels_read;

}

    

static int dvbsub_read_4bit_string(uint8_t *destbuf, int dbuf_len, 

                                   uint8_t **srcbuf, uint8_t *sbuf_end, 

                                   int non_mod, uint8_t *map_table)

{

    int bit_pos = 0;

    int bits;

    int run_length;

    int pixels_read = 0;

    

    

    while (*srcbuf < sbuf_end && pixels_read < dbuf_len) {

        bits = get_bits(4, srcbuf, sbuf_end, &bit_pos);



        if (bits != 0) {

            if (non_mod != 1 || bits != 1) {

                if (map_table != NULL)

                    *destbuf++ = map_table[bits];

                else

                    *destbuf++ = bits;

            }

            pixels_read++;

        } else {

            bits = get_bits(1, srcbuf, sbuf_end, &bit_pos);

            if (bits == 0) {

                run_length = get_bits(3, srcbuf, sbuf_end, &bit_pos);

                

                if (run_length == 0) {

                    if (bit_pos != 0)

                        (*srcbuf)++;

                    return pixels_read;

                }

                    

                run_length += 2;

                

                if (map_table != NULL)

                    bits = map_table[0];

                else

                    bits = 0;

                

                while (run_length-- > 0 && pixels_read < dbuf_len) {

                    *destbuf++ = bits;

                    pixels_read++;

                }

            } else {

                bits = get_bits(1, srcbuf, sbuf_end, &bit_pos);

                if (bits == 0) {

                    run_length = get_bits(2, srcbuf, sbuf_end, &bit_pos) + 4;

                    bits = get_bits(4, srcbuf, sbuf_end, &bit_pos);



                    if (non_mod == 1 && bits == 1)

                        pixels_read += run_length;

                    else {

                        if (map_table != NULL)

                            bits = map_table[bits];

                        while (run_length-- > 0 && pixels_read < dbuf_len) {

                            *destbuf++ = bits;

                            pixels_read++;

                        }

                    }

                } else {

                    bits = get_bits(2, srcbuf, sbuf_end, &bit_pos);

                    if (bits == 2) {

                        run_length = get_bits(4, srcbuf, sbuf_end, &bit_pos) + 9;

                        bits = get_bits(4, srcbuf, sbuf_end, &bit_pos);

                        

                        if (non_mod == 1 && bits == 1)

                            pixels_read += run_length;

                        else {

                            if (map_table != NULL)

                                bits = map_table[bits];

                            while (run_length-- > 0 && pixels_read < dbuf_len) {

                                *destbuf++ = bits;

                                pixels_read++;

                            }

                        }

                    } else if (bits == 3) {

                        run_length = get_bits(8, srcbuf, sbuf_end, &bit_pos) + 25;

                        bits = get_bits(4, srcbuf, sbuf_end, &bit_pos);



                        if (non_mod == 1 && bits == 1)

                            pixels_read += run_length;

                        else {

                            if (map_table != NULL)

                                bits = map_table[bits];

                            while (run_length-- > 0 && pixels_read < dbuf_len) {

                                *destbuf++ = bits;

                                pixels_read++;

                            }

                        }

                    } else if (bits == 1) {

                        pixels_read += 2;

                        if (map_table != NULL)

                            bits = map_table[0];

                        else

                            bits = 0;

                        if (pixels_read <= dbuf_len) {

                            *destbuf++ = bits;

                            *destbuf++ = bits;

                        }

                    } else {

                        if (map_table != NULL)

                            bits = map_table[0];

                        else

                            bits = 0;

                        *destbuf++ = bits;

                        pixels_read ++;

                    }

                }

            }

        }

    }

    

    if (get_bits(8, srcbuf, sbuf_end, &bit_pos) != 0)

        av_log(0, AV_LOG_ERROR, "DVBSub error: line overflow\n");

    

    if (bit_pos != 0)

        (*srcbuf)++;

    return pixels_read;

}

    

static int dvbsub_read_8bit_string(uint8_t *destbuf, int dbuf_len, 

                                    uint8_t **srcbuf, uint8_t *sbuf_end, 

                                    int non_mod, uint8_t *map_table)

{

    int bits;

    int run_length;

    int pixels_read = 0;

    

    

    while (*srcbuf < sbuf_end && pixels_read < dbuf_len) {

        bits = *(*srcbuf)++;

    

        if (bits != 0) {

            if (non_mod != 1 || bits != 1) {

                if (map_table != NULL)

                    *destbuf++ = map_table[bits];

                else

                    *destbuf++ = bits;

            }

            pixels_read++;

        } else {

            bits = *(*srcbuf)++;

            run_length = bits & 0x7f;

            if ((bits & 0x80) == 0) {

                if (run_length == 0) {

                    return pixels_read;

                }

                                    

                if (map_table != NULL)

                    bits = map_table[0];

                else

                    bits = 0;

                while (run_length-- > 0 && pixels_read < dbuf_len) {

                    *destbuf++ = bits;

                    pixels_read++;

                }

            } else {

                bits = *(*srcbuf)++;



                if (non_mod == 1 && bits == 1)

                    pixels_read += run_length;

                if (map_table != NULL)

                    bits = map_table[bits];

                else while (run_length-- > 0 && pixels_read < dbuf_len) {

                    *destbuf++ = bits;

                    pixels_read++;

                }

            }

        }

    }

    

    if (*(*srcbuf)++ != 0)

        av_log(0, AV_LOG_ERROR, "DVBSub error: line overflow\n");

    

    return pixels_read;

}

    





static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDisplay *display,

                                          uint8_t *buf, int buf_size, int top_bottom, int non_mod)

{

    DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data;



    DVBSubRegion *region = ctx->region[display->region_id];

    uint8_t *buf_end = buf + buf_size;

    uint8_t *pbuf;

    int x_pos, y_pos;

    int i;

    

    uint8_t map2to4[] = { 0x0,  0x7,  0x8,  0xf};

    uint8_t map2to8[] = {0x00, 0x77, 0x88, 0xff};

    uint8_t map4to8[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 

                         0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};

    uint8_t *map_table;

    

#ifdef DEBUG_PACKET_CONTENTS

    av_log(avctx, AV_LOG_INFO, "DVB pixel block size %d, %s field:\n", buf_size,

                top_bottom ? "bottom" : "top");



    for (i = 0; i < buf_size; i++)

    {

        av_log(avctx, AV_LOG_INFO, "%02x ", buf[i]);

        if (i % 16 == 15)

            av_log(avctx, AV_LOG_INFO, "\n");

    }

    

    if (i % 16 != 0)

        av_log(avctx, AV_LOG_INFO, "\n");



#endif



    if (region == 0)

        return;

    

    pbuf = region->pbuf;

    

    x_pos = display->x_pos;

    y_pos = display->y_pos;

    

    if ((y_pos & 1) != top_bottom)

        y_pos++;

        

    while (buf < buf_end) {

        if (x_pos > region->width || y_pos > region->height) {

            av_log(avctx, AV_LOG_ERROR, "Invalid object location!\n");

            return;

        }

        

        switch (*buf++) {

        case 0x10:

            if (region->depth == 8)

                map_table = map2to8;

            else if (region->depth == 4)

                map_table = map2to4;

            else

                map_table = NULL;

                

            x_pos += dvbsub_read_2bit_string(pbuf + (y_pos * region->width) + x_pos, 

                                                region->width - x_pos, &buf, buf_end, 

                                                non_mod, map_table);

            break;

        case 0x11:

            if (region->depth < 4) {

                av_log(avctx, AV_LOG_ERROR, "4-bit pixel string in %d-bit region!\n", region->depth);

                return;

            }

                

            if (region->depth == 8)

                map_table = map4to8;

            else

                map_table = NULL;

                

            x_pos += dvbsub_read_4bit_string(pbuf + (y_pos * region->width) + x_pos, 

                                                region->width - x_pos, &buf, buf_end, 

                                                non_mod, map_table);

            break;

        case 0x12:

            if (region->depth < 8) {

                av_log(avctx, AV_LOG_ERROR, "8-bit pixel string in %d-bit region!\n", region->depth);

                return;

            }

                

            x_pos += dvbsub_read_8bit_string(pbuf + (y_pos * region->width) + x_pos, 

                                                region->width - x_pos, &buf, buf_end, 

                                                non_mod, NULL);

            break;

            

        case 0x20:

            map2to4[0] = (*buf) >> 4;

            map2to4[1] = (*buf++) & 0xf;

            map2to4[2] = (*buf) >> 4;

            map2to4[3] = (*buf++) & 0xf;

            break;

        case 0x21:

            for (i = 0; i < 4; i++)

                map2to8[i] = *buf++;

            break;

        case 0x22:

            for (i = 0; i < 16; i++)

                map4to8[i] = *buf++;

            break;

            

        case 0xf0:

            x_pos = display->x_pos;

            y_pos += 2;

            break;

        default:

            av_log(avctx, AV_LOG_INFO, "Unknown/unsupported pixel block 0x%x\n", *(buf-1));

        }

    }

    

}



static void dvbsub_parse_object_segment(AVCodecContext *avctx,

                                        uint8_t *buf, int buf_size)

{

    DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data;

    

    uint8_t *buf_end = buf + buf_size;

    uint8_t *block;

    int object_id;

    DVBSubObject *object;

    DVBSubObjectDisplay *display;

    int top_field_len, bottom_field_len;

    

    int coding_method, non_modifying_colour;

    

    object_id = getbe16(buf);

    buf += 2;

    

    object = get_object(ctx->object, object_id);



    if (!object) 

        return; 

    

    coding_method = ((*buf) >> 2) & 3;

    non_modifying_colour = ((*buf++) >> 1) & 1;

    

    if (coding_method == 0) {

        top_field_len = getbe16(buf);

        buf += 2;

        bottom_field_len = getbe16(buf);

        buf += 2;

        

        if (buf + top_field_len + bottom_field_len > buf_end) {

            av_log(avctx, AV_LOG_ERROR, "Field data size too large\n");

            return;

        }       

        

        for (display = object->display_list; display != 0; display = display->object_list_next) {

            block = buf;



            dvbsub_parse_pixel_data_block(avctx, display, block, top_field_len, 0,

                                            non_modifying_colour);



            if (bottom_field_len > 0)

                block = buf + top_field_len;

            else

                bottom_field_len = top_field_len;



            dvbsub_parse_pixel_data_block(avctx, display, block, bottom_field_len, 1,

                                            non_modifying_colour);

        }

        

/*  } else if (coding_method == 1) {*/

        

    } else {

        av_log(avctx, AV_LOG_ERROR, "Unknown object coding %d\n", coding_method);

    }

    

}



#define SCALEBITS 10

#define ONE_HALF  (1 << (SCALEBITS - 1))

#define FIX(x)    ((int) ((x) * (1<<SCALEBITS) + 0.5))



#define YUV_TO_RGB1_CCIR(cb1, cr1)\

{\

    cb = (cb1) - 128;\

    cr = (cr1) - 128;\

    r_add = FIX(1.40200*255.0/224.0) * cr + ONE_HALF;\

    g_add = - FIX(0.34414*255.0/224.0) * cb - FIX(0.71414*255.0/224.0) * cr + \

            ONE_HALF;\

    b_add = FIX(1.77200*255.0/224.0) * cb + ONE_HALF;\

}



#define YUV_TO_RGB2_CCIR(r, g, b, y1)\

{\

    y = ((y1) - 16) * FIX(255.0/219.0);\

    r = cm[(y + r_add) >> SCALEBITS];\

    g = cm[(y + g_add) >> SCALEBITS];\

    b = cm[(y + b_add) >> SCALEBITS];\

}





static void dvbsub_parse_clut_segment(AVCodecContext *avctx,

                                        uint8_t *buf, int buf_size)

{

    DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data;

    

    uint8_t *buf_end = buf + buf_size;

    int clut_id;

    DVBSubCLUT *clut;

    int entry_id, depth , full_range;

    int y, cr, cb, alpha;

    int r, g, b, r_add, g_add, b_add;



#ifdef DEBUG_PACKET_CONTENTS

    int i;



    av_log(avctx, AV_LOG_INFO, "DVB clut packet:\n");



    for (i=0; i < buf_size; i++)

    {

        av_log(avctx, AV_LOG_INFO, "%02x ", buf[i]);

        if (i % 16 == 15)

            av_log(avctx, AV_LOG_INFO, "\n");

    }

    

    if (i % 16 != 0)

        av_log(avctx, AV_LOG_INFO, "\n");



#endif



    clut_id = *buf++;

    buf += 1;

    

    if (!ctx->clut[clut_id]) {

        ctx->clut[clut_id] = av_malloc(sizeof(DVBSubCLUT));

        

        memcpy(ctx->clut[clut_id], &default_clut, sizeof(DVBSubCLUT));

    }

        

    clut = ctx->clut[clut_id];

    

    while (buf + 4 < buf_end)

    {

        entry_id = *buf++;

        

        depth = (*buf) & 0xe0;

                    

        if (depth == 0) {

            av_log(avctx, AV_LOG_ERROR, "Invalid clut depth 0x%x!\n", *buf);

            return;

        }

        

        full_range = (*buf++) & 1;

        

        if (full_range) {

            y = *buf++;

            cr = *buf++;

            cb = *buf++;

            alpha = *buf++;

        } else {

            y = buf[0] & 0xfc;

            cr = (((buf[0] & 3) << 2) | ((buf[1] >> 6) & 3)) << 4;

            cb = (buf[1] << 2) & 0xf0;

            alpha = (buf[1] << 6) & 0xc0;

            

            buf += 2;

        }

        

        if (y == 0)

            alpha = 0xff;

        

        YUV_TO_RGB1_CCIR(cb, cr);

        YUV_TO_RGB2_CCIR(r, g, b, y);

        

#ifdef DEBUG

        av_log(avctx, AV_LOG_INFO, "clut %d := (%d,%d,%d,%d)\n", entry_id, r, g, b, alpha);

#endif

        

        if (depth & 0x80)

            clut->clut4[entry_id] = RGBA(r,g,b,255 - alpha);

        if (depth & 0x40)

            clut->clut16[entry_id] = RGBA(r,g,b,255 - alpha);

        if (depth & 0x20)

            clut->clut256[entry_id] = RGBA(r,g,b,255 - alpha);

    }

}





static void dvbsub_parse_region_segment(AVCodecContext *avctx,

                                        uint8_t *buf, int buf_size)

{

    DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data;

    

    uint8_t *buf_end = buf + buf_size;

    int region_id, object_id;

    DVBSubRegion *region;

    DVBSubObject *object;

    DVBSubObjectDisplay *display;

    int fill;

    

    if (buf_size < 10)

        return;

    

    region_id = *buf++;

    

    if (!ctx->region[region_id])

        ctx->region[region_id] = av_mallocz(sizeof(DVBSubRegion));

        

    region = ctx->region[region_id];

    

    fill = ((*buf++) >> 3) & 1;

    

    region->width = getbe16(buf);

    buf += 2;

    region->height = getbe16(buf);

    buf += 2;

    

    if (region->width * region->height != region->buf_size) {

        if (region->pbuf != 0)

            av_free(region->pbuf);

    

        region->buf_size = region->width * region->height;

        

        region->pbuf = av_malloc(region->buf_size);

        

        fill = 1;

    }

    

    region->depth = 1 << (((*buf++) >> 2) & 7);

    region->clut = *buf++;

        

    if (region->depth == 8)

        region->bgcolour = *buf++;

    else {

        buf += 1;

        

        if (region->depth == 4)

            region->bgcolour = (((*buf++) >> 4) & 15);

        else

            region->bgcolour = (((*buf++) >> 2) & 3);

    }



#ifdef DEBUG

    av_log(avctx, AV_LOG_INFO, "Region %d, (%dx%d)\n", region_id, region->width, region->height);

#endif



    if (fill) {

        memset(region->pbuf, region->bgcolour, region->buf_size);

#ifdef DEBUG

        av_log(avctx, AV_LOG_INFO, "Fill region (%d)\n", region->bgcolour);

#endif

    }



    delete_region_display_list(ctx, region);



    while (buf + 5 < buf_end) {

        object_id = getbe16(buf);

        buf += 2;

                

        object = get_object(ctx->object, object_id);



        if (!object) {

            object = av_mallocz(sizeof(DVBSubObject));

            

            object->id = object_id;

            object->next = ctx->object;

            ctx->object = object;

        }

        

        object->type = (*buf) >> 6;

        

        display = av_mallocz(sizeof(DVBSubObjectDisplay));

        

        display->object_id = object_id;

        display->region_id = region_id;

        

        display->x_pos = getbe16(buf) & 0xfff;

        buf += 2;

        display->y_pos = getbe16(buf) & 0xfff;

        buf += 2;

        

        if ((object->type == 1 || object->type == 2) && buf+1 < buf_end) {

            display->fgcolour = *buf++;

            display->bgcolour = *buf++;

        }

        

        display->region_list_next = region->display_list;

        region->display_list = display;

        

        display->object_list_next = object->display_list;

        object->display_list = display;

    }

}



static void dvbsub_parse_page_segment(AVCodecContext *avctx,

                                        uint8_t *buf, int buf_size)

{

    DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data;

    DVBSubRegionDisplay *display;

    DVBSubRegionDisplay *tmp_display_list, **tmp_ptr;

    

    uint8_t *buf_end = buf + buf_size;

    int region_id;

    int page_state;

    

    if (buf_size < 1)

        return;

    

    ctx->time_out = *buf++;

    page_state = ((*buf++) >> 2) & 3;

    

#ifdef DEBUG

    av_log(avctx, AV_LOG_INFO, "Page time out %ds, state %d\n", ctx->time_out, page_state);

#endif

    

    tmp_display_list = ctx->display_list;

    ctx->display_list = NULL;

    ctx->display_list_size = 0;

    

    while (buf + 5 < buf_end) {

        region_id = *buf++;

        buf += 1;

        

        display = tmp_display_list;

        tmp_ptr = &tmp_display_list;

        

        while (display != NULL && display->region_id != region_id) {

            tmp_ptr = &display->next;

            display = display->next;

        }

            

        if (display == NULL)

            display = av_mallocz(sizeof(DVBSubRegionDisplay));

        

        display->region_id = region_id;

        

        display->x_pos = getbe16(buf);

        buf += 2;

        display->y_pos = getbe16(buf);

        buf += 2;

        

        *tmp_ptr = display->next;

        

        display->next = ctx->display_list;

        ctx->display_list = display;

        ctx->display_list_size++;

        

#ifdef DEBUG

        av_log(avctx, AV_LOG_INFO, "Region %d, (%d,%d)\n", region_id, display->x_pos, display->y_pos);

#endif

    }

    

    while (tmp_display_list != 0) {

        display = tmp_display_list;

        

        tmp_display_list = display->next;

        

        av_free(display);

    }

    

}





#ifdef DEBUG_SAVE_IMAGES

static void save_display_set(DVBSubContext *ctx)

{

    DVBSubRegion *region;

    DVBSubRegionDisplay *display;

    DVBSubCLUT *clut;

    uint32_t *clut_table;

    int x_pos, y_pos, width, height;

    int x, y, y_off, x_off;

    uint32_t *pbuf;

    char filename[32];

    static int fileno_index = 0;



    x_pos = -1;

    y_pos = -1;

    width = 0;

    height = 0;

    

    for (display = ctx->display_list; display != 0; display = display->next) {

        region = ctx->region[display->region_id];

    

        if (x_pos == -1) {

            x_pos = display->x_pos;

            y_pos = display->y_pos;

            width = region->width;

            height = region->height;

        } else {

            if (display->x_pos < x_pos) {

                width += (x_pos - display->x_pos);

                x_pos = display->x_pos;

            }

            

            if (display->y_pos < y_pos) {

                height += (y_pos - display->y_pos);

                y_pos = display->y_pos;

            }

            

            if (display->x_pos + region->width > x_pos + width) {

                width = display->x_pos + region->width - x_pos;

            }

            

            if (display->y_pos + region->height > y_pos + height) {

                height = display->y_pos + region->height - y_pos;

            }

        }

    }

    

    if (x_pos >= 0) {

    

        pbuf = av_malloc(width * height * 4);



        for (display = ctx->display_list; display != 0; display = display->next) {

            region = ctx->region[display->region_id];



            x_off = display->x_pos - x_pos;

            y_off = display->y_pos - y_pos;



            clut = ctx->clut[region->clut];



            if (clut == 0)

                clut = &default_clut;



            switch (region->depth) {

            case 2:

                clut_table = clut->clut4;

                break;

            case 8:

                clut_table = clut->clut256;

                break;

            case 4:

            default:

                clut_table = clut->clut16;

                break;

            }

        

            for (y = 0; y < region->height; y++) {

                for (x = 0; x < region->width; x++) {

                    pbuf[((y + y_off) * width) + x_off + x] = 

                        clut_table[region->pbuf[y * region->width + x]];

                }

            }



        }   



        snprintf(filename, 32, "dvbs.%d", fileno_index);



        png_save2(filename, pbuf, width, height);



        av_free(pbuf);

    }

    

    fileno_index++;

}

#endif



static int dvbsub_display_end_segment(AVCodecContext *avctx, uint8_t *buf, 

                                        int buf_size, AVSubtitle *sub)

{

    DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data;



    DVBSubRegion *region;

    DVBSubRegionDisplay *display;

    AVSubtitleRect *rect;

    DVBSubCLUT *clut;

    uint32_t *clut_table;

    int i;

    

    sub->rects = NULL;

    sub->start_display_time = 0;

    sub->end_display_time = ctx->time_out * 1000;

    sub->format = 0;



    sub->num_rects = ctx->display_list_size;

    

    if (sub->num_rects == 0)

        return 0;

    

    sub->rects = av_mallocz(sizeof(AVSubtitleRect) * sub->num_rects);



    i = 0;



    for (display = ctx->display_list; display != 0; display = display->next) {

        region = ctx->region[display->region_id];

        rect = &sub->rects[i];

        

        rect->x = display->x_pos;

        rect->y = display->y_pos;

        rect->w = region->width;

        rect->h = region->height;

        rect->nb_colors = 16;

        rect->linesize = region->width;



        clut = ctx->clut[region->clut];

        

        if (clut == 0)

            clut = &default_clut;

            

        switch (region->depth) {

        case 2:

            clut_table = clut->clut4;

            break;

        case 8:

            clut_table = clut->clut256;

            break;

        case 4:

        default:

            clut_table = clut->clut16;

            break;

        }

        

        rect->rgba_palette = av_malloc((1 << region->depth) * sizeof(uint32_t));

        memcpy(rect->rgba_palette, clut_table, (1 << region->depth) * sizeof(uint32_t));

        

        rect->bitmap = av_malloc(region->buf_size);

        memcpy(rect->bitmap, region->pbuf, region->buf_size);

        

        i++;

    }

    

#ifdef DEBUG_SAVE_IMAGES

    save_display_set(ctx);

#endif

    

    return 1;

}



static int dvbsub_decode(AVCodecContext *avctx,

                         void *data, int *data_size,

                         uint8_t *buf, int buf_size)

{

    DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data;

    AVSubtitle *sub = (AVSubtitle*) data;

    uint8_t *p, *p_end;

    int segment_type;

    int page_id;

    int segment_length;

  

#ifdef DEBUG_PACKET_CONTENTS

    int i;



    av_log(avctx, AV_LOG_INFO, "DVB sub packet:\n");



    for (i=0; i < buf_size; i++)

    {

        av_log(avctx, AV_LOG_INFO, "%02x ", buf[i]);

        if (i % 16 == 15)

            av_log(avctx, AV_LOG_INFO, "\n");

    }

    

    if (i % 16 != 0)

        av_log(avctx, AV_LOG_INFO, "\n");



#endif



    if (buf_size <= 2)

        return -1;

        

    p = buf;

    p_end = buf + buf_size;

        

    if (*p++ != 0x20 || *p++ != 0x00) {

#ifdef DEBUG

        av_log(avctx, AV_LOG_INFO, "Bad packet header\n");

#endif

        return -1;

    }

        

    while (p < p_end && *p == 0x0f)

    {

        p += 1;

        segment_type = *p++;

        page_id = getbe16(p);

        p += 2;

        segment_length = getbe16(p);

        p += 2;

        

        if (page_id == ctx->composition_id || page_id == ctx->ancillary_id) {

            switch (segment_type) {

            case DVBSUB_PAGE_SEGMENT:

                dvbsub_parse_page_segment(avctx, p, segment_length);

                break;

            case DVBSUB_REGION_SEGMENT:

                dvbsub_parse_region_segment(avctx, p, segment_length);

                break;

            case DVBSUB_CLUT_SEGMENT:

                dvbsub_parse_clut_segment(avctx, p, segment_length);

                break;

            case DVBSUB_OBJECT_SEGMENT:

                dvbsub_parse_object_segment(avctx, p, segment_length);

                break;

            case DVBSUB_DISPLAY_SEGMENT:

                *data_size = dvbsub_display_end_segment(avctx, p, segment_length, sub);

                break;

            default:

#ifdef DEBUG

                av_log(avctx, AV_LOG_INFO, "Subtitling segment type 0x%x, page id %d, length %d\n", 

                        segment_type, page_id, segment_length);

#endif

                break;

            }

        }



        p += segment_length;

    }

    

    if (*p++ != 0xff || p != p_end)

    {

#ifdef DEBUG

        av_log(avctx, AV_LOG_INFO, "Bad packet footer\n");

#endif

        return -1;

    }



    return 0;

}





AVCodec dvbsub_decoder = {

    "dvbsub",

    CODEC_TYPE_SUBTITLE,

    CODEC_ID_DVB_SUBTITLE,

    sizeof(DVBSubContext),

    dvbsub_init_decoder,

    NULL,

    dvbsub_close_decoder,

    dvbsub_decode,

};



/* Parser (mostly) copied from dvdsub.c */



#define PARSE_BUF_SIZE  (65536 + 184)





/* parser definition */

typedef struct DVBSubParseContext {

    uint8_t *packet_buf;

    int packet_start;

    int packet_index;

} DVBSubParseContext;



static int dvbsub_parse_init(AVCodecParserContext *s)

{

    DVBSubParseContext *pc = s->priv_data;

    pc->packet_buf = av_malloc(PARSE_BUF_SIZE);



    return 0;

}



static int dvbsub_parse(AVCodecParserContext *s,

                        AVCodecContext *avctx,

                        uint8_t **poutbuf, int *poutbuf_size, 

                        const uint8_t *buf, int buf_size)

{

    DVBSubParseContext *pc = s->priv_data;

    

#ifdef DEBUG2

    int i;



    av_log(avctx, AV_LOG_INFO, "DVB parse packet pts=%Lx, lpts=%Lx, cpts=%Lx:\n", 

            s->pts, s->last_pts, s->cur_frame_pts[s->cur_frame_start_index]);



    for (i=0; i < buf_size; i++)

    {

        av_log(avctx, AV_LOG_INFO, "%02x ", buf[i]);

        if (i % 16 == 15)

            av_log(avctx, AV_LOG_INFO, "\n");

    }

    

    if (i % 16 != 0)

        av_log(avctx, AV_LOG_INFO, "\n");



#endif



    *poutbuf = NULL;

    *poutbuf_size = 0;

    

    s->fetch_timestamp = 1;



    if (buf_size == 0 || s->last_pts != AV_NOPTS_VALUE)

    {

        if (pc->packet_index != pc->packet_start)

        {

            *poutbuf = pc->packet_buf + pc->packet_start;

            *poutbuf_size = pc->packet_index - pc->packet_start;

        }

            

        pc->packet_start = pc->packet_index;

    } else {

        if (pc->packet_index != pc->packet_start)

        {

            memmove(pc->packet_buf, pc->packet_buf + pc->packet_start,

                    pc->packet_index - pc->packet_start);

                    

            pc->packet_index -= pc->packet_start;

            pc->packet_start = 0;

        }

    }

        

    if (buf_size + pc->packet_index > PARSE_BUF_SIZE)

        return -1;

        

    memcpy(pc->packet_buf + pc->packet_index, buf, buf_size);

    pc->packet_index += buf_size;



    return buf_size;

}



static void dvbsub_parse_close(AVCodecParserContext *s)

{

    DVBSubParseContext *pc = s->priv_data;

    av_freep(&pc->packet_buf);

}



AVCodecParser dvbsub_parser = {

    { CODEC_ID_DVB_SUBTITLE },

    sizeof(DVBSubParseContext),

    dvbsub_parse_init,

    dvbsub_parse,

    dvbsub_parse_close,

};




More information about the ffmpeg-devel mailing list