1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <android/log.h>
18 #include <jni.h>
19
20 #include <array>
21 #include <cstdlib>
22 #include <cstring>
23
24 #include "include/flac_parser.h"
25
26 #define LOG_TAG "flac_jni"
27 #define ALOGE(...) \
28 ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
29 #define ALOGV(...) \
30 ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
31
32 #define DECODER_FUNC(RETURN_TYPE, NAME, ...) \
33 extern "C" { \
34 JNIEXPORT RETURN_TYPE \
35 Java_com_google_android_exoplayer2_ext_flac_FlacDecoderJni_##NAME( \
36 JNIEnv *env, jobject thiz, ##__VA_ARGS__); \
37 } \
38 JNIEXPORT RETURN_TYPE \
39 Java_com_google_android_exoplayer2_ext_flac_FlacDecoderJni_##NAME( \
40 JNIEnv *env, jobject thiz, ##__VA_ARGS__)
41
42 class JavaDataSource : public DataSource {
43 public:
setFlacDecoderJni(JNIEnv * env,jobject flacDecoderJni)44 void setFlacDecoderJni(JNIEnv *env, jobject flacDecoderJni) {
45 this->env = env;
46 this->flacDecoderJni = flacDecoderJni;
47 if (mid == NULL) {
48 jclass cls = env->GetObjectClass(flacDecoderJni);
49 mid = env->GetMethodID(cls, "read", "(Ljava/nio/ByteBuffer;)I");
50 }
51 }
52
readAt(off64_t offset,void * const data,size_t size)53 ssize_t readAt(off64_t offset, void *const data, size_t size) {
54 jobject byteBuffer = env->NewDirectByteBuffer(data, size);
55 int result = env->CallIntMethod(flacDecoderJni, mid, byteBuffer);
56 if (env->ExceptionCheck()) {
57 // Exception is thrown in Java when returning from the native call.
58 result = -1;
59 }
60 return result;
61 }
62
63 private:
64 JNIEnv *env;
65 jobject flacDecoderJni;
66 jmethodID mid;
67 };
68
69 struct Context {
70 JavaDataSource *source;
71 FLACParser *parser;
72
ContextContext73 Context() {
74 source = new JavaDataSource();
75 parser = new FLACParser(source);
76 }
77
~ContextContext78 ~Context() {
79 delete parser;
80 delete source;
81 }
82 };
83
DECODER_FUNC(jlong,flacInit)84 DECODER_FUNC(jlong, flacInit) {
85 Context *context = new Context;
86 if (!context->parser->init()) {
87 delete context;
88 return 0;
89 }
90 return reinterpret_cast<intptr_t>(context);
91 }
92
DECODER_FUNC(jobject,flacDecodeMetadata,jlong jContext)93 DECODER_FUNC(jobject, flacDecodeMetadata, jlong jContext) {
94 Context *context = reinterpret_cast<Context *>(jContext);
95 context->source->setFlacDecoderJni(env, thiz);
96 if (!context->parser->decodeMetadata()) {
97 return NULL;
98 }
99
100 jclass arrayListClass = env->FindClass("java/util/ArrayList");
101 jmethodID arrayListConstructor =
102 env->GetMethodID(arrayListClass, "<init>", "()V");
103 jobject commentList = env->NewObject(arrayListClass, arrayListConstructor);
104 jmethodID arrayListAddMethod =
105 env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
106
107 if (context->parser->areVorbisCommentsValid()) {
108 std::vector<std::string> vorbisComments =
109 context->parser->getVorbisComments();
110 for (std::vector<std::string>::const_iterator vorbisComment =
111 vorbisComments.begin();
112 vorbisComment != vorbisComments.end(); ++vorbisComment) {
113 jstring commentString = env->NewStringUTF((*vorbisComment).c_str());
114 env->CallBooleanMethod(commentList, arrayListAddMethod, commentString);
115 env->DeleteLocalRef(commentString);
116 }
117 }
118
119 jobject pictureFrames = env->NewObject(arrayListClass, arrayListConstructor);
120 bool picturesValid = context->parser->arePicturesValid();
121 if (picturesValid) {
122 std::vector<FlacPicture> pictures = context->parser->getPictures();
123 jclass pictureFrameClass = env->FindClass(
124 "com/google/android/exoplayer2/metadata/flac/PictureFrame");
125 jmethodID pictureFrameConstructor =
126 env->GetMethodID(pictureFrameClass, "<init>",
127 "(ILjava/lang/String;Ljava/lang/String;IIII[B)V");
128 for (std::vector<FlacPicture>::const_iterator picture = pictures.begin();
129 picture != pictures.end(); ++picture) {
130 jstring mimeType = env->NewStringUTF(picture->mimeType.c_str());
131 jstring description = env->NewStringUTF(picture->description.c_str());
132 jbyteArray pictureData = env->NewByteArray(picture->data.size());
133 env->SetByteArrayRegion(pictureData, 0, picture->data.size(),
134 (signed char *)&picture->data[0]);
135 jobject pictureFrame = env->NewObject(
136 pictureFrameClass, pictureFrameConstructor, picture->type, mimeType,
137 description, picture->width, picture->height, picture->depth,
138 picture->colors, pictureData);
139 env->CallBooleanMethod(pictureFrames, arrayListAddMethod, pictureFrame);
140 env->DeleteLocalRef(mimeType);
141 env->DeleteLocalRef(description);
142 env->DeleteLocalRef(pictureData);
143 }
144 }
145
146 const FLAC__StreamMetadata_StreamInfo &streamInfo =
147 context->parser->getStreamInfo();
148
149 jclass flacStreamMetadataClass = env->FindClass(
150 "com/google/android/exoplayer2/extractor/"
151 "FlacStreamMetadata");
152 jmethodID flacStreamMetadataConstructor =
153 env->GetMethodID(flacStreamMetadataClass, "<init>",
154 "(IIIIIIIJLjava/util/ArrayList;Ljava/util/ArrayList;)V");
155
156 return env->NewObject(flacStreamMetadataClass, flacStreamMetadataConstructor,
157 streamInfo.min_blocksize, streamInfo.max_blocksize,
158 streamInfo.min_framesize, streamInfo.max_framesize,
159 streamInfo.sample_rate, streamInfo.channels,
160 streamInfo.bits_per_sample, streamInfo.total_samples,
161 commentList, pictureFrames);
162 }
163
DECODER_FUNC(jint,flacDecodeToBuffer,jlong jContext,jobject jOutputBuffer)164 DECODER_FUNC(jint, flacDecodeToBuffer, jlong jContext, jobject jOutputBuffer) {
165 Context *context = reinterpret_cast<Context *>(jContext);
166 context->source->setFlacDecoderJni(env, thiz);
167 void *outputBuffer = env->GetDirectBufferAddress(jOutputBuffer);
168 jint outputSize = env->GetDirectBufferCapacity(jOutputBuffer);
169 return context->parser->readBuffer(outputBuffer, outputSize);
170 }
171
DECODER_FUNC(jint,flacDecodeToArray,jlong jContext,jbyteArray jOutputArray)172 DECODER_FUNC(jint, flacDecodeToArray, jlong jContext, jbyteArray jOutputArray) {
173 Context *context = reinterpret_cast<Context *>(jContext);
174 context->source->setFlacDecoderJni(env, thiz);
175 jbyte *outputBuffer = env->GetByteArrayElements(jOutputArray, NULL);
176 jint outputSize = env->GetArrayLength(jOutputArray);
177 int count = context->parser->readBuffer(outputBuffer, outputSize);
178 env->ReleaseByteArrayElements(jOutputArray, outputBuffer, 0);
179 return count;
180 }
181
DECODER_FUNC(jlong,flacGetDecodePosition,jlong jContext)182 DECODER_FUNC(jlong, flacGetDecodePosition, jlong jContext) {
183 Context *context = reinterpret_cast<Context *>(jContext);
184 return context->parser->getDecodePosition();
185 }
186
DECODER_FUNC(jlong,flacGetLastFrameTimestamp,jlong jContext)187 DECODER_FUNC(jlong, flacGetLastFrameTimestamp, jlong jContext) {
188 Context *context = reinterpret_cast<Context *>(jContext);
189 return context->parser->getLastFrameTimestamp();
190 }
191
DECODER_FUNC(jlong,flacGetLastFrameFirstSampleIndex,jlong jContext)192 DECODER_FUNC(jlong, flacGetLastFrameFirstSampleIndex, jlong jContext) {
193 Context *context = reinterpret_cast<Context *>(jContext);
194 return context->parser->getLastFrameFirstSampleIndex();
195 }
196
DECODER_FUNC(jlong,flacGetNextFrameFirstSampleIndex,jlong jContext)197 DECODER_FUNC(jlong, flacGetNextFrameFirstSampleIndex, jlong jContext) {
198 Context *context = reinterpret_cast<Context *>(jContext);
199 return context->parser->getNextFrameFirstSampleIndex();
200 }
201
DECODER_FUNC(jboolean,flacGetSeekPoints,jlong jContext,jlong timeUs,jlongArray outSeekPoints)202 DECODER_FUNC(jboolean, flacGetSeekPoints, jlong jContext, jlong timeUs,
203 jlongArray outSeekPoints) {
204 Context *context = reinterpret_cast<Context *>(jContext);
205 std::array<int64_t, 4> result;
206 bool success = context->parser->getSeekPositions(timeUs, result);
207 if (success) {
208 env->SetLongArrayRegion(outSeekPoints, 0, result.size(), result.data());
209 }
210 return success;
211 }
212
DECODER_FUNC(jstring,flacGetStateString,jlong jContext)213 DECODER_FUNC(jstring, flacGetStateString, jlong jContext) {
214 Context *context = reinterpret_cast<Context *>(jContext);
215 const char *str = context->parser->getDecoderStateString();
216 return env->NewStringUTF(str);
217 }
218
DECODER_FUNC(jboolean,flacIsDecoderAtEndOfStream,jlong jContext)219 DECODER_FUNC(jboolean, flacIsDecoderAtEndOfStream, jlong jContext) {
220 Context *context = reinterpret_cast<Context *>(jContext);
221 return context->parser->isDecoderAtEndOfStream();
222 }
223
DECODER_FUNC(void,flacFlush,jlong jContext)224 DECODER_FUNC(void, flacFlush, jlong jContext) {
225 Context *context = reinterpret_cast<Context *>(jContext);
226 context->parser->flush();
227 }
228
DECODER_FUNC(void,flacReset,jlong jContext,jlong newPosition)229 DECODER_FUNC(void, flacReset, jlong jContext, jlong newPosition) {
230 Context *context = reinterpret_cast<Context *>(jContext);
231 context->parser->reset(newPosition);
232 }
233
DECODER_FUNC(void,flacRelease,jlong jContext)234 DECODER_FUNC(void, flacRelease, jlong jContext) {
235 Context *context = reinterpret_cast<Context *>(jContext);
236 delete context;
237 }
238