Added MP3 input support (coded by Erik Zandvliet)
This commit is contained in:
		
							parent
							
								
									bbb405269f
								
							
						
					
					
						commit
						39f06dc99a
					
				
					 3 changed files with 233 additions and 0 deletions
				
			
		
							
								
								
									
										6
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								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\"" | ||||
|  |  | |||
							
								
								
									
										196
									
								
								src/input/input_mp3.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								src/input/input_mp3.cpp
									
										
									
									
									
										Normal 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
									
								
							
							
						
						
									
										31
									
								
								src/input/input_mp3.h
									
										
									
									
									
										Normal 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; | ||||
| 
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Thulinma
						Thulinma