~ft/aacdec

e9b4be45b8eb828ddd32a5a1c0b1e1f7f921c624 — menno 17 years ago 9cb5698
new QCD plugin code by shaohao
D plugins/QCDMp4/AAC2Mp4Enc.c => plugins/QCDMp4/AAC2Mp4Enc.c +0 -270
@@ 1,270 0,0 @@
//-----------------------------------------------------------------------------
//
// File:	QCDEncodeDLL.cpp
//
// About:	See QCDOutputDLL.h
//
// Authors:	Written by Paul Quinn
//
//	QCD multimedia player application Software Development Kit Release 1.0.
//
//	Copyright (C) 1997-2002 Quinnware
//
//	This code is free.  If you redistribute it in any form, leave this notice 
//	here.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
//-----------------------------------------------------------------------------

#include <windows.h>
#include <shlobj.h>
#include <mp4.h>

#include "QCDInputDLL.h"
#include "QCDConvertDLL.h"
#include "resource.h"
#include "utils.h"
#include "config.h"
#include "aac2mp4.h"

HWND			hwndConfigEnc;
QCDModInitEnc	*QCDCallbacks_Enc;

BOOL CALLBACK config_convert_proc(HWND hwndDlg, UINT message,
                                 WPARAM wParam, LPARAM lParam);

static char m_output_folder[MAX_PATH];// our output folder

void config_enc_read()
{
	char output_folder[MAX_PATH];
	output_folder[0] = 0;

    RS(output_folder);

	lstrcpy(m_output_folder, output_folder);
}

void config_enc_write()
{
	char output_folder[MAX_PATH];
	lstrcpy(output_folder, m_output_folder);

	WS(output_folder);
}

BOOL isFolder(char *folder)
{
	if(lstrlen(folder) > 0 && folder[1] == ':')
		return TRUE;
	else
		return FALSE;
}

//-----------------------------------------------------------------------------

PLUGIN_API BOOL ENCODEDLL_ENTRY_POINT(QCDModInitEnc *ModInit, QCDModInfo *ModInfo)
{
	ModInit->version = PLUGIN_API_VERSION;
	ModInfo->moduleString = "AAC to Mp4 Converter v1.0";

	ModInit->toModule.ShutDown				= ShutDown_Enc;
	ModInit->toModule.Open					= Open_Enc;
	ModInit->toModule.Write					= Write_Enc;
	ModInit->toModule.Stop					= Stop_Enc;
	ModInit->toModule.GetCurrentPosition	= GetCurrentPosition_Enc;
	ModInit->toModule.Configure				= Configure_Enc;
	ModInit->toModule.About					= About_Enc;

/* encoders can handle these calls if they wish,
 * but they are generally not needed

	ModInit->toModule.Flush				= Flush;
	ModInit->toModule.Pause				= Pause;
	ModInit->toModule.Drain				= Drain;
	ModInit->toModule.SetVolume			= SetVolume;
	ModInit->toModule.DrainCancel		= DrainCancel;

*/

	QCDCallbacks_Enc = ModInit;

	hwndPlayer = (HWND)QCDCallbacks_Enc->Service(opGetParentWnd, 0, 0, 0);

	QCDCallbacks_Enc->Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0);

	//
	// TODO: all your plugin initialization here
	//
	config_enc_read();

	
	// return TRUE for successful initialization
	return TRUE;
}

//-----------------------------------------------------------------------------

void ShutDown_Enc(int flags)
{
	Stop_Enc(STOPFLAG_FORCESTOP);
}

//-----------------------------------------------------------------------------

BOOL Open_Enc(LPCSTR medianame, WAVEFORMATEX *wf)
{
	char mp4FileName[256];
	
	if(StringComp(strrchr(medianame, '.'), ".aac", 4))
	{
		MessageBox(hwndPlayer, "Only AAC files can be converted into Mp4 Files!", "File Type Error", MB_OK);
		return FALSE;
	}
	else if(!isFolder(m_output_folder) && MessageBox(hwndPlayer, "You should select a output folder!", "Error", MB_OK) == IDOK )
	{
		if(!IsWindow(hwndConfigEnc))
			hwndConfigEnc = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_CONFIG_CONVERT), 
				hwndPlayer, config_convert_proc);
		ShowWindow(hwndConfigEnc, SW_SHOW);
		return FALSE;
	}
	else
	{
		lstrcpy(mp4FileName, m_output_folder); // copy the folder path
		lstrcat(mp4FileName, strrchr(medianame, '\\')); //copy the file name
		lstrcpy(strrchr(mp4FileName, '.'), ".mp4"); // rename the file name to .mp4

		if(covert_aac_to_mp4(medianame, mp4FileName))
		{
			MessageBox(hwndPlayer, "An error occured while converting AAC to MP4!", "An error occured!", MB_OK);
			return FALSE;
		}
		else
		{
			QCDCallbacks_Enc->Service(opSetStatusMessage, "Converting OK", TEXT_TOOLTIP, 0);

			return TRUE;
		}
	}
}

//-----------------------------------------------------------------------------

BOOL Write_Enc(WriteDataStruct* writeData)
{
	//
	// TODO : Use the raw wave audio data passed from the player.
	//
	// Return value - one of the follwing:
	//
	// TRUE		- write succeeded
	// FALSE	- write failed
	//
	// Note: this call can block (eg: to wait for available space to write)

	return TRUE;
}

//-----------------------------------------------------------------------------

BOOL Stop_Enc(int flags)
{
	//
	// TODO : Stop and close the output.
	//
	// Return TRUE for success, FALSE for failure

	return TRUE;
}

//-----------------------------------------------------------------------------

BOOL GetCurrentPosition_Enc(UINT *position, int flags)
{
	// 
	// TODO : set position to exact current playing position
	// returned position needs to be latest marker sent to Write
	// return TRUE for success

	return TRUE;
}

//-----------------------------------------------------------------------------

BOOL CALLBACK config_convert_proc(HWND hwndDlg, UINT message,
                                 WPARAM wParam, LPARAM lParam)
{
	char str_buffer[MAX_PATH];

	switch (message) 
	{
    case WM_INITDIALOG:
		if(isFolder(m_output_folder))// is a driver folder
			SetDlgItemText(hwndDlg, IDC_OUTPUTFOLDER, m_output_folder);
		else
			SetDlgItemText(hwndDlg, IDC_OUTPUTFOLDER, "Select Output Folder");
        return TRUE;

    case WM_COMMAND:
        switch (LOWORD(wParam))
		{
		case IDC_OUTPUTFOLDER:
			{
				char name[MAX_PATH];
				BROWSEINFO bi;
				ITEMIDLIST *idlist;
				bi.hwndOwner = hwndDlg;
				bi.pidlRoot = 0;
				bi.pszDisplayName = name;
				bi.lpszTitle = "Select a directory for saving mp4 files converted from aac files: ";
				bi.ulFlags = BIF_RETURNONLYFSDIRS /*| BIF_USENEWUI*/;
				bi.lpfn = NULL;
				bi.lParam = 0;
				
				idlist = SHBrowseForFolder( &bi );
				if(idlist)
				{
					SHGetPathFromIDList( idlist, m_output_folder);
					SetDlgItemText(hwndDlg, IDC_OUTPUTFOLDER, m_output_folder);
				}
				return TRUE;
			}
        case IDOK:
			GetDlgItemText(hwndDlg, IDC_OUTPUTFOLDER, str_buffer, MAX_PATH);
			if(isFolder(str_buffer)) // is a driver foder
				lstrcpy(m_output_folder, str_buffer);
			else
				m_output_folder[0] = 0;

            /* save config */
            config_enc_write();
		default:
			/* close the dialogbox */
            DestroyWindow(hwndDlg);
            return TRUE;
        }
    }
    return FALSE;
}

void Configure_Enc(int flags)
{
	if(!IsWindow(hwndConfigEnc))
		hwndConfigEnc = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_CONFIG_CONVERT), 
			hwndPlayer, config_convert_proc);
	ShowWindow(hwndConfigEnc, SW_SHOWNORMAL);
}

//-----------------------------------------------------------------------------

void About_Enc(int flags)
{
	if(!IsWindow(hwndAbout))
		hwndAbout = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_ABOUT), 
			hwndPlayer, about_dialog_proc);
	ShowWindow(hwndAbout, SW_SHOWNORMAL);
}

D plugins/QCDMp4/QCDConvertDLL.h => plugins/QCDMp4/QCDConvertDLL.h +0 -40
@@ 1,40 0,0 @@
//-----------------------------------------------------------------------------
// 
// File:	QCDEncodeDLL.h
//
// About:	QCD Player Output module DLL interface.  For more documentation, see
//			QCDModOutput.h.
//
// Authors:	Written by Paul Quinn
//
//	QCD multimedia player application Software Development Kit Release 1.0.
//
//	Copyright (C) 1997-2002 Quinnware
//
//	This code is free.  If you redistribute it in any form, leave this notice 
//	here.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
//-----------------------------------------------------------------------------

#include "QCDModEncode.h"

#ifndef QCDENCODEDLL_H
#define QCDENCODEDLL_H

extern QCDModInitEnc	*QCDCallbacks_Enc;

// Calls from the Player
void ShutDown_Enc(int flags);
BOOL Open_Enc(LPCSTR, WAVEFORMATEX *wf);
BOOL Write_Enc(WriteDataStruct*);
BOOL Stop_Enc(int flags);
BOOL GetCurrentPosition_Enc(UINT *position, int flags);

void Configure_Enc(int flags);
void About_Enc(int flags);

#endif //QCDOUTPUTDLL_H
\ No newline at end of file

M plugins/QCDMp4/QCDInputDLL.h => plugins/QCDMp4/QCDInputDLL.h +1 -7
@@ 25,10 25,6 @@

#include "QCDModInput.h"

extern HINSTANCE		hInstance;
extern HWND				hwndPlayer, hwndAbout;
extern QCDModInitIn		sQCDCallbacks, *QCDCallbacks;

// Calls from the Player
int  GetMediaSupported(const char* medianame, MediaInfo *mediaInfo);
int  GetTrackExtents(const char* medianame, TrackExtents *ext, int flags);


@@ 47,6 43,4 @@ void ShutDown(int flags);
void Configure(int flags);
void About(int flags);

#endif //QCDInputDLL_H

INT_PTR CALLBACK about_dialog_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
#endif //QCDInputDLL_H
\ No newline at end of file

M plugins/QCDMp4/QCDModDefs.h => plugins/QCDMp4/QCDModDefs.h +69 -11
@@ 25,6 25,7 @@
#ifndef QCDMODDEFS_H
#define QCDMODDEFS_H

#include <mmreg.h>
#include <windows.h>

#ifdef __cplusplus


@@ 34,14 35,20 @@
#endif

// Current plugin version
#define PLUGIN_API_VERSION 250

// use this version for old style API calls (all returned text in native encoding)
#define PLUGIN_API_VERSION				250		

// use this version for new style API calls (all returned text in UTF8 encoding on WinNT/2K/XP (native encoding on Win9x))
#define PLUGIN_API_VERSION_WANTUTF8		((PLUGIN_API_WANTUTF8<<16)|PLUGIN_API_VERSION)
#define PLUGIN_API_WANTUTF8				100

//-----------------------------------------------------------------------------

typedef struct {
typedef struct 
{
	char				*moduleString;
	char				*moduleExtensions;

} QCDModInfo;

//-----------------------------------------------------------------------------


@@ 60,7 67,7 @@ typedef enum
	opGetNextIndex = 12,			// get index of next track to play (0 based), param1 = index start index. -1 for after current
	opGetTrackNum = 13,				// get track number of index, param1 = index of track in playlist, -1 for current
									//		- 'track number' is the number of the track in it's respective album, as opposed to playlist number
									//		- the 'track number' for digital files will be 1, unless they are tagged with the CDDB identifier
									//		- the 'track number' for digital files will be 1 if the tag is not set or the file is not identified

	opGetTrackLength = 14,			// get track length, param1 = index of track in playlist, -1 for current
									//                   param2 = 0 for seconds, 1 for milliseconds


@@ 88,7 95,7 @@ typedef enum

	opGetIndexFromPLNum = 28,		// get index from playlist number, param1 = playlist number

	opGetChildWnd = 30,				// handle to the draggable window extension (only available on some skins) 
	opGetExtensionWnd = 30,			// handle to the draggable window extension (only available on some skins), param1 = extension number (0 - 9)
	opGetExtVisWnd = 31,			// handle to the external visual window
	opGetMusicBrowserWnd = 32,		// handle to the music browser window 
	opGetSkinPreviewWnd = 33,		// handle to the skin preview window 


@@ 97,10 104,13 @@ typedef enum
	opGetAboutWnd = 36,				// handle to the about window 
	opGetSegmentsWnd = 37,			// handle to the segments window 
	opGetEQPresetsWnd = 38,			// handle to the EQ presets window 
	opGetVideoWnd = 39,				// handle to the video window 

	opGetVisDimensions = 50,		// gets the width and height of visual window (param1 = -1 current vis window, 0 internal vis, 1 external vis, 2 full screen)
									//		returns: HEIGHT in high word, WIDTH in low word 

	opShowVideoWindow = 55,			// Show or Close video window (param1 = 1 for create, 2 for create and show, 0 for close)

	opGetQueriesComplete = 60,		// get status on whether all tracks in playlist have been queryied for their info

									// playlist manipulation


@@ 108,9 118,9 @@ typedef enum
	opSelectIndex = 91,				// mark index as selected (param1 = index, param2 = 1 - set, 0 - unset)
	opBlockIndex = 92,				// mark index as blocked (param1 = index, param2 = 1 - set, 0 - unset)

	opGetMediaInfo = 99,			// get the CddbDisc object for the index specified, param1 = index of track, -1 for current
	opGetMediaInfo = 99,			// get the ICddbDisc object for the index specified, param1 = index of track, -1 for current
									//		param2 = pointer to integer that receives track value
									//		returns: pointer to CddbDisc object. Do not release or deallocate this pointer
									//		returns: pointer to ICddbDisc object. Do not release or deallocate this pointer


   									//*** below returns string info in buffer, param1 = size of buffer


@@ 141,6 151,7 @@ typedef enum
	opGetSupportedExtensions = 116,	// get file extensions supported by the player, param2 = 0 - get all extensions, 1 - get registered extensions
									//		- returned extensions will be colon delimited

	opGetPlaylistString = 117,		// get string for index as it appears in playlist, param2 = index

   									//*** below buffer points to struct or other object
   									//*** returns 1 on success, 0 on failure


@@ 149,6 160,8 @@ typedef enum
	opGetMainMenu = 121,			// Returns copy of HMENU handle to QCD Menu (must use DestroyMenu on handle when complete)

	opShowQuickTrack = 125,			// Display QuickTrack Menu (buffer = POINT* - location to display menu)
	opGetQuickTrack = 126,			// Returns copy of HMENU handle to QuickTrack menu (must use DestroyMenu on handle when complete)
									//		To use if QuickTrack item selected: PostMessage(hwndPlayer, WM_COMMAND, menu_id, 0);

	opGetEQVals = 200,				// get current EQ levels/on/off (buffer = EQInfo*)
	opSetEQVals = 201,				// set EQ levels/on/off (buffer = EQInfo*)


@@ 196,13 209,17 @@ typedef enum
	opMovePlaylistTrack = 1012,		// param1 = index of track to move, param2 = destination index (move shifts tracks between param1 and param2)
	opSwapPlaylistTracks = 1013,	// param1 = index of first track, param2 = index of second track (swap only switches indecies param1 and param2)


	opCreateDiscInfo = 1020,		// returns: pointer to ICddbDisc object. Do not release or deallocate this pointer
	opSetDiscInfo = 1021,			// buffer = ICddbDisc*, param1 = MediaInfo*, param2 = track number

	opSetSeekPosition = 1100,		// seek to position during playback
									//		buffer = NULL, param1 = position
									//		param2 = 0 - position is in seconds, 1 - position is in milliseconds, 2 - position is in percent (use (float)param1))


	opSetRepeatState = 1110,		// set playlist repeat state, buffer = NULL, param1 = 0 - off, 1 - repeat track, 2 - repeat playlist
	opSetShuffleState = 1111,		// set playlist shuffle state, buffer = NULL, param1 = 0 - off, 1 - on

									//*** below configures custom plugin menu items for the 'plugin menu'
									//*** Player will call plugin's configure routine with menu value when menu item selected
									//*** returns 1 on success, 0 on failure


@@ 213,8 230,18 @@ typedef enum
	opSetPluginMenuState = 2001,	// buffer = HINSTANCE of plugin, param1 = item id, param2 = menu flags (same as windows menu flags - eg: MF_CHECKED)


									//*** below are services for using the player's filename template editor
									//*** returns 1 on success, 0 on failure

	opShowTemplateEditor = 2100,	// displays template editor dialog, param1 = (HWND)parent window, param2 = modal flag
	opLoadTemplate = 2101,			// loads saved templates, buffer = (char*)string buf, param1 = bufsize, param2 = index of template (index < 0 for default formats, index >= 0 for user made formats)
	opRenderTemplate = 2102,		// create string based on template, buffer = (char*)template, param1 = FormatMetaInfo*, param2 = (char*)string buffer (min 260 bytes)

									//*** other services

	opUTF8toUCS2 = 9000,			// convert UTF8 string to UCS2 (Unicode) string, buffer = null terminated utf8 string, param1 = (WCHAR*)result string buffer, param2 = size of result buffer
	opUCS2toUTF8 = 9001,			// convert UCS2 (Unicode) string to UTF8 string, buffer = null terminated ucs2 string, param1 = (char*)result string buffer, param2 = size of result buffer

	opSafeWait = 10000				// plugin's can use this to wait on an object without worrying about deadlocking the player.
									// this should only be called by the thread that enters the plugin, not by any plugin-created threads



@@ 225,6 252,10 @@ typedef enum
//-----------------------------------------------------------------------------
typedef long (*PluginServiceFunc)(PluginServiceOp op, void *buffer, long param1, long param2);

// Use to retrieve service func for DSP plugins (or other inproc process that doesn't have access to PluginServiceFunc)
// Eg: PluginServiceFunc Service = (PluginServiceFunc)SendMessage(hwndPlayer, WM_GETSERVICEFUNC, 0, 0);
// Set WPARAM = PLUGIN_API_WANTUTF8 for UTF8 string parameters
#define WM_GETSERVICEFUNC			(WM_USER + 1)

//-----------------------------------------------------------------------------
typedef struct				// for Output Plugin Write callback


@@ 263,6 294,7 @@ typedef struct			// for opSetAudioInfo service
    long bitrate;		// audio bitrate in bits per second
    long frequency;		// audio freq in Hz
    long mode;			// 0 for stereo, 1 for joint-stereo, 2 for dual-channel, 3 for mono, 4 for multi-channel
	char text[8];		// up to eight characters to identify format (will override level and layer settings)
} AudioInfo;

//-----------------------------------------------------------------------------


@@ 316,6 348,21 @@ typedef struct

} MediaInfo;

//-----------------------------------------------------------------------------
typedef struct
{
	long	struct_size;
	LPCWSTR	title;
	LPCWSTR	artalb;
	LPCWSTR	album;
	LPCWSTR	genre;
	LPCWSTR	year;
	LPCWSTR	tracknum;
	LPCWSTR	filename;
	LPCWSTR	arttrk;
	long	reserved;

} FormatMetaInfo;

//-----------------------------------------------------------------------------
// When subclassing the parent window, a plugin can watch for these messages


@@ 328,8 375,8 @@ typedef struct
#define WM_PN_PLAYSTOPPED		(WM_USER + 102) // playback has stopped by user
#define WM_PN_PLAYPAUSED		(WM_USER + 103) // playback has been paused
#define WM_PN_PLAYDONE			(WM_USER + 104) // playback has finished (track completed)
#define WM_PN_MEDIAEJECTED		(WM_USER + 105) // a CD was ejected (lParam = (LPCSTR)medianame)
#define WM_PN_MEDIAINSERTED		(WM_USER + 106) // a CD was inserted (lParam = (LPCSTR)medianame)
#define WM_PN_MEDIAEJECTED		(WM_USER + 105) // a CD was ejected (CDRom drive letter= 'A' + lParam)
#define WM_PN_MEDIAINSERTED		(WM_USER + 106) // a CD was inserted (CDRom drive letter= 'A' + lParam)
#define WM_PN_INFOCHANGED		(WM_USER + 107) // track information was updated (lParam = (LPCSTR)medianame)
#define WM_PN_TRACKCHANGED		(WM_USER + 109)	// current track playing has changed (relevant from CD plugin) (lParam = (LPCSTR)medianame)



@@ 340,6 387,17 @@ typedef struct
// (so you can get handle, modify, and display your own)
#define WM_SHOWMAINMENU			(WM_USER + 20)

// For intercepting skinned border window commands
#define WM_BORDERWINDOW			(WM_USER + 26)
// WM_BORDERWINDOW	wParam's
#define BORDERWINDOW_NORMALSIZE			0x100000
#define BORDERWINDOW_DOUBLESIZE			0x200000
#define BORDERWINDOW_FULLSCREEN			0x400000

// send to border window to cause resize
// wParam = LPPOINT lpp; // point x-y is CLIENT area size of window
#define WM_SIZEBORDERWINDOW		(WM_USER + 1)

//-----------------------------------------------------------------------------
// To shutdown player, send this command
#define WM_SHUTDOWN				(WM_USER + 5)


@@ 350,6 408,6 @@ typedef struct
#define TEXT_TOOLTIP		0x1		// message acts as tooltip in status window
#define TEXT_URGENT			0x2		// forces message to appear even if no status window (using msg box)
#define TEXT_HOLD			0x4		// tooltip message stays up (no fade out)

#define TEXT_UNICODE		0x10	// buffer contains a unicode string (multibyte string otherwise)

#endif //QCDMODDEFS_H
\ No newline at end of file

