ref: 162e0015ec094669e30cfacb7c4f8088a14141e9
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;
}