[FFmpeg-devel] [PATCH v6 1/4] lavc: add FLIF decoding support

Anamitra Ghorui aghorui at teknik.io
Wed Aug 26 19:31:46 EEST 2020


Hello,
Thanks for the review. Please see replies below.

On 26/08/2020 18:44, Moritz Barsnick wrote:
> On Sun, Aug 23, 2020 at 00:09:32 +0530, Anamitra Ghorui wrote:
>> v2: Fix faulty patch
>> v3: Fix addressed errors, Add interlaced decoding support
>> v4: Fix Further cosmetics, C.Bucket Transform reading errors, Atomise patch
>> v5: Fix faulty patch
>> v6: Address pointed out errors, use av_freep everywhere, further cosmetics,
>>      redundancies.
> 
> These comments don't belong in the commit or the e-mail corresponding
> to the commit. So either in the "0/4" e-mail, or below the "---" below.
> 
>> Test files are available here: https://0x0.st/iYs_.zip
>>
>> Co-authored-by: Anamitra Ghorui <aghorui at teknik.io>
>> Co-authored-by: Kartik K Khullar <kartikkhullar840 at gmail.com>
>>
>> Signed-off-by: Anamitra Ghorui <aghorui at teknik.io>
>> ---
> 
> Place comments here.
>
Will do as a separate E-Mail

>> +    if (plane == 1 || plane == 2){
> 
> Please fix the bracket style -> ") {"
> 
>> +    if (plane != 2) {
>> +      prop_ranges[top].min = mind;
>> +      prop_ranges[top++].max = maxd;
>> +      prop_ranges[top].min = mind;
>> +      prop_ranges[top++].max = maxd;
>> +    }
> 
> Incorrect indentation.
> 
>> +    for(uint8_t i = 0; i < (lookback ? MAX_PLANES : num_planes); i++) {
> 
> Bracket style -> "for ("
> 
>> +/*
>> + * All constant plane pixel setting should be illegal in theory.
> 
> settings
> 
>> +static inline FLIF16ColorVal ff_flif16_pixel_get_fast(FLIF16Context *s,
>> +                                                      FLIF16PixelData *frame,
>> +                                                      uint8_t plane, uint32_t row,
>> +                                                      uint32_t col)
>> +{
>> +    if (s->plane_mode[plane]) {
>> +        return ((FLIF16ColorVal *) frame->data[plane])[row * frame->s_r[plane] + col * frame->s_c[plane]];
>> +    } else
>> +        return ((FLIF16ColorVal *) frame->data[plane])[0];
>> +    return 0;
> 
> Isn't this "return 0" dead code?
> 
>> +    if(bytestream2_get_bytes_left(gb) < FLIF16_RAC_MAX_RANGE_BYTES)
> 
> Bracket style -> "if ("
> 

Will fix all above

>> +uint8_t ff_flif16_rac_read_bit(FLIF16RangeCoder *rc,
>> +                               uint8_t *target)
>> +{
>> +    return ff_flif16_rac_get(rc, rc->range >> 1, target);
>> +}
> 
> If this is called often, you may want to mark it inline.
> 

On Nicolas George's suggestion, I had dropped the inline keyword because
the number of inline functions used may put stress on the memory cache.
I think I should only inine the "higher" functions and not the "lower"
functions like ff_flif16_rac_get in this case. Should I?

>> +uint32_t ff_flif16_rac_read_chance(FLIF16RangeCoder *rc,
>> +                                   uint64_t b12, uint8_t *target)
>> +{
>> +    uint32_t ret = ((rc->range) * b12 + 0x800) >> 12;
> 
> I don't think rc->range needs to be bracketed.
> 
>> +    if(!ff_flif16_rac_renorm(rc))
> 
> Bracket style.

Will fix

>> +#define RAC_NZ_GET(rc, ctx, chance, target)                                    \
>> +    if (!ff_flif16_rac_nz_read_internal((rc), (ctx), (chance),                 \
>> +                                        (uint8_t *) (target))) {               \
>> +        goto need_more_data;                                                   \
>> +    }
> 
> Functions are usually defined with a do{} while(0) wrapper. See the
> style in libavutil/intreadwrite.h.
Please see the section below regarding intermittent packet resuming.

>> +    return ret;
>> +
>> +}
> 
> Drop the empty line. ;-)
> 
>> +    if(!ff_flif16_rac_renorm(rc))
> 
> Bracket style.
> 

Will fix

>> +        while (rc->pos > 0) {
>> +            rc->pos--;
>> +            rc->left >>= 1;
>> +            rc->minabs1 = rc->have | (1 << rc->pos);
>> +            rc->maxabs0 = rc->have | rc->left;
>> +
>> +            if (rc->minabs1 > rc->amax) {
>> +                continue;
>> +            } else if (rc->maxabs0 >= rc->amin) {
>> +    case 3:
>> +                RAC_NZ_GET(rc, ctx, NZ_INT_MANT(rc->pos), &temp);
>> +                if (temp)
>> +                    rc->have = rc->minabs1;
>> +                temp = 0;
>> +            } else {
>> +                rc->have = rc->minabs1;
>> +            }
> 
> A case label in the middle of an if() (in a while() loop) is not
> readable to me. ;-)

Please see the section below regarding intermittent packet resuming.

>> +    m->stack_top = 0;
>> +    rc->segment2 = 0;
>> +    return 0;
>> +
>> +    need_more_data:
>> +    return AVERROR(EAGAIN);
> 
> I believe the label needs to be left-aligned.
> 
>> +        if(!m->forest[channel]->leaves)
> 
> Bracket style.
> 
>> +        if(!rc->curr_leaf) {
> 
> Ditto.
> 
>> +
>> +    end:
>> +    rc->curr_leaf = NULL;
> 
> "goto" label left alignment
> 
>> + * probability model. The other (simpler) model and this model ane non
> 
> *are
>

Will fix

>> +#define RAC_GET(rc, ctx, val1, val2, target, type) \
>> +    if (!ff_flif16_rac_process((rc), (ctx), (val1), (val2), (target), (type))) { \
>> +        goto need_more_data; \
>> +    }
> 
> do{} while(0)
> 
>> +#define MANIAC_GET(rc, m, prop, channel, min, max, target) \
>> +    { \
>> +        int ret = ff_flif16_maniac_read_int((rc), (m), (prop), (channel), (min), (max), (target)); \
>> +        if (ret < 0) { \
>> +            return ret; \
>> +        } else if (!ret) { \
>> +            goto need_more_data; \
>> +        } \
>> +    }
> 
> Ditto.
>

The FLIF format has no way of predetermining the end of the bitstream
without actually decompressing it first and reading all frame data. In
addition to that, frame data has been organised in a non-contiguous
manner (See also: [1]). In short, a pixel is encoded in this manner
for non interlaced images:

    for i in channels
        for j in rows
            for k in frames
                for l in columns
                    encode(pixel, i, j, k ,l)

Similarly, for non interlaced images, the successive "quality" blocks or
zoomlevels are coded in order.
Please see [2] for a more visual representation.

Therefore there is no way to split the image data into frames without
reading in the whole of the image data first. 
Hence what we had decided to do is write the decoder in a manner that it
will be able to take in any arbitrary packet size, and be able to resume
at any point the packet ends i.e. the range decoder says that it needs
more data.

To do that, decoding related functions which call the RAC have
FLIF16DecoderContext passed to them. The struct contains the member
segment. The function then contains a switch case which uses s->segment
to restore the function control to where it left off. All stack
allocated variables that may be otherwise lost are put in the context
instead. Similarly for other subsystem contexts.

I agree that the case statements between if statements and while
statements are not very good looking, but this allows us to extract the
function logic without having to set the, or look at the state
management variables and statements. Trying to make do without the
interleaving statements would cause us to set s->segment many times,
set multiple switch statements etc.

    func(FLIF16XYZContext *s, ...)
    {
        // Init stuff
        ...
        
        switch (s->segment) {
        /* Note how cases are right before RAC calls */
        case 1:
            RAC_GET(...);
            ...
        case 2:
            RAC_GET(...);
            ...
        ...
        }

        // End stuff
        ...
        return 0;

    need more data:
        ...
        return AVERROR(EAGAIN);
    }

