Rishabh Garg
Rishabh Garg

Reputation: 3

Dump subtitle from AVSubtitle in the file

In FFMPEG sofftware, AVPicture is used to store image data using data pointer and linesizes.It means all subtitles are stored in the form of picture inside ffmpeg. Now I have DVB subtitle and I want to dump picture of subtitles stored in AVPicture in a buffer. I know these images subtitles can be dump using for, fopen and sprintf. But do not know how to dump Subtitle.I have to dump subtitles in .ppm file format.

Can anyone help me to dump picture of subtitles in buffer from AVSubtitle .

Upvotes: 0

Views: 1349

Answers (2)

nafmo
nafmo

Reputation: 508

As this is where I ended up when searching for answers to how to create a thumbnail of an AVSubtitle, here is what I ended up using in my test application. The code is optimized for readability only. I got some help from this question which had some sample code.

Using avcodec_decode_subtitle2() I get a AVSubtitle structure. This contains a number of rectangles. First I iterate over the rectangles to find the max of x + w and y + h to determine the width and height of the target frame.

The color table in data[1] is RGBA, so I allocate an AVFrame called frame in AV_PIX_FMT_RGBA format and shuffle the pixels over to it:

struct [[gnu::packed]] rgbaPixel {
    uint8_t r;
    uint8_t g;
    uint8_t b;
    uint8_t a;
};

// Copy the pixel buffers
for (unsigned int i = 0; i < sub.num_rects; ++ i) {
    AVSubtitleRect* rect = sub.rects[i];
    for (int y = 0; y < rect->h; ++ y) {
        int dest_y = y + rect->y;

        // data[0] holds index data
        uint8_t *in_linedata = rect->data[0] + y * rect->linesize[0];

        // In AVFrame, data[0] holds the pixel buffer directly
        uint8_t *out_linedata = frame->data[0] + dest_y * frame->linesize[0];
        rgbaPixel *out_pixels = reinterpret_cast<rgbaPixel*>(out_linedata);

        for (int x = 0; x < rect->w; ++ x) {
            // data[1] contains the color map
            // compare libavcodec/dvbsubenc.c
            uint8_t colidx = in_linedata[x];
            uint32_t color = reinterpret_cast<uint32_t*>(rect->data[1])[colidx];

            // Now store the pixel in the target buffer
            out_pixels[x + rect->x] = rgbaPixel{
                .r = static_cast<uint8_t>((color >> 16) & 0xff),
                .g = static_cast<uint8_t>((color >>  8) & 0xff),
                .b = static_cast<uint8_t>((color >>  0) & 0xff),
                .a = static_cast<uint8_t>((color >> 24) & 0xff),
            };
        }
    }
}    

I did manage to push that AVFrame through an image decoder to output it as a bitmap image, and it looked OK. I did get green areas where the alpha channel is, but that might be an artifact of the settings in the JPEG encoder I used.

Upvotes: 0

the kamilz
the kamilz

Reputation: 1988

This process looks complex but actually very simple.
AVSubtitle is generic format, supports text and bitmap modes. Dvbsub format afaik bitmap only and the bitmap format can be differ like 16color or 256color mode as called CLUT_DEPTH.
I believe (in current ffmpeg) the bitmaps stored in AVSubtitleRect structure, which is member of AVSubtitle.
I assume you have a valid AVSubtitle packet(s) and if I understand correctly you can do these and it should work:
1) Check pkt->rect[0]->type. The pkt here is a valid AVSubtitle packet. It must be type of SUBTITLE_BITMAP.
2) If so, bitmap with and height can be read from pkt->rects[0]->w and pkt->rects[0]->h.
3) Bitmap data itself in will be pkt->rects[0]->data[0].
4) CLUT_DEPTH can be read from pkt->rects[0]->nb_colors.
5) And CLUT itself (color table) will be in pkt->rects[0]->data[1].

With these data, you can construct a valid .bmp file that can be viewable on windows or linux desktop, but I left this part to you.

PPM Info

First check this info about PPM format:
https://www.cs.swarthmore.edu/~soni/cs35/f13/Labs/extras/01/ppm_info.html

What I understand is PPM format uses RGB values (24bit/3bytes). It looks like to me all you have to do is construct a header according to data obtained from AVSubtitle packet above. And write a conversion function for dvbsub's indexed color buffer to RGB. I'm pretty sure somewhere there are some ready to use codes out there but I'll explain anyway.
In the picture frame data Dvbsub uses is liner and every pixel is 1 byte (even in 16color mode). This byte value is actually index value that correspond RGB (?) values stored in Color Look-Up Table (CLUT), in 16 color mode there are 16 index each 4 bytes, first 3 are R, G, B values and 4th one is alpha (transparency values, if PPM doesn't support this, ignore it).

I'm not sure if decoded subtitle still has encoded YUV values. I remember it should be plain RGBA format.

encode_dvb_subtitles function on ffmpeg shows how this encoding done. If you need it.
https://github.com/FFmpeg/FFmpeg/blob/a0ac49e38ee1d1011c394d7be67d0f08b2281526/libavcodec/dvbsub.c

Hope that helps.

Upvotes: 1

Related Questions