4eaa293808
This allows apps to implement MediaDataSource, which is modeled on stagefright's DataSource, to supply media data to the framework. This was already implemented for MediaExtractor, but it was renamed from DataSource. MediaExtractor, MediaPlayer and MediaMetadataRetriever each have a new overload: #setDataSource(android.media.MediaDataSource) Only NuPlayer supports this new data source. The change introduces: * IDataSource: The binder interface for DataSource. * JMediaDataSource: The native counterpart to the java interface. It implements IDataSource. * CallbackDataSource: A stagefright DataSource that wraps an IDataSource. Change-Id: I6d9c1167b4a7384c469b1e928f31791a7ebed716
832 lines
23 KiB
C++
832 lines
23 KiB
C++
/*
|
|
* Copyright 2012, The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
//#define LOG_NDEBUG 0
|
|
#define LOG_TAG "MediaExtractor-JNI"
|
|
#include <utils/Log.h>
|
|
|
|
#include "android_media_MediaExtractor.h"
|
|
|
|
#include "android_media_Utils.h"
|
|
#include "android_runtime/AndroidRuntime.h"
|
|
#include "android_runtime/Log.h"
|
|
#include "jni.h"
|
|
#include "JNIHelp.h"
|
|
#include "android_media_MediaDataSource.h"
|
|
|
|
#include <media/IMediaHTTPService.h>
|
|
#include <media/hardware/CryptoAPI.h>
|
|
#include <media/stagefright/foundation/ABuffer.h>
|
|
#include <media/stagefright/foundation/ADebug.h>
|
|
#include <media/stagefright/foundation/AMessage.h>
|
|
#include <media/stagefright/DataSource.h>
|
|
#include <media/stagefright/MediaErrors.h>
|
|
#include <media/stagefright/MetaData.h>
|
|
#include <media/stagefright/NuMediaExtractor.h>
|
|
|
|
#include <nativehelper/ScopedLocalRef.h>
|
|
|
|
#include "android_util_Binder.h"
|
|
|
|
namespace android {
|
|
|
|
struct fields_t {
|
|
jfieldID context;
|
|
|
|
jmethodID cryptoInfoSetID;
|
|
};
|
|
|
|
static fields_t gFields;
|
|
|
|
JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
|
|
: mClass(NULL),
|
|
mObject(NULL) {
|
|
jclass clazz = env->GetObjectClass(thiz);
|
|
CHECK(clazz != NULL);
|
|
|
|
mClass = (jclass)env->NewGlobalRef(clazz);
|
|
mObject = env->NewWeakGlobalRef(thiz);
|
|
|
|
mImpl = new NuMediaExtractor;
|
|
}
|
|
|
|
JMediaExtractor::~JMediaExtractor() {
|
|
JNIEnv *env = AndroidRuntime::getJNIEnv();
|
|
|
|
env->DeleteWeakGlobalRef(mObject);
|
|
mObject = NULL;
|
|
env->DeleteGlobalRef(mClass);
|
|
mClass = NULL;
|
|
}
|
|
|
|
status_t JMediaExtractor::setDataSource(
|
|
const sp<IMediaHTTPService> &httpService,
|
|
const char *path,
|
|
const KeyedVector<String8, String8> *headers) {
|
|
return mImpl->setDataSource(httpService, path, headers);
|
|
}
|
|
|
|
status_t JMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) {
|
|
return mImpl->setDataSource(fd, offset, size);
|
|
}
|
|
|
|
status_t JMediaExtractor::setDataSource(const sp<DataSource> &datasource) {
|
|
return mImpl->setDataSource(datasource);
|
|
}
|
|
|
|
size_t JMediaExtractor::countTracks() const {
|
|
return mImpl->countTracks();
|
|
}
|
|
|
|
status_t JMediaExtractor::getTrackFormat(size_t index, jobject *format) const {
|
|
sp<AMessage> msg;
|
|
status_t err;
|
|
if ((err = mImpl->getTrackFormat(index, &msg)) != OK) {
|
|
return err;
|
|
}
|
|
|
|
JNIEnv *env = AndroidRuntime::getJNIEnv();
|
|
|
|
return ConvertMessageToMap(env, msg, format);
|
|
}
|
|
|
|
status_t JMediaExtractor::getFileFormat(jobject *format) const {
|
|
sp<AMessage> msg;
|
|
status_t err;
|
|
if ((err = mImpl->getFileFormat(&msg)) != OK) {
|
|
return err;
|
|
}
|
|
|
|
JNIEnv *env = AndroidRuntime::getJNIEnv();
|
|
|
|
return ConvertMessageToMap(env, msg, format);
|
|
}
|
|
|
|
status_t JMediaExtractor::selectTrack(size_t index) {
|
|
return mImpl->selectTrack(index);
|
|
}
|
|
|
|
status_t JMediaExtractor::unselectTrack(size_t index) {
|
|
return mImpl->unselectTrack(index);
|
|
}
|
|
|
|
status_t JMediaExtractor::seekTo(
|
|
int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) {
|
|
return mImpl->seekTo(timeUs, mode);
|
|
}
|
|
|
|
status_t JMediaExtractor::advance() {
|
|
return mImpl->advance();
|
|
}
|
|
|
|
status_t JMediaExtractor::readSampleData(
|
|
jobject byteBuf, size_t offset, size_t *sampleSize) {
|
|
JNIEnv *env = AndroidRuntime::getJNIEnv();
|
|
|
|
void *dst = env->GetDirectBufferAddress(byteBuf);
|
|
|
|
size_t dstSize;
|
|
jbyteArray byteArray = NULL;
|
|
|
|
ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
|
|
CHECK(byteBufClass.get() != NULL);
|
|
|
|
if (dst == NULL) {
|
|
jmethodID arrayID =
|
|
env->GetMethodID(byteBufClass.get(), "array", "()[B");
|
|
CHECK(arrayID != NULL);
|
|
|
|
byteArray =
|
|
(jbyteArray)env->CallObjectMethod(byteBuf, arrayID);
|
|
|
|
if (byteArray == NULL) {
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
jboolean isCopy;
|
|
dst = env->GetByteArrayElements(byteArray, &isCopy);
|
|
|
|
dstSize = (size_t) env->GetArrayLength(byteArray);
|
|
} else {
|
|
dstSize = (size_t) env->GetDirectBufferCapacity(byteBuf);
|
|
}
|
|
|
|
if (dstSize < offset) {
|
|
if (byteArray != NULL) {
|
|
env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
|
|
}
|
|
|
|
return -ERANGE;
|
|
}
|
|
|
|
sp<ABuffer> buffer = new ABuffer((char *)dst + offset, dstSize - offset);
|
|
|
|
status_t err = mImpl->readSampleData(buffer);
|
|
|
|
if (byteArray != NULL) {
|
|
env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
|
|
}
|
|
|
|
if (err != OK) {
|
|
return err;
|
|
}
|
|
|
|
*sampleSize = buffer->size();
|
|
|
|
jmethodID positionID = env->GetMethodID(
|
|
byteBufClass.get(), "position", "(I)Ljava/nio/Buffer;");
|
|
|
|
CHECK(positionID != NULL);
|
|
|
|
jmethodID limitID = env->GetMethodID(
|
|
byteBufClass.get(), "limit", "(I)Ljava/nio/Buffer;");
|
|
|
|
CHECK(limitID != NULL);
|
|
|
|
jobject me = env->CallObjectMethod(
|
|
byteBuf, limitID, offset + *sampleSize);
|
|
env->DeleteLocalRef(me);
|
|
me = env->CallObjectMethod(
|
|
byteBuf, positionID, offset);
|
|
env->DeleteLocalRef(me);
|
|
me = NULL;
|
|
|
|
return OK;
|
|
}
|
|
|
|
status_t JMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
|
|
return mImpl->getSampleTrackIndex(trackIndex);
|
|
}
|
|
|
|
status_t JMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
|
|
return mImpl->getSampleTime(sampleTimeUs);
|
|
}
|
|
|
|
status_t JMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
|
|
*sampleFlags = 0;
|
|
|
|
sp<MetaData> meta;
|
|
status_t err = mImpl->getSampleMeta(&meta);
|
|
|
|
if (err != OK) {
|
|
return err;
|
|
}
|
|
|
|
int32_t val;
|
|
if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
|
|
(*sampleFlags) |= NuMediaExtractor::SAMPLE_FLAG_SYNC;
|
|
}
|
|
|
|
uint32_t type;
|
|
const void *data;
|
|
size_t size;
|
|
if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
|
|
(*sampleFlags) |= NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
status_t JMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
|
|
return mImpl->getSampleMeta(sampleMeta);
|
|
}
|
|
|
|
bool JMediaExtractor::getCachedDuration(int64_t *durationUs, bool *eos) const {
|
|
return mImpl->getCachedDuration(durationUs, eos);
|
|
}
|
|
|
|
} // namespace android
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
using namespace android;
|
|
|
|
static sp<JMediaExtractor> setMediaExtractor(
|
|
JNIEnv *env, jobject thiz, const sp<JMediaExtractor> &extractor) {
|
|
sp<JMediaExtractor> old =
|
|
(JMediaExtractor *)env->GetLongField(thiz, gFields.context);
|
|
|
|
if (extractor != NULL) {
|
|
extractor->incStrong(thiz);
|
|
}
|
|
if (old != NULL) {
|
|
old->decStrong(thiz);
|
|
}
|
|
env->SetLongField(thiz, gFields.context, (jlong)extractor.get());
|
|
|
|
return old;
|
|
}
|
|
|
|
static sp<JMediaExtractor> getMediaExtractor(JNIEnv *env, jobject thiz) {
|
|
return (JMediaExtractor *)env->GetLongField(thiz, gFields.context);
|
|
}
|
|
|
|
static void android_media_MediaExtractor_release(JNIEnv *env, jobject thiz) {
|
|
setMediaExtractor(env, thiz, NULL);
|
|
}
|
|
|
|
static jint android_media_MediaExtractor_getTrackCount(
|
|
JNIEnv *env, jobject thiz) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return -1;
|
|
}
|
|
|
|
return (jint) extractor->countTracks();
|
|
}
|
|
|
|
static jobject android_media_MediaExtractor_getTrackFormatNative(
|
|
JNIEnv *env, jobject thiz, jint index) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return NULL;
|
|
}
|
|
|
|
jobject format;
|
|
status_t err = extractor->getTrackFormat(index, &format);
|
|
|
|
if (err != OK) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return NULL;
|
|
}
|
|
|
|
return format;
|
|
}
|
|
|
|
static jobject android_media_MediaExtractor_getFileFormatNative(
|
|
JNIEnv *env, jobject thiz) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return NULL;
|
|
}
|
|
|
|
jobject format;
|
|
status_t err = extractor->getFileFormat(&format);
|
|
|
|
if (err != OK) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return NULL;
|
|
}
|
|
|
|
return format;
|
|
}
|
|
|
|
static void android_media_MediaExtractor_selectTrack(
|
|
JNIEnv *env, jobject thiz, jint index) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return;
|
|
}
|
|
|
|
status_t err = extractor->selectTrack(index);
|
|
|
|
if (err != OK) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void android_media_MediaExtractor_unselectTrack(
|
|
JNIEnv *env, jobject thiz, jint index) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return;
|
|
}
|
|
|
|
status_t err = extractor->unselectTrack(index);
|
|
|
|
if (err != OK) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void android_media_MediaExtractor_seekTo(
|
|
JNIEnv *env, jobject thiz, jlong timeUs, jint mode) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return;
|
|
}
|
|
|
|
if (mode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC
|
|
|| mode >= MediaSource::ReadOptions::SEEK_CLOSEST) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return;
|
|
}
|
|
|
|
extractor->seekTo(timeUs, (MediaSource::ReadOptions::SeekMode)mode);
|
|
}
|
|
|
|
static jboolean android_media_MediaExtractor_advance(
|
|
JNIEnv *env, jobject thiz) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
status_t err = extractor->advance();
|
|
|
|
if (err == ERROR_END_OF_STREAM) {
|
|
return JNI_FALSE;
|
|
} else if (err != OK) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
return JNI_TRUE;
|
|
}
|
|
|
|
static jint android_media_MediaExtractor_readSampleData(
|
|
JNIEnv *env, jobject thiz, jobject byteBuf, jint offset) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return -1;
|
|
}
|
|
|
|
size_t sampleSize;
|
|
status_t err = extractor->readSampleData(byteBuf, offset, &sampleSize);
|
|
|
|
if (err == ERROR_END_OF_STREAM) {
|
|
return -1;
|
|
} else if (err != OK) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return -1;
|
|
}
|
|
|
|
return (jint) sampleSize;
|
|
}
|
|
|
|
static jint android_media_MediaExtractor_getSampleTrackIndex(
|
|
JNIEnv *env, jobject thiz) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return -1;
|
|
}
|
|
|
|
size_t trackIndex;
|
|
status_t err = extractor->getSampleTrackIndex(&trackIndex);
|
|
|
|
if (err == ERROR_END_OF_STREAM) {
|
|
return -1;
|
|
} else if (err != OK) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return -1;
|
|
}
|
|
|
|
return (jint) trackIndex;
|
|
}
|
|
|
|
static jlong android_media_MediaExtractor_getSampleTime(
|
|
JNIEnv *env, jobject thiz) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return -1ll;
|
|
}
|
|
|
|
int64_t sampleTimeUs;
|
|
status_t err = extractor->getSampleTime(&sampleTimeUs);
|
|
|
|
if (err == ERROR_END_OF_STREAM) {
|
|
return -1ll;
|
|
} else if (err != OK) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return -1ll;
|
|
}
|
|
|
|
return (jlong) sampleTimeUs;
|
|
}
|
|
|
|
static jint android_media_MediaExtractor_getSampleFlags(
|
|
JNIEnv *env, jobject thiz) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return -1;
|
|
}
|
|
|
|
uint32_t sampleFlags;
|
|
status_t err = extractor->getSampleFlags(&sampleFlags);
|
|
|
|
if (err == ERROR_END_OF_STREAM) {
|
|
return -1;
|
|
} else if (err != OK) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return -1;
|
|
}
|
|
|
|
return (jint) sampleFlags;
|
|
}
|
|
|
|
static jboolean android_media_MediaExtractor_getSampleCryptoInfo(
|
|
JNIEnv *env, jobject thiz, jobject cryptoInfoObj) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
sp<MetaData> meta;
|
|
status_t err = extractor->getSampleMeta(&meta);
|
|
|
|
if (err != OK) {
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
uint32_t type;
|
|
const void *data;
|
|
size_t size;
|
|
if (!meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
size_t numSubSamples = size / sizeof(int32_t);
|
|
|
|
if (numSubSamples == 0) {
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
jintArray numBytesOfEncryptedDataObj = env->NewIntArray(numSubSamples);
|
|
jboolean isCopy;
|
|
jint *dst = env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
|
|
for (size_t i = 0; i < numSubSamples; ++i) {
|
|
dst[i] = ((const int32_t *)data)[i];
|
|
}
|
|
env->ReleaseIntArrayElements(numBytesOfEncryptedDataObj, dst, 0);
|
|
dst = NULL;
|
|
|
|
size_t encSize = size;
|
|
jintArray numBytesOfPlainDataObj = NULL;
|
|
if (meta->findData(kKeyPlainSizes, &type, &data, &size)) {
|
|
if (size != encSize) {
|
|
// The two must be of the same length.
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
numBytesOfPlainDataObj = env->NewIntArray(numSubSamples);
|
|
jboolean isCopy;
|
|
jint *dst = env->GetIntArrayElements(numBytesOfPlainDataObj, &isCopy);
|
|
for (size_t i = 0; i < numSubSamples; ++i) {
|
|
dst[i] = ((const int32_t *)data)[i];
|
|
}
|
|
env->ReleaseIntArrayElements(numBytesOfPlainDataObj, dst, 0);
|
|
dst = NULL;
|
|
}
|
|
|
|
jbyteArray keyObj = NULL;
|
|
if (meta->findData(kKeyCryptoKey, &type, &data, &size)) {
|
|
if (size != 16) {
|
|
// Keys must be 16 bytes in length.
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
keyObj = env->NewByteArray(size);
|
|
jboolean isCopy;
|
|
jbyte *dst = env->GetByteArrayElements(keyObj, &isCopy);
|
|
memcpy(dst, data, size);
|
|
env->ReleaseByteArrayElements(keyObj, dst, 0);
|
|
dst = NULL;
|
|
}
|
|
|
|
jbyteArray ivObj = NULL;
|
|
if (meta->findData(kKeyCryptoIV, &type, &data, &size)) {
|
|
if (size != 16) {
|
|
// IVs must be 16 bytes in length.
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
ivObj = env->NewByteArray(size);
|
|
jboolean isCopy;
|
|
jbyte *dst = env->GetByteArrayElements(ivObj, &isCopy);
|
|
memcpy(dst, data, size);
|
|
env->ReleaseByteArrayElements(ivObj, dst, 0);
|
|
dst = NULL;
|
|
}
|
|
|
|
int32_t mode;
|
|
if (!meta->findInt32(kKeyCryptoMode, &mode)) {
|
|
mode = CryptoPlugin::kMode_AES_CTR;
|
|
}
|
|
|
|
env->CallVoidMethod(
|
|
cryptoInfoObj,
|
|
gFields.cryptoInfoSetID,
|
|
(jint)numSubSamples,
|
|
numBytesOfPlainDataObj,
|
|
numBytesOfEncryptedDataObj,
|
|
keyObj,
|
|
ivObj,
|
|
mode);
|
|
|
|
return JNI_TRUE;
|
|
}
|
|
|
|
static void android_media_MediaExtractor_native_init(JNIEnv *env) {
|
|
jclass clazz = env->FindClass("android/media/MediaExtractor");
|
|
CHECK(clazz != NULL);
|
|
|
|
gFields.context = env->GetFieldID(clazz, "mNativeContext", "J");
|
|
CHECK(gFields.context != NULL);
|
|
|
|
clazz = env->FindClass("android/media/MediaCodec$CryptoInfo");
|
|
CHECK(clazz != NULL);
|
|
|
|
gFields.cryptoInfoSetID =
|
|
env->GetMethodID(clazz, "set", "(I[I[I[B[BI)V");
|
|
|
|
DataSource::RegisterDefaultSniffers();
|
|
}
|
|
|
|
static void android_media_MediaExtractor_native_setup(
|
|
JNIEnv *env, jobject thiz) {
|
|
sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz);
|
|
setMediaExtractor(env,thiz, extractor);
|
|
}
|
|
|
|
static void android_media_MediaExtractor_setDataSource(
|
|
JNIEnv *env, jobject thiz,
|
|
jobject httpServiceBinderObj,
|
|
jstring pathObj,
|
|
jobjectArray keysArray,
|
|
jobjectArray valuesArray) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return;
|
|
}
|
|
|
|
if (pathObj == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return;
|
|
}
|
|
|
|
KeyedVector<String8, String8> headers;
|
|
if (!ConvertKeyValueArraysToKeyedVector(
|
|
env, keysArray, valuesArray, &headers)) {
|
|
return;
|
|
}
|
|
|
|
const char *path = env->GetStringUTFChars(pathObj, NULL);
|
|
|
|
if (path == NULL) {
|
|
return;
|
|
}
|
|
|
|
sp<IMediaHTTPService> httpService;
|
|
if (httpServiceBinderObj != NULL) {
|
|
sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj);
|
|
httpService = interface_cast<IMediaHTTPService>(binder);
|
|
}
|
|
|
|
status_t err = extractor->setDataSource(httpService, path, &headers);
|
|
|
|
env->ReleaseStringUTFChars(pathObj, path);
|
|
path = NULL;
|
|
|
|
if (err != OK) {
|
|
jniThrowException(
|
|
env,
|
|
"java/io/IOException",
|
|
"Failed to instantiate extractor.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void android_media_MediaExtractor_setDataSourceFd(
|
|
JNIEnv *env, jobject thiz,
|
|
jobject fileDescObj, jlong offset, jlong length) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return;
|
|
}
|
|
|
|
if (fileDescObj == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return;
|
|
}
|
|
|
|
int fd = jniGetFDFromFileDescriptor(env, fileDescObj);
|
|
|
|
status_t err = extractor->setDataSource(fd, offset, length);
|
|
|
|
if (err != OK) {
|
|
jniThrowException(
|
|
env,
|
|
"java/io/IOException",
|
|
"Failed to instantiate extractor.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void android_media_MediaExtractor_setDataSourceCallback(
|
|
JNIEnv *env, jobject thiz,
|
|
jobject callbackObj) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return;
|
|
}
|
|
|
|
if (callbackObj == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return;
|
|
}
|
|
|
|
sp<DataSource> bridge =
|
|
DataSource::CreateFromIDataSource(new JMediaDataSource(env, callbackObj));
|
|
status_t err = extractor->setDataSource(bridge);
|
|
|
|
if (err != OK) {
|
|
jniThrowException(
|
|
env,
|
|
"java/io/IOException",
|
|
"Failed to instantiate extractor.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
static jlong android_media_MediaExtractor_getCachedDurationUs(
|
|
JNIEnv *env, jobject thiz) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return -1ll;
|
|
}
|
|
|
|
int64_t cachedDurationUs;
|
|
bool eos;
|
|
if (!extractor->getCachedDuration(&cachedDurationUs, &eos)) {
|
|
return -1ll;
|
|
}
|
|
|
|
return (jlong) cachedDurationUs;
|
|
}
|
|
|
|
static jboolean android_media_MediaExtractor_hasCacheReachedEOS(
|
|
JNIEnv *env, jobject thiz) {
|
|
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
|
|
|
|
if (extractor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
|
return JNI_TRUE;
|
|
}
|
|
|
|
int64_t cachedDurationUs;
|
|
bool eos;
|
|
if (!extractor->getCachedDuration(&cachedDurationUs, &eos)) {
|
|
return JNI_TRUE;
|
|
}
|
|
|
|
return eos ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static void android_media_MediaExtractor_native_finalize(
|
|
JNIEnv *env, jobject thiz) {
|
|
android_media_MediaExtractor_release(env, thiz);
|
|
}
|
|
|
|
static JNINativeMethod gMethods[] = {
|
|
{ "release", "()V", (void *)android_media_MediaExtractor_release },
|
|
|
|
{ "getTrackCount", "()I", (void *)android_media_MediaExtractor_getTrackCount },
|
|
|
|
{ "getFileFormatNative", "()Ljava/util/Map;",
|
|
(void *)android_media_MediaExtractor_getFileFormatNative },
|
|
|
|
{ "getTrackFormatNative", "(I)Ljava/util/Map;",
|
|
(void *)android_media_MediaExtractor_getTrackFormatNative },
|
|
|
|
{ "selectTrack", "(I)V", (void *)android_media_MediaExtractor_selectTrack },
|
|
|
|
{ "unselectTrack", "(I)V",
|
|
(void *)android_media_MediaExtractor_unselectTrack },
|
|
|
|
{ "seekTo", "(JI)V", (void *)android_media_MediaExtractor_seekTo },
|
|
|
|
{ "advance", "()Z", (void *)android_media_MediaExtractor_advance },
|
|
|
|
{ "readSampleData", "(Ljava/nio/ByteBuffer;I)I",
|
|
(void *)android_media_MediaExtractor_readSampleData },
|
|
|
|
{ "getSampleTrackIndex", "()I",
|
|
(void *)android_media_MediaExtractor_getSampleTrackIndex },
|
|
|
|
{ "getSampleTime", "()J",
|
|
(void *)android_media_MediaExtractor_getSampleTime },
|
|
|
|
{ "getSampleFlags", "()I",
|
|
(void *)android_media_MediaExtractor_getSampleFlags },
|
|
|
|
{ "getSampleCryptoInfo", "(Landroid/media/MediaCodec$CryptoInfo;)Z",
|
|
(void *)android_media_MediaExtractor_getSampleCryptoInfo },
|
|
|
|
{ "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
|
|
|
|
{ "native_setup", "()V",
|
|
(void *)android_media_MediaExtractor_native_setup },
|
|
|
|
{ "native_finalize", "()V",
|
|
(void *)android_media_MediaExtractor_native_finalize },
|
|
|
|
{ "nativeSetDataSource",
|
|
"(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
|
|
"[Ljava/lang/String;)V",
|
|
(void *)android_media_MediaExtractor_setDataSource },
|
|
|
|
{ "setDataSource", "(Ljava/io/FileDescriptor;JJ)V",
|
|
(void *)android_media_MediaExtractor_setDataSourceFd },
|
|
|
|
{ "setDataSource", "(Landroid/media/MediaDataSource;)V",
|
|
(void *)android_media_MediaExtractor_setDataSourceCallback },
|
|
|
|
{ "getCachedDuration", "()J",
|
|
(void *)android_media_MediaExtractor_getCachedDurationUs },
|
|
|
|
{ "hasCacheReachedEndOfStream", "()Z",
|
|
(void *)android_media_MediaExtractor_hasCacheReachedEOS },
|
|
};
|
|
|
|
int register_android_media_MediaExtractor(JNIEnv *env) {
|
|
return AndroidRuntime::registerNativeMethods(env,
|
|
"android/media/MediaExtractor", gMethods, NELEM(gMethods));
|
|
}
|