FFMPEG 之 msrledec.c

1 RLE 简介

RLE(Run Length Encoding) 行程长度编码,属于熵编码,是最简单的图像压缩方法之一,比如视频msrle,tscc 以及bmp 格式 都是用的RLE 编码。 在FFMPEG 中msrledec.c 就是描述rle 格式的解码过程。 

RLE编码的原理比较简单,即在被压缩文件中寻找连续重复的数值,以重复次数和重复数值自身两个值取代文件中的连续值,重复次数称为行程长度。例如,若有一串字符信息aaaaabbbbcccddeefffaa,经RLE编码处理可表示为5a4b3c2d2e3f2a。用两个字节表示,前一个字节表示重复的次数,后一个字节表示数据的本身。

2 ff_msrle_decode()

在msrledec.c 中开出去的就ff_msrle_decode()一支API , 用它来decode rle 格式。如下代码所示,它内部分为 msrle_decode_pal4() 和 msrle_decode_8_16_24_32() 两个case.  想必这两种case 的压缩算法有一定的差异, 从switch(depth)来看, 根据颜色位深不同,进入不同的函数。

int ff_msrle_decode(AVCodecContext *avctx, AVFrame *pic,
                    int depth, GetByteContext *gb)
{
    switch(depth){
    case  4:
        return msrle_decode_pal4(avctx, pic, gb);
    case  8:
    case 16:
    case 24:
    case 32:
        return msrle_decode_8_16_24_32(avctx, pic, depth, gb);
    default:
        av_log(avctx, AV_LOG_ERROR, "Unknown depth %d\n", depth);
        return -1;
    }
}

2.1  msrle_decode_pal4()

msrle_decode_pal4()是decode  depth 为4 的函数,也就是decode RLE4 的函数。 RLE4与一般的行程编码算法相同点是都以两个字节表示图像数据中一串重复的数据。其中第1字节表示数据重复次数,第2字节表示重复数据自身。不同点是RLE4压缩算法在第1字节中所表示的重复数据的次数是像素重复次数,而不是一般RLE编码中所定义的字节重复次数。例如,一般RLE编码中的数据0X08 0X58表示将图像数据0X58连续重复显示8个字节,但在RLE4压缩算法中,则表示连续显示8个像素。另外,RLE4压缩算法与一般RLE压缩算法不同点还表现在:由于是针对16色图像数据,如表9-8所示,引进了4组特殊的识别码。

 

设有一幅原始图像数据如下,以16色BMP文件进行压缩。第1行:0X28 0X28 0X28 0X28 0X30 0X60 0X58 0X09 0X22… 0X46… 0X46(共8个)

第2行:0X18 0X68 0X67 0X88 0X88 0X88 0X88 … 0X900X78 

根据16色图像文件的RLE4编码原理,压缩后的图像数据为:第1行:0X08 0X28 0X00 0X0a 0X30 0X60 0X58 0X09 0X22…0X00 0X10 0X46 0X00 0X00 (注意:这里0x08表示8个像素,源码连续4个0x28, 每4bits 为一个像素, 正好是8个)

第2行:0X00 0X06 0X18 0X68 0X67 0X00 0X08 0X88 …0X00 0X04 0X90 0X78 0X00 0X01 

下面我们就对照msrle_decode_pal4()源码,看看RLE4 是如何被解析的。下列源码有添加中文注释,对照特殊识别码即可明白函数都在做什么。

static int msrle_decode_pal4(AVCodecContext *avctx, AVFrame *pic,
                             GetByteContext *gb)
{

        ....
    while (line >= 0 && pixel_ptr <= avctx->width) {
        ....
        rle_code = stream_byte = bytestream2_get_byteu(gb);//从文件读一个byte
        if (rle_code == 0) {//参照上图,00 表述特殊识别码
            /* fetch the next byte to see how to handle escape code */
            stream_byte = bytestream2_get_byte(gb);//从文件中读取下一个byte
            if (stream_byte == 0) {// 0x00 0x00 表示这一行已经结束
                /* line is done, goto the next one */
                line--;
                pixel_ptr = 0;
            } else if (stream_byte == 1) {//0x00 0x01 表示整个图像结束
                /* decode is done */
                return 0;
            } else if (stream_byte == 2) {//0x00 0x02 请参照上图解释
                /* reposition frame decode coordinates */
                stream_byte = bytestream2_get_byte(gb);
                pixel_ptr += stream_byte;
                stream_byte = bytestream2_get_byte(gb);
                line -= stream_byte;
            } else {// 0x00 0xN 表示接下来N 个字节都是不连续的
                // copy pixels from encoded stream
                odd_pixel =  stream_byte & 1;
                rle_code = (stream_byte + 1) / 2;
                extra_byte = rle_code & 0x01;
                if (pixel_ptr + 2*rle_code - odd_pixel > avctx->width ||
                    bytestream2_get_bytes_left(gb) < rle_code) {
                    av_log(avctx, AV_LOG_ERROR,
                           "MS RLE: frame/stream ptr just went out of bounds (copy)\n");
                    return AVERROR_INVALIDDATA;
                }

                for (i = 0; i < rle_code; i++) {
                    if (pixel_ptr >= avctx->width)
                        break;
                    stream_byte = bytestream2_get_byteu(gb);
                    pic->data[0][line * pic->linesize[0] + pixel_ptr] = stream_byte >> 4;
                    pixel_ptr++;
                    if (i + 1 == rle_code && odd_pixel)
                        break;
                    if (pixel_ptr >= avctx->width)
                        break;
                    pic->data[0][line * pic->linesize[0] + pixel_ptr] = stream_byte & 0x0F;
                    pixel_ptr++;
                }

                // if the RLE code is odd, skip a byte in the stream
                if (extra_byte)
                    bytestream2_skip(gb, 1);
            }
        } else {//这里是连续数据部分
            // decode a run of data
            if (pixel_ptr + rle_code > avctx->width + 1) {
                av_log(avctx, AV_LOG_ERROR,
                       "MS RLE: frame ptr just went out of bounds (run) %d %d %d\n", pixel_ptr, rle_code, avctx->width);
                return AVERROR_INVALIDDATA;
            }
            stream_byte = bytestream2_get_byte(gb);//stream_byte表示连续数据本身
            for (i = 0; i < rle_code; i++) {//rle_code 表示连续数据个数
                if (pixel_ptr >= avctx->width)
                    break;
                if ((i & 1) == 0)
                    pic->data[0][line * pic->linesize[0] + pixel_ptr] = stream_byte >> 4;
                else
                    pic->data[0][line * pic->linesize[0] + pixel_ptr] = stream_byte & 0x0F;
                pixel_ptr++;
            }
        }
    }
        ....
    return 0;
}

 

