Initial community commit

This commit is contained in:
Jef 2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit fc06254474
16440 changed files with 4239995 additions and 2 deletions

View file

@ -0,0 +1,232 @@
#include "mp4.h"
#include "AlbumArt.h"
#include "api__in_mp4.h"
#include "main.h"
#include "../nu/AutoWide.h"
#include "VirtualIO.h"
#include "Stopper.h"
#include <shlwapi.h>
#include <strsafe.h>
bool IsMyExtension(const wchar_t *filename)
{
const wchar_t *extension = PathFindExtension(filename);
if (extension && *extension)
{
wchar_t exts[1024] = L"";
// TODO: build a copy of this at config load time so we don't have to run this every time
GetPrivateProfileStringW(L"in_mp4", L"extensionlist", defaultExtensions, exts, 1024, m_ini);
extension++;
wchar_t *b = exts;
wchar_t *c = 0;
do
{
wchar_t d[20] = {0};
StringCchCopyW(d, 15, b);
c = wcschr(b, L';');
if (c)
{
if ((c-b)<15)
d[c - b] = 0;
}
if (!_wcsicmp(extension, d))
return true;
b = c + 1;
}
while (c);
}
return false;
}
bool MP4_AlbumArtProvider::IsMine(const wchar_t *filename)
{
return IsMyExtension(filename);
}
int MP4_AlbumArtProvider::ProviderType()
{
return ALBUMARTPROVIDER_TYPE_EMBEDDED;
}
static int MimeTypeToFlags(const wchar_t *mime_type)
{
if (!mime_type)
return 0;
if (!_wcsicmp(mime_type, L"jpeg")
|| !_wcsicmp(mime_type, L"jpg")
|| !_wcsicmp(mime_type, L"image/jpeg")
|| !_wcsicmp(mime_type, L"image/jpg"))
return 13; /* JPEG */
if (!_wcsicmp(mime_type, L"png")
|| !_wcsicmp(mime_type, L"image/png"))
return 14; /* PNG */
if (!_wcsicmp(mime_type, L"gif")
|| !_wcsicmp(mime_type, L"image/gif"))
return 12; /* GIF */
if (!_wcsicmp(mime_type, L"bmp")
|| !_wcsicmp(mime_type, L"image/bmp"))
return 27; /* BMP */
return 0; /* default to binary, I guess */
}
int MP4_AlbumArtProvider::GetAlbumArtData(const wchar_t *filename, const wchar_t *type, void **bits, size_t *len, wchar_t **mimeType)
{
void *reader = CreateUnicodeReader(filename);
if (!reader)
return ALBUMARTPROVIDER_FAILURE;
MP4FileHandle mp4 = MP4ReadEx(filename, reader, &UnicodeIO);
if (!mp4)
{
DestroyUnicodeReader(reader);
return ALBUMARTPROVIDER_FAILURE;
}
else
{
UnicodeClose(reader); // go ahead and close the file so we don't lock it
}
u_int8_t *art = 0;
u_int32_t artSize = 0;
int flags = 0;
if (MP4GetMetadataCoverArt(mp4, &art, &artSize, &flags))
{
*bits = WASABI_API_MEMMGR->sysMalloc(artSize);
memcpy(*bits, art, artSize);
*len=artSize;
/* TODO: use flags */
*mimeType = 0; // no idea what the mime type is :(
MP4Free(art);
MP4Close(mp4);
DestroyUnicodeReader(reader);
return ALBUMARTPROVIDER_SUCCESS;
}
MP4Close(mp4);
DestroyUnicodeReader(reader);
return ALBUMARTPROVIDER_FAILURE;
}
int MP4_AlbumArtProvider::SetAlbumArtData(const wchar_t *filename, const wchar_t *type, void *bits, size_t len, const wchar_t *mimeType)
{
MP4FileHandle mp4 = MP4Modify(filename, 0, 0);
if (!mp4)
{
return ALBUMARTPROVIDER_FAILURE;
}
int flags = MimeTypeToFlags(mimeType);
if (MP4SetMetadataCoverArt(mp4, (u_int8_t *)bits, len, flags))
{
MP4Close(mp4);
return ALBUMARTPROVIDER_SUCCESS;
}
MP4Close(mp4);
return ALBUMARTPROVIDER_FAILURE;
}
int MP4_AlbumArtProvider::DeleteAlbumArt(const wchar_t *filename, const wchar_t *type)
{
MP4FileHandle mp4 = MP4Modify(filename, 0, 0);
if (!mp4)
{
return ALBUMARTPROVIDER_FAILURE;
}
if (MP4DeleteMetadataCoverArt(mp4))
{
Stopper stopper;
if (!_wcsicmp(filename, lastfn))
stopper.Stop();
MP4Close(mp4);
stopper.Play();
return ALBUMARTPROVIDER_SUCCESS;
}
MP4Close(mp4);
return ALBUMARTPROVIDER_FAILURE;
}
#define CBCLASS MP4_AlbumArtProvider
START_DISPATCH;
CB(SVC_ALBUMARTPROVIDER_PROVIDERTYPE, ProviderType);
CB(SVC_ALBUMARTPROVIDER_GETALBUMARTDATA, GetAlbumArtData);
CB(SVC_ALBUMARTPROVIDER_SETALBUMARTDATA, SetAlbumArtData);
CB(SVC_ALBUMARTPROVIDER_DELETEALBUMART, DeleteAlbumArt);
CB(SVC_ALBUMARTPROVIDER_ISMINE, IsMine);
END_DISPATCH;
#undef CBCLASS
static MP4_AlbumArtProvider albumArtProvider;
// {315CA473-4A7B-43a9-BB1B-7E1C24B3BFE2}
static const GUID mp4_albumartproviderGUID =
{ 0x315ca473, 0x4a7b, 0x43a9, { 0xbb, 0x1b, 0x7e, 0x1c, 0x24, 0xb3, 0xbf, 0xe2 } };
FOURCC AlbumArtFactory::GetServiceType()
{
return svc_albumArtProvider::SERVICETYPE;
}
const char *AlbumArtFactory::GetServiceName()
{
return "MP4 Album Art Provider";
}
GUID AlbumArtFactory::GetGUID()
{
return mp4_albumartproviderGUID;
}
void *AlbumArtFactory::GetInterface(int global_lock)
{
return &albumArtProvider;
}
int AlbumArtFactory::SupportNonLockingInterface()
{
return 1;
}
int AlbumArtFactory::ReleaseInterface(void *ifc)
{
//plugin.service->service_unlock(ifc);
return 1;
}
const char *AlbumArtFactory::GetTestString()
{
return 0;
}
int AlbumArtFactory::ServiceNotify(int msg, int param1, int param2)
{
return 1;
}
#ifdef CBCLASS
#undef CBCLASS
#endif
#define CBCLASS AlbumArtFactory
START_DISPATCH;
CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType)
CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName)
CB(WASERVICEFACTORY_GETGUID, GetGUID)
CB(WASERVICEFACTORY_GETINTERFACE, GetInterface)
CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface)
CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface)
CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString)
CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify)
END_DISPATCH;

View file

@ -0,0 +1,39 @@
#ifndef NULLSOFT_IN_MP3_ALBUMART_H
#define NULLSOFT_IN_MP3_ALBUMART_H
#include "../Agave/AlbumArt/svc_albumArtProvider.h"
class MP4_AlbumArtProvider : public svc_albumArtProvider
{
public:
bool IsMine(const wchar_t *filename);
int ProviderType();
// implementation note: use WASABI_API_MEMMGR to alloc bits and mimetype, so that the recipient can free through that
int GetAlbumArtData(const wchar_t *filename, const wchar_t *type, void **bits, size_t *len, wchar_t **mimeType);
int SetAlbumArtData(const wchar_t *filename, const wchar_t *type, void *bits, size_t len, const wchar_t *mimeType);
int DeleteAlbumArt(const wchar_t *filename, const wchar_t *type);
protected:
RECVS_DISPATCH;
};
#include <api/service/waservicefactory.h>
#include <api/service/services.h>
class AlbumArtFactory : public waServiceFactory
{
public:
FOURCC GetServiceType();
const char *GetServiceName();
GUID GetGUID();
void *GetInterface(int global_lock);
int SupportNonLockingInterface();
int ReleaseInterface(void *ifc);
const char *GetTestString();
int ServiceNotify(int msg, int param1, int param2);
protected:
RECVS_DISPATCH;
};
#endif

View file