D plugins/QCDMp4/QCDModEncode.h => plugins/QCDMp4/QCDModEncode.h +0 -76
@@ 1,76 0,0 @@
//-----------------------------------------------------------------------------
//
// File:	QCDModEncode.h
//
// About:	Encode plugin module interface.  This file is published with the 
//			Encode plugin SDK.
//
// Authors:	Written by Paul Quinn
//
// Copyright:
//
//	QCD multimedia player application Software Development Kit Release 1.0.
//
//	Copyright (C) 1997-2002 Quinnware
//
//	This code is free.  If you redistribute it in any form, leave this notice 
//	here.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
//-----------------------------------------------------------------------------

#ifndef QCDMODENCODE_H
#define QCDMODENCODE_H

#include "QCDModDefs.h"

// name of the DLL export for encode plugins
#define ENCODEDLL_ENTRY_POINT	QEncodeModule

// Stop will receive one of these flags
#define STOPFLAG_FORCESTOP		0
#define STOPFLAG_PLAYDONE		1

// pause flags
#define PAUSE_DISABLED			0	// Pause() call is to unpause playback
#define PAUSE_ENABLED			1	// Pause() call is to pause playback

//-----------------------------------------------------------------------------

typedef struct 
{
	UINT				size;			// size of init structure
	UINT				version;		// plugin structure version (set to PLUGIN_API_VERSION)
	PluginServiceFunc	Service;		// player supplied services callback

	struct
	{
		void *dummy;
		void (*PositionUpdate)(UINT marker);
		void *Reserved[2];
	} toPlayer;

	struct 
	{
		BOOL (*Open)(LPCSTR, WAVEFORMATEX *wf);
		BOOL (*Write)(WriteDataStruct*);
		BOOL (*Flush)(UINT marker);
		BOOL (*Stop)(int flags);
		BOOL (*Pause)(int flags);
		BOOL (*Drain)(int flags);
		void (*ShutDown)(int flags);

		void (*Configure)(int flags);
		void (*About)(int flags);

		BOOL (*SetVolume)(int levelleft, int levelright, int flags);
		BOOL (*GetCurrentPosition)(UINT *position, int flags);
		BOOL (*DrainCancel)(int flags);
		void *Reserved[8];
	} toModule;
} QCDModInitEnc;

#endif //QCDMODENCODE_H
\ No newline at end of file

A plugins/QCDMp4/QCDModTagEditor.h => plugins/QCDMp4/QCDModTagEditor.h +84 -0
@@ 0,0 1,84 @@
//-----------------------------------------------------------------------------
//
// File:	QCDModTagEditor
//
// About:	Tag Editing plugin module interface.  This file is published with the 
//			QCD plugin SDK.
//
// Authors:	Written by Paul Quinn
//
// Copyright:
//
//	QCD multimedia player application Software Development Kit Release 1.0.
//
//	Copyright (C) 2002 Quinnware
//
//	This code is free.  If you redistribute it in any form, leave this notice 
//	here.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
//-----------------------------------------------------------------------------

#ifndef QCDMODTAGEDITOR_H
#define QCDMODTAGEDITOR_H

#include "QCDModDefs.h"

// name of the DLL export for output plugins
#define TAGEDITORDLL_ENTRY_POINT	QTagEditorModule

// Tag field ids
typedef enum
{
	TAGFIELD_FIRSTFIELD = 0,

	TAGFIELD_TITLE = 0,
	TAGFIELD_ARTIST,
	TAGFIELD_ALBUM, 
	TAGFIELD_GENRE,
	TAGFIELD_YEAR,  
	TAGFIELD_TRACK, 
	TAGFIELD_COMMENT,

	TAGFIELD_COMPOSER,
	TAGFIELD_CONDUCTOR,
	TAGFIELD_ORCHESTRA,
	TAGFIELD_YEARCOMPOSED,

	TAGFIELD_ORIGARTIST,
	TAGFIELD_LABEL, 
	TAGFIELD_COPYRIGHT,
	TAGFIELD_ENCODER,
	TAGFIELD_CDDBTAGID,

	TAGFIELD_FIELDCOUNT
};

//-----------------------------------------------------------------------------

typedef struct 
{
	UINT	size;			// size of init structure
	UINT	version;		// plugin structure version (set to PLUGIN_API_VERSION)

	LPCSTR	description;
	LPCSTR	defaultexts;

	bool	(*Read)(LPCSTR filename, void* tagHandle);
	bool	(*Write)(LPCSTR filename, void* tagHandle);
	bool	(*Strip)(LPCSTR filename);

	void	(*ShutDown)(int flags);

	void	(*SetFieldA)(void* tagHandle, int fieldId, LPCSTR szNewText);
	void	(*SetFieldW)(void* tagHandle, int fieldId, LPCWSTR szNewText);

	LPCSTR	(*GetFieldA)(void* tagHandle, int fieldId);
	LPCWSTR	(*GetFieldW)(void* tagHandle, int fieldId);

} QCDModInitTag;

#endif //QCDMODTAGEDITOR_H
\ No newline at end of file

M plugins/QCDMp4/QCDMp4.c => plugins/QCDMp4/QCDMp4.c +2088 -776
@@ 1,6 1,6 @@
/*
** FAAD - Freeware Advanced Audio Decoder
** Copyright (C) 2002 M. Bakker
** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
** Copyright (C) 2003 M. Bakker, Ahead Software AG, http://www.nero.com
**  
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by


@@ 16,26 16,35 @@
** along with this program; if not, write to the Free Software 
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
** $Id: QCDMp4.c,v 1.2 2003/05/07 18:30:49 menno Exp $
** Any non-GPL usage of this software or parts of this software is strictly
** forbidden.
**
** Commercial non-GPL licensing of this software is possible.
** For more info contact Ahead Software through Mpeg4AAClicense@nero.com.
**
** $Id: QCDMp4.c,v 1.3 2003/11/16 19:52:41 menno Exp $
**/

//#define DEBUG_OUTPUT
#include "QCDInputDLL.h"

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <shellapi.h>
#include <stdlib.h>
#include <math.h>
#include <faad.h>
#include <mp4.h>

#include "resource.h"
#include "QCDInputDLL.h"
#include "utils.h"
#include "config.h"
#include "aacinfo.h"
//#include "aac2mp4.h"
//
//const char *long_ext_list = "MP4\0MPEG-4 Files (*.MP4)\0M4A\0MPEG-4 Files (*.M4A)\0AAC\0AAC Files (*.AAC)\0";
//const char *short_ext_list = "MP4\0MPEG-4 Files (*.MP4)\0M4A\0MPEG-4 Files (*.M4A)\0";

static long priority_table[] = {
    0,


@@ 50,15 59,27 @@ static int res_id_table[] = {
    IDC_24BITS,
    IDC_32BITS,
    0,
    IDC_16BITS_DITHERED
    0,
    /*IDC_16BITS_DITHERED*/ IDC_16BITS /* temp hack */
};
static int res_table[] = {
    16,
    24,
    32,
    0,
    0,
    16
};
//static char info_fn[_MAX_PATH];

// post this to the main window at end of file (after playback has stopped)
//#define WM_WA_AAC_EOF WM_USER+2

struct seek_list
{
    struct seek_list *next;
    __int64 offset;
};

typedef struct state
{


@@ 66,12 87,13 @@ typedef struct state
    faacDecHandle hDecoder;
    int samplerate;
    unsigned char channels;
    int decode_pos_ms; // current decoding position, in milliseconds
    double decode_pos_ms; // current decoding position, in milliseconds
    int paused; // are we paused?
    int seek_needed; // if != -1, it is the point that the decode thread should seek to, in ms.
    char filename[_MAX_PATH];
    int filetype; /* 0: MP4; 1: AAC */
    int last_frame;
    __int64 last_offset;

    /* MP4 stuff */
    MP4FileHandle mp4file;


@@ 81,66 103,114 @@ typedef struct state

    /* AAC stuff */
    FILE *aacfile;
    long filesize;
    long bytes_read;
    long bytes_into_buffer;
    long bytes_consumed;
    unsigned char *buffer;
    long seconds;
//    faadAACInfo aacInfo;
    long m_aac_bytes_into_buffer;
    long m_aac_bytes_consumed;
    __int64 m_file_offset;
    unsigned char *m_aac_buffer;
    int m_at_eof;
    double cur_pos_sec;
    int m_header_type;
    struct seek_list *m_head;
    struct seek_list *m_tail;
    unsigned long m_length;

    /* for gapless decoding */
    unsigned int useAacLength;
    unsigned int framesize;
    unsigned int initial;
    unsigned long timescale;
} state;

static state mp4state;

HINSTANCE		hInstance;
HWND			hwndPlayer, hwndConfig, hwndAbout;
QCDModInitIn	sQCDCallbacks, *QCDCallbacks;
BOOL			oldAPIs = 0;
//static In_Module module; // the output module (declared near the bottom of this file)
struct {
	HINSTANCE		hInstance;
	HWND			hMainWindow;
	QCDModInitIn	QCDCallbacks;
} module;
AudioInfo		ai;

static int killPlayThread;
static int PlayThreadAlive = 0; // 1=play thread still running
HANDLE play_thread_handle = INVALID_HANDLE_VALUE; // the handle to the decode thread

/* Function definitions */
void *decode_aac_frame(state *st, faacDecFrameInfo *frameInfo);
DWORD WINAPI MP4PlayThread(void *b); // the decode thread procedure
DWORD WINAPI AACPlayThread(void *b); // the decode thread procedure


#ifdef DEBUG_OUTPUT
void in_mp4_DebugOutput(char *message)
{
    char s[1024];

    sprintf(s, "%s: %s", mp4state.filename, message);
    MessageBox(NULL, s, "Debug Message", MB_OK);
    sprintf(s, "in_mp4: %s: %s", mp4state.filename, message);
    OutputDebugString(s);
}
#endif

int file_length(FILE *f)
{
    long end = 0;
    long cur = ftell(f);
    fseek(f, 0, SEEK_END);
    end = ftell(f);
    fseek(f, cur, SEEK_SET);

    return end;
}

static void show_error(HWND hwnd, char *message, ...)
{
    if (m_show_errors)
        MessageBox(hwnd, message, "Error", MB_OK);
}

static void config_init()
{
    //char *p=INI_FILE;
    //GetModuleFileName(NULL,INI_FILE,_MAX_PATH);
    //while (*p) p++;
    //while (p >= INI_FILE && *p != '.') p--;
    //strcpy(p+1,"ini");
	module.QCDCallbacks.Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0);
}

void config_read()
{
    char priority[10];
    char resolution[10];
    char show_errors[10];
    char use_for_aac[10];
    char downmix[10];
    char vbr_display[10];

    config_init();

    strcpy(show_errors, "1");
    strcpy(priority, "3");
    strcpy(resolution, "0");
    strcpy(use_for_aac, "1");
    strcpy(downmix, "0");
    strcpy(vbr_display, "1");
    //strcpy(titleformat, "%7");

    RS(priority);
	RS(resolution);
	RS(show_errors);
    RS(resolution);
    RS(show_errors);
    RS(use_for_aac);
    RS(downmix);
    RS(vbr_display);
    //RS(titleformat);

    m_priority = atoi(priority);
    m_resolution = atoi(resolution);
    m_show_errors = atoi(show_errors);
    m_use_for_aac = atoi(use_for_aac);
    m_downmix = atoi(downmix);
    m_vbr_display = atoi(vbr_display);
}

void config_write()


@@ 149,105 219,38 @@ void config_write()
    char resolution[10];
    char show_errors[10];
    char use_for_aac[10];
    char downmix[10];
    char vbr_display[10];

    itoa(m_priority, priority, 10);
    itoa(m_resolution, resolution, 10);
    itoa(m_show_errors, show_errors, 10);
    itoa(m_use_for_aac, use_for_aac, 10);
    itoa(m_downmix, downmix, 10);
    itoa(m_vbr_display, vbr_display, 10);

    WS(priority);
	WS(resolution);
	WS(show_errors);
	WS(use_for_aac);
}

//-----------------------------------------------------------------------------

int WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID pRes)
{
	if (fdwReason == DLL_PROCESS_ATTACH)
		hInstance = hInst;
	return TRUE;
}

//-----------------------------------------------------------------------------
//old entrypoint api
PLUGIN_API BOOL QInputModule(QCDModInitIn *ModInit, QCDModInfo *ModInfo)
{
	ModInit->version					= PLUGIN_API_VERSION;
	ModInit->toModule.ShutDown			= ShutDown;
	ModInit->toModule.GetTrackExtents	= GetTrackExtents;
	ModInit->toModule.GetMediaSupported = GetMediaSupported;
	ModInit->toModule.GetCurrentPosition= GetCurrentPosition;
	ModInit->toModule.Play				= Play;
	ModInit->toModule.Pause				= Pause;
	ModInit->toModule.Stop				= Stop;
	ModInit->toModule.SetVolume			= SetVolume;
	ModInit->toModule.About				= About;
	ModInit->toModule.Configure			= Configure;
	QCDCallbacks = ModInit;

	/* read config */
	QCDCallbacks->Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0);
	config_read();

	ModInfo->moduleString = "MPEG-4 General Audio Plugin v1.0";
	ModInfo->moduleExtensions = m_use_for_aac ? "MP4:M4A:AAC" : "MP4:M4A";

	hwndPlayer = (HWND)ModInit->Service(opGetParentWnd, 0, 0, 0);
	mp4state.filename[0] = 0;
	mp4state.seek_needed = -1;
	mp4state.paused = 0;
	play_thread_handle = INVALID_HANDLE_VALUE;

	oldAPIs = 1;

	return TRUE;
}

//-----------------------------------------------------------------------------

PLUGIN_API QCDModInitIn* INPUTDLL_ENTRY_POINT(QCDModInitIn *ModInit, QCDModInfo *ModInfo)
{
	sQCDCallbacks.version						= PLUGIN_API_VERSION;
	sQCDCallbacks.toModule.Initialize			= Initialize;
	sQCDCallbacks.toModule.ShutDown				= ShutDown;
	sQCDCallbacks.toModule.GetTrackExtents		= GetTrackExtents;
	sQCDCallbacks.toModule.GetMediaSupported	= GetMediaSupported;
	sQCDCallbacks.toModule.GetCurrentPosition	= GetCurrentPosition;
	sQCDCallbacks.toModule.Play					= Play;
	sQCDCallbacks.toModule.Pause				= Pause;
	sQCDCallbacks.toModule.Stop					= Stop;
	sQCDCallbacks.toModule.SetVolume			= SetVolume;
	sQCDCallbacks.toModule.About				= About;
	sQCDCallbacks.toModule.Configure			= Configure;

	QCDCallbacks = &sQCDCallbacks;
	return &sQCDCallbacks;
    WS(resolution);
    WS(show_errors);
    WS(use_for_aac);
    WS(downmix);
    WS(vbr_display);
    //WS(titleformat);
}

//----------------------------------------------------------------------------

int Initialize(QCDModInfo *ModInfo, int flags)
{
	hwndPlayer = (HWND)QCDCallbacks->Service(opGetParentWnd, 0, 0, 0);
	ModInfo->moduleString = "MP4 Plug-in v" FAAD2_VERSION;

	mp4state.filename[0] = 0;
	mp4state.seek_needed = -1;
	mp4state.paused = 0;
	play_thread_handle = INVALID_HANDLE_VALUE;
	module.hMainWindow = (HWND)module.QCDCallbacks.Service(opGetParentWnd, 0, 0, 0);

	/* read config */
	QCDCallbacks->Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0);
	// read config from config file
    config_read();

	ModInfo->moduleString = "MPEG-4 General Audio Plugin v1.0";
	ModInfo->moduleExtensions = m_use_for_aac ? "MP4:M4A:AAC" : "MP4:M4A";

	// insert menu item into plugin menu
//	QCDCallbacks->Service(opSetPluginMenuItem, hInstance, IDD_CONFIG, (long)"Mp4 Plug-in");
	ModInfo->moduleExtensions = !m_use_for_aac ? "MP4:M4A" : "MP4:M4A:AAC";

	return TRUE;
	// return TRUE for successful initialization
	return 1;
}

//----------------------------------------------------------------------------


@@ 255,122 258,689 @@ int Initialize(QCDModInfo *ModInfo, int flags)
void ShutDown(int flags) 
{
	Stop(mp4state.filename, STOPFLAG_FORCESTOP);

	// delete the inserted plugin menu
//	QCDCallbacks->Service(opSetPluginMenuItem, hInstance, 0, 0);
}

//-----------------------------------------------------------------------------

int GetMediaSupported(const char* medianame, MediaInfo *mediaInfo) 
{
	char *ch = strrchr(medianame, '.');

	if (!medianame || !*medianame)
		return FALSE;

	if(!ch)
		return (lstrlen(medianame) > 2); // no extension defaults to me (if not drive letter)

   /* Finally fixed */
    if(StringComp(ch, ".mp4", 4) == 0)
    {
		mediaInfo->mediaType = DIGITAL_FILE_MEDIA;
		mediaInfo->op_canSeek = TRUE;
		mp4state.filetype = 0;
		return TRUE;
	}
	else if(StringComp(ch, ".aac", 4) ==0)
	{
		mediaInfo->mediaType = DIGITAL_FILE_MEDIA;
		mediaInfo->op_canSeek = FALSE;
		mp4state.filetype = 1;
		return TRUE;
	}
	else
		return FALSE;
}

//-----------------------------------------------------------------------------

int getsonglength(char *fn)
{
    long msDuration = 0;

    if(StringComp(fn + strlen(fn) - 3, "MP4", 3) == 0)
    {
        int track;
        MP4Duration length;
        MP4FileHandle file;

        file = MP4Read(fn, 0);
        if (!file)
            return 0;

        if ((track = GetAACTrack(file)) < 0)
        {
            MP4Close(file);
            return -1;
        }

        length = MP4GetTrackDuration(file, track);

        msDuration = MP4ConvertFromTrackDuration(file, track,
            length, MP4_MSECS_TIME_SCALE);

        MP4Close(file);

        return msDuration;
    } 
	else 
	{
//        faadAACInfo aacInfo;
//        get_AAC_format(fn, &aacInfo);

//        return aacInfo.length;
        return 0;
    }
}

int GetTrackExtents(const char* medianame, TrackExtents *ext, int flags)
{
	ext->track = 1;
	ext->start = 0;
	if( (ext->end = getsonglength(medianame)) < 0 )
		return FALSE;
	ext->bytesize = 0;
	ext->unitpersec = 1000;

	return TRUE;
}

//-----------------------------------------------------------------------------
///* Convert UNICODE to UTF-8
//   Return number of bytes written */
//int unicodeToUtf8 ( const WCHAR* lpWideCharStr, char* lpMultiByteStr, int cwcChars )
//{
//    const unsigned short*   pwc = (unsigned short *)lpWideCharStr;
//    unsigned char*          pmb = (unsigned char  *)lpMultiByteStr;
//    const unsigned short*   pwce;
//    size_t  cBytes = 0;
//
//    if ( cwcChars >= 0 ) {
//        pwce = pwc + cwcChars;
//    } else {
//        pwce = (unsigned short *)((size_t)-1);
//    }
//
//    while ( pwc < pwce ) {
//        unsigned short  wc = *pwc++;
//
//        if ( wc < 0x00000080 ) {
//            *pmb++ = (char)wc;
//            cBytes++;
//        } else
//        if ( wc < 0x00000800 ) {
//            *pmb++ = (char)(0xC0 | ((wc >>  6) & 0x1F));
//            cBytes++;
//            *pmb++ = (char)(0x80 |  (wc        & 0x3F));
//            cBytes++;
//        } else
//        if ( wc < 0x00010000 ) {
//            *pmb++ = (char)(0xE0 | ((wc >> 12) & 0x0F));
//            cBytes++;
//            *pmb++ = (char)(0x80 | ((wc >>  6) & 0x3F));
//            cBytes++;
//            *pmb++ = (char)(0x80 |  (wc        & 0x3F));
//            cBytes++;
//        }
//        if ( wc == L'\0' )
//            return cBytes;
//    }
//
//    return cBytes;
//}
//
///* Convert UTF-8 coded string to UNICODE
//   Return number of characters converted */
//int utf8ToUnicode ( const char* lpMultiByteStr, WCHAR* lpWideCharStr, int cmbChars )
//{
//    const unsigned char*    pmb = (unsigned char  *)lpMultiByteStr;
//    unsigned short*         pwc = (unsigned short *)lpWideCharStr;
//    const unsigned char*    pmbe;
//    size_t  cwChars = 0;
//
//    if ( cmbChars >= 0 ) {
//        pmbe = pmb + cmbChars;
//    } else {
//        pmbe = (unsigned char *)((size_t)-1);
//    }
//
//    while ( pmb < pmbe ) {
//        char            mb = *pmb++;
//        unsigned int    cc = 0;
//        unsigned int    wc;
//
//        while ( (cc < 7) && (mb & (1 << (7 - cc)))) {
//            cc++;
//        }
//
//        if ( cc == 1 || cc > 6 )                    // illegal character combination for UTF-8
//            continue;
//
//        if ( cc == 0 ) {
//            wc = mb;
//        } else {
//            wc = (mb & ((1 << (7 - cc)) - 1)) << ((cc - 1) * 6);
//            while ( --cc > 0 ) {
//                if ( pmb == pmbe )                  // reached end of the buffer
//                    return cwChars;
//                mb = *pmb++;
//                if ( ((mb >> 6) & 0x03) != 2 )      // not part of multibyte character
//                    return cwChars;
//                wc |= (mb & 0x3F) << ((cc - 1) * 6);
//            }
//        }
//
//        if ( wc & 0xFFFF0000 )
//            wc = L'?';
//        *pwc++ = wc;
//        cwChars++;
//        if ( wc == L'\0' )
//            return cwChars;
//    }
//
//    return cwChars;
//}
//
///* convert Windows ANSI to UTF-8 */
//int ConvertANSIToUTF8 ( const char* ansi, char* utf8 )
//{
//    WCHAR*  wszValue;          // Unicode value
//    size_t  ansi_len;
//    size_t  len;
//
//    *utf8 = '\0';
//    if ( ansi == NULL )
//        return 0;
//
//    ansi_len = strlen ( ansi );
//
//    if ( (wszValue = (WCHAR *)malloc ( (ansi_len + 1) * 2 )) == NULL )
//        return 0;
//
//    /* Convert ANSI value to Unicode */
//    if ( (len = MultiByteToWideChar ( CP_ACP, 0, ansi, ansi_len + 1, wszValue, (ansi_len + 1) * 2 )) == 0 ) {
//        free ( wszValue );
//        return 0;
//    }
//
//    /* Convert Unicode value to UTF-8 */
//    if ( (len = unicodeToUtf8 ( wszValue, utf8, -1 )) == 0 ) {
//        free ( wszValue );
//        return 0;
//    }
//
//    free ( wszValue );
//
//    return len-1;
//}
//
///* convert UTF-8 to Windows ANSI */
//int ConvertUTF8ToANSI ( const char* utf8, char* ansi )
//{
//    WCHAR*  wszValue;          // Unicode value
//    size_t  utf8_len;
//    size_t  len;
//
//    *ansi = '\0';
//    if ( utf8 == NULL )
//        return 0;
//
//    utf8_len = strlen ( utf8 );
//
//    if ( (wszValue = (WCHAR *)malloc ( (utf8_len + 1) * 2 )) == NULL )
//        return 0;
//
//    /* Convert UTF-8 value to Unicode */
//    if ( (len = utf8ToUnicode ( utf8, wszValue, utf8_len + 1 )) == 0 ) {
//        free ( wszValue );
//        return 0;
//    }
//
//    /* Convert Unicode value to ANSI */
//    if ( (len = WideCharToMultiByte ( CP_ACP, 0, wszValue, -1, ansi, (utf8_len + 1) * 2, NULL, NULL )) == 0 ) {
//        free ( wszValue );
//        return 0;
//    }
//
//    free ( wszValue );
//
//    return len-1;
//}
//
//BOOL uSetDlgItemText(HWND hwnd, int id, const char *str)
//{
//    char *temp;
//    size_t len;
//    int r;
//
//    if (!str) return FALSE;
//    len = strlen(str);
//    temp = malloc(len+1);
//    if (!temp) return FALSE;
//    r = ConvertUTF8ToANSI(str, temp);
//    if (r > 0)
//        SetDlgItemText(hwnd, id, temp);
//    free(temp);
//
//    return r>0 ? TRUE : FALSE;
//}
//
//UINT uGetDlgItemText(HWND hwnd, int id, char *str, int max)
//{
//    char *temp, *utf8;
//    int len;
//    HWND w;
//
//    if (!str) return 0;
//    w = GetDlgItem(hwnd, id);
//    len = GetWindowTextLength(w);
//    temp = malloc(len+1);
//    if (!temp) return 0;
//    utf8 = malloc((len+1)*4);
//    if (!utf8)
//    {
//        free(temp);
//        return 0;
//    }
//
//    len = GetWindowText(w, temp, len+1);
//    if (len > 0)
//    {
//        len = ConvertANSIToUTF8(temp, utf8);
//        if (len > max-1)
//        {
//            len = max-1;
//            utf8[max] = '\0';
//        }
//        memcpy(str, utf8, len+1);
//    }
//
//    free(temp);
//    free(utf8);
//
//    return len;
//}
//
//BOOL CALLBACK mp4_info_dialog_proc(HWND hwndDlg, UINT message,
//                                   WPARAM wParam, LPARAM lParam)
//{
//    char *file_info;
//    MP4FileHandle file;
//    char *pVal, dummy1[1024], dummy3;
//    short dummy, dummy2;
//
//#ifdef DEBUG_OUTPUT
//    in_mp4_DebugOutput("mp4_info_dialog_proc");
//#endif
//
//    switch (message) {
//    case WM_INITDIALOG:
//        EnableWindow(GetDlgItem(hwndDlg,IDC_CONVERT), FALSE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_CONVERT), SW_HIDE);
//        EnableWindow(GetDlgItem(hwndDlg,IDC_CONVERT1), FALSE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_CONVERT1), SW_HIDE);
//        EnableWindow(GetDlgItem(hwndDlg,IDC_CONVERT2), FALSE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_CONVERT2), SW_HIDE);
//
//
//        file = MP4Read(info_fn, 0);
//
//        if (file == MP4_INVALID_FILE_HANDLE)
//            return FALSE;
//
//        file_info = MP4Info(file, MP4_INVALID_TRACK_ID);
//        SetDlgItemText(hwndDlg, IDC_INFOTEXT, file_info);
//        free(file_info);
//
//        /* get Metadata */
//
//        pVal = NULL;
//        MP4GetMetadataName(file, &pVal);
//        uSetDlgItemText(hwndDlg,IDC_METANAME, pVal);
//
//        pVal = NULL;
//        MP4GetMetadataArtist(file, &pVal);
//        uSetDlgItemText(hwndDlg,IDC_METAARTIST, pVal);
//
//        pVal = NULL;
//        MP4GetMetadataWriter(file, &pVal);
//        uSetDlgItemText(hwndDlg,IDC_METAWRITER, pVal);
//
//        pVal = NULL;
//        MP4GetMetadataComment(file, &pVal);
//        uSetDlgItemText(hwndDlg,IDC_METACOMMENTS, pVal);
//
//        pVal = NULL;
//        MP4GetMetadataAlbum(file, &pVal);
//        uSetDlgItemText(hwndDlg,IDC_METAALBUM, pVal);
//
//        pVal = NULL;
//        MP4GetMetadataGenre(file, &pVal);
//        uSetDlgItemText(hwndDlg,IDC_METAGENRE, pVal);
//
//        dummy = 0;
//        MP4GetMetadataTempo(file, &dummy);
//        wsprintf(dummy1, "%d", dummy);
//        SetDlgItemText(hwndDlg,IDC_METATEMPO, dummy1);
//
//        dummy = 0; dummy2 = 0;
//        MP4GetMetadataTrack(file, &dummy, &dummy2);
//        wsprintf(dummy1, "%d", dummy);
//        SetDlgItemText(hwndDlg,IDC_METATRACK1, dummy1);
//        wsprintf(dummy1, "%d", dummy2);
//        SetDlgItemText(hwndDlg,IDC_METATRACK2, dummy1);
//
//        dummy = 0; dummy2 = 0;
//        MP4GetMetadataDisk(file, &dummy, &dummy2);
//        wsprintf(dummy1, "%d", dummy);
//        SetDlgItemText(hwndDlg,IDC_METADISK1, dummy1);
//        wsprintf(dummy1, "%d", dummy2);
//        SetDlgItemText(hwndDlg,IDC_METADISK2, dummy1);
//
//        pVal = NULL;
//        MP4GetMetadataYear(file, &pVal);
//        uSetDlgItemText(hwndDlg,IDC_METAYEAR, pVal);
//
//        dummy3 = 0;
//        MP4GetMetadataCompilation(file, &dummy3);
//        if (dummy3)
//            SendMessage(GetDlgItem(hwndDlg, IDC_METACOMPILATION), BM_SETCHECK, BST_CHECKED, 0);
//
//        /* ! Metadata */
//
//        MP4Close(file);
//
//        return TRUE;
//
//    case WM_COMMAND:
//        switch (LOWORD(wParam)) {
//        case IDCANCEL:
//            EndDialog(hwndDlg, wParam);
//            return TRUE;
//        case IDOK:
//
//            /* save Metadata changes */
//
//            file = MP4Modify(info_fn, 0, 0);
//            if (file == MP4_INVALID_FILE_HANDLE)
//            {
//                EndDialog(hwndDlg, wParam);
//                return FALSE;
//            }
//
//            uGetDlgItemText(hwndDlg, IDC_METANAME, dummy1, 1024);
//            MP4SetMetadataName(file, dummy1);
//
//            uGetDlgItemText(hwndDlg, IDC_METAWRITER, dummy1, 1024);
//            MP4SetMetadataWriter(file, dummy1);
//
//            uGetDlgItemText(hwndDlg, IDC_METAARTIST, dummy1, 1024);
//            MP4SetMetadataArtist(file, dummy1);
//
//            uGetDlgItemText(hwndDlg, IDC_METAALBUM, dummy1, 1024);
//            MP4SetMetadataAlbum(file, dummy1);
//
//            uGetDlgItemText(hwndDlg, IDC_METACOMMENTS, dummy1, 1024);
//            MP4SetMetadataComment(file, dummy1);
//
//            uGetDlgItemText(hwndDlg, IDC_METAGENRE, dummy1, 1024);
//            MP4SetMetadataGenre(file, dummy1);
//
//            uGetDlgItemText(hwndDlg, IDC_METAYEAR, dummy1, 1024);
//            MP4SetMetadataYear(file, dummy1);
//
//            GetDlgItemText(hwndDlg, IDC_METATRACK1, dummy1, 1024);
//            dummy = atoi(dummy1);
//            GetDlgItemText(hwndDlg, IDC_METATRACK2, dummy1, 1024);
//            dummy2 = atoi(dummy1);
//            MP4SetMetadataTrack(file, dummy, dummy2);
//
//            GetDlgItemText(hwndDlg, IDC_METADISK1, dummy1, 1024);
//            dummy = atoi(dummy1);
//            GetDlgItemText(hwndDlg, IDC_METADISK2, dummy1, 1024);
//            dummy2 = atoi(dummy1);
//            MP4SetMetadataDisk(file, dummy, dummy2);
//
//            GetDlgItemText(hwndDlg, IDC_METATEMPO, dummy1, 1024);
//            dummy = atoi(dummy1);
//            MP4SetMetadataTempo(file, dummy);
//
//            dummy3 = SendMessage(GetDlgItem(hwndDlg, IDC_METACOMPILATION), BM_GETCHECK, 0, 0);
//            MP4SetMetadataCompilation(file, dummy3);
//
//            MP4Close(file);
//
//            MP4Optimize(info_fn, NULL, 0);
//            /* ! */
//
//            EndDialog(hwndDlg, wParam);
//            return TRUE;
//        }
//    }
//    return FALSE;
//}
//
///* returns the name of the object type */
//char *get_ot_string(int ot)
//{
//    switch (ot)
//    {
//    case 0:
//        return "Main";
//    case 1:
//        return "LC";
//    case 2:
//        return "SSR";
//    case 3:
//        return "LTP";
//    }
//    return NULL;
//}
//
//BOOL CALLBACK aac_info_dialog_proc(HWND hwndDlg, UINT message,
//                                   WPARAM wParam, LPARAM lParam)
//{
//    faadAACInfo aacInfo;
//    char *info_text, *header_string;
//
//#ifdef DEBUG_OUTPUT
//    in_mp4_DebugOutput("aac_info_dialog_proc");
//#endif
//
//    switch (message) {
//    case WM_INITDIALOG:
//        EnableWindow(GetDlgItem(hwndDlg,IDC_USERDATA), FALSE) ;
//        ShowWindow(GetDlgItem(hwndDlg,IDC_USERDATA), SW_HIDE);
//
//        ShowWindow(GetDlgItem(hwndDlg,IDC_STATIC1), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_STATIC2), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_STATIC3), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_STATIC4), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_STATIC5), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_STATIC6), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_STATIC7), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_STATIC8), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_STATIC9), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_STATIC10), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_STATIC11), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_STATIC12), SW_HIDE);
//
//        ShowWindow(GetDlgItem(hwndDlg,IDC_METANAME), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_METAARTIST), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_METAWRITER), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_METACOMMENTS), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_METAALBUM), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_METAGENRE), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_METATEMPO), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_METATRACK1), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_METATRACK2), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_METADISK1), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_METADISK2), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_METAYEAR), SW_HIDE);
//        ShowWindow(GetDlgItem(hwndDlg,IDC_METACOMPILATION), SW_HIDE);
//
//        info_text = malloc(1024*sizeof(char));
//
//        get_AAC_format(info_fn, &aacInfo);
//
//        switch (aacInfo.headertype)
//        {
//        case 0: /* RAW */
//            header_string = " RAW";
//            break;
//        case 1: /* ADIF */
//            header_string = " ADIF";
//            break;
//        case 2: /* ADTS */
//            header_string = " ADTS";
//            break;
//        }
//
//        sprintf(info_text, "%s AAC %s%s, %d sec, %d kbps, %d Hz",
//            (aacInfo.version==2)?"MPEG-2":"MPEG-4", get_ot_string(aacInfo.object_type),
//            header_string,
//            (int)((float)aacInfo.length/1000.0), (int)((float)aacInfo.bitrate/1000.0+0.5),
//            aacInfo.sampling_rate);
//
//        SetDlgItemText(hwndDlg, IDC_INFOTEXT, info_text);
//
//        free(info_text);
//
//        return TRUE;
//
//    case WM_COMMAND:
//        switch (LOWORD(wParam))
//        {
//        case IDC_CONVERT:
//            {
//                char mp4FileName[256];
//                char *extension;
//                OPENFILENAME ofn;
//
//                lstrcpy(mp4FileName, info_fn);
//                extension = strrchr(mp4FileName, '.');
//                lstrcpy(extension, ".mp4");
//
//                memset(&ofn, 0, sizeof(OPENFILENAME));
//                ofn.lStructSize = sizeof(OPENFILENAME);
//                ofn.hwndOwner = hwndDlg;
//                ofn.hInstance = module.hDllInstance;
//                ofn.nMaxFileTitle = 31;
//                ofn.lpstrFile = (LPSTR)mp4FileName;
//                ofn.nMaxFile = _MAX_PATH;
//                ofn.lpstrFilter = "MP4 Files (*.mp4)\0*.mp4\0";
//                ofn.lpstrDefExt = "mp4";
//                ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;
//                ofn.lpstrTitle = "Select Output File";
//
//                if (GetSaveFileName(&ofn))
//                {
//                    if (covert_aac_to_mp4(info_fn, mp4FileName))
//                    {
//                        MessageBox(hwndDlg, "An error occured while converting AAC to MP4!", "An error occured!", MB_OK);
//                        return FALSE;
//                    }
//                }
//                return TRUE;
//            }
//        case IDCANCEL:
//        case IDOK:
//            EndDialog(hwndDlg, wParam);
//            return TRUE;
//        }
//    }
//    return FALSE;
//}
//
//int infoDlg(char *fn, HWND hwndParent)
//{
//    if(!stricmp(fn + strlen(fn) - 3,"AAC"))
//    {
//        lstrcpy(info_fn, fn);
//
//        DialogBox(module.hDllInstance, MAKEINTRESOURCE(IDD_INFO),
//            hwndParent, aac_info_dialog_proc);
//    } else {
//        lstrcpy(info_fn, fn);
//
//        DialogBox(module.hDllInstance, MAKEINTRESOURCE(IDD_INFO),
//            hwndParent, mp4_info_dialog_proc);
//    }
//
//    return 0;
//}
//
///* Get the title from the file */
//void ConstructTitle(MP4FileHandle file, char *filename, char *title, char *format)
//{
//    char temp[4096];
//    int some_info = 0;
//    char *in = format;
//    char *out = temp;//title;
//    char *bound = out + sizeof(temp) - 256; //out + (MAX_PATH - 10 - 1);
//    char *pVal, dummy1[1024];
//    short dummy, dummy2;
//
//
//    while (*in && out < bound)
//    {
//        switch (*in)
//        {
//        case '%':
//            ++in;
//            break;
//
//        default:
//            *out++ = *in++;
//            continue;
//        }
//
//        /* handle % escape sequence */
//        switch (*in++)
//        {
//        case '0':
//            dummy = 0; dummy2 = 0;
//            if (MP4GetMetadataTrack(file, &dummy, &dummy2))
//            {
//                out += wsprintf(out, "%d", (int)dummy);
//                some_info = 1;
//            }
//            break;
//
//        case '1':
//            pVal = NULL;
//            if (MP4GetMetadataArtist(file, &pVal))
//            {
//                out += wsprintf(out, "%s", pVal);
//                some_info = 1;
//            }
//            break;
//
//        case '2':
//            pVal = NULL;
//            if (MP4GetMetadataName(file, &pVal))
//            {
//                out += wsprintf(out, "%s", pVal);
//                some_info = 1;
//            }
//            break;
//
//        case '3':
//            pVal = NULL;
//            if (MP4GetMetadataAlbum(file, &pVal))
//            {
//                out += wsprintf(out, "%s", pVal);
//                some_info = 1;
//            }
//            break;
//
//        case '4':
//            pVal = NULL;
//            if (MP4GetMetadataYear(file, &pVal))
//            {
//                out += wsprintf(out, "%s", pVal);
//                some_info = 1;
//            }
//            break;
//
//        case '5':
//            pVal = NULL;
//            if (MP4GetMetadataComment(file, &pVal))
//            {
//                out += wsprintf(out, "%s", pVal);
//                some_info = 1;
//            }
//            break;
//
//        case '6':
//            pVal = NULL;
//            if (MP4GetMetadataGenre(file, &pVal))
//            {
//                out += wsprintf(out, "%s", pVal);
//                some_info = 1;
//            }
//            break;
//
//        case '7':
//            {
//                const char *p=strrchr(filename,'\\');
//                if (!p) p=filename; else p++;
//                out += ConvertANSIToUTF8(p, out);
//                some_info = 1;
//                break;
//            }
//
//        default:
//            break;
//        }
//    }
//
//    *out = '\0';
//
//    if (!some_info)
//    {
//        char *p=filename+lstrlen(filename);
//        while (*p != '\\' && p >= filename) p--;
//        lstrcpy(title,++p);
//    }
//    else
//    {
//        int len = ConvertUTF8ToANSI(temp, dummy1);
//        if (len > (MAX_PATH - 10 - 1)) len = (MAX_PATH - 10 - 1);
//        memcpy(title, dummy1, len);
//        title[len] = '\0';
//    }
//}

