| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723 | #define PLUGIN_VERSION L"2.70"#include "Main.h"#include <windows.h>#include <stdio.h>#include <locale.h>#include "resource.h"#include "../Winamp/in2.h"#include "../Winamp/wa_ipc.h"#include "../nu/AutoChar.h"#include "api__in_mp4.h"#include <api/service/waservicefactory.h>#pragma warning(disable:4786)#include "mpeg4audio.h"#include <shlwapi.h>#include <malloc.h>#include "VirtualIO.h"#include "AlbumArt.h"#include <assert.h>#include "../in_wmvdrm/Remaining.h"#include "VideoThread.h"#include "RawMediaReader.h"#include "../nu/Singleton.h"#include <strsafe.h>Remaining remaining;nu::VideoClock video_clock;AlbumArtFactory albumArtFactory;wchar_t m_ini[MAX_PATH] = {0};int infoDlg(const wchar_t *fn, HWND hwnd);#define WM_WA_IPC WM_USER#define WM_WA_MPEG_EOF WM_USER+2HANDLE hThread;static DWORD WINAPI playProc(LPVOID lpParameter);static int paused, m_kill;HANDLE killEvent, seekEvent, pauseEvent;static int m_opened;static RawMediaReaderService raw_media_reader_service;static SingletonServiceFactory<svc_raw_media_reader, RawMediaReaderService> raw_factory;static void stop();// wasabi based services for localisation supportapi_language *WASABI_API_LNG = 0;HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;api_config *AGAVE_API_CONFIG = 0;api_memmgr *WASABI_API_MEMMGR = 0;api_application *WASABI_API_APP = 0;int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message){	MSGBOXPARAMS msgbx = {sizeof(MSGBOXPARAMS),0};	msgbx.lpszText = message;	msgbx.lpszCaption = title;	msgbx.lpszIcon = MAKEINTRESOURCE(102);	msgbx.hInstance = GetModuleHandle(0);	msgbx.dwStyle = MB_USERICON;	msgbx.hwndOwner = parent;	return MessageBoxIndirect(&msgbx);}void about(HWND hwndParent){	wchar_t message[1024] = {0}, text[1024] = {0};	WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_MPEG4_AUDIO_DECODER_OLD,text,1024);	StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),					 mod.description, TEXT(__DATE__));	DoAboutMessageBox(hwndParent,text,message);}static const wchar_t defaultExtensions_nonpro[] = {L"M4A;MP4"};static const wchar_t defaultExtensions_pro[] = {L"M4A;MP4;M4V"};const wchar_t *defaultExtensions = defaultExtensions_pro;// the return pointer has been malloc'd.  Use free() when you are done.char *BuildExtensions(const char *extensions){	char name[64] = {0};	WASABI_API_LNGSTRING_BUF(IDS_MP4_FILE,name,64);	size_t length = strlen(extensions) + 1 + strlen(name) + 2;	char *newExt = (char *)calloc(length, sizeof(char));	char *ret = newExt; // save because we modify newExt	// copy extensions	StringCchCopyExA(newExt, length, extensions, &newExt, &length, 0);	newExt++;	length--;	// copy description	StringCchCopyExA(newExt, length, name, &newExt, &length, 0);	newExt++;	length--;	// double null terminate	assert(length == 1);	*newExt = 0;	return ret;}int init(){	if (!IsWindow(mod.hMainWindow))		return IN_INIT_FAILURE;	killEvent = CreateEvent(NULL, TRUE, FALSE, NULL);	seekEvent = CreateEvent(NULL, TRUE, FALSE, NULL);	pauseEvent = CreateEvent(NULL, TRUE, TRUE, NULL);	mod.service->service_register(&albumArtFactory);	raw_factory.Register(mod.service, &raw_media_reader_service);	waServiceFactory *sf = mod.service->service_getServiceByGuid(AgaveConfigGUID);	if (sf)		AGAVE_API_CONFIG = (api_config *)sf->getInterface();	sf = mod.service->service_getServiceByGuid(applicationApiServiceGuid);	if (sf)		WASABI_API_APP = (api_application *)sf->getInterface();	sf = mod.service->service_getServiceByGuid(memMgrApiServiceGuid);	if (sf)		WASABI_API_MEMMGR = (api_memmgr *)sf->getInterface();	sf = mod.service->service_getServiceByGuid(DownloadManagerGUID);	if (sf)		WAC_API_DOWNLOADMANAGER = (api_downloadManager *)sf->getInterface();	sf = mod.service->service_getServiceByGuid(ThreadPoolGUID);	if (sf)		WASABI_API_THREADPOOL = (api_threadpool *)sf->getInterface();	// loader so that we can get the localisation service api for use	sf = mod.service->service_getServiceByGuid(languageApiGUID);	if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());	// need to have this initialised before we try to do anything with localisation features	WASABI_API_START_LANG(mod.hDllInstance,InMp4LangGUID);	static wchar_t szDescription[256];	StringCchPrintfW(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_MPEG4_AUDIO_DECODER),PLUGIN_VERSION);	mod.description = (char*)szDescription;	const wchar_t *inipath = (wchar_t *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW);	PathCombineW(m_ini, inipath, L"Plugins");	CreateDirectoryW(m_ini, NULL);	PathAppendW(m_ini, L"in_mp4.ini");	wchar_t exts[1024] = {0};	GetPrivateProfileStringW(L"in_mp4", L"extensionlist", defaultExtensions, exts, 1024, m_ini);	mod.FileExtensions = BuildExtensions(AutoChar(exts));	return IN_INIT_SUCCESS;}void quit(){	CloseHandle(killEvent);	CloseHandle(seekEvent);	raw_factory.Deregister(mod.service);	waServiceFactory *sf = mod.service->service_getServiceByGuid(AgaveConfigGUID);	if (sf)		sf->releaseInterface(AGAVE_API_CONFIG);	sf = mod.service->service_getServiceByGuid(applicationApiServiceGuid);	if (sf)		sf->releaseInterface(WASABI_API_APP);	sf = mod.service->service_getServiceByGuid(DownloadManagerGUID);	if (sf)		sf->releaseInterface( WAC_API_DOWNLOADMANAGER );	mod.service->service_deregister(&albumArtFactory);	free(mod.FileExtensions);}int isourfile(const wchar_t *fn){	return 0;}void config(HWND hwndParent);void setoutputtime(int time_in_ms){	m_needseek = time_in_ms;	SetEvent(seekEvent);}MP4TrackId GetVideoTrack(MP4FileHandle infile){	int numTracks = MP4GetNumberOfTracks(infile, NULL,      /* subType */ 0);	for (int i = 0; i < numTracks; i++)	{		MP4TrackId trackId = MP4FindTrackId(infile, i, NULL,      /* subType */ 0);		const char* trackType = MP4GetTrackType(infile, trackId);		if (!lstrcmpA(trackType, MP4_VIDEO_TRACK_TYPE))			return trackId;	}	/* can't decode this */	return MP4_INVALID_TRACK_ID;}MP4TrackId GetAudioTrack(MP4FileHandle infile){	int ret = MP4_INVALID_TRACK_ID;	__try	{			/* find AAC track */	int numTracks = MP4GetNumberOfTracks(infile, NULL,      /* subType */ 0);	for (int i = 0; i < numTracks; i++)	{		MP4TrackId trackId = MP4FindTrackId(infile, i, NULL,      /* subType */ 0);		if (trackId != MP4_INVALID_TRACK_ID)		{			const char* trackType = MP4GetTrackType(infile, trackId);			if (trackType && !lstrcmpA(trackType, MP4_AUDIO_TRACK_TYPE))				return trackId;		}	}	/* can't decode this */	return MP4_INVALID_TRACK_ID;	}	__except(EXCEPTION_EXECUTE_HANDLER)	{		return MP4_INVALID_TRACK_ID;	}	return ret;}MP4SampleId numSamples, numVideoSamples;MP4FileHandle MP4hFile;MP4TrackId audio_track, video_track;double m_length;volatile int m_needseek = -1;unsigned int audio_srate, audio_nch, audio_bps, audio_bitrate=0;unsigned int video_bitrate=0;wchar_t lastfn[MAX_PATH*4] = L"";MP4AudioDecoder *audio = 0;waServiceFactory *audioFactory = 0, *videoFactory = 0;MP4VideoDecoder *video = 0;uint32_t m_timescale = 0, m_video_timescale = 0;static void *reader = 0;bool audio_chunk = false;enum{	READER_UNICODE=0,	READER_HTTP=1,};int reader_type=READER_UNICODE;bool open_mp4(const wchar_t *fn){	audio = 0;	video = 0;	if (!_wcsnicmp(fn, L"http://", 7) || !_wcsnicmp(fn, L"https://", 8))	{		reader = CreateReader(fn, killEvent);		reader_type=READER_HTTP;		MP4hFile = MP4ReadEx(fn, reader, &HTTPIO);	}	else	{		reader = CreateUnicodeReader(fn);		if (!reader)			return false;		reader_type=READER_UNICODE;		MP4hFile = MP4ReadEx(fn, reader, &UnicodeIO);	}	if (!MP4hFile)	{		return false;	}	m_opened = 1;	unsigned int output_bits = AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"bits", 16);	if (output_bits  >= 24)			output_bits  = 24;	else			output_bits  = 16;	unsigned int max_channels;		// get max channels	if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"surround", true))		max_channels = 6;	else if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"mono", false))		max_channels = 1;	else		max_channels = 2;	audio_track = GetAudioTrack(MP4hFile);	if (audio_track == MP4_INVALID_TRACK_ID || !CreateDecoder(MP4hFile, audio_track, audio, audioFactory)	|| audio->OpenMP4(MP4hFile, audio_track, output_bits, max_channels, false) != MP4_SUCCESS)	{		audio = 0;		video_clock.Start();	}	video_track = GetVideoTrack(MP4hFile);	if (video_track != MP4_INVALID_TRACK_ID)	{		CreateVideoDecoder(MP4hFile, video_track, video, videoFactory);		if (video)			video->Open(MP4hFile, video_track);	}	else		video=0;	if (!audio && !video)	{		return false;	}	numVideoSamples = MP4GetTrackNumberOfSamples(MP4hFile, video_track);	m_video_timescale = MP4GetTrackTimeScale(MP4hFile, video_track);	unsigned __int64 trackDuration;	double lengthAudio = 0;	double lengthVideo = 0;		if (audio_track != MP4_INVALID_TRACK_ID)	{		if (audio)		{			ConfigureDecoderASC(MP4hFile, audio_track, audio);			audio_chunk = !!audio->RequireChunks();		}		else			audio_chunk = false;		numSamples = audio_chunk?MP4GetTrackNumberOfChunks(MP4hFile, audio_track):MP4GetTrackNumberOfSamples(MP4hFile, audio_track);		m_timescale = MP4GetTrackTimeScale(MP4hFile, audio_track);		trackDuration = MP4GetTrackDuration(MP4hFile, audio_track);		lengthAudio = (double)(__int64)trackDuration / (double)m_timescale;	}	else	{		numSamples = numVideoSamples;		trackDuration = MP4GetTrackDuration(MP4hFile, video_track);		lengthVideo = (double)(__int64)trackDuration / (double)m_video_timescale;	}		/* length in Sec. */	m_length = max(lengthAudio, lengthVideo); //(double)(__int64)trackDuration / (double)m_timescale;	audio_bitrate = MP4GetTrackBitRate(MP4hFile, audio_track) / 1000;	if (video)		video_bitrate = MP4GetTrackBitRate(MP4hFile, video_track) / 1000;	else	video_bitrate = 0;	if (audio && audio->SetGain(GetGain(MP4hFile)) == MP4_SUCCESS)		mod.UsesOutputPlug |= 8;	else		mod.UsesOutputPlug &= ~8;	return true;}int play(const wchar_t *fn){	video_clock.Reset();	if (!videoOutput) // grab this now while we're on the main thread		videoOutput = (IVideoOutput *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_IVIDEOOUTPUT);	audio = 0;	video = 0;	paused = 0;	m_kill = 0;	ResetEvent(killEvent);	m_length = 0;	ResetEvent(seekEvent);	m_needseek = -1;	SetEvent(pauseEvent);	if (m_force_seek != -1)	{		setoutputtime(m_force_seek);	}	m_opened = 0;	lstrcpynW(lastfn, fn, MAX_PATH*4);	DWORD thread_id;	HANDLE threadCreatedEvent = CreateEvent(0, FALSE, FALSE, 0);	hThread = CreateThread(NULL, NULL, PlayProc, (LPVOID)threadCreatedEvent, NULL, &thread_id);	SetThreadPriority(hThread, AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));	WaitForSingleObject(threadCreatedEvent, INFINITE);	CloseHandle(threadCreatedEvent);	return 0;}static inline wchar_t *IncSafe(wchar_t *val, int x){	while (x--)	{		if (*val)			val++;	}	return val;}void GetGaps(MP4FileHandle mp4, unsigned __int32 &pre, unsigned __int32 &post){	wchar_t gap_data[128] = {0};	if (GetCustomMetadata(mp4, "iTunSMPB", gap_data, 128) && gap_data[0])	{		wchar_t *itr = IncSafe(gap_data, 9);		pre = wcstoul(itr, 0, 16);		itr = IncSafe(itr, 9);		post = wcstoul(itr, 0, 16);		// don't care about total number of samples, really		/*		itr+=9;		unsigned int numSamples = wcstoul(itr, 0, 16);*/	}	else	{		pre = 0;		post = 0;	}}float GetGain(MP4FileHandle mp4, bool allowDefault){	if (AGAVE_API_CONFIG && AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))	{		float dB = 0, peak = 1.0f;		wchar_t gain[128] = L"", peakVal[128] = L"";		_locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();		switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0))		{		case 0:   // track			if ((!GetCustomMetadata(mp4, "replaygain_track_gain", gain, 128) || !gain[0])			    && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))				GetCustomMetadata(mp4, "replaygain_album_gain", gain, 128);			if ((!GetCustomMetadata(mp4, "replaygain_track_peak", peakVal, 128) || !peakVal[0])			    && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))				GetCustomMetadata(mp4, "replaygain_album_peak", peakVal, 128);			break;		case 1:			if ((!GetCustomMetadata(mp4, "replaygain_album_gain", gain, 128) || !gain[0])			    && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))				GetCustomMetadata(mp4, "replaygain_track_gain", gain, 128);			if ((!GetCustomMetadata(mp4, "replaygain_album_peak", peakVal, 128) || !peakVal[0])			    && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))				GetCustomMetadata(mp4, "replaygain_track_peak", peakVal, 128);			break;		}		if (gain[0])		{			if (gain[0] == L'+')				dB = (float)_wtof_l(&gain[1],C_locale);			else				dB = (float)_wtof_l(gain,C_locale);		}		else if (allowDefault)		{			dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);			return powf(10.0f, dB / 20.0f);		}		if (peakVal[0])		{			peak = (float)_wtof_l(peakVal,C_locale);		}		switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1))		{		case 0:   // apply gain			return powf(10.0f, dB / 20.0f);		case 1:   // apply gain, but don't clip			return min(powf(10.0f, dB / 20.0f), 1.0f / peak);		case 2:   // normalize			return 1.0f / peak;		case 3:   // prevent clipping			if (peak > 1.0f)				return 1.0f / peak;			else				return 1.0f;		}	}	return 1.0f; // no gain}bool first;void pause(){	paused = 1;	if (audio)	{		mod.outMod->Pause(1);	}	else	{		video_clock.Pause();	}	ResetEvent(pauseEvent); // pauseEvent signal state is opposite of pause state}void unpause(){	paused = 0;	if (audio)	{		mod.outMod->Pause(0);	}	else	{		video_clock.Unpause();	}	SetEvent(pauseEvent); // pauseEvent signal state is opposite of pause state}int ispaused(){	return paused;}void stop(){	if (reader && reader_type==READER_HTTP) StopReader(reader);	lastfn[0] = 0;	SetEvent(killEvent);	m_kill = 1;	WaitForSingleObject(hThread, INFINITE);	mod.outMod->Close();	mod.SAVSADeInit();	if (m_opened) MP4Close(MP4hFile);	MP4hFile = 0;	m_opened = 0;	if (audio)	{		audio->Close();		audioFactory->releaseInterface(audio);	}	audioFactory = 0;	audio = 0;	if (video)	{		video->Close();		videoFactory->releaseInterface(video);	}	videoFactory=0;	video = 0;	if (reader) 	{		if (reader_type == READER_HTTP)			DestroyReader(reader);		else			DestroyUnicodeReader(reader);	}	reader = 0;}int getlength(){	return (int)(m_length*1000);}int getoutputtime(){	if (m_needseek == -1)			return (int)GetClock();	else		return m_needseek; // this prevents the seekbar from jumping around while the playthread is seeking}void setvolume(int volume){	mod.outMod->SetVolume(volume);}void setpan(int pan){	mod.outMod->SetPan(pan);}/*void FillInfo(HWND hwndDlg, MP4FileHandle hMp4);void CALLBACK CurrentlyPlayingInfoBox(ULONG_PTR param){	ThreadInfoBox *threadInfo = (ThreadInfoBox *)param;	FillInfo(threadInfo->hwndDlg, MP4hFile);	SetEvent(threadInfo->completionEvent);}*/void getfileinfo(const wchar_t *filename, wchar_t *title, int *length_in_ms){	if (!filename || !*filename)  // currently playing file	{		if (length_in_ms) *length_in_ms = getlength();		if (title) // get non-path portion.of filename		{			lstrcpynW(title, lastfn, GETFILEINFO_TITLE_LENGTH);			PathStripPathW(title);			PathRemoveExtensionW(title);		}	}	else // some other file	{		if (length_in_ms) // calculate length		{			*length_in_ms = -1000; // the default is unknown file length (-1000).			MP4FileHandle hMp4 = MP4Read(filename);			if (hMp4)			{				double lengthAudio = 0;				double lengthVideo = 0;				MP4TrackId audio_track = GetAudioTrack(hMp4);				if (audio_track != -1)				{					int timescale = MP4GetTrackTimeScale(hMp4, audio_track);					unsigned __int64 trackDuration = MP4GetTrackDuration(hMp4, audio_track);					lengthAudio = (double)(__int64)trackDuration / (double)timescale;				}				MP4TrackId video_track = GetVideoTrack(hMp4);				if (video_track != -1)				{					int timescale = MP4GetTrackTimeScale(hMp4, video_track);					unsigned __int64 trackDuration = MP4GetTrackDuration(hMp4, video_track);					lengthVideo = (double)(__int64)trackDuration / (double)timescale;				}				*length_in_ms = (int)(max(lengthAudio, lengthVideo) * 1000);				MP4Close(hMp4);			}		}		if (title) // get non path portion of filename		{			lstrcpynW(title, filename, GETFILEINFO_TITLE_LENGTH);			PathStripPathW(title);			PathRemoveExtensionW(title);		}	}}void eq_set(int on, char data[10], int preamp){}// module definition.In_Module mod ={	IN_VER_RET, // defined in IN2.H	"nullsoft(in_mp4.dll)",		//"Nullsoft MPEG-4 Audio Decoder v1.22"	0,    	// hMainWindow (filled in by winamp)	0,      // hDllInstance (filled in by winamp)	0,	    // this is a double-null limited list. "EXT\0Description\0EXT\0Description\0" etc.	1,    	// is_seekable	1,    	// uses output plug-in system	config,	about,	init,	quit,	getfileinfo,	infoDlg,	isourfile,	play,	pause,	unpause,	ispaused,	stop,	getlength,	getoutputtime,	setoutputtime,	setvolume,	setpan,	0, 0, 0, 0, 0, 0, 0, 0, 0,     // visualization calls filled in by winamp	0, 0,     // dsp calls filled in by winamp	eq_set,	NULL,    		// setinfo call filled in by winamp	0 // out_mod filled in by winamp};extern "C"{	__declspec(dllexport) In_Module * winampGetInModule2()	{		return &mod;	}}
 |