toolkit/components/mediasniffer/mp3sniff.c

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /* MPEG format parsing */
michael@0 6
michael@0 7 #include "mp3sniff.h"
michael@0 8
michael@0 9 /* Maximum packet size is 320 kbits/s * 144 / 32 kHz + 1 padding byte */
michael@0 10 #define MP3_MAX_SIZE 1441
michael@0 11
michael@0 12 typedef struct {
michael@0 13 int version;
michael@0 14 int layer;
michael@0 15 int errp;
michael@0 16 int bitrate;
michael@0 17 int freq;
michael@0 18 int pad;
michael@0 19 int priv;
michael@0 20 int mode;
michael@0 21 int modex;
michael@0 22 int copyright;
michael@0 23 int original;
michael@0 24 int emphasis;
michael@0 25 } mp3_header;
michael@0 26
michael@0 27 /* Parse the 4-byte header in p and fill in the header struct. */
michael@0 28 static void mp3_parse(const uint8_t *p, mp3_header *header)
michael@0 29 {
michael@0 30 const int bitrates[2][16] = {
michael@0 31 /* MPEG version 1 layer 3 bitrates. */
michael@0 32 {0, 32000, 40000, 48000, 56000, 64000, 80000, 96000,
michael@0 33 112000, 128000, 160000, 192000, 224000, 256000, 320000, 0},
michael@0 34 /* MPEG Version 2 and 2.5 layer 3 bitrates */
michael@0 35 {0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000,
michael@0 36 80000, 96000, 112000, 128000, 144000, 160000, 0} };
michael@0 37 const int samplerates[4] = {44100, 48000, 32000, 0};
michael@0 38
michael@0 39 header->version = (p[1] & 0x18) >> 3;
michael@0 40 header->layer = 4 - ((p[1] & 0x06) >> 1);
michael@0 41 header->errp = (p[1] & 0x01);
michael@0 42
michael@0 43 header->bitrate = bitrates[(header->version & 1) ? 0 : 1][(p[2] & 0xf0) >> 4];
michael@0 44 header->freq = samplerates[(p[2] & 0x0c) >> 2];
michael@0 45 if (header->version == 2) header->freq >>= 1;
michael@0 46 else if (header->version == 0) header->freq >>= 2;
michael@0 47 header->pad = (p[2] & 0x02) >> 1;
michael@0 48 header->priv = (p[2] & 0x01);
michael@0 49
michael@0 50 header->mode = (p[3] & 0xc0) >> 6;
michael@0 51 header->modex = (p[3] & 0x30) >> 4;
michael@0 52 header->copyright = (p[3] & 0x08) >> 3;
michael@0 53 header->original = (p[3] & 0x04) >> 2;
michael@0 54 header->emphasis = (p[3] & 0x03);
michael@0 55 }
michael@0 56
michael@0 57 /* calculate the size of an mp3 frame from its header */
michael@0 58 static int mp3_framesize(mp3_header *header)
michael@0 59 {
michael@0 60 int size;
michael@0 61 int scale;
michael@0 62
michael@0 63 if ((header->version & 1) == 0) scale = 72;
michael@0 64 else scale = 144;
michael@0 65 size = header->bitrate * scale / header->freq;
michael@0 66 if (header->pad) size += 1;
michael@0 67
michael@0 68 return size;
michael@0 69 }
michael@0 70
michael@0 71 static int is_mp3(const uint8_t *p, long length) {
michael@0 72 /* Do we have enough room to see a 4 byte header? */
michael@0 73 if (length < 4) return 0;
michael@0 74 /* Do we have a sync pattern? */
michael@0 75 if (p[0] == 0xff && (p[1] & 0xe0) == 0xe0) {
michael@0 76 /* Do we have any illegal field values? */
michael@0 77 if (((p[1] & 0x06) >> 1) == 0) return 0; /* No layer 4 */
michael@0 78 if (((p[2] & 0xf0) >> 4) == 15) return 0; /* Bitrate can't be 1111 */
michael@0 79 if (((p[2] & 0x0c) >> 2) == 3) return 0; /* Samplerate can't be 11 */
michael@0 80 /* Looks like a header. */
michael@0 81 if ((4 - ((p[1] & 0x06) >> 1)) != 3) return 0; /* Only want level 3 */
michael@0 82 return 1;
michael@0 83 }
michael@0 84 return 0;
michael@0 85 }
michael@0 86
michael@0 87 /* Identify an ID3 tag based on its header. */
michael@0 88 /* http://id3.org/id3v2.4.0-structure */
michael@0 89 static int is_id3(const uint8_t *p, long length) {
michael@0 90 /* Do we have enough room to see the header? */
michael@0 91 if (length < 10) return 0;
michael@0 92 /* Do we have a sync pattern? */
michael@0 93 if (p[0] == 'I' && p[1] == 'D' && p[2] == '3') {
michael@0 94 if (p[3] == 0xff || p[4] == 0xff) return 0; /* Illegal version. */
michael@0 95 if (p[6] & 0x80 || p[7] & 0x80 ||
michael@0 96 p[8] & 0x80) return 0; /* Bad length encoding. */
michael@0 97 /* Looks like an id3 header. */
michael@0 98 return 1;
michael@0 99 }
michael@0 100 return 0;
michael@0 101 }
michael@0 102
michael@0 103 /* Calculate the size of an id3 tag structure from its header. */
michael@0 104 static int id3_framesize(const uint8_t *p, long length)
michael@0 105 {
michael@0 106 int size;
michael@0 107
michael@0 108 /* Header is 10 bytes. */
michael@0 109 if (length < 10) {
michael@0 110 return 0;
michael@0 111 }
michael@0 112 /* Frame is header plus declared size. */
michael@0 113 size = 10 + (p[9] | (p[8] << 7) | (p[7] << 14) | (p[6] << 21));
michael@0 114
michael@0 115 return size;
michael@0 116 }
michael@0 117
michael@0 118 int mp3_sniff(const uint8_t *buf, long length)
michael@0 119 {
michael@0 120 mp3_header header;
michael@0 121 const uint8_t *p, *q;
michael@0 122 long skip;
michael@0 123 long avail;
michael@0 124
michael@0 125 p = buf;
michael@0 126 q = p;
michael@0 127 avail = length;
michael@0 128 while (avail >= 4) {
michael@0 129 if (is_id3(p, avail)) {
michael@0 130 /* Skip over any id3 tags */
michael@0 131 skip = id3_framesize(p, avail);
michael@0 132 p += skip;
michael@0 133 avail -= skip;
michael@0 134 } else if (is_mp3(p, avail)) {
michael@0 135 mp3_parse(p, &header);
michael@0 136 skip = mp3_framesize(&header);
michael@0 137 if (skip < 4 || skip + 4 >= avail) {
michael@0 138 return 0;
michael@0 139 }
michael@0 140 p += skip;
michael@0 141 avail -= skip;
michael@0 142 /* Check for a second header at the expected offset. */
michael@0 143 if (is_mp3(p, avail)) {
michael@0 144 /* Looks like mp3. */
michael@0 145 return 1;
michael@0 146 } else {
michael@0 147 /* No second header. Not mp3. */
michael@0 148 return 0;
michael@0 149 }
michael@0 150 } else {
michael@0 151 /* No id3 tag or mp3 header. Not mp3. */
michael@0 152 return 0;
michael@0 153 }
michael@0 154 }
michael@0 155
michael@0 156 return 0;
michael@0 157 }

mercurial