BOOL CALLBACK config_dialog_proc(HWND hwndDlg, UINT message,
                                 WPARAM wParam, LPARAM lParam)
{
    int i;

    switch (message) 
	{
    switch (message) {
    case WM_INITDIALOG:
		SendMessage(GetDlgItem(hwndDlg, IDC_PRIORITY), TBM_SETRANGE, TRUE, MAKELONG(1,5)); 
		SendMessage(GetDlgItem(hwndDlg, IDC_PRIORITY), TBM_SETPOS, TRUE, m_priority);
        SendMessage(GetDlgItem(hwndDlg, IDC_PRIORITY), TBM_SETRANGE, TRUE, MAKELONG(1,5));
        SendMessage(GetDlgItem(hwndDlg, IDC_PRIORITY), TBM_SETPOS, TRUE, m_priority);
        SendMessage(GetDlgItem(hwndDlg, res_id_table[m_resolution]), BM_SETCHECK, BST_CHECKED, 0);
        if (m_show_errors)
            SendMessage(GetDlgItem(hwndDlg, IDC_ERROR), BM_SETCHECK, BST_CHECKED, 0);
        if (m_use_for_aac)
            SendMessage(GetDlgItem(hwndDlg, IDC_USEFORAAC), BM_SETCHECK, BST_CHECKED, 0);
        if (m_downmix)
            SendMessage(GetDlgItem(hwndDlg, IDC_DOWNMIX), BM_SETCHECK, BST_CHECKED, 0);
        if (m_vbr_display)
            SendMessage(GetDlgItem(hwndDlg, IDC_VBR), BM_SETCHECK, BST_CHECKED, 0);
        SetDlgItemText(hwndDlg, IDC_TITLEFORMAT, titleformat);
        return TRUE;

    case WM_COMMAND:
        switch (LOWORD(wParam)) 
		{
        switch (LOWORD(wParam)) {
        case IDCANCEL:
            EndDialog(hwndDlg, wParam);
            return TRUE;
        case IDOK:
            m_show_errors = SendMessage(GetDlgItem(hwndDlg, IDC_ERROR), BM_GETCHECK, 0, 0);
            m_use_for_aac = SendMessage(GetDlgItem(hwndDlg, IDC_USEFORAAC), BM_GETCHECK, 0, 0);
            m_downmix = SendMessage(GetDlgItem(hwndDlg, IDC_DOWNMIX), BM_GETCHECK, 0, 0);
            m_vbr_display = SendMessage(GetDlgItem(hwndDlg, IDC_VBR), BM_GETCHECK, 0, 0);
            GetDlgItemText(hwndDlg, IDC_TITLEFORMAT, titleformat, MAX_PATH);

            m_priority = SendMessage(GetDlgItem(hwndDlg, IDC_PRIORITY), TBM_GETPOS, 0, 0);
            for (i = 0; i < 5; i++)
            for (i = 0; i < 6; i++)
            {
                if (SendMessage(GetDlgItem(hwndDlg, res_id_table[i]), BM_GETCHECK, 0, 0))
                {


@@ 381,8 951,15 @@ BOOL CALLBACK config_dialog_proc(HWND hwndDlg, UINT message,

            /* save config */
            config_write();
        case IDCANCEL:
            DestroyWindow(hwndDlg);

            //if (!m_use_for_aac)
            //{
            //    module.FileExtensions = short_ext_list;
            //} else {
            //    module.FileExtensions = long_ext_list;
            //}

            EndDialog(hwndDlg, wParam);
            return TRUE;
        }
    }


@@ 391,724 968,1459 @@ BOOL CALLBACK config_dialog_proc(HWND hwndDlg, UINT message,

void Configure(int flags)
{
	if(!IsWindow(hwndConfig))
		hwndConfig = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_CONFIG_INPUT), hwndPlayer, config_dialog_proc);
	ShowWindow(hwndConfig, SW_SHOWNORMAL);
	DialogBox(module.hInstance, MAKEINTRESOURCE(IDD_CONFIG),
		module.hMainWindow, config_dialog_proc);
}

//-----------------------------------------------------------------------------
// proc of "About Dialog"
INT_PTR CALLBACK about_dialog_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static RECT rcLOGO, rcMail1, rcMail2/*, rcMail3*/;
	POINT ptMouse;
	static char szPluginVer[] = "QCD MP4 Input Plug-in v1.0\nCompiled on " __TIME__ ", " __DATE__;
	static char szFLACVer[] = "Using: FAAD2 v "FAAD2_VERSION" by";

	switch (uMsg)
	{
	case WM_INITDIALOG:
	case WM_MOVE:
		GetWindowRect(GetDlgItem(hwndDlg, IDC_LOGO), &rcLOGO);
		GetWindowRect(GetDlgItem(hwndDlg, IDC_MAIL1), &rcMail1);
		GetWindowRect(GetDlgItem(hwndDlg, IDC_MAIL2), &rcMail2);
//		GetWindowRect(GetDlgItem(hwndDlg, IDC_MAIL2), &rcMail3);

		SetDlgItemText(hwndDlg, IDC_PLUGINVER, szPluginVer);
		SetDlgItemText(hwndDlg, IDC_FAADVER, szFLACVer);
		
		return TRUE;
	case WM_MOUSEMOVE:
		ptMouse.x = LOWORD(lParam);
		ptMouse.y = HIWORD(lParam);
		ClientToScreen(hwndDlg, &ptMouse);
		if( (ptMouse.x >= rcLOGO.left && ptMouse.x <= rcLOGO.right && 
			ptMouse.y >= rcLOGO.top && ptMouse.y<= rcLOGO.bottom) 
			||
			(ptMouse.x >= rcMail1.left && ptMouse.x <= rcMail1.right && 
			ptMouse.y >= rcMail1.top && ptMouse.y<= rcMail1.bottom) 
			||
			(ptMouse.x >= rcMail2.left && ptMouse.x <= rcMail2.right && 
			ptMouse.y >= rcMail2.top && ptMouse.y<= rcMail2.bottom) 
/*			||
			(ptMouse.x >= rcMail3.left && ptMouse.x <= rcMail3.right && 
			ptMouse.y >= rcMail3.top && ptMouse.y<= rcMail3.bottom)*/ )
			SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(32649)));
		else
			SetCursor(LoadCursor(NULL, IDC_ARROW));

		return TRUE;
	case WM_LBUTTONDOWN:
		ptMouse.x = LOWORD(lParam);
		ptMouse.y = HIWORD(lParam);
		ClientToScreen(hwndDlg, &ptMouse);
		if(ptMouse.x >= rcLOGO.left && ptMouse.x <= rcLOGO.right && 
			ptMouse.y >= rcLOGO.top && ptMouse.y<= rcLOGO.bottom)
			ShellExecute(0, NULL, "http://www.audiocoding.com", NULL,NULL, SW_NORMAL);
		else if(ptMouse.x >= rcMail1.left && ptMouse.x <= rcMail1.right && 
			ptMouse.y >= rcMail1.top && ptMouse.y<= rcMail1.bottom)
			ShellExecute(0, NULL, "mailto:shaohao@elong.com", NULL,NULL, SW_NORMAL);
		else if(ptMouse.x >= rcMail2.left && ptMouse.x <= rcMail2.right && 
			ptMouse.y >= rcMail2.top && ptMouse.y<= rcMail2.bottom)
			ShellExecute(0, NULL, "mailto:menno@audiocoding.com", NULL,NULL, SW_NORMAL);
/*		else if(ptMouse.x >= rcMail3.left && ptMouse.x <= rcMail3.right && 
			ptMouse.y >= rcMail3.top && ptMouse.y<= rcMail3.bottom)
			ShellExecute(0, NULL, "I don't know", NULL,NULL, SW_NORMAL);
*/
		return TRUE;
	case WM_COMMAND:
		switch(LOWORD(wParam))
		{
		case IDOK:
		default:
			DestroyWindow(hwndDlg);
			return TRUE;
		}
	}
	return FALSE;
}

void About(int flags)
{
	if(!IsWindow(hwndAbout))
		hwndAbout = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_ABOUT), hwndPlayer, about_dialog_proc); 
	ShowWindow(hwndAbout, SW_SHOWNORMAL);
	MessageBox(module.hMainWindow,
        "AudioCoding.com MPEG-4 General Audio player " FAAD2_VERSION " compiled on " __DATE__ ".\n"
        "Visit the website for more info.\n"
		"Ported to QCD by Shao Hao.\n"
        "Copyright 2002-2003 AudioCoding.com",
        "About",
        MB_OK);
}

//-----------------------------------------------------------------------------

int Play(const char* medianame, int playfrom, int playto, int flags)
int fill_buffer(state *st)
{
	static WAVEFORMATEX wf;
    int bread;

	if(flags == PLAYFLAG_ENCODING)
	{
		if(QCDCallbacks->toPlayer.OutputOpen(medianame, &wf))
			Stop(medianame, STOPFLAG_FORCESTOP);
		return FALSE;
	}
    if (st->m_aac_bytes_consumed > 0)
    {
        if (st->m_aac_bytes_into_buffer)
        {
            memmove((void*)st->m_aac_buffer, (void*)(st->m_aac_buffer + st->m_aac_bytes_consumed),
                st->m_aac_bytes_into_buffer*sizeof(unsigned char));
        }

	if(stricmp(mp4state.filename, medianame) != 0)
	{
		sQCDCallbacks.toPlayer.OutputStop(STOPFLAG_PLAYDONE);
		Stop(mp4state.filename, STOPFLAG_PLAYDONE);
	}
        if (!st->m_at_eof)
        {
            bread = fread((void*)(st->m_aac_buffer + st->m_aac_bytes_into_buffer),
                1, st->m_aac_bytes_consumed, st->aacfile);

	if(mp4state.paused)
	{
		// Update the player controls to reflect the new unpaused state
		sQCDCallbacks.toPlayer.OutputPause(0);
		
		Pause(medianame, PAUSE_DISABLED);
		
		if (playfrom >= 0)
			mp4state.seek_needed = playfrom;
	}
	else if(play_thread_handle != INVALID_HANDLE_VALUE)
	{
		mp4state.seek_needed = playfrom;
		return TRUE;
	}
	else
	{
#ifdef DEBUG_OUTPUT
		in_mp4_DebugOutput("play");
#endif
		int thread_id;
		int avg_bitrate, br, sr;
		unsigned char *buffer;
		int buffer_size;
		faacDecConfigurationPtr config;	
		
		mp4state.channels = 0;
		mp4state.samplerate = 0;
		mp4state.filetype = 0;
            if (bread != st->m_aac_bytes_consumed)
                st->m_at_eof = 1;

		strcpy(mp4state.filename, medianame);
            st->m_aac_bytes_into_buffer += bread;
        }

		if(StringComp(medianame + strlen(medianame) - 3, "AAC", 3) == 0)
			mp4state.filetype = 1;
		
		mp4state.hDecoder = faacDecOpen();
		if (!mp4state.hDecoder)
		{
			show_error(hwndPlayer, "Unable to open decoder library.");
			return -1;
		}
		
		config = faacDecGetCurrentConfiguration(mp4state.hDecoder);
		config->outputFormat = m_resolution + 1;
		faacDecSetConfiguration(mp4state.hDecoder, config);
		
		if (mp4state.filetype)
		{
			long pos, tmp, read, tagsize;
			
			//        get_AAC_format(mp4state.filename, &mp4state.aacInfo);
			
			mp4state.aacfile = fopen(mp4state.filename, "rb");
			if (!mp4state.aacfile)
			{
				show_error(hwndPlayer, "Unable to open file.");
				faacDecClose(mp4state.hDecoder);
				return -1;
			}
			
			pos = ftell(mp4state.aacfile);
			fseek(mp4state.aacfile, 0, SEEK_END);
			mp4state.filesize = ftell(mp4state.aacfile);
			fseek(mp4state.aacfile, pos, SEEK_SET);
			
			if (!(mp4state.buffer=(unsigned char*)malloc(768*48)))
			{
				show_error(hwndPlayer, "Memory allocation error.");
				faacDecClose(mp4state.hDecoder);
				fclose(mp4state.aacfile);
				return -1;
			}
			memset(mp4state.buffer, 0, 768*48);
			
			if (mp4state.filesize < 768*48)
				tmp = mp4state.filesize;
			else
				tmp = 768*48;
			read = fread(mp4state.buffer, 1, tmp, mp4state.aacfile);
			if (read == tmp)
			{
				mp4state.bytes_read = read;
				mp4state.bytes_into_buffer = read;
			} 
			else 
			{
				show_error(hwndPlayer, "Error reading from file.");
				faacDecClose(mp4state.hDecoder);
				fclose(mp4state.aacfile);
				return -1;
			}
			
			if (StringComp(mp4state.buffer, "ID3", 3) == 0)
			{
				/* high bit is not used */
				tagsize = (mp4state.buffer[6] << 21) | (mp4state.buffer[7] << 14) |
					(mp4state.buffer[8] <<  7) | (mp4state.buffer[9] <<  0);
				
				tagsize += 10;
			} 
			else 
			{
				tagsize = 0;
			}
			
			if ((mp4state.bytes_consumed = faacDecInit(mp4state.hDecoder,
				mp4state.buffer+tagsize, mp4state.bytes_into_buffer,
				&mp4state.samplerate, &mp4state.channels)) < 0)
			{
				show_error(hwndPlayer, "Can't initialize library.");
				faacDecClose(mp4state.hDecoder);
				fclose(mp4state.aacfile);
				return -1;
			}
			mp4state.bytes_consumed += tagsize;
			mp4state.bytes_into_buffer -= mp4state.bytes_consumed;
			
			//        avg_bitrate = mp4state.aacInfo.bitrate;
			avg_bitrate = 0;
			
//			module.is_seekable = 0;
		} 
		else 
		{
			mp4state.mp4file = MP4Read(mp4state.filename, 0);
			if (!mp4state.mp4file)
			{
				show_error(hwndPlayer, "Unable to open file.");
				faacDecClose(mp4state.hDecoder);
				return -1;
			}
			
			if ((mp4state.mp4track = GetAACTrack(mp4state.mp4file)) < 0)
			{
				show_error(hwndPlayer, "Unsupported Audio track type.");
				faacDecClose(mp4state.hDecoder);
				MP4Close(mp4state.mp4file);
				return -1;
			}
			
			buffer = NULL;
			buffer_size = 0;
			MP4GetTrackESConfiguration(mp4state.mp4file, mp4state.mp4track,
				&buffer, &buffer_size);
			if (!buffer)
			{
				faacDecClose(mp4state.hDecoder);
				MP4Close(mp4state.mp4file);
				return -1;
			}
			
			if(faacDecInit2(mp4state.hDecoder, buffer, buffer_size,
				&mp4state.samplerate, &mp4state.channels) < 0)
			{
				/* If some error initializing occured, skip the file */
				faacDecClose(mp4state.hDecoder);
				MP4Close(mp4state.mp4file);
				return -1;
			}
			free(buffer);
			
			avg_bitrate = MP4GetTrackIntegerProperty(mp4state.mp4file, mp4state.mp4track,
				"mdia.minf.stbl.stsd.mp4a.esds.decConfigDescr.avgBitrate");
			
			mp4state.numSamples = MP4GetTrackNumberOfSamples(mp4state.mp4file, mp4state.mp4track);
			mp4state.sampleId = 1;
			
//			module.is_seekable = 1;
		}
		
		if (mp4state.channels == 0)
		{
			show_error(hwndPlayer, "Number of channels not supported for playback.");
			faacDecClose(mp4state.hDecoder);
			if (mp4state.filetype)
				fclose(mp4state.aacfile);
			else
				MP4Close(mp4state.mp4file);
			return -1;
		}
		
		// open outputdevice
		wf.wFormatTag = WAVE_FORMAT_PCM;
		wf.cbSize = 0;
		wf.nChannels = mp4state.channels;
		wf.wBitsPerSample = res_table[m_resolution];
		wf.nSamplesPerSec = mp4state.samplerate;
		wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8;
		wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
		if (!QCDCallbacks->toPlayer.OutputOpen(mp4state.filename, &wf))
		{
			show_error(hwndPlayer, "Error: Failed openning output plugin!");
			faacDecClose(mp4state.hDecoder);
			if (mp4state.filetype)
				fclose(mp4state.aacfile);
			else
				MP4Close(mp4state.mp4file);
			return -1; // cannot open sound device
		}
		
		mp4state.paused			= 0;
		mp4state.decode_pos_ms	= 0;
		mp4state.seek_needed	= playfrom > 0 ? playfrom : -1;
		
		br = (int)floor(((float)avg_bitrate + 500.0)/1000.0);
		sr = (int)floor((float)mp4state.samplerate/1000.0);
		// show constant bitrate at first
		{
			AudioInfo cai;
			cai.struct_size = sizeof(AudioInfo);
			cai.frequency = sr * 1000;
			cai.bitrate = br * 1000;
			cai.mode = (mp4state.channels == 2) ? 0 : 3;
			cai.layer = 0;
			cai.level = 0;
			QCDCallbacks->Service(opSetAudioInfo, &cai, sizeof(AudioInfo), 0);
		}
		
		killPlayThread = 0;
		
		if (mp4state.filetype)
		{
			if ((play_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AACPlayThread,
				(void *)&killPlayThread, 0, &thread_id)) == NULL)
			{
				show_error(hwndPlayer, "Cannot create playback thread");
				faacDecClose(mp4state.hDecoder);
				fclose(mp4state.aacfile);
				return -1;
			}
		} 
		else 
		{
			if ((play_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MP4PlayThread,
				(void *)&killPlayThread, 0, &thread_id)) == NULL)
			{
				show_error(hwndPlayer, "Cannot create playback thread");
				faacDecClose(mp4state.hDecoder);
				MP4Close(mp4state.mp4file);
				return -1;
			}
		}
		
		SetThreadAffinityMask(play_thread_handle, 1);
		
		if (m_priority != 3)
			SetThreadPriority(play_thread_handle, priority_table[m_priority]);
	}
	return TRUE;
}
        st->m_aac_bytes_consumed = 0;

//-----------------------------------------------------------------------------
        if (st->m_aac_bytes_into_buffer > 3)
        {
            if (memcmp(st->m_aac_buffer, "TAG", 3) == 0)
                st->m_aac_bytes_into_buffer = 0;
        }
        if (st->m_aac_bytes_into_buffer > 11)
        {
            if (memcmp(st->m_aac_buffer, "LYRICSBEGIN", 11) == 0)
                st->m_aac_bytes_into_buffer = 0;
        }
        if (st->m_aac_bytes_into_buffer > 8)
        {
            if (memcmp(st->m_aac_buffer, "APETAGEX", 8) == 0)
                st->m_aac_bytes_into_buffer = 0;
        }
    }

int Pause(const char* medianame, int flags)
{
#ifdef DEBUG_OUTPUT
    in_mp4_DebugOutput("pause");
#endif
	if(QCDCallbacks->toPlayer.OutputPause(flags))
	{
		// send back pause/unpause notification
		QCDCallbacks->toPlayer.PlayPaused(medianame, flags);
		mp4state.paused = flags;
		return TRUE;
	}
	return FALSE;
    return 1;
}

//-----------------------------------------------------------------------------

void SetVolume(int levelleft, int levelright, int flags)
void advance_buffer(state *st, int bytes)
{
	QCDCallbacks->toPlayer.OutputSetVol(levelleft, levelright, flags);
    st->m_file_offset += bytes;
    st->m_aac_bytes_consumed = bytes;
    st->m_aac_bytes_into_buffer -= bytes;
}

//-----------------------------------------------------------------------------

int GetCurrentPosition(const char* medianame, long *track, long *offset)
int adts_parse(state *st, __int64 *bitrate, double *length)
{
	return QCDCallbacks->toPlayer.OutputGetCurrentPosition((UINT*)offset, 0);
}
    static int sample_rates[] = {96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000};
    int frames, frame_length;
    int t_framelength = 0;
    int samplerate;
    double frames_per_sec, bytes_per_frame;

//-----------------------------------------------------------------------------
    /* Read all frames to ensure correct time and bitrate */
    for (frames = 0; /* */; frames++)
    {
        fill_buffer(st);

int Stop(const char* medianame, int flags)
{
#ifdef DEBUG_OUTPUT
    in_mp4_DebugOutput("stop");
#endif
	if(medianame && *medianame && stricmp(mp4state.filename, medianame) == 0)
	{
		sQCDCallbacks.toPlayer.OutputStop(flags);
        if (st->m_aac_bytes_into_buffer > 7)
        {
            /* check syncword */
            if (!((st->m_aac_buffer[0] == 0xFF)&&((st->m_aac_buffer[1] & 0xF6) == 0xF0)))
                break;

		killPlayThread = 1;
		if(play_thread_handle != INVALID_HANDLE_VALUE)
		{
			if (WaitForSingleObject(play_thread_handle, INFINITE) == WAIT_TIMEOUT)
			{
//				MessageBox(hwndPlayer, "MP4 thread kill timeout", "debug", 0);
				TerminateThread(play_thread_handle,0);
			}
			CloseHandle(play_thread_handle);
			play_thread_handle = INVALID_HANDLE_VALUE;
		}
		
		if (oldAPIs)
			QCDCallbacks->toPlayer.PlayStopped(mp4state.filename);
            st->m_tail->offset = st->m_file_offset;
            st->m_tail->next = (struct seek_list*)malloc(sizeof(struct seek_list));
            st->m_tail = st->m_tail->next;
            st->m_tail->next = NULL;

		mp4state.filename[0] = 0;
		if(mp4state.hDecoder)
			faacDecClose(mp4state.hDecoder);
		if (mp4state.filetype)
			fclose(mp4state.aacfile);
		else
			MP4Close(mp4state.mp4file);
	}
	
	return TRUE;
            if (frames == 0)
                samplerate = sample_rates[(st->m_aac_buffer[2]&0x3c)>>2];

            frame_length = ((((unsigned int)st->m_aac_buffer[3] & 0x3)) << 11)
                | (((unsigned int)st->m_aac_buffer[4]) << 3) | (st->m_aac_buffer[5] >> 5);

            t_framelength += frame_length;

            if (frame_length > st->m_aac_bytes_into_buffer)
                break;

            advance_buffer(st, frame_length);
        } else {
            break;
        }
    }

    frames_per_sec = (double)samplerate/1024.0;
    if (frames != 0)
        bytes_per_frame = (double)t_framelength/(double)(frames*1000);
    else
        bytes_per_frame = 0;
    *bitrate = (__int64)(8. * bytes_per_frame * frames_per_sec + 0.5);
    if (frames_per_sec != 0)
        *length = (double)frames/frames_per_sec;
    else
        *length = 1;

    return 1;
}

DWORD WINAPI MP4PlayThread(void *b)
int skip_id3v2_tag()
{
	BOOL done = FALSE, updatePos = FALSE;
    int l;
    unsigned char buf[10];
    int bread, tagsize = 0;

    void *sample_buffer;
    unsigned char *buffer;
    int buffer_size, ms;
    faacDecFrameInfo frameInfo;
    bread = fread(buf, 1, 10, mp4state.aacfile);
    if (bread != 10) return -1;

#ifdef DEBUG_OUTPUT
    in_mp4_DebugOutput("MP4PlayThread");
#endif
    if (!memcmp(buf, "ID3", 3))
    {
        /* high bit is not used */
        tagsize = (buf[6] << 21) | (buf[7] << 14) | (buf[8] << 7) | (buf[9] << 0);

    mp4state.last_frame = 0;
        tagsize += 10;
        fseek(mp4state.aacfile, tagsize, SEEK_SET);
    } else {
        fseek(mp4state.aacfile, 0, SEEK_SET);
    }

    return tagsize;
}

int GetMediaSupported(const char* medianame, MediaInfo *mediaInfo) 
{
    int tagsize = 0, init;

	if (!medianame || !*medianame)
		return 0;

    if (!stricmp(medianame + strlen(medianame) - 3,"MP4") || !stricmp(medianame + strlen(medianame) - 3,"M4A"))
    {
		if (mediaInfo)
		{
			mediaInfo->mediaType = DIGITAL_FILE_MEDIA;
			mediaInfo->op_canSeek = 1;
		}
		return 1;
	}
	else if (m_use_for_aac && !stricmp(medianame + strlen(medianame) - 3,"AAC"))
	{
		if (mediaInfo)
		{
			mediaInfo->mediaType = DIGITAL_FILE_MEDIA;
			mediaInfo->op_canSeek = 1;

			memset(&mp4state, 0, sizeof(state));
			lstrcpy(mp4state.filename, medianame);

			if (!(mp4state.aacfile = fopen(mp4state.filename, "rb")))
			{
				// error
				return 0;
			}

			tagsize = skip_id3v2_tag();
			if (tagsize<0) return 0;

			if (!(mp4state.m_aac_buffer = (unsigned char*)malloc(768*6)))
			{
				show_error(module.hMainWindow, "Memory allocation error.");
				return 0;
			}

			for (init=0; init<2; init++)
			{
				memset(mp4state.m_aac_buffer, 0, 768*6);
				fread(mp4state.m_aac_buffer, 1, 768*6, mp4state.aacfile);

				if (init==0)
					fseek(mp4state.aacfile, tagsize, SEEK_SET);
			}

			if (memcmp(mp4state.m_aac_buffer, "ADIF", 4) == 0)
				mediaInfo->op_canSeek = (double)file_length(mp4state.aacfile) == -1 ? 0 : 1;

			free(mp4state.m_aac_buffer);

			fclose(mp4state.aacfile);
		}
		return 1;
	}

	return 0;
}

