mlp_parser.c 12.4 KB
Newer Older
Ian Caulfield's avatar
Ian Caulfield committed
1 2 3 4
/*
 * MLP parser
 * Copyright (c) 2007 Ian Caulfield
 *
5
 * This file is part of Libav.
Ian Caulfield's avatar
Ian Caulfield committed
6
 *
7
 * Libav is free software; you can redistribute it and/or
Ian Caulfield's avatar
Ian Caulfield committed
8 9 10 11
 * 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.
 *
12
 * Libav is distributed in the hope that it will be useful,
Ian Caulfield's avatar
Ian Caulfield committed
13 14 15 16 17
 * 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
18
 * License along with Libav; if not, write to the Free Software
Ian Caulfield's avatar
Ian Caulfield committed
19 20 21 22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
23
 * @file
Ian Caulfield's avatar
Ian Caulfield committed
24 25 26
 * MLP parser
 */

27 28
#include <stdint.h>

29
#include "libavutil/channel_layout.h"
30
#include "libavutil/crc.h"
31
#include "libavutil/internal.h"
32
#include "get_bits.h"
Ian Caulfield's avatar
Ian Caulfield committed
33 34
#include "parser.h"
#include "mlp_parser.h"
35
#include "mlp.h"
Ian Caulfield's avatar
Ian Caulfield committed
36 37 38 39 40 41 42 43 44 45 46

static const uint8_t mlp_quants[16] = {
    16, 20, 24, 0, 0, 0, 0, 0,
     0,  0,  0, 0, 0, 0, 0, 0,
};

