#include #include "main.h" #include "mpeg4audio.h" #include "api__in_mp4.h" #include #include #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(nch), reinterpret_cast(bps), &verifyFloat); if (ret == MP4_SUCCESS && !verifyFloat) return false; } else { ret = audio->GetOutputProperties(&sample_rate, reinterpret_cast(nch), reinterpret_cast(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(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(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; } }