@ -0,0 +1,80 @@
#ifndef NULLSOFT_IN_MP4_AUDIOSAMPLE_H
#define NULLSOFT_IN_MP4_AUDIOSAMPLE_H
#include "main.h"
class AudioSample
{
public:
AudioSample(size_t maxInput, size_t maxOutput)
{
input = (unsigned __int8 *)calloc(maxInput, sizeof(unsigned __int8));
inputSize = maxInput;
output = (__int8 *)calloc(maxOutput, sizeof(__int8));
outputSize = maxOutput;
inputValid = outputValid = result = sampleRate = numChannels =
bitsPerSample = bitrate = sampleId = timestamp = duration = offset = 0;
outputCursor = 0;
}
~AudioSample()
{
free(output);
free(input);
}
bool OK()
{
return input && output;
}
// input
unsigned __int8 *input;
size_t inputSize, inputValid;
MP4SampleId sampleId;
// output
__int8 *output, *outputCursor;
size_t outputSize, outputValid;
MP4Duration duration, offset, timestamp;
int result;
unsigned int sampleRate, numChannels, bitsPerSample;
unsigned int bitrate;
};
class VideoSample
{
public:
VideoSample(size_t maxInput)
{
input = (unsigned __int8 *)calloc(maxInput, sizeof(unsigned __int8));
inputSize = maxInput;
timestamp = inputValid = 0;
}
~VideoSample()
{
free(input);
}
bool OK()
{
return !!input;
}
// input
unsigned __int8 *input;
size_t inputSize, inputValid;
MP4Timestamp timestamp;
};
class DecodedVideoSample
{
public:
~DecodedVideoSample()
{
decoder->FreePicture(output,decoder_data);
}
void *output;
void *decoder_data;
MP4VideoDecoder *decoder;
MP4Timestamp timestamp;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,239 @@
#include <stddef.h>
#include "main.h"
#include "mpeg4audio.h"
#include "api__in_mp4.h"
#include <api/service/waservicefactory.h>
#include <assert.h>
#include "../nu/GaplessRingBuffer.h"
#include "virtualIO.h"
struct ExtendedRead
{
ExtendedRead() : mp4(0), mp4track(0), sampleId(1),
samples(0), audio(0), audioFactory(0), frameSize(0), reader(0),
sample_rate(0), timescale(0), max_sample_size(0), sample_buffer(0), decode_buffer(0)
{}
~ExtendedRead()
{
if (mp4) MP4Close(mp4); mp4 = 0;
if (reader) DestroyUnicodeReader(reader); reader=0;
if (audio)
{
audio->Close();
audioFactory->releaseInterface(audio);
}
audioFactory = 0;
audio = 0;
free(sample_buffer);
sample_buffer=0;
free(decode_buffer);
}
bool Open(const wchar_t *fn, int *size, int *bps, int *nch, int *srate, bool useFloat);
MP4AudioDecoder *audio;
MP4FileHandle mp4;
MP4TrackId mp4track;
MP4SampleId sampleId, samples;
GaplessRingBuffer ringBuffer;
void *reader;
waServiceFactory *audioFactory;
size_t frameSize;
unsigned int sample_rate;
uint32_t timescale;
uint32_t max_sample_size;
void *sample_buffer;
uint8_t *decode_buffer;
};
bool ExtendedRead::Open(const wchar_t *fn, int *size, int *bps, int *nch, int *srate, bool useFloat)
{
unsigned __int32 pregap, postgap;
int numBits = *bps;
int numChannels = *nch;
reader = CreateUnicodeReader(fn);
if (!reader)
return false;
mp4 = MP4ReadEx(fn, reader, &UnicodeIO);
if (!mp4)
{
DestroyUnicodeReader(reader);
return false;
}
mp4track = GetAudioTrack(mp4);
if (mp4track == MP4_INVALID_TRACK_ID) return false;
if (!CreateDecoder(mp4, mp4track, audio, audioFactory))
return false;
int result;
result = audio->OpenMP4(mp4, mp4track, numBits, numChannels, useFloat);
if (result != MP4_SUCCESS)
return false;
GetGaps(mp4, pregap, postgap);
ConfigureDecoderASC(mp4, mp4track, audio);
timescale = MP4GetTrackTimeScale(mp4, mp4track);
samples = MP4GetTrackNumberOfSamples(mp4, mp4track);
MP4SampleId sample = 0;
// some codecs require a frame or two to get decoded. so we'll go until GetOutputProperties is valid
for (MP4SampleId sample = 1;sample <= samples; sample++)
{
int ret;
if (useFloat)
{
bool verifyFloat = false;
ret = audio->GetOutputPropertiesEx(&sample_rate, reinterpret_cast<unsigned int *>(nch), reinterpret_cast<unsigned int *>(bps), &verifyFloat);
if (ret == MP4_SUCCESS && !verifyFloat)
return false;
}
else
{
ret = audio->GetOutputProperties(&sample_rate, reinterpret_cast<unsigned int *>(nch), reinterpret_cast<unsigned int *>(bps));
}
if (ret == MP4_SUCCESS)
{
MP4Duration duration = MP4GetTrackDuration(mp4, mp4track);
*srate = sample_rate;
frameSize = (*nch) * (*bps / 8);
size_t outputFrameSize;
*size = duration * frameSize;
if (audio->OutputFrameSize(&outputFrameSize) == MP4_SUCCESS)
{
}
else
{
outputFrameSize = 65536; // err on the side of caution
}
decode_buffer = (uint8_t *)malloc(outputFrameSize*frameSize);
ringBuffer.Initialize(outputFrameSize, *bps, *nch, pregap, postgap);
max_sample_size = MP4GetTrackMaxSampleSize(mp4, mp4track);
sample_buffer = malloc(max_sample_size);
if (sample != 1) {
audio->Flush();
}
return true;
}
unsigned char *buffer = NULL;
unsigned __int32 buffer_size = 0;
if (MP4ReadSample(mp4, mp4track, sample, (unsigned __int8 **)&buffer, &buffer_size))
{
unsigned char tempBuf[65536];
size_t outSize = 65536;
int err = audio->DecodeSample(buffer, buffer_size, tempBuf, &outSize);
MP4Free(buffer);
if (err != MP4_SUCCESS)
continue;
}
}
return false;
}
extern "C"
{
//returns handle!=0 if successful, 0 if error
//size will return the final nb of bytes written to the output, -1 if unknown
__declspec( dllexport ) intptr_t winampGetExtendedRead_openW(const wchar_t *fn, int *size, int *bps, int *nch, int *srate)
{
ExtendedRead *ext = new ExtendedRead;
if (ext->Open(fn, size, bps, nch, srate, false))
return reinterpret_cast<intptr_t>(ext);
delete ext;
return 0;
}
//returns handle!=0 if successful, 0 if error
//size will return the final nb of bytes written to the output, -1 if unknown
__declspec( dllexport ) intptr_t winampGetExtendedRead_openW_float(const wchar_t *fn, int *size, int *bps, int *nch, int *srate)
{
ExtendedRead *ext = new ExtendedRead;
if (ext->Open(fn, size, bps, nch, srate, true))
return reinterpret_cast<intptr_t>(ext);
delete ext;
return 0;
}
//returns nb of bytes read. -1 if read error (like CD ejected). if (ret == 0), EOF is assumed
__declspec( dllexport ) intptr_t winampGetExtendedRead_getData(intptr_t handle, char *dest, int len, volatile int *killswitch)
{
ExtendedRead *ext = (ExtendedRead *)handle;
int bytesCopied = 0;
int skip = 0;
len -= (len % ext->frameSize); // round down to the nearest whole frame size
while (len && !*killswitch)
{
size_t copySize = ext->ringBuffer.Read(dest, len);
len -= copySize;
dest += copySize;
bytesCopied += copySize;
if (ext->ringBuffer.Empty())
{
size_t outSize = 0;
MP4Duration offset=0,duration=INT_MAX;
if (ext->sampleId <= ext->samples) {
unsigned char *buffer = (unsigned char *)ext->sample_buffer;
unsigned __int32 buffer_size = ext->max_sample_size;
MP4ReadSample(ext->mp4, ext->mp4track, ext->sampleId++, (unsigned __int8 **) & buffer, &buffer_size, 0, &duration, &offset);
ext->audio->DecodeSample(buffer, buffer_size, ext->decode_buffer, &outSize); // TODO error check
} else {
#if 0 // TODO Drain decode
ext->audio->DecodeSample(0, 0, decode_buffer, &outSize); // TODO Drain method?
#else
#endif
return bytesCopied;
}
// convert to the track timescale for purposes of duration/offset/gap stuff
int outSamples = MulDiv(outSize, ext->timescale, ext->sample_rate * ext->frameSize);
if (offset > 0)
outSamples -= min(outSamples, offset);
if (outSamples > duration)
outSamples = duration;
// convert back to sample rate timescale
outSize = MulDiv(ext->sample_rate * ext->frameSize, outSamples, ext->timescale);
ext->ringBuffer.Write(ext->decode_buffer+offset*ext->frameSize, outSize);
}
}
return bytesCopied;
}
// return nonzero on success, zero on failure.
__declspec( dllexport ) int winampGetExtendedRead_setTime(intptr_t handle, int millisecs)
{
ExtendedRead *ext = (ExtendedRead *)handle;
MP4Duration duration = MP4ConvertToTrackDuration(ext->mp4, ext->mp4track, millisecs, MP4_MSECS_TIME_SCALE);
if(duration == MP4_INVALID_DURATION) return 0;
MP4SampleId newSampleId = MP4GetSampleIdFromTime(ext->mp4, ext->mp4track, duration);
if(newSampleId > ext->samples) return 0;
ext->sampleId = newSampleId;
ext->audio->Flush();
// ext->bufferUsed=0;
ext->ringBuffer.Reset();
return 1;
}
__declspec( dllexport ) void winampGetExtendedRead_close(intptr_t handle)
{
ExtendedRead *ext = (ExtendedRead *)handle;
delete ext;
}
}

View file

@ -0,0 +1,723 @@
#define PLUGIN_VERSION L"2.70"
#include "Main.h"
#include <windows.h>
#include <stdio.h>
#include <locale.h>
#include "resource.h"
#include "../Winamp/in2.h"
#include "../Winamp/wa_ipc.h"
#include "../nu/AutoChar.h"
#include "api__in_mp4.h"
#include <api/service/waservicefactory.h>
#pragma warning(disable:4786)
#include "mpeg4audio.h"
#include <shlwapi.h>
#include <malloc.h>
#include "VirtualIO.h"
#include "AlbumArt.h"
#include <assert.h>
#include "../in_wmvdrm/Remaining.h"
#include "VideoThread.h"
#include "RawMediaReader.h"
#include "../nu/Singleton.h"
#include <strsafe.h>
Remaining remaining;
nu::VideoClock video_clock;
AlbumArtFactory albumArtFactory;
wchar_t m_ini[MAX_PATH] = {0};
int infoDlg(const wchar_t *fn, HWND hwnd);
#define WM_WA_IPC WM_USER
#define WM_WA_MPEG_EOF WM_USER+2
HANDLE hThread;
static DWORD WINAPI playProc(LPVOID lpParameter);
static int paused, m_kill;
HANDLE killEvent, seekEvent, pauseEvent;
static int m_opened;
static RawMediaReaderService raw_media_reader_service;
static SingletonServiceFactory<svc_raw_media_reader, RawMediaReaderService> raw_factory;
static void stop();
// wasabi based services for localisation support
api_language *WASABI_API_LNG = 0;
HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
api_config *AGAVE_API_CONFIG = 0;
api_memmgr *WASABI_API_MEMMGR = 0;
api_application *WASABI_API_APP = 0;
int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
{
MSGBOXPARAMS msgbx = {sizeof(MSGBOXPARAMS),0};
msgbx.lpszText = message;
msgbx.lpszCaption = title;
msgbx.lpszIcon = MAKEINTRESOURCE(102);
msgbx.hInstance = GetModuleHandle(0);
msgbx.dwStyle = MB_USERICON;
msgbx.hwndOwner = parent;
return MessageBoxIndirect(&msgbx);
}
void about(HWND hwndParent)
{
wchar_t message[1024] = {0}, text[1024] = {0};
WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_MPEG4_AUDIO_DECODER_OLD,text,1024);
StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
mod.description, TEXT(__DATE__));
DoAboutMessageBox(hwndParent,text,message);
}
static const wchar_t defaultExtensions_nonpro[] = {L"M4A;MP4"};
static const wchar_t defaultExtensions_pro[] = {L"M4A;MP4;M4V"};
const wchar_t *defaultExtensions = defaultExtensions_pro;
// the return pointer has been malloc'd. Use free() when you are done.
char *BuildExtensions(const char *extensions)
{
char name[64] = {0};
WASABI_API_LNGSTRING_BUF(IDS_MP4_FILE,name,64);
size_t length = strlen(extensions) + 1 + strlen(name) + 2;
char *newExt = (char *)calloc(length, sizeof(char));
char *ret = newExt; // save because we modify newExt
// copy extensions
StringCchCopyExA(newExt, length, extensions, &newExt, &length, 0);
newExt++;
length--;
// copy description
StringCchCopyExA(newExt, length, name, &newExt, &length, 0);
newExt++;
length--;
// double null terminate
assert(length == 1);
*newExt = 0;
return ret;
}
int init()
{
if (!IsWindow(mod.hMainWindow))
return IN_INIT_FAILURE;
killEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
seekEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
pauseEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
mod.service->service_register(&albumArtFactory);
raw_factory.Register(mod.service, &raw_media_reader_service);
waServiceFactory *sf = mod.service->service_getServiceByGuid(AgaveConfigGUID);
if (sf)
AGAVE_API_CONFIG = (api_config *)sf->getInterface();
sf = mod.service->service_getServiceByGuid(applicationApiServiceGuid);
if (sf)
WASABI_API_APP = (api_application *)sf->getInterface();
sf = mod.service->service_getServiceByGuid(memMgrApiServiceGuid);
if (sf)
WASABI_API_MEMMGR = (api_memmgr *)sf->getInterface();
sf = mod.service->service_getServiceByGuid(DownloadManagerGUID);
if (sf)
WAC_API_DOWNLOADMANAGER = (api_downloadManager *)sf->getInterface();
sf = mod.service->service_getServiceByGuid(ThreadPoolGUID);
if (sf)
WASABI_API_THREADPOOL = (api_threadpool *)sf->getInterface();
// loader so that we can get the localisation service api for use
sf = mod.service->service_getServiceByGuid(languageApiGUID);
if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
// need to have this initialised before we try to do anything with localisation features
WASABI_API_START_LANG(mod.hDllInstance,InMp4LangGUID);
static wchar_t szDescription[256];
StringCchPrintfW(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_MPEG4_AUDIO_DECODER),PLUGIN_VERSION);
mod.description = (char*)szDescription;
const wchar_t *inipath = (wchar_t *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW);
PathCombineW(m_ini, inipath, L"Plugins");
CreateDirectoryW(m_ini, NULL);
PathAppendW(m_ini, L"in_mp4.ini");
wchar_t exts[1024] = {0};
GetPrivateProfileStringW(L"in_mp4", L"extensionlist", defaultExtensions, exts, 1024, m_ini);
mod.FileExtensions = BuildExtensions(AutoChar(exts));
return IN_INIT_SUCCESS;
}
void quit()
{
CloseHandle(killEvent);
CloseHandle(seekEvent);
raw_factory.Deregister(mod.service);
waServiceFactory *sf = mod.service->service_getServiceByGuid(AgaveConfigGUID);
if (sf)
sf->releaseInterface(AGAVE_API_CONFIG);
sf = mod.service->service_getServiceByGuid(applicationApiServiceGuid);
if (sf)
sf->releaseInterface(WASABI_API_APP);
sf = mod.service->service_getServiceByGuid(DownloadManagerGUID);
if (sf)
sf->releaseInterface( WAC_API_DOWNLOADMANAGER );
mod.service->service_deregister(&albumArtFactory);
free(mod.FileExtensions);
}
int isourfile(const wchar_t *fn)
{
return 0;
}
void config(HWND hwndParent);
void setoutputtime(int time_in_ms)
{
m_needseek = time_in_ms;
SetEvent(seekEvent);
}
MP4TrackId GetVideoTrack(MP4FileHandle infile)
{
int numTracks = MP4GetNumberOfTracks(infile, NULL, /* subType */ 0);
for (int i = 0; i < numTracks; i++)
{
MP4TrackId trackId = MP4FindTrackId(infile, i, NULL, /* subType */ 0);
const char* trackType = MP4GetTrackType(infile, trackId);
if (!lstrcmpA(trackType, MP4_VIDEO_TRACK_TYPE))
return trackId;
}
/* can't decode this */
return MP4_INVALID_TRACK_ID;
}
MP4TrackId GetAudioTrack(MP4FileHandle infile)
{
int ret = MP4_INVALID_TRACK_ID;
__try
{
/* find AAC track */
int numTracks = MP4GetNumberOfTracks(infile, NULL, /* subType */ 0);
for (int i = 0; i < numTracks; i++)
{
MP4TrackId trackId = MP4FindTrackId(infile, i, NULL, /* subType */ 0);
if (trackId != MP4_INVALID_TRACK_ID)
{
const char* trackType = MP4GetTrackType(infile, trackId);
if (trackType && !lstrcmpA(trackType, MP4_AUDIO_TRACK_TYPE))
return trackId;
}
}
/* can't decode this */
return MP4_INVALID_TRACK_ID;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
return MP4_INVALID_TRACK_ID;
}
return ret;
}
MP4SampleId numSamples, numVideoSamples;
MP4FileHandle MP4hFile;
MP4TrackId audio_track, video_track;
double m_length;
volatile int m_needseek = -1;
unsigned int audio_srate, audio_nch, audio_bps, audio_bitrate=0;
unsigned int video_bitrate=0;
wchar_t lastfn[MAX_PATH*4] = L"";
MP4AudioDecoder *audio = 0;
waServiceFactory *audioFactory = 0, *videoFactory = 0;
MP4VideoDecoder *video = 0;
uint32_t m_timescale = 0, m_video_timescale = 0;
static void *reader = 0;
bool audio_chunk = false;
enum
{
READER_UNICODE=0,
READER_HTTP=1,
};
int reader_type=READER_UNICODE;
bool open_mp4(const wchar_t *fn)
{
audio = 0;
video = 0;
if (!_wcsnicmp(fn, L"http://", 7) || !_wcsnicmp(fn, L"https://", 8))
{
reader = CreateReader(fn, killEvent);
reader_type=READER_HTTP;
MP4hFile = MP4ReadEx(fn, reader, &HTTPIO);
}
else
{
reader = CreateUnicodeReader(fn);
if (!reader)
return false;
reader_type=READER_UNICODE;
MP4hFile = MP4ReadEx(fn, reader, &UnicodeIO);
}
if (!MP4hFile)
{
return false;
}
m_opened = 1;
unsigned int output_bits = AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"bits", 16);
if (output_bits >= 24)
output_bits = 24;
else
output_bits = 16;
unsigned int max_channels;
// get max channels
if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"surround", true))
max_channels = 6;
else if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"mono", false))
max_channels = 1;
else
max_channels = 2;
audio_track = GetAudioTrack(MP4hFile);
if (audio_track == MP4_INVALID_TRACK_ID || !CreateDecoder(MP4hFile, audio_track, audio, audioFactory) || audio->OpenMP4(MP4hFile, audio_track, output_bits, max_channels, false) != MP4_SUCCESS)
{
audio = 0;
video_clock.Start();
}
video_track = GetVideoTrack(MP4hFile);
if (video_track != MP4_INVALID_TRACK_ID)
{
CreateVideoDecoder(MP4hFile, video_track, video, videoFactory);
if (video)
video->Open(MP4hFile, video_track);
}
else
video=0;
if (!audio && !video)
{
return false;
}
numVideoSamples = MP4GetTrackNumberOfSamples(MP4hFile, video_track);
m_video_timescale = MP4GetTrackTimeScale(MP4hFile, video_track);
unsigned __int64 trackDuration;
double lengthAudio = 0;
double lengthVideo = 0;
if (audio_track != MP4_INVALID_TRACK_ID)
{
if (audio)
{
ConfigureDecoderASC(MP4hFile, audio_track, audio);
audio_chunk = !!audio->RequireChunks();
}
else
audio_chunk = false;
numSamples = audio_chunk?MP4GetTrackNumberOfChunks(MP4hFile, audio_track):MP4GetTrackNumberOfSamples(MP4hFile, audio_track);
m_timescale = MP4GetTrackTimeScale(MP4hFile, audio_track);
trackDuration = MP4GetTrackDuration(MP4hFile, audio_track);
lengthAudio = (double)(__int64)trackDuration / (double)m_timescale;
}
else
{
numSamples = numVideoSamples;
trackDuration = MP4GetTrackDuration(MP4hFile, video_track);
lengthVideo = (double)(__int64)trackDuration / (double)m_video_timescale;
}
/* length in Sec. */
m_length = max(lengthAudio, lengthVideo); //(double)(__int64)trackDuration / (double)m_timescale;
audio_bitrate = MP4GetTrackBitRate(MP4hFile, audio_track) / 1000;
if (video)
video_bitrate = MP4GetTrackBitRate(MP4hFile, video_track) / 1000;
else
video_bitrate = 0;
if (audio && audio->SetGain(GetGain(MP4hFile)) == MP4_SUCCESS)
mod.UsesOutputPlug |= 8;
else
mod.UsesOutputPlug &= ~8;
return true;
}
int play(const wchar_t *fn)
{
video_clock.Reset();
if (!videoOutput) // grab this now while we're on the main thread
videoOutput = (IVideoOutput *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_IVIDEOOUTPUT);
audio = 0;
video = 0;
paused = 0;
m_kill = 0;
ResetEvent(killEvent);
m_length = 0;
ResetEvent(seekEvent);
m_needseek = -1;
SetEvent(pauseEvent);
if (m_force_seek != -1)
{
setoutputtime(m_force_seek);
}
m_opened = 0;
lstrcpynW(lastfn, fn, MAX_PATH*4);
DWORD thread_id;
HANDLE threadCreatedEvent = CreateEvent(0, FALSE, FALSE, 0);
hThread = CreateThread(NULL, NULL, PlayProc, (LPVOID)threadCreatedEvent, NULL, &thread_id);
SetThreadPriority(hThread, AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
WaitForSingleObject(threadCreatedEvent, INFINITE);
CloseHandle(threadCreatedEvent);
return 0;
}
static inline wchar_t *IncSafe(wchar_t *val, int x)
{
while (x--)
{
if (*val)
val++;
}
return val;
}
void GetGaps(MP4FileHandle mp4, unsigned __int32 &pre, unsigned __int32 &post)
{
wchar_t gap_data[128] = {0};
if (GetCustomMetadata(mp4, "iTunSMPB", gap_data, 128) && gap_data[0])
{
wchar_t *itr = IncSafe(gap_data, 9);
pre = wcstoul(itr, 0, 16);
itr = IncSafe(itr, 9);
post = wcstoul(itr, 0, 16);
// don't care about total number of samples, really
/*
itr+=9;
unsigned int numSamples = wcstoul(itr, 0, 16);*/
}
else
{
pre = 0;
post = 0;
}
}
float GetGain(MP4FileHandle mp4, bool allowDefault)
{
if (AGAVE_API_CONFIG && AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
{
float dB = 0, peak = 1.0f;
wchar_t gain[128] = L"", peakVal[128] = L"";
_locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0))
{
case 0: // track
if ((!GetCustomMetadata(mp4, "replaygain_track_gain", gain, 128) || !gain[0])
&& !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
GetCustomMetadata(mp4, "replaygain_album_gain", gain, 128);
if ((!GetCustomMetadata(mp4, "replaygain_track_peak", peakVal, 128) || !peakVal[0])
&& !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
GetCustomMetadata(mp4, "replaygain_album_peak", peakVal, 128);
break;
case 1:
if ((!GetCustomMetadata(mp4, "replaygain_album_gain", gain, 128) || !gain[0])
&& !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
GetCustomMetadata(mp4, "replaygain_track_gain", gain, 128);
if ((!GetCustomMetadata(mp4, "replaygain_album_peak", peakVal, 128) || !peakVal[0])
&& !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
GetCustomMetadata(mp4, "replaygain_track_peak", peakVal, 128);
break;
}
if (gain[0])
{
if (gain[0] == L'+')
dB = (float)_wtof_l(&gain[1],C_locale);
else
dB = (float)_wtof_l(gain,C_locale);
}
else if (allowDefault)
{
dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);
return powf(10.0f, dB / 20.0f);
}
if (peakVal[0])
{
peak = (float)_wtof_l(peakVal,C_locale);
}
switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1))
{
case 0: // apply gain
return powf(10.0f, dB / 20.0f);
case 1: // apply gain, but don't clip
return min(powf(10.0f, dB / 20.0f), 1.0f / peak);
case 2: // normalize
return 1.0f / peak;
case 3: // prevent clipping
if (peak > 1.0f)
return 1.0f / peak;
else
return 1.0f;
}
}
return 1.0f; // no gain
}
bool first;
void pause()
{
paused = 1;
if (audio)
{
mod.outMod->Pause(1);
}
else
{
video_clock.Pause();
}
ResetEvent(pauseEvent); // pauseEvent signal state is opposite of pause state
}
void unpause()
{
paused = 0;
if (audio)
{
mod.outMod->Pause(0);
}
else
{
video_clock.Unpause();
}
SetEvent(pauseEvent); // pauseEvent signal state is opposite of pause state
}
int ispaused()
{
return paused;
}
void stop()
{
if (reader && reader_type==READER_HTTP) StopReader(reader);
lastfn[0] = 0;
SetEvent(killEvent);
m_kill = 1;
WaitForSingleObject(hThread, INFINITE);
mod.outMod->Close();
mod.SAVSADeInit();
if (m_opened) MP4Close(MP4hFile);
MP4hFile = 0;
m_opened = 0;
if (audio)
{
audio->Close();
audioFactory->releaseInterface(audio);
}
audioFactory = 0;
audio = 0;
if (video)
{
video->Close();
videoFactory->releaseInterface(video);
}
videoFactory=0;
video = 0;
if (reader)
{
if (reader_type == READER_HTTP)
DestroyReader(reader);
else
DestroyUnicodeReader(reader);
}
reader = 0;
}
int getlength()
{
return (int)(m_length*1000);
}
int getoutputtime()
{
if (m_needseek == -1)
return (int)GetClock();
else
return m_needseek; // this prevents the seekbar from jumping around while the playthread is seeking
}
void setvolume(int volume)
{
mod.outMod->SetVolume(volume);
}
void setpan(int pan)
{
mod.outMod->SetPan(pan);
}
/*
void FillInfo(HWND hwndDlg, MP4FileHandle hMp4);
void CALLBACK CurrentlyPlayingInfoBox(ULONG_PTR param)
{
ThreadInfoBox *threadInfo = (ThreadInfoBox *)param;
FillInfo(threadInfo->hwndDlg, MP4hFile);
SetEvent(threadInfo->completionEvent);
}
*/
void getfileinfo(const wchar_t *filename, wchar_t *title, int *length_in_ms)
{
if (!filename || !*filename) // currently playing file
{
if (length_in_ms) *length_in_ms = getlength();
if (title) // get non-path portion.of filename
{
lstrcpynW(title, lastfn, GETFILEINFO_TITLE_LENGTH);
PathStripPathW(title);
PathRemoveExtensionW(title);
}
}
else // some other file
{
if (length_in_ms) // calculate length
{
*length_in_ms = -1000; // the default is unknown file length (-1000).
MP4FileHandle hMp4 = MP4Read(filename);
if (hMp4)
{
double lengthAudio = 0;
double lengthVideo = 0;
MP4TrackId audio_track = GetAudioTrack(hMp4);
if (audio_track != -1)
{
int timescale = MP4GetTrackTimeScale(hMp4, audio_track);
unsigned __int64 trackDuration = MP4GetTrackDuration(hMp4, audio_track);
lengthAudio = (double)(__int64)trackDuration / (double)timescale;
}
MP4TrackId video_track = GetVideoTrack(hMp4);
if (video_track != -1)
{
int timescale = MP4GetTrackTimeScale(hMp4, video_track);
unsigned __int64 trackDuration = MP4GetTrackDuration(hMp4, video_track);
lengthVideo = (double)(__int64)trackDuration / (double)timescale;
}
*length_in_ms = (int)(max(lengthAudio, lengthVideo) * 1000);
MP4Close(hMp4);
}
}
if (title) // get non path portion of filename
{
lstrcpynW(title, filename, GETFILEINFO_TITLE_LENGTH);
PathStripPathW(title);
PathRemoveExtensionW(title);
}
}
}
void eq_set(int on, char data[10], int preamp)
{}
// module definition.
In_Module mod =
{
IN_VER_RET, // defined in IN2.H
"nullsoft(in_mp4.dll)", //"Nullsoft MPEG-4 Audio Decoder v1.22"
0, // hMainWindow (filled in by winamp)
0, // hDllInstance (filled in by winamp)
0, // this is a double-null limited list. "EXT\0Description\0EXT\0Description\0" etc.
1, // is_seekable
1, // uses output plug-in system
config,
about,
init,
quit,
getfileinfo,
infoDlg,
isourfile,
play,
pause,
unpause,
ispaused,
stop,
getlength,
getoutputtime,
setoutputtime,
setvolume,
setpan,
0, 0, 0, 0, 0, 0, 0, 0, 0, // visualization calls filled in by winamp
0, 0, // dsp calls filled in by winamp
eq_set,
NULL, // setinfo call filled in by winamp
0 // out_mod filled in by winamp
};
extern "C"
{
__declspec(dllexport) In_Module * winampGetInModule2()
{
return &mod;
}
}

View file

@ -0,0 +1,76 @@
#ifndef NULLSOFT_IN_MP4_MAINH
#define NULLSOFT_IN_MP4_MAINH
#include "mp4.h"
#include "../Winamp/in2.h"
#include "mpeg4audio.h"
#include "mpeg4video.h"
#include "AudioSample.h"
#include "../nu/AutoLock.h"
#include "../nu/VideoClock.h"
extern nu::VideoClock video_clock;
MP4TrackId GetAudioTrack(MP4FileHandle infile);
MP4TrackId GetVideoTrack(MP4FileHandle infile);
int GetAACTrack(MP4FileHandle infile);
class waServiceFactory;
bool CreateVideoDecoder(MP4FileHandle file, MP4TrackId track, MP4VideoDecoder *&decoder, waServiceFactory *&serviceFactory);
class MP4AudioDecoder;
bool CreateDecoder(MP4FileHandle file, MP4TrackId track, MP4AudioDecoder *&decoder, waServiceFactory *&serviceFactory);
void ConfigureDecoderASC(MP4FileHandle file, MP4TrackId track, MP4AudioDecoder *decoder);
bool GetCustomMetadata(MP4FileHandle mp4, char *metadata, wchar_t *dest, int destlen, const char *owner=0);
float GetGain(MP4FileHandle mp4, bool allowDefault=true);
void GetGaps(MP4FileHandle mp4, unsigned __int32 &pre, unsigned __int32 &post);
struct ThreadInfoBox
{
HWND hwndDlg;
HANDLE completionEvent;
};
VOID CALLBACK CurrentlyPlayingInfoBox(ULONG_PTR param);
extern wchar_t lastfn[MAX_PATH*4];
extern HANDLE killEvent, seekEvent, pauseEvent;
extern In_Module mod; // the output module
extern MP4FileHandle MP4hFile;
extern MP4TrackId audio_track, video_track;
class AudioSample;
int TryWriteAudio(AudioSample *sample);
extern MP4AudioDecoder *audio;
extern MP4VideoDecoder *video;
extern unsigned int audio_bitrate, video_bitrate;
extern MP4SampleId numSamples, numVideoSamples;
extern DWORD WINAPI PlayProc(LPVOID lpParameter);
extern bool first;
extern HANDLE hThread;
extern Nullsoft::Utility::LockGuard play_mp4_guard;
extern volatile int m_needseek;
void CALLBACK Seek(ULONG_PTR data);
void CALLBACK Pause(ULONG_PTR data);
extern uint32_t m_video_timescale;
extern uint32_t m_timescale;
extern const wchar_t *defaultExtensions;
extern wchar_t m_ini[MAX_PATH];
char *BuildExtensions(const char *extensions);
extern bool config_show_average_bitrate;
void FlushOutput();
extern int m_force_seek;
MP4Duration GetClock();
MP4Duration GetDecodeClock();
extern bool audio_chunk;
// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
static const GUID playbackConfigGroupGUID =
{
0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf }
};
#endif

