takdec.c 6.51 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * Raw TAK demuxer
 * Copyright (c) 2012 Paul B Mahol
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

22
#include "libavutil/crc.h"
23

24
#define BITSTREAM_READER_LE
25
#include "libavcodec/tak.h"
26 27

#include "apetag.h"
28
#include "avformat.h"
29
#include "avio_internal.h"
30 31 32 33
#include "internal.h"
#include "rawdec.h"

typedef struct TAKDemuxContext {
34 35
    int     mlast_frame;
    int64_t data_end;
36 37 38 39 40
} TAKDemuxContext;

static int tak_probe(AVProbeData *p)
{
    if (!memcmp(p->buf, "tBaK", 4))
41
        return AVPROBE_SCORE_EXTENSION;
42 43 44
    return 0;
}

45 46 47 48 49 50
static unsigned long tak_check_crc(unsigned long checksum, const uint8_t *buf,
                                   unsigned int len)
{
    return av_crc(av_crc_get_table(AV_CRC_24_IEEE), checksum, buf, len);
}

51 52 53
static int tak_read_header(AVFormatContext *s)
{
    TAKDemuxContext *tc = s->priv_data;
54
    AVIOContext *pb     = s->pb;
55 56 57 58 59 60 61 62 63
    GetBitContext gb;
    AVStream *st;
    uint8_t *buffer = NULL;
    int ret;

    st = avformat_new_stream(s, 0);
    if (!st)
        return AVERROR(ENOMEM);

64 65
    st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
    st->codecpar->codec_id   = AV_CODEC_ID_TAK;
66
    st->need_parsing         = AVSTREAM_PARSE_FULL_RAW;
67 68 69 70 71 72 73

    tc->mlast_frame = 0;
    if (avio_rl32(pb) != MKTAG('t', 'B', 'a', 'K')) {
        avio_seek(pb, -4, SEEK_CUR);
        return 0;
    }

74
    while (!avio_feof(pb)) {
75 76 77 78 79 80 81 82 83 84
        enum TAKMetaDataType type;
        int size;

        type = avio_r8(pb) & 0x7f;
        size = avio_rl24(pb);

        switch (type) {
        case TAK_METADATA_STREAMINFO:
        case TAK_METADATA_LAST_FRAME:
        case TAK_METADATA_ENCODER:
85 86 87
            if (size <= 3)
                return AVERROR_INVALIDDATA;

88
            buffer = av_malloc(size - 3 + AV_INPUT_BUFFER_PADDING_SIZE);
89 90
            if (!buffer)
                return AVERROR(ENOMEM);
91
            memset(buffer + size - 3, 0, AV_INPUT_BUFFER_PADDING_SIZE);
92

93 94
            ffio_init_checksum(pb, tak_check_crc, 0xCE04B7U);
            if (avio_read(pb, buffer, size - 3) != size - 3) {
95 96 97
                av_freep(&buffer);
                return AVERROR(EIO);
            }
98 99 100 101 102 103 104
            if (ffio_get_checksum(s->pb) != avio_rb24(pb)) {
                av_log(s, AV_LOG_ERROR, "%d metadata block CRC error.\n", type);
                if (s->error_recognition & AV_EF_EXPLODE) {
                    av_freep(&buffer);
                    return AVERROR_INVALIDDATA;
                }
            }
105

106
            init_get_bits8(&gb, buffer, size - 3);
107 108 109 110 111 112 113
            break;
        case TAK_METADATA_MD5: {
            uint8_t md5[16];
            int i;

            if (size != 19)
                return AVERROR_INVALIDDATA;
114
            ffio_init_checksum(pb, tak_check_crc, 0xCE04B7U);
115
            avio_read(pb, md5, 16);
116 117 118 119 120 121
            if (ffio_get_checksum(s->pb) != avio_rb24(pb)) {
                av_log(s, AV_LOG_ERROR, "MD5 metadata block CRC error.\n");
                if (s->error_recognition & AV_EF_EXPLODE)
                    return AVERROR_INVALIDDATA;
            }

122 123 124 125 126
            av_log(s, AV_LOG_VERBOSE, "MD5=");
            for (i = 0; i < 16; i++)
                av_log(s, AV_LOG_VERBOSE, "%02x", md5[i]);
            av_log(s, AV_LOG_VERBOSE, "\n");
            break;
127
        }
Paul B Mahol's avatar
Paul B Mahol committed
128 129
        case TAK_METADATA_END: {
            int64_t curpos = avio_tell(pb);
130

131
            if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
132 133 134
                ff_ape_parse_tag(s);
                avio_seek(pb, curpos, SEEK_SET);
            }
Paul B Mahol's avatar
Paul B Mahol committed
135 136

            tc->data_end += curpos;
137
            return 0;
138
        }
139 140 141 142 143 144 145 146 147 148 149 150
        default:
            ret = avio_skip(pb, size);
            if (ret < 0)
                return ret;
        }

        if (type == TAK_METADATA_STREAMINFO) {
            TAKStreamInfo ti;

            avpriv_tak_parse_streaminfo(&gb, &ti);
            if (ti.samples > 0)
                st->duration = ti.samples;
151
            st->codecpar->bits_per_coded_sample = ti.bps;
152
            if (ti.ch_layout)
153 154 155
                st->codecpar->channel_layout = ti.ch_layout;
            st->codecpar->sample_rate           = ti.sample_rate;
            st->codecpar->channels              = ti.channels;
156
            st->start_time                   = 0;
157 158
            avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
            st->codecpar->extradata             = buffer;
159
            st->codecpar->extradata_size        = size - 3;
160
            buffer                           = NULL;
161 162 163 164
        } else if (type == TAK_METADATA_LAST_FRAME) {
            if (size != 11)
                return AVERROR_INVALIDDATA;
            tc->mlast_frame = 1;
165 166
            tc->data_end    = get_bits64(&gb, TAK_LAST_FRAME_POS_BITS) +
                              get_bits(&gb, TAK_LAST_FRAME_SIZE_BITS);
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
            av_freep(&buffer);
        } else if (type == TAK_METADATA_ENCODER) {
            av_log(s, AV_LOG_VERBOSE, "encoder version: %0X\n",
                   get_bits_long(&gb, TAK_ENCODER_VERSION_BITS));
            av_freep(&buffer);
        }
    }

    return AVERROR_EOF;
}

static int raw_read_packet(AVFormatContext *s, AVPacket *pkt)
{
    TAKDemuxContext *tc = s->priv_data;
    int ret;

    if (tc->mlast_frame) {
        AVIOContext *pb = s->pb;
Paul B Mahol's avatar
Paul B Mahol committed
185
        int64_t size, left;
186

187
        left = tc->data_end - avio_tell(pb);
Paul B Mahol's avatar
Paul B Mahol committed
188 189
        size = FFMIN(left, 1024);
        if (size <= 0)
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
            return AVERROR_EOF;

        ret = av_get_packet(pb, pkt, size);
        if (ret < 0)
            return ret;

        pkt->stream_index = 0;
    } else {
        ret = ff_raw_read_partial_packet(s, pkt);
    }

    return ret;
}

AVInputFormat ff_tak_demuxer = {
    .name           = "tak",
    .long_name      = NULL_IF_CONFIG_SMALL("raw TAK"),
    .priv_data_size = sizeof(TAKDemuxContext),
    .read_probe     = tak_probe,
    .read_header    = tak_read_header,
    .read_packet    = raw_read_packet,
    .flags          = AVFMT_GENERIC_INDEX,
    .extensions     = "tak",
    .raw_codec_id   = AV_CODEC_ID_TAK,
};