shithub: aacenc

ref: d9132d3351fc8fb3a430d47ba469c25d1a46af97
dir: /plugins/cooledit/Faad.cpp/

View raw version
#include <windows.h>
#include <stdio.h>  // FILE *
#include "filters.h" //CoolEdit
#include "faad.h"
#include "faac.h"
#include "aacinfo.h"
#include "..\..\..\faad2\common\mp4v2\mp4.h"


#define MAX_CHANNELS 2


typedef struct input_tag // any special vars associated with input file
{
//AAC
 FILE			*aacFile;
 DWORD			lSize;    
 DWORD			tagsize;
 DWORD			bytes_read;		// from file
 DWORD			bytes_consumed;	// by faadDecDecode
 long			bytes_into_buffer;
 unsigned char	*buffer;

//MP4
 MP4FileHandle	mp4File;
 MP4SampleId	sampleId, numSamples;
 int			track;
 DWORD			type;

// GENERAL
 faacDecHandle	hDecoder;
 faadAACInfo	file_info;
 __int32		len_ms;
 WORD			wChannels;
 DWORD			dwSamprate;
 WORD			wBitsPerSample;
 char			szName[256];
 DWORD			full_size;		// size of decoded file needed to set the length of progress bar
 bool			IsAAC;
} MYINPUT;

static const char* mpeg4AudioNames[]=
{
 "Raw PCM",
 "AAC Main",
 "AAC Low Complexity",
 "AAC SSR",
 "AAC LTP",
 "Reserved",
 "AAC Scalable",
 "TwinVQ",
 "CELP",
 "HVXC",
 "Reserved",
 "Reserved",
 "TTSI",
 "Wavetable synthesis",
 "General MIDI",
 "Algorithmic Synthesis and Audio FX",
 "Reserved"
};

int id3v2_tag(unsigned char *buffer)
{
 if(StringComp((const char *)buffer, "ID3", 3) == 0) 
 {
unsigned long tagsize;

// high bit is not used 
  tagsize=(buffer[6] << 21) | (buffer[7] << 14) |
          (buffer[8] <<  7) | (buffer[9] <<  0);
  tagsize += 10;
  return tagsize;
 }
 else 
  return 0;
}

int GetAACTrack(MP4FileHandle infile)
{
    /* find AAC track */
    int i, rc;
	int numTracks = MP4GetNumberOfTracks(infile, NULL);

	for (i = 0; i < numTracks; i++)
    {
        MP4TrackId trackId = MP4FindTrackId(infile, i, NULL);
        const char* trackType = MP4GetTrackType(infile, trackId);

        if (!strcmp(trackType, MP4_AUDIO_TRACK_TYPE))
        {
            unsigned char *buff = NULL;
            unsigned __int32 buff_size = 0;
			DWORD dummy1_32;
			BYTE dummy2_8, dummy3_8, dummy4_8, dummy5_8, dummy6_8,
                dummy7_8, dummy8_8;
            MP4GetTrackESConfiguration(infile, trackId, &buff, &buff_size);

            if (buff)
            {
                rc = AudioSpecificConfig(buff, &dummy1_32, &dummy2_8, &dummy3_8,
                    &dummy4_8, &dummy5_8, &dummy6_8, &dummy7_8, &dummy8_8);
                free(buff);

                if (rc < 0)
                    return -1;
                return trackId;
            }
        }
    }

    /* can't decode this */
    return -1;
}



// *********************************************************************************************



__declspec(dllexport) BOOL FAR PASCAL FilterUnderstandsFormat(LPSTR filename)
{
WORD len;
 if((len=lstrlen(filename))>4 && 
	(!strcmpi(filename+len-4,".aac") ||
	 !strcmpi(filename+len-4,".mp4")))
  return TRUE;
 return FALSE;
}
// *********************************************************************************************

__declspec(dllexport) long FAR PASCAL FilterGetFileSize(HANDLE hInput)
{
DWORD full_size;

 if(hInput)  
 {
 MYINPUT *mi;
  mi=(MYINPUT *)GlobalLock(hInput);
  full_size=mi->full_size;

  GlobalUnlock(hInput);
 }

 return full_size;
}
// *********************************************************************************************

