Merge "Import translations. DO NOT MERGE" into mnc-dev
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index d22cfda..8220a74 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -664,11 +664,11 @@
if (!mHasSurface) {
throw new IllegalStateException("codec was not configured for an output surface");
}
-
- // TODO implement this
- throw new IllegalArgumentException("codec does not support this surface");
+ native_setSurface(surface);
}
+ private native void native_setSurface(@NonNull Surface surface);
+
/**
* Create a persistent input surface that can be used with codecs that normally have an input
* surface, such as video encoders. A persistent input can be reused by subsequent
@@ -681,12 +681,20 @@
*/
@NonNull
public static Surface createPersistentInputSurface() {
- // TODO implement this
- return new PersistentSurface();
+ return native_createPersistentInputSurface();
}
static class PersistentSurface extends Surface {
- PersistentSurface() {}
+ @SuppressWarnings("unused")
+ PersistentSurface() {} // used by native
+
+ @Override
+ public void release() {
+ native_releasePersistentInputSurface(this);
+ super.release();
+ }
+
+ private long mPersistentObject;
};
/**
@@ -700,9 +708,17 @@
* {@link #createPersistentInputSurface}.
*/
public void usePersistentInputSurface(@NonNull Surface surface) {
- throw new IllegalArgumentException("not implemented");
+ if (!(surface instanceof PersistentSurface)) {
+ throw new IllegalArgumentException("not a PersistentSurface");
+ }
+ native_usePersistentInputSurface(surface);
}
+ @NonNull
+ private static native final PersistentSurface native_createPersistentInputSurface();
+ private static native final void native_releasePersistentInputSurface(@NonNull Surface surface);
+ private native final void native_usePersistentInputSurface(@NonNull Surface surface);
+
private native final void native_setCallback(@Nullable Callback cb);
private native final void native_configure(
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 1b054cc..a2f596b 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -16,6 +16,7 @@
package android.media;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.app.Application;
@@ -142,22 +143,28 @@
/**
* Configures the recorder to use a persistent surface when using SURFACE video source.
- * <p> May only be called after {@link #prepare} in lieu of {@link #getSurface}.
- * Frames rendered to the Surface before {@link #start} will be discarded.</p>
+ * <p> May only be called before {@link #prepare}. If called, {@link #getSurface} should
+ * not be used and will throw IllegalStateException. Frames rendered to the Surface
+ * before {@link #start} will be discarded.</p>
* @param surface a persistent input surface created by
* {@link MediaCodec#createPersistentInputSurface}
- * @throws IllegalStateException if it is called before {@link #prepare}, after
- * {@link #stop}, or is called when VideoSource is not set to SURFACE.
+ * @throws IllegalStateException if it is called after {@link #prepare} and before
+ * {@link #stop}.
* @throws IllegalArgumentException if the surface was not created by
* {@link MediaCodec#createPersistentInputSurface}.
* @see MediaCodec#createPersistentInputSurface
* @see MediaRecorder.VideoSource
*/
- public void usePersistentSurface(Surface surface) {
- throw new IllegalArgumentException("not implemented");
+ public void usePersistentSurface(@NonNull Surface surface) {
+ if (!(surface instanceof MediaCodec.PersistentSurface)) {
+ throw new IllegalArgumentException("not a PersistentSurface");
+ }
+ native_usePersistentSurface(surface);
}
+ private native final void native_usePersistentSurface(@NonNull Surface surface);
+
/**
* Sets a Surface to show a preview of recorded media (video). Calls this
* before prepare() to make sure that the desirable preview display is
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index e34f9ed..f808c0d1 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -39,7 +39,7 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/MediaErrors.h>
-
+#include <media/stagefright/PersistentSurface.h>
#include <nativehelper/ScopedLocalRef.h>
#include <system/window.h>
@@ -75,6 +75,14 @@
jint reasonReclaimed;
} gExceptionReason;
+static struct {
+ jclass clazz;
+ jfieldID mLock;
+ jfieldID mPersistentObject;
+ jmethodID ctor;
+ jmethodID setNativeObjectLocked;
+} gPersistentSurfaceClassInfo;
+
struct fields_t {
jfieldID context;
jmethodID postEventFromNativeID;
@@ -87,6 +95,7 @@
};
static fields_t gFields;
+static const void *sRefBaseOwner;
////////////////////////////////////////////////////////////////////////////////
@@ -245,11 +254,29 @@
return mCodec->configure(format, mSurfaceTextureClient, crypto, flags);
}
+status_t JMediaCodec::setSurface(
+ const sp<IGraphicBufferProducer> &bufferProducer) {
+ sp<Surface> client;
+ if (bufferProducer != NULL) {
+ client = new Surface(bufferProducer, true /* controlledByApp */);
+ }
+ status_t err = mCodec->setSurface(client);
+ if (err == OK) {
+ mSurfaceTextureClient = client;
+ }
+ return err;
+}
+
status_t JMediaCodec::createInputSurface(
sp<IGraphicBufferProducer>* bufferProducer) {
return mCodec->createInputSurface(bufferProducer);
}
+status_t JMediaCodec::usePersistentInputSurface(
+ const sp<PersistentSurface> &surface) {
+ return mCodec->usePersistentInputSurface(surface);
+}
+
status_t JMediaCodec::start() {
return mCodec->start();
}
@@ -797,6 +824,10 @@
jniThrowException(env, "java/lang/IllegalStateException", msg);
return 0;
+ case BAD_VALUE:
+ jniThrowException(env, "java/lang/IllegalArgumentException", msg);
+ return 0;
+
default:
if (isCryptoError(err)) {
throwCryptoException(env, err, msg);
@@ -869,6 +900,149 @@
throwExceptionAsNecessary(env, err);
}
+static void android_media_MediaCodec_native_setSurface(
+ JNIEnv *env,
+ jobject thiz,
+ jobject jsurface) {
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return;
+ }
+
+ sp<IGraphicBufferProducer> bufferProducer;
+ if (jsurface != NULL) {
+ sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
+ if (surface != NULL) {
+ bufferProducer = surface->getIGraphicBufferProducer();
+ } else {
+ jniThrowException(
+ env,
+ "java/lang/IllegalArgumentException",
+ "The surface has been released");
+ return;
+ }
+ }
+
+ status_t err = codec->setSurface(bufferProducer);
+ throwExceptionAsNecessary(env, err);
+}
+
+sp<PersistentSurface> android_media_MediaCodec_getPersistentInputSurface(
+ JNIEnv* env, jobject object) {
+ sp<PersistentSurface> persistentSurface;
+
+ jobject lock = env->GetObjectField(
+ object, gPersistentSurfaceClassInfo.mLock);
+ if (env->MonitorEnter(lock) == JNI_OK) {
+ persistentSurface = reinterpret_cast<PersistentSurface *>(
+ env->GetLongField(object,
+ gPersistentSurfaceClassInfo.mPersistentObject));
+ env->MonitorExit(lock);
+ }
+ env->DeleteLocalRef(lock);
+
+ return persistentSurface;
+}
+
+static jobject android_media_MediaCodec_createPersistentInputSurface(
+ JNIEnv* env, jclass /* clazz */) {
+ ALOGV("android_media_MediaCodec_createPersistentInputSurface");
+ sp<PersistentSurface> persistentSurface =
+ MediaCodec::CreatePersistentInputSurface();
+
+ if (persistentSurface == NULL) {
+ return NULL;
+ }
+
+ sp<Surface> surface = new Surface(
+ persistentSurface->getBufferProducer(), true);
+ if (surface == NULL) {
+ return NULL;
+ }
+
+ jobject object = env->NewObject(
+ gPersistentSurfaceClassInfo.clazz,
+ gPersistentSurfaceClassInfo.ctor);
+
+ if (object == NULL) {
+ if (env->ExceptionCheck()) {
+ ALOGE("Could not create PersistentSurface.");
+ env->ExceptionClear();
+ }
+ return NULL;
+ }
+
+ jobject lock = env->GetObjectField(
+ object, gPersistentSurfaceClassInfo.mLock);
+ if (env->MonitorEnter(lock) == JNI_OK) {
+ env->CallVoidMethod(
+ object,
+ gPersistentSurfaceClassInfo.setNativeObjectLocked,
+ (jlong)surface.get());
+ env->SetLongField(
+ object,
+ gPersistentSurfaceClassInfo.mPersistentObject,
+ (jlong)persistentSurface.get());
+ env->MonitorExit(lock);
+ } else {
+ env->DeleteLocalRef(object);
+ object = NULL;
+ }
+ env->DeleteLocalRef(lock);
+
+ if (object != NULL) {
+ surface->incStrong(&sRefBaseOwner);
+ persistentSurface->incStrong(&sRefBaseOwner);
+ }
+
+ return object;
+}
+
+static void android_media_MediaCodec_releasePersistentInputSurface(
+ JNIEnv* env, jclass /* clazz */, jobject object) {
+ sp<PersistentSurface> persistentSurface;
+
+ jobject lock = env->GetObjectField(
+ object, gPersistentSurfaceClassInfo.mLock);
+ if (env->MonitorEnter(lock) == JNI_OK) {
+ persistentSurface = reinterpret_cast<PersistentSurface *>(
+ env->GetLongField(
+ object, gPersistentSurfaceClassInfo.mPersistentObject));
+ env->SetLongField(
+ object,
+ gPersistentSurfaceClassInfo.mPersistentObject,
+ (jlong)0);
+ env->MonitorExit(lock);
+ }
+ env->DeleteLocalRef(lock);
+
+ if (persistentSurface != NULL) {
+ persistentSurface->decStrong(&sRefBaseOwner);
+ }
+ // no need to release surface as it will be released by Surface's jni
+}
+
+static void android_media_MediaCodec_usePersistentInputSurface(
+ JNIEnv* env, jobject thiz, jobject object) {
+ ALOGV("android_media_MediaCodec_usePersistentInputSurface");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+ if (codec == NULL) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return;
+ }
+
+ sp<PersistentSurface> persistentSurface =
+ android_media_MediaCodec_getPersistentInputSurface(env, object);
+
+ status_t err = codec->usePersistentInputSurface(persistentSurface);
+ if (err != NO_ERROR) {
+ throwExceptionAsNecessary(env, err);
+ }
+}
+
static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
jobject thiz) {
ALOGV("android_media_MediaCodec_createInputSurface");
@@ -1471,6 +1645,29 @@
CHECK(field != NULL);
gExceptionReason.reasonReclaimed =
env->GetStaticIntField(clazz.get(), field);
+
+ clazz.reset(env->FindClass("android/view/Surface"));
+ CHECK(clazz.get() != NULL);
+
+ field = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;");
+ CHECK(field != NULL);
+ gPersistentSurfaceClassInfo.mLock = field;
+
+ jmethodID method = env->GetMethodID(clazz.get(), "setNativeObjectLocked", "(J)V");
+ CHECK(method != NULL);
+ gPersistentSurfaceClassInfo.setNativeObjectLocked = method;
+
+ clazz.reset(env->FindClass("android/media/MediaCodec$PersistentSurface"));
+ CHECK(clazz.get() != NULL);
+ gPersistentSurfaceClassInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
+
+ method = env->GetMethodID(clazz.get(), "<init>", "()V");
+ CHECK(method != NULL);
+ gPersistentSurfaceClassInfo.ctor = method;
+
+ field = env->GetFieldID(clazz.get(), "mPersistentObject", "J");
+ CHECK(field != NULL);
+ gPersistentSurfaceClassInfo.mPersistentObject = field;
}
static void android_media_MediaCodec_native_setup(
@@ -1521,6 +1718,17 @@
{ "native_reset", "()V", (void *)android_media_MediaCodec_reset },
+ { "native_releasePersistentInputSurface",
+ "(Landroid/view/Surface;)V",
+ (void *)android_media_MediaCodec_releasePersistentInputSurface},
+
+ { "native_createPersistentInputSurface",
+ "()Landroid/media/MediaCodec$PersistentSurface;",
+ (void *)android_media_MediaCodec_createPersistentInputSurface },
+
+ { "native_usePersistentInputSurface", "(Landroid/view/Surface;)V",
+ (void *)android_media_MediaCodec_usePersistentInputSurface },
+
{ "native_setCallback",
"(Landroid/media/MediaCodec$Callback;)V",
(void *)android_media_MediaCodec_native_setCallback },
@@ -1530,6 +1738,10 @@
"Landroid/media/MediaCrypto;I)V",
(void *)android_media_MediaCodec_native_configure },
+ { "native_setSurface",
+ "(Landroid/view/Surface;)V",
+ (void *)android_media_MediaCodec_native_setSurface },
+
{ "createInputSurface", "()Landroid/view/Surface;",
(void *)android_media_MediaCodec_createInputSurface },
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 9f2785a..bf61f42 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -33,6 +33,7 @@
struct ICrypto;
struct IGraphicBufferProducer;
struct MediaCodec;
+struct PersistentSurface;
class Surface;
struct JMediaCodec : public AHandler {
@@ -53,7 +54,11 @@
const sp<ICrypto> &crypto,
int flags);
+ status_t setSurface(
+ const sp<IGraphicBufferProducer> &surface);
+
status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer);
+ status_t usePersistentInputSurface(const sp<PersistentSurface> &surface);
status_t start();
status_t stop();
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 02297fc..0044bed 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -29,6 +29,7 @@
#include <camera/ICameraService.h>
#include <camera/Camera.h>
#include <media/mediarecorder.h>
+#include <media/stagefright/PersistentSurface.h>
#include <utils/threads.h>
#include <ScopedUtfChars.h>
@@ -48,6 +49,8 @@
// helper function to extract a native Camera object from a Camera Java object
extern sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, struct JNICameraContext** context);
+extern sp<PersistentSurface>
+android_media_MediaCodec_getPersistentInputSurface(JNIEnv* env, jobject object);
struct fields_t {
jfieldID context;
@@ -115,6 +118,12 @@
return android_view_Surface_getSurface(env, clazz);
}
+static sp<PersistentSurface> get_persistentSurface(JNIEnv* env, jobject object)
+{
+ ALOGV("get_persistentSurface");
+ return android_media_MediaCodec_getPersistentInputSurface(env, object);
+}
+
// Returns true if it throws an exception.
static bool process_media_recorder_call(JNIEnv *env, status_t opStatus, const char* exception, const char* message)
{
@@ -487,6 +496,18 @@
android_media_MediaRecorder_release(env, thiz);
}
+void android_media_MediaRecorder_usePersistentSurface(
+ JNIEnv* env, jobject thiz, jobject object) {
+ ALOGV("android_media_MediaRecorder_usePersistentSurface");
+
+ sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+
+ sp<PersistentSurface> persistentSurface = get_persistentSurface(env, object);
+
+ process_media_recorder_call(env, mr->usePersistentSurface(persistentSurface),
+ "java/lang/IllegalArgumentException", "native_usePersistentSurface failed.");
+}
+
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
@@ -513,6 +534,7 @@
{"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V",
(void *)android_media_MediaRecorder_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaRecorder_native_finalize},
+ {"native_usePersistentSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_usePersistentSurface },
};
// This function only registers the native methods, and is called from
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java
index e730329..563b0f3 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java
@@ -27,6 +27,7 @@
import android.graphics.Paint;
import android.graphics.Typeface;
import android.hardware.Camera;
+import android.media.MediaCodec;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
@@ -225,10 +226,12 @@
private boolean recordVideoFromSurface(
int frameRate, int captureRate, int width, int height,
- int videoFormat, int outFormat, String outFile, boolean videoOnly) {
+ int videoFormat, int outFormat, String outFile, boolean videoOnly,
+ Surface persistentSurface) {
Log.v(TAG,"recordVideoFromSurface");
MediaRecorder recorder = new MediaRecorder();
int sleepTime = 33; // normal capture at 33ms / frame
+ Surface surface = null;
try {
if (!videoOnly) {
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
@@ -246,8 +249,15 @@
if (!videoOnly) {
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
}
+ if (persistentSurface != null) {
+ Log.v(TAG, "using persistent surface");
+ surface = persistentSurface;
+ recorder.usePersistentSurface(surface);
+ }
recorder.prepare();
- Surface surface = recorder.getSurface();
+ if (persistentSurface == null) {
+ surface = recorder.getSurface();
+ }
Paint paint = new Paint();
paint.setTextSize(16);
@@ -283,11 +293,15 @@
Log.v(TAG, "stop");
recorder.stop();
- recorder.release();
} catch (Exception e) {
- Log.v("record video failed ", e.toString());
- recorder.release();
+ Log.v(TAG, "record video failed: " + e.toString());
return false;
+ } finally {
+ recorder.release();
+ // release surface if not using persistent surface
+ if (persistentSurface == null && surface != null) {
+ surface.release();
+ }
}
return true;
}
@@ -550,7 +564,7 @@
success = recordVideoFromSurface(frameRate, 0, 352, 288, codec,
MediaRecorder.OutputFormat.THREE_GPP, filename,
- k == 0 ? true : false /* videoOnly */);
+ k == 0 ? true : false /* videoOnly */, null);
if (success) {
success = validateVideo(filename, 352, 288);
}
@@ -564,6 +578,40 @@
assertTrue("testSurfaceRecording", noOfFailure == 0);
}
+ public void testPersistentSurfaceRecording() {
+ boolean success = false;
+ int noOfFailure = 0;
+ Surface surface = null;
+ try {
+ int codec = MediaRecorder.VideoEncoder.H264;
+ int frameRate = MediaProfileReader.getMaxFrameRateForCodec(codec);
+ surface = MediaCodec.createPersistentInputSurface();
+ for (int k = 0; k < 2; k++) {
+ String filename = "/sdcard/surface_persistent" + k + ".3gp";
+
+ Log.v(TAG, "test persistent surface - round " + k);
+ success = recordVideoFromSurface(frameRate, 0, 352, 288, codec,
+ MediaRecorder.OutputFormat.THREE_GPP, filename,
+ true /* videoOnly */, surface);
+ if (success) {
+ success = validateVideo(filename, 352, 288);
+ }
+ if (!success) {
+ noOfFailure++;
+ }
+ }
+ } catch (Exception e) {
+ Log.v(TAG, e.toString());
+ } finally {
+ if (surface != null) {
+ Log.v(TAG, "releasing persistent surface");
+ surface.release();
+ surface = null;
+ }
+ }
+ assertTrue("testPersistentSurfaceRecording", noOfFailure == 0);
+ }
+
// Test recording from surface source with/without audio
public void testSurfaceRecordingTimeLapse() {
boolean success = false;
@@ -583,7 +631,7 @@
success = recordVideoFromSurface(
frameRate, captureRate, 352, 288, codec,
MediaRecorder.OutputFormat.THREE_GPP,
- filename, false /* videoOnly */);
+ filename, false /* videoOnly */, null);
if (success) {
success = validateVideo(filename, 352, 288);
if (success) {