android_media_MediaExtractor.cpp revision 2b9d6bd092ef1d0cc142bf16f671648d1c84c307
1/* 2 * Copyright 2012, 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//#define LOG_NDEBUG 0 18#define LOG_TAG "MediaExtractor-JNI" 19#include <utils/Log.h> 20 21#include "android_media_MediaExtractor.h" 22 23#include "android_media_Utils.h" 24#include "android_runtime/AndroidRuntime.h" 25#include "jni.h" 26#include "JNIHelp.h" 27 28#include <media/stagefright/foundation/ABuffer.h> 29#include <media/stagefright/foundation/ADebug.h> 30#include <media/stagefright/foundation/AMessage.h> 31#include <media/stagefright/DataSource.h> 32#include <media/stagefright/MediaErrors.h> 33#include <media/stagefright/NuMediaExtractor.h> 34 35namespace android { 36 37struct fields_t { 38 jfieldID context; 39}; 40 41static fields_t gFields; 42 43//////////////////////////////////////////////////////////////////////////////// 44 45JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz) 46 : mClass(NULL), 47 mObject(NULL) { 48 jclass clazz = env->GetObjectClass(thiz); 49 CHECK(clazz != NULL); 50 51 mClass = (jclass)env->NewGlobalRef(clazz); 52 mObject = env->NewWeakGlobalRef(thiz); 53 54 mImpl = new NuMediaExtractor; 55} 56 57JMediaExtractor::~JMediaExtractor() { 58 JNIEnv *env = AndroidRuntime::getJNIEnv(); 59 60 env->DeleteWeakGlobalRef(mObject); 61 mObject = NULL; 62 env->DeleteGlobalRef(mClass); 63 mClass = NULL; 64} 65 66status_t JMediaExtractor::setDataSource( 67 const char *path, const KeyedVector<String8, String8> *headers) { 68 return mImpl->setDataSource(path, headers); 69} 70 71status_t JMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) { 72 return mImpl->setDataSource(fd, offset, size); 73} 74 75size_t JMediaExtractor::countTracks() const { 76 return mImpl->countTracks(); 77} 78 79status_t JMediaExtractor::getTrackFormat(size_t index, jobject *format) const { 80 sp<AMessage> msg; 81 status_t err; 82 if ((err = mImpl->getTrackFormat(index, &msg)) != OK) { 83 return err; 84 } 85 86 JNIEnv *env = AndroidRuntime::getJNIEnv(); 87 88 return ConvertMessageToMap(env, msg, format); 89} 90 91status_t JMediaExtractor::selectTrack(size_t index) { 92 return mImpl->selectTrack(index); 93} 94 95status_t JMediaExtractor::seekTo(int64_t timeUs) { 96 return mImpl->seekTo(timeUs); 97} 98 99status_t JMediaExtractor::advance() { 100 return mImpl->advance(); 101} 102 103status_t JMediaExtractor::readSampleData( 104 jobject byteBuf, size_t offset, size_t *sampleSize) { 105 JNIEnv *env = AndroidRuntime::getJNIEnv(); 106 107 void *dst = env->GetDirectBufferAddress(byteBuf); 108 109 jlong dstSize; 110 jbyteArray byteArray = NULL; 111 112 if (dst == NULL) { 113 jclass byteBufClass = env->FindClass("java/nio/ByteBuffer"); 114 CHECK(byteBufClass != NULL); 115 116 jmethodID arrayID = 117 env->GetMethodID(byteBufClass, "array", "()[B"); 118 CHECK(arrayID != NULL); 119 120 byteArray = 121 (jbyteArray)env->CallObjectMethod(byteBuf, arrayID); 122 123 if (byteArray == NULL) { 124 return INVALID_OPERATION; 125 } 126 127 jboolean isCopy; 128 dst = env->GetByteArrayElements(byteArray, &isCopy); 129 130 dstSize = env->GetArrayLength(byteArray); 131 } else { 132 dstSize = env->GetDirectBufferCapacity(byteBuf); 133 } 134 135 if (dstSize < offset) { 136 if (byteArray != NULL) { 137 env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0); 138 } 139 140 return -ERANGE; 141 } 142 143 sp<ABuffer> buffer = new ABuffer((char *)dst + offset, dstSize - offset); 144 145 status_t err = mImpl->readSampleData(buffer); 146 147 if (byteArray != NULL) { 148 env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0); 149 } 150 151 if (err != OK) { 152 return err; 153 } 154 155 *sampleSize = buffer->size(); 156 157 return OK; 158} 159 160status_t JMediaExtractor::getSampleTrackIndex(size_t *trackIndex) { 161 return mImpl->getSampleTrackIndex(trackIndex); 162} 163 164status_t JMediaExtractor::getSampleTime(int64_t *sampleTimeUs) { 165 return mImpl->getSampleTime(sampleTimeUs); 166} 167 168status_t JMediaExtractor::getSampleFlags(uint32_t *sampleFlags) { 169 return mImpl->getSampleFlags(sampleFlags); 170} 171 172} // namespace android 173 174//////////////////////////////////////////////////////////////////////////////// 175 176using namespace android; 177 178static sp<JMediaExtractor> setMediaExtractor( 179 JNIEnv *env, jobject thiz, const sp<JMediaExtractor> &extractor) { 180 sp<JMediaExtractor> old = 181 (JMediaExtractor *)env->GetIntField(thiz, gFields.context); 182 183 if (extractor != NULL) { 184 extractor->incStrong(thiz); 185 } 186 if (old != NULL) { 187 old->decStrong(thiz); 188 } 189 env->SetIntField(thiz, gFields.context, (int)extractor.get()); 190 191 return old; 192} 193 194static sp<JMediaExtractor> getMediaExtractor(JNIEnv *env, jobject thiz) { 195 return (JMediaExtractor *)env->GetIntField(thiz, gFields.context); 196} 197 198static void android_media_MediaExtractor_release(JNIEnv *env, jobject thiz) { 199 setMediaExtractor(env, thiz, NULL); 200} 201 202static jint android_media_MediaExtractor_countTracks( 203 JNIEnv *env, jobject thiz) { 204 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 205 206 if (extractor == NULL) { 207 jniThrowException(env, "java/lang/IllegalStateException", NULL); 208 return -1; 209 } 210 211 return extractor->countTracks(); 212} 213 214static jobject android_media_MediaExtractor_getTrackFormat( 215 JNIEnv *env, jobject thiz, jint index) { 216 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 217 218 if (extractor == NULL) { 219 jniThrowException(env, "java/lang/IllegalStateException", NULL); 220 return NULL; 221 } 222 223 jobject format; 224 status_t err = extractor->getTrackFormat(index, &format); 225 226 if (err != OK) { 227 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 228 return NULL; 229 } 230 231 return format; 232} 233 234static void android_media_MediaExtractor_selectTrack( 235 JNIEnv *env, jobject thiz, jint index) { 236 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 237 238 if (extractor == NULL) { 239 jniThrowException(env, "java/lang/IllegalStateException", NULL); 240 return; 241 } 242 243 status_t err = extractor->selectTrack(index); 244 245 if (err != OK) { 246 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 247 return; 248 } 249} 250 251static void android_media_MediaExtractor_seekTo( 252 JNIEnv *env, jobject thiz, jlong timeUs) { 253 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 254 255 if (extractor == NULL) { 256 jniThrowException(env, "java/lang/IllegalStateException", NULL); 257 return; 258 } 259 260 extractor->seekTo(timeUs); 261} 262 263static jboolean android_media_MediaExtractor_advance( 264 JNIEnv *env, jobject thiz) { 265 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 266 267 if (extractor == NULL) { 268 jniThrowException(env, "java/lang/IllegalStateException", NULL); 269 return false; 270 } 271 272 status_t err = extractor->advance(); 273 274 if (err == ERROR_END_OF_STREAM) { 275 return false; 276 } else if (err != OK) { 277 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 278 return false; 279 } 280 281 return true; 282} 283 284static jint android_media_MediaExtractor_readSampleData( 285 JNIEnv *env, jobject thiz, jobject byteBuf, jint offset) { 286 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 287 288 if (extractor == NULL) { 289 jniThrowException(env, "java/lang/IllegalStateException", NULL); 290 return -1; 291 } 292 293 size_t sampleSize; 294 status_t err = extractor->readSampleData(byteBuf, offset, &sampleSize); 295 296 if (err == ERROR_END_OF_STREAM) { 297 return -1; 298 } else if (err != OK) { 299 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 300 return false; 301 } 302 303 return sampleSize; 304} 305 306static jint android_media_MediaExtractor_getSampleTrackIndex( 307 JNIEnv *env, jobject thiz) { 308 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 309 310 if (extractor == NULL) { 311 jniThrowException(env, "java/lang/IllegalStateException", NULL); 312 return -1; 313 } 314 315 size_t trackIndex; 316 status_t err = extractor->getSampleTrackIndex(&trackIndex); 317 318 if (err == ERROR_END_OF_STREAM) { 319 return -1; 320 } else if (err != OK) { 321 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 322 return false; 323 } 324 325 return trackIndex; 326} 327 328static jlong android_media_MediaExtractor_getSampleTime( 329 JNIEnv *env, jobject thiz) { 330 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 331 332 if (extractor == NULL) { 333 jniThrowException(env, "java/lang/IllegalStateException", NULL); 334 return -1ll; 335 } 336 337 int64_t sampleTimeUs; 338 status_t err = extractor->getSampleTime(&sampleTimeUs); 339 340 if (err == ERROR_END_OF_STREAM) { 341 return -1ll; 342 } else if (err != OK) { 343 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 344 return false; 345 } 346 347 return sampleTimeUs; 348} 349 350static jint android_media_MediaExtractor_getSampleFlags( 351 JNIEnv *env, jobject thiz) { 352 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 353 354 if (extractor == NULL) { 355 jniThrowException(env, "java/lang/IllegalStateException", NULL); 356 return -1ll; 357 } 358 359 uint32_t sampleFlags; 360 status_t err = extractor->getSampleFlags(&sampleFlags); 361 362 if (err == ERROR_END_OF_STREAM) { 363 return -1ll; 364 } else if (err != OK) { 365 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 366 return false; 367 } 368 369 return sampleFlags; 370} 371 372static void android_media_MediaExtractor_native_init(JNIEnv *env) { 373 jclass clazz = env->FindClass("android/media/MediaExtractor"); 374 CHECK(clazz != NULL); 375 376 gFields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 377 CHECK(gFields.context != NULL); 378 379 DataSource::RegisterDefaultSniffers(); 380} 381 382static void android_media_MediaExtractor_native_setup( 383 JNIEnv *env, jobject thiz) { 384 sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz); 385 setMediaExtractor(env,thiz, extractor); 386} 387 388static void android_media_MediaExtractor_setDataSource( 389 JNIEnv *env, jobject thiz, 390 jstring pathObj, jobjectArray keysArray, jobjectArray valuesArray) { 391 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 392 393 if (extractor == NULL) { 394 jniThrowException(env, "java/lang/IllegalStateException", NULL); 395 return; 396 } 397 398 if (pathObj == NULL) { 399 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 400 return; 401 } 402 403 KeyedVector<String8, String8> headers; 404 if (!ConvertKeyValueArraysToKeyedVector( 405 env, keysArray, valuesArray, &headers)) { 406 return; 407 } 408 409 const char *path = env->GetStringUTFChars(pathObj, NULL); 410 411 if (path == NULL) { 412 return; 413 } 414 415 status_t err = extractor->setDataSource(path, &headers); 416 417 env->ReleaseStringUTFChars(pathObj, path); 418 path = NULL; 419 420 if (err != OK) { 421 jniThrowException( 422 env, 423 "java/io/IOException", 424 "Failed to instantiate extractor."); 425 return; 426 } 427} 428 429static void android_media_MediaExtractor_setDataSourceFd( 430 JNIEnv *env, jobject thiz, 431 jobject fileDescObj, jlong offset, jlong length) { 432 sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); 433 434 if (extractor == NULL) { 435 jniThrowException(env, "java/lang/IllegalStateException", NULL); 436 return; 437 } 438 439 if (fileDescObj == NULL) { 440 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 441 return; 442 } 443 444 int fd = jniGetFDFromFileDescriptor(env, fileDescObj); 445 446 status_t err = extractor->setDataSource(fd, offset, length); 447 448 if (err != OK) { 449 jniThrowException( 450 env, 451 "java/io/IOException", 452 "Failed to instantiate extractor."); 453 return; 454 } 455} 456 457static void android_media_MediaExtractor_native_finalize( 458 JNIEnv *env, jobject thiz) { 459 android_media_MediaExtractor_release(env, thiz); 460} 461 462static JNINativeMethod gMethods[] = { 463 { "release", "()V", (void *)android_media_MediaExtractor_release }, 464 465 { "countTracks", "()I", (void *)android_media_MediaExtractor_countTracks }, 466 467 { "getTrackFormat", "(I)Ljava/util/Map;", 468 (void *)android_media_MediaExtractor_getTrackFormat }, 469 470 { "selectTrack", "(I)V", (void *)android_media_MediaExtractor_selectTrack }, 471 472 { "seekTo", "(J)V", (void *)android_media_MediaExtractor_seekTo }, 473 474 { "advance", "()Z", (void *)android_media_MediaExtractor_advance }, 475 476 { "readSampleData", "(Ljava/nio/ByteBuffer;I)I", 477 (void *)android_media_MediaExtractor_readSampleData }, 478 479 { "getSampleTrackIndex", "()I", 480 (void *)android_media_MediaExtractor_getSampleTrackIndex }, 481 482 { "getSampleTime", "()J", 483 (void *)android_media_MediaExtractor_getSampleTime }, 484 485 { "getSampleFlags", "()I", 486 (void *)android_media_MediaExtractor_getSampleFlags }, 487 488 { "native_init", "()V", (void *)android_media_MediaExtractor_native_init }, 489 490 { "native_setup", "()V", 491 (void *)android_media_MediaExtractor_native_setup }, 492 493 { "native_finalize", "()V", 494 (void *)android_media_MediaExtractor_native_finalize }, 495 496 { "setDataSource", "(Ljava/lang/String;[Ljava/lang/String;" 497 "[Ljava/lang/String;)V", 498 (void *)android_media_MediaExtractor_setDataSource }, 499 500 { "setDataSource", "(Ljava/io/FileDescriptor;JJ)V", 501 (void *)android_media_MediaExtractor_setDataSourceFd }, 502}; 503 504int register_android_media_MediaExtractor(JNIEnv *env) { 505 return AndroidRuntime::registerNativeMethods(env, 506 "android/media/MediaExtractor", gMethods, NELEM(gMethods)); 507} 508