__declspec(dllexport) DWORD FAR PASCAL FilterOptionsString(HANDLE hInput, LPSTR szString)
{
char buf[20];

 if(hInput)
 {
 MYINPUT *mi;
  mi=(MYINPUT *)GlobalLock(hInput);
 
  lstrcpy(szString,"");

  if(mi->file_info.version == 2)
   lstrcat(szString,"MPEG2 - ");
  else
   lstrcat(szString,"MPEG4 - ");
 
  sprintf(buf,"%lu bps\n",mi->file_info.bitrate);
  lstrcat(szString,buf);
 
  if(mi->IsAAC)  // AAC file --------------------------------------------------------------------
  {
	switch(mi->file_info.headertype)
	{
	case 0:
		lstrcat(szString,"RAW\n");
		return 0L;
	case 1:
		lstrcat(szString,"ADIF\n");
		break;
	case 2:
		lstrcat(szString,"ADTS\n");
		break;
	}

	switch(mi->file_info.object_type)
	{
	case MAIN:
		lstrcat(szString,"Main");
		break;
	case LOW:
		lstrcat(szString,"Low Complexity");
		break;
	case SSR:
		lstrcat(szString,"SSR (unsupported)");
		break;
	case LTP:
		lstrcat(szString,"Main LTP");
		break;
	}
  }
  else  // MP4 file -----------------------------------------------------------------------------
	lstrcat(szString,mpeg4AudioNames[mi->type]);

  GlobalUnlock(hInput);
 }
 return 1;
}
// *********************************************************************************************

__declspec(dllexport) DWORD FAR PASCAL FilterGetFirstSpecialData(HANDLE hInput, 
	SPECIALDATA * psp)
{
return 0L;
}
// *********************************************************************************************
    
__declspec(dllexport) DWORD FAR PASCAL FilterGetNextSpecialData(HANDLE hInput, SPECIALDATA * psp)
{	return 0; // only has 1 special data!  Otherwise we would use psp->hSpecialData
			  // as either a counter to know which item to retrieve next, or as a
			  // structure with other state information in it.
}
// *********************************************************************************************

__declspec(dllexport) void FAR PASCAL CloseFilterInput(HANDLE hInput)
{
 if(hInput)
 {
 MYINPUT far *mi;
	mi=(MYINPUT far *)GlobalLock(hInput);

// AAC file ---------------------------------------------------------------------
	if(mi->aacFile)
		fclose(mi->aacFile);
  
	if(mi->buffer)
		free(mi->buffer);

// MP4 file ---------------------------------------------------------------------
	if(mi->mp4File)
		MP4Close(mi->mp4File);

	if(mi->hDecoder)
		faacDecClose(mi->hDecoder);

	GlobalUnlock(hInput);
	GlobalFree(hInput);
 }
}
// *********************************************************************************************

#define ERROR_OFI(msg) \
{ \
	if(msg) \
		MessageBox(0, msg, "FAAD plugin", MB_OK); \
	if(hInput) \
	{ \
		GlobalUnlock(hInput); \
		CloseFilterInput(hInput); \
	} \
	return 0; \
}

