• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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       env->DeleteLocalRef(cls);
51     }
52   }
53 
readAt(off64_t offset,void * const data,size_t size)54   ssize_t readAt(off64_t offset, void *const data, size_t size) {
55     jobject byteBuffer = env->NewDirectByteBuffer(data, size);
56     int result = env->CallIntMethod(flacDecoderJni, mid, byteBuffer);
57     if (env->ExceptionCheck()) {
58       // Exception is thrown in Java when returning from the native call.
59       result = -1;
60     }
61     env->DeleteLocalRef(byteBuffer);
62     return result;
63   }
64 
65  private:
66   JNIEnv *env;
67   jobject flacDecoderJni;
68   jmethodID mid;
69 };
70 
71 struct Context {
72   JavaDataSource *source;
73   FLACParser *parser;
74 
ContextContext75   Context() {
76     source = new JavaDataSource();
77     parser = new FLACParser(source);
78   }
79 
~ContextContext80   ~Context() {
81     delete parser;
82     delete source;
83   }
84 };
85 
DECODER_FUNC(jlong,flacInit)86 DECODER_FUNC(jlong, flacInit) {
87   Context *context = new Context;
88   if (!context->parser->init()) {
89     delete context;
90     return 0;
91   }
92   return reinterpret_cast<intptr_t>(context);
93 }
94 
DECODER_FUNC(jobject,flacDecodeMetadata,jlong jContext)95 DECODER_FUNC(jobject, flacDecodeMetadata, jlong jContext) {
96   Context *context = reinterpret_cast<Context *>(jContext);
97   context->source->setFlacDecoderJni(env, thiz);
98   if (!context->parser->decodeMetadata()) {
99     return NULL;
100   }
101 
102   jclass arrayListClass = env->FindClass("java/util/ArrayList");
103   jmethodID arrayListConstructor =
104       env->GetMethodID(arrayListClass, "<init>", "()V");
105   jobject commentList = env->NewObject(arrayListClass, arrayListConstructor);
106   jmethodID arrayListAddMethod =
107       env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
108 
109   if (context->parser->areVorbisCommentsValid()) {
110     std::vector<std::string> vorbisComments =
111         context->parser->getVorbisComments();
112     for (std::vector<std::string>::const_iterator vorbisComment =
113              vorbisComments.begin();
114          vorbisComment != vorbisComments.end(); ++vorbisComment) {
115       jstring commentString = env->NewStringUTF((*vorbisComment).c_str());
116       env->CallBooleanMethod(commentList, arrayListAddMethod, commentString);
117       env->DeleteLocalRef(commentString);
118     }
119   }
120 
121   jobject pictureFrames = env->NewObject(arrayListClass, arrayListConstructor);
122   bool picturesValid = context->parser->arePicturesValid();
123   if (picturesValid) {
124     std::vector<FlacPicture> pictures = context->parser->getPictures();
125     jclass pictureFrameClass = env->FindClass(
126         "com/google/android/exoplayer2/metadata/flac/PictureFrame");
127     jmethodID pictureFrameConstructor =
128         env->GetMethodID(pictureFrameClass, "<init>",
129                          "(ILjava/lang/String;Ljava/lang/String;IIII[B)V");
130     for (std::vector<FlacPicture>::const_iterator picture = pictures.begin();
131          picture != pictures.end(); ++picture) {
132       jstring mimeType = env->NewStringUTF(picture->mimeType.c_str());
133       jstring description = env->NewStringUTF(picture->description.c_str());
134       jbyteArray pictureData = env->NewByteArray(picture->data.size());
135       env->SetByteArrayRegion(pictureData, 0, picture->data.size(),
136                               (signed char *)&picture->data[0]);
137       jobject pictureFrame = env->NewObject(
138           pictureFrameClass, pictureFrameConstructor, picture->type, mimeType,
139           description, picture->width, picture->height, picture->depth,
140           picture->colors, pictureData);
141       env->CallBooleanMethod(pictureFrames, arrayListAddMethod, pictureFrame);
142       env->DeleteLocalRef(mimeType);
143       env->DeleteLocalRef(description);
144       env->DeleteLocalRef(pictureData);
145     }
146   }
147 
148   const FLAC__StreamMetadata_StreamInfo &streamInfo =
149       context->parser->getStreamInfo();
150 
151   jclass flacStreamMetadataClass = env->FindClass(
152       "com/google/android/exoplayer2/extractor/"
153       "FlacStreamMetadata");
154   jmethodID flacStreamMetadataConstructor =
155       env->GetMethodID(flacStreamMetadataClass, "<init>",
156                        "(IIIIIIIJLjava/util/ArrayList;Ljava/util/ArrayList;)V");
157 
158   return env->NewObject(flacStreamMetadataClass, flacStreamMetadataConstructor,
159                         streamInfo.min_blocksize, streamInfo.max_blocksize,
160                         streamInfo.min_framesize, streamInfo.max_framesize,
161                         streamInfo.sample_rate, streamInfo.channels,
162                         streamInfo.bits_per_sample, streamInfo.total_samples,
163                         commentList, pictureFrames);
164 }
165 
DECODER_FUNC(jint,flacDecodeToBuffer,jlong jContext,jobject jOutputBuffer)166 DECODER_FUNC(jint, flacDecodeToBuffer, jlong jContext, jobject jOutputBuffer) {
167   Context *context = reinterpret_cast<Context *>(jContext);
168   context->source->setFlacDecoderJni(env, thiz);
169   void *outputBuffer = env->GetDirectBufferAddress(jOutputBuffer);
170   jint outputSize = env->GetDirectBufferCapacity(jOutputBuffer);
171   return context->parser->readBuffer(outputBuffer, outputSize);
172 }
173 
DECODER_FUNC(jint,flacDecodeToArray,jlong jContext,jbyteArray jOutputArray)174 DECODER_FUNC(jint, flacDecodeToArray, jlong jContext, jbyteArray jOutputArray) {
175   Context *context = reinterpret_cast<Context *>(jContext);
176   context->source->setFlacDecoderJni(env, thiz);
177   jbyte *outputBuffer = env->GetByteArrayElements(jOutputArray, NULL);
178   jint outputSize = env->GetArrayLength(jOutputArray);
179   int count = context->parser->readBuffer(outputBuffer, outputSize);
180   env->ReleaseByteArrayElements(jOutputArray, outputBuffer, 0);
181   return count;
182 }
183 
DECODER_FUNC(jlong,flacGetDecodePosition,jlong jContext)184 DECODER_FUNC(jlong, flacGetDecodePosition, jlong jContext) {
185   Context *context = reinterpret_cast<Context *>(jContext);
186   return context->parser->getDecodePosition();
187 }
188 
DECODER_FUNC(jlong,flacGetLastFrameTimestamp,jlong jContext)189 DECODER_FUNC(jlong, flacGetLastFrameTimestamp, jlong jContext) {
190   Context *context = reinterpret_cast<Context *>(jContext);
191   return context->parser->getLastFrameTimestamp();
192 }
193 
DECODER_FUNC(jlong,flacGetLastFrameFirstSampleIndex,jlong jContext)194 DECODER_FUNC(jlong, flacGetLastFrameFirstSampleIndex, jlong jContext) {
195   Context *context = reinterpret_cast<Context *>(jContext);
196   return context->parser->getLastFrameFirstSampleIndex();
197 }
198 
DECODER_FUNC(jlong,flacGetNextFrameFirstSampleIndex,jlong jContext)199 DECODER_FUNC(jlong, flacGetNextFrameFirstSampleIndex, jlong jContext) {
200   Context *context = reinterpret_cast<Context *>(jContext);
201   return context->parser->getNextFrameFirstSampleIndex();
202 }
203 
DECODER_FUNC(jboolean,flacGetSeekPoints,jlong jContext,jlong timeUs,jlongArray outSeekPoints)204 DECODER_FUNC(jboolean, flacGetSeekPoints, jlong jContext, jlong timeUs,
205              jlongArray outSeekPoints) {
206   Context *context = reinterpret_cast<Context *>(jContext);
207   std::array<int64_t, 4> result;
208   bool success = context->parser->getSeekPositions(timeUs, result);
209   if (success) {
210     env->SetLongArrayRegion(outSeekPoints, 0, result.size(), result.data());
211   }
212   return success;
213 }
214 
DECODER_FUNC(jstring,flacGetStateString,jlong jContext)215 DECODER_FUNC(jstring, flacGetStateString, jlong jContext) {
216   Context *context = reinterpret_cast<Context *>(jContext);
217   const char *str = context->parser->getDecoderStateString();
218   return env->NewStringUTF(str);
219 }
220 
DECODER_FUNC(jboolean,flacIsDecoderAtEndOfStream,jlong jContext)221 DECODER_FUNC(jboolean, flacIsDecoderAtEndOfStream, jlong jContext) {
222   Context *context = reinterpret_cast<Context *>(jContext);
223   return context->parser->isDecoderAtEndOfStream();
224 }
225 
DECODER_FUNC(void,flacFlush,jlong jContext)226 DECODER_FUNC(void, flacFlush, jlong jContext) {
227   Context *context = reinterpret_cast<Context *>(jContext);
228   context->parser->flush();
229 }
230 
DECODER_FUNC(void,flacReset,jlong jContext,jlong newPosition)231 DECODER_FUNC(void, flacReset, jlong jContext, jlong newPosition) {
232   Context *context = reinterpret_cast<Context *>(jContext);
233   context->parser->reset(newPosition);
234 }
235 
DECODER_FUNC(void,flacRelease,jlong jContext)236 DECODER_FUNC(void, flacRelease, jlong jContext) {
237   Context *context = reinterpret_cast<Context *>(jContext);
238   delete context;
239 }
240