I get your point about using do {} while (0) to break the sequence, it
does make sense now to ditch the need_more_data entirely and hence free 
it from having to using a specific goto label, however it introduces an 
additional level of indentation. This is how majority of the decoder 
logic and transform logic has been written, so it may make the code 
look a bit bulkier. Should I do it anyway?

    func(FLIF16XYZContext *s, ...)
    {
        // Init stuff
        ...

        do {
            switch (s->segment) {
            /* Note how cases are right before RAC calls */
            case 1:
                RAC_GET(...);

            case 2:
                RAC_GET(...);
            ...
            }

            // End stuff
            ...
            return 0;
        } while (0);

        // EAGAIN stuff
        ...
        return AVERROR(EAGAIN);
    }


> 
> I give up here. There are further 4000+ lines to review. What an
> impressive patch.>> Just this:
> 
>> +static void transform_palette_reverse(FLIF16Context *ctx,
>> +                                     FLIF16TransformContext *t_ctx,
>> +                                     FLIF16PixelData *frame,
>> +                                     uint32_t stride_row,
>> +                                     uint32_t stride_col)
>> +{
>> +    int r, c;
>> +    int P;
> 
> ffmpeg doesn't use capitals in variable names.

Will fix. Will correct any similar errors as noted above.

> Cheers,
> Moritz

[1]: http://ffmpeg.org/pipermail/ffmpeg-devel/2020-March/258465.html
[2]: https://ffmpeg.org/pipermail/ffmpeg-devel/2020-February/257797.html

-- 
Thanks,
Anamitra



More information about the ffmpeg-devel mailing list