From 39f06dc99aa1aa500c2e564deb694bd9894fc069 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 10 Mar 2015 12:52:44 +0100 Subject: [PATCH] Added MP3 input support (coded by Erik Zandvliet) --- Makefile | 6 ++ src/input/input_mp3.cpp | 196 ++++++++++++++++++++++++++++++++++++++++ src/input/input_mp3.h | 31 +++++++ 3 files changed, 233 insertions(+) create mode 100644 src/input/input_mp3.cpp create mode 100644 src/input/input_mp3.h diff --git a/Makefile b/Makefile index 55923ac4..5832dfe1 100644 --- a/Makefile +++ b/Makefile @@ -75,6 +75,12 @@ MistInDTSC: override CPPFLAGS += "-DINPUTTYPE=\"input_dtsc.h\"" MistInDTSC: src/input/mist_in.cpp src/input/input.cpp src/input/input_dtsc.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ +inputs: MistInMP3 +MistInMP3: override LDLIBS += $(THREADLIB) +MistInMP3: override CPPFLAGS += "-DINPUTTYPE=\"input_mp3.h\"" +MistInMP3: src/input/mist_in.cpp src/input/input.cpp src/input/input_mp3.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + inputs: MistInFLV MistInFLV: override LDLIBS += $(THREADLIB) MistInFLV: override CPPFLAGS += "-DINPUTTYPE=\"input_flv.h\"" diff --git a/src/input/input_mp3.cpp b/src/input/input_mp3.cpp new file mode 100644 index 00000000..e49141b3 --- /dev/null +++ b/src/input/input_mp3.cpp @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "input_mp3.h" + +namespace Mist { + inputMP3::inputMP3(Util::Config * cfg) : Input(cfg) { + capa["name"] = "MP3"; + capa["desc"] = "Enables MP3 Input"; + capa["source_match"] = "/*.mp3"; + capa["priority"] = 9ll; + capa["codecs"][0u][0u].append("MP3"); + timestamp = 0; + } + + bool inputMP3::setup() { + if (config->getString("input") == "-") { + std::cerr << "Input from stdin not yet supported" << std::endl; + return false; + } + if (!config->getString("streamname").size()){ + if (config->getString("output") == "-") { + std::cerr << "Output to stdout not yet supported" << std::endl; + return false; + } + }else{ + if (config->getString("output") != "-") { + std::cerr << "File output in player mode not supported" << std::endl; + return false; + } + } + + //open File + inFile = fopen(config->getString("input").c_str(), "r"); + if (!inFile) { + return false; + } + return true; + } + + bool inputMP3::readHeader() { + if (!inFile) { + return false; + } + //See whether a separate header file exists. + DTSC::File tmp(config->getString("input") + ".dtsh"); + if (tmp){ + myMeta = tmp.getMeta(); + if (myMeta){ + return true; + } + } + myMeta = DTSC::Meta(); + myMeta.tracks[1].trackID = 1; + myMeta.tracks[1].type = "audio"; + myMeta.tracks[1].codec = "MP3"; + //Create header file from MP3 data + char header[10]; + fread(header, 10, 1, inFile);//Read a 10 byte header + if (header[0] == 'I' || header[1] == 'D' || header[2] == '3'){ + size_t id3size = (((int)header[6] & 0x7F) << 21) | (((int)header[7] & 0x7F) << 14) | (((int)header[8] & 0x7F) << 7) | (header[9] & 0x7F) + 10 + ((header[5] & 0x10) ? 10 : 0); + INFO_MSG("id3 size: %lu bytes", id3size); + fseek(inFile, id3size, SEEK_SET); + }else{ + fseek(inFile, 0, SEEK_SET); + } + //Read the first mp3 header for bitrate and such + size_t filePos = ftell(inFile); + fread(header, 4, 1, inFile); + fseek(inFile, filePos, SEEK_SET); + + //mpeg version is on the bits 0x18 of header[1], but only 0x08 is important --> 0 is version 2, 1 is version 1 + //leads to 2 - value == version, -1 to get the right index for the array + int mpegVersion = 1 - ((header[1] >> 3) & 0x01); + //samplerate is encoded in bits 0x0C of header[2]; + myMeta.tracks[1].rate = sampleRates[mpegVersion][((header[2] >> 2) & 0x03)] * 1000; + myMeta.tracks[1].channels = 2 - ( header[3] >> 7); + + + getNext(); + while (lastPack){ + myMeta.update(lastPack); + getNext(); + } + + fseek(inFile, 0, SEEK_SET); + timestamp = 0; + std::ofstream oFile(std::string(config->getString("input") + ".dtsh").c_str()); + oFile << myMeta.toJSON().toNetPacked(); + oFile.close(); + return true; + } + + void inputMP3::getNext(bool smart) { + lastPack.null(); + static char packHeader[3000]; + size_t filePos = ftell(inFile); + size_t read = fread(packHeader, 1, 3000, inFile); + if (!read) { + return; + } + if (packHeader[0] != 0xFF || (packHeader[1] & 0xE0) != 0xE0){ + //Find the first occurence of sync byte + char* i = (char*)memchr(packHeader, (char)0xFF, read); + if (!i) { + return; + } + size_t offset = i - packHeader; + while (offset && (i[1] & 0xE0) != 0xE0){ + i = (char*)memchr(i + 1, (char)0xFF, read - (offset + 1)); + if (!i) { + offset = 0; + break; + } + } + if (!offset){ + DEBUG_MSG(DLVL_FAIL, "Sync byte not found from offset %llu", filePos); + return; + } + filePos += offset; + fseek(inFile, filePos, SEEK_SET); + read = fread(packHeader, 1, 3000, inFile); + } + //We now have a sync byte for sure + + //mpeg version is on the bits 0x18 of packHeader[1], but only 0x08 is important --> 0 is version 2, 1 is version 1 + //leads to 2 - value == version, -1 to get the right index for the array + int mpegVersion = 1 - ((packHeader[1] >> 3) & 0x01); + //mpeg layer is on the bits 0x06 of packHeader[1] --> 1 is layer 3, 2 is layer 2, 3 is layer 1 + //leads to 4 - value == layer, -1 to get the right index for the array + int mpegLayer = 3 - ((packHeader[1] >> 1) & 0x03); + int sampleCount = sampleCounts[mpegVersion][mpegLayer]; + //samplerate is encoded in bits 0x0C of packHeader[2]; + int sampleRate = sampleRates[mpegVersion][((packHeader[2] >> 2) & 0x03)] * 1000; + + int bitRate = bitRates[mpegVersion][mpegLayer][((packHeader[2] >> 4) & 0x0F)] * 1000; + + + + size_t dataSize = 0; + if (mpegLayer == 0){ //layer 1 + //Layer 1: dataSize = (12 * BitRate / SampleRate + Padding) * 4 + dataSize = (12 * ((double)bitRate / sampleRate) + ((packHeader[2] >> 1) & 0x01)) * 4; + }else{//Layer 2 or 3 + //Layer 2, 3: dataSize = 144 * BitRate / SampleRate + Padding + dataSize = 144 * ((double)bitRate / sampleRate) + ((packHeader[2] >> 1) & 0x01); + } + + + if (!dataSize){ + return; + } + fseek(inFile, filePos + dataSize, SEEK_SET); + + //Create a json value with the right data + static JSON::Value thisPack; + thisPack.null(); + thisPack["trackid"] = 1; + thisPack["bpos"] = (long long)filePos; + thisPack["data"] = std::string(packHeader, dataSize); + thisPack["time"] = (long long)timestamp; + //Write the json value to lastpack + std::string tmpStr = thisPack.toNetPacked(); + lastPack.reInit(tmpStr.data(), tmpStr.size()); + + + //Update the internal timestamp + timestamp += (sampleCount / (sampleRate / 1000)); + } + + void inputMP3::seek(int seekTime) { + std::deque & keys = myMeta.tracks[1].keys; + size_t seekPos = keys[0].getBpos(); + for (unsigned int i = 0; i < keys.size(); i++){ + if (keys[i].getTime() > seekTime){ + break; + } + seekPos = keys[i].getBpos(); + } + fseek(inFile, seekPos, SEEK_SET); + } + + void inputMP3::trackSelect(std::string trackSpec) { + //Ignore, do nothing + //MP3 Always has only 1 track, so we can't select much else.. + } +} + diff --git a/src/input/input_mp3.h b/src/input/input_mp3.h new file mode 100644 index 00000000..bd2afa1d --- /dev/null +++ b/src/input/input_mp3.h @@ -0,0 +1,31 @@ +#include "input.h" +#include +#include + +namespace Mist { + const static double sampleRates[2][3] = {{44.1, 48.0, 32.0}, {22.05, 24.0, 16.0}}; + const static int sampleCounts[2][3] = {{374, 1152, 1152}, {384, 1152, 576}}; + const static int bitRates[2][3][16] = {{{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1}, + {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1}, + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1}}, + {{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1}, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1}, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1}}}; + class inputMP3 : public Input { + public: + inputMP3(Util::Config * cfg); + protected: + //Private Functions + bool setup(); + bool readHeader(); + void getNext(bool smart = true); + void seek(int seekTime); + void trackSelect(std::string trackSpec); + double timestamp; + + FILE * inFile; + }; +} + +typedef Mist::inputMP3 mistIn; +