ref: 1175ddf4fa78af763389920bf2818b4e0c155ea0
dir: /plugins/QCDMp4/QCDMp4.c/
/* ** FAAD - Freeware Advanced Audio Decoder ** Copyright (C) 2002 M. Bakker ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ** ** $Id: QCDMp4.c,v 1.2 2003/05/07 18:30:49 menno Exp $ **/ //#define DEBUG_OUTPUT #include "QCDInputDLL.h" #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <commctrl.h> #include <commdlg.h> #include <shellapi.h> #include <stdlib.h> #include <math.h> #include <faad.h> #include <mp4.h> #include "resource.h" #include "utils.h" #include "config.h" #include "aacinfo.h" 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, IDC_16BITS_DITHERED }; static int res_table[] = { 16, 24, 32, 0, 16 }; typedef struct state { /* general stuff */ faacDecHandle hDecoder; int samplerate; unsigned char channels; int 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; /* MP4 stuff */ MP4FileHandle mp4file; int mp4track; MP4SampleId numSamples; MP4SampleId sampleId; /* AAC stuff */ FILE *aacfile; long filesize; long bytes_read; long bytes_into_buffer; long bytes_consumed; unsigned char *buffer; long seconds; // faadAACInfo aacInfo; } state; static state mp4state; HINSTANCE hInstance; HWND hwndPlayer, hwndConfig, hwndAbout; QCDModInitIn sQCDCallbacks, *QCDCallbacks; BOOL oldAPIs = 0; static int killPlayThread; HANDLE play_thread_handle = INVALID_HANDLE_VALUE; // the handle to the decode thread /* Function definitions */ DWORD WINAPI MP4PlayThread(void *b); // the decode thread procedure DWORD WINAPI AACPlayThread(void *b); // the decode thread procedure #ifdef DEBUG_OUTPUT void in_mp4_DebugOutput(char *message) { char s[1024]; sprintf(s, "%s: %s", mp4state.filename, message); MessageBox(NULL, s, "Debug Message", MB_OK); } #endif static void show_error(HWND hwnd, char *message, ...) { if (m_show_errors) MessageBox(hwnd, message, "Error", MB_OK); } void config_read() { char priority[10]; char resolution[10]; char show_errors[10]; char use_for_aac[10]; strcpy(show_errors, "1"); strcpy(priority, "3"); strcpy(resolution, "0"); strcpy(use_for_aac, "1"); RS(priority); RS(resolution); RS(show_errors); RS(use_for_aac); m_priority = atoi(priority); m_resolution = atoi(resolution); m_show_errors = atoi(show_errors); m_use_for_aac = atoi(use_for_aac); } void config_write() { char priority[10]; char resolution[10]; char show_errors[10]; char use_for_aac[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); WS(priority); WS(resolution); WS(show_errors); WS(use_for_aac); } //----------------------------------------------------------------------------- int WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID pRes) { if (fdwReason == DLL_PROCESS_ATTACH) hInstance = hInst; return TRUE; } //----------------------------------------------------------------------------- //old entrypoint api PLUGIN_API BOOL QInputModule(QCDModInitIn *ModInit, QCDModInfo *ModInfo) { ModInit->version = PLUGIN_API_VERSION; ModInit->toModule.ShutDown = ShutDown; ModInit->toModule.GetTrackExtents = GetTrackExtents; ModInit->toModule.GetMediaSupported = GetMediaSupported; ModInit->toModule.GetCurrentPosition= GetCurrentPosition; ModInit->toModule.Play = Play; ModInit->toModule.Pause = Pause; ModInit->toModule.Stop = Stop; ModInit->toModule.SetVolume = SetVolume; ModInit->toModule.About = About; ModInit->toModule.Configure = Configure; QCDCallbacks = ModInit; /* read config */ QCDCallbacks->Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0); config_read(); ModInfo->moduleString = "MPEG-4 General Audio Plugin v1.0"; ModInfo->moduleExtensions = m_use_for_aac ? "MP4:M4A:AAC" : "MP4:M4A"; hwndPlayer = (HWND)ModInit->Service(opGetParentWnd, 0, 0, 0); mp4state.filename[0] = 0; mp4state.seek_needed = -1; mp4state.paused = 0; play_thread_handle = INVALID_HANDLE_VALUE; oldAPIs = 1; return TRUE; } //----------------------------------------------------------------------------- PLUGIN_API QCDModInitIn* INPUTDLL_ENTRY_POINT(QCDModInitIn *ModInit, QCDModInfo *ModInfo) { sQCDCallbacks.version = PLUGIN_API_VERSION; sQCDCallbacks.toModule.Initialize = Initialize; sQCDCallbacks.toModule.ShutDown = ShutDown; sQCDCallbacks.toModule.GetTrackExtents = GetTrackExtents; sQCDCallbacks.toModule.GetMediaSupported = GetMediaSupported; sQCDCallbacks.toModule.GetCurrentPosition = GetCurrentPosition; sQCDCallbacks.toModule.Play = Play; sQCDCallbacks.toModule.Pause = Pause; sQCDCallbacks.toModule.Stop = Stop; sQCDCallbacks.toModule.SetVolume = SetVolume; sQCDCallbacks.toModule.About = About; sQCDCallbacks.toModule.Configure = Configure; QCDCallbacks = &sQCDCallbacks; return &sQCDCallbacks; } //---------------------------------------------------------------------------- int Initialize(QCDModInfo *ModInfo, int flags) { hwndPlayer = (HWND)QCDCallbacks->Service(opGetParentWnd, 0, 0, 0); mp4state.filename[0] = 0; mp4state.seek_needed = -1; mp4state.paused = 0; play_thread_handle = INVALID_HANDLE_VALUE; /* read config */ QCDCallbacks->Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0); config_read(); ModInfo->moduleString = "MPEG-4 General Audio Plugin v1.0"; ModInfo->moduleExtensions = m_use_for_aac ? "MP4:M4A:AAC" : "MP4:M4A"; // insert menu item into plugin menu // QCDCallbacks->Service(opSetPluginMenuItem, hInstance, IDD_CONFIG, (long)"Mp4 Plug-in"); return TRUE; } //---------------------------------------------------------------------------- void ShutDown(int flags) { Stop(mp4state.filename, STOPFLAG_FORCESTOP); // delete the inserted plugin menu // QCDCallbacks->Service(opSetPluginMenuItem, hInstance, 0, 0); } //----------------------------------------------------------------------------- int GetMediaSupported(const char* medianame, MediaInfo *mediaInfo) { char *ch = strrchr(medianame, '.'); if (!medianame || !*medianame) return FALSE; if(!ch) return (lstrlen(medianame) > 2); // no extension defaults to me (if not drive letter) /* Finally fixed */ if(StringComp(ch, ".mp4", 4) == 0) { mediaInfo->mediaType = DIGITAL_FILE_MEDIA; mediaInfo->op_canSeek = TRUE; mp4state.filetype = 0; return TRUE; } else if(StringComp(ch, ".aac", 4) ==0) { mediaInfo->mediaType = DIGITAL_FILE_MEDIA; mediaInfo->op_canSeek = FALSE; mp4state.filetype = 1; return TRUE; } else return FALSE; } //----------------------------------------------------------------------------- int getsonglength(char *fn) { long msDuration = 0; if(StringComp(fn + strlen(fn) - 3, "MP4", 3) == 0) { int track; MP4Duration length; MP4FileHandle file; file = MP4Read(fn, 0); if (!file) return 0; if ((track = GetAACTrack(file)) < 0) { MP4Close(file); return -1; } length = MP4GetTrackDuration(file, track); msDuration = MP4ConvertFromTrackDuration(file, track, length, MP4_MSECS_TIME_SCALE); MP4Close(file); return msDuration; } else { // faadAACInfo aacInfo; // get_AAC_format(fn, &aacInfo); // return aacInfo.length; return 0; } } int GetTrackExtents(const char* medianame, TrackExtents *ext, int flags) { ext->track = 1; ext->start = 0; if( (ext->end = getsonglength(medianame)) < 0 ) return FALSE; ext->bytesize = 0; ext->unitpersec = 1000; return TRUE; } //----------------------------------------------------------------------------- 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); return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { 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_priority = SendMessage(GetDlgItem(hwndDlg, IDC_PRIORITY), TBM_GETPOS, 0, 0); for (i = 0; i < 5; i++) { if (SendMessage(GetDlgItem(hwndDlg, res_id_table[i]), BM_GETCHECK, 0, 0)) { m_resolution = i; break; } } /* save config */ config_write(); case IDCANCEL: DestroyWindow(hwndDlg); return TRUE; } } return FALSE; } void Configure(int flags) { if(!IsWindow(hwndConfig)) hwndConfig = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_CONFIG_INPUT), hwndPlayer, config_dialog_proc); ShowWindow(hwndConfig, SW_SHOWNORMAL); } //----------------------------------------------------------------------------- // proc of "About Dialog" INT_PTR CALLBACK about_dialog_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static RECT rcLOGO, rcMail1, rcMail2/*, rcMail3*/; POINT ptMouse; static char szPluginVer[] = "QCD MP4 Input Plug-in v1.0\nCompiled on " __TIME__ ", " __DATE__; static char szFLACVer[] = "Using: FAAD2 v "FAAD2_VERSION" by"; switch (uMsg) { case WM_INITDIALOG: case WM_MOVE: GetWindowRect(GetDlgItem(hwndDlg, IDC_LOGO), &rcLOGO); GetWindowRect(GetDlgItem(hwndDlg, IDC_MAIL1), &rcMail1); GetWindowRect(GetDlgItem(hwndDlg, IDC_MAIL2), &rcMail2); // GetWindowRect(GetDlgItem(hwndDlg, IDC_MAIL2), &rcMail3); SetDlgItemText(hwndDlg, IDC_PLUGINVER, szPluginVer); SetDlgItemText(hwndDlg, IDC_FAADVER, szFLACVer); return TRUE; case WM_MOUSEMOVE: ptMouse.x = LOWORD(lParam); ptMouse.y = HIWORD(lParam); ClientToScreen(hwndDlg, &ptMouse); if( (ptMouse.x >= rcLOGO.left && ptMouse.x <= rcLOGO.right && ptMouse.y >= rcLOGO.top && ptMouse.y<= rcLOGO.bottom) || (ptMouse.x >= rcMail1.left && ptMouse.x <= rcMail1.right && ptMouse.y >= rcMail1.top && ptMouse.y<= rcMail1.bottom) || (ptMouse.x >= rcMail2.left && ptMouse.x <= rcMail2.right && ptMouse.y >= rcMail2.top && ptMouse.y<= rcMail2.bottom) /* || (ptMouse.x >= rcMail3.left && ptMouse.x <= rcMail3.right && ptMouse.y >= rcMail3.top && ptMouse.y<= rcMail3.bottom)*/ ) SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(32649))); else SetCursor(LoadCursor(NULL, IDC_ARROW)); return TRUE; case WM_LBUTTONDOWN: ptMouse.x = LOWORD(lParam); ptMouse.y = HIWORD(lParam); ClientToScreen(hwndDlg, &ptMouse); if(ptMouse.x >= rcLOGO.left && ptMouse.x <= rcLOGO.right && ptMouse.y >= rcLOGO.top && ptMouse.y<= rcLOGO.bottom) ShellExecute(0, NULL, "http://www.audiocoding.com", NULL,NULL, SW_NORMAL); else if(ptMouse.x >= rcMail1.left && ptMouse.x <= rcMail1.right && ptMouse.y >= rcMail1.top && ptMouse.y<= rcMail1.bottom) ShellExecute(0, NULL, "mailto:shaohao@elong.com", NULL,NULL, SW_NORMAL); else if(ptMouse.x >= rcMail2.left && ptMouse.x <= rcMail2.right && ptMouse.y >= rcMail2.top && ptMouse.y<= rcMail2.bottom) ShellExecute(0, NULL, "mailto:menno@audiocoding.com", NULL,NULL, SW_NORMAL); /* else if(ptMouse.x >= rcMail3.left && ptMouse.x <= rcMail3.right && ptMouse.y >= rcMail3.top && ptMouse.y<= rcMail3.bottom) ShellExecute(0, NULL, "I don't know", NULL,NULL, SW_NORMAL); */ return TRUE; case WM_COMMAND: switch(LOWORD(wParam)) { case IDOK: default: DestroyWindow(hwndDlg); return TRUE; } } return FALSE; } void About(int flags) { if(!IsWindow(hwndAbout)) hwndAbout = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_ABOUT), hwndPlayer, about_dialog_proc); ShowWindow(hwndAbout, SW_SHOWNORMAL); } //----------------------------------------------------------------------------- int Play(const char* medianame, int playfrom, int playto, int flags) { static WAVEFORMATEX wf; if(flags == PLAYFLAG_ENCODING) { if(QCDCallbacks->toPlayer.OutputOpen(medianame, &wf)) Stop(medianame, STOPFLAG_FORCESTOP); return FALSE; } if(stricmp(mp4state.filename, medianame) != 0) { sQCDCallbacks.toPlayer.OutputStop(STOPFLAG_PLAYDONE); Stop(mp4state.filename, STOPFLAG_PLAYDONE); } if(mp4state.paused) { // Update the player controls to reflect the new unpaused state sQCDCallbacks.toPlayer.OutputPause(0); Pause(medianame, PAUSE_DISABLED); if (playfrom >= 0) mp4state.seek_needed = playfrom; } else if(play_thread_handle != INVALID_HANDLE_VALUE) { mp4state.seek_needed = playfrom; return TRUE; } else { #ifdef DEBUG_OUTPUT in_mp4_DebugOutput("play"); #endif int thread_id; int avg_bitrate, br, sr; unsigned char *buffer; int buffer_size; faacDecConfigurationPtr config; mp4state.channels = 0; mp4state.samplerate = 0; mp4state.filetype = 0; strcpy(mp4state.filename, medianame); if(StringComp(medianame + strlen(medianame) - 3, "AAC", 3) == 0) mp4state.filetype = 1; mp4state.hDecoder = faacDecOpen(); if (!mp4state.hDecoder) { show_error(hwndPlayer, "Unable to open decoder library."); return -1; } config = faacDecGetCurrentConfiguration(mp4state.hDecoder); config->outputFormat = m_resolution + 1; faacDecSetConfiguration(mp4state.hDecoder, config); if (mp4state.filetype) { long pos, tmp, read, tagsize; // get_AAC_format(mp4state.filename, &mp4state.aacInfo); mp4state.aacfile = fopen(mp4state.filename, "rb"); if (!mp4state.aacfile) { show_error(hwndPlayer, "Unable to open file."); faacDecClose(mp4state.hDecoder); return -1; } pos = ftell(mp4state.aacfile); fseek(mp4state.aacfile, 0, SEEK_END); mp4state.filesize = ftell(mp4state.aacfile); fseek(mp4state.aacfile, pos, SEEK_SET); if (!(mp4state.buffer=(unsigned char*)malloc(768*48))) { show_error(hwndPlayer, "Memory allocation error."); faacDecClose(mp4state.hDecoder); fclose(mp4state.aacfile); return -1; } memset(mp4state.buffer, 0, 768*48); if (mp4state.filesize < 768*48) tmp = mp4state.filesize; else tmp = 768*48; read = fread(mp4state.buffer, 1, tmp, mp4state.aacfile); if (read == tmp) { mp4state.bytes_read = read; mp4state.bytes_into_buffer = read; } else { show_error(hwndPlayer, "Error reading from file."); faacDecClose(mp4state.hDecoder); fclose(mp4state.aacfile); return -1; } if (StringComp(mp4state.buffer, "ID3", 3) == 0) { /* high bit is not used */ tagsize = (mp4state.buffer[6] << 21) | (mp4state.buffer[7] << 14) | (mp4state.buffer[8] << 7) | (mp4state.buffer[9] << 0); tagsize += 10; } else { tagsize = 0; } if ((mp4state.bytes_consumed = faacDecInit(mp4state.hDecoder, mp4state.buffer+tagsize, mp4state.bytes_into_buffer, &mp4state.samplerate, &mp4state.channels)) < 0) { show_error(hwndPlayer, "Can't initialize library."); faacDecClose(mp4state.hDecoder); fclose(mp4state.aacfile); return -1; } mp4state.bytes_consumed += tagsize; mp4state.bytes_into_buffer -= mp4state.bytes_consumed; // avg_bitrate = mp4state.aacInfo.bitrate; avg_bitrate = 0; // module.is_seekable = 0; } else { mp4state.mp4file = MP4Read(mp4state.filename, 0); if (!mp4state.mp4file) { show_error(hwndPlayer, "Unable to open file."); faacDecClose(mp4state.hDecoder); return -1; } if ((mp4state.mp4track = GetAACTrack(mp4state.mp4file)) < 0) { show_error(hwndPlayer, "Unsupported Audio track type."); faacDecClose(mp4state.hDecoder); MP4Close(mp4state.mp4file); return -1; } buffer = NULL; buffer_size = 0; MP4GetTrackESConfiguration(mp4state.mp4file, mp4state.mp4track, &buffer, &buffer_size); if (!buffer) { faacDecClose(mp4state.hDecoder); MP4Close(mp4state.mp4file); return -1; } if(faacDecInit2(mp4state.hDecoder, buffer, buffer_size, &mp4state.samplerate, &mp4state.channels) < 0) { /* If some error initializing occured, skip the file */ faacDecClose(mp4state.hDecoder); MP4Close(mp4state.mp4file); return -1; } free(buffer); avg_bitrate = MP4GetTrackIntegerProperty(mp4state.mp4file, mp4state.mp4track, "mdia.minf.stbl.stsd.mp4a.esds.decConfigDescr.avgBitrate"); mp4state.numSamples = MP4GetTrackNumberOfSamples(mp4state.mp4file, mp4state.mp4track); mp4state.sampleId = 1; // module.is_seekable = 1; } if (mp4state.channels == 0) { show_error(hwndPlayer, "Number of channels not supported for playback."); faacDecClose(mp4state.hDecoder); if (mp4state.filetype) fclose(mp4state.aacfile); else MP4Close(mp4state.mp4file); return -1; } // open outputdevice wf.wFormatTag = WAVE_FORMAT_PCM; wf.cbSize = 0; wf.nChannels = mp4state.channels; wf.wBitsPerSample = res_table[m_resolution]; wf.nSamplesPerSec = mp4state.samplerate; wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8; wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; if (!QCDCallbacks->toPlayer.OutputOpen(mp4state.filename, &wf)) { show_error(hwndPlayer, "Error: Failed openning output plugin!"); faacDecClose(mp4state.hDecoder); if (mp4state.filetype) fclose(mp4state.aacfile); else MP4Close(mp4state.mp4file); return -1; // cannot open sound device } mp4state.paused = 0; mp4state.decode_pos_ms = 0; mp4state.seek_needed = playfrom > 0 ? playfrom : -1; br = (int)floor(((float)avg_bitrate + 500.0)/1000.0); sr = (int)floor((float)mp4state.samplerate/1000.0); // show constant bitrate at first { AudioInfo cai; cai.struct_size = sizeof(AudioInfo); cai.frequency = sr * 1000; cai.bitrate = br * 1000; cai.mode = (mp4state.channels == 2) ? 0 : 3; cai.layer = 0; cai.level = 0; QCDCallbacks->Service(opSetAudioInfo, &cai, sizeof(AudioInfo), 0); } killPlayThread = 0; if (mp4state.filetype) { if ((play_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AACPlayThread, (void *)&killPlayThread, 0, &thread_id)) == NULL) { show_error(hwndPlayer, "Cannot create playback thread"); faacDecClose(mp4state.hDecoder); fclose(mp4state.aacfile); return -1; } } else { if ((play_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MP4PlayThread, (void *)&killPlayThread, 0, &thread_id)) == NULL) { show_error(hwndPlayer, "Cannot create playback thread"); faacDecClose(mp4state.hDecoder); MP4Close(mp4state.mp4file); return -1; } } SetThreadAffinityMask(play_thread_handle, 1); if (m_priority != 3) SetThreadPriority(play_thread_handle, priority_table[m_priority]); } return TRUE; } //----------------------------------------------------------------------------- int Pause(const char* medianame, int flags) { #ifdef DEBUG_OUTPUT in_mp4_DebugOutput("pause"); #endif if(QCDCallbacks->toPlayer.OutputPause(flags)) { // send back pause/unpause notification QCDCallbacks->toPlayer.PlayPaused(medianame, flags); mp4state.paused = flags; return TRUE; } return FALSE; } //----------------------------------------------------------------------------- void SetVolume(int levelleft, int levelright, int flags) { QCDCallbacks->toPlayer.OutputSetVol(levelleft, levelright, flags); } //----------------------------------------------------------------------------- int GetCurrentPosition(const char* medianame, long *track, long *offset) { return QCDCallbacks->toPlayer.OutputGetCurrentPosition((UINT*)offset, 0); } //----------------------------------------------------------------------------- int Stop(const char* medianame, int flags) { #ifdef DEBUG_OUTPUT in_mp4_DebugOutput("stop"); #endif if(medianame && *medianame && stricmp(mp4state.filename, medianame) == 0) { sQCDCallbacks.toPlayer.OutputStop(flags); killPlayThread = 1; if(play_thread_handle != INVALID_HANDLE_VALUE) { if (WaitForSingleObject(play_thread_handle, INFINITE) == WAIT_TIMEOUT) { // MessageBox(hwndPlayer, "MP4 thread kill timeout", "debug", 0); TerminateThread(play_thread_handle,0); } CloseHandle(play_thread_handle); play_thread_handle = INVALID_HANDLE_VALUE; } if (oldAPIs) QCDCallbacks->toPlayer.PlayStopped(mp4state.filename); mp4state.filename[0] = 0; if(mp4state.hDecoder) faacDecClose(mp4state.hDecoder); if (mp4state.filetype) fclose(mp4state.aacfile); else MP4Close(mp4state.mp4file); } return TRUE; } DWORD WINAPI MP4PlayThread(void *b) { BOOL done = FALSE, updatePos = FALSE; int l; void *sample_buffer; unsigned char *buffer; int buffer_size, ms; faacDecFrameInfo frameInfo; #ifdef DEBUG_OUTPUT in_mp4_DebugOutput("MP4PlayThread"); #endif mp4state.last_frame = 0; while (!*((int *)b)) { /* seeking */ if (!done && mp4state.seek_needed != -1) { MP4Duration duration; QCDCallbacks->toPlayer.OutputFlush(mp4state.decode_pos_ms); duration = MP4ConvertToTrackDuration(mp4state.mp4file, mp4state.mp4track, mp4state.seek_needed, MP4_MSECS_TIME_SCALE); mp4state.sampleId = MP4GetSampleIdFromTime(mp4state.mp4file, mp4state.mp4track, duration, 0); mp4state.decode_pos_ms = mp4state.seek_needed; mp4state.seek_needed = -1; updatePos = TRUE; } /* quit */ if (done) { if (QCDCallbacks->toPlayer.OutputDrain(0) && !(mp4state.seek_needed >= 0)) { play_thread_handle = INVALID_HANDLE_VALUE; QCDCallbacks->toPlayer.OutputStop(STOPFLAG_PLAYDONE); QCDCallbacks->toPlayer.PlayDone(mp4state.filename); } else if (mp4state.seek_needed >= 0) { done = FALSE; continue; } break; } /* decoding */ else { if (mp4state.last_frame) { done = TRUE; } else { int rc; /* get acces unit from MP4 file */ buffer = NULL; buffer_size = 0; rc = MP4ReadSample(mp4state.mp4file, mp4state.mp4track, mp4state.sampleId++, &buffer, &buffer_size, NULL, NULL, NULL, NULL); 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(hwndPlayer, faacDecGetErrorMessage(frameInfo.error)); mp4state.last_frame = 1; } if (mp4state.sampleId >= mp4state.numSamples) mp4state.last_frame = 1; if (buffer) free(buffer); if (!killPlayThread && (frameInfo.samples > 0)) { if (res_table[m_resolution] == 24) { /* convert libfaad output (3 bytes packed in 4) */ char *temp_buffer = convert3in4to3in3(sample_buffer, frameInfo.samples); memcpy((void*)sample_buffer, (void*)temp_buffer, frameInfo.samples*3); free(temp_buffer); } ms = (int)floor(((float)frameInfo.samples*1000.0) / ((float)mp4state.samplerate*(float)frameInfo.channels)); mp4state.decode_pos_ms += ms; l = frameInfo.samples * res_table[m_resolution] / 8; if (updatePos) { QCDCallbacks->toPlayer.PositionUpdate(mp4state.decode_pos_ms); updatePos = FALSE; } { WriteDataStruct wd; wd.bytelen = l; wd.data = sample_buffer; wd.markerend = 0; wd.markerstart = mp4state.decode_pos_ms; wd.bps = res_table[m_resolution]; wd.nch = frameInfo.channels; wd.numsamples = frameInfo.samples/frameInfo.channels; wd.srate = mp4state.samplerate; if (!QCDCallbacks->toPlayer.OutputWrite(&wd)) done = TRUE; } } } } Sleep(10); } // close up play_thread_handle = INVALID_HANDLE_VALUE; return 0; } DWORD WINAPI AACPlayThread(void *b) { BOOL done = FALSE, updatePos = FALSE; int l, ms; void *sample_buffer; faacDecFrameInfo frameInfo; #ifdef DEBUG_OUTPUT in_mp4_DebugOutput("AACPlayThread"); #endif mp4state.last_frame = 0; while (!*((int *)b)) { #if 0 /* seeking */ if (!done && mp4state.seek_needed != -1) { int ms; /* Round off to a second */ ms = mp4state.seek_needed - (mp4state.seek_needed%1000); QCDCallbacks->toPlayer.OutputFlush(mp4state.decode_pos_ms); aac_seek(ms); mp4state.decode_pos_ms = ms; mp4state.seek_needed = -1; updatePos = TRUE; } #endif /* quit */ if (done) { if (QCDCallbacks->toPlayer.OutputDrain(0) && !(mp4state.seek_needed >= 0)) { QCDCallbacks->toPlayer.OutputStop(STOPFLAG_PLAYDONE); QCDCallbacks->toPlayer.PlayDone(mp4state.filename); } else if (mp4state.seek_needed >= 0) { done = FALSE; continue; } break; } /* decoding */ else { if (mp4state.last_frame) { done = TRUE; } else { long tmp, read; unsigned char *buffer = mp4state.buffer; do { if (mp4state.bytes_consumed > 0) { if (mp4state.bytes_into_buffer) { memcpy(buffer, buffer+mp4state.bytes_consumed, mp4state.bytes_into_buffer); } if (mp4state.bytes_read < mp4state.filesize) { if (mp4state.bytes_read + mp4state.bytes_consumed < mp4state.filesize) tmp = mp4state.bytes_consumed; else tmp = mp4state.filesize - mp4state.bytes_read; read = fread(buffer + mp4state.bytes_into_buffer, 1, tmp, mp4state.aacfile); if (read == tmp) { mp4state.bytes_read += read; mp4state.bytes_into_buffer += read; } } else { if (mp4state.bytes_into_buffer) { memset(buffer + mp4state.bytes_into_buffer, 0, mp4state.bytes_consumed); } } mp4state.bytes_consumed = 0; } if (mp4state.bytes_into_buffer < 1) { if (mp4state.bytes_read < mp4state.filesize) { show_error(hwndPlayer, faacDecGetErrorMessage(frameInfo.error)); mp4state.last_frame = 1; } else { mp4state.last_frame = 1; } } sample_buffer = faacDecDecode(mp4state.hDecoder, &frameInfo, buffer, mp4state.bytes_into_buffer); mp4state.bytes_consumed += frameInfo.bytesconsumed; mp4state.bytes_into_buffer -= mp4state.bytes_consumed; } while (!frameInfo.samples && !frameInfo.error); if (!killPlayThread && (frameInfo.samples > 0)) { 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); } ms = (int)floor(((float)frameInfo.samples*1000.0) / ((float)mp4state.samplerate*(float)frameInfo.channels)); mp4state.decode_pos_ms += ms; l = frameInfo.samples * res_table[m_resolution] / 8; if (updatePos) { QCDCallbacks->toPlayer.PositionUpdate(mp4state.decode_pos_ms); updatePos = FALSE; } { WriteDataStruct wd; wd.bytelen = l; wd.data = sample_buffer; wd.markerend = 0; wd.markerstart = mp4state.decode_pos_ms; wd.bps = res_table[m_resolution]; wd.nch = frameInfo.channels; wd.numsamples = frameInfo.samples/frameInfo.channels; wd.srate = mp4state.samplerate; if (!QCDCallbacks->toPlayer.OutputWrite(&wd)) done = TRUE; } } } } Sleep(10); } // close up play_thread_handle = INVALID_HANDLE_VALUE; return 0; }