int Play(const char* medianame, int playfrom, int playto, int flags)
{
    WAVEFORMATEX wf;
    //int maxlatency;
    int thread_id;
    int avg_bitrate, br, sr;
    unsigned char *buffer;
    int buffer_size;
    faacDecConfigurationPtr config;

#ifdef DEBUG_OUTPUT
    in_mp4_DebugOutput("play");
#endif

	if (stricmp(mp4state.filename, medianame) != 0)
		Stop(mp4state.filename, STOPFLAG_PLAYDONE);

	if (mp4state.paused)
	{
		// Update the player controls to reflect the new unpaused state
		module.QCDCallbacks.toPlayer.OutputPause(0);

		Pause(medianame, 0);

		if (playfrom >= 0)
			mp4state.seek_needed = playfrom;
	}
	else if (PlayThreadAlive) // is playing
	{
		mp4state.seek_needed = playfrom;
		return 1;
	}
	else
	{
    memset(&mp4state, 0, sizeof(state));

    lstrcpy(mp4state.filename, medianame);

    if (!(mp4state.mp4file = MP4Read(mp4state.filename, 0)))
    {
        mp4state.filetype = 1;
    } else {
        MP4Close(mp4state.mp4file);
        mp4state.filetype = 0;
    }

    if (mp4state.filetype)
    {
        int tagsize = 0, tmp = 0, init;
        int bread = 0;
        double length = 0.;
        __int64 bitrate = 128;
        faacDecFrameInfo frameInfo;

        //module.is_seekable = 1;

        if (!(mp4state.aacfile = fopen(mp4state.filename, "rb")))
        {
            // error
            return -1;
        }

        tagsize = skip_id3v2_tag();
        if (tagsize<0) return 0;

        if (!(mp4state.m_aac_buffer = (unsigned char*)malloc(768*6)))
        {
            show_error(module.hMainWindow, "Memory allocation error.");
            return -1;
        }

        for (init=0; init<2; init++)
        {
            mp4state.hDecoder = faacDecOpen();
            if (!mp4state.hDecoder)
            {
                show_error(module.hMainWindow, "Unable to open decoder library.");
                return -1;
            }

            config = faacDecGetCurrentConfiguration(mp4state.hDecoder);
            config->outputFormat = m_resolution + 1;
            config->downMatrix = m_downmix;
            faacDecSetConfiguration(mp4state.hDecoder, config);

            memset(mp4state.m_aac_buffer, 0, 768*6);
            bread = fread(mp4state.m_aac_buffer, 1, 768*6, mp4state.aacfile);
            mp4state.m_aac_bytes_into_buffer = bread;
            mp4state.m_aac_bytes_consumed = 0;
            mp4state.m_file_offset = 0;
            mp4state.m_at_eof = (bread != 768*6) ? 1 : 0;

            if (init==0)
            {
                faacDecFrameInfo frameInfo;

                fill_buffer(&mp4state);

                if ((mp4state.m_aac_bytes_consumed = faacDecInit(mp4state.hDecoder,
                    mp4state.m_aac_buffer, mp4state.m_aac_bytes_into_buffer,
                    &mp4state.samplerate, &mp4state.channels)) < 0)
                {
                    show_error(module.hMainWindow, "Can't initialize decoder library.");
                    return -1;
                }
                advance_buffer(&mp4state, mp4state.m_aac_bytes_consumed);

                do {
                    memset(&frameInfo, 0, sizeof(faacDecFrameInfo));
                    fill_buffer(&mp4state);
                    faacDecDecode(mp4state.hDecoder, &frameInfo, mp4state.m_aac_buffer, mp4state.m_aac_bytes_into_buffer);
                } while (!frameInfo.samples && !frameInfo.error);

                if (frameInfo.error)
                {
                    show_error(module.hMainWindow, faacDecGetErrorMessage(frameInfo.error));
                    return -1;
                }

                mp4state.channels = frameInfo.channels;
                mp4state.samplerate = frameInfo.samplerate;
                mp4state.framesize = (frameInfo.channels != 0) ? frameInfo.samples/frameInfo.channels : 0;
                /*
                sbr = frameInfo.sbr;
                profile = frameInfo.object_type;
                header_type = frameInfo.header_type;
                */

                faacDecClose(mp4state.hDecoder);
                fseek(mp4state.aacfile, tagsize, SEEK_SET);
            }
        }

        mp4state.m_head = (struct seek_list*)malloc(sizeof(struct seek_list));
        mp4state.m_tail = mp4state.m_head;
        mp4state.m_tail->next = NULL;

        mp4state.m_header_type = 0;
        if ((mp4state.m_aac_buffer[0] == 0xFF) && ((mp4state.m_aac_buffer[1] & 0xF6) == 0xF0))
        {
            if (1) //(can_seek)
            {
                adts_parse(&mp4state, &bitrate, &length);
                fseek(mp4state.aacfile, tagsize, SEEK_SET);

                bread = fread(mp4state.m_aac_buffer, 1, 768*6, mp4state.aacfile);
                if (bread != 768*6)
                    mp4state.m_at_eof = 1;
                else
                    mp4state.m_at_eof = 0;
                mp4state.m_aac_bytes_into_buffer = bread;
                mp4state.m_aac_bytes_consumed = 0;

                mp4state.m_header_type = 1;
            }
        } else if (memcmp(mp4state.m_aac_buffer, "ADIF", 4) == 0) {
            int skip_size = (mp4state.m_aac_buffer[4] & 0x80) ? 9 : 0;
            bitrate = ((unsigned int)(mp4state.m_aac_buffer[4 + skip_size] & 0x0F)<<19) |
                ((unsigned int)mp4state.m_aac_buffer[5 + skip_size]<<11) |
                ((unsigned int)mp4state.m_aac_buffer[6 + skip_size]<<3) |
                ((unsigned int)mp4state.m_aac_buffer[7 + skip_size] & 0xE0);

            length = (double)file_length(mp4state.aacfile);
            if (length == -1)
            {
                //module.is_seekable = 0;
                length = 0;
            } else {
                length = ((double)length*8.)/((double)bitrate) + 0.5;
            }

            mp4state.m_header_type = 2;
        } else {
            length = (double)file_length(mp4state.aacfile);
            length = ((double)length*8.)/((double)bitrate*1000.) + 0.5;

            //module.is_seekable = 1;
        }

        mp4state.m_length = (int)(length*1000.);

        fill_buffer(&mp4state);
        if ((mp4state.m_aac_bytes_consumed = faacDecInit(mp4state.hDecoder,
            mp4state.m_aac_buffer, mp4state.m_aac_bytes_into_buffer,
            &mp4state.samplerate, &mp4state.channels)) < 0)
        {
            show_error(module.hMainWindow, "Can't initialize decoder library.");
            return -1;
        }
        advance_buffer(&mp4state, mp4state.m_aac_bytes_consumed);

        if (mp4state.m_header_type == 2)
            avg_bitrate = bitrate;
        else
            avg_bitrate = bitrate*1000;
    } else {
        mp4state.hDecoder = faacDecOpen();
        if (!mp4state.hDecoder)
        {
            show_error(module.hMainWindow, "Unable to open decoder library.");
            return -1;
        }

        config = faacDecGetCurrentConfiguration(mp4state.hDecoder);
        config->outputFormat = m_resolution + 1;
        config->downMatrix = m_downmix;
        faacDecSetConfiguration(mp4state.hDecoder, config);

        mp4state.mp4file = MP4Read(mp4state.filename, 0);
        if (!mp4state.mp4file)
        {
            show_error(module.hMainWindow, "Unable to open file.");
            faacDecClose(mp4state.hDecoder);
            return -1;
        }

        if ((mp4state.mp4track = GetAACTrack(mp4state.mp4file)) < 0)
        {
            show_error(module.hMainWindow, "Unsupported Audio track type.");
            faacDecClose(mp4state.hDecoder);
            MP4Close(mp4state.mp4file);
            return -1;
        }

        buffer = NULL;
        buffer_size = 0;
        MP4GetTrackESConfiguration(mp4state.mp4file, mp4state.mp4track,
            &buffer, &buffer_size);
        if (!buffer)
        {
            faacDecClose(mp4state.hDecoder);
            MP4Close(mp4state.mp4file);
            return -1;
        }

        if(faacDecInit2(mp4state.hDecoder, buffer, buffer_size,
            &mp4state.samplerate, &mp4state.channels) < 0)
        {
            /* If some error initializing occured, skip the file */
            faacDecClose(mp4state.hDecoder);
            MP4Close(mp4state.mp4file);
            if (buffer) free (buffer);
            return -1;
        }

        /* for gapless decoding */
        {
            mp4AudioSpecificConfig mp4ASC;

            mp4state.timescale = MP4GetTrackTimeScale(mp4state.mp4file, mp4state.mp4track);
            mp4state.framesize = 1024;
            mp4state.useAacLength = 0;

            if (buffer)
            {
                if (AudioSpecificConfig(buffer, buffer_size, &mp4ASC) >= 0)
                {
                    if (mp4ASC.frameLengthFlag == 1) mp4state.framesize = 960;
                    if (mp4ASC.sbr_present_flag == 1) mp4state.framesize *= 2;
                }
            }
        }

        free(buffer);

        avg_bitrate = MP4GetTrackIntegerProperty(mp4state.mp4file, mp4state.mp4track,
            "mdia.minf.stbl.stsd.mp4a.esds.decConfigDescr.avgBitrate");

        mp4state.numSamples = MP4GetTrackNumberOfSamples(mp4state.mp4file, mp4state.mp4track);
        mp4state.sampleId = 1;

        //module.is_seekable = 1;
    }

    if (mp4state.channels == 0)
    {
        show_error(module.hMainWindow, "Number of channels not supported for playback.");
        faacDecClose(mp4state.hDecoder);
        if (mp4state.filetype)
            fclose(mp4state.aacfile);
        else
            MP4Close(mp4state.mp4file);
        return -1;
    }

    if (m_downmix && (mp4state.channels == 5 || mp4state.channels == 6))
        mp4state.channels = 2;

	wf.wFormatTag = WAVE_FORMAT_PCM;
	wf.cbSize = 0;
	wf.nChannels = mp4state.channels;
	wf.wBitsPerSample = res_table[m_resolution];
	wf.nSamplesPerSec = mp4state.samplerate;
	wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8;
	wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
	if (!module.QCDCallbacks.toPlayer.OutputOpen(mp4state.filename, &wf)) // error opening device
    {
        faacDecClose(mp4state.hDecoder);
        if (mp4state.filetype)
            fclose(mp4state.aacfile);
        else
            MP4Close(mp4state.mp4file);
        return -1;
    }

    mp4state.paused        =  0;
    mp4state.decode_pos_ms =  0;
    mp4state.seek_needed   = -1;

    // initialize vis stuff
    //module.SAVSAInit(maxlatency, mp4state.samplerate);
    //module.VSASetInfo((int)mp4state.channels, mp4state.samplerate);

    br = (int)floor(((float)avg_bitrate + 500.0)/1000.0 + 0.5);
    sr = (int)floor((float)mp4state.samplerate/1000.0 + 0.5);
	ai.struct_size = sizeof(AudioInfo);
	ai.frequency = sr*1000;
	ai.bitrate = br*1000;
	ai.mode = (mp4state.channels == 2) ? 0 : 3;
	ai.layer = 0;
	ai.level = 0;
	strcpy(ai.text, mp4state.filetype ? "AAC" : "MP4");
	module.QCDCallbacks.Service(opSetAudioInfo, &ai, sizeof(AudioInfo), 0);

    //module.outMod->SetVolume(-666); // set the output plug-ins default volume

    killPlayThread = 0;

    if (mp4state.filetype)
    {
        if ((play_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AACPlayThread,
            (void *)&killPlayThread, 0, &thread_id)) == NULL)
        {
            show_error(module.hMainWindow, "Cannot create playback thread");
            faacDecClose(mp4state.hDecoder);
            fclose(mp4state.aacfile);
            return -1;
        }
    } else {
        if ((play_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MP4PlayThread,
            (void *)&killPlayThread, 0, &thread_id)) == NULL)
        {
            show_error(module.hMainWindow, "Cannot create playback thread");
            faacDecClose(mp4state.hDecoder);
            MP4Close(mp4state.mp4file);
            return -1;
        }
    }

    SetThreadAffinityMask(play_thread_handle, 1);

    SetThreadPriority(play_thread_handle, priority_table[m_priority]);
	}

    return 1;
}

//-----------------------------------------------------------------------------

int Pause(const char* medianame, int flags)
{
#ifdef DEBUG_OUTPUT
    in_mp4_DebugOutput("pause");
#endif

    mp4state.paused = flags;

	if (module.QCDCallbacks.toPlayer.OutputPause(flags))
		return 1;

	mp4state.paused = !flags;
	return 0;
}

//void unpause()
//{
//#ifdef DEBUG_OUTPUT
//    in_mp4_DebugOutput("unpause");
//#endif
//
//    mp4state.paused = 0;
//    module.outMod->Pause(0);
//}
//
//int ispaused()
//{
//#ifdef DEBUG_OUTPUT
//    in_mp4_DebugOutput("ispaused");
//#endif
//
//    return mp4state.paused;
//}

//-----------------------------------------------------------------------------

void SetVolume(int levelleft, int levelright, int flags)
{
#ifdef DEBUG_OUTPUT
    in_mp4_DebugOutput("setvolume");
#endif

	module.QCDCallbacks.toPlayer.OutputSetVol(levelleft, levelright, flags);
}

//void setpan(int pan)
//{
//#ifdef DEBUG_OUTPUT
//    in_mp4_DebugOutput("setpan");
//#endif
//
//    module.outMod->SetPan(pan);
//}

//-----------------------------------------------------------------------------

int Stop(const char* medianame, int flags)
{
	struct seek_list *target = mp4state.m_head;

#ifdef DEBUG_OUTPUT
	in_mp4_DebugOutput("stop");
#endif

	if (medianame && *medianame && stricmp(mp4state.filename, medianame) == 0)
	{
		module.QCDCallbacks.toPlayer.OutputStop(flags);
    killPlayThread = 1;

    if (play_thread_handle != INVALID_HANDLE_VALUE)
    {
        if (WaitForSingleObject(play_thread_handle, INFINITE) == WAIT_TIMEOUT)
            TerminateThread(play_thread_handle,0);
        CloseHandle(play_thread_handle);
        play_thread_handle = INVALID_HANDLE_VALUE;
    }


    if (mp4state.m_aac_buffer)
        free(mp4state.m_aac_buffer);

    while (target)
    {
        struct seek_list *tmp = target;
        target = target->next;
        if (tmp) free(tmp);
    }
    faacDecClose(mp4state.hDecoder);
    if (mp4state.filetype)
        fclose(mp4state.aacfile);
    else
        MP4Close(mp4state.mp4file);

    //module.outMod->Close();
    //module.SAVSADeInit();
		mp4state.filename[0] = '\0';
		mp4state.paused = 0;
	}

	return 1;
}

int getsonglength(char *fn)
{
    long msDuration = 0;

    if(!stricmp(fn + strlen(fn) - 3,"MP4") || !stricmp(fn + strlen(fn) - 3,"M4A"))
    {
        int track;
        MP4Duration length;
        MP4FileHandle file;

        file = MP4Read(fn, 0);
        if (!file)
            return 0;

        if ((track = GetAACTrack(file)) < 0)
        {
            MP4Close(file);
            return -1;
        }

        length = MP4GetTrackDuration(file, track);

        msDuration = MP4ConvertFromTrackDuration(file, track,
            length, MP4_MSECS_TIME_SCALE);

        MP4Close(file);

        return msDuration;
    } else {
        int tagsize = 0;
        int bread = 0;
        double length = 0.;
        __int64 bitrate = 128;
        struct seek_list *target;
        state len_state;

        memset(&len_state, 0, sizeof(state));

        if (!(len_state.aacfile = fopen(fn, "rb")))
        {
            // error
            return 0;
        }

        len_state.m_at_eof = 0;

        if (!(len_state.m_aac_buffer = (unsigned char*)malloc(768*6)))
        {
            //console::error("Memory allocation error.", "foo_mp4");
            return 0;
        }
        memset(len_state.m_aac_buffer, 0, 768*6);

        bread = fread(len_state.m_aac_buffer, 1, 768*6, len_state.aacfile);
        len_state.m_aac_bytes_into_buffer = bread;
        len_state.m_aac_bytes_consumed = 0;
        len_state.m_file_offset = 0;

        if (bread != 768*6)
            len_state.m_at_eof = 1;

        if (!memcmp(len_state.m_aac_buffer, "ID3", 3))
        {
            /* high bit is not used */
            tagsize = (len_state.m_aac_buffer[6] << 21) | (len_state.m_aac_buffer[7] << 14) |
                (len_state.m_aac_buffer[8] <<  7) | (len_state.m_aac_buffer[9] <<  0);

            tagsize += 10;
            advance_buffer(&len_state, tagsize);
        }

        len_state.m_head = (struct seek_list*)malloc(sizeof(struct seek_list));
        len_state.m_tail = len_state.m_head;
        len_state.m_tail->next = NULL;

        len_state.m_header_type = 0;
        if ((len_state.m_aac_buffer[0] == 0xFF) && ((len_state.m_aac_buffer[1] & 0xF6) == 0xF0))
        {
            if (1) //(m_reader->can_seek())
            {
                adts_parse(&len_state, &bitrate, &length);
                fseek(len_state.aacfile, tagsize, SEEK_SET);

                bread = fread(len_state.m_aac_buffer, 1, 768*6, len_state.aacfile);
                if (bread != 768*6)
                    len_state.m_at_eof = 1;
                else
                    len_state.m_at_eof = 0;
                len_state.m_aac_bytes_into_buffer = bread;
                len_state.m_aac_bytes_consumed = 0;

                len_state.m_header_type = 1;
            }
        } else if (memcmp(len_state.m_aac_buffer, "ADIF", 4) == 0) {
            int skip_size = (len_state.m_aac_buffer[4] & 0x80) ? 9 : 0;
            bitrate = ((unsigned int)(len_state.m_aac_buffer[4 + skip_size] & 0x0F)<<19) |
                ((unsigned int)len_state.m_aac_buffer[5 + skip_size]<<11) |
                ((unsigned int)len_state.m_aac_buffer[6 + skip_size]<<3) |
                ((unsigned int)len_state.m_aac_buffer[7 + skip_size] & 0xE0);

            length = (double)file_length(len_state.aacfile);
            if (length == -1)
                length = 0;
            else
                length = ((double)length*8.)/((double)bitrate) + 0.5;

            len_state.m_header_type = 2;
        } else {
            length = (double)file_length(len_state.aacfile);
            length = ((double)length*8.)/((double)bitrate*1000.) + 0.5;

            len_state.m_header_type = 0;
        }

        if (len_state.m_aac_buffer)
            free(len_state.m_aac_buffer);

        target = len_state.m_head;
        while (target)
        {
            struct seek_list *tmp = target;
            target = target->next;
            if (tmp) free(tmp);
        }

        fclose(len_state.aacfile);

        return (int)(length*1000.);
    }
}

//int getlength()
//{
//    if (!mp4state.filetype)
//    {
//        int track;
//        long msDuration;
//        MP4Duration length;
//
//        if ((track = GetAACTrack(mp4state.mp4file)) < 0)
//        {
//            return -1;
//        }
//
//        length = MP4GetTrackDuration(mp4state.mp4file, track);
//
//        msDuration = MP4ConvertFromTrackDuration(mp4state.mp4file, track,
//            length, MP4_MSECS_TIME_SCALE);
//
//        return msDuration;
//    } else {
//        return mp4state.m_length;
//    }
//    return 0;
//}

//-----------------------------------------------------------------------------

int GetCurrentPosition(const char* medianame, long *track, long *offset)
{
	return module.QCDCallbacks.toPlayer.OutputGetCurrentPosition((UINT*)offset, 0);
}

//void setoutputtime(int time_in_ms)
//{
//#ifdef DEBUG_OUTPUT
//    in_mp4_DebugOutput("setoutputtime");
//#endif
//
//    mp4state.seek_needed = time_in_ms;
//}

//-----------------------------------------------------------------------------

int GetTrackExtents(const char* medianame, TrackExtents *ext, int flags)
{
	int len;
	FILE *fh;

	len = getsonglength((char *)medianame);
	fh = fopen(medianame, "rb");
	if (len <= 0 || !fh)
		return 0;

	ext->track = 1;
	ext->start = 0;
	ext->end = len;
	ext->bytesize = file_length(fh);
	fclose(fh);
	ext->unitpersec = 1000;

	return 1;
}

//void eq_set(int on, char data[10], int preamp)
//{
//}

static void remap_channels(unsigned char *data, unsigned int samples, unsigned int bps)
{
    unsigned int i;

    switch (bps)
    {
    case 8:
        {
            unsigned char r1, r2, r3, r4, r5, r6;
            for (i = 0; i < samples; i += 6)
            {
                r1 = data[i];
                r2 = data[i+1];
                r3 = data[i+2];
                r4 = data[i+3];
                r5 = data[i+4];
                r6 = data[i+5];
                data[i] = r2;
                data[i+1] = r3;
                data[i+2] = r1;
                data[i+3] = r6;
                data[i+4] = r4;
                data[i+5] = r5;
            }
        }
        break;

    case 16:
    default:
        {
            unsigned short r1, r2, r3, r4, r5, r6;
            unsigned short *sample_buffer = (unsigned short *)data;
            for (i = 0; i < samples; i += 6)
            {
                r1 = sample_buffer[i];
                r2 = sample_buffer[i+1];
                r3 = sample_buffer[i+2];
                r4 = sample_buffer[i+3];
                r5 = sample_buffer[i+4];
                r6 = sample_buffer[i+5];
                sample_buffer[i] = r2;
                sample_buffer[i+1] = r3;
                sample_buffer[i+2] = r1;
                sample_buffer[i+3] = r6;
                sample_buffer[i+4] = r4;
                sample_buffer[i+5] = r5;
            }
        }
        break;

    case 24:
    case 32:
        {
            unsigned int r1, r2, r3, r4, r5, r6;
            unsigned int *sample_buffer = (unsigned int *)data;
            for (i = 0; i < samples; i += 6)
            {
                r1 = sample_buffer[i];
                r2 = sample_buffer[i+1];
                r3 = sample_buffer[i+2];
                r4 = sample_buffer[i+3];
                r5 = sample_buffer[i+4];
                r6 = sample_buffer[i+5];
                sample_buffer[i] = r2;
                sample_buffer[i+1] = r3;
                sample_buffer[i+2] = r1;
                sample_buffer[i+3] = r6;
                sample_buffer[i+4] = r4;
                sample_buffer[i+5] = r5;
            }
        }
        break;
    }
}

