blob: 441e0f3b2267353ad2326831ccfc821a34ee8dcb [file] [log] [blame]
/*
* Copyright (C) 2022 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.
*/
/* Original code copied from NDK Native-media sample code */
//#define LOG_NDEBUG 0
#define LOG_TAG "NdkMediaCodec-jni"
#include <log/log.h>
#include <android/native_window_jni.h>
#include <assert.h>
#include <jni.h>
#include <string.h>
#include <unistd.h>
#include "media/NdkMediaExtractor.h"
#include "media/NdkMediaCodec.h"
#include "media/NdkMediaCrypto.h"
#include "media/NdkMediaDataSource.h"
#include "media/NdkMediaFormat.h"
#include "media/NdkMediaMuxer.h"
extern "C" jlong Java_android_media_cts_NdkMediaCodec_AMediaCodecCreateCodecByName(
JNIEnv *env, jclass /*clazz*/, jstring name) {
if (name == NULL) {
return 0;
}
const char *tmp = env->GetStringUTFChars(name, NULL);
if (tmp == NULL) {
return 0;
}
AMediaCodec *codec = AMediaCodec_createCodecByName(tmp);
if (codec == NULL) {
env->ReleaseStringUTFChars(name, tmp);
return 0;
}
env->ReleaseStringUTFChars(name, tmp);
return reinterpret_cast<jlong>(codec);
}
extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecDelete(
JNIEnv * /*env*/, jclass /*clazz*/, jlong codec) {
media_status_t err = AMediaCodec_delete(reinterpret_cast<AMediaCodec *>(codec));
return err == AMEDIA_OK;
}
extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecStart(
JNIEnv * /*env*/, jclass /*clazz*/, jlong codec) {
media_status_t err = AMediaCodec_start(reinterpret_cast<AMediaCodec *>(codec));
return err == AMEDIA_OK;
}
extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecStop(
JNIEnv * /*env*/, jclass /*clazz*/, jlong codec) {
media_status_t err = AMediaCodec_stop(reinterpret_cast<AMediaCodec *>(codec));
return err == AMEDIA_OK;
}
extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecConfigure(
JNIEnv *env,
jclass /*clazz*/,
jlong codec,
jstring mime,
jint width,
jint height,
jint colorFormat,
jint bitRate,
jint frameRate,
jint iFrameInterval,
jobject csd0,
jobject csd1,
jint flags,
jint lowLatency,
jobject surface,
jint range,
jint standard,
jint transfer) {
AMediaFormat* format = AMediaFormat_new();
if (format == NULL) {
return false;
}
const char *tmp = env->GetStringUTFChars(mime, NULL);
if (tmp == NULL) {
AMediaFormat_delete(format);
return false;
}
AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, tmp);
env->ReleaseStringUTFChars(mime, tmp);
const char *keys[] = {
AMEDIAFORMAT_KEY_WIDTH,
AMEDIAFORMAT_KEY_HEIGHT,
AMEDIAFORMAT_KEY_COLOR_FORMAT,
AMEDIAFORMAT_KEY_BIT_RATE,
AMEDIAFORMAT_KEY_FRAME_RATE,
AMEDIAFORMAT_KEY_I_FRAME_INTERVAL,
// need to specify the actual string, since this test needs
// to run on API 29, where the symbol doesn't exist
"low-latency", // AMEDIAFORMAT_KEY_LOW_LATENCY
AMEDIAFORMAT_KEY_COLOR_RANGE,
AMEDIAFORMAT_KEY_COLOR_STANDARD,
AMEDIAFORMAT_KEY_COLOR_TRANSFER,
};
jint values[] = {width, height, colorFormat, bitRate, frameRate, iFrameInterval, lowLatency,
range, standard, transfer};
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) {
if (values[i] >= 0) {
AMediaFormat_setInt32(format, keys[i], values[i]);
}
}
if (csd0 != NULL) {
void *csd0Ptr = env->GetDirectBufferAddress(csd0);
jlong csd0Size = env->GetDirectBufferCapacity(csd0);
AMediaFormat_setBuffer(format, "csd-0", csd0Ptr, csd0Size);
}
if (csd1 != NULL) {
void *csd1Ptr = env->GetDirectBufferAddress(csd1);
jlong csd1Size = env->GetDirectBufferCapacity(csd1);
AMediaFormat_setBuffer(format, "csd-1", csd1Ptr, csd1Size);
}
media_status_t err = AMediaCodec_configure(
reinterpret_cast<AMediaCodec *>(codec),
format,
surface == NULL ? NULL : ANativeWindow_fromSurface(env, surface),
NULL,
flags);
AMediaFormat_delete(format);
return err == AMEDIA_OK;
}
extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecSetInputSurface(
JNIEnv* env, jclass /*clazz*/, jlong codec, jobject surface) {
media_status_t err = AMediaCodec_setInputSurface(
reinterpret_cast<AMediaCodec *>(codec),
ANativeWindow_fromSurface(env, surface));
return err == AMEDIA_OK;
}
extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecSetNativeInputSurface(
JNIEnv* /*env*/, jclass /*clazz*/, jlong codec, jlong nativeWindow) {
media_status_t err = AMediaCodec_setInputSurface(
reinterpret_cast<AMediaCodec *>(codec),
reinterpret_cast<ANativeWindow *>(nativeWindow));
return err == AMEDIA_OK;
}
extern "C" jlong Java_android_media_cts_NdkMediaCodec_AMediaCodecCreateInputSurface(
JNIEnv* /*env*/, jclass /*clazz*/, jlong codec) {
ANativeWindow *nativeWindow;
media_status_t err = AMediaCodec_createInputSurface(
reinterpret_cast<AMediaCodec *>(codec),
&nativeWindow);
if (err == AMEDIA_OK) {
return reinterpret_cast<jlong>(nativeWindow);
}
return 0;
}
extern "C" jlong Java_android_media_cts_NdkMediaCodec_AMediaCodecCreatePersistentInputSurface(
JNIEnv* /*env*/, jclass /*clazz*/) {
ANativeWindow *nativeWindow;
media_status_t err = AMediaCodec_createPersistentInputSurface(&nativeWindow);
if (err == AMEDIA_OK) {
return reinterpret_cast<jlong>(nativeWindow);
}
return 0;
}
extern "C" jstring Java_android_media_cts_NdkMediaCodec_AMediaCodecGetOutputFormatString(
JNIEnv* env, jclass /*clazz*/, jlong codec) {
AMediaFormat *format = AMediaCodec_getOutputFormat(reinterpret_cast<AMediaCodec *>(codec));
const char *str = AMediaFormat_toString(format);
jstring jstr = env->NewStringUTF(str);
AMediaFormat_delete(format);
return jstr;
}
extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecSignalEndOfInputStream(
JNIEnv* /*env*/, jclass /*clazz*/, jlong codec) {
media_status_t err = AMediaCodec_signalEndOfInputStream(reinterpret_cast<AMediaCodec *>(codec));
return err == AMEDIA_OK;
}
extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecReleaseOutputBuffer(
JNIEnv* /*env*/, jclass /*clazz*/, jlong codec, jint index, jboolean render) {
media_status_t err = AMediaCodec_releaseOutputBuffer(
reinterpret_cast<AMediaCodec *>(codec),
index,
render);
return err == AMEDIA_OK;
}
static jobject AMediaCodecGetBuffer(
JNIEnv* env,
jlong codec,
jint index,
uint8_t *(*getBuffer)(AMediaCodec*, size_t, size_t*)) {
size_t bufsize;
uint8_t *buf = getBuffer(
reinterpret_cast<AMediaCodec *>(codec),
index,
&bufsize);
return env->NewDirectByteBuffer(buf, bufsize);
}
extern "C" jobject Java_android_media_cts_NdkMediaCodec_AMediaCodecGetOutputBuffer(
JNIEnv* env, jclass /*clazz*/, jlong codec, jint index) {
return AMediaCodecGetBuffer(env, codec, index, AMediaCodec_getOutputBuffer);
}
extern "C" jlongArray Java_android_media_cts_NdkMediaCodec_AMediaCodecDequeueOutputBuffer(
JNIEnv* env, jclass /*clazz*/, jlong codec, jlong timeoutUs) {
AMediaCodecBufferInfo info;
memset(&info, 0, sizeof(info));
int status = AMediaCodec_dequeueOutputBuffer(
reinterpret_cast<AMediaCodec *>(codec),
&info,
timeoutUs);
jlong ret[5] = {0};
ret[0] = status;
ret[1] = 0; // NdkMediaCodec calls ABuffer::data, which already adds offset
ret[2] = info.size;
ret[3] = info.presentationTimeUs;
ret[4] = info.flags;
jlongArray jret = env->NewLongArray(5);
env->SetLongArrayRegion(jret, 0, 5, ret);
return jret;
}
extern "C" jobject Java_android_media_cts_NdkMediaCodec_AMediaCodecGetInputBuffer(
JNIEnv* env, jclass /*clazz*/, jlong codec, jint index) {
return AMediaCodecGetBuffer(env, codec, index, AMediaCodec_getInputBuffer);
}
extern "C" jint Java_android_media_cts_NdkMediaCodec_AMediaCodecDequeueInputBuffer(
JNIEnv* /*env*/, jclass /*clazz*/, jlong codec, jlong timeoutUs) {
return AMediaCodec_dequeueInputBuffer(
reinterpret_cast<AMediaCodec *>(codec),
timeoutUs);
}
extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecQueueInputBuffer(
JNIEnv* /*env*/,
jclass /*clazz*/,
jlong codec,
jint index,
jint offset,
jint size,
jlong presentationTimeUs,
jint flags) {
media_status_t err = AMediaCodec_queueInputBuffer(
reinterpret_cast<AMediaCodec *>(codec),
index,
offset,
size,
presentationTimeUs,
flags);
return err == AMEDIA_OK;
}
extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecSetParameter(
JNIEnv* env, jclass /*clazz*/, jlong codec, jstring jkey, jint value) {
AMediaFormat* params = AMediaFormat_new();
if (params == NULL) {
return false;
}
const char *key = env->GetStringUTFChars(jkey, NULL);
if (key == NULL) {
AMediaFormat_delete(params);
return false;
}
AMediaFormat_setInt32(params, key, value);
media_status_t err = AMediaCodec_setParameters(
reinterpret_cast<AMediaCodec *>(codec),
params);
env->ReleaseStringUTFChars(jkey, key);
AMediaFormat_delete(params);
return err == AMEDIA_OK;
}