View file

@ -0,0 +1,417 @@
#include "main.h"
#include "../winamp/wa_ipc.h"
#include "VideoThread.h"
#include "AudioSample.h"
#include "api__in_mp4.h"
#include <assert.h>
#include <api/service/waservicefactory.h>
#include "../nu/AudioOutput.h"
#include <strsafe.h>
const DWORD PAUSE_TIMEOUT = 100; // number of milliseconds to sleep for when paused
static bool audio_opened;
static HANDLE events[3];
static bool done;
bool open_mp4(const wchar_t *fn);
static AudioSample *sample = 0;
static DWORD waitTime;
static MP4SampleId nextSampleId;
Nullsoft::Utility::LockGuard play_mp4_guard;
static MP4Duration first_timestamp=0;
class MP4Wait
{
public:
int WaitOrAbort(int time_in_ms)
{
WaitForMultipleObjects(3, events, FALSE, INFINITE); // pauseEvent signal state is opposite of pause state
int ret = WaitForMultipleObjects(2, events, FALSE, time_in_ms);
if (ret == WAIT_TIMEOUT)
return 0;
if (ret == WAIT_OBJECT_0+1)
return 2;
return 1;
}
};
nu::AudioOutput<MP4Wait> audio_output(&mod);
MP4Duration GetClock()
{
if (audio)
{
return audio_output.GetFirstTimestamp() + mod.outMod->GetOutputTime();
}
else if (video)
{
return video_clock.GetOutputTime();
}
else
{
return 0;
}
}
static void OutputSample(AudioSample *sample)
{
if (first)
{
first_timestamp = MP4ConvertFromTrackTimestamp(MP4hFile, audio_track, sample->timestamp, MP4_MSECS_TIME_SCALE);
first = false;
}
if (sample->result == MP4_SUCCESS)
{
if (!audio_opened)
{
audio_opened=true;
if (audio_output.Open(first_timestamp, sample->numChannels, sample->sampleRate, sample->bitsPerSample) == false)
{
PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
return ;
}
unsigned __int32 pregap = 0;
unsigned __int32 postgap = 0;
GetGaps(MP4hFile, pregap, postgap);
audio_output.SetDelays(0, pregap, postgap);
mod.SetInfo(audio_bitrate + video_bitrate, sample->sampleRate / 1000, sample->numChannels, 1);
}
int skip = 0;
int sample_size = (sample->bitsPerSample / 8) * sample->numChannels;
int outSamples = MulDiv(sample->outputValid, m_timescale, sample->sampleRate * sample_size);
/* if (!audio_chunk && outSamples > sample->duration)
outSamples = (int)sample->duration; */
if (sample->offset > 0)
{
int cut = (int)min(outSamples, sample->offset);
outSamples -= cut;
skip = cut;
}
size_t outSize = MulDiv(sample_size * sample->sampleRate, outSamples, m_timescale);
if (audio_bitrate != sample->bitrate)
{
audio_bitrate = sample->bitrate;
mod.SetInfo(audio_bitrate + video_bitrate, -1, -1, 1);
}
if (audio_output.Write(sample->output + MulDiv(sample_size * sample->sampleRate, skip, m_timescale), outSize) == 1)
{
return ;
}
if (sample->sampleId == numSamples) // done!
done = true; // TODO: probably don't want to bail out yet if video is playing
}
}
static bool DecodeAudioSample(AudioSample *sample)
{
if (m_needseek != -1)
{
sample->outputValid = 0;
sample->outputCursor = sample->output;
sample->result = MP4_SUCCESS;
sample->sampleRate = m_timescale;
audio->GetOutputProperties(&sample->sampleRate, &sample->numChannels, &sample->bitsPerSample);
if (audio->GetCurrentBitrate(&sample->bitrate) != MP4_SUCCESS || !sample->bitrate)
sample->bitrate = audio_bitrate;
}
else
{
sample->outputValid = sample->outputSize;
sample->outputCursor = 0;
sample->result = audio->DecodeSample(sample->input, sample->inputValid, sample->output, &sample->outputValid);
if (sample->inputValid == 0 && sample->outputValid == 0) {
return false;
}
sample->sampleRate = m_timescale;
audio->GetOutputProperties(&sample->sampleRate, &sample->numChannels, &sample->bitsPerSample);
if (audio->GetCurrentBitrate(&sample->bitrate) != MP4_SUCCESS || !sample->bitrate)
sample->bitrate = audio_bitrate;
OutputSample(sample);
}
return true;
}
static void ReadNextAudioSample()
{
if (nextSampleId > numSamples)
{
return;
}
unsigned __int32 buffer_size = sample->inputSize;
bool sample_read = false;
play_mp4_guard.Lock();
if (audio_chunk)
sample_read = MP4ReadChunk(MP4hFile, audio_track, nextSampleId++, (unsigned __int8 **)&sample->input, &buffer_size, &sample->timestamp, &sample->duration);
else
sample_read = MP4ReadSample(MP4hFile, audio_track, nextSampleId++, (unsigned __int8 **)&sample->input, &buffer_size, &sample->timestamp, &sample->duration, &sample->offset);
play_mp4_guard.Unlock();
if (sample_read)
{
sample->inputValid = buffer_size;
if (audio_chunk)
{
sample->duration = 0;
sample->offset = 0;
}
sample->sampleId = nextSampleId-1;
DecodeAudioSample(sample);
}
}
static bool BuildAudioBuffers()
{
size_t outputFrameSize;
//if (audio->OutputFrameSize(&outputFrameSize) != MP4_SUCCESS || !outputFrameSize)
//{
outputFrameSize = 8192 * 6; // fallback size
//}
u_int32_t maxSize = 0;
if (audio)
{
if (audio_chunk)
maxSize = 65536; // TODO!!!!
else
maxSize = MP4GetTrackMaxSampleSize(MP4hFile, audio_track);
if (!maxSize)
return 0;
sample = new AudioSample(maxSize, outputFrameSize);
if (!sample->OK())
{
delete sample;
return false;
}
}
if (video)
{
maxSize = MP4GetTrackMaxSampleSize(MP4hFile, video_track);
video_sample = new VideoSample(maxSize);
if (!video_sample->OK())
{
delete video_sample;
return false;
}
}
return true;
}
DWORD WINAPI PlayProc(LPVOID lpParameter)
{
// set an event when we start. this keeps Windows from queueing an APC before the thread proc even starts (evil, evil windows)
HANDLE threadCreatedEvent = (HANDLE)lpParameter;
SetEvent(threadCreatedEvent);
video=0;
if (!open_mp4(lastfn))
{
if (WaitForSingleObject(killEvent, 200) != WAIT_OBJECT_0)
PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
return 0;
}
audio_output.Init(mod.outMod);
if (videoOutput && video)
{
// TODO: this is really just a placeholder, we should do smarter stuff
// like query the decoder object for a name rather than guess
char set_info[256] = {0};
char *audio_info = MP4PrintAudioInfo(MP4hFile, audio_track);
char *video_info = 0;
if (video_track != MP4_INVALID_TRACK_ID)
video_info = MP4PrintVideoInfo(MP4hFile, video_track);
if (video_info)
{
StringCchPrintfA(set_info, 256, "%s, %s %ux%u", audio_info, video_info, MP4GetTrackVideoWidth(MP4hFile, video_track), MP4GetTrackVideoHeight(MP4hFile, video_track));
videoOutput->extended(VIDUSER_SET_INFOSTRING,(INT_PTR)set_info,0);
MP4Free(video_info);
}
MP4Free(audio_info);
}
if (!BuildAudioBuffers())
{
// TODO: benski> more cleanup work has to be done here!
if (WaitForSingleObject(killEvent, 200) != WAIT_OBJECT_0)
PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
return 0;
}
nextSampleId = 1;
nextVideoSampleId = 1;
if (video)
Video_Init();
first = true;
audio_opened = false;
first_timestamp= 0;
events[0]=killEvent;
events[1]=seekEvent;
events[2]=pauseEvent;
waitTime = audio?0:INFINITE;
done = false;
while (!done)
{
int ret = WaitForMultipleObjects(2, events, FALSE, waitTime);
switch (ret)
{
case WAIT_OBJECT_0: // kill event
done = true;
break;
case WAIT_OBJECT_0 + 1: // seek event
{
bool rewind = m_needseek < GetClock();
// TODO: reset pregap?
MP4SampleId new_video_sample = MP4_INVALID_SAMPLE_ID;
if (video)
{
SetEvent(video_start_flushing);
WaitForSingleObject(video_flush_done, INFINITE);
MP4Duration duration = MP4ConvertToTrackDuration(MP4hFile, video_track, m_needseek, MP4_MSECS_TIME_SCALE);
if (duration != MP4_INVALID_DURATION)
{
new_video_sample = MP4GetSampleIdFromTime(MP4hFile, video_track, duration, true, rewind);
if (new_video_sample == MP4_INVALID_SAMPLE_ID)
new_video_sample = MP4GetSampleIdFromTime(MP4hFile, video_track, duration, false); // try again without keyframe seeking
/* TODO: make sure the new seek direction is in the same as the request seek direction.
e.g. make sure a seek FORWARD doesn't go BACKWARD
MP4Timestamp video_timestamp = MP4GetSampleTime(MP4hFile, video_track, seek_video_sample);
int new_time = MP4ConvertFromTrackTimestamp(MP4hFile, video_track, video_timestamp, MP4_MSECS_TIME_SCALE);
if (m_needseek < GetClock())
video_timestamp = MP4GetSampleIdFromTime(MP4hFile, video_track, duration, true); // first closest keyframe prior
*/
if (new_video_sample != MP4_INVALID_SAMPLE_ID)
{
int m_old_needseek = m_needseek;
MP4Timestamp video_timestamp = MP4GetSampleTime(MP4hFile, video_track, new_video_sample);
m_needseek = MP4ConvertFromTrackTimestamp(MP4hFile, video_track, video_timestamp, MP4_MSECS_TIME_SCALE);
if (!audio)
{
MP4Timestamp video_timestamp = MP4GetSampleTime(MP4hFile, video_track, new_video_sample);
m_needseek = MP4ConvertFromTrackTimestamp(MP4hFile, video_track, video_timestamp, MP4_MSECS_TIME_SCALE);
video_clock.Seek(m_needseek);
m_needseek = -1;
}
else
{
// TODO check this will just do what is needed
// aim of this is when there is 1 artwork
// frame then we don't lock audio<->video
// as it otherwise prevents audio seeking
if (!m_needseek && m_old_needseek != m_needseek && new_video_sample == 1)
{
m_needseek = m_old_needseek;
}
}
}
}
}
if (audio)
{
MP4Duration duration = MP4ConvertToTrackDuration(MP4hFile, audio_track, m_needseek, MP4_MSECS_TIME_SCALE);
if (duration != MP4_INVALID_DURATION)
{
MP4SampleId newSampleId = audio_chunk?MP4GetChunkIdFromTime(MP4hFile, audio_track, duration):MP4GetSampleIdFromTime(MP4hFile, audio_track, duration);
if (newSampleId != MP4_INVALID_SAMPLE_ID)
{
audio->Flush();
if (video)
{
if (new_video_sample == MP4_INVALID_SAMPLE_ID)
{
SetEvent(video_resume);
}
else
{
nextVideoSampleId = new_video_sample;
SetEvent(video_flush);
}
WaitForSingleObject(video_flush_done, INFINITE);
}
m_needseek = MP4ConvertFromTrackTimestamp(MP4hFile, audio_track, duration, MP4_MILLISECONDS_TIME_SCALE);
ResetEvent(seekEvent);
audio_output.Flush(m_needseek);
m_needseek = -1;
nextSampleId = newSampleId;
continue;
}
}
}
else
{
if (new_video_sample == MP4_INVALID_SAMPLE_ID)
{
SetEvent(video_resume);
}
else
{
nextVideoSampleId = new_video_sample;
SetEvent(video_flush);
}
WaitForSingleObject(video_flush_done, INFINITE);
ResetEvent(seekEvent);
continue;
}
}
break;
case WAIT_TIMEOUT:
ReadNextAudioSample();
break;
}
}
if (WaitForSingleObject(killEvent, 0) == WAIT_TIMEOUT) // if (!killed)
{
// tell audio decoder about end-of-stream and get remaining audio
/* if (audio) {
audio->EndOfStream();
sample->inputValid = 0;
while (DecodeAudioSample(sample)) {
}
}
*/
audio_output.Write(0,0);
audio_output.WaitWhilePlaying();
if (WaitForSingleObject(killEvent, 0) == WAIT_TIMEOUT)
PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
}
SetEvent(killEvent);
// eat the rest of the APC messages
while (SleepEx(0, TRUE) == WAIT_IO_COMPLETION) {}
if (video)
Video_Close();
return 0;
}

