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/QCDMp4/QCDMp4.c | 2992 +++++++++++++++++++++++++++++++++++++ 1 file changed, 2992 insertions(+) create mode 100644 faad2/src/plugins/QCDMp4/QCDMp4.c (limited to 'faad2/src/plugins/QCDMp4/QCDMp4.c') 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; +// } +//} -- cgit v1.2.3