Skip to content
Snippets Groups Projects
dvbsubdec.c 38.8 KiB
Newer Older
  • Learn to ignore specific revisions
  •         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;
    
        region = get_region(ctx, region_id);
    
        if (region == NULL)
        {
            region = av_mallocz(sizeof(DVBSubRegion));
    
            region->next = ctx->region_list;
            ctx->region_list = region;
        }
    
        region->width = AV_RB16(buf);
    
        region->height = AV_RB16(buf);
    
        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);
    
        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 = AV_RB16(buf);
    
            object = get_object(ctx, object_id);
    
            if (object == NULL) {
                object = av_mallocz(sizeof(DVBSubObject));
    
                object->id = object_id;
                object->next = ctx->object_list;
                ctx->object_list = object;
            }
    
            display = av_mallocz(sizeof(DVBSubObjectDisplay));
    
            display->object_id = object_id;
            display->region_id = region_id;
    
            display->x_pos = AV_RB16(buf) & 0xfff;
    
            display->y_pos = AV_RB16(buf) & 0xfff;
    
            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;
    
        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
    
        if (page_state == 2)
        {
            delete_state(ctx);
        }
    
        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 = AV_RB16(buf);
    
            display->y_pos = AV_RB16(buf);
    
            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;
    
    }
    
    
    #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 != NULL; display = display->next) {
            region = get_region(ctx, 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;
                }
            }
        }
    
            pbuf = av_malloc(width * height * 4);
    
            for (display = ctx->display_list; display != NULL; display = display->next) {
                region = get_region(ctx, display->region_id);
    
                x_off = display->x_pos - x_pos;
                y_off = display->y_pos - y_pos;
    
                clut = get_clut(ctx, 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);
        }
    
    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)
            sub->rects = av_mallocz(sizeof(AVSubtitleRect) * sub->num_rects);
    
    
        i = 0;
    
        for (display = ctx->display_list; display != NULL; display = display->next) {
            region = get_region(ctx, 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 = get_clut(ctx, region->clut);
    
            if (clut == NULL)
                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);
    
    #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;
    
        while (p < p_end && *p == 0x0f)
        {
            p += 1;
            segment_type = *p++;
    
            page_id = AV_RB16(p);
    
            segment_length = AV_RB16(p);
    
            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 != p_end)
        {
    #ifdef DEBUG
            av_log(avctx, AV_LOG_INFO, "Junk at end of packet\n");
    #endif
            return -1;
        }
    
    
    }
    
    
    AVCodec dvbsub_decoder = {
        "dvbsub",
        CODEC_TYPE_SUBTITLE,
        CODEC_ID_DVB_SUBTITLE,
        sizeof(DVBSubContext),
        dvbsub_init_decoder,
        NULL,
        dvbsub_close_decoder,
        dvbsub_decode,
    };