View file

@ -0,0 +1,145 @@
#include "RawMediaReader.h"
#include "virtualIO.h"
#include <limits.h>
bool IsMyExtension(const wchar_t *filename);
int RawMediaReaderService::CreateRawMediaReader(const wchar_t *filename, ifc_raw_media_reader **out_reader)
{
if (IsMyExtension(filename))
{
void *unicode_reader = CreateUnicodeReader(filename);
if (!unicode_reader )
return NErr_FileNotFound;
MP4FileHandle mp4 = MP4ReadEx(filename, unicode_reader, &UnicodeIO);
if (!mp4)
{
DestroyUnicodeReader(unicode_reader);
return NErr_Malformed;
}
RawMediaReader *reader = new RawMediaReader(mp4, unicode_reader);
if (!reader)
{
MP4Close(mp4);
DestroyUnicodeReader(unicode_reader);
return NErr_OutOfMemory;
}
*out_reader = reader;
return NErr_Success;
}
else
{
return NErr_False;
}
}
#define CBCLASS RawMediaReaderService
START_DISPATCH;
CB(CREATERAWMEDIAREADER, CreateRawMediaReader);
END_DISPATCH;
#undef CBCLASS
RawMediaReader::RawMediaReader(MP4FileHandle file, void *reader) : file(file), reader(reader)
{
track_num=0;
number_of_tracks=MP4GetNumberOfTracks(file);
current_track = MP4_INVALID_TRACK_ID;
chunk_position=0;
chunk_size=0;
chunk_buffer=0;
}
RawMediaReader::~RawMediaReader()
{
if (chunk_buffer)
MP4Free(chunk_buffer);
MP4Close(file);
DestroyUnicodeReader(reader);
}
int RawMediaReader::ReadNextChunk()
{
again:
/* see if it's time to cycle to the next track */
if (current_track == MP4_INVALID_TRACK_ID)
{
if (track_num == number_of_tracks)
return NErr_EndOfFile;
current_track = MP4FindTrackId(file, track_num);
if (current_track == MP4_INVALID_TRACK_ID)
return NErr_EndOfFile;
track_num++;
const char* trackType = MP4GetTrackType(file, current_track);
if (!MP4_IS_AUDIO_TRACK_TYPE(trackType) && !MP4_IS_VIDEO_TRACK_TYPE(trackType))
{
current_track = MP4_INVALID_TRACK_ID;
goto again;
}
chunk_id = 1;
number_of_chunks= MP4GetTrackNumberOfChunks(file, current_track);
}
/* see if we've read all of our samples */
if (chunk_id > number_of_chunks)
{
current_track = MP4_INVALID_TRACK_ID;
goto again;
}
bool readSuccess = MP4ReadChunk(file, current_track, chunk_id, &chunk_buffer, &chunk_size);
if (!readSuccess)
return NErr_Error;
chunk_position=0;
chunk_id++;
return NErr_Success;
}
int RawMediaReader::Read(void *buffer, size_t buffer_size, size_t *bytes_read)
{
if (buffer_size > INT_MAX)
return NErr_BadParameter;
if (chunk_position==chunk_size)
{
MP4Free(chunk_buffer);
chunk_buffer=0;
}
if (!chunk_buffer)
{
int ret = ReadNextChunk();
if (ret != NErr_Success)
return ret;
}
size_t to_read = chunk_size-chunk_position;
if (to_read > buffer_size)
to_read = buffer_size;
memcpy(buffer, &chunk_buffer[chunk_position], to_read);
chunk_position += to_read;
*bytes_read = to_read;
return NErr_Success;
}
size_t RawMediaReader::Release()
{
delete this;
return 0;
}
#define CBCLASS RawMediaReader
START_DISPATCH;
CB(RELEASE, Release);
CB(RAW_READ, Read);
END_DISPATCH;
#undef CBCLASS

View file

@ -0,0 +1,40 @@
#pragma once
#include "../Agave/DecodeFile/svc_raw_media_reader.h"
#include "../Agave/DecodeFile/ifc_raw_media_reader.h"
#include <mp4.h>
// {5CBD1F27-5A63-4D8C-9297-D74518E1EF3A}
static const GUID mpeg4_raw_reader_guid =
{ 0x5cbd1f27, 0x5a63, 0x4d8c, { 0x92, 0x97, 0xd7, 0x45, 0x18, 0xe1, 0xef, 0x3a } };
class RawMediaReaderService : public svc_raw_media_reader
{
public:
static const char *getServiceName() { return "MPEG-4 Raw Reader"; }
static GUID getServiceGuid() { return mpeg4_raw_reader_guid; }
int CreateRawMediaReader(const wchar_t *filename, ifc_raw_media_reader **reader);
protected:
RECVS_DISPATCH;
};
class RawMediaReader : public ifc_raw_media_reader
{
public:
RawMediaReader(MP4FileHandle file, void *reader);
~RawMediaReader();
int Read(void *buffer, size_t buffer_size, size_t *bytes_read);
size_t Release();
protected:
RECVS_DISPATCH;
private:
uint16_t track_num;
uint32_t number_of_tracks;
MP4TrackId current_track;
MP4FileHandle file;
void *reader;
MP4ChunkId chunk_id;
MP4ChunkId number_of_chunks;
uint32_t chunk_position, chunk_size;
uint8_t *chunk_buffer;
int ReadNextChunk();
};

View file

@ -0,0 +1,47 @@
#include "Stopper.h"
#include "main.h"
#include "../Winamp/wa_ipc.h"
int m_force_seek=-1;
Stopper::Stopper() : isplaying(0), timems(0)
{
}
void Stopper::ChangeTracking(bool mode)
{
SendMessage(mod.hMainWindow, WM_USER, mode, IPC_ALLOW_PLAYTRACKING); // enable / disable stats updating
}
void Stopper::Stop()
{
isplaying = SendMessage(mod.hMainWindow, WM_USER, 0, IPC_ISPLAYING);
if (isplaying)
{
ChangeTracking(0); // disable stats updating
timems = SendMessage(mod.hMainWindow, WM_USER, 0, IPC_GETOUTPUTTIME);
SendMessage(mod.hMainWindow, WM_COMMAND, 40047, 0); // Stop
}
}
void Stopper::Play()
{
if (isplaying) // this works _most_ of the time, not sure why a small portion of the time it doesnt hrmph :/
// ideally we should replace it with a system that pauses the decode thread, closes its file,
// does the shit, and reopens and reseeks to the new offset. for gaplessness
{
if (timems)
{
m_force_seek = timems; // SendMessage(mod.hMainWindow,WM_USER,timems,106);
}
else m_force_seek = -1;
SendMessage(mod.hMainWindow, WM_COMMAND, 40045, 0); // Play
m_force_seek = -1;
if (isplaying & 2)
{
SendMessage(mod.hMainWindow, WM_COMMAND, 40046, 0); // Pause
}
ChangeTracking(1); // enable stats updating
}
isplaying = 0;
}

View file

@ -0,0 +1,11 @@
#pragma once
class Stopper
{
public:
Stopper();
void ChangeTracking(bool);
void Stop();
void Play();
int isplaying, timems;
};

