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:
parent
46c259b90e
commit
0238561156
3 changed files with 82 additions and 45 deletions
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue