/* ** 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; // } //}