View file

@ -0,0 +1,215 @@
#include "main.h"
#include "VideoThread.h"
#include "../Winamp/wa_ipc.h"
VideoSample *video_sample=0;
IVideoOutput *videoOutput=0;
static bool video_reopen=false;
static int height=0;
static int width=0;
static bool video_opened=false;
static int consecutive_early_frames;
HANDLE video_flush = 0, video_start_flushing=0, video_flush_done = 0, video_resume = 0;
static HANDLE video_thread = 0;
MP4SampleId nextVideoSampleId=1; // set in conjunction with video_flush
static void OpenVideo()
{
if (!video_opened || video_reopen)
{
consecutive_early_frames = 0;
if (!videoOutput)
videoOutput = (IVideoOutput *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_IVIDEOOUTPUT);
int color_format;
double aspect_ratio=1.0;
if (video && video->GetOutputFormat(&width, &height, &color_format, &aspect_ratio) == MP4_VIDEO_SUCCESS)
{
videoOutput->extended(VIDUSER_SET_THREAD_SAFE, 1, 0);
videoOutput->open(width, height, 0, 1.0/aspect_ratio, color_format);
video_opened = true;
video_reopen = false;
}
}
}
static DecodedVideoSample *GetNextPicture()
{
void *data, *decoder_data;
MP4Timestamp timestamp=video_sample?(video_sample->timestamp):0;
switch(video->GetPicture(&data, &decoder_data, &timestamp))
{
case MP4_VIDEO_OUTPUT_FORMAT_CHANGED:
video_reopen=true;
// fall through
case MP4_VIDEO_SUCCESS:
DecodedVideoSample *decoded = new DecodedVideoSample;
decoded->decoder = video;
decoded->decoder_data = decoder_data;
decoded->timestamp = timestamp;
decoded->output = data;
return decoded;
}
return 0;
}
static void OutputPicture(DecodedVideoSample *decoded_video_sample)
{
if (decoded_video_sample)
{
int outputTime = (int)((decoded_video_sample->timestamp*1000ULL)/(uint64_t)m_video_timescale);
again:
MP4Duration realTime = GetClock();
int time_diff = outputTime - realTime;
if (time_diff > 12 && consecutive_early_frames) // plenty of time, go ahead and turn off frame dropping
{
if (--consecutive_early_frames == 0)
video->HurryUp(0);
}
else if (time_diff < -50) // shit we're way late, start dropping frames
{
video->HurryUp(1);
consecutive_early_frames += 3;
}
if (time_diff > 3)
{
HANDLE handles[] = {killEvent, video_start_flushing};
if (WaitForMultipleObjects(2, handles, FALSE, outputTime-realTime) != WAIT_TIMEOUT)
{
delete decoded_video_sample;
decoded_video_sample=0;
return;
}
goto again; // TODO: handle paused state a little better than this
}
OpenVideo(); // open video if we havn't already
videoOutput->draw(decoded_video_sample->output);
delete decoded_video_sample;
decoded_video_sample=0;
/* TODO: probably want separate audio and video done flags
if (temp->sampleId == numSamples) // done!
done = true;
*/
}
}
static bool ReadNextVideoSample()
{
while (nextVideoSampleId <= numVideoSamples)
{
VideoSample &sample=*video_sample;
unsigned __int32 buffer_size = sample.inputSize;
bool isSync=false;
MP4Duration duration, offset;
play_mp4_guard.Lock();
bool sample_read=MP4ReadSample(MP4hFile, video_track, nextVideoSampleId++, (unsigned __int8 **)&sample.input, &buffer_size, &sample.timestamp, &duration, &offset, &isSync);
play_mp4_guard.Unlock();
if (sample_read)
{
// some buggy movies store signed int32 offsets, so let's deal with it
offset = (uint32_t)offset;
int32_t signed_offset = (int32_t)offset;
sample.timestamp += signed_offset;
//int outputTime = (int)((sample.timestamp*1000ULL) /(uint64_t)m_video_timescale);
sample.inputValid = buffer_size;
return true;
}
}
return false;
}
static DWORD WINAPI VideoPlayThread(LPVOID parameter)
{
DWORD waitTime = 0;
HANDLE handles[] = {killEvent, video_flush, video_start_flushing, video_resume};
while (1)
{
int ret = WaitForMultipleObjects(4, handles, FALSE, waitTime);
if (ret == WAIT_OBJECT_0) // kill
break;
else if (ret == WAIT_OBJECT_0+1) // flush
{
if (video)
video->Flush();
ResetEvent(video_flush);
waitTime = 0;
SetEvent(video_flush_done);
}
else if (ret == WAIT_OBJECT_0+2) // start flushing
{
waitTime = INFINITE; // this will stop us from decoding samples for a while
ResetEvent(video_start_flushing);
SetEvent(video_flush_done);
}
else if (ret == WAIT_OBJECT_0+3) // resume playback (like flush but don't flush the decoder)
{
ResetEvent(video_resume);
waitTime = 0;
SetEvent(video_flush_done);
}
else if (ret == WAIT_TIMEOUT)
{
if (ReadNextVideoSample())
{
int ret = video->DecodeSample(video_sample->input, video_sample->inputValid, video_sample->timestamp);
if (ret == MP4_VIDEO_OUTPUT_FORMAT_CHANGED)
video_reopen=true;
if (ret == MP4_VIDEO_AGAIN)
nextVideoSampleId--;
DecodedVideoSample *picture = 0;
while (picture = GetNextPicture())
{
OutputPicture(picture);
}
waitTime = 0;
}
else
{
// TODO: tell decoder end-of-file and get any buffers in queue
if (!audio)
PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
waitTime = INFINITE; // out of stuff to do, wait for kill or flush
}
}
else // error
break;
}
if (videoOutput)
videoOutput->close();
return 0;
}
void Video_Init()
{
width=0;
height=0;
video_reopen=false;
video_opened=false;
video_flush = CreateEvent(NULL, TRUE, FALSE, NULL);
video_start_flushing = CreateEvent(NULL, TRUE, FALSE, NULL);
video_flush_done = CreateEvent(NULL, FALSE, FALSE, NULL);
video_resume = CreateEvent(NULL, TRUE, FALSE, NULL);
video_thread = CreateThread(0, 0, VideoPlayThread, 0, 0, 0);
}
void Video_Close()
{
WaitForSingleObject(video_thread, INFINITE);
CloseHandle(video_thread);
video_thread = 0;
CloseHandle(video_start_flushing);
video_start_flushing=0;
CloseHandle(video_flush);
video_flush=0;
CloseHandle(video_resume);
video_resume=0;
CloseHandle(video_flush_done);
video_flush_done = 0;
delete video_sample;
video_sample=0;
}

View file

@ -0,0 +1,10 @@
#pragma once
#include "AudioSample.h"
#include "../Winamp/wa_ipc.h"
void Video_Init();
void Video_Close();
extern IVideoOutput *videoOutput;
extern VideoSample *video_sample;
extern HANDLE video_start_flushing, video_flush, video_flush_done, video_resume;
extern MP4SampleId nextVideoSampleId; // set in conjunction with video_flush

View file

