Exemplo n.º 1
0
static int gif_read_header1(GifState *s)
{
    uint8_t sig[6];
    int v, n;
    int background_color_index;

    if (s->bytestream_end < s->bytestream + 13)
        return AVERROR_INVALIDDATA;

    /* read gif signature */
    bytestream_get_buffer(&s->bytestream, sig, 6);
    if (memcmp(sig, gif87a_sig, 6) != 0 &&
        memcmp(sig, gif89a_sig, 6) != 0)
        return AVERROR_INVALIDDATA;

    /* read screen header */
    s->transparent_color_index = -1;
    s->screen_width = bytestream_get_le16(&s->bytestream);
    s->screen_height = bytestream_get_le16(&s->bytestream);
    if(   (unsigned)s->screen_width  > 32767
       || (unsigned)s->screen_height > 32767){
        av_log(s->avctx, AV_LOG_ERROR, "picture size too large\n");
        return AVERROR_INVALIDDATA;
    }

    av_fast_malloc(&s->idx_line, &s->idx_line_size, s->screen_width);
    if (!s->idx_line)
        return AVERROR(ENOMEM);

    v = bytestream_get_byte(&s->bytestream);
    s->color_resolution = ((v & 0x70) >> 4) + 1;
    s->has_global_palette = (v & 0x80);
    s->bits_per_pixel = (v & 0x07) + 1;
    background_color_index = bytestream_get_byte(&s->bytestream);
    n = bytestream_get_byte(&s->bytestream);
    if (n) {
        s->avctx->sample_aspect_ratio.num = n + 15;
        s->avctx->sample_aspect_ratio.den = 64;
    }

    av_dlog(s->avctx, "screen_w=%d screen_h=%d bpp=%d global_palette=%d\n",
           s->screen_width, s->screen_height, s->bits_per_pixel,
           s->has_global_palette);

    if (s->has_global_palette) {
        s->background_color_index = background_color_index;
        n = 1 << s->bits_per_pixel;
        if (s->bytestream_end < s->bytestream + n * 3)
            return AVERROR_INVALIDDATA;

        gif_read_palette(&s->bytestream, s->global_palette, n);
        s->bg_color = s->global_palette[s->background_color_index];
    } else
        s->background_color_index = -1;

    return 0;
}
Exemplo n.º 2
0
static int gif_read_header1(GifState *s)
{
    uint8_t sig[6];
    int v, n;
    int has_global_palette;

    if (s->bytestream_end < s->bytestream + 13)
        return -1;

    /* read gif signature */
    bytestream_get_buffer(&s->bytestream, sig, 6);
    if (memcmp(sig, gif87a_sig, 6) != 0 &&
        memcmp(sig, gif89a_sig, 6) != 0)
        return -1;

    /* read screen header */
    s->transparent_color_index = -1;
    s->screen_width = bytestream_get_le16(&s->bytestream);
    s->screen_height = bytestream_get_le16(&s->bytestream);
    if(   (unsigned)s->screen_width  > 32767
       || (unsigned)s->screen_height > 32767){
        av_log(NULL, AV_LOG_ERROR, "picture size too large\n");
        return -1;
    }

    v = bytestream_get_byte(&s->bytestream);
    s->color_resolution = ((v & 0x70) >> 4) + 1;
    has_global_palette = (v & 0x80);
    s->bits_per_pixel = (v & 0x07) + 1;
    s->background_color_index = bytestream_get_byte(&s->bytestream);
    bytestream_get_byte(&s->bytestream);                /* ignored */
#ifdef DEBUG
    dprintf(s->avctx, "gif: screen_w=%d screen_h=%d bpp=%d global_palette=%d\n",
           s->screen_width, s->screen_height, s->bits_per_pixel,
           has_global_palette);
#endif
    if (has_global_palette) {
        n = 1 << s->bits_per_pixel;
        if (s->bytestream_end < s->bytestream + n * 3)
            return -1;
        bytestream_get_buffer(&s->bytestream, s->global_palette, n * 3);
    }
    return 0;
}
Exemplo n.º 3
0
static int flac_read_header(AVFormatContext *s)
{
    int ret, metadata_last=0, metadata_type, metadata_size, found_streaminfo=0;
    uint8_t header[4];
    uint8_t *buffer=NULL,*tmp=NULL;
    AVStream *st = avformat_new_stream(s, NULL);
    if (!st)
        return AVERROR(ENOMEM);
    st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
    st->codec->codec_id = AV_CODEC_ID_FLAC;
    st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
    /* the parameters will be extracted from the compressed bitstream */

    /* if fLaC marker is not found, assume there is no header */
    if (avio_rl32(s->pb) != MKTAG('f','L','a','C')) {
        avio_seek(s->pb, -4, SEEK_CUR);
        return 0;
    }

    /* process metadata blocks */
    while (!url_feof(s->pb) && !metadata_last) {
        avio_read(s->pb, header, 4);
        avpriv_flac_parse_block_header(header, &metadata_last, &metadata_type,
                                   &metadata_size);
        switch (metadata_type) {
        /* allocate and read metadata block for supported types */
        case FLAC_METADATA_TYPE_STREAMINFO:
        case FLAC_METADATA_TYPE_CUESHEET:
        case FLAC_METADATA_TYPE_PICTURE:
        case FLAC_METADATA_TYPE_VORBIS_COMMENT:
            buffer = av_mallocz(metadata_size + FF_INPUT_BUFFER_PADDING_SIZE);
            if (!buffer) {
                return AVERROR(ENOMEM);
            }
            if (avio_read(s->pb, buffer, metadata_size) != metadata_size) {
                RETURN_ERROR(AVERROR(EIO));
            }
            break;
        /* skip metadata block for unsupported types */
        default:
            ret = avio_skip(s->pb, metadata_size);
            if (ret < 0)
                return ret;
        }

        if (metadata_type == FLAC_METADATA_TYPE_STREAMINFO) {
            FLACStreaminfo si;
            /* STREAMINFO can only occur once */
            if (found_streaminfo) {
                RETURN_ERROR(AVERROR_INVALIDDATA);
            }
            if (metadata_size != FLAC_STREAMINFO_SIZE) {
                RETURN_ERROR(AVERROR_INVALIDDATA);
            }
            found_streaminfo = 1;
            st->codec->extradata      = av_malloc(metadata_size + 8 + FF_INPUT_BUFFER_PADDING_SIZE);
            if (!st->codec->extradata) {
              RETURN_ERROR(AVERROR(ENOMEM));
            }
            st->codec->extradata_size = metadata_size + 8;
            AV_WL32(st->codec->extradata, MKTAG('f','L','a','C'));
            memcpy(st->codec->extradata + 4, header, 4);
            memcpy(st->codec->extradata + 8, buffer, metadata_size);
            av_freep(&buffer);

            /* get codec params from STREAMINFO header */
            avpriv_flac_parse_streaminfo(st->codec, &si, st->codec->extradata + 8);

            /* set time base and duration */
            if (si.samplerate > 0) {
                avpriv_set_pts_info(st, 64, 1, si.samplerate);
                if (si.samples > 0)
                    st->duration = si.samples;
            }
        } else if (metadata_type == FLAC_METADATA_TYPE_CUESHEET) {
            uint8_t isrc[13];
            uint64_t start;
            const uint8_t *offset;
            int i, chapters, track, ti;
            if (metadata_size < 431)
                RETURN_ERROR(AVERROR_INVALIDDATA);
            offset = buffer + 395;
            chapters = bytestream_get_byte(&offset) - 1;
            if (chapters <= 0)
                RETURN_ERROR(AVERROR_INVALIDDATA);
            for (i = 0; i < chapters; i++) {
                if (offset + 36 - buffer > metadata_size)
                    RETURN_ERROR(AVERROR_INVALIDDATA);
                start = bytestream_get_be64(&offset);
                track = bytestream_get_byte(&offset);
                bytestream_get_buffer(&offset, isrc, 12);
                isrc[12] = 0;
                offset += 14;
                ti = bytestream_get_byte(&offset);
                if (ti <= 0) RETURN_ERROR(AVERROR_INVALIDDATA);
                offset += ti * 12;
                avpriv_new_chapter(s, track, st->time_base, start, AV_NOPTS_VALUE, isrc);
            }
            av_freep(&buffer);
        } else if (metadata_type == FLAC_METADATA_TYPE_PICTURE) {
            ret = parse_picture(s, buffer, metadata_size);
            av_freep(&buffer);
            if (ret < 0) {
                av_log(s, AV_LOG_ERROR, "Error parsing attached picture.\n");
                return ret;
            }
        } else {
            /* STREAMINFO must be the first block */
            if (!found_streaminfo) {
                RETURN_ERROR(AVERROR_INVALIDDATA);
            }
            /* process supported blocks other than STREAMINFO */
            if (metadata_type == FLAC_METADATA_TYPE_VORBIS_COMMENT) {

                /* append VorbisComment to extradata */
                tmp = av_realloc(st->codec->extradata, st->codec->extradata_size + 4 + metadata_size + FF_INPUT_BUFFER_PADDING_SIZE);
                if (!tmp) {
                  RETURN_ERROR(AVERROR(ENOMEM));
                }
                st->codec->extradata      = tmp;
                tmp += st->codec->extradata_size;
                memcpy(tmp, header, 4);
                memcpy(tmp + 4, buffer, metadata_size);
                st->codec->extradata_size = st->codec->extradata_size + 4 + metadata_size;

                if (ff_vorbis_comment(s, &s->metadata, buffer, metadata_size)) {
                    av_log(s, AV_LOG_WARNING, "error parsing VorbisComment metadata\n");
                }
            }
            av_freep(&buffer);
        }
    }

    return 0;

fail:
    av_free(buffer);
    return ret;
}
Exemplo n.º 4
0
static int gif_read_image(GifState *s)
{
    int left, top, width, height, bits_per_pixel, code_size, flags;
    int is_interleaved, has_local_palette, y, pass, y1, linesize, n, i;
    uint8_t *ptr, *spal, *palette, *ptr1;

    left = bytestream_get_le16(&s->bytestream);
    top = bytestream_get_le16(&s->bytestream);
    width = bytestream_get_le16(&s->bytestream);
    height = bytestream_get_le16(&s->bytestream);
    flags = bytestream_get_byte(&s->bytestream);
    is_interleaved = flags & 0x40;
    has_local_palette = flags & 0x80;
    bits_per_pixel = (flags & 0x07) + 1;
#ifdef DEBUG
    dprintf(s->avctx, "gif: image x=%d y=%d w=%d h=%d\n", left, top, width, height);
#endif

    if (has_local_palette) {
        bytestream_get_buffer(&s->bytestream, s->local_palette, 3 * (1 << bits_per_pixel));
        palette = s->local_palette;
    } else {
        palette = s->global_palette;
        bits_per_pixel = s->bits_per_pixel;
    }

    /* verify that all the image is inside the screen dimensions */
    if (left + width > s->screen_width ||
        top + height > s->screen_height)
        return AVERROR(EINVAL);

    /* build the palette */
    n = (1 << bits_per_pixel);
    spal = palette;
    for(i = 0; i < n; i++) {
        s->image_palette[i] = (0xff << 24) | AV_RB24(spal);
        spal += 3;
    }
    for(; i < 256; i++)
        s->image_palette[i] = (0xff << 24);
    /* handle transparency */
    if (s->transparent_color_index >= 0)
        s->image_palette[s->transparent_color_index] = 0;

    /* now get the image data */
    code_size = bytestream_get_byte(&s->bytestream);
    ff_lzw_decode_init(s->lzw, code_size, s->bytestream,
                       s->bytestream_end - s->bytestream, FF_LZW_GIF);

    /* read all the image */
    linesize = s->picture.linesize[0];
    ptr1 = s->picture.data[0] + top * linesize + left;
    ptr = ptr1;
    pass = 0;
    y1 = 0;
    for (y = 0; y < height; y++) {
        ff_lzw_decode(s->lzw, ptr, width);
        if (is_interleaved) {
            switch(pass) {
            default:
            case 0:
            case 1:
                y1 += 8;
                ptr += linesize * 8;
                if (y1 >= height) {
                    y1 = pass ? 2 : 4;
                    ptr = ptr1 + linesize * y1;
                    pass++;
                }
                break;
            case 2:
                y1 += 4;
                ptr += linesize * 4;
                if (y1 >= height) {
                    y1 = 1;
                    ptr = ptr1 + linesize;
                    pass++;
                }
                break;
            case 3:
                y1 += 2;
                ptr += linesize * 2;
                break;
            }
        } else {
            ptr += linesize;
        }
    }
    /* read the garbage data until end marker is found */
    ff_lzw_decode_tail(s->lzw);
    s->bytestream = ff_lzw_cur_ptr(s->lzw);
    return 0;
}
Exemplo n.º 5
0
Arquivo: c93.c Projeto: andoma/libav
static int decode_frame(AVCodecContext *avctx, void *data,
                            int *data_size, AVPacket *avpkt)
{
    const uint8_t *buf = avpkt->data;
    int buf_size = avpkt->size;
    C93DecoderContext * const c93 = avctx->priv_data;
    AVFrame * const newpic = &c93->pictures[c93->currentpic];
    AVFrame * const oldpic = &c93->pictures[c93->currentpic^1];
    AVFrame *picture = data;
    uint8_t *out;
    int stride, i, x, y, bt = 0;

    c93->currentpic ^= 1;

    newpic->reference = 1;
    newpic->buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE |
                         FF_BUFFER_HINTS_REUSABLE | FF_BUFFER_HINTS_READABLE;
    if (avctx->reget_buffer(avctx, newpic)) {
        av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
        return -1;
    }

    stride = newpic->linesize[0];

    if (buf[0] & C93_FIRST_FRAME) {
        newpic->pict_type = FF_I_TYPE;
        newpic->key_frame = 1;
    } else {
        newpic->pict_type = FF_P_TYPE;
        newpic->key_frame = 0;
    }

    if (*buf++ & C93_HAS_PALETTE) {
        uint32_t *palette = (uint32_t *) newpic->data[1];
        const uint8_t *palbuf = buf + buf_size - 768 - 1;
        for (i = 0; i < 256; i++) {
            palette[i] = bytestream_get_be24(&palbuf);
        }
    } else {
        if (oldpic->data[1])
            memcpy(newpic->data[1], oldpic->data[1], 256 * 4);
    }

    for (y = 0; y < HEIGHT; y += 8) {
        out = newpic->data[0] + y * stride;
        for (x = 0; x < WIDTH; x += 8) {
            uint8_t *copy_from = oldpic->data[0];
            unsigned int offset, j;
            uint8_t cols[4], grps[4];
            C93BlockType block_type;

            if (!bt)
                bt = *buf++;

            block_type= bt & 0x0F;
            switch (block_type) {
            case C93_8X8_FROM_PREV:
                offset = bytestream_get_le16(&buf);
                if (copy_block(avctx, out, copy_from, offset, 8, stride))
                    return -1;
                break;

            case C93_4X4_FROM_CURR:
                copy_from = newpic->data[0];
            case C93_4X4_FROM_PREV:
                for (j = 0; j < 8; j += 4) {
                    for (i = 0; i < 8; i += 4) {
                        offset = bytestream_get_le16(&buf);
                        if (copy_block(avctx, &out[j*stride+i],
                                           copy_from, offset, 4, stride))
                            return -1;
                    }
                }
                break;

            case C93_8X8_2COLOR:
                bytestream_get_buffer(&buf, cols, 2);
                for (i = 0; i < 8; i++) {
                    draw_n_color(out + i*stride, stride, 8, 1, 1, cols,
                                     NULL, *buf++);
                }

                break;

            case C93_4X4_2COLOR:
            case C93_4X4_4COLOR:
            case C93_4X4_4COLOR_GRP:
                for (j = 0; j < 8; j += 4) {
                    for (i = 0; i < 8; i += 4) {
                        if (block_type == C93_4X4_2COLOR) {
                            bytestream_get_buffer(&buf, cols, 2);
                            draw_n_color(out + i + j*stride, stride, 4, 4,
                                    1, cols, NULL, bytestream_get_le16(&buf));
                        } else if (block_type == C93_4X4_4COLOR) {
                            bytestream_get_buffer(&buf, cols, 4);
                            draw_n_color(out + i + j*stride, stride, 4, 4,
                                    2, cols, NULL, bytestream_get_le32(&buf));
                        } else {
                            bytestream_get_buffer(&buf, grps, 4);
                            draw_n_color(out + i + j*stride, stride, 4, 4,
                                    1, cols, grps, bytestream_get_le16(&buf));
                        }
                    }
                }
                break;

            case C93_NOOP:
                break;

            case C93_8X8_INTRA:
                for (j = 0; j < 8; j++)
                    bytestream_get_buffer(&buf, out + j*stride, 8);
                break;

            default:
                av_log(avctx, AV_LOG_ERROR, "unexpected type %x at %dx%d\n",
                       block_type, x, y);
                return -1;
            }
            bt >>= 4;
            out += 8;
        }
    }

    *picture = *newpic;
    *data_size = sizeof(AVFrame);

    return buf_size;
}
Exemplo n.º 6
0
static int bethsoftvid_decode_frame(AVCodecContext *avctx,
                              void *data, int *data_size,
                              AVPacket *avpkt)
{
    const uint8_t *buf = avpkt->data;
    int buf_size = avpkt->size;
    BethsoftvidContext * vid = avctx->priv_data;
    char block_type;
    uint8_t * dst;
    uint8_t * frame_end;
    int remaining = avctx->width;          // number of bytes remaining on a line
    const int wrap_to_next_line = vid->frame.linesize[0] - avctx->width;
    int code;
    int yoffset;

    if (avctx->reget_buffer(avctx, &vid->frame)) {
        av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
        return -1;
    }
    dst = vid->frame.data[0];
    frame_end = vid->frame.data[0] + vid->frame.linesize[0] * avctx->height;

    switch(block_type = *buf++){
        case PALETTE_BLOCK:
            set_palette(&vid->frame, buf);
            return 0;
        case VIDEO_YOFF_P_FRAME:
            yoffset = bytestream_get_le16(&buf);
            if(yoffset >= avctx->height)
                return -1;
            dst += vid->frame.linesize[0] * yoffset;
    }

    // main code
    while((code = *buf++)){
        int length = code & 0x7f;

        // copy any bytes starting at the current position, and ending at the frame width
        while(length > remaining){
            if(code < 0x80)
                bytestream_get_buffer(&buf, dst, remaining);
            else if(block_type == VIDEO_I_FRAME)
                memset(dst, buf[0], remaining);
            length -= remaining;      // decrement the number of bytes to be copied
            dst += remaining + wrap_to_next_line;    // skip over extra bytes at end of frame
            remaining = avctx->width;
            if(dst == frame_end)
                goto end;
        }

        // copy any remaining bytes after / if line overflows
        if(code < 0x80)
            bytestream_get_buffer(&buf, dst, length);
        else if(block_type == VIDEO_I_FRAME)
            memset(dst, *buf++, length);
        remaining -= length;
        dst += length;
    }
    end:

    *data_size = sizeof(AVFrame);
    *(AVFrame*)data = vid->frame;

    return buf_size;
}
Exemplo n.º 7
0
static int flac_read_header(AVFormatContext *s)
{
    int ret, metadata_last=0, metadata_type, metadata_size, found_streaminfo=0;
    uint8_t header[4];
    uint8_t *buffer=NULL;
    AVStream *st = avformat_new_stream(s, NULL);
    if (!st)
        return AVERROR(ENOMEM);
    st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
    st->codec->codec_id = AV_CODEC_ID_FLAC;
    st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
    /* the parameters will be extracted from the compressed bitstream */

    /* if fLaC marker is not found, assume there is no header */
    if (avio_rl32(s->pb) != MKTAG('f','L','a','C')) {
        avio_seek(s->pb, -4, SEEK_CUR);
        return 0;
    }

    /* process metadata blocks */
    while (!avio_feof(s->pb) && !metadata_last) {
        avio_read(s->pb, header, 4);
        flac_parse_block_header(header, &metadata_last, &metadata_type,
                                   &metadata_size);
        switch (metadata_type) {
        /* allocate and read metadata block for supported types */
        case FLAC_METADATA_TYPE_STREAMINFO:
        case FLAC_METADATA_TYPE_CUESHEET:
        case FLAC_METADATA_TYPE_PICTURE:
        case FLAC_METADATA_TYPE_VORBIS_COMMENT:
            buffer = av_mallocz(metadata_size + FF_INPUT_BUFFER_PADDING_SIZE);
            if (!buffer) {
                return AVERROR(ENOMEM);
            }
            if (avio_read(s->pb, buffer, metadata_size) != metadata_size) {
                RETURN_ERROR(AVERROR(EIO));
            }
            break;
        /* skip metadata block for unsupported types */
        default:
            ret = avio_skip(s->pb, metadata_size);
            if (ret < 0)
                return ret;
        }

        if (metadata_type == FLAC_METADATA_TYPE_STREAMINFO) {
            FLACStreaminfo si;
            /* STREAMINFO can only occur once */
            if (found_streaminfo) {
                RETURN_ERROR(AVERROR_INVALIDDATA);
            }
            if (metadata_size != FLAC_STREAMINFO_SIZE) {
                RETURN_ERROR(AVERROR_INVALIDDATA);
            }
            found_streaminfo = 1;
            st->codec->extradata      = buffer;
            st->codec->extradata_size = metadata_size;
            buffer = NULL;

            /* get codec params from STREAMINFO header */
            avpriv_flac_parse_streaminfo(st->codec, &si, st->codec->extradata);

            /* set time base and duration */
            if (si.samplerate > 0) {
                avpriv_set_pts_info(st, 64, 1, si.samplerate);
                if (si.samples > 0)
                    st->duration = si.samples;
            }
        } else if (metadata_type == FLAC_METADATA_TYPE_CUESHEET) {
            uint8_t isrc[13];
            uint64_t start;
            const uint8_t *offset;
            int i, chapters, track, ti;
            if (metadata_size < 431)
                RETURN_ERROR(AVERROR_INVALIDDATA);
            offset = buffer + 395;
            chapters = bytestream_get_byte(&offset) - 1;
            if (chapters <= 0)
                RETURN_ERROR(AVERROR_INVALIDDATA);
            for (i = 0; i < chapters; i++) {
                if (offset + 36 - buffer > metadata_size)
                    RETURN_ERROR(AVERROR_INVALIDDATA);
                start = bytestream_get_be64(&offset);
                track = bytestream_get_byte(&offset);
                bytestream_get_buffer(&offset, isrc, 12);
                isrc[12] = 0;
                offset += 14;
                ti = bytestream_get_byte(&offset);
                if (ti <= 0) RETURN_ERROR(AVERROR_INVALIDDATA);
                offset += ti * 12;
                avpriv_new_chapter(s, track, st->time_base, start, AV_NOPTS_VALUE, isrc);
            }
            av_freep(&buffer);
        } else if (metadata_type == FLAC_METADATA_TYPE_PICTURE) {
            ret = ff_flac_parse_picture(s, buffer, metadata_size);
            av_freep(&buffer);
            if (ret < 0) {
                av_log(s, AV_LOG_ERROR, "Error parsing attached picture.\n");
                return ret;
            }
        } else {
            /* STREAMINFO must be the first block */
            if (!found_streaminfo) {
                RETURN_ERROR(AVERROR_INVALIDDATA);
            }
            /* process supported blocks other than STREAMINFO */
            if (metadata_type == FLAC_METADATA_TYPE_VORBIS_COMMENT) {
                AVDictionaryEntry *chmask;

                ret = ff_vorbis_comment(s, &s->metadata, buffer, metadata_size, 1);
                if (ret < 0) {
                    av_log(s, AV_LOG_WARNING, "error parsing VorbisComment metadata\n");
                } else if (ret > 0) {
                    s->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
                }

                /* parse the channels mask if present */
                chmask = av_dict_get(s->metadata, "WAVEFORMATEXTENSIBLE_CHANNEL_MASK", NULL, 0);
                if (chmask) {
                    uint64_t mask = strtol(chmask->value, NULL, 0);
                    if (!mask || mask & ULLN(~0x3ffff)) {
                        av_log(s, AV_LOG_WARNING,
                               "Invalid value of WAVEFORMATEXTENSIBLE_CHANNEL_MASK\n");
                    } else {
                        st->codec->channel_layout = mask;
                        av_dict_set(&s->metadata, "WAVEFORMATEXTENSIBLE_CHANNEL_MASK", NULL, 0);
                    }
                }
            }
            av_freep(&buffer);
        }
    }

    ret = ff_replaygain_export(st, s->metadata);
    if (ret < 0)
        return ret;

    return 0;

fail:
    av_free(buffer);
    return ret;
}