static int wavpack_decode_block(AVCodecContext *avctx, int block_no,

                                void *data, int *data_size,

                                const uint8_t *buf, int buf_size)

{

    WavpackContext *wc = avctx->priv_data;

    WavpackFrameContext *s;

    void *samples = data;

    int samplecount;

    int got_terms = 0, got_weights = 0, got_samples = 0, got_entropy = 0, got_bs = 0, got_float = 0;

    int got_hybrid = 0;

    const uint8_t* orig_buf = buf;

    const uint8_t* buf_end = buf + buf_size;

    int i, j, id, size, ssize, weights, t;

    int bpp, chan, chmask;



    if (buf_size == 0){

        *data_size = 0;

        return 0;

    }



    if(block_no >= wc->fdec_num && wv_alloc_frame_context(wc) < 0){

        av_log(avctx, AV_LOG_ERROR, "Error creating frame decode context\n");

        return -1;

    }



    s = wc->fdec[block_no];

    if(!s){

        av_log(avctx, AV_LOG_ERROR, "Context for block %d is not present\n", block_no);

        return -1;

    }



    if(!s->samples_left){

        memset(s->decorr, 0, MAX_TERMS * sizeof(Decorr));

        memset(s->ch, 0, sizeof(s->ch));

        s->extra_bits = 0;

        s->and = s->or = s->shift = 0;

        s->got_extra_bits = 0;

    }



    if(!wc->mkv_mode){

        s->samples = AV_RL32(buf); buf += 4;

        if(!s->samples){

            *data_size = 0;

            return 0;

        }

    }else{

        s->samples = wc->samples;

    }

    s->frame_flags = AV_RL32(buf); buf += 4;

    if(s->frame_flags&0x80){

        bpp = sizeof(float);

        avctx->sample_fmt = AV_SAMPLE_FMT_FLT;

    } else if((s->frame_flags&0x03) <= 1){

        bpp = 2;

        avctx->sample_fmt = AV_SAMPLE_FMT_S16;

    } else {

        bpp = 4;

        avctx->sample_fmt = AV_SAMPLE_FMT_S32;

    }

    samples = (uint8_t*)samples + bpp * wc->ch_offset;



    s->stereo = !(s->frame_flags & WV_MONO);

    s->stereo_in = (s->frame_flags & WV_FALSE_STEREO) ? 0 : s->stereo;

    s->joint = s->frame_flags & WV_JOINT_STEREO;

    s->hybrid = s->frame_flags & WV_HYBRID_MODE;

    s->hybrid_bitrate = s->frame_flags & WV_HYBRID_BITRATE;

    s->post_shift = 8 * (bpp-1-(s->frame_flags&0x03)) + ((s->frame_flags >> 13) & 0x1f);

    s->CRC = AV_RL32(buf); buf += 4;

    if(wc->mkv_mode)

        buf += 4; //skip block size;



    wc->ch_offset += 1 + s->stereo;



    s->max_samples = *data_size / (bpp * avctx->channels);

    s->max_samples = FFMIN(s->max_samples, s->samples);

    if(s->samples_left > 0){

        s->max_samples = FFMIN(s->max_samples, s->samples_left);

        buf = buf_end;

    }



    // parse metadata blocks

    while(buf < buf_end){

        id = *buf++;

        size = *buf++;

        if(id & WP_IDF_LONG) {

            size |= (*buf++) << 8;

            size |= (*buf++) << 16;

        }

        size <<= 1; // size is specified in words

        ssize = size;

        if(id & WP_IDF_ODD) size--;

        if(size < 0){

            av_log(avctx, AV_LOG_ERROR, "Got incorrect block %02X with size %i\n", id, size);

            break;

        }

        if(buf + ssize > buf_end){

            av_log(avctx, AV_LOG_ERROR, "Block size %i is out of bounds\n", size);

            break;

        }

        if(id & WP_IDF_IGNORE){

            buf += ssize;

            continue;

        }

        switch(id & WP_IDF_MASK){

        case WP_ID_DECTERMS:

            s->terms = size;

            if(s->terms > MAX_TERMS){

                av_log(avctx, AV_LOG_ERROR, "Too many decorrelation terms\n");

                buf += ssize;

                continue;

            }

            for(i = 0; i < s->terms; i++) {

                s->decorr[s->terms - i - 1].value = (*buf & 0x1F) - 5;

                s->decorr[s->terms - i - 1].delta = *buf >> 5;

                buf++;

            }

            got_terms = 1;

            break;

        case WP_ID_DECWEIGHTS:

            if(!got_terms){

                av_log(avctx, AV_LOG_ERROR, "No decorrelation terms met\n");

                continue;

            }

            weights = size >> s->stereo_in;

            if(weights > MAX_TERMS || weights > s->terms){

                av_log(avctx, AV_LOG_ERROR, "Too many decorrelation weights\n");

                buf += ssize;

                continue;

            }

            for(i = 0; i < weights; i++) {

                t = (int8_t)(*buf++);

                s->decorr[s->terms - i - 1].weightA = t << 3;

                if(s->decorr[s->terms - i - 1].weightA > 0)

                    s->decorr[s->terms - i - 1].weightA += (s->decorr[s->terms - i - 1].weightA + 64) >> 7;

                if(s->stereo_in){

                    t = (int8_t)(*buf++);

                    s->decorr[s->terms - i - 1].weightB = t << 3;

                    if(s->decorr[s->terms - i - 1].weightB > 0)

                        s->decorr[s->terms - i - 1].weightB += (s->decorr[s->terms - i - 1].weightB + 64) >> 7;

                }

            }

            got_weights = 1;

            break;

        case WP_ID_DECSAMPLES:

            if(!got_terms){

                av_log(avctx, AV_LOG_ERROR, "No decorrelation terms met\n");

                continue;

            }

            t = 0;

            for(i = s->terms - 1; (i >= 0) && (t < size); i--) {

                if(s->decorr[i].value > 8){

                    s->decorr[i].samplesA[0] = wp_exp2(AV_RL16(buf)); buf += 2;

                    s->decorr[i].samplesA[1] = wp_exp2(AV_RL16(buf)); buf += 2;

                    if(s->stereo_in){

                        s->decorr[i].samplesB[0] = wp_exp2(AV_RL16(buf)); buf += 2;

                        s->decorr[i].samplesB[1] = wp_exp2(AV_RL16(buf)); buf += 2;

                        t += 4;

                    }

                    t += 4;

                }else if(s->decorr[i].value < 0){

                    s->decorr[i].samplesA[0] = wp_exp2(AV_RL16(buf)); buf += 2;

                    s->decorr[i].samplesB[0] = wp_exp2(AV_RL16(buf)); buf += 2;

                    t += 4;

                }else{

                    for(j = 0; j < s->decorr[i].value; j++){

                        s->decorr[i].samplesA[j] = wp_exp2(AV_RL16(buf)); buf += 2;

                        if(s->stereo_in){

                            s->decorr[i].samplesB[j] = wp_exp2(AV_RL16(buf)); buf += 2;

                        }

                    }

                    t += s->decorr[i].value * 2 * (s->stereo_in + 1);

                }

            }

            got_samples = 1;

            break;

        case WP_ID_ENTROPY:

            if(size != 6 * (s->stereo_in + 1)){

                av_log(avctx, AV_LOG_ERROR, "Entropy vars size should be %i, got %i", 6 * (s->stereo_in + 1), size);

                buf += ssize;

                continue;

            }

            for(j = 0; j <= s->stereo_in; j++){

                for(i = 0; i < 3; i++){

                    s->ch[j].median[i] = wp_exp2(AV_RL16(buf));

                    buf += 2;

                }

            }

            got_entropy = 1;

            break;

        case WP_ID_HYBRID:

            if(s->hybrid_bitrate){

                for(i = 0; i <= s->stereo_in; i++){

                    s->ch[i].slow_level = wp_exp2(AV_RL16(buf));

                    buf += 2;

                    size -= 2;

                }

            }

            for(i = 0; i < (s->stereo_in + 1); i++){

                s->ch[i].bitrate_acc = AV_RL16(buf) << 16;

                buf += 2;

                size -= 2;

            }

            if(size > 0){

                for(i = 0; i < (s->stereo_in + 1); i++){

                    s->ch[i].bitrate_delta = wp_exp2((int16_t)AV_RL16(buf));

                    buf += 2;

                }

            }else{

                for(i = 0; i < (s->stereo_in + 1); i++)

                    s->ch[i].bitrate_delta = 0;

            }

            got_hybrid = 1;

            break;

        case WP_ID_INT32INFO:

            if(size != 4){

                av_log(avctx, AV_LOG_ERROR, "Invalid INT32INFO, size = %i, sent_bits = %i\n", size, *buf);

                buf += ssize;

                continue;

            }

            if(buf[0])

                s->extra_bits = buf[0];

            else if(buf[1])

                s->shift = buf[1];

            else if(buf[2]){

                s->and = s->or = 1;

                s->shift = buf[2];

            }else if(buf[3]){

                s->and = 1;

                s->shift = buf[3];

            }

            buf += 4;

            break;

        case WP_ID_FLOATINFO:

            if(size != 4){

                av_log(avctx, AV_LOG_ERROR, "Invalid FLOATINFO, size = %i\n", size);

                buf += ssize;

                continue;

            }

            s->float_flag = buf[0];

            s->float_shift = buf[1];

            s->float_max_exp = buf[2];

            buf += 4;

            got_float = 1;

            break;

        case WP_ID_DATA:

            s->sc.offset = buf - orig_buf;

            s->sc.size   = size * 8;

            init_get_bits(&s->gb, buf, size * 8);

            s->data_size = size * 8;

            buf += size;

            got_bs = 1;

            break;

        case WP_ID_EXTRABITS:

            if(size <= 4){

                av_log(avctx, AV_LOG_ERROR, "Invalid EXTRABITS, size = %i\n", size);

                buf += size;

                continue;

            }

            s->extra_sc.offset = buf - orig_buf;

            s->extra_sc.size   = size * 8;

            init_get_bits(&s->gb_extra_bits, buf, size * 8);

            s->crc_extra_bits = get_bits_long(&s->gb_extra_bits, 32);

            buf += size;

            s->got_extra_bits = 1;

            break;

        case WP_ID_CHANINFO:

            if(size <= 1){

                av_log(avctx, AV_LOG_ERROR, "Insufficient channel information\n");

                return -1;

            }

            chan = *buf++;

            switch(size - 2){

            case 0:

                chmask = *buf;

                break;

            case 1:

                chmask = AV_RL16(buf);

                break;

            case 2:

                chmask = AV_RL24(buf);

                break;

            case 3:

                chmask = AV_RL32(buf);

                break;

            case 5:

                chan |= (buf[1] & 0xF) << 8;

                chmask = AV_RL24(buf + 2);

                break;

            default:

                av_log(avctx, AV_LOG_ERROR, "Invalid channel info size %d\n", size);

                chan = avctx->channels;

                chmask = avctx->channel_layout;

            }

            if(chan != avctx->channels){

                av_log(avctx, AV_LOG_ERROR, "Block reports total %d channels, decoder believes it's %d channels\n",

                       chan, avctx->channels);

                return -1;

            }

            if(!avctx->channel_layout)

                avctx->channel_layout = chmask;

            buf += size - 1;

            break;

        default:

            buf += size;

        }

        if(id & WP_IDF_ODD) buf++;

    }

    if(!s->samples_left){

        if(!got_terms){

            av_log(avctx, AV_LOG_ERROR, "No block with decorrelation terms\n");

            return -1;

        }

        if(!got_weights){

            av_log(avctx, AV_LOG_ERROR, "No block with decorrelation weights\n");

            return -1;

        }

        if(!got_samples){

            av_log(avctx, AV_LOG_ERROR, "No block with decorrelation samples\n");

            return -1;

        }

        if(!got_entropy){

            av_log(avctx, AV_LOG_ERROR, "No block with entropy info\n");

            return -1;

        }

        if(s->hybrid && !got_hybrid){

            av_log(avctx, AV_LOG_ERROR, "Hybrid config not found\n");

            return -1;

        }

        if(!got_bs){

            av_log(avctx, AV_LOG_ERROR, "Packed samples not found\n");

            return -1;

        }

        if(!got_float && avctx->sample_fmt == AV_SAMPLE_FMT_FLT){

            av_log(avctx, AV_LOG_ERROR, "Float information not found\n");

            return -1;

        }

        if(s->got_extra_bits && avctx->sample_fmt != AV_SAMPLE_FMT_FLT){

            const int size = get_bits_left(&s->gb_extra_bits);

            const int wanted = s->samples * s->extra_bits << s->stereo_in;

            if(size < wanted){

                av_log(avctx, AV_LOG_ERROR, "Too small EXTRABITS\n");

                s->got_extra_bits = 0;

            }

        }

        s->samples_left = s->samples;

    }else{

        init_get_bits(&s->gb, orig_buf + s->sc.offset, s->sc.size);

        skip_bits_long(&s->gb, s->sc.bits_used);

        if(s->got_extra_bits){

            init_get_bits(&s->gb_extra_bits, orig_buf + s->extra_sc.offset,

                          s->extra_sc.size);

            skip_bits_long(&s->gb_extra_bits, s->extra_sc.bits_used);

        }

    }



    if(s->stereo_in){

        if(avctx->sample_fmt == AV_SAMPLE_FMT_S16)

            samplecount = wv_unpack_stereo(s, &s->gb, samples, AV_SAMPLE_FMT_S16);

        else if(avctx->sample_fmt == AV_SAMPLE_FMT_S32)

            samplecount = wv_unpack_stereo(s, &s->gb, samples, AV_SAMPLE_FMT_S32);

        else

            samplecount = wv_unpack_stereo(s, &s->gb, samples, AV_SAMPLE_FMT_FLT);

        samplecount >>= 1;

    }else{

        const int channel_stride = avctx->channels;



        if(avctx->sample_fmt == AV_SAMPLE_FMT_S16)

            samplecount = wv_unpack_mono(s, &s->gb, samples, AV_SAMPLE_FMT_S16);

        else if(avctx->sample_fmt == AV_SAMPLE_FMT_S32)

            samplecount = wv_unpack_mono(s, &s->gb, samples, AV_SAMPLE_FMT_S32);

        else

            samplecount = wv_unpack_mono(s, &s->gb, samples, AV_SAMPLE_FMT_FLT);



        if(s->stereo && avctx->sample_fmt == AV_SAMPLE_FMT_S16){

            int16_t *dst = (int16_t*)samples + 1;

            int16_t *src = (int16_t*)samples;

            int cnt = samplecount;

            while(cnt--){

                *dst = *src;

                src += channel_stride;

                dst += channel_stride;

            }

        }else if(s->stereo && avctx->sample_fmt == AV_SAMPLE_FMT_S32){

            int32_t *dst = (int32_t*)samples + 1;

            int32_t *src = (int32_t*)samples;

            int cnt = samplecount;

            while(cnt--){

                *dst = *src;

                src += channel_stride;

                dst += channel_stride;

            }

        }else if(s->stereo){

            float *dst = (float*)samples + 1;

            float *src = (float*)samples;

            int cnt = samplecount;

            while(cnt--){

                *dst = *src;

                src += channel_stride;

                dst += channel_stride;

            }

        }

    }



    wc->samples_left = s->samples_left;



    return samplecount * bpp;

}
