Added MP3 input support (coded by Erik Zandvliet)

This commit is contained in:
Thulinma 2015-03-10 12:52:44 +01:00
parent bbb405269f
commit 39f06dc99a
3 changed files with 233 additions and 0 deletions

View file

@ -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\""

196
src/input/input_mp3.cpp Normal file
View file

@ -0,0 +1,196 @@
#include <iostream>
#include <fstream>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <mist/stream.h>
#include <mist/flv_tag.h>
#include <mist/defines.h>
#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<DTSC::Key> & 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..
}
}

31
src/input/input_mp3.h Normal file
View file

@ -0,0 +1,31 @@
#include "input.h"
#include <mist/dtsc.h>
#include <deque>
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;