DWORD WINAPI MP4PlayThread(void *b)
{
    int done = 0, updatepos = 0;
    int l;
    int seq_frames = 0;
    int seq_bytes = 0;

    void *sample_buffer;
    unsigned char *buffer;
    int buffer_size;
    faacDecFrameInfo frameInfo;

    WriteDataStruct wd;

#ifdef DEBUG_OUTPUT
    in_mp4_DebugOutput("MP4PlayThread");
#endif

    PlayThreadAlive = 1;
    mp4state.last_frame = 0;
    mp4state.initial = 1;

    while (!*((int *)b))
    {
        /* seeking */
        if (!done && mp4state.seek_needed != -1)
        if (mp4state.seek_needed != -1)
        {
            MP4Duration duration;

			QCDCallbacks->toPlayer.OutputFlush(mp4state.decode_pos_ms);
			module.QCDCallbacks.toPlayer.OutputFlush((unsigned int)mp4state.decode_pos_ms);
            duration = MP4ConvertToTrackDuration(mp4state.mp4file,
                mp4state.mp4track, mp4state.seek_needed, MP4_MSECS_TIME_SCALE);
            mp4state.sampleId = MP4GetSampleIdFromTime(mp4state.mp4file,
                mp4state.mp4track, duration, 0);

            mp4state.decode_pos_ms = mp4state.seek_needed;
			mp4state.seek_needed = -1;
			updatePos = TRUE;
            mp4state.seek_needed = -1;
        }
		/* quit */
		if (done)
		{
			if (QCDCallbacks->toPlayer.OutputDrain(0) && !(mp4state.seek_needed >= 0))

        if (done)
        {
			if (module.QCDCallbacks.toPlayer.OutputDrain(0) && !(mp4state.seek_needed >= 0))
			{
				play_thread_handle = INVALID_HANDLE_VALUE;
				QCDCallbacks->toPlayer.OutputStop(STOPFLAG_PLAYDONE);
				QCDCallbacks->toPlayer.PlayDone(mp4state.filename);
				module.QCDCallbacks.toPlayer.OutputStop(STOPFLAG_PLAYDONE);
				module.QCDCallbacks.toPlayer.PlayDone(mp4state.filename);
                PlayThreadAlive = 0;
			}
			else if (mp4state.seek_needed >= 0)
			{
				done = FALSE;
				done = 0;
				continue;
			}
			break;
		}
		/* decoding */
		else
		{
        } else/* if (module.outMod->CanWrite() >= (2048*mp4state.channels*sizeof(short)))*/ {

            if (mp4state.last_frame)
            {
                done = TRUE;
            } 
			else 
			{
                done = 1;
            } else {
                int rc;

                /* for gapless decoding */
                char *buf;
                MP4Duration dur;
                unsigned int sample_count;
                unsigned int delay = 0;

                /* get acces unit from MP4 file */
                buffer = NULL;
                buffer_size = 0;

                rc = MP4ReadSample(mp4state.mp4file, mp4state.mp4track,
                    mp4state.sampleId++, &buffer, &buffer_size,
                    NULL, NULL, NULL, NULL);
                    NULL, &dur, NULL, NULL);
                if (mp4state.sampleId-1 == 1) dur = 0;
                if (rc == 0 || buffer == NULL)
                {
                    mp4state.last_frame = 1;
                    sample_buffer = NULL;
                    frameInfo.samples = 0;
                } 
				else 
				{
                } else {
                    sample_buffer = faacDecDecode(mp4state.hDecoder, &frameInfo,
                        buffer, buffer_size);
                }
                if (frameInfo.error > 0)
                {
                    show_error(hwndPlayer, faacDecGetErrorMessage(frameInfo.error));
                    show_error(module.hMainWindow, faacDecGetErrorMessage(frameInfo.error));
                    mp4state.last_frame = 1;
                }
                if (mp4state.sampleId >= mp4state.numSamples)
                if (mp4state.sampleId > mp4state.numSamples)
                    mp4state.last_frame = 1;

                if (buffer) free(buffer);

                if (!killPlayThread && (frameInfo.samples > 0))
                if (mp4state.useAacLength || (mp4state.timescale != mp4state.samplerate)) {
                    sample_count = frameInfo.samples;
                } else {
                    sample_count = (unsigned int)(dur * frameInfo.channels);

                    if (!mp4state.useAacLength && !mp4state.initial && (mp4state.sampleId < mp4state.numSamples/2) && (dur*frameInfo.channels != frameInfo.samples))
                    {
                        //fprintf(stderr, "MP4 seems to have incorrect frame duration, using values from AAC data.\n");
                        mp4state.useAacLength = 1;
                        sample_count = frameInfo.samples;
                    }
                }

                if (mp4state.initial && (sample_count < mp4state.framesize*mp4state.channels) && (frameInfo.samples > sample_count))
                {
                    delay = frameInfo.samples - sample_count;
                }

                if (!killPlayThread && (sample_count > 0))
                {
                    buf = (char *)sample_buffer;
                    mp4state.initial = 0;

                    switch (res_table[m_resolution])
                    {
                    case 8:
                        buf += delay;
                        break;
                    case 16:
                    default:
                        buf += delay * 2;
                        break;
                    case 24:
                    case 32:
                        buf += delay * 4;
                        break;
                    case 64:
                        buf += delay * 8;
                    }

                    if (frameInfo.channels == 6 && frameInfo.num_lfe_channels)
                        remap_channels(buf, sample_count, res_table[m_resolution]);

                    if (res_table[m_resolution] == 24)
                    {
                        /* convert libfaad output (3 bytes packed in 4) */
                        char *temp_buffer = convert3in4to3in3(sample_buffer, frameInfo.samples);
                        memcpy((void*)sample_buffer, (void*)temp_buffer, frameInfo.samples*3);
                        char *temp_buffer = convert3in4to3in3(buf, sample_count);
                        memcpy((void*)buf, (void*)temp_buffer, sample_count*3);
                        free(temp_buffer);
                    }

                    ms = (int)floor(((float)frameInfo.samples*1000.0) /
                        ((float)mp4state.samplerate*(float)frameInfo.channels));
                    mp4state.decode_pos_ms += ms;
                    //module.SAAddPCMData(buf, (int)mp4state.channels, res_table[m_resolution],
                    //    mp4state.decode_pos_ms);
                    //module.VSAAddPCMData(buf, (int)mp4state.channels, res_table[m_resolution],
                    //    mp4state.decode_pos_ms);
                    mp4state.decode_pos_ms += (double)sample_count * 1000.0 /
                        ((double)frameInfo.samplerate * (double)frameInfo.channels);

                    l = frameInfo.samples * res_table[m_resolution] / 8;
                    l = sample_count * res_table[m_resolution] / 8;

					if (updatePos)
					if (updatepos)
					{
						QCDCallbacks->toPlayer.PositionUpdate(mp4state.decode_pos_ms);
						updatePos = FALSE;
						module.QCDCallbacks.toPlayer.PositionUpdate((unsigned int)mp4state.decode_pos_ms);
						updatepos = 0;
					}

					wd.bytelen = l;
					wd.data = (short*)buf;
					wd.markerend = 0;
					wd.markerstart = (UINT)mp4state.decode_pos_ms;
					wd.bps = res_table[m_resolution];
					wd.nch = frameInfo.channels;
					wd.numsamples = sample_count/frameInfo.channels;
					wd.srate = frameInfo.samplerate;

					if (!module.QCDCallbacks.toPlayer.OutputWrite(&wd))
						done = 1;

                    //if (module.dsp_isactive())
                    //{
                    //    void *dsp_buffer = malloc(l*2);
                    //    memcpy(dsp_buffer, buf, l);

                    //    l = module.dsp_dosamples((short*)dsp_buffer,
                    //        sample_count/frameInfo.channels,
                    //        res_table[m_resolution],
                    //        frameInfo.channels,
                    //        frameInfo.samplerate) *
                    //        (frameInfo.channels*(res_table[m_resolution]/8));

                    //    module.outMod->Write(dsp_buffer, l);
                    //    if (dsp_buffer) free(dsp_buffer);
                    //} else {
                    //    module.outMod->Write(buf, l);
                    //}

                    /* VBR bitrate display */
                    if (m_vbr_display)
                    {
						WriteDataStruct wd;

						wd.bytelen = l;
						wd.data = sample_buffer;
						wd.markerend = 0;
						wd.markerstart = mp4state.decode_pos_ms;
						wd.bps = res_table[m_resolution];
						wd.nch = frameInfo.channels;
						wd.numsamples = frameInfo.samples/frameInfo.channels;
						wd.srate = mp4state.samplerate;

						if (!QCDCallbacks->toPlayer.OutputWrite(&wd))
							done = TRUE;
                        seq_frames++;
                        seq_bytes += frameInfo.bytesconsumed;
                        if (seq_frames == (int)(floor((float)frameInfo.samplerate/(float)(sample_count/frameInfo.channels) + 0.5)))
                        {
							ai.bitrate = (int)floor(((float)seq_bytes*8.)/1000. + 0.5) * 1000;
							ai.frequency = (int)floor(frameInfo.samplerate/1000. + 0.5) * 1000;
							ai.mode = (mp4state.channels == 2) ? 0 : 3;
							module.QCDCallbacks.Service(opSetAudioInfo, &ai, sizeof(AudioInfo), 0);

                            seq_frames = 0;
                            seq_bytes = 0;
                        }
                    }
                }
            }
        }
		
		Sleep(10);

		// catch pause
		while (mp4state.paused && !killPlayThread)
			Sleep(50);
    }

	// close up
	play_thread_handle = INVALID_HANDLE_VALUE;
    PlayThreadAlive = 0;

	return 0;
    return 0;
}

void *decode_aac_frame(state *st, faacDecFrameInfo *frameInfo)
{
    void *sample_buffer = NULL;

    do
    {
        fill_buffer(st);

        if (st->m_aac_bytes_into_buffer != 0)
        {
            sample_buffer = faacDecDecode(st->hDecoder, frameInfo,
                st->m_aac_buffer, st->m_aac_bytes_into_buffer);

            if (st->m_header_type != 1)
            {
                if (st->last_offset < st->m_file_offset)
                {
                    st->m_tail->offset = st->m_file_offset;
                    st->m_tail->next = (struct seek_list*)malloc(sizeof(struct seek_list));
                    st->m_tail = st->m_tail->next;
                    st->m_tail->next = NULL;
                    st->last_offset = st->m_file_offset;
                }
            }

            advance_buffer(st, frameInfo->bytesconsumed);
        } else {
            break;
        }

    } while (!frameInfo->samples && !frameInfo->error);

    return sample_buffer;
}

int aac_seek(state *st, double seconds)
{
    int i, frames;
    int bread;
    struct seek_list *target = st->m_head;

    if (1 /*can_seek*/ && ((st->m_header_type == 1) || (seconds < st->cur_pos_sec)))
    {
        frames = (int)(seconds*((double)st->samplerate/(double)st->framesize) + 0.5);

        for (i = 0; i < frames; i++)
        {
            if (target->next)
                target = target->next;
            else
                return 0;
        }
        if (target->offset == 0 && frames > 0)
            return 0;
        fseek(st->aacfile, target->offset, SEEK_SET);
        st->m_file_offset = target->offset;

        bread = fread(st->m_aac_buffer, 1, 768*6, st->aacfile);
        if (bread != 768*6)
            st->m_at_eof = 1;
        else
            st->m_at_eof = 0;
        st->m_aac_bytes_into_buffer = bread;
        st->m_aac_bytes_consumed = 0;
        st->m_file_offset += bread;

        faacDecPostSeekReset(st->hDecoder, -1);

        return 1;
    } else {
        if (seconds > st->cur_pos_sec)
        {
            faacDecFrameInfo frameInfo;

            frames = (int)((seconds - st->cur_pos_sec)*((double)st->samplerate/(double)st->framesize));

            if (frames > 0)
            {
                for (i = 0; i < frames; i++)
                {
                    memset(&frameInfo, 0, sizeof(faacDecFrameInfo));
                    decode_aac_frame(st, &frameInfo);

                    if (frameInfo.error || (st->m_aac_bytes_into_buffer == 0))
                    {
                        if (frameInfo.error)
                        {
                            if (faacDecGetErrorMessage(frameInfo.error) != NULL)
                                show_error(module.hMainWindow, faacDecGetErrorMessage(frameInfo.error));
                        }
                        return 0;
                    }
                }
            }

            faacDecPostSeekReset(st->hDecoder, -1);
        }
        return 1;
    }
}

DWORD WINAPI AACPlayThread(void *b)
{
	BOOL done = FALSE, updatePos = FALSE;
    int l, ms;
    int done = 0, updatepos = 0;
    int l;
    int seq_frames = 0;
    int seq_bytes = 0;

    void *sample_buffer;
    faacDecFrameInfo frameInfo;
	WriteDataStruct wd;

#ifdef DEBUG_OUTPUT
    in_mp4_DebugOutput("AACPlayThread");
#endif

    PlayThreadAlive = 1;
    mp4state.last_frame = 0;

    while (!*((int *)b))
    {
#if 0
        /* seeking */
        if (!done && mp4state.seek_needed != -1)
        if (mp4state.seek_needed != -1)
        {
            int ms;
            double ms;

            /* Round off to a second */
            ms = mp4state.seek_needed - (mp4state.seek_needed%1000);
			QCDCallbacks->toPlayer.OutputFlush(mp4state.decode_pos_ms);
            aac_seek(ms);
            mp4state.decode_pos_ms = ms;
            ms = mp4state.seek_needed/1000;
            if (aac_seek(&mp4state, ms)!=0)
            {
                module.QCDCallbacks.toPlayer.OutputFlush((unsigned int)mp4state.decode_pos_ms);
                mp4state.cur_pos_sec = ms;
                mp4state.decode_pos_ms = mp4state.seek_needed;
            }
            mp4state.seek_needed = -1;
			updatePos = TRUE;
        }
#endif
		/* quit */
		if (done)
		{
			if (QCDCallbacks->toPlayer.OutputDrain(0) && !(mp4state.seek_needed >= 0))

        if (done)
        {
			if (module.QCDCallbacks.toPlayer.OutputDrain(0) && !(mp4state.seek_needed >= 0))
			{
				QCDCallbacks->toPlayer.OutputStop(STOPFLAG_PLAYDONE);
				QCDCallbacks->toPlayer.PlayDone(mp4state.filename);
				module.QCDCallbacks.toPlayer.OutputStop(STOPFLAG_PLAYDONE);
				module.QCDCallbacks.toPlayer.PlayDone(mp4state.filename);
                PlayThreadAlive = 0;
			}
			else if (mp4state.seek_needed >= 0)
			{
				done = FALSE;
				done = 0;
				continue;
			}
			break;
		}
        } else/* if (module.outMod->CanWrite() >= (2048*mp4state.channels*sizeof(short)))*/ {
            faacDecFrameInfo frameInfo;
            void *sample_buffer;

		/* decoding */
		else
		{
            if (mp4state.last_frame)
            {
                done = TRUE;
            } 
			else 
			{
                long tmp, read;
                unsigned char *buffer = mp4state.buffer;
            memset(&frameInfo, 0, sizeof(faacDecFrameInfo));

                do
                {
                    if (mp4state.bytes_consumed > 0)
                    {
                        if (mp4state.bytes_into_buffer)
                        {
                            memcpy(buffer, buffer+mp4state.bytes_consumed,
                                mp4state.bytes_into_buffer);
                        }
            sample_buffer = decode_aac_frame(&mp4state, &frameInfo);

                        if (mp4state.bytes_read < mp4state.filesize)
                        {
                            if (mp4state.bytes_read + mp4state.bytes_consumed < mp4state.filesize)
                                tmp = mp4state.bytes_consumed;
                            else
                                tmp = mp4state.filesize - mp4state.bytes_read;
                            read = fread(buffer + mp4state.bytes_into_buffer, 1, tmp, mp4state.aacfile);
                            if (read == tmp)
                            {
                                mp4state.bytes_read += read;
                                mp4state.bytes_into_buffer += read;
                            }
                        } 
						else 
						{
                            if (mp4state.bytes_into_buffer)
                            {
                                memset(buffer + mp4state.bytes_into_buffer, 0,
                                    mp4state.bytes_consumed);
                            }
                        }
            if (frameInfo.error || (mp4state.m_aac_bytes_into_buffer == 0))
            {
                if (frameInfo.error)
                {
                    if (faacDecGetErrorMessage(frameInfo.error) != NULL)
                        show_error(module.hMainWindow, faacDecGetErrorMessage(frameInfo.error));
                }
                done = 1;
            }

                        mp4state.bytes_consumed = 0;
                    }
            if (!killPlayThread && (frameInfo.samples > 0))
            {
                if (frameInfo.channels == 6 && frameInfo.num_lfe_channels)
                    remap_channels(sample_buffer, frameInfo.samples, res_table[m_resolution]);

                    if (mp4state.bytes_into_buffer < 1)
                    {
                        if (mp4state.bytes_read < mp4state.filesize)
                        {
                            show_error(hwndPlayer, faacDecGetErrorMessage(frameInfo.error));
                            mp4state.last_frame = 1;
                        } 
						else
						{
                            mp4state.last_frame = 1;
                        }
                    }
                if (res_table[m_resolution] == 24)
                {
                    /* convert libfaad output (3 bytes packed in 4 bytes) */
                    char *temp_buffer = convert3in4to3in3(sample_buffer, frameInfo.samples);
                    memcpy((void*)sample_buffer, (void*)temp_buffer, frameInfo.samples*3);
                    free(temp_buffer);
                }

                    sample_buffer = faacDecDecode(mp4state.hDecoder, &frameInfo,
                        buffer, mp4state.bytes_into_buffer);
                //module.SAAddPCMData(sample_buffer, (int)mp4state.channels, res_table[m_resolution],
                //    mp4state.decode_pos_ms);
                //module.VSAAddPCMData(sample_buffer, (int)mp4state.channels, res_table[m_resolution],
                //    mp4state.decode_pos_ms);
                mp4state.decode_pos_ms += (double)frameInfo.samples * 1000.0 /
                    ((double)frameInfo.samplerate* (double)frameInfo.channels);

                    mp4state.bytes_consumed += frameInfo.bytesconsumed;
                    mp4state.bytes_into_buffer -= mp4state.bytes_consumed;
                } while (!frameInfo.samples && !frameInfo.error);
                l = frameInfo.samples * res_table[m_resolution] / 8;

                if (!killPlayThread && (frameInfo.samples > 0))
				if (updatepos)
				{
					module.QCDCallbacks.toPlayer.PositionUpdate((unsigned int)mp4state.decode_pos_ms);
					updatepos = 0;
				}

				wd.bytelen = l;
				wd.data = (short*)sample_buffer;
				wd.markerend = 0;
				wd.markerstart = (UINT)mp4state.decode_pos_ms;
				wd.bps = res_table[m_resolution];
				wd.nch = frameInfo.channels;
				wd.numsamples = frameInfo.samples/frameInfo.channels;
				wd.srate = frameInfo.samplerate;

				if (!module.QCDCallbacks.toPlayer.OutputWrite(&wd))
					done = 1;

				//if (module.dsp_isactive())
                //{
                //    void *dsp_buffer = malloc(l*2);
                //    memcpy(dsp_buffer, sample_buffer, l);

                //    l = module.dsp_dosamples((short*)dsp_buffer,
                //        frameInfo.samples/frameInfo.channels,
                //        res_table[m_resolution],
                //        frameInfo.channels,
                //        frameInfo.samplerate) *
                //        (frameInfo.channels*(res_table[m_resolution]/8));

                //    module.outMod->Write(dsp_buffer, l);
                //    if (dsp_buffer) free(dsp_buffer);
                //} else {
                //    module.outMod->Write(sample_buffer, l);
                //}

                /* VBR bitrate display */
                if (m_vbr_display)
                {
                    if (res_table[m_resolution] == 24)
                    seq_frames++;
                    seq_bytes += frameInfo.bytesconsumed;
                    if (seq_frames == (int)(floor((float)frameInfo.samplerate/(float)(frameInfo.samples/frameInfo.channels) + 0.5)))
                    {
                        /* convert libfaad output (3 bytes packed in 4 bytes) */
                        char *temp_buffer = convert3in4to3in3(sample_buffer, frameInfo.samples);
                        memcpy((void*)sample_buffer, (void*)temp_buffer, frameInfo.samples*3);
                        free(temp_buffer);
                    }

                    ms = (int)floor(((float)frameInfo.samples*1000.0) /
                        ((float)mp4state.samplerate*(float)frameInfo.channels));
                    mp4state.decode_pos_ms += ms;

                    l = frameInfo.samples * res_table[m_resolution] / 8;
						ai.bitrate = (int)floor(((float)seq_bytes*8.)/1000. + 0.5) * 1000;
						ai.frequency = (int)floor(frameInfo.samplerate/1000. + 0.5) * 1000;
						ai.mode = (mp4state.channels == 2) ? 0 : 3;
						module.QCDCallbacks.Service(opSetAudioInfo, &ai, sizeof(AudioInfo), 0);

					if (updatePos)
					{
						QCDCallbacks->toPlayer.PositionUpdate(mp4state.decode_pos_ms);
						updatePos = FALSE;
					}
                    {
						WriteDataStruct wd;

						wd.bytelen = l;
						wd.data = sample_buffer;
						wd.markerend = 0;
						wd.markerstart = mp4state.decode_pos_ms;
						wd.bps = res_table[m_resolution];
						wd.nch = frameInfo.channels;
						wd.numsamples = frameInfo.samples/frameInfo.channels;
						wd.srate = mp4state.samplerate;

						if (!QCDCallbacks->toPlayer.OutputWrite(&wd))
							done = TRUE;
                    } 
                        seq_frames = 0;
                        seq_bytes = 0;
                    }
                }
            }
        } 

            if (frameInfo.channels > 0 && mp4state.samplerate > 0)
                mp4state.cur_pos_sec += ((double)(frameInfo.samples/frameInfo.channels))/(double)mp4state.samplerate;
        }
		
		Sleep(10);

		// catch pause
		while (mp4state.paused && !killPlayThread)
			Sleep(50);
    }

	// close up
	play_thread_handle = INVALID_HANDLE_VALUE;
    PlayThreadAlive = 0;

    return 0;
}

