# All files / src/demux mpegaudio.js

2.27% Statements 1/44
0% Branches 0/50
0% Functions 0/5
2.27% Lines 1/44

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99

    1x

     /**
     *  MPEG parser helper
     */

    const MpegAudio = {

        BitratesMap: [
            32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448,
            32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384,
            32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,
            32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256,
            8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160],

        SamplingRateMap: [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000],

        appendFrame: function (track, data, offset, pts, frameIndex) {
            // Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference
            if (offset + 24 > data.length) {
                return undefined;
            }

            var header = this.parseHeader(data, offset);
            if (header && offset + header.frameLength <= data.length) {
                var frameDuration = 1152 * 90000 / header.sampleRate;
                var stamp = pts + frameIndex * frameDuration;
                var sample = { unit: data.subarray(offset, offset + header.frameLength), pts: stamp, dts: stamp };

                track.config = [];
                track.channelCount = header.channelCount;
                track.samplerate = header.sampleRate;
                track.samples.push(sample);
                track.len += header.frameLength;

                return { sample, length: header.frameLength };
            }

            return undefined;
        },

        parseHeader: function (data, offset) {
            var headerB = (data[offset + 1] >> 3) & 3;
            var headerC = (data[offset + 1] >> 1) & 3;
            var headerE = (data[offset + 2] >> 4) & 15;
            var headerF = (data[offset + 2] >> 2) & 3;
            var headerG = !!(data[offset + 2] & 2);
            if (headerB !== 1 && headerE !== 0 && headerE !== 15 && headerF !== 3) {
                var columnInBitrates = headerB === 3 ? (3 - headerC) : (headerC === 3 ? 3 : 4);
                var bitRate = MpegAudio.BitratesMap[columnInBitrates * 14 + headerE - 1] * 1000;
                var columnInSampleRates = headerB === 3 ? 0 : headerB === 2 ? 1 : 2;
                var sampleRate = MpegAudio.SamplingRateMap[columnInSampleRates * 3 + headerF];
                var padding = headerG ? 1 : 0;
                var channelCount = data[offset + 3] >> 6 === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono)
                var frameLength = headerC === 3 ?
                    ((headerB === 3 ? 12 : 6) * bitRate / sampleRate + padding) << 2 :
                    ((headerB === 3 ? 144 : 72) * bitRate / sampleRate + padding) | 0;

                return { sampleRate, channelCount, frameLength };
            }

            return undefined;
        },

        isHeaderPattern: function (data, offset) {
            return data[offset] === 0xff && (data[offset + 1] & 0xe0) === 0xe0 && (data[offset + 1] & 0x06) !== 0x00;
        },

        isHeader: function (data, offset) {
            // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1
            // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III)
            // More info http://www.mp3-tech.org/programmer/frame_header.html
            if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) {
                return true;
            }
            return false;
        },

        probe: function (data, offset) {
            // same as isHeader but we also check that MPEG frame follows last MPEG frame
            // or end of data is reached
            if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) {
                // MPEG header Length
                let headerLength = 4;
                // MPEG frame Length
                let header = this.parseHeader(data, offset);
                let frameLength = headerLength;
                if (header && header.frameLength) {
                    frameLength = header.frameLength;
                }
                let newOffset = offset + frameLength;
                if (newOffset === data.length || (newOffset + 1 < data.length && this.isHeaderPattern(data, newOffset))) {
                    return true;
                }
            }
            return false;
        }
    };

    export default MpegAudio;