static const uint8_t mlp_channels[32] = {
    1, 2, 3, 4, 3, 4, 5, 3, 4, 5, 4, 5, 6, 4, 5, 4,
    5, 6, 5, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
static const uint64_t mlp_layout[32] = {
    AV_CH_LAYOUT_MONO,
    AV_CH_LAYOUT_STEREO,
    AV_CH_LAYOUT_2_1,
    AV_CH_LAYOUT_2_2,
    AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY,
    AV_CH_LAYOUT_2_1|AV_CH_LOW_FREQUENCY,
    AV_CH_LAYOUT_2_2|AV_CH_LOW_FREQUENCY,
    AV_CH_LAYOUT_SURROUND,
    AV_CH_LAYOUT_4POINT0,
    AV_CH_LAYOUT_5POINT0,
    AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY,
    AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY,
    AV_CH_LAYOUT_5POINT1,
    AV_CH_LAYOUT_4POINT0,
    AV_CH_LAYOUT_5POINT0,
    AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY,
    AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY,
    AV_CH_LAYOUT_5POINT1,
    AV_CH_LAYOUT_2_2|AV_CH_LOW_FREQUENCY,
    AV_CH_LAYOUT_5POINT0,
    AV_CH_LAYOUT_5POINT1,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

Ian Caulfield's avatar
Ian Caulfield committed
72 73 74 75 76
static const uint8_t thd_chancount[13] = {
//  LR    C   LFE  LRs LRvh  LRc LRrs  Cs   Ts  LRsd  LRw  Cvh  LFE2
     2,   1,   1,   2,   2,   2,   2,   1,   1,   2,   2,   1,   1
};

77 78 79 80 81 82
static const uint64_t thd_layout[13] = {
    AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT,                     // LR
    AV_CH_FRONT_CENTER,                                     // C
    AV_CH_LOW_FREQUENCY,                                    // LFE
    AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,                       // LRs
    AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_FRONT_RIGHT,             // LRvh
83
    AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER, // LRc
84 85
    AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT,                       // LRrs
    AV_CH_BACK_CENTER,                                      // Cs
86
    AV_CH_TOP_CENTER,                                       // Ts
87
    AV_CH_SURROUND_DIRECT_LEFT|AV_CH_SURROUND_DIRECT_RIGHT, // LRsd
88 89
    AV_CH_WIDE_LEFT|AV_CH_WIDE_RIGHT,                       // LRw
    AV_CH_TOP_FRONT_CENTER,                                 // Cvh
90
    AV_CH_LOW_FREQUENCY_2,                                  // LFE2
91 92
};

Ian Caulfield's avatar
Ian Caulfield committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
static int mlp_samplerate(int in)
{
    if (in == 0xF)
        return 0;

    return (in & 8 ? 44100 : 48000) << (in & 7) ;
}

static int truehd_channels(int chanmap)
{
    int channels = 0, i;

    for (i = 0; i < 13; i++)
        channels += thd_chancount[i] * ((chanmap >> i) & 1);

    return channels;
}

111
static uint64_t truehd_layout(int chanmap)
112
{
113 114
    int i;
    uint64_t layout = 0;
115 116 117 118 119 120 121

    for (i = 0; i < 13; i++)
        layout |= thd_layout[i] * ((chanmap >> i) & 1);

    return layout;
}

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
static int ff_mlp_get_major_sync_size(const uint8_t * buf, int bufsize)
{
    int has_extension, extensions = 0;
    int size = 28;
    if (bufsize < 28)
        return -1;

    if (AV_RB32(buf) == 0xf8726fba) {
        has_extension = buf[25] & 1;
        if (has_extension) {
            extensions = buf[26] >> 4;
            size += 2 + extensions * 2;
        }
    }
    return size;
}

Ian Caulfield's avatar
Ian Caulfield committed
139 140 141
/** Read a major sync info header - contains high level information about
 *  the stream - sample rate, channel arrangement etc. Most of this
 *  information is not actually necessary for decoding, only for playback.
142
 *  gb must be a freshly initialized GetBitContext with no bits read.
Ian Caulfield's avatar
Ian Caulfield committed
143 144
 */

145
int ff_mlp_read_major_sync(void *log, MLPHeaderInfo *mh, GetBitContext *gb)
Ian Caulfield's avatar
Ian Caulfield committed
146
{
147
    int ratebits, channel_arrangement, header_size;
Ian Caulfield's avatar
Ian Caulfield committed
148 149
    uint16_t checksum;

150 151
    assert(get_bits_count(gb) == 0);

152 153
    header_size = ff_mlp_get_major_sync_size(gb->buffer, gb->size_in_bits >> 3);
    if (header_size < 0 || gb->size_in_bits < header_size << 3) {
Diego Biurrun's avatar
Diego Biurrun committed
154
        av_log(log, AV_LOG_ERROR, "packet too short, unable to read major sync\n");
Ian Caulfield's avatar
Ian Caulfield committed
155 156 157
        return -1;
    }

158 159
    checksum = ff_mlp_checksum16(gb->buffer, header_size - 2);
    if (checksum != AV_RL16(gb->buffer+header_size-2)) {
Diego Biurrun's avatar
Diego Biurrun committed
160
        av_log(log, AV_LOG_ERROR, "major sync info header checksum error\n");
161
        return AVERROR_INVALIDDATA;
Ian Caulfield's avatar
Ian Caulfield committed
162 163
    }

164
    if (get_bits_long(gb, 24) != 0xf8726f) /* Sync words */
165
        return AVERROR_INVALIDDATA;
Ian Caulfield's avatar
Ian Caulfield committed
166

167
    mh->stream_type = get_bits(gb, 8);
168
    mh->header_size = header_size;
Ian Caulfield's avatar
Ian Caulfield committed
169 170

    if (mh->stream_type == 0xbb) {
171 172
        mh->group1_bits = mlp_quants[get_bits(gb, 4)];
        mh->group2_bits = mlp_quants[get_bits(gb, 4)];
Ian Caulfield's avatar
Ian Caulfield committed
173

174
        ratebits = get_bits(gb, 4);
Ian Caulfield's avatar
Ian Caulfield committed
175
        mh->group1_samplerate = mlp_samplerate(ratebits);
176
        mh->group2_samplerate = mlp_samplerate(get_bits(gb, 4));
Ian Caulfield's avatar
Ian Caulfield committed
177

178
        skip_bits(gb, 11);
Ian Caulfield's avatar
Ian Caulfield committed
179

180 181 182
        channel_arrangement    = get_bits(gb, 5);
        mh->channels_mlp       = mlp_channels[channel_arrangement];
        mh->channel_layout_mlp = mlp_layout[channel_arrangement];
Ian Caulfield's avatar
Ian Caulfield committed
183 184 185 186
    } else if (mh->stream_type == 0xba) {
        mh->group1_bits = 24; // TODO: Is this information actually conveyed anywhere?
        mh->group2_bits = 0;

187
        ratebits = get_bits(gb, 4);
Ian Caulfield's avatar
Ian Caulfield committed
188 189 190
        mh->group1_samplerate = mlp_samplerate(ratebits);
        mh->group2_samplerate = 0;

191 192 193 194
        skip_bits(gb, 4);

        mh->channel_modifier_thd_stream0 = get_bits(gb, 2);
        mh->channel_modifier_thd_stream1 = get_bits(gb, 2);
Ian Caulfield's avatar
Ian Caulfield committed
195

196 197 198
        channel_arrangement            = get_bits(gb, 5);
        mh->channels_thd_stream1       = truehd_channels(channel_arrangement);
        mh->channel_layout_thd_stream1 = truehd_layout(channel_arrangement);
Ian Caulfield's avatar
Ian Caulfield committed
199

200
        mh->channel_modifier_thd_stream2 = get_bits(gb, 2);
Ian Caulfield's avatar
Ian Caulfield committed
201

202 203 204
        channel_arrangement            = get_bits(gb, 13);
        mh->channels_thd_stream2       = truehd_channels(channel_arrangement);
        mh->channel_layout_thd_stream2 = truehd_layout(channel_arrangement);
Ian Caulfield's avatar
Ian Caulfield committed
205
    } else
206
        return AVERROR_INVALIDDATA;
Ian Caulfield's avatar
Ian Caulfield committed
207 208 209 210

    mh->access_unit_size = 40 << (ratebits & 7);
    mh->access_unit_size_pow2 = 64 << (ratebits & 7);

211
    skip_bits_long(gb, 48);
Ian Caulfield's avatar
Ian Caulfield committed
212

213
    mh->is_vbr = get_bits1(gb);
Ian Caulfield's avatar
Ian Caulfield committed
214

215
    mh->peak_bitrate = (get_bits(gb, 15) * mh->group1_samplerate + 8) >> 4;
Ian Caulfield's avatar
Ian Caulfield committed
216

217
    mh->num_substreams = get_bits(gb, 4);
Ian Caulfield's avatar
Ian Caulfield committed
218

219
    skip_bits_long(gb, 4 + (header_size - 17) * 8);
Ian Caulfield's avatar
Ian Caulfield committed
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234

    return 0;
}

typedef struct MLPParseContext
{
    ParseContext pc;

    int bytes_left;

    int in_sync;

    int num_substreams;
} MLPParseContext;

235 236 237 238 239 240
static av_cold int mlp_init(AVCodecParserContext *s)
{
    ff_mlp_init_crc();
    return 0;
}

Ian Caulfield's avatar
Ian Caulfield committed
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
static int mlp_parse(AVCodecParserContext *s,
                     AVCodecContext *avctx,
                     const uint8_t **poutbuf, int *poutbuf_size,
                     const uint8_t *buf, int buf_size)
{
    MLPParseContext *mp = s->priv_data;
    int sync_present;
    uint8_t parity_bits;
    int next;
    int i, p = 0;

    *poutbuf_size = 0;
    if (buf_size == 0)
        return 0;

    if (!mp->in_sync) {
        // Not in sync - find a major sync header

        for (i = 0; i < buf_size; i++) {
            mp->pc.state = (mp->pc.state << 8) | buf[i];
261 262 263
            if ((mp->pc.state & 0xfffffffe) == 0xf8726fba &&
                // ignore if we do not have the data for the start of header
                mp->pc.index + i >= 7) {
Ian Caulfield's avatar
Ian Caulfield committed
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
                mp->in_sync = 1;
                mp->bytes_left = 0;
                break;
            }
        }

        if (!mp->in_sync) {
            ff_combine_frame(&mp->pc, END_NOT_FOUND, &buf, &buf_size);
            return buf_size;
        }

        ff_combine_frame(&mp->pc, i - 7, &buf, &buf_size);

        return i - 7;
    }

    if (mp->bytes_left == 0) {
        // Find length of this packet

        /* Copy overread bytes from last frame into buffer. */
        for(; mp->pc.overread>0; mp->pc.overread--) {
            mp->pc.buffer[mp->pc.index++]= mp->pc.buffer[mp->pc.overread_index++];
        }

        if (mp->pc.index + buf_size < 2) {
            ff_combine_frame(&mp->pc, END_NOT_FOUND, &buf, &buf_size);
            return buf_size;
        }

        mp->bytes_left = ((mp->pc.index > 0 ? mp->pc.buffer[0] : buf[0]) << 8)
                       |  (mp->pc.index > 1 ? mp->pc.buffer[1] : buf[1-mp->pc.index]);
        mp->bytes_left = (mp->bytes_left & 0xfff) * 2;
        mp->bytes_left -= mp->pc.index;
    }

    next = (mp->bytes_left > buf_size) ? END_NOT_FOUND : mp->bytes_left;

    if (ff_combine_frame(&mp->pc, next, &buf, &buf_size) < 0) {
        mp->bytes_left -= buf_size;
        return buf_size;
    }

    mp->bytes_left = 0;

    sync_present = (AV_RB32(buf + 4) & 0xfffffffe) == 0xf8726fba;

    if (!sync_present) {
311 312
        /* The first nibble of a frame is a parity check of the 4-byte
         * access unit header and all the 2- or 4-byte substream headers. */
313
        // Only check when this isn't a sync frame - syncs have a checksum.
Ian Caulfield's avatar
Ian Caulfield committed
314 315

        parity_bits = 0;
316
        for (i = -1; i < mp->num_substreams; i++) {
Ian Caulfield's avatar
Ian Caulfield committed
317 318 319
            parity_bits ^= buf[p++];
            parity_bits ^= buf[p++];

320
            if (i < 0 || buf[p-2] & 0x80) {
Ian Caulfield's avatar
Ian Caulfield committed
321 322 323 324 325 326
                parity_bits ^= buf[p++];
                parity_bits ^= buf[p++];
            }
        }

        if ((((parity_bits >> 4) ^ parity_bits) & 0xF) != 0xF) {
327
            av_log(avctx, AV_LOG_INFO, "mlpparse: Parity check failed.\n");
Ian Caulfield's avatar
Ian Caulfield committed
328 329 330
            goto lost_sync;
        }
    } else {
331
        GetBitContext gb;
Ian Caulfield's avatar
Ian Caulfield committed
332 333
        MLPHeaderInfo mh;

334 335
        init_get_bits(&gb, buf + 4, (buf_size - 4) << 3);
        if (ff_mlp_read_major_sync(avctx, &mh, &gb) < 0)
Ian Caulfield's avatar
Ian Caulfield committed
336 337
            goto lost_sync;

338 339
        avctx->bits_per_raw_sample = mh.group1_bits;
        if (avctx->bits_per_raw_sample > 16)
340
            avctx->sample_fmt = AV_SAMPLE_FMT_S32;
341
        else
342
            avctx->sample_fmt = AV_SAMPLE_FMT_S16;
Ian Caulfield's avatar
Ian Caulfield committed
343
        avctx->sample_rate = mh.group1_samplerate;
344
        s->duration = mh.access_unit_size;
Ian Caulfield's avatar
Ian Caulfield committed
345 346 347

        if (mh.stream_type == 0xbb) {
            /* MLP stream */
348 349 350
            if (avctx->request_channel_layout &&
                (avctx->request_channel_layout & AV_CH_LAYOUT_STEREO) ==
                avctx->request_channel_layout &&
351 352 353
                mh.num_substreams > 1) {
                avctx->channels       = 2;
                avctx->channel_layout = AV_CH_LAYOUT_STEREO;
354 355 356 357
            } else {
                avctx->channels       = mh.channels_mlp;
                avctx->channel_layout = mh.channel_layout_mlp;
            }
Ian Caulfield's avatar
Ian Caulfield committed
358 359
        } else { /* mh.stream_type == 0xba */
            /* TrueHD stream */
360 361 362 363
                if (avctx->request_channel_layout &&
                    (avctx->request_channel_layout & AV_CH_LAYOUT_STEREO) ==
                    avctx->request_channel_layout &&
                    mh.num_substreams > 1) {
364 365
                avctx->channels       = 2;
                avctx->channel_layout = AV_CH_LAYOUT_STEREO;
366
            } else if (!mh.channels_thd_stream2 ||
367 368 369
                       (avctx->request_channel_layout &&
                        (avctx->request_channel_layout & mh.channel_layout_thd_stream1) ==
                        avctx->request_channel_layout)) {
370 371
                avctx->channels       = mh.channels_thd_stream1;
                avctx->channel_layout = mh.channel_layout_thd_stream1;
372 373 374
            } else {
                avctx->channels       = mh.channels_thd_stream2;
                avctx->channel_layout = mh.channel_layout_thd_stream2;
375
            }
Ian Caulfield's avatar
Ian Caulfield committed
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
        }

        if (!mh.is_vbr) /* Stream is CBR */
            avctx->bit_rate = mh.peak_bitrate;

        mp->num_substreams = mh.num_substreams;
    }

    *poutbuf = buf;
    *poutbuf_size = buf_size;

    return next;

lost_sync:
    mp->in_sync = 0;
391
    return 1;
Ian Caulfield's avatar
Ian Caulfield committed
392 393
}

394
AVCodecParser ff_mlp_parser = {
395
    .codec_ids      = { AV_CODEC_ID_MLP, AV_CODEC_ID_TRUEHD },
396 397 398 399
    .priv_data_size = sizeof(MLPParseContext),
    .parser_init    = mlp_init,
    .parser_parse   = mlp_parse,
    .parser_close   = ff_parse_close,
Ian Caulfield's avatar
Ian Caulfield committed
400
};