//-----------------------------------------------------------------------------

int WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID pRes)
{
	if (fdwReason == DLL_PROCESS_ATTACH)
	{
		module.hInstance = hInst;
	}
	return TRUE;
}

//-----------------------------------------------------------------------------

PLUGIN_API QCDModInitIn* INPUTDLL_ENTRY_POINT(QCDModInitIn *ModInit, QCDModInfo *ModInfo)
{
	module.QCDCallbacks.size						= sizeof(QCDModInitIn);
	module.QCDCallbacks.version						= PLUGIN_API_VERSION;
	module.QCDCallbacks.toModule.Initialize			= Initialize;
	module.QCDCallbacks.toModule.ShutDown			= ShutDown;
	module.QCDCallbacks.toModule.GetTrackExtents	= GetTrackExtents;
	module.QCDCallbacks.toModule.GetMediaSupported	= GetMediaSupported;
	module.QCDCallbacks.toModule.Play				= Play;
	module.QCDCallbacks.toModule.Pause				= Pause;
	module.QCDCallbacks.toModule.Stop				= Stop;
	module.QCDCallbacks.toModule.About				= About;
	module.QCDCallbacks.toModule.Configure			= Configure;	
	module.QCDCallbacks.toModule.SetEQ				= NULL;
	module.QCDCallbacks.toModule.SetVolume			= SetVolume;

	return &module.QCDCallbacks;
}

M plugins/QCDMp4/QCDMp4.dsp => plugins/QCDMp4/QCDMp4.dsp +13 -29
@@ 25,7 25,7 @@ CFG=QCDMp4 - Win32 Debug
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
CPP=xicl6.exe
MTL=midl.exe
RSC=rc.exe



@@ 43,7 43,7 @@ RSC=rc.exe
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /I "..\..\common\mp4v2" /I "..\..\common\mp4av" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /MD /W3 /GX /O1 /I "..\..\include" /I "..\..\common\mp4v2" /I "..\..\common\mp4av" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x413 /d "NDEBUG"


@@ 51,7 51,7 @@ RSC=rc.exe
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
LINK32=xilink6.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
# ADD LINK32 ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386



@@ 69,7 69,7 @@ LINK32=link.exe
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\include" /I "..\..\common\mp4v2" /I "..\..\common\mp4av" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\include" /I "..\..\common\mp4v2" /I "..\..\common\mp4av" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x413 /d "_DEBUG"


@@ 77,7 77,7 @@ LINK32=link.exe
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
LINK32=xilink6.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept
# ADD LINK32 ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept



@@ 96,10 96,6 @@ SOURCE=.\aac2mp4.cpp
# End Source File
# Begin Source File

SOURCE=.\AAC2Mp4Enc.c
# End Source File
# Begin Source File

SOURCE=.\aacinfo.c
# End Source File
# Begin Source File


@@ 112,6 108,10 @@ SOURCE=.\QCDMp4.c
# End Source File
# Begin Source File

SOURCE=.\QCDMp4Tag.cpp
# End Source File
# Begin Source File

SOURCE=.\utils.c
# End Source File
# End Group


@@ 120,10 120,6 @@ SOURCE=.\utils.c
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File

SOURCE=.\aac2mp4.h
# End Source File
# Begin Source File

SOURCE=.\aacinfo.h
# End Source File
# Begin Source File


@@ 136,31 132,23 @@ SOURCE=..\..\include\faad.h
# End Source File
# Begin Source File

SOURCE=.\mbs.h
# End Source File
# Begin Source File

SOURCE=.\QCDConvertDLL.h
# End Source File
# Begin Source File

SOURCE=.\QCDInputDLL.h
# End Source File
# Begin Source File

SOURCE=.\QCDModDefs.h
SOURCE=.\QCDModInput.h
# End Source File
# Begin Source File

SOURCE=.\QCDModEncode.h
SOURCE=.\QCDTagsDLL.h
# End Source File
# Begin Source File

SOURCE=.\QCDModInput.h
SOURCE=.\QCDModTagEditor.h
# End Source File
# Begin Source File

SOURCE=.\resource.h
SOURCE=.\QCDModDefs.h
# End Source File
# Begin Source File



@@ 172,10 160,6 @@ SOURCE=.\utils.h
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# Begin Source File

SOURCE=.\logo.bmp
# End Source File
# Begin Source File

SOURCE=.\QCDMp4.rc
# End Source File
# End Group

M plugins/QCDMp4/QCDMp4.dsw => plugins/QCDMp4/QCDMp4.dsw +6 -6
@@ 3,7 3,7 @@ Microsoft Developer Studio Workspace File, Format Version 6.00

###############################################################################

Project: "QCDMp4"=".\QCDMp4.dsp" - Package Owner=<4>
Project: "QCDMp4"=.\QCDMp4.dsp - Package Owner=<4>

Package=<5>
{{{


@@ 15,16 15,16 @@ Package=<4>
    Project_Dep_Name libfaad
    End Project Dependency
    Begin Project Dependency
    Project_Dep_Name libmp4av_st
    Project_Dep_Name libmp4v2_st
    End Project Dependency
    Begin Project Dependency
    Project_Dep_Name libmp4v2_st
    Project_Dep_Name libmp4av_st
    End Project Dependency
}}}

###############################################################################

Project: "libfaad"="..\..\libfaad\libfaad.dsp" - Package Owner=<4>
Project: "libfaad"=..\..\libfaad\libfaad.dsp - Package Owner=<4>

