1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

AndroidDocument: Make input stream more robust

Previously, input streams created by AndroidDocument instances did not
implement setPosition, so they were not useful for reading some file
formats, such as WAV.

Due to limitations of the Java InputStream interface, seeking backwards
in a stream requires creating a whole new stream and seeking from the
beginning, so it could be quite slow.
This commit is contained in:
reuk 2022-07-18 19:04:06 +01:00
parent 46c259b90e
commit 0238561156
No known key found for this signature in database
GPG key ID: FCB43929F012EE5C
3 changed files with 82 additions and 45 deletions

View file

@ -417,14 +417,18 @@ struct AndroidDocument::Utils
return false;
}
std::unique_ptr<InputStream> createInputStream() const override
std::unique_ptr<InputStream> createInputStream() const override
{
return makeStream<AndroidContentUriInputStream> (AndroidStreamHelpers::StreamKind::input);
auto result = std::make_unique<AndroidContentUriInputStream> (uri);
return result->openedSuccessfully() ? std::move (result) : nullptr;
}
std::unique_ptr<OutputStream> createOutputStream() const override
{
return makeStream<AndroidContentUriOutputStream> (AndroidStreamHelpers::StreamKind::output);
auto stream = AndroidStreamHelpers::createStream (uri, AndroidStreamHelpers::StreamKind::output);
return stream.get() != nullptr ? std::make_unique<AndroidContentUriOutputStream> (std::move (stream))
: nullptr;
}
AndroidDocumentInfo getInfo() const override
@ -507,15 +511,6 @@ struct AndroidDocument::Utils
NativeInfo getNativeInfo() const override { return { uri }; }
private:
template <typename Stream>
std::unique_ptr<Stream> makeStream (AndroidStreamHelpers::StreamKind kind) const
{
auto stream = AndroidStreamHelpers::createStream (uri, kind);
return stream.get() != nullptr ? std::make_unique<Stream> (std::move (stream))
: nullptr;
}
GlobalRef uri;
};

View file

@ -79,7 +79,8 @@ DECLARE_JNI_CLASS (AndroidOutputStream, "java/io/OutputStream")
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (close, "close", "()V") \
METHOD (read, "read", "([B)I")
METHOD (read, "read", "([B)I") \
METHOD (skip, "skip", "(J)J")
DECLARE_JNI_CLASS (AndroidInputStream, "java/io/InputStream")
#undef JNI_CLASS_MEMBERS
@ -550,15 +551,38 @@ private:
jsize size = 0;
};
//==============================================================================
struct AndroidStreamHelpers
{
enum class StreamKind { output, input };
static LocalRef<jobject> createStream (const GlobalRef& uri, StreamKind kind)
{
auto* env = getEnv();
auto contentResolver = AndroidContentUriResolver::getContentResolver();
if (contentResolver == nullptr)
return {};
return LocalRef<jobject> (env->CallObjectMethod (contentResolver.get(),
kind == StreamKind::input ? ContentResolver.openInputStream
: ContentResolver.openOutputStream,
uri.get()));
}
};
//==============================================================================
struct AndroidContentUriInputStream : public InputStream
{
explicit AndroidContentUriInputStream (LocalRef<jobject>&& streamIn)
: stream (std::move (streamIn)) {}
explicit AndroidContentUriInputStream (const GlobalRef& uriIn)
: uri (uriIn),
stream (AndroidStreamHelpers::createStream (uri, AndroidStreamHelpers::StreamKind::input))
{}
~AndroidContentUriInputStream() override
{
getEnv()->CallVoidMethod (stream.get(), AndroidInputStream.close);
jniCheckHasExceptionOccurredAndClear();
}
int64 getTotalLength() override { return -1; }
@ -569,30 +593,36 @@ struct AndroidContentUriInputStream : public InputStream
{
auto* env = getEnv();
if ((jsize) maxBytesToRead > byteArray.getSize())
if ((jsize) maxBytesToRead != byteArray.getSize())
byteArray = CachedByteArray { (jsize) maxBytesToRead };
const auto result = env->CallIntMethod (stream.get(), AndroidInputStream.read, byteArray.getNativeArray());
if (result != -1)
{
pos += result;
auto* rawBytes = env->GetByteArrayElements (byteArray.getNativeArray(), nullptr);
std::memcpy (destBuffer, rawBytes, static_cast<size_t> (result));
env->ReleaseByteArrayElements (byteArray.getNativeArray(), rawBytes, 0);
}
else
if (jniCheckHasExceptionOccurredAndClear() || result == -1)
{
exhausted = true;
return -1;
}
pos += result;
auto* rawBytes = env->GetByteArrayElements (byteArray.getNativeArray(), nullptr);
std::memcpy (destBuffer, rawBytes, static_cast<size_t> (result));
env->ReleaseByteArrayElements (byteArray.getNativeArray(), rawBytes, 0);
return result;
}
bool setPosition (int64 newPos) override
{
return (newPos == pos);
if (newPos == pos)
return true;
if (pos < newPos)
return skipImpl (newPos - pos);
AndroidContentUriInputStream (uri).swap (*this);
return skipImpl (newPos);
}
int64 getPosition() override
@ -600,6 +630,37 @@ struct AndroidContentUriInputStream : public InputStream
return pos;
}
bool openedSuccessfully() const { return stream != nullptr; }
void skipNextBytes (int64 num) override
{
skipImpl (num);
}
private:
bool skipImpl (int64 num)
{
if (stream == nullptr)
return false;
const auto skipped = getEnv()->CallLongMethod (stream, AndroidInputStream.skip, (jlong) num);
if (jniCheckHasExceptionOccurredAndClear())
return false;
pos += skipped;
return skipped == num;
}
auto tie() { return std::tie (uri, byteArray, stream, pos, exhausted); }
void swap (AndroidContentUriInputStream& other) noexcept
{
auto toSwap = other.tie();
tie().swap (toSwap);
}
GlobalRef uri;
CachedByteArray byteArray;
GlobalRef stream;
int64 pos = 0;

View file

@ -312,25 +312,6 @@ String URL::getFileName() const
return toString (false).fromLastOccurrenceOf ("/", false, true);
}
struct AndroidStreamHelpers
{
enum class StreamKind { output, input };
static LocalRef<jobject> createStream (const GlobalRef& uri, StreamKind kind)
{
auto* env = getEnv();
auto contentResolver = AndroidContentUriResolver::getContentResolver();
if (contentResolver == nullptr)
return {};
return LocalRef<jobject> (env->CallObjectMethod (contentResolver.get(),
kind == StreamKind::input ? ContentResolver.openInputStream
: ContentResolver.openOutputStream,
uri.get()));
}
};
//==============================================================================
class WebInputStream::Pimpl
{