// return handle that will be passed in to close, and write routines
__declspec(dllexport) HANDLE FAR PASCAL OpenFilterInput(LPSTR lpstrFilename, long far *lSamprate, WORD far *wBitsPerSample, WORD far *wChannels, HWND hWnd, long far *lChunkSize)
{
HANDLE					hInput;
MYINPUT					*mi;
faacDecConfigurationPtr	config;
DWORD					samplerate, channels;
DWORD					tmp;
BYTE					BitsPerSample=16;

	hInput=GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE|GMEM_ZEROINIT,sizeof(MYINPUT));
	if(!hInput)
		ERROR_OFI("Memory allocation error: hInput");
	mi=(MYINPUT *)GlobalLock(hInput);
	memset(mi,0,sizeof(MYINPUT));


	mi->IsAAC=strcmpi(lpstrFilename+lstrlen(lpstrFilename)-4,".aac")==0;

	if(!mi->IsAAC) // MP4 file ---------------------------------------------------------------------
	{
	MP4Duration			length;
	int					track;
	unsigned __int32	buffer_size;
	unsigned long		timeScale, sf;
    BYTE dummy1, dummy2, dummy3, dummy4;

		if(!(mi->mp4File = MP4Read(lpstrFilename, 0)))
		    ERROR_OFI("Error opening file");

		if ((track = GetAACTrack(mi->mp4File)) < 0)
			ERROR_OFI("Unable to find correct AAC sound track in the MP4 file");

		if(!(mi->hDecoder=faacDecOpen()))
			ERROR_OFI("Can't init library");

		mi->buffer = NULL;
		buffer_size = 0;
		MP4GetTrackESConfiguration(mi->mp4File, track, &mi->buffer, &buffer_size);
	    if(!mi->buffer)
			ERROR_OFI("MP4GetTrackESConfiguration");

		AudioSpecificConfig(mi->buffer, &timeScale, &channels, &sf, &mi->type, &dummy1,
            &dummy2, &dummy3, &dummy4);
		if(memcmp(mpeg4AudioNames[mi->type],"AAC",3))
			ERROR_OFI(0);
		if(faacDecInit2(mi->hDecoder, mi->buffer, buffer_size, &samplerate, &channels) < 0)
			ERROR_OFI("Error initializing decoder library");

	    free(mi->buffer);

		length = MP4GetTrackDuration(mi->mp4File, track);
		mi->len_ms=(DWORD) MP4ConvertFromTrackDuration(mi->mp4File, track, length, MP4_MSECS_TIME_SCALE);
		mi->file_info.bitrate=(int)MP4GetTrackIntegerProperty(mi->mp4File, track, "mdia.minf.stbl.stsd.mp4a.esds.decConfigDescr.avgBitrate");
		mi->numSamples = MP4GetTrackNumberOfSamples(mi->mp4File, track);

		mi->track=track;
		mi->sampleId=1;
	}
	else // AAC file ------------------------------------------------------------------------------
	{   
	DWORD	pos; // into the file. Needed to obtain length of file
	DWORD	read;
	int		*seek_table;
	long	tagsize;

		if(!(mi->aacFile=fopen(lpstrFilename,"rb")))
			ERROR_OFI("Error opening file"); 

		pos=ftell(mi->aacFile);
		fseek(mi->aacFile, 0, SEEK_END);
		mi->lSize=ftell(mi->aacFile);
		fseek(mi->aacFile, pos, SEEK_SET);

		if(!(mi->buffer=(BYTE *)malloc(768*MAX_CHANNELS)))
			ERROR_OFI("Memory allocation error"); 
		memset(mi->buffer, 0, 768*MAX_CHANNELS);

		if(mi->lSize<768*MAX_CHANNELS)
			tmp=mi->lSize;
		else
			tmp=768*MAX_CHANNELS;
		read=fread(mi->buffer, 1, tmp, mi->aacFile);
		if(read==tmp)
		{
			mi->bytes_read=read;
			mi->bytes_into_buffer=read;
		}
		else
			ERROR_OFI("fread");

		tagsize=id3v2_tag(mi->buffer);
		if(tagsize)
		{
			memcpy(mi->buffer,mi->buffer+tagsize,768*MAX_CHANNELS - tagsize);

			if(mi->bytes_read+tagsize<mi->lSize)
				tmp=tagsize;
			else
				tmp=mi->lSize-mi->bytes_read;
			read=fread(mi->buffer+mi->bytes_into_buffer, 1, tmp, mi->aacFile);
			if(read==tmp)
			{
				mi->bytes_read+=read;
				mi->bytes_into_buffer+=read;
			}
			else
				ERROR_OFI("fread");
		}
		mi->tagsize=tagsize;

		if(!(mi->hDecoder=faacDecOpen()))
			ERROR_OFI("Can't open library");

		if(seek_table=(int *)malloc(sizeof(int)*10800))
		{
			if(get_AAC_format(lpstrFilename, &(mi->file_info), seek_table)<0)
				ERROR_OFI("Error retrieving information form input file");
			free(seek_table);
		}
		if(mi->file_info.headertype==0)
		{
			config = faacDecGetCurrentConfiguration(mi->hDecoder);
			config->defObjectType = mi->file_info.object_type;
			config->defSampleRate = mi->file_info.sampling_rate;
			config->outputFormat=FAAD_FMT_16BIT;
			faacDecSetConfiguration(mi->hDecoder, config);
		}

		if((mi->bytes_consumed=faacDecInit(mi->hDecoder, mi->buffer, &samplerate, &channels)) < 0)
			ERROR_OFI("Can't init library");
		mi->bytes_into_buffer-=mi->bytes_consumed;
// if(mi->bytes_consumed>0) faacDecInit reports there is an header to skip
// this operation will be done in ReadFilterInput

		mi->len_ms=(DWORD)((1000*((float)mi->lSize*8))/mi->file_info.bitrate);
	} // END AAC file -----------------------------------------------------------------------------

	config = faacDecGetCurrentConfiguration(mi->hDecoder);
	switch(config->outputFormat)
	{
	case FAAD_FMT_16BIT:
		BitsPerSample=16;
		break;
	case FAAD_FMT_24BIT:
		BitsPerSample=24;
		break;
	case FAAD_FMT_32BIT:
		BitsPerSample=32;
		break;
	default:
		ERROR_OFI("Invalid format");
	}

	if(mi->len_ms)
		mi->full_size=(DWORD)(mi->len_ms*((float)samplerate/1000)*channels*(BitsPerSample/8));
	else
		mi->full_size=mi->lSize; // corrupted stream?

	*lSamprate=samplerate;
	*wBitsPerSample=BitsPerSample;
	*wChannels=(WORD)channels;
	*lChunkSize=(BitsPerSample/8)*1024*channels;

	mi->wChannels=(WORD)channels;
	mi->dwSamprate=samplerate;
	mi->wBitsPerSample=*wBitsPerSample;
	strcpy(mi->szName,lpstrFilename);

	GlobalUnlock(hInput);

	return hInput;
}
// *********************************************************************************************