@ -0,0 +1,716 @@
#include "main.h"
#include "VirtualIO.h"
#include "api__in_mp4.h"
#include "api/service/waservicefactory.h"
#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
#include "../nu/AutoChar.h"
#include "../nu/ProgressTracker.h"
#include <assert.h>
#include <strsafe.h>
#define HTTP_BUFFER_SIZE 65536
// {C0A565DC-0CFE-405a-A27C-468B0C8A3A5C}
static const GUID internetConfigGroupGUID =
{
0xc0a565dc, 0xcfe, 0x405a, { 0xa2, 0x7c, 0x46, 0x8b, 0xc, 0x8a, 0x3a, 0x5c }
};
static void SetUserAgent(api_httpreceiver *http)
{
char agent[256] = {0};
StringCchPrintfA(agent, 256, "User-Agent: %S/%S", WASABI_API_APP->main_getAppName(), WASABI_API_APP->main_getVersionNumString());
http->addheader(agent);
}
static api_httpreceiver *SetupConnection(const char *url, uint64_t start_position, uint64_t end_position)
{
api_httpreceiver *http = 0;
waServiceFactory *sf = mod.service->service_getServiceByGuid(httpreceiverGUID);
if (sf) http = (api_httpreceiver *)sf->getInterface();
if (!http)
return http;
int use_proxy = 1;
bool proxy80 = AGAVE_API_CONFIG->GetBool(internetConfigGroupGUID, L"proxy80", false);
if (proxy80 && strstr(url, ":") && (!strstr(url, ":80/") && strstr(url, ":80") != (url + strlen(url) - 3)))
use_proxy = 0;
const wchar_t *proxy = use_proxy?AGAVE_API_CONFIG->GetString(internetConfigGroupGUID, L"proxy", 0):0;
http->open(API_DNS_AUTODNS, HTTP_BUFFER_SIZE, (proxy && proxy[0]) ? (const char *)AutoChar(proxy) : NULL);
if (start_position && start_position != (uint64_t)-1)
{
if (end_position == (uint64_t)-1)
{
char temp[128] = {0};
StringCchPrintfA(temp, 128, "Range: bytes=%I64u-", start_position);
http->addheader(temp);
}
else
{
char temp[128] = {0};
StringCchPrintfA(temp, 128, "Range: bytes=%I64u-%I64u", start_position, end_position);
http->addheader(temp);
}
}
SetUserAgent(http);
http->connect(url);
return http;
}
static DWORD CALLBACK ProgressiveThread(LPVOID param);
static __int64 Seek64(HANDLE hf, __int64 distance, DWORD MoveMethod)
{
LARGE_INTEGER li;
li.QuadPart = distance;
li.LowPart = SetFilePointer (hf, li.LowPart, &li.HighPart, MoveMethod);
if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
{
li.QuadPart = -1;
}
return li.QuadPart;
}
int bufferCount;
static void Buffering(int bufStatus, const wchar_t *displayString)
{
if (bufStatus < 0 || bufStatus > 100)
return;
char tempdata[75*2] = {0, };
int csa = mod.SAGetMode();
if (csa & 1)
{
for (int x = 0; x < bufStatus*75 / 100; x ++)
tempdata[x] = x * 16 / 75;
}
else if (csa&2)
{
int offs = (csa & 1) ? 75 : 0;
int x = 0;
while (x < bufStatus*75 / 100)
{
tempdata[offs + x++] = -6 + x * 14 / 75;
}
while (x < 75)
{
tempdata[offs + x++] = 0;
}
}
else if (csa == 4)
{
tempdata[0] = tempdata[1] = (bufStatus * 127 / 100);
}
if (csa) mod.SAAdd(tempdata, ++bufferCount, (csa == 3) ? 0x80000003 : csa);
/*
TODO
wchar_t temp[64] = {0};
StringCchPrintf(temp, 64, L"%s: %d%%",displayString, bufStatus);
SetStatus(temp);
*/
//SetVideoStatusText(temp); // TODO: find a way to set the old status back
// videoOutput->notifyBufferState(static_cast<int>(bufStatus*2.55f));
}
class ProgressiveReader
{
public:
ProgressiveReader(const char *url, HANDLE killswitch) : killswitch(killswitch)
{
thread_abort = CreateEvent(NULL, FALSE, FALSE, NULL);
download_thread = 0;
progressive_file_read = 0;
progressive_file_write = 0;
content_length=0;
current_position=0;
stream_disconnected=false;
connected=false;
end_of_file=false;
wchar_t temppath[MAX_PATH-14] = {0}; // MAX_PATH-14 'cause MSDN said so
GetTempPathW(MAX_PATH-14, temppath);
GetTempFileNameW(temppath, L"wdl", 0, filename);
this->url = _strdup(url);
http = SetupConnection(url, 0, (uint64_t)-1);
progressive_file_read = CreateFileW(filename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0);
progressive_file_write = CreateFileW(filename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0);
download_thread = CreateThread(0, 0, ProgressiveThread, this, 0, 0);
while (!connected && !stream_disconnected && WaitForSingleObject(killswitch, 55) == WAIT_TIMEOUT)
{
// nop
}
Buffer();
}
~ProgressiveReader()
{
if (download_thread)
{
SetEvent(thread_abort);
WaitForSingleObject(download_thread, INFINITE);
CloseHandle(download_thread);
}
if (thread_abort)
{
CloseHandle(thread_abort);
}
CloseHandle(progressive_file_read);
CloseHandle(progressive_file_write);
DeleteFile(filename);
if (http)
{
waServiceFactory *sf = mod.service->service_getServiceByGuid(httpreceiverGUID);
if (sf) http = (api_httpreceiver *)sf->releaseInterface(http);
http=0;
}
}
void Buffer()
{
bufferCount=0;
for (int i=0;i<101;i++)
{
Buffering(i, L"Buffering: ");
WaitForSingleObject(killswitch, 55);
}
}
void OnFinish()
{
stream_disconnected=true;
}
bool WaitForPosition(uint64_t position, uint64_t size)
{
do
{
bool valid = progress_tracker.Valid(position, position+size);
if (valid)
return true;
else
{
if (position < current_position)
{
Reconnect(position, position+size);
}
else
{
Buffer();
}
}
} while (WaitForSingleObject(killswitch, 0) == WAIT_TIMEOUT);
return false;
}
size_t Read(void *buffer, size_t size)
{
if (WaitForPosition(current_position, (uint64_t)size) == false)
return 0;
DWORD bytes_read=0;
ReadFile(progressive_file_read, buffer, size, &bytes_read, NULL);
current_position += bytes_read;
return bytes_read;
}
uint64_t GetFileLength()
{
return content_length;
}
void Reconnect(uint64_t position, uint64_t end)
{
SetEvent(thread_abort);
WaitForSingleObject(download_thread, INFINITE);
ResetEvent(thread_abort);
uint64_t new_start, new_end;
progress_tracker.Seek(position, end, &new_start, &new_end);
CloseHandle(download_thread);
stream_disconnected=false;
connected=false;
if (http)
{
waServiceFactory *sf = mod.service->service_getServiceByGuid(httpreceiverGUID);
if (sf) http = (api_httpreceiver *)sf->releaseInterface(http);
http=0;
}
http = SetupConnection(url, new_start, new_end);
Seek64(progressive_file_write, new_start, SEEK_SET);
download_thread = CreateThread(0, 0, ProgressiveThread, this, 0, 0);
while (!connected && !stream_disconnected && WaitForSingleObject(killswitch, 55) == WAIT_TIMEOUT)
{
// nop
}
Buffer();
}
int SetPosition(uint64_t position)
{
if (position == content_length)
{
end_of_file=true;
}
else
{
if (!progress_tracker.Valid(position, position))
{
Reconnect(position, (uint64_t)-1);
}
current_position = Seek64(progressive_file_read, position, SEEK_SET);
end_of_file=false;
}
return 0;
}
int GetPosition(uint64_t *position)
{
if (end_of_file)
*position = content_length;
else
*position = current_position;
return 0;
}
int EndOfFile()
{
return !!stream_disconnected;
}
int Close()
{
SetEvent(thread_abort);
while (!stream_disconnected && WaitForSingleObject(killswitch, 55) == WAIT_TIMEOUT)
{
// nop
}
return 0;
}
/* API used by download thread */
void Write(const void *data, size_t data_len)
{
DWORD bytes_written = 0;
WriteFile(progressive_file_write, data, data_len, &bytes_written, 0);
progress_tracker.Write(data_len);
}
int Wait(int milliseconds)
{
HANDLE handles[] = {killswitch, thread_abort};
int ret = WaitForMultipleObjects(2, handles, FALSE, milliseconds);
if (ret == WAIT_OBJECT_0+1)
return 1;
else if (ret == WAIT_TIMEOUT)
return 0;
else
return -1;
}
int DoRead(void *buffer, size_t bufferlen)
{
int ret = http->run();
int bytes_received;
do
{
ret = http->run();
bytes_received= http->get_bytes(buffer, bufferlen);
if (bytes_received)
Write(buffer, bytes_received);
} while (bytes_received);
return ret;
}
int Connect()
{
do
{
int ret = http->run();
if (ret == -1) // connection failed
return ret;
// ---- check our reply code ----
int replycode = http->getreplycode();
switch (replycode)
{
case 0:
case 100:
break;
case 200:
case 206:
{
const char *content_length_header = http->getheader("Content-Length");
if (content_length_header)
{
uint64_t new_content_length = _strtoui64(content_length_header, 0, 10);
//InterlockedExchange64((volatile LONGLONG *)&content_length, new_content_length);
content_length = new_content_length; // TODO interlock on win32
}
connected=true;
return 0;
}
default:
return -1;
}
}
while (Wait(55) == 0);
return 0;
}
private:
uint64_t current_position;
volatile uint64_t content_length;
bool end_of_file;
bool stream_disconnected;
bool connected;
char *url;
wchar_t filename[MAX_PATH];
HANDLE progressive_file_read, progressive_file_write;
ProgressTracker progress_tracker;
HANDLE killswitch;
HANDLE download_thread;
api_httpreceiver *http;
HANDLE thread_abort;
};
static DWORD CALLBACK ProgressiveThread(LPVOID param)
{
ProgressiveReader *reader = (ProgressiveReader *)param;
if (reader->Connect() == 0)
{
int ret = 0;
while (ret == 0)
{
ret=reader->Wait(10);
if (ret >= 0)
{
char buffer[HTTP_BUFFER_SIZE] = {0};
reader->DoRead(buffer, sizeof(buffer));
}
}
}
reader->OnFinish();
return 0;
}
u_int64_t HTTPGetFileLength(void *user)
{
ProgressiveReader *reader = (ProgressiveReader *)user;
return reader->GetFileLength();
}
int HTTPSetPosition(void *user, u_int64_t position)
{
ProgressiveReader *reader = (ProgressiveReader *)user;
return reader->SetPosition(position);
}
int HTTPGetPosition(void *user, u_int64_t *position)
{
ProgressiveReader *reader = (ProgressiveReader *)user;
return reader->GetPosition(position);
}
size_t HTTPRead(void *user, void *buffer, size_t size)
{
ProgressiveReader *reader = (ProgressiveReader *)user;
return reader->Read(buffer, size);
}
size_t HTTPWrite(void *user, void *buffer, size_t size)
{
return 1;
}
int HTTPEndOfFile(void *user)
{
ProgressiveReader *reader = (ProgressiveReader *)user;
return reader->EndOfFile();
}
int HTTPClose(void *user)
{
ProgressiveReader *reader = (ProgressiveReader *)user;
return reader->Close();
}
Virtual_IO HTTPIO =
{
HTTPGetFileLength,
HTTPSetPosition,
HTTPGetPosition,
HTTPRead,
HTTPWrite,
HTTPEndOfFile,
HTTPClose,
};
void *CreateReader(const wchar_t *url, HANDLE killswitch)
{
if ( WAC_API_DOWNLOADMANAGER )
{
return new ProgressiveReader(AutoChar(url), killswitch);
}
else
return 0;
}
void DestroyReader(void *user)
{
ProgressiveReader *reader = (ProgressiveReader *)user;
delete reader;
}
void StopReader(void *user)
{
ProgressiveReader *reader = (ProgressiveReader *)user;
reader->Close();
}
/* ----------------------------------- */
struct Win32_State
{
Win32_State()
{
memset(buffer, 0, sizeof(buffer));
handle=0;
endOfFile=false;
position.QuadPart = 0;
event = CreateEvent(NULL, TRUE, TRUE, NULL);
read_offset=0;
io_active=false;
}
~Win32_State()
{
if (handle && handle != INVALID_HANDLE_VALUE)
CancelIo(handle);
CloseHandle(event);
}
// void *userData;
HANDLE handle;
bool endOfFile;
LARGE_INTEGER position;
HANDLE event;
OVERLAPPED overlapped;
DWORD read_offset;
bool io_active;
char buffer[16384];
};
static __int64 FileSize64(HANDLE file)
{
LARGE_INTEGER position;
position.QuadPart=0;
position.LowPart = GetFileSize(file, (LPDWORD)&position.HighPart);
if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
return INVALID_FILE_SIZE;
else
return position.QuadPart;
}
u_int64_t UnicodeGetFileLength(void *user)
{
Win32_State *state = static_cast<Win32_State *>(user);
assert(state->handle);
return FileSize64(state->handle);
}
int UnicodeSetPosition(void *user, u_int64_t position)
{
Win32_State *state = static_cast<Win32_State *>(user);
assert(state->handle);
__int64 diff = position - state->position.QuadPart;
if ((diff+state->read_offset) >= sizeof(state->buffer)
|| (diff+state->read_offset) < 0)
{
CancelIo(state->handle);
state->io_active = 0;
state->read_offset = 0;
}
else if (diff)
state->read_offset += (DWORD)diff;
state->position.QuadPart = position;
state->endOfFile = false;
return 0;
}
int UnicodeGetPosition(void *user, u_int64_t *position)
{
Win32_State *state = static_cast<Win32_State *>(user);
assert(state->handle);
*position = state->position.QuadPart;
return 0;
}
static void DoRead(Win32_State *state)
{
WaitForSingleObject(state->event, INFINITE);
state->overlapped.hEvent = state->event;
state->overlapped.Offset = state->position.LowPart;
state->overlapped.OffsetHigh = state->position.HighPart;
state->read_offset = 0;
ResetEvent(state->event);
ReadFile(state->handle, state->buffer, sizeof(state->buffer), NULL, &state->overlapped);
//int error = GetLastError();//ERROR_IO_PENDING = 997
state->io_active=true;
}
size_t UnicodeRead(void *user, void *buffer, size_t size)
{
Win32_State *state = static_cast<Win32_State *>(user);
assert(state->handle);
size_t totalRead=0;
HANDLE file = state->handle;
if (!state->io_active)
{
DoRead(state);
}
if (state->read_offset == sizeof(state->buffer))
{
DoRead(state);
}
while (size > (sizeof(state->buffer) - state->read_offset))
{
DWORD bytesRead=0;
BOOL res = GetOverlappedResult(file, &state->overlapped, &bytesRead, TRUE);
if ((res && bytesRead != sizeof(state->buffer))
|| (!res && GetLastError() == ERROR_HANDLE_EOF))
{
state->endOfFile = true;
}
if (bytesRead > state->read_offset)
{
size_t bytesToCopy = bytesRead-state->read_offset;
memcpy(buffer, state->buffer + state->read_offset, bytesToCopy);
buffer=(uint8_t *)buffer + bytesToCopy;
totalRead+=bytesToCopy;
size-=bytesToCopy;
if (state->endOfFile)
return totalRead;
state->position.QuadPart += bytesToCopy;
DoRead(state);
}
else
break;
}
while (1)
{
DWORD bytesRead=0;
BOOL res = GetOverlappedResult(file, &state->overlapped, &bytesRead, FALSE);
if ((res && bytesRead != sizeof(state->buffer))
|| (!res && GetLastError() == ERROR_HANDLE_EOF))
{
state->endOfFile = true;
}
if (bytesRead >= (size + state->read_offset))
{
memcpy(buffer, state->buffer + state->read_offset, size);
state->read_offset += size;
totalRead+=size;
state->position.QuadPart += size;
break;
}
if (state->endOfFile)
break;
WaitForSingleObject(state->event, 10); // wait 10 milliseconds or when buffer is done, whichever is faster
}
return totalRead;
}
size_t UnicodeWrite(void *user, void *buffer, size_t size)
{
Win32_State *state = static_cast<Win32_State *>(user);
DWORD written = 0;
assert(state->handle);
WriteFile(state->handle, buffer, size, &written, NULL);
return 0;
}
int UnicodeEndOfFile(void *user)
{
Win32_State *state = static_cast<Win32_State *>(user);
return state->endOfFile;
}
int UnicodeClose(void *user)
{
Win32_State *state = static_cast<Win32_State *>(user);
if (state->handle)
CloseHandle(state->handle);
state->handle=0;
return 0;
}
Virtual_IO UnicodeIO =
{
UnicodeGetFileLength,
UnicodeSetPosition,
UnicodeGetPosition,
UnicodeRead,
UnicodeWrite,
UnicodeEndOfFile,
UnicodeClose,
};
void *CreateUnicodeReader(const wchar_t *filename)
{
HANDLE fileHandle = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (fileHandle == INVALID_HANDLE_VALUE)
return 0;
Win32_State *state = new Win32_State;
if (!state)
{
CloseHandle(fileHandle);
return 0;
}
state->endOfFile = false;
state->handle = fileHandle;
return state;
}
void DestroyUnicodeReader(void *reader)
{
if (reader) // need to check because of the cast
delete (Win32_State *)reader;
}

View file

@ -0,0 +1,17 @@
#ifndef NULLSOFT_IN_MP4_VIRTUALIO_H
#define NULLSOFT_IN_MP4_VIRTUALIO_H
#include "main.h"
#include <api/service/svcs/svc_fileread.h>
#include <virtual_io.h>
void *CreateReader(const wchar_t *url, HANDLE killswitch);
void DestroyReader(void *reader);
void StopReader(void *reader);
extern Virtual_IO HTTPIO;
extern Virtual_IO UnicodeIO;
void *CreateUnicodeReader(const wchar_t *filename);
void DestroyUnicodeReader(void *reader);
int UnicodeClose(void *user);
#endif

View file

@ -0,0 +1,4 @@
#include "api__in_mp4.h"
api_downloadManager *WAC_API_DOWNLOADMANAGER =0;
api_threadpool *WASABI_API_THREADPOOL=0;

View file

@ -0,0 +1,22 @@
#ifndef NULLSOFT_APIH
#define NULLSOFT_APIH
#include "../Agave/Config/api_config.h"
extern api_config *AGAVE_API_CONFIG;
#include <api/application/api_application.h>
#define WASABI_API_APP applicationApi
#include <api/memmgr/api_memmgr.h>
extern api_memmgr *memmgrApi;
#define WASABI_API_MEMMGR memmgrApi
#include "../Agave/Language/api_language.h"
#include "../Components/wac_downloadManager/wac_downloadManager_api.h"
#include "../nu/threadpool/api_threadpool.h"
extern api_threadpool *threadPoolApi;
#define WASABI_API_THREADPOOL threadPoolApi
#endif

View file

@ -0,0 +1,49 @@
#include "main.h"
#include "api__in_mp4.h"
#include "../nu/AutoChar.h"
#include "resource.h"
bool config_show_average_bitrate = true;
INT_PTR CALLBACK ConfigProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_INITDIALOG:
{
wchar_t exts[1024] = {0};
GetPrivateProfileStringW(L"in_mp4", L"extensionlist", defaultExtensions, exts, 1024, m_ini);
SetDlgItemTextW(hwndDlg, IDC_EXTENSIONLIST, exts);
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_DEFAULT:
SetDlgItemTextW(hwndDlg, IDC_EXTENSIONLIST, defaultExtensions);
break;
case IDOK:
{
wchar_t exts[1024] = {0};
GetDlgItemTextW(hwndDlg, IDC_EXTENSIONLIST, exts, 1024);
if (!_wcsicmp(exts, defaultExtensions)) // same as default?
WritePrivateProfileStringW(L"in_mp4", L"extensionlist", 0, m_ini);
else
WritePrivateProfileStringW(L"in_mp4", L"extensionlist", exts, m_ini);
free(mod.FileExtensions);
mod.FileExtensions = BuildExtensions(AutoChar(exts));
EndDialog(hwndDlg, 0);
}
break;
case IDCANCEL:
EndDialog(hwndDlg, 1);
break;
}
break;
}
return 0;
}
void config(HWND hwndParent)
{
WASABI_API_DIALOGBOXW(IDD_CONFIG, hwndParent, ConfigProc);
}

View file

@ -0,0 +1,129 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"#include ""version.rc2""\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_CONFIG DIALOGEX 0, 0, 215, 50
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "MP4 Configuration"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Extension List (semicolon delimited):",IDC_STATIC,4,4,208,10
EDITTEXT IDC_EXTENSIONLIST,4,16,207,13,ES_AUTOHSCROLL
PUSHBUTTON "Set to Defaults",IDC_DEFAULT,4,33,60,13
DEFPUSHBUTTON "OK",IDOK,107,33,50,13
PUSHBUTTON "Cancel",IDCANCEL,161,33,50,13
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_CONFIG, DIALOG
BEGIN
LEFTMARGIN, 4
RIGHTMARGIN, 211
TOPMARGIN, 4
BOTTOMMARGIN, 46
END
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE
BEGIN
IDS_NULLSOFT_MPEG4_AUDIO_DECODER "Nullsoft MP4 Demuxer v%s"
65535 "{F30C75C1-D284-4cd5-9CED-2BD9E7869438}"
END
STRINGTABLE
BEGIN
IDS_NULLSOFT_MPEG4_AUDIO_DECODER_OLD "Nullsoft MP4 Demuxer"
IDS_CANNOT_UPDATE_THE_FILE "Cannot update the file!"
IDS_ERROR "Error"
IDS_MP4_FILE "MP4 File"
IDS_FAMILY_STRING_M4A "MPEG-4 Audio File Format"
IDS_FAMILY_STRING_MPEG4 "MPEG-4 File Format"
IDS_FAMILY_STRING_M4V "MPEG-4 Video File Format"
IDS_FAMILY_STRING_QUICKTIME "Quicktime Movie"
IDS_FAMILY_STRING_3GPP "3GPP File Format"
IDS_FAMILY_STRING_FLV "Flash MPEG-4 Video"
IDS_ABOUT_TEXT "%s\n© 2005-2023 Winamp SA\n\nBuild date: %s\n\nUses the libmp4v2 library\nfrom the MPEG4IP package\n(http://mpeg4ip.sourceforge.net)"
IDS_AUDIO_INFO "Audio:\n\t%S\n\t%u kbps\n"
IDS_VIDEO_INFO "Video:\n\t%S\n\t%u kbps\n"
END
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
#include "version.rc2"
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View file

