Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
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;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue