AAC input
This commit is contained in:
parent
12246581e6
commit
e961d71c16
5 changed files with 363 additions and 0 deletions
|
@ -511,6 +511,8 @@ if(RIST_LIB)
|
||||||
makeInput(TSRIST tsrist with_rist)#LTS
|
makeInput(TSRIST tsrist with_rist)#LTS
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
makeInput(AAC aac)
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
# MistServer - Outputs #
|
# MistServer - Outputs #
|
||||||
########################################
|
########################################
|
||||||
|
|
12
lib/adts.cpp
12
lib/adts.cpp
|
@ -133,6 +133,18 @@ namespace aac{
|
||||||
|
|
||||||
return res.str();
|
return res.str();
|
||||||
}
|
}
|
||||||
|
// Returns Init info used to init DTSC audio track
|
||||||
|
std::string adts::getInit() const{
|
||||||
|
std::string init;
|
||||||
|
init.resize(2);
|
||||||
|
|
||||||
|
init[0] = ((getAACProfile() & 0x1F) << 3) |
|
||||||
|
((getFrequencyIndex() & 0x0E) >> 1);
|
||||||
|
init[1] = ((getFrequencyIndex() & 0x01) << 7) |
|
||||||
|
((getChannelConfig() & 0x0F) << 3);
|
||||||
|
|
||||||
|
return init;
|
||||||
|
}
|
||||||
adts::operator bool() const{
|
adts::operator bool() const{
|
||||||
return hasSync() && len && len >= getHeaderSize() && getFrequency() && getChannelCount() &&
|
return hasSync() && len && len >= getHeaderSize() && getFrequency() && getChannelCount() &&
|
||||||
getSampleCount();
|
getSampleCount();
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace aac{
|
||||||
bool hasSync() const;
|
bool hasSync() const;
|
||||||
char *getPayload();
|
char *getPayload();
|
||||||
std::string toPrettyString() const;
|
std::string toPrettyString() const;
|
||||||
|
std::string getInit() const;
|
||||||
operator bool() const;
|
operator bool() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
321
src/input/input_aac.cpp
Normal file
321
src/input/input_aac.cpp
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
/*
|
||||||
|
* 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 <cerrno>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <mist/defines.h>
|
||||||
|
#include <mist/stream.h>
|
||||||
|
#include <mist/util.h>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/stat.h> //for stat
|
||||||
|
#include <sys/types.h> //for stat
|
||||||
|
#include <unistd.h> //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 <idx> 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 <seekTime> timestamp of the DTSH entry containing required file pos info
|
||||||
|
// @param <idx> 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
|
||||||
|
|
||||||
|
|
||||||
|
|
27
src/input/input_aac.h
Normal file
27
src/input/input_aac.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#include "input.h"
|
||||||
|
#include <mist/dtsc.h>
|
||||||
|
#include <mist/adts.h>
|
||||||
|
#include <mist/urireader.h>
|
||||||
|
|
||||||
|
namespace Mist{
|
||||||
|
class inputAAC : public Input{
|
||||||
|
public:
|
||||||
|
inputAAC(Util::Config *cfg);
|
||||||
|
~inputAAC();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Private Functions
|
||||||
|
bool checkArguments();
|
||||||
|
bool preRun();
|
||||||
|
bool readHeader();
|
||||||
|
void seek(uint64_t seekTime, size_t idx);
|
||||||
|
void getNext(size_t idx = INVALID_TRACK_ID);
|
||||||
|
bool keepRunning();
|
||||||
|
uint64_t lastModTime;
|
||||||
|
HTTP::URIReader inFile;
|
||||||
|
double timestamp;
|
||||||
|
size_t filePos;
|
||||||
|
};
|
||||||
|
}// namespace Mist
|
||||||
|
|
||||||
|
typedef Mist::inputAAC mistIn;
|
Loading…
Add table
Reference in a new issue