diff options
Diffstat (limited to 'Src/Plugins/Input/in_mp4/ExtendedRead.cpp')
-rw-r--r-- | Src/Plugins/Input/in_mp4/ExtendedRead.cpp | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_mp4/ExtendedRead.cpp b/Src/Plugins/Input/in_mp4/ExtendedRead.cpp new file mode 100644 index 00000000..137c8a81 --- /dev/null +++ b/Src/Plugins/Input/in_mp4/ExtendedRead.cpp @@ -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; + } +} |