/* * This file adds AAC input capabilities * Dependent on lib/adts and lib/urireader * * Input can be any AAC file which consists of ADTS frames. * * NOTE some .AAC files are containers (eg .M4A) with an AAC audio track in it. * Since these files do not consist solely of ADTS frames, they can not be parsed * * NOTE All output AAC's are MPEG-4, while inputs can be MPEG-2. This will cause * a slight different header (FFF1 instead of FFF0) but this is fine * * NOTE: The adts_buffer_fullness and number_of_raw_data_blocks_in_frame are different * in the input and output file * * NOTE: sometimes AAC files have metadata at the end. This gets removed for now. * * * Other useful info for debugging: * ADTS Fixed Header Structure: * Item # Bits Note Bit# Byte# * syncword 12 0xFFF 0-11 0-1 (bits 0-15) * ID 1 12 1 (bits 8-15) * layer 2 13-14 1 (bits 8-15) * protection_absent 1 15 2 (bits 16-23) * profile_ObjectType 2 16-17 2 (bits 16-23) * sampling_frequency_index 4 18-21 2 (bits 16-23) * private_bit 1 22 2 (bits 16-23) * channel_configuration 3 23-25 2-3 (bits 16-31) * original_copy 1 26 3 (bits 24-31) * home 1 27 3 (bits 24-31) * == 28 bits * ADTS Variable Header Structure * Item # Bits Note * copyright_identification_bit 1 28 3 (bits 24-31) * copyright_identification_start 1 29 3 (bits 24-31) * aac_frame_length 13 30-42 3-4-5 (bits 24-47) * adts_buffer_fullness 11 43-53 5-6 (bits 40-55) * number_of_raw_data_blocks_in_frame 2 54-55 6 (bits 48-55) * == 28 bits * The rest of the ADTS data is the data itself with CRC info for detecting * changes in sent/received files * */ #include #include #include #include #include #include #include #include #include #include #include //for stat #include //for stat #include //for stat #include "input_aac.h" namespace Mist{ inputAAC::inputAAC(Util::Config *cfg) : Input(cfg){ capa["name"] = "AAC"; capa["desc"] = "Allows loading AAC files"; capa["source_match"] = "/*.aac"; capa["source_file"] = "$source"; capa["priority"] = 9; capa["codecs"][0u][1u].append("AAC"); timestamp = 0; // init filePos at 1, else a 15 bit mismatch in expected frame size occurs // dtsc.ccp +- line 215 // ( bpos, if >= 0, adds 9 bytes (integer type) and 6 bytes (2+namelen) ) // but at line 224: (packBytePos ? 15 : 0) filePos = 1; } inputAAC::~inputAAC(){} bool inputAAC::checkArguments(){ 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; } } return true; } bool inputAAC::preRun(){ inFile.open(config->getString("input")); if (!inFile || inFile.isEOF()){return false;} struct stat statData; lastModTime = 0; if (stat(config->getString("input").c_str(), &statData) != -1){ lastModTime = statData.st_mtime; } return true; } // Overrides the default keepRunning function to shut down // if the file disappears or changes, by polling the file's mtime. // If neither applies, calls the original function. bool inputAAC::keepRunning(){ struct stat statData; if (stat(config->getString("input").c_str(), &statData) == -1){ INFO_MSG("Shutting down because input file disappeared"); return false; } if (lastModTime != statData.st_mtime){ INFO_MSG("Shutting down because input file changed"); return false; } return Input::keepRunning(); } // Reads the first frame to init track // Then calls getNext untill all other frames have been added to the DTSH file bool inputAAC::readHeader(){ char *aacData; char *aacFrame; uint64_t frameSize = 0; size_t bytesRead = 0; if (!inFile || inFile.isEOF()){ INFO_MSG("Could not open input stream"); return false; } DONTEVEN_MSG("Parsing first ADTS frame..."); // Read fixed + variable header inFile.readSome(aacData, bytesRead, 6); if (bytesRead < 6){ WARN_MSG("Not enough bytes left in buffer. Quitting..."); // Dump for debug purposes INFO_MSG("Header contains bytes: %x %x %x %x %x %x", aacData[0] , aacData[1], aacData[2], aacData[3], aacData[4], aacData[5]); return false; } // Confirm syncword (= FFF) if (aacData[0] != 0xFF || (aacData[1] & 0xF0) != 0xF0){ WARN_MSG("Invalid sync word at start of header"); return false; } // Calculate the starting position of the next frame frameSize = (((aacData[3] & 0x03) << 11) | (aacData[4] << 3) | ((aacData[5] >> 5) & 0x07)); // Copy AAC header info aacFrame = (char*)malloc(frameSize); for (int i = 0; i < 6; i++){ aacFrame[i] = aacData[i]; } // Read the rest of the AAC frame inFile.readSome(aacData, bytesRead, frameSize - 6); if (bytesRead < frameSize - 6){ WARN_MSG("Not enough bytes left in buffer."); WARN_MSG("Wanted %li bytes but read %li bytes...", frameSize - 6, bytesRead); } for (int i = 0; i < (frameSize - 6); i++){ aacFrame[i+6] = aacData[i]; } // Create ADTS object of complete frame info aac::adts adtsPack(aacFrame, frameSize); if (!adtsPack){ WARN_MSG("Could not parse ADTS package!"); return false; } // Init track info meta.reInit(config->getString("streamname")); size_t audioTrack = meta.addTrack(); meta.setID(audioTrack, audioTrack); meta.setInit(audioTrack, adtsPack.getInit()); meta.setType(audioTrack, "audio"); meta.setCodec(audioTrack, "AAC"); meta.setRate(audioTrack, adtsPack.getFrequency()); meta.setChannels(audioTrack, adtsPack.getChannelCount()); // Add current frame info thisPacket.genericFill(timestamp, 0, audioTrack, adtsPack.getPayload(), adtsPack.getPayloadSize(), filePos, false); meta.update(thisPacket); // Update internal variables timestamp += (adtsPack.getSampleCount() * 1000) / adtsPack.getFrequency(); filePos += frameSize; // Parse the rest of the ADTS frames getNext(audioTrack); while (thisPacket){ meta.update(thisPacket); getNext(audioTrack); } if (!inFile.seek(0)) ERROR_MSG("Could not seek back to position 0!"); timestamp = 0; M.toFile(config->getString("input") + ".dtsh"); return true; } // Reads the ADTS frame at the current position then updates thisPacket // @param contains the trackID to which we want to add the ADTS payload void inputAAC::getNext(size_t idx){ DONTEVEN_MSG("Parsing next ADTS frame..."); // Temp variable which points to the urireader buffer so that we can copy this data char *aacData; // Will contain a local copy of uriReader/fileIn buffer in order to fill thisPacket char *aacFrame; // Temp variable which stores the frame size as defined in the first // 6 bytes of the ADTS frame uint64_t frameSize = 0; // Temp variable which gets incremented with bytesRead to indicate the start // pos of next ADTS frame size_t nextFramePos = filePos; // Temp var which indicates how many bytes the urireader put into the buffer size_t bytesRead = 0; // Amount of bytes to subtract from expected payload if the found payload // is smaller than the ADTS header specifies size_t disregardAmount = 0; //packets should be initialised to null to ensure termination thisPacket.null(); if (!inFile || inFile.isEOF()){ INFO_MSG("Reached EOF"); return; } // Read fixed + variable header inFile.readSome(aacData, bytesRead, 6); if (bytesRead < 6){ WARN_MSG("Not enough bytes left in buffer to extract a new ADTS frame"); WARN_MSG("Wanted %i bytes but read %li bytes...", 6, bytesRead); WARN_MSG("Header contains bytes: %x %x %x %x %x %x", aacData[0] , aacData[1], aacData[2], aacData[3], aacData[4], aacData[5]); return; } // Confirm syncword (= FFF) if (aacData[0] != 0xFF || (aacData[1] & 0xF0) != 0xF0){ // Check for APE tag (metadata, which we throw for now) if (aacData[0] == 0x41 && aacData[1] == 0x50 && aacData[2] == 0x45 && aacData[3] == 0x54 && aacData[4] == 0x41 && aacData[5] == 0x47){ inFile.readAll(aacData, bytesRead); INFO_MSG("Throwing out %li bytes of metadata...", bytesRead); return; } WARN_MSG("Invalid sync word at start of header"); return; } // Calculate the starting position of the next frame frameSize = (((aacData[3] & 0x03) << 11) | (aacData[4] << 3) | ((aacData[5] >> 5) & 0x07)); nextFramePos += frameSize; // Copy AAC header info aacFrame = (char*)malloc(frameSize); for (int i = 0; i < 6; i++){ aacFrame[i] = aacData[i]; } // Read the rest of the AAC frame inFile.readSome(aacData, bytesRead, frameSize - 6); if (bytesRead < frameSize - 6){ WARN_MSG("Not enough bytes left in buffer."); WARN_MSG("Wanted %li bytes but read %li bytes...", frameSize - 6, bytesRead); disregardAmount = frameSize - 6 - bytesRead; } for (int i = 0; i < (frameSize - 6); i++){ aacFrame[i+6] = aacData[i]; } // Create ADTS object of frame aac::adts adtsPack(aacFrame, frameSize); if (!adtsPack){ WARN_MSG("Could not parse ADTS package!"); WARN_MSG("Current frame info:"); WARN_MSG("Current frame pos: %li", filePos); WARN_MSG("Next frame pos: %li", nextFramePos); WARN_MSG("Frame size expected: %li", frameSize); WARN_MSG("Bytes read: %li", bytesRead); WARN_MSG("ADTS getAACProfile: %li", adtsPack.getAACProfile()); WARN_MSG("ADTS getFrequencyIndex: %li", adtsPack.getFrequencyIndex()); WARN_MSG("ADTS getFrequency: %li", adtsPack.getFrequency()); WARN_MSG("ADTS getChannelConfig: %li", adtsPack.getChannelConfig()); WARN_MSG("ADTS getChannelCount: %li", adtsPack.getChannelCount()); WARN_MSG("ADTS getHeaderSize: %li", adtsPack.getHeaderSize()); WARN_MSG("ADTS getPayloadSize: %li", adtsPack.getPayloadSize()); WARN_MSG("ADTS getCompleteSize: %li", adtsPack.getCompleteSize()); WARN_MSG("ADTS getSampleCount: %li", adtsPack.getSampleCount()); return; } thisPacket.genericFill(timestamp, 0, idx, adtsPack.getPayload(), adtsPack.getPayloadSize() - disregardAmount, filePos, false); //Update the internal timestamp timestamp += (adtsPack.getSampleCount() * 1000) / adtsPack.getFrequency(); filePos = nextFramePos; } // Seeks to the filePos // @param timestamp of the DTSH entry containing required file pos info // @param trackID of the AAC track void inputAAC::seek(uint64_t seekTime, size_t idx){ DTSC::Keys keys(M.keys(idx)); uint32_t keyNum = keys.getNumForTime(seekTime); // We minus the filePos by one, since we init it 1 higher inFile.seek(keys.getBpos(keyNum)-1); timestamp = keys.getTime(keyNum); DONTEVEN_MSG("inputAAC wants to seek to timestamp %li on track %li", seekTime, idx); DONTEVEN_MSG("inputAAC seeked to timestamp %f with bytePos %li", timestamp, keys.getBpos(keyNum)-1); } }// namespace Mist