2.2 msrle_decode_8_16_24_32()

msrle_decode_8_16_24_32() 与msrle_decode_pal4()  压缩算法大致都差不多。 下列源码有添加中文注释,参照RLE 识别码阅读即可读懂代码都在做什么。

static int msrle_decode_8_16_24_32(AVCodecContext *avctx, AVFrame *pic,
                                   int depth, GetByteContext *gb)
{
       ....
    while (bytestream2_get_bytes_left(gb) > 0) {
        p1 = bytestream2_get_byteu(gb);//读取一个byte
        if(p1 == 0) { //Escape code// 0x00 表示特殊识别码
            p2 = bytestream2_get_byte(gb);//读取下一个byte
            if(p2 == 0) { //End-of-line// 0x00 0x00 表示行结束
                if (--line < 0) {
                    if (bytestream2_get_be16(gb) == 1) { // end-of-picture
                        return 0;
                    } else {
                        av_log(avctx, AV_LOG_ERROR,
                               "Next line is beyond picture bounds (%d bytes left)\n",
                               bytestream2_get_bytes_left(gb));
                        return AVERROR_INVALIDDATA;
                    }
                }
                output = pic->data[0] + line * pic->linesize[0];
                output_end = output + FFABS(pic->linesize[0]);
                pos = 0;
                continue;
            } else if(p2 == 1) { //End-of-picture 0x00 0x01 表示图像结束
                return 0;
            } else if(p2 == 2) { //Skip
                p1 = bytestream2_get_byte(gb);
                p2 = bytestream2_get_byte(gb);
                line -= p2;
                pos += p1;
                if (line < 0 || pos >= width){
                    av_log(avctx, AV_LOG_ERROR, "Skip beyond picture bounds\n");
                    return -1;
                }
                output = pic->data[0] + line * pic->linesize[0] + pos * (depth >> 3);
                output_end = pic->data[0] + line * pic->linesize[0] + FFABS(pic->linesize[0]);
                continue;
            }
            // Copy data
            if (output + p2 * (depth >> 3) > output_end) {
                bytestream2_skip(gb, 2 * (depth >> 3));
                continue;
            } else if (bytestream2_get_bytes_left(gb) < p2 * (depth >> 3)) {
                av_log(avctx, AV_LOG_ERROR, "bytestream overrun\n");
                return AVERROR_INVALIDDATA;
            }

            if ((depth == 8) || (depth == 24)) {
                bytestream2_get_bufferu(gb, output, p2 * (depth >> 3));
                output += p2 * (depth >> 3);

                // RLE8 copy is actually padded - and runs are not!
                if(depth == 8 && (p2 & 1)) {
                    bytestream2_skip(gb, 1);
                }
            } else if (depth == 16) {
                for(i = 0; i < p2; i++) {//0x00 0xN 表示有p2 = N 个不连续像素, 
                    *(uint16_t*)output = bytestream2_get_le16u(gb);
                    output += 2;
                }
            } else if (depth == 32) {
                for(i = 0; i < p2; i++) {
                    *(uint32_t*)output = bytestream2_get_le32u(gb);
                    output += 4;
                }
            }
            pos += p2;
        } else { //run of pixels 这里表示连续像素
            uint8_t pix[3]; //original pixel
            if (output + p1 * (depth >> 3) > output_end)
                continue;

            switch(depth){
            case  8:
                pix[0] = bytestream2_get_byte(gb);
                memset(output, pix[0], p1);
                output += p1;
                break;
            case 16:
                pix16  = bytestream2_get_le16(gb);
                for(i = 0; i < p1; i++) {//p1 表示连续像素的个数
                        *(uint16_t*)output = pix16;
                        output += 2;
                }
                break;
            case 24:
                pix[0] = bytestream2_get_byte(gb);
                pix[1] = bytestream2_get_byte(gb);
                pix[2] = bytestream2_get_byte(gb);
                for(i = 0; i < p1; i++) {
                        *output++ = pix[0];
                        *output++ = pix[1];
                        *output++ = pix[2];
                }
                break;
            case 32:
                pix32  = bytestream2_get_le32(gb);
                for(i = 0; i < p1; i++) {
                        *(uint32_t*)output = pix32;
                        output += 4;
                }
                break;
            }
            pos += p1;
        }
    }

    av_log(avctx, AV_LOG_WARNING, "MS RLE warning: no end-of-picture code\n");
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值