Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
232
Src/Plugins/Input/in_mp4/AlbumArt.cpp
Normal file
232
Src/Plugins/Input/in_mp4/AlbumArt.cpp
Normal 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;
|
39
Src/Plugins/Input/in_mp4/AlbumArt.h
Normal file
39
Src/Plugins/Input/in_mp4/AlbumArt.h
Normal 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
|
80
Src/Plugins/Input/in_mp4/AudioSample.h
Normal file
80
Src/Plugins/Input/in_mp4/AudioSample.h
Normal 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
|
1022
Src/Plugins/Input/in_mp4/ExtendedInfo.cpp
Normal file
1022
Src/Plugins/Input/in_mp4/ExtendedInfo.cpp
Normal file
File diff suppressed because it is too large
Load diff
239
Src/Plugins/Input/in_mp4/ExtendedRead.cpp
Normal file
239
Src/Plugins/Input/in_mp4/ExtendedRead.cpp
Normal 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;
|
||||
}
|
||||
}
|
723
Src/Plugins/Input/in_mp4/Main.cpp
Normal file
723
Src/Plugins/Input/in_mp4/Main.cpp
Normal 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;
|
||||
}
|
||||
}
|
76
Src/Plugins/Input/in_mp4/Main.h
Normal file
76
Src/Plugins/Input/in_mp4/Main.h
Normal 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
|
417
Src/Plugins/Input/in_mp4/PlayThread.cpp
Normal file
417
Src/Plugins/Input/in_mp4/PlayThread.cpp
Normal 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;
|
||||
}
|
145
Src/Plugins/Input/in_mp4/RawMediaReader.cpp
Normal file
145
Src/Plugins/Input/in_mp4/RawMediaReader.cpp
Normal 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
|
40
Src/Plugins/Input/in_mp4/RawMediaReader.h
Normal file
40
Src/Plugins/Input/in_mp4/RawMediaReader.h
Normal 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();
|
||||
};
|
47
Src/Plugins/Input/in_mp4/Stopper.cpp
Normal file
47
Src/Plugins/Input/in_mp4/Stopper.cpp
Normal 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;
|
||||
}
|
11
Src/Plugins/Input/in_mp4/Stopper.h
Normal file
11
Src/Plugins/Input/in_mp4/Stopper.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
class Stopper
|
||||
{
|
||||
public:
|
||||
Stopper();
|
||||
void ChangeTracking(bool);
|
||||
void Stop();
|
||||
void Play();
|
||||
int isplaying, timems;
|
||||
};
|
||||
|
215
Src/Plugins/Input/in_mp4/VideoThread.cpp
Normal file
215
Src/Plugins/Input/in_mp4/VideoThread.cpp
Normal 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, ×tamp))
|
||||
{
|
||||
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;
|
||||
}
|
10
Src/Plugins/Input/in_mp4/VideoThread.h
Normal file
10
Src/Plugins/Input/in_mp4/VideoThread.h
Normal 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
|
716
Src/Plugins/Input/in_mp4/VirtualIO.cpp
Normal file
716
Src/Plugins/Input/in_mp4/VirtualIO.cpp
Normal 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;
|
||||
}
|
17
Src/Plugins/Input/in_mp4/VirtualIO.h
Normal file
17
Src/Plugins/Input/in_mp4/VirtualIO.h
Normal 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
|
4
Src/Plugins/Input/in_mp4/api.cpp
Normal file
4
Src/Plugins/Input/in_mp4/api.cpp
Normal file
|
@ -0,0 +1,4 @@
|
|||
#include "api__in_mp4.h"
|
||||
|
||||
api_downloadManager *WAC_API_DOWNLOADMANAGER =0;
|
||||
api_threadpool *WASABI_API_THREADPOOL=0;
|
22
Src/Plugins/Input/in_mp4/api__in_mp4.h
Normal file
22
Src/Plugins/Input/in_mp4/api__in_mp4.h
Normal 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
|
49
Src/Plugins/Input/in_mp4/config.cpp
Normal file
49
Src/Plugins/Input/in_mp4/config.cpp
Normal 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);
|
||||
}
|
129
Src/Plugins/Input/in_mp4/in_mp4.rc
Normal file
129
Src/Plugins/Input/in_mp4/in_mp4.rc
Normal 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
|
||||
|
67
Src/Plugins/Input/in_mp4/in_mp4.sln
Normal file
67
Src/Plugins/Input/in_mp4/in_mp4.sln
Normal 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
|
301
Src/Plugins/Input/in_mp4/in_mp4.vcxproj
Normal file
301
Src/Plugins/Input/in_mp4/in_mp4.vcxproj
Normal 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>
|
122
Src/Plugins/Input/in_mp4/in_mp4.vcxproj.filters
Normal file
122
Src/Plugins/Input/in_mp4/in_mp4.vcxproj.filters
Normal 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>
|
20
Src/Plugins/Input/in_mp4/infobox.cpp
Normal file
20
Src/Plugins/Input/in_mp4/infobox.cpp
Normal 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 :)
|
||||
};
|
98
Src/Plugins/Input/in_mp4/mp4.cpp
Normal file
98
Src/Plugins/Input/in_mp4/mp4.cpp
Normal 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;
|
||||
}
|
1
Src/Plugins/Input/in_mp4/mpeg4audio.cpp
Normal file
1
Src/Plugins/Input/in_mp4/mpeg4audio.cpp
Normal file
|
@ -0,0 +1 @@
|
|||
#include "mpeg4audio.h"
|
208
Src/Plugins/Input/in_mp4/mpeg4audio.h
Normal file
208
Src/Plugins/Input/in_mp4/mpeg4audio.h
Normal 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
|
1
Src/Plugins/Input/in_mp4/mpeg4video.cpp
Normal file
1
Src/Plugins/Input/in_mp4/mpeg4video.cpp
Normal file
|
@ -0,0 +1 @@
|
|||
#include "mpeg4video.h"
|
92
Src/Plugins/Input/in_mp4/mpeg4video.h
Normal file
92
Src/Plugins/Input/in_mp4/mpeg4video.h
Normal 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
|
61
Src/Plugins/Input/in_mp4/resource.h
Normal file
61
Src/Plugins/Input/in_mp4/resource.h
Normal 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
|
39
Src/Plugins/Input/in_mp4/version.rc2
Normal file
39
Src/Plugins/Input/in_mp4/version.rc2
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue