aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_mp4/ExtendedRead.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Input/in_mp4/ExtendedRead.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Plugins/Input/in_mp4/ExtendedRead.cpp')
-rw-r--r--Src/Plugins/Input/in_mp4/ExtendedRead.cpp239
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;
+ }
+}