From cc2160de5cc05dc3aa77f3a34358e66f6193c8c9 Mon Sep 17 00:00:00 2001 From: MichaÅ‚ CichoÅ„ Date: Tue, 26 Jun 2012 20:35:30 +0200 Subject: Add support for AAC. --- faad2/src/plugins/Makefile.am | 13 + faad2/src/plugins/QCD/QCDFAAD.c | 1030 ++++++++++ faad2/src/plugins/QCD/QCDFAAD.sln | 29 + faad2/src/plugins/QCD/QCDFAAD.vcproj | 348 ++++ faad2/src/plugins/QCD/QCDInputDLL.h | 50 + faad2/src/plugins/QCD/QCDModDefs.h | 340 ++++ faad2/src/plugins/QCD/QCDModInput.h | 117 ++ faad2/src/plugins/QCD/ReadMe.txt | 11 + faad2/src/plugins/QCD/logo.bmp | Bin 0 -> 87614 bytes faad2/src/plugins/QCD/plugin_dlg.rc | 141 ++ faad2/src/plugins/QCD/resource.h | 33 + faad2/src/plugins/QCDMp4/QCDInputDLL.h | 46 + faad2/src/plugins/QCDMp4/QCDModDefs.h | 413 ++++ faad2/src/plugins/QCDMp4/QCDModInput.h | 117 ++ faad2/src/plugins/QCDMp4/QCDModTagEditor.h | 84 + faad2/src/plugins/QCDMp4/QCDMp4.c | 2992 ++++++++++++++++++++++++++++ faad2/src/plugins/QCDMp4/QCDMp4.rc | 191 ++ faad2/src/plugins/QCDMp4/QCDMp4.sln | 35 + faad2/src/plugins/QCDMp4/QCDMp4.vcproj | 285 +++ faad2/src/plugins/QCDMp4/QCDMp4Tag.cpp | 839 ++++++++ faad2/src/plugins/QCDMp4/QCDTagsDLL.h | 14 + faad2/src/plugins/QCDMp4/aac2mp4.cpp | 319 +++ faad2/src/plugins/QCDMp4/aac2mp4.h | 42 + faad2/src/plugins/QCDMp4/aacinfo.c | 224 +++ faad2/src/plugins/QCDMp4/aacinfo.h | 46 + faad2/src/plugins/QCDMp4/config.c | 48 + faad2/src/plugins/QCDMp4/config.h | 42 + faad2/src/plugins/QCDMp4/mbs.h | 81 + faad2/src/plugins/QCDMp4/resource.h | 67 + faad2/src/plugins/QCDMp4/utils.c | 153 ++ faad2/src/plugins/QCDMp4/utils.h | 40 + faad2/src/plugins/mpeg4ip/Makefile.am | 22 + faad2/src/plugins/mpeg4ip/README_WIN32.txt | 11 + faad2/src/plugins/mpeg4ip/aa_file.cpp | 130 ++ faad2/src/plugins/mpeg4ip/aa_file.h | 26 + faad2/src/plugins/mpeg4ip/faad2.cpp | 388 ++++ faad2/src/plugins/mpeg4ip/faad2.h | 92 + faad2/src/plugins/xmms/AUTHORS | 3 + faad2/src/plugins/xmms/ChangeLog | 43 + faad2/src/plugins/xmms/INSTALL | 2 + faad2/src/plugins/xmms/Makefile.am | 2 + faad2/src/plugins/xmms/NEWS | 21 + faad2/src/plugins/xmms/README | 23 + faad2/src/plugins/xmms/TODO | 6 + faad2/src/plugins/xmms/src/Makefile.am | 14 + faad2/src/plugins/xmms/src/aac_utils.c | 104 + faad2/src/plugins/xmms/src/libmp4.c | 586 ++++++ faad2/src/plugins/xmms/src/mp4_utils.c | 469 +++++ 48 files changed, 10132 insertions(+) create mode 100644 faad2/src/plugins/Makefile.am create mode 100644 faad2/src/plugins/QCD/QCDFAAD.c create mode 100644 faad2/src/plugins/QCD/QCDFAAD.sln create mode 100644 faad2/src/plugins/QCD/QCDFAAD.vcproj create mode 100644 faad2/src/plugins/QCD/QCDInputDLL.h create mode 100644 faad2/src/plugins/QCD/QCDModDefs.h create mode 100644 faad2/src/plugins/QCD/QCDModInput.h create mode 100644 faad2/src/plugins/QCD/ReadMe.txt create mode 100644 faad2/src/plugins/QCD/logo.bmp create mode 100644 faad2/src/plugins/QCD/plugin_dlg.rc create mode 100644 faad2/src/plugins/QCD/resource.h create mode 100644 faad2/src/plugins/QCDMp4/QCDInputDLL.h create mode 100644 faad2/src/plugins/QCDMp4/QCDModDefs.h create mode 100644 faad2/src/plugins/QCDMp4/QCDModInput.h create mode 100644 faad2/src/plugins/QCDMp4/QCDModTagEditor.h create mode 100644 faad2/src/plugins/QCDMp4/QCDMp4.c create mode 100644 faad2/src/plugins/QCDMp4/QCDMp4.rc create mode 100644 faad2/src/plugins/QCDMp4/QCDMp4.sln create mode 100644 faad2/src/plugins/QCDMp4/QCDMp4.vcproj create mode 100644 faad2/src/plugins/QCDMp4/QCDMp4Tag.cpp create mode 100644 faad2/src/plugins/QCDMp4/QCDTagsDLL.h create mode 100644 faad2/src/plugins/QCDMp4/aac2mp4.cpp create mode 100644 faad2/src/plugins/QCDMp4/aac2mp4.h create mode 100644 faad2/src/plugins/QCDMp4/aacinfo.c create mode 100644 faad2/src/plugins/QCDMp4/aacinfo.h create mode 100644 faad2/src/plugins/QCDMp4/config.c create mode 100644 faad2/src/plugins/QCDMp4/config.h create mode 100644 faad2/src/plugins/QCDMp4/mbs.h create mode 100644 faad2/src/plugins/QCDMp4/resource.h create mode 100644 faad2/src/plugins/QCDMp4/utils.c create mode 100644 faad2/src/plugins/QCDMp4/utils.h create mode 100644 faad2/src/plugins/mpeg4ip/Makefile.am create mode 100644 faad2/src/plugins/mpeg4ip/README_WIN32.txt create mode 100644 faad2/src/plugins/mpeg4ip/aa_file.cpp create mode 100644 faad2/src/plugins/mpeg4ip/aa_file.h create mode 100644 faad2/src/plugins/mpeg4ip/faad2.cpp create mode 100644 faad2/src/plugins/mpeg4ip/faad2.h create mode 100644 faad2/src/plugins/xmms/AUTHORS create mode 100644 faad2/src/plugins/xmms/ChangeLog create mode 100644 faad2/src/plugins/xmms/INSTALL create mode 100644 faad2/src/plugins/xmms/Makefile.am create mode 100644 faad2/src/plugins/xmms/NEWS create mode 100644 faad2/src/plugins/xmms/README create mode 100644 faad2/src/plugins/xmms/TODO create mode 100644 faad2/src/plugins/xmms/src/Makefile.am create mode 100644 faad2/src/plugins/xmms/src/aac_utils.c create mode 100644 faad2/src/plugins/xmms/src/libmp4.c create mode 100644 faad2/src/plugins/xmms/src/mp4_utils.c (limited to 'faad2/src/plugins') diff --git a/faad2/src/plugins/Makefile.am b/faad2/src/plugins/Makefile.am new file mode 100644 index 0000000..6286f9f --- /dev/null +++ b/faad2/src/plugins/Makefile.am @@ -0,0 +1,13 @@ +if HAVE_MPEG4IP_PLUG +if HAVE_XMMS +SUBDIRS = xmms mpeg4ip +else +SUBDIRS = mpeg4ip +endif #HAVE_XMMS +else +if HAVE_XMMS +SUBDIRS = xmms +else +SUBDIRS = +endif #HAVE_XMMS +endif #HAVE_MPEG4IP_PLUG diff --git a/faad2/src/plugins/QCD/QCDFAAD.c b/faad2/src/plugins/QCD/QCDFAAD.c new file mode 100644 index 0000000..1d12295 --- /dev/null +++ b/faad2/src/plugins/QCD/QCDFAAD.c @@ -0,0 +1,1030 @@ +/* +** FAAD - Freeware Advanced Audio Decoder +** Copyright (C) 2002 M. Bakker +** +** 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 +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** 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. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** $Id: QCDFAAD.c,v 1.2 2003/04/28 19:04:35 menno Exp $ +** based on menno's in_faad.dll plugin for Winamp +** +** The tag function has been removed because QCD supports ID3v1 & ID3v2 very well +** About how to tagging: Please read the "ReadMe.txt" first +**/ + +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include +#include +#include +#include "QCDInputDLL.h" + +#include "resource.h" + +#include +#include +#include +//#include + +static char app_name[] = "QCDFAAD"; + +faadAACInfo file_info; + +faacDecHandle hDecoder; +faacDecFrameInfo frameInfo; + +HINSTANCE hInstance; +HWND hwndPlayer, hwndConfig, hwndAbout; +QCDModInitIn sQCDCallbacks, *QCDCallbacks; +BOOL oldAPIs = 0; +static char lastfn[MAX_PATH]; // currently playing file (used for getting info on the current file) +int file_length; // file length, in bytes +int paused; // are we paused? +int seek_needed; // if != -1, it is the point that the decode thread should seek to, in ms. + +char *sample_buffer; // sample buffer +unsigned char *buffer; // input buffer +unsigned char *memmap_buffer; // input buffer for whole file +long memmap_index; + +long buffercount, fileread, bytecount; + +// seek table for ADTS header files +unsigned long *seek_table = NULL; +int seek_table_length=0; + +int killPlayThread = 0; // the kill switch for the decode thread +HANDLE play_thread_handle = INVALID_HANDLE_VALUE; // the handle to the decode thread +FILE_STREAM *infile; + +/* Function definitions */ +int id3v2_tag(unsigned char *buffer); +DWORD WINAPI PlayThread(void *b); // the decode thread procedure + +// general funcz +static void show_error(const char *message,...) +{ + char foo[512]; + va_list args; + va_start(args, message); + vsprintf(foo, message, args); + va_end(args); + MessageBox(hwndPlayer, foo, "FAAD Plug-in Error", MB_ICONSTOP); +} + + +// 1= use vbr display, 0 = use average bitrate. This value only controls what shows up in the +// configuration form. Also- Streaming uses an on-the-fly bitrate display regardless of this value. +long m_variable_bitrate_display=0; +long m_priority = 5; +long m_memmap_file = 0; +static char INI_FILE[MAX_PATH]; + +char *priority_text[] = { "", + "Decode Thread Priority: Lowest", + "Decode Thread Priority: Lower", + "Decode Thread Priority: Normal", + "Decode Thread Priority: Higher", + "Decode Thread Priority: Highest (default)" + }; + +long priority_table[] = {0, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST}; + +long current_file_mode = 0; + +int PlayThread_memmap(); +int PlayThread_file(); + +static void _r_s(char *name,char *data, int mlen) +{ + char buf[10]; + strcpy(buf,data); + GetPrivateProfileString(app_name,name,buf,data,mlen,INI_FILE); +} + +#define RS(x) (_r_s(#x,x,sizeof(x))) +#define WS(x) (WritePrivateProfileString(app_name,#x,x,INI_FILE)) + +void config_read() +{ + char variable_bitrate_display[10]; + char priority[10]; + char memmap_file[10]; + char local_buffer_size[10]; + char stream_buffer_size[10]; + + strcpy(variable_bitrate_display, "1"); + strcpy(priority, "5"); + strcpy(memmap_file, "0"); + strcpy(local_buffer_size, "128"); + strcpy(stream_buffer_size, "64"); + + RS(variable_bitrate_display); + RS(priority); + RS(memmap_file); + RS(local_buffer_size); + RS(stream_buffer_size); + + m_priority = atoi(priority); + m_variable_bitrate_display = atoi(variable_bitrate_display); + m_memmap_file = atoi(memmap_file); + m_local_buffer_size = atoi(local_buffer_size); + m_stream_buffer_size = atoi(stream_buffer_size); +} + +void config_write() +{ + char variable_bitrate_display[10]; + char priority[10]; + char memmap_file[10]; + char local_buffer_size[10]; + char stream_buffer_size[10]; + + itoa(m_priority, priority, 10); + itoa(m_variable_bitrate_display, variable_bitrate_display, 10); + itoa(m_memmap_file, memmap_file, 10); + itoa(m_local_buffer_size, local_buffer_size, 10); + itoa(m_stream_buffer_size, stream_buffer_size, 10); + + WS(variable_bitrate_display); + WS(priority); + WS(memmap_file); + WS(local_buffer_size); + WS(stream_buffer_size); +} + +//----------------------------------------------------------------------------- + +BOOL 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; + + ModInfo->moduleString = "FAAD Plugin v1.0b"; + /* read config */ + QCDCallbacks->Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0); + + config_read(); + ModInfo->moduleExtensions = "AAC"; + + hwndPlayer = (HWND)ModInit->Service(opGetParentWnd, 0, 0, 0); + lastfn[0] = 0; + play_thread_handle = INVALID_HANDLE_VALUE; + + oldAPIs = 1; + + return TRUE; +} + +//----------------------------------------------------------------------------- + +PLUGIN_API QCDModInitIn* INPUTDLL_ENTRY_POINT() +{ + 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; +} + +//---------------------------------------------------------------------------- + +BOOL CALLBACK config_dialog_proc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + char tmp[10]; + + switch (message) + { + case WM_INITDIALOG: + /* Set priority slider range and previous position */ + SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_SETRANGE, TRUE, MAKELONG(1, 5)); + SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_SETPOS, TRUE, m_priority); + SetDlgItemText(hwndDlg, IDC_STATIC2, priority_text[m_priority]); + + /* Put a limit to the amount of characters allowed in the buffer boxes */ + SendMessage(GetDlgItem(hwndDlg, LOCAL_BUFFER_TXT), EM_LIMITTEXT, 4, 0); + SendMessage(GetDlgItem(hwndDlg, STREAM_BUFFER_TXT), EM_LIMITTEXT, 4, 0); + + if(m_variable_bitrate_display) + SendMessage(GetDlgItem(hwndDlg, VARBITRATE_CHK), BM_SETCHECK, BST_CHECKED, 0); + if(m_memmap_file) + SendMessage(GetDlgItem(hwndDlg, IDC_MEMMAP), BM_SETCHECK, BST_CHECKED, 0); + + itoa(m_local_buffer_size, tmp, 10); + SetDlgItemText(hwndDlg, LOCAL_BUFFER_TXT, tmp); + + itoa(m_stream_buffer_size, tmp, 10); + SetDlgItemText(hwndDlg, STREAM_BUFFER_TXT, tmp); + + return TRUE; + + case WM_HSCROLL: + + /* Thread priority slider moved */ + if(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER) == (HWND) lParam) + { + int tmp; + tmp = SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_GETPOS, 0, 0); + + if(tmp > 0) + { + m_priority = tmp; + + SetDlgItemText(hwndDlg, IDC_STATIC2, priority_text[m_priority]); + + if(play_thread_handle) + SetThreadPriority(play_thread_handle, priority_table[m_priority]); + } + } + + return TRUE; + + case WM_COMMAND: + + if(HIWORD(wParam) == BN_CLICKED) + { + if(GetDlgItem(hwndDlg, VARBITRATE_CHK) == (HWND) lParam) + { + /* Variable Bitrate checkbox hit */ + m_variable_bitrate_display = SendMessage(GetDlgItem(hwndDlg, VARBITRATE_CHK), BM_GETCHECK, 0, 0); + } + if(GetDlgItem(hwndDlg, IDC_MEMMAP) == (HWND) lParam) + { + /* Variable Bitrate checkbox hit */ + m_memmap_file = SendMessage(GetDlgItem(hwndDlg, IDC_MEMMAP), BM_GETCHECK, 0, 0); + } + } + + switch (LOWORD(wParam)) + { + case OK_BTN: + /* User hit OK, save buffer settings (all others are set on command) */ + GetDlgItemText(hwndDlg, LOCAL_BUFFER_TXT, tmp, 5); + m_local_buffer_size = atol(tmp); + + GetDlgItemText(hwndDlg, STREAM_BUFFER_TXT, tmp, 5); + m_stream_buffer_size = atol(tmp); + + config_write(); + + EndDialog(hwndDlg, wParam); + return TRUE; + case RESET_BTN: + SendMessage(GetDlgItem(hwndDlg, VARBITRATE_CHK), BM_SETCHECK, BST_CHECKED, 0); + m_variable_bitrate_display = 1; + SendMessage(GetDlgItem(hwndDlg, IDC_MEMMAP), BM_SETCHECK, BST_UNCHECKED, 0); + m_memmap_file = 0; + SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_SETPOS, TRUE, 5); + m_priority = 5; + SetDlgItemText(hwndDlg, IDC_STATIC2, priority_text[5]); + SetDlgItemText(hwndDlg, LOCAL_BUFFER_TXT, "128"); + m_local_buffer_size = 128; + SetDlgItemText(hwndDlg, STREAM_BUFFER_TXT, "64"); + m_stream_buffer_size = 64; + return TRUE; + case IDCANCEL: + case CANCEL_BTN: + /* User hit Cancel or the X, just close without saving buffer settings */ + DestroyWindow(hwndDlg); + return TRUE; + } + } + return FALSE; +} + + +void Configure(int flags) +{ + if(!IsWindow(hwndConfig)) + hwndConfig = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_CONFIG), hwndPlayer, config_dialog_proc); + ShowWindow(hwndConfig, SW_NORMAL); +} + +//----------------------------------------------------------------------------- +// 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 FAAD Input Plug-in v1.0b\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_SHOW); +} + +//----------------------------------------------------------------------------- + +BOOL Initialize(QCDModInfo *ModInfo, int flags) +{ + hwndPlayer = (HWND)QCDCallbacks->Service(opGetParentWnd, 0, 0, 0); + + lastfn[0] = 0; + seek_needed = -1; + paused = 0; + play_thread_handle = INVALID_HANDLE_VALUE; + + /* read config */ + QCDCallbacks->Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0); + config_read(); + + ModInfo->moduleString = "FAAD Plugin v1.0b"; + ModInfo->moduleExtensions = "AAC"; + + /* Initialize winsock, necessary for streaming */ + WinsockInit(); + + // insert menu item into plugin menu +// QCDCallbacks->Service(opSetPluginMenuItem, hInstance, IDD_CONFIG, (long)"FAAD Plug-in"); + + return TRUE; +} + +//---------------------------------------------------------------------------- + +void ShutDown(int flags) +{ + Stop(lastfn, STOPFLAG_FORCESTOP); + + if(buffer) + LocalFree(buffer); + + /* Deallocate winsock */ + WinsockDeInit(); + + // delete the inserted plugin menu +// QCDCallbacks->Service(opSetPluginMenuItem, hInstance, 0, 0); +} + +//----------------------------------------------------------------------------- + +BOOL GetMediaSupported(LPCSTR medianame, MediaInfo *mediaInfo) +{ + FILE_STREAM *in; + faadAACInfo tmp; + 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, ".aac", 4) == 0) + { + in = open_filestream((char *)medianame); + + if(in != NULL && mediaInfo) + { + if(in->http) + { + /* No seeking in http streams */ + mediaInfo->mediaType = DIGITAL_STREAM_MEDIA; + mediaInfo->op_canSeek = FALSE; + } + else + { + mediaInfo->mediaType = DIGITAL_FILE_MEDIA; + get_AAC_format((char *)medianame, &tmp, NULL, NULL, 1); + if(tmp.headertype == 2) /* ADTS header - seekable */ + mediaInfo->op_canSeek = TRUE; + else + mediaInfo->op_canSeek = FALSE; /* ADIF or Headerless - not seekable */ + } + + close_filestream(in); + return TRUE; + } + else + { + close_filestream(in); + return FALSE; + } + } + else + return FALSE; +} + +unsigned long samplerate, channels; + +int play_memmap(char *fn) +{ + int tagsize = 0; + + infile = open_filestream(fn); + + if (infile == NULL) + return 1; + + fileread = filelength_filestream(infile); + + memmap_buffer = (char*)LocalAlloc(LPTR, fileread); + read_buffer_filestream(infile, memmap_buffer, fileread); + + /* skip id3v2 tag */ + memmap_index = id3v2_tag(memmap_buffer); + + hDecoder = faacDecOpen(); + + /* Copy the configuration dialog setting and use it as the default */ + /* initialize the decoder, and get samplerate and channel info */ + + if( (buffercount = faacDecInit(hDecoder, memmap_buffer + memmap_index, + fileread - memmap_index - 1, &samplerate, &channels)) < 0 ) + { + show_error("Error opening input file"); + return 1; + } + + memmap_index += buffercount; + + PlayThread_memmap(); + + return 0; +} + +int play_file(char *fn) +{ + int k; + int tagsize; + + ZeroMemory(buffer, 768*2); + + infile = open_filestream(fn); + + if (infile == NULL) + return 1; + + fileread = filelength_filestream(infile); + + buffercount = bytecount = 0; + read_buffer_filestream(infile, buffer, 768*2); + + tagsize = id3v2_tag(buffer); + + /* If we find a tag, run right over it */ + if(tagsize) + { + if(infile->http) + { + int i; + /* Crude way of doing this, but I believe its fast enough to not make a big difference */ + close_filestream(infile); + infile = open_filestream(fn); + + for(i=0; i < tagsize; i++) + read_byte_filestream(infile); + } + else + seek_filestream(infile, tagsize, FILE_BEGIN); + + bytecount = tagsize; + buffercount = 0; + read_buffer_filestream(infile, buffer, 768*2); + } + + hDecoder = faacDecOpen(); + + /* Copy the configuration dialog setting and use it as the default */ + /* initialize the decoder, and get samplerate and channel info */ + + if((buffercount = faacDecInit(hDecoder, buffer, 768*2, &samplerate, &channels)) < 0) + { + show_error("Error opening input file"); + return 1; + } + + if(buffercount > 0) + { + bytecount += buffercount; + + for (k = 0; k < (768*2 - buffercount); k++) + buffer[k] = buffer[k + buffercount]; + + read_buffer_filestream(infile, buffer + (768*2) - buffercount, buffercount); + buffercount = 0; + } + + PlayThread_file(); + + return 0; +} + + +//----------------------------------------------------------------------------- + +BOOL Play(LPCSTR medianame, int playfrom, int playto, int flags) +{ + if(stricmp(lastfn, medianame) != 0) + { + sQCDCallbacks.toPlayer.OutputStop(STOPFLAG_PLAYDONE); + Stop(lastfn, STOPFLAG_PLAYDONE); + } + + if(paused) + { + // Update the player controls to reflect the new unpaused state + sQCDCallbacks.toPlayer.OutputPause(0); + + Pause(medianame, PAUSE_DISABLED); + + if (playfrom >= 0) + seek_needed = playfrom; + } + else if(play_thread_handle != INVALID_HANDLE_VALUE) + { + seek_needed = playfrom; + return TRUE; + } + else + { + int thread_id; + + // alloc the input buffer + buffer = (unsigned char*)LocalAlloc(LPTR, 768*2); + + current_file_mode = m_memmap_file; + + if(current_file_mode) + { + if(play_memmap((char *)medianame)) + return FALSE; + } + else + { + if(play_file((char *)medianame)) + return FALSE; + } + + if(seek_table) + { + free(seek_table); + seek_table = NULL; + seek_table_length = 0; + } + + get_AAC_format((char *)medianame, &file_info, &seek_table, &seek_table_length, 0); + + seek_needed = playfrom > 0 ? playfrom : -1; + killPlayThread = 0; + strcpy(lastfn,medianame); + + /* + To RageAmp: This is really needed, because aacinfo isn't very accurate on ADIF files yet. + Can be fixed though :-) + */ + file_info.sampling_rate = samplerate; + file_info.channels = frameInfo.channels; + + play_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) PlayThread, (void *) &killPlayThread, 0, &thread_id); + if(!play_thread_handle) + return FALSE; + + // Note: This line seriously slows down start up time + if(m_priority != 3) // if the priority in config window is set to normal, there is nothing to reset! + SetThreadPriority(play_thread_handle, priority_table[m_priority]); + + } + + return TRUE; +} + +//----------------------------------------------------------------------------- + +BOOL Pause(LPCSTR medianame, int flags) +{ + if(QCDCallbacks->toPlayer.OutputPause(flags)) + { + // send back pause/unpause notification + QCDCallbacks->toPlayer.PlayPaused(medianame, flags); + paused = flags; + return TRUE; + } + return FALSE; +} + +//----------------------------------------------------------------------------- + +BOOL Stop(LPCSTR medianame, int flags) +{ + if(medianame && *medianame && stricmp(lastfn, medianame) == 0) + { + sQCDCallbacks.toPlayer.OutputStop(flags); + + killPlayThread = 1; + if(play_thread_handle != INVALID_HANDLE_VALUE) + { + if(WaitForSingleObject(play_thread_handle, INFINITE) == WAIT_TIMEOUT) + { +// MessageBox(hwndPlayer, "FAAD 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(lastfn); + + lastfn[0] = 0; + } + + return TRUE; +} + +int aac_seek(int pos_ms, int *sktable) +{ + double offset_sec; + + offset_sec = pos_ms / 1000.0; + if(!current_file_mode) + { + seek_filestream(infile, sktable[(int)(offset_sec+0.5)], FILE_BEGIN); + + bytecount = sktable[(int)(offset_sec+0.5)]; + buffercount = 0; + read_buffer_filestream(infile, buffer, 768*2); + } + else + { + memmap_index = sktable[(int)(offset_sec+0.5)]; + } + + return 0; +} + +//----------------------------------------------------------------------------- + +void SetVolume(int levelleft, int levelright, int flags) +{ + QCDCallbacks->toPlayer.OutputSetVol(levelleft, levelright, flags); +} + +//----------------------------------------------------------------------------- + +BOOL GetCurrentPosition(LPCSTR medianame, long *track, long *offset) +{ + return QCDCallbacks->toPlayer.OutputGetCurrentPosition((UINT*)offset, 0); +} + +//----------------------------------------------------------------------------- + +BOOL GetTrackExtents(LPCSTR medianame, TrackExtents *ext, int flags) +{ + faadAACInfo tmp; + + if(get_AAC_format((char*)medianame, &tmp, NULL, NULL, 1)) + return FALSE; + + ext->track = 1; + ext->start = 0; + ext->end = tmp.length; + ext->bytesize = tmp.bitrate * tmp.length; + ext->unitpersec = 1000; + + return TRUE; +} + +//--------------------------for play thread------------------------------------- + +int last_frame; + +DWORD WINAPI PlayThread(void *b) +{ + BOOL done = FALSE, updatePos = FALSE; + int decode_pos_ms = 0; // current decoding position, in milliseconds + int l; + int decoded_frames=0; + int br_calc_frames=0; + int br_bytes_consumed=0; + unsigned long bytesconsumed; + + last_frame = 0; + + if(!done) + { + // open outputdevice + WAVEFORMATEX wf; + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.cbSize = 0; + wf.nChannels = file_info.channels; + wf.wBitsPerSample = 16; + wf.nSamplesPerSec = file_info.sampling_rate; + wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8; + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; + if (!QCDCallbacks->toPlayer.OutputOpen(lastfn, &wf)) + { + show_error("Error: Failed openning output plugin!"); + done = TRUE; // cannot open sound device + } + } + + while (! *((int *)b) ) + { + /********************** SEEK ************************/ + if (!done && seek_needed >= 0) + { + int seconds; + + // Round off to a second + seconds = seek_needed - (seek_needed%1000); + QCDCallbacks->toPlayer.OutputFlush(decode_pos_ms); + aac_seek(seconds, seek_table); + decode_pos_ms = seconds; + decoded_frames = 0; + br_calc_frames = 0; + br_bytes_consumed = 0; + + seek_needed = -1; + updatePos = 1; + } + + /********************* QUIT *************************/ + if (done) + { + if (QCDCallbacks->toPlayer.OutputDrain(0) && !(seek_needed >= 0)) + { + play_thread_handle = INVALID_HANDLE_VALUE; + QCDCallbacks->toPlayer.OutputStop(STOPFLAG_PLAYDONE); + QCDCallbacks->toPlayer.PlayDone(lastfn); + } + else if (seek_needed >= 0) + { + done = FALSE; + continue; + } + break; + } + + /******************* DECODE TO BUFFER ****************/ + else + { + if (current_file_mode) + bytesconsumed = PlayThread_memmap(); + else + bytesconsumed = PlayThread_file(); + + if(last_frame) + done = TRUE; + else + { + + decoded_frames++; + br_calc_frames++; + br_bytes_consumed += bytesconsumed; + + /* Update the variable bitrate about every second */ + if(m_variable_bitrate_display && br_calc_frames == 43) + { + AudioInfo vai; + vai.struct_size = sizeof(AudioInfo); + vai.frequency = file_info.sampling_rate; + vai.bitrate = (int)((br_bytes_consumed * 8) / (decoded_frames / 43.07)); + vai.mode = (channels == 2) ? 0 : 3; + vai.layer = 0; + vai.level = file_info.version; + QCDCallbacks->Service(opSetAudioInfo, &vai, sizeof(AudioInfo), 0); + + br_calc_frames = 0; + } + + if (!killPlayThread && (frameInfo.samples > 0)) + { + //update the time display + if (updatePos) + { + QCDCallbacks->toPlayer.PositionUpdate(decode_pos_ms); + updatePos = 0; + } + + { + WriteDataStruct wd; + + l = frameInfo.samples * sizeof(short); + + decode_pos_ms += (1024*1000)/file_info.sampling_rate; + + wd.bytelen = l; + wd.data = sample_buffer; + wd.markerend = 0; + wd.markerstart = decode_pos_ms; + wd.bps = 16; + wd.nch = frameInfo.channels; + wd.numsamples =l/file_info.channels/(16/8); + wd.srate = file_info.sampling_rate; + + if (!QCDCallbacks->toPlayer.OutputWrite(&wd)) + done = TRUE; + } + } + } + } + Sleep(10); + } + + // close up + play_thread_handle = INVALID_HANDLE_VALUE; + + faacDecClose(hDecoder); + hDecoder = INVALID_HANDLE_VALUE; + close_filestream(infile); + infile = NULL; + + if(seek_table) + { + free(seek_table); + seek_table = NULL; + seek_table_length = 0; + } + + if(buffer) + { + LocalFree(buffer); + buffer = NULL; + } + if(memmap_buffer) + { + LocalFree(memmap_buffer); + memmap_buffer = NULL; + } + + return 0; +} + +// thread play funcs +int PlayThread_memmap() +{ + sample_buffer = (char*)faacDecDecode(hDecoder, &frameInfo, + memmap_buffer + memmap_index, fileread - memmap_index - 1); + if (frameInfo.error) + { +// show_error(faacDecGetErrorMessage(frameInfo.error)); + last_frame = 1; + } + + memmap_index += frameInfo.bytesconsumed; + if (memmap_index >= fileread) + last_frame = 1; + + return frameInfo.bytesconsumed; +} + +int PlayThread_file() +{ + int k; + + if (buffercount > 0) + { + for (k = 0; k < (768*2 - buffercount); k++) + buffer[k] = buffer[k + buffercount]; + + read_buffer_filestream(infile, buffer + (768*2) - buffercount, buffercount); + buffercount = 0; + } + + sample_buffer = (char*)faacDecDecode(hDecoder, &frameInfo, buffer, 768*2); + if (frameInfo.error) + { +// show_error(faacDecGetErrorMessage(frameInfo.error)); + last_frame = 1; + } + + buffercount += frameInfo.bytesconsumed; + + bytecount += frameInfo.bytesconsumed; + if (bytecount >= fileread) + last_frame = 1; + + return frameInfo.bytesconsumed; +} + +// tag +int id3v2_tag(unsigned char *buffer) +{ + if (StringComp(buffer, "ID3", 3) == 0) + { + unsigned long tagsize; + + /* high bit is not used */ + tagsize = (buffer[6] << 21) | (buffer[7] << 14) | + (buffer[8] << 7) | (buffer[9] << 0); + + tagsize += 10; + + return tagsize; + } + else + { + return 0; + } +} \ No newline at end of file diff --git a/faad2/src/plugins/QCD/QCDFAAD.sln b/faad2/src/plugins/QCD/QCDFAAD.sln new file mode 100644 index 0000000..5810dde --- /dev/null +++ b/faad2/src/plugins/QCD/QCDFAAD.sln @@ -0,0 +1,29 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QCDFAAD", "QCDFAAD.vcproj", "{71955EB0-4F77-462B-A844-2BA56E74B87E}" + ProjectSection(ProjectDependencies) = postProject + {BC3EFE27-9015-4C9C-AD3C-72B3B7ED2114} = {BC3EFE27-9015-4C9C-AD3C-72B3B7ED2114} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfaad", "..\..\libfaad\libfaad.vcproj", "{BC3EFE27-9015-4C9C-AD3C-72B3B7ED2114}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {71955EB0-4F77-462B-A844-2BA56E74B87E}.Debug|Win32.ActiveCfg = Debug|Win32 + {71955EB0-4F77-462B-A844-2BA56E74B87E}.Debug|Win32.Build.0 = Debug|Win32 + {71955EB0-4F77-462B-A844-2BA56E74B87E}.Release|Win32.ActiveCfg = Release|Win32 + {71955EB0-4F77-462B-A844-2BA56E74B87E}.Release|Win32.Build.0 = Release|Win32 + {BC3EFE27-9015-4C9C-AD3C-72B3B7ED2114}.Debug|Win32.ActiveCfg = Debug|Win32 + {BC3EFE27-9015-4C9C-AD3C-72B3B7ED2114}.Debug|Win32.Build.0 = Debug|Win32 + {BC3EFE27-9015-4C9C-AD3C-72B3B7ED2114}.Release|Win32.ActiveCfg = Release|Win32 + {BC3EFE27-9015-4C9C-AD3C-72B3B7ED2114}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/faad2/src/plugins/QCD/QCDFAAD.vcproj b/faad2/src/plugins/QCD/QCDFAAD.vcproj new file mode 100644 index 0000000..a57975e --- /dev/null +++ b/faad2/src/plugins/QCD/QCDFAAD.vcproj @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/faad2/src/plugins/QCD/QCDInputDLL.h b/faad2/src/plugins/QCD/QCDInputDLL.h new file mode 100644 index 0000000..a69023b --- /dev/null +++ b/faad2/src/plugins/QCD/QCDInputDLL.h @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// +// File: QCDInputDLL.h +// +// About: QCD Player Input module DLL interface. For more documentation, see +// QCDModInput.h. +// +// Authors: Written by Paul Quinn and Richard Carlson. +// +// 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 QCDInputDLL_H +#define QCDInputDLL_H + +#include "QCDModInput.h" + +extern HINSTANCE hInstance; +extern HWND hwndPlayer; +extern QCDModInitIn sQCDCallbacks, *QCDCallbacks; + +// Calls from the Player +int GetMediaSupported(const char* medianame, MediaInfo *mediaInfo); +int GetTrackExtents(const char* medianame, TrackExtents *ext, int flags); +int GetCurrentPosition(const char* medianame, long *track, long *offset); + +void SetEQ(EQInfo*); +void SetVolume(int levelleft, int levelright, int flags); + +int Play(const char* medianame, int framefrom, int frameto, int flags); +int Pause(const char* medianame, int flags); +int Stop(const char* medianame, int flags); +int Eject(const char* medianame, int flags); + +int Initialize(QCDModInfo *ModInfo, int flags); +void ShutDown(int flags); +void Configure(int flags); +void About(int flags); + +#endif //QCDInputDLL_H \ No newline at end of file diff --git a/faad2/src/plugins/QCD/QCDModDefs.h b/faad2/src/plugins/QCD/QCDModDefs.h new file mode 100644 index 0000000..1f68baf --- /dev/null +++ b/faad2/src/plugins/QCD/QCDModDefs.h @@ -0,0 +1,340 @@ +//----------------------------------------------------------------------------- +// +// File: QCDModDefs.h +// +// About: Module definitions file. Miscellanious definitions used by different +// module types. This file is published with the plugin SDKs. +// +// Authors: Written by Paul Quinn and Richard Carlson. +// +// 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 QCDMODDEFS_H +#define QCDMODDEFS_H + +#include + +#ifdef __cplusplus +#define PLUGIN_API extern "C" __declspec(dllexport) +#else +#define PLUGIN_API __declspec(dllexport) +#endif + +// Current plugin version +#define PLUGIN_API_VERSION 250 + +//----------------------------------------------------------------------------- + +typedef struct { + char *moduleString; + char *moduleExtensions; + +} QCDModInfo; + +//----------------------------------------------------------------------------- +// Services (ops) provided by the Player +//----------------------------------------------------------------------------- +typedef enum +{ //*** below returns numeric info (*buffer not used) + + opGetPlayerVersion = 0, // high-order word = major version (eg 3.01 is 3), low-order word = minor (eg 3.01 = 1) + opGetParentWnd = 1, // handle to player window + opGetPlayerInstance = 2, // HINSTANCE to player executable + + opGetPlayerState = 9, // get current state of the player (returns: 1 = stopped, 2 = playing, 3 = paused, 0 = failed) + opGetNumTracks = 10, // number of tracks in playlist + opGetCurrentIndex = 11, // index of current track in playlist (0 based) + 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 + + opGetTrackLength = 14, // get track length, param1 = index of track in playlist, -1 for current + // param2 = 0 for seconds, 1 for milliseconds + opGetTime = 15, // get time on player, param1 = 0 for time displayed, 1 for track time, 2 for playlist time + // param2 = 0 for elapsed, 1 for remaining + opGetTrackState = 16, // get whether track is marked, param1 = index of track, -1 for current + opGetPlaylistNum = 17, // get playlist number of index, param1 = index of track in playlist, -1 for current + opGetMediaType = 18, // get media type of track, param1 = index if track in playlist, -1 for current + // - see MediaTypes below for return values + + opGetAudioInfo = 19, // get format info about currently playing track + // - param1 = 0 for samplerate, 1 for bitrate, 2 for num channels + + opGetOffline = 20, // true if client is in Offline Mode + opGetVisTarget = 21, // where is vis being drawn > 0 - internal to skin, 1 - external window, 2 - full screen + opGetAlwaysOnTop = 22, // true if player is set to 'Always on Top' + opGetRepeatState = 23, // returns: 0 - repeat off, 1 - repeat track, 2 - repeat all + opGetShuffleState = 27, // returns: 0 - shuffle off, 1 - shuffle enabled + + opGetTimerState = 24, // low-order word: 0 - track ascend, 1 - playlist ascend, 2 - track descend, 3 - playlist descend + // hi-order word: 1 if 'show hours' is set, else 0 + + opGetVolume = 25, // get master volume level (0 - 100), param1: 0 = combined, 1 = left, 2 = right + opSetVolume = 26, // set master volume level, param1: vol level 0 - 100, param2: balance (-100 left, 0 center, 100 right) + + opGetIndexFromPLNum = 28, // get index from playlist number, param1 = playlist number + + opGetChildWnd = 30, // handle to the draggable window extension (only available on some skins) + opGetExtVisWnd = 31, // handle to the external visual window + opGetMusicBrowserWnd = 32, // handle to the music browser window + opGetSkinPreviewWnd = 33, // handle to the skin preview window + opGetPropertiesWnd = 34, // handle to the player properties window + opGetExtInfoWnd = 35, // handle to the extended information window + opGetAboutWnd = 36, // handle to the about window + opGetSegmentsWnd = 37, // handle to the segments window + opGetEQPresetsWnd = 38, // handle to the EQ presets 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 + + opGetQueriesComplete = 60, // get status on whether all tracks in playlist have been queryied for their info + + // playlist manipulation + opDeleteIndex = 90, // delete index from playlist (param1 = index) + 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 + // param2 = pointer to integer that receives track value + // returns: pointer to CddbDisc object. Do not release or deallocate this pointer + + + //*** below returns string info in buffer, param1 = size of buffer + //*** returns 1 on success, 0 on failure + + opGetTrackName = 100, // get track name, param2 = index of track in playlist, -1 for current + opGetArtistName = 101, // get artist name, param2 = index of track in playlist, -1 for current + opGetDiscName = 102, // get disc name, param2 = index of track in playlist, -1 for current + + opGetTrackFile = 103, // file name of track in playlist, param2 = index of track in playlist, -1 for current + opGetSkinName = 104, // get current skin name + + opGetPluginFolder = 105, // get current plugin folder + opGetPluginSettingsFile = 106, // get settings file (plugins.ini) that plugin should save settings to + opGetPluginCacheFile = 107, // get file that describes plugin validity, functions and names + opGetPlayerSettingsFile = 108, // get settings file (qcd.ini) that player saves it settings to (should use for read-only) + + opGetMusicFolder = 110, // get current music folder + opGetPlaylistFolder = 111, // get current playlist folder + opGetSkinFolder = 112, // get current skin folder + opGetCDDBCacheFolder = 113, // get current folder for CDDB cached info + + opGetCurrentPlaylist = 114, // get full pathname of playlist currently loaded + + opGetMediaID = 115, // get media identifier, param2 = index of track in playlist, -1 for current + // - for CD's it's the TOC - for anything else, right now it's 0 + + opGetSupportedExtensions = 116, // get file extensions supported by the player, param2 = 0 - get all extensions, 1 - get registered extensions + // - returned extensions will be colon delimited + + + //*** below buffer points to struct or other object + //*** returns 1 on success, 0 on failure + + opShowMainMenu = 120, // Display Main QCD Menu (buffer = POINT* - location to display menu) + 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) + + opGetEQVals = 200, // get current EQ levels/on/off (buffer = EQInfo*) + opSetEQVals = 201, // set EQ levels/on/off (buffer = EQInfo*) + + opGetProxyInfo = 202, // get proxy info (buffer = ProxyInfo*), returns 0 if proxy not in use + + + //*** below returns numeric info, buffer used + + opGetIndexFromFilename = 210, // get the index of a file that exists in current playlist (buffer = full path of file), + // param1 = startindex (index to start searching on) + // returns -1 if file not in playlist + + + //*** below send information to player + //*** returns 1 on success, 0 on failure + + opSetStatusMessage = 1000, // display message in status area (buffer = msg buffer (null term), param1 = text flags (see below)) + + opSetBrowserUrl = 1001, // set music browser URL (buffer = url (null term)) + // null url buffer - closes browser + // param1 = 0 - normal, 1 - force open + + opSetAudioInfo = 1002, // set the current music bitrate/khz (buffer = AudioInfo*, param1 = size of AudioInfo) + + opSetTrackAlbum = 1003, // update track ablum name (buffer = album (null term), param1 = (string ptr)file name), param2 = MediaTypes + opSetTrackTitle = 1004, // update track title (buffer = title (null term), param1 = (string ptr)file name), param2 = MediaTypes + opSetTrackArtist = 1005, // update track artist name (buffer = artist (null term), param1 = (string ptr)file name), param2 = MediaTypes + + opSetPlaylist = 1006, // add files to or reset playlist with new files (buffer = file list (null term), param1 = (string ptr)originating path (can be NULL), param2 = 1 - clear playlist flag, 2 - enqueue to top + + opSetTrackExtents = 1007, // update track TrackExtents info (buffer = &TrackExtents), param1 = (string ptr)file name) + opSetTrackSeekable = 1008, // update track seekable flag (buffer = (string ptr)file name), param1 = TRUE/FALSE + opSetPlayNext = 1009, // set the next index to be played (buffer = NULL, param1 = index, index = -1 unsets playnext) + opSetIndexFilename = 1010, // updates the filename (or stream) that an index in the current playlist refers to, buffer = new filename, param1 = index + + 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)) + + + //*** 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 + + opSetPluginMenuItem = 2000, // buffer = HINSTANCE of plugin, param1 = item id, param2 = (string ptr)string to display + // - set param2 = 0 to remove item id from menu + // - set param1 = 0 and param2 = 0 to remove whole menu + opSetPluginMenuState = 2001, // buffer = HINSTANCE of plugin, param1 = item id, param2 = menu flags (same as windows menu flags - eg: MF_CHECKED) + + + //*** other services + + 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 + +} PluginServiceOp; + +//----------------------------------------------------------------------------- +// Info services api provided by the Player, called by Plugin. +//----------------------------------------------------------------------------- +typedef long (*PluginServiceFunc)(PluginServiceOp op, void *buffer, long param1, long param2); + + +//----------------------------------------------------------------------------- +typedef struct // for Output Plugin Write callback +{ + void *data; // pointer to valid data + int bytelen; // length of data pointed to by 'data' in bytes + UINT numsamples; // number of samples represented by 'data' + UINT bps; // bits per sample + UINT nch; // number of channels + UINT srate; // sample rate + + UINT markerstart; // Marker position at start of data (marker is time value of data) + // (set to WAVE_VIS_DATA_ONLY to not have data sent to output plugins) + UINT markerend; // Marker position at end of data (not currently used, set to 0) +} WriteDataStruct; + +//----------------------------------------------------------------------------- +typedef struct // for GetTrackExtents Input Plugin callback +{ + UINT track; // for CD's, set the track number. Otherwise set to 1. + UINT start; // for CD's or media that doesn't start at the beginning + // of the file, set to start position. Otherwise set to 0. + UINT end; // set to end position of media. + UINT unitpersec; // whatever units are being used for this media, how many + // of them per second. + // (Note: ((end - start) / unitpersecond) = file length + UINT bytesize; // size of file in bytes (if applicable, otherwise 0). +} TrackExtents; + +//----------------------------------------------------------------------------- +typedef struct // for opSetAudioInfo service +{ + long struct_size; // sizeof(AudioInfo) + long level; // MPEG level (1 for MPEG1, 2 for MPEG2, 3 for MPEG2.5, 7 for MPEGpro) + long layer; // and layer (1, 2 or 3) + 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 +} AudioInfo; + +//----------------------------------------------------------------------------- +// Equalizer Info +//----------------------------------------------------------------------------- +typedef struct // for coming QCD version +{ + long struct_size; // sizeof(EQInfo) + char enabled; + char preamp; // -128 to 127, 0 is even + char bands[10]; // -128 to 127, 0 is even +} EQInfo; + +//----------------------------------------------------------------------------- +typedef struct +{ + long struct_size; // sizeof(ProxyInfo) + char hostname[200]; + long port; + char username[100]; + char password[100]; +} ProxyInfo; + +//----------------------------------------------------------------------------- +typedef enum // for MediaInfo.mediaType +{ + UNKNOWN_MEDIA = 0, + CD_AUDIO_MEDIA = 1, + DIGITAL_FILE_MEDIA = 2, + DIGITAL_STREAM_MEDIA = 3 +} MediaTypes; + +//----------------------------------------------------------------------------- +#define MAX_TOC_LEN 2048 +typedef struct +{ + // media descriptors + CHAR mediaFile[MAX_PATH]; + MediaTypes mediaType; + + // cd audio media info + CHAR cd_mediaTOC[MAX_TOC_LEN]; + int cd_numTracks; + int cd_hasAudio; + + // operation info + int op_canSeek; + + // not used + int reserved[4]; + +} MediaInfo; + + +//----------------------------------------------------------------------------- +// When subclassing the parent window, a plugin can watch for these messages +// to react to events going on between plugins and player +// DO NOT SEND THESE MESSAGES - can only watch for them + +// Plugin to Player Notifiers +#define WM_PN_POSITIONUPDATE (WM_USER + 100) // playback progress updated +#define WM_PN_PLAYSTARTED (WM_USER + 101) // playback has started +#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_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) + +// Player to Plugin Notifiers +#define WM_PN_PLAYLISTCHANGED (WM_USER + 200) // playlist has changed in some way (add, delete, sort, shuffle, drag-n-drop, etc...) + +// For intercepting main menu display +// (so you can get handle, modify, and display your own) +#define WM_SHOWMAINMENU (WM_USER + 20) + +//----------------------------------------------------------------------------- +// To shutdown player, send this command +#define WM_SHUTDOWN (WM_USER + 5) + +//----------------------------------------------------------------------------- +// opSetStatusMessage textflags +#define TEXT_DEFAULT 0x0 // message scrolls by in status window +#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) + + +#endif //QCDMODDEFS_H \ No newline at end of file diff --git a/faad2/src/plugins/QCD/QCDModInput.h b/faad2/src/plugins/QCD/QCDModInput.h new file mode 100644 index 0000000..40a7ae5 --- /dev/null +++ b/faad2/src/plugins/QCD/QCDModInput.h @@ -0,0 +1,117 @@ +//----------------------------------------------------------------------------- +// +// File: QCDModInput.h +// +// About: Input plugin module interface. This file is published with the +// Input plugin SDK. +// +// Authors: Written by Paul Quinn and Richard Carlson. +// +// 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 QCDMODINPUT_H +#define QCDMODINPUT_H + +#include "QCDModDefs.h" + +// name of the DLL export for input plugins +#define INPUTDLL_ENTRY_POINT QInputModule2 // (updated plugin api version 240+) + +// media insert flags +#define MEDIAINSERT_PLAY 0x1 +#define MEDIAINSERT_ADDTRACKS 0x2 +#define MEDIAINSERT_ADDSEGMENTS 0x4 +#define MEDIAINSERT_CLEARPLAYLIST 0x8 + +// Stop will receive one of these flags (pass to output plugin's stop()) +#define STOPFLAG_FORCESTOP 0 // stop occuring due to user action or other event +#define STOPFLAG_PLAYDONE 1 // stop occuring due to playlist completion + +// play flags +#define PLAYFLAG_PLAYBACK 0x0 +#define PLAYFLAG_ENCODING 0x1 +#define PLAYFLAG_SEEKING 0x2 + +// Wave Marker flags +#define WAVE_VIS_DATA_ONLY -1 // set to WaveDataStruct.markerstart in OutputWrite() call have data only go to vis + // and not to output plugin +// pause flags +#define PAUSE_DISABLED 0 // Pause() call is to unpause playback +#define PAUSE_ENABLED 1 // Pause() call is to pause playback + +//----------------------------------------------------------------------------- +// Input Module +//----------------------------------------------------------------------------- +typedef struct +{ + unsigned int size; // size of init structure + unsigned int version; // plugin structure version (set to PLUGIN_API_VERSION) + PluginServiceFunc Service; // player supplied services callback + + struct + { + void (*PositionUpdate)(unsigned int position); + void (*PlayStopped)(const char* medianame); // notify player of play stop + void (*PlayStarted)(const char* medianame); // notify player of play start + void (*PlayPaused)(const char* medianame, int flags); // notify player of play pause + void (*PlayDone)(const char* medianame); // notify player when play done + void (*PlayTrackChanged)(const char* medianame); // notify player when playing track changes (cd audio relevant only) + void (*MediaEjected)(const char* medianame); // notify player of media eject (cd audio relevant) + void (*MediaInserted)(const char* medianame, int flags); // notify player of media insert (cd audio relevant) + + // output plugin calls + int (*OutputOpen)(const char* medianame, WAVEFORMATEX*); // open output for wave data + int (*OutputWrite)(WriteDataStruct*); // send PCM audio data to output + // (blocks until write completes, thus if output is paused can + // block until unpaused) + int (*OutputDrain)(int flags); // wait for all output to complete (blocking) + int (*OutputDrainCancel)(int flags); // break a drain in progress + int (*OutputFlush)(unsigned int marker); // flush output upto marker + int (*OutputStop)(int flags); // stop output + int (*OutputPause)(int flags); // pause output + + int (*OutputSetVol)(int levelleft, int levelright, int flags); + int (*OutputGetCurrentPosition)(unsigned int *position, int flags); + + void *Reserved[10]; + } toPlayer; + + struct + { + int (*Initialize)(QCDModInfo *modInfo, int flags); // initialize plugin + void (*ShutDown)(int flags); // shutdown plugin + + int (*Play)(const char* medianame, int playfrom, int playto, int flags); // start playing playfrom->playto + int (*Stop)(const char* medianame, int flags); // stop playing + int (*Pause)(const char* medianame, int flags); // pause playback + int (*Eject)(const char* medianame, int flags); // eject media + void (*SetEQ)(EQInfo*); // update EQ settings + + int (*GetMediaSupported)(const char* medianame, MediaInfo *mediaInfo); // does plugin support medianame (and provides info for media) + int (*GetTrackExtents)(const char* medianame, TrackExtents *ext, int flags); // get media start, end & units + int (*GetCurrentPosition)(const char* medianame, long *track, long *offset); // get playing media's position + + void (*Configure)(int flags); // launch configuration + void (*About)(int flags); // launch about info + + void (*SetVolume)(int levelleft, int levelright, int flags); // level 0 - 100 + + void *Reserved[10]; + } toModule; + +} QCDModInitIn; + +#endif //QCDMODINPUT_H diff --git a/faad2/src/plugins/QCD/ReadMe.txt b/faad2/src/plugins/QCD/ReadMe.txt new file mode 100644 index 0000000..2e89c09 --- /dev/null +++ b/faad2/src/plugins/QCD/ReadMe.txt @@ -0,0 +1,11 @@ +QCDFAAD.dll input Plugin for Quintessential Player (QCD) +Please goto http://www.quinnware.com to download the latest version of QCD. + +About Tagging music file: + +Because QCD support ID3v1 & ID3v2 functions. So you can add a string -- ":AAC" +in the configuration dialog box of QCDcddb.dll Libarary Plugin. +(I think you will find it and will know how to do it, +otherwise you can visite the message forum on the web site.) + +Have a good time:) \ No newline at end of file diff --git a/faad2/src/plugins/QCD/logo.bmp b/faad2/src/plugins/QCD/logo.bmp new file mode 100644 index 0000000..a01eb84 Binary files /dev/null and b/faad2/src/plugins/QCD/logo.bmp differ diff --git a/faad2/src/plugins/QCD/plugin_dlg.rc b/faad2/src/plugins/QCD/plugin_dlg.rc new file mode 100644 index 0000000..1316be9 --- /dev/null +++ b/faad2/src/plugins/QCD/plugin_dlg.rc @@ -0,0 +1,141 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CONFIG DIALOGEX 0, 0, 179, 133 +STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "FAAC Decoder Configuration" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "Enable variable bitrate display",VARBITRATE_CHK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,7,140,10 + EDITTEXT STREAM_BUFFER_TXT,87,21,21,12,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT LOCAL_BUFFER_TXT,87,38,21,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Buffer entire files into memory",IDC_MEMMAP,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,60,140,10 + CONTROL "Slider1",THREAD_PRIORITY_SLIDER,"msctls_trackbar32", + TBS_AUTOTICKS | WS_TABSTOP,7,89,165,11,WS_EX_TRANSPARENT + PUSHBUTTON "OK",OK_BTN,64,112,50,14 + PUSHBUTTON "Cancel",CANCEL_BTN,121,112,50,14 + LTEXT "HTTP stream buffer:",IDC_STATIC,7,22,77,8 + LTEXT "Decode Thread Priority: Highest (default)",IDC_STATIC2, + 7,78,165,8 + LTEXT "Local file buffer: ",IDC_STATIC,7,40,77,8 + PUSHBUTTON "Reset",RESET_BTN,7,112,50,14 +END + +IDD_ABOUT DIALOG DISCARDABLE 0, 0, 191, 168 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About FAAD Plug-in" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,70,147,50,14 + CTEXT "",IDC_PLUGINVER,29,73,133,16 + CTEXT "",IDC_FAADVER,40,123,111,8 + LTEXT "QCD Input Plug-in by",IDC_STATIC,62,97,67,8 + CONTROL 107,IDC_LOGO,"Static",SS_BITMAP | SS_CENTERIMAGE | + SS_REALSIZEIMAGE,7,0,177,68 + LTEXT "Shao Hao",IDC_MAIL1,79,111,33,8 + LTEXT "M. Bakker",IDC_MAIL3,59,134,34,8 + LTEXT "menno",IDC_MAIL2,109,134,22,8 + LTEXT "&&",IDC_STATIC,97,134,8,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_CONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 172 + TOPMARGIN, 7 + BOTTOMMARGIN, 126 + END + + IDD_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 184 + BOTTOMMARGIN, 161 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_LOGO BITMAP DISCARDABLE "logo.bmp" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/faad2/src/plugins/QCD/resource.h b/faad2/src/plugins/QCD/resource.h new file mode 100644 index 0000000..7837a2c --- /dev/null +++ b/faad2/src/plugins/QCD/resource.h @@ -0,0 +1,33 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by plugin_dlg.rc +// +#define IDD_CONFIG 101 +#define IDD_ABOUT 106 +#define IDB_LOGO 107 +#define VARBITRATE_CHK 1004 +#define THREAD_PRIORITY_SLIDER 1007 +#define IDC_STATIC2 1034 +#define LOCAL_BUFFER_TXT 1035 +#define STREAM_BUFFER_TXT 1036 +#define IDC_MEMMAP 1037 +#define OK_BTN 1038 +#define CANCEL_BTN 1039 +#define RESET_BTN 1047 +#define IDC_LOGO 1048 +#define IDC_PLUGINVER 1050 +#define IDC_FAADVER 1051 +#define IDC_MAIL1 1052 +#define IDC_MAIL2 1053 +#define IDC_MAIL3 1054 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 109 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1055 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/faad2/src/plugins/QCDMp4/QCDInputDLL.h b/faad2/src/plugins/QCDMp4/QCDInputDLL.h new file mode 100644 index 0000000..46a811a --- /dev/null +++ b/faad2/src/plugins/QCDMp4/QCDInputDLL.h @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// +// File: QCDInputDLL.h +// +// About: QCD Player Input module DLL interface. For more documentation, see +// QCDModInput.h. +// +// Authors: Written by Paul Quinn and Richard Carlson. +// +// 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 QCDInputDLL_H +#define QCDInputDLL_H + +#include "QCDModInput.h" + +// Calls from the Player +int GetMediaSupported(const char* medianame, MediaInfo *mediaInfo); +int GetTrackExtents(const char* medianame, TrackExtents *ext, int flags); +int GetCurrentPosition(const char* medianame, long *track, long *offset); + +void SetEQ(EQInfo*); +void SetVolume(int levelleft, int levelright, int flags); + +int Play(const char* medianame, int framefrom, int frameto, int flags); +int Pause(const char* medianame, int flags); +int Stop(const char* medianame, int flags); +int Eject(const char* medianame, int flags); + +int Initialize(QCDModInfo *ModInfo, int flags); +void ShutDown(int flags); +void Configure(int flags); +void About(int flags); + +#endif //QCDInputDLL_H \ No newline at end of file diff --git a/faad2/src/plugins/QCDMp4/QCDModDefs.h b/faad2/src/plugins/QCDMp4/QCDModDefs.h new file mode 100644 index 0000000..6b87006 --- /dev/null +++ b/faad2/src/plugins/QCDMp4/QCDModDefs.h @@ -0,0 +1,413 @@ +//----------------------------------------------------------------------------- +// +// File: QCDModDefs.h +// +// About: Module definitions file. Miscellanious definitions used by different +// module types. This file is published with the plugin SDKs. +// +// Authors: Written by Paul Quinn and Richard Carlson. +// +// 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 QCDMODDEFS_H +#define QCDMODDEFS_H + +#include +#include + +#ifdef __cplusplus +#define PLUGIN_API extern "C" __declspec(dllexport) +#else +#define PLUGIN_API __declspec(dllexport) +#endif + +// Current plugin version + +// 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 +{ + char *moduleString; + char *moduleExtensions; +} QCDModInfo; + +//----------------------------------------------------------------------------- +// Services (ops) provided by the Player +//----------------------------------------------------------------------------- +typedef enum +{ //*** below returns numeric info (*buffer not used) + + opGetPlayerVersion = 0, // high-order word = major version (eg 3.01 is 3), low-order word = minor (eg 3.01 = 1) + opGetParentWnd = 1, // handle to player window + opGetPlayerInstance = 2, // HINSTANCE to player executable + + opGetPlayerState = 9, // get current state of the player (returns: 1 = stopped, 2 = playing, 3 = paused, 0 = failed) + opGetNumTracks = 10, // number of tracks in playlist + opGetCurrentIndex = 11, // index of current track in playlist (0 based) + 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 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 + opGetTime = 15, // get time on player, param1 = 0 for time displayed, 1 for track time, 2 for playlist time + // param2 = 0 for elapsed, 1 for remaining + opGetTrackState = 16, // get whether track is marked, param1 = index of track, -1 for current + opGetPlaylistNum = 17, // get playlist number of index, param1 = index of track in playlist, -1 for current + opGetMediaType = 18, // get media type of track, param1 = index if track in playlist, -1 for current + // - see MediaTypes below for return values + + opGetAudioInfo = 19, // get format info about currently playing track + // - param1 = 0 for samplerate, 1 for bitrate, 2 for num channels + + opGetOffline = 20, // true if client is in Offline Mode + opGetVisTarget = 21, // where is vis being drawn > 0 - internal to skin, 1 - external window, 2 - full screen + opGetAlwaysOnTop = 22, // true if player is set to 'Always on Top' + opGetRepeatState = 23, // returns: 0 - repeat off, 1 - repeat track, 2 - repeat all + opGetShuffleState = 27, // returns: 0 - shuffle off, 1 - shuffle enabled + + opGetTimerState = 24, // low-order word: 0 - track ascend, 1 - playlist ascend, 2 - track descend, 3 - playlist descend + // hi-order word: 1 if 'show hours' is set, else 0 + + opGetVolume = 25, // get master volume level (0 - 100), param1: 0 = combined, 1 = left, 2 = right + opSetVolume = 26, // set master volume level, param1: vol level 0 - 100, param2: balance (-100 left, 0 center, 100 right) + + opGetIndexFromPLNum = 28, // get index from playlist number, param1 = playlist number + + 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 + opGetPropertiesWnd = 34, // handle to the player properties window + opGetExtInfoWnd = 35, // handle to the extended information window + 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 + opDeleteIndex = 90, // delete index from playlist (param1 = index) + 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 ICddbDisc object for the index specified, param1 = index of track, -1 for current + // param2 = pointer to integer that receives track value + // returns: pointer to ICddbDisc object. Do not release or deallocate this pointer + + + //*** below returns string info in buffer, param1 = size of buffer + //*** returns 1 on success, 0 on failure + + opGetTrackName = 100, // get track name, param2 = index of track in playlist, -1 for current + opGetArtistName = 101, // get artist name, param2 = index of track in playlist, -1 for current + opGetDiscName = 102, // get disc name, param2 = index of track in playlist, -1 for current + + opGetTrackFile = 103, // file name of track in playlist, param2 = index of track in playlist, -1 for current + opGetSkinName = 104, // get current skin name + + opGetPluginFolder = 105, // get current plugin folder + opGetPluginSettingsFile = 106, // get settings file (plugins.ini) that plugin should save settings to + opGetPluginCacheFile = 107, // get file that describes plugin validity, functions and names + opGetPlayerSettingsFile = 108, // get settings file (qcd.ini) that player saves it settings to (should use for read-only) + + opGetMusicFolder = 110, // get current music folder + opGetPlaylistFolder = 111, // get current playlist folder + opGetSkinFolder = 112, // get current skin folder + opGetCDDBCacheFolder = 113, // get current folder for CDDB cached info + + opGetCurrentPlaylist = 114, // get full pathname of playlist currently loaded + + opGetMediaID = 115, // get media identifier, param2 = index of track in playlist, -1 for current + // - for CD's it's the TOC - for anything else, right now it's 0 + + 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 + + opShowMainMenu = 120, // Display Main QCD Menu (buffer = POINT* - location to display menu) + 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*) + + opGetProxyInfo = 202, // get proxy info (buffer = ProxyInfo*), returns 0 if proxy not in use + + + //*** below returns numeric info, buffer used + + opGetIndexFromFilename = 210, // get the index of a file that exists in current playlist (buffer = full path of file), + // param1 = startindex (index to start searching on) + // returns -1 if file not in playlist + + + //*** below send information to player + //*** returns 1 on success, 0 on failure + + opSetStatusMessage = 1000, // display message in status area (buffer = msg buffer (null term), param1 = text flags (see below)) + + opSetBrowserUrl = 1001, // set music browser URL (buffer = url (null term)) + // null url buffer - closes browser + // param1 = 0 - normal, 1 - force open + + opSetAudioInfo = 1002, // set the current music bitrate/khz (buffer = AudioInfo*, param1 = size of AudioInfo) + + opSetTrackAlbum = 1003, // update track ablum name (buffer = album (null term), param1 = (string ptr)file name), param2 = MediaTypes + opSetTrackTitle = 1004, // update track title (buffer = title (null term), param1 = (string ptr)file name), param2 = MediaTypes + opSetTrackArtist = 1005, // update track artist name (buffer = artist (null term), param1 = (string ptr)file name), param2 = MediaTypes + + opSetTrackExtents = 1007, // update track TrackExtents info (buffer = &TrackExtents), param1 = (string ptr)file name) + opSetTrackSeekable = 1008, // update track seekable flag (buffer = (string ptr)file name), param1 = TRUE/FALSE + opSetPlayNext = 1009, // set the next index to be played (buffer = NULL, param1 = index, index = -1 unsets playnext) + opSetIndexFilename = 1010, // updates the filename (or stream) that an index in the current playlist refers to, buffer = new filename, param1 = index + + opSetPlaylist = 1006, // clear playlist, add files to playlist or reset playlist with new files + // buffer = file list (each file in quotes, string null terminated) Eg; buffer="\"file1.mp3\" \"file2.mp3\"\0" - NULL to clear playlist + // param1 = (string ptr)originating path (can be NULL if paths included with files) + // param2 = 1 - clear playlist flag, 2 - enqueue to top + + opInsertPlaylist = 1011, // insert tracks into playlist + // buffer = file list (each file in quotes, string null terminated) Eg; buffer="\"file1.mp3\" \"file2.mp3\"\0" + // param1 = (string ptr)originating path (can be NULL if paths included with files) + // param2 = index location to insert tracks (-1 to insert at end) + + 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 + + opSetPluginMenuItem = 2000, // buffer = HINSTANCE of plugin, param1 = item id, param2 = (string ptr)string to display + // - set param2 = 0 to remove item id from menu + // - set param1 = 0 and param2 = 0 to remove whole menu + 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 + +} PluginServiceOp; + +//----------------------------------------------------------------------------- +// Info services api provided by the Player, called by Plugin. +//----------------------------------------------------------------------------- +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 +{ + void *data; // pointer to valid data + int bytelen; // length of data pointed to by 'data' in bytes + UINT numsamples; // number of samples represented by 'data' + UINT bps; // bits per sample + UINT nch; // number of channels + UINT srate; // sample rate + + UINT markerstart; // Marker position at start of data (marker is time value of data) + // (set to WAVE_VIS_DATA_ONLY to not have data sent to output plugins) + UINT markerend; // Marker position at end of data (not currently used, set to 0) +} WriteDataStruct; + +//----------------------------------------------------------------------------- +typedef struct // for GetTrackExtents Input Plugin callback +{ + UINT track; // for CD's, set the track number. Otherwise set to 1. + UINT start; // for CD's or media that doesn't start at the beginning + // of the file, set to start position. Otherwise set to 0. + UINT end; // set to end position of media. + UINT unitpersec; // whatever units are being used for this media, how many + // of them per second. + // (Note: ((end - start) / unitpersecond) = file length + UINT bytesize; // size of file in bytes (if applicable, otherwise 0). +} TrackExtents; + +//----------------------------------------------------------------------------- +typedef struct // for opSetAudioInfo service +{ + long struct_size; // sizeof(AudioInfo) + long level; // MPEG level (1 for MPEG1, 2 for MPEG2, 3 for MPEG2.5, 7 for MPEGpro) + long layer; // and layer (1, 2 or 3) + 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; + +//----------------------------------------------------------------------------- +// Equalizer Info +//----------------------------------------------------------------------------- +typedef struct // for coming QCD version +{ + long struct_size; // sizeof(EQInfo) + char enabled; + char preamp; // -128 to 127, 0 is even + char bands[10]; // -128 to 127, 0 is even +} EQInfo; + +//----------------------------------------------------------------------------- +typedef struct +{ + long struct_size; // sizeof(ProxyInfo) + char hostname[200]; + long port; + char username[100]; + char password[100]; +} ProxyInfo; + +//----------------------------------------------------------------------------- +typedef enum // for MediaInfo.mediaType +{ + UNKNOWN_MEDIA = 0, + CD_AUDIO_MEDIA = 1, + DIGITAL_FILE_MEDIA = 2, + DIGITAL_STREAM_MEDIA = 3 +} MediaTypes; + +//----------------------------------------------------------------------------- +#define MAX_TOC_LEN 2048 +typedef struct +{ + // media descriptors + CHAR mediaFile[MAX_PATH]; + MediaTypes mediaType; + + // cd audio media info + CHAR cd_mediaTOC[MAX_TOC_LEN]; + int cd_numTracks; + int cd_hasAudio; + + // operation info + int op_canSeek; + + // not used + int reserved[4]; + +} 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 +// to react to events going on between plugins and player +// DO NOT SEND THESE MESSAGES - can only watch for them + +// Plugin to Player Notifiers +#define WM_PN_POSITIONUPDATE (WM_USER + 100) // playback progress updated +#define WM_PN_PLAYSTARTED (WM_USER + 101) // playback has started +#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 (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) + +// Player to Plugin Notifiers +#define WM_PN_PLAYLISTCHANGED (WM_USER + 200) // playlist has changed in some way (add, delete, sort, shuffle, drag-n-drop, etc...) + +// For intercepting main menu display +// (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) + +//----------------------------------------------------------------------------- +// opSetStatusMessage textflags +#define TEXT_DEFAULT 0x0 // message scrolls by in status window +#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 diff --git a/faad2/src/plugins/QCDMp4/QCDModInput.h b/faad2/src/plugins/QCDMp4/QCDModInput.h new file mode 100644 index 0000000..4150ff1 --- /dev/null +++ b/faad2/src/plugins/QCDMp4/QCDModInput.h @@ -0,0 +1,117 @@ +//----------------------------------------------------------------------------- +// +// File: QCDModInput.h +// +// About: Input plugin module interface. This file is published with the +// Input plugin SDK. +// +// Authors: Written by Paul Quinn and Richard Carlson. +// +// 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 QCDMODINPUT_H +#define QCDMODINPUT_H + +#include "QCDModDefs.h" + +// name of the DLL export for input plugins +#define INPUTDLL_ENTRY_POINT QInputModule2 // (updated plugin api version 240+) + +// media insert flags +#define MEDIAINSERT_PLAY 0x1 +#define MEDIAINSERT_ADDTRACKS 0x2 +#define MEDIAINSERT_ADDSEGMENTS 0x4 +#define MEDIAINSERT_CLEARPLAYLIST 0x8 + +// Stop will receive one of these flags (pass to output plugin's stop()) +#define STOPFLAG_FORCESTOP 0 // stop occuring due to user action or other event +#define STOPFLAG_PLAYDONE 1 // stop occuring due to playlist completion + +// play flags +#define PLAYFLAG_PLAYBACK 0x0 +#define PLAYFLAG_ENCODING 0x1 +#define PLAYFLAG_SEEKING 0x2 + +// Wave Marker flags +#define WAVE_VIS_DATA_ONLY -1 // set to WaveDataStruct.markerstart in OutputWrite() call have data only go to vis + // and not to output plugin +// pause flags +#define PAUSE_DISABLED 0 // Pause() call is to unpause playback +#define PAUSE_ENABLED 1 // Pause() call is to pause playback + +//----------------------------------------------------------------------------- +// Input Module +//----------------------------------------------------------------------------- +typedef struct +{ + unsigned int size; // size of init structure + unsigned int version; // plugin structure version (set to PLUGIN_API_VERSION) + PluginServiceFunc Service; // player supplied services callback + + struct + { + void (*PositionUpdate)(unsigned int position); + void (*PlayStopped)(const char* medianame); // notify player of play stop + void (*PlayStarted)(const char* medianame); // notify player of play start + void (*PlayPaused)(const char* medianame, int flags); // notify player of play pause + void (*PlayDone)(const char* medianame); // notify player when play done + void (*PlayTrackChanged)(const char* medianame); // notify player when playing track changes (cd audio relevant only) + void (*MediaEjected)(const char* medianame); // notify player of media eject (cd audio relevant) + void (*MediaInserted)(const char* medianame, int flags); // notify player of media insert (cd audio relevant) + + // output plugin calls + int (*OutputOpen)(const char* medianame, WAVEFORMATEX*); // open output for wave data + int (*OutputWrite)(WriteDataStruct*); // send PCM audio data to output + // (blocks until write completes, thus if output is paused can + // block until unpaused) + int (*OutputDrain)(int flags); // wait for all output to complete (blocking) + int (*OutputDrainCancel)(int flags); // break a drain in progress + int (*OutputFlush)(unsigned int marker); // flush output upto marker + int (*OutputStop)(int flags); // stop output + int (*OutputPause)(int flags); // pause output + + int (*OutputSetVol)(int levelleft, int levelright, int flags); + int (*OutputGetCurrentPosition)(unsigned int *position, int flags); + + void *Reserved[10]; + } toPlayer; + + struct + { + int (*Initialize)(QCDModInfo *modInfo, int flags); // initialize plugin + void (*ShutDown)(int flags); // shutdown plugin + + int (*Play)(const char* medianame, int playfrom, int playto, int flags); // start playing playfrom->playto + int (*Stop)(const char* medianame, int flags); // stop playing + int (*Pause)(const char* medianame, int flags); // pause playback + int (*Eject)(const char* medianame, int flags); // eject media + void (*SetEQ)(EQInfo*); // update EQ settings + + int (*GetMediaSupported)(const char* medianame, MediaInfo *mediaInfo); // does plugin support medianame (and provides info for media) + int (*GetTrackExtents)(const char* medianame, TrackExtents *ext, int flags); // get media start, end & units + int (*GetCurrentPosition)(const char* medianame, long *track, long *offset); // get playing media's position + + void (*Configure)(int flags); // launch configuration + void (*About)(int flags); // launch about info + + void (*SetVolume)(int levelleft, int levelright, int flags); // level 0 - 100 + + void *Reserved[10]; + } toModule; + +} QCDModInitIn; + +#endif //QCDMODINPUT_H diff --git a/faad2/src/plugins/QCDMp4/QCDModTagEditor.h b/faad2/src/plugins/QCDMp4/QCDModTagEditor.h new file mode 100644 index 0000000..13555f3 --- /dev/null +++ b/faad2/src/plugins/QCDMp4/QCDModTagEditor.h @@ -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 diff --git a/faad2/src/plugins/QCDMp4/QCDMp4.c b/faad2/src/plugins/QCDMp4/QCDMp4.c new file mode 100644 index 0000000..0709629 --- /dev/null +++ b/faad2/src/plugins/QCDMp4/QCDMp4.c @@ -0,0 +1,2992 @@ +/* +** 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 +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** 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. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** 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.4 2003/12/06 04:24:17 rjamorim Exp $ +**/ + +//#define DEBUG_OUTPUT + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include + +#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, + THREAD_PRIORITY_HIGHEST, + THREAD_PRIORITY_ABOVE_NORMAL, + THREAD_PRIORITY_NORMAL, + THREAD_PRIORITY_BELOW_NORMAL, + THREAD_PRIORITY_LOWEST +}; +static int res_id_table[] = { + IDC_16BITS, + IDC_24BITS, + IDC_32BITS, + 0, + 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 +{ + /* general stuff */ + faacDecHandle hDecoder; + int samplerate; + unsigned char channels; + 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; + int mp4track; + MP4SampleId numSamples; + MP4SampleId sampleId; + + /* AAC stuff */ + FILE *aacfile; + 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; + +//static In_Module module; // the output module (declared near the bottom of this file) +struct { + HINSTANCE hDllInstance; + 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 + + +//typedef struct tag +//{ +// char *item; +// char *value; +//} tag; +// +//typedef struct medialib_tags +//{ +// struct tag *tags; +// unsigned int count; +//} medialib_tags; +// +//int tag_add_field(medialib_tags *tags, const char *item, const char *value) +//{ +// void *backup = (void *)tags->tags; +// +// if (!item || (item && !*item) || !value) return 0; +// +// tags->tags = (struct tag *)realloc(tags->tags, (tags->count+1) * sizeof(tag)); +// if (!tags->tags) { +// if (backup) free(backup); +// return 0; +// } +// else +// { +// int i_len = strlen(item); +// int v_len = strlen(value); +// +// tags->tags[tags->count].item = (char *)malloc(i_len+1); +// tags->tags[tags->count].value = (char *)malloc(v_len+1); +// +// if (!tags->tags[tags->count].item || !tags->tags[tags->count].value) +// { +// if (!tags->tags[tags->count].item) free (tags->tags[tags->count].item); +// if (!tags->tags[tags->count].value) free (tags->tags[tags->count].value); +// tags->tags[tags->count].item = NULL; +// tags->tags[tags->count].value = NULL; +// return 0; +// } +// +// memcpy(tags->tags[tags->count].item, item, i_len); +// memcpy(tags->tags[tags->count].value, value, v_len); +// tags->tags[tags->count].item[i_len] = '\0'; +// tags->tags[tags->count].value[v_len] = '\0'; +// +// tags->count++; +// return 1; +// } +//} +// +//int tag_set_field(medialib_tags *tags, const char *item, const char *value) +//{ +// unsigned int i; +// +// if (!item || (item && !*item) || !value) return 0; +// +// for (i = 0; i < tags->count; i++) +// { +// if (!stricmp(tags->tags[i].item, item)) +// { +// void *backup = (void *)tags->tags[i].value; +// int v_len = strlen(value); +// +// tags->tags[i].value = (char *)realloc(tags->tags[i].value, v_len+1); +// if (!tags->tags[i].value) +// { +// if (backup) free(backup); +// return 0; +// } +// +// memcpy(tags->tags[i].value, value, v_len); +// tags->tags[i].value[v_len] = '\0'; +// +// return 1; +// } +// } +// +// return tag_add_field(tags, item, value); +//} +// +//int tag_delete(medialib_tags *tags) +//{ +// unsigned int i; +// +// for (i = 0; i < tags->count; i++) +// { +// if (tags->tags[i].item) free(tags->tags[i].item); +// if (tags->tags[i].value) free(tags->tags[i].value); +// } +// +// if (tags->tags) free(tags->tags); +// +// tags->tags = NULL; +// tags->count = 0; +//} +// +//int ReadMP4Tag(MP4FileHandle file, medialib_tags *tags) +//{ +// unsigned __int32 valueSize; +// unsigned __int8 *pValue; +// char *pName; +// unsigned int i = 0; +// +// do { +// pName = 0; +// pValue = 0; +// valueSize = 0; +// +// MP4GetMetadataByIndex(file, i, (const char **)&pName, &pValue, &valueSize); +// +// if (valueSize > 0) +// { +// char *val = (char *)malloc(valueSize+1); +// if (!val) return 0; +// memcpy(val, pValue, valueSize); +// val[valueSize] = '\0'; +// +// if (pName[0] == '\xa9') +// { +// if (memcmp(pName, "©nam", 4) == 0) +// { +// tag_add_field(tags, "title", val); +// } else if (memcmp(pName, "©ART", 4) == 0) { +// tag_add_field(tags, "artist", val); +// } else if (memcmp(pName, "©wrt", 4) == 0) { +// tag_add_field(tags, "writer", val); +// } else if (memcmp(pName, "©alb", 4) == 0) { +// tag_add_field(tags, "album", val); +// } else if (memcmp(pName, "©day", 4) == 0) { +// tag_add_field(tags, "date", val); +// } else if (memcmp(pName, "©too", 4) == 0) { +// tag_add_field(tags, "tool", val); +// } else if (memcmp(pName, "©cmt", 4) == 0) { +// tag_add_field(tags, "comment", val); +// } else if (memcmp(pName, "©gen", 4) == 0) { +// tag_add_field(tags, "genre", val); +// } else { +// tag_add_field(tags, pName, val); +// } +// } else if (memcmp(pName, "gnre", 4) == 0) { +// char *t=0; +// if (MP4GetMetadataGenre(file, &t)) +// { +// tag_add_field(tags, "genre", t); +// } +// } else if (memcmp(pName, "trkn", 4) == 0) { +// unsigned __int16 trkn = 0, tot = 0; +// char t[200]; +// if (MP4GetMetadataTrack(file, &trkn, &tot)) +// { +// if (tot > 0) +// wsprintf(t, "%d/%d", trkn, tot); +// else +// wsprintf(t, "%d", trkn); +// tag_add_field(tags, "tracknumber", t); +// } +// } else if (memcmp(pName, "disk", 4) == 0) { +// unsigned __int16 disk = 0, tot = 0; +// char t[200]; +// if (MP4GetMetadataDisk(file, &disk, &tot)) +// { +// if (tot > 0) +// wsprintf(t, "%d/%d", disk, tot); +// else +// wsprintf(t, "%d", disk); +// tag_add_field(tags, "disc", t); +// } +// } else if (memcmp(pName, "cpil", 4) == 0) { +// unsigned __int8 cpil = 0; +// char t[200]; +// if (MP4GetMetadataCompilation(file, &cpil)) +// { +// wsprintf(t, "%d", cpil); +// tag_add_field(tags, "compilation", t); +// } +// } else if (memcmp(pName, "tmpo", 4) == 0) { +// unsigned __int16 tempo = 0; +// char t[200]; +// if (MP4GetMetadataTempo(file, &tempo)) +// { +// wsprintf(t, "%d BPM", tempo); +// tag_add_field(tags, "tempo", t); +// } +// } else if (memcmp(pName, "NDFL", 4) == 0) { +// /* Removed */ +// } else { +// tag_add_field(tags, pName, val); +// } +// +// free(val); +// } +// +// i++; +// } while (valueSize > 0); +// +// return 1; +//} +// +//int mp4_set_metadata(MP4FileHandle file, const char *item, const char *val) +//{ +// if (!item || (item && !*item) || !val || (val && !*val)) return 0; +// +// if (!stricmp(item, "track") || !stricmp(item, "tracknumber")) +// { +// unsigned __int16 trkn, tot; +// int t1 = 0, t2 = 0; +// sscanf(val, "%d/%d", &t1, &t2); +// trkn = t1, tot = t2; +// if (!trkn) return 1; +// if (MP4SetMetadataTrack(file, trkn, tot)) return 1; +// } +// else if (!stricmp(item, "disc") || !stricmp(item, "disknumber")) +// { +// unsigned __int16 disk, tot; +// int t1 = 0, t2 = 0; +// sscanf(val, "%d/%d", &t1, &t2); +// disk = t1, tot = t2; +// if (!disk) return 1; +// if (MP4SetMetadataDisk(file, disk, tot)) return 1; +// } +// else if (!stricmp(item, "compilation")) +// { +// unsigned __int8 cpil = atoi(val); +// if (!cpil) return 1; +// if (MP4SetMetadataCompilation(file, cpil)) return 1; +// } +// else if (!stricmp(item, "tempo")) +// { +// unsigned __int16 tempo = atoi(val); +// if (!tempo) return 1; +// if (MP4SetMetadataTempo(file, tempo)) return 1; +// } +// else if (!stricmp(item, "artist")) +// { +// if (MP4SetMetadataArtist(file, val)) return 1; +// } +// else if (!stricmp(item, "writer")) +// { +// if (MP4SetMetadataWriter(file, val)) return 1; +// } +// else if (!stricmp(item, "title")) +// { +// if (MP4SetMetadataName(file, val)) return 1; +// } +// else if (!stricmp(item, "album")) +// { +// if (MP4SetMetadataAlbum(file, val)) return 1; +// } +// else if (!stricmp(item, "date") || !stricmp(item, "year")) +// { +// if (MP4SetMetadataYear(file, val)) return 1; +// } +// else if (!stricmp(item, "comment")) +// { +// if (MP4SetMetadataComment(file, val)) return 1; +// } +// else if (!stricmp(item, "genre")) +// { +// if (MP4SetMetadataGenre(file, val)) return 1; +// } +// else if (!stricmp(item, "tool")) +// { +// if (MP4SetMetadataTool(file, val)) return 1; +// } +// else +// { +// if (MP4SetMetadataFreeForm(file, (char *)item, (u_int8_t *)val, (u_int32_t)strlen(val))) return 1; +// } +// +// return 0; +//} +// +//int WriteMP4Tag(MP4FileHandle file, const medialib_tags *tags) +//{ +// unsigned int i; +// +// for (i = 0; i < tags->count; i++) +// { +// const char *item = tags->tags[i].item; +// const char *value = tags->tags[i].value; +// +// if (value && *value) +// { +// mp4_set_metadata(file, item, value); +// } +// } +//} + + +#ifdef DEBUG_OUTPUT +void in_mp4_DebugOutput(char *message) +{ + char s[1024]; + + 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(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() +{ + char priority[10]; + 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); + WS(downmix); + WS(vbr_display); + //WS(titleformat); +} + +int Initialize(QCDModInfo *ModInfo, int flags) +{ + ModInfo->moduleString = "MP4 Plug-in v" FAAD2_VERSION; + + module.hMainWindow = (HWND)module.QCDCallbacks.Service(opGetParentWnd, 0, 0, 0); + + // read config from config file + config_read(); + + ModInfo->moduleExtensions = !m_use_for_aac ? "MP4:M4A" : "MP4:M4A:AAC"; + + // return TRUE for successful initialization + return 1; +} + +//---------------------------------------------------------------------------- + +void ShutDown(int flags) +{ + Stop(mp4state.filename, STOPFLAG_FORCESTOP); +} + +///* 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; +// if (!*str) return TRUE; +// 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 || !max) return 0; +// *str = '\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; +// char temp[1024]; +// struct medialib_tags tags; +// tags.count = 0; +// tags.tags = NULL; +// +//#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; +// if (MP4GetMetadataName(file, &pVal)) +// uSetDlgItemText(hwndDlg,IDC_METANAME, pVal); +// +// pVal = NULL; +// if (MP4GetMetadataArtist(file, &pVal)) +// uSetDlgItemText(hwndDlg,IDC_METAARTIST, pVal); +// +// pVal = NULL; +// if (MP4GetMetadataWriter(file, &pVal)) +// uSetDlgItemText(hwndDlg,IDC_METAWRITER, pVal); +// +// pVal = NULL; +// if (MP4GetMetadataComment(file, &pVal)) +// uSetDlgItemText(hwndDlg,IDC_METACOMMENTS, pVal); +// +// pVal = NULL; +// if (MP4GetMetadataAlbum(file, &pVal)) +// uSetDlgItemText(hwndDlg,IDC_METAALBUM, pVal); +// +// pVal = NULL; +// if (MP4GetMetadataGenre(file, &pVal)) +// uSetDlgItemText(hwndDlg,IDC_METAGENRE, pVal); +// +// dummy = 0; +// MP4GetMetadataTempo(file, &dummy); +// if (dummy) +// { +// wsprintf(dummy1, "%d", dummy); +// SetDlgItemText(hwndDlg,IDC_METATEMPO, dummy1); +// } +// +// dummy = 0; dummy2 = 0; +// MP4GetMetadataTrack(file, &dummy, &dummy2); +// if (dummy) +// { +// wsprintf(dummy1, "%d", dummy); +// SetDlgItemText(hwndDlg,IDC_METATRACK1, dummy1); +// } +// if (dummy2) +// { +// wsprintf(dummy1, "%d", dummy2); +// SetDlgItemText(hwndDlg,IDC_METATRACK2, dummy1); +// } +// +// dummy = 0; dummy2 = 0; +// MP4GetMetadataDisk(file, &dummy, &dummy2); +// if (dummy) +// { +// wsprintf(dummy1, "%d", dummy); +// SetDlgItemText(hwndDlg,IDC_METADISK1, dummy1); +// } +// if (dummy2) +// { +// wsprintf(dummy1, "%d", dummy2); +// SetDlgItemText(hwndDlg,IDC_METADISK2, dummy1); +// } +// +// pVal = NULL; +// if (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 */ +// +// tag_delete(&tags); +// file = MP4Read(info_fn, 0); +// if (file != MP4_INVALID_FILE_HANDLE) +// { +// ReadMP4Tag(file, &tags); +// MP4Close(file); +// +// file = MP4Modify(info_fn, 0, 0); +// if (file != MP4_INVALID_FILE_HANDLE) +// { +// MP4MetadataDelete(file); +// MP4Close(file); +// } +// } +// +// file = MP4Modify(info_fn, 0, 0); +// if (file == MP4_INVALID_FILE_HANDLE) +// { +// tag_delete(&tags); +// EndDialog(hwndDlg, wParam); +// return FALSE; +// } +// +// uGetDlgItemText(hwndDlg, IDC_METANAME, dummy1, 1024); +// tag_set_field(&tags, "title", dummy1); +// +// uGetDlgItemText(hwndDlg, IDC_METAWRITER, dummy1, 1024); +// tag_set_field(&tags, "writer", dummy1); +// +// uGetDlgItemText(hwndDlg, IDC_METAARTIST, dummy1, 1024); +// tag_set_field(&tags, "artist", dummy1); +// +// uGetDlgItemText(hwndDlg, IDC_METAALBUM, dummy1, 1024); +// tag_set_field(&tags, "album", dummy1); +// +// uGetDlgItemText(hwndDlg, IDC_METACOMMENTS, dummy1, 1024); +// tag_set_field(&tags, "comment", dummy1); +// +// uGetDlgItemText(hwndDlg, IDC_METAGENRE, dummy1, 1024); +// tag_set_field(&tags, "genre", dummy1); +// +// uGetDlgItemText(hwndDlg, IDC_METAYEAR, dummy1, 1024); +// tag_set_field(&tags, "year", dummy1); +// +// GetDlgItemText(hwndDlg, IDC_METATRACK1, dummy1, 1024); +// dummy = atoi(dummy1); +// GetDlgItemText(hwndDlg, IDC_METATRACK2, dummy1, 1024); +// dummy2 = atoi(dummy1); +// wsprintf(temp, "%d/%d", dummy, dummy2); +// tag_set_field(&tags, "track", temp); +// +// GetDlgItemText(hwndDlg, IDC_METADISK1, dummy1, 1024); +// dummy = atoi(dummy1); +// GetDlgItemText(hwndDlg, IDC_METADISK2, dummy1, 1024); +// dummy2 = atoi(dummy1); +// wsprintf(temp, "%d/%d", dummy, dummy2); +// tag_set_field(&tags, "disc", temp); +// +// GetDlgItemText(hwndDlg, IDC_METATEMPO, dummy1, 1024); +// tag_set_field(&tags, "tempo", dummy1); +// +// dummy3 = SendMessage(GetDlgItem(hwndDlg, IDC_METACOMPILATION), BM_GETCHECK, 0, 0); +// tag_set_field(&tags, "compilation", (dummy3 ? "1" : "0")); +// +// WriteMP4Tag(file, &tags); +// +// 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) { + 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, 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)) { + 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 < 6; i++) + { + if (SendMessage(GetDlgItem(hwndDlg, res_id_table[i]), BM_GETCHECK, 0, 0)) + { + m_resolution = i; + break; + } + } + + /* save config */ + config_write(); + + //if (!m_use_for_aac) + //{ + // module.FileExtensions = short_ext_list; + //} else { + // module.FileExtensions = long_ext_list; + //} + + EndDialog(hwndDlg, wParam); + return TRUE; + } + } + return FALSE; +} + +void Configure(int flags) +{ + DialogBox(module.hDllInstance, MAKEINTRESOURCE(IDD_CONFIG), + module.hMainWindow, config_dialog_proc); +} + +//----------------------------------------------------------------------------- + +void About(int flags) +{ + 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 fill_buffer(state *st) +{ + int bread; + + 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 (!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 (bread != st->m_aac_bytes_consumed) + st->m_at_eof = 1; + + st->m_aac_bytes_into_buffer += bread; + } + + 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; + } + } + + return 1; +} + +void advance_buffer(state *st, int bytes) +{ + st->m_file_offset += bytes; + st->m_aac_bytes_consumed = bytes; + st->m_aac_bytes_into_buffer -= bytes; +} + +int adts_parse(state *st, __int64 *bitrate, double *length) +{ + 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); + + 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; + + 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; + + 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; +} + +int skip_id3v2_tag() +{ + unsigned char buf[10]; + int bread, tagsize = 0; + + bread = fread(buf, 1, 10, mp4state.aacfile); + if (bread != 10) return -1; + + if (!memcmp(buf, "ID3", 3)) + { + /* high bit is not used */ + tagsize = (buf[6] << 21) | (buf[7] << 14) | (buf[8] << 7) | (buf[9] << 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; + + //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(const 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 (mp4state.seek_needed != -1) + { + MP4Duration duration; + + 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 = 1; + } + + if (done) + { + if (module.QCDCallbacks.toPlayer.OutputDrain(0) && !(mp4state.seek_needed >= 0)) + { + module.QCDCallbacks.toPlayer.OutputStop(STOPFLAG_PLAYDONE); + module.QCDCallbacks.toPlayer.PlayDone(mp4state.filename); + PlayThreadAlive = 0; + } + else if (mp4state.seek_needed >= 0) + { + done = 0; + continue; + } + break; + } else/* if (module.outMod->CanWrite() >= (2048*mp4state.channels*sizeof(short)))*/ { + + if (mp4state.last_frame) + { + 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, &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 { + sample_buffer = faacDecDecode(mp4state.hDecoder, &frameInfo, + buffer, buffer_size); + } + if (frameInfo.error > 0) + { + show_error(module.hMainWindow, faacDecGetErrorMessage(frameInfo.error)); + mp4state.last_frame = 1; + } + if (mp4state.sampleId > mp4state.numSamples) + mp4state.last_frame = 1; + + if (buffer) free(buffer); + + 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(buf, sample_count); + memcpy((void*)buf, (void*)temp_buffer, sample_count*3); + free(temp_buffer); + } + + //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 = sample_count * res_table[m_resolution] / 8; + + if (updatepos) + { + 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) + { + 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); + } + + PlayThreadAlive = 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) +{ + int done = 0, updatepos = 0; + int l; + int seq_frames = 0; + int seq_bytes = 0; + + WriteDataStruct wd; + +#ifdef DEBUG_OUTPUT + in_mp4_DebugOutput("AACPlayThread"); +#endif + + PlayThreadAlive = 1; + mp4state.last_frame = 0; + + while (!*((int *)b)) + { + /* seeking */ + if (mp4state.seek_needed != -1) + { + double 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 = 1; + } + + if (done) + { + if (module.QCDCallbacks.toPlayer.OutputDrain(0) && !(mp4state.seek_needed >= 0)) + { + module.QCDCallbacks.toPlayer.OutputStop(STOPFLAG_PLAYDONE); + module.QCDCallbacks.toPlayer.PlayDone(mp4state.filename); + PlayThreadAlive = 0; + } + else if (mp4state.seek_needed >= 0) + { + done = 0; + continue; + } + break; + } else/* if (module.outMod->CanWrite() >= (2048*mp4state.channels*sizeof(short)))*/ { + faacDecFrameInfo frameInfo; + void *sample_buffer; + + memset(&frameInfo, 0, sizeof(faacDecFrameInfo)); + + sample_buffer = decode_aac_frame(&mp4state, &frameInfo); + + 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; + } + + if (!killPlayThread && (frameInfo.samples > 0)) + { + if (frameInfo.channels == 6 && frameInfo.num_lfe_channels) + remap_channels(sample_buffer, frameInfo.samples, res_table[m_resolution]); + + 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); + } + + //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); + + l = frameInfo.samples * res_table[m_resolution] / 8; + + 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) + { + seq_frames++; + seq_bytes += frameInfo.bytesconsumed; + if (seq_frames == (int)(floor((float)frameInfo.samplerate/(float)(frameInfo.samples/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; + } + } + } + + 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); + } + + PlayThreadAlive = 0; + + return 0; +} + +//----------------------------------------------------------------------------- + +int WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID pRes) +{ + if (fdwReason == DLL_PROCESS_ATTACH) + { + module.hDllInstance = hInst; + } + return 1; +} + +//----------------------------------------------------------------------------- + +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; +} + +///* new Media Library interface */ +// +//int mp4_get_metadata(MP4FileHandle file, const char *item, char *dest, int dlen) +//{ +// char *pVal = NULL, dummy1[4096]; +// short dummy = 0, dummy2 = 0; +// +// if (dlen < 1) return 0; +// +// if (!stricmp(item, "track") || !stricmp(item, "tracknumber")) +// { +// if (MP4GetMetadataTrack(file, &dummy, &dummy2)) +// { +// wsprintf(dummy1, "%d", (int)dummy); +// strncpy(dest, dummy1, dlen-1); +// dest[dlen-1] = '\0'; +// return 1; +// } +// } +// else if (!stricmp(item, "disc") || !stricmp(item, "disknumber")) +// { +// if (MP4GetMetadataDisk(file, &dummy, &dummy2)) +// { +// wsprintf(dummy1, "%d", (int)dummy); +// strncpy(dest, dummy1, dlen-1); +// dest[dlen-1] = '\0'; +// return 1; +// } +// } +// else if (!stricmp(item, "compilation")) +// { +// u_int8_t cpil = 0; +// if (MP4GetMetadataCompilation(file, &cpil)) +// { +// wsprintf(dummy1, "%d", (int)cpil); +// strncpy(dest, dummy1, dlen-1); +// dest[dlen-1] = '\0'; +// return 1; +// } +// } +// else if (!stricmp(item, "tempo")) +// { +// u_int16_t tempo = 0; +// if (MP4GetMetadataTempo(file, &tempo)) +// { +// wsprintf(dummy1, "%d", (int)tempo); +// strncpy(dest, dummy1, dlen-1); +// dest[dlen-1] = '\0'; +// return 1; +// } +// } +// else if (!stricmp(item, "artist")) +// { +// if (MP4GetMetadataArtist(file, &pVal)) +// { +// strncpy(dest, pVal, dlen-1); +// dest[dlen-1] = '\0'; +// return 1; +// } +// } +// else if (!stricmp(item, "writer")) +// { +// if (MP4GetMetadataWriter(file, &pVal)) +// { +// strncpy(dest, pVal, dlen-1); +// dest[dlen-1] = '\0'; +// return 1; +// } +// } +// else if (!stricmp(item, "title")) +// { +// if (MP4GetMetadataName(file, &pVal)) +// { +// strncpy(dest, pVal, dlen-1); +// dest[dlen-1] = '\0'; +// return 1; +// } +// } +// else if (!stricmp(item, "album")) +// { +// if (MP4GetMetadataAlbum(file, &pVal)) +// { +// strncpy(dest, pVal, dlen-1); +// dest[dlen-1] = '\0'; +// return 1; +// } +// } +// else if (!stricmp(item, "date") || !stricmp(item, "year")) +// { +// if (MP4GetMetadataYear(file, &pVal)) +// { +// strncpy(dest, pVal, dlen-1); +// dest[dlen-1] = '\0'; +// return 1; +// } +// } +// else if (!stricmp(item, "comment")) +// { +// if (MP4GetMetadataComment(file, &pVal)) +// { +// strncpy(dest, pVal, dlen-1); +// dest[dlen-1] = '\0'; +// return 1; +// } +// } +// else if (!stricmp(item, "genre")) +// { +// if (MP4GetMetadataGenre(file, &pVal)) +// { +// strncpy(dest, pVal, dlen-1); +// dest[dlen-1] = '\0'; +// return 1; +// } +// } +// else if (!stricmp(item, "tool")) +// { +// if (MP4GetMetadataTool(file, &pVal)) +// { +// strncpy(dest, pVal, dlen-1); +// dest[dlen-1] = '\0'; +// return 1; +// } +// } +// else +// { +// u_int32_t valueSize = 0; +// u_int8_t *pValue = NULL; +// +// if (MP4GetMetadataFreeForm(file, (char *)item, &pValue, &valueSize)) +// { +// unsigned int len = (valueSize < (unsigned int)(dlen-1)) ? valueSize : (unsigned int)(dlen-1); +// memcpy(dest, pValue, len); +// dest[len] = '\0'; +// return 1; +// } +// } +// +// return 0; +//} +// +//__declspec(dllexport) int winampGetExtendedFileInfo(const char *fn, const char *data, char *dest, int destlen) +//{ +// if (!fn || (fn && !*fn) || !destlen) return 0; +// +// dest[0] = '\0'; +// +// if (!stricmp(data, "length")) +// { +// char temp[32]; +// int len = getsonglength(fn); +// itoa(len, temp, 10); +// strncpy(dest, temp, destlen-1); +// dest[destlen-1] = '\0'; +// } +// else +// { +// char temp[2048], temp2[2048]; +// MP4FileHandle file = MP4Read(fn, 0); +// if (file == MP4_INVALID_FILE_HANDLE) return 0; +// +// if (mp4_get_metadata(file, data, temp, sizeof(temp))) +// { +// int len = ConvertUTF8ToANSI(temp, temp2); +// if (len > destlen-1) len = destlen-1; +// memcpy(dest, temp2, len); +// dest[len] = '\0'; +// } +// +// MP4Close(file); +// } +// +// return 1; +//} +// +//static struct medialib_tags mltags = {0, 0}; +//static BOOL medialib_init = FALSE; +//static char medialib_lastfn[2048] = ""; +// +//__declspec(dllexport) int winampSetExtendedFileInfo(const char *fn, const char *data, char *val) +//{ +// int len, ret = 0; +// char *temp; +// +// if (!medialib_init || (medialib_init && stricmp(fn, medialib_lastfn))) { +// MP4FileHandle file; +// strcpy(medialib_lastfn, fn); +// +// if (medialib_init) tag_delete(&mltags); +// +// file = MP4Read(fn, 0); +// if (file == MP4_INVALID_FILE_HANDLE) return 0; +// ReadMP4Tag(file, &mltags); +// MP4Close(file); +// medialib_init = TRUE; +// } +// +// len = strlen(val); +// temp = (char *)malloc((len+1)*4); +// if (!temp) return 0; +// +// if (ConvertANSIToUTF8(val, temp)) +// { +// ret = 1; +// tag_set_field(&mltags, data, temp); +// } +// +// free(temp); +// +// return ret; +//} +// +//__declspec(dllexport) int winampWriteExtendedFileInfo() +//{ +// if (medialib_init) +// { +// MP4FileHandle file = MP4Modify(medialib_lastfn, 0, 0); +// if (file == MP4_INVALID_FILE_HANDLE) return 0; +// +// MP4MetadataDelete(file); +// MP4Close(file); +// +// file = MP4Modify(medialib_lastfn, 0, 0); +// if (file == MP4_INVALID_FILE_HANDLE) return 0; +// +// WriteMP4Tag(file, &mltags); +// +// MP4Close(file); +// +// return 1; +// } +// else +// { +// return 0; +// } +//} diff --git a/faad2/src/plugins/QCDMp4/QCDMp4.rc b/faad2/src/plugins/QCDMp4/QCDMp4.rc new file mode 100644 index 0000000..1ed6198 --- /dev/null +++ b/faad2/src/plugins/QCDMp4/QCDMp4.rc @@ -0,0 +1,191 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// ÖÐÎÄ(ÖлªÈËÃñ¹²ºÍ¹ú) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) +#ifdef _WIN32 +LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED +#pragma code_page(936) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CONFIG DIALOGEX 0, 0, 242, 93 +STYLE DS_SETFONT | DS_MODALFRAME | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Configuration" +FONT 9, "ËÎÌå", 400, 0, 0x86 +BEGIN + CONTROL "Slider1",IDC_PRIORITY,"msctls_trackbar32",TBS_VERT | + TBS_NOTICKS | WS_TABSTOP,13,15,18,46 + CONTROL "16 λ",IDC_16BITS,"Button",BS_AUTORADIOBUTTON,77,18,37, + 10 + CONTROL "16 λ¸ßƵ¶¶¶¯",IDC_16BITS_DITHERED,"Button", + BS_AUTORADIOBUTTON | WS_DISABLED,77,29,64,10 + CONTROL "24 λ",IDC_24BITS,"Button",BS_AUTORADIOBUTTON,77,40,37, + 10 + CONTROL "32 λ",IDC_32BITS,"Button",BS_AUTORADIOBUTTON,77,51,37, + 10 + CONTROL "ÏòÏ»ìƵµ½Á¢ÌåÉù",IDC_DOWNMIX,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,152,13,80,10 + CONTROL "Ö§³Ö AAC ¸ñʽ",IDC_USEFORAAC,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,152,26,68,10 + CONTROL "ÏÔʾ¿É±ä±ÈÌØÂÊ",IDC_VBR,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,152,39,72,10 + CONTROL "ÏÔʾ´íÎóÐÅÏ¢",IDC_ERROR,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,152,52,64,10 + DEFPUSHBUTTON "È·¶¨",IDOK,185,72,50,14 + PUSHBUTTON "È¡Ïû",IDCANCEL,127,72,50,14 + GROUPBOX "ÓÅÏȼ¶",IDC_STATIC,7,7,57,58 + LTEXT "¸ß",IDC_STATIC,34,18,25,8 + LTEXT "±ê×¼",IDC_STATIC,34,35,23,8 + LTEXT "µÍ",IDC_STATIC,34,52,24,8 + GROUPBOX "½âÎö¶È",IDC_STATIC,71,7,73,58 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_CONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 235 + TOPMARGIN, 7 + BOTTOMMARGIN, 86 + END +END +#endif // APSTUDIO_INVOKED + +#endif // ÖÐÎÄ(ÖлªÈËÃñ¹²ºÍ¹ú) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// ºÉÀ¼Óï(ºÉÀ¼) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NLD) +#ifdef _WIN32 +LANGUAGE LANG_DUTCH, SUBLANG_DUTCH +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +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", 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 | 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 "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,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 + LTEXT "Lowest",IDC_STATIC,34,52,24,8 + GROUPBOX "Resolution",IDC_STATIC,71,7,73,58 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_CONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 226 + TOPMARGIN, 7 + BOTTOMMARGIN, 86 + END +END +#endif // APSTUDIO_INVOKED + +#endif // ºÉÀ¼Óï(ºÉÀ¼) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/faad2/src/plugins/QCDMp4/QCDMp4.sln b/faad2/src/plugins/QCDMp4/QCDMp4.sln new file mode 100644 index 0000000..7167f45 --- /dev/null +++ b/faad2/src/plugins/QCDMp4/QCDMp4.sln @@ -0,0 +1,35 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +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} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfaad", "..\..\libfaad\libfaad.vcproj", "{BC3EFE27-9015-4C9C-AD3C-72B3B7ED2114}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmp4v2_st", "..\..\common\mp4v2\libmp4v2_st60.vcproj", "{2398BB2F-FFF9-490B-B4CC-863F2D21AE46}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmp4av_st", "..\..\common\mp4av\libmp4av_st.vcproj", "{8CAC9E26-BAA5-45A4-8721-E3170B3F8F49}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2D8F479D-A591-4502-9456-398425D5F834}.Debug|Win32.ActiveCfg = Debug|Win32 + {2D8F479D-A591-4502-9456-398425D5F834}.Debug|Win32.Build.0 = Debug|Win32 + {2D8F479D-A591-4502-9456-398425D5F834}.Release|Win32.ActiveCfg = Release|Win32 + {2D8F479D-A591-4502-9456-398425D5F834}.Release|Win32.Build.0 = Release|Win32 + {BC3EFE27-9015-4C9C-AD3C-72B3B7ED2114}.Debug|Win32.ActiveCfg = Debug|Win32 + {BC3EFE27-9015-4C9C-AD3C-72B3B7ED2114}.Release|Win32.ActiveCfg = Release|Win32 + {2398BB2F-FFF9-490B-B4CC-863F2D21AE46}.Debug|Win32.ActiveCfg = Debug|Win32 + {2398BB2F-FFF9-490B-B4CC-863F2D21AE46}.Release|Win32.ActiveCfg = Release|Win32 + {8CAC9E26-BAA5-45A4-8721-E3170B3F8F49}.Debug|Win32.ActiveCfg = Debug|Win32 + {8CAC9E26-BAA5-45A4-8721-E3170B3F8F49}.Release|Win32.ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/faad2/src/plugins/QCDMp4/QCDMp4.vcproj b/faad2/src/plugins/QCDMp4/QCDMp4.vcproj new file mode 100644 index 0000000..2770362 --- /dev/null +++ b/faad2/src/plugins/QCDMp4/QCDMp4.vcproj @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/faad2/src/plugins/QCDMp4/QCDMp4Tag.cpp b/faad2/src/plugins/QCDMp4/QCDMp4Tag.cpp new file mode 100644 index 0000000..6fabbe1 --- /dev/null +++ b/faad2/src/plugins/QCDMp4/QCDMp4Tag.cpp @@ -0,0 +1,839 @@ +#include +#include +#include "QCDTagsDLL.h" + + +//.............................................................................. +// Global Variables + +typedef struct tag +{ + char *item; + char *value; +} tag; + +typedef struct medialib_tags +{ + struct tag *tags; + unsigned int count; +} medialib_tags; + +int tag_add_field(medialib_tags *tags, const char *item, const char *value) +{ + void *backup = (void *)tags->tags; + + if (!item || (item && !*item) || !value) return 0; + + tags->tags = (struct tag *)realloc(tags->tags, (tags->count+1) * sizeof(tag)); + if (!tags->tags) { + if (backup) free(backup); + return 0; + } + else + { + int i_len = strlen(item); + int v_len = strlen(value); + + tags->tags[tags->count].item = (char *)malloc(i_len+1); + tags->tags[tags->count].value = (char *)malloc(v_len+1); + + if (!tags->tags[tags->count].item || !tags->tags[tags->count].value) + { + if (!tags->tags[tags->count].item) free (tags->tags[tags->count].item); + if (!tags->tags[tags->count].value) free (tags->tags[tags->count].value); + tags->tags[tags->count].item = NULL; + tags->tags[tags->count].value = NULL; + return 0; + } + + memcpy(tags->tags[tags->count].item, item, i_len); + memcpy(tags->tags[tags->count].value, value, v_len); + tags->tags[tags->count].item[i_len] = '\0'; + tags->tags[tags->count].value[v_len] = '\0'; + + tags->count++; + return 1; + } +} + +int tag_set_field(medialib_tags *tags, const char *item, const char *value) +{ + unsigned int i; + + if (!item || (item && !*item) || !value) return 0; + + for (i = 0; i < tags->count; i++) + { + if (!stricmp(tags->tags[i].item, item)) + { + void *backup = (void *)tags->tags[i].value; + int v_len = strlen(value); + + tags->tags[i].value = (char *)realloc(tags->tags[i].value, v_len+1); + if (!tags->tags[i].value) + { + if (backup) free(backup); + return 0; + } + + memcpy(tags->tags[i].value, value, v_len); + tags->tags[i].value[v_len] = '\0'; + + return 1; + } + } + + return tag_add_field(tags, item, value); +} + +void tag_delete(medialib_tags *tags) +{ + unsigned int i; + + for (i = 0; i < tags->count; i++) + { + if (tags->tags[i].item) free(tags->tags[i].item); + if (tags->tags[i].value) free(tags->tags[i].value); + } + + if (tags->tags) free(tags->tags); + + tags->tags = NULL; + tags->count = 0; +} + +int ReadMP4Tag(MP4FileHandle file, medialib_tags *tags) +{ + unsigned __int32 valueSize; + unsigned __int8 *pValue; + char *pName; + unsigned int i = 0; + + do { + pName = 0; + pValue = 0; + valueSize = 0; + + MP4GetMetadataByIndex(file, i, (const char **)&pName, &pValue, &valueSize); + + if (valueSize > 0) + { + char *val = (char *)malloc(valueSize+1); + if (!val) return 0; + memcpy(val, pValue, valueSize); + val[valueSize] = '\0'; + + if (pName[0] == '\xa9') + { + if (memcmp(pName, "©nam", 4) == 0) + { + tag_add_field(tags, "title", val); + } else if (memcmp(pName, "©ART", 4) == 0) { + tag_add_field(tags, "artist", val); + } else if (memcmp(pName, "©wrt", 4) == 0) { + tag_add_field(tags, "writer", val); + } else if (memcmp(pName, "©alb", 4) == 0) { + tag_add_field(tags, "album", val); + } else if (memcmp(pName, "©day", 4) == 0) { + tag_add_field(tags, "date", val); + } else if (memcmp(pName, "©too", 4) == 0) { + tag_add_field(tags, "tool", val); + } else if (memcmp(pName, "©cmt", 4) == 0) { + tag_add_field(tags, "comment", val); + } else if (memcmp(pName, "©gen", 4) == 0) { + tag_add_field(tags, "genre", val); + } else { + tag_add_field(tags, pName, val); + } + } else if (memcmp(pName, "gnre", 4) == 0) { + char *t=0; + if (MP4GetMetadataGenre(file, &t)) + { + tag_add_field(tags, "genre", t); + } + } else if (memcmp(pName, "trkn", 4) == 0) { + unsigned __int16 trkn = 0, tot = 0; + char t[200]; + if (MP4GetMetadataTrack(file, &trkn, &tot)) + { + if (tot > 0) + wsprintf(t, "%d/%d", trkn, tot); + else + wsprintf(t, "%d", trkn); + tag_add_field(tags, "tracknumber", t); + } + } else if (memcmp(pName, "disk", 4) == 0) { + unsigned __int16 disk = 0, tot = 0; + char t[200]; + if (MP4GetMetadataDisk(file, &disk, &tot)) + { + if (tot > 0) + wsprintf(t, "%d/%d", disk, tot); + else + wsprintf(t, "%d", disk); + tag_add_field(tags, "disc", t); + } + } else if (memcmp(pName, "cpil", 4) == 0) { + unsigned __int8 cpil = 0; + char t[200]; + if (MP4GetMetadataCompilation(file, &cpil)) + { + wsprintf(t, "%d", cpil); + tag_add_field(tags, "compilation", t); + } + } else if (memcmp(pName, "tmpo", 4) == 0) { + unsigned __int16 tempo = 0; + char t[200]; + if (MP4GetMetadataTempo(file, &tempo)) + { + wsprintf(t, "%d BPM", tempo); + tag_add_field(tags, "tempo", t); + } + } else if (memcmp(pName, "NDFL", 4) == 0) { + /* Removed */ + } else { + tag_add_field(tags, pName, val); + } + + free(val); + } + + i++; + } while (valueSize > 0); + + return 1; +} + +int mp4_set_metadata(MP4FileHandle file, const char *item, const char *val) +{ + if (!item || (item && !*item) || !val || (val && !*val)) return 0; + + if (!stricmp(item, "track") || !stricmp(item, "tracknumber")) + { + unsigned __int16 trkn, tot; + int t1 = 0, t2 = 0; + sscanf(val, "%d/%d", &t1, &t2); + trkn = t1, tot = t2; + if (!trkn) return 1; + if (MP4SetMetadataTrack(file, trkn, tot)) return 1; + } + else if (!stricmp(item, "disc") || !stricmp(item, "disknumber")) + { + unsigned __int16 disk, tot; + int t1 = 0, t2 = 0; + sscanf(val, "%d/%d", &t1, &t2); + disk = t1, tot = t2; + if (!disk) return 1; + if (MP4SetMetadataDisk(file, disk, tot)) return 1; + } + else if (!stricmp(item, "compilation")) + { + unsigned __int8 cpil = atoi(val); + if (!cpil) return 1; + if (MP4SetMetadataCompilation(file, cpil)) return 1; + } + else if (!stricmp(item, "tempo")) + { + unsigned __int16 tempo = atoi(val); + if (!tempo) return 1; + if (MP4SetMetadataTempo(file, tempo)) return 1; + } + else if (!stricmp(item, "artist")) + { + if (MP4SetMetadataArtist(file, val)) return 1; + } + else if (!stricmp(item, "writer")) + { + if (MP4SetMetadataWriter(file, val)) return 1; + } + else if (!stricmp(item, "title")) + { + if (MP4SetMetadataName(file, val)) return 1; + } + else if (!stricmp(item, "album")) + { + if (MP4SetMetadataAlbum(file, val)) return 1; + } + else if (!stricmp(item, "date") || !stricmp(item, "year")) + { + if (MP4SetMetadataYear(file, val)) return 1; + } + else if (!stricmp(item, "comment")) + { + if (MP4SetMetadataComment(file, val)) return 1; + } + else if (!stricmp(item, "genre")) + { + if (MP4SetMetadataGenre(file, val)) return 1; + } + else if (!stricmp(item, "tool")) + { + if (MP4SetMetadataTool(file, val)) return 1; + } + else + { + if (MP4SetMetadataFreeForm(file, (char *)item, (u_int8_t *)val, (u_int32_t)strlen(val) + 1)) return 1; + } + + return 0; +} + +void WriteMP4Tag(MP4FileHandle file, const medialib_tags *tags) +{ + unsigned int i; + + for (i = 0; i < tags->count; i++) + { + const char *item = tags->tags[i].item; + const char *value = tags->tags[i].value; + + if (value && *value) + { + mp4_set_metadata(file, item, value); + } + } +} + +QCDModInitTag ModInitTag; + +medialib_tags tags; + +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 + tag_delete(&tags); +} + +//----------------------------------------------------------------------------- + +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; + u_int32_t 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); + //if (dummy) + //{ + // wsprintf(dummy1, "%d", dummy); + // SetDlgItemText(hwndDlg,IDC_METATEMPO, dummy1); + //} + + dummy = 0; dummy2 = 0; + MP4GetMetadataTrack(file, (unsigned __int16*)&dummy, (unsigned __int16*)&dummy2); + if (dummy) + { + wsprintf(dummy1, "%d", dummy); + ModInitTag.SetFieldA(tagHandle, TAGFIELD_TRACK, dummy1); + } + //if (dumm2) + //{ + // wsprintf(dummy1, "%d", dummy2); + // SetDlgItemText(hwndDlg,IDC_METATRACK2, dummy1); + //} + + //dummy = 0; dummy2 = 0; + //MP4GetMetadataDisk(file, &dummy, &dummy2); + //if (dummy) + //{ + // wsprintf(dummy1, "%d", dummy); + // SetDlgItemText(hwndDlg,IDC_METADISK1, dummy1); + //} + //if (dummy) + //{ + // wsprintf(dummy1, "%d", dummy2); + // SetDlgItemText(hwndDlg,IDC_METADISK2, dummy1); + //} + + pVal = NULL; + if (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]; + char temp[1024]; + short dummy, dummy2; + +#ifdef DEBUG_OUTPUT + in_mp4_DebugOutput("mp4_tag_write"); +#endif + + /* save Metadata changes */ + + tag_delete(&tags); + file = MP4Read(filename, 0); + if (file != MP4_INVALID_FILE_HANDLE) + { + ReadMP4Tag(file, &tags); + MP4Close(file); + + file = MP4Modify(filename, 0, 0); + if (file != MP4_INVALID_FILE_HANDLE) + { + MP4MetadataDelete(file); + MP4Close(file); + } + } + + file = MP4Modify(filename, 0, 0); + if (file == MP4_INVALID_FILE_HANDLE) + { + tag_delete(&tags); + //EndDialog(hwndDlg, wParam); + return false; + } + + uGetDlgItemText(tagHandle, TAGFIELD_TITLE, dummy1, 1024); + tag_set_field(&tags, "title", dummy1); + + uGetDlgItemText(tagHandle, TAGFIELD_COMPOSER, dummy1, 1024); + tag_set_field(&tags, "writer", dummy1); + + uGetDlgItemText(tagHandle, TAGFIELD_ARTIST, dummy1, 1024); + tag_set_field(&tags, "artist", dummy1); + + uGetDlgItemText(tagHandle, TAGFIELD_ALBUM, dummy1, 1024); + tag_set_field(&tags, "album", dummy1); + + uGetDlgItemText(tagHandle, TAGFIELD_COMMENT, dummy1, 1024); + tag_set_field(&tags, "comment", dummy1); + + uGetDlgItemText(tagHandle, TAGFIELD_GENRE, dummy1, 1024); + tag_set_field(&tags, "genre", dummy1); + + uGetDlgItemText(tagHandle, TAGFIELD_YEAR, dummy1, 1024); + tag_set_field(&tags, "year", dummy1); + + dummy = 0; + MP4GetMetadataTrack(file, (unsigned __int16*)&dummy, (unsigned __int16*)&dummy2); + memcpy(dummy1, ModInitTag.GetFieldA(tagHandle, TAGFIELD_TRACK), sizeof(dummy1)); + dummy = atoi(dummy1); + wsprintf(temp, "%d/%d", dummy, dummy2); + tag_set_field(&tags, "track", temp); + + //GetDlgItemText(hwndDlg, IDC_METADISK1, dummy1, 1024); + //dummy = atoi(dummy1); + //GetDlgItemText(hwndDlg, IDC_METADISK2, dummy1, 1024); + //dummy2 = atoi(dummy1); + //wsprintf(temp, "%d/%d", dummy, dummy2); + //tag_set_field(&tags, "disc", temp); + + //GetDlgItemText(hwndDlg, IDC_METATEMPO, dummy1, 1024); + //tag_set_field(&tags, "tempo", dummy1); + + //dummy3 = SendMessage(GetDlgItem(hwndDlg, IDC_METACOMPILATION), BM_GETCHECK, 0, 0); + //tag_set_field(&tags, "compilation", (dummy3 ? "1" : "0")); + + uGetDlgItemText(tagHandle, TAGFIELD_ENCODER, dummy1, 1024); + tag_set_field(&tags, "tool", dummy1); + + uGetDlgItemText(tagHandle, TAGFIELD_CONDUCTOR, dummy1, 1024); + tag_set_field(&tags, "CONDUCTOR", dummy1); + + uGetDlgItemText(tagHandle, TAGFIELD_ORCHESTRA, dummy1, 1024); + tag_set_field(&tags, "ORCHESTRA", dummy1); + + uGetDlgItemText(tagHandle, TAGFIELD_YEARCOMPOSED, dummy1, 1024); + tag_set_field(&tags, "YEARCOMPOSED", dummy1); + + uGetDlgItemText(tagHandle, TAGFIELD_ORIGARTIST, dummy1, 1024); + tag_set_field(&tags, "ORIGARTIST", dummy1); + + uGetDlgItemText(tagHandle, TAGFIELD_LABEL, dummy1, 1024); + tag_set_field(&tags, "LABEL", dummy1); + + uGetDlgItemText(tagHandle, TAGFIELD_COPYRIGHT, dummy1, 1024); + tag_set_field(&tags, "COPYRIGHT", dummy1); + + uGetDlgItemText(tagHandle, TAGFIELD_CDDBTAGID, dummy1, 1024); + tag_set_field(&tags, "CDDBTAGID", dummy1); + + WriteMP4Tag(file, &tags); + + 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; + 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 || !max) 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); + memset(str, '\0', max); + 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; +} diff --git a/faad2/src/plugins/QCDMp4/QCDTagsDLL.h b/faad2/src/plugins/QCDMp4/QCDTagsDLL.h new file mode 100644 index 0000000..d8621cc --- /dev/null +++ b/faad2/src/plugins/QCDMp4/QCDTagsDLL.h @@ -0,0 +1,14 @@ +#ifndef QCDTAGS_H +#define QCDTAGS_H + +#include "QCDModTagEditor.h" + +extern HINSTANCE hInstance; + +void ShutDown_Tag(int flags); +bool Read_Tag(LPCSTR filename, void* tagData); +bool Write_Tag(LPCSTR filename, void* tagData); +bool Strip_Tag(LPCSTR filename); + + +#endif \ No newline at end of file diff --git a/faad2/src/plugins/QCDMp4/aac2mp4.cpp b/faad2/src/plugins/QCDMp4/aac2mp4.cpp new file mode 100644 index 0000000..8dd0811 --- /dev/null +++ b/faad2/src/plugins/QCDMp4/aac2mp4.cpp @@ -0,0 +1,319 @@ +/* +** 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 +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** 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. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** 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: aac2mp4.cpp,v 1.3 2003/12/06 04:24:17 rjamorim Exp $ +**/ + +#include +#include +#include + +#include "aac2mp4.h" + +int covert_aac_to_mp4(char *inputFileName, char *mp4FileName) +{ + int Mp4TimeScale = 90000; + int allMpeg4Streams = 0; + MP4FileHandle mp4File; + FILE* inFile; + const char *type; + MP4TrackId createdTrackId = MP4_INVALID_TRACK_ID; + + mp4File = MP4Create(mp4FileName, 0, 0, 0); + if (mp4File) + { + MP4SetTimeScale(mp4File, Mp4TimeScale); + } else { + return 1; + } + + inFile = fopen(inputFileName, "rb"); + + if (inFile == NULL) + { + MP4Close(mp4File); + return 2; + } + + createdTrackId = AacCreator(mp4File, inFile); + + if (createdTrackId == MP4_INVALID_TRACK_ID) + { + fclose(inFile); + MP4Close(mp4File); + return 3; + } + + type = MP4GetTrackType(mp4File, createdTrackId); + + if (!strcmp(type, MP4_AUDIO_TRACK_TYPE)) + { + allMpeg4Streams &= + (MP4GetTrackAudioType(mp4File, createdTrackId) + == MP4_MPEG4_AUDIO_TYPE); + } + + if (inFile) + { + fclose(inFile); + } + + MP4Close(mp4File); + MP4MakeIsmaCompliant(mp4FileName, 0, allMpeg4Streams); + + return 0; +} + +#define ADTS_HEADER_MAX_SIZE 10 /* bytes */ + +static u_int8_t firstHeader[ADTS_HEADER_MAX_SIZE]; + +/* + * hdr must point to at least ADTS_HEADER_MAX_SIZE bytes of memory + */ +static bool LoadNextAdtsHeader(FILE* inFile, u_int8_t* hdr) +{ + u_int state = 0; + u_int dropped = 0; + u_int hdrByteSize = ADTS_HEADER_MAX_SIZE; + + while (1) { + /* read a byte */ + u_int8_t b; + + if (fread(&b, 1, 1, inFile) == 0) { + return false; + } + + /* header is complete, return it */ + if (state == hdrByteSize - 1) { + hdr[state] = b; + if (dropped > 0) { + fprintf(stderr, "Warning: dropped %u input bytes\n", dropped); + } + return true; + } + + /* collect requisite number of bytes, no constraints on data */ + if (state >= 2) { + hdr[state++] = b; + } else { + /* have first byte, check if we have 1111X00X */ + if (state == 1) { + if ((b & 0xF6) == 0xF0) { + hdr[state] = b; + state = 2; + /* compute desired header size */ + hdrByteSize = MP4AV_AdtsGetHeaderByteSize(hdr); + } else { + state = 0; + } + } + /* initial state, looking for 11111111 */ + if (state == 0) { + if (b == 0xFF) { + hdr[state] = b; + state = 1; + } else { + /* else drop it */ + dropped++; + } + } + } + } +} + +/* + * Load the next frame from the file + * into the supplied buffer, which better be large enough! + * + * Note: Frames are padded to byte boundaries + */ +static bool LoadNextAacFrame(FILE* inFile, u_int8_t* pBuf, u_int32_t* pBufSize, bool stripAdts) +{ + u_int16_t frameSize; + u_int16_t hdrBitSize, hdrByteSize; + u_int8_t hdrBuf[ADTS_HEADER_MAX_SIZE]; + + /* get the next AAC frame header, more or less */ + if (!LoadNextAdtsHeader(inFile, hdrBuf)) { + return false; + } + + /* get frame size from header */ + frameSize = MP4AV_AdtsGetFrameSize(hdrBuf); + + /* get header size in bits and bytes from header */ + hdrBitSize = MP4AV_AdtsGetHeaderBitSize(hdrBuf); + hdrByteSize = MP4AV_AdtsGetHeaderByteSize(hdrBuf); + + /* adjust the frame size to what remains to be read */ + frameSize -= hdrByteSize; + + if (stripAdts) { + if ((hdrBitSize % 8) == 0) { + /* header is byte aligned, i.e. MPEG-2 ADTS */ + /* read the frame data into the buffer */ + if (fread(pBuf, 1, frameSize, inFile) != frameSize) { + return false; + } + (*pBufSize) = frameSize; + } else { + /* header is not byte aligned, i.e. MPEG-4 ADTS */ + int i; + u_int8_t newByte; + int upShift = hdrBitSize % 8; + int downShift = 8 - upShift; + + pBuf[0] = hdrBuf[hdrBitSize / 8] << upShift; + + for (i = 0; i < frameSize; i++) { + if (fread(&newByte, 1, 1, inFile) != 1) { + return false; + } + pBuf[i] |= (newByte >> downShift); + pBuf[i+1] = (newByte << upShift); + } + (*pBufSize) = frameSize + 1; + } + } else { /* don't strip ADTS headers */ + memcpy(pBuf, hdrBuf, hdrByteSize); + if (fread(&pBuf[hdrByteSize], 1, frameSize, inFile) != frameSize) { + return false; + } + } + + return true; +} + +static bool GetFirstHeader(FILE* inFile) +{ + /* read file until we find an audio frame */ + fpos_t curPos; + + /* already read first header */ + if (firstHeader[0] == 0xff) { + return true; + } + + /* remember where we are */ + fgetpos(inFile, &curPos); + + /* go back to start of file */ + rewind(inFile); + + if (!LoadNextAdtsHeader(inFile, firstHeader)) { + return false; + } + + /* reposition the file to where we were */ + fsetpos(inFile, &curPos); + + return true; +} + +MP4TrackId AacCreator(MP4FileHandle mp4File, FILE* inFile) +{ + // collect all the necessary meta information + u_int32_t samplesPerSecond; + u_int8_t mpegVersion; + u_int8_t profile; + u_int8_t channelConfig; + + if (!GetFirstHeader(inFile)) { + return MP4_INVALID_TRACK_ID; + } + + samplesPerSecond = MP4AV_AdtsGetSamplingRate(firstHeader); + mpegVersion = MP4AV_AdtsGetVersion(firstHeader); + profile = MP4AV_AdtsGetProfile(firstHeader); + channelConfig = MP4AV_AdtsGetChannels(firstHeader); + + u_int8_t audioType = MP4_INVALID_AUDIO_TYPE; + switch (mpegVersion) { + case 0: + audioType = MP4_MPEG4_AUDIO_TYPE; + break; + case 1: + switch (profile) { + case 0: + audioType = MP4_MPEG2_AAC_MAIN_AUDIO_TYPE; + break; + case 1: + audioType = MP4_MPEG2_AAC_LC_AUDIO_TYPE; + break; + case 2: + audioType = MP4_MPEG2_AAC_SSR_AUDIO_TYPE; + break; + case 3: + return MP4_INVALID_TRACK_ID; + } + break; + } + + // add the new audio track + MP4TrackId trackId = + MP4AddAudioTrack(mp4File, + samplesPerSecond, 1024, audioType); + + if (trackId == MP4_INVALID_TRACK_ID) { + return MP4_INVALID_TRACK_ID; + } + + if (MP4GetNumberOfTracks(mp4File, MP4_AUDIO_TRACK_TYPE) == 1) { + MP4SetAudioProfileLevel(mp4File, 0x0F); + } + + u_int8_t* pConfig = NULL; + u_int32_t configLength = 0; + + MP4AV_AacGetConfiguration( + &pConfig, + &configLength, + profile, + samplesPerSecond, + channelConfig); + + if (!MP4SetTrackESConfiguration(mp4File, trackId, + pConfig, configLength)) { + MP4DeleteTrack(mp4File, trackId); + return MP4_INVALID_TRACK_ID; + } + + // parse the ADTS frames, and write the MP4 samples + u_int8_t sampleBuffer[8 * 1024]; + u_int32_t sampleSize = sizeof(sampleBuffer); + MP4SampleId sampleId = 1; + + while (LoadNextAacFrame(inFile, sampleBuffer, &sampleSize, true)) { + if (!MP4WriteSample(mp4File, trackId, sampleBuffer, sampleSize)) { + MP4DeleteTrack(mp4File, trackId); + return MP4_INVALID_TRACK_ID; + } + sampleId++; + sampleSize = sizeof(sampleBuffer); + } + + return trackId; +} diff --git a/faad2/src/plugins/QCDMp4/aac2mp4.h b/faad2/src/plugins/QCDMp4/aac2mp4.h new file mode 100644 index 0000000..b14913c --- /dev/null +++ b/faad2/src/plugins/QCDMp4/aac2mp4.h @@ -0,0 +1,42 @@ +/* +** 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 +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** 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. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** 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: aac2mp4.h,v 1.3 2003/12/06 04:24:17 rjamorim Exp $ +**/ + +#ifndef AAC2MP4_H__ +#define AAC2MP4_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +int covert_aac_to_mp4(char *inputFileName, char *mp4FileName); +MP4TrackId AacCreator(MP4FileHandle mp4File, FILE* inFile); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif \ No newline at end of file diff --git a/faad2/src/plugins/QCDMp4/aacinfo.c b/faad2/src/plugins/QCDMp4/aacinfo.c new file mode 100644 index 0000000..fb689d6 --- /dev/null +++ b/faad2/src/plugins/QCDMp4/aacinfo.c @@ -0,0 +1,224 @@ +/* +** 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 +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** 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. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** 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: aacinfo.c,v 1.3 2003/12/06 04:24:17 rjamorim Exp $ +**/ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include "aacinfo.h" +#include "utils.h" + +#define ADIF_MAX_SIZE 30 /* Should be enough */ +#define ADTS_MAX_SIZE 10 /* Should be enough */ + +static int sample_rates[] = {96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000}; + +static int read_ADIF_header(FILE *file, faadAACInfo *info) +{ + int bitstream; + unsigned char buffer[ADIF_MAX_SIZE]; + int skip_size = 0; + int sf_idx; + + /* Get ADIF header data */ + info->headertype = 1; + + if (fread(buffer, 1, ADIF_MAX_SIZE, file) != ADIF_MAX_SIZE) + return -1; + + /* copyright string */ + if(buffer[0] & 0x80) + skip_size += 9; /* skip 9 bytes */ + + bitstream = buffer[0 + skip_size] & 0x10; + info->bitrate = ((unsigned int)(buffer[0 + skip_size] & 0x0F)<<19)| + ((unsigned int)buffer[1 + skip_size]<<11)| + ((unsigned int)buffer[2 + skip_size]<<3)| + ((unsigned int)buffer[3 + skip_size] & 0xE0); + + if (bitstream == 0) + { + info->object_type = ((buffer[6 + skip_size]&0x01)<<1)|((buffer[7 + skip_size]&0x80)>>7); + sf_idx = (buffer[7 + skip_size]&0x78)>>3; + } else { + info->object_type = (buffer[4 + skip_size] & 0x18)>>3; + sf_idx = ((buffer[4 + skip_size] & 0x07)<<1)|((buffer[5 + skip_size] & 0x80)>>7); + } + info->sampling_rate = sample_rates[sf_idx]; + + return 0; +} + +static int read_ADTS_header(FILE *file, faadAACInfo *info) +{ + /* Get ADTS header data */ + unsigned char buffer[ADTS_MAX_SIZE]; + int frames, t_framelength = 0, frame_length, sr_idx = 0, ID; + int second = 0, pos; + float frames_per_sec = 0; + unsigned long bytes; + unsigned long *tmp_seek_table = NULL; + + info->headertype = 2; + + /* Read all frames to ensure correct time and bitrate */ + for (frames = 0; /* */; frames++) + { + bytes = fread(buffer, 1, ADTS_MAX_SIZE, file); + + if (bytes != ADTS_MAX_SIZE) + break; + + /* check syncword */ + if (!((buffer[0] == 0xFF)&&((buffer[1] & 0xF6) == 0xF0))) + break; + + if (!frames) + { + /* fixed ADTS header is the same for every frame, so we read it only once */ + /* Syncword found, proceed to read in the fixed ADTS header */ + ID = buffer[1] & 0x08; + info->object_type = (buffer[2]&0xC0)>>6; + sr_idx = (buffer[2]&0x3C)>>2; + info->channels = ((buffer[2]&0x01)<<2)|((buffer[3]&0xC0)>>6); + + frames_per_sec = sample_rates[sr_idx] / 1024.f; + } + + /* ...and the variable ADTS header */ + if (ID == 0) + { + info->version = 4; + } else { /* MPEG-2 */ + info->version = 2; + } + frame_length = ((((unsigned int)buffer[3] & 0x3)) << 11) + | (((unsigned int)buffer[4]) << 3) | (buffer[5] >> 5); + + t_framelength += frame_length; + + pos = ftell(file) - ADTS_MAX_SIZE; + + fseek(file, frame_length - ADTS_MAX_SIZE, SEEK_CUR); + } + + if (frames > 0) + { + float sec_per_frame, bytes_per_frame; + info->sampling_rate = sample_rates[sr_idx]; + sec_per_frame = (float)info->sampling_rate/1024.0; + bytes_per_frame = (float)t_framelength / (float)frames; + info->bitrate = 8 * (int)floor(bytes_per_frame * sec_per_frame); + info->length = (int)floor((float)frames/frames_per_sec)*1000; + } else { + info->sampling_rate = 4; + info->bitrate = 128000; + info->length = 0; + info->channels = 0; + } + + return 0; +} + +int get_AAC_format(char *filename, faadAACInfo *info) +{ + unsigned long tagsize; + FILE *file; + char buffer[10]; + unsigned long file_len; + unsigned char adxx_id[5]; + unsigned long tmp; + + memset(info, 0, sizeof(faadAACInfo)); + + file = fopen(filename, "rb"); + + if(file == NULL) + return -1; + + fseek(file, 0, SEEK_END); + file_len = ftell(file); + fseek(file, 0, SEEK_SET); + + /* Skip the tag, if it's there */ + tmp = fread(buffer, 10, 1, file); + + if (StringComp(buffer, "ID3", 3) == 0) + { + /* high bit is not used */ + tagsize = (buffer[6] << 21) | (buffer[7] << 14) | + (buffer[8] << 7) | (buffer[9] << 0); + + fseek(file, tagsize, SEEK_CUR); + tagsize += 10; + } else { + tagsize = 0; + fseek(file, 0, SEEK_SET); + } + + if (file_len) + file_len -= tagsize; + + tmp = fread(adxx_id, 2, 1, file); + adxx_id[5-1] = 0; + info->length = 0; + + /* Determine the header type of the file, check the first two bytes */ + if (StringComp(adxx_id, "AD", 2) == 0) + { + /* We think its an ADIF header, but check the rest just to make sure */ + tmp = fread(adxx_id + 2, 2, 1, file); + + if (StringComp(adxx_id, "ADIF", 4) == 0) + { + read_ADIF_header(file, info); + + info->length = (int)((float)file_len*8000.0/((float)info->bitrate)); + } + } else { + /* No ADIF, check for ADTS header */ + if ((adxx_id[0] == 0xFF)&&((adxx_id[1] & 0xF6) == 0xF0)) + { + /* ADTS header located */ + fseek(file, tagsize, SEEK_SET); + read_ADTS_header(file, info); + } else { + /* Unknown/headerless AAC file, assume format: */ + info->version = 2; + info->bitrate = 128000; + info->sampling_rate = 44100; + info->channels = 2; + info->headertype = 0; + info->object_type = 1; + } + } + + fclose(file); + + return 0; +} diff --git a/faad2/src/plugins/QCDMp4/aacinfo.h b/faad2/src/plugins/QCDMp4/aacinfo.h new file mode 100644 index 0000000..867fef9 --- /dev/null +++ b/faad2/src/plugins/QCDMp4/aacinfo.h @@ -0,0 +1,46 @@ +/* +** 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 +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** 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. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** 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: aacinfo.h,v 1.3 2003/12/06 04:24:17 rjamorim Exp $ +**/ + +#ifndef AACINFO_INCLUDED +#define AACINFO_INCLUDED + +typedef struct { + int version; + int channels; + int sampling_rate; + int bitrate; + int length; + int object_type; + int headertype; +} faadAACInfo; + +int get_AAC_format(char *filename, faadAACInfo *info); + +static int read_ADIF_header(FILE *file, faadAACInfo *info); +static int read_ADTS_header(FILE *file, faadAACInfo *info); + +#endif diff --git a/faad2/src/plugins/QCDMp4/config.c b/faad2/src/plugins/QCDMp4/config.c new file mode 100644 index 0000000..af5af5a --- /dev/null +++ b/faad2/src/plugins/QCDMp4/config.c @@ -0,0 +1,48 @@ +/* +** 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 +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** 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. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** 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: config.c,v 1.3 2003/12/06 04:24:17 rjamorim Exp $ +**/ + +#define WIN32_LEAN_AND_MEAN +#include +#include "config.h" + +char app_name[] = "AudioCoding.com MPEG-4 General Audio player"; +char INI_FILE[MAX_PATH]; +int m_priority = 3; +int m_resolution = 0; +int m_show_errors = 1; +int m_use_for_aac = 1; +int m_downmix = 0; +int m_vbr_display = 0; +char titleformat[MAX_PATH]; + +void _r_s(char *name,char *data, int mlen) +{ + char buf[10]; + strcpy(buf,data); + GetPrivateProfileString(app_name,name,buf,data,mlen,INI_FILE); +} + diff --git a/faad2/src/plugins/QCDMp4/config.h b/faad2/src/plugins/QCDMp4/config.h new file mode 100644 index 0000000..93f6d71 --- /dev/null +++ b/faad2/src/plugins/QCDMp4/config.h @@ -0,0 +1,42 @@ +/* +** 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 +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** 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. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** 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: config.h,v 1.3 2003/12/06 04:24:17 rjamorim Exp $ +**/ + +char app_name[]; +char INI_FILE[]; +int m_priority; +int m_resolution; +int m_show_errors; +int m_use_for_aac; +int m_downmix; +int m_vbr_display; +char titleformat[MAX_PATH]; + +#define RS(x) (_r_s(#x,x,sizeof(x))) +#define WS(x) (WritePrivateProfileString(app_name,#x,x,INI_FILE)) + +void _r_s(char *name,char *data, int mlen); + diff --git a/faad2/src/plugins/QCDMp4/mbs.h b/faad2/src/plugins/QCDMp4/mbs.h new file mode 100644 index 0000000..bda58ed --- /dev/null +++ b/faad2/src/plugins/QCDMp4/mbs.h @@ -0,0 +1,81 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is MPEG4IP. + * + * The Initial Developer of the Original Code is Cisco Systems Inc. + * Portions created by Cisco Systems Inc. are + * Copyright (C) Cisco Systems Inc. 2001-2002. All Rights Reserved. + * + * Contributor(s): + * Dave Mackie dmackie@cisco.com + */ + +#ifndef __MBS_INCLUDED__ +#define __MBS_INCLUDED__ + +class CMemoryBitstream { +public: + CMemoryBitstream() { + m_pBuf = NULL; + m_bitPos = 0; + m_numBits = 0; + } + + void AllocBytes(u_int32_t numBytes); + + void SetBytes(u_int8_t* pBytes, u_int32_t numBytes); + + void PutBytes(u_int8_t* pBytes, u_int32_t numBytes); + + void PutBits(u_int32_t bits, u_int32_t numBits); + + u_int32_t GetBits(u_int32_t numBits); + + void SkipBytes(u_int32_t numBytes) { + SkipBits(numBytes << 3); + } + + void SkipBits(u_int32_t numBits) { + SetBitPosition(GetBitPosition() + numBits); + } + + u_int32_t GetBitPosition() { + return m_bitPos; + } + + void SetBitPosition(u_int32_t bitPos) { + if (bitPos > m_numBits) { + throw; + } + m_bitPos = bitPos; + } + + u_int8_t* GetBuffer() { + return m_pBuf; + } + + u_int32_t GetNumberOfBytes() { + return (GetNumberOfBits() + 7) / 8; + } + + u_int32_t GetNumberOfBits() { + return m_numBits; + } + +protected: + u_int8_t* m_pBuf; + u_int32_t m_bitPos; + u_int32_t m_numBits; +}; + +#endif /* __MBS_INCLUDED__ */ + diff --git a/faad2/src/plugins/QCDMp4/resource.h b/faad2/src/plugins/QCDMp4/resource.h new file mode 100644 index 0000000..e04de22 --- /dev/null +++ b/faad2/src/plugins/QCDMp4/resource.h @@ -0,0 +1,67 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by QCDMp4.rc +// +#define IDD_CONFIG 102 +#define IDC_TYPE 1000 +#define IDC_INFOTEXT 1000 +#define IDC_DURATION 1001 +#define IDC_BITRATE 1002 +#define IDC_SAMPLERATE 1003 +#define IDC_VTYPE 1004 +#define IDC_PRIORITY 1004 +#define IDC_VBITRATE 1005 +#define IDC_ERROR 1005 +#define IDC_VDURATION 1006 +#define IDC_16BITS 1006 +#define IDC_VSIZE 1007 +#define IDC_24BITS 1007 +#define IDC_CONVERT 1007 +#define IDC_VFPS 1008 +#define IDC_32BITS 1008 +#define IDC_CONVERT2 1008 +#define IDC_CHANNELS 1009 +#define IDC_24BITS2 1009 +#define IDC_16BITS_DITHERED 1009 +#define IDC_CONVERT1 1009 +#define IDC_USERDATA 1010 +#define IDC_USEFORAAC 1011 +#define IDC_METACOMPILATION 1012 +#define IDC_METANAME 1013 +#define IDC_METAARTIST 1014 +#define IDC_METAWRITER 1015 +#define IDC_METAALBUM 1016 +#define IDC_METACOMMENTS 1017 +#define IDC_METAGENRE 1018 +#define IDC_METAYEAR 1019 +#define IDC_METATRACK1 1020 +#define IDC_METADISK1 1021 +#define IDC_METATEMPO 1022 +#define IDC_METATRACK2 1023 +#define IDC_METADISK2 1024 +#define IDC_STATIC1 1025 +#define IDC_STATIC2 1026 +#define IDC_STATIC3 1027 +#define IDC_STATIC4 1028 +#define IDC_STATIC5 1029 +#define IDC_STATIC6 1030 +#define IDC_STATIC7 1031 +#define IDC_STATIC8 1032 +#define IDC_STATIC9 1033 +#define IDC_STATIC10 1034 +#define IDC_STATIC11 1035 +#define IDC_STATIC12 1036 +#define IDC_DOWNMIX 1038 +#define IDC_VBR 1039 +#define IDC_TITLEFORMAT 1040 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1041 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/faad2/src/plugins/QCDMp4/utils.c b/faad2/src/plugins/QCDMp4/utils.c new file mode 100644 index 0000000..9302de6 --- /dev/null +++ b/faad2/src/plugins/QCDMp4/utils.c @@ -0,0 +1,153 @@ +/* +** 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 +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** 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. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** 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: utils.c,v 1.3 2003/12/06 04:24:17 rjamorim Exp $ +**/ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include "utils.h" + +int StringComp(char const *str1, char const *str2, unsigned long len) +{ + signed int c1 = 0, c2 = 0; + + while (len--) + { + c1 = tolower(*str1++); + c2 = tolower(*str2++); + + if (c1 == 0 || c1 != c2) + break; + } + + return c1 - c2; +} + +int GetAACTrack(MP4FileHandle infile) +{ + /* find AAC track */ + int i, rc; + int numTracks = MP4GetNumberOfTracks(infile, NULL, 0); + + for (i = 0; i < numTracks; i++) + { + MP4TrackId trackId = MP4FindTrackId(infile, i, NULL, 0); + const char* trackType = MP4GetTrackType(infile, trackId); + + if (!strcmp(trackType, MP4_AUDIO_TRACK_TYPE)) + { + unsigned char *buff = NULL; + int buff_size = 0; + mp4AudioSpecificConfig mp4ASC; + + MP4GetTrackESConfiguration(infile, trackId, &buff, &buff_size); + + if (buff) + { + rc = AudioSpecificConfig(buff, buff_size, &mp4ASC); + free(buff); + + if (rc < 0) + return -1; + return trackId; + } + } + } + + /* can't decode this */ + return -1; +} + +int GetAudioTrack(MP4FileHandle infile) +{ + /* find AAC track */ + int i; + int numTracks = MP4GetNumberOfTracks(infile, NULL, 0); + + for (i = 0; i < numTracks; i++) + { + MP4TrackId trackId = MP4FindTrackId(infile, i, NULL, 0); + const char* trackType = MP4GetTrackType(infile, trackId); + + if (!strcmp(trackType, MP4_AUDIO_TRACK_TYPE)) + { + return trackId; + } + } + + /* can't decode this */ + return -1; +} + +int GetVideoTrack(MP4FileHandle infile) +{ + /* find AAC track */ + int i; + int numTracks = MP4GetNumberOfTracks(infile, NULL, 0); + + for (i = 0; i < numTracks; i++) + { + MP4TrackId trackId = MP4FindTrackId(infile, i, NULL, 0); + const char* trackType = MP4GetTrackType(infile, trackId); + + if (!strcmp(trackType, MP4_VIDEO_TRACK_TYPE)) + { + return trackId; + } + } + + /* can't decode this */ + return -1; +} + +LPTSTR PathFindFileName(LPCTSTR pPath) +{ + LPCTSTR pT; + + for (pT = pPath; *pPath; pPath = CharNext(pPath)) { + if ((pPath[0] == TEXT('\\') || pPath[0] == TEXT(':')) && pPath[1] && (pPath[1] != TEXT('\\'))) + pT = pPath + 1; + } + + return (LPTSTR)pT; // const -> non const +} + +char *convert3in4to3in3(void *sample_buffer, int samples) +{ + int i; + long *sample_buffer24 = (long*)sample_buffer; + char *data = malloc(samples*3*sizeof(char)); + + for (i = 0; i < samples; i++) + { + data[i*3] = sample_buffer24[i] & 0xFF; + data[i*3+1] = (sample_buffer24[i] >> 8) & 0xFF; + data[i*3+2] = (sample_buffer24[i] >> 16) & 0xFF; + } + + return data; +} diff --git a/faad2/src/plugins/QCDMp4/utils.h b/faad2/src/plugins/QCDMp4/utils.h new file mode 100644 index 0000000..494e019 --- /dev/null +++ b/faad2/src/plugins/QCDMp4/utils.h @@ -0,0 +1,40 @@ +/* +** 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 +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** 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. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** 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: utils.h,v 1.3 2003/12/06 04:24:17 rjamorim Exp $ +**/ + +#ifndef UTILS_INCLUDED +#define UTILS_INCLUDED + +#include + +LPTSTR PathFindFileName(LPCTSTR pPath); +int GetVideoTrack(MP4FileHandle infile); +int GetAudioTrack(MP4FileHandle infile); +int GetAACTrack(MP4FileHandle infile); +int StringComp(char const *str1, char const *str2, unsigned long len); +char *convert3in4to3in3(void *sample_buffer, int samples); + +#endif \ No newline at end of file diff --git a/faad2/src/plugins/mpeg4ip/Makefile.am b/faad2/src/plugins/mpeg4ip/Makefile.am new file mode 100644 index 0000000..8024d2a --- /dev/null +++ b/faad2/src/plugins/mpeg4ip/Makefile.am @@ -0,0 +1,22 @@ +libdir = @MPEG4IP_PLAYER_PLUGIN_DIR@ + +lib_LTLIBRARIES = faad2_plugin.la +faad2_plugin_la_LDFLAGS = -module +faad2_plugin_la_SOURCES = \ + faad2.cpp \ + faad2.h \ + aa_file.cpp \ + aa_file.h + +faad2_plugin_la_LIBADD = \ + $(top_builddir)/libfaad/libfaad.la \ + -lm + + +INCLUDES = -I$(top_srcdir)/include + +AM_CFLAGS = -D_REENTRANT -fexceptions + +AM_CXXFLAGS = -D_REENTRANT -DNOCONTROLS -fexceptions + + diff --git a/faad2/src/plugins/mpeg4ip/README_WIN32.txt b/faad2/src/plugins/mpeg4ip/README_WIN32.txt new file mode 100644 index 0000000..7fc864a --- /dev/null +++ b/faad2/src/plugins/mpeg4ip/README_WIN32.txt @@ -0,0 +1,11 @@ +Creating mpeg4ip plugin for Windows. + +You will need to have mpeg4ip installed. If you install it on the same drive, with the top level directory +name of /mpeg4ip, you will have to do nothing other than move the faad2_plugin.dll from the Release or +Debug directory to the same directory as the other mpeg4ip plugins. + +If you install it somewhere else, you will have to change the include paths and link paths in the project +settings for faad2_plugin to the proper directory. Look for /mpeg4ip (or \mpeg4ip) and change all occurances +of these in the file. + +It might be best to hand-edit the faad_plugin.sdp file with wordpad and use the search and replace function. \ No newline at end of file diff --git a/faad2/src/plugins/mpeg4ip/aa_file.cpp b/faad2/src/plugins/mpeg4ip/aa_file.cpp new file mode 100644 index 0000000..a0cccb9 --- /dev/null +++ b/faad2/src/plugins/mpeg4ip/aa_file.cpp @@ -0,0 +1,130 @@ +/* +** MPEG4IP plugin for FAAD2 +** Copyright (C) 2003 Bill May wmay@cisco.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 +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** 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. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** $Id: aa_file.cpp,v 1.2 2004/01/05 14:05:12 menno Exp $ +**/ +/* + * aa_file.cpp - create media structure for aac files + */ + +#include "faad2.h" +codec_data_t *aac_file_check (lib_message_func_t message, + const char *name, + double *max, + char *desc[4] +#ifdef HAVE_PLUGIN_VERSION_0_8 + , CConfigSet *pConfig +#endif +) +{ + aac_codec_t *aac; + int len = strlen(name); + if (strcasecmp(name + len - 4, ".aac") != 0) { + return (NULL); + } + + aac = MALLOC_STRUCTURE(aac_codec_t); + memset(aac, 0, sizeof(*aac)); + *max = 0; + + aac->m_buffer = (uint8_t *)malloc(MAX_READ_BUFFER); + aac->m_buffer_size_max = MAX_READ_BUFFER; + aac->m_ifile = fopen(name, FOPEN_READ_BINARY); + if (aac->m_ifile == NULL) { + free(aac); + return NULL; + } + aac->m_output_frame_size = 1024; + aac->m_info = faacDecOpen(); // use defaults here... + aac->m_buffer_size = fread(aac->m_buffer, + 1, + aac->m_buffer_size_max, + aac->m_ifile); + + unsigned long freq; + unsigned char chans; + + faacDecInit(aac->m_info, (unsigned char *)aac->m_buffer, + aac->m_buffer_size, &freq, &chans); + // may want to actually decode the first frame... + if (freq == 0) { + message(LOG_ERR, aaclib, "Couldn't determine AAC frame rate"); + aac_close((codec_data_t *)aac); + return (NULL); + } + aac->m_freq = freq; + aac->m_chans = chans; + aac->m_faad_inited = 1; + aac->m_framecount = 0; + return ((codec_data_t *)aac); +} + + +int aac_file_next_frame (codec_data_t *your, + uint8_t **buffer, + uint64_t *ts) +{ + aac_codec_t *aac = (aac_codec_t *)your; + + if (aac->m_buffer_on > 0) { + memmove(aac->m_buffer, + &aac->m_buffer[aac->m_buffer_on], + aac->m_buffer_size - aac->m_buffer_on); + } + aac->m_buffer_size -= aac->m_buffer_on; + aac->m_buffer_size += fread(aac->m_buffer + aac->m_buffer_size, + 1, + aac->m_buffer_size_max - aac->m_buffer_size, + aac->m_ifile); + aac->m_buffer_on = 0; + if (aac->m_buffer_size == 0) return 0; + + + uint64_t calc; + calc = aac->m_framecount * 1024 * M_LLU; + calc /= aac->m_freq; + *ts = calc; + *buffer = aac->m_buffer; + aac->m_framecount++; + return (aac->m_buffer_size); +} + +void aac_file_used_for_frame (codec_data_t *ifptr, + uint32_t bytes) +{ + aac_codec_t *aac = (aac_codec_t *)ifptr; + aac->m_buffer_on += bytes; + if (aac->m_buffer_on > aac->m_buffer_size) aac->m_buffer_on = aac->m_buffer_size; +} + +int aac_file_eof (codec_data_t *ifptr) +{ + aac_codec_t *aac = (aac_codec_t *)ifptr; + return aac->m_buffer_on == aac->m_buffer_size && feof(aac->m_ifile); +} + +int aac_raw_file_seek_to (codec_data_t *ifptr, uint64_t ts) +{ + if (ts != 0) return -1; + + aac_codec_t *aac = (aac_codec_t *)ifptr; + rewind(aac->m_ifile); + aac->m_buffer_size = aac->m_buffer_on = 0; + aac->m_framecount = 0; + return 0; +} diff --git a/faad2/src/plugins/mpeg4ip/aa_file.h b/faad2/src/plugins/mpeg4ip/aa_file.h new file mode 100644 index 0000000..dfed15d --- /dev/null +++ b/faad2/src/plugins/mpeg4ip/aa_file.h @@ -0,0 +1,26 @@ +/* +** MPEG4IP plugin for FAAD2 +** Copyright (C) 2003 Bill May wmay@cisco.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 +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** 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. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** $Id: aa_file.h,v 1.1 2003/08/07 17:21:21 menno Exp $ +**/ +/* + * aa_file.h - prototypes for aac files + */ +int create_media_for_aac_file (CPlayerSession *pspstr, + const char *name, + const char **errmsg); diff --git a/faad2/src/plugins/mpeg4ip/faad2.cpp b/faad2/src/plugins/mpeg4ip/faad2.cpp new file mode 100644 index 0000000..72ff0c8 --- /dev/null +++ b/faad2/src/plugins/mpeg4ip/faad2.cpp @@ -0,0 +1,388 @@ +/* +** MPEG4IP plugin for FAAD2 +** Copyright (C) 2003 Bill May wmay@cisco.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 +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** 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. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** $Id: faad2.cpp,v 1.3 2004/08/20 08:30:53 menno Exp $ +**/ +#include "faad2.h" +#include +#include +#include +#include + +#define DEBUG_SYNC 2 + +#ifndef M_LLU +#define M_LLU M_64 +#define LLU U64 +#endif +const char *aaclib="faad2"; + +/* + * Create CAACodec class + */ +static codec_data_t *aac_codec_create (const char *compressor, + int type, + int profile, + format_list_t *media_fmt, + audio_info_t *audio, + const uint8_t *userdata, + uint32_t userdata_size, + audio_vft_t *vft, + void *ifptr) + +{ + aac_codec_t *aac; + + aac = (aac_codec_t *)malloc(sizeof(aac_codec_t)); + memset(aac, 0, sizeof(aac_codec_t)); + + aac->m_vft = vft; + aac->m_ifptr = ifptr; + fmtp_parse_t *fmtp = NULL; + // Start setting up FAAC stuff... + + aac->m_resync_with_header = 1; + aac->m_record_sync_time = 1; + + aac->m_audio_inited = 0; + + // Use media_fmt to indicate that we're streaming. + if (media_fmt != NULL) { + // haven't checked for null buffer + // This is not necessarilly right - it is, for the most part, but + // we should be reading the fmtp statement, and looking at the config. + // (like we do below in the userdata section... + aac->m_freq = media_fmt->rtpmap->clock_rate; + fmtp = parse_fmtp_for_mpeg4(media_fmt->fmt_param, vft->log_msg); + if (fmtp != NULL) { + userdata = fmtp->config_binary; + userdata_size = fmtp->config_binary_len; + } + } + + aac->m_info = faacDecOpen(); + unsigned long srate; + unsigned char chan; + if ((userdata == NULL && fmtp == NULL) || + (faacDecInit2(aac->m_info, + (uint8_t *)userdata, + userdata_size, + &srate, + &chan) < 0)) { + if (fmtp != NULL) free_fmtp_parse(fmtp); + return NULL; + } + + mp4AudioSpecificConfig mp4ASC; + aac->m_output_frame_size = 1024; + if (AudioSpecificConfig((unsigned char *)userdata, + userdata_size, + &mp4ASC)) { + if (mp4ASC.frameLengthFlag) { + aac->m_output_frame_size = 960; + } + } + aac->m_freq = srate; + aac->m_chans = chan; + aac->m_faad_inited = 1; + aac->m_msec_per_frame = aac->m_output_frame_size; + aac->m_msec_per_frame *= M_LLU; + aac->m_msec_per_frame /= aac->m_freq; + + // faad_init_bytestream(&m_info->ld, c_read_byte, c_bookmark, m_bytestream); + + aa_message(LOG_INFO, aaclib, "Setting freq to %d", aac->m_freq); +#if DUMP_OUTPUT_TO_FILE + aac->m_outfile = fopen("temp.raw", "w"); +#endif + if (fmtp != NULL) { + free_fmtp_parse(fmtp); + } + return (codec_data_t *)aac; +} + +void aac_close (codec_data_t *ptr) +{ + if (ptr == NULL) { + return; + } + aac_codec_t *aac = (aac_codec_t *)ptr; + faacDecClose(aac->m_info); + aac->m_info = NULL; + +#if DUMP_OUTPUT_TO_FILE + fclose(aac->m_outfile); +#endif + free(aac); +} + +/* + * Handle pause - basically re-init the codec + */ +static void aac_do_pause (codec_data_t *ifptr) +{ + aac_codec_t *aac = (aac_codec_t *)ifptr; + aac->m_resync_with_header = 1; + aac->m_record_sync_time = 1; + aac->m_audio_inited = 0; + aac->m_ignore_first_sample = 0; + faacDecPostSeekReset(aac->m_info, 0); +} + +/* + * Decode task call for FAAC + */ +static int aac_decode (codec_data_t *ptr, + uint64_t ts, + int from_rtp, + int *sync_frame, + uint8_t *buffer, + uint32_t buflen, + void *userdata) +{ + aac_codec_t *aac = (aac_codec_t *)ptr; + unsigned long bytes_consummed; + int bits = -1; + // struct timezone tz; + + if (aac->m_record_sync_time) { + aac->m_current_frame = 0; + aac->m_record_sync_time = 0; + aac->m_current_time = ts; + aac->m_last_rtp_ts = ts; + } else { + if (aac->m_last_rtp_ts == ts) { + aac->m_current_time += aac->m_msec_per_frame; + aac->m_current_frame++; + } else { + aac->m_last_rtp_ts = ts; + aac->m_current_time = ts; + aac->m_current_frame = 0; + } + + // Note - here m_current_time should pretty much always be >= rtpts. + // If we're not, we most likely want to stop and resync. We don't + // need to keep decoding - just decode this frame and indicate we + // need a resync... That should handle fast forwards... We need + // someway to handle reverses - perhaps if we're more than .5 seconds + // later... + } + + if (aac->m_faad_inited == 0) { + /* + * If not initialized, do so. + */ + abort(); + unsigned long freq; + unsigned char chans; + + faacDecInit(aac->m_info, + (unsigned char *)buffer, + buflen, + &freq, + &chans); + aac->m_freq = freq; + aac->m_chans = chans; + aac->m_faad_inited = 1; + } + + uint8_t *buff; + unsigned long samples; + bytes_consummed = buflen; + //aa_message(LOG_DEBUG, aaclib, "decoding %d bits", buflen * 8); + faacDecFrameInfo frame_info; + buff = (uint8_t *)faacDecDecode(aac->m_info, + &frame_info, + buffer, + buflen); + if (buff != NULL) { + bytes_consummed = frame_info.bytesconsumed; +#if 0 + aa_message(LOG_DEBUG, aaclib, LLU" bytes %d samples %d", + ts, bytes_consummed, frame_info.samples); +#endif + if (aac->m_audio_inited != 0) { + int tempchans = frame_info.channels; + if (tempchans != aac->m_chans) { + aa_message(LOG_NOTICE, aaclib, "chupdate - chans from data is %d", + tempchans); + } + } else { + int tempchans = frame_info.channels; + + if (tempchans == 0) { + aa_message(LOG_ERR, aaclib, "initializing aac, returned channels are 0"); + aac->m_resync_with_header = 1; + aac->m_record_sync_time = 1; + return bytes_consummed; + } + aac->m_chans = tempchans; + aac->m_freq = frame_info.samplerate; + + aac->m_vft->audio_configure(aac->m_ifptr, + aac->m_freq, + aac->m_chans, + (audio_format_t)AUDIO_S16SYS, + aac->m_output_frame_size); + uint8_t *now = aac->m_vft->audio_get_buffer(aac->m_ifptr); + aac->m_audio_inited = 1; + } + /* + * good result - give it to audio sync class + */ +#if DUMP_OUTPUT_TO_FILE + fwrite(buff, aac->m_output_frame_size * 4, 1, aac->m_outfile); +#endif + if (frame_info.samples != 0) { + aac->m_vft->audio_load_buffer(aac->m_ifptr, + buff, + frame_info.samples * 2, + aac->m_last_ts, + aac->m_resync_with_header); + if (aac->m_resync_with_header == 1) { + aac->m_resync_with_header = 0; +#ifdef DEBUG_SYNC + aa_message(LOG_DEBUG, aaclib, "Back to good at "LLU, aac->m_current_time); +#endif + } + } + } else { + aa_message(LOG_ERR, aaclib, "error return is %d", frame_info.error); + aac->m_resync_with_header = 1; +#ifdef DEBUG_SYNC + aa_message(LOG_ERR, aaclib, "Audio decode problem - at "LLU, + aac->m_current_time); +#endif + } + aac->m_last_ts = aac->m_current_time; + return (bytes_consummed); +} + +static const char *aac_compressors[] = { + "aac ", + "mp4a", + "enca", + NULL +}; + +static int aac_codec_check (lib_message_func_t message, + const char *compressor, + int type, + int profile, + format_list_t *fptr, + const uint8_t *userdata, + uint32_t userdata_size +#ifdef HAVE_PLUGIN_VERSION_0_8 + ,CConfigSet *pConfig +#endif + ) +{ + fmtp_parse_t *fmtp = NULL; + if (compressor != NULL && + strcasecmp(compressor, "MP4 FILE") == 0 && + type != -1) { + switch (type) { + case MP4_MPEG2_AAC_MAIN_AUDIO_TYPE: + case MP4_MPEG2_AAC_LC_AUDIO_TYPE: + case MP4_MPEG2_AAC_SSR_AUDIO_TYPE: + case MP4_MPEG4_AUDIO_TYPE: + break; + default: + return -1; + } + } + if (fptr != NULL && + fptr->rtpmap != NULL && + fptr->rtpmap->encode_name != NULL) { + if (strcasecmp(fptr->rtpmap->encode_name, "mpeg4-generic") != 0) { + return -1; + } + if (userdata == NULL) { + fmtp = parse_fmtp_for_mpeg4(fptr->fmt_param, message); + if (fmtp != NULL) { + userdata = fmtp->config_binary; + userdata_size = fmtp->config_binary_len; + } + } + } + if (userdata != NULL) { + mpeg4_audio_config_t audio_config; + decode_mpeg4_audio_config(userdata, userdata_size, &audio_config); + message(LOG_DEBUG, "aac", "audio type is %d", audio_config.audio_object_type); + if (fmtp != NULL) free_fmtp_parse(fmtp); + + if (audio_object_type_is_aac(&audio_config) == 0) { + return -1; + } +#if 0 + if (audio_config.audio_object_type == 17) { + message(LOG_INFO, "aac", "audio type is legal ISMA, but not supported"); + return -1; + } +#endif + return 2; + } +#if 0 + // I'm not sure I want to be here if we don't have an audio config + if (compressor != NULL) { + const char **lptr = aac_compressors; + while (*lptr != NULL) { + if (strcasecmp(*lptr, compressor) == 0) { + return 2; + } + lptr++; + } + } +#endif + return -1; +} + +#ifndef HAVE_PLUGIN_VERSION_0_8 +AUDIO_CODEC_WITH_RAW_FILE_PLUGIN("faad2", + aac_codec_create, + aac_do_pause, + aac_decode, + NULL, + aac_close, + aac_codec_check, + aac_file_check, + aac_file_next_frame, + aac_file_used_for_frame, + aac_raw_file_seek_to, + aac_file_eof + ); +#else +AUDIO_CODEC_WITH_RAW_FILE_PLUGIN("faad2", + aac_codec_create, + aac_do_pause, + aac_decode, + NULL, + aac_close, + aac_codec_check, + aac_file_check, + aac_file_next_frame, + aac_file_used_for_frame, + aac_raw_file_seek_to, + aac_file_eof, + NULL, + 0 + ); +#endif +/* end file aa.cpp */ + + diff --git a/faad2/src/plugins/mpeg4ip/faad2.h b/faad2/src/plugins/mpeg4ip/faad2.h new file mode 100644 index 0000000..b74cdff --- /dev/null +++ b/faad2/src/plugins/mpeg4ip/faad2.h @@ -0,0 +1,92 @@ +/* +** MPEG4IP plugin for FAAD2 +** Copyright (C) 2003 Bill May wmay@cisco.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 +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** 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. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** $Id: faad2.h,v 1.2 2004/01/05 14:05:12 menno Exp $ +**/ +/* + * aa.h - class definition for AAC codec. + */ + +#ifndef __AA_H__ +#define __AA_H__ 1 +#include "faad.h" +#include "codec_plugin.h" + +#ifndef M_LLU +#define M_LLU M_64 +#define LLU U64 +#endif + +typedef struct aac_codec_t { + codec_data_t c; + audio_vft_t *m_vft; + void *m_ifptr; + faacDecHandle m_info; + int m_object_type; + int m_resync_with_header; + int m_record_sync_time; + uint64_t m_current_time; + uint64_t m_last_rtp_ts; + uint64_t m_msec_per_frame; + uint32_t m_current_frame; + int m_audio_inited; + int m_faad_inited; + int m_freq; // frequency + int m_chans; // channels + int m_output_frame_size; +#if DUMP_OUTPUT_TO_FILE + FILE *m_outfile; +#endif + FILE *m_ifile; + uint8_t *m_buffer; + uint32_t m_buffer_size_max; + uint32_t m_buffer_size; + uint32_t m_buffer_on; + uint64_t m_framecount; + int m_ignore_first_sample; + uint64_t m_last_ts; +} aac_codec_t; + +#define m_vft c.v.audio_vft +#define m_ifptr c.ifptr +#define MAX_READ_BUFFER (768 * 8) + +#define aa_message aac->m_vft->log_msg +void aac_close(codec_data_t *ptr); +extern const char *aaclib; + +codec_data_t *aac_file_check(lib_message_func_t message, + const char *name, + double *max, + char *desc[4] +#ifdef HAVE_PLUGIN_VERSION_0_8 + , CConfigSet *pConfig +#endif +); + +int aac_file_next_frame(codec_data_t *ifptr, + uint8_t **buffer, + uint64_t *ts); +int aac_file_eof(codec_data_t *ifptr); + +void aac_file_used_for_frame(codec_data_t *ifptr, + uint32_t bytes); + +int aac_raw_file_seek_to(codec_data_t *ifptr, + uint64_t ts); +#endif diff --git a/faad2/src/plugins/xmms/AUTHORS b/faad2/src/plugins/xmms/AUTHORS new file mode 100644 index 0000000..1def44f --- /dev/null +++ b/faad2/src/plugins/xmms/AUTHORS @@ -0,0 +1,3 @@ +xmms-mp4 plugin for xmms-1.2.x + +re-coded by ciberfred from scratch diff --git a/faad2/src/plugins/xmms/ChangeLog b/faad2/src/plugins/xmms/ChangeLog new file mode 100644 index 0000000..4a89f46 --- /dev/null +++ b/faad2/src/plugins/xmms/ChangeLog @@ -0,0 +1,43 @@ +22 August 2004: + * move from libmp4v2 to libmp4ff + +1 December 2003: + * remove aac plug and merge the aac part with the mp4 part + so now 2 plugins in one :) + +4 juillet 2003: + * package the plugin for faad2 + * upgrade code to libfaad2-2.0 API + * version 0.5 + +15 juin 2003: + * better configuration code + (choose automaticaly at compile time) + * installation must be set by root + * version 0.4 + +15 mai 2003: + * update configure script to be better + * version 0.3 + +01 Novembre 2002: + * check automake/autoconf/libtool for plugin, now 'make install' work + * handle seeking + * configuration box created (thanks glade !) + * handle aac informations + * modification to a better infobox (thanks angain glade !) + * version 0.2 ready to public :) + +25 Aout 2002: + * gtk-1.2.x info file with some 'static' useful ID3 info + * title show in xmms correctly + * version 0.1 ready to public :) + +20 Aout 2002: + * everything... + * work with faad2lib-1.1, id3lib-3.8.0 (glibc-2.2.5, gcc-2.95.3) + i think the minimum required but need testing... + * playlist working + * handle id3tag, the plugin work :) + * new maintener : ciberfred + diff --git a/faad2/src/plugins/xmms/INSTALL b/faad2/src/plugins/xmms/INSTALL new file mode 100644 index 0000000..3f5cccf --- /dev/null +++ b/faad2/src/plugins/xmms/INSTALL @@ -0,0 +1,2 @@ +the installation of this plugin is provide by the faad2 package. +add to configure time the option '--with-xmms' and the plugin will be build \ No newline at end of file diff --git a/faad2/src/plugins/xmms/Makefile.am b/faad2/src/plugins/xmms/Makefile.am new file mode 100644 index 0000000..5d39788 --- /dev/null +++ b/faad2/src/plugins/xmms/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = src + diff --git a/faad2/src/plugins/xmms/NEWS b/faad2/src/plugins/xmms/NEWS new file mode 100644 index 0000000..32b85d3 --- /dev/null +++ b/faad2/src/plugins/xmms/NEWS @@ -0,0 +1,21 @@ +22 Aout 2004 +------------ +modification du system de compilation, passage de libmp4v2 a libmp4ff +en static, libfaad en dynamique. + +1 Decembre 2003 +--------------- +merge du plugin aac et du plugin mp4. modification des script du projet faad +le plugin ne doit pas etre construit en meme temps que le projet + +4 Juillet 2003 +-------------- +integration du plugin xmms-aac dans le projet faad2 version 2.x + +15 aout 2002 +------------ + +Recodage en entier du plugin aac +me contacter par mail a : + +frederic.fondriest@laposte.net diff --git a/faad2/src/plugins/xmms/README b/faad2/src/plugins/xmms/README new file mode 100644 index 0000000..7380891 --- /dev/null +++ b/faad2/src/plugins/xmms/README @@ -0,0 +1,23 @@ + xmms-mp4 plugin v0.5 + (dynamic version) + "a mp4/aac audio player for xmms" + coded by ciberfred from france + ------------- + +This source code allow to xmms to read .mp4/.m4a/.aac files + +About. + This plugin is a merge between aac and mp4 plugin. so now you could read +all new and old files encoded with different encoder and different format +(for the aac part). This is possible since the libfaad2 allow to read +old aac ADTS format. + +For any informations about this plugin contact me at : + +mail : frederic.fondriest@laposte.net +ICQ : 17293220 +aac plugin homepage (and more) : http://fondriest.frederic.free.fr/realisations/ +IRC : irc.eu.freenode.net (#lamip) + +-- +Frederic Fondriest diff --git a/faad2/src/plugins/xmms/TODO b/faad2/src/plugins/xmms/TODO new file mode 100644 index 0000000..e2de779 --- /dev/null +++ b/faad2/src/plugins/xmms/TODO @@ -0,0 +1,6 @@ +TODO: + + * handle AAC info such as MPEG-AAC type, header (ADTS/ADIF),... + * seeking... but it will certainly an option because it need + a reading of whole file.... + * any suggestions ... send me somes messages about it :) diff --git a/faad2/src/plugins/xmms/src/Makefile.am b/faad2/src/plugins/xmms/src/Makefile.am new file mode 100644 index 0000000..33d21fc --- /dev/null +++ b/faad2/src/plugins/xmms/src/Makefile.am @@ -0,0 +1,14 @@ +local_CFLAGS=`$(XMMS_CONFIG) --cflags` -Wall +local_LDFLAGS=`$(XMMS_CONFIG) --libs` +libdir = `$(XMMS_CONFIG) --input-plugin-dir` +lib_LTLIBRARIES = libmp4.la + +libmp4_la_CFLAGS = $(local_CFLAGS) -Wall \ + -I$(top_srcdir)/include -I$(top_srcdir)/common/mp4ff + +libmp4_la_LIBADD = $(top_builddir)/libfaad/libfaad.la \ + $(top_builddir)/common/mp4ff/libmp4ff.a + +libmp4_la_LDFLAGS = -module -avoid-version $(local_LDFLAGS) -lpthread + +libmp4_la_SOURCES = libmp4.c mp4_utils.c aac_utils.c diff --git a/faad2/src/plugins/xmms/src/aac_utils.c b/faad2/src/plugins/xmms/src/aac_utils.c new file mode 100644 index 0000000..45543e6 --- /dev/null +++ b/faad2/src/plugins/xmms/src/aac_utils.c @@ -0,0 +1,104 @@ +/* + * + * utils for AAC informations +*/ +#include +#include +#include +#include + + +#define ADTS_HEADER_SIZE 8 +#define SEEK_TABLE_CHUNK 60 +#define MPEG4_TYPE 0 +#define MPEG2_TYPE 1 + +// Read ADTS header, the file descriptor must be at +// the begining of the aac frame not at the id3tag + +int getAacInfo(FILE *fd) +{ + unsigned char header[ADTS_HEADER_SIZE]; + unsigned int id; + unsigned long originPosition; + + originPosition = ftell(fd); + if(fread(header, 1, ADTS_HEADER_SIZE, fd) != ADTS_HEADER_SIZE){ + fseek(fd, originPosition, SEEK_SET); + return(-1); + } + if(!((header[0]==0xFF)&&((header[1]& 0xF6)==0xF0))){ + printf("Bad header\n"); + return(-1); + } + id = header[1]&0x08; + if(id==0){//MPEG-4 AAC + fseek(fd, originPosition, SEEK_SET); + return(MPEG4_TYPE); + }else{ + fseek(fd, originPosition, SEEK_SET); + return(MPEG2_TYPE); + } + fseek(fd, originPosition, SEEK_SET); + return(-1); +} + +// as AAC is VBR we need to check all ADTS header +// to enable seeking... +// there is no other solution +void checkADTSForSeeking(FILE *fd, + unsigned long **seekTable, + unsigned long *seekTableLength) +{ + unsigned long originPosition; + unsigned long position; + unsigned char header[ADTS_HEADER_SIZE]; + unsigned int frameCount, frameLength, frameInsec; + unsigned int id=0, seconds=0; + + originPosition = ftell(fd); + + for(frameCount=0,frameInsec=0;; frameCount++,frameInsec++){ + position = ftell(fd); + if(fread(header, 1, ADTS_HEADER_SIZE, fd)!=ADTS_HEADER_SIZE){ + break; + } + if(!strncmp(header, "ID3", 3)){ + break; + } + if(!((header[0]==0xFF)&&((header[1]& 0xF6)==0xF0))){ + printf("error : Bad 1st header, file may be corrupt !\n"); + break; + } + if(!frameCount){ + id=header[1]&0x08; + if(((*seekTable) = malloc(SEEK_TABLE_CHUNK * sizeof(unsigned long)))==0){ + printf("malloc error\n"); + return; + } + (*seekTableLength) = SEEK_TABLE_CHUNK; + } + + //if(id==0){//MPEG-4 + //frameLength = ((unsigned int)header[4]<<5)|((unsigned int)header[5]>>3); + //}else{//MPEG-2 + frameLength = (((unsigned int)header[3]&0x3)<<11)|((unsigned int)header[4]<<3)|(header[5]>>5); + //} + if(frameInsec==43){//??? + frameInsec=0; + } + if(frameInsec==0){ + if(seconds == (*seekTableLength)){ + (*seekTable) = realloc((*seekTable), (seconds+SEEK_TABLE_CHUNK)*sizeof(unsigned long)); + (*seekTableLength) = seconds+SEEK_TABLE_CHUNK; + } + (*seekTable)[seconds] = position; + seconds++; + } + if(fseek(fd, frameLength-ADTS_HEADER_SIZE, SEEK_CUR)==-1){ + break; + } + } + (*seekTableLength) = seconds; + fseek(fd, originPosition, SEEK_SET); +} diff --git a/faad2/src/plugins/xmms/src/libmp4.c b/faad2/src/plugins/xmms/src/libmp4.c new file mode 100644 index 0000000..117a087 --- /dev/null +++ b/faad2/src/plugins/xmms/src/libmp4.c @@ -0,0 +1,586 @@ +/* + * MP4/AAC decoder for xmms + * + * OPTIONNAL need + * -------------- + * libid3 (3.8.x - www.id3.org) +*/ + +#include +#include +#include +#include + +#if defined(HAVE_BMP) +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif /*HAVE_BMP*/ + +#include "neaacdec.h" +#include "mp4ff.h" + +#define MP4_DESCRIPTION "MP4 & MPEG2/4-AAC audio player - 1.2.x" +#define MP4_VERSION "ver. 0.5-faad2-version - 22 August 2004" +#define MP4_ABOUT "Written by ciberfred" +#define BUFFER_SIZE FAAD_MIN_STREAMSIZE*64 + +static void mp4_init(void); +static void mp4_about(void); +static void mp4_play(char *); +static void mp4_stop(void); +static void mp4_pause(short); +static void mp4_seek(int); +static int mp4_getTime(void); +static void mp4_cleanup(void); +static void mp4_getSongTitle(char *, char **, int *); +static void mp4_getSongInfo(char *); +static int mp4_isFile(char *); +static void* mp4Decode(void *); + +InputPlugin mp4_ip = + { + 0, // handle + 0, // filename + MP4_DESCRIPTION, + mp4_init, + mp4_about, + 0, // configuration + mp4_isFile, + 0, //scandir + mp4_play, + mp4_stop, + mp4_pause, + mp4_seek, + 0, // set equalizer + mp4_getTime, + 0, // get volume + 0, + mp4_cleanup, + 0, // obsolete + 0, // send visualisation data + 0, // set player window info + 0, // set song title text + mp4_getSongTitle, // get song title text + mp4_getSongInfo, // info box + 0, // to output plugin + }; + +typedef struct _mp4cfg{ + gshort file_type; +#define FILE_UNKNOW 0 +#define FILE_MP4 1 +#define FILE_AAC 2 +} Mp4Config; + +static Mp4Config mp4cfg; +static gboolean bPlaying = FALSE; +static pthread_t decodeThread; +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static int seekPosition = -1; + +// Functions from mp4_utils.c +extern int getAACTrack(mp4ff_t *infile); +extern mp4ff_callback_t *getMP4FF_cb(FILE *mp4file); +extern char *getMP4title(mp4ff_t *infile, char *filename); +extern void getMP4info(char* filename, FILE *mp4file); + +InputPlugin *get_iplugin_info(void) +{ + return(&mp4_ip); +} + +static void mp4_init(void) +{ + memset(&decodeThread, 0, sizeof(pthread_t)); + mp4cfg.file_type = FILE_UNKNOW; + seekPosition = -1; + return; +} + +static void mp4_play(char *filename) +{ + bPlaying = TRUE; + pthread_create(&decodeThread, 0, mp4Decode, g_strdup(filename)); + return; +} + +static void mp4_stop(void) +{ + if(bPlaying){ + bPlaying = FALSE; + pthread_join(decodeThread, NULL); + memset(&decodeThread, 0, sizeof(pthread_t)); + mp4_ip.output->close_audio(); + } +} + +static int mp4_isFile(char *filename) +{ + if(filename){ + gchar* extention; + + extention = strrchr(filename, '.'); + if(extention && + (!strncasecmp(extention, ".mp4", 4) || // official extention + !strncasecmp(extention, ".m4a", 4) || // Apple mp4 extention + !strncasecmp(extention, ".aac", 4) // old MPEG2/4-AAC extention + )){ + return (1); + } + } + return(0); +} + +static void mp4_about(void) +{ + static GtkWidget *aboutbox; + + if(aboutbox!=NULL) + return; + aboutbox = xmms_show_message("About MP4 AAC player plugin", + "libfaad2-" FAAD2_VERSION "\n" + "plugin version: " MP4_VERSION "\n" + MP4_ABOUT, + "Ok", FALSE, NULL, NULL); + gtk_signal_connect(GTK_OBJECT(aboutbox), "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), + &aboutbox); +} + +static void mp4_pause(short flag) +{ + mp4_ip.output->pause(flag); +} + +static void mp4_seek(int time) +{ + seekPosition = time; + while(bPlaying && seekPosition!=-1) + xmms_usleep(10000); +} + +static int mp4_getTime(void) +{ + if(!bPlaying) + return (-1); + else + return (mp4_ip.output->output_time()); +} + +static void mp4_cleanup(void) +{ +} + +void mp4_get_file_type(FILE *mp4file) +{ + unsigned char header[10] = {0}; + + fseek(mp4file, 0, SEEK_SET); + fread(header, 1, 8, mp4file); + if(header[4]=='f' && + header[5]=='t' && + header[6]=='y' && + header[7]=='p'){ + mp4cfg.file_type = FILE_MP4; + }else{ + mp4cfg.file_type = FILE_AAC; + } +} + +static void mp4_getSongTitle(char *filename, char **title, int *len) { + FILE* mp4file; + + (*title) = NULL; + (*len) = -1; + + if((mp4file = fopen(filename, "rb"))){ + mp4_get_file_type(mp4file); + fseek(mp4file, 0, SEEK_SET); + if(mp4cfg.file_type == FILE_MP4){ + mp4ff_callback_t* mp4cb; + mp4ff_t* infile; + gint mp4track; + + mp4cb = getMP4FF_cb(mp4file); + if ((infile = mp4ff_open_read_metaonly(mp4cb)) && + ((mp4track = getAACTrack(infile)) >= 0)){ + (*title) = getMP4title(infile, filename); + + double track_duration = mp4ff_get_track_duration(infile, mp4track); + unsigned long time_scale = mp4ff_time_scale(infile, mp4track); + unsigned long length = (track_duration * 1000 / time_scale); + (*len) = length; + } + if(infile) mp4ff_close(infile); + if(mp4cb) g_free(mp4cb); + } + else{ + // Check AAC ID3 tag... + } + fclose(mp4file); + } +} + +static void mp4_getSongInfo(char *filename) +{ + FILE* mp4file; + if((mp4file = fopen(filename, "rb"))){ + if (mp4cfg.file_type == FILE_UNKNOW) + mp4_get_file_type(mp4file); + fseek(mp4file, 0, SEEK_SET); + if(mp4cfg.file_type == FILE_MP4) + getMP4info(filename, mp4file); + else if(mp4cfg.file_type == FILE_AAC) + /* + * check the id3tagv2 + */ + ; + fclose(mp4file); + } +} + +static void *mp4Decode(void *args) +{ + FILE* mp4file; + + pthread_mutex_lock(&mutex); + seekPosition = -1; + bPlaying = TRUE; + + if(!(mp4file = fopen(args, "rb"))){ + g_print("MP4!AAC - Can't open file\n"); + g_free(args); + bPlaying = FALSE; + pthread_mutex_unlock(&mutex); + pthread_exit(NULL); + + } + mp4_get_file_type(mp4file); + fseek(mp4file, 0, SEEK_SET); + if(mp4cfg.file_type == FILE_MP4){// We are reading a MP4 file + mp4ff_callback_t* mp4cb; + mp4ff_t* infile; + gint mp4track; + + mp4cb = getMP4FF_cb(mp4file); + if(!(infile = mp4ff_open_read(mp4cb))){ + g_print("MP4 - Can't open file\n"); + goto end; + } + + if((mp4track = getAACTrack(infile)) < 0){ + /* + * TODO: check here for others Audio format..... + * + */ + g_print("Unsupported Audio track type\n"); + g_free(args); + fclose(mp4file); + bPlaying = FALSE; + pthread_mutex_unlock(&mutex); + pthread_exit(NULL); + }else{ + NeAACDecHandle decoder; + unsigned char *buffer = NULL; + guint bufferSize = 0; + gulong samplerate; + guchar channels; + //guint avgBitrate; + //MP4Duration duration; + int msDuration; + int numSamples; + int sampleID = 0; + unsigned int framesize; + mp4AudioSpecificConfig mp4ASC; + gchar *xmmstitle; + + decoder = NeAACDecOpen(); + mp4ff_get_decoder_config(infile, mp4track, &buffer, &bufferSize); + if(NeAACDecInit2(decoder, buffer, bufferSize, &samplerate, &channels)<0){ + goto end; + } + if(buffer){ + framesize = 1024; + if(NeAACDecAudioSpecificConfig(buffer, bufferSize, &mp4ASC) >= 0){ + if(mp4ASC.frameLengthFlag == 1) framesize = 960; + if(mp4ASC.sbr_present_flag == 1) framesize *= 2; + } + g_free(buffer); + } + if(channels == 0){ + g_print("Number of Channels not supported\n"); + goto end; + } + + //duration = MP4GetTrackDuration(mp4file, mp4track); + //msDuration = MP4ConvertFromTrackDuration(mp4file, mp4track, + // duration,MP4_MSECS_TIME_SCALE); + + //msDuration = mp4ff_get_track_duration(infile, mp4track); + //printf("%d\n", msDuration); + + //numSamples = MP4GetTrackNumberOfSamples(mp4file, mp4track); + numSamples = mp4ff_num_samples(infile, mp4track); + { + float f = 1024.0; + if(mp4ASC.sbr_present_flag == 1) + f = f * 2.0; + msDuration = ((float)numSamples*(float)(f-1.0)/ + (float)samplerate)*1000; + } + xmmstitle = getMP4title(infile, args); + mp4_ip.output->open_audio(FMT_S16_NE, samplerate, channels); + mp4_ip.output->flush(0); + mp4_ip.set_info(xmmstitle, msDuration, -1, samplerate/1000, channels); + g_print("MP4 - %d channels @ %ld Hz\n", channels, samplerate); + + while(bPlaying){ + void* sampleBuffer; + faacDecFrameInfo frameInfo; + gint rc; + + if(seekPosition!=-1){ + /* + duration = MP4ConvertToTrackDuration(mp4file, + mp4track, + seekPosition*1000, + MP4_MSECS_TIME_SCALE); + sampleID = MP4GetSampleIdFromTime(mp4file, mp4track, duration, 0); + */ + float f = 1024.0; + if(mp4ASC.sbr_present_flag == 1) + f = f * 2.0; + sampleID = (float)seekPosition*(float)samplerate/(float)(f-1.0); + mp4_ip.output->flush(seekPosition*1000); + seekPosition = -1; + } + buffer=NULL; + bufferSize=0; + rc = mp4ff_read_sample(infile, mp4track, sampleID++, + &buffer, &bufferSize); + //g_print("%d/%d\n", sampleID-1, numSamples); + if((rc==0) || (buffer== NULL)){ + g_print("MP4: read error\n"); + sampleBuffer = NULL; + sampleID=0; + mp4_ip.output->buffer_free(); + goto end; + }else{ + sampleBuffer = NeAACDecDecode(decoder, &frameInfo, buffer, bufferSize); + if(frameInfo.error > 0){ + g_print("MP4: %s\n", + faacDecGetErrorMessage(frameInfo.error)); + goto end; + } + if(buffer){ + g_free(buffer); buffer=NULL; bufferSize=0; + } + while(bPlaying && mp4_ip.output->buffer_free()written_time(), + FMT_S16_NE, + channels, + frameInfo.samples<<1, + sampleBuffer); + mp4_ip.output->write_audio(sampleBuffer, frameInfo.samples<<1); + if(sampleID >= numSamples){ + break; + } + } + while(bPlaying && mp4_ip.output->buffer_playing() && mp4_ip.output->buffer_free()){ + xmms_usleep(10000); + } +end: + mp4_ip.output->close_audio(); + g_free(args); + NeAACDecClose(decoder); + if(infile) mp4ff_close(infile); + if(mp4cb) g_free(mp4cb); + bPlaying = FALSE; + fclose(mp4file); + pthread_mutex_unlock(&mutex); + pthread_exit(NULL); + } + }else{ + // WE ARE READING AN AAC FILE + FILE *file = NULL; + NeAACDecHandle decoder = 0; + guchar *buffer = 0; + gulong bufferconsumed = 0; + gulong samplerate = 0; + guchar channels; + gulong buffervalid = 0; + TitleInput* input; + gchar *temp = g_strdup(args); + gchar *ext = strrchr(temp, '.'); + gchar *xmmstitle = NULL; + NeAACDecConfigurationPtr config; + + if((file = fopen(args, "rb")) == 0){ + g_print("AAC: can't find file %s\n", args); + bPlaying = FALSE; + pthread_mutex_unlock(&mutex); + pthread_exit(NULL); + } + if((decoder = NeAACDecOpen()) == NULL){ + g_print("AAC: Open Decoder Error\n"); + fclose(file); + bPlaying = FALSE; + pthread_mutex_unlock(&mutex); + pthread_exit(NULL); + } + config = NeAACDecGetCurrentConfiguration(decoder); + config->useOldADTSFormat = 0; + NeAACDecSetConfiguration(decoder, config); + if((buffer = g_malloc(BUFFER_SIZE)) == NULL){ + g_print("AAC: error g_malloc\n"); + fclose(file); + bPlaying = FALSE; + NeAACDecClose(decoder); + pthread_mutex_unlock(&mutex); + pthread_exit(NULL); + } + if((buffervalid = fread(buffer, 1, BUFFER_SIZE, file))==0){ + g_print("AAC: Error reading file\n"); + g_free(buffer); + fclose(file); + bPlaying = FALSE; + NeAACDecClose(decoder); + pthread_mutex_unlock(&mutex); + pthread_exit(NULL); + } + XMMS_NEW_TITLEINPUT(input); + input->file_name = g_basename(temp); + input->file_ext = ext ? ext+1 : NULL; + input->file_path = temp; + if(!strncmp(buffer, "ID3", 3)){ + gint size = 0; + + fseek(file, 0, SEEK_SET); + size = (buffer[6]<<21) | (buffer[7]<<14) | (buffer[8]<<7) | buffer[9]; + size+=10; + fread(buffer, 1, size, file); + buffervalid = fread(buffer, 1, BUFFER_SIZE, file); + } + xmmstitle = xmms_get_titlestring(xmms_get_gentitle_format(), input); + if(xmmstitle == NULL) + xmmstitle = g_strdup(input->file_name); + if(temp) g_free(temp); + if(input->performer) g_free(input->performer); + if(input->album_name) g_free(input->album_name); + if(input->track_name) g_free(input->track_name); + if(input->genre) g_free(input->genre); + g_free(input); + bufferconsumed = NeAACDecInit(decoder, + buffer, + buffervalid, + &samplerate, + &channels); + if(mp4_ip.output->open_audio(FMT_S16_NE,samplerate,channels) == FALSE){ + g_print("AAC: Output Error\n"); + g_free(buffer); buffer=0; + faacDecClose(decoder); + fclose(file); + mp4_ip.output->close_audio(); + /* + if(positionTable){ + g_free(positionTable); positionTable=0; + } + */ + g_free(xmmstitle); + bPlaying = FALSE; + pthread_mutex_unlock(&mutex); + pthread_exit(NULL); + } + //if(bSeek){ + //mp4_ip.set_info(xmmstitle, lenght*1000, -1, samplerate, channels); + //}else{ + mp4_ip.set_info(xmmstitle, -1, -1, samplerate, channels); + //} + mp4_ip.output->flush(0); + + while(bPlaying && buffervalid > 0){ + NeAACDecFrameInfo finfo; + unsigned long samplesdecoded; + char* sample_buffer = NULL; + /* + if(bSeek && seekPosition!=-1){ + fseek(file, positionTable[seekPosition], SEEK_SET); + bufferconsumed=0; + buffervalid = fread(buffer, 1, BUFFER_SIZE, file); + aac_ip.output->flush(seekPosition*1000); + seekPosition=-1; + } + */ + if(bufferconsumed > 0){ + memmove(buffer, &buffer[bufferconsumed], buffervalid-bufferconsumed); + buffervalid -= bufferconsumed; + buffervalid += fread(&buffer[buffervalid], 1, + BUFFER_SIZE-buffervalid, file); + bufferconsumed = 0; + } + sample_buffer = NeAACDecDecode(decoder, &finfo, buffer, buffervalid); + if(finfo.error){ + config = NeAACDecGetCurrentConfiguration(decoder); + if(config->useOldADTSFormat != 1){ + NeAACDecClose(decoder); + decoder = NeAACDecOpen(); + config = NeAACDecGetCurrentConfiguration(decoder); + config->useOldADTSFormat = 1; + NeAACDecSetConfiguration(decoder, config); + finfo.bytesconsumed=0; + finfo.samples = 0; + NeAACDecInit(decoder, + buffer, + buffervalid, + &samplerate, + &channels); + }else{ + g_print("FAAD2 Warning %s\n", NeAACDecGetErrorMessage(finfo.error)); + buffervalid = 0; + } + } + bufferconsumed += finfo.bytesconsumed; + samplesdecoded = finfo.samples; + if((samplesdecoded<=0) && !sample_buffer){ + g_print("AAC: error sample decoding\n"); + continue; + } + while(bPlaying && mp4_ip.output->buffer_free() < (samplesdecoded<<1)){ + xmms_usleep(10000); + } + mp4_ip.add_vis_pcm(mp4_ip.output->written_time(), + FMT_S16_LE, channels, + samplesdecoded<<1, sample_buffer); + mp4_ip.output->write_audio(sample_buffer, samplesdecoded<<1); + } + while(bPlaying && mp4_ip.output->buffer_playing()){ + xmms_usleep(10000); + } + mp4_ip.output->buffer_free(); + mp4_ip.output->close_audio(); + bPlaying = FALSE; + g_free(buffer); + NeAACDecClose(decoder); + g_free(xmmstitle); + fclose(file); + seekPosition = -1; + /* + if(positionTable){ + g_free(positionTable); positionTable=0; + } + */ + bPlaying = FALSE; + pthread_mutex_unlock(&mutex); + pthread_exit(NULL); + + } +} diff --git a/faad2/src/plugins/xmms/src/mp4_utils.c b/faad2/src/plugins/xmms/src/mp4_utils.c new file mode 100644 index 0000000..a517963 --- /dev/null +++ b/faad2/src/plugins/xmms/src/mp4_utils.c @@ -0,0 +1,469 @@ +/* + * some functions for MP4 files +*/ + +#include "mp4ff.h" +#include "faad.h" + +#include +#include +#include +#include +#include +#include +#include + +const char *mp4AudioNames[]= + { + "MPEG-1 Audio Layers 1,2 or 3", + "MPEG-2 low biterate (MPEG-1 extension) - MP3", + "MPEG-2 AAC Main Profile", + "MPEG-2 AAC Low Complexity profile", + "MPEG-2 AAC SSR profile", + "MPEG-4 audio (MPEG-4 AAC)", + 0 + }; + +/* MPEG-4 Audio types from 14496-3 Table 1.5.1 (from mp4.h)*/ +const char *mpeg4AudioNames[]= + { + "!!!!MPEG-4 Audio track Invalid !!!!!!!", + "MPEG-4 AAC Main profile", + "MPEG-4 AAC Low Complexity profile", + "MPEG-4 AAC SSR profile", + "MPEG-4 AAC Long Term Prediction profile", + "MPEG-4 AAC Scalable", + "MPEG-4 CELP", + "MPEG-4 HVXC", + "MPEG-4 Text To Speech", + "MPEG-4 Main Synthetic profile", + "MPEG-4 Wavetable Synthesis profile", + "MPEG-4 MIDI Profile", + "MPEG-4 Algorithmic Synthesis and Audio FX profile" + }; + +static GtkWidget *mp4_info_dialog = NULL; + +/* + * find AAC track +*/ + +int getAACTrack(mp4ff_t *infile) +{ + int i, rc; + int numTracks = mp4ff_total_tracks(infile); + + printf("total-tracks: %d\n", numTracks); + for(i=0; iread = read_callback; + mp4cb->seek = seek_callback; + mp4cb->user_data = mp4file; + return mp4cb; +} + +/* + * Function to display an info box for an mp4 file. + * This code is based heavily on fileinfo.c from the xmms mpg123 + * plugin, and the info box layout mimics that plugin. +*/ +void create_mp4_info_dialog (char *filename, FILE *mp4file, mp4ff_t *infile, gint mp4track) +{ + char *window_title, *value, *value2; + static GtkWidget *filename_entry, *title_entry, *artist_entry, *album_entry; + static GtkWidget *genre_entry, *year_entry, *track_entry, *comment_entry; + static GtkWidget *mp4_info_label; + + if (!mp4_info_dialog) + { + GtkWidget *dialog_vbox1, *vbox1, *hbox2, *hbox3, *hbox4; + GtkWidget *frame2, *frame3, *table1, *dialog_action_area1; + GtkWidget *filename_label, *title_label, *artist_label, *album_label; + GtkWidget *genre_label, *year_label, *track_label, *comment_label; + GtkWidget *close_button; + + mp4_info_dialog = gtk_dialog_new (); + gtk_object_set_data (GTK_OBJECT (mp4_info_dialog), "mp4_info_dialog", mp4_info_dialog); + gtk_window_set_policy (GTK_WINDOW (mp4_info_dialog), TRUE, TRUE, FALSE); + gtk_signal_connect(GTK_OBJECT (mp4_info_dialog), "destroy", + gtk_widget_destroyed, &mp4_info_dialog); + + dialog_vbox1 = GTK_DIALOG (mp4_info_dialog)->vbox; + gtk_object_set_data (GTK_OBJECT (mp4_info_dialog), "dialog_vbox1", dialog_vbox1); + gtk_container_set_border_width (GTK_CONTAINER (dialog_vbox1), 3); + + hbox2 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox2); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "hbox2", hbox2, + (GtkDestroyNotify) gtk_widget_unref); + gtk_box_pack_start (GTK_BOX (dialog_vbox1), hbox2, FALSE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox2), 3); + + filename_label = gtk_label_new ("Filename: "); + gtk_widget_ref (filename_label); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "filename_label", filename_label, + (GtkDestroyNotify) gtk_widget_unref); + gtk_box_pack_start (GTK_BOX (hbox2), filename_label, FALSE, FALSE, 0); + + filename_entry = gtk_entry_new (); + gtk_widget_ref (filename_entry); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "filename_entry", filename_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_box_pack_start (GTK_BOX (hbox2), filename_entry, TRUE, TRUE, 0); + gtk_entry_set_editable (GTK_ENTRY (filename_entry), FALSE); + + hbox3 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox3); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "hbox3", hbox3, + (GtkDestroyNotify) gtk_widget_unref); + gtk_box_pack_start (GTK_BOX (dialog_vbox1), hbox3, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox3), 3); + + frame2 = gtk_frame_new ("Tag Info: "); + gtk_widget_ref (frame2); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "frame2", frame2, + (GtkDestroyNotify) gtk_widget_unref); + gtk_box_pack_start (GTK_BOX (hbox3), frame2, TRUE, TRUE, 0); + + table1 = gtk_table_new (6, 2, FALSE); + gtk_widget_ref (table1); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "table1", table1, + (GtkDestroyNotify) gtk_widget_unref); + gtk_container_add (GTK_CONTAINER (frame2), table1); + gtk_container_set_border_width (GTK_CONTAINER (table1), 5); + + comment_label = gtk_label_new ("Comment: "); + gtk_widget_ref (comment_label); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "comment_label", comment_label, + (GtkDestroyNotify) gtk_widget_unref); + gtk_table_attach (GTK_TABLE (table1), comment_label, 0, 1, 5, 6, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND), 0, 0); + gtk_misc_set_alignment (GTK_MISC (comment_label), 1, 0.5); + + genre_label = gtk_label_new ("Genre: "); + gtk_widget_ref (genre_label); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "genre_label", genre_label, + (GtkDestroyNotify) gtk_widget_unref); + gtk_table_attach (GTK_TABLE (table1), genre_label, 0, 1, 4, 5, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND), 0, 0); + gtk_misc_set_alignment (GTK_MISC (genre_label), 1, 0.5); + + album_label = gtk_label_new ("Album: "); + gtk_widget_ref (album_label); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "album_label", album_label, + (GtkDestroyNotify) gtk_widget_unref); + gtk_table_attach (GTK_TABLE (table1), album_label, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND), 0, 0); + gtk_misc_set_alignment (GTK_MISC (album_label), 1, 0.5); + + title_label = gtk_label_new ("Title: "); + gtk_widget_ref (title_label); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "title_label", title_label, + (GtkDestroyNotify) gtk_widget_unref); + gtk_table_attach (GTK_TABLE (table1), title_label, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND), 0, 0); + gtk_misc_set_alignment (GTK_MISC (title_label), 1, 0.5); + + artist_label = gtk_label_new ("Artist: "); + gtk_widget_ref (artist_label); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "artist_label", artist_label, + (GtkDestroyNotify) gtk_widget_unref); + gtk_table_attach (GTK_TABLE (table1), artist_label, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND), 0, 0); + gtk_misc_set_alignment (GTK_MISC (artist_label), 1, 0.5); + + year_label = gtk_label_new ("Year: "); + gtk_widget_ref (year_label); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "year_label", year_label, + (GtkDestroyNotify) gtk_widget_unref); + gtk_table_attach (GTK_TABLE (table1), year_label, 0, 1, 3, 4, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND), 0, 0); + gtk_misc_set_alignment (GTK_MISC (year_label), 1, 0.5); + + hbox4 = gtk_hbox_new (FALSE, 0); + gtk_widget_ref (hbox4); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "hbox4", hbox4, + (GtkDestroyNotify) gtk_widget_unref); + gtk_table_attach (GTK_TABLE (table1), hbox4, 1, 2, 3, 4, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + + year_entry = gtk_entry_new (); + gtk_widget_ref (year_entry); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "year_entry", year_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_box_pack_start (GTK_BOX (hbox4), year_entry, FALSE, FALSE, 0); + gtk_widget_set_usize (year_entry, 60, -2); + gtk_entry_set_editable (GTK_ENTRY (year_entry), FALSE); + + track_label = gtk_label_new (" Track: "); + gtk_widget_ref (track_label); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "track_label", track_label, + (GtkDestroyNotify) gtk_widget_unref); + gtk_box_pack_start (GTK_BOX (hbox4), track_label, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (track_label), 1, 0.5); + + track_entry = gtk_entry_new (); + gtk_widget_ref (track_entry); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "track_entry", track_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_box_pack_start (GTK_BOX (hbox4), track_entry, FALSE, FALSE, 0); + gtk_widget_set_usize (track_entry, 60, -2); + gtk_entry_set_editable (GTK_ENTRY (track_entry), FALSE); + + title_entry = gtk_entry_new (); + gtk_widget_ref (title_entry); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "title_entry", title_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_table_attach (GTK_TABLE (table1), title_entry, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_entry_set_editable (GTK_ENTRY (title_entry), FALSE); + + artist_entry = gtk_entry_new (); + gtk_widget_ref (artist_entry); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "artist_entry", artist_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_table_attach (GTK_TABLE (table1), artist_entry, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_entry_set_editable (GTK_ENTRY (artist_entry), FALSE); + + album_entry = gtk_entry_new (); + gtk_widget_ref (album_entry); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "album_entry", album_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_table_attach (GTK_TABLE (table1), album_entry, 1, 2, 2, 3, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_entry_set_editable (GTK_ENTRY (album_entry), FALSE); + + genre_entry = gtk_entry_new (); + gtk_widget_ref (genre_entry); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "genre_entry", genre_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_table_attach (GTK_TABLE (table1), genre_entry, 1, 2, 4, 5, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_entry_set_editable (GTK_ENTRY (genre_entry), FALSE); + + comment_entry = gtk_entry_new (); + gtk_widget_ref (comment_entry); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "comment_entry", comment_entry, + (GtkDestroyNotify) gtk_widget_unref); + gtk_table_attach (GTK_TABLE (table1), comment_entry, 1, 2, 5, 6, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_entry_set_editable (GTK_ENTRY (comment_entry), FALSE); + + frame3 = gtk_frame_new ("MP4 Info: "); + gtk_widget_ref (frame3); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "frame3", frame3, + (GtkDestroyNotify) gtk_widget_unref); + gtk_box_pack_start (GTK_BOX (hbox3), frame3, FALSE, TRUE, 0); + + vbox1 = gtk_vbox_new (FALSE, 0); + gtk_widget_ref (vbox1); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "vbox1", vbox1, + (GtkDestroyNotify) gtk_widget_unref); + gtk_container_add (GTK_CONTAINER (frame3), vbox1); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), 5); + + mp4_info_label = gtk_label_new (""); + gtk_widget_ref (mp4_info_label); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "mp4_info_label", mp4_info_label, + (GtkDestroyNotify) gtk_widget_unref); + gtk_box_pack_start (GTK_BOX (vbox1), mp4_info_label, TRUE, TRUE, 0); + gtk_label_set_justify (GTK_LABEL (mp4_info_label), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment (GTK_MISC (mp4_info_label), 0, 0); + + dialog_action_area1 = GTK_DIALOG (mp4_info_dialog)->action_area; + gtk_object_set_data (GTK_OBJECT (mp4_info_dialog), "dialog_action_area1", dialog_action_area1); + gtk_container_set_border_width (GTK_CONTAINER (dialog_action_area1), 4); + + close_button = gtk_button_new_with_label ("Close"); + gtk_widget_ref (close_button); + gtk_object_set_data_full (GTK_OBJECT (mp4_info_dialog), "close_button", close_button, + (GtkDestroyNotify) gtk_widget_unref); + gtk_box_pack_start (GTK_BOX (dialog_action_area1), close_button, FALSE, FALSE, 0); + + gtk_signal_connect_object (GTK_OBJECT (close_button), "clicked", + GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (mp4_info_dialog)); + + } + + window_title = g_strdup_printf("File Info - %s", g_basename(filename)); + gtk_window_set_title (GTK_WINDOW (mp4_info_dialog), window_title); + g_free(window_title); + + gtk_entry_set_text (GTK_ENTRY (filename_entry), filename); + + gtk_entry_set_text (GTK_ENTRY (title_entry), ""); + gtk_entry_set_text (GTK_ENTRY (artist_entry), ""); + gtk_entry_set_text (GTK_ENTRY (album_entry), ""); + gtk_entry_set_text (GTK_ENTRY (year_entry), ""); + gtk_entry_set_text (GTK_ENTRY (track_entry), ""); + gtk_entry_set_text (GTK_ENTRY (genre_entry), ""); + gtk_entry_set_text (GTK_ENTRY (comment_entry), ""); + + if ((mp4ff_meta_get_title(infile, &value)) && value != NULL) { + gtk_entry_set_text (GTK_ENTRY(title_entry), value); + g_free(value); + } + if ((mp4ff_meta_get_artist(infile, &value)) && value != NULL) { + gtk_entry_set_text (GTK_ENTRY(artist_entry), value); + g_free(value); + } + if ((mp4ff_meta_get_album(infile, &value)) && value != NULL) { + gtk_entry_set_text (GTK_ENTRY(album_entry), value); + g_free(value); + } + if ((mp4ff_meta_get_date(infile, &value)) && value != NULL) { + gtk_entry_set_text (GTK_ENTRY(year_entry), value); + g_free(value); + } + if ((mp4ff_meta_get_track(infile, &value)) && value != NULL) { + if ((mp4ff_meta_get_totaltracks(infile, &value2)) && value2 != NULL) { + char *tmp = g_strdup_printf("%s of %s", value, value2); + g_free(value2); + g_free(value); + value = tmp; + } + gtk_entry_set_text (GTK_ENTRY(track_entry), value); + g_free(value); + } + if ((mp4ff_meta_get_genre(infile, &value)) && value != NULL) { + gtk_entry_set_text (GTK_ENTRY(genre_entry), value); + g_free(value); + } + if ((mp4ff_meta_get_comment(infile, &value)) && value != NULL) { + gtk_entry_set_text (GTK_ENTRY(comment_entry), value); + g_free(value); + } + + // Get the length of the track. + double track_duration = mp4ff_get_track_duration(infile, mp4track); + unsigned long time_scale = mp4ff_time_scale(infile, mp4track); + unsigned long length = (track_duration / time_scale); + int min = length / 60; + int sec = length % 60; + + // Get other info about the track. + unsigned long bitrate = mp4ff_get_avg_bitrate(infile, mp4track) / 1000; + unsigned long samplerate = mp4ff_get_sample_rate(infile, mp4track); + unsigned long channels = mp4ff_get_channel_count(infile, mp4track); + unsigned long audio_type = mp4ff_get_audio_type(infile, mp4track); + fseek(mp4file, 0, SEEK_END); + int filesize = ftell(mp4file) / 1024; + + value = g_strdup_printf("Length: %d:%d\nAvg. Bitrate: %ld kbps\nSample Rate: %ld Hz\nChannels: %ld\nAudio Type: %ld\nFile Size: %d KB", min, sec, bitrate, samplerate, channels, audio_type, filesize); + gtk_label_set_text (GTK_LABEL(mp4_info_label), value); + g_free(value); + + gtk_widget_show_all(mp4_info_dialog); +} + + +void getMP4info(char* filename, FILE* mp4file) +{ + mp4ff_callback_t* mp4cb; + mp4ff_t* infile; + gint mp4track; + + mp4cb = getMP4FF_cb(mp4file); + if ((infile = mp4ff_open_read_metaonly(mp4cb)) && + ((mp4track = getAACTrack(infile)) >= 0)){ + create_mp4_info_dialog (filename, mp4file, infile, mp4track); + } + if(infile) mp4ff_close(infile); + if(mp4cb) g_free(mp4cb); +} + +/* Get the xmms titlestring for the file based on metadata. +The following code was adapted from the gtkpod project, specifically +mp4file.c (C) Jorg Schuler, but written to use the mp4ff library. The +mpg123 input plugin for xmms was used as a guide for this function. + --Jason Arroyo, 2004 */ +char *getMP4title(mp4ff_t *infile, char *filename) { + char *ret=NULL; + gchar *value, *path, *temp; + + TitleInput *input; + XMMS_NEW_TITLEINPUT(input); + + // Fill in the TitleInput with the relevant data + // from the mp4 file that can be used to display the title. + mp4ff_meta_get_title(infile, &input->track_name); + mp4ff_meta_get_artist(infile, &input->performer); + mp4ff_meta_get_album(infile, &input->album_name); + if (mp4ff_meta_get_track(infile, &value) && value != NULL) { + input->track_number = atoi(value); + g_free(value); + } + if (mp4ff_meta_get_date(infile, &value) && value != NULL) { + input->year = atoi(value); + g_free(value); + } + mp4ff_meta_get_genre(infile, &input->genre); + mp4ff_meta_get_comment(infile, &input->comment); + input->file_name = g_strdup(g_basename(filename)); + path = g_strdup(filename); + temp = strrchr(path, '.'); + if (temp != NULL) {++temp;} + input->file_ext = g_strdup_printf("%s", temp); + temp = strrchr(path, '/'); + if (temp) {*temp = '\0';} + input->file_path = g_strdup_printf("%s/", path); + + // Use the default xmms title format to format the + // title from the above info. + ret = xmms_get_titlestring(xmms_get_gentitle_format(), input); + + g_free(input->track_name); + g_free(input->performer); + g_free(input->album_name); + g_free(input->genre); + g_free(input->comment); + g_free(input->file_name); + g_free(input->file_ext); + g_free(input->file_path); + g_free(input); + g_free(path); + + return ret; +} -- cgit v1.2.3