Package=<5>
{{{


@@ 36,7 36,7 @@ Package=<4>

###############################################################################

Project: "libmp4av_st"="..\..\common\mp4av\libmp4av_st.dsp" - Package Owner=<4>
Project: "libmp4av_st"=..\..\common\mp4av\libmp4av_st.dsp - Package Owner=<4>

Package=<5>
{{{


@@ 48,7 48,7 @@ Package=<4>

###############################################################################

Project: "libmp4v2_st"="..\..\common\mp4v2\libmp4v2_st60.dsp" - Package Owner=<4>
Project: "libmp4v2_st"=..\..\common\mp4v2\libmp4v2_st60.dsp - Package Owner=<4>

Package=<5>
{{{

M plugins/QCDMp4/QCDMp4.rc => plugins/QCDMp4/QCDMp4.rc +24 -73
@@ 1,4 1,4 @@
//Microsoft Developer Studio generated resource script.
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"



@@ 13,7 13,7 @@
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// Dutch (Netherlands) resources
// ������(����) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NLD)
#ifdef _WIN32


@@ 27,18 27,18 @@ LANGUAGE LANG_DUTCH, SUBLANG_DUTCH
// TEXTINCLUDE
//

1 TEXTINCLUDE MOVEABLE PURE 
1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE MOVEABLE PURE 
2 TEXTINCLUDE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE MOVEABLE PURE 
3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"


@@ 52,27 52,32 @@ END
// Dialog
//

IDD_CONFIG_INPUT DIALOG DISCARDABLE  0, 0, 151, 108
STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
IDD_CONFIG DIALOGEX 0, 0, 233, 93
STYLE DS_SETFONT | DS_MODALFRAME | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | 
    WS_SYSMENU
CAPTION "Configuration"
FONT 8, "MS Sans Serif"
FONT 8, "MS Sans Serif", 0, 0, 0x0
BEGIN
    CONTROL         "Slider1",IDC_PRIORITY,"msctls_trackbar32",TBS_VERT | 
                    TBS_NOTICKS | WS_TABSTOP,13,15,18,46
    CONTROL         "16 bits",IDC_16BITS,"Button",BS_AUTORADIOBUTTON,77,18,
                    37,10
    CONTROL         "16 bits dithered",IDC_16BITS_DITHERED,"Button",
                    BS_AUTORADIOBUTTON,77,29,64,10
                    BS_AUTORADIOBUTTON | WS_DISABLED,77,29,64,10
    CONTROL         "24 bits",IDC_24BITS,"Button",BS_AUTORADIOBUTTON,77,40,
                    37,10
    CONTROL         "32 bits",IDC_32BITS,"Button",BS_AUTORADIOBUTTON,77,51,
                    37,10
    CONTROL         "Show errors",IDC_ERROR,"Button",BS_AUTOCHECKBOX | 
                    WS_TABSTOP,7,70,53,10
    CONTROL         "Downmix to stereo",IDC_DOWNMIX,"Button",BS_AUTOCHECKBOX | 
                    WS_TABSTOP,152,13,74,10
    CONTROL         "Use for AAC",IDC_USEFORAAC,"Button",BS_AUTOCHECKBOX | 
                    WS_TABSTOP,78,70,55,10
    PUSHBUTTON      "Cancel",IDCANCEL,7,87,50,14
    DEFPUSHBUTTON   "OK",IDOK,94,87,50,14
                    WS_TABSTOP,152,26,55,10
    CONTROL         "VBR Display",IDC_VBR,"Button",BS_AUTOCHECKBOX | 
                    WS_TABSTOP,152,39,55,10
    CONTROL         "Show errors",IDC_ERROR,"Button",BS_AUTOCHECKBOX | 
                    WS_TABSTOP,152,52,53,10
    DEFPUSHBUTTON   "OK",IDOK,176,72,50,14
    PUSHBUTTON      "Cancel",IDCANCEL,115,72,50,14
    GROUPBOX        "Priority",IDC_STATIC,7,7,57,58
    LTEXT           "Highest",IDC_STATIC,34,18,25,8
    LTEXT           "Normal",IDC_STATIC,34,35,23,8


@@ 80,38 85,6 @@ BEGIN
    GROUPBOX        "Resolution",IDC_STATIC,71,7,73,58
END

IDD_CONFIG_CONVERT DIALOG DISCARDABLE  0, 0, 306, 143
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "AAC to Mp4 Converter Setting"
FONT 8, "MS Sans Serif"
BEGIN
    DEFPUSHBUTTON   "&OK",IDOK,127,122,50,14
    PUSHBUTTON      "Select Oupput Folder",IDC_OUTPUTFOLDER,16,19,277,20
    GROUPBOX        "Ouput Folder",IDC_STATIC,7,7,292,39
    CONTROL         "You can convert AAC files to Mp4 files if you like.\n\nThis does not involve re-encoding, only the container format is changed.\n\nAdvantage Mp4 files are playable by a lot more players\nand they will have a lot of support in the future. ",
                    IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,7,62,
                    292,51
END

IDD_ABOUT DIALOG DISCARDABLE  0, 0, 191, 190
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "About MPEG-4 Plug-in"
FONT 8, "MS Sans Serif"
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,69,168,50,14
    CTEXT           "",IDC_PLUGINVER,29,94,133,16
    CTEXT           "",IDC_FAADVER,39,144,111,8
    LTEXT           "QCD Input Plug-in by",IDC_STATIC,61,118,67,8
    CONTROL         105,IDC_LOGO,"Static",SS_BITMAP | SS_CENTERIMAGE | 
                    SS_REALSIZEIMAGE,7,0,177,68
    LTEXT           "Shao Hao",IDC_MAIL1,78,132,33,8
    LTEXT           "M. Bakker",IDC_MAIL3,58,155,34,8
    LTEXT           "menno",IDC_MAIL2,108,155,22,8
    LTEXT           "&&",IDC_STATIC,96,155,8,8
    CTEXT           "AudioCoding.com MPEG-4 General Audio player\nCopyright 2002 AudioCoding.com",
                    IDC_STATIC,7,72,177,18
END


/////////////////////////////////////////////////////////////////////////////
//


@@ 119,41 92,19 @@ END
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO MOVEABLE PURE 
GUIDELINES DESIGNINFO 
BEGIN
    IDD_CONFIG_INPUT, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 144
        TOPMARGIN, 7
        BOTTOMMARGIN, 101
    END

    IDD_CONFIG_CONVERT, DIALOG
    IDD_CONFIG, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 299
        RIGHTMARGIN, 226
        TOPMARGIN, 7
        BOTTOMMARGIN, 136
    END

    IDD_ABOUT, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 184
        BOTTOMMARGIN, 183
        BOTTOMMARGIN, 86
    END
END
#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//

IDB_LOGO                BITMAP  DISCARDABLE     "logo.bmp"
#endif    // Dutch (Netherlands) resources
#endif    // ������(����) resources
/////////////////////////////////////////////////////////////////////////////



M plugins/QCDMp4/QCDMp4.sln => plugins/QCDMp4/QCDMp4.sln +15 -9
@@ 1,21 1,27 @@
Microsoft Visual Studio Solution File, Format Version 7.00
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "in_mp4", "in_mp4.vcproj", "{2D8F479D-A591-4502-9456-398425D5F834}"
Microsoft Visual Studio Solution File, Format Version 8.00
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QCDMp4", "QCDMp4.vcproj", "{2D8F479D-A591-4502-9456-398425D5F834}"
	ProjectSection(ProjectDependencies) = postProject
		{2398BB2F-FFF9-490B-B4CC-863F2D21AE46} = {2398BB2F-FFF9-490B-B4CC-863F2D21AE46}
		{8CAC9E26-BAA5-45A4-8721-E3170B3F8F49} = {8CAC9E26-BAA5-45A4-8721-E3170B3F8F49}
		{82CAD808-21AF-40A6-92EC-AE01AA67B413} = {82CAD808-21AF-40A6-92EC-AE01AA67B413}
	EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfaad", "..\..\libfaad\libfaad.vcproj", "{82CAD808-21AF-40A6-92EC-AE01AA67B413}"
	ProjectSection(ProjectDependencies) = postProject
	EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmp4v2_st", "..\..\common\mp4v2\libmp4v2_st60.vcproj", "{2398BB2F-FFF9-490B-B4CC-863F2D21AE46}"
	ProjectSection(ProjectDependencies) = postProject
	EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmp4av_st", "..\..\common\mp4av\libmp4av_st.vcproj", "{8CAC9E26-BAA5-45A4-8721-E3170B3F8F49}"
	ProjectSection(ProjectDependencies) = postProject
	EndProjectSection
EndProject
Global
	GlobalSection(SolutionConfiguration) = preSolution
		ConfigName.0 = Debug
		ConfigName.1 = Release
	EndGlobalSection
	GlobalSection(ProjectDependencies) = postSolution
		{2D8F479D-A591-4502-9456-398425D5F834}.0 = {82CAD808-21AF-40A6-92EC-AE01AA67B413}
		{2D8F479D-A591-4502-9456-398425D5F834}.1 = {8CAC9E26-BAA5-45A4-8721-E3170B3F8F49}
		{2D8F479D-A591-4502-9456-398425D5F834}.2 = {2398BB2F-FFF9-490B-B4CC-863F2D21AE46}
		Debug = Debug
		Release = Release
	EndGlobalSection
	GlobalSection(ProjectConfiguration) = postSolution
		{2D8F479D-A591-4502-9456-398425D5F834}.Debug.ActiveCfg = Debug|Win32

M plugins/QCDMp4/QCDMp4.vcproj => plugins/QCDMp4/QCDMp4.vcproj +71 -54
@@ 1,8 1,8 @@
<?xml version="1.0" encoding = "Windows-1252"?>
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
	ProjectType="Visual C++"
	Version="7.00"
	Name="in_mp4"
	Version="7.10"
	Name="QCDMp4"
	SccProjectName=""
	SccLocalPath="">
	<Platforms>


@@ 25,44 25,34 @@
				BasicRuntimeChecks="0"
				RuntimeLibrary="1"
				UsePrecompiledHeader="2"
				PrecompiledHeaderFile=".\Debug/in_mp4.pch"
				PrecompiledHeaderFile=".\Debug/QCDMp4.pch"
				AssemblerListingLocation=".\Debug/"
				ObjectFile=".\Debug/"
				ProgramDataBaseFileName=".\Debug/"
				WarningLevel="3"
				SuppressStartupBanner="TRUE"
				DebugInformationFormat="4"
				CompileAs="0">
				<IntelOptions
					Optimization="0"
					MinimalRebuild="1"
					BasicRuntimeChecks="0"
					RuntimeLibrary="1"
					AllOptions="/c  /I &quot;..\..\include&quot; /I &quot;..\..\common\mp4v2&quot; /I &quot;..\..\common\mp4av&quot; /ZI /nologo /W3 /Od /D &quot;WIN32&quot; /D &quot;_DEBUG&quot; /D &quot;_WINDOWS&quot; /D &quot;_WINDLL&quot; /Gm /EHsc /MTd /YX&quot;StdAfx.h&quot; /Fp&quot;.\Debug/in_mp4.pch&quot; /Fo&quot;.\Debug/&quot; /Fd&quot;.\Debug/&quot; /Gd"/>
			</Tool>
				CompileAs="0"/>
			<Tool
				Name="VCCustomBuildTool"/>
			<Tool
				Name="VCLinkerTool"
				AdditionalOptions="/MACHINE:I386"
				AdditionalDependencies="ws2_32.lib odbc32.lib odbccp32.lib"
				OutputFile=".\Debug/in_mp4.dll"
				OutputFile=".\Debug/QCDMp4.dll"
				LinkIncremental="2"
				SuppressStartupBanner="TRUE"
				GenerateDebugInformation="TRUE"
				ProgramDatabaseFile=".\Debug/in_mp4.pdb"
				ProgramDatabaseFile=".\Debug/QCDMp4.pdb"
				SubSystem="2"
				ImportLibrary=".\Debug/in_mp4.lib">
				<IntelOptions
					AllOptions="/NOLOGO /DLL /OUT:&quot;.\Debug/in_mp4.dll&quot; /INCREMENTAL ws2_32.lib odbc32.lib odbccp32.lib /DEBUG /PDB:&quot;.\Debug/in_mp4.pdb&quot; /SUBSYSTEM:WINDOWS /TLBID:1 /IMPLIB:&quot;.\Debug/in_mp4.lib&quot; /MACHINE:I386 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib"/>
			</Tool>
				ImportLibrary=".\Debug/QCDMp4.lib"/>
			<Tool
				Name="VCMIDLTool"
				PreprocessorDefinitions="_DEBUG"
				MkTypLibCompatible="TRUE"
				SuppressStartupBanner="TRUE"
				TargetEnvironment="1"
				TypeLibraryName=".\Debug/in_mp4.tlb"/>
				TypeLibraryName=".\Debug/QCDMp4.tlb"/>
			<Tool
				Name="VCPostBuildEventTool"/>
			<Tool


@@ 76,7 66,13 @@
			<Tool
				Name="VCWebServiceProxyGeneratorTool"/>
			<Tool
				Name="VCXMLDataGeneratorTool"/>
			<Tool
				Name="VCWebDeploymentTool"/>
			<Tool
				Name="VCManagedWrapperGeneratorTool"/>
			<Tool
				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
		</Configuration>
		<Configuration
			Name="Release|Win32"


@@ 98,46 94,32 @@
				RuntimeLibrary="0"
				EnableFunctionLevelLinking="TRUE"
				UsePrecompiledHeader="2"
				PrecompiledHeaderFile=".\Release/in_mp4.pch"
				PrecompiledHeaderFile=".\Release/QCDMp4.pch"
				AssemblerListingLocation=".\Release/"
				ObjectFile=".\Release/"
				ProgramDataBaseFileName=".\Release/"
				WarningLevel="3"
				SuppressStartupBanner="TRUE"
				CompileAs="0">
				<IntelOptions
					Optimization="2"
					GlobalOptimizations="1"
					InlineFuncExpansion="1"
					OmitFramePtrs="1"
					StringPooling="1"
					RuntimeLibrary="0"
					BufferSecurityCheck="1"
					FunctionLevelLinking="1"
					AllOptions="/c  /I &quot;..\..\include&quot; /I &quot;..\..\common\mp4v2&quot; /I &quot;..\..\common\mp4av&quot; /nologo /W3 /O2 /Og /Ob1 /Oy /D &quot;WIN32&quot; /D &quot;NDEBUG&quot; /D &quot;_WINDOWS&quot; /D &quot;_WINDLL&quot; /GF /FD /EHsc /MT /GS /Gy /YX&quot;StdAfx.h&quot; /Fp&quot;.\Release/in_mp4.pch&quot; /Fo&quot;.\Release/&quot; /Fd&quot;.\Release/&quot; /Gd"/>
			</Tool>
				CompileAs="0"/>
			<Tool
				Name="VCCustomBuildTool"/>
			<Tool
				Name="VCLinkerTool"
				AdditionalOptions="/MACHINE:I386"
				AdditionalDependencies="ws2_32.lib"
				OutputFile=".\Release/in_mp4.dll"
				OutputFile=".\Release/QCDMp4.dll"
				LinkIncremental="1"
				SuppressStartupBanner="TRUE"
				ProgramDatabaseFile=".\Release/in_mp4.pdb"
				ProgramDatabaseFile=".\Release/QCDMp4.pdb"
				SubSystem="2"
				ImportLibrary=".\Release/in_mp4.lib">
				<IntelOptions
					AllOptions="/NOLOGO /DLL /OUT:&quot;.\Release/in_mp4.dll&quot; /INCREMENTAL:NO ws2_32.lib /PDB:&quot;.\Release/in_mp4.pdb&quot; /SUBSYSTEM:WINDOWS /TLBID:1 /IMPLIB:&quot;.\Release/in_mp4.lib&quot; /MACHINE:I386 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib"/>
			</Tool>
				ImportLibrary=".\Release/QCDMp4.lib"/>
			<Tool
				Name="VCMIDLTool"
				PreprocessorDefinitions="NDEBUG"
				MkTypLibCompatible="TRUE"
				SuppressStartupBanner="TRUE"
				TargetEnvironment="1"
				TypeLibraryName=".\Release/in_mp4.tlb"/>
				TypeLibraryName=".\Release/QCDMp4.tlb"/>
			<Tool
				Name="VCPostBuildEventTool"/>
			<Tool


@@ 151,50 133,85 @@
			<Tool
				Name="VCWebServiceProxyGeneratorTool"/>
			<Tool
				Name="VCXMLDataGeneratorTool"/>
			<Tool
				Name="VCWebDeploymentTool"/>
			<Tool
				Name="VCManagedWrapperGeneratorTool"/>
			<Tool
				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
		</Configuration>
	</Configurations>
	<References>
	</References>
	<Files>
		<Filter
			Name="Source Files"
			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
			<File
				RelativePath="aac2mp4.cpp"/>
				RelativePath="aac2mp4.cpp">
			</File>
			<File
				RelativePath="aacinfo.c">
			</File>
			<File
				RelativePath="aacinfo.c"/>
				RelativePath=".\config.c">
			</File>
			<File
				RelativePath=".\config.c"/>
				RelativePath=".\QCDMp4.c">
			</File>
			<File
				RelativePath=".\in_mp4.c"/>
				RelativePath=".\QCDMp4Tag.cpp">
			</File>
			<File
				RelativePath=".\utils.c"/>
				RelativePath=".\utils.c">
			</File>
		</Filter>
		<Filter
			Name="Header Files"
			Filter="h;hpp;hxx;hm;inl">
			<File
				RelativePath="aac2mp4.h"/>
				RelativePath="aac2mp4.h">
			</File>
			<File
				RelativePath="aacinfo.h">
			</File>
			<File
				RelativePath=".\config.h">
			</File>
			<File
				RelativePath="..\..\include\faad.h">
			</File>
			<File
				RelativePath="aacinfo.h"/>
				RelativePath=".\QCDInputDLL.h">
			</File>
			<File
				RelativePath=".\config.h"/>
				RelativePath=".\QCDModDefs.h">
			</File>
			<File
				RelativePath="..\..\include\faad.h"/>
				RelativePath=".\QCDModInput.h">
			</File>
			<File
				RelativePath=".\in2.h"/>
				RelativePath=".\QCDModTagEditor.h">
			</File>
			<File
				RelativePath=".\out.h"/>
				RelativePath=".\QCDTagsDLL.h">
			</File>
			<File
				RelativePath="resource.h"/>
				RelativePath="resource.h">
			</File>
			<File
				RelativePath=".\utils.h"/>
				RelativePath=".\utils.h">
			</File>
		</Filter>
		<Filter
			Name="Resource Files"
			Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
			<File
				RelativePath=".\in_mp4.rc"/>
				RelativePath=".\QCDMp4.rc">
			</File>
		</Filter>
	</Files>
	<Globals/>
	<Globals>
	</Globals>
</VisualStudioProject>

A plugins/QCDMp4/QCDMp4Tag.cpp => plugins/QCDMp4/QCDMp4Tag.cpp +511 -0
@@ 0,0 1,511 @@
#include <mp4.h>
#include <faad.h>
#include "QCDTagsDLL.h"


//..............................................................................
// Global Variables

QCDModInitTag	ModInitTag;
BOOL uSetDlgItemText(void *tagHandle, int fieldId, const char *str);
UINT uGetDlgItemText(void *tagHandle, int fieldId, char *str, int max);

//------------------------------------------------------------------------------

PLUGIN_API QCDModInitTag* TAGEDITORDLL_ENTRY_POINT()
{	
	ModInitTag.size			= sizeof(QCDModInitTag);
	ModInitTag.version		= PLUGIN_API_VERSION;
	ModInitTag.ShutDown		= ShutDown_Tag;

	ModInitTag.Read			= Read_Tag;
	ModInitTag.Write		= Write_Tag;	// Leave null for operations that plugin does not support
	ModInitTag.Strip		= Strip_Tag;	// ie: if plugin only reads tags, leave Write and Strip null

	ModInitTag.description	= "MP4 Tags";
	ModInitTag.defaultexts	= "MP4:M4A";

	return &ModInitTag;
}

//-----------------------------------------------------------------------------

void ShutDown_Tag(int flags)
{
	// TODO:
	// prepare plugin to be unloaded. All allocations should be freed.
	// flags param is unused
}

//-----------------------------------------------------------------------------

bool Read_Tag(LPCSTR filename, void* tagHandle)
{
	// TODO:
	// read metadata from tag and set each field to tagHandle
	// only TAGFIELD_* are supported (see QCDModTagEditor.h)

	// example of how to set value to tagHandle
	// use SetFieldA for ASCII or MultiBytes strings.
	// use SetFieldW for UNICODE strings
	//
	//	ModInitTag.SetFieldW(tagHandle, TAGFIELD_COMPOSER, szwValue);

	// return true for successfull read, false for failure

	MP4FileHandle file = MP4_INVALID_FILE_HANDLE;
	char *pVal, dummy1[1024];
	short dummy, dummy2;

	unsigned __int32 valueSize = 0;

#ifdef DEBUG_OUTPUT
	in_mp4_DebugOutput("mp4_tag_read");
#endif

	file = MP4Read(filename, 0);

	if (file == MP4_INVALID_FILE_HANDLE)
		return false;

	/* get Metadata */

	pVal = NULL;
	MP4GetMetadataName(file, &pVal);
	uSetDlgItemText(tagHandle, TAGFIELD_TITLE, pVal);

	pVal = NULL;
	MP4GetMetadataArtist(file, &pVal);
	uSetDlgItemText(tagHandle, TAGFIELD_ARTIST, pVal);

	pVal = NULL;
	MP4GetMetadataWriter(file, &pVal);
	uSetDlgItemText(tagHandle, TAGFIELD_COMPOSER, pVal);

	pVal = NULL;
	MP4GetMetadataComment(file, &pVal);
	uSetDlgItemText(tagHandle, TAGFIELD_COMMENT, pVal);

	pVal = NULL;
	MP4GetMetadataAlbum(file, &pVal);
	uSetDlgItemText(tagHandle, TAGFIELD_ALBUM, pVal);

	pVal = NULL;
	MP4GetMetadataGenre(file, &pVal);
	uSetDlgItemText(tagHandle, TAGFIELD_GENRE, pVal);

	//dummy = 0;
	//MP4GetMetadataTempo(file, &dummy);
	//wsprintf(dummy1, "%d", dummy);
	//SetDlgItemText(hwndDlg,IDC_METATEMPO, dummy1);

	dummy = 0; dummy2 = 0;
	MP4GetMetadataTrack(file, (unsigned __int16*)&dummy, (unsigned __int16*)&dummy2);
	wsprintf(dummy1, "%d", dummy);
	ModInitTag.SetFieldA(tagHandle, TAGFIELD_TRACK, dummy1);
	//wsprintf(dummy1, "%d", dummy2);
	//SetDlgItemText(hwndDlg,IDC_METATRACK2, dummy1);

	//dummy = 0; dummy2 = 0;
	//MP4GetMetadataDisk(file, &dummy, &dummy2);
	//wsprintf(dummy1, "%d", dummy);
	//SetDlgItemText(hwndDlg,IDC_METADISK1, dummy1);
	//wsprintf(dummy1, "%d", dummy2);
	//SetDlgItemText(hwndDlg,IDC_METADISK2, dummy1);

	pVal = NULL;
	MP4GetMetadataYear(file, &pVal);
	uSetDlgItemText(tagHandle, TAGFIELD_YEAR, pVal);

	//dummy3 = 0;
	//MP4GetMetadataCompilation(file, &dummy3);
	//if (dummy3)
	//	SendMessage(GetDlgItem(hwndDlg, IDC_METACOMPILATION), BM_SETCHECK, BST_CHECKED, 0);

	pVal = NULL;
	MP4GetMetadataTool(file, &pVal);
	uSetDlgItemText(tagHandle, TAGFIELD_ENCODER, pVal);

	pVal = NULL;
	MP4GetMetadataFreeForm(file, "CONDUCTOR", (unsigned __int8**)&pVal, &valueSize);
	uSetDlgItemText(tagHandle, TAGFIELD_CONDUCTOR, pVal);

	pVal = NULL;
	MP4GetMetadataFreeForm(file, "ORCHESTRA", (unsigned __int8**)&pVal, &valueSize);
	uSetDlgItemText(tagHandle, TAGFIELD_ORCHESTRA, pVal);

	pVal = NULL;
	MP4GetMetadataFreeForm(file, "YEARCOMPOSED", (unsigned __int8**)&pVal, &valueSize);
	uSetDlgItemText(tagHandle, TAGFIELD_YEARCOMPOSED, pVal);

	pVal = NULL;
	MP4GetMetadataFreeForm(file, "ORIGARTIST", (unsigned __int8**)&pVal, &valueSize);
	uSetDlgItemText(tagHandle, TAGFIELD_ORIGARTIST, pVal);

	pVal = NULL;
	MP4GetMetadataFreeForm(file, "LABEL", (unsigned __int8**)&pVal, &valueSize);
	uSetDlgItemText(tagHandle, TAGFIELD_LABEL, pVal);

	pVal = NULL;
	MP4GetMetadataFreeForm(file, "COPYRIGHT", (unsigned __int8**)&pVal, &valueSize);
	uSetDlgItemText(tagHandle, TAGFIELD_COPYRIGHT, pVal);

	pVal = NULL;
	MP4GetMetadataFreeForm(file, "CDDBTAGID", (unsigned __int8**)&pVal, &valueSize);
	uSetDlgItemText(tagHandle, TAGFIELD_CDDBTAGID, pVal);

	/* ! Metadata */

	MP4Close(file);

	return true;
}

//-----------------------------------------------------------------------------

bool Write_Tag(LPCSTR filename, void* tagHandle)
{
	// TODO:
	// read metadata from tagHandle and set each field to supported tag
	// only TAGFIELD_* are supported (see QCDModTagEditor.h)

	// example of how to get value from tagHandle
	// use SetFieldA for ASCII or MultiBytes strings.
	// use SetFieldW for UNICODE strings
	//
	// szwValue = ModInitTag.GetFieldW(tagHandle, TAGFIELD_ORCHESTRA);

	// write tag to file

	MP4FileHandle file = MP4_INVALID_FILE_HANDLE;
    char dummy1[1024];
    short dummy, dummy2;

#ifdef DEBUG_OUTPUT
    in_mp4_DebugOutput("mp4_tag_write");
#endif

	/* save Metadata changes */

	file = MP4Modify(filename, 0, 0);
	if (file == MP4_INVALID_FILE_HANDLE)
		return false;

	uGetDlgItemText(tagHandle, TAGFIELD_TITLE, dummy1, 1024);
	MP4SetMetadataName(file, dummy1);

	uGetDlgItemText(tagHandle, TAGFIELD_COMPOSER, dummy1, 1024);
	MP4SetMetadataWriter(file, dummy1);

	uGetDlgItemText(tagHandle, TAGFIELD_ARTIST, dummy1, 1024);
	MP4SetMetadataArtist(file, dummy1);

	uGetDlgItemText(tagHandle, TAGFIELD_ALBUM, dummy1, 1024);
	MP4SetMetadataAlbum(file, dummy1);

	uGetDlgItemText(tagHandle, TAGFIELD_COMMENT, dummy1, 1024);
	MP4SetMetadataComment(file, dummy1);

	uGetDlgItemText(tagHandle, TAGFIELD_GENRE, dummy1, 1024);
	MP4SetMetadataGenre(file, dummy1);

	uGetDlgItemText(tagHandle, TAGFIELD_YEAR, dummy1, 1024);
	MP4SetMetadataYear(file, dummy1);

	dummy = 0; dummy2 = 0;
	MP4GetMetadataTrack(file, (unsigned __int16*)&dummy, (unsigned __int16*)&dummy2);
	memcpy(dummy1, ModInitTag.GetFieldA(tagHandle, TAGFIELD_TRACK), sizeof(dummy1));
	//GetDlgItemText(hwndDlg, IDC_METATRACK1, dummy1, 1024);
	dummy = atoi(dummy1);
	//GetDlgItemText(hwndDlg, IDC_METATRACK2, dummy1, 1024);
	//dummy2 = atoi(dummy1);
	MP4SetMetadataTrack(file, dummy, dummy2);

	//GetDlgItemText(hwndDlg, IDC_METADISK1, dummy1, 1024);
	//dummy = atoi(dummy1);
	//GetDlgItemText(hwndDlg, IDC_METADISK2, dummy1, 1024);
	//dummy2 = atoi(dummy1);
	//MP4SetMetadataDisk(file, dummy, dummy2);

	//GetDlgItemText(hwndDlg, IDC_METATEMPO, dummy1, 1024);
	//dummy = atoi(dummy1);
	//MP4SetMetadataTempo(file, dummy);

	//dummy3 = SendMessage(GetDlgItem(hwndDlg, IDC_METACOMPILATION), BM_GETCHECK, 0, 0);
	//MP4SetMetadataCompilation(file, dummy3);

	uGetDlgItemText(tagHandle, TAGFIELD_ENCODER, dummy1, 1024);
	MP4SetMetadataTool(file, dummy1);

	uGetDlgItemText(tagHandle, TAGFIELD_CONDUCTOR, dummy1, 1024);
	MP4SetMetadataFreeForm(file, "CONDUCTOR", (unsigned __int8*)dummy1, strlen(dummy1) + 1);

	uGetDlgItemText(tagHandle, TAGFIELD_ORCHESTRA, dummy1, 1024);
	MP4SetMetadataFreeForm(file, "ORCHESTRA", (unsigned __int8*)dummy1, strlen(dummy1) + 1);

	uGetDlgItemText(tagHandle, TAGFIELD_YEARCOMPOSED, dummy1, 1024);
	MP4SetMetadataFreeForm(file, "YEARCOMPOSED", (unsigned __int8*)dummy1, strlen(dummy1) + 1);

	uGetDlgItemText(tagHandle, TAGFIELD_ORIGARTIST, dummy1, 1024);
	MP4SetMetadataFreeForm(file, "ORIGARTIST", (unsigned __int8*)dummy1, strlen(dummy1) + 1);

	uGetDlgItemText(tagHandle, TAGFIELD_LABEL, dummy1, 1024);
	MP4SetMetadataFreeForm(file, "LABEL", (unsigned __int8*)dummy1, strlen(dummy1) + 1);

	uGetDlgItemText(tagHandle, TAGFIELD_COPYRIGHT, dummy1, 1024);
	MP4SetMetadataFreeForm(file, "COPYRIGHT", (unsigned __int8*)dummy1, strlen(dummy1) + 1);

	uGetDlgItemText(tagHandle, TAGFIELD_CDDBTAGID, dummy1, 1024);
	MP4SetMetadataFreeForm(file, "CDDBTAGID", (unsigned __int8*)dummy1, strlen(dummy1) + 1);

	MP4Close(file);

	MP4Optimize(filename, NULL, 0);
	/* ! */

	return true;
}

//-----------------------------------------------------------------------------

bool Strip_Tag(LPCSTR filename)
{
	// TODO:
	// remove tag from file.
	// do whatever is need to remove the supported tag from filename

	// return true for successfull strip, false for failure

	MP4FileHandle file;

	file = MP4Modify(filename, 0, 0);
	if (file == MP4_INVALID_FILE_HANDLE)
		return false;
	
	MP4MetadataDelete(file);

	MP4Close(file);

	return true;
}

//-----------------------------------------------------------------------------

/* Convert UNICODE to UTF-8
   Return number of bytes written */
int unicodeToUtf8 ( const WCHAR* lpWideCharStr, char* lpMultiByteStr, int cwcChars )
{
    const unsigned short*   pwc = (unsigned short *)lpWideCharStr;
    unsigned char*          pmb = (unsigned char  *)lpMultiByteStr;
    const unsigned short*   pwce;
    size_t  cBytes = 0;

    if ( cwcChars >= 0 ) {
        pwce = pwc + cwcChars;
    } else {
        pwce = (unsigned short *)((size_t)-1);
    }

    while ( pwc < pwce ) {
        unsigned short  wc = *pwc++;

        if ( wc < 0x00000080 ) {
            *pmb++ = (char)wc;
            cBytes++;
        } else
        if ( wc < 0x00000800 ) {
            *pmb++ = (char)(0xC0 | ((wc >>  6) & 0x1F));
            cBytes++;
            *pmb++ = (char)(0x80 |  (wc        & 0x3F));
            cBytes++;
        } else
        if ( wc < 0x00010000 ) {
            *pmb++ = (char)(0xE0 | ((wc >> 12) & 0x0F));
            cBytes++;
            *pmb++ = (char)(0x80 | ((wc >>  6) & 0x3F));
            cBytes++;
            *pmb++ = (char)(0x80 |  (wc        & 0x3F));
            cBytes++;
        }
        if ( wc == L'\0' )
            return cBytes;
    }

    return cBytes;
}

/* Convert UTF-8 coded string to UNICODE
   Return number of characters converted */
int utf8ToUnicode ( const char* lpMultiByteStr, WCHAR* lpWideCharStr, int cmbChars )
{
    const unsigned char*    pmb = (unsigned char  *)lpMultiByteStr;
    unsigned short*         pwc = (unsigned short *)lpWideCharStr;
    const unsigned char*    pmbe;
    size_t  cwChars = 0;

    if ( cmbChars >= 0 ) {
        pmbe = pmb + cmbChars;
    } else {
        pmbe = (unsigned char *)((size_t)-1);
    }

    while ( pmb < pmbe ) {
        char            mb = *pmb++;
        unsigned int    cc = 0;
        unsigned int    wc;

        while ( (cc < 7) && (mb & (1 << (7 - cc)))) {
            cc++;
        }

        if ( cc == 1 || cc > 6 )                    // illegal character combination for UTF-8
            continue;

        if ( cc == 0 ) {
            wc = mb;
        } else {
            wc = (mb & ((1 << (7 - cc)) - 1)) << ((cc - 1) * 6);
            while ( --cc > 0 ) {
                if ( pmb == pmbe )                  // reached end of the buffer
                    return cwChars;
                mb = *pmb++;
                if ( ((mb >> 6) & 0x03) != 2 )      // not part of multibyte character
                    return cwChars;
                wc |= (mb & 0x3F) << ((cc - 1) * 6);
            }
        }

        if ( wc & 0xFFFF0000 )
            wc = L'?';
        *pwc++ = wc;
        cwChars++;
        if ( wc == L'\0' )
            return cwChars;
    }

    return cwChars;
}

/* convert Windows ANSI to UTF-8 */
int ConvertANSIToUTF8 ( const char* ansi, char* utf8 )
{
    WCHAR*  wszValue;          // Unicode value
    size_t  ansi_len;
    size_t  len;

    *utf8 = '\0';
    if ( ansi == NULL )
        return 0;

    ansi_len = strlen ( ansi );

    if ( (wszValue = (WCHAR *)malloc ( (ansi_len + 1) * 2 )) == NULL )
        return 0;

    /* Convert ANSI value to Unicode */
    if ( (len = MultiByteToWideChar ( CP_ACP, 0, ansi, ansi_len + 1, wszValue, (ansi_len + 1) * 2 )) == 0 ) {
        free ( wszValue );
        return 0;
    }

    /* Convert Unicode value to UTF-8 */
    if ( (len = unicodeToUtf8 ( wszValue, utf8, -1 )) == 0 ) {
        free ( wszValue );
        return 0;
    }

    free ( wszValue );

    return len-1;
}

/* convert UTF-8 to Windows ANSI */
int ConvertUTF8ToANSI ( const char* utf8, char* ansi )
{
    WCHAR*  wszValue;          // Unicode value
    size_t  utf8_len;
    size_t  len;

    *ansi = '\0';
    if ( utf8 == NULL )
        return 0;

    utf8_len = strlen ( utf8 );

    if ( (wszValue = (WCHAR *)malloc ( (utf8_len + 1) * 2 )) == NULL )
        return 0;

    /* Convert UTF-8 value to Unicode */
    if ( (len = utf8ToUnicode ( utf8, wszValue, utf8_len + 1 )) == 0 ) {
        free ( wszValue );
        return 0;
    }

    /* Convert Unicode value to ANSI */
    if ( (len = WideCharToMultiByte ( CP_ACP, 0, wszValue, -1, ansi, (utf8_len + 1) * 2, NULL, NULL )) == 0 ) {
        free ( wszValue );
        return 0;
    }

    free ( wszValue );

    return len-1;
}

BOOL uSetDlgItemText(void *tagHandle, int fieldId, const char *str)
{
    char *temp;
    size_t len;
    int r;

    if (!str) return FALSE;
    len = strlen(str);
    temp = (char *)malloc(len+1);
    if (!temp) return FALSE;
	memset(temp, '\0', len+1);
    r = ConvertUTF8ToANSI(str, temp);
    if (r > 0)
        ModInitTag.SetFieldA(tagHandle, fieldId, temp);
    free(temp);

    return r>0 ? TRUE : FALSE;
}

UINT uGetDlgItemText(void *tagHandle, int fieldId, char *str, int max)
{
    char *temp, *utf8;;
    int len;

	const char *p;

    if (!str) return 0;
    len = strlen( ModInitTag.GetFieldA(tagHandle, fieldId) );
    temp = (char *)malloc(len+1);
    if (!temp) return 0;
    utf8 = (char *)malloc((len+1)*4);
    if (!utf8)
    {
        free(temp);
        return 0;
    }

	memset(temp, '\0', len+1);
	memset(utf8, '\0', (len+1)*4);
	p = ModInitTag.GetFieldA(tagHandle, fieldId);
	memcpy(temp, p, len+1);
    if (len > 0)
    {
        len = ConvertANSIToUTF8(temp, utf8);
        if (len > max-1)
        {
            len = max-1;
            utf8[max] = '\0';
        }
        memcpy(str, utf8, len+1);
    }

    free(temp);
    free(utf8);

    return len;
}

A plugins/QCDMp4/QCDTagsDLL.h => plugins/QCDMp4/QCDTagsDLL.h +14 -0
@@