@ -0,0 +1,67 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29424.173
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "in_mp4", "in_mp4.vcxproj", "{9F6D17FA-6DFD-436F-B54E-1B63D012D1A2}"
ProjectSection(ProjectDependencies) = postProject
{F6962367-6A6C-4E7D-B661-B767E904F451} = {F6962367-6A6C-4E7D-B661-B767E904F451}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmp4v2", "..\libmp4v2\libmp4v2.vcxproj", "{EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "aacdec-mft", "..\aacdec-mft\aacdec-mft.vcxproj", "{F6962367-6A6C-4E7D-B661-B767E904F451}"
ProjectSection(ProjectDependencies) = postProject
{DABE6307-F8DD-416D-9DAC-673E2DECB73F} = {DABE6307-F8DD-416D-9DAC-673E2DECB73F}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsutil", "..\nsutil\nsutil.vcxproj", "{DABE6307-F8DD-416D-9DAC-673E2DECB73F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9F6D17FA-6DFD-436F-B54E-1B63D012D1A2}.Debug|Win32.ActiveCfg = Debug|Win32
{9F6D17FA-6DFD-436F-B54E-1B63D012D1A2}.Debug|Win32.Build.0 = Debug|Win32
{9F6D17FA-6DFD-436F-B54E-1B63D012D1A2}.Debug|x64.ActiveCfg = Debug|x64
{9F6D17FA-6DFD-436F-B54E-1B63D012D1A2}.Debug|x64.Build.0 = Debug|x64
{9F6D17FA-6DFD-436F-B54E-1B63D012D1A2}.Release|Win32.ActiveCfg = Release|Win32
{9F6D17FA-6DFD-436F-B54E-1B63D012D1A2}.Release|Win32.Build.0 = Release|Win32
{9F6D17FA-6DFD-436F-B54E-1B63D012D1A2}.Release|x64.ActiveCfg = Release|x64
{9F6D17FA-6DFD-436F-B54E-1B63D012D1A2}.Release|x64.Build.0 = Release|x64
{EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Debug|Win32.ActiveCfg = Debug|Win32
{EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Debug|Win32.Build.0 = Debug|Win32
{EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Debug|x64.ActiveCfg = Debug|x64
{EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Debug|x64.Build.0 = Debug|x64
{EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Release|Win32.ActiveCfg = Release|Win32
{EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Release|Win32.Build.0 = Release|Win32
{EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Release|x64.ActiveCfg = Release|x64
{EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Release|x64.Build.0 = Release|x64
{F6962367-6A6C-4E7D-B661-B767E904F451}.Debug|Win32.ActiveCfg = Debug|Win32
{F6962367-6A6C-4E7D-B661-B767E904F451}.Debug|Win32.Build.0 = Debug|Win32
{F6962367-6A6C-4E7D-B661-B767E904F451}.Debug|x64.ActiveCfg = Debug|x64
{F6962367-6A6C-4E7D-B661-B767E904F451}.Debug|x64.Build.0 = Debug|x64
{F6962367-6A6C-4E7D-B661-B767E904F451}.Release|Win32.ActiveCfg = Release|Win32
{F6962367-6A6C-4E7D-B661-B767E904F451}.Release|Win32.Build.0 = Release|Win32
{F6962367-6A6C-4E7D-B661-B767E904F451}.Release|x64.ActiveCfg = Release|x64
{F6962367-6A6C-4E7D-B661-B767E904F451}.Release|x64.Build.0 = Release|x64
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|Win32.ActiveCfg = Debug|Win32
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|Win32.Build.0 = Debug|Win32
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|x64.ActiveCfg = Debug|x64
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|x64.Build.0 = Debug|x64
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|Win32.ActiveCfg = Release|Win32
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|Win32.Build.0 = Release|Win32
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|x64.ActiveCfg = Release|x64
{DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FC646532-2050-40A5-A2AB-F699F1C071C4}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,301 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{9F6D17FA-6DFD-436F-B54E-1B63D012D1A2}</ProjectGuid>
<RootNamespace>in_mp4</RootNamespace>
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
<LibraryPath>$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
<LibraryPath>$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Label="Vcpkg">
<VcpkgEnabled>false</VcpkgEnabled>
</PropertyGroup>
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<VcpkgConfiguration>Debug</VcpkgConfiguration>
</PropertyGroup>
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<VcpkgConfiguration>Debug</VcpkgConfiguration>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>.;..\..\..\external_dependencies\libmp4v2;..\..\..\Wasabi;..\..\..\external_dependencies\libmp4v2\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_DEBUG;WIN32;_WINDOWS;_USRDLL;in_mp4_EXPORTS;UNICODE_INPUT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<BrowseInformation>true</BrowseInformation>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CompileAs>Default</CompileAs>
<DisableSpecificWarnings>4018;4244;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0409</Culture>
</ResourceCompile>
<Link>
<AdditionalDependencies>winmm.lib;wsock32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<DelayLoadDLLs>libmp4v2.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX86</TargetMachine>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<SubSystem>Windows</SubSystem>
</Link>
<PostBuildEvent>
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>.;..\..\..\libmp4v2;..\..\..\Wasabi;..\..\..\libmp4v2\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_DEBUG;WIN64;_WINDOWS;_USRDLL;in_mp4_EXPORTS;UNICODE_INPUT_PLUGIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<BrowseInformation>true</BrowseInformation>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CompileAs>Default</CompileAs>
<DisableSpecificWarnings>4018;4244;4267;4312;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0409</Culture>
</ResourceCompile>
<Link>
<AdditionalDependencies>winmm.lib;wsock32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<DelayLoadDLLs>libmp4v2.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<SubSystem>Windows</SubSystem>
</Link>
<PostBuildEvent>
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MinSpace</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
<AdditionalIncludeDirectories>.;..\..\..\external_dependencies\libmp4v2;..\..\..\Wasabi;..\..\..\external_dependencies\libmp4v2\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>NDEBUG;WIN32;_WINDOWS;_USRDLL;in_mp4_EXPORTS;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<BufferSecurityCheck>true</BufferSecurityCheck>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>None</DebugInformationFormat>
<CompileAs>Default</CompileAs>
<DisableSpecificWarnings>4018;4244;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0409</Culture>
</ResourceCompile>
<Link>
<AdditionalDependencies>winmm.lib;wsock32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<GenerateDebugInformation>false</GenerateDebugInformation>
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX86</TargetMachine>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<SubSystem>Windows</SubSystem>
</Link>
<PostBuildEvent>
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<Optimization>MinSpace</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
<AdditionalIncludeDirectories>.;..\..\..\libmp4v2;..\..\..\Wasabi;..\..\..\libmp4v2\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>NDEBUG;WIN64;_WINDOWS;_USRDLL;in_mp4_EXPORTS;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<BufferSecurityCheck>true</BufferSecurityCheck>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>None</DebugInformationFormat>
<CompileAs>Default</CompileAs>
<DisableSpecificWarnings>4018;4244;4267;4312;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0409</Culture>
</ResourceCompile>
<Link>
<AdditionalDependencies>winmm.lib;wsock32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<GenerateDebugInformation>false</GenerateDebugInformation>
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<SubSystem>Windows</SubSystem>
</Link>
<PostBuildEvent>
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\external_dependencies\libmp4v2\libmp4v2.vcxproj">
<Project>{efb9b882-6a8b-463d-a8e3-a2807afc5d9f}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
<Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\nu\bitbuffer.cpp" />
<ClCompile Include="..\..\..\nu\GaplessRingBuffer.cpp" />
<ClCompile Include="..\..\..\nu\RingBuffer.cpp" />
<ClCompile Include="..\..\..\nu\SpillBuffer.cpp" />
<ClCompile Include="AlbumArt.cpp" />
<ClCompile Include="api.cpp" />
<ClCompile Include="config.cpp" />
<ClCompile Include="ExtendedInfo.cpp" />
<ClCompile Include="ExtendedRead.cpp" />
<ClCompile Include="infobox.cpp" />
<ClCompile Include="Main.cpp" />
<ClCompile Include="mp4.cpp" />
<ClCompile Include="mpeg4audio.cpp" />
<ClCompile Include="mpeg4video.cpp" />
<ClCompile Include="PlayThread.cpp" />
<ClCompile Include="RawMediaReader.cpp" />
<ClCompile Include="Stopper.cpp" />
<ClCompile Include="VideoThread.cpp" />
<ClCompile Include="VirtualIO.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\nu\GaplessRingBuffer.h" />
<ClInclude Include="..\..\..\nu\RingBuffer.h" />
<ClInclude Include="..\..\..\nu\VideoClock.h" />
<ClInclude Include="AlbumArt.h" />
<ClInclude Include="api__in_mp4.h" />
<ClInclude Include="AudioSample.h" />
<ClInclude Include="Main.h" />
<ClInclude Include="mpeg4audio.h" />
<ClInclude Include="mpeg4video.h" />
<ClInclude Include="RawMediaReader.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="Stopper.h" />
<ClInclude Include="VideoThread.h" />
<ClInclude Include="VirtualIO.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="in_mp4.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="AlbumArt.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="api.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="config.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ExtendedInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ExtendedRead.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="infobox.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="mp4.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="mpeg4audio.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="mpeg4video.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PlayThread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RawMediaReader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Stopper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="VideoThread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="VirtualIO.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nu\bitbuffer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nu\GaplessRingBuffer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nu\RingBuffer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\nu\SpillBuffer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="AlbumArt.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="api__in_mp4.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="AudioSample.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Main.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="mpeg4audio.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="mpeg4video.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RawMediaReader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Stopper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="VideoThread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="VirtualIO.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\nu\GaplessRingBuffer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\nu\RingBuffer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\nu\VideoClock.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Header Files">
<UniqueIdentifier>{90f60180-1dcd-423f-9b3c-19e90036737f}</UniqueIdentifier>
</Filter>
<Filter Include="Ressource Files">
<UniqueIdentifier>{d93a6e62-1a2b-4fe9-bca8-34c3fb1c77d7}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{5f0d4520-0772-4236-af42-4348bc4c5561}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="in_mp4.rc">
<Filter>Ressource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,20 @@
#include "main.h"
#include "resource.h"
#include "../winamp/in2.h"
#include "api__in_mp4.h"
int infoDlg(const wchar_t *fn, HWND hwnd)
{ // this has been made obsolete by the below.
return 0;
}
extern "C"
{
// return 1 if you want winamp to show it's own file info dialogue, 0 if you want to show your own (via In_Module.InfoBox)
// if returning 1, remember to implement winampGetExtendedFileInfo("formatinformation")!
__declspec(dllexport) int winampUseUnifiedFileInfoDlg(const wchar_t * fn)
{
return 1;
}
// no advanced pane in this plugin :)
};

View file

@ -0,0 +1,98 @@
#include "main.h"
#include "mpeg4audio.h"
#include "api__in_mp4.h"
#include <api/service/waservicefactory.h>
#include <api/service/services.h>
//#include "JPEGDecoder.h"
//#include "MPEG4VideoDecoder.h"
//#include "AVCDecoder.h"
#include "../nu/bitbuffer.h"
#include <assert.h>
void ConfigureDecoderASC(MP4FileHandle file, MP4TrackId track, MP4AudioDecoder *decoder)
{
unsigned char *buffer = NULL;
unsigned __int32 buffer_size = 0;
// TODO: HUGE hack
const char *location = decoder->GetCodecInfoString();
if (location)
MP4GetTrackBytesProperty(file, track, location , (unsigned __int8 **)&buffer, &buffer_size);
else
MP4GetTrackESConfiguration(file, track, (unsigned __int8 **)&buffer, &buffer_size);
if (buffer)
{
decoder->AudioSpecificConfiguration(buffer, buffer_size);
MP4Free(buffer);
}
}
bool CreateDecoder(MP4FileHandle file, MP4TrackId track, MP4AudioDecoder *&decoder, waServiceFactory *&serviceFactory)
{
const char *media_data_name = MP4GetTrackMediaDataName(file, track);
u_int8_t audioType = MP4GetTrackEsdsObjectTypeId(file, track);
u_int8_t mpeg4Type = MP4GetTrackAudioMpeg4Type(file, track);
if (!media_data_name)
media_data_name = "mp4a"; // let's assume it's AAC if nothing else is said
waServiceFactory *sf = 0;
int n = 0;
while (sf = mod.service->service_enumService(WaSvc::MP4AUDIODECODER, n++))
{
MP4AudioDecoder * dec = static_cast<MP4AudioDecoder *>(sf->getInterface());
if (dec && dec->CanHandleCodec(media_data_name)
&& (!audioType || dec->CanHandleType(audioType))
&& (!mpeg4Type || dec->CanHandleMPEG4Type(mpeg4Type))
/*&& dec->Open() == MP4_SUCCESS*/)
{
//ConfigureDecoderASC(file, track, dec);
decoder = dec;
serviceFactory = sf;
return true;
}
sf->releaseInterface(dec);
}
return false;
}
bool CreateVideoDecoder(MP4FileHandle file, MP4TrackId track, MP4VideoDecoder *&decoder, waServiceFactory *&serviceFactory)
{
const char *media_data_name = MP4GetTrackMediaDataName(file, track);
// TODO check this is ok to disable...
//u_int8_t audioType = MP4GetTrackEsdsObjectTypeId(file, track);
//u_int8_t profileLevel = MP4GetVideoProfileLevel(file, track);
if (!media_data_name)
return false;
waServiceFactory *sf = 0;
int n = 0;
while (sf = mod.service->service_enumService(WaSvc::MP4VIDEODECODER, n++))
{
MP4VideoDecoder *dec = static_cast<MP4VideoDecoder *>(sf->getInterface());
if (dec && dec->CanHandleCodec(media_data_name)
/*&& dec->Open() == MP4_SUCCESS*/)
{
decoder = dec;
serviceFactory = sf;
return true;
}
sf->releaseInterface(dec);
}
/*
if (!strcmp(media_data_name, "mp4v"))
{
// TODO: write a way to get the binary data out of an atom
uint8_t *buffer;
uint32_t buffer_size = 0;
MP4GetTrackESConfiguration(file, track, &buffer, &buffer_size);
decoder = new MPEG4VideoDecoder(0,0);//buffer, buffer_size);
return true;
}
*/
return false;
}

View file

@ -0,0 +1 @@
#include "mpeg4audio.h"

View file

@ -0,0 +1,208 @@
#ifndef NULLSOFT_MPEG4AUDIOH
#define NULLSOFT_MPEG4AUDIOH
#include "../external_dependencies/libmp4v2/mp4.h"
#include <bfc/dispatch.h>
#include <bfc/platform/types.h>
#include <api/service/services.h>
enum
{
MP4_SUCCESS = 0,
MP4_FAILURE = 1,
MP4_GETOUTPUTPROPERTIES_NEED_MORE_INPUT = 2,
MP4_NEED_MORE_INPUT = 2,
MP4_GETCURRENTBITRATE_UNKNOWN = 2, // unable to calculate (e.g. VBR for CT's decoder)
MP4_OUTPUTFRAMESIZE_VARIABLE = 2, // don't know if any codecs do this
MP4_TYPE_MPEG1_AUDIO = 0x6B,
MP4_TYPE_MPEG2_AUDIO = 0x69,
MP4_TYPE_MPEG2_AAC_MAIN_AUDIO = 0x66,
MP4_TYPE_MPEG2_AAC_LC_AUDIO = 0x67,
MP4_TYPE_MPEG2_AAC_SSR_AUDIO = 0x68,
MP4_TYPE_MPEG4_AUDIO = 0x40,
MP4_TYPE_PRIVATE_AUDIO = 0xC0,
MP4_TYPE_PCM16_LITTLE_ENDIAN_AUDIO = 0xE0,
MP4_TYPE_VORBIS_AUDIO = 0xE1,
MP4_TYPE_AC3_AUDIO = 0xE2,
MP4_TYPE_ALAW_AUDIO = 0xE3,
MP4_TYPE_ULAW_AUDIO = 0xE4,
MP4_TYPE_G723_AUDIO = 0xE5,
MP4_TYPE_PCM16_BIG_ENDIAN_AUDIO = 0xE6,
/* MP4 MPEG-4 Audio types from 14496-3 Table 1.5.1 */
MP4_MPEG4_TYPE_AAC_MAIN_AUDIO = 1,
MP4_MPEG4_TYPE_AAC_LC_AUDIO = 2,
MP4_MPEG4_TYPE_AAC_SSR_AUDIO = 3,
MP4_MPEG4_TYPE_AAC_LTP_AUDIO = 4,
MP4_MPEG4_TYPE_AAC_HE_AUDIO = 5,
MP4_MPEG4_TYPE_AAC_SCALABLE_AUDIO = 6,
MP4_MPEG4_TYPE_CELP_AUDIO = 8,
MP4_MPEG4_TYPE_HVXC_AUDIO = 9,
MP4_MPEG4_TYPE_TTSI_AUDIO = 12,
MP4_MPEG4_TYPE_MAIN_SYNTHETIC_AUDIO = 13,
MP4_MPEG4_TYPE_WAVETABLE_AUDIO = 14,
MP4_MPEG4_TYPE_MIDI_AUDIO = 15,
MP4_MPEG4_TYPE_ALGORITHMIC_FX_AUDIO = 16,
MP4_MPEG4_TYPE_PARAMETRIC_STEREO=29,
MP4_MPEG4_ALS_AUDIO=31,
MP4_MPEG4_LAYER1_AUDIO = 32,
MP4_MPEG4_LAYER2_AUDIO= 33,
MP4_MPEG4_LAYER3_AUDIO= 34,
MP4_MPEG4_SLS_AUDIO=37,
};
class MP4AudioDecoder : public Dispatchable
{
protected:
MP4AudioDecoder() {}
~MP4AudioDecoder() {}
public:
static FOURCC getServiceType() { return WaSvc::MP4AUDIODECODER; }
int Open();
int OpenEx(size_t bits, size_t maxChannels, bool useFloat);
int OpenMP4(MP4FileHandle mp4_file, MP4TrackId mp4_track, size_t output_bits, size_t maxChannels, bool useFloat);
int AudioSpecificConfiguration(void *buffer, size_t buffer_size); // reads ASC block from mp4 file
int GetCurrentBitrate(unsigned int *bitrate);
int OutputFrameSize(size_t *frameSize); // in Frames
int GetOutputProperties(unsigned int *sampleRate, unsigned int *channels, unsigned int *bitsPerSample); // can return an error code for "havn't locked to stream yet"
int GetOutputPropertiesEx(unsigned int *sampleRate, unsigned int *channels, unsigned int *bitsPerSample, bool *isFloat);
int DecodeSample(void *inputBuffer, size_t inputBufferBytes, void *outputBuffer, size_t *outputBufferBytes);
void Flush();
void Close();
const char *GetCodecInfoString();
int CanHandleCodec(const char *codecName);
int CanHandleType(uint8_t type);
int CanHandleMPEG4Type(uint8_t type);
int SetGain(float gain);
int RequireChunks(); // return 1 if your decoder wants to read whole chunks rather than samples
void EndOfStream();
public:
DISPATCH_CODES
{
MPEG4_AUDIO_OPEN = 10,
MPEG4_AUDIO_OPEN_EX = 11,
MPEG4_AUDIO_OPENMP4 = 12,
MPEG4_AUDIO_ASC = 20,
MPEG4_AUDIO_BITRATE = 30,
MPEG4_AUDIO_FRAMESIZE = 40,
MPEG4_AUDIO_OUTPUTINFO = 50,
MPEG4_AUDIO_OUTPUTINFO_EX = 51,
MPEG4_AUDIO_DECODE = 60,
MPEG4_AUDIO_FLUSH = 70,
MPEG4_AUDIO_CLOSE = 80,
MPEG4_AUDIO_CODEC_INFO_STRING = 90,
MPEG4_AUDIO_HANDLES_CODEC = 100,
MPEG4_AUDIO_HANDLES_TYPE = 110,
MPEG4_AUDIO_HANDLES_MPEG4_TYPE = 120,
MPEG4_AUDIO_SET_GAIN=130,
MPEG4_AUDIO_REQUIRE_CHUNKS = 140,
MPEG4_END_OF_STREAM = 150,
};
};
inline int MP4AudioDecoder::Open()
{
return _call(MPEG4_AUDIO_OPEN, (int)MP4_FAILURE);
}
inline int MP4AudioDecoder::OpenEx(size_t bits, size_t maxChannels, bool useFloat)
{
void *params[3] = { &bits, &maxChannels, &useFloat};
int retval;
if (_dispatch(MPEG4_AUDIO_OPEN_EX, &retval, params, 3))
return retval;
else
return Open();
}
inline int MP4AudioDecoder::OpenMP4(MP4FileHandle mp4_file, MP4TrackId mp4_track, size_t output_bits, size_t maxChannels, bool useFloat)
{
void *params[5] = { &mp4_file, &mp4_track, &output_bits, &maxChannels, &useFloat};
int retval;
if (_dispatch(MPEG4_AUDIO_OPENMP4, &retval, params, 5))
return retval;
else
return OpenEx(output_bits, maxChannels, useFloat);
}
inline int MP4AudioDecoder::AudioSpecificConfiguration(void *buffer, size_t buffer_size)
{
return _call(MPEG4_AUDIO_ASC, (int)MP4_FAILURE, buffer, buffer_size);
}
inline int MP4AudioDecoder::GetCurrentBitrate(unsigned int *bitrate)
{
return _call(MPEG4_AUDIO_BITRATE, (int)MP4_FAILURE, bitrate);
}
inline int MP4AudioDecoder::OutputFrameSize(size_t *frameSize)
{
return _call(MPEG4_AUDIO_FRAMESIZE, (int)MP4_FAILURE, frameSize);
}
inline int MP4AudioDecoder::GetOutputProperties(unsigned int *sampleRate, unsigned int *channels, unsigned int *bitsPerSample)
{
return _call(MPEG4_AUDIO_OUTPUTINFO, (int)MP4_FAILURE, sampleRate, channels, bitsPerSample);
}
inline int MP4AudioDecoder::GetOutputPropertiesEx(unsigned int *sampleRate, unsigned int *channels, unsigned int *bitsPerSample, bool *useFloat)
{
void *params[4] = { &sampleRate, &channels, &bitsPerSample, &useFloat};
int retval;
if (_dispatch(MPEG4_AUDIO_OUTPUTINFO_EX, &retval, params, 4))
return retval;
else
{
*useFloat=false;
return GetOutputProperties(sampleRate, channels, bitsPerSample);
}
// return _call(MPEG4_AUDIO_OUTPUTINFO_EX, (int)MP4_FAILURE, sampleRate, channels, bitsPerSample);
}
inline int MP4AudioDecoder::DecodeSample(void *inputBuffer, size_t inputBufferBytes, void *outputBuffer, size_t *outputBufferBytes)
{
return _call(MPEG4_AUDIO_DECODE, (int)MP4_FAILURE, inputBuffer, inputBufferBytes, outputBuffer, outputBufferBytes);
}
inline void MP4AudioDecoder::Flush()
{
_voidcall(MPEG4_AUDIO_FLUSH);
}
inline void MP4AudioDecoder::Close()
{
_voidcall(MPEG4_AUDIO_CLOSE);
}
inline const char *MP4AudioDecoder::GetCodecInfoString()
{
return _call(MPEG4_AUDIO_CODEC_INFO_STRING, (const char *)0);
}
inline int MP4AudioDecoder::CanHandleCodec(const char *codecName)
{
return _call(MPEG4_AUDIO_HANDLES_CODEC, (int)0, codecName);
}
inline int MP4AudioDecoder::CanHandleType(uint8_t type)
{
return _call(MPEG4_AUDIO_HANDLES_TYPE, (int)0, type);
}
inline int MP4AudioDecoder::CanHandleMPEG4Type(uint8_t type)
{
return _call(MPEG4_AUDIO_HANDLES_MPEG4_TYPE, (int)0, type);
}
inline int MP4AudioDecoder::SetGain(float gain)
{
return _call(MPEG4_AUDIO_SET_GAIN, (int)MP4_FAILURE, gain);
}
inline int MP4AudioDecoder::RequireChunks()
{
return _call(MPEG4_AUDIO_REQUIRE_CHUNKS, (int)0);
}
inline void MP4AudioDecoder::EndOfStream()
{
_voidcall(MPEG4_END_OF_STREAM);
}
#endif

View file

@ -0,0 +1 @@
#include "mpeg4video.h"

View file

@ -0,0 +1,92 @@
#ifndef NULLSOFT_MPEG4VIDEO_H
#define NULLSOFT_MPEG4VIDEO_H
#include "../external_dependencies/libmp4v2/mp4.h"
#include <bfc/dispatch.h>
#include <api/service/services.h>
enum
{
MP4_VIDEO_SUCCESS = 0,
MP4_VIDEO_FAILURE = 1,
MP4_VIDEO_OUTPUT_FORMAT_CHANGED = -1, // succeeded, but call GetOutputFormat again!
MP4_VIDEO_AGAIN = -2,
};
class MP4VideoDecoder : public Dispatchable
{
protected:
MP4VideoDecoder() {}
~MP4VideoDecoder() {}
public:
static FOURCC getServiceType() { return WaSvc::MP4VIDEODECODER; }
int Open(MP4FileHandle mp4_file, MP4TrackId mp4_track);
int GetOutputFormat(int *x, int *y, int *color_format, double *aspect_ratio);
int DecodeSample(const void *inputBuffer, size_t inputBufferBytes, MP4Timestamp timestamp);
void Flush();
void Close();
int CanHandleCodec(const char *codecName); // return 0 for no, anything else for yes
int GetPicture(void **data, void **decoder_data, MP4Timestamp *timestamp);
void FreePicture(void *data, void *decoder_data);
void HurryUp(int state);
DISPATCH_CODES
{
MPEG4_VIDEO_OPEN = 11,
MPEG4_VIDEO_GETOUTPUTFORMAT = 21,
MPEG4_VIDEO_DECODE = 31,
MPEG4_VIDEO_FLUSH = 40,
MPEG4_VIDEO_CLOSE = 50,
MPEG4_VIDEO_HANDLES_CODEC = 60,
MPEG4_VIDEO_GET_PICTURE = 70,
MPEG4_VIDEO_FREE_PICTURE = 80,
MPEG4_VIDEO_HURRY_UP = 90,
};
};
inline int MP4VideoDecoder::Open(MP4FileHandle mp4_file, MP4TrackId mp4_track)
{
return _call(MPEG4_VIDEO_OPEN, (int)MP4_VIDEO_FAILURE, mp4_file, mp4_track);
}
inline int MP4VideoDecoder::GetOutputFormat(int *x, int *y, int *color_format, double *aspect_ratio)
{
return _call(MPEG4_VIDEO_GETOUTPUTFORMAT, (int)MP4_VIDEO_FAILURE, x, y, color_format, aspect_ratio);
}
inline int MP4VideoDecoder::DecodeSample(const void *inputBuffer, size_t inputBufferBytes, MP4Timestamp timestamp)
{
return _call(MPEG4_VIDEO_DECODE, (int)MP4_VIDEO_FAILURE, inputBuffer, inputBufferBytes, timestamp);
}
inline void MP4VideoDecoder::Flush()
{
_voidcall(MPEG4_VIDEO_FLUSH);
}
inline void MP4VideoDecoder::Close()
{
_voidcall(MPEG4_VIDEO_CLOSE);
}
inline int MP4VideoDecoder::CanHandleCodec(const char *codecName)
{
return _call(MPEG4_VIDEO_HANDLES_CODEC, (int)0, codecName);
}
inline int MP4VideoDecoder::GetPicture(void **data, void **decoder_data, MP4Timestamp *timestamp)
{
return _call(MPEG4_VIDEO_GET_PICTURE, (int)MP4_VIDEO_FAILURE, data, decoder_data, timestamp);
}
inline void MP4VideoDecoder::FreePicture(void *data, void *decoder_data)
{
_voidcall(MPEG4_VIDEO_FREE_PICTURE, data, decoder_data);
}
inline void MP4VideoDecoder::HurryUp(int state)
{
_voidcall(MPEG4_VIDEO_HURRY_UP, state);
}
#endif

View file

@ -0,0 +1,61 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by in_mp4.rc
//
#define IDS_NULLSOFT_MPEG4_AUDIO_DECODER_OLD 0
#define IDS_CANNOT_UPDATE_THE_FILE 1
#define IDS_ERROR 2
#define IDS_ABOUT_STRING 3
#define IDS_ABOUT_TITLE 4
#define IDS_STRING5 5
#define IDS_MP4_FILE 5
#define IDS_FAMILY_STRING_M4A 6
#define IDS_FAMILY_STRING_MPEG4 7
#define IDS_FAMILY_STRING_M4V 8
#define IDS_FAMILY_STRING_QUICKTIME 9
#define IDS_FAMILY_STRING_3GPP 10
#define IDS_FAMILY_STRING_FLV 11
#define IDS_ABOUT_TEXT 12
#define IDS_AUDIO_INFO 13
#define IDS_VIDEO_INFO 14
#define IDD_CONFIG 102
#define IDC_EDIT1 1000
#define IDC_CHECK2 1002
#define IDC_DEINT 1002
#define IDC_SLIDER1 1003
#define IDC_DVDCACHE 1005
#define IDC_CACHE 1006
#define IDD_NAME 1007
#define IDC_NAME 1008
#define IDC_ARTIST 1009
#define IDC_COMPOSER 1010
#define IDC_ALBUM 1011
#define IDC_COMMENTS 1012
#define IDC_GENRE 1013
#define IDC_YEAR 1014
#define IDC_TRACK1 1015
#define IDC_TRACK2 1016
#define IDC_DISC1 1017
#define IDC_DISC2 1018
#define IDC_ALBUM_ARTIST 1019
#define IDC_INFOTEXT 1023
#define IDC_COMPILATION 1024
#define IDC_BPM 1026
#define IDC_EDIT2 1027
#define IDC_TRACK_GAIN 1027
#define IDC_ALBUM_GAIN 1028
#define IDC_EXTENSIONLIST 1028
#define IDC_BUTTON1 1029
#define IDC_DEFAULT 1029
#define IDS_NULLSOFT_MPEG4_AUDIO_DECODER 65534
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 15
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1030
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View file

@ -0,0 +1,39 @@
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
#include "../../../Winamp/buildType.h"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,70,0,0
PRODUCTVERSION WINAMP_PRODUCTVER
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Winamp SA"
VALUE "FileDescription", "Winamp Input Plug-in"
VALUE "FileVersion", "2,70,0,0"
VALUE "InternalName", "Nullsoft MP4 Demuxer"
VALUE "LegalCopyright", "Copyright © 2005-2023 Winamp SA"
VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
VALUE "OriginalFilename", "in_mp4.dll"
VALUE "ProductName", "Winamp"
VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END