You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
203 lines
5.8 KiB
203 lines
5.8 KiB
/* aKode: Magic
|
|
|
|
Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library 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
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <cstring>
|
|
#include "akodelib.h"
|
|
#include "magic.h"
|
|
#include "file.h"
|
|
#include <iostream>
|
|
using std::cerr;
|
|
|
|
namespace aKode {
|
|
namespace Magic {
|
|
|
|
static int detectID3v2(File *src)
|
|
{
|
|
char header[6];
|
|
unsigned char *buf = (unsigned char*)header;
|
|
if(src->read(header, 4)) {
|
|
// skip id3v2 headers
|
|
if (memcmp(header, "ID3", 3) == 0) {
|
|
src->read(header, 6);
|
|
int size = 10;
|
|
if (buf[1] & 0x10) size += 10;
|
|
if (buf[5] > 127 || buf[4] > 127 || buf[3] > 127 || buf[2] > 127)
|
|
{
|
|
size += buf[5];
|
|
size += buf[4] << 8;
|
|
size += buf[3] << 18;
|
|
size += buf[2] << 24;
|
|
cerr << "Un-unsynchronized size\n";
|
|
}
|
|
size += buf[5];
|
|
size += buf[4] << 7;
|
|
size += buf[3] << 14;
|
|
size += buf[2] << 21;
|
|
// cerr << "ID3v2 header found(size="<< size << ")\n";
|
|
return size;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
/*
|
|
struct OggMagic {
|
|
char magic[8];
|
|
int len;
|
|
void *format;
|
|
}
|
|
|
|
OggMagic ogg_magic = {
|
|
{"fLaC", 4, &oggflac_format},
|
|
{"\x7fFLAC", 5, &oggflac_format},
|
|
{"\x01vorbis", 7, &vorbis_format},
|
|
{"Speex ", 7, &speex_format}
|
|
{"", 0, 0}
|
|
}
|
|
|
|
Format* detectOgg(File *src, int skip) {
|
|
Format *res = 0;
|
|
char oggmagic[7];
|
|
src->lseek(skip+28);
|
|
src->read(oggmagic, 7);
|
|
|
|
for (OggMagic *i = ogg_magic; i->len >0; i++) {
|
|
if (memcmp(oggmagic, i->magic, i->len) == 0)
|
|
return i->format;
|
|
}
|
|
|
|
return res;
|
|
} */
|
|
|
|
string detectRIFF(File *src, int skip) {
|
|
string res;
|
|
char riffmagic[4];
|
|
src->lseek(skip+8);
|
|
src->read(riffmagic, 4);
|
|
if (memcmp(riffmagic, "WAVE",4) == 0) {
|
|
char wavmagic[2];
|
|
src->lseek(skip+20);
|
|
src->read(wavmagic, 2);
|
|
if (wavmagic[0] == 1)
|
|
res = "wav";
|
|
else
|
|
if (wavmagic[0] == 80)
|
|
res = "mpeg";
|
|
else
|
|
if (wavmagic[0] == 85)
|
|
res = "mpeg";
|
|
}
|
|
return res;
|
|
}
|
|
|
|
string detectMPEG(File *src, int skip) {
|
|
string res;
|
|
unsigned char mpegheader[2];
|
|
src->lseek(skip);
|
|
src->read((char*)mpegheader, 2);
|
|
|
|
if (mpegheader[0] == 0xff && (mpegheader[1] & 0xe0) == 0xe0) // frame synchronizer
|
|
if((mpegheader[1] & 0x18) != 0x08) // support MPEG 1, 2 and 2.5
|
|
if((mpegheader[1] & 0x06) != 0x00) // Layer I, II and III
|
|
res = "mpeg";
|
|
|
|
return res;
|
|
}
|
|
|
|
string detectSuffix(string filename) {
|
|
// A lot of mp3s dont start with a synchronization
|
|
// so use some suffix matching as well.
|
|
|
|
int len = filename.length();
|
|
if (len < 4) return "";
|
|
string end = filename.substr(len-4, 4);
|
|
|
|
if (end == ".mp3") return "mpeg";
|
|
if (end == ".ogg") return "xiph";
|
|
if (end == ".wma") return "ffmpeg";
|
|
if (end == ".m4a") return "ffmpeg";
|
|
if (end == ".aac") return "ffmpeg";
|
|
if (end == ".ac3") return "ffmpeg";
|
|
return "";
|
|
}
|
|
|
|
string detectFile(File *src) {
|
|
string res;
|
|
if (!src->openRO())
|
|
return res;
|
|
|
|
int skip = detectID3v2(src);
|
|
char magic[4];
|
|
src->lseek(skip);
|
|
src->read(magic, 4);
|
|
|
|
if (memcmp(magic, "fLaC", 4) == 0)
|
|
res = "xiph";
|
|
else
|
|
if (memcmp(magic, "OggS", 4) == 0)
|
|
res = "xiph";
|
|
else
|
|
if (memcmp(magic, "MP+", 3) == 0)
|
|
res = "mpc";
|
|
else
|
|
if (memcmp(magic, "\x30\x26\xb2\x75", 4) == 0) // ASF
|
|
res = "ffmpeg";
|
|
else
|
|
if (memcmp(magic, ".RMF", 4) == 0) // RealAudio
|
|
res = "ffmpeg";
|
|
else
|
|
if (memcmp(magic, ".ra", 3) == 0) // Old RealAudio
|
|
res = "ffmpeg";
|
|
else
|
|
if (memcmp(magic, "RIFF", 4) == 0)
|
|
res = detectRIFF(src, skip);
|
|
else
|
|
res = detectMPEG(src, skip);
|
|
|
|
if (res.empty()) res = detectSuffix(src->filename);
|
|
|
|
src->close();
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
Format *detectStream(Stream *src) {
|
|
Format *res = 0;
|
|
if (!src->openRO())
|
|
return 0;
|
|
|
|
// Search for Ogg or MPEG synchronization header
|
|
char *page = new char[4096];
|
|
src->read(page, 4096);
|
|
for (int i=0; i<4096; i++) {
|
|
if (page[i] == 'O' && page[i+1] == 'g' && page[i+1] == 'g' && page[i+1] == 'S') {
|
|
res = detectOgg(src, i);
|
|
if (res) break;
|
|
}
|
|
if (page[i] == 0xff && (page[i+1] & 0x80) == 0x80) {
|
|
res = detectMPEG(src, i);
|
|
if (res) break;
|
|
}
|
|
}
|
|
return res;
|
|
} */
|
|
} // namespace
|
|
|
|
} // namespace
|