#define ERROR_RFI(msg) \
{ \
	if(msg) \
		MessageBox(0, msg, "FAAD plugin", MB_OK); \
	if(hInput) \
		GlobalUnlock(hInput); \
	return 0; \
}

__declspec(dllexport) DWORD FAR PASCAL ReadFilterInput(HANDLE hInput, unsigned char far *bufout, long lBytes)
{
DWORD				read,
					tmp,
					shorts_decoded=0;
unsigned char		*buffer=0;
faacDecFrameInfo	frameInfo;
char				*sample_buffer=0;
MYINPUT				*mi;

	if(!hInput)
		ERROR_RFI("Memory allocation error: hInput");
	mi=(MYINPUT *)GlobalLock(hInput);

	if(!mi->IsAAC) // MP4 file --------------------------------------------------------------------------
	{   
	unsigned __int32 buffer_size=0;
    int rc;

		do
		{
			buffer=NULL;
			if(mi->sampleId>=mi->numSamples)
				ERROR_RFI(0);

			rc=MP4ReadSample(mi->mp4File, mi->track, mi->sampleId++, &buffer, &buffer_size, NULL, NULL, NULL, NULL);
			if(rc==0 || buffer==NULL)
			{
				if(buffer) free(buffer);
				ERROR_RFI("MP4ReadSample")
			}

			sample_buffer=(char *)faacDecDecode(mi->hDecoder,&frameInfo,buffer);
			shorts_decoded=frameInfo.samples*sizeof(short);
			memcpy(bufout,sample_buffer,shorts_decoded);
			if (buffer) free(buffer);
		}while(!shorts_decoded && !frameInfo.error);
	}
	else // AAC file --------------------------------------------------------------------------
	{   
		buffer=mi->buffer;
		do
		{
			if(mi->bytes_consumed>0)
			{
				if(mi->bytes_into_buffer)
					memcpy(buffer,buffer+mi->bytes_consumed,mi->bytes_into_buffer);

				if(mi->bytes_read<mi->lSize)
				{
					if(mi->bytes_read+mi->bytes_consumed<mi->lSize)
						tmp=mi->bytes_consumed;
					else
						tmp=mi->lSize-mi->bytes_read;
					read=fread(buffer+mi->bytes_into_buffer, 1, tmp, mi->aacFile);
					if(read==tmp)
					{
						mi->bytes_read+=read;
						mi->bytes_into_buffer+=read;
					}	
				}
				else
					if(mi->bytes_into_buffer)
						memset(buffer+mi->bytes_into_buffer, 0, mi->bytes_consumed);

				mi->bytes_consumed=0;
			}

			if(mi->bytes_into_buffer<1)
				if(mi->bytes_read<mi->lSize)
					ERROR_RFI("ReadFilterInput: buffer empty!")
				else
					return 0;

			sample_buffer=(char *)faacDecDecode(mi->hDecoder,&frameInfo,buffer);
			shorts_decoded=frameInfo.samples*sizeof(short);
			memcpy(bufout,sample_buffer,shorts_decoded);
		    mi->bytes_consumed +=frameInfo.bytesconsumed;
			mi->bytes_into_buffer-=mi->bytes_consumed;
		}while(!shorts_decoded && !frameInfo.error);
	} // END AAC file --------------------------------------------------------------------------

	GlobalUnlock(hInput);

	if(frameInfo.error)
		ERROR_RFI(faacDecGetErrorMessage(frameInfo.error));

	return shorts_decoded;
}